From 3de7817da43abfeaba88b9f9ee30968e93ae398a Mon Sep 17 00:00:00 2001 From: Gabor Bata Date: Wed, 7 Feb 2024 14:09:18 +0100 Subject: [PATCH] remove trailing spaces from java sources --- DMUtils/src/CmdLib.java | 30 +- DMUtils/src/dcolors.java | 26 +- src/automap/IAutoMap.java | 2 +- src/automap/Map.java | 12 +- src/automap/fline_t.java | 6 +- src/automap/fpoint_t.java | 2 +- src/automap/islope_t.java | 2 +- src/automap/mline_t.java | 8 +- src/automap/mpoint_t.java | 2 +- src/awt/DisplayModePicker.java | 8 +- src/awt/DoomFrame.java | 6 +- src/awt/DoomWindow.java | 16 +- src/awt/DoomWindowController.java | 456 +- src/awt/EventBase.java | 946 +- src/awt/EventHandler.java | 780 +- src/awt/EventObserver.java | 6 +- src/awt/FullscreenOptions.java | 378 +- src/awt/MsgBox.java | 12 +- src/boom/Compatibility.java | 4 +- src/boom/DeepBSPNodesV4.java | 2 +- src/boom/E6Y.java | 4 +- src/boom/ZNodeSegs.java | 2 +- src/boom/mapglvertex_t.java | 2 +- src/boom/mapnode_v4_t.java | 4 +- src/boom/mapnode_znod_t.java | 2 +- src/boom/mapseg_v4_t.java | 6 +- src/boom/mapseg_znod_t.java | 2 +- src/boom/mapsubsector_v4_t.java | 2 +- src/boom/mapsubsector_znod_t.java | 2 +- src/boom/prboom_comp_t.java | 2 +- src/data/Defines.java | 12 +- src/data/Limits.java | 2 +- src/data/SineCosine.java | 8 +- src/data/Tables.java | 122 +- src/data/doomdata.java | 4 +- src/data/doomtype.java | 4 +- src/data/dstrings.java | 8 +- src/data/info.java | 8938 ++++++++--------- src/data/maplinedef_t.java | 2 +- src/data/mapnode_t.java | 4 +- src/data/mapsector_t.java | 2 +- src/data/mapseg_t.java | 6 +- src/data/mapsidedef_t.java | 2 +- src/data/mapsubsector_t.java | 2 +- src/data/mapthing_t.java | 2 +- src/data/mapvertex_t.java | 2 +- src/data/mobjinfo_t.java | 2 +- src/data/mobjtype_t.java | 2 +- src/data/musicinfo_t.java | 2 +- src/data/sfxinfo_t.java | 8 +- src/data/sounds.java | 8 +- src/data/spritenum_t.java | 2 +- src/data/state_t.java | 160 +- src/defines/DoomVersion.java | 8 +- src/defines/GameMission_t.java | 4 +- src/defines/GameMode.java | 6 +- src/defines/Language_t.java | 2 +- src/defines/ammotype_t.java | 4 +- src/defines/card_t.java | 2 +- src/defines/gamestate_t.java | 4 +- src/defines/skill_t.java | 2 +- src/defines/slopetype_t.java | 2 +- src/defines/statenum_t.java | 2 +- src/demo/IDemoTicCmd.java | 10 +- src/demo/IDoomDemo.java | 8 +- src/demo/VanillaDoomDemo.java | 4 +- src/demo/VanillaTiccmd.java | 18 +- src/doom/CVarManager.java | 10 +- src/doom/CommandVariable.java | 490 +- src/doom/ConfigBase.java | 6 +- src/doom/ConfigManager.java | 558 +- src/doom/DoomContext.java | 26 +- src/doom/DoomMain.java | 8018 +++++++-------- src/doom/DoomStatus.java | 18 +- src/doom/IDatagramSerializable.java | 24 +- src/doom/IDoom.java | 4 +- src/doom/IDoomGame.java | 8 +- src/doom/IDoomGameNetworking.java | 10 +- src/doom/KeyboardManager.java | 50 +- src/doom/NetConsts.java | 2 +- src/doom/SourceCode.java | 1352 +-- src/doom/doomcom_t.java | 2 +- src/doom/doomdata_t.java | 4 +- src/doom/englsh.java | 4 +- src/doom/event_t.java | 12 +- src/doom/evtype_t.java | 2 +- src/doom/gameaction_t.java | 2 +- src/doom/items.java | 2 +- src/doom/net.java | 170 +- src/doom/pic_t.java | 2 +- src/doom/player_t.java | 3130 +++--- src/doom/playerstate_t.java | 2 +- src/doom/th_class.java | 2 +- src/doom/thinker_t.java | 154 +- src/doom/ticcmd_t.java | 6 +- src/doom/wbplayerstruct_t.java | 2 +- src/doom/wbstartstruct_t.java | 2 +- src/doom/weaponinfo_t.java | 4 +- src/doom/weapontype_t.java | 2 +- src/f/AbstractEndLevel.java | 4 +- src/f/EndLevel.java | 16 +- src/f/Finale.java | 6 +- src/f/Wiper.java | 2 +- src/f/anim_t.java | 2 +- src/f/castinfo_t.java | 2 +- src/f/point_t.java | 2 +- src/g/DoomGameInterface.java | 4 +- src/g/DoomSaveGame.java | 24 +- src/g/Overflow.java | 38 +- src/g/Signals.java | 2 +- src/hu/HU.java | 8 +- src/hu/IHeadsUp.java | 2 +- src/i/DiskDrawer.java | 2 +- src/i/DoomEventInterface.java | 8 +- src/i/DoomSoundInterface.java | 2 +- src/i/DoomSystem.java | 4 +- src/i/DummySystem.java | 2 +- src/i/IDiskDrawer.java | 2 +- src/i/IDoomSystem.java | 8 +- src/i/IDrawer.java | 2 +- src/i/Strings.java | 2 +- src/i/SystemSoundInterface.java | 2 +- src/i/system.java | 10 +- src/m/AbstractDoomMenu.java | 2 +- src/m/BBox.java | 10 +- src/m/DelegateRandom.java | 244 +- src/m/DoomRandom.java | 362 +- src/m/DoomSetting.java | 34 +- src/m/DrawRoutine.java | 4 +- src/m/DummyMenu.java | 4 +- src/m/FixedFloat.java | 20 +- src/m/IDoomMenu.java | 6 +- src/m/IRandom.java | 46 +- src/m/ISyncLogger.java | 2 +- src/m/JavaRandom.java | 231 +- src/m/Menu.java | 14 +- src/m/MenuMisc.java | 8 +- src/m/MenuRoutine.java | 4 +- src/m/Settings.java | 8 +- src/m/Swap.java | 8 +- src/m/cheatseq_t.java | 8 +- src/m/default_t.java | 2 +- src/m/fixed_t.java | 30 +- src/m/menu_t.java | 4 +- src/m/menuitem_t.java | 2 +- src/m/pcx_t.java | 10 +- src/mochadoom/Engine.java | 4 +- src/mochadoom/Loggers.java | 338 +- src/n/BasicNetworkInterface.java | 6 +- src/n/DoomSystemNetworking.java | 3 +- src/n/DummyNetworkDriver.java | 4 +- src/n/DummyNetworkHandler.java | 2 +- src/n/IDoomNet.java | 2 +- src/p/AbstractLevelLoader.java | 22 +- src/p/ActionFunctions.java | 442 +- src/p/Actions/ActionTrait.java | 1122 +-- src/p/Actions/ActionsAim.java | 382 +- src/p/Actions/ActionsAttacks.java | 654 +- src/p/Actions/ActionsCeilings.java | 596 +- src/p/Actions/ActionsDoors.java | 986 +- src/p/Actions/ActionsEnemies.java | 568 +- src/p/Actions/ActionsFloors.java | 844 +- src/p/Actions/ActionsLights.java | 730 +- src/p/Actions/ActionsMissiles.java | 354 +- src/p/Actions/ActionsMobj.java | 772 +- src/p/Actions/ActionsMoveEvents.java | 1026 +- src/p/Actions/ActionsMovement.java | 1362 +-- src/p/Actions/ActionsPathTraverse.java | 772 +- src/p/Actions/ActionsPlats.java | 504 +- src/p/Actions/ActionsSectors.java | 950 +- src/p/Actions/ActionsShootEvents.java | 208 +- src/p/Actions/ActionsSight.java | 552 +- src/p/Actions/ActionsSlideDoors.java | 468 +- src/p/Actions/ActionsSpawns.java | 902 +- src/p/Actions/ActionsTeleportation.java | 402 +- src/p/Actions/ActionsThings.java | 1134 +-- src/p/Actions/ActionsThinkers.java | 694 +- src/p/Actions/ActionsUseEvents.java | 1078 +- src/p/Actions/ActiveStates/Ai.java | 590 +- src/p/Actions/ActiveStates/Attacks.java | 628 +- .../ActiveStates/MonsterStates/Bosses.java | 428 +- .../MonsterStates/Demonspawns.java | 220 +- .../MonsterStates/HorrendousVisages.java | 404 +- .../ActiveStates/MonsterStates/Mancubi.java | 194 +- .../MonsterStates/PainsSouls.java | 318 +- .../ActiveStates/MonsterStates/Skels.java | 292 +- .../ActiveStates/MonsterStates/Spiders.java | 102 +- .../ActiveStates/MonsterStates/Viles.java | 412 +- .../ActiveStates/MonsterStates/Zombies.java | 212 +- src/p/Actions/ActiveStates/Monsters.java | 90 +- src/p/Actions/ActiveStates/Sounds.java | 252 +- src/p/Actions/ActiveStates/Thinkers.java | 278 +- src/p/Actions/ActiveStates/Weapons.java | 416 +- src/p/ActiveStates.java | 450 +- src/p/BoomLevelLoader.java | 4474 ++++----- src/p/ChaseDirections.java | 2 +- src/p/DoomPlayer.java | 2 +- src/p/DoorDefines.java | 2 +- src/p/ILevelLoader.java | 16 +- src/p/ISightChecker.java | 2 +- src/p/Interceptable.java | 2 +- src/p/LevelLoader.java | 1886 ++-- src/p/MapUtils.java | 12 +- src/p/MobjFlags.java | 2 +- src/p/RemoveState.java | 2 +- src/p/Resettable.java | 2 +- src/p/Specials.java | 10 +- src/p/ThinkerList.java | 2 +- src/p/ThinkerStates.java | 2 +- src/p/UnifiedGameMap.java | 1404 +-- src/p/anim_t.java | 4 +- src/p/animdef_t.java | 12 +- src/p/button_t.java | 2 +- src/p/bwhere_e.java | 2 +- src/p/ceiling_e.java | 2 +- src/p/ceiling_t.java | 6 +- src/p/divline_t.java | 28 +- src/p/floor_e.java | 2 +- src/p/floormove_t.java | 6 +- src/p/intercept_t.java | 2 +- src/p/mobj_t.java | 1168 +-- src/p/plat_e.java | 2 +- src/p/plat_t.java | 10 +- src/p/plattype_e.java | 2 +- src/p/pspdef_t.java | 2 +- src/p/result_e.java | 2 +- src/p/sd_e.java | 2 +- src/p/sdt_e.java | 2 +- src/p/slidedoor_t.java | 48 +- src/p/slideframe_t.java | 2 +- src/p/slidename_t.java | 2 +- src/p/stair_e.java | 4 +- src/p/strobe_t.java | 6 +- src/p/switchlist_t.java | 2 +- src/p/vldoor_e.java | 2 +- src/p/vldoor_t.java | 6 +- src/pooling/AudioChunkPool.java | 2 +- src/pooling/GenericIntMap.java | 4 +- src/pooling/ObjectPool.java | 4 +- src/pooling/ObjectQueuePool.java | 6 +- src/pooling/RoguePatchMap.java | 2 +- src/pooling/RoguePatchMap2.java | 2 +- src/rr/AbstractThings.java | 30 +- src/rr/BSPVars.java | 2 +- src/rr/IDetailAware.java | 2 +- src/rr/IGetCachedColumn.java | 4 +- src/rr/IGetColumn.java | 4 +- src/rr/ILimitResettable.java | 8 +- src/rr/IMaskedDrawer.java | 2 +- src/rr/ISpriteManager.java | 4 +- src/rr/IVisSpriteManagement.java | 4 +- src/rr/MultiPatchSynthesizer.java | 34 +- src/rr/PlaneDrawer.java | 20 +- src/rr/RendererState.java | 6226 ++++++------ src/rr/SceneRenderer.java | 2 +- src/rr/SectorAction.java | 8 +- src/rr/SegVars.java | 6 +- src/rr/SimpleTextureManager.java | 162 +- src/rr/SimpleThings.java | 4 +- src/rr/SpriteManager.java | 36 +- src/rr/TextureManager.java | 8 +- src/rr/UnifiedRenderer.java | 6 +- src/rr/ViewVars.java | 2 +- src/rr/VisSprites.java | 8 +- src/rr/Visplanes.java | 18 +- src/rr/base_ratio_t.java | 2 +- src/rr/cliprange_t.java | 2 +- src/rr/column_t.java | 14 +- src/rr/drawfuns/ColFuncs.java | 2 +- src/rr/drawfuns/ColVars.java | 16 +- src/rr/drawfuns/ColumnFunction.java | 4 +- src/rr/drawfuns/DcFlags.java | 6 +- src/rr/drawfuns/DoomColumnFunction.java | 10 +- src/rr/drawfuns/DoomSpanFunction.java | 2 +- src/rr/drawfuns/R_DrawColumn.java | 10 +- src/rr/drawfuns/R_DrawColumnBoom.java | 4 +- src/rr/drawfuns/R_DrawColumnBoomLow.java | 4 +- src/rr/drawfuns/R_DrawColumnBoomOpt.java | 4 +- src/rr/drawfuns/R_DrawColumnBoomOptLow.java | 8 +- src/rr/drawfuns/R_DrawColumnBoomSuperOpt.java | 6 +- src/rr/drawfuns/R_DrawColumnLow.java | 2 +- src/rr/drawfuns/R_DrawColumnUnrolled.java | 8 +- src/rr/drawfuns/R_DrawFuzzColumn.java | 4 +- src/rr/drawfuns/R_DrawFuzzColumnLow.java | 4 +- src/rr/drawfuns/R_DrawSpan.java | 6 +- src/rr/drawfuns/R_DrawSpanLow.java | 2 +- src/rr/drawfuns/R_DrawSpanUnrolled.java | 4 +- src/rr/drawfuns/R_DrawSpanUnrolled2.java | 2 +- src/rr/drawfuns/R_DrawTLColumn.java | 2 +- src/rr/drawfuns/R_DrawTranslatedColumn.java | 2 +- .../drawfuns/R_DrawTranslatedColumnLow.java | 2 +- src/rr/drawfuns/SpanFunction.java | 4 +- src/rr/drawfuns/SpanVars.java | 2 +- src/rr/drawseg_t.java | 4 +- src/rr/flat_t.java | 2 +- src/rr/line_t.java | 8 +- src/rr/mappatch_t.java | 4 +- src/rr/maptexture_t.java | 6 +- src/rr/maskdraw_t.java | 6 +- src/rr/node_t.java | 2 +- src/rr/pQuickSprite.java | 2 +- src/rr/parallel/AbstractParallelRenderer.java | 8 +- src/rr/parallel/IGetSmpColumn.java | 6 +- src/rr/parallel/MaskedWorker.java | 30 +- src/rr/parallel/ParallelRenderer.java | 10 +- src/rr/parallel/ParallelRenderer2.java | 12 +- src/rr/parallel/ParallelThings.java | 6 +- src/rr/parallel/ParallelThings2.java | 14 +- src/rr/parallel/RWI.java | 2 +- src/rr/parallel/RenderMaskedExecutor.java | 4 +- src/rr/parallel/RenderSegExecutor.java | 34 +- src/rr/parallel/RenderSegInstruction.java | 4 +- src/rr/parallel/RenderWallExecutor.java | 4 +- src/rr/parallel/VisplaneWorker.java | 8 +- src/rr/parallel/VisplaneWorker2.java | 36 +- src/rr/patch_t.java | 14 +- src/rr/planefunction_t.java | 2 +- src/rr/sector_t.java | 660 +- src/rr/seg_t.java | 8 +- src/rr/side_t.java | 4 +- src/rr/spritedef_t.java | 4 +- src/rr/spriteframe_t.java | 6 +- src/rr/subsector_t.java | 6 +- src/rr/texpatch_t.java | 2 +- src/rr/texture_t.java | 6 +- src/rr/vertex_t.java | 4 +- src/rr/visplane_t.java | 8 +- src/rr/vissprite_t.java | 6 +- src/rr/z_vertex_t.java | 2 +- src/s/AbstractDoomAudio.java | 44 +- src/s/AbstractSoundDriver.java | 24 +- src/s/AudioChunk.java | 2 +- src/s/ClassicDoomSoundDriver.java | 12 +- src/s/ClipSFXModule.java | 30 +- src/s/DMXSound.java | 2 +- src/s/DSP.java | 6 +- src/s/DavidMusicModule.java | 10 +- src/s/DavidSFXModule.java | 38 +- src/s/DoomIO.java | 2 +- src/s/DoomSound.java | 6 +- src/s/DoomToWave.java | 2 +- src/s/DummyMusic.java | 2 +- src/s/DummySFX.java | 2 +- src/s/DummySoundDriver.java | 4 +- src/s/FinnwMusicModule.java | 4 +- src/s/IDoomSound.java | 14 +- src/s/IMusic.java | 6 +- src/s/ISoundDriver.java | 12 +- src/s/ISoundOrigin.java | 2 +- src/s/MusReader.java | 4 +- src/s/QMusToMid.java | 12 +- src/s/SpeakerDoomSoundDriver.java | 8 +- src/s/SpeakerSound.java | 10 +- src/s/SuperDoomSoundDriver.java | 36 +- src/s/VolumeScalingReceiver.java | 6 +- src/s/channel_t.java | 2 +- src/s/degenmobj_t.java | 2 +- src/savegame/IDoomSaveGame.java | 2 +- src/savegame/IDoomSaveGameHeader.java | 6 +- src/savegame/ILoadSaveGame.java | 2 +- src/savegame/VanillaDSG.java | 1758 ++-- src/savegame/VanillaDSGHeader.java | 24 +- src/st/AbstractStatusBar.java | 2 +- src/st/IDoomStatusBar.java | 4 +- src/st/StatusBar.java | 30 +- src/st/st_chatstateenum_t.java | 2 +- src/st/st_enumstatcodes_t.java | 2 +- src/st/st_stateenum_t.java | 2 +- src/timing/DelegateTicker.java | 98 +- src/timing/FastTicker.java | 2 +- src/timing/ITicker.java | 2 +- src/timing/MilliTicker.java | 2 +- src/timing/NanoTicker.java | 2 +- src/utils/BinarySearch.java | 1772 ++-- src/utils/C2JUtils.java | 64 +- src/utils/GenericCopy.java | 338 +- src/utils/OSValidator.java | 10 +- src/utils/OrderedExecutor.java | 234 +- src/utils/ParseString.java | 192 +- src/utils/QuoteType.java | 2 +- src/utils/ResourceIO.java | 196 +- src/utils/Throwers.java | 616 +- src/utils/TraitFactory.java | 598 +- src/v/DoomGraphicSystem.java | 40 +- src/v/graphics/Blocks.java | 270 +- src/v/graphics/ColorTransform.java | 146 +- src/v/graphics/Colors.java | 720 +- src/v/graphics/Columns.java | 10 +- src/v/graphics/Direction.java | 332 +- src/v/graphics/Horizontal.java | 6 +- src/v/graphics/Lights.java | 852 +- src/v/graphics/Lines.java | 166 +- src/v/graphics/Melt.java | 346 +- src/v/graphics/Palettes.java | 466 +- src/v/graphics/Patches.java | 452 +- src/v/graphics/Plotter.java | 504 +- src/v/graphics/Points.java | 114 +- src/v/graphics/Rectangles.java | 216 +- src/v/graphics/Relocation.java | 4 +- src/v/graphics/Screens.java | 214 +- src/v/graphics/Wipers.java | 6 +- src/v/renderers/BppMode.java | 2 +- src/v/renderers/BufferedRenderer.java | 2 +- src/v/renderers/BufferedRenderer16.java | 6 +- src/v/renderers/BufferedRenderer32.java | 2 +- src/v/renderers/DoomScreen.java | 76 +- src/v/renderers/RendererFactory.java | 234 +- src/v/renderers/SceneRendererMode.java | 236 +- src/v/renderers/SoftwareGraphicsSystem.java | 740 +- .../SoftwareIndexedVideoRenderer.java | 2 +- .../SoftwareParallelVideoRenderer.java | 6 +- src/v/scale/VideoScale.java | 2 +- src/v/scale/VideoScaleInfo.java | 10 +- src/v/scale/VisualSettings.java | 2 +- src/v/tables/BlurryTable.java | 456 +- src/v/tables/ColorTint.java | 4 +- src/v/tables/FuzzMix.java | 162 +- src/v/tables/GammaTables.java | 236 +- src/v/tables/GreyscaleFilter.java | 258 +- src/v/tables/LightsAndColors.java | 18 +- src/v/tables/Playpal.java | 4 +- src/w/AidedReadableDoomObject.java | 4 +- src/w/CacheableDoomObject.java | 32 +- src/w/CacheableDoomObjectContainer.java | 16 +- src/w/DoomBuffer.java | 16 +- src/w/DoomIO.java | 46 +- src/w/IPackableDoomObject.java | 2 +- src/w/IReadWriteDoomObject.java | 2 +- src/w/IReadableDoomObject.java | 4 +- src/w/IWadLoader.java | 4 +- src/w/IWritableDoomObject.java | 2 +- src/w/InputStreamSugar.java | 24 +- src/w/JadDecompress.java | 2 +- src/w/WadLoader.java | 86 +- src/w/animenum_t.java | 2 +- src/w/filelump_t.java | 4 +- src/w/li_namespace.java | 3 +- src/w/lumpinfo_t.java | 2 +- src/w/name8.java | 6 +- src/w/statenum_t.java | 2 +- src/w/wad_source_t.java | 4 +- src/w/wadfile_info_t.java | 4 +- src/w/wadheader_t.java | 2 +- src/w/wadinfo_t.java | 2 +- 444 files changed, 40644 insertions(+), 40647 deletions(-) diff --git a/DMUtils/src/CmdLib.java b/DMUtils/src/CmdLib.java index 2818999..a612121 100644 --- a/DMUtils/src/CmdLib.java +++ b/DMUtils/src/CmdLib.java @@ -128,7 +128,7 @@ public static FileInputStream SafeOpenRead (String filename) } catch (Exception e){ Error ("Error opening %s: %s",filename,e.getCause().getMessage()); } - + return handle; } @@ -138,9 +138,9 @@ public static void SafeRead (InputStream handle, byte[] buffer) throws IOExcepti int iocount; int read=0; int count=buffer.length; - + BufferedInputStream bis=new BufferedInputStream(handle,0x8000); - + while (count!=0) { iocount=bis.read(buffer,read,count-read); @@ -158,7 +158,7 @@ public static void SafeWrite (OutputStream handle, byte[] buffer, int count) thr public static void SafeWrite (OutputStream handle, short[] buffer) throws IOException { DataOutputStream dos=new DataOutputStream(handle); - + for (int i=0;i=0) { if (path.charAt(src) == '.') @@ -259,11 +259,11 @@ public static String StripFilename (String path) /** Return the filename without extension, and stripped * of the path. - * + * * @param s * @return */ - + public static final String StripExtension(String s) { String separator = System.getProperty("file.separator"); @@ -292,7 +292,7 @@ public static final String StripExtension(String s) { * indicators. There's normally no need to enforce this behavior, as there's * nothing preventing the engine from INTERNALLY using lump names with >8 * chars. However, just to be sure... - * + * * @param path * @param limit Set to any value >0 to enforce a length limit * @param whole keep extension if set to true @@ -300,9 +300,9 @@ public static final String StripExtension(String s) { */ public static final String ExtractFileBase(String path, int limit, boolean whole) { - + if (path==null) return path; - + int src = path.length() - 1; String separator = System.getProperty("file.separator"); @@ -313,11 +313,11 @@ public static final String ExtractFileBase(String path, int limit, boolean whole int len = path.lastIndexOf('.'); if (whole || len<0 ) len=path.length()-src; // No extension. - else len-= src; + else len-= src; - // copy UP to the specific number of characters, or all + // copy UP to the specific number of characters, or all if (limit > 0) len = Math.min(limit, len); - + return path.substring(src, src + len); } @@ -419,4 +419,4 @@ public static long LittleLong (long l) } */ -} +} \ No newline at end of file diff --git a/DMUtils/src/dcolors.java b/DMUtils/src/dcolors.java index c6ed0b3..eb8dfc3 100644 --- a/DMUtils/src/dcolors.java +++ b/DMUtils/src/dcolors.java @@ -4,7 +4,7 @@ public class dcolors { public static final String VERSION= "1.1"; - + public final static int playpal[] = { 0x00,0x00,0x00,0x1F,0x17,0x0B,0x17,0x0F,0x07,0x4B,0x4B,0x4B,0xFF,0xFF,0xFF,0x1B, 0x1B,0x1B,0x13,0x13,0x13,0x0B,0x0B,0x0B,0x07,0x07,0x07,0x2F,0x37,0x1F,0x23,0x2B, @@ -194,7 +194,7 @@ static private void RF_BuildLights () color15 = getRGB555(red,green,blue); System.out.printf("Color15 for %2x %2x %2x: %4x %4x\n",red,green,blue,color12,color15); - + lightpalette[l][c] = BestColor(red,green,blue,palette,0,255); color12s[l][c] = color12; @@ -213,7 +213,7 @@ static private void RF_BuildLights () public static final short getRGB555(int red,int green,int blue){ int ri,gi,bi; - + ri = (((red+4)>255?255:red+4))>>3; ri = ri > 31 ? 31 : ri; gi = (((green+4)>255?255:green+4))>>3; @@ -224,10 +224,10 @@ public static final short getRGB555(int red,int green,int blue){ // RGB555 for HiColor return (short) ((ri<<10) + (gi<<5) + bi); } - + public static final short getRGBA4444(int red,int green,int blue){ int ri,gi,bi; - + ri = (((red+8)>255?255:red+8))>>4; ri = ri > 15 ? 15 : ri; gi = (((green+8)>255?255:green+8))>>4; @@ -236,7 +236,7 @@ public static final short getRGBA4444(int red,int green,int blue){ bi = bi > 15 ? 15 : bi; // RGBA 4-4-4-4 packed for NeXT - + return (short) ((ri<<12)+ (gi<<8) +(bi<<4)+15); //return (short)(0xDEAD); } @@ -264,12 +264,12 @@ static private void BuildSpecials () green = (float) ((0xFF&palette[palsrc++]) / 256.0); blue = (float) ((0xFF&palette[palsrc++]) / 256.0); - gray = (int) (255*(1.0-(red*0.299 + green*0.587 + blue*0.144))); - + gray = (int) (255*(1.0-(red*0.299 + green*0.587 + blue*0.144))); + color12s[GRAYCOLORMAP][c] = getRGBA4444(gray,gray,gray); System.out.printf("%4x for %x\n",color12s[GRAYCOLORMAP][c],gray); color15s[GRAYCOLORMAP][c] = getRGB555(gray,gray,gray); - + lightpalette[GRAYCOLORMAP][c] = BestColor(gray,gray,gray,palette,0,255); } @@ -301,7 +301,7 @@ public static void main (String[] argv) throws IOException System.out.printf ("\nDCOLORS %s by John Carmack, copyright (c) 1992 Id Software\n",VERSION); /* - if (argv.length != 2) { + if (argv.length != 2) { CmdLib.Error("dcolors picture.lbm"); } */ @@ -320,14 +320,14 @@ public static void main (String[] argv) throws IOException palhandle=CmdLib.SafeOpenRead("PLAYPAL.lmp"); CmdLib.SafeRead(palhandle,palette); palhandle.close(); - + for (i=0;i { public void Stop(); public void Start(); -} +} \ No newline at end of file diff --git a/src/automap/Map.java b/src/automap/Map.java index f6adc29..bfe5d27 100644 --- a/src/automap/Map.java +++ b/src/automap/Map.java @@ -233,7 +233,7 @@ public class Map implements IAutoMap { /** * Configurable colors - now an enum * - Good Sign 2017/04/05 - * + * * Use colormap-specific colors to support extended modes. * Moved hardcoding in here. Potentially configurable. */ @@ -611,7 +611,7 @@ protected void initVectorGraphics() { * Calculates the slope and slope according to the x-axis of a line segment * in map coordinates (with the upright y-axis n' all) so that it can be * used with the brain-dead drawing stuff. - * + * * @param ml * @param is */ @@ -1000,7 +1000,7 @@ public final boolean Responder(event_t ev) { cheating = (cheating + 1) % 3; } - /** + /** * MAES: brought back strobe effect * Good Sign: setting can be saved/loaded from config */ @@ -1250,7 +1250,7 @@ private boolean clipMline(mline_t ml, fline_t fl) { /** * MAES: the result was supposed to be passed in an "oc" parameter by * reference. Not convenient, so I made some changes... - * + * * @param mx * @param my */ @@ -1405,7 +1405,7 @@ else if (plr.powers[pw_allmap] != 0) { /** * Rotation in 2D. Used to rotate player arrow line character. - * + * * @param x * fixed_t * @param y @@ -1587,4 +1587,4 @@ public final void Drawer() { //DOOM.videoRenderer.MarkRect(f_x, f_y, f_w, f_h); } -} +} \ No newline at end of file diff --git a/src/automap/fline_t.java b/src/automap/fline_t.java index 659b45d..085c9c2 100644 --- a/src/automap/fline_t.java +++ b/src/automap/fline_t.java @@ -7,7 +7,7 @@ public class fline_t { a=new fpoint_t(); b=new fpoint_t(); } - + public fline_t(fpoint_t a, fpoint_t b){ this.a=a; this.b=b; @@ -33,6 +33,6 @@ public void reset() { this.a.y=0; this.b.x=0; this.b.y=0; - + }*/ -} +} \ No newline at end of file diff --git a/src/automap/fpoint_t.java b/src/automap/fpoint_t.java index 80616a1..cdeca28 100644 --- a/src/automap/fpoint_t.java +++ b/src/automap/fpoint_t.java @@ -13,4 +13,4 @@ public fpoint_t(int x, int y) { this.y = y; } -} +} \ No newline at end of file diff --git a/src/automap/islope_t.java b/src/automap/islope_t.java index d928174..50ccc09 100644 --- a/src/automap/islope_t.java +++ b/src/automap/islope_t.java @@ -4,4 +4,4 @@ public class islope_t { /** fixed_t */ int slp, islp; -} +} \ No newline at end of file diff --git a/src/automap/mline_t.java b/src/automap/mline_t.java index 4523444..c00313f 100644 --- a/src/automap/mline_t.java +++ b/src/automap/mline_t.java @@ -33,17 +33,17 @@ public mline_t(int ax,int ay,int bx,int by) { this.a = new mpoint_t(ax,ay); this.b = new mpoint_t(bx,by); } - + public mline_t(double ax,double ay,double bx,double by) { this.a = new mpoint_t(ax,ay); this.b = new mpoint_t(bx,by); } - + public mpoint_t a, b; public int ax; - + public String toString(){ return a.toString()+" - "+ b.toString(); } */ -} +} \ No newline at end of file diff --git a/src/automap/mpoint_t.java b/src/automap/mpoint_t.java index 0e8a5e5..3e64a2b 100644 --- a/src/automap/mpoint_t.java +++ b/src/automap/mpoint_t.java @@ -30,4 +30,4 @@ public mpoint_t() { public String toString() { return (Integer.toHexString(x) + " , " + Integer.toHexString(y)); } -}; +}; \ No newline at end of file diff --git a/src/awt/DisplayModePicker.java b/src/awt/DisplayModePicker.java index 4936b2f..57a0259 100644 --- a/src/awt/DisplayModePicker.java +++ b/src/awt/DisplayModePicker.java @@ -40,11 +40,11 @@ public DisplayMode pickClosest(int width, int height) { return picks.get(0); } - /** - * Return offsets to center rasters too oddly shaped to fit entirely into + /** + * Return offsets to center rasters too oddly shaped to fit entirely into * a standard display mode (unfortunately, this means most stuff > 640 x 400), * with doom's standard 8:5 ratio. - * + * * @param width * @param height * @param dm @@ -87,4 +87,4 @@ public int compare(DisplayMode arg0, DisplayMode arg1) { } } -} +} \ No newline at end of file diff --git a/src/awt/DoomFrame.java b/src/awt/DoomFrame.java index d3443b0..858ca14 100644 --- a/src/awt/DoomFrame.java +++ b/src/awt/DoomFrame.java @@ -88,7 +88,7 @@ public void turnOn() { /** * Set it to be later then setResizable to avoid extra space on right and bottom * - Good Sign 2017/04/09 - * + * * JFrame's size is auto-set here. */ pack(); @@ -98,7 +98,7 @@ public void turnOn() { setVisible(true); - // Gently tell the eventhandler to wake up and set itself. + // Gently tell the eventhandler to wake up and set itself. requestFocus(); content.requestFocusInWindow(); } @@ -180,4 +180,4 @@ private Graphics2D getGraphics2D() { private final boolean showFPS = Engine.getCVM().bool(CommandVariable.SHOWFPS); private long lastTime = System.currentTimeMillis(); private int frames = 0; -} +} \ No newline at end of file diff --git a/src/awt/DoomWindow.java b/src/awt/DoomWindow.java index c57ce27..cdc477f 100644 --- a/src/awt/DoomWindow.java +++ b/src/awt/DoomWindow.java @@ -15,14 +15,14 @@ import javax.swing.JPanel; import mochadoom.Engine; -/** - * Methods specific to Doom-System video interfacing. +/** + * Methods specific to Doom-System video interfacing. * In essence, whatever you are using as a final system-specific way to display * the screens, should be able to respond to these commands. In particular, * screen update requests must be honored, and palette/gamma request changes * must be intercepted before they are forwarded to the renderers (in case they * are system-specific, rather than renderer-specific). - * + * * The idea is that the final screen rendering module sees/handles as less as * possible, and only gets a screen to render, no matter what depth it is. */ @@ -78,10 +78,10 @@ default boolean handleGeom() { String d; int n; int pnum; - + boolean oktodraw; long attribmask; - + // Try setting the locale the US, otherwise there will be problems // with non-US keyboards. if (this.getInputContext() == null || !this.getInputContext().selectInputMethod(java.util.Locale.US)) { @@ -89,10 +89,10 @@ default boolean handleGeom() { } else { System.err.println("Input context successfully set to US."); } - + // check for command-line display name displayname = Game.getCVM().get(CommandVariable.DISP, String.class, 0).orElse(null); - + // check for command-line geometry*/ if (Engine.getCVM().present(CommandVariable.GEOM)) { try { @@ -158,4 +158,4 @@ private CanvasWindow(GraphicsConfiguration config) { super(config); } } -} +} \ No newline at end of file diff --git a/src/awt/DoomWindowController.java b/src/awt/DoomWindowController.java index ef61c1f..fd299ac 100644 --- a/src/awt/DoomWindowController.java +++ b/src/awt/DoomWindowController.java @@ -1,228 +1,228 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package awt; - -import doom.event_t; -import java.awt.Color; -import java.awt.Component; -import java.awt.DisplayMode; -import java.awt.GraphicsDevice; -import java.awt.Image; -import java.awt.Toolkit; -import java.util.function.Consumer; -import java.util.function.Supplier; -import java.util.logging.Level; -import java.util.logging.Logger; -import m.Settings; -import mochadoom.Engine; -import mochadoom.Loggers; - -/** - * Display, its configuration and resolution related stuff, - * DoomFrame creation, full-screen related code. Window recreation control. - * That sort of things. - */ -public class DoomWindowController, H extends Enum & EventBase> implements FullscreenOptions { - - private static final Logger LOGGER = Loggers.getLogger(DoomWindow.class.getName()); - - private static final long ALL_EVENTS_MASK = 0xFFFF_FFFF_FFFF_FFFFL; - - final GraphicsDevice device; - final FullscreenFunction switcher; - final int defaultWidth, defaultHeight; - - private final E component; - private final EventObserver observer; - private DoomFrame doomFrame; - - /** - * Default window size. It might change upon entering full screen, so don't consider it absolute. Due to letter - * boxing and screen doubling, stretching etc. it might be different that the screen buffer (typically, larger). - */ - private final DimensionImpl dimension; - private boolean isFullScreen; - - DoomWindowController( - final Class handlerClass, - final GraphicsDevice device, - final Supplier imageSource, - final Consumer doomEventConsumer, - final E component, - final int defaultWidth, - final int defaultHeight - ) { - this.device = device; - this.switcher = createFullSwitcher(device); - this.component = component; - this.defaultWidth = defaultWidth; - this.defaultHeight = defaultHeight; - this.dimension = new DimensionImpl(defaultWidth, defaultHeight); - this.doomFrame = new DoomFrame<>(dimension, component, imageSource); - this.observer = new EventObserver<>(handlerClass, component, doomEventConsumer); - Toolkit.getDefaultToolkit().addAWTEventListener(observer::observe, ALL_EVENTS_MASK); - sizeInit(); - doomFrame.turnOn(); - } - - private void sizeInit() { - try { - if (!(Engine.getConfig().equals(Settings.fullscreen, Boolean.TRUE) && switchToFullScreen())) { - updateSize(); - } - } catch (Exception e) { - LOGGER.log(Level.SEVERE, - String.format("Error creating DOOM AWT frame. Exiting. Reason: %s", e.getMessage()), e); - throw e; - } - } - - public void updateFrame() { - doomFrame.update(); - } - - public EventObserver getObserver() { - return observer; - } - - public boolean switchFullscreen() { - LOGGER.log(Level.INFO, "Fullscreen switched"); - // remove the frame from view - doomFrame.dispose(); - doomFrame = new DoomFrame<>(dimension, component, doomFrame.imageSupplier); - // change all the properties - final boolean ret = switchToFullScreen(); - // now show back the frame - doomFrame.turnOn(); - return ret; - } - - /** - * FULLSCREEN SWITCH CODE TODO: it's not enough to do this without also switching the screen's resolution. - * Unfortunately, Java only has a handful of options which depend on the OS, driver, display, JVM etc. and it's not - * possible to switch to arbitrary resolutions. - * - * Therefore, a "best fit" strategy with centering is used. - */ - public final boolean switchToFullScreen() { - if (!isFullScreen) { - isFullScreen = device.isFullScreenSupported(); - if (!isFullScreen) { - return false; - } - } else { - isFullScreen = false; - } - final DisplayMode displayMode = switcher.get(defaultWidth, defaultHeight); - doomFrame.setUndecorated(isFullScreen); - - // Full-screen mode - device.setFullScreenWindow(isFullScreen ? doomFrame : null); - if (device.isDisplayChangeSupported()) { - device.setDisplayMode(displayMode); - } - - component.validate(); - dimension.setSize(displayMode); - updateSize(); - return isFullScreen; - } - - private void updateSize() { - doomFrame.setPreferredSize(isFullscreen() ? dimension : null); - component.setPreferredSize(dimension); - component.setBounds(0, 0, defaultWidth - 1, defaultHeight - 1); - component.setBackground(Color.black); - doomFrame.renewGraphics(); - } - - public boolean isFullscreen() { - return isFullScreen; - } - - private class DimensionImpl extends java.awt.Dimension implements Dimension { - - private static final long serialVersionUID = 4598094740125688728L; - private int offsetX, offsetY; - private int fitWidth, fitHeight; - - DimensionImpl(int width, int height) { - this.width = defaultWidth; - this.height = defaultHeight; - this.offsetX = offsetY = 0; - this.fitWidth = width; - this.fitHeight = height; - } - - @Override - public int width() { - return width; - } - - @Override - public int height() { - return height; - } - - @Override - public int defWidth() { - return defaultWidth; - } - - @Override - public int defHeight() { - return defaultHeight; - } - - @Override - public int fitX() { - return fitWidth; - } - - @Override - public int fitY() { - return fitHeight; - } - - @Override - public int offsX() { - return offsetX; - } - - @Override - public int offsY() { - return offsetY; - } - - private void setSize(DisplayMode mode) { - if (isFullScreen) { - this.width = mode.getWidth(); - this.height = mode.getHeight(); - this.offsetX = Dimension.super.offsX(); - this.offsetY = Dimension.super.offsY(); - this.fitWidth = Dimension.super.fitX(); - this.fitHeight = Dimension.super.fitY(); - } else { - this.width = defaultWidth; - this.height = defaultHeight; - this.offsetX = offsetY = 0; - this.fitWidth = width; - this.fitHeight = height; - } - } - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package awt; + +import doom.event_t; +import java.awt.Color; +import java.awt.Component; +import java.awt.DisplayMode; +import java.awt.GraphicsDevice; +import java.awt.Image; +import java.awt.Toolkit; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; +import m.Settings; +import mochadoom.Engine; +import mochadoom.Loggers; + +/** + * Display, its configuration and resolution related stuff, + * DoomFrame creation, full-screen related code. Window recreation control. + * That sort of things. + */ +public class DoomWindowController, H extends Enum & EventBase> implements FullscreenOptions { + + private static final Logger LOGGER = Loggers.getLogger(DoomWindow.class.getName()); + + private static final long ALL_EVENTS_MASK = 0xFFFF_FFFF_FFFF_FFFFL; + + final GraphicsDevice device; + final FullscreenFunction switcher; + final int defaultWidth, defaultHeight; + + private final E component; + private final EventObserver observer; + private DoomFrame doomFrame; + + /** + * Default window size. It might change upon entering full screen, so don't consider it absolute. Due to letter + * boxing and screen doubling, stretching etc. it might be different that the screen buffer (typically, larger). + */ + private final DimensionImpl dimension; + private boolean isFullScreen; + + DoomWindowController( + final Class handlerClass, + final GraphicsDevice device, + final Supplier imageSource, + final Consumer doomEventConsumer, + final E component, + final int defaultWidth, + final int defaultHeight + ) { + this.device = device; + this.switcher = createFullSwitcher(device); + this.component = component; + this.defaultWidth = defaultWidth; + this.defaultHeight = defaultHeight; + this.dimension = new DimensionImpl(defaultWidth, defaultHeight); + this.doomFrame = new DoomFrame<>(dimension, component, imageSource); + this.observer = new EventObserver<>(handlerClass, component, doomEventConsumer); + Toolkit.getDefaultToolkit().addAWTEventListener(observer::observe, ALL_EVENTS_MASK); + sizeInit(); + doomFrame.turnOn(); + } + + private void sizeInit() { + try { + if (!(Engine.getConfig().equals(Settings.fullscreen, Boolean.TRUE) && switchToFullScreen())) { + updateSize(); + } + } catch (Exception e) { + LOGGER.log(Level.SEVERE, + String.format("Error creating DOOM AWT frame. Exiting. Reason: %s", e.getMessage()), e); + throw e; + } + } + + public void updateFrame() { + doomFrame.update(); + } + + public EventObserver getObserver() { + return observer; + } + + public boolean switchFullscreen() { + LOGGER.log(Level.INFO, "Fullscreen switched"); + // remove the frame from view + doomFrame.dispose(); + doomFrame = new DoomFrame<>(dimension, component, doomFrame.imageSupplier); + // change all the properties + final boolean ret = switchToFullScreen(); + // now show back the frame + doomFrame.turnOn(); + return ret; + } + + /** + * FULLSCREEN SWITCH CODE TODO: it's not enough to do this without also switching the screen's resolution. + * Unfortunately, Java only has a handful of options which depend on the OS, driver, display, JVM etc. and it's not + * possible to switch to arbitrary resolutions. + * + * Therefore, a "best fit" strategy with centering is used. + */ + public final boolean switchToFullScreen() { + if (!isFullScreen) { + isFullScreen = device.isFullScreenSupported(); + if (!isFullScreen) { + return false; + } + } else { + isFullScreen = false; + } + final DisplayMode displayMode = switcher.get(defaultWidth, defaultHeight); + doomFrame.setUndecorated(isFullScreen); + + // Full-screen mode + device.setFullScreenWindow(isFullScreen ? doomFrame : null); + if (device.isDisplayChangeSupported()) { + device.setDisplayMode(displayMode); + } + + component.validate(); + dimension.setSize(displayMode); + updateSize(); + return isFullScreen; + } + + private void updateSize() { + doomFrame.setPreferredSize(isFullscreen() ? dimension : null); + component.setPreferredSize(dimension); + component.setBounds(0, 0, defaultWidth - 1, defaultHeight - 1); + component.setBackground(Color.black); + doomFrame.renewGraphics(); + } + + public boolean isFullscreen() { + return isFullScreen; + } + + private class DimensionImpl extends java.awt.Dimension implements Dimension { + + private static final long serialVersionUID = 4598094740125688728L; + private int offsetX, offsetY; + private int fitWidth, fitHeight; + + DimensionImpl(int width, int height) { + this.width = defaultWidth; + this.height = defaultHeight; + this.offsetX = offsetY = 0; + this.fitWidth = width; + this.fitHeight = height; + } + + @Override + public int width() { + return width; + } + + @Override + public int height() { + return height; + } + + @Override + public int defWidth() { + return defaultWidth; + } + + @Override + public int defHeight() { + return defaultHeight; + } + + @Override + public int fitX() { + return fitWidth; + } + + @Override + public int fitY() { + return fitHeight; + } + + @Override + public int offsX() { + return offsetX; + } + + @Override + public int offsY() { + return offsetY; + } + + private void setSize(DisplayMode mode) { + if (isFullScreen) { + this.width = mode.getWidth(); + this.height = mode.getHeight(); + this.offsetX = Dimension.super.offsX(); + this.offsetY = Dimension.super.offsY(); + this.fitWidth = Dimension.super.fitX(); + this.fitHeight = Dimension.super.fitY(); + } else { + this.width = defaultWidth; + this.height = defaultHeight; + this.offsetX = offsetY = 0; + this.fitWidth = width; + this.fitHeight = height; + } + } + } +} \ No newline at end of file diff --git a/src/awt/EventBase.java b/src/awt/EventBase.java index 670a0f3..df50957 100644 --- a/src/awt/EventBase.java +++ b/src/awt/EventBase.java @@ -1,473 +1,473 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package awt; - -import g.Signals; -import java.awt.AWTEvent; -import java.util.Arrays; -import java.util.Comparator; -import java.util.EnumMap; -import java.util.EnumSet; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.function.Function; -import java.util.function.IntFunction; -import java.util.function.IntSupplier; - -/** - * The base for construction of Event handling dictionaries - * EventHandler is a reference implementation of this base - * - * Note the type safety with generics.It could be a complex task, but you can avoid - unchecked casts and warnings suppression. Whoa... Make my head swirl around! - - Good Sign 2017/04/24 - * - * @author Good Sign - * @param the type of handler - */ -public interface EventBase & EventBase> extends IntSupplier { - - static final Comparator EVENT_SORT = Comparator.comparingInt(IntSupplier::getAsInt); - - static & EventBase> H[] sortHandlers(H[] values) { - Arrays.sort(values, EVENT_SORT); - return values; - } - - /** - * Find event by id - * @param type of event - * @param values event values - * @param eventId the id - * @return the event (can be null) - */ - static & EventBase> H findById(H[] values, int eventId) { - final int index = Arrays.binarySearch(values, (IntSupplier) () -> eventId, EVENT_SORT); - if (index < 0) { - return null; - } - return values[index]; - } - - @SafeVarargs - static & EventBase> Relation[] Relate(H src, H... dests) { - @SuppressWarnings("unchecked") - final IntFunction[]> arrayer = Relation[]::new; - return Arrays.stream(dests) - .map(dest -> new Relation<>(src, dest)) - .toArray(arrayer); - } - - Set defaultEnabledActions(); - - Map> allActions(); - - Map> cooperations(); - - Map> adjustments(); - - default boolean hasActions(final ActionMode... modes) { - final Set actions = defaultEnabledActions(); - if (actions.isEmpty()) { - return false; - } - - for (final ActionMode m : modes) { - if (!actions.contains(m)) { - return false; - } - } - - return true; - } - - enum KeyStateSatisfaction { - SATISFIED_ATE, - GENEOROUS_PASS, - WANTS_MORE_ATE, - WANTS_MORE_PASS - } - - enum ActionMode { - PERFORM, DEPEND, CAUSE, REVERT; - } - - enum RelationAffection { - ENABLES, DISABLES, COOPERATES; - } - - enum RelationType { - ENABLE(RelationAffection.ENABLES, ActionMode.PERFORM), - ENABLE_DEPEND(RelationAffection.ENABLES, ActionMode.DEPEND), - ENABLE_CAUSE(RelationAffection.ENABLES, ActionMode.CAUSE), - ENABLE_REVERT(RelationAffection.ENABLES, ActionMode.REVERT), - DISABLE(RelationAffection.DISABLES, ActionMode.PERFORM), - DISABLE_DEPEND(RelationAffection.DISABLES, ActionMode.DEPEND), - DISABLE_CAUSE(RelationAffection.DISABLES, ActionMode.CAUSE), - DISABLE_REVERT(RelationAffection.DISABLES, ActionMode.REVERT), - DEPEND(RelationAffection.COOPERATES, ActionMode.DEPEND), - CAUSE(RelationAffection.COOPERATES, ActionMode.CAUSE), - REVERT(RelationAffection.COOPERATES, ActionMode.REVERT); - - final RelationAffection affection; - final ActionMode affectedMode; - - private RelationType(RelationAffection affection, ActionMode affectedMode) { - this.affection = affection; - this.affectedMode = affectedMode; - } - - @Override - public String toString() { - return String.format("%s on [%s]", affection, affectedMode); - } - } - - @FunctionalInterface - interface ActionMapper & EventBase> { - - void map(ActionMode mode, EventAction action); - } - - @FunctionalInterface - interface RelationMapper & EventBase> { - - void map(RelationType type, Relation[] relations); - } - - @FunctionalInterface - interface EventAction & EventBase> { - - void act(EventObserver obs, AWTEvent ev); - } - - interface KeyStateCallback & EventBase> { - - KeyStateSatisfaction call(EventObserver observer); - } - - final class KeyStateInterest & EventBase> { - - private final Set interestSet; - private final KeyStateCallback satisfiedCallback; - - public KeyStateInterest( - final KeyStateCallback satisfiedCallback, - final Signals.ScanCode interestFirstKey, - Signals.ScanCode... interestKeyChain - ) { - this.interestSet = EnumSet.of(interestFirstKey, interestKeyChain); - this.satisfiedCallback = satisfiedCallback; - } - } - - final class KeyStateHolder & EventBase> { - - private final Set holdingSet; - private final LinkedHashSet> keyInterests; - @SuppressWarnings("unchecked") - private final IntFunction[]> generator = KeyStateInterest[]::new; - - public KeyStateHolder() { - this.holdingSet = EnumSet.noneOf(Signals.ScanCode.class); - this.keyInterests = new LinkedHashSet<>(); - } - - public void removeAllKeys() { - holdingSet.clear(); - } - - public boolean contains(Signals.ScanCode sc) { - return holdingSet.contains(sc); - } - - public void addInterest(KeyStateInterest interest) { - this.keyInterests.add(interest); - } - - public void removeInterest(KeyStateInterest interest) { - this.keyInterests.remove(interest); - } - - public boolean matchInterest(final KeyStateInterest check) { - return holdingSet.containsAll(check.interestSet); - } - - public boolean notifyKeyChange(EventObserver observer, Signals.ScanCode code, boolean press) { - if (press) { - holdingSet.add(code); - - final KeyStateInterest[] matched = keyInterests.stream() - .filter(this::matchInterest) - .toArray(this.generator); - - boolean ret = false; - for (int i = 0; i < matched.length; ++i) { - switch (matched[i].satisfiedCallback.call(observer)) { - case SATISFIED_ATE: - ret = true; - case GENEOROUS_PASS: - keyInterests.remove(matched[i]); - break; - case WANTS_MORE_ATE: - ret = true; - case WANTS_MORE_PASS: - break; - } - } - - return ret; - } else { - holdingSet.remove(code); - return false; - } - } - } - - /** - * Enable/disable and remaps of actions is actually reflected here. It is only initial template in the Handler - */ - final class ActionStateHolder & EventBase> { - - private final Map> enabledActions; - private final Map>> actionsMap; - private final Map>> cooperationMap; - private final Map>> adjustmentMap; - private final EventObserver observer; - private final EnumSet emptyEnumSet; - - public boolean hasActionsEnabled(final Handler h, final ActionMode... modes) { - final Set actions = enabledActions.get(h); - if (actions.isEmpty()) { - return false; - } - - for (final ActionMode m : modes) { - if (!actions.contains(m)) { - return false; - } - } - - return true; - } - - public ActionStateHolder(final Class hClass, final EventObserver observer) { - final Handler[] values = hClass.getEnumConstants(); - this.enabledActions = populate(hClass, values, h -> { - final Set set = h.defaultEnabledActions(); - return set.isEmpty() ? EnumSet.noneOf(ActionMode.class) : EnumSet.copyOf(set); - }); - this.actionsMap = populate(hClass, values, h -> { - final Map> map = h.allActions(); - return map.isEmpty() ? new EnumMap<>(ActionMode.class) : new EnumMap<>(map); - }); - this.cooperationMap = populate(hClass, values, h -> deepCopyMap(h.cooperations())); - this.adjustmentMap = populate(hClass, values, h -> deepCopyMap(h.adjustments())); - this.observer = observer; - this.emptyEnumSet = EnumSet.noneOf(hClass); - } - - private Map> deepCopyMap(final Map> map) { - if (map.isEmpty()) { - return new EnumMap<>(RelationType.class); - } - - // shallow copy first - final EnumMap> copy = new EnumMap<>(map); - // now values - copy.replaceAll((r, l) -> EnumSet.copyOf(l)); - return copy; - } - - private Map populate(Class hClass, Handler[] values, Function mapper) { - return Arrays.stream(values).collect( - () -> new EnumMap<>(hClass), - (m, h) -> m.put(h, mapper.apply(h)), - EnumMap::putAll - ); - } - - public ActionStateHolder run(final Handler h, final ActionMode mode, final AWTEvent ev) { - if (enabledActions.get(h).contains(mode)) { - Optional.ofNullable(actionsMap.get(h).get(mode)).ifPresent(action -> action.act(observer, ev)); - } - - return this; - } - - public Map> cooperations(final Handler h) { - return cooperationMap.get(h); - } - - public Map> adjustments(final Handler h) { - return adjustmentMap.get(h); - } - - public Set cooperations(final Handler h, final RelationType type) { - return cooperationMap.get(h).getOrDefault(type, emptyEnumSet); - } - - public Set adjustments(final Handler h, final RelationType type) { - return adjustmentMap.get(h).getOrDefault(type, emptyEnumSet); - } - - @SafeVarargs - public final ActionStateHolder unmapCooperation(final Handler h, RelationType type, final Handler... targets) { - final Set set = cooperationMap.get(h).get(type); - if (set == null || set.isEmpty()) { - return this; - } - - if (targets.length == 0) { - set.clear(); - } else { - set.removeAll(Arrays.asList(targets)); - } - - return this; - } - - @SafeVarargs - public final ActionStateHolder mapCooperation(final Handler h, RelationType mode, final Handler... targets) { - cooperationMap.get(h).compute(mode, (m, set) -> { - if (set == null) { - set = EnumSet.copyOf(emptyEnumSet); - } - set.addAll(Arrays.asList(targets)); - return set; - }); - - return this; - } - - @SafeVarargs - public final ActionStateHolder restoreCooperation(final Handler h, RelationType mode, final Handler... targets) { - final Set orig = h.adjustments().get(mode); - - if (orig != null) { - final Set a = EnumSet.copyOf(orig); - final Set b = cooperationMap.get(h).get(mode); - a.retainAll(Arrays.asList(targets)); - b.addAll(a); - } else { - cooperationMap.get(h).remove(mode); - } - - return this; - } - - @SafeVarargs - public final ActionStateHolder unmapAdjustment(final Handler h, RelationType type, final Handler... targets) { - final Set set = adjustmentMap.get(h).get(type); - if (set == null || set.isEmpty()) { - return this; - } - - if (targets.length == 0) { - set.clear(); - } else { - set.removeAll(Arrays.asList(targets)); - } - - return this; - } - - @SafeVarargs - public final ActionStateHolder mapAdjustment(final Handler h, RelationType mode, final Handler... targets) { - adjustmentMap.get(h).compute(mode, (m, set) -> { - if (set == null) { - set = EnumSet.copyOf(emptyEnumSet); - } - set.addAll(Arrays.asList(targets)); - return set; - }); - - return this; - } - - @SafeVarargs - public final ActionStateHolder restoreAdjustment(final Handler h, RelationType mode, final Handler... targets) { - final Set orig = h.adjustments().get(mode); - - if (orig != null) { - final Set a = EnumSet.copyOf(orig); - final Set b = adjustmentMap.get(h).get(mode); - a.retainAll(Arrays.asList(targets)); - b.addAll(a); - } else { - adjustmentMap.get(h).remove(mode); - } - - return this; - } - - public ActionStateHolder enableAction(final Handler h, ActionMode mode) { - enabledActions.get(h).add(mode); - - return this; - } - - public ActionStateHolder disableAction(final Handler h, ActionMode mode) { - enabledActions.get(h).remove(mode); - - return this; - } - - public ActionStateHolder unmapAction(final Handler h, ActionMode mode) { - actionsMap.get(h).remove(mode); - - return this; - } - - public ActionStateHolder mapAction(final Handler h, ActionMode mode, EventAction remap) { - actionsMap.get(h).put(mode, remap); - - return this; - } - - public ActionStateHolder remapAction(final Handler h, ActionMode mode, EventAction remap) { - actionsMap.get(h).replace(mode, remap); - - return this; - } - - public ActionStateHolder restoreAction(final Handler h, ActionMode mode) { - final EventAction a = h.allActions().get(mode); - - if (a != null) { - actionsMap.get(h).put(mode, a); - } else { - actionsMap.get(h).remove(mode); - } - - return this; - } - } - - final class Relation & EventBase> { - - public final Handler sourceHandler; - public final Handler targetHandler; - - public Relation(Handler sourceHandler, Handler targetHandler) { - this.sourceHandler = sourceHandler; - this.targetHandler = targetHandler; - } - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package awt; + +import g.Signals; +import java.awt.AWTEvent; +import java.util.Arrays; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.IntSupplier; + +/** + * The base for construction of Event handling dictionaries + * EventHandler is a reference implementation of this base + * + * Note the type safety with generics.It could be a complex task, but you can avoid + unchecked casts and warnings suppression. Whoa... Make my head swirl around! + - Good Sign 2017/04/24 + * + * @author Good Sign + * @param the type of handler + */ +public interface EventBase & EventBase> extends IntSupplier { + + static final Comparator EVENT_SORT = Comparator.comparingInt(IntSupplier::getAsInt); + + static & EventBase> H[] sortHandlers(H[] values) { + Arrays.sort(values, EVENT_SORT); + return values; + } + + /** + * Find event by id + * @param type of event + * @param values event values + * @param eventId the id + * @return the event (can be null) + */ + static & EventBase> H findById(H[] values, int eventId) { + final int index = Arrays.binarySearch(values, (IntSupplier) () -> eventId, EVENT_SORT); + if (index < 0) { + return null; + } + return values[index]; + } + + @SafeVarargs + static & EventBase> Relation[] Relate(H src, H... dests) { + @SuppressWarnings("unchecked") + final IntFunction[]> arrayer = Relation[]::new; + return Arrays.stream(dests) + .map(dest -> new Relation<>(src, dest)) + .toArray(arrayer); + } + + Set defaultEnabledActions(); + + Map> allActions(); + + Map> cooperations(); + + Map> adjustments(); + + default boolean hasActions(final ActionMode... modes) { + final Set actions = defaultEnabledActions(); + if (actions.isEmpty()) { + return false; + } + + for (final ActionMode m : modes) { + if (!actions.contains(m)) { + return false; + } + } + + return true; + } + + enum KeyStateSatisfaction { + SATISFIED_ATE, + GENEOROUS_PASS, + WANTS_MORE_ATE, + WANTS_MORE_PASS + } + + enum ActionMode { + PERFORM, DEPEND, CAUSE, REVERT; + } + + enum RelationAffection { + ENABLES, DISABLES, COOPERATES; + } + + enum RelationType { + ENABLE(RelationAffection.ENABLES, ActionMode.PERFORM), + ENABLE_DEPEND(RelationAffection.ENABLES, ActionMode.DEPEND), + ENABLE_CAUSE(RelationAffection.ENABLES, ActionMode.CAUSE), + ENABLE_REVERT(RelationAffection.ENABLES, ActionMode.REVERT), + DISABLE(RelationAffection.DISABLES, ActionMode.PERFORM), + DISABLE_DEPEND(RelationAffection.DISABLES, ActionMode.DEPEND), + DISABLE_CAUSE(RelationAffection.DISABLES, ActionMode.CAUSE), + DISABLE_REVERT(RelationAffection.DISABLES, ActionMode.REVERT), + DEPEND(RelationAffection.COOPERATES, ActionMode.DEPEND), + CAUSE(RelationAffection.COOPERATES, ActionMode.CAUSE), + REVERT(RelationAffection.COOPERATES, ActionMode.REVERT); + + final RelationAffection affection; + final ActionMode affectedMode; + + private RelationType(RelationAffection affection, ActionMode affectedMode) { + this.affection = affection; + this.affectedMode = affectedMode; + } + + @Override + public String toString() { + return String.format("%s on [%s]", affection, affectedMode); + } + } + + @FunctionalInterface + interface ActionMapper & EventBase> { + + void map(ActionMode mode, EventAction action); + } + + @FunctionalInterface + interface RelationMapper & EventBase> { + + void map(RelationType type, Relation[] relations); + } + + @FunctionalInterface + interface EventAction & EventBase> { + + void act(EventObserver obs, AWTEvent ev); + } + + interface KeyStateCallback & EventBase> { + + KeyStateSatisfaction call(EventObserver observer); + } + + final class KeyStateInterest & EventBase> { + + private final Set interestSet; + private final KeyStateCallback satisfiedCallback; + + public KeyStateInterest( + final KeyStateCallback satisfiedCallback, + final Signals.ScanCode interestFirstKey, + Signals.ScanCode... interestKeyChain + ) { + this.interestSet = EnumSet.of(interestFirstKey, interestKeyChain); + this.satisfiedCallback = satisfiedCallback; + } + } + + final class KeyStateHolder & EventBase> { + + private final Set holdingSet; + private final LinkedHashSet> keyInterests; + @SuppressWarnings("unchecked") + private final IntFunction[]> generator = KeyStateInterest[]::new; + + public KeyStateHolder() { + this.holdingSet = EnumSet.noneOf(Signals.ScanCode.class); + this.keyInterests = new LinkedHashSet<>(); + } + + public void removeAllKeys() { + holdingSet.clear(); + } + + public boolean contains(Signals.ScanCode sc) { + return holdingSet.contains(sc); + } + + public void addInterest(KeyStateInterest interest) { + this.keyInterests.add(interest); + } + + public void removeInterest(KeyStateInterest interest) { + this.keyInterests.remove(interest); + } + + public boolean matchInterest(final KeyStateInterest check) { + return holdingSet.containsAll(check.interestSet); + } + + public boolean notifyKeyChange(EventObserver observer, Signals.ScanCode code, boolean press) { + if (press) { + holdingSet.add(code); + + final KeyStateInterest[] matched = keyInterests.stream() + .filter(this::matchInterest) + .toArray(this.generator); + + boolean ret = false; + for (int i = 0; i < matched.length; ++i) { + switch (matched[i].satisfiedCallback.call(observer)) { + case SATISFIED_ATE: + ret = true; + case GENEOROUS_PASS: + keyInterests.remove(matched[i]); + break; + case WANTS_MORE_ATE: + ret = true; + case WANTS_MORE_PASS: + break; + } + } + + return ret; + } else { + holdingSet.remove(code); + return false; + } + } + } + + /** + * Enable/disable and remaps of actions is actually reflected here. It is only initial template in the Handler + */ + final class ActionStateHolder & EventBase> { + + private final Map> enabledActions; + private final Map>> actionsMap; + private final Map>> cooperationMap; + private final Map>> adjustmentMap; + private final EventObserver observer; + private final EnumSet emptyEnumSet; + + public boolean hasActionsEnabled(final Handler h, final ActionMode... modes) { + final Set actions = enabledActions.get(h); + if (actions.isEmpty()) { + return false; + } + + for (final ActionMode m : modes) { + if (!actions.contains(m)) { + return false; + } + } + + return true; + } + + public ActionStateHolder(final Class hClass, final EventObserver observer) { + final Handler[] values = hClass.getEnumConstants(); + this.enabledActions = populate(hClass, values, h -> { + final Set set = h.defaultEnabledActions(); + return set.isEmpty() ? EnumSet.noneOf(ActionMode.class) : EnumSet.copyOf(set); + }); + this.actionsMap = populate(hClass, values, h -> { + final Map> map = h.allActions(); + return map.isEmpty() ? new EnumMap<>(ActionMode.class) : new EnumMap<>(map); + }); + this.cooperationMap = populate(hClass, values, h -> deepCopyMap(h.cooperations())); + this.adjustmentMap = populate(hClass, values, h -> deepCopyMap(h.adjustments())); + this.observer = observer; + this.emptyEnumSet = EnumSet.noneOf(hClass); + } + + private Map> deepCopyMap(final Map> map) { + if (map.isEmpty()) { + return new EnumMap<>(RelationType.class); + } + + // shallow copy first + final EnumMap> copy = new EnumMap<>(map); + // now values + copy.replaceAll((r, l) -> EnumSet.copyOf(l)); + return copy; + } + + private Map populate(Class hClass, Handler[] values, Function mapper) { + return Arrays.stream(values).collect( + () -> new EnumMap<>(hClass), + (m, h) -> m.put(h, mapper.apply(h)), + EnumMap::putAll + ); + } + + public ActionStateHolder run(final Handler h, final ActionMode mode, final AWTEvent ev) { + if (enabledActions.get(h).contains(mode)) { + Optional.ofNullable(actionsMap.get(h).get(mode)).ifPresent(action -> action.act(observer, ev)); + } + + return this; + } + + public Map> cooperations(final Handler h) { + return cooperationMap.get(h); + } + + public Map> adjustments(final Handler h) { + return adjustmentMap.get(h); + } + + public Set cooperations(final Handler h, final RelationType type) { + return cooperationMap.get(h).getOrDefault(type, emptyEnumSet); + } + + public Set adjustments(final Handler h, final RelationType type) { + return adjustmentMap.get(h).getOrDefault(type, emptyEnumSet); + } + + @SafeVarargs + public final ActionStateHolder unmapCooperation(final Handler h, RelationType type, final Handler... targets) { + final Set set = cooperationMap.get(h).get(type); + if (set == null || set.isEmpty()) { + return this; + } + + if (targets.length == 0) { + set.clear(); + } else { + set.removeAll(Arrays.asList(targets)); + } + + return this; + } + + @SafeVarargs + public final ActionStateHolder mapCooperation(final Handler h, RelationType mode, final Handler... targets) { + cooperationMap.get(h).compute(mode, (m, set) -> { + if (set == null) { + set = EnumSet.copyOf(emptyEnumSet); + } + set.addAll(Arrays.asList(targets)); + return set; + }); + + return this; + } + + @SafeVarargs + public final ActionStateHolder restoreCooperation(final Handler h, RelationType mode, final Handler... targets) { + final Set orig = h.adjustments().get(mode); + + if (orig != null) { + final Set a = EnumSet.copyOf(orig); + final Set b = cooperationMap.get(h).get(mode); + a.retainAll(Arrays.asList(targets)); + b.addAll(a); + } else { + cooperationMap.get(h).remove(mode); + } + + return this; + } + + @SafeVarargs + public final ActionStateHolder unmapAdjustment(final Handler h, RelationType type, final Handler... targets) { + final Set set = adjustmentMap.get(h).get(type); + if (set == null || set.isEmpty()) { + return this; + } + + if (targets.length == 0) { + set.clear(); + } else { + set.removeAll(Arrays.asList(targets)); + } + + return this; + } + + @SafeVarargs + public final ActionStateHolder mapAdjustment(final Handler h, RelationType mode, final Handler... targets) { + adjustmentMap.get(h).compute(mode, (m, set) -> { + if (set == null) { + set = EnumSet.copyOf(emptyEnumSet); + } + set.addAll(Arrays.asList(targets)); + return set; + }); + + return this; + } + + @SafeVarargs + public final ActionStateHolder restoreAdjustment(final Handler h, RelationType mode, final Handler... targets) { + final Set orig = h.adjustments().get(mode); + + if (orig != null) { + final Set a = EnumSet.copyOf(orig); + final Set b = adjustmentMap.get(h).get(mode); + a.retainAll(Arrays.asList(targets)); + b.addAll(a); + } else { + adjustmentMap.get(h).remove(mode); + } + + return this; + } + + public ActionStateHolder enableAction(final Handler h, ActionMode mode) { + enabledActions.get(h).add(mode); + + return this; + } + + public ActionStateHolder disableAction(final Handler h, ActionMode mode) { + enabledActions.get(h).remove(mode); + + return this; + } + + public ActionStateHolder unmapAction(final Handler h, ActionMode mode) { + actionsMap.get(h).remove(mode); + + return this; + } + + public ActionStateHolder mapAction(final Handler h, ActionMode mode, EventAction remap) { + actionsMap.get(h).put(mode, remap); + + return this; + } + + public ActionStateHolder remapAction(final Handler h, ActionMode mode, EventAction remap) { + actionsMap.get(h).replace(mode, remap); + + return this; + } + + public ActionStateHolder restoreAction(final Handler h, ActionMode mode) { + final EventAction a = h.allActions().get(mode); + + if (a != null) { + actionsMap.get(h).put(mode, a); + } else { + actionsMap.get(h).remove(mode); + } + + return this; + } + } + + final class Relation & EventBase> { + + public final Handler sourceHandler; + public final Handler targetHandler; + + public Relation(Handler sourceHandler, Handler targetHandler) { + this.sourceHandler = sourceHandler; + this.targetHandler = targetHandler; + } + } +} \ No newline at end of file diff --git a/src/awt/EventHandler.java b/src/awt/EventHandler.java index 7a3fa3e..6bbc3c6 100644 --- a/src/awt/EventHandler.java +++ b/src/awt/EventHandler.java @@ -1,390 +1,390 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package awt; - -import static awt.EventBase.Relate; -import g.Signals; -import static g.Signals.ScanCode.SC_PRTSCRN; -import java.awt.Point; -import java.awt.event.ComponentEvent; -import java.awt.event.KeyEvent; -import java.awt.event.MouseEvent; -import java.awt.event.WindowEvent; -import java.util.Arrays; -import java.util.Collections; -import java.util.EnumMap; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.function.Consumer; -import java.util.stream.Stream; - -/** - * This class is catching events thrown at him by someone, and sends them to underlying DOOM engine. - * As a way to preserve vanilla until full understand the code, everything inside of underlying engine - * is still considered black box and changes to it are minimal. - * But I've tried to make high-level API on top level effective. - * - * For example, we do not need to create some MochaDoomInputEvent for unique combination of AWTEvent and - * its type - it can be easily switched by ID value from AWTEvent's ow information method. Also, we can - * certainly know which ScanCodes we will get and what are their minimal and max values, because - * of using Enum for them (my favorite type of data structure, huh!) - * And if we know that ScanCodes can only be something very limited, and every KeyEvent (an AWTEvent for keys) - * will be translated into one of them, we only have to pre-create two copies of DOOM's event structure - * for each entry of ScanCode Enum: one for press state, one for release. - * - * Note: SysRq / Print Screen key only sends release state, so it have to send press to underlying engine - * on release or it will be ignored. - * - Good Sign 2017/04/21 - * - * The secondary purpose of this class is to automatically handle relations between different - * event handlers. Everything like "when window is not in focus, don't process keys". - * - Good Sign 2017/04/22 - * - * New way of on-event actions and definitions: - * Enums and lambdas are magic combination. Here you cave a static pre-defined constant pool of events and - * abstract reactions on them that can work with many copies of Observer if you want, and with different ways - * of listening for events. - * - * How to use: - * define enum constant with arbitrary name (i.e. JOYSTICK_SOEMTHING) - * then write its arguments: - * first argument is AWTEvent id it will react to, - * second argument is lambda accepting mapper. map function on mapper maps ActionMode to EventAction - * ActionMode.PERFORM it what you want to do when the event occured. - * Function or lambda you map through mapper.map method accepts Observer object and AWTEvent - * (you can cast your AWTEvent assuming the proper id will correspond to the proper AWTEvent) - * ActionMode.REVERT is what you want to do when some event negates effect of this event - * (i.e. when the user switched to another application, clear joystick pressed button states) - * - Good Sign 2017/04/24 - * - * @author Good Sign - */ -public enum EventHandler implements EventBase { - KEY_PRESS(KeyEvent.KEY_PRESSED, mapper -> { - mapper.map(ActionMode.REVERT, EventObserver::cancelKeys); - mapper.map(ActionMode.PERFORM, EventObserver::sendKeyDowns); - mapper.map(ActionMode.DEPEND, (observer, event) -> { - // Add keyDown for Print Screen because he doesn't send one - if (Signals.getScanCode((KeyEvent) event) == SC_PRTSCRN) { - observer.feed(SC_PRTSCRN.doomEventDown); - } - }); - }, ActionMode.REVERT, ActionMode.PERFORM, ActionMode.DEPEND), - KEY_RELEASE(KeyEvent.KEY_RELEASED, mapper -> { - mapper.map(ActionMode.PERFORM, EventObserver::sendKeyUps); - /*mapper.map(ActionMode.DEPEND_BEFORE, (observer, event) -> { - // release alt key on fullscreen switch - if (Signals.getScanCode((KeyEvent) event) == SC_ENTER) { - observer.consume(SC_LALT.doomEventUp); - } - });*/ - }, ActionMode.PERFORM/*, ActionMode.DEPEND*/), - KEY_TYPE(KeyEvent.KEY_TYPED, mapper -> { - mapper.map(ActionMode.PERFORM, EventObserver::sendKeyUps); - }, ActionMode.PERFORM), - MOUSE_PRESS(MouseEvent.MOUSE_PRESSED, mapper -> { - mapper.map(ActionMode.REVERT, EventObserver::cancelMouse); - mapper.map(ActionMode.PERFORM, (observer, ev) -> { - observer.mouseEvent.buttonOn((MouseEvent) ev); - observer.mouseEvent.x = observer.mouseEvent.y = 0; - if (observer.mouseEvent.processed) { - observer.mouseEvent.resetNotify(); - observer.feed(observer.mouseEvent); - } - }); - }, ActionMode.REVERT, ActionMode.PERFORM), - MOUSE_RELEASE(MouseEvent.MOUSE_RELEASED, mapper -> { - mapper.map(ActionMode.PERFORM, (observer, ev) -> { - observer.mouseEvent.buttonOff((MouseEvent) ev); - observer.mouseEvent.x = observer.mouseEvent.y = 0; - if (observer.mouseEvent.processed) { - observer.mouseEvent.resetNotify(); - observer.feed(observer.mouseEvent); - } - }); - }, ActionMode.PERFORM), - MOUSE_CLICK(MouseEvent.MOUSE_CLICKED, mapper -> { - // Set input method and mouse cursor, move cursor to the centre - mapper.map(ActionMode.PERFORM, EventObserver::centreCursor); - }), - MOUSE_MOVE(MouseEvent.MOUSE_MOVED, mapper -> { - mapper.map(ActionMode.PERFORM, mouseMoveAction(false)); - }, ActionMode.PERFORM), - MOUSE_DRAG(MouseEvent.MOUSE_DRAGGED, mapper -> { - mapper.map(ActionMode.PERFORM, mouseMoveAction(true)); - }, ActionMode.PERFORM), - WINDOW_ACTIVATE(WindowEvent.WINDOW_ACTIVATED, ActionMode.PERFORM, ActionMode.CAUSE), - WINDOW_DEICONIFY(WindowEvent.WINDOW_DEICONIFIED, ActionMode.PERFORM), - COMPONENT_RESIZE(ComponentEvent.COMPONENT_RESIZED, ActionMode.PERFORM), - MOUSE_ENTER(MouseEvent.MOUSE_ENTERED, mapper -> { - // Set input method and mouse cursor, move cursor to the centre - mapper.map(ActionMode.PERFORM, EventObserver::centreCursor); - }), - WINDOW_OPEN(WindowEvent.WINDOW_OPENED, mapper -> { - // Set input method and mouse cursor - mapper.map(ActionMode.PERFORM, EventObserver::centreCursor); - }, ActionMode.PERFORM), - WINDOW_GAIN_FOCUS(WindowEvent.WINDOW_GAINED_FOCUS, mapper -> { - mapper.map(ActionMode.PERFORM, EventObserver::modifyCursor); - }), - WINDOW_LOSE_FOCUS(WindowEvent.WINDOW_LOST_FOCUS, mapper -> { - mapper.map(ActionMode.PERFORM, EventObserver::restoreCursor); - }, ActionMode.PERFORM), - COMPONENT_MOVE(ComponentEvent.COMPONENT_MOVED, ActionMode.PERFORM), - MOUSE_EXIT(MouseEvent.MOUSE_EXITED, ActionMode.PERFORM), - /** - * We need to take charge of various scenarios such as what to do with mouse and keys when the - * window lose focus, how to enter/return from alt-tab/full-screen switch, how to behave - * when the use crucially drag our window over all the desktop... - */ - RELATIONS(relationMapper -> { - // Add keyDown for Print Screen because he doesn't send one - relationMapper.map(RelationType.DEPEND, Relate(KEY_RELEASE, KEY_PRESS)); - - /** - * After the window is opened, it must disable its own event, but capture all the keyboard and mouse input - */ - relationMapper.map(RelationType.DISABLE, Relate(WINDOW_OPEN, WINDOW_OPEN)); - relationMapper.map(RelationType.ENABLE, Relate(WINDOW_OPEN, - WINDOW_LOSE_FOCUS, KEY_PRESS, KEY_RELEASE, KEY_TYPE, MOUSE_ENTER, MOUSE_MOVE, MOUSE_DRAG, MOUSE_PRESS, MOUSE_RELEASE - )); - - /** - * On any activation/reconfiguration/resize/restore-from-something, request focus in window - */ - relationMapper.map(RelationType.CAUSE, Relate(WINDOW_ACTIVATE, WINDOW_ACTIVATE)); - relationMapper.map(RelationType.CAUSE, Relate(WINDOW_DEICONIFY, WINDOW_ACTIVATE)); - relationMapper.map(RelationType.CAUSE, Relate(COMPONENT_RESIZE, WINDOW_ACTIVATE)); - - /** - * This set of rules are for ultimately releasing any capture on mouse and keyboard, - * and also releases all pressed keys and mouse buttons. - * - * Disables itself too, but enables event on the focus return. - */ - relationMapper.map(RelationType.REVERT, Relate(WINDOW_LOSE_FOCUS, KEY_PRESS, MOUSE_PRESS)); - relationMapper.map(RelationType.DISABLE, Relate(WINDOW_LOSE_FOCUS, WINDOW_LOSE_FOCUS, - KEY_PRESS, KEY_RELEASE, KEY_TYPE, MOUSE_MOVE, MOUSE_DRAG, MOUSE_PRESS, MOUSE_RELEASE, MOUSE_ENTER - )); - - /** - * The next set of rules is for active focus gain. It could be done in two ways: - * natural, when window become visible topmost window with active borders, - * and when you click with mouse into the unfocused window. - * - * For clicky way, it must cause window focus and immediate capture of the mouse. - * Enables back losing focus, disables itself and natural focus gain. - */ - relationMapper.map(RelationType.ENABLE, Relate(WINDOW_LOSE_FOCUS, - WINDOW_GAIN_FOCUS, MOUSE_CLICK - )); - relationMapper.map(RelationType.ENABLE, Relate(MOUSE_CLICK, - WINDOW_LOSE_FOCUS, KEY_PRESS, KEY_RELEASE, KEY_TYPE, MOUSE_ENTER, MOUSE_MOVE, MOUSE_DRAG, MOUSE_PRESS, MOUSE_RELEASE - )); - relationMapper.map(RelationType.DISABLE, Relate(MOUSE_CLICK, WINDOW_GAIN_FOCUS, MOUSE_CLICK)); - - /** - * For natural way, focus gain *must not* capture the mouse immediately, only after it enters the window. - * Enables back losing focus, disables itself and clicky way of capture. - */ - relationMapper.map(RelationType.ENABLE, Relate(WINDOW_GAIN_FOCUS, - WINDOW_LOSE_FOCUS, KEY_PRESS, KEY_RELEASE, KEY_TYPE, MOUSE_ENTER - )); - relationMapper.map(RelationType.DISABLE, Relate(WINDOW_GAIN_FOCUS, WINDOW_GAIN_FOCUS)); - - /** - * When the mouse returns to the window, it should be captured back, and the event disabled. - */ - relationMapper.map(RelationType.ENABLE, Relate(MOUSE_ENTER, MOUSE_MOVE, MOUSE_DRAG, MOUSE_PRESS, MOUSE_RELEASE)); - relationMapper.map(RelationType.DISABLE, Relate(MOUSE_ENTER, MOUSE_ENTER)); - - /** - * The last scenario is component move. Example of it - user drags the window by its head. - * This way, first window is activated and gained focus, than component moved. Normally, the mouse would - * go into the window position and MOUSE_ENTER will be processed. We do not need it. If the user drags window, - * he then should manually click inside it to regain mouse capture - or alt-tab twice (regain window focus) - */ - relationMapper.map(RelationType.DISABLE, Relate(COMPONENT_MOVE, - MOUSE_MOVE, MOUSE_DRAG, MOUSE_PRESS, MOUSE_RELEASE, MOUSE_ENTER - )); - relationMapper.map(RelationType.ENABLE, Relate(COMPONENT_MOVE, MOUSE_CLICK)); - }); - - public static void menuCaptureChanges(EventObserver observer, boolean capture) { - if (capture) { - observer.enableAction(MOUSE_MOVE, ActionMode.PERFORM); - observer.enableAction(MOUSE_DRAG, ActionMode.PERFORM); - observer.enableAction(MOUSE_PRESS, ActionMode.PERFORM); - observer.enableAction(MOUSE_RELEASE, ActionMode.PERFORM); - observer.enableAction(MOUSE_ENTER, ActionMode.PERFORM); - observer.disableAction(MOUSE_CLICK, ActionMode.PERFORM); - observer.centreCursor(null); - } else { - observer.disableAction(MOUSE_MOVE, ActionMode.PERFORM); - observer.disableAction(MOUSE_DRAG, ActionMode.PERFORM); - observer.disableAction(MOUSE_PRESS, ActionMode.PERFORM); - observer.disableAction(MOUSE_RELEASE, ActionMode.PERFORM); - observer.disableAction(MOUSE_ENTER, ActionMode.PERFORM); - observer.enableAction(MOUSE_CLICK, ActionMode.PERFORM); - observer.restoreCursor(null); - } - } - - public static void fullscreenChanges(EventObserver observer, boolean fullscreen) { - /** - * Clear any holding keys - */ - observer.cancelKeys(null); - if (fullscreen) { - /** - * When in full-screen mode, COMPONENT_RESIZE is fired when you get the game visible - * (immediately after switch, or after return from alt-tab) - */ - observer.mapRelation(COMPONENT_RESIZE, RelationType.ENABLE, WINDOW_OPEN, - WINDOW_LOSE_FOCUS, KEY_PRESS, KEY_RELEASE, KEY_TYPE, MOUSE_ENTER, MOUSE_MOVE, MOUSE_DRAG, MOUSE_PRESS, MOUSE_RELEASE - ); - - /** - * COMPONENT_MOVE is fired often in full-screen mode and does not mean that used did - * something with the window frame, actually there is no frame, there is no sense - disable it - */ - observer.disableAction(COMPONENT_MOVE, ActionMode.PERFORM); - } else { - /** - * Remove full-screen COMPONENT_RESIZE relations, if they was added earlier - */ - observer.unmapRelation(COMPONENT_RESIZE, RelationType.ENABLE); - - /** - * Immediately after return from full-screen mode, a bunch of events will occur, - * some of them will cause mouse capture to be lost. Disable them. - */ - observer.disableAction(WINDOW_LOSE_FOCUS, ActionMode.PERFORM); - observer.disableAction(COMPONENT_MOVE, ActionMode.PERFORM); - - /** - * The last of the bunch of events should be WINDOW_ACTIVATE, add a function to him - * to restore the proper reaction on events we have switched off. It also should remove - * this function after it fired. - */ - observer.mapAction(WINDOW_ACTIVATE, ActionMode.PERFORM, (ob, ev) -> { - observer.unmapAction(WINDOW_ACTIVATE, ActionMode.PERFORM); - observer.enableAction(WINDOW_LOSE_FOCUS, ActionMode.PERFORM); - observer.enableAction(COMPONENT_MOVE, ActionMode.PERFORM); - }); - } - } - - private static EventAction mouseMoveAction(boolean isDrag) { - return (observer, ev) -> { - // Do not send robot-generated moves (centering) to the underlying DOOM's event engine - if (observer.mouseEvent.robotMove) { - observer.mouseEvent.robotMove = false; - return; - } - - final int centreX = observer.component.getWidth() >> 1, centreY = observer.component.getHeight() >> 1; - if (observer.component.isShowing() && EventObserver.MOUSE_ROBOT.isPresent()) { - final Point offset = observer.component.getLocationOnScreen(); - observer.mouseEvent.moveIn((MouseEvent) ev, EventObserver.MOUSE_ROBOT.get(), offset, centreX, centreY, isDrag); - } else { - observer.mouseEvent.moveIn((MouseEvent) ev, centreX, centreY, isDrag); - } - - if (observer.mouseEvent.processed) { - observer.mouseEvent.resetNotify(); - observer.feed(observer.mouseEvent); - } - }; - } - - final int eventId; - final Set enabled; - final Map> actions; - final Map> adjustments; - final Map> cooperations; - - private EventHandler(final Consumer> relationMapper) { - this.eventId = -1; - this.actions = Collections.emptyMap(); - this.adjustments = Collections.emptyMap(); - this.cooperations = Collections.emptyMap(); - this.enabled = Collections.emptySet(); - relationMapper.accept((type, relations) - -> Stream.of(relations).forEach(relation - -> (type.affection == RelationAffection.COOPERATES - ? relation.sourceHandler.cooperations - : relation.sourceHandler.adjustments).compute(type, (t, set) -> { - (set == null ? (set = new HashSet<>()) : set).add(relation.targetHandler); - return set; - }) - ) - ); - } - - private EventHandler(final int eventId, final ActionMode... enableModes) { - this(eventId, null, enableModes); - } - - private EventHandler(final int eventId, final Consumer> actionMapper, final ActionMode... enableModes) { - this.eventId = eventId; - this.actions = new EnumMap<>(ActionMode.class); - this.enabled = EnumSet.noneOf(ActionMode.class); - this.enabled.addAll(Arrays.asList(enableModes)); - - this.adjustments = new EnumMap<>(RelationType.class); - this.cooperations = new EnumMap<>(RelationType.class); - - if (actionMapper != null) { - actionMapper.accept(actions::put); - } - } - - /** - * Interface implementation - */ - @Override - public Set defaultEnabledActions() { - return enabled; - } - - @Override - public Map> allActions() { - return actions; - } - - @Override - public Map> cooperations() { - return cooperations; - } - - @Override - public Map> adjustments() { - return adjustments; - } - - /** - * A hack to make this Enum implementation sortable by primitive integers in another way then by ordinal() - * The hack consists of implementing IntSupplier interface, this method and EVENT_SORT Comparator constant - */ - @Override - public int getAsInt() { - return eventId; - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package awt; + +import static awt.EventBase.Relate; +import g.Signals; +import static g.Signals.ScanCode.SC_PRTSCRN; +import java.awt.Point; +import java.awt.event.ComponentEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.WindowEvent; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Stream; + +/** + * This class is catching events thrown at him by someone, and sends them to underlying DOOM engine. + * As a way to preserve vanilla until full understand the code, everything inside of underlying engine + * is still considered black box and changes to it are minimal. + * But I've tried to make high-level API on top level effective. + * + * For example, we do not need to create some MochaDoomInputEvent for unique combination of AWTEvent and + * its type - it can be easily switched by ID value from AWTEvent's ow information method. Also, we can + * certainly know which ScanCodes we will get and what are their minimal and max values, because + * of using Enum for them (my favorite type of data structure, huh!) + * And if we know that ScanCodes can only be something very limited, and every KeyEvent (an AWTEvent for keys) + * will be translated into one of them, we only have to pre-create two copies of DOOM's event structure + * for each entry of ScanCode Enum: one for press state, one for release. + * + * Note: SysRq / Print Screen key only sends release state, so it have to send press to underlying engine + * on release or it will be ignored. + * - Good Sign 2017/04/21 + * + * The secondary purpose of this class is to automatically handle relations between different + * event handlers. Everything like "when window is not in focus, don't process keys". + * - Good Sign 2017/04/22 + * + * New way of on-event actions and definitions: + * Enums and lambdas are magic combination. Here you cave a static pre-defined constant pool of events and + * abstract reactions on them that can work with many copies of Observer if you want, and with different ways + * of listening for events. + * + * How to use: + * define enum constant with arbitrary name (i.e. JOYSTICK_SOEMTHING) + * then write its arguments: + * first argument is AWTEvent id it will react to, + * second argument is lambda accepting mapper. map function on mapper maps ActionMode to EventAction + * ActionMode.PERFORM it what you want to do when the event occured. + * Function or lambda you map through mapper.map method accepts Observer object and AWTEvent + * (you can cast your AWTEvent assuming the proper id will correspond to the proper AWTEvent) + * ActionMode.REVERT is what you want to do when some event negates effect of this event + * (i.e. when the user switched to another application, clear joystick pressed button states) + * - Good Sign 2017/04/24 + * + * @author Good Sign + */ +public enum EventHandler implements EventBase { + KEY_PRESS(KeyEvent.KEY_PRESSED, mapper -> { + mapper.map(ActionMode.REVERT, EventObserver::cancelKeys); + mapper.map(ActionMode.PERFORM, EventObserver::sendKeyDowns); + mapper.map(ActionMode.DEPEND, (observer, event) -> { + // Add keyDown for Print Screen because he doesn't send one + if (Signals.getScanCode((KeyEvent) event) == SC_PRTSCRN) { + observer.feed(SC_PRTSCRN.doomEventDown); + } + }); + }, ActionMode.REVERT, ActionMode.PERFORM, ActionMode.DEPEND), + KEY_RELEASE(KeyEvent.KEY_RELEASED, mapper -> { + mapper.map(ActionMode.PERFORM, EventObserver::sendKeyUps); + /*mapper.map(ActionMode.DEPEND_BEFORE, (observer, event) -> { + // release alt key on fullscreen switch + if (Signals.getScanCode((KeyEvent) event) == SC_ENTER) { + observer.consume(SC_LALT.doomEventUp); + } + });*/ + }, ActionMode.PERFORM/*, ActionMode.DEPEND*/), + KEY_TYPE(KeyEvent.KEY_TYPED, mapper -> { + mapper.map(ActionMode.PERFORM, EventObserver::sendKeyUps); + }, ActionMode.PERFORM), + MOUSE_PRESS(MouseEvent.MOUSE_PRESSED, mapper -> { + mapper.map(ActionMode.REVERT, EventObserver::cancelMouse); + mapper.map(ActionMode.PERFORM, (observer, ev) -> { + observer.mouseEvent.buttonOn((MouseEvent) ev); + observer.mouseEvent.x = observer.mouseEvent.y = 0; + if (observer.mouseEvent.processed) { + observer.mouseEvent.resetNotify(); + observer.feed(observer.mouseEvent); + } + }); + }, ActionMode.REVERT, ActionMode.PERFORM), + MOUSE_RELEASE(MouseEvent.MOUSE_RELEASED, mapper -> { + mapper.map(ActionMode.PERFORM, (observer, ev) -> { + observer.mouseEvent.buttonOff((MouseEvent) ev); + observer.mouseEvent.x = observer.mouseEvent.y = 0; + if (observer.mouseEvent.processed) { + observer.mouseEvent.resetNotify(); + observer.feed(observer.mouseEvent); + } + }); + }, ActionMode.PERFORM), + MOUSE_CLICK(MouseEvent.MOUSE_CLICKED, mapper -> { + // Set input method and mouse cursor, move cursor to the centre + mapper.map(ActionMode.PERFORM, EventObserver::centreCursor); + }), + MOUSE_MOVE(MouseEvent.MOUSE_MOVED, mapper -> { + mapper.map(ActionMode.PERFORM, mouseMoveAction(false)); + }, ActionMode.PERFORM), + MOUSE_DRAG(MouseEvent.MOUSE_DRAGGED, mapper -> { + mapper.map(ActionMode.PERFORM, mouseMoveAction(true)); + }, ActionMode.PERFORM), + WINDOW_ACTIVATE(WindowEvent.WINDOW_ACTIVATED, ActionMode.PERFORM, ActionMode.CAUSE), + WINDOW_DEICONIFY(WindowEvent.WINDOW_DEICONIFIED, ActionMode.PERFORM), + COMPONENT_RESIZE(ComponentEvent.COMPONENT_RESIZED, ActionMode.PERFORM), + MOUSE_ENTER(MouseEvent.MOUSE_ENTERED, mapper -> { + // Set input method and mouse cursor, move cursor to the centre + mapper.map(ActionMode.PERFORM, EventObserver::centreCursor); + }), + WINDOW_OPEN(WindowEvent.WINDOW_OPENED, mapper -> { + // Set input method and mouse cursor + mapper.map(ActionMode.PERFORM, EventObserver::centreCursor); + }, ActionMode.PERFORM), + WINDOW_GAIN_FOCUS(WindowEvent.WINDOW_GAINED_FOCUS, mapper -> { + mapper.map(ActionMode.PERFORM, EventObserver::modifyCursor); + }), + WINDOW_LOSE_FOCUS(WindowEvent.WINDOW_LOST_FOCUS, mapper -> { + mapper.map(ActionMode.PERFORM, EventObserver::restoreCursor); + }, ActionMode.PERFORM), + COMPONENT_MOVE(ComponentEvent.COMPONENT_MOVED, ActionMode.PERFORM), + MOUSE_EXIT(MouseEvent.MOUSE_EXITED, ActionMode.PERFORM), + /** + * We need to take charge of various scenarios such as what to do with mouse and keys when the + * window lose focus, how to enter/return from alt-tab/full-screen switch, how to behave + * when the use crucially drag our window over all the desktop... + */ + RELATIONS(relationMapper -> { + // Add keyDown for Print Screen because he doesn't send one + relationMapper.map(RelationType.DEPEND, Relate(KEY_RELEASE, KEY_PRESS)); + + /** + * After the window is opened, it must disable its own event, but capture all the keyboard and mouse input + */ + relationMapper.map(RelationType.DISABLE, Relate(WINDOW_OPEN, WINDOW_OPEN)); + relationMapper.map(RelationType.ENABLE, Relate(WINDOW_OPEN, + WINDOW_LOSE_FOCUS, KEY_PRESS, KEY_RELEASE, KEY_TYPE, MOUSE_ENTER, MOUSE_MOVE, MOUSE_DRAG, MOUSE_PRESS, MOUSE_RELEASE + )); + + /** + * On any activation/reconfiguration/resize/restore-from-something, request focus in window + */ + relationMapper.map(RelationType.CAUSE, Relate(WINDOW_ACTIVATE, WINDOW_ACTIVATE)); + relationMapper.map(RelationType.CAUSE, Relate(WINDOW_DEICONIFY, WINDOW_ACTIVATE)); + relationMapper.map(RelationType.CAUSE, Relate(COMPONENT_RESIZE, WINDOW_ACTIVATE)); + + /** + * This set of rules are for ultimately releasing any capture on mouse and keyboard, + * and also releases all pressed keys and mouse buttons. + * + * Disables itself too, but enables event on the focus return. + */ + relationMapper.map(RelationType.REVERT, Relate(WINDOW_LOSE_FOCUS, KEY_PRESS, MOUSE_PRESS)); + relationMapper.map(RelationType.DISABLE, Relate(WINDOW_LOSE_FOCUS, WINDOW_LOSE_FOCUS, + KEY_PRESS, KEY_RELEASE, KEY_TYPE, MOUSE_MOVE, MOUSE_DRAG, MOUSE_PRESS, MOUSE_RELEASE, MOUSE_ENTER + )); + + /** + * The next set of rules is for active focus gain. It could be done in two ways: + * natural, when window become visible topmost window with active borders, + * and when you click with mouse into the unfocused window. + * + * For clicky way, it must cause window focus and immediate capture of the mouse. + * Enables back losing focus, disables itself and natural focus gain. + */ + relationMapper.map(RelationType.ENABLE, Relate(WINDOW_LOSE_FOCUS, + WINDOW_GAIN_FOCUS, MOUSE_CLICK + )); + relationMapper.map(RelationType.ENABLE, Relate(MOUSE_CLICK, + WINDOW_LOSE_FOCUS, KEY_PRESS, KEY_RELEASE, KEY_TYPE, MOUSE_ENTER, MOUSE_MOVE, MOUSE_DRAG, MOUSE_PRESS, MOUSE_RELEASE + )); + relationMapper.map(RelationType.DISABLE, Relate(MOUSE_CLICK, WINDOW_GAIN_FOCUS, MOUSE_CLICK)); + + /** + * For natural way, focus gain *must not* capture the mouse immediately, only after it enters the window. + * Enables back losing focus, disables itself and clicky way of capture. + */ + relationMapper.map(RelationType.ENABLE, Relate(WINDOW_GAIN_FOCUS, + WINDOW_LOSE_FOCUS, KEY_PRESS, KEY_RELEASE, KEY_TYPE, MOUSE_ENTER + )); + relationMapper.map(RelationType.DISABLE, Relate(WINDOW_GAIN_FOCUS, WINDOW_GAIN_FOCUS)); + + /** + * When the mouse returns to the window, it should be captured back, and the event disabled. + */ + relationMapper.map(RelationType.ENABLE, Relate(MOUSE_ENTER, MOUSE_MOVE, MOUSE_DRAG, MOUSE_PRESS, MOUSE_RELEASE)); + relationMapper.map(RelationType.DISABLE, Relate(MOUSE_ENTER, MOUSE_ENTER)); + + /** + * The last scenario is component move. Example of it - user drags the window by its head. + * This way, first window is activated and gained focus, than component moved. Normally, the mouse would + * go into the window position and MOUSE_ENTER will be processed. We do not need it. If the user drags window, + * he then should manually click inside it to regain mouse capture - or alt-tab twice (regain window focus) + */ + relationMapper.map(RelationType.DISABLE, Relate(COMPONENT_MOVE, + MOUSE_MOVE, MOUSE_DRAG, MOUSE_PRESS, MOUSE_RELEASE, MOUSE_ENTER + )); + relationMapper.map(RelationType.ENABLE, Relate(COMPONENT_MOVE, MOUSE_CLICK)); + }); + + public static void menuCaptureChanges(EventObserver observer, boolean capture) { + if (capture) { + observer.enableAction(MOUSE_MOVE, ActionMode.PERFORM); + observer.enableAction(MOUSE_DRAG, ActionMode.PERFORM); + observer.enableAction(MOUSE_PRESS, ActionMode.PERFORM); + observer.enableAction(MOUSE_RELEASE, ActionMode.PERFORM); + observer.enableAction(MOUSE_ENTER, ActionMode.PERFORM); + observer.disableAction(MOUSE_CLICK, ActionMode.PERFORM); + observer.centreCursor(null); + } else { + observer.disableAction(MOUSE_MOVE, ActionMode.PERFORM); + observer.disableAction(MOUSE_DRAG, ActionMode.PERFORM); + observer.disableAction(MOUSE_PRESS, ActionMode.PERFORM); + observer.disableAction(MOUSE_RELEASE, ActionMode.PERFORM); + observer.disableAction(MOUSE_ENTER, ActionMode.PERFORM); + observer.enableAction(MOUSE_CLICK, ActionMode.PERFORM); + observer.restoreCursor(null); + } + } + + public static void fullscreenChanges(EventObserver observer, boolean fullscreen) { + /** + * Clear any holding keys + */ + observer.cancelKeys(null); + if (fullscreen) { + /** + * When in full-screen mode, COMPONENT_RESIZE is fired when you get the game visible + * (immediately after switch, or after return from alt-tab) + */ + observer.mapRelation(COMPONENT_RESIZE, RelationType.ENABLE, WINDOW_OPEN, + WINDOW_LOSE_FOCUS, KEY_PRESS, KEY_RELEASE, KEY_TYPE, MOUSE_ENTER, MOUSE_MOVE, MOUSE_DRAG, MOUSE_PRESS, MOUSE_RELEASE + ); + + /** + * COMPONENT_MOVE is fired often in full-screen mode and does not mean that used did + * something with the window frame, actually there is no frame, there is no sense - disable it + */ + observer.disableAction(COMPONENT_MOVE, ActionMode.PERFORM); + } else { + /** + * Remove full-screen COMPONENT_RESIZE relations, if they was added earlier + */ + observer.unmapRelation(COMPONENT_RESIZE, RelationType.ENABLE); + + /** + * Immediately after return from full-screen mode, a bunch of events will occur, + * some of them will cause mouse capture to be lost. Disable them. + */ + observer.disableAction(WINDOW_LOSE_FOCUS, ActionMode.PERFORM); + observer.disableAction(COMPONENT_MOVE, ActionMode.PERFORM); + + /** + * The last of the bunch of events should be WINDOW_ACTIVATE, add a function to him + * to restore the proper reaction on events we have switched off. It also should remove + * this function after it fired. + */ + observer.mapAction(WINDOW_ACTIVATE, ActionMode.PERFORM, (ob, ev) -> { + observer.unmapAction(WINDOW_ACTIVATE, ActionMode.PERFORM); + observer.enableAction(WINDOW_LOSE_FOCUS, ActionMode.PERFORM); + observer.enableAction(COMPONENT_MOVE, ActionMode.PERFORM); + }); + } + } + + private static EventAction mouseMoveAction(boolean isDrag) { + return (observer, ev) -> { + // Do not send robot-generated moves (centering) to the underlying DOOM's event engine + if (observer.mouseEvent.robotMove) { + observer.mouseEvent.robotMove = false; + return; + } + + final int centreX = observer.component.getWidth() >> 1, centreY = observer.component.getHeight() >> 1; + if (observer.component.isShowing() && EventObserver.MOUSE_ROBOT.isPresent()) { + final Point offset = observer.component.getLocationOnScreen(); + observer.mouseEvent.moveIn((MouseEvent) ev, EventObserver.MOUSE_ROBOT.get(), offset, centreX, centreY, isDrag); + } else { + observer.mouseEvent.moveIn((MouseEvent) ev, centreX, centreY, isDrag); + } + + if (observer.mouseEvent.processed) { + observer.mouseEvent.resetNotify(); + observer.feed(observer.mouseEvent); + } + }; + } + + final int eventId; + final Set enabled; + final Map> actions; + final Map> adjustments; + final Map> cooperations; + + private EventHandler(final Consumer> relationMapper) { + this.eventId = -1; + this.actions = Collections.emptyMap(); + this.adjustments = Collections.emptyMap(); + this.cooperations = Collections.emptyMap(); + this.enabled = Collections.emptySet(); + relationMapper.accept((type, relations) + -> Stream.of(relations).forEach(relation + -> (type.affection == RelationAffection.COOPERATES + ? relation.sourceHandler.cooperations + : relation.sourceHandler.adjustments).compute(type, (t, set) -> { + (set == null ? (set = new HashSet<>()) : set).add(relation.targetHandler); + return set; + }) + ) + ); + } + + private EventHandler(final int eventId, final ActionMode... enableModes) { + this(eventId, null, enableModes); + } + + private EventHandler(final int eventId, final Consumer> actionMapper, final ActionMode... enableModes) { + this.eventId = eventId; + this.actions = new EnumMap<>(ActionMode.class); + this.enabled = EnumSet.noneOf(ActionMode.class); + this.enabled.addAll(Arrays.asList(enableModes)); + + this.adjustments = new EnumMap<>(RelationType.class); + this.cooperations = new EnumMap<>(RelationType.class); + + if (actionMapper != null) { + actionMapper.accept(actions::put); + } + } + + /** + * Interface implementation + */ + @Override + public Set defaultEnabledActions() { + return enabled; + } + + @Override + public Map> allActions() { + return actions; + } + + @Override + public Map> cooperations() { + return cooperations; + } + + @Override + public Map> adjustments() { + return adjustments; + } + + /** + * A hack to make this Enum implementation sortable by primitive integers in another way then by ordinal() + * The hack consists of implementing IntSupplier interface, this method and EVENT_SORT Comparator constant + */ + @Override + public int getAsInt() { + return eventId; + } +} \ No newline at end of file diff --git a/src/awt/EventObserver.java b/src/awt/EventObserver.java index ada439f..80a469a 100644 --- a/src/awt/EventObserver.java +++ b/src/awt/EventObserver.java @@ -50,11 +50,11 @@ of EventHandler Enum. This class uses rules in Handler extends Enum & EventBase * to react on AWTEvent events given to him by some listener (or by fake, don't matter) and feeds them * to someone who needs them (DOOM's internal event handling system) - * + * * Also, you may use any Enum & EventBase dictionary, not just EventHandler. * It may be useful if you design a game with several modes or with several systems, or something, * and you need one part to react in one way, another part in another. - * + * * @author Good Sign * @param the type of the handler */ @@ -376,4 +376,4 @@ protected void restoreAction(final Handler h, ActionMode mode) { LOGGER.log(Level.FINE, () -> String.format("RESTORE ACTION: %s [%s]", h, mode)); } } -} +} \ No newline at end of file diff --git a/src/awt/FullscreenOptions.java b/src/awt/FullscreenOptions.java index 3f564fe..7e6ae51 100644 --- a/src/awt/FullscreenOptions.java +++ b/src/awt/FullscreenOptions.java @@ -1,189 +1,189 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package awt; - -import java.awt.DisplayMode; -import java.awt.Graphics2D; -import java.awt.GraphicsDevice; -import java.awt.Image; -import static java.awt.RenderingHints.KEY_INTERPOLATION; -import static java.awt.RenderingHints.VALUE_INTERPOLATION_BICUBIC; -import static java.awt.RenderingHints.VALUE_INTERPOLATION_BILINEAR; -import static java.awt.RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR; -import java.awt.image.ImageObserver; -import m.Settings; -import mochadoom.Engine; - -/** - * Full-screen switch and scale governor - * - * @author Good Sign - */ -public interface FullscreenOptions { - - enum InterpolationMode { - Nearest, Bilinear, Bicubic; - } - - enum StretchMode { - Centre( - (w, defW, h, defH) -> Math.min(defW, w), - (w, defW, h, defH) -> Math.min(defH, h), - (w, defW, h, defH) -> Math.max(0, (w - defW) / 2), - (w, defW, h, defH) -> Math.max(0, (h - defH) / 2) - ), Stretch( - (w, defW, h, defH) -> w, - (w, defW, h, defH) -> h, - (w, defW, h, defH) -> 0, - (w, defW, h, defH) -> 0 - ), Fit( - (w, defW, h, defH) -> (int) (defW * minScale(w, defW, h, defH)), - (w, defW, h, defH) -> (int) (defH * minScale(w, defW, h, defH)), - (w, defW, h, defH) -> (w - (int) (defW * minScale(w, defW, h, defH))) / 2, - (w, defW, h, defH) -> (h - (int) (defH * minScale(w, defW, h, defH))) / 2 - ), Aspect_4_3( - (w, defW, h, defH) -> Fit.widthFun.fit(w, defW, h, (int) (defH * 1.2f)), - (w, defW, h, defH) -> Fit.heightFun.fit(w, defW, h, (int) (defH * 1.2f)), - (w, defW, h, defH) -> Fit.offsXFun.fit(w, defW, h, (int) (defH * 1.2f)), - (w, defW, h, defH) -> Fit.offsYFun.fit(w, defW, h, (int) (defH * 1.2f)) - ); - - final Fitter widthFun, heightFun, offsXFun, offsYFun; - - private StretchMode( - final Fitter widthFun, - final Fitter heightFun, - final Fitter offsXFun, - final Fitter offsYFun - ) { - this.widthFun = widthFun; - this.heightFun = heightFun; - this.offsXFun = offsXFun; - this.offsYFun = offsYFun; - } - - private static float minScale(int w, int defW, int h, int defH) { - float scaleX = w / (float) defW; - float scaleY = h / (float) defH; - return Math.min(scaleX, scaleY); - } - } - - enum FullMode { - Best, Native; - } - - static FullMode FULLMODE = Engine.getConfig().getValue(Settings.fullscreen_mode, FullMode.class); - static StretchMode STRETCH = Engine.getConfig().getValue(Settings.fullscreen_stretch, StretchMode.class); - static InterpolationMode INTERPOLATION = Engine.getConfig().getValue(Settings.fullscreen_interpolation, InterpolationMode.class); - - interface Dimension { - - int width(); - - int height(); - - int defWidth(); - - int defHeight(); - - default int fitX() { - return STRETCH.widthFun.fit(width(), defWidth(), height(), defHeight()); - } - - default int fitY() { - return STRETCH.heightFun.fit(width(), defWidth(), height(), defHeight()); - } - - default int offsX() { - return STRETCH.offsXFun.fit(width(), defWidth(), height(), defHeight()); - } - - default int offsY() { - return STRETCH.offsYFun.fit(width(), defWidth(), height(), defHeight()); - } - } - - interface Fitter { - - int fit(int width, int defWidth, int height, int defHeight); - } - - default void applyFullscreenOptions(Graphics2D graphics) { - switch (INTERPOLATION) { - case Nearest: - graphics.setRenderingHint(KEY_INTERPOLATION, VALUE_INTERPOLATION_NEAREST_NEIGHBOR); - break; - case Bilinear: - graphics.setRenderingHint(KEY_INTERPOLATION, VALUE_INTERPOLATION_BILINEAR); - break; - case Bicubic: - graphics.setRenderingHint(KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC); - break; - } - } - - default void draw(Graphics2D graphics, Image image, Dimension dim, ImageObserver observer) { - graphics.drawImage(image, dim.offsX(), dim.offsY(), dim.fitX(), dim.fitY(), observer); - } - - default FullscreenFunction createFullSwitcher(final GraphicsDevice device) { - switch (FULLMODE) { - case Best: - return new FullscreenSwitch(device, new DisplayModePicker(device)); - case Native: - return (w, h) -> device.getDisplayMode(); - } - - throw new Error(String.format("Unsupported mode: %s", String.valueOf(FULLMODE))); - } - - @FunctionalInterface - interface FullscreenFunction { - - DisplayMode get(int width, int height); - } - - static class FullscreenSwitch implements FullscreenFunction { - - private final GraphicsDevice dev; - private final DisplayModePicker dmp; - private DisplayMode oldDisplayMode; - private DisplayMode displayMode; - - private FullscreenSwitch(GraphicsDevice dev, DisplayModePicker dmp) { - this.dev = dev; - this.dmp = dmp; - } - - @Override - public DisplayMode get(final int width, final int height) { - if (oldDisplayMode == null) { - // In case we need to revert. - oldDisplayMode = dev.getDisplayMode(); - // TODO: what if bit depths are too small? - displayMode = dmp.pickClosest(width, height); - } else { - // We restore the original resolution - displayMode = oldDisplayMode; - oldDisplayMode = null; - } - - return displayMode; - } - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package awt; + +import java.awt.DisplayMode; +import java.awt.Graphics2D; +import java.awt.GraphicsDevice; +import java.awt.Image; +import static java.awt.RenderingHints.KEY_INTERPOLATION; +import static java.awt.RenderingHints.VALUE_INTERPOLATION_BICUBIC; +import static java.awt.RenderingHints.VALUE_INTERPOLATION_BILINEAR; +import static java.awt.RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR; +import java.awt.image.ImageObserver; +import m.Settings; +import mochadoom.Engine; + +/** + * Full-screen switch and scale governor + * + * @author Good Sign + */ +public interface FullscreenOptions { + + enum InterpolationMode { + Nearest, Bilinear, Bicubic; + } + + enum StretchMode { + Centre( + (w, defW, h, defH) -> Math.min(defW, w), + (w, defW, h, defH) -> Math.min(defH, h), + (w, defW, h, defH) -> Math.max(0, (w - defW) / 2), + (w, defW, h, defH) -> Math.max(0, (h - defH) / 2) + ), Stretch( + (w, defW, h, defH) -> w, + (w, defW, h, defH) -> h, + (w, defW, h, defH) -> 0, + (w, defW, h, defH) -> 0 + ), Fit( + (w, defW, h, defH) -> (int) (defW * minScale(w, defW, h, defH)), + (w, defW, h, defH) -> (int) (defH * minScale(w, defW, h, defH)), + (w, defW, h, defH) -> (w - (int) (defW * minScale(w, defW, h, defH))) / 2, + (w, defW, h, defH) -> (h - (int) (defH * minScale(w, defW, h, defH))) / 2 + ), Aspect_4_3( + (w, defW, h, defH) -> Fit.widthFun.fit(w, defW, h, (int) (defH * 1.2f)), + (w, defW, h, defH) -> Fit.heightFun.fit(w, defW, h, (int) (defH * 1.2f)), + (w, defW, h, defH) -> Fit.offsXFun.fit(w, defW, h, (int) (defH * 1.2f)), + (w, defW, h, defH) -> Fit.offsYFun.fit(w, defW, h, (int) (defH * 1.2f)) + ); + + final Fitter widthFun, heightFun, offsXFun, offsYFun; + + private StretchMode( + final Fitter widthFun, + final Fitter heightFun, + final Fitter offsXFun, + final Fitter offsYFun + ) { + this.widthFun = widthFun; + this.heightFun = heightFun; + this.offsXFun = offsXFun; + this.offsYFun = offsYFun; + } + + private static float minScale(int w, int defW, int h, int defH) { + float scaleX = w / (float) defW; + float scaleY = h / (float) defH; + return Math.min(scaleX, scaleY); + } + } + + enum FullMode { + Best, Native; + } + + static FullMode FULLMODE = Engine.getConfig().getValue(Settings.fullscreen_mode, FullMode.class); + static StretchMode STRETCH = Engine.getConfig().getValue(Settings.fullscreen_stretch, StretchMode.class); + static InterpolationMode INTERPOLATION = Engine.getConfig().getValue(Settings.fullscreen_interpolation, InterpolationMode.class); + + interface Dimension { + + int width(); + + int height(); + + int defWidth(); + + int defHeight(); + + default int fitX() { + return STRETCH.widthFun.fit(width(), defWidth(), height(), defHeight()); + } + + default int fitY() { + return STRETCH.heightFun.fit(width(), defWidth(), height(), defHeight()); + } + + default int offsX() { + return STRETCH.offsXFun.fit(width(), defWidth(), height(), defHeight()); + } + + default int offsY() { + return STRETCH.offsYFun.fit(width(), defWidth(), height(), defHeight()); + } + } + + interface Fitter { + + int fit(int width, int defWidth, int height, int defHeight); + } + + default void applyFullscreenOptions(Graphics2D graphics) { + switch (INTERPOLATION) { + case Nearest: + graphics.setRenderingHint(KEY_INTERPOLATION, VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + break; + case Bilinear: + graphics.setRenderingHint(KEY_INTERPOLATION, VALUE_INTERPOLATION_BILINEAR); + break; + case Bicubic: + graphics.setRenderingHint(KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC); + break; + } + } + + default void draw(Graphics2D graphics, Image image, Dimension dim, ImageObserver observer) { + graphics.drawImage(image, dim.offsX(), dim.offsY(), dim.fitX(), dim.fitY(), observer); + } + + default FullscreenFunction createFullSwitcher(final GraphicsDevice device) { + switch (FULLMODE) { + case Best: + return new FullscreenSwitch(device, new DisplayModePicker(device)); + case Native: + return (w, h) -> device.getDisplayMode(); + } + + throw new Error(String.format("Unsupported mode: %s", String.valueOf(FULLMODE))); + } + + @FunctionalInterface + interface FullscreenFunction { + + DisplayMode get(int width, int height); + } + + static class FullscreenSwitch implements FullscreenFunction { + + private final GraphicsDevice dev; + private final DisplayModePicker dmp; + private DisplayMode oldDisplayMode; + private DisplayMode displayMode; + + private FullscreenSwitch(GraphicsDevice dev, DisplayModePicker dmp) { + this.dev = dev; + this.dmp = dmp; + } + + @Override + public DisplayMode get(final int width, final int height) { + if (oldDisplayMode == null) { + // In case we need to revert. + oldDisplayMode = dev.getDisplayMode(); + // TODO: what if bit depths are too small? + displayMode = dmp.pickClosest(width, height); + } else { + // We restore the original resolution + displayMode = oldDisplayMode; + oldDisplayMode = null; + } + + return displayMode; + } + } +} \ No newline at end of file diff --git a/src/awt/MsgBox.java b/src/awt/MsgBox.java index d286c16..281adf1 100644 --- a/src/awt/MsgBox.java +++ b/src/awt/MsgBox.java @@ -12,24 +12,24 @@ import javax.swing.JLabel; /** A convenient message box class to pop up here and there. - * + * * @author zyklon * */ public class MsgBox extends Dialog implements ActionListener { /** - * + * */ private static final long serialVersionUID = -872019680203708495L; private Button ok, can; private boolean isOk = false; - /* + /* * * @param frame parent frame - * + * * @param msg message to be displayed - * + * * @param okcan true : ok cancel buttons, false : ok button only */ public boolean isOk() { @@ -95,4 +95,4 @@ public void actionPerformed(ActionEvent ae) { * System.out.println("Ok pressed"); if (!message.isOk) * System.out.println("Cancel pressed"); message.dispose(); } */ -} +} \ No newline at end of file diff --git a/src/boom/Compatibility.java b/src/boom/Compatibility.java index c7fb2ce..dad5301 100644 --- a/src/boom/Compatibility.java +++ b/src/boom/Compatibility.java @@ -1,7 +1,7 @@ package boom; /** cph - move compatibility levels here so we can use them in d_server.c - * + * * @author cph * */ @@ -129,4 +129,4 @@ public enum PC { PC_MAX }; -} +} \ No newline at end of file diff --git a/src/boom/DeepBSPNodesV4.java b/src/boom/DeepBSPNodesV4.java index 1de0cf2..17d6c80 100644 --- a/src/boom/DeepBSPNodesV4.java +++ b/src/boom/DeepBSPNodesV4.java @@ -47,4 +47,4 @@ public void unpack(ByteBuffer buf) throws IOException { nodes[i].unpack(buf); } } -} +} \ No newline at end of file diff --git a/src/boom/E6Y.java b/src/boom/E6Y.java index 5373182..5258d21 100644 --- a/src/boom/E6Y.java +++ b/src/boom/E6Y.java @@ -294,7 +294,7 @@ public class camera_t { int kill[MAXPLAYERS]; int item[MAXPLAYERS]; int secret[MAXPLAYERS]; - + int stat[TT_MAX]; } timetable_t; @@ -409,4 +409,4 @@ public class camera_t { #endif */ -} +} \ No newline at end of file diff --git a/src/boom/ZNodeSegs.java b/src/boom/ZNodeSegs.java index f665bad..b2c6372 100644 --- a/src/boom/ZNodeSegs.java +++ b/src/boom/ZNodeSegs.java @@ -47,4 +47,4 @@ public void unpack(ByteBuffer buf) throws IOException { nodes[i].unpack(buf); } } -} +} \ No newline at end of file diff --git a/src/boom/mapglvertex_t.java b/src/boom/mapglvertex_t.java index 6fda1ba..6b64d84 100644 --- a/src/boom/mapglvertex_t.java +++ b/src/boom/mapglvertex_t.java @@ -21,4 +21,4 @@ public void unpack(ByteBuffer buf) x = buf.getInt(); y = buf.getInt(); } -} +} \ No newline at end of file diff --git a/src/boom/mapnode_v4_t.java b/src/boom/mapnode_v4_t.java index 2dd15cd..9c924a6 100644 --- a/src/boom/mapnode_v4_t.java +++ b/src/boom/mapnode_v4_t.java @@ -21,7 +21,7 @@ public mapnode_v4_t() { /** Bounding box for each child, clip against view frustum. */ public short[][] bbox; - /** If NF_SUBSECTOR its a subsector, else it's a node of another subtree. + /** If NF_SUBSECTOR its a subsector, else it's a node of another subtree. * In simpler words: if the first bit is set, strip it and use the rest * as a subtree index. Else it's a node index. * */ @@ -44,4 +44,4 @@ public void unpack(ByteBuffer buf) DoomBuffer.readIntArray(buf, this.children, 2); } -} +} \ No newline at end of file diff --git a/src/boom/mapnode_znod_t.java b/src/boom/mapnode_znod_t.java index 590d95c..8e54b16 100644 --- a/src/boom/mapnode_znod_t.java +++ b/src/boom/mapnode_znod_t.java @@ -40,4 +40,4 @@ public void unpack(ByteBuffer buf) } -} +} \ No newline at end of file diff --git a/src/boom/mapseg_v4_t.java b/src/boom/mapseg_v4_t.java index 9ff89ac..2cf5d12 100644 --- a/src/boom/mapseg_v4_t.java +++ b/src/boom/mapseg_v4_t.java @@ -8,8 +8,8 @@ /** * LineSeg, generated by splitting LineDefs * using partition lines selected by BSP builder. - * MAES: this is the ON-DISK structure. The corresponding memory structure, - * segs_t, has fixed_t members. + * MAES: this is the ON-DISK structure. The corresponding memory structure, + * segs_t, has fixed_t members. */ public class mapseg_v4_t implements CacheableDoomObject { @@ -41,4 +41,4 @@ public void unpack(ByteBuffer buf) } -}; +}; \ No newline at end of file diff --git a/src/boom/mapseg_znod_t.java b/src/boom/mapseg_znod_t.java index a62580d..050d78f 100644 --- a/src/boom/mapseg_znod_t.java +++ b/src/boom/mapseg_znod_t.java @@ -32,4 +32,4 @@ public void unpack(ByteBuffer buf) this.side = buf.get(); } -}; +}; \ No newline at end of file diff --git a/src/boom/mapsubsector_v4_t.java b/src/boom/mapsubsector_v4_t.java index d04058d..b1431dd 100644 --- a/src/boom/mapsubsector_v4_t.java +++ b/src/boom/mapsubsector_v4_t.java @@ -23,4 +23,4 @@ public static int sizeOf() { return 6; } -} +} \ No newline at end of file diff --git a/src/boom/mapsubsector_znod_t.java b/src/boom/mapsubsector_znod_t.java index 19bd91d..b2d6c07 100644 --- a/src/boom/mapsubsector_znod_t.java +++ b/src/boom/mapsubsector_znod_t.java @@ -21,4 +21,4 @@ public static final int sizeOf() { return 4; } -} +} \ No newline at end of file diff --git a/src/boom/prboom_comp_t.java b/src/boom/prboom_comp_t.java index bc0da37..4b1b545 100644 --- a/src/boom/prboom_comp_t.java +++ b/src/boom/prboom_comp_t.java @@ -16,4 +16,4 @@ public prboom_comp_t(int minver, int maxver, boolean state, String cmd) { public boolean state; public String cmd; -} +} \ No newline at end of file diff --git a/src/data/Defines.java b/src/data/Defines.java index 1a41762..e058132 100644 --- a/src/data/Defines.java +++ b/src/data/Defines.java @@ -60,7 +60,7 @@ public final class Defines { // The integrated sound support is experimental, // and unfinished. Default is synchronous. // Experimental asynchronous timer based is -// handled by SNDINTR. +// handled by SNDINTR. //#define SNDSERV 1 //#define SNDINTR 1 // Defines suck. C sucks. @@ -141,11 +141,11 @@ public final class Defines { public static final int NUMMAPS = 9; //in tics -//U #define PAUSELEN (TICRATE*2) +//U #define PAUSELEN (TICRATE*2) //U #define SCORESTEP 100 //U #define ANIMPERIOD 32 //pixel distance from "(YOU)" to "PLAYER N" -//U #define STARDIST 10 +//U #define STARDIST 10 //U #define WK 1 // MAES 23/5/2011: moved SP_... stuff to EndLevel public static final int BACKUPTICS = 12; @@ -219,7 +219,7 @@ public final class Defines { public static final int BTS_SAVEGAME = 2; // Savegame slot numbers - // occupy the second byte of buttons. + // occupy the second byte of buttons. public static final int BTS_SAVEMASK = (4 + 8 + 16); public static final int BTS_SAVESHIFT = 2; @@ -267,7 +267,7 @@ public final class Defines { public static final int NF_SUBSECTOR_CLASSIC = 0x8000; /** Player states. */ - public static final int PST_LIVE = 0, // Playing or camping. + public static final int PST_LIVE = 0, // Playing or camping. PST_DEAD = 1, // Dead on the ground, view follows killer. PST_REBORN = 2; // Ready to restart/respawn??? @@ -276,4 +276,4 @@ public final class Defines { public static final int FF_FRAMEMASK = 0x7fff; static final String rcsid = "$Id: Defines.java,v 1.48 2012/09/24 17:16:22 velktron Exp $"; -} +} \ No newline at end of file diff --git a/src/data/Limits.java b/src/data/Limits.java index 45c3ff5..7df8eae 100644 --- a/src/data/Limits.java +++ b/src/data/Limits.java @@ -98,4 +98,4 @@ public final class Limits { public static final int NUMBRAINTARGETS = 32; public static final int NUMMOBJTYPES = mobjtype_t.NUMMOBJTYPES.ordinal(); -} +} \ No newline at end of file diff --git a/src/data/SineCosine.java b/src/data/SineCosine.java index 5e2929a..08cfff3 100644 --- a/src/data/SineCosine.java +++ b/src/data/SineCosine.java @@ -4,7 +4,7 @@ /** Sine and Cosine. * Java can't have that much initialization data in one file, so I had to separate it. - * + * * @author Maes * */ @@ -1054,10 +1054,10 @@ public class SineCosine { // Re-use data, is just PI/2 phase shift. public static final int[] finecosine = new int[FINEANGLES]; - /* MAES: Java can't use that much direct init data in a single class so a first workaround + /* MAES: Java can't use that much direct init data in a single class so a first workaround * s to generate the data procedurally. * If this proves to cause accuracy problems, an external data file could be used. - * Another workaround is to define the int table in its own file and then import + * Another workaround is to define the int table in its own file and then import * or "implement" it. */ static { @@ -1068,4 +1068,4 @@ private SineCosine() { } -} +} \ No newline at end of file diff --git a/src/data/Tables.java b/src/data/Tables.java index 826f928..7fa372d 100644 --- a/src/data/Tables.java +++ b/src/data/Tables.java @@ -108,7 +108,7 @@ // DESCRIPTION: // Lookup tables. // Do not try to look them up :-). -// In the order of appearance: +// In the order of appearance: // // int finetangent[4096] - Tangens LUT. // Should work with BAM fairly well (12 of 16bit, @@ -120,8 +120,8 @@ // // int tantoangle[2049] - ArcTan LUT, // maps tan(angle) to angle fast. Gotta search. -// -// +// +// //----------------------------------------------------------------------------- public final class Tables { @@ -130,11 +130,11 @@ public final class Tables { public static final double PI = 3.141592657; /** Normally set to 12, and this sets the value of other constants too. - * Howevever changing it will also distort the view, resulting in a - * nightmare-like vision. There are some practical minimum and + * Howevever changing it will also distort the view, resulting in a + * nightmare-like vision. There are some practical minimum and * maximums as well. - * - * + * + * */ public static final int BITSPRECISION = 12; public static final int FINEANGLES = 2 << BITSPRECISION; @@ -161,32 +161,32 @@ public final class Tables { * mapped to 2^32 values!!! But the lookup tables are only 8K (2^13) * long (for sine/cosine), which means that we're 19 bits too precise * -> ergo, >>ANGLETOFINESHIFT must be applied. - * + * * Also, the original angle_t type was "unsigned int", so we should be - * using longs here. However, as BAM is used only after shifting, so + * using longs here. However, as BAM is used only after shifting, so * using ints doesn't cause a problem for LUT access. - * - * However, some problems may arise with comparisons and ordinary arithmetic: - * e.g. ANG270 is supposed to be larger not only than ANG180, but also from + * + * However, some problems may arise with comparisons and ordinary arithmetic: + * e.g. ANG270 is supposed to be larger not only than ANG180, but also from * ANG45, which does not hold true if those constants were stored as ints. - * - * As a rule of thumb, whenever you need to store JUST a BAM index, then + * + * As a rule of thumb, whenever you need to store JUST a BAM index, then * ints are ok (actually, you'll have to cast down to int anyway). - * - * Whenever you need to add or compare angles directly however, you need + * + * Whenever you need to add or compare angles directly however, you need * longs. Furthermore, you must account for possible rollovers by modding * with 0x100000000 or else long ints will store angles exceeding 360 degrees! * Under no circumstances the value actually stored in the "long angles" should * exceed 0xFFFFFFFF. - * + * * An example: comparing any two long angles directly is OK, provided they were * constructed correctly. - * + * * Adding, subtracting, multiplying etc. with two or more angles however requires * rollover compensation (e.g. result=(a+b+c) is wrong, result=(a+b+c)%0xFFFFFFFF * is correct and will produce an angle you can "trust". - * - * + * + * */ /** Doom angle constants. */ public static final long ANG45 = 0x20000000L, @@ -211,12 +211,12 @@ public final class Tables { public static final int[] finesine = new int[FINEANGLES + QUARTERMARK]; public static final int[] finecosine = new int[FINEANGLES]; - /** Any denominator smaller than 512 will result in + /** Any denominator smaller than 512 will result in * maximum slope (45 degrees, or an index into tantoangle of 2048) * The original method used unsigned args. So if this returns NEGATIVES * in any way, it means you fucked up. Another "consistency" for Doom. * Even though it was called upon fixed_t signed numbers. - * + * */ public static final int SlopeDiv(long num, long den) { int ans; @@ -232,34 +232,34 @@ public static final int SlopeDiv(long num, long den) { /** Finetangent table. It only has 4096 values corresponding roughly * to -90/+90 angles, with values between are -/+ 2607 for "infinity". - * + * * Since in vanilla accesses to the table can overflow way beyond 4096 * indexes, the access index must be clipped to 4K tops via an accessor, * or, in order to simulate some aspects of vanilla overflowing, replicate * 4K of finesine's values AFTER the 4K index. This removes the need * for access checking, at the cost of some extra memory. It also allows * a small degree of "vanilla like" compatibility. - * - * + * + * */ public final static int[] finetangent = new int[2 * FINETANS]; // MAES: original range 2049 -// This obviously +// This obviously // Range goes from 0x00000000 to 0x20000000, so in theory plain ints should be enough... /** This maps a value 0-2048 to a BAM unsigned integer angle, ranging from 0x0 to 0x2000000: - * - * In practice, this means there are only tangent values for angles up to 45 degrees. - * + * + * In practice, this means there are only tangent values for angles up to 45 degrees. + * * These values are valid BAM measurements in the first quadrant - * - * + * + * */ public static final int[] tantoangle = new int[SLOPERANGE + 1]; - /** Use this to get a value from the finesine table. It will be automatically shifte, + /** Use this to get a value from the finesine table. It will be automatically shifte, * Equivalent to finesine[angle>>>ANGLETOFINESHIFT] - * + * * @param angle in BAM units * @return */ @@ -269,9 +269,9 @@ public static final int finesine(int angle) { /** Use this to get a value from the finesine table using a long argument. * It will automatically shift, apply rollover module and cast. - * + * * Equivalent to finesine[(int) ((angle>>ANGLETOFINESHIFT)%ANGLEMODULE)]; - * + * * @param angle in BAM units * @return */ @@ -279,7 +279,7 @@ public static final int finesine(long angle) { return finesine[(int) ((angle & BITS32) >>> ANGLETOFINESHIFT)]; } - /** Use this to get a value from the finecosine table. It will be automatically shifted, + /** Use this to get a value from the finecosine table. It will be automatically shifted, * Equivalent to finecosine[angle>>>ANGLETOFINESHIFT] * @param angle in BAM units * @return @@ -288,9 +288,9 @@ public static final int finecosine(int angle) { return finecosine[angle >>> ANGLETOFINESHIFT]; } - /** Use this to get a value from the finecosine table. + /** Use this to get a value from the finecosine table. * It will automatically shift, apply rollover module and cast. - * + * * Equivalent to finecosine[(int) ((angle&BITS32)>>>ANGLETOFINESHIFT)] * @param angle in BAM units * @return @@ -299,7 +299,7 @@ public static final int finecosine(long angle) { return finecosine[(int) ((angle & BITS32) >>> ANGLETOFINESHIFT)]; } - /** Compare BAM angles in 32-bit format + /** Compare BAM angles in 32-bit format * "Greater or Equal" bam0>bam1 * */ public static final boolean GE(int bam0, int bam1) { @@ -353,9 +353,9 @@ public static final int BAMDiv(int bam0, int bam1) { return (int) ((long) (0x0FFFFFFFFL & bam0) / (0x0FFFFFFFFL & bam1)); } - /** Converts a long angle to a BAM LUT-ready angle (13 bits, between 0-8191). - * Cuts away rollover. - * + /** Converts a long angle to a BAM LUT-ready angle (13 bits, between 0-8191). + * Cuts away rollover. + * * @param angle * @return */ @@ -363,9 +363,9 @@ public static final int toBAMIndex(long angle) { return (int) ((angle & BITS32) >>> ANGLETOFINESHIFT); } - /** Converts a long angle to a TAN BAM LUT-ready angle (12 bits, between 0-4195). - * Cuts away rollover. - * + /** Converts a long angle to a TAN BAM LUT-ready angle (12 bits, between 0-4195). + * Cuts away rollover. + * * @param angle * @return */ @@ -373,8 +373,8 @@ public static final int toFineTanIndex(long angle) { return (int) ((angle & BITS31) >>> ANGLETOFINESHIFT); } - /** Converts an 32-bit int angle to a BAM LUT-ready angle (13 bits, between 0-8192). - * + /** Converts an 32-bit int angle to a BAM LUT-ready angle (13 bits, between 0-8192). + * * @param angle * @return */ @@ -390,18 +390,18 @@ public static final long addAngles(long a, long b) { /** MAES: I brought this function "back from the dead" since * Java has some pretty low static limits for statically defined LUTs. * In order to keep the codebase clutter and static allocation to a minimum, - * I decided to procedurally generate the tables during runtime, + * I decided to procedurally generate the tables during runtime, * using the original functions. - * + * * The code has been thoroughly checked in both Sun's JDK and GCC and was * found to, indeed, produce the same values found in the finesine/finecosine * and finetangent tables, at least on Intel. - * - * The "tantoangle" table is also generated procedurally, but since there + * + * The "tantoangle" table is also generated procedurally, but since there * was no "dead code" to build upon, it was recreated through reverse - * engineering and also found to be 100% faithful to the original data. - * - * + * engineering and also found to be 100% faithful to the original data. + * + * */ public static void InitTables() { int i; @@ -445,18 +445,18 @@ public static void InitTables() { * accessed in special cases (overflow) so we only need to consider 0..2047 aka 11 bits. * So: we take "minislopes" 0-2048, we blow them up to a full fixed_t unit with <<5. * We make this into a float (?), then use trigonometric ATAN, and then go to BAM. - * + * * Any questions? - * + * */ - /* This is the recreated code + /* This is the recreated code for (i=0 ; i DOOM, final String doomwaddir) { @@ -70,8 +70,8 @@ public static String tryAllWads(final DoomMain DOOM, final String doomwadd } /** - * Try only one IWAD. - * + * Try only one IWAD. + * * @param iwad * @param doomwaddir * @return game mode @@ -95,4 +95,4 @@ public static GameMode tryOnlyOne(String iwad, String doomwaddir) { // Fuck that. return null; } -} +} \ No newline at end of file diff --git a/src/defines/GameMission_t.java b/src/defines/GameMission_t.java index d863b68..b345e92 100644 --- a/src/defines/GameMission_t.java +++ b/src/defines/GameMission_t.java @@ -1,7 +1,7 @@ package defines; /** Mission packs - might be useful for TC stuff? - * + * */ public enum GameMission_t { doom, // DOOM 1 @@ -9,4 +9,4 @@ public enum GameMission_t { pack_tnt, // TNT mission pack pack_plut, // Plutonia pack none -} +} \ No newline at end of file diff --git a/src/defines/GameMode.java b/src/defines/GameMode.java index 33ea7de..36a20b5 100644 --- a/src/defines/GameMode.java +++ b/src/defines/GameMode.java @@ -27,9 +27,9 @@ public enum GameMode { pack_plut("cdata", PLUTONIA_WAD, CommandVariable.COMDEV), // Plutonia pack pack_xbla("cdata", XBLA_WAD, CommandVariable.COMDEV), // XBLA Doom. How you got hold of it, I don't care :-p freedm("cdata", FREEDM_WAD, CommandVariable.FRDMDEV), // FreeDM - freedoom1("data_se", FREEDOOM1_WAD, CommandVariable.FR1DEV), // Freedoom phase 1 + freedoom1("data_se", FREEDOOM1_WAD, CommandVariable.FR1DEV), // Freedoom phase 1 freedoom2("cdata", FREEDOOM2_WAD, CommandVariable.FR2DEV), // Freedoom phase 2 - indetermined("data_se", null, null); // Well, no IWAD found. + indetermined("data_se", null, null); // Well, no IWAD found. public final String devDir; public final DoomVersion version; @@ -72,4 +72,4 @@ private GameMode(String devDir, DoomVersion version, CommandVariable devVar) { public boolean hasTexture2() { return !(this == GameMode.shareware || this == GameMode.freedoom2 || this == GameMode.commercial); } -}; +}; \ No newline at end of file diff --git a/src/defines/Language_t.java b/src/defines/Language_t.java index 8b911c9..8f013b6 100644 --- a/src/defines/Language_t.java +++ b/src/defines/Language_t.java @@ -6,4 +6,4 @@ public enum Language_t { french, german, unknown -} +} \ No newline at end of file diff --git a/src/defines/ammotype_t.java b/src/defines/ammotype_t.java index 9a10fab..b2a98a3 100644 --- a/src/defines/ammotype_t.java +++ b/src/defines/ammotype_t.java @@ -7,6 +7,6 @@ public enum ammotype_t { am_cell, // Plasma rifle, BFG. am_misl, // Missile launcher. NUMAMMO, - am_noammo // Unlimited for chainsaw / fist. + am_noammo // Unlimited for chainsaw / fist. -} +} \ No newline at end of file diff --git a/src/defines/card_t.java b/src/defines/card_t.java index f0cf08a..b708945 100644 --- a/src/defines/card_t.java +++ b/src/defines/card_t.java @@ -12,4 +12,4 @@ public enum card_t { it_redskull, NUMCARDS -} +} \ No newline at end of file diff --git a/src/defines/gamestate_t.java b/src/defines/gamestate_t.java index ff03192..55f1c19 100644 --- a/src/defines/gamestate_t.java +++ b/src/defines/gamestate_t.java @@ -1,7 +1,7 @@ package defines; /** The current state of the game: whether we are - playing, gazing at the intermission screen, + playing, gazing at the intermission screen, the game final animation, or a demo. */ public enum gamestate_t { GS_LEVEL, @@ -9,4 +9,4 @@ public enum gamestate_t { GS_FINALE, GS_DEMOSCREEN, GS_MINUS_ONE // hack used for the "-1" state -} +} \ No newline at end of file diff --git a/src/defines/skill_t.java b/src/defines/skill_t.java index 08e49f1..a40fc4d 100644 --- a/src/defines/skill_t.java +++ b/src/defines/skill_t.java @@ -6,4 +6,4 @@ public enum skill_t { sk_medium, sk_hard, sk_nightmare -} +} \ No newline at end of file diff --git a/src/defines/slopetype_t.java b/src/defines/slopetype_t.java index 86fbab7..3542e42 100644 --- a/src/defines/slopetype_t.java +++ b/src/defines/slopetype_t.java @@ -7,4 +7,4 @@ public enum slopetype_t { ST_POSITIVE, ST_NEGATIVE -} +} \ No newline at end of file diff --git a/src/defines/statenum_t.java b/src/defines/statenum_t.java index 511a07d..e380875 100644 --- a/src/defines/statenum_t.java +++ b/src/defines/statenum_t.java @@ -969,4 +969,4 @@ public enum statenum_t { S_TECH2LAMP3, S_TECH2LAMP4, NUMSTATES -} +} \ No newline at end of file diff --git a/src/demo/IDemoTicCmd.java b/src/demo/IDemoTicCmd.java index 996733e..4369678 100644 --- a/src/demo/IDemoTicCmd.java +++ b/src/demo/IDemoTicCmd.java @@ -7,22 +7,22 @@ * and are not necessarily equal to the in-game ticcmd_t. * Thus, it's necessary for them to implement some * adaptor method (both ways). - * + * * @author admin * */ public interface IDemoTicCmd extends IWritableDoomObject { - /** Decode this IDemoTicCmd into a standard ticcmd_t. - * + /** Decode this IDemoTicCmd into a standard ticcmd_t. + * * @param source */ public void decode(ticcmd_t dest); /** Encode this IDemoTicCmd from a standard ticcmd_t. - * + * * @param dest */ public void encode(ticcmd_t source); -} +} \ No newline at end of file diff --git a/src/demo/IDoomDemo.java b/src/demo/IDoomDemo.java index ac9e2b6..033ba1a 100644 --- a/src/demo/IDoomDemo.java +++ b/src/demo/IDoomDemo.java @@ -9,16 +9,16 @@ public interface IDoomDemo extends IWritableDoomObject { public static final int DEMOMARKER = 0x80; /** Get next demo command, in its raw format. Use - * its own adapters if you need it converted to a + * its own adapters if you need it converted to a * standard ticcmd_t. - * + * * @return */ IDemoTicCmd getNextTic(); /** Record a demo command in the IDoomDemo's native format. * Use the IDemoTicCmd's objects adaptors to convert it. - * + * * @param tic */ void putTic(IDemoTicCmd tic); @@ -65,4 +65,4 @@ public interface IDoomDemo extends IWritableDoomObject { void resetDemo(); -} +} \ No newline at end of file diff --git a/src/demo/VanillaDoomDemo.java b/src/demo/VanillaDoomDemo.java index 7df08ce..747c617 100644 --- a/src/demo/VanillaDoomDemo.java +++ b/src/demo/VanillaDoomDemo.java @@ -47,7 +47,7 @@ public void unpack(ByteBuffer b) { // Just the Header info for vanilla should be 13 bytes. // 1 byte at the end is the end-demo marker // So valid vanilla demos should have sizes that - // fit the formula 14+4n, since each vanilla + // fit the formula 14+4n, since each vanilla // demo ticcmd_t is 4 bytes. int lens = (b.limit() - 13) / 4; boolean vanilla = (b.limit() == (14 + 4 * lens)); @@ -235,4 +235,4 @@ public void resetDemo() { } /////////////////////// VARIOUS BORING GETTERS ///////////////////// -} +} \ No newline at end of file diff --git a/src/demo/VanillaTiccmd.java b/src/demo/VanillaTiccmd.java index 97d1e1a..151dba1 100644 --- a/src/demo/VanillaTiccmd.java +++ b/src/demo/VanillaTiccmd.java @@ -13,7 +13,7 @@ * loading and recording easier, this class contains only the * necessary stuff to read/write from/to disk during VANILLA * demos. It can be converted from/to ticcmd_t, if needed. - * + * * @author admin * */ @@ -33,8 +33,8 @@ public class VanillaTiccmd implements CacheableDoomObject, IDemoTicCmd, IWritabl * in reading ticcmd_t's from a lump is when playing back demos. * Therefore, we use this specialized reading method which does NOT, * I repeat, DOES NOT set all fields and some are read differently. - * NOT 1:1 intercangeable with the Datagram methods! - * + * NOT 1:1 intercangeable with the Datagram methods! + * */ @Override public void unpack(ByteBuffer f) @@ -42,10 +42,10 @@ public void unpack(ByteBuffer f) // MAES: the original ID code for reference. // demo_p++ is a pointer inside a raw byte buffer. - //cmd->forwardmove = ((signed char)*demo_p++); - //cmd->sidemove = ((signed char)*demo_p++); - //cmd->angleturn = ((unsigned char)*demo_p++)<<8; - //cmd->buttons = (unsigned char)*demo_p++; + //cmd->forwardmove = ((signed char)*demo_p++); + //cmd->sidemove = ((signed char)*demo_p++); + //cmd->angleturn = ((unsigned char)*demo_p++)<<8; + //cmd->buttons = (unsigned char)*demo_p++; forwardmove = f.get(); sidemove = f.get(); // Even if they use the "unsigned char" syntax, angleturn is signed. @@ -55,7 +55,7 @@ public void unpack(ByteBuffer f) } /** Ditto, we only pack some of the fields. - * + * * @param f * @throws IOException */ @@ -113,4 +113,4 @@ public void write(DataOutputStream f) f.writeByte(buttons); } -} +} \ No newline at end of file diff --git a/src/doom/CVarManager.java b/src/doom/CVarManager.java index 8d49dfa..059db0a 100644 --- a/src/doom/CVarManager.java +++ b/src/doom/CVarManager.java @@ -35,7 +35,7 @@ * 1. Define CVars in CommandVariable Enum * 2. In program entry main function, create any ICommandLineManager and pass an instance to create CVarManager * 3. Use methods bool, present, get and with to check or get CVars - * + * * @author Good Sign */ public class CVarManager { @@ -99,14 +99,14 @@ public Optional get(final CommandVariable cv, final Class itemType, fi * Tries to apply a CVar argument at position to the consuming function * The magic is that you declare a lambda function or reference some method * and the type of object will be automatically picked from what you hinted - * + * * i.e. (String s) -> System.out.println(s) will try to get string, * (Object o) -> map.put(key, o) or o -> list.add(o.hashCode()) will try to get objects * and you dont have to specify class - * + * * The drawback is the ClassCastException will be thrown if the value is neither * what you expected, nor a subclass of it - * + * * @param cv * @param position * @param action @@ -298,4 +298,4 @@ public void accept(final String line) { cVarCount += processAllArgs(Arrays.asList(line.split(" "))); } } -} +} \ No newline at end of file diff --git a/src/doom/CommandVariable.java b/src/doom/CommandVariable.java index e9df6c1..f88dba0 100644 --- a/src/doom/CommandVariable.java +++ b/src/doom/CommandVariable.java @@ -1,245 +1,245 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package doom; - -/** - * A new way to define Command Line Arguments for the Engine - * - * @author Good Sign - */ -public enum CommandVariable { - DISP(String.class), GEOM(String[].class), CONFIG(String[].class), TRANMAP(String.class), - PLAYDEMO(String.class), FASTDEMO(String.class), TIMEDEMO(String.class), RECORD(String.class), STATCOPY(String.class), - TURBO(Integer.class), SKILL(Integer.class), EPISODE(Integer.class), TIMER(Integer.class), PORT(Integer.class), - MULTIPLY(Integer.class), WIDTH(Integer.class), HEIGHT(Integer.class), - PARALLELRENDERER(Integer.class, Integer.class, Integer.class), - PARALLELRENDERER2(Integer.class, Integer.class, Integer.class), - LOADGAME(Character.class), DUP(Character.class), - NET(Character.class, String[].class), - WART(Integer.class, Integer.class), - WARP(WarpFormat.class), - MAP('+', MapFormat.class), - FILE(String[].class), - IWAD(String.class), - NOVERT(ForbidFormat.class), - NOVOLATILEIMAGE(ForbidFormat.class), - AWTFRAME, - DEBUGFILE, - SHDEV, - REGDEV, - FRDMDEV, - FR1DEV, - FR2DEV, - COMDEV, - NOMONSTERS, - RESPAWN, - FAST, - DEVPARM, - ALTDEATH, - DEATHMATCH, - MILLIS, - FASTTIC, - CDROM, - AVG, - NODRAW, - NOBLIT, - NOPLAYPAL, - NOCOLORMAP, - SERIALRENDERER, - EXTRATIC, - NOMUSIC, - NOSOUND, - NOSFX, - AUDIOLINES, - SPEAKERSOUND, - CLIPSOUND, - CLASSICSOUND, - INDEXED, - HICOLOR, - TRUECOLOR, - ALPHATRUECOLOR, - BLOCKMAP, - SHOWFPS, - JAVARANDOM, - GREYPAL; - - public final char prefix; - public final Class[] arguments; - public final static int MIN_CVAR_LENGTH = 4; - - CommandVariable(final char prefix, final Class... arguments) { - this.prefix = prefix; - this.arguments = arguments; - } - - CommandVariable(final Class... arguments) { - this('-', arguments); - } - - public Type getType() { - return arguments.length > 0 - ? (arguments[arguments.length - 1].isArray() - ? Type.VARARG - : Type.PARAMETER) - : Type.SWITCH; - } - - public enum Type { - PARAMETER, VARARG, SWITCH; - } - - public interface WarpMetric { - - int getEpisode(); - - int getMap(); - } - - public static class ForbidFormat { - - public static ForbidFormat FORBID = new ForbidFormat("disable"); - public static ForbidFormat ALLOW = new ForbidFormat(null); - private final boolean isForbidden; - - public ForbidFormat(final String forbidString) { - this.isForbidden = "disable".equals(forbidString); - } - - @Override - public int hashCode() { - int hash = 3; - hash = 67 * hash + (this.isForbidden ? 1 : 0); - return hash; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ForbidFormat other = (ForbidFormat) obj; - return this.isForbidden == other.isForbidden; - } - } - - public static class WarpFormat { - - final int warpInt; - - public WarpFormat(final int warpInt) { - this.warpInt = warpInt; - } - - public WarpFormat(final String warpString) { - int tryParse; - try { - tryParse = Integer.parseInt(warpString); - } catch (NumberFormatException e) { - // swallow exception. No warp. - tryParse = 0; - } - this.warpInt = tryParse; - } - - public WarpMetric getMetric(final boolean commercial) { - return new Metric(commercial); - } - - private class Metric implements WarpMetric { - - final int episode; - final int map; - - Metric(final boolean commercial) { - if (commercial) { - episode = 1; - map = WarpFormat.this.warpInt; - } else { - final int evalInt = WarpFormat.this.warpInt > 99 - ? WarpFormat.this.warpInt % 100 - : WarpFormat.this.warpInt; - - episode = evalInt / 10; - map = evalInt % 10; - } - } - - @Override - public int getEpisode() { - return episode; - } - - @Override - public int getMap() { - return map; - } - } - } - - public static class MapFormat { - - final String mapString; - - public MapFormat(final String mapString) { - this.mapString = mapString.toLowerCase(); - } - - protected int parseAsMapXX() { - if (mapString.length() != 5 || mapString.lastIndexOf("map") != 0) { - return -1; // Meh. - } - - final int map; - try { - map = Integer.parseInt(mapString.substring(3)); - } catch (NumberFormatException e) { - return -1; // eww - } - - return map; - } - - protected int parseAsExMx() { - if (mapString.length() != 4 || mapString.charAt(0) != 'e' || mapString.charAt(2) != 'm') { - return -1; // Nah. - } - - final char episode = mapString.charAt(1); - final char mission = mapString.charAt(3); - - if (episode < '0' || episode > '9' || mission < '0' || mission > '9') { - return -1; - } - - return (episode - '0') * 10 + (mission - '0'); - } - - public WarpMetric getMetric(final boolean commercial) { - final int parse = commercial - ? parseAsMapXX() - : parseAsExMx(); - - return new WarpFormat(Math.max(parse, 0)).getMetric(commercial); - } - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package doom; + +/** + * A new way to define Command Line Arguments for the Engine + * + * @author Good Sign + */ +public enum CommandVariable { + DISP(String.class), GEOM(String[].class), CONFIG(String[].class), TRANMAP(String.class), + PLAYDEMO(String.class), FASTDEMO(String.class), TIMEDEMO(String.class), RECORD(String.class), STATCOPY(String.class), + TURBO(Integer.class), SKILL(Integer.class), EPISODE(Integer.class), TIMER(Integer.class), PORT(Integer.class), + MULTIPLY(Integer.class), WIDTH(Integer.class), HEIGHT(Integer.class), + PARALLELRENDERER(Integer.class, Integer.class, Integer.class), + PARALLELRENDERER2(Integer.class, Integer.class, Integer.class), + LOADGAME(Character.class), DUP(Character.class), + NET(Character.class, String[].class), + WART(Integer.class, Integer.class), + WARP(WarpFormat.class), + MAP('+', MapFormat.class), + FILE(String[].class), + IWAD(String.class), + NOVERT(ForbidFormat.class), + NOVOLATILEIMAGE(ForbidFormat.class), + AWTFRAME, + DEBUGFILE, + SHDEV, + REGDEV, + FRDMDEV, + FR1DEV, + FR2DEV, + COMDEV, + NOMONSTERS, + RESPAWN, + FAST, + DEVPARM, + ALTDEATH, + DEATHMATCH, + MILLIS, + FASTTIC, + CDROM, + AVG, + NODRAW, + NOBLIT, + NOPLAYPAL, + NOCOLORMAP, + SERIALRENDERER, + EXTRATIC, + NOMUSIC, + NOSOUND, + NOSFX, + AUDIOLINES, + SPEAKERSOUND, + CLIPSOUND, + CLASSICSOUND, + INDEXED, + HICOLOR, + TRUECOLOR, + ALPHATRUECOLOR, + BLOCKMAP, + SHOWFPS, + JAVARANDOM, + GREYPAL; + + public final char prefix; + public final Class[] arguments; + public final static int MIN_CVAR_LENGTH = 4; + + CommandVariable(final char prefix, final Class... arguments) { + this.prefix = prefix; + this.arguments = arguments; + } + + CommandVariable(final Class... arguments) { + this('-', arguments); + } + + public Type getType() { + return arguments.length > 0 + ? (arguments[arguments.length - 1].isArray() + ? Type.VARARG + : Type.PARAMETER) + : Type.SWITCH; + } + + public enum Type { + PARAMETER, VARARG, SWITCH; + } + + public interface WarpMetric { + + int getEpisode(); + + int getMap(); + } + + public static class ForbidFormat { + + public static ForbidFormat FORBID = new ForbidFormat("disable"); + public static ForbidFormat ALLOW = new ForbidFormat(null); + private final boolean isForbidden; + + public ForbidFormat(final String forbidString) { + this.isForbidden = "disable".equals(forbidString); + } + + @Override + public int hashCode() { + int hash = 3; + hash = 67 * hash + (this.isForbidden ? 1 : 0); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ForbidFormat other = (ForbidFormat) obj; + return this.isForbidden == other.isForbidden; + } + } + + public static class WarpFormat { + + final int warpInt; + + public WarpFormat(final int warpInt) { + this.warpInt = warpInt; + } + + public WarpFormat(final String warpString) { + int tryParse; + try { + tryParse = Integer.parseInt(warpString); + } catch (NumberFormatException e) { + // swallow exception. No warp. + tryParse = 0; + } + this.warpInt = tryParse; + } + + public WarpMetric getMetric(final boolean commercial) { + return new Metric(commercial); + } + + private class Metric implements WarpMetric { + + final int episode; + final int map; + + Metric(final boolean commercial) { + if (commercial) { + episode = 1; + map = WarpFormat.this.warpInt; + } else { + final int evalInt = WarpFormat.this.warpInt > 99 + ? WarpFormat.this.warpInt % 100 + : WarpFormat.this.warpInt; + + episode = evalInt / 10; + map = evalInt % 10; + } + } + + @Override + public int getEpisode() { + return episode; + } + + @Override + public int getMap() { + return map; + } + } + } + + public static class MapFormat { + + final String mapString; + + public MapFormat(final String mapString) { + this.mapString = mapString.toLowerCase(); + } + + protected int parseAsMapXX() { + if (mapString.length() != 5 || mapString.lastIndexOf("map") != 0) { + return -1; // Meh. + } + + final int map; + try { + map = Integer.parseInt(mapString.substring(3)); + } catch (NumberFormatException e) { + return -1; // eww + } + + return map; + } + + protected int parseAsExMx() { + if (mapString.length() != 4 || mapString.charAt(0) != 'e' || mapString.charAt(2) != 'm') { + return -1; // Nah. + } + + final char episode = mapString.charAt(1); + final char mission = mapString.charAt(3); + + if (episode < '0' || episode > '9' || mission < '0' || mission > '9') { + return -1; + } + + return (episode - '0') * 10 + (mission - '0'); + } + + public WarpMetric getMetric(final boolean commercial) { + final int parse = commercial + ? parseAsMapXX() + : parseAsExMx(); + + return new WarpFormat(Math.max(parse, 0)).getMetric(commercial); + } + } +} \ No newline at end of file diff --git a/src/doom/ConfigBase.java b/src/doom/ConfigBase.java index 4fc68f1..baddbbf 100644 --- a/src/doom/ConfigBase.java +++ b/src/doom/ConfigBase.java @@ -31,7 +31,7 @@ /** * Manages loading different config files from different places * (the part about different places is still unfinished) - * + * * @author Good Sign */ public enum ConfigBase { @@ -126,7 +126,7 @@ public boolean equals(Object obj) { /** * Get file / paths combinations - * + * * @return a one or more path to the file */ private String[] getPaths() { @@ -192,4 +192,4 @@ public static List getFiles() { return ret; } -} +} \ No newline at end of file diff --git a/src/doom/ConfigManager.java b/src/doom/ConfigManager.java index 9897b67..669b902 100644 --- a/src/doom/ConfigManager.java +++ b/src/doom/ConfigManager.java @@ -1,279 +1,279 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package doom; - -import doom.ConfigBase.Files; -import java.nio.file.StandardOpenOption; -import java.util.Arrays; -import java.util.EnumMap; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Pattern; -import m.Settings; -import static m.Settings.SETTINGS_MAP; -import mochadoom.Loggers; -import utils.ParseString; -import utils.QuoteType; -import utils.ResourceIO; - -/** - * Loads and saves game cfg files - * - * @author Good Sign - */ -public class ConfigManager { - - private static final Logger LOGGER = Loggers.getLogger(ConfigManager.class.getName()); - - private static final Pattern SPLITTER = Pattern.compile("[ \t\n\r\f]+"); - - private final List configFiles = ConfigBase.getFiles(); - private final EnumMap configMap = new EnumMap<>(Settings.class); - - public enum UpdateStatus { - UNCHANGED, UPDATED, INVALID; - } - - public ConfigManager() { - LoadDefaults(); - } - - public UpdateStatus update(final Settings setting, final String value) { - if (setting.valueType == String.class) { - return setting.hasChange(!Objects.equals(configMap.put(setting, value), value)); - } else if (setting.valueType == Character.class - || setting.valueType == Long.class - || setting.valueType == Integer.class - || setting.valueType == Boolean.class) { - final Object parse = ParseString.parseString(value); - if (setting.valueType.isInstance(parse)) { - return setting.hasChange(!Objects.equals(configMap.put(setting, parse), parse)); - } - } else if (setting.valueType.getSuperclass() == Enum.class) { - // Enum search by name - @SuppressWarnings({"unchecked", "rawtypes"}) - final Object enumerated = Enum.valueOf((Class) setting.valueType, value); - return setting.hasChange(!Objects.equals(configMap.put(setting, enumerated), enumerated)); - } - - return UpdateStatus.INVALID; - } - - public UpdateStatus update(final Settings setting, final Object value) { - if (setting.valueType == String.class) { - return setting.hasChange(!Objects.equals(configMap.put(setting, value.toString()), value.toString())); - } - - return UpdateStatus.INVALID; - } - - public UpdateStatus update(final Settings setting, final int value) { - if (setting.valueType == Integer.class) { - return setting.hasChange(!Objects.equals(configMap.put(setting, value), value)); - } else if (setting.valueType == String.class) { - final String valStr = Integer.toString(value); - return setting.hasChange(!Objects.equals(configMap.put(setting, valStr), valStr)); - } else if (setting.valueType.getSuperclass() == Enum.class) { - final Object[] enumValues = setting.valueType.getEnumConstants(); - if (value >= 0 && value < enumValues.length) { - return setting.hasChange(!Objects.equals(configMap.put(setting, enumValues[value]), enumValues[value])); - } - } - - return UpdateStatus.INVALID; - } - - public UpdateStatus update(final Settings setting, final long value) { - if (setting.valueType == Long.class) { - return setting.hasChange(!Objects.equals(configMap.put(setting, value), value)); - } else if (setting.valueType == String.class) { - final String valStr = Long.toString(value); - return setting.hasChange(!Objects.equals(configMap.put(setting, valStr), valStr)); - } - - return UpdateStatus.INVALID; - } - - public UpdateStatus update(final Settings setting, final double value) { - if (setting.valueType == Double.class) { - return setting.hasChange(!Objects.equals(configMap.put(setting, value), value)); - } else if (setting.valueType == String.class) { - final String valStr = Double.toString(value); - return setting.hasChange(!Objects.equals(configMap.put(setting, valStr), valStr)); - } - - return UpdateStatus.INVALID; - } - - public UpdateStatus update(final Settings setting, final char value) { - if (setting.valueType == Character.class) { - return setting.hasChange(!Objects.equals(configMap.put(setting, value), value)); - } else if (setting.valueType == String.class) { - final String valStr = Character.toString(value); - return setting.hasChange(!Objects.equals(configMap.put(setting, valStr), valStr)); - } - - return UpdateStatus.INVALID; - } - - public UpdateStatus update(final Settings setting, final boolean value) { - if (setting.valueType == Boolean.class) { - return setting.hasChange(!Objects.equals(configMap.put(setting, value), value)); - } else if (setting.valueType == String.class) { - final String valStr = Boolean.toString(value); - return setting.hasChange(!Objects.equals(configMap.put(setting, valStr), valStr)); - } - - return UpdateStatus.INVALID; - } - - private String export(final Settings setting) { - return setting.quoteType().map(qt -> { - return new StringBuilder() - .append(setting.name()) - .append("\t\t") - .append(qt.quoteChar) - .append(configMap.get(setting)) - .append(qt.quoteChar) - .toString(); - }).orElseGet(() -> { - return new StringBuilder() - .append(setting.name()) - .append("\t\t") - .append(configMap.get(setting)) - .toString(); - }); - } - - public boolean equals(final Settings setting, final Object obj) { - return obj.equals(configMap.get(setting)); - } - - @SuppressWarnings("unchecked") - public T getValue(final Settings setting, final Class valueType) { - if (setting.valueType == valueType) { - return (T) configMap.get(setting); - } else if (valueType == String.class) { - return (T) configMap.get(setting).toString(); - } else if (setting.valueType == String.class) { - if (valueType == Character.class - || valueType == Long.class - || valueType == Integer.class - || valueType == Boolean.class) { - final Object parse = ParseString.parseString(configMap.get(setting).toString()); - if (valueType.isInstance(parse)) { - return (T) parse; - } - } - } else if (valueType == Integer.class && setting.valueType.getSuperclass() == Enum.class) { - return (T) ((Integer) ((Enum) configMap.get(setting)).ordinal()); - } - - throw new IllegalArgumentException("Unsupported cast: " + setting.valueType + " to " + valueType); - } - - public void SaveDefaults() { - SETTINGS_MAP.forEach((file, settings) -> { - // skip writing settings which are not part of the loaded config files, - // this helps to not overwrite default.cfg with empty content in case we're using the -config argument - if (!this.configFiles.contains(file)) { - return; - } - - // do not write unless there is changes - if (!file.changed) { - return; - } - - // choose existing config file or create one in current working directory - final ResourceIO rio = file.firstValidPathIO().orElseGet(file::workDirIO); - final Iterator it = settings.stream().sorted(file.comparator).iterator(); - if (rio.writeLines(() -> { - if (it.hasNext()) { - return export(it.next()); - } - - return null; - }, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) { - // we wrote successfully - so it will not try to write it again, unless something really change - file.changed = false; - } - }); - } - - /** - * Handles variables and settings from default.cfg and other config files - * They can be load even earlier then other systems - */ - private void LoadDefaults() { - Arrays.stream(Settings.values()) - .forEach(setting -> { - configMap.put(setting, setting.defaultValue); - }); - - LOGGER.log(Level.INFO, "M_LoadDefaults: Load system defaults."); - this.configFiles.forEach(file -> { - final Optional maybeRIO = file.firstValidPathIO(); - - /** - * Each file successfully read marked as not changed, and as changed - those who don't exist - * - */ - file.changed = !(maybeRIO.isPresent() && readFoundConfig(file, maybeRIO.get())); - }); - - // create files who don't exist (it will skip those with changed = false - all who exists) - SaveDefaults(); - } - - private boolean readFoundConfig(Files file, ResourceIO rio) { - LOGGER.log(Level.INFO, String.format("M_LoadDefaults: Using config %s.", rio.getFileame())); - if (rio.readLines(line -> { - final String[] split = SPLITTER.split(line, 2); - if (split.length < 2) { - return; - } - - final String name = split[0]; - try { - final Settings setting = Settings.valueOf(name); - final String value = setting.quoteType() - .filter(qt -> qt == QuoteType.DOUBLE) - .map(qt -> qt.unQuote(split[1])) - .orElse(split[1]); - - if (update(setting, value) == UpdateStatus.INVALID) { - LOGGER.log(Level.WARNING, String.format("WARNING: invalid config value for: %s in %s", name, rio.getFileame())); - } else { - setting.rebase(file); - } - } catch (IllegalArgumentException ex) { - } - })) { - return true; // successfully read a file - } - - // Something went bad, but this won't destroy successfully read values, though. - LOGGER.log(Level.SEVERE, String.format("Can't read the settings file %s", rio.getFileame())); - return false; - } - -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package doom; + +import doom.ConfigBase.Files; +import java.nio.file.StandardOpenOption; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; +import m.Settings; +import static m.Settings.SETTINGS_MAP; +import mochadoom.Loggers; +import utils.ParseString; +import utils.QuoteType; +import utils.ResourceIO; + +/** + * Loads and saves game cfg files + * + * @author Good Sign + */ +public class ConfigManager { + + private static final Logger LOGGER = Loggers.getLogger(ConfigManager.class.getName()); + + private static final Pattern SPLITTER = Pattern.compile("[ \t\n\r\f]+"); + + private final List configFiles = ConfigBase.getFiles(); + private final EnumMap configMap = new EnumMap<>(Settings.class); + + public enum UpdateStatus { + UNCHANGED, UPDATED, INVALID; + } + + public ConfigManager() { + LoadDefaults(); + } + + public UpdateStatus update(final Settings setting, final String value) { + if (setting.valueType == String.class) { + return setting.hasChange(!Objects.equals(configMap.put(setting, value), value)); + } else if (setting.valueType == Character.class + || setting.valueType == Long.class + || setting.valueType == Integer.class + || setting.valueType == Boolean.class) { + final Object parse = ParseString.parseString(value); + if (setting.valueType.isInstance(parse)) { + return setting.hasChange(!Objects.equals(configMap.put(setting, parse), parse)); + } + } else if (setting.valueType.getSuperclass() == Enum.class) { + // Enum search by name + @SuppressWarnings({"unchecked", "rawtypes"}) + final Object enumerated = Enum.valueOf((Class) setting.valueType, value); + return setting.hasChange(!Objects.equals(configMap.put(setting, enumerated), enumerated)); + } + + return UpdateStatus.INVALID; + } + + public UpdateStatus update(final Settings setting, final Object value) { + if (setting.valueType == String.class) { + return setting.hasChange(!Objects.equals(configMap.put(setting, value.toString()), value.toString())); + } + + return UpdateStatus.INVALID; + } + + public UpdateStatus update(final Settings setting, final int value) { + if (setting.valueType == Integer.class) { + return setting.hasChange(!Objects.equals(configMap.put(setting, value), value)); + } else if (setting.valueType == String.class) { + final String valStr = Integer.toString(value); + return setting.hasChange(!Objects.equals(configMap.put(setting, valStr), valStr)); + } else if (setting.valueType.getSuperclass() == Enum.class) { + final Object[] enumValues = setting.valueType.getEnumConstants(); + if (value >= 0 && value < enumValues.length) { + return setting.hasChange(!Objects.equals(configMap.put(setting, enumValues[value]), enumValues[value])); + } + } + + return UpdateStatus.INVALID; + } + + public UpdateStatus update(final Settings setting, final long value) { + if (setting.valueType == Long.class) { + return setting.hasChange(!Objects.equals(configMap.put(setting, value), value)); + } else if (setting.valueType == String.class) { + final String valStr = Long.toString(value); + return setting.hasChange(!Objects.equals(configMap.put(setting, valStr), valStr)); + } + + return UpdateStatus.INVALID; + } + + public UpdateStatus update(final Settings setting, final double value) { + if (setting.valueType == Double.class) { + return setting.hasChange(!Objects.equals(configMap.put(setting, value), value)); + } else if (setting.valueType == String.class) { + final String valStr = Double.toString(value); + return setting.hasChange(!Objects.equals(configMap.put(setting, valStr), valStr)); + } + + return UpdateStatus.INVALID; + } + + public UpdateStatus update(final Settings setting, final char value) { + if (setting.valueType == Character.class) { + return setting.hasChange(!Objects.equals(configMap.put(setting, value), value)); + } else if (setting.valueType == String.class) { + final String valStr = Character.toString(value); + return setting.hasChange(!Objects.equals(configMap.put(setting, valStr), valStr)); + } + + return UpdateStatus.INVALID; + } + + public UpdateStatus update(final Settings setting, final boolean value) { + if (setting.valueType == Boolean.class) { + return setting.hasChange(!Objects.equals(configMap.put(setting, value), value)); + } else if (setting.valueType == String.class) { + final String valStr = Boolean.toString(value); + return setting.hasChange(!Objects.equals(configMap.put(setting, valStr), valStr)); + } + + return UpdateStatus.INVALID; + } + + private String export(final Settings setting) { + return setting.quoteType().map(qt -> { + return new StringBuilder() + .append(setting.name()) + .append("\t\t") + .append(qt.quoteChar) + .append(configMap.get(setting)) + .append(qt.quoteChar) + .toString(); + }).orElseGet(() -> { + return new StringBuilder() + .append(setting.name()) + .append("\t\t") + .append(configMap.get(setting)) + .toString(); + }); + } + + public boolean equals(final Settings setting, final Object obj) { + return obj.equals(configMap.get(setting)); + } + + @SuppressWarnings("unchecked") + public T getValue(final Settings setting, final Class valueType) { + if (setting.valueType == valueType) { + return (T) configMap.get(setting); + } else if (valueType == String.class) { + return (T) configMap.get(setting).toString(); + } else if (setting.valueType == String.class) { + if (valueType == Character.class + || valueType == Long.class + || valueType == Integer.class + || valueType == Boolean.class) { + final Object parse = ParseString.parseString(configMap.get(setting).toString()); + if (valueType.isInstance(parse)) { + return (T) parse; + } + } + } else if (valueType == Integer.class && setting.valueType.getSuperclass() == Enum.class) { + return (T) ((Integer) ((Enum) configMap.get(setting)).ordinal()); + } + + throw new IllegalArgumentException("Unsupported cast: " + setting.valueType + " to " + valueType); + } + + public void SaveDefaults() { + SETTINGS_MAP.forEach((file, settings) -> { + // skip writing settings which are not part of the loaded config files, + // this helps to not overwrite default.cfg with empty content in case we're using the -config argument + if (!this.configFiles.contains(file)) { + return; + } + + // do not write unless there is changes + if (!file.changed) { + return; + } + + // choose existing config file or create one in current working directory + final ResourceIO rio = file.firstValidPathIO().orElseGet(file::workDirIO); + final Iterator it = settings.stream().sorted(file.comparator).iterator(); + if (rio.writeLines(() -> { + if (it.hasNext()) { + return export(it.next()); + } + + return null; + }, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) { + // we wrote successfully - so it will not try to write it again, unless something really change + file.changed = false; + } + }); + } + + /** + * Handles variables and settings from default.cfg and other config files + * They can be load even earlier then other systems + */ + private void LoadDefaults() { + Arrays.stream(Settings.values()) + .forEach(setting -> { + configMap.put(setting, setting.defaultValue); + }); + + LOGGER.log(Level.INFO, "M_LoadDefaults: Load system defaults."); + this.configFiles.forEach(file -> { + final Optional maybeRIO = file.firstValidPathIO(); + + /** + * Each file successfully read marked as not changed, and as changed - those who don't exist + * + */ + file.changed = !(maybeRIO.isPresent() && readFoundConfig(file, maybeRIO.get())); + }); + + // create files who don't exist (it will skip those with changed = false - all who exists) + SaveDefaults(); + } + + private boolean readFoundConfig(Files file, ResourceIO rio) { + LOGGER.log(Level.INFO, String.format("M_LoadDefaults: Using config %s.", rio.getFileame())); + if (rio.readLines(line -> { + final String[] split = SPLITTER.split(line, 2); + if (split.length < 2) { + return; + } + + final String name = split[0]; + try { + final Settings setting = Settings.valueOf(name); + final String value = setting.quoteType() + .filter(qt -> qt == QuoteType.DOUBLE) + .map(qt -> qt.unQuote(split[1])) + .orElse(split[1]); + + if (update(setting, value) == UpdateStatus.INVALID) { + LOGGER.log(Level.WARNING, String.format("WARNING: invalid config value for: %s in %s", name, rio.getFileame())); + } else { + setting.rebase(file); + } + } catch (IllegalArgumentException ex) { + } + })) { + return true; // successfully read a file + } + + // Something went bad, but this won't destroy successfully read values, though. + LOGGER.log(Level.SEVERE, String.format("Can't read the settings file %s", rio.getFileame())); + return false; + } + +} \ No newline at end of file diff --git a/src/doom/DoomContext.java b/src/doom/DoomContext.java index 9682c58..a8e0b61 100644 --- a/src/doom/DoomContext.java +++ b/src/doom/DoomContext.java @@ -1,36 +1,36 @@ package doom; -/** Since a lot of stuff requires shared/global access to +/** Since a lot of stuff requires shared/global access to * the WadLoader, the Renderer, the Video system etc. and * we're trying to depart from the global/static mentality, * a common sharing is required. Ideally, this would be a perfect * example of where multiple inheritance could be adopted, since most * stuff needs to share this status anyway. The next best thing is * to have local references of any used fields in the classes that use them. - * + * * About generics: T refers to the type of the graphics resources, and is * currently byte[], as all graphics resources are 8-bit indexed. There are - * no plans that this will change anytime soon. Some classes should allow - * different types in theory, but it would be too complex and pointless to + * no plans that this will change anytime soon. Some classes should allow + * different types in theory, but it would be too complex and pointless to * make everything fully compliant at the moment. - * - * V refers to the type of DISPLAY, and can be 8-bit (byte[]), 16-bit (short[] - * for HiColor and lesser modes such as ARGB4444, etc.), and, in the future, + * + * V refers to the type of DISPLAY, and can be 8-bit (byte[]), 16-bit (short[] + * for HiColor and lesser modes such as ARGB4444, etc.), and, in the future, * int[] (truecolor). - * - * The general approach is sharing as much code as possible between different + * + * The general approach is sharing as much code as possible between different * implementations (e.g. rendering code), and only specialize methods/classes when * the abstraction of generics isn't enough (typically, when you have to assign * directly to primitive arrays or deal with primitive method signatures). - * + * * Classes that have specialized code for indexed and hicolor modes should be top-level * classes in their package, and contain two nested, static, extending classes called - * Indexed and HiColor e.g. new MyClass.Indexed() and new MyClass.HiColor(), while any common + * Indexed and HiColor e.g. new MyClass.Indexed() and new MyClass.HiColor(), while any common * code should reside in MyClass. - * + * * @author velktron * */ public final class DoomContext { -} +} \ No newline at end of file diff --git a/src/doom/DoomMain.java b/src/doom/DoomMain.java index 68ab172..030d3a7 100644 --- a/src/doom/DoomMain.java +++ b/src/doom/DoomMain.java @@ -1,4009 +1,4009 @@ -package doom; - -import automap.IAutoMap; -import static data.Defines.BACKUPTICS; -import static data.Defines.BTS_PAUSE; -import static data.Defines.BTS_SAVEGAME; -import static data.Defines.BTS_SAVEMASK; -import static data.Defines.BTS_SAVESHIFT; -import static data.Defines.BT_ATTACK; -import static data.Defines.BT_CHANGE; -import static data.Defines.BT_SPECIAL; -import static data.Defines.BT_SPECIALMASK; -import static data.Defines.BT_USE; -import static data.Defines.BT_WEAPONSHIFT; -import static data.Defines.JAVARANDOM_MASK; -import static data.Defines.NORMALUNIX; -import static data.Defines.NUMWEAPONS; -import static data.Defines.PST_DEAD; -import static data.Defines.PST_LIVE; -import static data.Defines.PST_REBORN; -import static data.Defines.PU_CACHE; -import static data.Defines.PU_STATIC; -import static data.Defines.SKYFLATNAME; -import static data.Defines.TICRATE; -import static data.Defines.TOCENTER; -import static data.Defines.VERSION; -import static data.Limits.MAXEVENTS; -import static data.Limits.MAXINT; -import static data.Limits.MAXNETNODES; -import static data.Limits.MAXPLAYERS; -import data.Tables; -import static data.Tables.ANG45; -import static data.Tables.ANGLETOFINESHIFT; -import static data.Tables.finecosine; -import static data.Tables.finesine; -import data.dstrings; -import static data.dstrings.DEVMAPS; -import static data.dstrings.SAVEGAMENAME; -import static data.info.mobjinfo; -import static data.info.states; -import data.mapthing_t; -import data.mobjtype_t; -import data.sounds.musicenum_t; -import data.sounds.sfxenum_t; -import defines.DoomVersion; -import defines.GameMission_t; -import defines.GameMode; -import defines.Language_t; -import defines.gamestate_t; -import static defines.gamestate_t.GS_DEMOSCREEN; -import static defines.gamestate_t.GS_FINALE; -import static defines.gamestate_t.GS_INTERMISSION; -import static defines.gamestate_t.GS_LEVEL; -import static defines.gamestate_t.GS_MINUS_ONE; -import defines.skill_t; -import defines.statenum_t; -import demo.IDemoTicCmd; -import demo.VanillaDoomDemo; -import demo.VanillaTiccmd; -import static doom.NetConsts.CMD_GET; -import static doom.NetConsts.CMD_SEND; -import static doom.NetConsts.DOOMCOM_ID; -import static doom.NetConsts.NCMD_CHECKSUM; -import static doom.NetConsts.NCMD_EXIT; -import static doom.NetConsts.NCMD_KILL; -import static doom.NetConsts.NCMD_RETRANSMIT; -import static doom.NetConsts.NCMD_SETUP; -import doom.SourceCode.CauseOfDesyncProbability; -import doom.SourceCode.D_Main; -import static doom.SourceCode.D_Main.D_DoomLoop; -import static doom.SourceCode.D_Main.D_ProcessEvents; -import doom.SourceCode.G_Game; -import static doom.SourceCode.G_Game.G_BeginRecording; -import static doom.SourceCode.G_Game.G_BuildTiccmd; -import static doom.SourceCode.G_Game.G_CheckSpot; -import static doom.SourceCode.G_Game.G_DeathMatchSpawnPlayer; -import static doom.SourceCode.G_Game.G_DoCompleted; -import static doom.SourceCode.G_Game.G_DoLoadGame; -import static doom.SourceCode.G_Game.G_DoLoadLevel; -import static doom.SourceCode.G_Game.G_DoNewGame; -import static doom.SourceCode.G_Game.G_DoPlayDemo; -import static doom.SourceCode.G_Game.G_DoReborn; -import static doom.SourceCode.G_Game.G_DoSaveGame; -import static doom.SourceCode.G_Game.G_InitNew; -import static doom.SourceCode.G_Game.G_Responder; -import static doom.SourceCode.G_Game.G_Ticker; -import static doom.englsh.D_CDROM; -import static doom.englsh.D_DEVSTR; -import static doom.englsh.GGSAVED; -import static doom.englsh.SCREENSHOT; -import static doom.evtype_t.ev_joystick; -import static doom.evtype_t.ev_keydown; -import static doom.evtype_t.ev_keyup; -import static doom.evtype_t.ev_mouse; -import static doom.gameaction_t.ga_completed; -import static doom.gameaction_t.ga_failure; -import static doom.gameaction_t.ga_loadgame; -import static doom.gameaction_t.ga_loadlevel; -import static doom.gameaction_t.ga_newgame; -import static doom.gameaction_t.ga_nothing; -import static doom.gameaction_t.ga_playdemo; -import static doom.gameaction_t.ga_savegame; -import static doom.gameaction_t.ga_screenshot; -import static doom.gameaction_t.ga_victory; -import static doom.gameaction_t.ga_worlddone; -import f.EndLevel; -import f.Finale; -import f.Wiper; -import g.Signals; -import static g.Signals.ScanCode.SC_CAPSLK; -import static g.Signals.ScanCode.SC_DOWN; -import static g.Signals.ScanCode.SC_ESCAPE; -import static g.Signals.ScanCode.SC_F12; -import static g.Signals.ScanCode.SC_LALT; -import static g.Signals.ScanCode.SC_LCTRL; -import static g.Signals.ScanCode.SC_LEFT; -import static g.Signals.ScanCode.SC_LSHIFT; -import static g.Signals.ScanCode.SC_NUMKEY2; -import static g.Signals.ScanCode.SC_NUMKEY4; -import static g.Signals.ScanCode.SC_NUMKEY6; -import static g.Signals.ScanCode.SC_NUMKEY8; -import static g.Signals.ScanCode.SC_PAUSE; -import static g.Signals.ScanCode.SC_RALT; -import static g.Signals.ScanCode.SC_RCTRL; -import static g.Signals.ScanCode.SC_RIGHT; -import static g.Signals.ScanCode.SC_RSHIFT; -import static g.Signals.ScanCode.SC_SEMICOLON; -import static g.Signals.ScanCode.SC_UP; -import hu.HU; -import i.DiskDrawer; -import i.DoomSystem; -import i.IDiskDrawer; -import i.IDoomSystem; -import i.Strings; -import java.awt.Rectangle; -import java.io.BufferedInputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.util.Arrays; -import java.util.Objects; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import m.DelegateRandom; -import m.IDoomMenu; -import m.Menu; -import m.MenuMisc; -import m.Settings; -import static m.fixed_t.FRACBITS; -import static m.fixed_t.MAPFRACUNIT; -import mochadoom.Engine; -import mochadoom.Loggers; -import n.DoomSystemNetworking; -import n.DummyNetworkDriver; -import p.AbstractLevelLoader; -import p.ActionFunctions; -import p.BoomLevelLoader; -import p.mobj_t; -import rr.ISpriteManager; -import rr.SceneRenderer; -import rr.SpriteManager; -import rr.TextureManager; -import rr.ViewVars; -import rr.patch_t; -import rr.subsector_t; -import s.IDoomSound; -import s.IMusic; -import s.ISoundDriver; -import savegame.IDoomSaveGame; -import savegame.IDoomSaveGameHeader; -import savegame.VanillaDSG; -import savegame.VanillaDSGHeader; -import st.AbstractStatusBar; -import st.StatusBar; -import timing.ITicker; -import timing.MilliTicker; -import utils.C2JUtils; -import static utils.C2JUtils.eval; -import static utils.C2JUtils.flags; -import static utils.C2JUtils.memcpy; -import static utils.C2JUtils.memset; -import v.DoomGraphicSystem; -import v.renderers.BppMode; -import static v.renderers.DoomScreen.FG; -import v.renderers.RendererFactory; -import v.scale.VideoScale; -import v.scale.VisualSettings; -import w.IWadLoader; -import w.WadLoader; - -// Emacs style mode select -*- Java -*- -//----------------------------------------------------------------------------- -// -// $Id: DoomMain.java,v 1.109 2012/11/06 16:04:58 velktron Exp $ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// DESCRIPTION: -// DOOM main program (D_DoomMain) and game loop (D_DoomLoop), -// plus functions to determine game mode (shareware, registered), -// parse command line parameters, configure game parameters (turbo), -// and call the startup functions. -// -// In Mocha Doom, this was unified with d_game and doomstat.c -// -//----------------------------------------------------------------------------- -@SuppressWarnings({ - "UseOfSystemOutOrSystemErr", - "MalformedFormatString", - "CallToPrintStackTrace", - "override", - "StringBufferMayBeStringBuilder" -}) -public class DoomMain extends DoomStatus implements IDoomGameNetworking, IDoomGame, IDoom { - - private static final Logger LOGGER = Loggers.getLogger(DoomMain.class.getName()); - - public static final String RCSID = "$Id: DoomMain.java,v 1.109 2012/11/06 16:04:58 velktron Exp $"; - - // - // EVENT HANDLING - // - // Events are asynchronous inputs generally generated by the game user. - // Events can be discarded if no responder claims them - // - public final event_t[] events = new event_t[MAXEVENTS]; - public int eventhead; - public int eventtail; - - /** - * D_PostEvent - * Called by the I/O functions when input is detected - */ - public void PostEvent(event_t ev) { - /** - * Do not pollute DOOM's internal event queue - clear keys there - * - Good Sign 2017/04/24 - */ - if (ev == event_t.CANCEL_KEYS) { - // PAINFULLY and FORCEFULLY clear the buttons. - memset(gamekeydown, false, gamekeydown.length); - keysCleared = true; - return; // Nothing more to do here. - } - - events[eventhead] = ev; - eventhead = (++eventhead) & (MAXEVENTS - 1); - } - - /** - * D_ProcessEvents - * Send all the events of the given timestamp down the responder chain - */ - @D_Main.C(D_ProcessEvents) - public void ProcessEvents() { - // IF STORE DEMO, DO NOT ACCEPT INPUT - if ((isCommercial())) { - W_CheckNumForName: - { - if ((wadLoader.CheckNumForName("MAP01") < 0)) { - return; - } - } - } - - for (; eventtail != eventhead; eventtail = (++eventtail) & (MAXEVENTS - 1)) { - final event_t ev = events[eventtail]; - ev.withMouse(event_t.mouseevent_t::processedNotify); - - M_Responder: - { - if (menu.Responder(ev)) { - continue; // menu ate the event - } - } - - G_Responder: - { - Responder(ev); - } - } - } - - // "static" to Display, don't move. - private boolean viewactivestate = false; - private boolean menuactivestate = false; - private boolean inhelpscreensstate = false; - private boolean fullscreen = false; - private gamestate_t oldgamestate = GS_MINUS_ONE; - private int borderdrawcount; - - /** - * D_Display - * draw current display, possibly wiping it from the previous - * @throws IOException - */ - public void Display() throws IOException { - int nowtime; - int tics; - int wipestart; - int y; - boolean done; - boolean wipe; - boolean redrawsbar; - - // for comparative timing / profiling - if (nodrawers) { - return; - } - redrawsbar = false; - - // change the view size if needed - if (sceneRenderer.getSetSizeNeeded()) { - sceneRenderer.ExecuteSetViewSize(); - // force background redraw - oldgamestate = GS_MINUS_ONE; - borderdrawcount = 3; - } - - // save the current screen if about to wipe - wipe = (gamestate != wipegamestate); - if (wipe) { - wiper.StartScreen(0, 0, vs.getScreenWidth(), vs.getScreenHeight()); - } - - if (gamestate == GS_LEVEL && eval(gametic)) { - headsUp.Erase(); - } - - // do buffered drawing - switch (gamestate) { - case GS_LEVEL: - if (!eval(gametic)) { - break; - } - - if (automapactive) { - autoMap.Drawer(); - } - - if (wipe - || (!sceneRenderer.isFullHeight() && fullscreen) - || (inhelpscreensstate && !inhelpscreens) - || (diskDrawer.justDoneReading())) { - redrawsbar = true; // just put away the help screen - } - statusBar.Drawer(sceneRenderer.isFullHeight(), redrawsbar); - fullscreen = sceneRenderer.isFullHeight(); - break; - case GS_INTERMISSION: - endLevel.Drawer(); - break; - case GS_FINALE: - finale.Drawer(); - break; - case GS_DEMOSCREEN: - PageDrawer(); - break; - default: - break; - } - - // draw the view directly - if (gamestate == GS_LEVEL && !automapactive && eval(gametic)) { - if (flashing_hom) { - graphicSystem.FillRect(FG, new Rectangle(view.getViewWindowX(), view.getViewWindowY(), - view.getScaledViewWidth(), view.getScaledViewHeight()), gametic % 256); - } - sceneRenderer.RenderPlayerView(players[displayplayer]); - } - - // Automap was active, update only HU. - if (gamestate == GS_LEVEL && eval(gametic)) { - headsUp.Drawer(); - } - - // clean up border stuff - if (gamestate != oldgamestate && gamestate != GS_LEVEL) { - graphicSystem.setPalette(0); - } - - // see if the border needs to be initially drawn - if (gamestate == GS_LEVEL && oldgamestate != GS_LEVEL) { - // view was not active - viewactivestate = false; - // draw the pattern into the back screen - sceneRenderer.FillBackScreen(); - } - - // see if the border needs to be updated to the screen - if (gamestate == GS_LEVEL && !automapactive && !sceneRenderer.isFullScreen()) { - if (menuactive || menuactivestate || !viewactivestate) { - borderdrawcount = 3; - } - - if (eval(borderdrawcount)) { - // erase old menu stuff - sceneRenderer.DrawViewBorder(); - borderdrawcount--; - } - } - - menuactivestate = menuactive; - viewactivestate = viewactive; - inhelpscreensstate = inhelpscreens; - oldgamestate = wipegamestate = gamestate; - - // draw pause pic - if (paused) { - if (automapactive) { - y = 4 * graphicSystem.getScalingY(); - } else { - y = view.getViewWindowY() + 4 * graphicSystem.getScalingY(); - } - - final patch_t pause = wadLoader.CachePatchName("M_PAUSE", PU_CACHE); - graphicSystem.DrawPatchCenteredScaled(FG, pause, vs, y, DoomGraphicSystem.V_NOSCALESTART); - } - - // menus go directly to the screen - menu.Drawer(); // menu is drawn even on top of everything - NetUpdate(); // send out any new accumulation - - // Disk access goes after everything. - diskDrawer.Drawer(); - - // normal update - if (!wipe) { - //System.out.print("Tick "+gametic+"\t"); - //System.out.print(players[0]); - Engine.updateFrame(); // page flip or blit buffer - return; - } - - // wipe update. At this point, AT LEAST one frame of the game must have been - // rendered for this to work. 22/5/2011: Fixed a vexing bug with the wiper. - // Jesus Christ with a Super Shotgun! - wiper.EndScreen(0, 0, vs.getScreenWidth(), vs.getScreenHeight()); - - wipestart = ticker.GetTime() - 1; - - do { - do { - nowtime = ticker.GetTime(); - tics = nowtime - wipestart; - } while (tics == 0); // Wait until a single tic has passed. - wipestart = nowtime; - Wiper.Wipe wipeType = CM.equals(Settings.scale_melt, Boolean.TRUE) - ? Wiper.Wipe.ScaledMelt : Wiper.Wipe.Melt; - - done = wiper.ScreenWipe(wipeType, 0, 0, vs.getScreenWidth(), vs.getScreenHeight(), tics); - soundDriver.UpdateSound(); - soundDriver.SubmitSound(); // update sounds after one wipe tic. - menu.Drawer(); // menu is drawn even on top of wipes - Engine.updateFrame(); // page flip or blit buffer - } while (!done); - } - - /** - * To be able to debug vanilla incompatibilitites, the DoomLoop - * and all that is called by it that relates to the Loop itself, - * the ticks, game object modifications, mode changes and so on, - * ***MUST*** be preceded by a label, containing original - * underscored naming of the method in Doom Source Code. - * - * Remember the label blocks will retain their name even in case - * of *automated refactoring*, thus if you rename some method - * and update it throughout the whole codebase, the named label - * will still be named the same underscored original method name - * - * Do it the most verbose way you can - preserving both, or all - * brackets of all blocks containing and contained in the label, - * and the brackets of the label itself, with one exception: - * - * If there is no more function to do the task was given to the - * function in original Doom Source Code, the label stull ***MUST*** - * be present, just type a semicolon to end it without actions. - * The syntax is short and displays clearly that nothing is done. - * - Good Sign 2017/04/26 - * - * D_DoomLoop() - * Not a globally visible function, - * just included for source reference, - * called by D_DoomMain, never exits. - * Manages timing and IO, - * calls all ?_Responder, ?_Ticker, and ?_Drawer, - * calls I_GetTime, I_StartFrame, and I_StartTic - * @throws IOException - */ - @D_Main.C(D_DoomLoop) - public void DoomLoop() throws IOException { - if (demorecording) { - G_BeginRecording: - { - BeginRecording(); - } - } - - M_CheckParm: - { - if (cVarManager.bool(CommandVariable.DEBUGFILE)) { - String filename = "debug" + consoleplayer + ".txt"; - LOGGER.log(Level.INFO, String.format("Debug output to: %s", filename)); - try { - debugfile = new OutputStreamWriter(new FileOutputStream(filename)); - } catch (FileNotFoundException e) { - LOGGER.log(Level.SEVERE, "Couldn't open debugfile.", e); - } - } - } - - I_InitGraphics: - { - view = sceneRenderer.getView(); - } - - while (true) { - // frame syncronous IO operations - I_StartFrame: - ; - - // process one or more tics - if (singletics) { - I_StartTic: - ; - D_ProcessEvents: - { - ProcessEvents(); - } - G_BuildTiccmd: - { - BuildTiccmd(netcmds[consoleplayer][maketic % BACKUPTICS]); - } - if (advancedemo) { - D_DoAdvanceDemo: - { - DoAdvanceDemo(); - } - } - M_Ticker: - { - menu.Ticker(); - } - G_Ticker: - { - Ticker(); - } - gametic++; - maketic++; - } else { - gameNetworking.TryRunTics(); // will run at least one tic (in NET) - } - S_UpdateSounds: - { - doomSound.UpdateSounds(players[consoleplayer].mo); // move positional sounds - } - D_Display: - { // Update display, next frame, with current state. - Display(); - } - //#ifndef SNDSERV - // Sound mixing for the buffer is snychronous. - soundDriver.UpdateSound(); - //#endif - // Synchronous sound output is explicitly called. - //#ifndef SNDINTR - // Update sound output. - soundDriver.SubmitSound(); - //#endif - } - } - - // To keep an "eye" on the renderer. - protected ViewVars view; - - // - // DEMO LOOP - // - int demosequence; - int pagetic; - String pagename; - - /** - * D_PageTicker - * Handles timing for warped projection - */ - public final void PageTicker() { - if (--pagetic < 0) { - AdvanceDemo(); - } - } - - /** - * D_PageDrawer - */ - public final void PageDrawer() { - // FIXME: this check wasn't necessary in vanilla, since pagename was - // guaranteed(?) not to be null or had a safe default value. - if (pagename != null) { - graphicSystem.DrawPatchScaled(FG, wadLoader.CachePatchName(pagename, PU_CACHE), vs, 0, 0, DoomGraphicSystem.V_SAFESCALE); - } - } - - /** - * D_AdvanceDemo - * Called after each demo or intro demosequence finishes - */ - public void AdvanceDemo() { - advancedemo = true; - } - - /** - * This cycles through the demo sequences. - * FIXME - version dependant demo numbers? - */ - public void DoAdvanceDemo() { - players[consoleplayer].playerstate = PST_LIVE; // not reborn - advancedemo = false; - usergame = false; // no save / end game here - paused = false; - gameaction = ga_nothing; - - if (isRetail()) // Allows access to a 4th demo. - { - demosequence = (demosequence + 1) % 7; - } else { - demosequence = (demosequence + 1) % 6; - } - - switch (demosequence) { - case 0: - if (isCommercial()) { - pagetic = 35 * 11; - } else { - pagetic = 170; - } - gamestate = GS_DEMOSCREEN; - - if (wadLoader.CheckNumForName("TITLEPIC") != -1) { - pagename = "TITLEPIC"; - } else { - if (wadLoader.CheckNumForName("DMENUPIC") != -1) { - pagename = "DMENUPIC"; - } - } - - if (isCommercial()) { - doomSound.StartMusic(musicenum_t.mus_dm2ttl); - } else { - doomSound.StartMusic(musicenum_t.mus_intro); - } - break; - case 1: - DeferedPlayDemo("demo1"); - break; - case 2: - pagetic = 200; - gamestate = GS_DEMOSCREEN; - pagename = "CREDIT"; - break; - case 3: - DeferedPlayDemo("demo2"); - break; - case 4: - gamestate = GS_DEMOSCREEN; - if (isCommercial()) { - pagetic = 35 * 11; - pagename = "TITLEPIC"; - doomSound.StartMusic(musicenum_t.mus_dm2ttl); - } else { - pagetic = 200; - - if (isRetail()) { - pagename = "CREDIT"; - } else { - pagename = "HELP1"; - } - } - break; - case 5: - DeferedPlayDemo("demo3"); - break; - // THE DEFINITIVE DOOM Special Edition demo - case 6: - DeferedPlayDemo("demo4"); - break; - } - } - - /** - * D_StartTitle - */ - public void StartTitle() { - gameaction = ga_nothing; - demosequence = -1; - AdvanceDemo(); - } - - // print title for every printed line - StringBuffer title = new StringBuffer(); - - /** - * D_AddFile - * - * Adds file to the end of the wadfiles[] list. - * Quite crude, we could use a listarray instead. - * - * @param file - */ - private void AddFile(String file) { - int numwadfiles; - for (numwadfiles = 0; eval(wadfiles[numwadfiles]); numwadfiles++) { - } - wadfiles[numwadfiles] = file; - } - - /** - * IdentifyVersion - * Checks availability of IWAD files by name, - * to determine whether registered/commercial features - * should be executed (notably loading PWAD's). - */ - public final String IdentifyVersion() { - String doomwaddir; - // By default. - language = Language_t.english; - - // First, check for -iwad parameter. - // If valid, then it trumps all others. - if (cVarManager.present(CommandVariable.IWAD)) { - LOGGER.log(Level.INFO, "-iwad specified. Will be used with priority"); - // It might be quoted. - final String test = C2JUtils.unquoteIfQuoted(cVarManager.get(CommandVariable.IWAD, String.class, 0).get(), '"'); - final String separator = System.getProperty("file.separator"); - final String iwad = test.substring(1 + test.lastIndexOf(separator)); - doomwaddir = test.substring(0, 1 + test.lastIndexOf(separator)); - final GameMode attempt = DoomVersion.tryOnlyOne(iwad, doomwaddir); - // Note: at this point we can't distinguish between "doom" retail - // and "doom" ultimate yet. - if (attempt != null) { - AddFile(doomwaddir + iwad); - this.setGameMode(attempt); - return (doomwaddir + iwad); - } - } else { - // Unix-like checking. Might come in handy sometimes. - // This should ALWAYS be activated, else doomwaddir etc. won't be defined. - - doomwaddir = System.getenv("DOOMWADDIR"); - if (doomwaddir != null) { - LOGGER.log(Level.INFO, "DOOMWADDIR found. Will be used with priority"); - } - - // None found, using current. - if (!eval(doomwaddir)) { - doomwaddir = "."; - } - } - - for (GameMode mode : GameMode.values()) { - if (mode != GameMode.indetermined && cVarManager.bool(mode.devVar)) { - return devParmOn(mode); - } - } - - final String wadFullPath = DoomVersion.tryAllWads(this, doomwaddir); - if (wadFullPath == null) { - LOGGER.log(Level.INFO, "Game mode indeterminate."); - setGameMode(GameMode.indetermined); - // We don't abort. Let's see what the PWAD contains. - //exit(1); - //I_Error ("Game mode indeterminate\n"); - } else { - AddFile(wadFullPath); - } - - return wadFullPath; - } - - private String devParmOn(GameMode mode) { - setGameMode(mode); - devparm = true; - AddFile(dstrings.DEVDATA + mode.version); - AddFile(dstrings.DEVMAPS + mode.devDir + "/texture1.lmp"); - if (mode.hasTexture2()) { - AddFile(dstrings.DEVMAPS + mode.devDir + "/texture2.lmp"); - } - AddFile(dstrings.DEVMAPS + mode.devDir + "/pnames.lmp"); - return (dstrings.DEVDATA + mode.version); - } - - /** - * - */ - protected final void CheckForPWADSInShareware() { - if (modifiedgame) { - // These are the lumps that will be checked in IWAD, - // if any one is not present, execution will be aborted. - String[] name = { - "e2m1", "e2m2", "e2m3", "e2m4", "e2m5", "e2m6", "e2m7", "e2m8", "e2m9", - "e3m1", "e3m3", "e3m3", "e3m4", "e3m5", "e3m6", "e3m7", "e3m8", "e3m9", - "dphoof", "bfgga0", "heada1", "cybra1", "spida1d1" - }; - int i; - - // Oh yes I can. - if (isShareware()) { - LOGGER.log(Level.WARNING, "You cannot -file with the shareware version. Register!"); - } - - // Check for fake IWAD with right name, - // but w/o all the lumps of the registered version. - if (isRegistered()) { - for (i = 0; i < 23; i++) { - if (wadLoader.CheckNumForName(name[i].toUpperCase()) < 0) { - doomSystem.Error("This is not the registered version: " + name[i]); - } - } - } - } - } - - /** Check whether the "doom.wad" we actually loaded - * is ultimate Doom's, by checking if it contains - * e4m1 - e4m9. - * - */ - protected final void CheckForUltimateDoom(WadLoader W) { - if (isRegistered()) { - // These are the lumps that will be checked in IWAD, - // if any one is not present, execution will be aborted. - String[] lumps = {"e4m1", "e4m2", "e4m3", "e4m4", "e4m5", "e4m6", "e4m7", "e4m8", "e4m9"}; - - // Check for fake IWAD with right name, - // but w/o all the lumps of the registered version. - if (!CheckForLumps(lumps, W)) { - return; - } - // Checks passed, so we can set the mode to Ultimate - setGameMode(GameMode.retail); - } - - } - - /** Check if ALL of the lumps exist. - * - * @param name - * @return - */ - protected boolean CheckForLumps(String[] name, WadLoader W) { - for (String name1 : name) { - if (W.CheckNumForName(name1.toUpperCase()) < 0) { - // Even one is missing? Not OK. - return false; - } - } - return true; - } - - protected final void GenerateTitle() { - switch (getGameMode()) { - case retail: - title.append("The Ultimate DOOM Startup v"); - title.append(VERSION / 100); - title.append("."); - title.append(VERSION % 100); - break; - case shareware: - title.append("DOOM Shareware Startup v"); - title.append(VERSION / 100); - title.append("."); - title.append(VERSION % 100); - break; - case registered: - title.append("DOOM Registered Startup v"); - title.append(VERSION / 100); - title.append("."); - title.append(VERSION % 100); - break; - case commercial: - title.append("DOOM 2: Hell on Earth v"); - title.append(VERSION / 100); - title.append("."); - title.append(VERSION % 100); - break; - case pack_plut: - title.append("DOOM 2: Plutonia Experiment v"); - title.append(VERSION / 100); - title.append("."); - title.append(VERSION % 100); - break; - case pack_tnt: - title.append("DOOM 2: TNT - Evilution v"); - title.append(VERSION / 100); - title.append("."); - title.append(VERSION % 100); - break; - case pack_xbla: - title.append("DOOM 2: No Rest for the Living v"); - title.append(VERSION / 100); - title.append("."); - title.append(VERSION % 100); - break; - case freedm: - title.append("FreeDM v"); - title.append(VERSION / 100); - title.append("."); - title.append(VERSION % 100); - break; - case freedoom1: - title.append("FreeDoom: Phase 1 v"); - title.append(VERSION / 100); - title.append("."); - title.append(VERSION % 100); - break; - case freedoom2: - title.append("FreeDoom: Phase 2 v"); - title.append(VERSION / 100); - title.append("."); - title.append(VERSION % 100); - break; - default: - title.append("Public DOOM - v"); - title.append(VERSION / 100); - title.append("."); - title.append(VERSION % 100); - break; - } - } - - // Used in BuildTiccmd. - protected ticcmd_t base = new ticcmd_t(); - - /** - * G_BuildTiccmd - * Builds a ticcmd from all of the available inputs - * or reads it from the demo buffer. - * If recording a demo, write it out . - * - * The CURRENT event to process is written to the various - * gamekeydown etc. arrays by the Responder method. - * So look there for any fuckups in constructing them. - * - */ - @SourceCode.Compatible - @G_Game.C(G_BuildTiccmd) - private void BuildTiccmd(ticcmd_t cmd) { - int i; - boolean strafe; - boolean bstrafe; - int speed, tspeed, lspeed; - int forward; - int side; - int look; - - I_BaseTiccmd: - ; // empty, or external driver - base.copyTo(cmd); - - cmd.consistancy = consistancy[consoleplayer][maketic % BACKUPTICS]; - - strafe = gamekeydown[key_strafe] || mousebuttons(mousebstrafe) || joybuttons(joybstrafe); - speed = ((gamekeydown[key_speed] ^ alwaysrun) || joybuttons(joybspeed)) ? 1 : 0; - - forward = side = look = 0; - - // use two stage accelerative turning - // on the keyboard and joystick - if (joyxmove < 0 || joyxmove > 0 || gamekeydown[key_right] || gamekeydown[key_left]) { - turnheld += ticdup; - } else { - turnheld = 0; - } - - tspeed = turnheld < SLOWTURNTICS ? 2 /* slowturn */ : speed; - - if (gamekeydown[key_lookdown] || gamekeydown[key_lookup]) { - lookheld += ticdup; - } else { - lookheld = 0; - } - - lspeed = lookheld < SLOWTURNTICS ? 1 : 2; - - // let movement keys cancel each other out - if (strafe) { - if (gamekeydown[key_right]) { - // fprintf(stderr, "strafe right\n"); - side += sidemove[speed]; - } - - if (gamekeydown[key_left]) { - // fprintf(stderr, "strafe left\n"); - side -= sidemove[speed]; - } - - if (joyxmove > 0) { - side += sidemove[speed]; - } else if (joyxmove < 0) { - side -= sidemove[speed]; - } - } else { - if (gamekeydown[key_right]) { - cmd.angleturn -= angleturn[tspeed]; - } - - if (gamekeydown[key_left]) { - cmd.angleturn += angleturn[tspeed]; - } - - if (joyxmove > 0) { - cmd.angleturn -= angleturn[tspeed]; - } else if (joyxmove < 0) { - cmd.angleturn += angleturn[tspeed]; - } - } - - if (gamekeydown[key_up]) { - forward += forwardmove[speed]; - } - - if (gamekeydown[key_down]) { - forward -= forwardmove[speed]; - } - - if (joyymove < 0) { - forward += forwardmove[speed]; - } else if (joyymove > 0) { - forward -= forwardmove[speed]; - } - - if (gamekeydown[key_straferight]) { - side += sidemove[speed]; - } - - if (gamekeydown[key_strafeleft]) { - side -= sidemove[speed]; - } - - // Look up/down/center keys - if (gamekeydown[key_lookup]) { - look = lspeed; - } - - if (gamekeydown[key_lookdown]) { - look = -lspeed; - } - - if (gamekeydown[key_lookcenter]) { - look = TOCENTER; - } - - // buttons - cmd.chatchar = headsUp.dequeueChatChar(); - - if (gamekeydown[key_fire] || mousebuttons(mousebfire) || joybuttons(joybfire)) { - cmd.buttons |= BT_ATTACK; - } - - if (gamekeydown[key_use] || joybuttons(joybuse)) { - cmd.buttons |= BT_USE; - // clear double clicks if hit use button - dclicks = 0; - } - - // chainsaw overrides - for (i = 0; i < NUMWEAPONS - 1; i++) { - if (gamekeydown[key_numbers[i]]) { - //System.out.println("Attempting weapon change (building ticcmd)"); - cmd.buttons |= BT_CHANGE; - cmd.buttons |= i << BT_WEAPONSHIFT; - break; - } - } - - // mouse - if (mousebuttons(mousebforward)) { - forward += forwardmove[speed]; - } - - // forward double click - if (mousebuttons(mousebforward) != eval(dclickstate) && dclicktime > 1) { - dclickstate = mousebuttons(mousebforward) ? 1 : 0; - if (dclickstate != 0) { - dclicks++; - } - if (dclicks == 2) { - cmd.buttons |= BT_USE; - dclicks = 0; - } else { - dclicktime = 0; - } - } else { - dclicktime += ticdup; - if (dclicktime > 20) { - dclicks = 0; - dclickstate = 0; - } - } - - // strafe double click - bstrafe = mousebuttons(mousebstrafe) || joybuttons(joybstrafe); - if ((bstrafe != eval(dclickstate2)) && dclicktime2 > 1) { - dclickstate2 = bstrafe ? 1 : 0; - if (dclickstate2 != 0) { - dclicks2++; - } - if (dclicks2 == 2) { - cmd.buttons |= BT_USE; - dclicks2 = 0; - } else { - dclicktime2 = 0; - } - } else { - dclicktime2 += ticdup; - if (dclicktime2 > 20) { - dclicks2 = 0; - dclickstate2 = 0; - } - } - - // By default, no vertical mouse movement - if (!novert) { - forward += mousey; - } - - if (strafe) { - side += mousex * 2; - } else { - cmd.angleturn -= mousex * 0x8; - } - - mousex = mousey = 0; - - if (forward > MAXPLMOVE()) { - forward = MAXPLMOVE(); - } else if (forward < -MAXPLMOVE()) { - forward = -MAXPLMOVE(); - } - if (side > MAXPLMOVE()) { - side = MAXPLMOVE(); - } else if (side < -MAXPLMOVE()) { - side = -MAXPLMOVE(); - } - - cmd.forwardmove += forward; - cmd.sidemove += side; - - if (players[consoleplayer].playerstate == PST_LIVE) { - if (look < 0) { - look += 16; - } - - cmd.lookfly = (char) look; - } - - // special buttons - if (sendpause) { - sendpause = false; - cmd.buttons = BT_SPECIAL | BTS_PAUSE; - } - - if (sendsave) { - sendsave = false; - cmd.buttons = (char) (BT_SPECIAL | BTS_SAVEGAME | (savegameslot << BTS_SAVESHIFT)); - } - } - - /** - * G_DoLoadLevel - * - * //extern gamestate_t wipegamestate; - */ - @SourceCode.Suspicious(CauseOfDesyncProbability.LOW) - @G_Game.C(G_DoLoadLevel) - public boolean DoLoadLevel() { - /** - * Added a config switch to this fix - * - Good Sign 2017/04/26 - * - * Fixed R_FlatNumForName was a part of the fix, not vanilla code - * - Good Sign 2017/05/07 - * - * DOOM determines the sky texture to be used - * depending on the current episode, and the game version. - * - * @SourceCode.Compatible - */ - if (Engine.getConfig().equals(Settings.fix_sky_change, Boolean.TRUE) && (isCommercial() - || (gamemission == GameMission_t.pack_tnt) - || (gamemission == GameMission_t.pack_plut))) { - // Set the sky map. - // First thing, we have a dummy sky texture name, - // a flat. The data is in the WAD only because - // we look for an actual index, instead of simply - // setting one. - textureManager.setSkyFlatNum(textureManager.FlatNumForName(SKYFLATNAME)); - - textureManager.setSkyTexture(textureManager.TextureNumForName("SKY3")); - if (gamemap < 12) { - textureManager.setSkyTexture(textureManager.TextureNumForName("SKY1")); - } else { - if (gamemap < 21) { - textureManager.setSkyTexture(textureManager.TextureNumForName("SKY2")); - } - } - } - - levelstarttic = gametic; // for time calculation - - if (wipegamestate == GS_LEVEL) { - wipegamestate = GS_MINUS_ONE; // force a wipe - } - gamestate = GS_LEVEL; - - for (int i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].playerstate == PST_DEAD) { - players[i].playerstate = PST_REBORN; - } - - memset(players[i].frags, 0, players[i].frags.length); - } - - try { - P_SetupLevel: - { - levelLoader.SetupLevel(gameepisode, gamemap, 0, gameskill); - } - } catch (IOException e) { - LOGGER.log(Level.SEVERE, "Failure loading level.", e); - // Failure loading level. - return false; - } - - displayplayer = consoleplayer; // view the guy you are playing - I_GetTime: - { - starttime = ticker.GetTime(); - } - gameaction = ga_nothing; - Z_CheckHeap: - ; - - // clear cmd building stuff - memset(gamekeydown, false, gamekeydown.length); - keysCleared = true; - joyxmove = joyymove = 0; - mousex = mousey = 0; - sendpause = sendsave = paused = false; - memset(mousearray, false, mousearray.length); - memset(joyarray, false, joyarray.length); - - /** - * Probably no desync-effect - * - GoodSign 2017/05/07 - * - * @SourceCode.Suspicious - */ - // killough 5/13/98: in case netdemo has consoleplayer other than green - statusBar.Start(); - headsUp.Start(); - - // killough: make -timedemo work on multilevel demos - // Move to end of function to minimize noise -- killough 2/22/98: - if (timingdemo) { - if (first) { - starttime = RealTime.GetTime(); - first = false; - } - } - - // Try reclaiming some memory from limit-expanded buffers. - sceneRenderer.resetLimits(); - return true; - } - - protected boolean first = true; - - /** - * G_Responder - * Get info needed to make ticcmd_ts for the players. - */ - @SourceCode.Compatible - @G_Game.C(G_Responder) - public boolean Responder(event_t ev) { - // allow spy mode changes even during the demo - if (gamestate == GS_LEVEL && ev.isKey(SC_F12, ev_keydown) && (singledemo || !deathmatch)) { - // spy mode - do { - displayplayer++; - if (displayplayer == MAXPLAYERS) { - displayplayer = 0; - } - } while (!playeringame[displayplayer] && displayplayer != consoleplayer); - return true; - } - - // any other key pops up menu if in demos - if (gameaction == ga_nothing && !singledemo && (demoplayback || gamestate == GS_DEMOSCREEN)) { - if (ev.isType(ev_keydown) - || (use_mouse && ev.ifMouse(ev_mouse, event_t::hasData)) - || (use_joystick && ev.ifJoy(ev_joystick, event_t::hasData))) { - M_StartControlPanel: - { - menu.StartControlPanel(); - } - return true; - } - return false; - } - - if (gamestate == GS_LEVEL) { - if (devparm && ev.isKey(SC_SEMICOLON, ev_keydown)) { - G_DeathMatchSpawnPlayer: - { - DeathMatchSpawnPlayer(0); - } - return true; - } - - HU_Responder: - { - if (headsUp.Responder(ev)) { - return true; // chat ate the event - } - } - ST_Responder: - { - if (statusBar.Responder(ev)) { - return true; // status window ate it - } - } - AM_Responder: - { - if (autoMap.Responder(ev)) { - return true; // automap ate it - } - } - } - - if (gamestate == GS_FINALE) { - F_Responder: - { - if (finale.Responder(ev)) { - return true; // finale ate the event - } - } - } - - switch (ev.type()) { - case ev_keydown: - if (ev.isKey(SC_PAUSE)) { - sendpause = true; - return true; - } - - ev.withKey(sc -> { - gamekeydown[sc.ordinal()] = true; - if (vanillaKeyBehavior) { - handleVanillaKeys(sc, true); - } - }); - return true; // eat key down events - case ev_keyup: - /* CAPS lock will only go through as a keyup event */ - if (ev.isKey(SC_CAPSLK)) { - // Just toggle it. It's too hard to read the state. - alwaysrun = !alwaysrun; - players[consoleplayer].message = String.format("Always run: %s", alwaysrun); - } - - ev.withKey(sc -> { - gamekeydown[sc.ordinal()] = false; - if (vanillaKeyBehavior) { - handleVanillaKeys(sc, false); - } - }); - return false; // always let key up events filter down - - case ev_mouse: - // Ignore them at the responder level - if (use_mouse) { - mousebuttons(0, ev.isMouse(event_t.MOUSE_LEFT)); - mousebuttons(1, ev.isMouse(event_t.MOUSE_RIGHT)); - mousebuttons(2, ev.isMouse(event_t.MOUSE_MID)); - ev.withMouse(mouseEvent -> { - mousex = mouseEvent.x * (mouseSensitivity + 5) / 10; - mousey = mouseEvent.y * (mouseSensitivity + 5) / 10; - }); - } - return true; // eat events - case ev_joystick: - if (use_joystick) { - joybuttons(0, ev.isJoy(event_t.JOY_1)); - joybuttons(1, ev.isJoy(event_t.JOY_2)); - joybuttons(2, ev.isJoy(event_t.JOY_3)); - joybuttons(3, ev.isJoy(event_t.JOY_4)); - ev.withJoy(joyEvent -> { - joyxmove = joyEvent.x; - joyymove = joyEvent.y; - }); - } - return true; // eat events - default: - break; - } - - return false; - } - - private void handleVanillaKeys(Signals.ScanCode sc, boolean keyDown) { - switch (sc) { - case SC_LSHIFT: - case SC_RSHIFT: - gamekeydown[SC_RSHIFT.ordinal()] = gamekeydown[SC_LSHIFT.ordinal()] = keyDown; - break; - case SC_LCTRL: - case SC_RCTRL: - gamekeydown[SC_RCTRL.ordinal()] = gamekeydown[SC_LCTRL.ordinal()] = keyDown; - break; - case SC_LALT: - case SC_RALT: - gamekeydown[SC_RALT.ordinal()] = gamekeydown[SC_LALT.ordinal()] = keyDown; - break; - case SC_UP: - gamekeydown[SC_NUMKEY8.ordinal()] = keyDown; - break; - case SC_DOWN: - gamekeydown[SC_NUMKEY2.ordinal()] = keyDown; - break; - case SC_LEFT: - gamekeydown[SC_NUMKEY4.ordinal()] = keyDown; - break; - case SC_RIGHT: - gamekeydown[SC_NUMKEY6.ordinal()] = keyDown; - break; - default: - break; - } - } - - private final String turbomessage = "is turbo!"; - - /** - * G_Ticker - * - * Make ticcmd_ts for the players. - */ - @G_Game.C(G_Ticker) - public void Ticker() { - // do player reborns if needed - for (int i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && players[i].playerstate == PST_REBORN) { - G_DoReborn: - { - DoReborn(i); - } - } - } - - // do things to change the game state - while (gameaction != ga_nothing) { - switch (gameaction) { - case ga_loadlevel: - G_DoLoadLevel: - { - DoLoadLevel(); - } - break; - case ga_newgame: - G_DoNewGame: - { - DoNewGame(); - } - break; - case ga_loadgame: - G_DoLoadGame: - { - DoLoadGame(); - } - break; - case ga_savegame: - G_DoSaveGame: - { - DoSaveGame(); - } - break; - case ga_playdemo: - G_DoPlayDemo: - { - DoPlayDemo(); - } - break; - case ga_completed: - G_DoCompleted: - { - DoCompleted(); - } - break; - case ga_victory: - finale.StartFinale(); - break; - case ga_worlddone: - DoWorldDone(); - break; - case ga_screenshot: - ScreenShot(); - gameaction = ga_nothing; - break; - case ga_nothing: - break; - default: - break; - } - } - - // get commands, check consistancy, - // and build new consistancy check - final int buf = (gametic / ticdup) % BACKUPTICS; - for (int i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i]) { - final ticcmd_t cmd = players[i].cmd; - //System.out.println("Current command:"+cmd); - - //memcpy (cmd, &netcmds[i][buf], sizeof(ticcmd_t)); - netcmds[i][buf].copyTo(cmd); - - // MAES: this is where actual demo commands are being issued or created! - // Essentially, a demo is a sequence of stored ticcmd_t with a header. - // Knowing that, it's possible to objectify it. - if (demoplayback) { - ReadDemoTiccmd(cmd); - } - - if (demorecording) { - WriteDemoTiccmd(cmd); - } - - // check for turbo cheats - if (cmd.forwardmove > TURBOTHRESHOLD && ((gametic & 31) == 0) && ((gametic >> 5) & 3) == i) { - //extern char *player_names[4]; - //sprintf (turbomessage, "%s is turbo!",player_names[i]); - players[consoleplayer].message = hu.HU.player_names[i] + turbomessage; - } - - if (netgame && !netdemo && (gametic % ticdup) == 0) { - if (gametic > BACKUPTICS && consistancy[i][buf] != cmd.consistancy) { - doomSystem.Error("consistency failure (%d should be %d)", cmd.consistancy, consistancy[i][buf]); - } - - if (players[i].mo != null) { - consistancy[i][buf] = (short) players[i].mo.x; - } else { - consistancy[i][buf] = (short) random.getIndex(); - } - } - } - } - - // check for special buttons - for (int i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i]) { - if ((players[i].cmd.buttons & BT_SPECIAL) != 0) { - switch (players[i].cmd.buttons & BT_SPECIALMASK) { - case BTS_PAUSE: - // MAES: fixed stupid ^pause bug. - paused = !paused; - if (paused) { - doomSound.PauseSound(); - } else { - doomSound.ResumeSound(); - } - break; - case BTS_SAVEGAME: - if (savedescription == null) { - savedescription = "NET GAME"; - } - savegameslot = (players[i].cmd.buttons & BTS_SAVEMASK) >> BTS_SAVESHIFT; - gameaction = ga_savegame; - break; - } - } - } - } - - // do main actions - switch (gamestate) { - case GS_LEVEL: - actions.Ticker(); - statusBar.Ticker(); - autoMap.Ticker(); - headsUp.Ticker(); - break; - - case GS_INTERMISSION: - endLevel.Ticker(); - break; - - case GS_FINALE: - finale.Ticker(); - break; - - case GS_DEMOSCREEN: - PageTicker(); - break; - - default: - break; - } - } - - // - // PLAYER STRUCTURE FUNCTIONS - // also see P_SpawnPlayer in P_Things - // - /** - * G_InitPlayer - * Called at the start. - * Called by the game initialization functions. - * - * MAES: looks like dead code. It's never called. - * - */ - protected void InitPlayer(int player) { - // set up the saved info - // clear everything else to defaults - players[player].PlayerReborn(); - } - - // - // G_CheckSpot - // Returns false if the player cannot be respawned - // at the given mapthing_t spot - // because something is occupying it - // - //void P_SpawnPlayer (mapthing_t* mthing); - @SourceCode.Exact - @G_Game.C(G_CheckSpot) - private boolean CheckSpot(int playernum, mapthing_t mthing) { - if (players[playernum].mo == null) { - // first spawn of level, before corpses - for (int i = 0; i < playernum; i++) { - if (players[i].mo.x == mthing.x << FRACBITS && players[i].mo.y == mthing.y << FRACBITS) { - return false; - } - } - return true; - } - - final int x = mthing.x << FRACBITS, y = mthing.y << FRACBITS; - - P_CheckPosition: - { - if (!actions.CheckPosition(players[playernum].mo, x, y)) { - return false; - } - } - - // flush an old corpse if needed - if (bodyqueslot >= BODYQUESIZE) { - P_RemoveMobj: - { - actions.RemoveMobj(bodyque[bodyqueslot % BODYQUESIZE]); - } - } - bodyque[bodyqueslot % BODYQUESIZE] = players[playernum].mo; - bodyqueslot++; - - // spawn a teleport fog - final subsector_t ss; - R_PointInSubsector: - { - ss = levelLoader.PointInSubsector(x, y); - } - // Angles stored in things are supposed to be "sanitized" against rollovers. - final int angle = (int) ((ANG45 * (mthing.angle / 45)) >>> ANGLETOFINESHIFT); - final mobj_t mo; - P_SpawnMobj: - { - mo = actions.SpawnMobj(x + 20 * finecosine[angle], y + 20 * finesine[angle], ss.sector.floorheight, mobjtype_t.MT_TFOG); - } - - // FIXME: maybe false fix - if (players[consoleplayer].viewz != 1) { - S_StartSound: - { - doomSound.StartSound(mo, sfxenum_t.sfx_telept); // don't start sound on first frame - } - } - - return true; - } - - // - // G_DeathMatchSpawnPlayer - // Spawns a player at one of the random death match spots - // called at level load and each death - // - @Override - @SourceCode.Exact - @G_Game.C(G_DeathMatchSpawnPlayer) - public void DeathMatchSpawnPlayer(int playernum) { - final int selections = deathmatch_p; - if (selections < 4) { - I_Error: - { - doomSystem.Error("Only %d deathmatch spots, 4 required", selections); - } - } - - for (int j = 0; j < 20; j++) { - final int i; - P_Random: - { - i = random.P_Random() % selections; - } - G_CheckSpot: - { - if (CheckSpot(playernum, deathmatchstarts[i])) { - deathmatchstarts[i].type = (short) (playernum + 1); - P_SpawnPlayer: - { - actions.SpawnPlayer(deathmatchstarts[i]); - } - return; - } - } - } - - // no good spot, so the player will probably get stuck - // MAES: seriously, fuck him. - P_SpawnPlayer: - { - actions.SpawnPlayer(playerstarts[playernum]); - } - } - - /** - * G_DoReborn - */ - @SourceCode.Exact - @G_Game.C(G_DoReborn) - public void DoReborn(int playernum) { - if (!netgame) { - // reload the level from scratch - gameaction = ga_loadlevel; - } else { - // respawn at the start - - // first dissasociate the corpse - players[playernum].mo.player = null; - - // spawn at random spot if in death match - if (deathmatch) { - G_DeathMatchSpawnPlayer: - { - DeathMatchSpawnPlayer(playernum); - } - return; - } - - G_CheckSpot: - { - if (CheckSpot(playernum, playerstarts[playernum])) { - P_SpawnPlayer: - { - actions.SpawnPlayer(playerstarts[playernum]); - } - return; - } - } - - // try to spawn at one of the other players spots - for (int i = 0; i < MAXPLAYERS; i++) { - G_CheckSpot: - { - if (CheckSpot(playernum, playerstarts[i])) { - playerstarts[i].type = (short) (playernum + 1); // fake as other player - P_SpawnPlayer: - { - actions.SpawnPlayer(playerstarts[i]); - } - playerstarts[i].type = (short) (i + 1); // restore - return; - } - } - // he's going to be inside something. Too bad. - // MAES: Yeah, they're like, fuck him. - } - - P_SpawnPlayer: - { - actions.SpawnPlayer(playerstarts[playernum]); - } - } - } - - /** DOOM Par Times [4][10] */ - final int[][] pars = { - {0}, - {0, 30, 75, 120, 90, 165, 180, 180, 30, 165}, - {0, 90, 90, 90, 120, 90, 360, 240, 30, 170}, - {0, 90, 45, 90, 150, 90, 90, 165, 30, 135} - }; - - /** DOOM II Par Times */ - final int[] cpars = { - 30, 90, 120, 120, 90, 150, 120, 120, 270, 90, // 1-10 - 210, 150, 150, 150, 210, 150, 420, 150, 210, 150, // 11-20 - 240, 150, 180, 150, 150, 300, 330, 420, 300, 180, // 21-30 - 120, 30 // 31-32 - }; - - // - // G_DoCompleted - // - boolean secretexit; - - public final void ExitLevel() { - secretexit = false; - gameaction = ga_completed; - } - - // Here's for the german edition. - public void SecretExitLevel() { - // IF NO WOLF3D LEVELS, NO SECRET EXIT! - secretexit = !(isCommercial() && (wadLoader.CheckNumForName("MAP31") < 0)); - gameaction = ga_completed; - } - - @SourceCode.Exact - @G_Game.C(G_DoCompleted) - protected void DoCompleted() { - gameaction = ga_nothing; - - for (int i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i]) { - G_PlayerFinishLevel: - { // take away cards and stuff - players[i].PlayerFinishLevel(); - } - } - } - - if (automapactive) { - AM_Stop: - { - autoMap.Stop(); - } - } - - if (!isCommercial()) { - switch (gamemap) { - case 8: - // MAES: end of episode - gameaction = ga_victory; - return; - case 9: - // MAES: end of secret level - for (int i = 0; i < MAXPLAYERS; i++) { - players[i].didsecret = true; - } - break; - default: - break; - } - } - - wminfo.didsecret = players[consoleplayer].didsecret; - wminfo.epsd = gameepisode - 1; - wminfo.last = gamemap - 1; - - // wminfo.next is 0 biased, unlike gamemap - if (isCommercial()) { - if (secretexit) { - switch (gamemap) { - case 2: - wminfo.next = 32; //Fix Doom 3 BFG Edition, MAP02 secret exit to MAP33 Betray - break; - case 15: - wminfo.next = 30; - break; - case 31: - wminfo.next = 31; - break; - default: - break; - } - } else { - switch (gamemap) { - case 31: - case 32: - wminfo.next = 15; - break; - case 33: - wminfo.next = 2; //Fix Doom 3 BFG Edition, MAP33 Betray exit back to MAP03 - break; - default: - wminfo.next = gamemap; - } - } - } else { - if (secretexit) { - wminfo.next = 8; // go to secret level - } else if (gamemap == 9) { - // returning from secret level - switch (gameepisode) { - case 1: - wminfo.next = 3; - break; - case 2: - wminfo.next = 5; - break; - case 3: - wminfo.next = 6; - break; - case 4: - wminfo.next = 2; - break; - default: - break; - } - } else { - wminfo.next = gamemap; // go to next level - } - } - - wminfo.maxkills = totalkills; - wminfo.maxitems = totalitems; - wminfo.maxsecret = totalsecret; - wminfo.maxfrags = 0; - - if (isCommercial()) { - wminfo.partime = 35 * cpars[gamemap - 1]; - } else if (gameepisode >= pars.length) { - wminfo.partime = 0; - } else { - wminfo.partime = 35 * pars[gameepisode][gamemap]; - } - - wminfo.pnum = consoleplayer; - - for (int i = 0; i < MAXPLAYERS; i++) { - wminfo.plyr[i].in = playeringame[i]; - wminfo.plyr[i].skills = players[i].killcount; - wminfo.plyr[i].sitems = players[i].itemcount; - wminfo.plyr[i].ssecret = players[i].secretcount; - wminfo.plyr[i].stime = leveltime; - memcpy(wminfo.plyr[i].frags, players[i].frags, wminfo.plyr[i].frags.length); - } - - gamestate = GS_INTERMISSION; - viewactive = false; - automapactive = false; - - if (statcopy != null) { - memcpy(statcopy, wminfo, 1); - } - - WI_Start: - { - endLevel.Start(wminfo); - } - } - - /** - * G_WorldDone - */ - public void WorldDone() { - gameaction = ga_worlddone; - - if (secretexit) { - players[consoleplayer].didsecret = true; - } - - if (isCommercial()) { - switch (gamemap) { - case 15: - case 31: - if (!secretexit) { - break; - } - case 6: - case 11: - case 20: - case 30: - finale.StartFinale(); - break; - } - } - } - - public void DoWorldDone() { - gamestate = GS_LEVEL; - gamemap = wminfo.next + 1; - DoLoadLevel(); - gameaction = ga_nothing; - viewactive = true; - } - - // - // G_InitFromSavegame - // Can be called by the startup code or the menu task. - // - //extern boolean setsizeneeded; - //void R_ExecuteSetViewSize (void); - String savename; - - public void LoadGame(String name) { - savename = name; - gameaction = ga_loadgame; - } - - /** - * This is fugly. Making a "savegame object" will make at least certain comparisons easier, and avoid writing code - * twice. - */ - @SourceCode.Suspicious(CauseOfDesyncProbability.MEDIUM) - @G_Game.C(G_DoLoadGame) - protected void DoLoadGame() { - try { - StringBuffer vcheck = new StringBuffer(); - VanillaDSGHeader header = new VanillaDSGHeader(); - IDoomSaveGame dsg = new VanillaDSG<>(this); - - gameaction = ga_nothing; - - DataInputStream f = new DataInputStream(new BufferedInputStream(new FileInputStream(savename))); - - header.read(f); - f.close(); - - // skip the description field - vcheck.append("version "); - vcheck.append(VERSION); - - if (vcheck.toString().compareTo(header.getVersion()) != 0) { - f.close(); - return; // bad version - } - - // Ok so far, reopen stream. - f = new DataInputStream(new BufferedInputStream(new FileInputStream(savename))); - gameskill = header.getGameskill(); - gameepisode = header.getGameepisode(); - gamemap = header.getGamemap(); - System.arraycopy(header.getPlayeringame(), 0, playeringame, 0, MAXPLAYERS); - - // load a base level - G_InitNew: - { - InitNew(gameskill, gameepisode, gamemap); - } - - if (gameaction == ga_failure) { - // failure to load. Abort. - f.close(); - return; - } - - gameaction = ga_nothing; - - // get the times - leveltime = header.getLeveltime(); - - boolean ok; - // dearchive all the modifications - P_UnArchivePlayers: - P_UnArchiveWorld: - P_UnArchiveThinkers: - P_UnArchiveSpecials: - { - ok = dsg.doLoad(f); - } - f.close(); - - // MAES: this will cause a forced exit. - // The problem is that the status will have already been altered - // (perhaps VERY badly) so it makes no sense to progress. - // If you want it bullet-proof, you could implement - // a "tentative loading" subsystem, which will only alter the game - // if everything works out without errors. But who cares :-p - if (!ok) { - I_Error: - { - doomSystem.Error("Bad savegame"); - } - } - - // done - //Z_Free (savebuffer); - if (sceneRenderer.getSetSizeNeeded()) { - R_ExecuteSetViewSize: - { - sceneRenderer.ExecuteSetViewSize(); - } - } - - // draw the pattern into the back screen - R_FillBackScreen: - { - sceneRenderer.FillBackScreen(); - } - } catch (IOException e) { - LOGGER.log(Level.SEVERE, "Failure loading game.", e); - } - } - - // - // G_SaveGame - // Called by the menu task. - // Description is a 24 byte text string - // - public void SaveGame(int slot, String description) { - savegameslot = slot; - savedescription = description; - sendsave = true; - } - - @SourceCode.Suspicious(CauseOfDesyncProbability.LOW) - @G_Game.C(G_DoSaveGame) - protected void DoSaveGame() { - - try { - String name; - //char[] name2=new char[VERSIONSIZE]; - String description; - StringBuffer build = new StringBuffer(); - IDoomSaveGameHeader header = new VanillaDSGHeader(); - IDoomSaveGame dsg = new VanillaDSG<>(this); - - M_CheckParm: - { - if (cVarManager.bool(CommandVariable.CDROM)) { - build.append("c:\\doomdata\\"); - } - } - - build.append(String.format("%s%d.dsg", SAVEGAMENAME, savegameslot)); - name = build.toString(); - - description = savedescription; - - header.setName(description); - header.setVersion(String.format("version %d", VERSION)); - header.setGameskill(gameskill); - header.setGameepisode(gameepisode); - header.setGamemap(gamemap); - header.setPlayeringame(playeringame); - header.setLeveltime(leveltime); - dsg.setHeader(header); - - // Try opening a save file. No intermediate buffer (performance?) - try ( DataOutputStream f = new DataOutputStream(new FileOutputStream(name))) { - P_ArchivePlayers: - P_ArchiveWorld: - P_ArchiveThinkers: - P_ArchiveSpecials: - { - boolean ok = dsg.doSave(f); - } - } - } catch (IOException e) { - LOGGER.log(Level.SEVERE, "Failure saving game.", e); - } - // Saving is not as destructive as loading. - - gameaction = ga_nothing; - savedescription = ""; - - players[consoleplayer].message = GGSAVED; - - // draw the pattern into the back screen - R_FillBackScreen: - { - sceneRenderer.FillBackScreen(); - } - } - - skill_t d_skill; - int d_episode; - int d_map; - - public void DeferedInitNew(skill_t skill, int episode, int map) { - d_skill = skill; - d_episode = episode; - d_map = map; - gameaction = ga_newgame; - } - - @SourceCode.Exact - @G_Game.C(G_DoNewGame) - public void DoNewGame() { - demoplayback = false; - netdemo = false; - netgame = false; - deathmatch = false; - playeringame[1] = playeringame[2] = playeringame[3] = false; - respawnparm = false; - fastparm = false; - nomonsters = false; - consoleplayer = 0; - G_InitNew: - { - InitNew(d_skill, d_episode, d_map); - } - gameaction = ga_nothing; - } - - /** - * G_InitNew - * Can be called by the startup code or the menu task, - * consoleplayer, displayplayer, playeringame[] should be set. - */ - @SourceCode.Compatible - @G_Game.C(G_InitNew) - public void InitNew(skill_t skill, int episode, int map) { - InitNew(skill, episode, map, false); - } - - private void InitNew(skill_t skill, int episode, int map, boolean noSwitchRandom) { - if (paused) { - paused = false; - S_ResumeSound: - { - doomSound.ResumeSound(); - } - } - - if (skill.ordinal() > skill_t.sk_nightmare.ordinal()) { - skill = skill_t.sk_nightmare; - } - - // This was quite messy with SPECIAL and commented parts. - // Supposedly hacks to make the latest edition work. - // It might not work properly. - if (episode < 1) { - episode = 1; - } - - if (isRetail()) { - if (episode > 4) { - episode = 4; - } - } else if (isShareware()) { - if (episode > 1) { - episode = 1; // only start episode 1 on shareware - } - } else { - if (episode > 3) { - episode = 3; - } - } - - if (map < 1) { - map = 1; - } - - if ((map > 9) && (!isCommercial())) { - map = 9; - } - - /** - * I wrote it that way. No worries JavaRandom will never be picked on vanilla demo playback - * - Good Sign 2017/05/08 - * - * @SourceCode.Compatible - */ - if (!noSwitchRandom) { - if (cVarManager.bool(CommandVariable.JAVARANDOM)) { - random.requireRandom(VERSION | JAVARANDOM_MASK); - } else { - random.requireRandom(VERSION); - } - } - - M_ClearRandom: - { - random.ClearRandom(); - } - - respawnmonsters = skill == skill_t.sk_nightmare || respawnparm; - - // If on nightmare/fast monsters make everything MOAR pimp. - if (fastparm || (skill == skill_t.sk_nightmare && gameskill != skill_t.sk_nightmare)) { - for (int i = statenum_t.S_SARG_RUN1.ordinal(); i <= statenum_t.S_SARG_PAIN2.ordinal(); i++) { - states[i].tics >>= 1; - } - - mobjinfo[mobjtype_t.MT_BRUISERSHOT.ordinal()].speed = 20 * MAPFRACUNIT; - mobjinfo[mobjtype_t.MT_HEADSHOT.ordinal()].speed = 20 * MAPFRACUNIT; - mobjinfo[mobjtype_t.MT_TROOPSHOT.ordinal()].speed = 20 * MAPFRACUNIT; - } else if (skill != skill_t.sk_nightmare && gameskill == skill_t.sk_nightmare) { - for (int i = statenum_t.S_SARG_RUN1.ordinal(); i <= statenum_t.S_SARG_PAIN2.ordinal(); i++) { - states[i].tics <<= 1; - } - - mobjinfo[mobjtype_t.MT_BRUISERSHOT.ordinal()].speed = 15 * MAPFRACUNIT; - mobjinfo[mobjtype_t.MT_HEADSHOT.ordinal()].speed = 10 * MAPFRACUNIT; - mobjinfo[mobjtype_t.MT_TROOPSHOT.ordinal()].speed = 10 * MAPFRACUNIT; - } - - // force players to be initialized upon first level load - for (int i = 0; i < MAXPLAYERS; i++) { - players[i].playerstate = PST_REBORN; - } - - // will be set false if a demo - usergame = true; - paused = false; - demoplayback = false; - automapactive = false; - viewactive = true; - gameepisode = episode; - gamemap = map; - gameskill = skill; - viewactive = true; - - // set the sky map for the episode - if (isCommercial()) { - textureManager.setSkyTexture(textureManager.TextureNumForName("SKY3")); - if (gamemap < 12) { - textureManager.setSkyTexture(textureManager.TextureNumForName("SKY1")); - } else if (gamemap < 21) { - textureManager.setSkyTexture(textureManager.TextureNumForName("SKY2")); - } - } else { - switch (episode) { - case 1: - textureManager.setSkyTexture(textureManager.TextureNumForName("SKY1")); - break; - case 2: - textureManager.setSkyTexture(textureManager.TextureNumForName("SKY2")); - break; - case 3: - textureManager.setSkyTexture(textureManager.TextureNumForName("SKY3")); - break; - case 4: // Special Edition sky - textureManager.setSkyTexture(textureManager.TextureNumForName("SKY4")); - break; - default: - break; - } - } - - G_DoLoadLevel: - { - if (!DoLoadLevel()) { - levelLoadFailure(); - } - } - } - - protected void levelLoadFailure() { - boolean endgame = doomSystem.GenerateAlert(Strings.LEVEL_FAILURE_TITLE, Strings.LEVEL_FAILURE_CAUSE, true); - - // Initiate endgame - if (endgame) { - gameaction = ga_failure; - gamestate = GS_DEMOSCREEN; - menu.ClearMenus(); - StartTitle(); - } else { - // Shutdown immediately. - doomSystem.Quit(); - } - } - - // - // DEMO RECORDING - // - public void ReadDemoTiccmd(ticcmd_t cmd) { - final IDemoTicCmd democmd = demobuffer.getNextTic(); - if (democmd == null) { - // end of demo data stream - CheckDemoStatus(); - - // Force status resetting - this.demobuffer.resetDemo(); - return; - } - - democmd.decode(cmd); - } - - public void WriteDemoTiccmd(ticcmd_t cmd) { - // press q to end demo recording - if (gamekeydown[key_recordstop]) { - CheckDemoStatus(); - } - - final IDemoTicCmd reccmd = new VanillaTiccmd(); - reccmd.encode(cmd); - demobuffer.putTic(reccmd); - - // MAES: Useless, we can't run out of space anymore (at least not in theory). - - /* demobuffer[demo_p++] = cmd.forwardmove; - demobuffer[demo_p++] = cmd.sidemove; - demobuffer[demo_p++] = (byte) ((cmd.angleturn+128)>>8); - demobuffer[demo_p++] = (byte) cmd.buttons; - demo_p -= 4; - if (demo_p > demoend - 16) - { - // no more space - CheckDemoStatus (); - return; - } */ - //ReadDemoTiccmd (cmd); // make SURE it is exactly the same - // MAES: this is NOT the way to do in Mocha, because we are not manipulating - // the demo index directly anymore. Instead, decode what we have just saved. - reccmd.decode(cmd); - } - - /** - * G_RecordDemo - */ - public void RecordDemo(String name) { - StringBuffer buf = new StringBuffer(); - usergame = false; - buf.append(name); - buf.append(".lmp"); - demoname = buf.toString(); - demobuffer = new VanillaDoomDemo(); - demorecording = true; - } - - @G_Game.C(G_BeginRecording) - public void BeginRecording() { - demobuffer.setVersion(cVarManager.bool(CommandVariable.JAVARANDOM) ? VERSION | JAVARANDOM_MASK : VERSION); - demobuffer.setSkill(gameskill); - demobuffer.setEpisode(gameepisode); - demobuffer.setMap(gamemap); - demobuffer.setDeathmatch(deathmatch); - demobuffer.setRespawnparm(respawnparm); - demobuffer.setFastparm(fastparm); - demobuffer.setNomonsters(nomonsters); - demobuffer.setConsoleplayer(consoleplayer); - demobuffer.setPlayeringame(playeringame); - } - - String defdemoname; - - /** - * G_PlayDemo - */ - public void DeferedPlayDemo(String name) { - defdemoname = name; - gameaction = ga_playdemo; - } - - @SuppressWarnings("UnusedAssignment") - @SourceCode.Compatible - @G_Game.C(G_DoPlayDemo) - public void DoPlayDemo() { - - skill_t skill; - boolean fail; - int i, episode, map; - - gameaction = ga_nothing; - // MAES: Yeah, it's OO all the way now, baby ;-) - W_CacheLumpName: - { - try { - demobuffer = wadLoader.CacheLumpName(defdemoname.toUpperCase(), PU_STATIC, VanillaDoomDemo.class); - } catch (Exception e) { - fail = true; - } - } - - fail = (demobuffer.getSkill() == null); - - final int version; - if (fail || ((version = demobuffer.getVersion() & 0xFF) & ~JAVARANDOM_MASK) != VERSION) { - LOGGER.log(Level.WARNING, String.format("Demo is from a different game version, version code read: %d", - demobuffer.getVersion())); - gameaction = ga_nothing; - return; - } - - random.requireRandom(version); - - skill = demobuffer.getSkill(); - episode = demobuffer.getEpisode(); - map = demobuffer.getMap(); - deathmatch = demobuffer.isDeathmatch(); - respawnparm = demobuffer.isRespawnparm(); - fastparm = demobuffer.isFastparm(); - nomonsters = demobuffer.isNomonsters(); - consoleplayer = demobuffer.getConsoleplayer(); - // Do this, otherwise previously loaded demos will be stuck at their end. - demobuffer.resetDemo(); - - boolean[] pigs = demobuffer.getPlayeringame(); - for (i = 0; i < MAXPLAYERS; i++) { - playeringame[i] = pigs[i]; - } - if (playeringame[1]) { - netgame = true; - netdemo = true; - } - - // don't spend a lot of time in loadlevel - precache = false; - G_InitNew: - { - InitNew(skill, episode, map, true); - } - precache = true; - - usergame = false; - demoplayback = true; - - } - - // - // G_TimeDemo - // - public void TimeDemo(String name) { - nodrawers = cVarManager.bool(CommandVariable.NODRAW); - noblit = cVarManager.bool(CommandVariable.NOBLIT); - timingdemo = true; - singletics = true; - defdemoname = name; - gameaction = ga_playdemo; - } - - /** G_CheckDemoStatus - * - * Called after a death or level completion to allow demos to be cleaned up - * Returns true if a new demo loop action will take place - * - */ - public boolean CheckDemoStatus() { - int endtime; - - if (timingdemo) { - endtime = RealTime.GetTime(); - // killough -- added fps information and made it work for longer demos: - long realtics = endtime - starttime; - - this.commit(); - CM.SaveDefaults(); - doomSystem.Error("timed %d gametics in %d realtics = %f frames per second", gametic, - realtics, gametic * (double) (TICRATE) / realtics); - } - - if (demoplayback) { - if (singledemo) { - doomSystem.Quit(); - } - - // Z_ChangeTag (demobuffer, PU_CACHE); - demoplayback = false; - netdemo = false; - netgame = false; - deathmatch = false; - playeringame[1] = playeringame[2] = playeringame[3] = false; - respawnparm = false; - fastparm = false; - nomonsters = false; - consoleplayer = 0; - AdvanceDemo(); - return true; - } - - if (demorecording) { - //demobuffer[demo_p++] = (byte) DEMOMARKER; - - MenuMisc.WriteFile(demoname, demobuffer); - //Z_Free (demobuffer); - demorecording = false; - doomSystem.Error("Demo %s recorded", demoname); - } - - return false; - } - - /** This should always be available for real timing */ - protected ITicker RealTime; - - // Bookkeeping on players - state. - public player_t[] players; - - public DelegateRandom random; - public final CVarManager cVarManager; - public final IWadLoader wadLoader; - public final IDoomSound doomSound; - public final ISoundDriver soundDriver; - public final IMusic music; - public final AbstractStatusBar statusBar; - public final DoomGraphicSystem graphicSystem; - public final DoomSystemNetworking systemNetworking; - public final IDoomGameNetworking gameNetworking; - public final AbstractLevelLoader levelLoader; - public final IDoomMenu menu; - public final ActionFunctions actions; - public final SceneRenderer sceneRenderer; - public final HU headsUp; - public final IAutoMap autoMap; - public final Finale finale; - public final EndLevel endLevel; - public final Wiper wiper; - public final TextureManager textureManager; - public final ISpriteManager spriteManager; - public final ITicker ticker; - public final IDiskDrawer diskDrawer; - public final IDoomSystem doomSystem; - public final BppMode bppMode; - - /** - * Since this is a fully OO implementation, we need a way to create - * the instances of the Refresh daemon, the Playloop, the Wadloader - * etc. which however are now completely independent of each other - * (well, ALMOST), and are typically only passed context when - * instantiated. - * - * If you instantiate one too early, it will have null context. - * - * The trick is to construct objects in the correct order. Some of - * them have Init() methods which are NOT yet safe to call. - * - */ - @SuppressWarnings("LeakingThisInConstructor") - public DoomMain() throws IOException { - // Init game status... - super(); - - // Init players - players = new player_t[MAXPLAYERS]; - Arrays.setAll(players, i -> new player_t(this)); - - // Init objects - this.cVarManager = Engine.getCVM(); - - // Prepare events array with event instances - Arrays.fill(events, event_t.EMPTY_EVENT); - - // Create DoomSystem - this.doomSystem = new DoomSystem(this); - - // Choose bppMode depending on CVar's - // TODO: add config options - this.bppMode = BppMode.chooseBppMode(cVarManager); - - // Create real time ticker - this.RealTime = new MilliTicker(); - - // Doommain is both "main" and handles most of the game status. - this.gameNetworking = this; // DoomMain also handles its own Game Networking. - - // Set ticker. It is a shared status object, but not a holder itself. - this.ticker = ITicker.createTicker(cVarManager); - - // Network "driver" - this.systemNetworking = new DummyNetworkDriver<>(this); - - // Random number generator, but we can have others too. - this.random = new DelegateRandom(); - LOGGER.log(Level.INFO, String.format("M_Random: Using %s.", random.getClass().getSimpleName())); - - // Sound can be left until later, in Start - this.wadLoader = new WadLoader(this.doomSystem); // The wadloader is a "weak" status holder. - - // TODO: find out if we have requests for a specific resolution, - // and try honouring them as closely as possible. - // 23/5/2011: Experimental dynamic resolution subsystem - this.vs = VisualSettings.parse(cVarManager, CM); - this.spriteManager = new SpriteManager<>(this); - - // Heads-up, Menu, Level Loader - this.headsUp = new HU(this); - this.menu = new Menu<>(this); - this.levelLoader = new BoomLevelLoader(this); - - // Renderer, Actions, StatusBar, AutoMap - this.sceneRenderer = bppMode.sceneRenderer(this); - this.actions = new ActionFunctions(this); - this.statusBar = new StatusBar(this); - - // Let the renderer pick its own. It makes linking easier. - this.textureManager = sceneRenderer.getTextureManager(); - // Instantiating EndLevel, Finale - this.endLevel = new EndLevel<>(this); - this.finale = selectFinale(); - - readCVars(); - LOGGER.log(Level.INFO, String.format("W_Init: Init WAD files: [%s]", - Arrays.stream(wadfiles).filter(Objects::nonNull).collect(Collectors.joining(", ")))); - try { - wadLoader.InitMultipleFiles(wadfiles); - } catch (Exception e1) { - LOGGER.log(Level.SEVERE, "Could not init WAD files", e1); - } - - // Video Renderer - this.graphicSystem = RendererFactory.newBuilder() - .setVideoScale(vs).setBppMode(bppMode).setWadLoader(wadLoader) - .build(); - - LOGGER.log(Level.INFO, "V_Init: Allocate screens."); - - // Disk access visualizer - this.diskDrawer = new DiskDrawer(this, DiskDrawer.STDISK); - - // init subsystems - LOGGER.log(Level.INFO, "AM_Init: Init Automap colors."); - this.autoMap = new automap.Map<>(this); - - this.wiper = graphicSystem.createWiper(random); - - // Update variables and stuff NOW. - this.update(); - - // Check for -file in shareware - CheckForPWADSInShareware(); - - printGameInfo(); - - LOGGER.log(Level.INFO, "Tables.InitTables: Init trigonometric LUTs."); - Tables.InitTables(); - - LOGGER.log(Level.INFO, "M_Init: Init miscellaneous info."); - menu.Init(); - - LOGGER.log(Level.INFO, "R_Init: Init DOOM refresh daemon."); - sceneRenderer.Init(); - - LOGGER.log(Level.INFO, "P_Init: Init Playloop state."); - actions.Init(); - - LOGGER.log(Level.INFO, "I_Init: Setting up machine state."); - doomSystem.Init(); - - LOGGER.log(Level.INFO, "D_CheckNetGame: Checking network game status."); - CheckNetGame(); - - LOGGER.log(Level.INFO, "S_Init: Setting up sound."); - // Sound "drivers" before the game sound controller. - this.music = IMusic.chooseModule(cVarManager); - this.soundDriver = ISoundDriver.chooseModule(this, cVarManager); - this.doomSound = IDoomSound.chooseSoundIsPresent(this, cVarManager, soundDriver); - - music.InitMusic(); - doomSound.Init(snd_SfxVolume * 8, snd_MusicVolume * 8); - - LOGGER.log(Level.INFO, "HU_Init: Setting up heads up display."); - headsUp.Init(); - - LOGGER.log(Level.INFO, "ST_Init: Init status bar."); - statusBar.Init(); - - if (statcopy != null) { - LOGGER.log(Level.INFO, "External statistics registered."); - } - - // NOW it's safe to init the disk reader. - diskDrawer.Init(); - } - - @Override - public final void update() { - super.update(); - // Video...so you should wait until video renderer is active. - this.graphicSystem.setUsegamma(CM.getValue(Settings.usegamma, Integer.class)); - - // These should really be handled by the menu. - this.menu.setShowMessages(CM.equals(Settings.show_messages, 1)); - this.menu.setScreenBlocks(CM.getValue(Settings.screenblocks, Integer.class)); - - // These should be handled by the HU - for (int i = 0; i <= 9; i++) { - - String chatmacro = String.format("chatmacro%d", i); - this.headsUp.setChatMacro(i, CM.getValue(Settings.valueOf(chatmacro), String.class)); - } - } - - @Override - public final void commit() { - super.commit(); - // Video... - CM.update(Settings.usegamma, graphicSystem.getUsegamma()); - - // These should really be handled by the menu. - CM.update(Settings.show_messages, this.menu.getShowMessages()); - CM.update(Settings.screenblocks, this.menu.getScreenBlocks()); - - // These should be handled by the HU - for (int i = 0; i <= 9; i++) { - CM.update(Settings.valueOf(String.format("chatmacro%d", i)), this.headsUp.chat_macros[i]); - } - } - - public void setupLoop() throws IOException { - // check for a driver that wants intermission stats - cVarManager.with(CommandVariable.STATCOPY, 0, (String s) -> { - // TODO: this should be chained to a logger - statcopy = s; - LOGGER.log(Level.INFO, "External statistics registered."); - }); - - // start the apropriate game based on parms - cVarManager.with(CommandVariable.RECORD, 0, (String s) -> { - RecordDemo(s); - autostart = true; - }); - - //p = CM.CheckParm ("-timedemo"); - ChooseLoop: - { - if (singletics) { - TimeDemo(loaddemo); - autostart = true; - break ChooseLoop; // DoomLoop(); // never returns - } - - if (fastdemo || normaldemo) { - singledemo = true; // quit after one demo - if (fastdemo) { - timingdemo = true; - } - InitNew(startskill, startepisode, startmap); - gamestate = GS_DEMOSCREEN; - DeferedPlayDemo(loaddemo); - break ChooseLoop; // DoomLoop(); // never returns - } - - if (gameaction != ga_loadgame) { - if (autostart || netgame) { - InitNew(startskill, startepisode, startmap); - } else { - StartTitle(); // start up intro loop - } - } - } - - DoomLoop(); // never returns - } - - private void printGameInfo() { - // Iff additonal PWAD files are used, print modified banner - if (modifiedgame) // Generate WAD loading alert. Abort upon denial. - { - if (!doomSystem.GenerateAlert(Strings.MODIFIED_GAME_TITLE, Strings.MODIFIED_GAME_DIALOG, true)) { - wadLoader.CloseAllHandles(); - System.exit(-2); - } - } - - // Check and print which version is executed. - switch (getGameMode()) { - case shareware: - case indetermined: - LOGGER.log(Level.INFO, "Game Info: Shareware!"); - break; - case registered: - case retail: - case commercial: - case pack_tnt: - case pack_plut: - case pack_xbla: - LOGGER.log(Level.INFO, "Game Info: Commercial product - do not distribute!"); - LOGGER.log(Level.INFO, "Game Note: Please report software piracy to the SPA: 1-800-388-PIR8"); - break; - case freedoom1: - case freedoom2: - case freedm: - LOGGER.log(Level.INFO, "Game Info: Copyright (c) 2001-2017 Contributors to the Freedoom project. All rights reserved."); - LOGGER.log(Level.INFO, "Game Note: See https://github.com/freedoom/freedoom/blob/master/COPYING.adoc"); - break; - default: - // Ouch. - break; - } - } - - private void readCVars() { - /** - * D_DoomMain - * - * Completes the job started by Init. Here everything priority-critical is called and created in more detail. - */ - - final StringBuffer file = new StringBuffer(); - final String iwadfilename = IdentifyVersion(); - nomonsters = cVarManager.bool(CommandVariable.NOMONSTERS); - respawnparm = cVarManager.bool(CommandVariable.RESPAWN); - fastparm = cVarManager.bool(CommandVariable.FAST); - devparm = cVarManager.bool(CommandVariable.DEVPARM); - - if (!(altdeath = cVarManager.bool(CommandVariable.ALTDEATH))) { - deathmatch = cVarManager.bool(CommandVariable.DEATHMATCH); - } - - // MAES: Check for Ultimate Doom in "doom.wad" filename. - final WadLoader tmpwad = new WadLoader(); - try { - tmpwad.InitFile(iwadfilename); - // Check using a reloadable hack. - CheckForUltimateDoom(tmpwad); - } catch (Exception e2) { - // TODO Auto-generated catch block - LOGGER.log(Level.SEVERE, "Failure reading CVars.", e2); - } finally { - tmpwad.CloseAllHandles(); - } - - // MAES: better extract a method for this. - GenerateTitle(); - // Print ticker info. It has already been set at Init() though. - if (cVarManager.bool(CommandVariable.MILLIS)) { - LOGGER.log(Level.INFO, "ITicker: Using millisecond accuracy timer."); - } else if (cVarManager.bool(CommandVariable.FASTTIC)) { - LOGGER.log(Level.INFO, "ITicker: Using fastest possible timer."); - } else { - LOGGER.log(Level.INFO, "ITicker: Using nanosecond accuracy timer."); - } - LOGGER.log(Level.INFO, title.toString()); - if (devparm) { - LOGGER.log(Level.INFO, D_DEVSTR); - } - // Running from CDROM? - if (cVarManager.bool(CommandVariable.CDROM)) { - LOGGER.log(Level.INFO, D_CDROM); - //System.get("c:\\doomdata",0); - //System.out.println (Settings.basedefault+"c:/doomdata/default.cfg"); - } - // turbo option - if (cVarManager.specified(CommandVariable.TURBO)) { - int scale = 200; - if (cVarManager.present(CommandVariable.TURBO)) { - scale = cVarManager.get(CommandVariable.TURBO, Integer.class, 0).get(); - } - if (scale < 10) { - scale = 10; - } - if (scale > 400) { - scale = 400; - } - LOGGER.log(Level.INFO, String.format("turbo scale: %d", scale)); - forwardmove[0] = forwardmove[0] * scale / 100; - forwardmove[1] = forwardmove[1] * scale / 100; - sidemove[0] = sidemove[0] * scale / 100; - sidemove[1] = sidemove[1] * scale / 100; - } - // add any files specified on the command line with -file wadfile - // to the wad list - // - // convenience hack to allow -wart e m to add a wad file - // prepend a tilde to the filename so wadfile will be reloadable - if (cVarManager.present(CommandVariable.WART)) { - final int ep = cVarManager.get(CommandVariable.WART, Integer.class, 0).get(); - final int map = cVarManager.get(CommandVariable.WART, Integer.class, 1).get(); - cVarManager.override(CommandVariable.WARP, new CommandVariable.WarpFormat(ep * 10 + map), 0); - GameMode gamemode = getGameMode(); - // Map name handling. - switch (gamemode) { - case shareware: - case retail: - case registered: - case freedoom1: - file.append("~"); - file.append(DEVMAPS); - file.append(String.format("E%dM%d.wad", ep, map)); - file.append(String.format("Warping to Episode %s, Map %s.\n", ep, map)); - break; - case commercial: - case freedoom2: - case freedm: - default: - if (ep < 10) { - file.append("~"); - file.append(DEVMAPS); - file.append(String.format("cdata/map0%d.wad", ep)); - } else { - file.append("~"); - file.append(DEVMAPS); - file.append(String.format("cdata/map%d.wad", ep)); - } - break; - } - AddFile(file.toString()); - } - - if (cVarManager.present(CommandVariable.FILE)) { - // the parms after p are wadfile/lump names, - // until end of parms or another - preceded parm - modifiedgame = true; // homebrew levels - cVarManager.with(CommandVariable.FILE, 0, (String[] a) -> { - Arrays.stream(a) - .map(s -> C2JUtils.unquoteIfQuoted(s, '"')) - .forEach(this::AddFile); - }); - } - - if (cVarManager.present(CommandVariable.PLAYDEMO)) { - normaldemo = true; - loaddemo = cVarManager.get(CommandVariable.PLAYDEMO, String.class, 0).get(); - } else if (cVarManager.present(CommandVariable.FASTDEMO)) { - LOGGER.log(Level.INFO, "Fastdemo mode. Boundless clock!"); - fastdemo = true; - loaddemo = cVarManager.get(CommandVariable.FASTDEMO, String.class, 0).get(); - } else if (cVarManager.present(CommandVariable.TIMEDEMO)) { - singletics = true; - loaddemo = cVarManager.get(CommandVariable.TIMEDEMO, String.class, 0).get(); - } - - // If any of the previous succeeded, try grabbing the filename. - if (loaddemo != null) { - loaddemo = C2JUtils.unquoteIfQuoted(loaddemo, '"'); - AddFile(loaddemo + ".lmp"); - LOGGER.log(Level.INFO, String.format("Playing demo %s.lmp.", loaddemo)); - autostart = true; - } - - // Subsequent uses of loaddemo use only the lump name. - loaddemo = C2JUtils.extractFileBase(loaddemo, 0, true); - // get skill / episode / map from parms - // FIXME: should get them FROM THE DEMO itself. - startskill = skill_t.sk_medium; - startepisode = 1; - startmap = 1; - //autostart = false; - - if (cVarManager.present(CommandVariable.NOVERT)) { - novert = cVarManager.get(CommandVariable.NOVERT, CommandVariable.ForbidFormat.class, 0) - .filter(CommandVariable.ForbidFormat.FORBID::equals) - .isPresent(); - - if (!novert) { - LOGGER.log(Level.INFO, "-novert ENABLED (default)"); - } else { - LOGGER.log(Level.INFO, "-novert DISABLED. Hope you know what you're doing..."); - } - } - - cVarManager.with(CommandVariable.SKILL, 0, (Integer s) -> { - startskill = skill_t.values()[s - 1]; - autostart = true; - }); - - cVarManager.with(CommandVariable.EPISODE, 0, (Integer ep) -> { - startepisode = ep; - startmap = 1; - autostart = true; - }); - - if (cVarManager.present(CommandVariable.TIMER) && deathmatch) { - // Good Sign (2017/03/31) How this should work? - final int time = cVarManager.get(CommandVariable.TIMER, Integer.class, 0).get(); - LOGGER.log(Level.INFO, String.format("Levels will end after %d minute(s)", time)); - } - // OK, and exactly how is this enforced? - if (cVarManager.bool(CommandVariable.AVG) && deathmatch) { - LOGGER.log(Level.INFO, "Austin Virtual Gaming: Levels will end after 20 minutes"); - } - - // MAES 31/5/2011: added support for +map variation. - cVarManager.with(CommandVariable.WARP, 0, (CommandVariable.WarpFormat w) -> { - final CommandVariable.WarpMetric metric = w.getMetric(isCommercial()); - startepisode = metric.getEpisode(); - startmap = metric.getMap(); - autostart = true; - }); - - // Maes: 1/6/2011 Added +map support - cVarManager.with(CommandVariable.MAP, 0, (CommandVariable.MapFormat m) -> { - final CommandVariable.WarpMetric metric = m.getMetric(isCommercial()); - startepisode = metric.getEpisode(); - startmap = metric.getMap(); - autostart = true; - }); - - cVarManager.with(CommandVariable.LOADGAME, 0, (Character c) -> { - file.delete(0, file.length()); - if (cVarManager.bool(CommandVariable.CDROM)) { - file.append("c:\\doomdata\\"); - } - - file.append(String.format("%s%d.dsg", SAVEGAMENAME, c)); - LoadGame(file.toString()); - }); - } - - /** - * Since it's so intimately tied, it's less troublesome to merge the "main" and "network" code. - */ - /** To be initialized by the DoomNetworkingInterface via a setter */ - //private doomcom_t doomcom; - //private doomdata_t netbuffer; // points inside doomcom - protected StringBuilder sb = new StringBuilder(); - - // - // NETWORKING - // - // gametic is the tic about to (or currently being) run - // maketic is the tick that hasn't had control made for it yet - // nettics[] has the maketics for all players - // - // a gametic cannot be run until nettics[] > gametic for all players - // - //ticcmd_t[] localcmds= new ticcmd_t[BACKUPTICS]; - //ticcmd_t [][] netcmds=new ticcmd_t [MAXPLAYERS][BACKUPTICS]; - int[] nettics = new int[MAXNETNODES]; - boolean[] nodeingame = new boolean[MAXNETNODES]; // set false as nodes leave game - boolean[] remoteresend = new boolean[MAXNETNODES]; // set when local needs tics - int[] resendto = new int[MAXNETNODES]; // set when remote needs tics - int[] resendcount = new int[MAXNETNODES]; - - int[] nodeforplayer = new int[MAXPLAYERS]; - - int maketic; - int lastnettic; - int skiptics; - protected int ticdup; - - public int getTicdup() { - return ticdup; - } - - public void setTicdup(int ticdup) { - this.ticdup = ticdup; - } - - int maxsend; // BACKUPTICS/(2*ticdup)-1; - - //void D_ProcessEvents (void); - //void G_BuildTiccmd (ticcmd_t *cmd); - //void D_DoAdvanceDemo (void); - // _D_ - boolean reboundpacket = false; - doomdata_t reboundstore = new doomdata_t(); - - /** - * MAES: interesting. After testing it was found to return the following size: - * (8*(netbuffer.numtics+1)); - */ - int NetbufferSize() { - // return (int)(((doomdata_t)0).cmds[netbuffer.numtics]); - return (8 * (netbuffer.numtics + 1)); - } - - protected long NetbufferChecksum() { - // FIXME -endianess? - if (NORMALUNIX) { - return 0; // byte order problems - } - - /** - * Here it was trying to get the length of a doomdata_t struct up to retransmit from. - * l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4; - * (int)&(((doomdata_t *)0)->retransmitfrom) evaluates to "4" - * Therefore, l= (netbuffersize - 4)/4 - */ - final int l = (NetbufferSize() - 4) / 4; - long c = 0x1234567L; - for (int i = 0; i < l; i++) { // TODO: checksum would be better computer in the netbuffer itself. - // The C code actually takes all fields into account. - c += 0;// TODO: (netbuffer->retransmitfrom)[i] * (i+1); - } - return c & NCMD_CHECKSUM; - } - - protected int ExpandTics(int low) { - int delta; - - delta = low - (maketic & 0xff); - - if (delta >= -64 && delta <= 64) { - return (maketic & ~0xff) + low; - } - - if (delta > 64) { - return (maketic & ~0xff) - 256 + low; - } - - if (delta < -64) { - return (maketic & ~0xff) + 256 + low; - } - - doomSystem.Error("ExpandTics: strange value %d at maketic %d", low, maketic); - return 0; - } - - /** - * HSendPacket - * - * Will send out a packet to all involved parties. A special case is the rebound storage, which acts as a local - * "echo" which is then picked up by the host itself. This is necessary to simulate a 1-node network. - * - * @throws IOException - */ - void HSendPacket(int node, int flags) { - netbuffer.checksum = (int) (NetbufferChecksum() | flags); - - // Local node's comms are sent to rebound packet, which is - // then picked up again. THIS IS VITAL FOR SINGLE-PLAYER - // SPEED THROTTLING TOO, AS IT RELIES ON NETWORK ACKS/BUSY - // WAITING. - if (node == 0) { - // _D_ - reboundstore.copyFrom(netbuffer); - reboundpacket = true; - return; - } - - if (demoplayback) { - return; - } - - if (!netgame) { - doomSystem.Error("Tried to transmit to another node"); - } - - doomcom.command = CMD_SEND; - doomcom.remotenode = (short) node; - doomcom.datalength = (short) NetbufferSize(); - - if (debugfile != null) { - int i; - int realretrans; - if (flags(netbuffer.checksum, NCMD_RETRANSMIT)) { - realretrans = ExpandTics(netbuffer.retransmitfrom); - } else { - realretrans = -1; - } - - logger(debugfile, "send (" + ExpandTics(netbuffer.starttic) + ", " + netbuffer.numtics + ", R " - + realretrans + "[" + doomcom.datalength + "]"); - - for (i = 0; i < doomcom.datalength; i++) // TODO: get a serialized string representation. - { - logger(debugfile, netbuffer.toString() + "\n"); - } - } - - // This should execute a "send" command for the current stuff in doomcom. - systemNetworking.NetCmd(); - } - - // - // HGetPacket - // Returns false if no packet is waiting - // - private boolean HGetPacket() { - // Fugly way of "clearing" the buffer. - sb.setLength(0); - if (reboundpacket) { - // FIXME: MAES: this looks like a struct copy - netbuffer.copyFrom(reboundstore); - doomcom.remotenode = 0; - reboundpacket = false; - return true; - } - - // If not actually a netgame (e.g. single player, demo) return. - if (!netgame) { - return false; - } - - if (demoplayback) { - return false; - } - - doomcom.command = CMD_GET; - systemNetworking.NetCmd(); - - // Invalid node? - if (doomcom.remotenode == -1) { - return false; - } - - if (doomcom.datalength != NetbufferSize()) { - if (eval(debugfile)) { - logger(debugfile, "bad packet length " + doomcom.datalength + "\n"); - } - return false; - } - - if (NetbufferChecksum() != (netbuffer.checksum & NCMD_CHECKSUM)) { - if (eval(debugfile)) { - logger(debugfile, "bad packet checksum\n"); - } - return false; - } - - if (eval(debugfile)) { - int realretrans; - int i; - - if (flags(netbuffer.checksum, NCMD_SETUP)) { - logger(debugfile, "setup packet\n"); - } else { - if (flags(netbuffer.checksum, NCMD_RETRANSMIT)) { - realretrans = ExpandTics(netbuffer.retransmitfrom); - } else { - realretrans = -1; - } - - sb.append("get "); - sb.append(doomcom.remotenode); - sb.append(" = ("); - sb.append(ExpandTics(netbuffer.starttic)); - sb.append(" + "); - sb.append(netbuffer.numtics); - sb.append(", R "); - sb.append(realretrans); - sb.append(")["); - sb.append(doomcom.datalength); - sb.append("]"); - - logger(debugfile, sb.toString()); - - // Trick: force update of internal buffer. - netbuffer.pack(); - - /** - * TODO: Could it be actually writing stuff beyond the boundaries of a single doomdata object? - * A doomcom object has a lot of header info, and a single "raw" data placeholder, which by now - * should be inside netbuffer....right? - **/ - try { - for (i = 0; i < doomcom.datalength; i++) { - debugfile.write(Integer.toHexString(netbuffer.cached()[i])); - debugfile.write('\n'); - } - } catch (IOException e) { - } // "Drown" IOExceptions here. - } - } - return true; - } - - //// GetPackets - StringBuilder exitmsg = new StringBuilder(80); - - public void GetPackets() { - int netconsole; - int netnode; - ticcmd_t src, dest; - int realend; - int realstart; - - while (HGetPacket()) { - if (flags(netbuffer.checksum, NCMD_SETUP)) { - continue; // extra setup packet - } - netconsole = netbuffer.player & ~PL_DRONE; - netnode = doomcom.remotenode; - - // to save bytes, only the low byte of tic numbers are sent - // Figure out what the rest of the bytes are - realstart = ExpandTics(netbuffer.starttic); - realend = (realstart + netbuffer.numtics); - - // check for exiting the game - if (flags(netbuffer.checksum, NCMD_EXIT)) { - if (!nodeingame[netnode]) { - continue; - } - nodeingame[netnode] = false; - playeringame[netconsole] = false; - exitmsg.insert(0, "Player 1 left the game"); - exitmsg.setCharAt(7, (char) (exitmsg.charAt(7) + netconsole)); - players[consoleplayer].message = exitmsg.toString(); - if (demorecording) { - CheckDemoStatus(); - } - continue; - } - - // check for a remote game kill - if (flags(netbuffer.checksum, NCMD_KILL)) { - doomSystem.Error("Killed by network driver"); - } - - nodeforplayer[netconsole] = netnode; - - // check for retransmit request - if (resendcount[netnode] <= 0 - && flags(netbuffer.checksum, NCMD_RETRANSMIT)) { - resendto[netnode] = ExpandTics(netbuffer.retransmitfrom); - if (eval(debugfile)) { - sb.setLength(0); - sb.append("retransmit from "); - sb.append(resendto[netnode]); - sb.append('\n'); - logger(debugfile, sb.toString()); - resendcount[netnode] = RESENDCOUNT; - } - } else { - resendcount[netnode]--; - } - - // check for out of order / duplicated packet - if (realend == nettics[netnode]) { - continue; - } - - if (realend < nettics[netnode]) { - if (eval(debugfile)) { - sb.setLength(0); - sb.append("out of order packet ("); - sb.append(realstart); - sb.append(" + "); - sb.append(netbuffer.numtics); - sb.append(")\n"); - logger(debugfile, sb.toString()); - } - continue; - } - - // check for a missed packet - if (realstart > nettics[netnode]) { - // stop processing until the other system resends the missed tics - if (eval(debugfile)) { - sb.setLength(0); - sb.append("missed tics from "); - sb.append(netnode); - sb.append(" ("); - sb.append(realstart); - sb.append(" - "); - sb.append(nettics[netnode]); - sb.append(")\n"); - logger(debugfile, sb.toString()); - } - remoteresend[netnode] = true; - continue; - } - - // update command store from the packet - { - int start; - - remoteresend[netnode] = false; - - start = nettics[netnode] - realstart; - src = netbuffer.cmds[start]; - - while (nettics[netnode] < realend) { - dest = netcmds[netconsole][nettics[netnode] % BACKUPTICS]; - nettics[netnode]++; - // MAES: this is a struct copy. - src.copyTo(dest); - // Advance src - start++; - - //_D_: had to add this (see linuxdoom source). That fixed that damn consistency failure!!! - if (start < netbuffer.cmds.length) { - src = netbuffer.cmds[start]; - } - - } - } - } - } - - protected void logger(OutputStreamWriter debugfile, String string) { - try { - debugfile.write(string); - } catch (IOException e) { - // TODO Auto-generated catch block - LOGGER.log(Level.SEVERE, "Failure writing debug file.", e); - } - } - - int gametime; - - @Override - public void NetUpdate() { - int nowtime; - int newtics; - int i, j; - int realstart; - int gameticdiv; - - // check time - nowtime = ticker.GetTime() / ticdup; - newtics = nowtime - gametime; - gametime = nowtime; - - if (newtics <= 0) { // nothing new to update - // listen for other packets - GetPackets(); - } else { - - if (skiptics <= newtics) { - newtics -= skiptics; - skiptics = 0; - } else { - skiptics -= newtics; - newtics = 0; - } - - netbuffer.player = (byte) consoleplayer; - - // build new ticcmds for console player - gameticdiv = gametic / ticdup; - for (i = 0; i < newtics; i++) { - //videoInterface.StartTic(); - ProcessEvents(); - if (maketic - gameticdiv >= BACKUPTICS / 2 - 1) { - break; // can't hold any more - } - //System.out.printf ("mk:%d ",maketic); - BuildTiccmd(localcmds[maketic % BACKUPTICS]); - maketic++; - } - - if (singletics) { - return; // singletic update is syncronous - } - // send the packet to the other nodes - for (i = 0; i < doomcom.numnodes; i++) { - if (nodeingame[i]) { - netbuffer.starttic = (byte) (realstart = resendto[i]); - netbuffer.numtics = (byte) (maketic - realstart); - if (netbuffer.numtics > BACKUPTICS) { - doomSystem.Error("NetUpdate: netbuffer.numtics > BACKUPTICS"); - } - - resendto[i] = maketic - doomcom.extratics; - - for (j = 0; j < netbuffer.numtics; j++) { - localcmds[(realstart + j) % BACKUPTICS].copyTo(netbuffer.cmds[j]); - } - // MAES: one of _D_ fixes. - //netbuffer.cmds[j] = localcmds[(realstart+j)%BACKUPTICS]; - - if (remoteresend[i]) { - netbuffer.retransmitfrom = (byte) nettics[i]; - HSendPacket(i, NCMD_RETRANSMIT); - } else { - netbuffer.retransmitfrom = 0; - HSendPacket(i, 0); - } - } - } - GetPackets(); - } - } - - // - // CheckAbort - // - private void CheckAbort() { - event_t ev; - int stoptic; - - stoptic = ticker.GetTime() + 2; - while (ticker.GetTime() < stoptic) { - } - //videoInterface.StartTic (); - - //videoInterface.StartTic (); - for (; eventtail != eventhead; eventtail = (++eventtail) & (MAXEVENTS - 1)) { - ev = events[eventtail]; - if (ev.isKey(SC_ESCAPE, ev_keydown)) { - doomSystem.Error("Network game synchronization aborted."); - } - } - } - - boolean[] gotinfo = new boolean[MAXNETNODES]; - - /** - * D_ArbitrateNetStart - * @throws IOException - * - * - */ - public void ArbitrateNetStart() throws IOException { - int i; - autostart = true; - - // Clear it up... - memset(gotinfo, false, gotinfo.length); - if (doomcom.consoleplayer != 0) { - // listen for setup info from key player - LOGGER.log(Level.INFO, "listening for network start info..."); - while (true) { - CheckAbort(); - if (!HGetPacket()) { - continue; - } - if (flags(netbuffer.checksum, NCMD_SETUP)) { - if (netbuffer.player != VERSION) { - doomSystem.Error("Different DOOM versions cannot play a net game!"); - } - startskill = skill_t.values()[netbuffer.retransmitfrom & 15]; - - if (((netbuffer.retransmitfrom & 0xc0) >> 6) == 1) { - // Deathmatch - deathmatch = true; - } else if (((netbuffer.retransmitfrom & 0xc0) >> 6) == 2) { - // Cooperative - altdeath = true; - } - - nomonsters = (netbuffer.retransmitfrom & 0x20) > 0; - respawnparm = (netbuffer.retransmitfrom & 0x10) > 0; - startmap = netbuffer.starttic & 0x3f; - startepisode = netbuffer.starttic >> 6; - return; - } - } - } else { - // key player, send the setup info - LOGGER.log(Level.INFO, "sending network start info..."); - do { - CheckAbort(); - for (i = 0; i < doomcom.numnodes; i++) { - netbuffer.retransmitfrom = (byte) startskill.ordinal(); - if (deathmatch) { - netbuffer.retransmitfrom |= (1 << 6); - } else if (altdeath) { - netbuffer.retransmitfrom |= (2 << 6); - } - - if (nomonsters) { - netbuffer.retransmitfrom |= 0x20; - } - - if (respawnparm) { - netbuffer.retransmitfrom |= 0x10; - } - - netbuffer.starttic = (byte) (startepisode * 64 + startmap); - netbuffer.player = VERSION; - netbuffer.numtics = 0; - HSendPacket(i, NCMD_SETUP); - } - - //#if 1 - for (i = 10; (i > 0) && HGetPacket(); --i) { - if ((netbuffer.player & 0x7f) < MAXNETNODES) { - gotinfo[netbuffer.player & 0x7f] = true; - } - } - /* - while (HGetPacket ()) - { - gotinfo[netbuffer.player&0x7f] = true; - } - */ - - for (i = 1; i < doomcom.numnodes; i++) { - if (!gotinfo[i]) { - break; - } - } - } while (i < doomcom.numnodes); - } - } - - /** - * D_CheckNetGame - * Works out player numbers among the net participants - **/ - private void CheckNetGame() throws IOException { - for (int i = 0; i < MAXNETNODES; i++) { - nodeingame[i] = false; - nettics[i] = 0; - remoteresend[i] = false; // set when local needs tics - resendto[i] = 0; // which tic to start sending - } - - // I_InitNetwork sets doomcom and netgame - systemNetworking.InitNetwork(); - if (doomcom.id != DOOMCOM_ID) { - doomSystem.Error("Doomcom buffer invalid!"); - } - - // Maes: This is the only place where netbuffer is definitively set to something - netbuffer = doomcom.data; - consoleplayer = displayplayer = doomcom.consoleplayer; - if (netgame) { - ArbitrateNetStart(); - } - - LOGGER.log(Level.FINE, String.format("startskill %s, deathmatch: %s, startmap: %d, startepisode: %d", - startskill.toString(), Boolean.toString(deathmatch), startmap, startepisode)); - - // read values out of doomcom - ticdup = doomcom.ticdup; - // MAES: ticdup must not be zero at this point. Obvious, no? - maxsend = BACKUPTICS / (2 * ticdup) - 1; - if (maxsend < 1) { - maxsend = 1; - } - - for (int i = 0; i < doomcom.numplayers; i++) { - playeringame[i] = true; - } - - for (int i = 0; i < doomcom.numnodes; i++) { - nodeingame[i] = true; - } - - LOGGER.log(Level.INFO, String.format("Player %d of %d (%d node(s))", (consoleplayer + 1), doomcom.numplayers, doomcom.numnodes)); - } - - /** - * D_QuitNetGame - * Called before quitting to leave a net game - * without hanging the other players - **/ - @Override - public void QuitNetGame() throws IOException { - if (eval(debugfile)) { - try { - debugfile.close(); - } catch (IOException e) { - LOGGER.log(Level.SEVERE, "Quit net game failure.", e); - } - } - - if (!netgame || !usergame || consoleplayer == -1 || demoplayback) { - return; - } - - // send a bunch of packets for security - netbuffer.player = (byte) consoleplayer; - netbuffer.numtics = 0; - for (int i = 0; i < 4; i++) { - for (int j = 1; j < doomcom.numnodes; j++) { - if (nodeingame[j]) { - HSendPacket(j, NCMD_EXIT); - } - } - doomSystem.WaitVBL(1); - } - } - - /** - * TryRunTics - **/ - int[] frametics = new int[4]; - int frameon; - boolean[] frameskip = new boolean[4]; - int oldnettics; - int oldentertics; - - @Override - public void TryRunTics() throws IOException { - int i; - int lowtic; - int entertic; - - int realtics; - int availabletics; - int counts; - int numplaying; - - // get real tics - entertic = ticker.GetTime() / ticdup; - realtics = entertic - oldentertics; - oldentertics = entertic; - - //System.out.printf("Entertic %d, realtics %d, oldentertics %d\n",entertic,realtics,oldentertics); - // get available tics - NetUpdate(); - - lowtic = MAXINT; - numplaying = 0; - for (i = 0; i < doomcom.numnodes; i++) { - if (nodeingame[i]) { - numplaying++; - if (nettics[i] < lowtic) { - lowtic = nettics[i]; - } - } - } - availabletics = lowtic - gametic / ticdup; - - // decide how many tics to run - if (realtics < availabletics - 1) { - counts = realtics + 1; - } else if (realtics < availabletics) { - counts = realtics; - } else { - counts = availabletics; - } - - if (counts < 1) { - counts = 1; - } - - frameon++; - - if (eval(debugfile)) { - sb.setLength(0); - sb.append("=======real: "); - sb.append(realtics); - sb.append(" avail: "); - sb.append(availabletics); - sb.append(" game: "); - sb.append(counts); - sb.append("\n"); - debugfile.write(sb.toString()); - } - - if (!demoplayback) { - // ideally nettics[0] should be 1 - 3 tics above lowtic - // if we are consistantly slower, speed up time - for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i]) { - break; - } - } - if (consoleplayer == i) { - // the key player does not adapt - } else { - if (nettics[0] <= nettics[nodeforplayer[i]]) { - gametime--; - //System.out.print("-"); - } - frameskip[frameon & 3] = oldnettics > nettics[nodeforplayer[i]]; - oldnettics = nettics[0]; - if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3]) { - skiptics = 1; - //System.out.print("+"); - } - } - } // demoplayback - - // wait for new tics if needed - while (lowtic < gametic / ticdup + counts) { - NetUpdate(); - lowtic = MAXINT; - - // Finds the node with the lowest number of tics. - for (i = 0; i < doomcom.numnodes; i++) { - if (nodeingame[i] && nettics[i] < lowtic) { - lowtic = nettics[i]; - } - } - - if (lowtic < gametic / ticdup) { - doomSystem.Error("TryRunTics: lowtic < gametic"); - } - - // don't stay in here forever -- give the menu a chance to work - int time = ticker.GetTime(); - if (time / ticdup - entertic >= 20) { - menu.Ticker(); - return; - } - } - - // run the count * ticdup dics - while (counts-- > 0) { - for (i = 0; i < ticdup; i++) { - if (gametic / ticdup > lowtic) { - doomSystem.Error("gametic>lowtic"); - } - if (advancedemo) { - DoAdvanceDemo(); - } - menu.Ticker(); - Ticker(); - gametic++; - - // modify command for duplicated tics - if (i != ticdup - 1) { - ticcmd_t cmd; - int buf; - int j; - - buf = (gametic / ticdup) % BACKUPTICS; - for (j = 0; j < MAXPLAYERS; j++) { - cmd = netcmds[j][buf]; - cmd.chatchar = 0; - if (flags(cmd.buttons, BT_SPECIAL)) { - cmd.buttons = 0; - } - } - } - } - NetUpdate(); // check for new console commands - } - } - - @Override - public doomcom_t getDoomCom() { - return this.doomcom; - } - - @Override - public void setDoomCom(doomcom_t doomcom) { - this.doomcom = doomcom; - } - - @Override - public void setGameAction(gameaction_t action) { - this.gameaction = action; - } - - @Override - public gameaction_t getGameAction() { - return this.gameaction; - } - - public final VideoScale vs; - - public boolean shouldPollLockingKeys() { - if (keysCleared) { - keysCleared = false; - return true; - } - return false; - } - - private String findFileNameToSave() { - String format = "DOOM%d%d%d%d.png"; - String lbmname = null; - // find a file name to save it to - int[] digit = new int[4]; - int i; - for (i = 0; i <= 9999; i++) { - digit[0] = ((i / 1000) % 10); - digit[1] = ((i / 100) % 10); - digit[2] = ((i / 10) % 10); - digit[3] = (i % 10); - lbmname = String.format(format, digit[0], digit[1], digit[2], digit[3]); - if (!C2JUtils.testReadAccess(lbmname)) { - break; // file doesn't exist - } - } - if (i == 10000) { - doomSystem.Error("M_ScreenShot: Couldn't create a PNG"); - } - return lbmname; - } - - protected final Finale selectFinale() { - return new Finale<>(this); - } - - /** - * M_Screenshot - * - * Currently saves PCX screenshots, and only in devparm. - * Very oldschool ;-) - * - * TODO: add non-devparm hotkey for screenshots, sequential screenshot - * messages, option to save as either PCX or PNG. Also, request - * current palette from VI (otherwise gamma settings and palette effects - * don't show up). - * - */ - public void ScreenShot() { - // find a file name to save it to - final String lbmname = findFileNameToSave(); // file doesn't exist - - if (graphicSystem.writeScreenShot(lbmname, FG)) { - players[consoleplayer].message = SCREENSHOT; - } - } -} - -//$Log: DoomMain.java,v $ -//Revision 1.109 2012/11/06 16:04:58 velktron -//Variables manager less tightly integrated. -// -//Revision 1.108 2012/11/05 17:25:29 velktron -//Fixed tinting system according to SodaHolic's advice. -// -//Revision 1.107 2012/09/27 16:53:46 velktron -//Stupid brokeness prevented -loadgame from working. -// -//Revision 1.106 2012/09/26 23:15:20 velktron -//Parallel renderer restored...sort of. -// -//Revision 1.105 2012/09/26 15:54:22 velktron -//Spritemanager is set up by renderer. -// -//Revision 1.104 2012/09/24 22:36:49 velktron -//Fixed HOM detection. -// -//Revision 1.103 2012/09/24 17:16:22 velktron -//Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD. -// -//Revision 1.101.2.11 2012/09/24 16:58:06 velktron -//TrueColor, Generics. -// -//Revision 1.101.2.10 2012/09/21 16:17:25 velktron -//More generic. -// -//Revision 1.101.2.9 2012/09/20 14:25:13 velktron -//Unified DOOM!!! -// +package doom; + +import automap.IAutoMap; +import static data.Defines.BACKUPTICS; +import static data.Defines.BTS_PAUSE; +import static data.Defines.BTS_SAVEGAME; +import static data.Defines.BTS_SAVEMASK; +import static data.Defines.BTS_SAVESHIFT; +import static data.Defines.BT_ATTACK; +import static data.Defines.BT_CHANGE; +import static data.Defines.BT_SPECIAL; +import static data.Defines.BT_SPECIALMASK; +import static data.Defines.BT_USE; +import static data.Defines.BT_WEAPONSHIFT; +import static data.Defines.JAVARANDOM_MASK; +import static data.Defines.NORMALUNIX; +import static data.Defines.NUMWEAPONS; +import static data.Defines.PST_DEAD; +import static data.Defines.PST_LIVE; +import static data.Defines.PST_REBORN; +import static data.Defines.PU_CACHE; +import static data.Defines.PU_STATIC; +import static data.Defines.SKYFLATNAME; +import static data.Defines.TICRATE; +import static data.Defines.TOCENTER; +import static data.Defines.VERSION; +import static data.Limits.MAXEVENTS; +import static data.Limits.MAXINT; +import static data.Limits.MAXNETNODES; +import static data.Limits.MAXPLAYERS; +import data.Tables; +import static data.Tables.ANG45; +import static data.Tables.ANGLETOFINESHIFT; +import static data.Tables.finecosine; +import static data.Tables.finesine; +import data.dstrings; +import static data.dstrings.DEVMAPS; +import static data.dstrings.SAVEGAMENAME; +import static data.info.mobjinfo; +import static data.info.states; +import data.mapthing_t; +import data.mobjtype_t; +import data.sounds.musicenum_t; +import data.sounds.sfxenum_t; +import defines.DoomVersion; +import defines.GameMission_t; +import defines.GameMode; +import defines.Language_t; +import defines.gamestate_t; +import static defines.gamestate_t.GS_DEMOSCREEN; +import static defines.gamestate_t.GS_FINALE; +import static defines.gamestate_t.GS_INTERMISSION; +import static defines.gamestate_t.GS_LEVEL; +import static defines.gamestate_t.GS_MINUS_ONE; +import defines.skill_t; +import defines.statenum_t; +import demo.IDemoTicCmd; +import demo.VanillaDoomDemo; +import demo.VanillaTiccmd; +import static doom.NetConsts.CMD_GET; +import static doom.NetConsts.CMD_SEND; +import static doom.NetConsts.DOOMCOM_ID; +import static doom.NetConsts.NCMD_CHECKSUM; +import static doom.NetConsts.NCMD_EXIT; +import static doom.NetConsts.NCMD_KILL; +import static doom.NetConsts.NCMD_RETRANSMIT; +import static doom.NetConsts.NCMD_SETUP; +import doom.SourceCode.CauseOfDesyncProbability; +import doom.SourceCode.D_Main; +import static doom.SourceCode.D_Main.D_DoomLoop; +import static doom.SourceCode.D_Main.D_ProcessEvents; +import doom.SourceCode.G_Game; +import static doom.SourceCode.G_Game.G_BeginRecording; +import static doom.SourceCode.G_Game.G_BuildTiccmd; +import static doom.SourceCode.G_Game.G_CheckSpot; +import static doom.SourceCode.G_Game.G_DeathMatchSpawnPlayer; +import static doom.SourceCode.G_Game.G_DoCompleted; +import static doom.SourceCode.G_Game.G_DoLoadGame; +import static doom.SourceCode.G_Game.G_DoLoadLevel; +import static doom.SourceCode.G_Game.G_DoNewGame; +import static doom.SourceCode.G_Game.G_DoPlayDemo; +import static doom.SourceCode.G_Game.G_DoReborn; +import static doom.SourceCode.G_Game.G_DoSaveGame; +import static doom.SourceCode.G_Game.G_InitNew; +import static doom.SourceCode.G_Game.G_Responder; +import static doom.SourceCode.G_Game.G_Ticker; +import static doom.englsh.D_CDROM; +import static doom.englsh.D_DEVSTR; +import static doom.englsh.GGSAVED; +import static doom.englsh.SCREENSHOT; +import static doom.evtype_t.ev_joystick; +import static doom.evtype_t.ev_keydown; +import static doom.evtype_t.ev_keyup; +import static doom.evtype_t.ev_mouse; +import static doom.gameaction_t.ga_completed; +import static doom.gameaction_t.ga_failure; +import static doom.gameaction_t.ga_loadgame; +import static doom.gameaction_t.ga_loadlevel; +import static doom.gameaction_t.ga_newgame; +import static doom.gameaction_t.ga_nothing; +import static doom.gameaction_t.ga_playdemo; +import static doom.gameaction_t.ga_savegame; +import static doom.gameaction_t.ga_screenshot; +import static doom.gameaction_t.ga_victory; +import static doom.gameaction_t.ga_worlddone; +import f.EndLevel; +import f.Finale; +import f.Wiper; +import g.Signals; +import static g.Signals.ScanCode.SC_CAPSLK; +import static g.Signals.ScanCode.SC_DOWN; +import static g.Signals.ScanCode.SC_ESCAPE; +import static g.Signals.ScanCode.SC_F12; +import static g.Signals.ScanCode.SC_LALT; +import static g.Signals.ScanCode.SC_LCTRL; +import static g.Signals.ScanCode.SC_LEFT; +import static g.Signals.ScanCode.SC_LSHIFT; +import static g.Signals.ScanCode.SC_NUMKEY2; +import static g.Signals.ScanCode.SC_NUMKEY4; +import static g.Signals.ScanCode.SC_NUMKEY6; +import static g.Signals.ScanCode.SC_NUMKEY8; +import static g.Signals.ScanCode.SC_PAUSE; +import static g.Signals.ScanCode.SC_RALT; +import static g.Signals.ScanCode.SC_RCTRL; +import static g.Signals.ScanCode.SC_RIGHT; +import static g.Signals.ScanCode.SC_RSHIFT; +import static g.Signals.ScanCode.SC_SEMICOLON; +import static g.Signals.ScanCode.SC_UP; +import hu.HU; +import i.DiskDrawer; +import i.DoomSystem; +import i.IDiskDrawer; +import i.IDoomSystem; +import i.Strings; +import java.awt.Rectangle; +import java.io.BufferedInputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.Arrays; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import m.DelegateRandom; +import m.IDoomMenu; +import m.Menu; +import m.MenuMisc; +import m.Settings; +import static m.fixed_t.FRACBITS; +import static m.fixed_t.MAPFRACUNIT; +import mochadoom.Engine; +import mochadoom.Loggers; +import n.DoomSystemNetworking; +import n.DummyNetworkDriver; +import p.AbstractLevelLoader; +import p.ActionFunctions; +import p.BoomLevelLoader; +import p.mobj_t; +import rr.ISpriteManager; +import rr.SceneRenderer; +import rr.SpriteManager; +import rr.TextureManager; +import rr.ViewVars; +import rr.patch_t; +import rr.subsector_t; +import s.IDoomSound; +import s.IMusic; +import s.ISoundDriver; +import savegame.IDoomSaveGame; +import savegame.IDoomSaveGameHeader; +import savegame.VanillaDSG; +import savegame.VanillaDSGHeader; +import st.AbstractStatusBar; +import st.StatusBar; +import timing.ITicker; +import timing.MilliTicker; +import utils.C2JUtils; +import static utils.C2JUtils.eval; +import static utils.C2JUtils.flags; +import static utils.C2JUtils.memcpy; +import static utils.C2JUtils.memset; +import v.DoomGraphicSystem; +import v.renderers.BppMode; +import static v.renderers.DoomScreen.FG; +import v.renderers.RendererFactory; +import v.scale.VideoScale; +import v.scale.VisualSettings; +import w.IWadLoader; +import w.WadLoader; + +// Emacs style mode select -*- Java -*- +//----------------------------------------------------------------------------- +// +// $Id: DoomMain.java,v 1.109 2012/11/06 16:04:58 velktron Exp $ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// DESCRIPTION: +// DOOM main program (D_DoomMain) and game loop (D_DoomLoop), +// plus functions to determine game mode (shareware, registered), +// parse command line parameters, configure game parameters (turbo), +// and call the startup functions. +// +// In Mocha Doom, this was unified with d_game and doomstat.c +// +//----------------------------------------------------------------------------- +@SuppressWarnings({ + "UseOfSystemOutOrSystemErr", + "MalformedFormatString", + "CallToPrintStackTrace", + "override", + "StringBufferMayBeStringBuilder" +}) +public class DoomMain extends DoomStatus implements IDoomGameNetworking, IDoomGame, IDoom { + + private static final Logger LOGGER = Loggers.getLogger(DoomMain.class.getName()); + + public static final String RCSID = "$Id: DoomMain.java,v 1.109 2012/11/06 16:04:58 velktron Exp $"; + + // + // EVENT HANDLING + // + // Events are asynchronous inputs generally generated by the game user. + // Events can be discarded if no responder claims them + // + public final event_t[] events = new event_t[MAXEVENTS]; + public int eventhead; + public int eventtail; + + /** + * D_PostEvent + * Called by the I/O functions when input is detected + */ + public void PostEvent(event_t ev) { + /** + * Do not pollute DOOM's internal event queue - clear keys there + * - Good Sign 2017/04/24 + */ + if (ev == event_t.CANCEL_KEYS) { + // PAINFULLY and FORCEFULLY clear the buttons. + memset(gamekeydown, false, gamekeydown.length); + keysCleared = true; + return; // Nothing more to do here. + } + + events[eventhead] = ev; + eventhead = (++eventhead) & (MAXEVENTS - 1); + } + + /** + * D_ProcessEvents + * Send all the events of the given timestamp down the responder chain + */ + @D_Main.C(D_ProcessEvents) + public void ProcessEvents() { + // IF STORE DEMO, DO NOT ACCEPT INPUT + if ((isCommercial())) { + W_CheckNumForName: + { + if ((wadLoader.CheckNumForName("MAP01") < 0)) { + return; + } + } + } + + for (; eventtail != eventhead; eventtail = (++eventtail) & (MAXEVENTS - 1)) { + final event_t ev = events[eventtail]; + ev.withMouse(event_t.mouseevent_t::processedNotify); + + M_Responder: + { + if (menu.Responder(ev)) { + continue; // menu ate the event + } + } + + G_Responder: + { + Responder(ev); + } + } + } + + // "static" to Display, don't move. + private boolean viewactivestate = false; + private boolean menuactivestate = false; + private boolean inhelpscreensstate = false; + private boolean fullscreen = false; + private gamestate_t oldgamestate = GS_MINUS_ONE; + private int borderdrawcount; + + /** + * D_Display + * draw current display, possibly wiping it from the previous + * @throws IOException + */ + public void Display() throws IOException { + int nowtime; + int tics; + int wipestart; + int y; + boolean done; + boolean wipe; + boolean redrawsbar; + + // for comparative timing / profiling + if (nodrawers) { + return; + } + redrawsbar = false; + + // change the view size if needed + if (sceneRenderer.getSetSizeNeeded()) { + sceneRenderer.ExecuteSetViewSize(); + // force background redraw + oldgamestate = GS_MINUS_ONE; + borderdrawcount = 3; + } + + // save the current screen if about to wipe + wipe = (gamestate != wipegamestate); + if (wipe) { + wiper.StartScreen(0, 0, vs.getScreenWidth(), vs.getScreenHeight()); + } + + if (gamestate == GS_LEVEL && eval(gametic)) { + headsUp.Erase(); + } + + // do buffered drawing + switch (gamestate) { + case GS_LEVEL: + if (!eval(gametic)) { + break; + } + + if (automapactive) { + autoMap.Drawer(); + } + + if (wipe + || (!sceneRenderer.isFullHeight() && fullscreen) + || (inhelpscreensstate && !inhelpscreens) + || (diskDrawer.justDoneReading())) { + redrawsbar = true; // just put away the help screen + } + statusBar.Drawer(sceneRenderer.isFullHeight(), redrawsbar); + fullscreen = sceneRenderer.isFullHeight(); + break; + case GS_INTERMISSION: + endLevel.Drawer(); + break; + case GS_FINALE: + finale.Drawer(); + break; + case GS_DEMOSCREEN: + PageDrawer(); + break; + default: + break; + } + + // draw the view directly + if (gamestate == GS_LEVEL && !automapactive && eval(gametic)) { + if (flashing_hom) { + graphicSystem.FillRect(FG, new Rectangle(view.getViewWindowX(), view.getViewWindowY(), + view.getScaledViewWidth(), view.getScaledViewHeight()), gametic % 256); + } + sceneRenderer.RenderPlayerView(players[displayplayer]); + } + + // Automap was active, update only HU. + if (gamestate == GS_LEVEL && eval(gametic)) { + headsUp.Drawer(); + } + + // clean up border stuff + if (gamestate != oldgamestate && gamestate != GS_LEVEL) { + graphicSystem.setPalette(0); + } + + // see if the border needs to be initially drawn + if (gamestate == GS_LEVEL && oldgamestate != GS_LEVEL) { + // view was not active + viewactivestate = false; + // draw the pattern into the back screen + sceneRenderer.FillBackScreen(); + } + + // see if the border needs to be updated to the screen + if (gamestate == GS_LEVEL && !automapactive && !sceneRenderer.isFullScreen()) { + if (menuactive || menuactivestate || !viewactivestate) { + borderdrawcount = 3; + } + + if (eval(borderdrawcount)) { + // erase old menu stuff + sceneRenderer.DrawViewBorder(); + borderdrawcount--; + } + } + + menuactivestate = menuactive; + viewactivestate = viewactive; + inhelpscreensstate = inhelpscreens; + oldgamestate = wipegamestate = gamestate; + + // draw pause pic + if (paused) { + if (automapactive) { + y = 4 * graphicSystem.getScalingY(); + } else { + y = view.getViewWindowY() + 4 * graphicSystem.getScalingY(); + } + + final patch_t pause = wadLoader.CachePatchName("M_PAUSE", PU_CACHE); + graphicSystem.DrawPatchCenteredScaled(FG, pause, vs, y, DoomGraphicSystem.V_NOSCALESTART); + } + + // menus go directly to the screen + menu.Drawer(); // menu is drawn even on top of everything + NetUpdate(); // send out any new accumulation + + // Disk access goes after everything. + diskDrawer.Drawer(); + + // normal update + if (!wipe) { + //System.out.print("Tick "+gametic+"\t"); + //System.out.print(players[0]); + Engine.updateFrame(); // page flip or blit buffer + return; + } + + // wipe update. At this point, AT LEAST one frame of the game must have been + // rendered for this to work. 22/5/2011: Fixed a vexing bug with the wiper. + // Jesus Christ with a Super Shotgun! + wiper.EndScreen(0, 0, vs.getScreenWidth(), vs.getScreenHeight()); + + wipestart = ticker.GetTime() - 1; + + do { + do { + nowtime = ticker.GetTime(); + tics = nowtime - wipestart; + } while (tics == 0); // Wait until a single tic has passed. + wipestart = nowtime; + Wiper.Wipe wipeType = CM.equals(Settings.scale_melt, Boolean.TRUE) + ? Wiper.Wipe.ScaledMelt : Wiper.Wipe.Melt; + + done = wiper.ScreenWipe(wipeType, 0, 0, vs.getScreenWidth(), vs.getScreenHeight(), tics); + soundDriver.UpdateSound(); + soundDriver.SubmitSound(); // update sounds after one wipe tic. + menu.Drawer(); // menu is drawn even on top of wipes + Engine.updateFrame(); // page flip or blit buffer + } while (!done); + } + + /** + * To be able to debug vanilla incompatibilitites, the DoomLoop + * and all that is called by it that relates to the Loop itself, + * the ticks, game object modifications, mode changes and so on, + * ***MUST*** be preceded by a label, containing original + * underscored naming of the method in Doom Source Code. + * + * Remember the label blocks will retain their name even in case + * of *automated refactoring*, thus if you rename some method + * and update it throughout the whole codebase, the named label + * will still be named the same underscored original method name + * + * Do it the most verbose way you can - preserving both, or all + * brackets of all blocks containing and contained in the label, + * and the brackets of the label itself, with one exception: + * + * If there is no more function to do the task was given to the + * function in original Doom Source Code, the label stull ***MUST*** + * be present, just type a semicolon to end it without actions. + * The syntax is short and displays clearly that nothing is done. + * - Good Sign 2017/04/26 + * + * D_DoomLoop() + * Not a globally visible function, + * just included for source reference, + * called by D_DoomMain, never exits. + * Manages timing and IO, + * calls all ?_Responder, ?_Ticker, and ?_Drawer, + * calls I_GetTime, I_StartFrame, and I_StartTic + * @throws IOException + */ + @D_Main.C(D_DoomLoop) + public void DoomLoop() throws IOException { + if (demorecording) { + G_BeginRecording: + { + BeginRecording(); + } + } + + M_CheckParm: + { + if (cVarManager.bool(CommandVariable.DEBUGFILE)) { + String filename = "debug" + consoleplayer + ".txt"; + LOGGER.log(Level.INFO, String.format("Debug output to: %s", filename)); + try { + debugfile = new OutputStreamWriter(new FileOutputStream(filename)); + } catch (FileNotFoundException e) { + LOGGER.log(Level.SEVERE, "Couldn't open debugfile.", e); + } + } + } + + I_InitGraphics: + { + view = sceneRenderer.getView(); + } + + while (true) { + // frame syncronous IO operations + I_StartFrame: + ; + + // process one or more tics + if (singletics) { + I_StartTic: + ; + D_ProcessEvents: + { + ProcessEvents(); + } + G_BuildTiccmd: + { + BuildTiccmd(netcmds[consoleplayer][maketic % BACKUPTICS]); + } + if (advancedemo) { + D_DoAdvanceDemo: + { + DoAdvanceDemo(); + } + } + M_Ticker: + { + menu.Ticker(); + } + G_Ticker: + { + Ticker(); + } + gametic++; + maketic++; + } else { + gameNetworking.TryRunTics(); // will run at least one tic (in NET) + } + S_UpdateSounds: + { + doomSound.UpdateSounds(players[consoleplayer].mo); // move positional sounds + } + D_Display: + { // Update display, next frame, with current state. + Display(); + } + //#ifndef SNDSERV + // Sound mixing for the buffer is snychronous. + soundDriver.UpdateSound(); + //#endif + // Synchronous sound output is explicitly called. + //#ifndef SNDINTR + // Update sound output. + soundDriver.SubmitSound(); + //#endif + } + } + + // To keep an "eye" on the renderer. + protected ViewVars view; + + // + // DEMO LOOP + // + int demosequence; + int pagetic; + String pagename; + + /** + * D_PageTicker + * Handles timing for warped projection + */ + public final void PageTicker() { + if (--pagetic < 0) { + AdvanceDemo(); + } + } + + /** + * D_PageDrawer + */ + public final void PageDrawer() { + // FIXME: this check wasn't necessary in vanilla, since pagename was + // guaranteed(?) not to be null or had a safe default value. + if (pagename != null) { + graphicSystem.DrawPatchScaled(FG, wadLoader.CachePatchName(pagename, PU_CACHE), vs, 0, 0, DoomGraphicSystem.V_SAFESCALE); + } + } + + /** + * D_AdvanceDemo + * Called after each demo or intro demosequence finishes + */ + public void AdvanceDemo() { + advancedemo = true; + } + + /** + * This cycles through the demo sequences. + * FIXME - version dependant demo numbers? + */ + public void DoAdvanceDemo() { + players[consoleplayer].playerstate = PST_LIVE; // not reborn + advancedemo = false; + usergame = false; // no save / end game here + paused = false; + gameaction = ga_nothing; + + if (isRetail()) // Allows access to a 4th demo. + { + demosequence = (demosequence + 1) % 7; + } else { + demosequence = (demosequence + 1) % 6; + } + + switch (demosequence) { + case 0: + if (isCommercial()) { + pagetic = 35 * 11; + } else { + pagetic = 170; + } + gamestate = GS_DEMOSCREEN; + + if (wadLoader.CheckNumForName("TITLEPIC") != -1) { + pagename = "TITLEPIC"; + } else { + if (wadLoader.CheckNumForName("DMENUPIC") != -1) { + pagename = "DMENUPIC"; + } + } + + if (isCommercial()) { + doomSound.StartMusic(musicenum_t.mus_dm2ttl); + } else { + doomSound.StartMusic(musicenum_t.mus_intro); + } + break; + case 1: + DeferedPlayDemo("demo1"); + break; + case 2: + pagetic = 200; + gamestate = GS_DEMOSCREEN; + pagename = "CREDIT"; + break; + case 3: + DeferedPlayDemo("demo2"); + break; + case 4: + gamestate = GS_DEMOSCREEN; + if (isCommercial()) { + pagetic = 35 * 11; + pagename = "TITLEPIC"; + doomSound.StartMusic(musicenum_t.mus_dm2ttl); + } else { + pagetic = 200; + + if (isRetail()) { + pagename = "CREDIT"; + } else { + pagename = "HELP1"; + } + } + break; + case 5: + DeferedPlayDemo("demo3"); + break; + // THE DEFINITIVE DOOM Special Edition demo + case 6: + DeferedPlayDemo("demo4"); + break; + } + } + + /** + * D_StartTitle + */ + public void StartTitle() { + gameaction = ga_nothing; + demosequence = -1; + AdvanceDemo(); + } + + // print title for every printed line + StringBuffer title = new StringBuffer(); + + /** + * D_AddFile + * + * Adds file to the end of the wadfiles[] list. + * Quite crude, we could use a listarray instead. + * + * @param file + */ + private void AddFile(String file) { + int numwadfiles; + for (numwadfiles = 0; eval(wadfiles[numwadfiles]); numwadfiles++) { + } + wadfiles[numwadfiles] = file; + } + + /** + * IdentifyVersion + * Checks availability of IWAD files by name, + * to determine whether registered/commercial features + * should be executed (notably loading PWAD's). + */ + public final String IdentifyVersion() { + String doomwaddir; + // By default. + language = Language_t.english; + + // First, check for -iwad parameter. + // If valid, then it trumps all others. + if (cVarManager.present(CommandVariable.IWAD)) { + LOGGER.log(Level.INFO, "-iwad specified. Will be used with priority"); + // It might be quoted. + final String test = C2JUtils.unquoteIfQuoted(cVarManager.get(CommandVariable.IWAD, String.class, 0).get(), '"'); + final String separator = System.getProperty("file.separator"); + final String iwad = test.substring(1 + test.lastIndexOf(separator)); + doomwaddir = test.substring(0, 1 + test.lastIndexOf(separator)); + final GameMode attempt = DoomVersion.tryOnlyOne(iwad, doomwaddir); + // Note: at this point we can't distinguish between "doom" retail + // and "doom" ultimate yet. + if (attempt != null) { + AddFile(doomwaddir + iwad); + this.setGameMode(attempt); + return (doomwaddir + iwad); + } + } else { + // Unix-like checking. Might come in handy sometimes. + // This should ALWAYS be activated, else doomwaddir etc. won't be defined. + + doomwaddir = System.getenv("DOOMWADDIR"); + if (doomwaddir != null) { + LOGGER.log(Level.INFO, "DOOMWADDIR found. Will be used with priority"); + } + + // None found, using current. + if (!eval(doomwaddir)) { + doomwaddir = "."; + } + } + + for (GameMode mode : GameMode.values()) { + if (mode != GameMode.indetermined && cVarManager.bool(mode.devVar)) { + return devParmOn(mode); + } + } + + final String wadFullPath = DoomVersion.tryAllWads(this, doomwaddir); + if (wadFullPath == null) { + LOGGER.log(Level.INFO, "Game mode indeterminate."); + setGameMode(GameMode.indetermined); + // We don't abort. Let's see what the PWAD contains. + //exit(1); + //I_Error ("Game mode indeterminate\n"); + } else { + AddFile(wadFullPath); + } + + return wadFullPath; + } + + private String devParmOn(GameMode mode) { + setGameMode(mode); + devparm = true; + AddFile(dstrings.DEVDATA + mode.version); + AddFile(dstrings.DEVMAPS + mode.devDir + "/texture1.lmp"); + if (mode.hasTexture2()) { + AddFile(dstrings.DEVMAPS + mode.devDir + "/texture2.lmp"); + } + AddFile(dstrings.DEVMAPS + mode.devDir + "/pnames.lmp"); + return (dstrings.DEVDATA + mode.version); + } + + /** + * + */ + protected final void CheckForPWADSInShareware() { + if (modifiedgame) { + // These are the lumps that will be checked in IWAD, + // if any one is not present, execution will be aborted. + String[] name = { + "e2m1", "e2m2", "e2m3", "e2m4", "e2m5", "e2m6", "e2m7", "e2m8", "e2m9", + "e3m1", "e3m3", "e3m3", "e3m4", "e3m5", "e3m6", "e3m7", "e3m8", "e3m9", + "dphoof", "bfgga0", "heada1", "cybra1", "spida1d1" + }; + int i; + + // Oh yes I can. + if (isShareware()) { + LOGGER.log(Level.WARNING, "You cannot -file with the shareware version. Register!"); + } + + // Check for fake IWAD with right name, + // but w/o all the lumps of the registered version. + if (isRegistered()) { + for (i = 0; i < 23; i++) { + if (wadLoader.CheckNumForName(name[i].toUpperCase()) < 0) { + doomSystem.Error("This is not the registered version: " + name[i]); + } + } + } + } + } + + /** Check whether the "doom.wad" we actually loaded + * is ultimate Doom's, by checking if it contains + * e4m1 - e4m9. + * + */ + protected final void CheckForUltimateDoom(WadLoader W) { + if (isRegistered()) { + // These are the lumps that will be checked in IWAD, + // if any one is not present, execution will be aborted. + String[] lumps = {"e4m1", "e4m2", "e4m3", "e4m4", "e4m5", "e4m6", "e4m7", "e4m8", "e4m9"}; + + // Check for fake IWAD with right name, + // but w/o all the lumps of the registered version. + if (!CheckForLumps(lumps, W)) { + return; + } + // Checks passed, so we can set the mode to Ultimate + setGameMode(GameMode.retail); + } + + } + + /** Check if ALL of the lumps exist. + * + * @param name + * @return + */ + protected boolean CheckForLumps(String[] name, WadLoader W) { + for (String name1 : name) { + if (W.CheckNumForName(name1.toUpperCase()) < 0) { + // Even one is missing? Not OK. + return false; + } + } + return true; + } + + protected final void GenerateTitle() { + switch (getGameMode()) { + case retail: + title.append("The Ultimate DOOM Startup v"); + title.append(VERSION / 100); + title.append("."); + title.append(VERSION % 100); + break; + case shareware: + title.append("DOOM Shareware Startup v"); + title.append(VERSION / 100); + title.append("."); + title.append(VERSION % 100); + break; + case registered: + title.append("DOOM Registered Startup v"); + title.append(VERSION / 100); + title.append("."); + title.append(VERSION % 100); + break; + case commercial: + title.append("DOOM 2: Hell on Earth v"); + title.append(VERSION / 100); + title.append("."); + title.append(VERSION % 100); + break; + case pack_plut: + title.append("DOOM 2: Plutonia Experiment v"); + title.append(VERSION / 100); + title.append("."); + title.append(VERSION % 100); + break; + case pack_tnt: + title.append("DOOM 2: TNT - Evilution v"); + title.append(VERSION / 100); + title.append("."); + title.append(VERSION % 100); + break; + case pack_xbla: + title.append("DOOM 2: No Rest for the Living v"); + title.append(VERSION / 100); + title.append("."); + title.append(VERSION % 100); + break; + case freedm: + title.append("FreeDM v"); + title.append(VERSION / 100); + title.append("."); + title.append(VERSION % 100); + break; + case freedoom1: + title.append("FreeDoom: Phase 1 v"); + title.append(VERSION / 100); + title.append("."); + title.append(VERSION % 100); + break; + case freedoom2: + title.append("FreeDoom: Phase 2 v"); + title.append(VERSION / 100); + title.append("."); + title.append(VERSION % 100); + break; + default: + title.append("Public DOOM - v"); + title.append(VERSION / 100); + title.append("."); + title.append(VERSION % 100); + break; + } + } + + // Used in BuildTiccmd. + protected ticcmd_t base = new ticcmd_t(); + + /** + * G_BuildTiccmd + * Builds a ticcmd from all of the available inputs + * or reads it from the demo buffer. + * If recording a demo, write it out . + * + * The CURRENT event to process is written to the various + * gamekeydown etc. arrays by the Responder method. + * So look there for any fuckups in constructing them. + * + */ + @SourceCode.Compatible + @G_Game.C(G_BuildTiccmd) + private void BuildTiccmd(ticcmd_t cmd) { + int i; + boolean strafe; + boolean bstrafe; + int speed, tspeed, lspeed; + int forward; + int side; + int look; + + I_BaseTiccmd: + ; // empty, or external driver + base.copyTo(cmd); + + cmd.consistancy = consistancy[consoleplayer][maketic % BACKUPTICS]; + + strafe = gamekeydown[key_strafe] || mousebuttons(mousebstrafe) || joybuttons(joybstrafe); + speed = ((gamekeydown[key_speed] ^ alwaysrun) || joybuttons(joybspeed)) ? 1 : 0; + + forward = side = look = 0; + + // use two stage accelerative turning + // on the keyboard and joystick + if (joyxmove < 0 || joyxmove > 0 || gamekeydown[key_right] || gamekeydown[key_left]) { + turnheld += ticdup; + } else { + turnheld = 0; + } + + tspeed = turnheld < SLOWTURNTICS ? 2 /* slowturn */ : speed; + + if (gamekeydown[key_lookdown] || gamekeydown[key_lookup]) { + lookheld += ticdup; + } else { + lookheld = 0; + } + + lspeed = lookheld < SLOWTURNTICS ? 1 : 2; + + // let movement keys cancel each other out + if (strafe) { + if (gamekeydown[key_right]) { + // fprintf(stderr, "strafe right\n"); + side += sidemove[speed]; + } + + if (gamekeydown[key_left]) { + // fprintf(stderr, "strafe left\n"); + side -= sidemove[speed]; + } + + if (joyxmove > 0) { + side += sidemove[speed]; + } else if (joyxmove < 0) { + side -= sidemove[speed]; + } + } else { + if (gamekeydown[key_right]) { + cmd.angleturn -= angleturn[tspeed]; + } + + if (gamekeydown[key_left]) { + cmd.angleturn += angleturn[tspeed]; + } + + if (joyxmove > 0) { + cmd.angleturn -= angleturn[tspeed]; + } else if (joyxmove < 0) { + cmd.angleturn += angleturn[tspeed]; + } + } + + if (gamekeydown[key_up]) { + forward += forwardmove[speed]; + } + + if (gamekeydown[key_down]) { + forward -= forwardmove[speed]; + } + + if (joyymove < 0) { + forward += forwardmove[speed]; + } else if (joyymove > 0) { + forward -= forwardmove[speed]; + } + + if (gamekeydown[key_straferight]) { + side += sidemove[speed]; + } + + if (gamekeydown[key_strafeleft]) { + side -= sidemove[speed]; + } + + // Look up/down/center keys + if (gamekeydown[key_lookup]) { + look = lspeed; + } + + if (gamekeydown[key_lookdown]) { + look = -lspeed; + } + + if (gamekeydown[key_lookcenter]) { + look = TOCENTER; + } + + // buttons + cmd.chatchar = headsUp.dequeueChatChar(); + + if (gamekeydown[key_fire] || mousebuttons(mousebfire) || joybuttons(joybfire)) { + cmd.buttons |= BT_ATTACK; + } + + if (gamekeydown[key_use] || joybuttons(joybuse)) { + cmd.buttons |= BT_USE; + // clear double clicks if hit use button + dclicks = 0; + } + + // chainsaw overrides + for (i = 0; i < NUMWEAPONS - 1; i++) { + if (gamekeydown[key_numbers[i]]) { + //System.out.println("Attempting weapon change (building ticcmd)"); + cmd.buttons |= BT_CHANGE; + cmd.buttons |= i << BT_WEAPONSHIFT; + break; + } + } + + // mouse + if (mousebuttons(mousebforward)) { + forward += forwardmove[speed]; + } + + // forward double click + if (mousebuttons(mousebforward) != eval(dclickstate) && dclicktime > 1) { + dclickstate = mousebuttons(mousebforward) ? 1 : 0; + if (dclickstate != 0) { + dclicks++; + } + if (dclicks == 2) { + cmd.buttons |= BT_USE; + dclicks = 0; + } else { + dclicktime = 0; + } + } else { + dclicktime += ticdup; + if (dclicktime > 20) { + dclicks = 0; + dclickstate = 0; + } + } + + // strafe double click + bstrafe = mousebuttons(mousebstrafe) || joybuttons(joybstrafe); + if ((bstrafe != eval(dclickstate2)) && dclicktime2 > 1) { + dclickstate2 = bstrafe ? 1 : 0; + if (dclickstate2 != 0) { + dclicks2++; + } + if (dclicks2 == 2) { + cmd.buttons |= BT_USE; + dclicks2 = 0; + } else { + dclicktime2 = 0; + } + } else { + dclicktime2 += ticdup; + if (dclicktime2 > 20) { + dclicks2 = 0; + dclickstate2 = 0; + } + } + + // By default, no vertical mouse movement + if (!novert) { + forward += mousey; + } + + if (strafe) { + side += mousex * 2; + } else { + cmd.angleturn -= mousex * 0x8; + } + + mousex = mousey = 0; + + if (forward > MAXPLMOVE()) { + forward = MAXPLMOVE(); + } else if (forward < -MAXPLMOVE()) { + forward = -MAXPLMOVE(); + } + if (side > MAXPLMOVE()) { + side = MAXPLMOVE(); + } else if (side < -MAXPLMOVE()) { + side = -MAXPLMOVE(); + } + + cmd.forwardmove += forward; + cmd.sidemove += side; + + if (players[consoleplayer].playerstate == PST_LIVE) { + if (look < 0) { + look += 16; + } + + cmd.lookfly = (char) look; + } + + // special buttons + if (sendpause) { + sendpause = false; + cmd.buttons = BT_SPECIAL | BTS_PAUSE; + } + + if (sendsave) { + sendsave = false; + cmd.buttons = (char) (BT_SPECIAL | BTS_SAVEGAME | (savegameslot << BTS_SAVESHIFT)); + } + } + + /** + * G_DoLoadLevel + * + * //extern gamestate_t wipegamestate; + */ + @SourceCode.Suspicious(CauseOfDesyncProbability.LOW) + @G_Game.C(G_DoLoadLevel) + public boolean DoLoadLevel() { + /** + * Added a config switch to this fix + * - Good Sign 2017/04/26 + * + * Fixed R_FlatNumForName was a part of the fix, not vanilla code + * - Good Sign 2017/05/07 + * + * DOOM determines the sky texture to be used + * depending on the current episode, and the game version. + * + * @SourceCode.Compatible + */ + if (Engine.getConfig().equals(Settings.fix_sky_change, Boolean.TRUE) && (isCommercial() + || (gamemission == GameMission_t.pack_tnt) + || (gamemission == GameMission_t.pack_plut))) { + // Set the sky map. + // First thing, we have a dummy sky texture name, + // a flat. The data is in the WAD only because + // we look for an actual index, instead of simply + // setting one. + textureManager.setSkyFlatNum(textureManager.FlatNumForName(SKYFLATNAME)); + + textureManager.setSkyTexture(textureManager.TextureNumForName("SKY3")); + if (gamemap < 12) { + textureManager.setSkyTexture(textureManager.TextureNumForName("SKY1")); + } else { + if (gamemap < 21) { + textureManager.setSkyTexture(textureManager.TextureNumForName("SKY2")); + } + } + } + + levelstarttic = gametic; // for time calculation + + if (wipegamestate == GS_LEVEL) { + wipegamestate = GS_MINUS_ONE; // force a wipe + } + gamestate = GS_LEVEL; + + for (int i = 0; i < MAXPLAYERS; i++) { + if (playeringame[i] && players[i].playerstate == PST_DEAD) { + players[i].playerstate = PST_REBORN; + } + + memset(players[i].frags, 0, players[i].frags.length); + } + + try { + P_SetupLevel: + { + levelLoader.SetupLevel(gameepisode, gamemap, 0, gameskill); + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Failure loading level.", e); + // Failure loading level. + return false; + } + + displayplayer = consoleplayer; // view the guy you are playing + I_GetTime: + { + starttime = ticker.GetTime(); + } + gameaction = ga_nothing; + Z_CheckHeap: + ; + + // clear cmd building stuff + memset(gamekeydown, false, gamekeydown.length); + keysCleared = true; + joyxmove = joyymove = 0; + mousex = mousey = 0; + sendpause = sendsave = paused = false; + memset(mousearray, false, mousearray.length); + memset(joyarray, false, joyarray.length); + + /** + * Probably no desync-effect + * - GoodSign 2017/05/07 + * + * @SourceCode.Suspicious + */ + // killough 5/13/98: in case netdemo has consoleplayer other than green + statusBar.Start(); + headsUp.Start(); + + // killough: make -timedemo work on multilevel demos + // Move to end of function to minimize noise -- killough 2/22/98: + if (timingdemo) { + if (first) { + starttime = RealTime.GetTime(); + first = false; + } + } + + // Try reclaiming some memory from limit-expanded buffers. + sceneRenderer.resetLimits(); + return true; + } + + protected boolean first = true; + + /** + * G_Responder + * Get info needed to make ticcmd_ts for the players. + */ + @SourceCode.Compatible + @G_Game.C(G_Responder) + public boolean Responder(event_t ev) { + // allow spy mode changes even during the demo + if (gamestate == GS_LEVEL && ev.isKey(SC_F12, ev_keydown) && (singledemo || !deathmatch)) { + // spy mode + do { + displayplayer++; + if (displayplayer == MAXPLAYERS) { + displayplayer = 0; + } + } while (!playeringame[displayplayer] && displayplayer != consoleplayer); + return true; + } + + // any other key pops up menu if in demos + if (gameaction == ga_nothing && !singledemo && (demoplayback || gamestate == GS_DEMOSCREEN)) { + if (ev.isType(ev_keydown) + || (use_mouse && ev.ifMouse(ev_mouse, event_t::hasData)) + || (use_joystick && ev.ifJoy(ev_joystick, event_t::hasData))) { + M_StartControlPanel: + { + menu.StartControlPanel(); + } + return true; + } + return false; + } + + if (gamestate == GS_LEVEL) { + if (devparm && ev.isKey(SC_SEMICOLON, ev_keydown)) { + G_DeathMatchSpawnPlayer: + { + DeathMatchSpawnPlayer(0); + } + return true; + } + + HU_Responder: + { + if (headsUp.Responder(ev)) { + return true; // chat ate the event + } + } + ST_Responder: + { + if (statusBar.Responder(ev)) { + return true; // status window ate it + } + } + AM_Responder: + { + if (autoMap.Responder(ev)) { + return true; // automap ate it + } + } + } + + if (gamestate == GS_FINALE) { + F_Responder: + { + if (finale.Responder(ev)) { + return true; // finale ate the event + } + } + } + + switch (ev.type()) { + case ev_keydown: + if (ev.isKey(SC_PAUSE)) { + sendpause = true; + return true; + } + + ev.withKey(sc -> { + gamekeydown[sc.ordinal()] = true; + if (vanillaKeyBehavior) { + handleVanillaKeys(sc, true); + } + }); + return true; // eat key down events + case ev_keyup: + /* CAPS lock will only go through as a keyup event */ + if (ev.isKey(SC_CAPSLK)) { + // Just toggle it. It's too hard to read the state. + alwaysrun = !alwaysrun; + players[consoleplayer].message = String.format("Always run: %s", alwaysrun); + } + + ev.withKey(sc -> { + gamekeydown[sc.ordinal()] = false; + if (vanillaKeyBehavior) { + handleVanillaKeys(sc, false); + } + }); + return false; // always let key up events filter down + + case ev_mouse: + // Ignore them at the responder level + if (use_mouse) { + mousebuttons(0, ev.isMouse(event_t.MOUSE_LEFT)); + mousebuttons(1, ev.isMouse(event_t.MOUSE_RIGHT)); + mousebuttons(2, ev.isMouse(event_t.MOUSE_MID)); + ev.withMouse(mouseEvent -> { + mousex = mouseEvent.x * (mouseSensitivity + 5) / 10; + mousey = mouseEvent.y * (mouseSensitivity + 5) / 10; + }); + } + return true; // eat events + case ev_joystick: + if (use_joystick) { + joybuttons(0, ev.isJoy(event_t.JOY_1)); + joybuttons(1, ev.isJoy(event_t.JOY_2)); + joybuttons(2, ev.isJoy(event_t.JOY_3)); + joybuttons(3, ev.isJoy(event_t.JOY_4)); + ev.withJoy(joyEvent -> { + joyxmove = joyEvent.x; + joyymove = joyEvent.y; + }); + } + return true; // eat events + default: + break; + } + + return false; + } + + private void handleVanillaKeys(Signals.ScanCode sc, boolean keyDown) { + switch (sc) { + case SC_LSHIFT: + case SC_RSHIFT: + gamekeydown[SC_RSHIFT.ordinal()] = gamekeydown[SC_LSHIFT.ordinal()] = keyDown; + break; + case SC_LCTRL: + case SC_RCTRL: + gamekeydown[SC_RCTRL.ordinal()] = gamekeydown[SC_LCTRL.ordinal()] = keyDown; + break; + case SC_LALT: + case SC_RALT: + gamekeydown[SC_RALT.ordinal()] = gamekeydown[SC_LALT.ordinal()] = keyDown; + break; + case SC_UP: + gamekeydown[SC_NUMKEY8.ordinal()] = keyDown; + break; + case SC_DOWN: + gamekeydown[SC_NUMKEY2.ordinal()] = keyDown; + break; + case SC_LEFT: + gamekeydown[SC_NUMKEY4.ordinal()] = keyDown; + break; + case SC_RIGHT: + gamekeydown[SC_NUMKEY6.ordinal()] = keyDown; + break; + default: + break; + } + } + + private final String turbomessage = "is turbo!"; + + /** + * G_Ticker + * + * Make ticcmd_ts for the players. + */ + @G_Game.C(G_Ticker) + public void Ticker() { + // do player reborns if needed + for (int i = 0; i < MAXPLAYERS; i++) { + if (playeringame[i] && players[i].playerstate == PST_REBORN) { + G_DoReborn: + { + DoReborn(i); + } + } + } + + // do things to change the game state + while (gameaction != ga_nothing) { + switch (gameaction) { + case ga_loadlevel: + G_DoLoadLevel: + { + DoLoadLevel(); + } + break; + case ga_newgame: + G_DoNewGame: + { + DoNewGame(); + } + break; + case ga_loadgame: + G_DoLoadGame: + { + DoLoadGame(); + } + break; + case ga_savegame: + G_DoSaveGame: + { + DoSaveGame(); + } + break; + case ga_playdemo: + G_DoPlayDemo: + { + DoPlayDemo(); + } + break; + case ga_completed: + G_DoCompleted: + { + DoCompleted(); + } + break; + case ga_victory: + finale.StartFinale(); + break; + case ga_worlddone: + DoWorldDone(); + break; + case ga_screenshot: + ScreenShot(); + gameaction = ga_nothing; + break; + case ga_nothing: + break; + default: + break; + } + } + + // get commands, check consistancy, + // and build new consistancy check + final int buf = (gametic / ticdup) % BACKUPTICS; + for (int i = 0; i < MAXPLAYERS; i++) { + if (playeringame[i]) { + final ticcmd_t cmd = players[i].cmd; + //System.out.println("Current command:"+cmd); + + //memcpy (cmd, &netcmds[i][buf], sizeof(ticcmd_t)); + netcmds[i][buf].copyTo(cmd); + + // MAES: this is where actual demo commands are being issued or created! + // Essentially, a demo is a sequence of stored ticcmd_t with a header. + // Knowing that, it's possible to objectify it. + if (demoplayback) { + ReadDemoTiccmd(cmd); + } + + if (demorecording) { + WriteDemoTiccmd(cmd); + } + + // check for turbo cheats + if (cmd.forwardmove > TURBOTHRESHOLD && ((gametic & 31) == 0) && ((gametic >> 5) & 3) == i) { + //extern char *player_names[4]; + //sprintf (turbomessage, "%s is turbo!",player_names[i]); + players[consoleplayer].message = hu.HU.player_names[i] + turbomessage; + } + + if (netgame && !netdemo && (gametic % ticdup) == 0) { + if (gametic > BACKUPTICS && consistancy[i][buf] != cmd.consistancy) { + doomSystem.Error("consistency failure (%d should be %d)", cmd.consistancy, consistancy[i][buf]); + } + + if (players[i].mo != null) { + consistancy[i][buf] = (short) players[i].mo.x; + } else { + consistancy[i][buf] = (short) random.getIndex(); + } + } + } + } + + // check for special buttons + for (int i = 0; i < MAXPLAYERS; i++) { + if (playeringame[i]) { + if ((players[i].cmd.buttons & BT_SPECIAL) != 0) { + switch (players[i].cmd.buttons & BT_SPECIALMASK) { + case BTS_PAUSE: + // MAES: fixed stupid ^pause bug. + paused = !paused; + if (paused) { + doomSound.PauseSound(); + } else { + doomSound.ResumeSound(); + } + break; + case BTS_SAVEGAME: + if (savedescription == null) { + savedescription = "NET GAME"; + } + savegameslot = (players[i].cmd.buttons & BTS_SAVEMASK) >> BTS_SAVESHIFT; + gameaction = ga_savegame; + break; + } + } + } + } + + // do main actions + switch (gamestate) { + case GS_LEVEL: + actions.Ticker(); + statusBar.Ticker(); + autoMap.Ticker(); + headsUp.Ticker(); + break; + + case GS_INTERMISSION: + endLevel.Ticker(); + break; + + case GS_FINALE: + finale.Ticker(); + break; + + case GS_DEMOSCREEN: + PageTicker(); + break; + + default: + break; + } + } + + // + // PLAYER STRUCTURE FUNCTIONS + // also see P_SpawnPlayer in P_Things + // + /** + * G_InitPlayer + * Called at the start. + * Called by the game initialization functions. + * + * MAES: looks like dead code. It's never called. + * + */ + protected void InitPlayer(int player) { + // set up the saved info + // clear everything else to defaults + players[player].PlayerReborn(); + } + + // + // G_CheckSpot + // Returns false if the player cannot be respawned + // at the given mapthing_t spot + // because something is occupying it + // + //void P_SpawnPlayer (mapthing_t* mthing); + @SourceCode.Exact + @G_Game.C(G_CheckSpot) + private boolean CheckSpot(int playernum, mapthing_t mthing) { + if (players[playernum].mo == null) { + // first spawn of level, before corpses + for (int i = 0; i < playernum; i++) { + if (players[i].mo.x == mthing.x << FRACBITS && players[i].mo.y == mthing.y << FRACBITS) { + return false; + } + } + return true; + } + + final int x = mthing.x << FRACBITS, y = mthing.y << FRACBITS; + + P_CheckPosition: + { + if (!actions.CheckPosition(players[playernum].mo, x, y)) { + return false; + } + } + + // flush an old corpse if needed + if (bodyqueslot >= BODYQUESIZE) { + P_RemoveMobj: + { + actions.RemoveMobj(bodyque[bodyqueslot % BODYQUESIZE]); + } + } + bodyque[bodyqueslot % BODYQUESIZE] = players[playernum].mo; + bodyqueslot++; + + // spawn a teleport fog + final subsector_t ss; + R_PointInSubsector: + { + ss = levelLoader.PointInSubsector(x, y); + } + // Angles stored in things are supposed to be "sanitized" against rollovers. + final int angle = (int) ((ANG45 * (mthing.angle / 45)) >>> ANGLETOFINESHIFT); + final mobj_t mo; + P_SpawnMobj: + { + mo = actions.SpawnMobj(x + 20 * finecosine[angle], y + 20 * finesine[angle], ss.sector.floorheight, mobjtype_t.MT_TFOG); + } + + // FIXME: maybe false fix + if (players[consoleplayer].viewz != 1) { + S_StartSound: + { + doomSound.StartSound(mo, sfxenum_t.sfx_telept); // don't start sound on first frame + } + } + + return true; + } + + // + // G_DeathMatchSpawnPlayer + // Spawns a player at one of the random death match spots + // called at level load and each death + // + @Override + @SourceCode.Exact + @G_Game.C(G_DeathMatchSpawnPlayer) + public void DeathMatchSpawnPlayer(int playernum) { + final int selections = deathmatch_p; + if (selections < 4) { + I_Error: + { + doomSystem.Error("Only %d deathmatch spots, 4 required", selections); + } + } + + for (int j = 0; j < 20; j++) { + final int i; + P_Random: + { + i = random.P_Random() % selections; + } + G_CheckSpot: + { + if (CheckSpot(playernum, deathmatchstarts[i])) { + deathmatchstarts[i].type = (short) (playernum + 1); + P_SpawnPlayer: + { + actions.SpawnPlayer(deathmatchstarts[i]); + } + return; + } + } + } + + // no good spot, so the player will probably get stuck + // MAES: seriously, fuck him. + P_SpawnPlayer: + { + actions.SpawnPlayer(playerstarts[playernum]); + } + } + + /** + * G_DoReborn + */ + @SourceCode.Exact + @G_Game.C(G_DoReborn) + public void DoReborn(int playernum) { + if (!netgame) { + // reload the level from scratch + gameaction = ga_loadlevel; + } else { + // respawn at the start + + // first dissasociate the corpse + players[playernum].mo.player = null; + + // spawn at random spot if in death match + if (deathmatch) { + G_DeathMatchSpawnPlayer: + { + DeathMatchSpawnPlayer(playernum); + } + return; + } + + G_CheckSpot: + { + if (CheckSpot(playernum, playerstarts[playernum])) { + P_SpawnPlayer: + { + actions.SpawnPlayer(playerstarts[playernum]); + } + return; + } + } + + // try to spawn at one of the other players spots + for (int i = 0; i < MAXPLAYERS; i++) { + G_CheckSpot: + { + if (CheckSpot(playernum, playerstarts[i])) { + playerstarts[i].type = (short) (playernum + 1); // fake as other player + P_SpawnPlayer: + { + actions.SpawnPlayer(playerstarts[i]); + } + playerstarts[i].type = (short) (i + 1); // restore + return; + } + } + // he's going to be inside something. Too bad. + // MAES: Yeah, they're like, fuck him. + } + + P_SpawnPlayer: + { + actions.SpawnPlayer(playerstarts[playernum]); + } + } + } + + /** DOOM Par Times [4][10] */ + final int[][] pars = { + {0}, + {0, 30, 75, 120, 90, 165, 180, 180, 30, 165}, + {0, 90, 90, 90, 120, 90, 360, 240, 30, 170}, + {0, 90, 45, 90, 150, 90, 90, 165, 30, 135} + }; + + /** DOOM II Par Times */ + final int[] cpars = { + 30, 90, 120, 120, 90, 150, 120, 120, 270, 90, // 1-10 + 210, 150, 150, 150, 210, 150, 420, 150, 210, 150, // 11-20 + 240, 150, 180, 150, 150, 300, 330, 420, 300, 180, // 21-30 + 120, 30 // 31-32 + }; + + // + // G_DoCompleted + // + boolean secretexit; + + public final void ExitLevel() { + secretexit = false; + gameaction = ga_completed; + } + + // Here's for the german edition. + public void SecretExitLevel() { + // IF NO WOLF3D LEVELS, NO SECRET EXIT! + secretexit = !(isCommercial() && (wadLoader.CheckNumForName("MAP31") < 0)); + gameaction = ga_completed; + } + + @SourceCode.Exact + @G_Game.C(G_DoCompleted) + protected void DoCompleted() { + gameaction = ga_nothing; + + for (int i = 0; i < MAXPLAYERS; i++) { + if (playeringame[i]) { + G_PlayerFinishLevel: + { // take away cards and stuff + players[i].PlayerFinishLevel(); + } + } + } + + if (automapactive) { + AM_Stop: + { + autoMap.Stop(); + } + } + + if (!isCommercial()) { + switch (gamemap) { + case 8: + // MAES: end of episode + gameaction = ga_victory; + return; + case 9: + // MAES: end of secret level + for (int i = 0; i < MAXPLAYERS; i++) { + players[i].didsecret = true; + } + break; + default: + break; + } + } + + wminfo.didsecret = players[consoleplayer].didsecret; + wminfo.epsd = gameepisode - 1; + wminfo.last = gamemap - 1; + + // wminfo.next is 0 biased, unlike gamemap + if (isCommercial()) { + if (secretexit) { + switch (gamemap) { + case 2: + wminfo.next = 32; //Fix Doom 3 BFG Edition, MAP02 secret exit to MAP33 Betray + break; + case 15: + wminfo.next = 30; + break; + case 31: + wminfo.next = 31; + break; + default: + break; + } + } else { + switch (gamemap) { + case 31: + case 32: + wminfo.next = 15; + break; + case 33: + wminfo.next = 2; //Fix Doom 3 BFG Edition, MAP33 Betray exit back to MAP03 + break; + default: + wminfo.next = gamemap; + } + } + } else { + if (secretexit) { + wminfo.next = 8; // go to secret level + } else if (gamemap == 9) { + // returning from secret level + switch (gameepisode) { + case 1: + wminfo.next = 3; + break; + case 2: + wminfo.next = 5; + break; + case 3: + wminfo.next = 6; + break; + case 4: + wminfo.next = 2; + break; + default: + break; + } + } else { + wminfo.next = gamemap; // go to next level + } + } + + wminfo.maxkills = totalkills; + wminfo.maxitems = totalitems; + wminfo.maxsecret = totalsecret; + wminfo.maxfrags = 0; + + if (isCommercial()) { + wminfo.partime = 35 * cpars[gamemap - 1]; + } else if (gameepisode >= pars.length) { + wminfo.partime = 0; + } else { + wminfo.partime = 35 * pars[gameepisode][gamemap]; + } + + wminfo.pnum = consoleplayer; + + for (int i = 0; i < MAXPLAYERS; i++) { + wminfo.plyr[i].in = playeringame[i]; + wminfo.plyr[i].skills = players[i].killcount; + wminfo.plyr[i].sitems = players[i].itemcount; + wminfo.plyr[i].ssecret = players[i].secretcount; + wminfo.plyr[i].stime = leveltime; + memcpy(wminfo.plyr[i].frags, players[i].frags, wminfo.plyr[i].frags.length); + } + + gamestate = GS_INTERMISSION; + viewactive = false; + automapactive = false; + + if (statcopy != null) { + memcpy(statcopy, wminfo, 1); + } + + WI_Start: + { + endLevel.Start(wminfo); + } + } + + /** + * G_WorldDone + */ + public void WorldDone() { + gameaction = ga_worlddone; + + if (secretexit) { + players[consoleplayer].didsecret = true; + } + + if (isCommercial()) { + switch (gamemap) { + case 15: + case 31: + if (!secretexit) { + break; + } + case 6: + case 11: + case 20: + case 30: + finale.StartFinale(); + break; + } + } + } + + public void DoWorldDone() { + gamestate = GS_LEVEL; + gamemap = wminfo.next + 1; + DoLoadLevel(); + gameaction = ga_nothing; + viewactive = true; + } + + // + // G_InitFromSavegame + // Can be called by the startup code or the menu task. + // + //extern boolean setsizeneeded; + //void R_ExecuteSetViewSize (void); + String savename; + + public void LoadGame(String name) { + savename = name; + gameaction = ga_loadgame; + } + + /** + * This is fugly. Making a "savegame object" will make at least certain comparisons easier, and avoid writing code + * twice. + */ + @SourceCode.Suspicious(CauseOfDesyncProbability.MEDIUM) + @G_Game.C(G_DoLoadGame) + protected void DoLoadGame() { + try { + StringBuffer vcheck = new StringBuffer(); + VanillaDSGHeader header = new VanillaDSGHeader(); + IDoomSaveGame dsg = new VanillaDSG<>(this); + + gameaction = ga_nothing; + + DataInputStream f = new DataInputStream(new BufferedInputStream(new FileInputStream(savename))); + + header.read(f); + f.close(); + + // skip the description field + vcheck.append("version "); + vcheck.append(VERSION); + + if (vcheck.toString().compareTo(header.getVersion()) != 0) { + f.close(); + return; // bad version + } + + // Ok so far, reopen stream. + f = new DataInputStream(new BufferedInputStream(new FileInputStream(savename))); + gameskill = header.getGameskill(); + gameepisode = header.getGameepisode(); + gamemap = header.getGamemap(); + System.arraycopy(header.getPlayeringame(), 0, playeringame, 0, MAXPLAYERS); + + // load a base level + G_InitNew: + { + InitNew(gameskill, gameepisode, gamemap); + } + + if (gameaction == ga_failure) { + // failure to load. Abort. + f.close(); + return; + } + + gameaction = ga_nothing; + + // get the times + leveltime = header.getLeveltime(); + + boolean ok; + // dearchive all the modifications + P_UnArchivePlayers: + P_UnArchiveWorld: + P_UnArchiveThinkers: + P_UnArchiveSpecials: + { + ok = dsg.doLoad(f); + } + f.close(); + + // MAES: this will cause a forced exit. + // The problem is that the status will have already been altered + // (perhaps VERY badly) so it makes no sense to progress. + // If you want it bullet-proof, you could implement + // a "tentative loading" subsystem, which will only alter the game + // if everything works out without errors. But who cares :-p + if (!ok) { + I_Error: + { + doomSystem.Error("Bad savegame"); + } + } + + // done + //Z_Free (savebuffer); + if (sceneRenderer.getSetSizeNeeded()) { + R_ExecuteSetViewSize: + { + sceneRenderer.ExecuteSetViewSize(); + } + } + + // draw the pattern into the back screen + R_FillBackScreen: + { + sceneRenderer.FillBackScreen(); + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Failure loading game.", e); + } + } + + // + // G_SaveGame + // Called by the menu task. + // Description is a 24 byte text string + // + public void SaveGame(int slot, String description) { + savegameslot = slot; + savedescription = description; + sendsave = true; + } + + @SourceCode.Suspicious(CauseOfDesyncProbability.LOW) + @G_Game.C(G_DoSaveGame) + protected void DoSaveGame() { + + try { + String name; + //char[] name2=new char[VERSIONSIZE]; + String description; + StringBuffer build = new StringBuffer(); + IDoomSaveGameHeader header = new VanillaDSGHeader(); + IDoomSaveGame dsg = new VanillaDSG<>(this); + + M_CheckParm: + { + if (cVarManager.bool(CommandVariable.CDROM)) { + build.append("c:\\doomdata\\"); + } + } + + build.append(String.format("%s%d.dsg", SAVEGAMENAME, savegameslot)); + name = build.toString(); + + description = savedescription; + + header.setName(description); + header.setVersion(String.format("version %d", VERSION)); + header.setGameskill(gameskill); + header.setGameepisode(gameepisode); + header.setGamemap(gamemap); + header.setPlayeringame(playeringame); + header.setLeveltime(leveltime); + dsg.setHeader(header); + + // Try opening a save file. No intermediate buffer (performance?) + try ( DataOutputStream f = new DataOutputStream(new FileOutputStream(name))) { + P_ArchivePlayers: + P_ArchiveWorld: + P_ArchiveThinkers: + P_ArchiveSpecials: + { + boolean ok = dsg.doSave(f); + } + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Failure saving game.", e); + } + // Saving is not as destructive as loading. + + gameaction = ga_nothing; + savedescription = ""; + + players[consoleplayer].message = GGSAVED; + + // draw the pattern into the back screen + R_FillBackScreen: + { + sceneRenderer.FillBackScreen(); + } + } + + skill_t d_skill; + int d_episode; + int d_map; + + public void DeferedInitNew(skill_t skill, int episode, int map) { + d_skill = skill; + d_episode = episode; + d_map = map; + gameaction = ga_newgame; + } + + @SourceCode.Exact + @G_Game.C(G_DoNewGame) + public void DoNewGame() { + demoplayback = false; + netdemo = false; + netgame = false; + deathmatch = false; + playeringame[1] = playeringame[2] = playeringame[3] = false; + respawnparm = false; + fastparm = false; + nomonsters = false; + consoleplayer = 0; + G_InitNew: + { + InitNew(d_skill, d_episode, d_map); + } + gameaction = ga_nothing; + } + + /** + * G_InitNew + * Can be called by the startup code or the menu task, + * consoleplayer, displayplayer, playeringame[] should be set. + */ + @SourceCode.Compatible + @G_Game.C(G_InitNew) + public void InitNew(skill_t skill, int episode, int map) { + InitNew(skill, episode, map, false); + } + + private void InitNew(skill_t skill, int episode, int map, boolean noSwitchRandom) { + if (paused) { + paused = false; + S_ResumeSound: + { + doomSound.ResumeSound(); + } + } + + if (skill.ordinal() > skill_t.sk_nightmare.ordinal()) { + skill = skill_t.sk_nightmare; + } + + // This was quite messy with SPECIAL and commented parts. + // Supposedly hacks to make the latest edition work. + // It might not work properly. + if (episode < 1) { + episode = 1; + } + + if (isRetail()) { + if (episode > 4) { + episode = 4; + } + } else if (isShareware()) { + if (episode > 1) { + episode = 1; // only start episode 1 on shareware + } + } else { + if (episode > 3) { + episode = 3; + } + } + + if (map < 1) { + map = 1; + } + + if ((map > 9) && (!isCommercial())) { + map = 9; + } + + /** + * I wrote it that way. No worries JavaRandom will never be picked on vanilla demo playback + * - Good Sign 2017/05/08 + * + * @SourceCode.Compatible + */ + if (!noSwitchRandom) { + if (cVarManager.bool(CommandVariable.JAVARANDOM)) { + random.requireRandom(VERSION | JAVARANDOM_MASK); + } else { + random.requireRandom(VERSION); + } + } + + M_ClearRandom: + { + random.ClearRandom(); + } + + respawnmonsters = skill == skill_t.sk_nightmare || respawnparm; + + // If on nightmare/fast monsters make everything MOAR pimp. + if (fastparm || (skill == skill_t.sk_nightmare && gameskill != skill_t.sk_nightmare)) { + for (int i = statenum_t.S_SARG_RUN1.ordinal(); i <= statenum_t.S_SARG_PAIN2.ordinal(); i++) { + states[i].tics >>= 1; + } + + mobjinfo[mobjtype_t.MT_BRUISERSHOT.ordinal()].speed = 20 * MAPFRACUNIT; + mobjinfo[mobjtype_t.MT_HEADSHOT.ordinal()].speed = 20 * MAPFRACUNIT; + mobjinfo[mobjtype_t.MT_TROOPSHOT.ordinal()].speed = 20 * MAPFRACUNIT; + } else if (skill != skill_t.sk_nightmare && gameskill == skill_t.sk_nightmare) { + for (int i = statenum_t.S_SARG_RUN1.ordinal(); i <= statenum_t.S_SARG_PAIN2.ordinal(); i++) { + states[i].tics <<= 1; + } + + mobjinfo[mobjtype_t.MT_BRUISERSHOT.ordinal()].speed = 15 * MAPFRACUNIT; + mobjinfo[mobjtype_t.MT_HEADSHOT.ordinal()].speed = 10 * MAPFRACUNIT; + mobjinfo[mobjtype_t.MT_TROOPSHOT.ordinal()].speed = 10 * MAPFRACUNIT; + } + + // force players to be initialized upon first level load + for (int i = 0; i < MAXPLAYERS; i++) { + players[i].playerstate = PST_REBORN; + } + + // will be set false if a demo + usergame = true; + paused = false; + demoplayback = false; + automapactive = false; + viewactive = true; + gameepisode = episode; + gamemap = map; + gameskill = skill; + viewactive = true; + + // set the sky map for the episode + if (isCommercial()) { + textureManager.setSkyTexture(textureManager.TextureNumForName("SKY3")); + if (gamemap < 12) { + textureManager.setSkyTexture(textureManager.TextureNumForName("SKY1")); + } else if (gamemap < 21) { + textureManager.setSkyTexture(textureManager.TextureNumForName("SKY2")); + } + } else { + switch (episode) { + case 1: + textureManager.setSkyTexture(textureManager.TextureNumForName("SKY1")); + break; + case 2: + textureManager.setSkyTexture(textureManager.TextureNumForName("SKY2")); + break; + case 3: + textureManager.setSkyTexture(textureManager.TextureNumForName("SKY3")); + break; + case 4: // Special Edition sky + textureManager.setSkyTexture(textureManager.TextureNumForName("SKY4")); + break; + default: + break; + } + } + + G_DoLoadLevel: + { + if (!DoLoadLevel()) { + levelLoadFailure(); + } + } + } + + protected void levelLoadFailure() { + boolean endgame = doomSystem.GenerateAlert(Strings.LEVEL_FAILURE_TITLE, Strings.LEVEL_FAILURE_CAUSE, true); + + // Initiate endgame + if (endgame) { + gameaction = ga_failure; + gamestate = GS_DEMOSCREEN; + menu.ClearMenus(); + StartTitle(); + } else { + // Shutdown immediately. + doomSystem.Quit(); + } + } + + // + // DEMO RECORDING + // + public void ReadDemoTiccmd(ticcmd_t cmd) { + final IDemoTicCmd democmd = demobuffer.getNextTic(); + if (democmd == null) { + // end of demo data stream + CheckDemoStatus(); + + // Force status resetting + this.demobuffer.resetDemo(); + return; + } + + democmd.decode(cmd); + } + + public void WriteDemoTiccmd(ticcmd_t cmd) { + // press q to end demo recording + if (gamekeydown[key_recordstop]) { + CheckDemoStatus(); + } + + final IDemoTicCmd reccmd = new VanillaTiccmd(); + reccmd.encode(cmd); + demobuffer.putTic(reccmd); + + // MAES: Useless, we can't run out of space anymore (at least not in theory). + + /* demobuffer[demo_p++] = cmd.forwardmove; + demobuffer[demo_p++] = cmd.sidemove; + demobuffer[demo_p++] = (byte) ((cmd.angleturn+128)>>8); + demobuffer[demo_p++] = (byte) cmd.buttons; + demo_p -= 4; + if (demo_p > demoend - 16) + { + // no more space + CheckDemoStatus (); + return; + } */ + //ReadDemoTiccmd (cmd); // make SURE it is exactly the same + // MAES: this is NOT the way to do in Mocha, because we are not manipulating + // the demo index directly anymore. Instead, decode what we have just saved. + reccmd.decode(cmd); + } + + /** + * G_RecordDemo + */ + public void RecordDemo(String name) { + StringBuffer buf = new StringBuffer(); + usergame = false; + buf.append(name); + buf.append(".lmp"); + demoname = buf.toString(); + demobuffer = new VanillaDoomDemo(); + demorecording = true; + } + + @G_Game.C(G_BeginRecording) + public void BeginRecording() { + demobuffer.setVersion(cVarManager.bool(CommandVariable.JAVARANDOM) ? VERSION | JAVARANDOM_MASK : VERSION); + demobuffer.setSkill(gameskill); + demobuffer.setEpisode(gameepisode); + demobuffer.setMap(gamemap); + demobuffer.setDeathmatch(deathmatch); + demobuffer.setRespawnparm(respawnparm); + demobuffer.setFastparm(fastparm); + demobuffer.setNomonsters(nomonsters); + demobuffer.setConsoleplayer(consoleplayer); + demobuffer.setPlayeringame(playeringame); + } + + String defdemoname; + + /** + * G_PlayDemo + */ + public void DeferedPlayDemo(String name) { + defdemoname = name; + gameaction = ga_playdemo; + } + + @SuppressWarnings("UnusedAssignment") + @SourceCode.Compatible + @G_Game.C(G_DoPlayDemo) + public void DoPlayDemo() { + + skill_t skill; + boolean fail; + int i, episode, map; + + gameaction = ga_nothing; + // MAES: Yeah, it's OO all the way now, baby ;-) + W_CacheLumpName: + { + try { + demobuffer = wadLoader.CacheLumpName(defdemoname.toUpperCase(), PU_STATIC, VanillaDoomDemo.class); + } catch (Exception e) { + fail = true; + } + } + + fail = (demobuffer.getSkill() == null); + + final int version; + if (fail || ((version = demobuffer.getVersion() & 0xFF) & ~JAVARANDOM_MASK) != VERSION) { + LOGGER.log(Level.WARNING, String.format("Demo is from a different game version, version code read: %d", + demobuffer.getVersion())); + gameaction = ga_nothing; + return; + } + + random.requireRandom(version); + + skill = demobuffer.getSkill(); + episode = demobuffer.getEpisode(); + map = demobuffer.getMap(); + deathmatch = demobuffer.isDeathmatch(); + respawnparm = demobuffer.isRespawnparm(); + fastparm = demobuffer.isFastparm(); + nomonsters = demobuffer.isNomonsters(); + consoleplayer = demobuffer.getConsoleplayer(); + // Do this, otherwise previously loaded demos will be stuck at their end. + demobuffer.resetDemo(); + + boolean[] pigs = demobuffer.getPlayeringame(); + for (i = 0; i < MAXPLAYERS; i++) { + playeringame[i] = pigs[i]; + } + if (playeringame[1]) { + netgame = true; + netdemo = true; + } + + // don't spend a lot of time in loadlevel + precache = false; + G_InitNew: + { + InitNew(skill, episode, map, true); + } + precache = true; + + usergame = false; + demoplayback = true; + + } + + // + // G_TimeDemo + // + public void TimeDemo(String name) { + nodrawers = cVarManager.bool(CommandVariable.NODRAW); + noblit = cVarManager.bool(CommandVariable.NOBLIT); + timingdemo = true; + singletics = true; + defdemoname = name; + gameaction = ga_playdemo; + } + + /** G_CheckDemoStatus + * + * Called after a death or level completion to allow demos to be cleaned up + * Returns true if a new demo loop action will take place + * + */ + public boolean CheckDemoStatus() { + int endtime; + + if (timingdemo) { + endtime = RealTime.GetTime(); + // killough -- added fps information and made it work for longer demos: + long realtics = endtime - starttime; + + this.commit(); + CM.SaveDefaults(); + doomSystem.Error("timed %d gametics in %d realtics = %f frames per second", gametic, + realtics, gametic * (double) (TICRATE) / realtics); + } + + if (demoplayback) { + if (singledemo) { + doomSystem.Quit(); + } + + // Z_ChangeTag (demobuffer, PU_CACHE); + demoplayback = false; + netdemo = false; + netgame = false; + deathmatch = false; + playeringame[1] = playeringame[2] = playeringame[3] = false; + respawnparm = false; + fastparm = false; + nomonsters = false; + consoleplayer = 0; + AdvanceDemo(); + return true; + } + + if (demorecording) { + //demobuffer[demo_p++] = (byte) DEMOMARKER; + + MenuMisc.WriteFile(demoname, demobuffer); + //Z_Free (demobuffer); + demorecording = false; + doomSystem.Error("Demo %s recorded", demoname); + } + + return false; + } + + /** This should always be available for real timing */ + protected ITicker RealTime; + + // Bookkeeping on players - state. + public player_t[] players; + + public DelegateRandom random; + public final CVarManager cVarManager; + public final IWadLoader wadLoader; + public final IDoomSound doomSound; + public final ISoundDriver soundDriver; + public final IMusic music; + public final AbstractStatusBar statusBar; + public final DoomGraphicSystem graphicSystem; + public final DoomSystemNetworking systemNetworking; + public final IDoomGameNetworking gameNetworking; + public final AbstractLevelLoader levelLoader; + public final IDoomMenu menu; + public final ActionFunctions actions; + public final SceneRenderer sceneRenderer; + public final HU headsUp; + public final IAutoMap autoMap; + public final Finale finale; + public final EndLevel endLevel; + public final Wiper wiper; + public final TextureManager textureManager; + public final ISpriteManager spriteManager; + public final ITicker ticker; + public final IDiskDrawer diskDrawer; + public final IDoomSystem doomSystem; + public final BppMode bppMode; + + /** + * Since this is a fully OO implementation, we need a way to create + * the instances of the Refresh daemon, the Playloop, the Wadloader + * etc. which however are now completely independent of each other + * (well, ALMOST), and are typically only passed context when + * instantiated. + * + * If you instantiate one too early, it will have null context. + * + * The trick is to construct objects in the correct order. Some of + * them have Init() methods which are NOT yet safe to call. + * + */ + @SuppressWarnings("LeakingThisInConstructor") + public DoomMain() throws IOException { + // Init game status... + super(); + + // Init players + players = new player_t[MAXPLAYERS]; + Arrays.setAll(players, i -> new player_t(this)); + + // Init objects + this.cVarManager = Engine.getCVM(); + + // Prepare events array with event instances + Arrays.fill(events, event_t.EMPTY_EVENT); + + // Create DoomSystem + this.doomSystem = new DoomSystem(this); + + // Choose bppMode depending on CVar's + // TODO: add config options + this.bppMode = BppMode.chooseBppMode(cVarManager); + + // Create real time ticker + this.RealTime = new MilliTicker(); + + // Doommain is both "main" and handles most of the game status. + this.gameNetworking = this; // DoomMain also handles its own Game Networking. + + // Set ticker. It is a shared status object, but not a holder itself. + this.ticker = ITicker.createTicker(cVarManager); + + // Network "driver" + this.systemNetworking = new DummyNetworkDriver<>(this); + + // Random number generator, but we can have others too. + this.random = new DelegateRandom(); + LOGGER.log(Level.INFO, String.format("M_Random: Using %s.", random.getClass().getSimpleName())); + + // Sound can be left until later, in Start + this.wadLoader = new WadLoader(this.doomSystem); // The wadloader is a "weak" status holder. + + // TODO: find out if we have requests for a specific resolution, + // and try honouring them as closely as possible. + // 23/5/2011: Experimental dynamic resolution subsystem + this.vs = VisualSettings.parse(cVarManager, CM); + this.spriteManager = new SpriteManager<>(this); + + // Heads-up, Menu, Level Loader + this.headsUp = new HU(this); + this.menu = new Menu<>(this); + this.levelLoader = new BoomLevelLoader(this); + + // Renderer, Actions, StatusBar, AutoMap + this.sceneRenderer = bppMode.sceneRenderer(this); + this.actions = new ActionFunctions(this); + this.statusBar = new StatusBar(this); + + // Let the renderer pick its own. It makes linking easier. + this.textureManager = sceneRenderer.getTextureManager(); + // Instantiating EndLevel, Finale + this.endLevel = new EndLevel<>(this); + this.finale = selectFinale(); + + readCVars(); + LOGGER.log(Level.INFO, String.format("W_Init: Init WAD files: [%s]", + Arrays.stream(wadfiles).filter(Objects::nonNull).collect(Collectors.joining(", ")))); + try { + wadLoader.InitMultipleFiles(wadfiles); + } catch (Exception e1) { + LOGGER.log(Level.SEVERE, "Could not init WAD files", e1); + } + + // Video Renderer + this.graphicSystem = RendererFactory.newBuilder() + .setVideoScale(vs).setBppMode(bppMode).setWadLoader(wadLoader) + .build(); + + LOGGER.log(Level.INFO, "V_Init: Allocate screens."); + + // Disk access visualizer + this.diskDrawer = new DiskDrawer(this, DiskDrawer.STDISK); + + // init subsystems + LOGGER.log(Level.INFO, "AM_Init: Init Automap colors."); + this.autoMap = new automap.Map<>(this); + + this.wiper = graphicSystem.createWiper(random); + + // Update variables and stuff NOW. + this.update(); + + // Check for -file in shareware + CheckForPWADSInShareware(); + + printGameInfo(); + + LOGGER.log(Level.INFO, "Tables.InitTables: Init trigonometric LUTs."); + Tables.InitTables(); + + LOGGER.log(Level.INFO, "M_Init: Init miscellaneous info."); + menu.Init(); + + LOGGER.log(Level.INFO, "R_Init: Init DOOM refresh daemon."); + sceneRenderer.Init(); + + LOGGER.log(Level.INFO, "P_Init: Init Playloop state."); + actions.Init(); + + LOGGER.log(Level.INFO, "I_Init: Setting up machine state."); + doomSystem.Init(); + + LOGGER.log(Level.INFO, "D_CheckNetGame: Checking network game status."); + CheckNetGame(); + + LOGGER.log(Level.INFO, "S_Init: Setting up sound."); + // Sound "drivers" before the game sound controller. + this.music = IMusic.chooseModule(cVarManager); + this.soundDriver = ISoundDriver.chooseModule(this, cVarManager); + this.doomSound = IDoomSound.chooseSoundIsPresent(this, cVarManager, soundDriver); + + music.InitMusic(); + doomSound.Init(snd_SfxVolume * 8, snd_MusicVolume * 8); + + LOGGER.log(Level.INFO, "HU_Init: Setting up heads up display."); + headsUp.Init(); + + LOGGER.log(Level.INFO, "ST_Init: Init status bar."); + statusBar.Init(); + + if (statcopy != null) { + LOGGER.log(Level.INFO, "External statistics registered."); + } + + // NOW it's safe to init the disk reader. + diskDrawer.Init(); + } + + @Override + public final void update() { + super.update(); + // Video...so you should wait until video renderer is active. + this.graphicSystem.setUsegamma(CM.getValue(Settings.usegamma, Integer.class)); + + // These should really be handled by the menu. + this.menu.setShowMessages(CM.equals(Settings.show_messages, 1)); + this.menu.setScreenBlocks(CM.getValue(Settings.screenblocks, Integer.class)); + + // These should be handled by the HU + for (int i = 0; i <= 9; i++) { + + String chatmacro = String.format("chatmacro%d", i); + this.headsUp.setChatMacro(i, CM.getValue(Settings.valueOf(chatmacro), String.class)); + } + } + + @Override + public final void commit() { + super.commit(); + // Video... + CM.update(Settings.usegamma, graphicSystem.getUsegamma()); + + // These should really be handled by the menu. + CM.update(Settings.show_messages, this.menu.getShowMessages()); + CM.update(Settings.screenblocks, this.menu.getScreenBlocks()); + + // These should be handled by the HU + for (int i = 0; i <= 9; i++) { + CM.update(Settings.valueOf(String.format("chatmacro%d", i)), this.headsUp.chat_macros[i]); + } + } + + public void setupLoop() throws IOException { + // check for a driver that wants intermission stats + cVarManager.with(CommandVariable.STATCOPY, 0, (String s) -> { + // TODO: this should be chained to a logger + statcopy = s; + LOGGER.log(Level.INFO, "External statistics registered."); + }); + + // start the apropriate game based on parms + cVarManager.with(CommandVariable.RECORD, 0, (String s) -> { + RecordDemo(s); + autostart = true; + }); + + //p = CM.CheckParm ("-timedemo"); + ChooseLoop: + { + if (singletics) { + TimeDemo(loaddemo); + autostart = true; + break ChooseLoop; // DoomLoop(); // never returns + } + + if (fastdemo || normaldemo) { + singledemo = true; // quit after one demo + if (fastdemo) { + timingdemo = true; + } + InitNew(startskill, startepisode, startmap); + gamestate = GS_DEMOSCREEN; + DeferedPlayDemo(loaddemo); + break ChooseLoop; // DoomLoop(); // never returns + } + + if (gameaction != ga_loadgame) { + if (autostart || netgame) { + InitNew(startskill, startepisode, startmap); + } else { + StartTitle(); // start up intro loop + } + } + } + + DoomLoop(); // never returns + } + + private void printGameInfo() { + // Iff additonal PWAD files are used, print modified banner + if (modifiedgame) // Generate WAD loading alert. Abort upon denial. + { + if (!doomSystem.GenerateAlert(Strings.MODIFIED_GAME_TITLE, Strings.MODIFIED_GAME_DIALOG, true)) { + wadLoader.CloseAllHandles(); + System.exit(-2); + } + } + + // Check and print which version is executed. + switch (getGameMode()) { + case shareware: + case indetermined: + LOGGER.log(Level.INFO, "Game Info: Shareware!"); + break; + case registered: + case retail: + case commercial: + case pack_tnt: + case pack_plut: + case pack_xbla: + LOGGER.log(Level.INFO, "Game Info: Commercial product - do not distribute!"); + LOGGER.log(Level.INFO, "Game Note: Please report software piracy to the SPA: 1-800-388-PIR8"); + break; + case freedoom1: + case freedoom2: + case freedm: + LOGGER.log(Level.INFO, "Game Info: Copyright (c) 2001-2017 Contributors to the Freedoom project. All rights reserved."); + LOGGER.log(Level.INFO, "Game Note: See https://github.com/freedoom/freedoom/blob/master/COPYING.adoc"); + break; + default: + // Ouch. + break; + } + } + + private void readCVars() { + /** + * D_DoomMain + * + * Completes the job started by Init. Here everything priority-critical is called and created in more detail. + */ + + final StringBuffer file = new StringBuffer(); + final String iwadfilename = IdentifyVersion(); + nomonsters = cVarManager.bool(CommandVariable.NOMONSTERS); + respawnparm = cVarManager.bool(CommandVariable.RESPAWN); + fastparm = cVarManager.bool(CommandVariable.FAST); + devparm = cVarManager.bool(CommandVariable.DEVPARM); + + if (!(altdeath = cVarManager.bool(CommandVariable.ALTDEATH))) { + deathmatch = cVarManager.bool(CommandVariable.DEATHMATCH); + } + + // MAES: Check for Ultimate Doom in "doom.wad" filename. + final WadLoader tmpwad = new WadLoader(); + try { + tmpwad.InitFile(iwadfilename); + // Check using a reloadable hack. + CheckForUltimateDoom(tmpwad); + } catch (Exception e2) { + // TODO Auto-generated catch block + LOGGER.log(Level.SEVERE, "Failure reading CVars.", e2); + } finally { + tmpwad.CloseAllHandles(); + } + + // MAES: better extract a method for this. + GenerateTitle(); + // Print ticker info. It has already been set at Init() though. + if (cVarManager.bool(CommandVariable.MILLIS)) { + LOGGER.log(Level.INFO, "ITicker: Using millisecond accuracy timer."); + } else if (cVarManager.bool(CommandVariable.FASTTIC)) { + LOGGER.log(Level.INFO, "ITicker: Using fastest possible timer."); + } else { + LOGGER.log(Level.INFO, "ITicker: Using nanosecond accuracy timer."); + } + LOGGER.log(Level.INFO, title.toString()); + if (devparm) { + LOGGER.log(Level.INFO, D_DEVSTR); + } + // Running from CDROM? + if (cVarManager.bool(CommandVariable.CDROM)) { + LOGGER.log(Level.INFO, D_CDROM); + //System.get("c:\\doomdata",0); + //System.out.println (Settings.basedefault+"c:/doomdata/default.cfg"); + } + // turbo option + if (cVarManager.specified(CommandVariable.TURBO)) { + int scale = 200; + if (cVarManager.present(CommandVariable.TURBO)) { + scale = cVarManager.get(CommandVariable.TURBO, Integer.class, 0).get(); + } + if (scale < 10) { + scale = 10; + } + if (scale > 400) { + scale = 400; + } + LOGGER.log(Level.INFO, String.format("turbo scale: %d", scale)); + forwardmove[0] = forwardmove[0] * scale / 100; + forwardmove[1] = forwardmove[1] * scale / 100; + sidemove[0] = sidemove[0] * scale / 100; + sidemove[1] = sidemove[1] * scale / 100; + } + // add any files specified on the command line with -file wadfile + // to the wad list + // + // convenience hack to allow -wart e m to add a wad file + // prepend a tilde to the filename so wadfile will be reloadable + if (cVarManager.present(CommandVariable.WART)) { + final int ep = cVarManager.get(CommandVariable.WART, Integer.class, 0).get(); + final int map = cVarManager.get(CommandVariable.WART, Integer.class, 1).get(); + cVarManager.override(CommandVariable.WARP, new CommandVariable.WarpFormat(ep * 10 + map), 0); + GameMode gamemode = getGameMode(); + // Map name handling. + switch (gamemode) { + case shareware: + case retail: + case registered: + case freedoom1: + file.append("~"); + file.append(DEVMAPS); + file.append(String.format("E%dM%d.wad", ep, map)); + file.append(String.format("Warping to Episode %s, Map %s.\n", ep, map)); + break; + case commercial: + case freedoom2: + case freedm: + default: + if (ep < 10) { + file.append("~"); + file.append(DEVMAPS); + file.append(String.format("cdata/map0%d.wad", ep)); + } else { + file.append("~"); + file.append(DEVMAPS); + file.append(String.format("cdata/map%d.wad", ep)); + } + break; + } + AddFile(file.toString()); + } + + if (cVarManager.present(CommandVariable.FILE)) { + // the parms after p are wadfile/lump names, + // until end of parms or another - preceded parm + modifiedgame = true; // homebrew levels + cVarManager.with(CommandVariable.FILE, 0, (String[] a) -> { + Arrays.stream(a) + .map(s -> C2JUtils.unquoteIfQuoted(s, '"')) + .forEach(this::AddFile); + }); + } + + if (cVarManager.present(CommandVariable.PLAYDEMO)) { + normaldemo = true; + loaddemo = cVarManager.get(CommandVariable.PLAYDEMO, String.class, 0).get(); + } else if (cVarManager.present(CommandVariable.FASTDEMO)) { + LOGGER.log(Level.INFO, "Fastdemo mode. Boundless clock!"); + fastdemo = true; + loaddemo = cVarManager.get(CommandVariable.FASTDEMO, String.class, 0).get(); + } else if (cVarManager.present(CommandVariable.TIMEDEMO)) { + singletics = true; + loaddemo = cVarManager.get(CommandVariable.TIMEDEMO, String.class, 0).get(); + } + + // If any of the previous succeeded, try grabbing the filename. + if (loaddemo != null) { + loaddemo = C2JUtils.unquoteIfQuoted(loaddemo, '"'); + AddFile(loaddemo + ".lmp"); + LOGGER.log(Level.INFO, String.format("Playing demo %s.lmp.", loaddemo)); + autostart = true; + } + + // Subsequent uses of loaddemo use only the lump name. + loaddemo = C2JUtils.extractFileBase(loaddemo, 0, true); + // get skill / episode / map from parms + // FIXME: should get them FROM THE DEMO itself. + startskill = skill_t.sk_medium; + startepisode = 1; + startmap = 1; + //autostart = false; + + if (cVarManager.present(CommandVariable.NOVERT)) { + novert = cVarManager.get(CommandVariable.NOVERT, CommandVariable.ForbidFormat.class, 0) + .filter(CommandVariable.ForbidFormat.FORBID::equals) + .isPresent(); + + if (!novert) { + LOGGER.log(Level.INFO, "-novert ENABLED (default)"); + } else { + LOGGER.log(Level.INFO, "-novert DISABLED. Hope you know what you're doing..."); + } + } + + cVarManager.with(CommandVariable.SKILL, 0, (Integer s) -> { + startskill = skill_t.values()[s - 1]; + autostart = true; + }); + + cVarManager.with(CommandVariable.EPISODE, 0, (Integer ep) -> { + startepisode = ep; + startmap = 1; + autostart = true; + }); + + if (cVarManager.present(CommandVariable.TIMER) && deathmatch) { + // Good Sign (2017/03/31) How this should work? + final int time = cVarManager.get(CommandVariable.TIMER, Integer.class, 0).get(); + LOGGER.log(Level.INFO, String.format("Levels will end after %d minute(s)", time)); + } + // OK, and exactly how is this enforced? + if (cVarManager.bool(CommandVariable.AVG) && deathmatch) { + LOGGER.log(Level.INFO, "Austin Virtual Gaming: Levels will end after 20 minutes"); + } + + // MAES 31/5/2011: added support for +map variation. + cVarManager.with(CommandVariable.WARP, 0, (CommandVariable.WarpFormat w) -> { + final CommandVariable.WarpMetric metric = w.getMetric(isCommercial()); + startepisode = metric.getEpisode(); + startmap = metric.getMap(); + autostart = true; + }); + + // Maes: 1/6/2011 Added +map support + cVarManager.with(CommandVariable.MAP, 0, (CommandVariable.MapFormat m) -> { + final CommandVariable.WarpMetric metric = m.getMetric(isCommercial()); + startepisode = metric.getEpisode(); + startmap = metric.getMap(); + autostart = true; + }); + + cVarManager.with(CommandVariable.LOADGAME, 0, (Character c) -> { + file.delete(0, file.length()); + if (cVarManager.bool(CommandVariable.CDROM)) { + file.append("c:\\doomdata\\"); + } + + file.append(String.format("%s%d.dsg", SAVEGAMENAME, c)); + LoadGame(file.toString()); + }); + } + + /** + * Since it's so intimately tied, it's less troublesome to merge the "main" and "network" code. + */ + /** To be initialized by the DoomNetworkingInterface via a setter */ + //private doomcom_t doomcom; + //private doomdata_t netbuffer; // points inside doomcom + protected StringBuilder sb = new StringBuilder(); + + // + // NETWORKING + // + // gametic is the tic about to (or currently being) run + // maketic is the tick that hasn't had control made for it yet + // nettics[] has the maketics for all players + // + // a gametic cannot be run until nettics[] > gametic for all players + // + //ticcmd_t[] localcmds= new ticcmd_t[BACKUPTICS]; + //ticcmd_t [][] netcmds=new ticcmd_t [MAXPLAYERS][BACKUPTICS]; + int[] nettics = new int[MAXNETNODES]; + boolean[] nodeingame = new boolean[MAXNETNODES]; // set false as nodes leave game + boolean[] remoteresend = new boolean[MAXNETNODES]; // set when local needs tics + int[] resendto = new int[MAXNETNODES]; // set when remote needs tics + int[] resendcount = new int[MAXNETNODES]; + + int[] nodeforplayer = new int[MAXPLAYERS]; + + int maketic; + int lastnettic; + int skiptics; + protected int ticdup; + + public int getTicdup() { + return ticdup; + } + + public void setTicdup(int ticdup) { + this.ticdup = ticdup; + } + + int maxsend; // BACKUPTICS/(2*ticdup)-1; + + //void D_ProcessEvents (void); + //void G_BuildTiccmd (ticcmd_t *cmd); + //void D_DoAdvanceDemo (void); + // _D_ + boolean reboundpacket = false; + doomdata_t reboundstore = new doomdata_t(); + + /** + * MAES: interesting. After testing it was found to return the following size: + * (8*(netbuffer.numtics+1)); + */ + int NetbufferSize() { + // return (int)(((doomdata_t)0).cmds[netbuffer.numtics]); + return (8 * (netbuffer.numtics + 1)); + } + + protected long NetbufferChecksum() { + // FIXME -endianess? + if (NORMALUNIX) { + return 0; // byte order problems + } + + /** + * Here it was trying to get the length of a doomdata_t struct up to retransmit from. + * l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4; + * (int)&(((doomdata_t *)0)->retransmitfrom) evaluates to "4" + * Therefore, l= (netbuffersize - 4)/4 + */ + final int l = (NetbufferSize() - 4) / 4; + long c = 0x1234567L; + for (int i = 0; i < l; i++) { // TODO: checksum would be better computer in the netbuffer itself. + // The C code actually takes all fields into account. + c += 0;// TODO: (netbuffer->retransmitfrom)[i] * (i+1); + } + return c & NCMD_CHECKSUM; + } + + protected int ExpandTics(int low) { + int delta; + + delta = low - (maketic & 0xff); + + if (delta >= -64 && delta <= 64) { + return (maketic & ~0xff) + low; + } + + if (delta > 64) { + return (maketic & ~0xff) - 256 + low; + } + + if (delta < -64) { + return (maketic & ~0xff) + 256 + low; + } + + doomSystem.Error("ExpandTics: strange value %d at maketic %d", low, maketic); + return 0; + } + + /** + * HSendPacket + * + * Will send out a packet to all involved parties. A special case is the rebound storage, which acts as a local + * "echo" which is then picked up by the host itself. This is necessary to simulate a 1-node network. + * + * @throws IOException + */ + void HSendPacket(int node, int flags) { + netbuffer.checksum = (int) (NetbufferChecksum() | flags); + + // Local node's comms are sent to rebound packet, which is + // then picked up again. THIS IS VITAL FOR SINGLE-PLAYER + // SPEED THROTTLING TOO, AS IT RELIES ON NETWORK ACKS/BUSY + // WAITING. + if (node == 0) { + // _D_ + reboundstore.copyFrom(netbuffer); + reboundpacket = true; + return; + } + + if (demoplayback) { + return; + } + + if (!netgame) { + doomSystem.Error("Tried to transmit to another node"); + } + + doomcom.command = CMD_SEND; + doomcom.remotenode = (short) node; + doomcom.datalength = (short) NetbufferSize(); + + if (debugfile != null) { + int i; + int realretrans; + if (flags(netbuffer.checksum, NCMD_RETRANSMIT)) { + realretrans = ExpandTics(netbuffer.retransmitfrom); + } else { + realretrans = -1; + } + + logger(debugfile, "send (" + ExpandTics(netbuffer.starttic) + ", " + netbuffer.numtics + ", R " + + realretrans + "[" + doomcom.datalength + "]"); + + for (i = 0; i < doomcom.datalength; i++) // TODO: get a serialized string representation. + { + logger(debugfile, netbuffer.toString() + "\n"); + } + } + + // This should execute a "send" command for the current stuff in doomcom. + systemNetworking.NetCmd(); + } + + // + // HGetPacket + // Returns false if no packet is waiting + // + private boolean HGetPacket() { + // Fugly way of "clearing" the buffer. + sb.setLength(0); + if (reboundpacket) { + // FIXME: MAES: this looks like a struct copy + netbuffer.copyFrom(reboundstore); + doomcom.remotenode = 0; + reboundpacket = false; + return true; + } + + // If not actually a netgame (e.g. single player, demo) return. + if (!netgame) { + return false; + } + + if (demoplayback) { + return false; + } + + doomcom.command = CMD_GET; + systemNetworking.NetCmd(); + + // Invalid node? + if (doomcom.remotenode == -1) { + return false; + } + + if (doomcom.datalength != NetbufferSize()) { + if (eval(debugfile)) { + logger(debugfile, "bad packet length " + doomcom.datalength + "\n"); + } + return false; + } + + if (NetbufferChecksum() != (netbuffer.checksum & NCMD_CHECKSUM)) { + if (eval(debugfile)) { + logger(debugfile, "bad packet checksum\n"); + } + return false; + } + + if (eval(debugfile)) { + int realretrans; + int i; + + if (flags(netbuffer.checksum, NCMD_SETUP)) { + logger(debugfile, "setup packet\n"); + } else { + if (flags(netbuffer.checksum, NCMD_RETRANSMIT)) { + realretrans = ExpandTics(netbuffer.retransmitfrom); + } else { + realretrans = -1; + } + + sb.append("get "); + sb.append(doomcom.remotenode); + sb.append(" = ("); + sb.append(ExpandTics(netbuffer.starttic)); + sb.append(" + "); + sb.append(netbuffer.numtics); + sb.append(", R "); + sb.append(realretrans); + sb.append(")["); + sb.append(doomcom.datalength); + sb.append("]"); + + logger(debugfile, sb.toString()); + + // Trick: force update of internal buffer. + netbuffer.pack(); + + /** + * TODO: Could it be actually writing stuff beyond the boundaries of a single doomdata object? + * A doomcom object has a lot of header info, and a single "raw" data placeholder, which by now + * should be inside netbuffer....right? + **/ + try { + for (i = 0; i < doomcom.datalength; i++) { + debugfile.write(Integer.toHexString(netbuffer.cached()[i])); + debugfile.write('\n'); + } + } catch (IOException e) { + } // "Drown" IOExceptions here. + } + } + return true; + } + + //// GetPackets + StringBuilder exitmsg = new StringBuilder(80); + + public void GetPackets() { + int netconsole; + int netnode; + ticcmd_t src, dest; + int realend; + int realstart; + + while (HGetPacket()) { + if (flags(netbuffer.checksum, NCMD_SETUP)) { + continue; // extra setup packet + } + netconsole = netbuffer.player & ~PL_DRONE; + netnode = doomcom.remotenode; + + // to save bytes, only the low byte of tic numbers are sent + // Figure out what the rest of the bytes are + realstart = ExpandTics(netbuffer.starttic); + realend = (realstart + netbuffer.numtics); + + // check for exiting the game + if (flags(netbuffer.checksum, NCMD_EXIT)) { + if (!nodeingame[netnode]) { + continue; + } + nodeingame[netnode] = false; + playeringame[netconsole] = false; + exitmsg.insert(0, "Player 1 left the game"); + exitmsg.setCharAt(7, (char) (exitmsg.charAt(7) + netconsole)); + players[consoleplayer].message = exitmsg.toString(); + if (demorecording) { + CheckDemoStatus(); + } + continue; + } + + // check for a remote game kill + if (flags(netbuffer.checksum, NCMD_KILL)) { + doomSystem.Error("Killed by network driver"); + } + + nodeforplayer[netconsole] = netnode; + + // check for retransmit request + if (resendcount[netnode] <= 0 + && flags(netbuffer.checksum, NCMD_RETRANSMIT)) { + resendto[netnode] = ExpandTics(netbuffer.retransmitfrom); + if (eval(debugfile)) { + sb.setLength(0); + sb.append("retransmit from "); + sb.append(resendto[netnode]); + sb.append('\n'); + logger(debugfile, sb.toString()); + resendcount[netnode] = RESENDCOUNT; + } + } else { + resendcount[netnode]--; + } + + // check for out of order / duplicated packet + if (realend == nettics[netnode]) { + continue; + } + + if (realend < nettics[netnode]) { + if (eval(debugfile)) { + sb.setLength(0); + sb.append("out of order packet ("); + sb.append(realstart); + sb.append(" + "); + sb.append(netbuffer.numtics); + sb.append(")\n"); + logger(debugfile, sb.toString()); + } + continue; + } + + // check for a missed packet + if (realstart > nettics[netnode]) { + // stop processing until the other system resends the missed tics + if (eval(debugfile)) { + sb.setLength(0); + sb.append("missed tics from "); + sb.append(netnode); + sb.append(" ("); + sb.append(realstart); + sb.append(" - "); + sb.append(nettics[netnode]); + sb.append(")\n"); + logger(debugfile, sb.toString()); + } + remoteresend[netnode] = true; + continue; + } + + // update command store from the packet + { + int start; + + remoteresend[netnode] = false; + + start = nettics[netnode] - realstart; + src = netbuffer.cmds[start]; + + while (nettics[netnode] < realend) { + dest = netcmds[netconsole][nettics[netnode] % BACKUPTICS]; + nettics[netnode]++; + // MAES: this is a struct copy. + src.copyTo(dest); + // Advance src + start++; + + //_D_: had to add this (see linuxdoom source). That fixed that damn consistency failure!!! + if (start < netbuffer.cmds.length) { + src = netbuffer.cmds[start]; + } + + } + } + } + } + + protected void logger(OutputStreamWriter debugfile, String string) { + try { + debugfile.write(string); + } catch (IOException e) { + // TODO Auto-generated catch block + LOGGER.log(Level.SEVERE, "Failure writing debug file.", e); + } + } + + int gametime; + + @Override + public void NetUpdate() { + int nowtime; + int newtics; + int i, j; + int realstart; + int gameticdiv; + + // check time + nowtime = ticker.GetTime() / ticdup; + newtics = nowtime - gametime; + gametime = nowtime; + + if (newtics <= 0) { // nothing new to update + // listen for other packets + GetPackets(); + } else { + + if (skiptics <= newtics) { + newtics -= skiptics; + skiptics = 0; + } else { + skiptics -= newtics; + newtics = 0; + } + + netbuffer.player = (byte) consoleplayer; + + // build new ticcmds for console player + gameticdiv = gametic / ticdup; + for (i = 0; i < newtics; i++) { + //videoInterface.StartTic(); + ProcessEvents(); + if (maketic - gameticdiv >= BACKUPTICS / 2 - 1) { + break; // can't hold any more + } + //System.out.printf ("mk:%d ",maketic); + BuildTiccmd(localcmds[maketic % BACKUPTICS]); + maketic++; + } + + if (singletics) { + return; // singletic update is syncronous + } + // send the packet to the other nodes + for (i = 0; i < doomcom.numnodes; i++) { + if (nodeingame[i]) { + netbuffer.starttic = (byte) (realstart = resendto[i]); + netbuffer.numtics = (byte) (maketic - realstart); + if (netbuffer.numtics > BACKUPTICS) { + doomSystem.Error("NetUpdate: netbuffer.numtics > BACKUPTICS"); + } + + resendto[i] = maketic - doomcom.extratics; + + for (j = 0; j < netbuffer.numtics; j++) { + localcmds[(realstart + j) % BACKUPTICS].copyTo(netbuffer.cmds[j]); + } + // MAES: one of _D_ fixes. + //netbuffer.cmds[j] = localcmds[(realstart+j)%BACKUPTICS]; + + if (remoteresend[i]) { + netbuffer.retransmitfrom = (byte) nettics[i]; + HSendPacket(i, NCMD_RETRANSMIT); + } else { + netbuffer.retransmitfrom = 0; + HSendPacket(i, 0); + } + } + } + GetPackets(); + } + } + + // + // CheckAbort + // + private void CheckAbort() { + event_t ev; + int stoptic; + + stoptic = ticker.GetTime() + 2; + while (ticker.GetTime() < stoptic) { + } + //videoInterface.StartTic (); + + //videoInterface.StartTic (); + for (; eventtail != eventhead; eventtail = (++eventtail) & (MAXEVENTS - 1)) { + ev = events[eventtail]; + if (ev.isKey(SC_ESCAPE, ev_keydown)) { + doomSystem.Error("Network game synchronization aborted."); + } + } + } + + boolean[] gotinfo = new boolean[MAXNETNODES]; + + /** + * D_ArbitrateNetStart + * @throws IOException + * + * + */ + public void ArbitrateNetStart() throws IOException { + int i; + autostart = true; + + // Clear it up... + memset(gotinfo, false, gotinfo.length); + if (doomcom.consoleplayer != 0) { + // listen for setup info from key player + LOGGER.log(Level.INFO, "listening for network start info..."); + while (true) { + CheckAbort(); + if (!HGetPacket()) { + continue; + } + if (flags(netbuffer.checksum, NCMD_SETUP)) { + if (netbuffer.player != VERSION) { + doomSystem.Error("Different DOOM versions cannot play a net game!"); + } + startskill = skill_t.values()[netbuffer.retransmitfrom & 15]; + + if (((netbuffer.retransmitfrom & 0xc0) >> 6) == 1) { + // Deathmatch + deathmatch = true; + } else if (((netbuffer.retransmitfrom & 0xc0) >> 6) == 2) { + // Cooperative + altdeath = true; + } + + nomonsters = (netbuffer.retransmitfrom & 0x20) > 0; + respawnparm = (netbuffer.retransmitfrom & 0x10) > 0; + startmap = netbuffer.starttic & 0x3f; + startepisode = netbuffer.starttic >> 6; + return; + } + } + } else { + // key player, send the setup info + LOGGER.log(Level.INFO, "sending network start info..."); + do { + CheckAbort(); + for (i = 0; i < doomcom.numnodes; i++) { + netbuffer.retransmitfrom = (byte) startskill.ordinal(); + if (deathmatch) { + netbuffer.retransmitfrom |= (1 << 6); + } else if (altdeath) { + netbuffer.retransmitfrom |= (2 << 6); + } + + if (nomonsters) { + netbuffer.retransmitfrom |= 0x20; + } + + if (respawnparm) { + netbuffer.retransmitfrom |= 0x10; + } + + netbuffer.starttic = (byte) (startepisode * 64 + startmap); + netbuffer.player = VERSION; + netbuffer.numtics = 0; + HSendPacket(i, NCMD_SETUP); + } + + //#if 1 + for (i = 10; (i > 0) && HGetPacket(); --i) { + if ((netbuffer.player & 0x7f) < MAXNETNODES) { + gotinfo[netbuffer.player & 0x7f] = true; + } + } + /* + while (HGetPacket ()) + { + gotinfo[netbuffer.player&0x7f] = true; + } + */ + + for (i = 1; i < doomcom.numnodes; i++) { + if (!gotinfo[i]) { + break; + } + } + } while (i < doomcom.numnodes); + } + } + + /** + * D_CheckNetGame + * Works out player numbers among the net participants + **/ + private void CheckNetGame() throws IOException { + for (int i = 0; i < MAXNETNODES; i++) { + nodeingame[i] = false; + nettics[i] = 0; + remoteresend[i] = false; // set when local needs tics + resendto[i] = 0; // which tic to start sending + } + + // I_InitNetwork sets doomcom and netgame + systemNetworking.InitNetwork(); + if (doomcom.id != DOOMCOM_ID) { + doomSystem.Error("Doomcom buffer invalid!"); + } + + // Maes: This is the only place where netbuffer is definitively set to something + netbuffer = doomcom.data; + consoleplayer = displayplayer = doomcom.consoleplayer; + if (netgame) { + ArbitrateNetStart(); + } + + LOGGER.log(Level.FINE, String.format("startskill %s, deathmatch: %s, startmap: %d, startepisode: %d", + startskill.toString(), Boolean.toString(deathmatch), startmap, startepisode)); + + // read values out of doomcom + ticdup = doomcom.ticdup; + // MAES: ticdup must not be zero at this point. Obvious, no? + maxsend = BACKUPTICS / (2 * ticdup) - 1; + if (maxsend < 1) { + maxsend = 1; + } + + for (int i = 0; i < doomcom.numplayers; i++) { + playeringame[i] = true; + } + + for (int i = 0; i < doomcom.numnodes; i++) { + nodeingame[i] = true; + } + + LOGGER.log(Level.INFO, String.format("Player %d of %d (%d node(s))", (consoleplayer + 1), doomcom.numplayers, doomcom.numnodes)); + } + + /** + * D_QuitNetGame + * Called before quitting to leave a net game + * without hanging the other players + **/ + @Override + public void QuitNetGame() throws IOException { + if (eval(debugfile)) { + try { + debugfile.close(); + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Quit net game failure.", e); + } + } + + if (!netgame || !usergame || consoleplayer == -1 || demoplayback) { + return; + } + + // send a bunch of packets for security + netbuffer.player = (byte) consoleplayer; + netbuffer.numtics = 0; + for (int i = 0; i < 4; i++) { + for (int j = 1; j < doomcom.numnodes; j++) { + if (nodeingame[j]) { + HSendPacket(j, NCMD_EXIT); + } + } + doomSystem.WaitVBL(1); + } + } + + /** + * TryRunTics + **/ + int[] frametics = new int[4]; + int frameon; + boolean[] frameskip = new boolean[4]; + int oldnettics; + int oldentertics; + + @Override + public void TryRunTics() throws IOException { + int i; + int lowtic; + int entertic; + + int realtics; + int availabletics; + int counts; + int numplaying; + + // get real tics + entertic = ticker.GetTime() / ticdup; + realtics = entertic - oldentertics; + oldentertics = entertic; + + //System.out.printf("Entertic %d, realtics %d, oldentertics %d\n",entertic,realtics,oldentertics); + // get available tics + NetUpdate(); + + lowtic = MAXINT; + numplaying = 0; + for (i = 0; i < doomcom.numnodes; i++) { + if (nodeingame[i]) { + numplaying++; + if (nettics[i] < lowtic) { + lowtic = nettics[i]; + } + } + } + availabletics = lowtic - gametic / ticdup; + + // decide how many tics to run + if (realtics < availabletics - 1) { + counts = realtics + 1; + } else if (realtics < availabletics) { + counts = realtics; + } else { + counts = availabletics; + } + + if (counts < 1) { + counts = 1; + } + + frameon++; + + if (eval(debugfile)) { + sb.setLength(0); + sb.append("=======real: "); + sb.append(realtics); + sb.append(" avail: "); + sb.append(availabletics); + sb.append(" game: "); + sb.append(counts); + sb.append("\n"); + debugfile.write(sb.toString()); + } + + if (!demoplayback) { + // ideally nettics[0] should be 1 - 3 tics above lowtic + // if we are consistantly slower, speed up time + for (i = 0; i < MAXPLAYERS; i++) { + if (playeringame[i]) { + break; + } + } + if (consoleplayer == i) { + // the key player does not adapt + } else { + if (nettics[0] <= nettics[nodeforplayer[i]]) { + gametime--; + //System.out.print("-"); + } + frameskip[frameon & 3] = oldnettics > nettics[nodeforplayer[i]]; + oldnettics = nettics[0]; + if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3]) { + skiptics = 1; + //System.out.print("+"); + } + } + } // demoplayback + + // wait for new tics if needed + while (lowtic < gametic / ticdup + counts) { + NetUpdate(); + lowtic = MAXINT; + + // Finds the node with the lowest number of tics. + for (i = 0; i < doomcom.numnodes; i++) { + if (nodeingame[i] && nettics[i] < lowtic) { + lowtic = nettics[i]; + } + } + + if (lowtic < gametic / ticdup) { + doomSystem.Error("TryRunTics: lowtic < gametic"); + } + + // don't stay in here forever -- give the menu a chance to work + int time = ticker.GetTime(); + if (time / ticdup - entertic >= 20) { + menu.Ticker(); + return; + } + } + + // run the count * ticdup dics + while (counts-- > 0) { + for (i = 0; i < ticdup; i++) { + if (gametic / ticdup > lowtic) { + doomSystem.Error("gametic>lowtic"); + } + if (advancedemo) { + DoAdvanceDemo(); + } + menu.Ticker(); + Ticker(); + gametic++; + + // modify command for duplicated tics + if (i != ticdup - 1) { + ticcmd_t cmd; + int buf; + int j; + + buf = (gametic / ticdup) % BACKUPTICS; + for (j = 0; j < MAXPLAYERS; j++) { + cmd = netcmds[j][buf]; + cmd.chatchar = 0; + if (flags(cmd.buttons, BT_SPECIAL)) { + cmd.buttons = 0; + } + } + } + } + NetUpdate(); // check for new console commands + } + } + + @Override + public doomcom_t getDoomCom() { + return this.doomcom; + } + + @Override + public void setDoomCom(doomcom_t doomcom) { + this.doomcom = doomcom; + } + + @Override + public void setGameAction(gameaction_t action) { + this.gameaction = action; + } + + @Override + public gameaction_t getGameAction() { + return this.gameaction; + } + + public final VideoScale vs; + + public boolean shouldPollLockingKeys() { + if (keysCleared) { + keysCleared = false; + return true; + } + return false; + } + + private String findFileNameToSave() { + String format = "DOOM%d%d%d%d.png"; + String lbmname = null; + // find a file name to save it to + int[] digit = new int[4]; + int i; + for (i = 0; i <= 9999; i++) { + digit[0] = ((i / 1000) % 10); + digit[1] = ((i / 100) % 10); + digit[2] = ((i / 10) % 10); + digit[3] = (i % 10); + lbmname = String.format(format, digit[0], digit[1], digit[2], digit[3]); + if (!C2JUtils.testReadAccess(lbmname)) { + break; // file doesn't exist + } + } + if (i == 10000) { + doomSystem.Error("M_ScreenShot: Couldn't create a PNG"); + } + return lbmname; + } + + protected final Finale selectFinale() { + return new Finale<>(this); + } + + /** + * M_Screenshot + * + * Currently saves PCX screenshots, and only in devparm. + * Very oldschool ;-) + * + * TODO: add non-devparm hotkey for screenshots, sequential screenshot + * messages, option to save as either PCX or PNG. Also, request + * current palette from VI (otherwise gamma settings and palette effects + * don't show up). + * + */ + public void ScreenShot() { + // find a file name to save it to + final String lbmname = findFileNameToSave(); // file doesn't exist + + if (graphicSystem.writeScreenShot(lbmname, FG)) { + players[consoleplayer].message = SCREENSHOT; + } + } +} + +//$Log: DoomMain.java,v $ +//Revision 1.109 2012/11/06 16:04:58 velktron +//Variables manager less tightly integrated. +// +//Revision 1.108 2012/11/05 17:25:29 velktron +//Fixed tinting system according to SodaHolic's advice. +// +//Revision 1.107 2012/09/27 16:53:46 velktron +//Stupid brokeness prevented -loadgame from working. +// +//Revision 1.106 2012/09/26 23:15:20 velktron +//Parallel renderer restored...sort of. +// +//Revision 1.105 2012/09/26 15:54:22 velktron +//Spritemanager is set up by renderer. +// +//Revision 1.104 2012/09/24 22:36:49 velktron +//Fixed HOM detection. +// +//Revision 1.103 2012/09/24 17:16:22 velktron +//Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD. +// +//Revision 1.101.2.11 2012/09/24 16:58:06 velktron +//TrueColor, Generics. +// +//Revision 1.101.2.10 2012/09/21 16:17:25 velktron +//More generic. +// +//Revision 1.101.2.9 2012/09/20 14:25:13 velktron +//Unified DOOM!!! +// \ No newline at end of file diff --git a/src/doom/DoomStatus.java b/src/doom/DoomStatus.java index 1adc917..fe2863d 100644 --- a/src/doom/DoomStatus.java +++ b/src/doom/DoomStatus.java @@ -79,7 +79,7 @@ public abstract class DoomStatus { /////////// Local to doomstat.c //////////// // TODO: hide those behind getters - /** Game Mode - identify IWAD as shareware, retail etc. + /** Game Mode - identify IWAD as shareware, retail etc. * This is now hidden behind getters so some cases like plutonia * etc. can be handled more cleanly. * */ @@ -98,7 +98,7 @@ public boolean isShareware() { } /** Commercial means Doom 2, Plutonia, TNT, and possibly others like XBLA. - * + * * @return */ public boolean isCommercial() { @@ -111,15 +111,15 @@ public boolean isCommercial() { } /** Retail means Ultimate. - * + * * @return */ public boolean isRetail() { return (gamemode == GameMode.retail || gamemode == GameMode.freedoom1); } - /** Registered is a subset of Ultimate - * + /** Registered is a subset of Ultimate + * * @return */ public boolean isRegistered() { @@ -261,7 +261,7 @@ public boolean isRegistered() { public boolean mapstrobe; - /** + /** * Set this to GS_DEMOSCREEN upon init, else it will be null * Good Sign at 2017/03/21: I hope it is no longer true info, since I've checked its assignment by NetBeans */ @@ -345,7 +345,7 @@ public boolean isRegistered() { public ticcmd_t[][] netcmds;// [MAXPLAYERS][BACKUPTICS]; - /** MAES: this WAS NOT in the original. + /** MAES: this WAS NOT in the original. * Remember to call it! */ protected final void initNetGameStuff() { @@ -523,7 +523,7 @@ protected void joybuttons(int i, int value) { /** More prBoom+ stuff. Used mostly for code uhm..reuse, rather * than to actually change the way stuff works. - * + * */ public static int compatibility_level; @@ -685,4 +685,4 @@ public void commit() { // // Revision 1.18 2011/05/24 17:44:37 velktron // usemouse added for defaults -// +// \ No newline at end of file diff --git a/src/doom/IDatagramSerializable.java b/src/doom/IDatagramSerializable.java index 4137e55..17c1000 100644 --- a/src/doom/IDatagramSerializable.java +++ b/src/doom/IDatagramSerializable.java @@ -4,26 +4,26 @@ * specifically for the purpose of sending * Objects implementing this can return references to one same byte array, with minimal * overhead. Since it's for send-only purposes, it won't matter if it's modified. - * * - * But don't use it in lieu of CacheableDoomObject! - * + * * + * But don't use it in lieu of CacheableDoomObject! + * * @author admin * */ public interface IDatagramSerializable { - /** Packs object into a byte array suitable to send over + /** Packs object into a byte array suitable to send over * datagram networks. Typically, objects cache this array * for later use, and is availabe through cached() - * + * * @return */ public byte[] pack(); - /** Packs object into a byte array suitable to send over - * datagram networks. The array is supplied externally + /** Packs object into a byte array suitable to send over + * datagram networks. The array is supplied externally * (good for daisy-chaining stuff into a single packet). - * + * * @return */ public void pack(byte[] buf, int offset); @@ -31,22 +31,22 @@ public interface IDatagramSerializable { /** Deserializes an object from a given byte buffer. * Only the first (sizeof) bytes will be used, dependant * on each object's implementation. Will NOT also copy - * the byte[] caches. + * the byte[] caches. */ public void unpack(byte[] buf); /** Deserializes an object from a given byte buffer. * Only the first (sizeof) bytes will be used, starting - * from a specified offset, dependant on each object's + * from a specified offset, dependant on each object's * implementation. */ public void unpack(byte[] buf, int offset); /** Only use this if you are 100% sure that the object's content - * won't have changed since the last call of pack(). + * won't have changed since the last call of pack(). * * @return Should return the underlying byte[] array directly. */ public byte[] cached(); -} +} \ No newline at end of file diff --git a/src/doom/IDoom.java b/src/doom/IDoom.java index bada870..39bcaf6 100644 --- a/src/doom/IDoom.java +++ b/src/doom/IDoom.java @@ -3,7 +3,7 @@ import java.io.IOException; /** Stuff that the "main" is supposed to do. DoomMain implements those. - * + * * @author Maes * */ @@ -22,4 +22,4 @@ public interface IDoom { void QuitNetGame() throws IOException; -} +} \ No newline at end of file diff --git a/src/doom/IDoomGame.java b/src/doom/IDoomGame.java index 70295a2..69d38d0 100644 --- a/src/doom/IDoomGame.java +++ b/src/doom/IDoomGame.java @@ -2,8 +2,8 @@ import defines.skill_t; -/** Groups functions formerly in d_game, - * in case you want to provide a different implementation +/** Groups functions formerly in d_game, + * in case you want to provide a different implementation */ public interface IDoomGame { @@ -26,7 +26,7 @@ public interface IDoomGame { public void SaveGame(int slot, String description); /** Takes a screenshot *NOW* - * + * */ public void ScreenShot(); @@ -39,4 +39,4 @@ public interface IDoomGame { // public void PlayerReborn(int player); void DeathMatchSpawnPlayer(int playernum); -} +} \ No newline at end of file diff --git a/src/doom/IDoomGameNetworking.java b/src/doom/IDoomGameNetworking.java index b16bd28..3f043e2 100644 --- a/src/doom/IDoomGameNetworking.java +++ b/src/doom/IDoomGameNetworking.java @@ -5,11 +5,11 @@ /** Doom is actually tied to its networking module. * Therefore, no matter where and how you implement it, these functions * need to be callable from within many modules. - * + * * This is the so called "game networking" which is internal and game-specific, - * and not system networking which deals with the low level sockets and packet + * and not system networking which deals with the low level sockets and packet * stuff. You'll need DoomSystemNetworking for that one. - * + * * @author Velktron * */ @@ -21,7 +21,7 @@ public interface IDoomGameNetworking { * NetUpdate * Builds ticcmds for console player, * sends out a packet - * @throws IOException + * @throws IOException */ public void NetUpdate(); @@ -33,4 +33,4 @@ public interface IDoomGameNetworking { public void setTicdup(int ticdup); -} +} \ No newline at end of file diff --git a/src/doom/KeyboardManager.java b/src/doom/KeyboardManager.java index eaa00e7..43a7e60 100644 --- a/src/doom/KeyboardManager.java +++ b/src/doom/KeyboardManager.java @@ -1,25 +1,25 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package doom; - -/** - * - * @author Good Sign - */ -public class KeyboardManager { - -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package doom; + +/** + * + * @author Good Sign + */ +public class KeyboardManager { + +} \ No newline at end of file diff --git a/src/doom/NetConsts.java b/src/doom/NetConsts.java index 91045e6..f61da63 100644 --- a/src/doom/NetConsts.java +++ b/src/doom/NetConsts.java @@ -16,4 +16,4 @@ public interface NetConsts { public static short CMD_SEND = 1; public static short CMD_GET = 2; -} +} \ No newline at end of file diff --git a/src/doom/SourceCode.java b/src/doom/SourceCode.java index 379f555..b9399a1 100644 --- a/src/doom/SourceCode.java +++ b/src/doom/SourceCode.java @@ -1,676 +1,676 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package doom; - -import java.lang.annotation.Documented; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.LOCAL_VARIABLE; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import java.lang.annotation.Retention; -import static java.lang.annotation.RetentionPolicy.SOURCE; -import java.lang.annotation.Target; - -@Target({}) -@Retention(SOURCE) -public @interface SourceCode { - - public enum AM_Map { - AM_Responder, - AM_Ticker, - AM_Drawer, - AM_Stop; - - @Documented - @Retention(SOURCE) - public @interface C { - - AM_Map value(); - } - } - - public enum D_Main { - D_DoomLoop, - D_ProcessEvents; - - @Documented - @Retention(SOURCE) - public @interface C { - - D_Main value(); - } - } - - public enum F_Finale { - F_Responder, - F_Ticker, - F_Drawer, - F_StartFinale; - - @Documented - @Retention(SOURCE) - public @interface C { - - F_Finale value(); - } - } - - public enum G_Game { - G_BuildTiccmd, - G_DoCompleted, - G_DoReborn, - G_DoLoadLevel, - G_DoSaveGame, - G_DoPlayDemo, - G_PlayerFinishLevel, - G_DoNewGame, - G_PlayerReborn, - G_CheckSpot, - G_DeathMatchSpawnPlayer, - G_InitNew, - G_DeferedInitNew, - G_DeferedPlayDemo, - G_LoadGame, - G_DoLoadGame, - G_SaveGame, - G_RecordDemo, - G_BeginRecording, - G_PlayDemo, - G_TimeDemo, - G_CheckDemoStatus, - G_ExitLevel, - G_SecretExitLevel, - G_WorldDone, - G_Ticker, - G_Responder, - G_ScreenShot; - - @Documented - @Retention(SOURCE) - public @interface C { - - G_Game value(); - } - } - - public enum HU_Lib { - HUlib_init, - HUlib_clearTextLine, - HUlib_initTextLine, - HUlib_addCharToTextLine, - HUlib_delCharFromTextLine, - HUlib_drawTextLine, - HUlib_eraseTextLine, - HUlib_initSText, - HUlib_addLineToSText, - HUlib_addMessageToSText, - HUlib_drawSText, - HUlib_eraseSText, - HUlib_initIText, - HUlib_delCharFromIText, - HUlib_eraseLineFromIText, - HUlib_resetIText, - HUlib_addPrefixToIText, - HUlib_keyInIText, - HUlib_drawIText, - HUlib_eraseIText; - - @Documented - @Retention(SOURCE) - public @interface C { - - HU_Lib value(); - } - } - - public enum HU_Stuff { - HU_Init, - HU_Start, - HU_Responder, - HU_Ticker, - HU_Drawer, - HU_queueChatChar, - HU_dequeueChatChar, - HU_Erase; - - @Documented - @Retention(SOURCE) - public @interface C { - - HU_Stuff value(); - } - } - - public enum I_IBM { - I_GetTime, - I_WaitVBL, - I_SetPalette, - I_FinishUpdate, - I_StartTic, - I_InitNetwork, - I_NetCmd; - - @Documented - @Retention(SOURCE) - public @interface C { - - I_IBM value(); - } - } - - public enum M_Argv { - M_CheckParm; - - @Documented - @Retention(SOURCE) - public @interface C { - - M_Argv value(); - } - } - - public enum M_Menu { - M_Responder, - M_Ticker, - M_Drawer, - M_Init, - M_StartControlPanel; - - @Documented - @Retention(SOURCE) - public @interface C { - - M_Menu value(); - } - } - - public enum M_Random { - M_Random, - P_Random, - M_ClearRandom; - - @Documented - @Retention(SOURCE) - public @interface C { - - M_Random value(); - } - } - - public enum P_Doors { - T_VerticalDoor, - EV_VerticalDoor, - EV_DoDoor, - EV_DoLockedDoor, - P_SpawnDoorCloseIn30, - P_SpawnDoorRaiseIn5Mins, - P_InitSlidingDoorFrames, - P_FindSlidingDoorType, - T_SlidingDoor, - EV_SlidingDoor; - - @Documented - @Retention(SOURCE) - public @interface C { - - P_Doors value(); - } - } - - public enum P_Map { - P_CheckPosition, - PIT_CheckThing, - PIT_CheckLine, - PIT_RadiusAttack, - PIT_ChangeSector, - PIT_StompThing, - PTR_SlideTraverse, - PTR_AimTraverse, - PTR_ShootTraverse, - PTR_UseTraverse; - - @Documented - @Retention(SOURCE) - public @interface C { - - P_Map value(); - } - } - - public enum P_MapUtl { - P_BlockThingsIterator, - P_BlockLinesIterator, - P_PathTraverse, - P_UnsetThingPosition, - P_SetThingPosition, - PIT_AddLineIntercepts, - PIT_AddThingIntercepts; - - @Documented - @Retention(SOURCE) - public @interface C { - - P_MapUtl value(); - } - } - - public enum P_Mobj { - G_PlayerReborn, - P_SpawnMapThing, - P_SetMobjState, - P_ExplodeMissile, - P_XYMovement, - P_ZMovement, - P_NightmareRespawn, - P_MobjThinker, - P_SpawnMobj, - P_RemoveMobj, - P_RespawnSpecials, - P_SpawnPlayer, - P_SpawnPuff, - P_SpawnBlood, - P_CheckMissileSpawn, - P_SpawnMissile, - P_SpawnPlayerMissile; - - @Documented - @Retention(SOURCE) - public @interface C { - - P_Mobj value(); - } - } - - public enum P_Enemy { - PIT_VileCheck; - - @Documented - @Retention(SOURCE) - public @interface C { - - P_Enemy value(); - } - } - - public enum P_Lights { - T_FireFlicker, - P_SpawnFireFlicker, - T_LightFlash, - P_SpawnLightFlash, - T_StrobeFlash, - P_SpawnStrobeFlash, - EV_StartLightStrobing, - EV_TurnTagLightsOff, - EV_LightTurnOn, - T_Glow, - P_SpawnGlowingLight; - - @Documented - @Retention(SOURCE) - public @interface C { - - P_Lights value(); - } - } - - public enum P_SaveG { - P_ArchivePlayers, - P_UnArchivePlayers, - P_ArchiveWorld, - P_UnArchiveWorld, - P_ArchiveThinkers, - P_UnArchiveThinkers, - P_ArchiveSpecials, - P_UnArchiveSpecials; - - @Documented - @Retention(SOURCE) - public @interface C { - - P_SaveG value(); - } - } - - public enum P_Setup { - P_SetupLevel, - P_LoadThings; - - @Documented - @Retention(SOURCE) - public @interface C { - - P_Setup value(); - } - } - - public enum P_Spec { - P_InitPicAnims, - P_SpawnSpecials, - P_UpdateSpecials, - P_UseSpecialLine, - P_ShootSpecialLine, - P_CrossSpecialLine, - P_PlayerInSpecialSector, - twoSided, - getSector, - getSide, - P_FindLowestFloorSurrounding, - P_FindHighestFloorSurrounding, - P_FindNextHighestFloor, - P_FindLowestCeilingSurrounding, - P_FindHighestCeilingSurrounding, - P_FindSectorFromLineTag, - P_FindMinSurroundingLight, - getNextSector, - EV_DoDonut, - P_ChangeSwitchTexture, - P_InitSwitchList, - T_PlatRaise, - EV_DoPlat, - P_AddActivePlat, - P_RemoveActivePlat, - EV_StopPlat, - P_ActivateInStasis, - EV_DoCeiling, - T_MoveCeiling, - P_AddActiveCeiling, - P_RemoveActiveCeiling, - EV_CeilingCrushStop, - P_ActivateInStasisCeiling, - T_MovePlane, - EV_BuildStairs, - EV_DoFloor, - T_MoveFloor, - EV_Teleport; - - @Documented - @Retention(SOURCE) - public @interface C { - - P_Spec value(); - } - } - - public enum P_Ceiling { - EV_DoCeiling; - - @Documented - @Retention(SOURCE) - public @interface C { - - P_Ceiling value(); - } - } - - public enum P_Tick { - P_InitThinkers, - P_RemoveThinker, - P_AddThinker, - P_AllocateThinker, - P_RunThinkers, - P_Ticker; - - @Documented - @Retention(SOURCE) - public @interface C { - - P_Tick value(); - } - } - - public enum P_Pspr { - P_SetPsprite, - P_CalcSwing, - P_BringUpWeapon, - P_CheckAmmo, - P_FireWeapon, - P_DropWeapon, - A_WeaponReady, - A_ReFire, - A_CheckReload, - A_Lower, - A_Raise, - A_GunFlash, - A_Punch, - A_Saw, - A_FireMissile, - A_FireBFG, - A_FirePlasma, - P_BulletSlope, - P_GunShot, - A_FirePistol, - A_FireShotgun, - A_FireShotgun2, - A_FireCGun, - A_Light0, - A_Light1, - A_Light2, - A_BFGSpray, - A_BFGsound, - P_SetupPsprites, - P_MovePsprites; - - @Documented - @Retention(SOURCE) - public @interface C { - - P_Pspr value(); - } - } - - public enum R_Data { - R_GetColumn, - R_InitData, - R_PrecacheLevel, - R_FlatNumForName, - R_TextureNumForName, - R_CheckTextureNumForName; - - @Documented - @Retention(SOURCE) - public @interface C { - - R_Data value(); - } - } - - public enum R_Draw { - R_FillBackScreen; - - @Documented - @Retention(SOURCE) - public @interface C { - - R_Draw value(); - } - } - - public enum R_Main { - R_PointOnSide, - R_PointOnSegSide, - R_PointToAngle, - R_PointToAngle2, - R_PointToDist, - R_ScaleFromGlobalAngle, - R_PointInSubsector, - R_AddPointToBox, - R_RenderPlayerView, - R_Init, - R_SetViewSize; - - @Documented - @Retention(SOURCE) - public @interface C { - - R_Main value(); - } - } - - public enum ST_Stuff { - ST_Responder, - ST_Ticker, - ST_Drawer, - ST_Start, - ST_Init; - - @Documented - @Retention(SOURCE) - public @interface C { - - ST_Stuff value(); - } - } - - public enum W_Wad { - W_InitMultipleFiles, - W_Reload, - W_CheckNumForName, - W_GetNumForName, - W_LumpLength, - W_ReadLump, - W_CacheLumpNum, - W_CacheLumpName; - - @Documented - @Retention(SOURCE) - public @interface C { - - W_Wad value(); - } - } - - public enum WI_Stuff { - WI_initVariables, - WI_loadData, - WI_initDeathmatchStats, - WI_initAnimatedBack, - WI_initNetgameStats, - WI_initStats, - WI_Ticker, - WI_Drawer, - WI_Start; - - @Documented - @Retention(SOURCE) - public @interface C { - - WI_Stuff value(); - } - } - - public interface D_Think { - - public enum actionf_t { - acp1, - acv, - acp2 - } - - @Documented - @Retention(SOURCE) - public @interface C { - - actionf_t value(); - } - } - - public enum Z_Zone { - Z_Malloc; - - @Documented - @Retention(SOURCE) - public @interface C { - - Z_Zone value(); - } - } - - @Documented - @Retention(SOURCE) - public @interface Exact { - - String description() default "Indicates that the method behaves exactly in vanilla way\n" - + " and can be skipped when traversing for compatibility"; - } - - @Documented - @Retention(SOURCE) - public @interface Compatible { - - String[] value() default ""; - - String description() default "Indicates that the method can behave differently from vanilla way,\n" - + " but this behavior is reviewed and can be turned back to vanilla as an option." - + "A value might be specivied with the equivalent vanilla code"; - } - - public enum CauseOfDesyncProbability { - LOW, - MEDIUM, - HIGH - } - - @Documented - @Retention(SOURCE) - public @interface Suspicious { - - CauseOfDesyncProbability value() default CauseOfDesyncProbability.HIGH; - - String description() default "Indicates that the method contains behavior totally different\n" - + "from vanilla, and by so should be considered suspicious\n" - + "in terms of compatibility"; - } - - @Documented - @Retention(SOURCE) - @Target({METHOD, FIELD, LOCAL_VARIABLE, PARAMETER}) - public @interface angle_t { - } - - @Documented - @Retention(SOURCE) - @Target({METHOD, FIELD, LOCAL_VARIABLE, PARAMETER}) - public @interface fixed_t { - } - - @Documented - @Retention(SOURCE) - public @interface actionf_p1 { - } - - @Documented - @Retention(SOURCE) - public @interface actionf_v { - } - - @Documented - @Retention(SOURCE) - public @interface actionf_p2 { - } - - @Documented - @Retention(SOURCE) - @Target({FIELD, LOCAL_VARIABLE, PARAMETER}) - public @interface thinker_t { - } - - @Documented - @Retention(SOURCE) - @Target({FIELD, LOCAL_VARIABLE, PARAMETER}) - public @interface think_t { - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package doom; + +import java.lang.annotation.Documented; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import java.lang.annotation.Retention; +import static java.lang.annotation.RetentionPolicy.SOURCE; +import java.lang.annotation.Target; + +@Target({}) +@Retention(SOURCE) +public @interface SourceCode { + + public enum AM_Map { + AM_Responder, + AM_Ticker, + AM_Drawer, + AM_Stop; + + @Documented + @Retention(SOURCE) + public @interface C { + + AM_Map value(); + } + } + + public enum D_Main { + D_DoomLoop, + D_ProcessEvents; + + @Documented + @Retention(SOURCE) + public @interface C { + + D_Main value(); + } + } + + public enum F_Finale { + F_Responder, + F_Ticker, + F_Drawer, + F_StartFinale; + + @Documented + @Retention(SOURCE) + public @interface C { + + F_Finale value(); + } + } + + public enum G_Game { + G_BuildTiccmd, + G_DoCompleted, + G_DoReborn, + G_DoLoadLevel, + G_DoSaveGame, + G_DoPlayDemo, + G_PlayerFinishLevel, + G_DoNewGame, + G_PlayerReborn, + G_CheckSpot, + G_DeathMatchSpawnPlayer, + G_InitNew, + G_DeferedInitNew, + G_DeferedPlayDemo, + G_LoadGame, + G_DoLoadGame, + G_SaveGame, + G_RecordDemo, + G_BeginRecording, + G_PlayDemo, + G_TimeDemo, + G_CheckDemoStatus, + G_ExitLevel, + G_SecretExitLevel, + G_WorldDone, + G_Ticker, + G_Responder, + G_ScreenShot; + + @Documented + @Retention(SOURCE) + public @interface C { + + G_Game value(); + } + } + + public enum HU_Lib { + HUlib_init, + HUlib_clearTextLine, + HUlib_initTextLine, + HUlib_addCharToTextLine, + HUlib_delCharFromTextLine, + HUlib_drawTextLine, + HUlib_eraseTextLine, + HUlib_initSText, + HUlib_addLineToSText, + HUlib_addMessageToSText, + HUlib_drawSText, + HUlib_eraseSText, + HUlib_initIText, + HUlib_delCharFromIText, + HUlib_eraseLineFromIText, + HUlib_resetIText, + HUlib_addPrefixToIText, + HUlib_keyInIText, + HUlib_drawIText, + HUlib_eraseIText; + + @Documented + @Retention(SOURCE) + public @interface C { + + HU_Lib value(); + } + } + + public enum HU_Stuff { + HU_Init, + HU_Start, + HU_Responder, + HU_Ticker, + HU_Drawer, + HU_queueChatChar, + HU_dequeueChatChar, + HU_Erase; + + @Documented + @Retention(SOURCE) + public @interface C { + + HU_Stuff value(); + } + } + + public enum I_IBM { + I_GetTime, + I_WaitVBL, + I_SetPalette, + I_FinishUpdate, + I_StartTic, + I_InitNetwork, + I_NetCmd; + + @Documented + @Retention(SOURCE) + public @interface C { + + I_IBM value(); + } + } + + public enum M_Argv { + M_CheckParm; + + @Documented + @Retention(SOURCE) + public @interface C { + + M_Argv value(); + } + } + + public enum M_Menu { + M_Responder, + M_Ticker, + M_Drawer, + M_Init, + M_StartControlPanel; + + @Documented + @Retention(SOURCE) + public @interface C { + + M_Menu value(); + } + } + + public enum M_Random { + M_Random, + P_Random, + M_ClearRandom; + + @Documented + @Retention(SOURCE) + public @interface C { + + M_Random value(); + } + } + + public enum P_Doors { + T_VerticalDoor, + EV_VerticalDoor, + EV_DoDoor, + EV_DoLockedDoor, + P_SpawnDoorCloseIn30, + P_SpawnDoorRaiseIn5Mins, + P_InitSlidingDoorFrames, + P_FindSlidingDoorType, + T_SlidingDoor, + EV_SlidingDoor; + + @Documented + @Retention(SOURCE) + public @interface C { + + P_Doors value(); + } + } + + public enum P_Map { + P_CheckPosition, + PIT_CheckThing, + PIT_CheckLine, + PIT_RadiusAttack, + PIT_ChangeSector, + PIT_StompThing, + PTR_SlideTraverse, + PTR_AimTraverse, + PTR_ShootTraverse, + PTR_UseTraverse; + + @Documented + @Retention(SOURCE) + public @interface C { + + P_Map value(); + } + } + + public enum P_MapUtl { + P_BlockThingsIterator, + P_BlockLinesIterator, + P_PathTraverse, + P_UnsetThingPosition, + P_SetThingPosition, + PIT_AddLineIntercepts, + PIT_AddThingIntercepts; + + @Documented + @Retention(SOURCE) + public @interface C { + + P_MapUtl value(); + } + } + + public enum P_Mobj { + G_PlayerReborn, + P_SpawnMapThing, + P_SetMobjState, + P_ExplodeMissile, + P_XYMovement, + P_ZMovement, + P_NightmareRespawn, + P_MobjThinker, + P_SpawnMobj, + P_RemoveMobj, + P_RespawnSpecials, + P_SpawnPlayer, + P_SpawnPuff, + P_SpawnBlood, + P_CheckMissileSpawn, + P_SpawnMissile, + P_SpawnPlayerMissile; + + @Documented + @Retention(SOURCE) + public @interface C { + + P_Mobj value(); + } + } + + public enum P_Enemy { + PIT_VileCheck; + + @Documented + @Retention(SOURCE) + public @interface C { + + P_Enemy value(); + } + } + + public enum P_Lights { + T_FireFlicker, + P_SpawnFireFlicker, + T_LightFlash, + P_SpawnLightFlash, + T_StrobeFlash, + P_SpawnStrobeFlash, + EV_StartLightStrobing, + EV_TurnTagLightsOff, + EV_LightTurnOn, + T_Glow, + P_SpawnGlowingLight; + + @Documented + @Retention(SOURCE) + public @interface C { + + P_Lights value(); + } + } + + public enum P_SaveG { + P_ArchivePlayers, + P_UnArchivePlayers, + P_ArchiveWorld, + P_UnArchiveWorld, + P_ArchiveThinkers, + P_UnArchiveThinkers, + P_ArchiveSpecials, + P_UnArchiveSpecials; + + @Documented + @Retention(SOURCE) + public @interface C { + + P_SaveG value(); + } + } + + public enum P_Setup { + P_SetupLevel, + P_LoadThings; + + @Documented + @Retention(SOURCE) + public @interface C { + + P_Setup value(); + } + } + + public enum P_Spec { + P_InitPicAnims, + P_SpawnSpecials, + P_UpdateSpecials, + P_UseSpecialLine, + P_ShootSpecialLine, + P_CrossSpecialLine, + P_PlayerInSpecialSector, + twoSided, + getSector, + getSide, + P_FindLowestFloorSurrounding, + P_FindHighestFloorSurrounding, + P_FindNextHighestFloor, + P_FindLowestCeilingSurrounding, + P_FindHighestCeilingSurrounding, + P_FindSectorFromLineTag, + P_FindMinSurroundingLight, + getNextSector, + EV_DoDonut, + P_ChangeSwitchTexture, + P_InitSwitchList, + T_PlatRaise, + EV_DoPlat, + P_AddActivePlat, + P_RemoveActivePlat, + EV_StopPlat, + P_ActivateInStasis, + EV_DoCeiling, + T_MoveCeiling, + P_AddActiveCeiling, + P_RemoveActiveCeiling, + EV_CeilingCrushStop, + P_ActivateInStasisCeiling, + T_MovePlane, + EV_BuildStairs, + EV_DoFloor, + T_MoveFloor, + EV_Teleport; + + @Documented + @Retention(SOURCE) + public @interface C { + + P_Spec value(); + } + } + + public enum P_Ceiling { + EV_DoCeiling; + + @Documented + @Retention(SOURCE) + public @interface C { + + P_Ceiling value(); + } + } + + public enum P_Tick { + P_InitThinkers, + P_RemoveThinker, + P_AddThinker, + P_AllocateThinker, + P_RunThinkers, + P_Ticker; + + @Documented + @Retention(SOURCE) + public @interface C { + + P_Tick value(); + } + } + + public enum P_Pspr { + P_SetPsprite, + P_CalcSwing, + P_BringUpWeapon, + P_CheckAmmo, + P_FireWeapon, + P_DropWeapon, + A_WeaponReady, + A_ReFire, + A_CheckReload, + A_Lower, + A_Raise, + A_GunFlash, + A_Punch, + A_Saw, + A_FireMissile, + A_FireBFG, + A_FirePlasma, + P_BulletSlope, + P_GunShot, + A_FirePistol, + A_FireShotgun, + A_FireShotgun2, + A_FireCGun, + A_Light0, + A_Light1, + A_Light2, + A_BFGSpray, + A_BFGsound, + P_SetupPsprites, + P_MovePsprites; + + @Documented + @Retention(SOURCE) + public @interface C { + + P_Pspr value(); + } + } + + public enum R_Data { + R_GetColumn, + R_InitData, + R_PrecacheLevel, + R_FlatNumForName, + R_TextureNumForName, + R_CheckTextureNumForName; + + @Documented + @Retention(SOURCE) + public @interface C { + + R_Data value(); + } + } + + public enum R_Draw { + R_FillBackScreen; + + @Documented + @Retention(SOURCE) + public @interface C { + + R_Draw value(); + } + } + + public enum R_Main { + R_PointOnSide, + R_PointOnSegSide, + R_PointToAngle, + R_PointToAngle2, + R_PointToDist, + R_ScaleFromGlobalAngle, + R_PointInSubsector, + R_AddPointToBox, + R_RenderPlayerView, + R_Init, + R_SetViewSize; + + @Documented + @Retention(SOURCE) + public @interface C { + + R_Main value(); + } + } + + public enum ST_Stuff { + ST_Responder, + ST_Ticker, + ST_Drawer, + ST_Start, + ST_Init; + + @Documented + @Retention(SOURCE) + public @interface C { + + ST_Stuff value(); + } + } + + public enum W_Wad { + W_InitMultipleFiles, + W_Reload, + W_CheckNumForName, + W_GetNumForName, + W_LumpLength, + W_ReadLump, + W_CacheLumpNum, + W_CacheLumpName; + + @Documented + @Retention(SOURCE) + public @interface C { + + W_Wad value(); + } + } + + public enum WI_Stuff { + WI_initVariables, + WI_loadData, + WI_initDeathmatchStats, + WI_initAnimatedBack, + WI_initNetgameStats, + WI_initStats, + WI_Ticker, + WI_Drawer, + WI_Start; + + @Documented + @Retention(SOURCE) + public @interface C { + + WI_Stuff value(); + } + } + + public interface D_Think { + + public enum actionf_t { + acp1, + acv, + acp2 + } + + @Documented + @Retention(SOURCE) + public @interface C { + + actionf_t value(); + } + } + + public enum Z_Zone { + Z_Malloc; + + @Documented + @Retention(SOURCE) + public @interface C { + + Z_Zone value(); + } + } + + @Documented + @Retention(SOURCE) + public @interface Exact { + + String description() default "Indicates that the method behaves exactly in vanilla way\n" + + " and can be skipped when traversing for compatibility"; + } + + @Documented + @Retention(SOURCE) + public @interface Compatible { + + String[] value() default ""; + + String description() default "Indicates that the method can behave differently from vanilla way,\n" + + " but this behavior is reviewed and can be turned back to vanilla as an option." + + "A value might be specivied with the equivalent vanilla code"; + } + + public enum CauseOfDesyncProbability { + LOW, + MEDIUM, + HIGH + } + + @Documented + @Retention(SOURCE) + public @interface Suspicious { + + CauseOfDesyncProbability value() default CauseOfDesyncProbability.HIGH; + + String description() default "Indicates that the method contains behavior totally different\n" + + "from vanilla, and by so should be considered suspicious\n" + + "in terms of compatibility"; + } + + @Documented + @Retention(SOURCE) + @Target({METHOD, FIELD, LOCAL_VARIABLE, PARAMETER}) + public @interface angle_t { + } + + @Documented + @Retention(SOURCE) + @Target({METHOD, FIELD, LOCAL_VARIABLE, PARAMETER}) + public @interface fixed_t { + } + + @Documented + @Retention(SOURCE) + public @interface actionf_p1 { + } + + @Documented + @Retention(SOURCE) + public @interface actionf_v { + } + + @Documented + @Retention(SOURCE) + public @interface actionf_p2 { + } + + @Documented + @Retention(SOURCE) + @Target({FIELD, LOCAL_VARIABLE, PARAMETER}) + public @interface thinker_t { + } + + @Documented + @Retention(SOURCE) + @Target({FIELD, LOCAL_VARIABLE, PARAMETER}) + public @interface think_t { + } +} \ No newline at end of file diff --git a/src/doom/doomcom_t.java b/src/doom/doomcom_t.java index 8a3449f..20343c3 100644 --- a/src/doom/doomcom_t.java +++ b/src/doom/doomcom_t.java @@ -54,4 +54,4 @@ public doomcom_t() { // The packet data to be sent. public doomdata_t data; -} +} \ No newline at end of file diff --git a/src/doom/doomdata_t.java b/src/doom/doomdata_t.java index aef0faa..9611b67 100644 --- a/src/doom/doomdata_t.java +++ b/src/doom/doomdata_t.java @@ -47,7 +47,7 @@ public byte[] pack() { bbuf.put(player); bbuf.put(numtics); - // FIXME: it's probably more efficient to use System.arraycopy ? + // FIXME: it's probably more efficient to use System.arraycopy ? // Or are the packets too small anyway? At most we'll be sending "doomdata_t's" for (int i = 0; i < cmds.length; i++) { bbuf.put(cmds[i].pack()); @@ -124,4 +124,4 @@ public String toString() { return sb.toString(); } -} +} \ No newline at end of file diff --git a/src/doom/englsh.java b/src/doom/englsh.java index a2d4efa..93a5b4f 100644 --- a/src/doom/englsh.java +++ b/src/doom/englsh.java @@ -495,7 +495,7 @@ public class englsh { + "SUPER SECRET LEVEL! YOU'D BETTER\n" + "BLAZE THROUGH THIS ONE!\n"); -// after map 06 +// after map 06 public final static String P1TEXT = ("You gloat over the steaming carcass of the\n" + "Guardian. With its death, you've wrested\n" + "the Accelerator from the stinking claws\n" @@ -639,4 +639,4 @@ public class englsh { public final static String CC_BARREL = "EXPLODING BARREL"; public final static String CC_HERO = "OUR HERO"; -} +} \ No newline at end of file diff --git a/src/doom/event_t.java b/src/doom/event_t.java index 37571c2..526735f 100644 --- a/src/doom/event_t.java +++ b/src/doom/event_t.java @@ -419,19 +419,19 @@ public void moveIn(MouseEvent ev, int centreX, int centreY, boolean drag) { /** * Now also fix for -fasttic mode * - Good Sign 2017/05/07 - * + * * Fix bug with processing mouse: the DOOM underlying engine does not * react on the event as fast as it came, they are processed in constant time instead. - * + * * In Mocha Doom, mouse events are not generated in bulks and sent to underlying DOOM engine, * instead the one only mouse event reused and resend modified if was consumed. - * + * * So, if we have event system reacting faster then DOOM underlying engine, * mouse will be harder to move because the new move is forgotten earlier then processed. - * + * * As a workaround, do not replace value in moveIn, and increment it instead, * and only when the underlying engine gives signal it has processed event, we clear x and y - * + * * - Good Sign 2017/05/06 */ if (processed) { @@ -559,4 +559,4 @@ public T mapByJoy(Function mouseMapper) { return mouseMapper.apply(this); } } -}; +}; \ No newline at end of file diff --git a/src/doom/evtype_t.java b/src/doom/evtype_t.java index f316c84..8227628 100644 --- a/src/doom/evtype_t.java +++ b/src/doom/evtype_t.java @@ -9,4 +9,4 @@ public enum evtype_t { ev_joystick, ev_mousewheel, // extension ev_clear // Forcibly clear all button input (e.g. when losing focus) -}; +}; \ No newline at end of file diff --git a/src/doom/gameaction_t.java b/src/doom/gameaction_t.java index d7416e5..8590911 100644 --- a/src/doom/gameaction_t.java +++ b/src/doom/gameaction_t.java @@ -12,4 +12,4 @@ public enum gameaction_t { ga_worlddone, ga_screenshot, ga_failure // HACK: communicate failures silently -} +} \ No newline at end of file diff --git a/src/doom/items.java b/src/doom/items.java index c90fa23..7843e9a 100644 --- a/src/doom/items.java +++ b/src/doom/items.java @@ -129,4 +129,4 @@ public class items { statenum_t.S_DSGUNFLASH1 ) }; -} +} \ No newline at end of file diff --git a/src/doom/net.java b/src/doom/net.java index 0cf8590..994546a 100644 --- a/src/doom/net.java +++ b/src/doom/net.java @@ -81,7 +81,7 @@ public class net { // // gametic is the tic about to (or currently being) run // maketic is the tick that hasn't had control made for it yet -// nettics[] has the maketics for all players +// nettics[] has the maketics for all players // // a gametic cannot be run until nettics[] > gametic for all players // @@ -107,17 +107,17 @@ public class net { int ticdup; int maxsend; // BACKUPTICS/(2*ticdup)-1 -//void D_ProcessEvents (void); -//void G_BuildTiccmd (ticcmd_t *cmd); +//void D_ProcessEvents (void); +//void G_BuildTiccmd (ticcmd_t *cmd); //void D_DoAdvanceDemo (void); boolean reboundpacket; doomdata_t reboundstore; -// +// // //123 /** MAES: interesting. After testing it was found to return the following size: - * + * */ int NetbufferSize() { // return (int)(((doomdata_t)0).cmds[netbuffer.numtics]); @@ -128,7 +128,7 @@ int NetbufferSize() { /* //1 -// Checksum +// Checksum // unsigned NetbufferChecksum (void) { @@ -155,16 +155,16 @@ unsigned NetbufferChecksum (void) int ExpandTics (int low) { int delta; - + delta = low - (maketic&0xff); - + if (delta >= -64 && delta <= 64) return (maketic&~0xff) + low; if (delta > 64) return (maketic&~0xff) - 256 + low; if (delta < -64) return (maketic&~0xff) + 256 + low; - + I_Error ("ExpandTics: strange value %i at maketic %i",low,maketic); return 0; } @@ -193,11 +193,11 @@ int ExpandTics (int low) if (!netgame) I_Error ("Tried to transmit to another node"); - + doomcom->command = CMD_SEND; doomcom->remotenode = node; doomcom->datalength = NetbufferSize (); - + if (debugfile) { int i; @@ -210,7 +210,7 @@ int ExpandTics (int low) fprintf (debugfile,"send (%i + %i, R %i) [%i] ", ExpandTics(netbuffer->starttic), netbuffer->numtics, realretrans, doomcom->datalength); - + for (i=0 ; idatalength ; i++) fprintf (debugfile,"%i ",((byte *)netbuffer)[i]); @@ -225,7 +225,7 @@ int ExpandTics (int low) // Returns false if no packet is waiting // boolean HGetPacket (void) -{ +{ if (reboundpacket) { *netbuffer = reboundstore; @@ -239,10 +239,10 @@ boolean HGetPacket (void) if (demoplayback) return false; - + doomcom->command = CMD_GET; I_NetCmd (); - + if (doomcom->remotenode == -1) return false; @@ -252,7 +252,7 @@ boolean HGetPacket (void) fprintf (debugfile,"bad packet length %i\n",doomcom->datalength); return false; } - + if (NetbufferChecksum () != (netbuffer->checksum&NCMD_CHECKSUM) ) { if (debugfile) @@ -264,7 +264,7 @@ boolean HGetPacket (void) { int realretrans; int i; - + if (netbuffer->checksum & NCMD_SETUP) fprintf (debugfile,"setup packet\n"); else @@ -273,7 +273,7 @@ boolean HGetPacket (void) realretrans = ExpandTics (netbuffer->retransmitfrom); else realretrans = -1; - + fprintf (debugfile,"get %i = (%i + %i, R %i)[%i] ", doomcom->remotenode, ExpandTics(netbuffer->starttic), @@ -284,7 +284,7 @@ boolean HGetPacket (void) fprintf (debugfile,"\n"); } } - return true; + return true; } @@ -300,20 +300,20 @@ void GetPackets (void) ticcmd_t *src, *dest; int realend; int realstart; - + while ( HGetPacket() ) { if (netbuffer->checksum & NCMD_SETUP) continue; // extra setup packet - + netconsole = netbuffer->player & ~PL_DRONE; netnode = doomcom->remotenode; - + // to save bytes, only the low byte of tic numbers are sent // Figure out what the rest of the bytes are - realstart = ExpandTics (netbuffer->starttic); + realstart = ExpandTics (netbuffer->starttic); realend = (realstart+netbuffer->numtics); - + // check for exiting the game if (netbuffer->checksum & NCMD_EXIT) { @@ -328,15 +328,15 @@ void GetPackets (void) G_CheckDemoStatus (); continue; } - + // check for a remote game kill if (netbuffer->checksum & NCMD_KILL) I_Error ("Killed by network driver"); nodeforplayer[netconsole] = netnode; - + // check for retransmit request - if ( resendcount[netnode] <= 0 + if ( resendcount[netnode] <= 0 && (netbuffer->checksum & NCMD_RETRANSMIT) ) { resendto[netnode] = ExpandTics(netbuffer->retransmitfrom); @@ -346,11 +346,11 @@ void GetPackets (void) } else resendcount[netnode]--; - - // check for out of order / duplicated packet + + // check for out of order / duplicated packet if (realend == nettics[netnode]) continue; - + if (realend < nettics[netnode]) { if (debugfile) @@ -359,7 +359,7 @@ void GetPackets (void) realstart,netbuffer->numtics); continue; } - + // check for a missed packet if (realstart > nettics[netnode]) { @@ -377,8 +377,8 @@ void GetPackets (void) int start; remoteresend[netnode] = false; - - start = nettics[netnode] - realstart; + + start = nettics[netnode] - realstart; src = &netbuffer->cmds[start]; while (nettics[netnode] < realend) @@ -407,14 +407,14 @@ void NetUpdate (void) int i,j; int realstart; int gameticdiv; - + // check time nowtime = I_GetTime ()/ticdup; newtics = nowtime - gametime; gametime = nowtime; - + if (newtics <= 0) // nothing new to update - goto listen; + goto listen; if (skiptics <= newtics) { @@ -426,10 +426,10 @@ void NetUpdate (void) skiptics -= newtics; newtics = 0; } - - + + netbuffer->player = consoleplayer; - + // build new ticcmds for console player gameticdiv = gametic/ticdup; for (i=0 ; i= BACKUPTICS/2-1) break; // can't hold any more - + //printf ("mk:%i ",maketic); G_BuildTiccmd (&localcmds[maketic%BACKUPTICS]); maketic++; @@ -447,7 +447,7 @@ void NetUpdate (void) if (singletics) return; // singletic update is syncronous - + // send the packet to the other nodes for (i=0 ; inumnodes ; i++) if (nodeingame[i]) @@ -460,9 +460,9 @@ void NetUpdate (void) resendto[i] = maketic - doomcom->extratics; for (j=0 ; j< netbuffer->numtics ; j++) - netbuffer->cmds[j] = + netbuffer->cmds[j] = localcmds[(realstart+j)%BACKUPTICS]; - + if (remoteresend[i]) { netbuffer->retransmitfrom = nettics[i]; @@ -474,7 +474,7 @@ void NetUpdate (void) HSendPacket (i, 0); } } - + // listen for other packets listen: GetPackets (); @@ -489,19 +489,19 @@ void CheckAbort (void) { event_t *ev; int stoptic; - - stoptic = I_GetTime () + 2; - while (I_GetTime() < stoptic) - I_StartTic (); - + + stoptic = I_GetTime () + 2; + while (I_GetTime() < stoptic) + I_StartTic (); + I_StartTic (); - for ( ; eventtail != eventhead - ; eventtail = (++eventtail)&(MAXEVENTS-1) ) - { - ev = &events[eventtail]; + for ( ; eventtail != eventhead + ; eventtail = (++eventtail)&(MAXEVENTS-1) ) + { + ev = &events[eventtail]; if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE) I_Error ("Network game synchronization aborted."); - } + } } @@ -512,10 +512,10 @@ void D_ArbitrateNetStart (void) { int i; boolean gotinfo[MAXNETNODES]; - + autostart = true; memset (gotinfo,0,sizeof(gotinfo)); - + if (doomcom->consoleplayer) { // listen for setup info from key player @@ -590,7 +590,7 @@ void D_ArbitrateNetStart (void) void D_CheckNetGame (void) { int i; - + for (i=0 ; iid != DOOMCOM_ID) I_Error ("Doomcom buffer invalid!"); - + netbuffer = &doomcom->data; consoleplayer = displayplayer = doomcom->consoleplayer; if (netgame) @@ -611,18 +611,18 @@ void D_CheckNetGame (void) printf ("startskill %i deathmatch: %i startmap: %i startepisode: %i\n", startskill, deathmatch, startmap, startepisode); - + // read values out of doomcom ticdup = doomcom->ticdup; maxsend = BACKUPTICS/(2*ticdup)-1; if (maxsend<1) maxsend = 1; - + for (i=0 ; inumplayers ; i++) playeringame[i] = true; for (i=0 ; inumnodes ; i++) nodeingame[i] = true; - + printf ("player %i of %i (%i nodes)\n", consoleplayer+1, doomcom->numplayers, doomcom->numnodes); @@ -637,13 +637,13 @@ void D_CheckNetGame (void) void D_QuitNetGame (void) { int i, j; - + if (debugfile) fclose (debugfile); - + if (!netgame || !usergame || consoleplayer == -1 || demoplayback) return; - + // send a bunch of packets for security netbuffer->player = consoleplayer; netbuffer->numtics = 0; @@ -678,15 +678,15 @@ public static void TryRunTics () int availabletics; int counts; int numplaying; - - // get real tics + + // get real tics entertic = I_GetTime ()/ticdup; realtics = entertic - oldentertics; oldentertics = entertic; - + // get available tics NetUpdate (); - + lowtic = MAXINT; numplaying = 0; for (i=0 ; inumnodes ; i++) @@ -699,7 +699,7 @@ public static void TryRunTics () } } availabletics = lowtic - gametic/ticdup; - + // decide how many tics to run if (realtics < availabletics-1) counts = realtics+1; @@ -707,10 +707,10 @@ else if (realtics < availabletics) counts = realtics; else counts = availabletics; - + if (counts < 1) counts = 1; - + frameon++; if (debugfile) @@ -719,7 +719,7 @@ else if (realtics < availabletics) realtics, availabletics,counts); if (!demoplayback) - { + { // ideally nettics[0] should be 1 - 3 tics above lowtic // if we are consistantly slower, speed up time for (i=0 ; inumnodes ; i++) if (nodeingame[i] && nettics[i] < lowtic) lowtic = nettics[i]; - + if (lowtic < gametic/ticdup) I_Error ("TryRunTics: lowtic < gametic"); - + // don't stay in here forever -- give the menu a chance to work if (I_GetTime ()/ticdup - entertic >= 20) { M_Ticker (); return; - } + } } - + // run the count * ticdup dics while (counts--) { @@ -779,15 +779,15 @@ else if (realtics < availabletics) M_Ticker (); G_Ticker (); gametic++; - + // modify command for duplicated tics if (i != ticdup-1) { ticcmd_t *cmd; int buf; int j; - - buf = (gametic/ticdup)%BACKUPTICS; + + buf = (gametic/ticdup)%BACKUPTICS; for (j=0 ; j DOOM; - - /* Fugly hack to "reset" the player. Not worth the fugliness. - public static player_t nullplayer; - static { - nullplayer = new player_t(); - } - */ - public player_t(DoomMain DOOM) { - this.DOOM = DOOM; - powers = new int[NUMPOWERS]; - frags = new int[MAXPLAYERS]; - ammo = new int[NUMAMMO]; - //maxammo = new int[NUMAMMO]; - maxammo = new int[NUMAMMO]; - cards = new boolean[card_t.NUMCARDS.ordinal()]; - weaponowned = new boolean[NUMWEAPONS]; - psprites = malloc(pspdef_t::new, pspdef_t[]::new, NUMPSPRITES); - this.mo = mobj_t.createOn(DOOM); - // If a player doesn't reference himself through his object, he will have an existential crisis. - this.mo.player = this; - readyweapon = weapontype_t.wp_fist; - this.cmd = new ticcmd_t(); - //weaponinfo=new weaponinfo_t(); - } - - public final static int CF_NOCLIP = 1; // No damage, no health loss. - - public final static int CF_GODMODE = 2; - - public final static int CF_NOMOMENTUM = 4; // Not really a cheat, just a debug aid. - - /** - * The "mobj state" of the player is stored here, even though he "inherits" - * all mobj_t properties (except being a thinker). However, for good or bad, - * his mobj properties are modified by accessing player.mo - */ - public mobj_t mo; - - /** - * playerstate_t - */ - public int playerstate; - - public ticcmd_t cmd; - - /** - * Determine POV, including viewpoint bobbing during movement. (fixed_t) - * Focal origin above r.z - */ - public int viewz; - - /** - * (fixed_t) Base height above floor for viewz. - */ - public int viewheight; - - /** - * (fixed_t) Bob/squat speed. - */ - public int deltaviewheight; - - /** - * (fixed_t) bounded/scaled total momentum. - */ - public int bob; - - // Heretic stuff - public int flyheight; - public int lookdir; - public boolean centering; - - /** - * This is only used between levels, mo->health is used during levels. - * CORRECTION: this is also used by the automap widget. - * MAES: fugly hax, as even passing "Integers" won't work, as they are immutable. - * Fuck that, I'm doing it the fugly MPI Java way! - */ - public int[] health = new int[1]; - - /** - * has to be passed around :-( - */ - public int[] armorpoints = new int[1]; - - /** - * Armor type is 0-2. - */ - public int armortype; - - /** - * Power ups. invinc and invis are tic counters. - */ - public int[] powers; - - public boolean[] cards; - - public boolean backpack; - - // Frags, kills of other players. - public int[] frags; - - public weapontype_t readyweapon; - - // Is wp_nochange if not changing. - public weapontype_t pendingweapon; - - public boolean[] weaponowned; - - public int[] ammo; - - public int[] maxammo; - - /** - * True if button down last tic. - */ - public boolean attackdown; - - public boolean usedown; - - // Bit flags, for cheats and debug. - // See cheat_t, above. - public int cheats; - - // Refired shots are less accurate. - public int refire; - - // For intermission stats. - public int killcount; - - public int itemcount; - - public int secretcount; - - // Hint messages. - public String message; - - // For screen flashing (red or bright). - public int damagecount; - - public int bonuscount; - - // Who did damage (NULL for floors/ceilings). - public mobj_t attacker; - - // So gun flashes light up areas. - public int extralight; - - /** - * Current PLAYPAL, ??? can be set to REDCOLORMAP for pain, etc. MAES: "int" - * my ass. It's yet another pointer alias into colormaps. Ergo, array and - * pointer. - */ - // public byte[] fixedcolormap; - /** - * *NOT* preshifted index of colormap in light color maps. - * It could be written when the player_t object is packed. Dont shift this value, - * do shifts after retrieving this. - */ - public int fixedcolormap; - - // Player skin colorshift, - // 0-3 for which color to draw player. - public int colormap; - - // TODO: Overlay view sprites (gun, etc). - public pspdef_t[] psprites; - - // True if secret level has been done. - public boolean didsecret; - - /** - * It's probably faster to clone the null player - */ - public void reset() { - memset(ammo, 0, ammo.length); - memset(armorpoints, 0, armorpoints.length); - memset(cards, false, cards.length); - memset(frags, 0, frags.length); - memset(health, 0, health.length); - memset(maxammo, 0, maxammo.length); - memset(powers, 0, powers.length); - memset(weaponowned, false, weaponowned.length); - //memset(psprites, null, psprites.length); - this.cheats = 0; // Forgot to clear up cheats flag... - this.armortype = 0; - this.attackdown = false; - this.attacker = null; - this.backpack = false; - this.bob = 0; - } - - @Override - public player_t clone() - throws CloneNotSupportedException { - return (player_t) super.clone(); - } - - /** - * 16 pixels of bob - */ - private static int MAXBOB = 0x100000; - - /** - * P_Thrust Moves the given origin along a given angle. - * - * @param angle - * (angle_t) - * @param move - * (fixed_t) - */ - public void Thrust(long angle, int move) { - mo.momx += FixedMul(move, finecosine(angle)); - mo.momy += FixedMul(move, finesine(angle)); - } - - protected final static int PLAYERTHRUST = 2048 / TIC_MUL; - - /** - * P_MovePlayer - */ - public void MovePlayer() { - ticcmd_t cmd = this.cmd; - - mo.angle += (cmd.angleturn << 16); - mo.angle &= BITS32; - - // Do not let the player control movement - // if not onground. - onground = (mo.z <= mo.floorz); - - if (cmd.forwardmove != 0 && onground) { - Thrust(mo.angle, cmd.forwardmove * PLAYERTHRUST); - } - - if (cmd.sidemove != 0 && onground) { - Thrust((mo.angle - ANG90) & BITS32, cmd.sidemove * PLAYERTHRUST); - } - - if ((cmd.forwardmove != 0 || cmd.sidemove != 0) - && mo.mobj_state == states[statenum_t.S_PLAY.ordinal()]) { - this.mo.SetMobjState(statenum_t.S_PLAY_RUN1); - } - - // Freelook code ripped off Heretic. Sieg heil! - int look = cmd.lookfly & 15; - - if (look > 7) { - look -= 16; - } - if (look != 0) { - if (look == TOCENTER) { - centering = true; - } else { - lookdir += 5 * look; - if (lookdir > 90 || lookdir < -110) { - lookdir -= 5 * look; - } - } - } - - // Centering is done over several tics - if (centering) { - if (lookdir > 0) { - lookdir -= 8; - } else if (lookdir < 0) { - lookdir += 8; - } - if (Math.abs(lookdir) < 8) { - lookdir = 0; - centering = false; - } - } - /* Flight stuff from Heretic - fly = cmd.lookfly>>4; - - if(fly > 7) - { - fly -= 16; - } - if(fly && player->powers[pw_flight]) - { - if(fly != TOCENTER) - { - player->flyheight = fly*2; - if(!(player->mo->flags2&MF2_FLY)) - { - player->mo->flags2 |= MF2_FLY; - player->mo->flags |= MF_NOGRAVITY; - } - } - else - { - player->mo->flags2 &= ~MF2_FLY; - player->mo->flags &= ~MF_NOGRAVITY; - } - } - else if(fly > 0) - { - P_PlayerUseArtifact(player, arti_fly); - } - if(player->mo->flags2&MF2_FLY) - { - player->mo->momz = player->flyheight*FRACUNIT; - if(player->flyheight) - { - player->flyheight /= 2; - } - } */ - } - - // - // GET STUFF - // - // a weapon is found with two clip loads, - // a big item has five clip loads - public static final int[] clipammo = {10, 4, 20, 1}; - - /** - * P_GiveAmmo Num is the number of clip loads, not the individual count (0= - * 1/2 clip). - * - * @return false if the ammo can't be picked up at all - * @param ammo - * intended to be ammotype_t. - */ - public boolean GiveAmmo(ammotype_t amm, int num) { - int oldammo; - int ammo = amm.ordinal(); - if (ammo == ammotype_t.am_noammo.ordinal()) { - return false; - } - - if (ammo < 0 || ammo > NUMAMMO) { - DOOM.doomSystem.Error("P_GiveAmmo: bad type %d", ammo); - } - - if (this.ammo[ammo] == maxammo[ammo]) { - return false; - } - - if (num != 0) { - num *= clipammo[ammo]; - } else { - num = clipammo[ammo] / 2; - } - - if (DOOM.gameskill == skill_t.sk_baby - || DOOM.gameskill == skill_t.sk_nightmare) { - // give double ammo in trainer mode, - // you'll need in nightmare - num <<= 1; - } - - oldammo = this.ammo[ammo]; - this.ammo[ammo] += num; - - if (this.ammo[ammo] > maxammo[ammo]) { - this.ammo[ammo] = maxammo[ammo]; - } - - // If non zero ammo, - // don't change up weapons, - // player was lower on purpose. - if (oldammo != 0) { - return true; - } - - // We were down to zero, - // so select a new weapon. - // Preferences are not user selectable. - switch (ammotype_t.values()[ammo]) { - case am_clip: - if (readyweapon == weapontype_t.wp_fist) { - if (weaponowned[weapontype_t.wp_chaingun.ordinal()]) { - pendingweapon = weapontype_t.wp_chaingun; - } else { - pendingweapon = weapontype_t.wp_pistol; - } - } - break; - - case am_shell: - if (readyweapon == weapontype_t.wp_fist - || readyweapon == weapontype_t.wp_pistol) { - if (weaponowned[weapontype_t.wp_shotgun.ordinal()]) { - pendingweapon = weapontype_t.wp_shotgun; - } - } - break; - - case am_cell: - if (readyweapon == weapontype_t.wp_fist - || readyweapon == weapontype_t.wp_pistol) { - if (weaponowned[weapontype_t.wp_plasma.ordinal()]) { - pendingweapon = weapontype_t.wp_plasma; - } - } - break; - - case am_misl: - if (readyweapon == weapontype_t.wp_fist) { - if (weaponowned[weapontype_t.wp_missile.ordinal()]) { - pendingweapon = weapontype_t.wp_missile; - } - } - default: - break; - } - - return true; - } - - public static final int BONUSADD = 6; - - /** - * P_GiveWeapon - * The weapon name may have a MF_DROPPED flag ored in. - */ - public boolean GiveWeapon(weapontype_t weapn, boolean dropped) { - boolean gaveammo; - boolean gaveweapon; - int weapon = weapn.ordinal(); - - if (DOOM.netgame && (DOOM.deathmatch != true) // ???? was "2" - && !dropped) { - // leave placed weapons forever on net games - if (weaponowned[weapon]) { - return false; - } - - bonuscount += BONUSADD; - weaponowned[weapon] = true; - - if (DOOM.deathmatch) { - GiveAmmo(weaponinfo[weapon].ammo, 5); - } else { - GiveAmmo(weaponinfo[weapon].ammo, 2); - } - pendingweapon = weapn; - - if (this == DOOM.players[DOOM.consoleplayer]) { - DOOM.doomSound.StartSound(null, sfxenum_t.sfx_wpnup); - } - return false; - } - - if (weaponinfo[weapon].ammo != ammotype_t.am_noammo) { - // give one clip with a dropped weapon, - // two clips with a found weapon - if (dropped) { - gaveammo = GiveAmmo(weaponinfo[weapon].ammo, 1); - } else { - gaveammo = GiveAmmo(weaponinfo[weapon].ammo, 2); - } - } else { - gaveammo = false; - } - - if (weaponowned[weapon]) { - gaveweapon = false; - } else { - gaveweapon = true; - weaponowned[weapon] = true; - pendingweapon = weapn; - } - - return (gaveweapon || gaveammo); - } - - /** - * P_GiveBody Returns false if the body isn't needed at all - */ - public boolean GiveBody(int num) { - if (this.health[0] >= MAXHEALTH) { - return false; - } - - health[0] += num; - if (health[0] > MAXHEALTH) { - health[0] = MAXHEALTH; - } - mo.health = health[0]; - - return true; - } - - /** - * P_GiveArmor Returns false if the armor is worse than the current armor. - */ - public boolean GiveArmor(int armortype) { - int hits; - - hits = armortype * 100; - if (armorpoints[0] >= hits) { - return false; // don't pick up - } - this.armortype = armortype; - armorpoints[0] = hits; - - return true; - } - - /** - * P_GiveCard - */ - public void GiveCard(card_t crd) { - int card = crd.ordinal(); - if (cards[card]) { - return; - } - - bonuscount = BONUSADD; - cards[card] = true; - } - - // - // P_GivePower - // - public boolean GivePower(int /* powertype_t */ power) // MAES: - // I - // didn't - // change - // this! - { - if (power == pw_invulnerability) { - powers[power] = INVULNTICS; - return true; - } - - if (power == pw_invisibility) { - powers[power] = INVISTICS; - mo.flags |= MF_SHADOW; - return true; - } - - if (power == pw_infrared) { - powers[power] = INFRATICS; - return true; - } - - if (power == pw_ironfeet) { - powers[power] = IRONTICS; - return true; - } - - if (power == pw_strength) { - GiveBody(100); - powers[power] = 1; - return true; - } - - if (powers[power] != 0) { - return false; // already got it - } - powers[power] = 1; - return true; - } - - /** - * G_PlayerFinishLevel - * Called when a player completes a level. - */ - @SourceCode.Compatible - @G_Game.C(G_PlayerFinishLevel) - public final void PlayerFinishLevel() { - memset(powers, 0, powers.length); - memset(cards, false, cards.length); - mo.flags &= ~mobj_t.MF_SHADOW; // cancel invisibility - extralight = 0; // cancel gun flashes - fixedcolormap = Palettes.COLORMAP_FIXED; // cancel ir gogles - damagecount = 0; // no palette changes - bonuscount = 0; - lookdir = 0; // From heretic - } - - /** - * P_PlayerInSpecialSector - * Called every tic frame - * that the player origin is in a special sector - */ - protected void PlayerInSpecialSector() { - sector_t sector; - - sector = mo.subsector.sector; - - // Falling, not all the way down yet? - if (mo.z != sector.floorheight) { - return; - } - - // Has hitten ground. - switch (sector.special) { - case 5: - // HELLSLIME DAMAGE - if (powers[pw_ironfeet] == 0) { - if (!flags(DOOM.leveltime, 0x1f)) { - DOOM.actions.DamageMobj(mo, null, null, 10); - } - } - break; - - case 7: - // NUKAGE DAMAGE - if (powers[pw_ironfeet] == 0) { - if (!flags(DOOM.leveltime, 0x1f)) { - DOOM.actions.DamageMobj(mo, null, null, 5); - } - } - break; - - case 16: - // SUPER HELLSLIME DAMAGE - case 4: - // STROBE HURT - if (!eval(powers[pw_ironfeet]) - || (DOOM.random.P_Random() < 5)) { - if (!flags(DOOM.leveltime, 0x1f)) { - DOOM.actions.DamageMobj(mo, null, null, 20); - } - } - break; - - case 9: - // SECRET SECTOR - secretcount++; - sector.special = 0; - break; - - case 11: - // EXIT SUPER DAMAGE! (for E1M8 finale) - cheats &= ~CF_GODMODE; - - if (!flags(DOOM.leveltime, 0x1f)) { - DOOM.actions.DamageMobj(mo, null, null, 20); - } - - if (health[0] <= 10) { - DOOM.ExitLevel(); - } - break; - - default: - DOOM.doomSystem.Error("P_PlayerInSpecialSector: unknown special %d", sector.special); - break; - } - } - - // -//P_CalcHeight -//Calculate the walking / running height adjustment -// - public void CalcHeight() { - int angle; - int bob; // fixed - - // Regular movement bobbing - // (needs to be calculated for gun swing - // even if not on ground) - // OPTIMIZE: tablify angle - // Note: a LUT allows for effects - // like a ramp with low health. - this.bob - = FixedMul(mo.momx, mo.momx) - + FixedMul(mo.momy, mo.momy); - - this.bob >>= 2; - - if (this.bob > MAXBOB) { - this.bob = MAXBOB; - } - - if (flags(cheats, CF_NOMOMENTUM) || !onground) { - viewz = mo.z + VIEWHEIGHT; - - if (viewz > mo.ceilingz - 4 * FRACUNIT) { - viewz = mo.ceilingz - 4 * FRACUNIT; - } - - viewz = mo.z + viewheight; - return; - } - - angle = (FINEANGLES / 20 * DOOM.leveltime) & FINEMASK; - bob = FixedMul(this.bob / 2, finesine[angle]); - - // move viewheight - if (playerstate == PST_LIVE) { - viewheight += deltaviewheight; - - if (viewheight > VIEWHEIGHT) { - viewheight = VIEWHEIGHT; - deltaviewheight = 0; - } - - if (viewheight < VIEWHEIGHT / 2) { - viewheight = VIEWHEIGHT / 2; - if (deltaviewheight <= 0) { - deltaviewheight = 1; - } - } - - if (deltaviewheight != 0) { - deltaviewheight += FRACUNIT / 4; - if (deltaviewheight == 0) { - deltaviewheight = 1; - } - } - } - viewz = mo.z + viewheight + bob; - - if (viewz > mo.ceilingz - 4 * FRACUNIT) { - viewz = mo.ceilingz - 4 * FRACUNIT; - } - } - - private static final long ANG5 = (ANG90 / 18); - - /** - * P_DeathThink - * Fall on your face when dying. - * Decrease POV height to floor height. - * - * DOOMGUY IS SO AWESOME THAT HE THINKS EVEN WHEN DEAD!!! - * - */ - public void DeathThink() { - long angle; //angle_t - long delta; - - MovePsprites(); - - // fall to the ground - if (viewheight > 6 * FRACUNIT) { - viewheight -= FRACUNIT; - } - - if (viewheight < 6 * FRACUNIT) { - viewheight = 6 * FRACUNIT; - } - - deltaviewheight = 0; - onground = (mo.z <= mo.floorz); - CalcHeight(); - - if (attacker != null && attacker != mo) { - angle = DOOM.sceneRenderer.PointToAngle2(mo.x, - mo.y, - attacker.x, - attacker.y); - - delta = Tables.addAngles(angle, -mo.angle); - - if (delta < ANG5 || delta > -ANG5) { - // Looking at killer, - // so fade damage flash down. - mo.angle = angle; - - if (damagecount != 0) { - damagecount--; - } - } else if (delta < ANG180) { - mo.angle += ANG5; - } else { - mo.angle -= ANG5; - } - } else if (damagecount != 0) { - damagecount--; - } - - if (flags(cmd.buttons, BT_USE)) { - playerstate = PST_REBORN; - } - } - -// -// P_MovePsprites -// Called every tic by player thinking routine. -// - public void MovePsprites() { - - pspdef_t psp; - @SuppressWarnings("unused") // Shut up compiler - state_t state = null; - - for (int i = 0; i < NUMPSPRITES; i++) { - psp = psprites[i]; - // a null state means not active - if ((state = psp.state) != null) { - // drop tic count and possibly change state - - // a -1 tic count never changes - if (psp.tics != -1) { - psp.tics--; - if (!eval(psp.tics)) { - this.SetPsprite(i, psp.state.nextstate); - } - } - } - } - - psprites[ps_flash].sx = psprites[ps_weapon].sx; - psprites[ps_flash].sy = psprites[ps_weapon].sy; - } - - /** - * P_SetPsprite - */ - @SourceCode.Exact - @P_Pspr.C(P_SetPsprite) - public void SetPsprite(int position, statenum_t newstate) { - final pspdef_t psp; - state_t state; - - psp = psprites[position]; - - do { - if (!eval(newstate)) { - // object removed itself - psp.state = null; - break; - } - - state = states[newstate.ordinal()]; - psp.state = state; - psp.tics = state.tics; // could be 0 - - if (eval(state.misc1)) { - // coordinate set - psp.sx = state.misc1 << FRACBITS; - psp.sy = state.misc2 << FRACBITS; - } - - // Call action routine. - // Modified handling. - if (state.action.isParamType(PlayerSpriteConsumer.class)) { - state.action.fun(PlayerSpriteConsumer.class).accept(DOOM.actions, this, psp); - if (!eval(psp.state)) { - break; - } - } - - newstate = psp.state.nextstate; - - } while (!eval(psp.tics)); - // an initial state of 0 could cycle through - } - - /** - * Accessory method to identify which "doomguy" we are. - * Because we can't use the [target.player-players] syntax - * in order to get an array index, in Java. - * - * If -1 is returned, then we have existential problems. - * - */ - public int identify() { - - if (id >= 0) { - return id; - } - int i; - // Let's assume that we know jack. - for (i = 0; i < DOOM.players.length; i++) { - if (this == DOOM.players[i]) { - break; - } - } - - return id = i; - - } - - private int id = -1; - - private boolean onground; - - /* psprnum_t enum */ - public static int ps_weapon = 0, - ps_flash = 1, - NUMPSPRITES = 2; - - public static int LOWERSPEED = MAPFRACUNIT * 6; - public static int RAISESPEED = MAPFRACUNIT * 6; - - public static int WEAPONBOTTOM = 128 * FRACUNIT; - public static int WEAPONTOP = 32 * FRACUNIT; - - // plasma cells for a bfg attack - private static int BFGCELLS = 40; - - - /* - P_SetPsprite - - - public void - SetPsprite - ( player_t player, - int position, - statenum_t newstate ) - { - pspdef_t psp; - state_t state; - - psp = psprites[position]; - - do - { - if (newstate==null) - { - // object removed itself - psp.state = null; - break; - } - - state = states[newstate.ordinal()]; - psp.state = state; - psp.tics = (int) state.tics; // could be 0 - - if (state.misc1!=0) - { - // coordinate set - psp.sx = (int) (state.misc1 << FRACBITS); - psp.sy = (int) (state.misc2 << FRACBITS); - } - - // Call action routine. - // Modified handling. - if (state.action.getType()==acp2) - { - P.A.dispatch(state.action,this, psp); - if (psp.state==null) - break; - } - - newstate = psp.state.nextstate; - - } while (psp.tics==0); - // an initial state of 0 could cycle through - } - */ - /** - * fixed_t - */ - int swingx, swingy; - - /** - * P_CalcSwing - * - * @param player - */ - public void CalcSwing(player_t player) { - int swing; // fixed_t - int angle; - - // OPTIMIZE: tablify this. - // A LUT would allow for different modes, - // and add flexibility. - swing = this.bob; - - angle = (FINEANGLES / 70 * DOOM.leveltime) & FINEMASK; - swingx = FixedMul(swing, finesine[angle]); - - angle = (FINEANGLES / 70 * DOOM.leveltime + FINEANGLES / 2) & FINEMASK; - swingy = -FixedMul(swingx, finesine[angle]); - } - - // - // P_BringUpWeapon - // Starts bringing the pending weapon up - // from the bottom of the screen. - // Uses player - // - @SourceCode.Exact - @P_Pspr.C(P_BringUpWeapon) - public void BringUpWeapon() { - statenum_t newstate; - - if (pendingweapon == weapontype_t.wp_nochange) { - pendingweapon = readyweapon; - } - - if (pendingweapon == weapontype_t.wp_chainsaw) { - S_StartSound: - { - DOOM.doomSound.StartSound(mo, sfxenum_t.sfx_sawup); - } - } - - newstate = weaponinfo[pendingweapon.ordinal()].upstate; - - pendingweapon = weapontype_t.wp_nochange; - psprites[ps_weapon].sy = WEAPONBOTTOM; - - P_SetPsprite: - { - this.SetPsprite(ps_weapon, newstate); - } - } - - /** - * P_CheckAmmo - * Returns true if there is enough ammo to shoot. - * If not, selects the next weapon to use. - */ - public boolean CheckAmmo() { - ammotype_t ammo; - int count; - - ammo = weaponinfo[readyweapon.ordinal()].ammo; - - // Minimal amount for one shot varies. - if (readyweapon == weapontype_t.wp_bfg) { - count = BFGCELLS; - } else if (readyweapon == weapontype_t.wp_supershotgun) { - count = 2; // Double barrel. - } else { - count = 1; // Regular. - } - // Some do not need ammunition anyway. - // Return if current ammunition sufficient. - if (ammo == ammotype_t.am_noammo || this.ammo[ammo.ordinal()] >= count) { - return true; - } - - // Out of ammo, pick a weapon to change to. - // Preferences are set here. - do { - if (weaponowned[weapontype_t.wp_plasma.ordinal()] - && (this.ammo[ammotype_t.am_cell.ordinal()] != 0) - && !DOOM.isShareware()) { - pendingweapon = weapontype_t.wp_plasma; - } else if (weaponowned[weapontype_t.wp_supershotgun.ordinal()] - && this.ammo[ammotype_t.am_shell.ordinal()] > 2 - && DOOM.isCommercial()) { - pendingweapon = weapontype_t.wp_supershotgun; - } else if (weaponowned[weapontype_t.wp_chaingun.ordinal()] - && this.ammo[ammotype_t.am_clip.ordinal()] != 0) { - pendingweapon = weapontype_t.wp_chaingun; - } else if (weaponowned[weapontype_t.wp_shotgun.ordinal()] - && this.ammo[ammotype_t.am_shell.ordinal()] != 0) { - pendingweapon = weapontype_t.wp_shotgun; - } else if (this.ammo[ammotype_t.am_clip.ordinal()] != 0) { - pendingweapon = weapontype_t.wp_pistol; - } else if (weaponowned[weapontype_t.wp_chainsaw.ordinal()]) { - pendingweapon = weapontype_t.wp_chainsaw; - } else if (weaponowned[weapontype_t.wp_missile.ordinal()] - && this.ammo[ammotype_t.am_misl.ordinal()] != 0) { - pendingweapon = weapontype_t.wp_missile; - } else if (weaponowned[weapontype_t.wp_bfg.ordinal()] - && this.ammo[ammotype_t.am_cell.ordinal()] > 40 - && !DOOM.isShareware()) { - pendingweapon = weapontype_t.wp_bfg; - } else { - // If everything fails. - pendingweapon = weapontype_t.wp_fist; - } - - } while (pendingweapon == weapontype_t.wp_nochange); - - // Now set appropriate weapon overlay. - this.SetPsprite( - ps_weapon, - weaponinfo[readyweapon.ordinal()].downstate); - - return false; - } - - /** - * P_DropWeapon - * Player died, so put the weapon away. - */ - public void DropWeapon() { - this.SetPsprite( - ps_weapon, - weaponinfo[readyweapon.ordinal()].downstate); - } - - /** - * P_SetupPsprites - * Called at start of level for each - */ - @SourceCode.Exact - @P_Pspr.C(P_SetupPsprites) - public void SetupPsprites() { - // remove all psprites - for (int i = 0; i < NUMPSPRITES; i++) { - psprites[i].state = null; - } - - // spawn the gun - pendingweapon = readyweapon; - BringUpWeapon(); - } - - /** - * P_PlayerThink - */ - public void PlayerThink(player_t player) { - ticcmd_t cmd; - weapontype_t newweapon; - - // fixme: do this in the cheat code - if (flags(player.cheats, player_t.CF_NOCLIP)) { - player.mo.flags |= MF_NOCLIP; - } else { - player.mo.flags &= ~MF_NOCLIP; - } - - // chain saw run forward - cmd = player.cmd; - if (flags(player.mo.flags, MF_JUSTATTACKED)) { - cmd.angleturn = 0; - cmd.forwardmove = (0xc800 / 512); - cmd.sidemove = 0; - player.mo.flags &= ~MF_JUSTATTACKED; - } - - if (player.playerstate == PST_DEAD) { - player.DeathThink(); - return; - } - - // Move around. - // Reactiontime is used to prevent movement - // for a bit after a teleport. - if (eval(player.mo.reactiontime)) { - player.mo.reactiontime--; - } else { - player.MovePlayer(); - } - - player.CalcHeight(); - - if (eval(player.mo.subsector.sector.special)) { - player.PlayerInSpecialSector(); - } - - // Check for weapon change. - // A special event has no other buttons. - if (flags(cmd.buttons, BT_SPECIAL)) { - cmd.buttons = 0; - } - - if (flags(cmd.buttons, BT_CHANGE)) { - // The actual changing of the weapon is done - // when the weapon psprite can do it - // (read: not in the middle of an attack). - // System.out.println("Weapon change detected, attempting to perform"); - - newweapon = weapontype_t.values()[(cmd.buttons & BT_WEAPONMASK) >> BT_WEAPONSHIFT]; - - // If chainsaw is available, it won't change back to the fist - // unless player also has berserk. - if (newweapon == weapontype_t.wp_fist - && player.weaponowned[weapontype_t.wp_chainsaw.ordinal()] - && !(player.readyweapon == weapontype_t.wp_chainsaw - && eval(player.powers[pw_strength]))) { - newweapon = weapontype_t.wp_chainsaw; - } - - // Will switch between SG and SSG in Doom 2. - if (DOOM.isCommercial() - && newweapon == weapontype_t.wp_shotgun - && player.weaponowned[weapontype_t.wp_supershotgun.ordinal()] - && player.readyweapon != weapontype_t.wp_supershotgun) { - newweapon = weapontype_t.wp_supershotgun; - } - - if (player.weaponowned[newweapon.ordinal()] - && newweapon != player.readyweapon) { - // Do not go to plasma or BFG in shareware, - // even if cheated. - if ((newweapon != weapontype_t.wp_plasma - && newweapon != weapontype_t.wp_bfg) - || !DOOM.isShareware()) { - player.pendingweapon = newweapon; - } - } - } - - // check for use - if (flags(cmd.buttons, BT_USE)) { - if (!player.usedown) { - DOOM.actions.UseLines(player); - player.usedown = true; - } - } else { - player.usedown = false; - } - - // cycle psprites - player.MovePsprites(); - - // Counters, time dependent power ups. - // Strength counts up to diminish fade. - if (eval(player.powers[pw_strength])) { - player.powers[pw_strength]++; - } - - if (eval(player.powers[pw_invulnerability])) { - player.powers[pw_invulnerability]--; - } - - if (eval(player.powers[pw_invisibility])) { - if (!eval(--player.powers[pw_invisibility])) { - player.mo.flags &= ~MF_SHADOW; - } - } - - if (eval(player.powers[pw_infrared])) { - player.powers[pw_infrared]--; - } - - if (eval(player.powers[pw_ironfeet])) { - player.powers[pw_ironfeet]--; - } - - if (eval(player.damagecount)) { - player.damagecount--; - } - - if (eval(player.bonuscount)) { - player.bonuscount--; - } - - // Handling colormaps. - if (eval(player.powers[pw_invulnerability])) { - if (player.powers[pw_invulnerability] > 4 * 32 || flags(player.powers[pw_invulnerability], 8)) { - player.fixedcolormap = Palettes.COLORMAP_INVERSE; - } else { - player.fixedcolormap = Palettes.COLORMAP_FIXED; - } - } else if (eval(player.powers[pw_infrared])) { - if (player.powers[pw_infrared] > 4 * 32 - || flags(player.powers[pw_infrared], 8)) { - // almost full bright - player.fixedcolormap = Palettes.COLORMAP_BULLBRIGHT; - } else { - player.fixedcolormap = Palettes.COLORMAP_FIXED; - } - } else { - player.fixedcolormap = Palettes.COLORMAP_FIXED; - } - } - - /** - * G_PlayerReborn - * Called after a player dies - * almost everything is cleared and initialized - * - * - */ - @G_Game.C(G_PlayerReborn) - public void PlayerReborn() { - final int[] localFrags = new int[MAXPLAYERS]; - final int localKillCount, localItemCount, localSecretCount; - - // System.arraycopy(players[player].frags, 0, frags, 0, frags.length); - // We save the player's frags here... - C2JUtils.memcpy(localFrags, this.frags, localFrags.length); - localKillCount = this.killcount; - localItemCount = this.itemcount; - localSecretCount = this.secretcount; - - //MAES: we need to simulate an erasure, possibly without making - // a new object.memset (p, 0, sizeof(*p)); - //players[player]=(player_t) player_t.nullplayer.clone(); - // players[player]=new player_t(); - this.reset(); - - // And we copy the old frags into the "new" player. - C2JUtils.memcpy(this.frags, localFrags, this.frags.length); - - this.killcount = localKillCount; - this.itemcount = localItemCount; - this.secretcount = localSecretCount; - - usedown = attackdown = true; // don't do anything immediately - playerstate = PST_LIVE; - health[0] = MAXHEALTH; - readyweapon = pendingweapon = weapontype_t.wp_pistol; - weaponowned[weapontype_t.wp_fist.ordinal()] = true; - weaponowned[weapontype_t.wp_pistol.ordinal()] = true; - ammo[ammotype_t.am_clip.ordinal()] = 50; - lookdir = 0; // From Heretic - - System.arraycopy(DoomStatus.maxammo, 0, this.maxammo, 0, NUMAMMO); - } - - /** - * Called by Actions ticker - */ - public void PlayerThink() { - PlayerThink(this); - } - - public String toString() { - sb.setLength(0); - sb.append("player"); - sb.append(" momx "); - sb.append(this.mo.momx); - sb.append(" momy "); - sb.append(this.mo.momy); - sb.append(" x "); - sb.append(this.mo.x); - sb.append(" y "); - sb.append(this.mo.y); - return sb.toString(); - } - - private static StringBuilder sb = new StringBuilder(); - - public void read(DataInputStream f) throws IOException { - - // Careful when loading/saving: - // A player only carries a pointer to a mobj, which is "saved" - // but later discarded at load time, at least in vanilla. In any case, - // it has the size of a 32-bit integer, so make sure you skip it. - // TODO: OK, so vanilla's monsters lost "state" when saved, including non-Doomguy - // infighting. Did they "remember" Doomguy too? - // ANSWER: they didn't. - // The player is special in that it unambigously allows identifying - // its own map object in an absolute way. Once we identify - // at least one (e.g. object #45 is pointer 0x43545345) then, since - // map objects are stored in a nice serialized order. - this.p_mobj = DoomIO.readLEInt(f); // player mobj pointer - - this.playerstate = DoomIO.readLEInt(f); - this.cmd.read(f); - this.viewz = DoomIO.readLEInt(f); - this.viewheight = DoomIO.readLEInt(f); - this.deltaviewheight = DoomIO.readLEInt(f); - this.bob = DoomIO.readLEInt(f); - this.health[0] = DoomIO.readLEInt(f); - this.armorpoints[0] = DoomIO.readLEInt(f); - this.armortype = DoomIO.readLEInt(f); - DoomIO.readIntArray(f, this.powers, ByteOrder.LITTLE_ENDIAN); - DoomIO.readBooleanIntArray(f, this.cards); - this.backpack = DoomIO.readIntBoolean(f); - DoomIO.readIntArray(f, frags, ByteOrder.LITTLE_ENDIAN); - this.readyweapon = weapontype_t.values()[DoomIO.readLEInt(f)]; - this.pendingweapon = weapontype_t.values()[DoomIO.readLEInt(f)]; - DoomIO.readBooleanIntArray(f, this.weaponowned); - DoomIO.readIntArray(f, ammo, ByteOrder.LITTLE_ENDIAN); - DoomIO.readIntArray(f, maxammo, ByteOrder.LITTLE_ENDIAN); - // Read these as "int booleans" - this.attackdown = DoomIO.readIntBoolean(f); - this.usedown = DoomIO.readIntBoolean(f); - this.cheats = DoomIO.readLEInt(f); - this.refire = DoomIO.readLEInt(f); - // For intermission stats. - this.killcount = DoomIO.readLEInt(f); - this.itemcount = DoomIO.readLEInt(f); - this.secretcount = DoomIO.readLEInt(f); - // Hint messages. - f.skipBytes(4); - // For screen flashing (red or bright). - this.damagecount = DoomIO.readLEInt(f); - this.bonuscount = DoomIO.readLEInt(f); - // Who did damage (NULL for floors/ceilings). - // TODO: must be properly denormalized before saving/loading - f.skipBytes(4); // TODO: waste a read for attacker mobj. - // So gun flashes light up areas. - this.extralight = DoomIO.readLEInt(f); - // Current PLAYPAL, ??? - // can be set to REDCOLORMAP for pain, etc. - this.fixedcolormap = DoomIO.readLEInt(f); - this.colormap = DoomIO.readLEInt(f); - // PSPDEF _is_ readable. - for (pspdef_t p : this.psprites) { - p.read(f); - } - this.didsecret = DoomIO.readIntBoolean(f); - // Total size should be 280 bytes. - } - - public void write(DataOutputStream f) throws IOException { - - // It's much more convenient to pre-buffer, since - // we'll be writing all Little Endian stuff. - ByteBuffer b = ByteBuffer.allocate(280); - this.pack(b); - // Total size should be 280 bytes. - // Write everything nicely and at once. - f.write(b.array()); - } - - // Used to disambiguate between objects - public int p_mobj; - - @Override - public void pack(ByteBuffer buf) - throws IOException { - - ByteOrder bo = ByteOrder.LITTLE_ENDIAN; - buf.order(bo); - // The player is special in that it unambiguously allows identifying - // its own map object in an absolute way. Once we identify - // at least one (e.g. object #45 is pointer 0x43545345) then, since - // map objects are stored in a nice serialized order by using - // their next/prev pointers, you can reconstruct their - // relationships a posteriori. - // Store our own hashcode or "pointer" if you wish. - buf.putInt(pointer(mo)); - buf.putInt(playerstate); - cmd.pack(buf); - buf.putInt(viewz); - buf.putInt(viewheight); - buf.putInt(deltaviewheight); - buf.putInt(bob); - buf.putInt(health[0]); - buf.putInt(armorpoints[0]); - buf.putInt(armortype); - DoomBuffer.putIntArray(buf, this.powers, this.powers.length, bo); - DoomBuffer.putBooleanIntArray(buf, this.cards, this.cards.length, bo); - DoomBuffer.putBooleanInt(buf, backpack, bo); - DoomBuffer.putIntArray(buf, this.frags, this.frags.length, bo); - buf.putInt(readyweapon.ordinal()); - buf.putInt(pendingweapon.ordinal()); - DoomBuffer.putBooleanIntArray(buf, this.weaponowned, this.weaponowned.length, bo); - DoomBuffer.putIntArray(buf, this.ammo, this.ammo.length, bo); - DoomBuffer.putIntArray(buf, this.maxammo, this.maxammo.length, bo); - // Read these as "int booleans" - DoomBuffer.putBooleanInt(buf, attackdown, bo); - DoomBuffer.putBooleanInt(buf, usedown, bo); - buf.putInt(cheats); - buf.putInt(refire); - // For intermission stats. - buf.putInt(this.killcount); - buf.putInt(this.itemcount); - buf.putInt(this.secretcount); - // Hint messages. - buf.putInt(0); - // For screen flashing (red or bright). - buf.putInt(this.damagecount); - buf.putInt(this.bonuscount); - // Who did damage (NULL for floors/ceilings). - // TODO: must be properly denormalized before saving/loading - buf.putInt(pointer(attacker)); - // So gun flashes light up areas. - buf.putInt(this.extralight); - // Current PLAYPAL, ??? - // can be set to REDCOLORMAP for pain, etc. - - /** - * Here the fixed color map of player is written when player_t object is packed. - * Make sure not to write any preshifted value there! Do not scale player_r.fixedcolormap, - * scale dependent array accesses. - * - Good Sign 2017/04/15 - */ - buf.putInt(this.fixedcolormap); - buf.putInt(this.colormap); - // PSPDEF _is_ readable. - for (pspdef_t p : this.psprites) { - p.pack(buf); - } - buf.putInt(this.didsecret ? 1 : 0); - - } -} +package doom; + +import static data.Defines.BT_CHANGE; +import static data.Defines.BT_SPECIAL; +import static data.Defines.BT_USE; +import static data.Defines.BT_WEAPONMASK; +import static data.Defines.BT_WEAPONSHIFT; +import static data.Defines.INFRATICS; +import static data.Defines.INVISTICS; +import static data.Defines.INVULNTICS; +import static data.Defines.IRONTICS; +import static data.Defines.NUMAMMO; +import static data.Defines.NUMPOWERS; +import static data.Defines.NUMWEAPONS; +import static data.Defines.PST_DEAD; +import static data.Defines.PST_LIVE; +import static data.Defines.PST_REBORN; +import static data.Defines.TIC_MUL; +import static data.Defines.TOCENTER; +import static data.Defines.VIEWHEIGHT; +import static data.Defines.pw_infrared; +import static data.Defines.pw_invisibility; +import static data.Defines.pw_invulnerability; +import static data.Defines.pw_ironfeet; +import static data.Defines.pw_strength; +import static data.Limits.MAXHEALTH; +import static data.Limits.MAXPLAYERS; +import data.Tables; +import static data.Tables.ANG180; +import static data.Tables.ANG90; +import static data.Tables.BITS32; +import static data.Tables.FINEANGLES; +import static data.Tables.FINEMASK; +import static data.Tables.finecosine; +import static data.Tables.finesine; +import static data.info.states; +import data.sounds.sfxenum_t; +import data.state_t; +import defines.ammotype_t; +import defines.card_t; +import defines.skill_t; +import defines.statenum_t; +import doom.SourceCode.G_Game; +import static doom.SourceCode.G_Game.G_PlayerFinishLevel; +import static doom.SourceCode.G_Game.G_PlayerReborn; +import doom.SourceCode.P_Pspr; +import static doom.SourceCode.P_Pspr.P_BringUpWeapon; +import static doom.SourceCode.P_Pspr.P_SetPsprite; +import static doom.SourceCode.P_Pspr.P_SetupPsprites; +import static doom.items.weaponinfo; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import static m.fixed_t.FRACBITS; +import static m.fixed_t.FRACUNIT; +import static m.fixed_t.FixedMul; +import static m.fixed_t.MAPFRACUNIT; +import p.ActiveStates.PlayerSpriteConsumer; +import p.mobj_t; +import static p.mobj_t.MF_JUSTATTACKED; +import static p.mobj_t.MF_NOCLIP; +import static p.mobj_t.MF_SHADOW; +import p.pspdef_t; +import rr.sector_t; +import utils.C2JUtils; +import static utils.C2JUtils.eval; +import static utils.C2JUtils.flags; +import static utils.C2JUtils.memset; +import static utils.C2JUtils.pointer; +import static utils.GenericCopy.malloc; +import v.graphics.Palettes; +import w.DoomBuffer; +import w.DoomIO; +import w.IPackableDoomObject; +import w.IReadableDoomObject; + +/** + * Extended player object info: player_t The player data structure depends on a + * number of other structs: items (internal inventory), animation states + * (closely tied to the sprites used to represent them, unfortunately). + * + * #include "d_items.h" + * #include "p_pspr.h" + * + * In addition, the player is just a special + * case of the generic moving object/actor. + * NOTE: this doesn't mean it needs to extend it, although it would be + * possible. + * + * #include "p_mobj.h" + * + * Finally, for odd reasons, the player input is buffered within + * the player data struct, as commands per game tick. + * + * #include "d_ticcmd.h" + */ +public class player_t /*extends mobj_t */ implements Cloneable, IReadableDoomObject, IPackableDoomObject { + + /** + * Probably doomguy needs to know what the fuck is going on + */ + private final DoomMain DOOM; + + /* Fugly hack to "reset" the player. Not worth the fugliness. + public static player_t nullplayer; + static { + nullplayer = new player_t(); + } + */ + public player_t(DoomMain DOOM) { + this.DOOM = DOOM; + powers = new int[NUMPOWERS]; + frags = new int[MAXPLAYERS]; + ammo = new int[NUMAMMO]; + //maxammo = new int[NUMAMMO]; + maxammo = new int[NUMAMMO]; + cards = new boolean[card_t.NUMCARDS.ordinal()]; + weaponowned = new boolean[NUMWEAPONS]; + psprites = malloc(pspdef_t::new, pspdef_t[]::new, NUMPSPRITES); + this.mo = mobj_t.createOn(DOOM); + // If a player doesn't reference himself through his object, he will have an existential crisis. + this.mo.player = this; + readyweapon = weapontype_t.wp_fist; + this.cmd = new ticcmd_t(); + //weaponinfo=new weaponinfo_t(); + } + + public final static int CF_NOCLIP = 1; // No damage, no health loss. + + public final static int CF_GODMODE = 2; + + public final static int CF_NOMOMENTUM = 4; // Not really a cheat, just a debug aid. + + /** + * The "mobj state" of the player is stored here, even though he "inherits" + * all mobj_t properties (except being a thinker). However, for good or bad, + * his mobj properties are modified by accessing player.mo + */ + public mobj_t mo; + + /** + * playerstate_t + */ + public int playerstate; + + public ticcmd_t cmd; + + /** + * Determine POV, including viewpoint bobbing during movement. (fixed_t) + * Focal origin above r.z + */ + public int viewz; + + /** + * (fixed_t) Base height above floor for viewz. + */ + public int viewheight; + + /** + * (fixed_t) Bob/squat speed. + */ + public int deltaviewheight; + + /** + * (fixed_t) bounded/scaled total momentum. + */ + public int bob; + + // Heretic stuff + public int flyheight; + public int lookdir; + public boolean centering; + + /** + * This is only used between levels, mo->health is used during levels. + * CORRECTION: this is also used by the automap widget. + * MAES: fugly hax, as even passing "Integers" won't work, as they are immutable. + * Fuck that, I'm doing it the fugly MPI Java way! + */ + public int[] health = new int[1]; + + /** + * has to be passed around :-( + */ + public int[] armorpoints = new int[1]; + + /** + * Armor type is 0-2. + */ + public int armortype; + + /** + * Power ups. invinc and invis are tic counters. + */ + public int[] powers; + + public boolean[] cards; + + public boolean backpack; + + // Frags, kills of other players. + public int[] frags; + + public weapontype_t readyweapon; + + // Is wp_nochange if not changing. + public weapontype_t pendingweapon; + + public boolean[] weaponowned; + + public int[] ammo; + + public int[] maxammo; + + /** + * True if button down last tic. + */ + public boolean attackdown; + + public boolean usedown; + + // Bit flags, for cheats and debug. + // See cheat_t, above. + public int cheats; + + // Refired shots are less accurate. + public int refire; + + // For intermission stats. + public int killcount; + + public int itemcount; + + public int secretcount; + + // Hint messages. + public String message; + + // For screen flashing (red or bright). + public int damagecount; + + public int bonuscount; + + // Who did damage (NULL for floors/ceilings). + public mobj_t attacker; + + // So gun flashes light up areas. + public int extralight; + + /** + * Current PLAYPAL, ??? can be set to REDCOLORMAP for pain, etc. MAES: "int" + * my ass. It's yet another pointer alias into colormaps. Ergo, array and + * pointer. + */ + // public byte[] fixedcolormap; + /** + * *NOT* preshifted index of colormap in light color maps. + * It could be written when the player_t object is packed. Dont shift this value, + * do shifts after retrieving this. + */ + public int fixedcolormap; + + // Player skin colorshift, + // 0-3 for which color to draw player. + public int colormap; + + // TODO: Overlay view sprites (gun, etc). + public pspdef_t[] psprites; + + // True if secret level has been done. + public boolean didsecret; + + /** + * It's probably faster to clone the null player + */ + public void reset() { + memset(ammo, 0, ammo.length); + memset(armorpoints, 0, armorpoints.length); + memset(cards, false, cards.length); + memset(frags, 0, frags.length); + memset(health, 0, health.length); + memset(maxammo, 0, maxammo.length); + memset(powers, 0, powers.length); + memset(weaponowned, false, weaponowned.length); + //memset(psprites, null, psprites.length); + this.cheats = 0; // Forgot to clear up cheats flag... + this.armortype = 0; + this.attackdown = false; + this.attacker = null; + this.backpack = false; + this.bob = 0; + } + + @Override + public player_t clone() + throws CloneNotSupportedException { + return (player_t) super.clone(); + } + + /** + * 16 pixels of bob + */ + private static int MAXBOB = 0x100000; + + /** + * P_Thrust Moves the given origin along a given angle. + * + * @param angle + * (angle_t) + * @param move + * (fixed_t) + */ + public void Thrust(long angle, int move) { + mo.momx += FixedMul(move, finecosine(angle)); + mo.momy += FixedMul(move, finesine(angle)); + } + + protected final static int PLAYERTHRUST = 2048 / TIC_MUL; + + /** + * P_MovePlayer + */ + public void MovePlayer() { + ticcmd_t cmd = this.cmd; + + mo.angle += (cmd.angleturn << 16); + mo.angle &= BITS32; + + // Do not let the player control movement + // if not onground. + onground = (mo.z <= mo.floorz); + + if (cmd.forwardmove != 0 && onground) { + Thrust(mo.angle, cmd.forwardmove * PLAYERTHRUST); + } + + if (cmd.sidemove != 0 && onground) { + Thrust((mo.angle - ANG90) & BITS32, cmd.sidemove * PLAYERTHRUST); + } + + if ((cmd.forwardmove != 0 || cmd.sidemove != 0) + && mo.mobj_state == states[statenum_t.S_PLAY.ordinal()]) { + this.mo.SetMobjState(statenum_t.S_PLAY_RUN1); + } + + // Freelook code ripped off Heretic. Sieg heil! + int look = cmd.lookfly & 15; + + if (look > 7) { + look -= 16; + } + if (look != 0) { + if (look == TOCENTER) { + centering = true; + } else { + lookdir += 5 * look; + if (lookdir > 90 || lookdir < -110) { + lookdir -= 5 * look; + } + } + } + + // Centering is done over several tics + if (centering) { + if (lookdir > 0) { + lookdir -= 8; + } else if (lookdir < 0) { + lookdir += 8; + } + if (Math.abs(lookdir) < 8) { + lookdir = 0; + centering = false; + } + } + /* Flight stuff from Heretic + fly = cmd.lookfly>>4; + + if(fly > 7) + { + fly -= 16; + } + if(fly && player->powers[pw_flight]) + { + if(fly != TOCENTER) + { + player->flyheight = fly*2; + if(!(player->mo->flags2&MF2_FLY)) + { + player->mo->flags2 |= MF2_FLY; + player->mo->flags |= MF_NOGRAVITY; + } + } + else + { + player->mo->flags2 &= ~MF2_FLY; + player->mo->flags &= ~MF_NOGRAVITY; + } + } + else if(fly > 0) + { + P_PlayerUseArtifact(player, arti_fly); + } + if(player->mo->flags2&MF2_FLY) + { + player->mo->momz = player->flyheight*FRACUNIT; + if(player->flyheight) + { + player->flyheight /= 2; + } + } */ + } + + // + // GET STUFF + // + // a weapon is found with two clip loads, + // a big item has five clip loads + public static final int[] clipammo = {10, 4, 20, 1}; + + /** + * P_GiveAmmo Num is the number of clip loads, not the individual count (0= + * 1/2 clip). + * + * @return false if the ammo can't be picked up at all + * @param ammo + * intended to be ammotype_t. + */ + public boolean GiveAmmo(ammotype_t amm, int num) { + int oldammo; + int ammo = amm.ordinal(); + if (ammo == ammotype_t.am_noammo.ordinal()) { + return false; + } + + if (ammo < 0 || ammo > NUMAMMO) { + DOOM.doomSystem.Error("P_GiveAmmo: bad type %d", ammo); + } + + if (this.ammo[ammo] == maxammo[ammo]) { + return false; + } + + if (num != 0) { + num *= clipammo[ammo]; + } else { + num = clipammo[ammo] / 2; + } + + if (DOOM.gameskill == skill_t.sk_baby + || DOOM.gameskill == skill_t.sk_nightmare) { + // give double ammo in trainer mode, + // you'll need in nightmare + num <<= 1; + } + + oldammo = this.ammo[ammo]; + this.ammo[ammo] += num; + + if (this.ammo[ammo] > maxammo[ammo]) { + this.ammo[ammo] = maxammo[ammo]; + } + + // If non zero ammo, + // don't change up weapons, + // player was lower on purpose. + if (oldammo != 0) { + return true; + } + + // We were down to zero, + // so select a new weapon. + // Preferences are not user selectable. + switch (ammotype_t.values()[ammo]) { + case am_clip: + if (readyweapon == weapontype_t.wp_fist) { + if (weaponowned[weapontype_t.wp_chaingun.ordinal()]) { + pendingweapon = weapontype_t.wp_chaingun; + } else { + pendingweapon = weapontype_t.wp_pistol; + } + } + break; + + case am_shell: + if (readyweapon == weapontype_t.wp_fist + || readyweapon == weapontype_t.wp_pistol) { + if (weaponowned[weapontype_t.wp_shotgun.ordinal()]) { + pendingweapon = weapontype_t.wp_shotgun; + } + } + break; + + case am_cell: + if (readyweapon == weapontype_t.wp_fist + || readyweapon == weapontype_t.wp_pistol) { + if (weaponowned[weapontype_t.wp_plasma.ordinal()]) { + pendingweapon = weapontype_t.wp_plasma; + } + } + break; + + case am_misl: + if (readyweapon == weapontype_t.wp_fist) { + if (weaponowned[weapontype_t.wp_missile.ordinal()]) { + pendingweapon = weapontype_t.wp_missile; + } + } + default: + break; + } + + return true; + } + + public static final int BONUSADD = 6; + + /** + * P_GiveWeapon + * The weapon name may have a MF_DROPPED flag ored in. + */ + public boolean GiveWeapon(weapontype_t weapn, boolean dropped) { + boolean gaveammo; + boolean gaveweapon; + int weapon = weapn.ordinal(); + + if (DOOM.netgame && (DOOM.deathmatch != true) // ???? was "2" + && !dropped) { + // leave placed weapons forever on net games + if (weaponowned[weapon]) { + return false; + } + + bonuscount += BONUSADD; + weaponowned[weapon] = true; + + if (DOOM.deathmatch) { + GiveAmmo(weaponinfo[weapon].ammo, 5); + } else { + GiveAmmo(weaponinfo[weapon].ammo, 2); + } + pendingweapon = weapn; + + if (this == DOOM.players[DOOM.consoleplayer]) { + DOOM.doomSound.StartSound(null, sfxenum_t.sfx_wpnup); + } + return false; + } + + if (weaponinfo[weapon].ammo != ammotype_t.am_noammo) { + // give one clip with a dropped weapon, + // two clips with a found weapon + if (dropped) { + gaveammo = GiveAmmo(weaponinfo[weapon].ammo, 1); + } else { + gaveammo = GiveAmmo(weaponinfo[weapon].ammo, 2); + } + } else { + gaveammo = false; + } + + if (weaponowned[weapon]) { + gaveweapon = false; + } else { + gaveweapon = true; + weaponowned[weapon] = true; + pendingweapon = weapn; + } + + return (gaveweapon || gaveammo); + } + + /** + * P_GiveBody Returns false if the body isn't needed at all + */ + public boolean GiveBody(int num) { + if (this.health[0] >= MAXHEALTH) { + return false; + } + + health[0] += num; + if (health[0] > MAXHEALTH) { + health[0] = MAXHEALTH; + } + mo.health = health[0]; + + return true; + } + + /** + * P_GiveArmor Returns false if the armor is worse than the current armor. + */ + public boolean GiveArmor(int armortype) { + int hits; + + hits = armortype * 100; + if (armorpoints[0] >= hits) { + return false; // don't pick up + } + this.armortype = armortype; + armorpoints[0] = hits; + + return true; + } + + /** + * P_GiveCard + */ + public void GiveCard(card_t crd) { + int card = crd.ordinal(); + if (cards[card]) { + return; + } + + bonuscount = BONUSADD; + cards[card] = true; + } + + // + // P_GivePower + // + public boolean GivePower(int /* powertype_t */ power) // MAES: + // I + // didn't + // change + // this! + { + if (power == pw_invulnerability) { + powers[power] = INVULNTICS; + return true; + } + + if (power == pw_invisibility) { + powers[power] = INVISTICS; + mo.flags |= MF_SHADOW; + return true; + } + + if (power == pw_infrared) { + powers[power] = INFRATICS; + return true; + } + + if (power == pw_ironfeet) { + powers[power] = IRONTICS; + return true; + } + + if (power == pw_strength) { + GiveBody(100); + powers[power] = 1; + return true; + } + + if (powers[power] != 0) { + return false; // already got it + } + powers[power] = 1; + return true; + } + + /** + * G_PlayerFinishLevel + * Called when a player completes a level. + */ + @SourceCode.Compatible + @G_Game.C(G_PlayerFinishLevel) + public final void PlayerFinishLevel() { + memset(powers, 0, powers.length); + memset(cards, false, cards.length); + mo.flags &= ~mobj_t.MF_SHADOW; // cancel invisibility + extralight = 0; // cancel gun flashes + fixedcolormap = Palettes.COLORMAP_FIXED; // cancel ir gogles + damagecount = 0; // no palette changes + bonuscount = 0; + lookdir = 0; // From heretic + } + + /** + * P_PlayerInSpecialSector + * Called every tic frame + * that the player origin is in a special sector + */ + protected void PlayerInSpecialSector() { + sector_t sector; + + sector = mo.subsector.sector; + + // Falling, not all the way down yet? + if (mo.z != sector.floorheight) { + return; + } + + // Has hitten ground. + switch (sector.special) { + case 5: + // HELLSLIME DAMAGE + if (powers[pw_ironfeet] == 0) { + if (!flags(DOOM.leveltime, 0x1f)) { + DOOM.actions.DamageMobj(mo, null, null, 10); + } + } + break; + + case 7: + // NUKAGE DAMAGE + if (powers[pw_ironfeet] == 0) { + if (!flags(DOOM.leveltime, 0x1f)) { + DOOM.actions.DamageMobj(mo, null, null, 5); + } + } + break; + + case 16: + // SUPER HELLSLIME DAMAGE + case 4: + // STROBE HURT + if (!eval(powers[pw_ironfeet]) + || (DOOM.random.P_Random() < 5)) { + if (!flags(DOOM.leveltime, 0x1f)) { + DOOM.actions.DamageMobj(mo, null, null, 20); + } + } + break; + + case 9: + // SECRET SECTOR + secretcount++; + sector.special = 0; + break; + + case 11: + // EXIT SUPER DAMAGE! (for E1M8 finale) + cheats &= ~CF_GODMODE; + + if (!flags(DOOM.leveltime, 0x1f)) { + DOOM.actions.DamageMobj(mo, null, null, 20); + } + + if (health[0] <= 10) { + DOOM.ExitLevel(); + } + break; + + default: + DOOM.doomSystem.Error("P_PlayerInSpecialSector: unknown special %d", sector.special); + break; + } + } + + // +//P_CalcHeight +//Calculate the walking / running height adjustment +// + public void CalcHeight() { + int angle; + int bob; // fixed + + // Regular movement bobbing + // (needs to be calculated for gun swing + // even if not on ground) + // OPTIMIZE: tablify angle + // Note: a LUT allows for effects + // like a ramp with low health. + this.bob + = FixedMul(mo.momx, mo.momx) + + FixedMul(mo.momy, mo.momy); + + this.bob >>= 2; + + if (this.bob > MAXBOB) { + this.bob = MAXBOB; + } + + if (flags(cheats, CF_NOMOMENTUM) || !onground) { + viewz = mo.z + VIEWHEIGHT; + + if (viewz > mo.ceilingz - 4 * FRACUNIT) { + viewz = mo.ceilingz - 4 * FRACUNIT; + } + + viewz = mo.z + viewheight; + return; + } + + angle = (FINEANGLES / 20 * DOOM.leveltime) & FINEMASK; + bob = FixedMul(this.bob / 2, finesine[angle]); + + // move viewheight + if (playerstate == PST_LIVE) { + viewheight += deltaviewheight; + + if (viewheight > VIEWHEIGHT) { + viewheight = VIEWHEIGHT; + deltaviewheight = 0; + } + + if (viewheight < VIEWHEIGHT / 2) { + viewheight = VIEWHEIGHT / 2; + if (deltaviewheight <= 0) { + deltaviewheight = 1; + } + } + + if (deltaviewheight != 0) { + deltaviewheight += FRACUNIT / 4; + if (deltaviewheight == 0) { + deltaviewheight = 1; + } + } + } + viewz = mo.z + viewheight + bob; + + if (viewz > mo.ceilingz - 4 * FRACUNIT) { + viewz = mo.ceilingz - 4 * FRACUNIT; + } + } + + private static final long ANG5 = (ANG90 / 18); + + /** + * P_DeathThink + * Fall on your face when dying. + * Decrease POV height to floor height. + * + * DOOMGUY IS SO AWESOME THAT HE THINKS EVEN WHEN DEAD!!! + * + */ + public void DeathThink() { + long angle; //angle_t + long delta; + + MovePsprites(); + + // fall to the ground + if (viewheight > 6 * FRACUNIT) { + viewheight -= FRACUNIT; + } + + if (viewheight < 6 * FRACUNIT) { + viewheight = 6 * FRACUNIT; + } + + deltaviewheight = 0; + onground = (mo.z <= mo.floorz); + CalcHeight(); + + if (attacker != null && attacker != mo) { + angle = DOOM.sceneRenderer.PointToAngle2(mo.x, + mo.y, + attacker.x, + attacker.y); + + delta = Tables.addAngles(angle, -mo.angle); + + if (delta < ANG5 || delta > -ANG5) { + // Looking at killer, + // so fade damage flash down. + mo.angle = angle; + + if (damagecount != 0) { + damagecount--; + } + } else if (delta < ANG180) { + mo.angle += ANG5; + } else { + mo.angle -= ANG5; + } + } else if (damagecount != 0) { + damagecount--; + } + + if (flags(cmd.buttons, BT_USE)) { + playerstate = PST_REBORN; + } + } + +// +// P_MovePsprites +// Called every tic by player thinking routine. +// + public void MovePsprites() { + + pspdef_t psp; + @SuppressWarnings("unused") // Shut up compiler + state_t state = null; + + for (int i = 0; i < NUMPSPRITES; i++) { + psp = psprites[i]; + // a null state means not active + if ((state = psp.state) != null) { + // drop tic count and possibly change state + + // a -1 tic count never changes + if (psp.tics != -1) { + psp.tics--; + if (!eval(psp.tics)) { + this.SetPsprite(i, psp.state.nextstate); + } + } + } + } + + psprites[ps_flash].sx = psprites[ps_weapon].sx; + psprites[ps_flash].sy = psprites[ps_weapon].sy; + } + + /** + * P_SetPsprite + */ + @SourceCode.Exact + @P_Pspr.C(P_SetPsprite) + public void SetPsprite(int position, statenum_t newstate) { + final pspdef_t psp; + state_t state; + + psp = psprites[position]; + + do { + if (!eval(newstate)) { + // object removed itself + psp.state = null; + break; + } + + state = states[newstate.ordinal()]; + psp.state = state; + psp.tics = state.tics; // could be 0 + + if (eval(state.misc1)) { + // coordinate set + psp.sx = state.misc1 << FRACBITS; + psp.sy = state.misc2 << FRACBITS; + } + + // Call action routine. + // Modified handling. + if (state.action.isParamType(PlayerSpriteConsumer.class)) { + state.action.fun(PlayerSpriteConsumer.class).accept(DOOM.actions, this, psp); + if (!eval(psp.state)) { + break; + } + } + + newstate = psp.state.nextstate; + + } while (!eval(psp.tics)); + // an initial state of 0 could cycle through + } + + /** + * Accessory method to identify which "doomguy" we are. + * Because we can't use the [target.player-players] syntax + * in order to get an array index, in Java. + * + * If -1 is returned, then we have existential problems. + * + */ + public int identify() { + + if (id >= 0) { + return id; + } + int i; + // Let's assume that we know jack. + for (i = 0; i < DOOM.players.length; i++) { + if (this == DOOM.players[i]) { + break; + } + } + + return id = i; + + } + + private int id = -1; + + private boolean onground; + + /* psprnum_t enum */ + public static int ps_weapon = 0, + ps_flash = 1, + NUMPSPRITES = 2; + + public static int LOWERSPEED = MAPFRACUNIT * 6; + public static int RAISESPEED = MAPFRACUNIT * 6; + + public static int WEAPONBOTTOM = 128 * FRACUNIT; + public static int WEAPONTOP = 32 * FRACUNIT; + + // plasma cells for a bfg attack + private static int BFGCELLS = 40; + + + /* + P_SetPsprite + + + public void + SetPsprite + ( player_t player, + int position, + statenum_t newstate ) + { + pspdef_t psp; + state_t state; + + psp = psprites[position]; + + do + { + if (newstate==null) + { + // object removed itself + psp.state = null; + break; + } + + state = states[newstate.ordinal()]; + psp.state = state; + psp.tics = (int) state.tics; // could be 0 + + if (state.misc1!=0) + { + // coordinate set + psp.sx = (int) (state.misc1 << FRACBITS); + psp.sy = (int) (state.misc2 << FRACBITS); + } + + // Call action routine. + // Modified handling. + if (state.action.getType()==acp2) + { + P.A.dispatch(state.action,this, psp); + if (psp.state==null) + break; + } + + newstate = psp.state.nextstate; + + } while (psp.tics==0); + // an initial state of 0 could cycle through + } + */ + /** + * fixed_t + */ + int swingx, swingy; + + /** + * P_CalcSwing + * + * @param player + */ + public void CalcSwing(player_t player) { + int swing; // fixed_t + int angle; + + // OPTIMIZE: tablify this. + // A LUT would allow for different modes, + // and add flexibility. + swing = this.bob; + + angle = (FINEANGLES / 70 * DOOM.leveltime) & FINEMASK; + swingx = FixedMul(swing, finesine[angle]); + + angle = (FINEANGLES / 70 * DOOM.leveltime + FINEANGLES / 2) & FINEMASK; + swingy = -FixedMul(swingx, finesine[angle]); + } + + // + // P_BringUpWeapon + // Starts bringing the pending weapon up + // from the bottom of the screen. + // Uses player + // + @SourceCode.Exact + @P_Pspr.C(P_BringUpWeapon) + public void BringUpWeapon() { + statenum_t newstate; + + if (pendingweapon == weapontype_t.wp_nochange) { + pendingweapon = readyweapon; + } + + if (pendingweapon == weapontype_t.wp_chainsaw) { + S_StartSound: + { + DOOM.doomSound.StartSound(mo, sfxenum_t.sfx_sawup); + } + } + + newstate = weaponinfo[pendingweapon.ordinal()].upstate; + + pendingweapon = weapontype_t.wp_nochange; + psprites[ps_weapon].sy = WEAPONBOTTOM; + + P_SetPsprite: + { + this.SetPsprite(ps_weapon, newstate); + } + } + + /** + * P_CheckAmmo + * Returns true if there is enough ammo to shoot. + * If not, selects the next weapon to use. + */ + public boolean CheckAmmo() { + ammotype_t ammo; + int count; + + ammo = weaponinfo[readyweapon.ordinal()].ammo; + + // Minimal amount for one shot varies. + if (readyweapon == weapontype_t.wp_bfg) { + count = BFGCELLS; + } else if (readyweapon == weapontype_t.wp_supershotgun) { + count = 2; // Double barrel. + } else { + count = 1; // Regular. + } + // Some do not need ammunition anyway. + // Return if current ammunition sufficient. + if (ammo == ammotype_t.am_noammo || this.ammo[ammo.ordinal()] >= count) { + return true; + } + + // Out of ammo, pick a weapon to change to. + // Preferences are set here. + do { + if (weaponowned[weapontype_t.wp_plasma.ordinal()] + && (this.ammo[ammotype_t.am_cell.ordinal()] != 0) + && !DOOM.isShareware()) { + pendingweapon = weapontype_t.wp_plasma; + } else if (weaponowned[weapontype_t.wp_supershotgun.ordinal()] + && this.ammo[ammotype_t.am_shell.ordinal()] > 2 + && DOOM.isCommercial()) { + pendingweapon = weapontype_t.wp_supershotgun; + } else if (weaponowned[weapontype_t.wp_chaingun.ordinal()] + && this.ammo[ammotype_t.am_clip.ordinal()] != 0) { + pendingweapon = weapontype_t.wp_chaingun; + } else if (weaponowned[weapontype_t.wp_shotgun.ordinal()] + && this.ammo[ammotype_t.am_shell.ordinal()] != 0) { + pendingweapon = weapontype_t.wp_shotgun; + } else if (this.ammo[ammotype_t.am_clip.ordinal()] != 0) { + pendingweapon = weapontype_t.wp_pistol; + } else if (weaponowned[weapontype_t.wp_chainsaw.ordinal()]) { + pendingweapon = weapontype_t.wp_chainsaw; + } else if (weaponowned[weapontype_t.wp_missile.ordinal()] + && this.ammo[ammotype_t.am_misl.ordinal()] != 0) { + pendingweapon = weapontype_t.wp_missile; + } else if (weaponowned[weapontype_t.wp_bfg.ordinal()] + && this.ammo[ammotype_t.am_cell.ordinal()] > 40 + && !DOOM.isShareware()) { + pendingweapon = weapontype_t.wp_bfg; + } else { + // If everything fails. + pendingweapon = weapontype_t.wp_fist; + } + + } while (pendingweapon == weapontype_t.wp_nochange); + + // Now set appropriate weapon overlay. + this.SetPsprite( + ps_weapon, + weaponinfo[readyweapon.ordinal()].downstate); + + return false; + } + + /** + * P_DropWeapon + * Player died, so put the weapon away. + */ + public void DropWeapon() { + this.SetPsprite( + ps_weapon, + weaponinfo[readyweapon.ordinal()].downstate); + } + + /** + * P_SetupPsprites + * Called at start of level for each + */ + @SourceCode.Exact + @P_Pspr.C(P_SetupPsprites) + public void SetupPsprites() { + // remove all psprites + for (int i = 0; i < NUMPSPRITES; i++) { + psprites[i].state = null; + } + + // spawn the gun + pendingweapon = readyweapon; + BringUpWeapon(); + } + + /** + * P_PlayerThink + */ + public void PlayerThink(player_t player) { + ticcmd_t cmd; + weapontype_t newweapon; + + // fixme: do this in the cheat code + if (flags(player.cheats, player_t.CF_NOCLIP)) { + player.mo.flags |= MF_NOCLIP; + } else { + player.mo.flags &= ~MF_NOCLIP; + } + + // chain saw run forward + cmd = player.cmd; + if (flags(player.mo.flags, MF_JUSTATTACKED)) { + cmd.angleturn = 0; + cmd.forwardmove = (0xc800 / 512); + cmd.sidemove = 0; + player.mo.flags &= ~MF_JUSTATTACKED; + } + + if (player.playerstate == PST_DEAD) { + player.DeathThink(); + return; + } + + // Move around. + // Reactiontime is used to prevent movement + // for a bit after a teleport. + if (eval(player.mo.reactiontime)) { + player.mo.reactiontime--; + } else { + player.MovePlayer(); + } + + player.CalcHeight(); + + if (eval(player.mo.subsector.sector.special)) { + player.PlayerInSpecialSector(); + } + + // Check for weapon change. + // A special event has no other buttons. + if (flags(cmd.buttons, BT_SPECIAL)) { + cmd.buttons = 0; + } + + if (flags(cmd.buttons, BT_CHANGE)) { + // The actual changing of the weapon is done + // when the weapon psprite can do it + // (read: not in the middle of an attack). + // System.out.println("Weapon change detected, attempting to perform"); + + newweapon = weapontype_t.values()[(cmd.buttons & BT_WEAPONMASK) >> BT_WEAPONSHIFT]; + + // If chainsaw is available, it won't change back to the fist + // unless player also has berserk. + if (newweapon == weapontype_t.wp_fist + && player.weaponowned[weapontype_t.wp_chainsaw.ordinal()] + && !(player.readyweapon == weapontype_t.wp_chainsaw + && eval(player.powers[pw_strength]))) { + newweapon = weapontype_t.wp_chainsaw; + } + + // Will switch between SG and SSG in Doom 2. + if (DOOM.isCommercial() + && newweapon == weapontype_t.wp_shotgun + && player.weaponowned[weapontype_t.wp_supershotgun.ordinal()] + && player.readyweapon != weapontype_t.wp_supershotgun) { + newweapon = weapontype_t.wp_supershotgun; + } + + if (player.weaponowned[newweapon.ordinal()] + && newweapon != player.readyweapon) { + // Do not go to plasma or BFG in shareware, + // even if cheated. + if ((newweapon != weapontype_t.wp_plasma + && newweapon != weapontype_t.wp_bfg) + || !DOOM.isShareware()) { + player.pendingweapon = newweapon; + } + } + } + + // check for use + if (flags(cmd.buttons, BT_USE)) { + if (!player.usedown) { + DOOM.actions.UseLines(player); + player.usedown = true; + } + } else { + player.usedown = false; + } + + // cycle psprites + player.MovePsprites(); + + // Counters, time dependent power ups. + // Strength counts up to diminish fade. + if (eval(player.powers[pw_strength])) { + player.powers[pw_strength]++; + } + + if (eval(player.powers[pw_invulnerability])) { + player.powers[pw_invulnerability]--; + } + + if (eval(player.powers[pw_invisibility])) { + if (!eval(--player.powers[pw_invisibility])) { + player.mo.flags &= ~MF_SHADOW; + } + } + + if (eval(player.powers[pw_infrared])) { + player.powers[pw_infrared]--; + } + + if (eval(player.powers[pw_ironfeet])) { + player.powers[pw_ironfeet]--; + } + + if (eval(player.damagecount)) { + player.damagecount--; + } + + if (eval(player.bonuscount)) { + player.bonuscount--; + } + + // Handling colormaps. + if (eval(player.powers[pw_invulnerability])) { + if (player.powers[pw_invulnerability] > 4 * 32 || flags(player.powers[pw_invulnerability], 8)) { + player.fixedcolormap = Palettes.COLORMAP_INVERSE; + } else { + player.fixedcolormap = Palettes.COLORMAP_FIXED; + } + } else if (eval(player.powers[pw_infrared])) { + if (player.powers[pw_infrared] > 4 * 32 + || flags(player.powers[pw_infrared], 8)) { + // almost full bright + player.fixedcolormap = Palettes.COLORMAP_BULLBRIGHT; + } else { + player.fixedcolormap = Palettes.COLORMAP_FIXED; + } + } else { + player.fixedcolormap = Palettes.COLORMAP_FIXED; + } + } + + /** + * G_PlayerReborn + * Called after a player dies + * almost everything is cleared and initialized + * + * + */ + @G_Game.C(G_PlayerReborn) + public void PlayerReborn() { + final int[] localFrags = new int[MAXPLAYERS]; + final int localKillCount, localItemCount, localSecretCount; + + // System.arraycopy(players[player].frags, 0, frags, 0, frags.length); + // We save the player's frags here... + C2JUtils.memcpy(localFrags, this.frags, localFrags.length); + localKillCount = this.killcount; + localItemCount = this.itemcount; + localSecretCount = this.secretcount; + + //MAES: we need to simulate an erasure, possibly without making + // a new object.memset (p, 0, sizeof(*p)); + //players[player]=(player_t) player_t.nullplayer.clone(); + // players[player]=new player_t(); + this.reset(); + + // And we copy the old frags into the "new" player. + C2JUtils.memcpy(this.frags, localFrags, this.frags.length); + + this.killcount = localKillCount; + this.itemcount = localItemCount; + this.secretcount = localSecretCount; + + usedown = attackdown = true; // don't do anything immediately + playerstate = PST_LIVE; + health[0] = MAXHEALTH; + readyweapon = pendingweapon = weapontype_t.wp_pistol; + weaponowned[weapontype_t.wp_fist.ordinal()] = true; + weaponowned[weapontype_t.wp_pistol.ordinal()] = true; + ammo[ammotype_t.am_clip.ordinal()] = 50; + lookdir = 0; // From Heretic + + System.arraycopy(DoomStatus.maxammo, 0, this.maxammo, 0, NUMAMMO); + } + + /** + * Called by Actions ticker + */ + public void PlayerThink() { + PlayerThink(this); + } + + public String toString() { + sb.setLength(0); + sb.append("player"); + sb.append(" momx "); + sb.append(this.mo.momx); + sb.append(" momy "); + sb.append(this.mo.momy); + sb.append(" x "); + sb.append(this.mo.x); + sb.append(" y "); + sb.append(this.mo.y); + return sb.toString(); + } + + private static StringBuilder sb = new StringBuilder(); + + public void read(DataInputStream f) throws IOException { + + // Careful when loading/saving: + // A player only carries a pointer to a mobj, which is "saved" + // but later discarded at load time, at least in vanilla. In any case, + // it has the size of a 32-bit integer, so make sure you skip it. + // TODO: OK, so vanilla's monsters lost "state" when saved, including non-Doomguy + // infighting. Did they "remember" Doomguy too? + // ANSWER: they didn't. + // The player is special in that it unambigously allows identifying + // its own map object in an absolute way. Once we identify + // at least one (e.g. object #45 is pointer 0x43545345) then, since + // map objects are stored in a nice serialized order. + this.p_mobj = DoomIO.readLEInt(f); // player mobj pointer + + this.playerstate = DoomIO.readLEInt(f); + this.cmd.read(f); + this.viewz = DoomIO.readLEInt(f); + this.viewheight = DoomIO.readLEInt(f); + this.deltaviewheight = DoomIO.readLEInt(f); + this.bob = DoomIO.readLEInt(f); + this.health[0] = DoomIO.readLEInt(f); + this.armorpoints[0] = DoomIO.readLEInt(f); + this.armortype = DoomIO.readLEInt(f); + DoomIO.readIntArray(f, this.powers, ByteOrder.LITTLE_ENDIAN); + DoomIO.readBooleanIntArray(f, this.cards); + this.backpack = DoomIO.readIntBoolean(f); + DoomIO.readIntArray(f, frags, ByteOrder.LITTLE_ENDIAN); + this.readyweapon = weapontype_t.values()[DoomIO.readLEInt(f)]; + this.pendingweapon = weapontype_t.values()[DoomIO.readLEInt(f)]; + DoomIO.readBooleanIntArray(f, this.weaponowned); + DoomIO.readIntArray(f, ammo, ByteOrder.LITTLE_ENDIAN); + DoomIO.readIntArray(f, maxammo, ByteOrder.LITTLE_ENDIAN); + // Read these as "int booleans" + this.attackdown = DoomIO.readIntBoolean(f); + this.usedown = DoomIO.readIntBoolean(f); + this.cheats = DoomIO.readLEInt(f); + this.refire = DoomIO.readLEInt(f); + // For intermission stats. + this.killcount = DoomIO.readLEInt(f); + this.itemcount = DoomIO.readLEInt(f); + this.secretcount = DoomIO.readLEInt(f); + // Hint messages. + f.skipBytes(4); + // For screen flashing (red or bright). + this.damagecount = DoomIO.readLEInt(f); + this.bonuscount = DoomIO.readLEInt(f); + // Who did damage (NULL for floors/ceilings). + // TODO: must be properly denormalized before saving/loading + f.skipBytes(4); // TODO: waste a read for attacker mobj. + // So gun flashes light up areas. + this.extralight = DoomIO.readLEInt(f); + // Current PLAYPAL, ??? + // can be set to REDCOLORMAP for pain, etc. + this.fixedcolormap = DoomIO.readLEInt(f); + this.colormap = DoomIO.readLEInt(f); + // PSPDEF _is_ readable. + for (pspdef_t p : this.psprites) { + p.read(f); + } + this.didsecret = DoomIO.readIntBoolean(f); + // Total size should be 280 bytes. + } + + public void write(DataOutputStream f) throws IOException { + + // It's much more convenient to pre-buffer, since + // we'll be writing all Little Endian stuff. + ByteBuffer b = ByteBuffer.allocate(280); + this.pack(b); + // Total size should be 280 bytes. + // Write everything nicely and at once. + f.write(b.array()); + } + + // Used to disambiguate between objects + public int p_mobj; + + @Override + public void pack(ByteBuffer buf) + throws IOException { + + ByteOrder bo = ByteOrder.LITTLE_ENDIAN; + buf.order(bo); + // The player is special in that it unambiguously allows identifying + // its own map object in an absolute way. Once we identify + // at least one (e.g. object #45 is pointer 0x43545345) then, since + // map objects are stored in a nice serialized order by using + // their next/prev pointers, you can reconstruct their + // relationships a posteriori. + // Store our own hashcode or "pointer" if you wish. + buf.putInt(pointer(mo)); + buf.putInt(playerstate); + cmd.pack(buf); + buf.putInt(viewz); + buf.putInt(viewheight); + buf.putInt(deltaviewheight); + buf.putInt(bob); + buf.putInt(health[0]); + buf.putInt(armorpoints[0]); + buf.putInt(armortype); + DoomBuffer.putIntArray(buf, this.powers, this.powers.length, bo); + DoomBuffer.putBooleanIntArray(buf, this.cards, this.cards.length, bo); + DoomBuffer.putBooleanInt(buf, backpack, bo); + DoomBuffer.putIntArray(buf, this.frags, this.frags.length, bo); + buf.putInt(readyweapon.ordinal()); + buf.putInt(pendingweapon.ordinal()); + DoomBuffer.putBooleanIntArray(buf, this.weaponowned, this.weaponowned.length, bo); + DoomBuffer.putIntArray(buf, this.ammo, this.ammo.length, bo); + DoomBuffer.putIntArray(buf, this.maxammo, this.maxammo.length, bo); + // Read these as "int booleans" + DoomBuffer.putBooleanInt(buf, attackdown, bo); + DoomBuffer.putBooleanInt(buf, usedown, bo); + buf.putInt(cheats); + buf.putInt(refire); + // For intermission stats. + buf.putInt(this.killcount); + buf.putInt(this.itemcount); + buf.putInt(this.secretcount); + // Hint messages. + buf.putInt(0); + // For screen flashing (red or bright). + buf.putInt(this.damagecount); + buf.putInt(this.bonuscount); + // Who did damage (NULL for floors/ceilings). + // TODO: must be properly denormalized before saving/loading + buf.putInt(pointer(attacker)); + // So gun flashes light up areas. + buf.putInt(this.extralight); + // Current PLAYPAL, ??? + // can be set to REDCOLORMAP for pain, etc. + + /** + * Here the fixed color map of player is written when player_t object is packed. + * Make sure not to write any preshifted value there! Do not scale player_r.fixedcolormap, + * scale dependent array accesses. + * - Good Sign 2017/04/15 + */ + buf.putInt(this.fixedcolormap); + buf.putInt(this.colormap); + // PSPDEF _is_ readable. + for (pspdef_t p : this.psprites) { + p.pack(buf); + } + buf.putInt(this.didsecret ? 1 : 0); + + } +} \ No newline at end of file diff --git a/src/doom/playerstate_t.java b/src/doom/playerstate_t.java index 4bb2fc7..a7abf4b 100644 --- a/src/doom/playerstate_t.java +++ b/src/doom/playerstate_t.java @@ -10,4 +10,4 @@ public enum playerstate_t { // Ready to restart/respawn??? PST_REBORN -}; +}; \ No newline at end of file diff --git a/src/doom/th_class.java b/src/doom/th_class.java index 177a886..daab682 100644 --- a/src/doom/th_class.java +++ b/src/doom/th_class.java @@ -12,4 +12,4 @@ public enum th_class { th_all; public static final int NUMTHCLASS = th_class.values().length; -} +} \ No newline at end of file diff --git a/src/doom/thinker_t.java b/src/doom/thinker_t.java index ba669f7..35815e0 100644 --- a/src/doom/thinker_t.java +++ b/src/doom/thinker_t.java @@ -1,77 +1,77 @@ -package doom; - -import java.io.DataInputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import p.ActiveStates; -import p.ThinkerStates; -import static utils.C2JUtils.pointer; -import w.CacheableDoomObject; -import w.IPackableDoomObject; -import w.IReadableDoomObject; - -public class thinker_t implements CacheableDoomObject, IReadableDoomObject, IPackableDoomObject { - - public thinker_t prev; - public thinker_t next; - public ThinkerStates thinkerFunction = ActiveStates.NOP; - - /** - * killough's code for thinkers seems to be totally broken in M.D, - * so commented it out and will not probably restore, but may invent - * something new in future - * - Good Sign 2017/05/1 - * - * killough 8/29/98: we maintain thinkers in several equivalence classes, - * according to various criteria, so as to allow quicker searches. - */ - /** - * Next, previous thinkers in same class - */ - //public thinker_t cnext, cprev; - /** - * extra fields, to use when archiving/unarchiving for - * identification. Also in blocklinks, etc. - */ - public int id, previd, nextid, functionid; - - @Override - public void read(DataInputStream f) - throws IOException { - readbuffer.position(0); - readbuffer.order(ByteOrder.LITTLE_ENDIAN); - f.read(readbuffer.array()); - unpack(readbuffer); - } - - /** - * This adds 12 bytes - */ - @Override - public void pack(ByteBuffer b) - throws IOException { - // It's possible to reconstruct even by hashcodes. - // As for the function, that should be implied by the mobj_t type. - b.order(ByteOrder.LITTLE_ENDIAN); - b.putInt(pointer(prev)); - b.putInt(pointer(next)); - b.putInt(pointer(thinkerFunction.ordinal())); - //System.out.printf("Packed thinker %d %d %d\n",pointer(prev),pointer(next),pointer(function)); - } - - @Override - public void unpack(ByteBuffer b) - throws IOException { - // We are supposed to archive pointers to other thinkers, - // but they are rather useless once on disk. - b.order(ByteOrder.LITTLE_ENDIAN); - previd = b.getInt(); - nextid = b.getInt(); - functionid = b.getInt(); - //System.out.printf("Unpacked thinker %d %d %d\n",pointer(previd),pointer(nextid),pointer(functionid)); - } - - private static final ByteBuffer readbuffer = ByteBuffer.allocate(12); - -} +package doom; + +import java.io.DataInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import p.ActiveStates; +import p.ThinkerStates; +import static utils.C2JUtils.pointer; +import w.CacheableDoomObject; +import w.IPackableDoomObject; +import w.IReadableDoomObject; + +public class thinker_t implements CacheableDoomObject, IReadableDoomObject, IPackableDoomObject { + + public thinker_t prev; + public thinker_t next; + public ThinkerStates thinkerFunction = ActiveStates.NOP; + + /** + * killough's code for thinkers seems to be totally broken in M.D, + * so commented it out and will not probably restore, but may invent + * something new in future + * - Good Sign 2017/05/1 + * + * killough 8/29/98: we maintain thinkers in several equivalence classes, + * according to various criteria, so as to allow quicker searches. + */ + /** + * Next, previous thinkers in same class + */ + //public thinker_t cnext, cprev; + /** + * extra fields, to use when archiving/unarchiving for + * identification. Also in blocklinks, etc. + */ + public int id, previd, nextid, functionid; + + @Override + public void read(DataInputStream f) + throws IOException { + readbuffer.position(0); + readbuffer.order(ByteOrder.LITTLE_ENDIAN); + f.read(readbuffer.array()); + unpack(readbuffer); + } + + /** + * This adds 12 bytes + */ + @Override + public void pack(ByteBuffer b) + throws IOException { + // It's possible to reconstruct even by hashcodes. + // As for the function, that should be implied by the mobj_t type. + b.order(ByteOrder.LITTLE_ENDIAN); + b.putInt(pointer(prev)); + b.putInt(pointer(next)); + b.putInt(pointer(thinkerFunction.ordinal())); + //System.out.printf("Packed thinker %d %d %d\n",pointer(prev),pointer(next),pointer(function)); + } + + @Override + public void unpack(ByteBuffer b) + throws IOException { + // We are supposed to archive pointers to other thinkers, + // but they are rather useless once on disk. + b.order(ByteOrder.LITTLE_ENDIAN); + previd = b.getInt(); + nextid = b.getInt(); + functionid = b.getInt(); + //System.out.printf("Unpacked thinker %d %d %d\n",pointer(previd),pointer(nextid),pointer(functionid)); + } + + private static final ByteBuffer readbuffer = ByteBuffer.allocate(12); + +} \ No newline at end of file diff --git a/src/doom/ticcmd_t.java b/src/doom/ticcmd_t.java index 347f45d..cf1cb00 100644 --- a/src/doom/ticcmd_t.java +++ b/src/doom/ticcmd_t.java @@ -29,7 +29,7 @@ public ticcmd_t() { /** checks for net game */ public short consistancy; - /** MAES: these are unsigned bytes :-( + /** MAES: these are unsigned bytes :-( * However over networks, if we wish for vanilla compatibility, * these must be reduced to 8-bit "chars" * */ @@ -168,7 +168,7 @@ public void unpack(ByteBuffer f) } /** Ditto, we only pack some of the fields. - * + * * @param f * @throws IOException */ @@ -187,4 +187,4 @@ public void pack(ByteBuffer f) private static ByteBuffer iobuffer = ByteBuffer.allocate(8); -}; +}; \ No newline at end of file diff --git a/src/doom/wbplayerstruct_t.java b/src/doom/wbplayerstruct_t.java index 0df29d2..39bf433 100644 --- a/src/doom/wbplayerstruct_t.java +++ b/src/doom/wbplayerstruct_t.java @@ -45,4 +45,4 @@ public wbplayerstruct_t clone() { } -} +} \ No newline at end of file diff --git a/src/doom/wbstartstruct_t.java b/src/doom/wbstartstruct_t.java index 70ac771..8ecab4f 100644 --- a/src/doom/wbstartstruct_t.java +++ b/src/doom/wbstartstruct_t.java @@ -62,4 +62,4 @@ public wbstartstruct_t clone() { } -} +} \ No newline at end of file diff --git a/src/doom/weaponinfo_t.java b/src/doom/weaponinfo_t.java index 709bde4..0975b0b 100644 --- a/src/doom/weaponinfo_t.java +++ b/src/doom/weaponinfo_t.java @@ -17,7 +17,7 @@ // public class weaponinfo_t { - /* + /* public weaponinfo_t(ammotype_t ammo, int upstate, int downstate, int readystate, int atkstate, int flashstate) { super(); @@ -55,4 +55,4 @@ public weaponinfo_t(ammotype_t ammo, statenum_t upstate, public int atkstate; public int flashstate; */ -} +} \ No newline at end of file diff --git a/src/doom/weapontype_t.java b/src/doom/weapontype_t.java index 8a53c24..983f65f 100644 --- a/src/doom/weapontype_t.java +++ b/src/doom/weapontype_t.java @@ -22,4 +22,4 @@ public String toString() { return this.name(); } -} +} \ No newline at end of file diff --git a/src/f/AbstractEndLevel.java b/src/f/AbstractEndLevel.java index d15ce7c..1c6fb17 100644 --- a/src/f/AbstractEndLevel.java +++ b/src/f/AbstractEndLevel.java @@ -22,7 +22,7 @@ public abstract class AbstractEndLevel { public static final int DM_VICTIMSX = 5; public static final int DM_VICTIMSY = 50; - // static point_t lnodes[NUMEPISODES][NUMMAPS] + // static point_t lnodes[NUMEPISODES][NUMMAPS] final static public point_t[][] lnodes = { // Episode 0 World Map @@ -123,4 +123,4 @@ public abstract class AbstractEndLevel { epsd2animinfo }; -} +} \ No newline at end of file diff --git a/src/f/EndLevel.java b/src/f/EndLevel.java index 109b49a..0c937f8 100644 --- a/src/f/EndLevel.java +++ b/src/f/EndLevel.java @@ -361,7 +361,7 @@ public boolean Responder(event_t ev) { protected void drawLF() { int y = WI_TITLEY; - // draw + // draw DOOM.graphicSystem.DrawPatchScaled(FG, lnames[wbs.last], DOOM.vs, (320 - lnames[wbs.last].width) / 2, y); // draw "Finished!" @@ -382,7 +382,7 @@ protected void drawEL() { // HACK: if lnames[wbs.next] DOES have a defined nonzero topoffset, use it. // implicitly in DrawScaledPatch, and trump the normal behavior. // FIXME: this is only useful in a handful of prBoom+ maps, which use - // a modified endlevel screen. The reason it works there is the behavior of the + // a modified endlevel screen. The reason it works there is the behavior of the // unified patch drawing function, which is approximated with this hack. if (lnames[wbs.next].topoffset == 0) { y += (5 * lnames[wbs.next].height) / 4; @@ -1593,7 +1593,7 @@ protected void loadData() { // "secret" sp_secret = DOOM.wadLoader.CacheLumpName("WISCRT2", PU_STATIC, patch_t.class); - // Yuck. + // Yuck. if (DOOM.language == Language_t.french) { // "items" if (DOOM.netgame && !DOOM.deathmatch) { @@ -1660,7 +1660,7 @@ public void WI_unloadData() for (i=0 ; i<10 ; i++) W.UnlockLumpNum(num[i], PU_CACHE); - + if (gamemode == commercial) { for (i=0 ; i= min && what <= max); } -} +} \ No newline at end of file diff --git a/src/f/Finale.java b/src/f/Finale.java index cc84aae..f579d23 100644 --- a/src/f/Finale.java +++ b/src/f/Finale.java @@ -51,7 +51,7 @@ // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // DESCRIPTION: // Game completion, final screen animation. // @@ -559,7 +559,7 @@ public void CastPrint(String text) { /** * F_CastDrawer - * + * * @throws IOException */ // public void V_DrawPatchFlipped (int x, int y, int scrn, patch_t patch); @@ -699,4 +699,4 @@ public Finale(DoomMain DOOM) { } } -// /$Log +// /$Log \ No newline at end of file diff --git a/src/f/Wiper.java b/src/f/Wiper.java index f44f18e..c246055 100644 --- a/src/f/Wiper.java +++ b/src/f/Wiper.java @@ -56,4 +56,4 @@ private Wipe(Wipers.WipeFunc initFunc, Wipers.WipeFunc doFunc, Wipers.WipeFunc e this.exitFunc = exitFunc; } } -} +} \ No newline at end of file diff --git a/src/f/anim_t.java b/src/f/anim_t.java index e324753..7565124 100644 --- a/src/f/anim_t.java +++ b/src/f/anim_t.java @@ -82,4 +82,4 @@ public anim_t(animenum_t type, int period, int nanims, point_t loc, int data1 // used by RANDOM and LEVEL when animating public int state; -} +} \ No newline at end of file diff --git a/src/f/castinfo_t.java b/src/f/castinfo_t.java index a7c0d58..169cfa9 100644 --- a/src/f/castinfo_t.java +++ b/src/f/castinfo_t.java @@ -19,4 +19,4 @@ public castinfo_t(String name, mobjtype_t type) { this.type = type; } -} +} \ No newline at end of file diff --git a/src/f/point_t.java b/src/f/point_t.java index 53b98ea..497357f 100644 --- a/src/f/point_t.java +++ b/src/f/point_t.java @@ -9,4 +9,4 @@ public point_t(int x, int y) { public int x; public int y; -} +} \ No newline at end of file diff --git a/src/g/DoomGameInterface.java b/src/g/DoomGameInterface.java index ae7d801..3dcf2b5 100644 --- a/src/g/DoomGameInterface.java +++ b/src/g/DoomGameInterface.java @@ -23,7 +23,7 @@ // // DESCRIPTION: // Duh. -// +// //----------------------------------------------------------------------------- public interface DoomGameInterface { @@ -81,4 +81,4 @@ public interface DoomGameInterface { public void setPaused(boolean on); -} +} \ No newline at end of file diff --git a/src/g/DoomSaveGame.java b/src/g/DoomSaveGame.java index 0d03ece..62f5bda 100644 --- a/src/g/DoomSaveGame.java +++ b/src/g/DoomSaveGame.java @@ -18,10 +18,10 @@ import w.IWritableDoomObject; /** represents the header of Doom savegame, so that basic info can be checked quickly. - * + * * To load the whole game and check if there are final mistakes, you must go through it all. * Savegames need to be aware of ALL status and context, so maybe they should be inner classes? - * + * */ public class DoomSaveGame implements CacheableDoomObject, IReadableDoomObject, IWritableDoomObject { @@ -59,9 +59,9 @@ public void unpack(ByteBuffer buf) throws IOException { playeringame[i] = buf.get() != 0; } - // load a base level (this doesn't advance the pointer?) - //G_InitNew (gameskill, gameepisode, gamemap); - // get the times + // load a base level (this doesn't advance the pointer?) + //G_InitNew (gameskill, gameepisode, gamemap); + // get the times int a = C2JUtils.toUnsignedByte(buf.get()); int b = C2JUtils.toUnsignedByte(buf.get()); int c = C2JUtils.toUnsignedByte(buf.get()); @@ -89,9 +89,9 @@ public void write(DataOutputStream f) throws IOException { f.writeBoolean(playeringame[i]); } - // load a base level (this doesn't advance the pointer?) - //G_InitNew (gameskill, gameepisode, gamemap); - // get the times + // load a base level (this doesn't advance the pointer?) + //G_InitNew (gameskill, gameepisode, gamemap); + // get the times byte a = (byte) (0x0000FF & (leveltime >>> 16)); byte b = (byte) (0x00FF & (leveltime >>> 8)); byte c = (byte) (0x00FF & (leveltime)); @@ -124,9 +124,9 @@ public void read(DataInputStream f) throws IOException { playeringame[i] = f.readBoolean(); } - // load a base level (this doesn't advance the pointer?) - //G_InitNew (gameskill, gameepisode, gamemap); - // get the times + // load a base level (this doesn't advance the pointer?) + //G_InitNew (gameskill, gameepisode, gamemap); + // get the times int a = f.readUnsignedByte(); int b = f.readUnsignedByte(); int c = f.readUnsignedByte(); @@ -164,4 +164,4 @@ public void fromStat(DoomStatus DS) { } -} +} \ No newline at end of file diff --git a/src/g/Overflow.java b/src/g/Overflow.java index 452425e..12f526b 100644 --- a/src/g/Overflow.java +++ b/src/g/Overflow.java @@ -103,11 +103,11 @@ static void ShowOverflowWarning(int overflow, int fatal, String ... params) "Too big or not supported %s overflow has been detected. " "Desync or crash can occur soon " "or during playback with the vanilla engine in case you're recording demo.%s%s"; - - static const char str2[] = + + static const char str2[] = "%s overflow has been detected.%s%s"; - static const char str3[] = + static const char str3[] = "%s overflow has been detected. " "The option responsible for emulation of this overflow is switched off " "hence desync or crash can occur soon " @@ -116,11 +116,11 @@ static void ShowOverflowWarning(int overflow, int fatal, String ... params) overflows[overflow].promted = true; sprintf(buffer, - (fatal ? str1 : (EMULATE(overflow) ? str2 : str3)), + (fatal ? str1 : (EMULATE(overflow) ? str2 : str3)), name[overflow], "\nYou can change PrBoom behaviour for this overflow through in-game menu.", params); - + va_start(argptr, params); I_vWarning(buffer, argptr); va_end(argptr); @@ -196,7 +196,7 @@ void InterceptsOverrun(int num_intercepts, intercept_t intercept) // the values from the intercept structure. // // Note: the .d.{thing,line} member should really have its - // address translated into the correct address value for + // address translated into the correct address value for // Vanilla Doom. InterceptsMemoryOverrun(location, intercept.frac); @@ -264,9 +264,9 @@ static void SpechitOverrun(spechit_overrun_param_t params) line_t[] spechit = params.spechit; ShowOverflowWarning(OVERFLOW_SPECHIT, - numspechit > - (compatibility_level == dosdoom_compatibility || - compatibility_level == tasdoom_compatibility ? 10 : 14), + numspechit > + (compatibility_level == dosdoom_compatibility || + compatibility_level == tasdoom_compatibility ? 10 : 14), "\n\nThe list of LineID leading to overrun:\n%d, %d, %d, %d, %d, %d, %d, %d, %d.", spechit[0].iLineID, spechit[1].iLineID, spechit[2].iLineID, spechit[3].iLineID, spechit[4].iLineID, spechit[5].iLineID, @@ -289,7 +289,7 @@ static void SpechitOverrun(spechit_overrun_param_t params) // p = M_CheckParm("-spechit"); - + if (p > 0) { //baseaddr = atoi(myargv[p+1]); @@ -307,7 +307,7 @@ static void SpechitOverrun(spechit_overrun_param_t params) if (compatibility_level == dosdoom_compatibility || compatibility_level == tasdoom_compatibility) { - // There are no more desyncs in the following dosdoom demos: + // There are no more desyncs in the following dosdoom demos: // flsofdth.wad\fod3uv.lmp - http://www.doomworld.com/sda/flsofdth.htm // hr.wad\hf181430.lmp - http://www.doomworld.com/tas/hf181430.zip // hr.wad\hr181329.lmp - http://www.doomworld.com/tas/hr181329.zip @@ -315,13 +315,13 @@ static void SpechitOverrun(spechit_overrun_param_t params) switch(numspechit) { - case 9: + case 9: *(params.tmfloorz) = addr; break; case 10: *(params.tmceilingz) = addr; break; - + default: fprintf(stderr, "SpechitOverrun: Warning: unable to emulate" "an overrun where numspechit=%i\n", @@ -333,7 +333,7 @@ static void SpechitOverrun(spechit_overrun_param_t params) { switch(numspechit) { - case 9: + case 9: case 10: case 11: case 12: @@ -363,7 +363,7 @@ static void SpechitOverrun(spechit_overrun_param_t params) // padding the reject table if it is too short // totallines must be the number returned by P_GroupLines() // an underflow will be padded with zeroes, or a doom.exe z_zone header - // + // // e6y // reject overrun emulation code // It's emulated successfully if the size of overflow no more than 16 bytes. @@ -388,7 +388,7 @@ static void SpechitOverrun(spechit_overrun_param_t params) // e6y // PrBoom 2.2.5 and 2.2.6 padded a short REJECT with 0xff - // This command line switch is needed for all potential demos + // This command line switch is needed for all potential demos // recorded with these versions of PrBoom on maps with too short REJECT // I don't think there are any demos that will need it but yes that seems sensible // pad = prboom_comp[PC_REJECT_PAD_WITH_FF].state ? 0xff : 0; @@ -525,7 +525,7 @@ int DonutOverrun(fixed_t *pfloorheight, short *pfloorpic) { GetMemoryValue(0, pfloorheight, 4); GetMemoryValue(8, pfloorpic, 2); - + // bounds-check floorpic if ((*pfloorpic) <= 0 || (*pfloorpic) >= numflats) { @@ -564,7 +564,7 @@ int MissedBackSideOverrun(sector_t *sector, seg_t *seg) { GetMemoryValue(0, §or.floorheight, 4); GetMemoryValue(4, §or.ceilingheight, 4); - + return true; } } @@ -572,4 +572,4 @@ int MissedBackSideOverrun(sector_t *sector, seg_t *seg) return false; }*/ -} +} \ No newline at end of file diff --git a/src/g/Signals.java b/src/g/Signals.java index f5a8fb1..f90bd5e 100644 --- a/src/g/Signals.java +++ b/src/g/Signals.java @@ -227,4 +227,4 @@ public enum ScanCode { map[virtualKey] = (byte) ordinal(); } } -} +} \ No newline at end of file diff --git a/src/hu/HU.java b/src/hu/HU.java index fc0dce0..a1c0369 100644 --- a/src/hu/HU.java +++ b/src/hu/HU.java @@ -387,7 +387,7 @@ public HU(final DoomMain DOOM) { /** * Loads a bunch of STCFNx fonts from WAD, and sets some of the remaining * constants. - * + * * @throws Exception */ @Override @@ -1144,7 +1144,7 @@ public boolean addCharToTextLine(char ch) { /** * MAES: This is much better than cluttering up the syntax everytime a * STRING must be added. - * + * * @param s * @return */ @@ -1226,7 +1226,7 @@ void drawTextLine(boolean drawcursor) { * Only erases when NOT in automap and the screen is reduced, * and the text must either need updating or refreshing * (because of a recent change back from the automap) - * + * * Rewritten by Good Sign 2017/04/06 */ @SuppressWarnings("unchecked") @@ -1338,4 +1338,4 @@ public patch_t[] getHUFonts() { //FINALLY fixed a stupid bug that broke HU messages. // //Revision 1.21 2011/05/18 16:52:40 velktron -//Changed to DoomStatus +//Changed to DoomStatus \ No newline at end of file diff --git a/src/hu/IHeadsUp.java b/src/hu/IHeadsUp.java index 458d271..5667b6b 100644 --- a/src/hu/IHeadsUp.java +++ b/src/hu/IHeadsUp.java @@ -28,4 +28,4 @@ public interface IHeadsUp { void Stop(); -} +} \ No newline at end of file diff --git a/src/i/DiskDrawer.java b/src/i/DiskDrawer.java index bc468f2..eb4129d 100644 --- a/src/i/DiskDrawer.java +++ b/src/i/DiskDrawer.java @@ -51,4 +51,4 @@ public boolean justDoneReading() { return timer == 0; } -} +} \ No newline at end of file diff --git a/src/i/DoomEventInterface.java b/src/i/DoomEventInterface.java index 99ad8f6..26fd774 100644 --- a/src/i/DoomEventInterface.java +++ b/src/i/DoomEventInterface.java @@ -1,19 +1,19 @@ package i; /** Interface for Doom-to-System event handling methods - * + * * @author Velktron * */ public interface DoomEventInterface { - /** The implementation is windowing subsystem-specific + /** The implementation is windowing subsystem-specific * e.g. DOS, XServer, AWT or Swing or whatever. - * + * */ public void GetEvent(); public boolean mouseMoving(); public void setMouseMoving(boolean mousMoving); -} +} \ No newline at end of file diff --git a/src/i/DoomSoundInterface.java b/src/i/DoomSoundInterface.java index 92313ef..c23c52f 100644 --- a/src/i/DoomSoundInterface.java +++ b/src/i/DoomSoundInterface.java @@ -109,4 +109,4 @@ public interface DoomSoundInterface { // See above (register), then think backwards public void I_UnRegisterSong(int handle); -} +} \ No newline at end of file diff --git a/src/i/DoomSystem.java b/src/i/DoomSystem.java index dd01dd1..87622f3 100644 --- a/src/i/DoomSystem.java +++ b/src/i/DoomSystem.java @@ -256,10 +256,10 @@ public void Error(String error) { System.exit(-1); } - // This particular implementation will generate a popup box.// + // This particular implementation will generate a popup box.// @Override public boolean GenerateAlert(String title, String cause, boolean showCancelButton) { MsgBox alert = new MsgBox(null, title, cause, showCancelButton); return alert.isOk(); } -} +} \ No newline at end of file diff --git a/src/i/DummySystem.java b/src/i/DummySystem.java index a5a7bcf..26eb5b5 100644 --- a/src/i/DummySystem.java +++ b/src/i/DummySystem.java @@ -82,4 +82,4 @@ public boolean GenerateAlert(String title, String cause, boolean showCancelButto return false; } -} +} \ No newline at end of file diff --git a/src/i/IDiskDrawer.java b/src/i/IDiskDrawer.java index 87027ad..cee021b 100644 --- a/src/i/IDiskDrawer.java +++ b/src/i/IDiskDrawer.java @@ -29,4 +29,4 @@ public interface IDiskDrawer extends IDrawer { */ boolean justDoneReading(); -} +} \ No newline at end of file diff --git a/src/i/IDoomSystem.java b/src/i/IDoomSystem.java index 7a2893a..730f229 100644 --- a/src/i/IDoomSystem.java +++ b/src/i/IDoomSystem.java @@ -29,13 +29,13 @@ public interface IDoomSystem { void Init(); /** Generate a blocking alert with the intention of continuing or aborting - * a certain game-altering action. E.g. loading PWADs, or upon critical - * level loading failures. This can be either a popup panel or console + * a certain game-altering action. E.g. loading PWADs, or upon critical + * level loading failures. This can be either a popup panel or console * message. - * + * * @param cause Provide a clear string explaining why the alert was generated * @return true if we should continue, false if an alternate action should be taken. */ boolean GenerateAlert(String title, String cause, boolean showCancelButton); -} +} \ No newline at end of file diff --git a/src/i/IDrawer.java b/src/i/IDrawer.java index 1034277..cb98f6e 100644 --- a/src/i/IDrawer.java +++ b/src/i/IDrawer.java @@ -4,4 +4,4 @@ public interface IDrawer { public void Drawer(); -} +} \ No newline at end of file diff --git a/src/i/Strings.java b/src/i/Strings.java index d38775d..50de530 100644 --- a/src/i/Strings.java +++ b/src/i/Strings.java @@ -38,4 +38,4 @@ public final class Strings { + "===========================================================================
" + ""; -} +} \ No newline at end of file diff --git a/src/i/SystemSoundInterface.java b/src/i/SystemSoundInterface.java index fee94f1..d74dc3b 100644 --- a/src/i/SystemSoundInterface.java +++ b/src/i/SystemSoundInterface.java @@ -109,4 +109,4 @@ public interface SystemSoundInterface { // See above (register), then think backwards public void UnRegisterSong(int handle); -} +} \ No newline at end of file diff --git a/src/i/system.java b/src/i/system.java index caa733d..8315367 100644 --- a/src/i/system.java +++ b/src/i/system.java @@ -109,7 +109,7 @@ int I_GetTime () struct timezone tzp; int newtics; static int basetime=0; - + gettimeofday(&tp, &tzp); if (!basetime) basetime = tp.tv_sec; @@ -143,12 +143,12 @@ void I_Quit (void) void I_WaitVBL(int count) { #ifdef SGI - sginap(1); + sginap(1); #else #ifdef SUN sleep(0); #else - usleep (count * (1000000/70) ); + usleep (count * (1000000/70) ); #endif #endif } @@ -164,7 +164,7 @@ void I_EndRead(void) byte* I_AllocLow(int length) { byte* mem; - + mem = (byte *)malloc (length); memset (mem,0,length); return mem; @@ -192,4 +192,4 @@ public static void Error(String error, Object... args) { //I_ShutdownGraphics(); System.exit(-1); } -} +} \ No newline at end of file diff --git a/src/m/AbstractDoomMenu.java b/src/m/AbstractDoomMenu.java index 8140786..b4921b7 100644 --- a/src/m/AbstractDoomMenu.java +++ b/src/m/AbstractDoomMenu.java @@ -10,4 +10,4 @@ public abstract class AbstractDoomMenu implements IDoomMenu { public AbstractDoomMenu(DoomMain DOOM) { this.DOOM = DOOM; } -} +} \ No newline at end of file diff --git a/src/m/BBox.java b/src/m/BBox.java index 951676a..7b0e4e5 100644 --- a/src/m/BBox.java +++ b/src/m/BBox.java @@ -6,7 +6,7 @@ /** A fucked-up bounding box class. * Fucked-up because it's supposed to wrap fixed_t's.... no fucking way I'm doing * this with fixed_t objects. - * + * * @author admin * */ @@ -69,7 +69,7 @@ public void AddToBox(fixed_t x, fixed_t y) { /** * MAES: Keeping with C's type (in)consistency, we also allow to input ints * -_- - * + * * @param x * @param y */ @@ -90,7 +90,7 @@ public void AddToBox(int x, int y) { /** * R_AddPointToBox Expand a given bbox so that it encloses a given point. - * + * * @param x * @param y * @param box @@ -112,7 +112,7 @@ public static void AddPointToBox(int x, int y, fixed_t[] box) { /** * R_AddPointToBox Expand this bbox so that it encloses a given point. - * + * * @param x * @param y * @param box @@ -162,4 +162,4 @@ public static void AddToBox(int[] box, int x, int y) { } } -} +} \ No newline at end of file diff --git a/src/m/DelegateRandom.java b/src/m/DelegateRandom.java index daad53c..315232c 100644 --- a/src/m/DelegateRandom.java +++ b/src/m/DelegateRandom.java @@ -1,122 +1,122 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package m; - -import data.Defines; -import data.mobjtype_t; -import doom.SourceCode.M_Random; -import static doom.SourceCode.M_Random.M_ClearRandom; -import static doom.SourceCode.M_Random.M_Random; -import static doom.SourceCode.M_Random.P_Random; -import java.util.logging.Level; -import java.util.logging.Logger; -import mochadoom.Loggers; -import p.ActiveStates; -import utils.C2JUtils; - -/** - * A "IRandom" that delegates its function to one of the two available IRandom implementations - * By default, MochaDoom now uses JavaRandom, however it switches - * to DoomRandom (supposedly Vanilla DOOM v1.9 compatible, tested only in Chocolate DOOM) - * whenever you start recording or playing demo. When you start then new game, MochaDoom restores new JavaRandom. - * - * However, if you start MochaDoom with -javarandom command line argument and -record demo, - * then MochaDoom will record the demo using JavaRandom. Such demo will be neither compatible - * with Vanilla DOOM v1.9, nor with another source port. - * - * Only MochaDoom can play JavaRandom demos. - * - Good Sign 2017/04/10 - * - * @author Good Sign - */ -public class DelegateRandom implements IRandom { - - private static final Logger LOGGER = Loggers.getLogger(DelegateRandom.class.getName()); - - private IRandom random; - private IRandom altRandom; - - public DelegateRandom() { - this.random = new JavaRandom(); - } - - public void requireRandom(final int version) { - if (C2JUtils.flags(version, Defines.JAVARANDOM_MASK) && this.random instanceof DoomRandom) { - switchRandom(true); - } else if (!C2JUtils.flags(version, Defines.JAVARANDOM_MASK) && !(this.random instanceof DoomRandom)) { - switchRandom(false); - } - } - - private void switchRandom(boolean which) { - IRandom arandom = altRandom; - if (arandom != null && ((!which && arandom instanceof DoomRandom) || (which && arandom instanceof JavaRandom))) { - this.altRandom = random; - this.random = arandom; - LOGGER.log(Level.INFO, String.format("M_Random: Switching to %s", random.getClass().getSimpleName())); - } else { - this.altRandom = random; - this.random = which ? new JavaRandom() : new DoomRandom(); - LOGGER.log(Level.INFO, String.format("M_Random: Switching to %s (new instance)", random.getClass().getSimpleName())); - } - //random.ClearRandom(); - } - - @Override - @M_Random.C(P_Random) - public int P_Random() { - return random.P_Random(); - } - - @Override - @M_Random.C(M_Random) - public int M_Random() { - return random.M_Random(); - } - - @Override - @M_Random.C(M_ClearRandom) - public void ClearRandom() { - random.ClearRandom(); - } - - @Override - public int getIndex() { - return random.getIndex(); - } - - @Override - public int P_Random(int caller) { - return random.P_Random(caller); - } - - @Override - public int P_Random(String message) { - return random.P_Random(message); - } - - @Override - public int P_Random(ActiveStates caller, int sequence) { - return random.P_Random(caller, sequence); - } - - @Override - public int P_Random(ActiveStates caller, mobjtype_t type, int sequence) { - return random.P_Random(caller, type, sequence); - } - -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package m; + +import data.Defines; +import data.mobjtype_t; +import doom.SourceCode.M_Random; +import static doom.SourceCode.M_Random.M_ClearRandom; +import static doom.SourceCode.M_Random.M_Random; +import static doom.SourceCode.M_Random.P_Random; +import java.util.logging.Level; +import java.util.logging.Logger; +import mochadoom.Loggers; +import p.ActiveStates; +import utils.C2JUtils; + +/** + * A "IRandom" that delegates its function to one of the two available IRandom implementations + * By default, MochaDoom now uses JavaRandom, however it switches + * to DoomRandom (supposedly Vanilla DOOM v1.9 compatible, tested only in Chocolate DOOM) + * whenever you start recording or playing demo. When you start then new game, MochaDoom restores new JavaRandom. + * + * However, if you start MochaDoom with -javarandom command line argument and -record demo, + * then MochaDoom will record the demo using JavaRandom. Such demo will be neither compatible + * with Vanilla DOOM v1.9, nor with another source port. + * + * Only MochaDoom can play JavaRandom demos. + * - Good Sign 2017/04/10 + * + * @author Good Sign + */ +public class DelegateRandom implements IRandom { + + private static final Logger LOGGER = Loggers.getLogger(DelegateRandom.class.getName()); + + private IRandom random; + private IRandom altRandom; + + public DelegateRandom() { + this.random = new JavaRandom(); + } + + public void requireRandom(final int version) { + if (C2JUtils.flags(version, Defines.JAVARANDOM_MASK) && this.random instanceof DoomRandom) { + switchRandom(true); + } else if (!C2JUtils.flags(version, Defines.JAVARANDOM_MASK) && !(this.random instanceof DoomRandom)) { + switchRandom(false); + } + } + + private void switchRandom(boolean which) { + IRandom arandom = altRandom; + if (arandom != null && ((!which && arandom instanceof DoomRandom) || (which && arandom instanceof JavaRandom))) { + this.altRandom = random; + this.random = arandom; + LOGGER.log(Level.INFO, String.format("M_Random: Switching to %s", random.getClass().getSimpleName())); + } else { + this.altRandom = random; + this.random = which ? new JavaRandom() : new DoomRandom(); + LOGGER.log(Level.INFO, String.format("M_Random: Switching to %s (new instance)", random.getClass().getSimpleName())); + } + //random.ClearRandom(); + } + + @Override + @M_Random.C(P_Random) + public int P_Random() { + return random.P_Random(); + } + + @Override + @M_Random.C(M_Random) + public int M_Random() { + return random.M_Random(); + } + + @Override + @M_Random.C(M_ClearRandom) + public void ClearRandom() { + random.ClearRandom(); + } + + @Override + public int getIndex() { + return random.getIndex(); + } + + @Override + public int P_Random(int caller) { + return random.P_Random(caller); + } + + @Override + public int P_Random(String message) { + return random.P_Random(message); + } + + @Override + public int P_Random(ActiveStates caller, int sequence) { + return random.P_Random(caller, sequence); + } + + @Override + public int P_Random(ActiveStates caller, mobjtype_t type, int sequence) { + return random.P_Random(caller, type, sequence); + } + +} \ No newline at end of file diff --git a/src/m/DoomRandom.java b/src/m/DoomRandom.java index b01472d..44737e6 100644 --- a/src/m/DoomRandom.java +++ b/src/m/DoomRandom.java @@ -1,181 +1,181 @@ -package m; - -import data.mobjtype_t; -import p.ActiveStates; - -// Emacs style mode select -*- Java -*- -//----------------------------------------------------------------------------- -// -// $Id: DoomRandom.java,v 1.4 2013/06/04 11:29:25 velktron Exp $ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// $Log: DoomRandom.java,v $ -// Revision 1.4 2013/06/04 11:29:25 velktron -// Dummy implementations -// -// Revision 1.3 2013/06/03 10:53:29 velktron -// Implements the new IRandom. -// -// Revision 1.2.10.3 2013/01/09 19:38:26 velktron -// Printing arbitrary messages -// -// Revision 1.2.10.2 2012/11/20 15:59:20 velktron -// More tooling functions. -// -// Revision 1.2.10.1 2012/11/19 22:11:36 velktron -// Added demo sync tooling. -// -// Revision 1.2 2011/05/30 02:24:30 velktron -// *** empty log message *** -// -// Revision 1.1 2011/05/29 22:15:32 velktron -// Introduced IRandom interface. -// -// Revision 1.4 2010/09/22 16:40:02 velktron -// MASSIVE changes in the status passing model. -// DoomMain and DoomGame unified. -// Doomstat merged into DoomMain (now status and game functions are one). -// -// Most of DoomMain implemented. Possible to attempt a "classic type" start but will stop when reading sprites. -// -// Revision 1.3 2010/09/10 17:35:49 velktron -// DoomGame, Menu, renderers -// -// Revision 1.2 2010/07/06 16:32:38 velktron -// Threw some work in WI, now EndLevel. YEAH THERE'S GONNA BE A SEPARATE EndLevel OBJECT THAT'S HOW PIMP THE PROJECT IS!!!!11!!! -// -// Revision 1.1 2010/06/30 08:58:50 velktron -// Let's see if this stuff will finally commit.... -// -// -// Most stuff is still being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package. -// -// Revision 1.1 2010/06/29 11:07:34 velktron -// Release often, release early they say... -// -// Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there. -// -// A good place to start is the testers/ directory, where you can get an idea of how a few of the implemented stuff works. -// -// -// DESCRIPTION: -// Random number LUT. -// -//----------------------------------------------------------------------------- -class DoomRandom implements IRandom { - - /** - * M_Random - * Returns a 0-255 number. Made into shorts for Java, because of their nature. - */ - public static short rndtable[] = { - 0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66, - 74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36, - 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188, - 52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224, - 149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242, - 145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0, - 175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235, - 25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113, - 94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75, - 136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196, - 135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113, - 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241, - 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224, - 145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95, - 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226, - 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36, - 17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106, - 197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136, - 120, 163, 236, 249 - }; - - protected int rndindex = 0; - protected int prndindex = 0; - - // Which one is deterministic? - @Override - public int P_Random() { - prndindex = (prndindex + 1) & 0xff; - return rndtable[prndindex]; - } - - /** - * [Maes] I'd rather dispatch the call here, than making IRandom aware of DoomStatus. Replace RND.P_Random calls - * with DM.P_Random(callerid) etc. - * - * Fixme: this could be made into a proper enum - * - * @param caller - */ - @Override - public int P_Random(int caller) { - int value = P_Random(); - SLY.sync("PR #%d [%d]=%d\n", caller, prndindex, value); - return value; - } - - @Override - public int P_Random(String message) { - int value = P_Random(); - SLY.sync("PR %s [%d]=%d\n", message, - prndindex, value); - return value; - } - - @Override - public int P_Random(ActiveStates caller, int sequence) { - /* - SLY.sync("PR #%d %s_%d [%d]=%d\n", caller.ordinal(),caller,sequence, - prndindex, value);*/ - - return P_Random(); - } - - @Override - public int P_Random(ActiveStates caller, mobjtype_t type, int sequence) { - /* - SLY.sync("PR #%d %s_%d %s [%d]=%d\n", caller.ordinal(),caller,sequence, - type, prndindex, value);*/ - - return P_Random(); - } - - @Override - public int M_Random() { - rndindex = (rndindex + 1) & 0xff; - return rndtable[rndindex]; - } - - @Override - public void ClearRandom() { - rndindex = prndindex = 0; - } - - DoomRandom() { - SLY = null; - } - - @Override - public int getIndex() { - return prndindex; - } - - DoomRandom(ISyncLogger SLY) { - this.SLY = SLY; - } - - private final ISyncLogger SLY; - -} +package m; + +import data.mobjtype_t; +import p.ActiveStates; + +// Emacs style mode select -*- Java -*- +//----------------------------------------------------------------------------- +// +// $Id: DoomRandom.java,v 1.4 2013/06/04 11:29:25 velktron Exp $ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// $Log: DoomRandom.java,v $ +// Revision 1.4 2013/06/04 11:29:25 velktron +// Dummy implementations +// +// Revision 1.3 2013/06/03 10:53:29 velktron +// Implements the new IRandom. +// +// Revision 1.2.10.3 2013/01/09 19:38:26 velktron +// Printing arbitrary messages +// +// Revision 1.2.10.2 2012/11/20 15:59:20 velktron +// More tooling functions. +// +// Revision 1.2.10.1 2012/11/19 22:11:36 velktron +// Added demo sync tooling. +// +// Revision 1.2 2011/05/30 02:24:30 velktron +// *** empty log message *** +// +// Revision 1.1 2011/05/29 22:15:32 velktron +// Introduced IRandom interface. +// +// Revision 1.4 2010/09/22 16:40:02 velktron +// MASSIVE changes in the status passing model. +// DoomMain and DoomGame unified. +// Doomstat merged into DoomMain (now status and game functions are one). +// +// Most of DoomMain implemented. Possible to attempt a "classic type" start but will stop when reading sprites. +// +// Revision 1.3 2010/09/10 17:35:49 velktron +// DoomGame, Menu, renderers +// +// Revision 1.2 2010/07/06 16:32:38 velktron +// Threw some work in WI, now EndLevel. YEAH THERE'S GONNA BE A SEPARATE EndLevel OBJECT THAT'S HOW PIMP THE PROJECT IS!!!!11!!! +// +// Revision 1.1 2010/06/30 08:58:50 velktron +// Let's see if this stuff will finally commit.... +// +// +// Most stuff is still being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package. +// +// Revision 1.1 2010/06/29 11:07:34 velktron +// Release often, release early they say... +// +// Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there. +// +// A good place to start is the testers/ directory, where you can get an idea of how a few of the implemented stuff works. +// +// +// DESCRIPTION: +// Random number LUT. +// +//----------------------------------------------------------------------------- +class DoomRandom implements IRandom { + + /** + * M_Random + * Returns a 0-255 number. Made into shorts for Java, because of their nature. + */ + public static short rndtable[] = { + 0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66, + 74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36, + 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188, + 52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224, + 149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242, + 145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0, + 175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235, + 25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113, + 94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75, + 136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196, + 135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113, + 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241, + 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224, + 145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95, + 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226, + 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36, + 17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106, + 197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136, + 120, 163, 236, 249 + }; + + protected int rndindex = 0; + protected int prndindex = 0; + + // Which one is deterministic? + @Override + public int P_Random() { + prndindex = (prndindex + 1) & 0xff; + return rndtable[prndindex]; + } + + /** + * [Maes] I'd rather dispatch the call here, than making IRandom aware of DoomStatus. Replace RND.P_Random calls + * with DM.P_Random(callerid) etc. + * + * Fixme: this could be made into a proper enum + * + * @param caller + */ + @Override + public int P_Random(int caller) { + int value = P_Random(); + SLY.sync("PR #%d [%d]=%d\n", caller, prndindex, value); + return value; + } + + @Override + public int P_Random(String message) { + int value = P_Random(); + SLY.sync("PR %s [%d]=%d\n", message, + prndindex, value); + return value; + } + + @Override + public int P_Random(ActiveStates caller, int sequence) { + /* + SLY.sync("PR #%d %s_%d [%d]=%d\n", caller.ordinal(),caller,sequence, + prndindex, value);*/ + + return P_Random(); + } + + @Override + public int P_Random(ActiveStates caller, mobjtype_t type, int sequence) { + /* + SLY.sync("PR #%d %s_%d %s [%d]=%d\n", caller.ordinal(),caller,sequence, + type, prndindex, value);*/ + + return P_Random(); + } + + @Override + public int M_Random() { + rndindex = (rndindex + 1) & 0xff; + return rndtable[rndindex]; + } + + @Override + public void ClearRandom() { + rndindex = prndindex = 0; + } + + DoomRandom() { + SLY = null; + } + + @Override + public int getIndex() { + return prndindex; + } + + DoomRandom(ISyncLogger SLY) { + this.SLY = SLY; + } + + private final ISyncLogger SLY; + +} \ No newline at end of file diff --git a/src/m/DoomSetting.java b/src/m/DoomSetting.java index aecd90f..bdbb4c1 100644 --- a/src/m/DoomSetting.java +++ b/src/m/DoomSetting.java @@ -2,22 +2,22 @@ import utils.C2JUtils; -/** A "Doom setting". Based on current experience, it could +/** A "Doom setting". Based on current experience, it could * represent an integer value, a string, or a boolean value. - * - * Therefore, every setting can be interpreted as any of the above, - * based on some rules. Strings that can be interpreted as parseable + * + * Therefore, every setting can be interpreted as any of the above, + * based on some rules. Strings that can be interpreted as parseable * numbers are obvious, and numbers can also be interpreted as strings. * Strings that can't be interpreted as numbers will return "0" as a default * value. - * + * * A numerical value of 1 means "true", any other value is "false". - * A string representing the (case insensitive) value "true" will + * A string representing the (case insensitive) value "true" will * be interpreted as a true boolean, false otherwise. - * + * * @author velktron * - * + * */ public class DoomSetting implements Comparable { @@ -86,7 +86,7 @@ public int getTypeFlag() { } /** All the gory disambiguation work should go here. - * + * * @param value */ public void updateValue(String value) { @@ -104,7 +104,7 @@ public void updateValue(String value) { // String value always available this.string_val = value; - // If quoted and sensibly ranged, it gets priority as a "character" + // If quoted and sensibly ranged, it gets priority as a "character" if (quoted && value.length() == 1 && value.charAt(0) >= 0 && value.charAt(0) < 255) { char_val = Character.toLowerCase(value.charAt(0)); int_val = char_val; @@ -149,7 +149,7 @@ public void updateValue(String value) { char_val = (char) int_val; // Boolean has a few more options; - // Only mark something explicitly as boolean if the string reads + // Only mark something explicitly as boolean if the string reads // actually "true" or "false". Numbers such as 0 and 1 might still get // interpreted as booleans, but that shouldn't trump the entire number, // otherwise everything and the cat is boolean @@ -162,10 +162,10 @@ public void updateValue(String value) { } } - /** Answer definitively if a setting cannot ABSOLUTELY be - * parsed into a number using simple Integer rules. + /** Answer definitively if a setting cannot ABSOLUTELY be + * parsed into a number using simple Integer rules. * This excludes some special names like "+Inf" and "NaN". - * + * * @return */ public boolean isIntegerNumeric() { @@ -188,8 +188,8 @@ public boolean isIntegerNumeric() { } /** Settings are "comparable" to each other by name, so we can save - * nicely sorted setting files ;-) - * + * nicely sorted setting files ;-) + * * @param o * @return */ @@ -220,4 +220,4 @@ public String toString() { NULL_SETTING.long_val = 0; } -} +} \ No newline at end of file diff --git a/src/m/DrawRoutine.java b/src/m/DrawRoutine.java index 752eba1..5376c74 100644 --- a/src/m/DrawRoutine.java +++ b/src/m/DrawRoutine.java @@ -3,11 +3,11 @@ /** menu_t required a function pointer to a (routine)() that drew stuff. * So any class implementing them will implement this interface, and * we can have a single class type for all of them. - * + * * @author Maes * */ public interface DrawRoutine { public void invoke(); -} +} \ No newline at end of file diff --git a/src/m/DummyMenu.java b/src/m/DummyMenu.java index f64f79c..d579092 100644 --- a/src/m/DummyMenu.java +++ b/src/m/DummyMenu.java @@ -5,7 +5,7 @@ /** A dummy menu, useful for testers that do need a defined * menu object. - * + * * @author Maes * */ @@ -81,4 +81,4 @@ public void ClearMenus() { } -} +} \ No newline at end of file diff --git a/src/m/FixedFloat.java b/src/m/FixedFloat.java index ca5c590..a77a8cb 100644 --- a/src/m/FixedFloat.java +++ b/src/m/FixedFloat.java @@ -2,16 +2,16 @@ /** Some utilities for switching between floating and signed 16.16 fixed-point at will. * They use direct bit manipulation with little -if any- looping. - * + * * The methods can probably be generalized but not a priority for now. * They do not handle Infinities, NaNs and unnormalized numbers. - * + * * @author Maes * */ public class FixedFloat { - // Various bit masks for IEEE-754 floating point + // Various bit masks for IEEE-754 floating point public static final int MANTISSA_32 = 0x007FFFFF; public static final int EXP_32 = 0x7F800000; public static final int IMPLICIT_32 = 0x00800000; @@ -81,7 +81,7 @@ public static int toFixed(float fl) { // Remember sign. int sign = flbits & SIGN_32; // Join together: the implcit 1 and the mantissa bits. - // We now have the "denormalized" value. + // We now have the "denormalized" value. int denorm = IMPLICIT_32 | (flbits & MANTISSA_32); // Get exponent...acceptable values are (-15 ~ 15), else wrap around (use only sign and lowest 4 bits). int exp = (((flbits & EXP_32) >> 23) - 127) & 0x8000000F; @@ -89,9 +89,9 @@ public static int toFixed(float fl) { * So for an exponent of 0, we must shift to position 16. * For positive exponents in general, we must shift -7 + exp. * and for one of 15, to position 30, plus the sign. - * While there is space for all bits, we can't keep them all, + * While there is space for all bits, we can't keep them all, * as some (well, many)numbers can't be represented in fixed point. - * + * */ int result; if ((exp - 7) >= 0) { @@ -109,7 +109,7 @@ public static int toFixed(double fl) { // Remember sign. int sign = (int) ((flbits & SIGN_64) >> 32); // Join together: the implcit 1 and the mantissa bits. - // We now have the "denormalized" value. + // We now have the "denormalized" value. long denorm = IMPLICIT_64 | (flbits & MANTISSA_64); //System.out.println("Denorm"+Integer.toBinaryString(denorm)); // Get exponent...acceptable values are (-15 ~ 15), else wrap around (use only sign and lowest 4 bits). @@ -118,9 +118,9 @@ public static int toFixed(double fl) { * So for an exponent of 0, we must shift to position 16. * For positive exponents in general, we must shift -37 + exp. * and for one of 15, to position 30, plus the sign. - * While there is space for all bits, we can't keep them all, + * While there is space for all bits, we can't keep them all, * as some (well, many)numbers can't be represented in fixed point. - * + * */ int result; if ((exp - 36) >= 0) { @@ -132,4 +132,4 @@ public static int toFixed(double fl) { return result; } -} +} \ No newline at end of file diff --git a/src/m/IDoomMenu.java b/src/m/IDoomMenu.java index 5eaec57..82f12bf 100644 --- a/src/m/IDoomMenu.java +++ b/src/m/IDoomMenu.java @@ -27,10 +27,10 @@ // // DESCRIPTION: // Menu widget stuff, episode selection and such. -// +// // ----------------------------------------------------------------------------- /** - * + * */ public interface IDoomMenu { @@ -81,4 +81,4 @@ public interface IDoomMenu { public int getDetailLevel(); void ClearMenus(); -} +} \ No newline at end of file diff --git a/src/m/IRandom.java b/src/m/IRandom.java index 7896c4a..97bf0aa 100644 --- a/src/m/IRandom.java +++ b/src/m/IRandom.java @@ -1,23 +1,23 @@ -package m; - -import data.mobjtype_t; -import p.ActiveStates; - -public interface IRandom { - - public int P_Random(); - - public int M_Random(); - - public void ClearRandom(); - - public int getIndex(); - - public int P_Random(int caller); - - public int P_Random(String message); - - public int P_Random(ActiveStates caller, int sequence); - - public int P_Random(ActiveStates caller, mobjtype_t type, int sequence); -} +package m; + +import data.mobjtype_t; +import p.ActiveStates; + +public interface IRandom { + + public int P_Random(); + + public int M_Random(); + + public void ClearRandom(); + + public int getIndex(); + + public int P_Random(int caller); + + public int P_Random(String message); + + public int P_Random(ActiveStates caller, int sequence); + + public int P_Random(ActiveStates caller, mobjtype_t type, int sequence); +} \ No newline at end of file diff --git a/src/m/ISyncLogger.java b/src/m/ISyncLogger.java index 845c5bf..7aa13ab 100644 --- a/src/m/ISyncLogger.java +++ b/src/m/ISyncLogger.java @@ -9,4 +9,4 @@ public interface ISyncLogger { public void debugEnd(); public void sync(String format, Object... args); -} +} \ No newline at end of file diff --git a/src/m/JavaRandom.java b/src/m/JavaRandom.java index 7bb9d63..021f971 100644 --- a/src/m/JavaRandom.java +++ b/src/m/JavaRandom.java @@ -1,116 +1,115 @@ -package m; - -import data.mobjtype_t; -import java.util.Random; -import p.ActiveStates; - -// Emacs style mode select -*- Java -*- -//----------------------------------------------------------------------------- -// -// $Id: JavaRandom.java,v 1.3 2013/06/03 11:00:03 velktron Exp $ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// -// DESCRIPTION: -// Random number LUT using java.util.Random -// Don't expect vanilla demo compatibility with THIS! -// -//----------------------------------------------------------------------------- -/** - * Actually, now there is demo compatilbility switch: use of JavaRandom is now - * default in singleplayer, unless you play demo, unless you record demo, - * when you play demo, DoomRandom is picked instead, same for record, unless - * you specify -javarandom command line argument, in that case when you record - * demo, version information will be changed, and JavaRandom used, - * when you play this demo, DoomRandom will not be picked, when you play - * another demo, it will pick DoomRandom. - * - * When you dont pass -javarandom, but play demo recorded with JavaRandom, - * it will pick JavaRandom for this demo playback - * - Good Sign 2017/04/14 - */ -class JavaRandom implements IRandom { - - protected int rndindex = 0; - protected int prndindex = 0; - - // Which one is deterministic? - @Override - public int P_Random() { - rndindex++; - return (0xFF & r.nextInt()); - } - - @Override - public int M_Random() { - prndindex++; - return (0xFF & m.nextInt()); - } - - @Override - public final void ClearRandom() { - rndindex = prndindex = 0; - r.setSeed(666); - } - - JavaRandom() { - r = new Random(666); - m = new Random(666); - this.ClearRandom(); - } - - @Override - public int getIndex() { - return rndindex; - } - - private final Random r; - private final Random m; - - @Override - public int P_Random(int caller) { - // DUMMY - return P_Random(); - } - - @Override - public int P_Random(String message) { - // DUMMY - return P_Random(); - } - - @Override - public int P_Random(ActiveStates caller, int sequence) { - // DUMMY - return P_Random(); - } - - @Override - public int P_Random(ActiveStates caller, mobjtype_t type, int sequence) { - // DUMMY - return P_Random(); - } - -} - -//$Log: JavaRandom.java,v $ -//Revision 1.3 2013/06/03 11:00:03 velktron -//Implements interface without logging. -// -//Revision 1.2 2011/07/27 20:47:46 velktron -//Proper commenting, cleanup. -// -//Revision 1.1 2011/05/29 22:15:32 velktron -//Introduced IRandom interface. - +package m; + +import data.mobjtype_t; +import java.util.Random; +import p.ActiveStates; + +// Emacs style mode select -*- Java -*- +//----------------------------------------------------------------------------- +// +// $Id: JavaRandom.java,v 1.3 2013/06/03 11:00:03 velktron Exp $ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// +// DESCRIPTION: +// Random number LUT using java.util.Random +// Don't expect vanilla demo compatibility with THIS! +// +//----------------------------------------------------------------------------- +/** + * Actually, now there is demo compatilbility switch: use of JavaRandom is now + * default in singleplayer, unless you play demo, unless you record demo, + * when you play demo, DoomRandom is picked instead, same for record, unless + * you specify -javarandom command line argument, in that case when you record + * demo, version information will be changed, and JavaRandom used, + * when you play this demo, DoomRandom will not be picked, when you play + * another demo, it will pick DoomRandom. + * + * When you dont pass -javarandom, but play demo recorded with JavaRandom, + * it will pick JavaRandom for this demo playback + * - Good Sign 2017/04/14 + */ +class JavaRandom implements IRandom { + + protected int rndindex = 0; + protected int prndindex = 0; + + // Which one is deterministic? + @Override + public int P_Random() { + rndindex++; + return (0xFF & r.nextInt()); + } + + @Override + public int M_Random() { + prndindex++; + return (0xFF & m.nextInt()); + } + + @Override + public final void ClearRandom() { + rndindex = prndindex = 0; + r.setSeed(666); + } + + JavaRandom() { + r = new Random(666); + m = new Random(666); + this.ClearRandom(); + } + + @Override + public int getIndex() { + return rndindex; + } + + private final Random r; + private final Random m; + + @Override + public int P_Random(int caller) { + // DUMMY + return P_Random(); + } + + @Override + public int P_Random(String message) { + // DUMMY + return P_Random(); + } + + @Override + public int P_Random(ActiveStates caller, int sequence) { + // DUMMY + return P_Random(); + } + + @Override + public int P_Random(ActiveStates caller, mobjtype_t type, int sequence) { + // DUMMY + return P_Random(); + } + +} + +//$Log: JavaRandom.java,v $ +//Revision 1.3 2013/06/03 11:00:03 velktron +//Implements interface without logging. +// +//Revision 1.2 2011/07/27 20:47:46 velktron +//Proper commenting, cleanup. +// +//Revision 1.1 2011/05/29 22:15:32 velktron +//Introduced IRandom interface. diff --git a/src/m/Menu.java b/src/m/Menu.java index b9005cd..928f965 100644 --- a/src/m/Menu.java +++ b/src/m/Menu.java @@ -298,7 +298,7 @@ private void initMenuItems() { OptionsDef = new menu_t(opt_end, this.MainDef, OptionsMenu, DrawOptions, 60, 37, 0); - // Read This! MENU 1 + // Read This! MENU 1 ReadMenu1 = new menuitem_t[]{new menuitem_t(1, "", ReadThis2, SC_0)}; ReadDef1 = new menu_t(read1_end, MainDef, ReadMenu1, DrawReadThis1, 280, 185, 0); @@ -399,7 +399,7 @@ private void DrawSaveLoadBorder(int x, int y) { /** Draws slider rail of a specified width (each notch is 8 base units wide) * and with a slider selector at position thermDot. - * + * * @param x * @param y * @param thermWidth @@ -448,7 +448,7 @@ public void invoke() { /** * M_Responder calls this when user is finished - * + * * @param slot */ public void DoSave(int slot) { @@ -810,7 +810,7 @@ public int StringWidth(char[] string) { /** * Find string height from hu_font chars. - * + * * Actually it just counts occurences of 'n' and adds height to height. */ private int StringHeight(char[] string) { @@ -970,7 +970,7 @@ public boolean Responder(event_t ev) { return r; }); } else if (DOOM.use_mouse && ev.isType(evtype_t.ev_mouse) && mousewait < DOOM.ticker.GetTime()) { - // Mouse input + // Mouse input if ((sc = ev.mapByMouse(mouseEvent -> { ScanCode r = SC_NULL; mousey += mouseEvent.y; @@ -1451,7 +1451,7 @@ public void Init() { /** * M_DrawText Returns the final X coordinate HU_Init must have been called * to init the font. Unused? - * + * * @param x * @param y * @param direct @@ -1872,4 +1872,4 @@ public int getScreenBlocks() { public int getDetailLevel() { return this.detailLevel; } -} +} \ No newline at end of file diff --git a/src/m/MenuMisc.java b/src/m/MenuMisc.java index d8e5c0b..9998c35 100644 --- a/src/m/MenuMisc.java +++ b/src/m/MenuMisc.java @@ -83,10 +83,10 @@ public static boolean WriteFile(String name, IWritableDoomObject source) { return true; } - /** M_ReadFile + /** M_ReadFile * This version returns a variable-size ByteBuffer, so * we don't need to know a-priori how much stuff to read. - * + * */ public static ByteBuffer ReadFile(String name) { BufferedInputStream handle; @@ -281,7 +281,7 @@ public static void WritePNGfile(String imagename, byte[] linear, int width, int // -verbosity // // Revision 1.22 2011/05/31 09:57:45 velktron -// Fixed broken parsing of unspaced strings. +// Fixed broken parsing of unspaced strings. // It's never fun having to come up with your own function for string manipulation! // // Revision 1.21 2011/05/30 15:46:50 velktron @@ -295,4 +295,4 @@ public static void WritePNGfile(String imagename, byte[] linear, int width, int // // Revision 1.18 2011/05/24 17:46:03 velktron // Added vanilla default.cfg loading. -// +// \ No newline at end of file diff --git a/src/m/MenuRoutine.java b/src/m/MenuRoutine.java index f7965f9..386bf9a 100644 --- a/src/m/MenuRoutine.java +++ b/src/m/MenuRoutine.java @@ -3,11 +3,11 @@ /** menuitem_t required a function pointer to a (routine)(int choice) * So any class implementing them will implement this interface, and * we can have a single class type for all of them. - * + * * @author Velktron * */ public interface MenuRoutine { public void invoke(int choice); -} +} \ No newline at end of file diff --git a/src/m/Settings.java b/src/m/Settings.java index 60de1e1..1f0f1cf 100644 --- a/src/m/Settings.java +++ b/src/m/Settings.java @@ -57,10 +57,10 @@ /** * An enumeration with the most basic default Doom settings their default values, used if nothing else is available. * They are applied first thing, and then updated from the .cfg file. - * + * * The file now also contains settings on many features introduced by this new version of Mocha Doom * - Good Sign 2017/04/11 - * + * * TODO: find a trick to separate settings groups in the same file vanilla-compatibly */ public enum Settings { @@ -119,7 +119,7 @@ public enum Settings { fullscreen_stretch(FILE_MOCHADOOM, FullscreenOptions.StretchMode.Fit), fullscreen_interpolation(FILE_MOCHADOOM, FullscreenOptions.InterpolationMode.Nearest), alwaysrun(FILE_MOCHADOOM, false), // Always run is OFF - vanilla_key_behavior(FILE_MOCHADOOM, true), // Currently forces LSHIFT on RSHIFT, TODO: layouts, etc + vanilla_key_behavior(FILE_MOCHADOOM, true), // Currently forces LSHIFT on RSHIFT, TODO: layouts, etc automap_plotter_style(FILE_MOCHADOOM, Plotter.Style.Thin), // Thin is vanilla, Thick is scaled, Deep slightly rounded scaled enable_colormap_lump(FILE_MOCHADOOM, true), // Enables usage of COLORMAP lump read from wad during lights and specials generation color_depth(FILE_MOCHADOOM, BppMode.Indexed), // Indexed: 256, HiColor: 32 768, TrueColor: 16 777 216 @@ -240,4 +240,4 @@ private void updateConfig() { return list; }); } -} +} \ No newline at end of file diff --git a/src/m/Swap.java b/src/m/Swap.java index 566b4e2..68443bb 100644 --- a/src/m/Swap.java +++ b/src/m/Swap.java @@ -27,7 +27,7 @@ public final class Swap { // Swap 16bit, that is, MSB and LSB byte. public final static short SHORT(short x) { - // No masking with 0xFF should be necessary. + // No masking with 0xFF should be necessary. // MAES: necessary with java due to sign trailing. return (short) ((short) ((x >>> 8) & 0xFF) | (x << 8)); @@ -35,7 +35,7 @@ public final static short SHORT(short x) { //Swap 16bit, that is, MSB and LSB byte. public final static short SHORT(char x) { - // No masking with 0xFF should be necessary. + // No masking with 0xFF should be necessary. // MAES: necessary with java due to sign trailing. return (short) ((short) ((x >>> 8) & 0xFF) | (x << 8)); @@ -43,7 +43,7 @@ public final static short SHORT(char x) { //Swap 16bit, that is, MSB and LSB byte. public final static char USHORT(char x) { - // No masking with 0xFF should be necessary. + // No masking with 0xFF should be necessary. // MAES: necessary with java due to sign trailing. return (char) ((char) ((x >>> 8) & 0xFF) | (x << 8)); @@ -64,4 +64,4 @@ public final static int LONG(int x) { //Proper commenting, cleanup. // //Revision 1.1 2010/06/30 08:58:50 velktron -//Let's see if this stuff will finally commit.... +//Let's see if this stuff will finally commit.... \ No newline at end of file diff --git a/src/m/cheatseq_t.java b/src/m/cheatseq_t.java index cdac87f..9976365 100644 --- a/src/m/cheatseq_t.java +++ b/src/m/cheatseq_t.java @@ -131,7 +131,7 @@ public void GetParam(char[] buffer) { /** * Called in st_stuff module, which handles the input. Returns true if the * cheat was successful, false if failed. MAES: Let's make this boolean. - * + * * @param cht * @param key * @return @@ -167,7 +167,7 @@ public boolean CheckCheat(cheatseq_t cht, int key) { /** * Called in st_stuff module, which handles the input. Returns true if the * cheat was successful, false if failed. MAES: Let's make this boolean. - * + * * @param key * @return */ @@ -202,7 +202,7 @@ else if (cheat_xlate_table[(char) key] == sequence[p]) { /** * Scrambles a character. 7 -> 0 6 -> 1 5 -> 5 4 -> 3 3 -> 4 2 -> 2 1 -> 6 0 * -> 7 - * + * * @param a * @return */ @@ -239,4 +239,4 @@ public static char[] scrambleString(String s) { } } } -} +} \ No newline at end of file diff --git a/src/m/default_t.java b/src/m/default_t.java index 41fd56c..32a2b06 100644 --- a/src/m/default_t.java +++ b/src/m/default_t.java @@ -18,4 +18,4 @@ public default_t(String name, int[] location, int defaultvalue) { int scantranslate; // PC scan code hack int untranslated; // lousy hack -}; +}; \ No newline at end of file diff --git a/src/m/fixed_t.java b/src/m/fixed_t.java index a5ebb62..9dc280c 100644 --- a/src/m/fixed_t.java +++ b/src/m/fixed_t.java @@ -30,7 +30,7 @@ // Most functionality of C-based ports is preserved, EXCEPT that there's // no typedef of ints into fixed_t, and that there's no actual object fixed_t // type that is actually instantiated in the current codebase, for performance reasons. -// There are still remnants of a full OO implementation that still do work, +// There are still remnants of a full OO implementation that still do work, // and the usual FixedMul/FixedDiv etc. methods are still used throughout the codebase, // but operate on int operants (signed, 32-bit integers). public class fixed_t implements Comparable { @@ -75,7 +75,7 @@ public fixed_t(fixed_t x) { public static final String rcsid = "$Id: fixed_t.java,v 1.14 2011/10/25 19:52:13 velktron Exp $"; /** Creates a new fixed_t object for the result a*b - * + * * @param a * @param b * @return @@ -96,7 +96,7 @@ public static final int FixedMul(int a, } /** Returns result straight as an int.. - * + * * @param a * @param b * @return @@ -107,7 +107,7 @@ public static int FixedMulInt(fixed_t a, } /** In-place c=a*b - * + * * @param a * @param b * @param c @@ -119,7 +119,7 @@ public final static void FixedMul(fixed_t a, } /** In-place this=this*a - * + * * @param a * @param b * @param c @@ -157,7 +157,7 @@ public final void FixedMul(fixed_t a) { if (c >= 2147483648.0 || c < -2147483648.0) throw new ArithmeticException("FixedDiv: divide by zero"); - + return (int)c;*/ } @@ -204,7 +204,7 @@ public void sub(int a) { } /** a+b - * + * * @param a * @param b * @return @@ -214,7 +214,7 @@ public static int add(fixed_t a, fixed_t b) { } /** a-b - * + * * @param a * @param b * @return @@ -224,7 +224,7 @@ public static int sub(fixed_t a, fixed_t b) { } /** c=a+b - * + * * @param c * @param a * @param b @@ -234,7 +234,7 @@ public static void add(fixed_t c, fixed_t a, fixed_t b) { } /** c=a-b - * + * * @param c * @param a * @param b @@ -244,7 +244,7 @@ public static void sub(fixed_t c, fixed_t a, fixed_t b) { } /** Equals Zero - * + * * @return */ public boolean isEZ() { @@ -252,7 +252,7 @@ public boolean isEZ() { } /** Greater than Zero - * + * * @return */ public boolean isGZ() { @@ -260,14 +260,14 @@ public boolean isGZ() { } /** Less than Zero - * + * * @return */ public boolean isLZ() { return (this.val < 0); } -// These are here to make easier handling all those methods in R +// These are here to make easier handling all those methods in R // that return "1" or "0" based on one result. public int oneEZ() { return (this.val == 0) ? 1 : 0; @@ -281,4 +281,4 @@ public int oneLZ() { return (this.val < 0) ? 1 : 0; } -} +} \ No newline at end of file diff --git a/src/m/menu_t.java b/src/m/menu_t.java index 96264f5..1159f7d 100644 --- a/src/m/menu_t.java +++ b/src/m/menu_t.java @@ -3,7 +3,7 @@ /** General form for a classic, Doom-style menu with a bunch of * items and a drawing routine (menu_t's don't have action callbacks * proper, though). - * + * * @author Maes * */ @@ -34,4 +34,4 @@ public menu_t(int numitems, menu_t prev, menuitem_t[] items, public int x, y; /** last item user was on in menu */ public int lastOn; -} +} \ No newline at end of file diff --git a/src/m/menuitem_t.java b/src/m/menuitem_t.java index abdee06..dadf321 100644 --- a/src/m/menuitem_t.java +++ b/src/m/menuitem_t.java @@ -35,4 +35,4 @@ public menuitem_t(int status, String name, MenuRoutine routine) { * hotkey in menu */ Signals.ScanCode alphaKey; -} +} \ No newline at end of file diff --git a/src/m/pcx_t.java b/src/m/pcx_t.java index a8a9462..fd9cb03 100644 --- a/src/m/pcx_t.java +++ b/src/m/pcx_t.java @@ -6,10 +6,10 @@ /** Yeah, this is actually a PCX header implementation, and Mocha Doom * saved PCX screenshots. Implemented it back just to shot that it can be - * done (will switch to PNG ASAP though). - * + * done (will switch to PNG ASAP though). + * * @author Maes - * + * */ public class pcx_t implements IWritableDoomObject { @@ -38,7 +38,7 @@ public class pcx_t implements IWritableDoomObject { /** vertical dots per inch when printed (unreliable) */ char vres; - /** 16-color palette (16 RGB triples between 0-255) + /** 16-color palette (16 RGB triples between 0-255) * UNUSED in Doom. */ byte[] palette = new byte[48]; @@ -89,4 +89,4 @@ public void write(DataOutputStream f) throws IOException { //unsigned char data; // unbounded f.write(data); } -}; +}; \ No newline at end of file diff --git a/src/mochadoom/Engine.java b/src/mochadoom/Engine.java index e0c69e9..b0023b7 100644 --- a/src/mochadoom/Engine.java +++ b/src/mochadoom/Engine.java @@ -55,7 +55,7 @@ public static void main(final String[] argv) throws IOException { /** * Add eventHandler listeners to JFrame and its Canvas element */ - /*content.addKeyListener(listener); + /*content.addKeyListener(listener); content.addMouseListener(listener); content.addMouseMotionListener(listener); frame.addComponentListener(listener); @@ -177,4 +177,4 @@ public static CVarManager getCVM() { public static ConfigManager getConfig() { return getEngine().cm; } -} +} \ No newline at end of file diff --git a/src/mochadoom/Loggers.java b/src/mochadoom/Loggers.java index c5cb1bd..5e54583 100644 --- a/src/mochadoom/Loggers.java +++ b/src/mochadoom/Loggers.java @@ -1,169 +1,169 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package mochadoom; - -import awt.DoomWindow; -import awt.EventBase; -import awt.EventBase.ActionMode; -import awt.EventBase.ActionStateHolder; -import awt.EventBase.RelationType; -import doom.CVarManager; -import doom.ConfigManager; -import doom.DoomMain; -import i.DoomSystem; -import java.awt.AWTEvent; -import java.io.OutputStream; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.function.IntFunction; -import java.util.logging.ConsoleHandler; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import p.ActiveStates; -import v.graphics.Patches; - -/** - * Facility to manage Logger Levels for different classes - * All of that should be used instead of System.err.println for debug - * - * @author Good Sign - */ -public class Loggers { - - static { - System.setProperty("java.util.logging.SimpleFormatter.format", "[%1$tF %1$tT] [%4$-7s] [%3$-20s] %5$s %n"); - } - - private static final Level DEFAULT_LEVEL = Level.WARNING; - - private static final Map PARENT_LOGGERS_MAP = Stream.of( - Level.FINE, Level.FINER, Level.FINEST, Level.INFO, Level.SEVERE, Level.WARNING - ).collect(Collectors.toMap(l -> l, Loggers::newLoggerHandlingLevel)); - - private static final Logger DEFAULT_LOGGER = PARENT_LOGGERS_MAP.get(DEFAULT_LEVEL); - private static final HashMap INDIVIDUAL_CLASS_LOGGERS = new HashMap<>(); - - static { - //INDIVIDUAL_CLASS_LOGGERS.put(EventObserver.class.getName(), PARENT_LOGGERS_MAP.get(Level.FINE)); - //INDIVIDUAL_CLASS_LOGGERS.put(TraitFactory.class.getName(), PARENT_LOGGERS_MAP.get(Level.FINER)); - INDIVIDUAL_CLASS_LOGGERS.put(ActiveStates.class.getName(), PARENT_LOGGERS_MAP.get(Level.FINER)); - INDIVIDUAL_CLASS_LOGGERS.put(DoomWindow.class.getName(), PARENT_LOGGERS_MAP.get(Level.FINE)); - INDIVIDUAL_CLASS_LOGGERS.put(Patches.class.getName(), PARENT_LOGGERS_MAP.get(Level.INFO)); - INDIVIDUAL_CLASS_LOGGERS.put(ConfigManager.class.getName(), PARENT_LOGGERS_MAP.get(Level.INFO)); - INDIVIDUAL_CLASS_LOGGERS.put(DoomMain.class.getName(), PARENT_LOGGERS_MAP.get(Level.INFO)); - INDIVIDUAL_CLASS_LOGGERS.put(DoomSystem.class.getName(), PARENT_LOGGERS_MAP.get(Level.INFO)); - INDIVIDUAL_CLASS_LOGGERS.put(CVarManager.class.getName(), PARENT_LOGGERS_MAP.get(Level.INFO)); - INDIVIDUAL_CLASS_LOGGERS.put(Engine.class.getName(), PARENT_LOGGERS_MAP.get(Level.INFO)); - } - - public static Logger getLogger(final String className) { - final Logger ret = Logger.getLogger(className); - ret.setParent(INDIVIDUAL_CLASS_LOGGERS.getOrDefault(className, DEFAULT_LOGGER)); - - return ret; - } - - private static EventBase lastHandler = null; - - public static & EventBase> void LogEvent( - final Logger logger, - final ActionStateHolder actionStateHolder, - final EventHandler handler, - final AWTEvent event - ) { - if (!logger.isLoggable(Level.ALL) && lastHandler == handler) { - return; - } - - lastHandler = handler; - - @SuppressWarnings("unchecked") - final IntFunction[]> arrayGenerator = EventBase[]::new; - final EventBase[] depends = actionStateHolder - .cooperations(handler, RelationType.DEPEND) - .stream() - .filter(hdl -> actionStateHolder.hasActionsEnabled(hdl, ActionMode.DEPEND)) - .toArray(arrayGenerator); - - final Map> adjusts = actionStateHolder - .adjustments(handler); - - final EventBase[] causes = actionStateHolder - .cooperations(handler, RelationType.CAUSE) - .stream() - .filter(hdl -> actionStateHolder.hasActionsEnabled(hdl, ActionMode.DEPEND)) - .toArray(arrayGenerator); - - final EventBase[] reverts = actionStateHolder - .cooperations(handler, RelationType.REVERT) - .stream() - .filter(hdl -> actionStateHolder.hasActionsEnabled(hdl, ActionMode.DEPEND)) - .toArray(arrayGenerator); - - if (logger.isLoggable(Level.FINEST)) { - logger.log(Level.FINEST, () -> String.format( - "\n\nENCOUNTERED EVENT: %s [%s] \n%s: %s \n%s \n%s: %s \n%s: %s \nOn event: %s", - handler, ActionMode.PERFORM, - RelationType.DEPEND, Arrays.toString(depends), - adjusts.entrySet().stream().collect(StringBuilder::new, (sb, e) -> sb.append(e.getKey()).append(' ').append(e.getValue()).append('\n'), StringBuilder::append), - RelationType.CAUSE, Arrays.toString(causes), - RelationType.REVERT, Arrays.toString(reverts), - event - )); - } else if (logger.isLoggable(Level.FINER)) { - logger.log(Level.FINER, () -> String.format( - "\n\nENCOUNTERED EVENT: %s [%s] \n%s: %s \n%s \n%s: %s \n%s: %s \n", - handler, ActionMode.PERFORM, - RelationType.DEPEND, Arrays.toString(depends), - adjusts.entrySet().stream().collect(StringBuilder::new, (sb, e) -> sb.append(e.getKey()).append(' ').append(e.getValue()).append('\n'), StringBuilder::append), - RelationType.CAUSE, Arrays.toString(causes), - RelationType.REVERT, Arrays.toString(reverts) - )); - } else { - logger.log(Level.FINE, () -> String.format( - "\nENCOUNTERED EVENT: %s [%s]", - handler, ActionMode.PERFORM - )); - } - } - - private Loggers() { - } - - private static Logger newLoggerHandlingLevel(final Level l) { - final OutHandler h = new OutHandler(); - h.setLevel(l); - final Logger ret = Logger.getAnonymousLogger(); - ret.setUseParentHandlers(false); - ret.setLevel(l); - ret.addHandler(h); - return ret; - } - - private static final class OutHandler extends ConsoleHandler { - - @Override - @SuppressWarnings("UseOfSystemOutOrSystemErr") - protected synchronized void setOutputStream(final OutputStream out) throws SecurityException { - super.setOutputStream(System.out); - } - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package mochadoom; + +import awt.DoomWindow; +import awt.EventBase; +import awt.EventBase.ActionMode; +import awt.EventBase.ActionStateHolder; +import awt.EventBase.RelationType; +import doom.CVarManager; +import doom.ConfigManager; +import doom.DoomMain; +import i.DoomSystem; +import java.awt.AWTEvent; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.IntFunction; +import java.util.logging.ConsoleHandler; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import p.ActiveStates; +import v.graphics.Patches; + +/** + * Facility to manage Logger Levels for different classes + * All of that should be used instead of System.err.println for debug + * + * @author Good Sign + */ +public class Loggers { + + static { + System.setProperty("java.util.logging.SimpleFormatter.format", "[%1$tF %1$tT] [%4$-7s] [%3$-20s] %5$s %n"); + } + + private static final Level DEFAULT_LEVEL = Level.WARNING; + + private static final Map PARENT_LOGGERS_MAP = Stream.of( + Level.FINE, Level.FINER, Level.FINEST, Level.INFO, Level.SEVERE, Level.WARNING + ).collect(Collectors.toMap(l -> l, Loggers::newLoggerHandlingLevel)); + + private static final Logger DEFAULT_LOGGER = PARENT_LOGGERS_MAP.get(DEFAULT_LEVEL); + private static final HashMap INDIVIDUAL_CLASS_LOGGERS = new HashMap<>(); + + static { + //INDIVIDUAL_CLASS_LOGGERS.put(EventObserver.class.getName(), PARENT_LOGGERS_MAP.get(Level.FINE)); + //INDIVIDUAL_CLASS_LOGGERS.put(TraitFactory.class.getName(), PARENT_LOGGERS_MAP.get(Level.FINER)); + INDIVIDUAL_CLASS_LOGGERS.put(ActiveStates.class.getName(), PARENT_LOGGERS_MAP.get(Level.FINER)); + INDIVIDUAL_CLASS_LOGGERS.put(DoomWindow.class.getName(), PARENT_LOGGERS_MAP.get(Level.FINE)); + INDIVIDUAL_CLASS_LOGGERS.put(Patches.class.getName(), PARENT_LOGGERS_MAP.get(Level.INFO)); + INDIVIDUAL_CLASS_LOGGERS.put(ConfigManager.class.getName(), PARENT_LOGGERS_MAP.get(Level.INFO)); + INDIVIDUAL_CLASS_LOGGERS.put(DoomMain.class.getName(), PARENT_LOGGERS_MAP.get(Level.INFO)); + INDIVIDUAL_CLASS_LOGGERS.put(DoomSystem.class.getName(), PARENT_LOGGERS_MAP.get(Level.INFO)); + INDIVIDUAL_CLASS_LOGGERS.put(CVarManager.class.getName(), PARENT_LOGGERS_MAP.get(Level.INFO)); + INDIVIDUAL_CLASS_LOGGERS.put(Engine.class.getName(), PARENT_LOGGERS_MAP.get(Level.INFO)); + } + + public static Logger getLogger(final String className) { + final Logger ret = Logger.getLogger(className); + ret.setParent(INDIVIDUAL_CLASS_LOGGERS.getOrDefault(className, DEFAULT_LOGGER)); + + return ret; + } + + private static EventBase lastHandler = null; + + public static & EventBase> void LogEvent( + final Logger logger, + final ActionStateHolder actionStateHolder, + final EventHandler handler, + final AWTEvent event + ) { + if (!logger.isLoggable(Level.ALL) && lastHandler == handler) { + return; + } + + lastHandler = handler; + + @SuppressWarnings("unchecked") + final IntFunction[]> arrayGenerator = EventBase[]::new; + final EventBase[] depends = actionStateHolder + .cooperations(handler, RelationType.DEPEND) + .stream() + .filter(hdl -> actionStateHolder.hasActionsEnabled(hdl, ActionMode.DEPEND)) + .toArray(arrayGenerator); + + final Map> adjusts = actionStateHolder + .adjustments(handler); + + final EventBase[] causes = actionStateHolder + .cooperations(handler, RelationType.CAUSE) + .stream() + .filter(hdl -> actionStateHolder.hasActionsEnabled(hdl, ActionMode.DEPEND)) + .toArray(arrayGenerator); + + final EventBase[] reverts = actionStateHolder + .cooperations(handler, RelationType.REVERT) + .stream() + .filter(hdl -> actionStateHolder.hasActionsEnabled(hdl, ActionMode.DEPEND)) + .toArray(arrayGenerator); + + if (logger.isLoggable(Level.FINEST)) { + logger.log(Level.FINEST, () -> String.format( + "\n\nENCOUNTERED EVENT: %s [%s] \n%s: %s \n%s \n%s: %s \n%s: %s \nOn event: %s", + handler, ActionMode.PERFORM, + RelationType.DEPEND, Arrays.toString(depends), + adjusts.entrySet().stream().collect(StringBuilder::new, (sb, e) -> sb.append(e.getKey()).append(' ').append(e.getValue()).append('\n'), StringBuilder::append), + RelationType.CAUSE, Arrays.toString(causes), + RelationType.REVERT, Arrays.toString(reverts), + event + )); + } else if (logger.isLoggable(Level.FINER)) { + logger.log(Level.FINER, () -> String.format( + "\n\nENCOUNTERED EVENT: %s [%s] \n%s: %s \n%s \n%s: %s \n%s: %s \n", + handler, ActionMode.PERFORM, + RelationType.DEPEND, Arrays.toString(depends), + adjusts.entrySet().stream().collect(StringBuilder::new, (sb, e) -> sb.append(e.getKey()).append(' ').append(e.getValue()).append('\n'), StringBuilder::append), + RelationType.CAUSE, Arrays.toString(causes), + RelationType.REVERT, Arrays.toString(reverts) + )); + } else { + logger.log(Level.FINE, () -> String.format( + "\nENCOUNTERED EVENT: %s [%s]", + handler, ActionMode.PERFORM + )); + } + } + + private Loggers() { + } + + private static Logger newLoggerHandlingLevel(final Level l) { + final OutHandler h = new OutHandler(); + h.setLevel(l); + final Logger ret = Logger.getAnonymousLogger(); + ret.setUseParentHandlers(false); + ret.setLevel(l); + ret.addHandler(h); + return ret; + } + + private static final class OutHandler extends ConsoleHandler { + + @Override + @SuppressWarnings("UseOfSystemOutOrSystemErr") + protected synchronized void setOutputStream(final OutputStream out) throws SecurityException { + super.setOutputStream(System.out); + } + } +} \ No newline at end of file diff --git a/src/n/BasicNetworkInterface.java b/src/n/BasicNetworkInterface.java index 4d52383..8aa3a13 100644 --- a/src/n/BasicNetworkInterface.java +++ b/src/n/BasicNetworkInterface.java @@ -82,7 +82,7 @@ public BasicNetworkInterface(DoomMain DOOM) { sendPacket = new DatagramPacket(sendData.cached(), sendData.cached().length); } - // Bind it to the ones inside DN and DM; + // Bind it to the ones inside DN and DM; //doomdata_t netbuffer; doomcom_t doomcom; @@ -170,7 +170,7 @@ public void invoke() { sendData.cmds[c].buttons = netbuffer.cmds[c].buttons; } */ - //printf ("sending %i\n",gametic); + //printf ("sending %i\n",gametic); sendData.copyFrom(netbuffer); // MAES: This will force the buffer to be refreshed. byte[] bytes = sendData.pack(); @@ -407,4 +407,4 @@ public void NetCmd() { // Instance StringBuilder private StringBuilder sb = new StringBuilder(); -} +} \ No newline at end of file diff --git a/src/n/DoomSystemNetworking.java b/src/n/DoomSystemNetworking.java index ecad0ed..d22b437 100644 --- a/src/n/DoomSystemNetworking.java +++ b/src/n/DoomSystemNetworking.java @@ -40,5 +40,4 @@ public interface DoomSystemNetworking { // Renderer works stably enough but a ton of bleeding. Started working on netcode. // // -//----------------------------------------------------------------------------- - +//----------------------------------------------------------------------------- \ No newline at end of file diff --git a/src/n/DummyNetworkDriver.java b/src/n/DummyNetworkDriver.java index 5ff3222..ff6f3be 100644 --- a/src/n/DummyNetworkDriver.java +++ b/src/n/DummyNetworkDriver.java @@ -9,7 +9,7 @@ /** Does nothing. * Allows running single-player games without an actual network. * Hopefully, it will be replaced by a real UDP-based driver one day. - * + * * @author Velktron * */ @@ -42,4 +42,4 @@ public void NetCmd() { // TODO Auto-generated method stub } -} +} \ No newline at end of file diff --git a/src/n/DummyNetworkHandler.java b/src/n/DummyNetworkHandler.java index 4686e0c..8a2566c 100644 --- a/src/n/DummyNetworkHandler.java +++ b/src/n/DummyNetworkHandler.java @@ -41,4 +41,4 @@ public void setTicdup(int ticdup) { } -} +} \ No newline at end of file diff --git a/src/n/IDoomNet.java b/src/n/IDoomNet.java index 21790b8..3f11cae 100644 --- a/src/n/IDoomNet.java +++ b/src/n/IDoomNet.java @@ -3,4 +3,4 @@ public interface IDoomNet { public void NetUpdate(); -} +} \ No newline at end of file diff --git a/src/p/AbstractLevelLoader.java b/src/p/AbstractLevelLoader.java index 031dddc..ace43a3 100644 --- a/src/p/AbstractLevelLoader.java +++ b/src/p/AbstractLevelLoader.java @@ -40,7 +40,7 @@ * blockmap/reject table etc. then you should ask for this class. If you only * need access to some methods like e.g. SetupLevel, you can simply use the * ILevelLoader interface. - * + * * @author velktron */ public abstract class AbstractLevelLoader implements ILevelLoader { @@ -249,7 +249,7 @@ protected class linelist_t // type used to list lines in each block /** * Subroutine to add a line number to a block list It simply returns if the * line is already in the block - * + * * @param lists * @param count * @param done @@ -637,7 +637,7 @@ protected void AddLineToSector(line_t li, sector_t sector) { /** * Compute density of reject table. Aids choosing LUT optimizations. - * + * * @return */ protected float rejectDensity() { @@ -677,7 +677,7 @@ protected float rejectDensity() { * called, visibility between sectors x and y will be set to "false" for the * rest of the level, aka they will be rejected based on subsequent sight * checks. - * + * * @param x * @param y */ @@ -754,7 +754,7 @@ protected void retrieveFromReject(int x, int y, boolean value) { /** * Returns an int[] array with orgx, orgy, and number of blocks. Order is: * orgx,orgy,bckx,bcky - * + * * @return */ protected final int[] getMapBoundingBox(boolean playable) { @@ -853,10 +853,10 @@ protected void LoadReject(int lumpnum) { /** * Added config switch to turn on/off support - * + * * Gets the proper blockmap block for a given X 16.16 Coordinate, sanitized - * for 512-wide blockmaps. - * + * for 512-wide blockmaps. + * * @param blockx * @return */ @@ -873,8 +873,8 @@ public final int getSafeBlockX(long blockx) { } /** Gets the proper blockmap block for a given Y 16.16 Coordinate, sanitized - * for 512-wide blockmaps. - * + * for 512-wide blockmaps. + * * @param blocky * @return */ @SourceCode.Compatible("blocky >> MAPBLOCKSHIFT") @@ -920,4 +920,4 @@ public void InitTagLists() { } } -} +} \ No newline at end of file diff --git a/src/p/ActionFunctions.java b/src/p/ActionFunctions.java index aadefa2..b43bc94 100644 --- a/src/p/ActionFunctions.java +++ b/src/p/ActionFunctions.java @@ -1,221 +1,221 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p; - -import automap.IAutoMap; -import data.sounds; -import defines.skill_t; -import doom.DoomMain; -import doom.player_t; -import hu.IHeadsUp; -import i.IDoomSystem; -import java.util.logging.Level; -import java.util.logging.Logger; -import mochadoom.Loggers; -import p.Actions.ActionsAttacks; -import p.Actions.ActionsEnemies; -import p.Actions.ActionsThinkers; -import p.Actions.ActiveStates.Ai; -import p.Actions.ActiveStates.Attacks; -import p.Actions.ActiveStates.Thinkers; -import p.Actions.ActiveStates.Weapons; -import rr.SceneRenderer; -import s.ISoundOrigin; -import st.IDoomStatusBar; -import utils.TraitFactory; -import utils.TraitFactory.SharedContext; - -public class ActionFunctions extends UnifiedGameMap implements - ActionsThinkers, ActionsEnemies, ActionsAttacks, Ai, Attacks, Thinkers, Weapons { - - private static final Logger LOGGER = Loggers.getLogger(ActionFunctions.class.getName()); - - private final SharedContext traitsSharedContext; - - public ActionFunctions(final DoomMain DOOM) { - super(DOOM); - this.traitsSharedContext = buildContext(); - } - - private SharedContext buildContext() { - try { - return TraitFactory.build(this, ACTION_KEY_CHAIN); - } catch (IllegalArgumentException | IllegalAccessException ex) { - LOGGER.log(Level.SEVERE, "buildContext failure", ex); - throw new RuntimeException(ex); - } - } - - @Override - public AbstractLevelLoader levelLoader() { - return DOOM.levelLoader; - } - - @Override - public IHeadsUp headsUp() { - return DOOM.headsUp; - } - - @Override - public IDoomSystem doomSystem() { - return DOOM.doomSystem; - } - - @Override - public IDoomStatusBar statusBar() { - return DOOM.statusBar; - } - - @Override - public IAutoMap autoMap() { - return DOOM.autoMap; - } - - @Override - public SceneRenderer sceneRenderer() { - return DOOM.sceneRenderer; - } - - @Override - public UnifiedGameMap.Specials getSpecials() { - return SPECS; - } - - @Override - public UnifiedGameMap.Switches getSwitches() { - return SW; - } - - @Override - public void StopSound(ISoundOrigin origin) { - DOOM.doomSound.StopSound(origin); - } - - @Override - public void StartSound(ISoundOrigin origin, sounds.sfxenum_t s) { - DOOM.doomSound.StartSound(origin, s); - } - - @Override - public void StartSound(ISoundOrigin origin, int s) { - DOOM.doomSound.StartSound(origin, s); - } - - @Override - public player_t getPlayer(int number) { - return DOOM.players[number]; - } - - @Override - public skill_t getGameSkill() { - return DOOM.gameskill; - } - - @Override - public mobj_t createMobj() { - return mobj_t.createOn(DOOM); - } - - @Override - public int LevelTime() { - return DOOM.leveltime; - } - - @Override - public int P_Random() { - return DOOM.random.P_Random(); - } - - @Override - public int ConsolePlayerNumber() { - return DOOM.consoleplayer; - } - - @Override - public int MapNumber() { - return DOOM.gamemap; - } - - @Override - public boolean PlayerInGame(int number) { - return DOOM.playeringame[number]; - } - - @Override - public boolean IsFastParm() { - return DOOM.fastparm; - } - - @Override - public boolean IsPaused() { - return DOOM.paused; - } - - @Override - public boolean IsNetGame() { - return DOOM.netgame; - } - - @Override - public boolean IsDemoPlayback() { - return DOOM.demoplayback; - } - - @Override - public boolean IsDeathMatch() { - return DOOM.deathmatch; - } - - @Override - public boolean IsAutoMapActive() { - return DOOM.automapactive; - } - - @Override - public boolean IsMenuActive() { - return DOOM.menuactive; - } - - /** - * TODO: avoid, deprecate - */ - @Override - public DoomMain DOOM() { - return DOOM; - } - - @Override - public SharedContext getContext() { - return traitsSharedContext; - } - - @Override - public ActionsThinkers getThinkers() { - return this; - } - - @Override - public ActionsEnemies getEnemies() { - return this; - } - - @Override - public ActionsAttacks getAttacks() { - return this; - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p; + +import automap.IAutoMap; +import data.sounds; +import defines.skill_t; +import doom.DoomMain; +import doom.player_t; +import hu.IHeadsUp; +import i.IDoomSystem; +import java.util.logging.Level; +import java.util.logging.Logger; +import mochadoom.Loggers; +import p.Actions.ActionsAttacks; +import p.Actions.ActionsEnemies; +import p.Actions.ActionsThinkers; +import p.Actions.ActiveStates.Ai; +import p.Actions.ActiveStates.Attacks; +import p.Actions.ActiveStates.Thinkers; +import p.Actions.ActiveStates.Weapons; +import rr.SceneRenderer; +import s.ISoundOrigin; +import st.IDoomStatusBar; +import utils.TraitFactory; +import utils.TraitFactory.SharedContext; + +public class ActionFunctions extends UnifiedGameMap implements + ActionsThinkers, ActionsEnemies, ActionsAttacks, Ai, Attacks, Thinkers, Weapons { + + private static final Logger LOGGER = Loggers.getLogger(ActionFunctions.class.getName()); + + private final SharedContext traitsSharedContext; + + public ActionFunctions(final DoomMain DOOM) { + super(DOOM); + this.traitsSharedContext = buildContext(); + } + + private SharedContext buildContext() { + try { + return TraitFactory.build(this, ACTION_KEY_CHAIN); + } catch (IllegalArgumentException | IllegalAccessException ex) { + LOGGER.log(Level.SEVERE, "buildContext failure", ex); + throw new RuntimeException(ex); + } + } + + @Override + public AbstractLevelLoader levelLoader() { + return DOOM.levelLoader; + } + + @Override + public IHeadsUp headsUp() { + return DOOM.headsUp; + } + + @Override + public IDoomSystem doomSystem() { + return DOOM.doomSystem; + } + + @Override + public IDoomStatusBar statusBar() { + return DOOM.statusBar; + } + + @Override + public IAutoMap autoMap() { + return DOOM.autoMap; + } + + @Override + public SceneRenderer sceneRenderer() { + return DOOM.sceneRenderer; + } + + @Override + public UnifiedGameMap.Specials getSpecials() { + return SPECS; + } + + @Override + public UnifiedGameMap.Switches getSwitches() { + return SW; + } + + @Override + public void StopSound(ISoundOrigin origin) { + DOOM.doomSound.StopSound(origin); + } + + @Override + public void StartSound(ISoundOrigin origin, sounds.sfxenum_t s) { + DOOM.doomSound.StartSound(origin, s); + } + + @Override + public void StartSound(ISoundOrigin origin, int s) { + DOOM.doomSound.StartSound(origin, s); + } + + @Override + public player_t getPlayer(int number) { + return DOOM.players[number]; + } + + @Override + public skill_t getGameSkill() { + return DOOM.gameskill; + } + + @Override + public mobj_t createMobj() { + return mobj_t.createOn(DOOM); + } + + @Override + public int LevelTime() { + return DOOM.leveltime; + } + + @Override + public int P_Random() { + return DOOM.random.P_Random(); + } + + @Override + public int ConsolePlayerNumber() { + return DOOM.consoleplayer; + } + + @Override + public int MapNumber() { + return DOOM.gamemap; + } + + @Override + public boolean PlayerInGame(int number) { + return DOOM.playeringame[number]; + } + + @Override + public boolean IsFastParm() { + return DOOM.fastparm; + } + + @Override + public boolean IsPaused() { + return DOOM.paused; + } + + @Override + public boolean IsNetGame() { + return DOOM.netgame; + } + + @Override + public boolean IsDemoPlayback() { + return DOOM.demoplayback; + } + + @Override + public boolean IsDeathMatch() { + return DOOM.deathmatch; + } + + @Override + public boolean IsAutoMapActive() { + return DOOM.automapactive; + } + + @Override + public boolean IsMenuActive() { + return DOOM.menuactive; + } + + /** + * TODO: avoid, deprecate + */ + @Override + public DoomMain DOOM() { + return DOOM; + } + + @Override + public SharedContext getContext() { + return traitsSharedContext; + } + + @Override + public ActionsThinkers getThinkers() { + return this; + } + + @Override + public ActionsEnemies getEnemies() { + return this; + } + + @Override + public ActionsAttacks getAttacks() { + return this; + } +} \ No newline at end of file diff --git a/src/p/Actions/ActionTrait.java b/src/p/Actions/ActionTrait.java index f4fdc9b..9d17743 100644 --- a/src/p/Actions/ActionTrait.java +++ b/src/p/Actions/ActionTrait.java @@ -1,561 +1,561 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import automap.IAutoMap; -import static data.Limits.MAXRADIUS; -import static data.Limits.MAXSPECIALCROSS; -import data.sounds; -import defines.skill_t; -import doom.DoomMain; -import doom.SourceCode; -import doom.SourceCode.P_Map; -import static doom.SourceCode.P_Map.PIT_CheckLine; -import static doom.SourceCode.P_Map.P_CheckPosition; -import doom.SourceCode.P_MapUtl; -import static doom.SourceCode.P_MapUtl.P_BlockLinesIterator; -import static doom.SourceCode.P_MapUtl.P_BlockThingsIterator; -import doom.SourceCode.fixed_t; -import doom.player_t; -import hu.IHeadsUp; -import i.IDoomSystem; -import java.util.function.Predicate; -import static m.BBox.BOXBOTTOM; -import static m.BBox.BOXLEFT; -import static m.BBox.BOXRIGHT; -import static m.BBox.BOXTOP; -import p.AbstractLevelLoader; -import static p.AbstractLevelLoader.FIX_BLOCKMAP_512; -import p.ThinkerList; -import p.UnifiedGameMap; -import p.intercept_t; -import p.mobj_t; -import static p.mobj_t.MF_MISSILE; -import static p.mobj_t.MF_NOCLIP; -import rr.SceneRenderer; -import rr.line_t; -import static rr.line_t.ML_BLOCKING; -import static rr.line_t.ML_BLOCKMONSTERS; -import rr.sector_t; -import rr.subsector_t; -import s.ISoundOrigin; -import st.IDoomStatusBar; -import utils.C2JUtils; -import static utils.C2JUtils.eval; -import utils.TraitFactory; -import utils.TraitFactory.ContextKey; -import utils.TraitFactory.Trait; - -public interface ActionTrait extends Trait, ThinkerList { - - TraitFactory.KeyChain ACTION_KEY_CHAIN = new TraitFactory.KeyChain(); - - ContextKey KEY_SLIDEMOVE = ACTION_KEY_CHAIN.newKey(ActionTrait.class, SlideMove::new); - ContextKey KEY_SPECHITS = ACTION_KEY_CHAIN.newKey(ActionTrait.class, Spechits::new); - ContextKey KEY_MOVEMENT = ACTION_KEY_CHAIN.newKey(ActionTrait.class, Movement::new); - - AbstractLevelLoader levelLoader(); - - IHeadsUp headsUp(); - - IDoomSystem doomSystem(); - - IDoomStatusBar statusBar(); - - IAutoMap autoMap(); - - SceneRenderer sceneRenderer(); - - UnifiedGameMap.Specials getSpecials(); - - UnifiedGameMap.Switches getSwitches(); - - ActionsThinkers getThinkers(); - - ActionsEnemies getEnemies(); - - ActionsAttacks getAttacks(); - - void StopSound(ISoundOrigin origin); // DOOM.doomSound.StopSound - - void StartSound(ISoundOrigin origin, sounds.sfxenum_t s); // DOOM.doomSound.StartSound - - void StartSound(ISoundOrigin origin, int s); // DOOM.doomSound.StartSound - - player_t getPlayer(int number); //DOOM.players[] - - skill_t getGameSkill(); // DOOM.gameskill - - mobj_t createMobj(); // mobj_t.from(DOOM); - - int LevelTime(); // DOOM.leveltime - - int P_Random(); - - int ConsolePlayerNumber(); // DOOM.consoleplayer - - int MapNumber(); // DOOM.gamemap - - boolean PlayerInGame(int number); // DOOM.palyeringame - - boolean IsFastParm(); // DOOM.fastparm - - boolean IsPaused(); // DOOM.paused - - boolean IsNetGame(); // DOOM.netgame - - boolean IsDemoPlayback(); // DOOM.demoplayback - - boolean IsDeathMatch(); // DOOM.deathmatch - - boolean IsAutoMapActive(); // DOOM.automapactive - - boolean IsMenuActive(); // DOOM.menuactive - - boolean CheckThing(mobj_t m); - - boolean StompThing(mobj_t m); - - default void SetThingPosition(mobj_t mobj) { - levelLoader().SetThingPosition(mobj); - } - - /** - * Try to avoid. - */ - DoomMain DOOM(); - - final class SlideMove { - - // - // SLIDE MOVE - // Allows the player to slide along any angled walls. - // - mobj_t slidemo; - - @fixed_t - int bestslidefrac, secondslidefrac; - - line_t bestslideline, secondslideline; - - @fixed_t - int tmxmove, tmymove; - } - - final class Spechits { - - line_t[] spechit = new line_t[MAXSPECIALCROSS]; - int numspechit; - - // - // USE LINES - // - mobj_t usething; - } - - ///////////////// MOVEMENT'S ACTIONS //////////////////////// - final class Movement { - - /** - * If "floatok" true, move would be ok if within "tmfloorz - tmceilingz". - */ - public boolean floatok; - - @fixed_t - public int tmfloorz, - tmceilingz, - tmdropoffz; - - // keep track of the line that lowers the ceiling, - // so missiles don't explode against sky hack walls - public line_t ceilingline; - @fixed_t - int[] tmbbox = new int[4]; - - mobj_t tmthing; - - long tmflags; - - @fixed_t - int tmx, tmy; - - ////////////////////// FROM p_maputl.c //////////////////// - @fixed_t - int opentop, openbottom, openrange, lowfloor; - } - - /** - * P_LineOpening Sets opentop and openbottom to the window through a two - * sided line. OPTIMIZE: keep this precalculated - */ - default void LineOpening(line_t linedef) { - final Movement ma = contextRequire(KEY_MOVEMENT); - sector_t front; - sector_t back; - - if (linedef.sidenum[1] == line_t.NO_INDEX) { - // single sided line - ma.openrange = 0; - return; - } - - front = linedef.frontsector; - back = linedef.backsector; - - if (front.ceilingheight < back.ceilingheight) { - ma.opentop = front.ceilingheight; - } else { - ma.opentop = back.ceilingheight; - } - - if (front.floorheight > back.floorheight) { - ma.openbottom = front.floorheight; - ma.lowfloor = back.floorheight; - } else { - ma.openbottom = back.floorheight; - ma.lowfloor = front.floorheight; - } - - ma.openrange = ma.opentop - ma.openbottom; - } - - // - //P_BlockThingsIterator - // - @SourceCode.Exact - @P_MapUtl.C(P_BlockThingsIterator) - default boolean BlockThingsIterator(int x, int y, Predicate func) { - final AbstractLevelLoader ll = levelLoader(); - mobj_t mobj; - - if (x < 0 || y < 0 || x >= ll.bmapwidth || y >= ll.bmapheight) { - return true; - } - - for (mobj = ll.blocklinks[y * ll.bmapwidth + x]; mobj != null; mobj = (mobj_t) mobj.bnext) { - if (!func.test(mobj)) { - return false; - } - } - return true; - } - - // - // SECTOR HEIGHT CHANGING - // After modifying a sectors floor or ceiling height, - // call this routine to adjust the positions - // of all things that touch the sector. - // - // If anything doesn't fit anymore, true will be returned. - // If crunch is true, they will take damage - // as they are being crushed. - // If Crunch is false, you should set the sector height back - // the way it was and call P_ChangeSector again - // to undo the changes. - // - /** - * P_BlockLinesIterator The validcount flags are used to avoid checking lines that are marked in multiple mapblocks, - * so increment validcount before the first call to P_BlockLinesIterator, then make one or more calls to it. - */ - @P_MapUtl.C(P_BlockLinesIterator) - default boolean BlockLinesIterator(int x, int y, Predicate func) { - final AbstractLevelLoader ll = levelLoader(); - final SceneRenderer sr = sceneRenderer(); - int offset; - int lineinblock; - line_t ld; - - if (x < 0 || y < 0 || x >= ll.bmapwidth || y >= ll.bmapheight) { - return true; - } - - // This gives us the index to look up (in blockmap) - offset = y * ll.bmapwidth + x; - - // The index contains yet another offset, but this time - offset = ll.blockmap[offset]; - - // MAES: blockmap terminating marker is always -1 - @SourceCode.Compatible("validcount") - final int validcount = sr.getValidCount(); - - // [SYNC ISSUE]: don't skip offset+1 :-/ - for (@SourceCode.Compatible("list = blockmaplump+offset ; *list != -1 ; list++") int list = offset; (lineinblock = ll.blockmap[list]) != -1; list++) { - ld = ll.lines[lineinblock]; - //System.out.println(ld); - if (ld.validcount == validcount) { - continue; // line has already been checked - } - ld.validcount = validcount; - if (!func.test(ld)) { - return false; - } - } - return true; // everything was checked - } - - // keep track of the line that lowers the ceiling, - // so missiles don't explode against sky hack walls - default void ResizeSpechits() { - final Spechits spechits = contextRequire(KEY_SPECHITS); - spechits.spechit = C2JUtils.resize(spechits.spechit[0], spechits.spechit, spechits.spechit.length * 2); - } - - /** - * PIT_CheckLine Adjusts tmfloorz and tmceilingz as lines are contacted - * - */ - @P_Map.C(PIT_CheckLine) - default boolean CheckLine(line_t ld) { - final Spechits spechits = contextRequire(KEY_SPECHITS); - final Movement ma = contextRequire(KEY_MOVEMENT); - - if (ma.tmbbox[BOXRIGHT] <= ld.bbox[BOXLEFT] - || ma.tmbbox[BOXLEFT] >= ld.bbox[BOXRIGHT] - || ma.tmbbox[BOXTOP] <= ld.bbox[BOXBOTTOM] - || ma.tmbbox[BOXBOTTOM] >= ld.bbox[BOXTOP]) { - return true; - } - - if (ld.BoxOnLineSide(ma.tmbbox) != -1) { - return true; - } - - // A line has been hit - // The moving thing's destination position will cross - // the given line. - // If this should not be allowed, return false. - // If the line is special, keep track of it - // to process later if the move is proven ok. - // NOTE: specials are NOT sorted by order, - // so two special lines that are only 8 pixels apart - // could be crossed in either order. - if (ld.backsector == null) { - return false; // one sided line - } - if (!eval(ma.tmthing.flags & MF_MISSILE)) { - if (eval(ld.flags & ML_BLOCKING)) { - return false; // explicitly blocking everything - } - if ((ma.tmthing.player == null) && eval(ld.flags & ML_BLOCKMONSTERS)) { - return false; // block monsters only - } - } - - // set openrange, opentop, openbottom - LineOpening(ld); - - // adjust floor / ceiling heights - if (ma.opentop < ma.tmceilingz) { - ma.tmceilingz = ma.opentop; - ma.ceilingline = ld; - } - - if (ma.openbottom > ma.tmfloorz) { - ma.tmfloorz = ma.openbottom; - } - - if (ma.lowfloor < ma.tmdropoffz) { - ma.tmdropoffz = ma.lowfloor; - } - - // if contacted a special line, add it to the list - if (ld.special != 0) { - spechits.spechit[spechits.numspechit] = ld; - spechits.numspechit++; - // Let's be proactive about this. - if (spechits.numspechit >= spechits.spechit.length) { - this.ResizeSpechits(); - } - } - - return true; - } - - ; - - // - // MOVEMENT CLIPPING - // - /** - * P_CheckPosition This is purely informative, nothing is modified (except things picked up). - * - * in: a mobj_t (can be valid or invalid) a position to be checked (doesn't need to be related to the mobj_t.x,y) - * - * during: special things are touched if MF_PICKUP early out on solid lines? - * - * out: newsubsec floorz ceilingz tmdropoffz the lowest point contacted (monsters won't move to a dropoff) - * speciallines[] numspeciallines - * - * @param thing - * @param x fixed_t - * @param y fixed_t - */ - @SourceCode.Compatible - @P_Map.C(P_CheckPosition) - default boolean CheckPosition(mobj_t thing, @fixed_t int x, @fixed_t int y) { - final AbstractLevelLoader ll = levelLoader(); - final Spechits spechits = contextRequire(KEY_SPECHITS); - final Movement ma = contextRequire(KEY_MOVEMENT); - int xl; - int xh; - int yl; - int yh; - int bx; - int by; - subsector_t newsubsec; - - ma.tmthing = thing; - ma.tmflags = thing.flags; - - ma.tmx = x; - ma.tmy = y; - - ma.tmbbox[BOXTOP] = y + ma.tmthing.radius; - ma.tmbbox[BOXBOTTOM] = y - ma.tmthing.radius; - ma.tmbbox[BOXRIGHT] = x + ma.tmthing.radius; - ma.tmbbox[BOXLEFT] = x - ma.tmthing.radius; - - R_PointInSubsector: - { - newsubsec = levelLoader().PointInSubsector(x, y); - } - ma.ceilingline = null; - - // The base floor / ceiling is from the subsector - // that contains the point. - // Any contacted lines the step closer together - // will adjust them. - ma.tmfloorz = ma.tmdropoffz = newsubsec.sector.floorheight; - ma.tmceilingz = newsubsec.sector.ceilingheight; - - sceneRenderer().increaseValidCount(1); - spechits.numspechit = 0; - - if (eval(ma.tmflags & MF_NOCLIP)) { - return true; - } - - // Check things first, possibly picking things up. - // The bounding box is extended by MAXRADIUS - // because mobj_ts are grouped into mapblocks - // based on their origin point, and can overlap - // into adjacent blocks by up to MAXRADIUS units. - xl = ll.getSafeBlockX(ma.tmbbox[BOXLEFT] - ll.bmaporgx - MAXRADIUS); - xh = ll.getSafeBlockX(ma.tmbbox[BOXRIGHT] - ll.bmaporgx + MAXRADIUS); - yl = ll.getSafeBlockY(ma.tmbbox[BOXBOTTOM] - ll.bmaporgy - MAXRADIUS); - yh = ll.getSafeBlockY(ma.tmbbox[BOXTOP] - ll.bmaporgy + MAXRADIUS); - - for (bx = xl; bx <= xh; bx++) { - for (by = yl; by <= yh; by++) { - P_BlockThingsIterator: - { - if (!BlockThingsIterator(bx, by, this::CheckThing)) { - return false; - } - } - } - } - - // check lines - xl = ll.getSafeBlockX(ma.tmbbox[BOXLEFT] - ll.bmaporgx); - xh = ll.getSafeBlockX(ma.tmbbox[BOXRIGHT] - ll.bmaporgx); - yl = ll.getSafeBlockY(ma.tmbbox[BOXBOTTOM] - ll.bmaporgy); - yh = ll.getSafeBlockY(ma.tmbbox[BOXTOP] - ll.bmaporgy); - - if (FIX_BLOCKMAP_512) { - // Maes's quick and dirty blockmap extension hack - // E.g. for an extension of 511 blocks, max negative is -1. - // A full 512x512 blockmap doesn't have negative indexes. - if (xl <= ll.blockmapxneg) { - xl = 0x1FF & xl; // Broke width boundary - } - if (xh <= ll.blockmapxneg) { - xh = 0x1FF & xh; // Broke width boundary - } - if (yl <= ll.blockmapyneg) { - yl = 0x1FF & yl; // Broke height boundary - } - if (yh <= ll.blockmapyneg) { - yh = 0x1FF & yh; // Broke height boundary - } - } - for (bx = xl; bx <= xh; bx++) { - for (by = yl; by <= yh; by++) { - P_BlockLinesIterator: - { - if (!this.BlockLinesIterator(bx, by, this::CheckLine)) { - return false; - } - } - } - } - - return true; - } - - // - // P_ThingHeightClip - // Takes a valid thing and adjusts the thing.floorz, - // thing.ceilingz, and possibly thing.z. - // This is called for all nearby monsters - // whenever a sector changes height. - // If the thing doesn't fit, - // the z will be set to the lowest value - // and false will be returned. - // - default boolean ThingHeightClip(mobj_t thing) { - final Movement ma = contextRequire(KEY_MOVEMENT); - boolean onfloor; - - onfloor = (thing.z == thing.floorz); - - this.CheckPosition(thing, thing.x, thing.y); - // what about stranding a monster partially off an edge? - - thing.floorz = ma.tmfloorz; - thing.ceilingz = ma.tmceilingz; - - if (onfloor) { - // walking monsters rise and fall with the floor - thing.z = thing.floorz; - } else { - // don't adjust a floating monster unless forced to - if (thing.z + thing.height > thing.ceilingz) { - thing.z = thing.ceilingz - thing.height; - } - } - - return thing.ceilingz - thing.floorz >= thing.height; - } - - default boolean isblocking(intercept_t in, line_t li) { - final SlideMove slideMove = contextRequire(KEY_SLIDEMOVE); - // the line does block movement, - // see if it is closer than best so far - - if (in.frac < slideMove.bestslidefrac) { - slideMove.secondslidefrac = slideMove.bestslidefrac; - slideMove.secondslideline = slideMove.bestslideline; - slideMove.bestslidefrac = in.frac; - slideMove.bestslideline = li; - } - - return false; // stop - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import automap.IAutoMap; +import static data.Limits.MAXRADIUS; +import static data.Limits.MAXSPECIALCROSS; +import data.sounds; +import defines.skill_t; +import doom.DoomMain; +import doom.SourceCode; +import doom.SourceCode.P_Map; +import static doom.SourceCode.P_Map.PIT_CheckLine; +import static doom.SourceCode.P_Map.P_CheckPosition; +import doom.SourceCode.P_MapUtl; +import static doom.SourceCode.P_MapUtl.P_BlockLinesIterator; +import static doom.SourceCode.P_MapUtl.P_BlockThingsIterator; +import doom.SourceCode.fixed_t; +import doom.player_t; +import hu.IHeadsUp; +import i.IDoomSystem; +import java.util.function.Predicate; +import static m.BBox.BOXBOTTOM; +import static m.BBox.BOXLEFT; +import static m.BBox.BOXRIGHT; +import static m.BBox.BOXTOP; +import p.AbstractLevelLoader; +import static p.AbstractLevelLoader.FIX_BLOCKMAP_512; +import p.ThinkerList; +import p.UnifiedGameMap; +import p.intercept_t; +import p.mobj_t; +import static p.mobj_t.MF_MISSILE; +import static p.mobj_t.MF_NOCLIP; +import rr.SceneRenderer; +import rr.line_t; +import static rr.line_t.ML_BLOCKING; +import static rr.line_t.ML_BLOCKMONSTERS; +import rr.sector_t; +import rr.subsector_t; +import s.ISoundOrigin; +import st.IDoomStatusBar; +import utils.C2JUtils; +import static utils.C2JUtils.eval; +import utils.TraitFactory; +import utils.TraitFactory.ContextKey; +import utils.TraitFactory.Trait; + +public interface ActionTrait extends Trait, ThinkerList { + + TraitFactory.KeyChain ACTION_KEY_CHAIN = new TraitFactory.KeyChain(); + + ContextKey KEY_SLIDEMOVE = ACTION_KEY_CHAIN.newKey(ActionTrait.class, SlideMove::new); + ContextKey KEY_SPECHITS = ACTION_KEY_CHAIN.newKey(ActionTrait.class, Spechits::new); + ContextKey KEY_MOVEMENT = ACTION_KEY_CHAIN.newKey(ActionTrait.class, Movement::new); + + AbstractLevelLoader levelLoader(); + + IHeadsUp headsUp(); + + IDoomSystem doomSystem(); + + IDoomStatusBar statusBar(); + + IAutoMap autoMap(); + + SceneRenderer sceneRenderer(); + + UnifiedGameMap.Specials getSpecials(); + + UnifiedGameMap.Switches getSwitches(); + + ActionsThinkers getThinkers(); + + ActionsEnemies getEnemies(); + + ActionsAttacks getAttacks(); + + void StopSound(ISoundOrigin origin); // DOOM.doomSound.StopSound + + void StartSound(ISoundOrigin origin, sounds.sfxenum_t s); // DOOM.doomSound.StartSound + + void StartSound(ISoundOrigin origin, int s); // DOOM.doomSound.StartSound + + player_t getPlayer(int number); //DOOM.players[] + + skill_t getGameSkill(); // DOOM.gameskill + + mobj_t createMobj(); // mobj_t.from(DOOM); + + int LevelTime(); // DOOM.leveltime + + int P_Random(); + + int ConsolePlayerNumber(); // DOOM.consoleplayer + + int MapNumber(); // DOOM.gamemap + + boolean PlayerInGame(int number); // DOOM.palyeringame + + boolean IsFastParm(); // DOOM.fastparm + + boolean IsPaused(); // DOOM.paused + + boolean IsNetGame(); // DOOM.netgame + + boolean IsDemoPlayback(); // DOOM.demoplayback + + boolean IsDeathMatch(); // DOOM.deathmatch + + boolean IsAutoMapActive(); // DOOM.automapactive + + boolean IsMenuActive(); // DOOM.menuactive + + boolean CheckThing(mobj_t m); + + boolean StompThing(mobj_t m); + + default void SetThingPosition(mobj_t mobj) { + levelLoader().SetThingPosition(mobj); + } + + /** + * Try to avoid. + */ + DoomMain DOOM(); + + final class SlideMove { + + // + // SLIDE MOVE + // Allows the player to slide along any angled walls. + // + mobj_t slidemo; + + @fixed_t + int bestslidefrac, secondslidefrac; + + line_t bestslideline, secondslideline; + + @fixed_t + int tmxmove, tmymove; + } + + final class Spechits { + + line_t[] spechit = new line_t[MAXSPECIALCROSS]; + int numspechit; + + // + // USE LINES + // + mobj_t usething; + } + + ///////////////// MOVEMENT'S ACTIONS //////////////////////// + final class Movement { + + /** + * If "floatok" true, move would be ok if within "tmfloorz - tmceilingz". + */ + public boolean floatok; + + @fixed_t + public int tmfloorz, + tmceilingz, + tmdropoffz; + + // keep track of the line that lowers the ceiling, + // so missiles don't explode against sky hack walls + public line_t ceilingline; + @fixed_t + int[] tmbbox = new int[4]; + + mobj_t tmthing; + + long tmflags; + + @fixed_t + int tmx, tmy; + + ////////////////////// FROM p_maputl.c //////////////////// + @fixed_t + int opentop, openbottom, openrange, lowfloor; + } + + /** + * P_LineOpening Sets opentop and openbottom to the window through a two + * sided line. OPTIMIZE: keep this precalculated + */ + default void LineOpening(line_t linedef) { + final Movement ma = contextRequire(KEY_MOVEMENT); + sector_t front; + sector_t back; + + if (linedef.sidenum[1] == line_t.NO_INDEX) { + // single sided line + ma.openrange = 0; + return; + } + + front = linedef.frontsector; + back = linedef.backsector; + + if (front.ceilingheight < back.ceilingheight) { + ma.opentop = front.ceilingheight; + } else { + ma.opentop = back.ceilingheight; + } + + if (front.floorheight > back.floorheight) { + ma.openbottom = front.floorheight; + ma.lowfloor = back.floorheight; + } else { + ma.openbottom = back.floorheight; + ma.lowfloor = front.floorheight; + } + + ma.openrange = ma.opentop - ma.openbottom; + } + + // + //P_BlockThingsIterator + // + @SourceCode.Exact + @P_MapUtl.C(P_BlockThingsIterator) + default boolean BlockThingsIterator(int x, int y, Predicate func) { + final AbstractLevelLoader ll = levelLoader(); + mobj_t mobj; + + if (x < 0 || y < 0 || x >= ll.bmapwidth || y >= ll.bmapheight) { + return true; + } + + for (mobj = ll.blocklinks[y * ll.bmapwidth + x]; mobj != null; mobj = (mobj_t) mobj.bnext) { + if (!func.test(mobj)) { + return false; + } + } + return true; + } + + // + // SECTOR HEIGHT CHANGING + // After modifying a sectors floor or ceiling height, + // call this routine to adjust the positions + // of all things that touch the sector. + // + // If anything doesn't fit anymore, true will be returned. + // If crunch is true, they will take damage + // as they are being crushed. + // If Crunch is false, you should set the sector height back + // the way it was and call P_ChangeSector again + // to undo the changes. + // + /** + * P_BlockLinesIterator The validcount flags are used to avoid checking lines that are marked in multiple mapblocks, + * so increment validcount before the first call to P_BlockLinesIterator, then make one or more calls to it. + */ + @P_MapUtl.C(P_BlockLinesIterator) + default boolean BlockLinesIterator(int x, int y, Predicate func) { + final AbstractLevelLoader ll = levelLoader(); + final SceneRenderer sr = sceneRenderer(); + int offset; + int lineinblock; + line_t ld; + + if (x < 0 || y < 0 || x >= ll.bmapwidth || y >= ll.bmapheight) { + return true; + } + + // This gives us the index to look up (in blockmap) + offset = y * ll.bmapwidth + x; + + // The index contains yet another offset, but this time + offset = ll.blockmap[offset]; + + // MAES: blockmap terminating marker is always -1 + @SourceCode.Compatible("validcount") + final int validcount = sr.getValidCount(); + + // [SYNC ISSUE]: don't skip offset+1 :-/ + for (@SourceCode.Compatible("list = blockmaplump+offset ; *list != -1 ; list++") int list = offset; (lineinblock = ll.blockmap[list]) != -1; list++) { + ld = ll.lines[lineinblock]; + //System.out.println(ld); + if (ld.validcount == validcount) { + continue; // line has already been checked + } + ld.validcount = validcount; + if (!func.test(ld)) { + return false; + } + } + return true; // everything was checked + } + + // keep track of the line that lowers the ceiling, + // so missiles don't explode against sky hack walls + default void ResizeSpechits() { + final Spechits spechits = contextRequire(KEY_SPECHITS); + spechits.spechit = C2JUtils.resize(spechits.spechit[0], spechits.spechit, spechits.spechit.length * 2); + } + + /** + * PIT_CheckLine Adjusts tmfloorz and tmceilingz as lines are contacted + * + */ + @P_Map.C(PIT_CheckLine) + default boolean CheckLine(line_t ld) { + final Spechits spechits = contextRequire(KEY_SPECHITS); + final Movement ma = contextRequire(KEY_MOVEMENT); + + if (ma.tmbbox[BOXRIGHT] <= ld.bbox[BOXLEFT] + || ma.tmbbox[BOXLEFT] >= ld.bbox[BOXRIGHT] + || ma.tmbbox[BOXTOP] <= ld.bbox[BOXBOTTOM] + || ma.tmbbox[BOXBOTTOM] >= ld.bbox[BOXTOP]) { + return true; + } + + if (ld.BoxOnLineSide(ma.tmbbox) != -1) { + return true; + } + + // A line has been hit + // The moving thing's destination position will cross + // the given line. + // If this should not be allowed, return false. + // If the line is special, keep track of it + // to process later if the move is proven ok. + // NOTE: specials are NOT sorted by order, + // so two special lines that are only 8 pixels apart + // could be crossed in either order. + if (ld.backsector == null) { + return false; // one sided line + } + if (!eval(ma.tmthing.flags & MF_MISSILE)) { + if (eval(ld.flags & ML_BLOCKING)) { + return false; // explicitly blocking everything + } + if ((ma.tmthing.player == null) && eval(ld.flags & ML_BLOCKMONSTERS)) { + return false; // block monsters only + } + } + + // set openrange, opentop, openbottom + LineOpening(ld); + + // adjust floor / ceiling heights + if (ma.opentop < ma.tmceilingz) { + ma.tmceilingz = ma.opentop; + ma.ceilingline = ld; + } + + if (ma.openbottom > ma.tmfloorz) { + ma.tmfloorz = ma.openbottom; + } + + if (ma.lowfloor < ma.tmdropoffz) { + ma.tmdropoffz = ma.lowfloor; + } + + // if contacted a special line, add it to the list + if (ld.special != 0) { + spechits.spechit[spechits.numspechit] = ld; + spechits.numspechit++; + // Let's be proactive about this. + if (spechits.numspechit >= spechits.spechit.length) { + this.ResizeSpechits(); + } + } + + return true; + } + + ; + + // + // MOVEMENT CLIPPING + // + /** + * P_CheckPosition This is purely informative, nothing is modified (except things picked up). + * + * in: a mobj_t (can be valid or invalid) a position to be checked (doesn't need to be related to the mobj_t.x,y) + * + * during: special things are touched if MF_PICKUP early out on solid lines? + * + * out: newsubsec floorz ceilingz tmdropoffz the lowest point contacted (monsters won't move to a dropoff) + * speciallines[] numspeciallines + * + * @param thing + * @param x fixed_t + * @param y fixed_t + */ + @SourceCode.Compatible + @P_Map.C(P_CheckPosition) + default boolean CheckPosition(mobj_t thing, @fixed_t int x, @fixed_t int y) { + final AbstractLevelLoader ll = levelLoader(); + final Spechits spechits = contextRequire(KEY_SPECHITS); + final Movement ma = contextRequire(KEY_MOVEMENT); + int xl; + int xh; + int yl; + int yh; + int bx; + int by; + subsector_t newsubsec; + + ma.tmthing = thing; + ma.tmflags = thing.flags; + + ma.tmx = x; + ma.tmy = y; + + ma.tmbbox[BOXTOP] = y + ma.tmthing.radius; + ma.tmbbox[BOXBOTTOM] = y - ma.tmthing.radius; + ma.tmbbox[BOXRIGHT] = x + ma.tmthing.radius; + ma.tmbbox[BOXLEFT] = x - ma.tmthing.radius; + + R_PointInSubsector: + { + newsubsec = levelLoader().PointInSubsector(x, y); + } + ma.ceilingline = null; + + // The base floor / ceiling is from the subsector + // that contains the point. + // Any contacted lines the step closer together + // will adjust them. + ma.tmfloorz = ma.tmdropoffz = newsubsec.sector.floorheight; + ma.tmceilingz = newsubsec.sector.ceilingheight; + + sceneRenderer().increaseValidCount(1); + spechits.numspechit = 0; + + if (eval(ma.tmflags & MF_NOCLIP)) { + return true; + } + + // Check things first, possibly picking things up. + // The bounding box is extended by MAXRADIUS + // because mobj_ts are grouped into mapblocks + // based on their origin point, and can overlap + // into adjacent blocks by up to MAXRADIUS units. + xl = ll.getSafeBlockX(ma.tmbbox[BOXLEFT] - ll.bmaporgx - MAXRADIUS); + xh = ll.getSafeBlockX(ma.tmbbox[BOXRIGHT] - ll.bmaporgx + MAXRADIUS); + yl = ll.getSafeBlockY(ma.tmbbox[BOXBOTTOM] - ll.bmaporgy - MAXRADIUS); + yh = ll.getSafeBlockY(ma.tmbbox[BOXTOP] - ll.bmaporgy + MAXRADIUS); + + for (bx = xl; bx <= xh; bx++) { + for (by = yl; by <= yh; by++) { + P_BlockThingsIterator: + { + if (!BlockThingsIterator(bx, by, this::CheckThing)) { + return false; + } + } + } + } + + // check lines + xl = ll.getSafeBlockX(ma.tmbbox[BOXLEFT] - ll.bmaporgx); + xh = ll.getSafeBlockX(ma.tmbbox[BOXRIGHT] - ll.bmaporgx); + yl = ll.getSafeBlockY(ma.tmbbox[BOXBOTTOM] - ll.bmaporgy); + yh = ll.getSafeBlockY(ma.tmbbox[BOXTOP] - ll.bmaporgy); + + if (FIX_BLOCKMAP_512) { + // Maes's quick and dirty blockmap extension hack + // E.g. for an extension of 511 blocks, max negative is -1. + // A full 512x512 blockmap doesn't have negative indexes. + if (xl <= ll.blockmapxneg) { + xl = 0x1FF & xl; // Broke width boundary + } + if (xh <= ll.blockmapxneg) { + xh = 0x1FF & xh; // Broke width boundary + } + if (yl <= ll.blockmapyneg) { + yl = 0x1FF & yl; // Broke height boundary + } + if (yh <= ll.blockmapyneg) { + yh = 0x1FF & yh; // Broke height boundary + } + } + for (bx = xl; bx <= xh; bx++) { + for (by = yl; by <= yh; by++) { + P_BlockLinesIterator: + { + if (!this.BlockLinesIterator(bx, by, this::CheckLine)) { + return false; + } + } + } + } + + return true; + } + + // + // P_ThingHeightClip + // Takes a valid thing and adjusts the thing.floorz, + // thing.ceilingz, and possibly thing.z. + // This is called for all nearby monsters + // whenever a sector changes height. + // If the thing doesn't fit, + // the z will be set to the lowest value + // and false will be returned. + // + default boolean ThingHeightClip(mobj_t thing) { + final Movement ma = contextRequire(KEY_MOVEMENT); + boolean onfloor; + + onfloor = (thing.z == thing.floorz); + + this.CheckPosition(thing, thing.x, thing.y); + // what about stranding a monster partially off an edge? + + thing.floorz = ma.tmfloorz; + thing.ceilingz = ma.tmceilingz; + + if (onfloor) { + // walking monsters rise and fall with the floor + thing.z = thing.floorz; + } else { + // don't adjust a floating monster unless forced to + if (thing.z + thing.height > thing.ceilingz) { + thing.z = thing.ceilingz - thing.height; + } + } + + return thing.ceilingz - thing.floorz >= thing.height; + } + + default boolean isblocking(intercept_t in, line_t li) { + final SlideMove slideMove = contextRequire(KEY_SLIDEMOVE); + // the line does block movement, + // see if it is closer than best so far + + if (in.frac < slideMove.bestslidefrac) { + slideMove.secondslidefrac = slideMove.bestslidefrac; + slideMove.secondslideline = slideMove.bestslideline; + slideMove.bestslidefrac = in.frac; + slideMove.bestslideline = li; + } + + return false; // stop + } +} \ No newline at end of file diff --git a/src/p/Actions/ActionsAim.java b/src/p/Actions/ActionsAim.java index 2ef9ae8..f486500 100644 --- a/src/p/Actions/ActionsAim.java +++ b/src/p/Actions/ActionsAim.java @@ -1,191 +1,191 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import static data.Defines.PT_ADDLINES; -import static data.Defines.PT_ADDTHINGS; -import static data.Tables.BITS32; -import static data.Tables.finecosine; -import static data.Tables.finesine; -import doom.SourceCode.P_Map; -import static doom.SourceCode.P_Map.PTR_AimTraverse; -import static m.fixed_t.FRACBITS; -import static m.fixed_t.FRACUNIT; -import static m.fixed_t.FixedDiv; -import static m.fixed_t.FixedMul; -import p.intercept_t; -import p.mobj_t; -import static p.mobj_t.MF_SHOOTABLE; -import rr.line_t; -import static rr.line_t.ML_TWOSIDED; -import static utils.C2JUtils.eval; - -public interface ActionsAim extends ActionsMissiles { - - /** - * P_AimLineAttack - * - * @param t1 - * @param angle long - * @param distance int - */ - @Override - default int AimLineAttack(mobj_t t1, long angle, int distance) { - final Spawn targ = contextRequire(KEY_SPAWN); - int x2, y2; - targ.shootthing = t1; - - x2 = t1.x + (distance >> FRACBITS) * finecosine(angle); - y2 = t1.y + (distance >> FRACBITS) * finesine(angle); - targ.shootz = t1.z + (t1.height >> 1) + 8 * FRACUNIT; - - // can't shoot outside view angles - targ.topslope = 100 * FRACUNIT / 160; - targ.bottomslope = -100 * FRACUNIT / 160; - - targ.attackrange = distance; - targ.linetarget = null; - - PathTraverse(t1.x, t1.y, x2, y2, PT_ADDLINES | PT_ADDTHINGS, this::AimTraverse); - - if (targ.linetarget != null) { - return targ.aimslope; - } - - return 0; - } - - // - // P_BulletSlope - // Sets a slope so a near miss is at aproximately - // the height of the intended target - // - default void P_BulletSlope(mobj_t mo) { - final Spawn targ = contextRequire(KEY_SPAWN); - long an; - - // see which target is to be aimed at - // FIXME: angle can already be negative here. - // Not a problem if it's just moving about (accumulation will work) - // but it needs to be sanitized before being used in any function. - an = mo.angle; - //_D_: &BITS32 will be used later in this function, by fine(co)sine() - targ.bulletslope = AimLineAttack(mo, an/*&BITS32*/, 16 * 64 * FRACUNIT); - - if (!eval(targ.linetarget)) { - an += 1 << 26; - targ.bulletslope = AimLineAttack(mo, an/*&BITS32*/, 16 * 64 * FRACUNIT); - if (!eval(targ.linetarget)) { - an -= 2 << 26; - targ.bulletslope = AimLineAttack(mo, an/*&BITS32*/, 16 * 64 * FRACUNIT); - } - - // Give it one more try, with freelook - if (mo.player.lookdir != 0 && !eval(targ.linetarget)) { - an += 2 << 26; - an &= BITS32; - targ.bulletslope = (mo.player.lookdir << FRACBITS) / 173; - } - } - } - - ////////////////// PTR Traverse Interception Functions /////////////////////// - // Height if not aiming up or down - // ???: use slope for monsters? - @P_Map.C(PTR_AimTraverse) - default boolean AimTraverse(intercept_t in) { - final Movement mov = contextRequire(KEY_MOVEMENT); - final Spawn targ = contextRequire(KEY_SPAWN); - - line_t li; - mobj_t th; - int slope; - int thingtopslope; - int thingbottomslope; - int dist; - - if (in.isaline) { - li = (line_t) in.d(); - - if (!eval(li.flags & ML_TWOSIDED)) { - return false; // stop - } - // Crosses a two sided line. - // A two sided line will restrict - // the possible target ranges. - LineOpening(li); - - if (mov.openbottom >= mov.opentop) { - return false; // stop - } - dist = FixedMul(targ.attackrange, in.frac); - - if (li.frontsector.floorheight != li.backsector.floorheight) { - slope = FixedDiv(mov.openbottom - targ.shootz, dist); - if (slope > targ.bottomslope) { - targ.bottomslope = slope; - } - } - - if (li.frontsector.ceilingheight != li.backsector.ceilingheight) { - slope = FixedDiv(mov.opentop - targ.shootz, dist); - if (slope < targ.topslope) { - targ.topslope = slope; - } - } - - // determine whether shot continues - return targ.topslope > targ.bottomslope; - } - - // shoot a thing - th = (mobj_t) in.d(); - if (th == targ.shootthing) { - return true; // can't shoot self - } - if (!eval(th.flags & MF_SHOOTABLE)) { - return true; // corpse or something - } - // check angles to see if the thing can be aimed at - dist = FixedMul(targ.attackrange, in.frac); - thingtopslope = FixedDiv(th.z + th.height - targ.shootz, dist); - - if (thingtopslope < targ.bottomslope) { - return true; // shot over the thing - } - thingbottomslope = FixedDiv(th.z - targ.shootz, dist); - - if (thingbottomslope > targ.topslope) { - return true; // shot under the thing - } - // this thing can be hit! - if (thingtopslope > targ.topslope) { - thingtopslope = targ.topslope; - } - - if (thingbottomslope < targ.bottomslope) { - thingbottomslope = targ.bottomslope; - } - - targ.aimslope = (thingtopslope + thingbottomslope) / 2; - targ.linetarget = th; - - return false; // don't go any farther - } - -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import static data.Defines.PT_ADDLINES; +import static data.Defines.PT_ADDTHINGS; +import static data.Tables.BITS32; +import static data.Tables.finecosine; +import static data.Tables.finesine; +import doom.SourceCode.P_Map; +import static doom.SourceCode.P_Map.PTR_AimTraverse; +import static m.fixed_t.FRACBITS; +import static m.fixed_t.FRACUNIT; +import static m.fixed_t.FixedDiv; +import static m.fixed_t.FixedMul; +import p.intercept_t; +import p.mobj_t; +import static p.mobj_t.MF_SHOOTABLE; +import rr.line_t; +import static rr.line_t.ML_TWOSIDED; +import static utils.C2JUtils.eval; + +public interface ActionsAim extends ActionsMissiles { + + /** + * P_AimLineAttack + * + * @param t1 + * @param angle long + * @param distance int + */ + @Override + default int AimLineAttack(mobj_t t1, long angle, int distance) { + final Spawn targ = contextRequire(KEY_SPAWN); + int x2, y2; + targ.shootthing = t1; + + x2 = t1.x + (distance >> FRACBITS) * finecosine(angle); + y2 = t1.y + (distance >> FRACBITS) * finesine(angle); + targ.shootz = t1.z + (t1.height >> 1) + 8 * FRACUNIT; + + // can't shoot outside view angles + targ.topslope = 100 * FRACUNIT / 160; + targ.bottomslope = -100 * FRACUNIT / 160; + + targ.attackrange = distance; + targ.linetarget = null; + + PathTraverse(t1.x, t1.y, x2, y2, PT_ADDLINES | PT_ADDTHINGS, this::AimTraverse); + + if (targ.linetarget != null) { + return targ.aimslope; + } + + return 0; + } + + // + // P_BulletSlope + // Sets a slope so a near miss is at aproximately + // the height of the intended target + // + default void P_BulletSlope(mobj_t mo) { + final Spawn targ = contextRequire(KEY_SPAWN); + long an; + + // see which target is to be aimed at + // FIXME: angle can already be negative here. + // Not a problem if it's just moving about (accumulation will work) + // but it needs to be sanitized before being used in any function. + an = mo.angle; + //_D_: &BITS32 will be used later in this function, by fine(co)sine() + targ.bulletslope = AimLineAttack(mo, an/*&BITS32*/, 16 * 64 * FRACUNIT); + + if (!eval(targ.linetarget)) { + an += 1 << 26; + targ.bulletslope = AimLineAttack(mo, an/*&BITS32*/, 16 * 64 * FRACUNIT); + if (!eval(targ.linetarget)) { + an -= 2 << 26; + targ.bulletslope = AimLineAttack(mo, an/*&BITS32*/, 16 * 64 * FRACUNIT); + } + + // Give it one more try, with freelook + if (mo.player.lookdir != 0 && !eval(targ.linetarget)) { + an += 2 << 26; + an &= BITS32; + targ.bulletslope = (mo.player.lookdir << FRACBITS) / 173; + } + } + } + + ////////////////// PTR Traverse Interception Functions /////////////////////// + // Height if not aiming up or down + // ???: use slope for monsters? + @P_Map.C(PTR_AimTraverse) + default boolean AimTraverse(intercept_t in) { + final Movement mov = contextRequire(KEY_MOVEMENT); + final Spawn targ = contextRequire(KEY_SPAWN); + + line_t li; + mobj_t th; + int slope; + int thingtopslope; + int thingbottomslope; + int dist; + + if (in.isaline) { + li = (line_t) in.d(); + + if (!eval(li.flags & ML_TWOSIDED)) { + return false; // stop + } + // Crosses a two sided line. + // A two sided line will restrict + // the possible target ranges. + LineOpening(li); + + if (mov.openbottom >= mov.opentop) { + return false; // stop + } + dist = FixedMul(targ.attackrange, in.frac); + + if (li.frontsector.floorheight != li.backsector.floorheight) { + slope = FixedDiv(mov.openbottom - targ.shootz, dist); + if (slope > targ.bottomslope) { + targ.bottomslope = slope; + } + } + + if (li.frontsector.ceilingheight != li.backsector.ceilingheight) { + slope = FixedDiv(mov.opentop - targ.shootz, dist); + if (slope < targ.topslope) { + targ.topslope = slope; + } + } + + // determine whether shot continues + return targ.topslope > targ.bottomslope; + } + + // shoot a thing + th = (mobj_t) in.d(); + if (th == targ.shootthing) { + return true; // can't shoot self + } + if (!eval(th.flags & MF_SHOOTABLE)) { + return true; // corpse or something + } + // check angles to see if the thing can be aimed at + dist = FixedMul(targ.attackrange, in.frac); + thingtopslope = FixedDiv(th.z + th.height - targ.shootz, dist); + + if (thingtopslope < targ.bottomslope) { + return true; // shot over the thing + } + thingbottomslope = FixedDiv(th.z - targ.shootz, dist); + + if (thingbottomslope > targ.topslope) { + return true; // shot under the thing + } + // this thing can be hit! + if (thingtopslope > targ.topslope) { + thingtopslope = targ.topslope; + } + + if (thingbottomslope < targ.bottomslope) { + thingbottomslope = targ.bottomslope; + } + + targ.aimslope = (thingtopslope + thingbottomslope) / 2; + targ.linetarget = th; + + return false; // don't go any farther + } + +} \ No newline at end of file diff --git a/src/p/Actions/ActionsAttacks.java b/src/p/Actions/ActionsAttacks.java index 9226ccf..b532760 100644 --- a/src/p/Actions/ActionsAttacks.java +++ b/src/p/Actions/ActionsAttacks.java @@ -1,327 +1,327 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import static data.Defines.MISSILERANGE; -import static data.Defines.PT_ADDLINES; -import static data.Defines.PT_ADDTHINGS; -import static data.Limits.MAXRADIUS; -import static data.Tables.finecosine; -import static data.Tables.finesine; -import static data.info.mobjinfo; -import data.mobjtype_t; -import defines.statenum_t; -import doom.SourceCode.P_Enemy; -import static doom.SourceCode.P_Enemy.PIT_VileCheck; -import doom.SourceCode.P_Map; -import static doom.SourceCode.P_Map.PIT_RadiusAttack; -import static doom.SourceCode.P_Map.PTR_ShootTraverse; -import doom.SourceCode.angle_t; -import doom.SourceCode.fixed_t; -import static m.fixed_t.FRACBITS; -import static m.fixed_t.FRACUNIT; -import static m.fixed_t.FixedDiv; -import static m.fixed_t.FixedMul; -import p.AbstractLevelLoader; -import p.intercept_t; -import p.mobj_t; -import static p.mobj_t.MF_CORPSE; -import static p.mobj_t.MF_NOBLOOD; -import static p.mobj_t.MF_SHOOTABLE; -import rr.line_t; -import static rr.line_t.ML_TWOSIDED; -import static utils.C2JUtils.eval; -import utils.TraitFactory.ContextKey; - -public interface ActionsAttacks extends ActionsAim, ActionsMobj, ActionsSight, ActionsShootEvents { - - ContextKey KEY_ATTACKS = ACTION_KEY_CHAIN.newKey(ActionsAttacks.class, Attacks::new); - - final class Attacks { - - // - // RADIUS ATTACK - // - public mobj_t bombsource; - public mobj_t bombspot; - public int bombdamage; - ///////////////////// PIT AND PTR FUNCTIONS ////////////////// - /** - * PIT_VileCheck Detect a corpse that could be raised. - */ - public mobj_t vileCorpseHit; - public mobj_t vileObj; - public int vileTryX; - public int vileTryY; - } - - // - // P_GunShot - // - default void P_GunShot(mobj_t mo, boolean accurate) { - final Spawn targ = contextRequire(KEY_SPAWN); - long angle; - int damage; - - damage = 5 * (P_Random() % 3 + 1); - angle = mo.angle; - - if (!accurate) { - angle += (P_Random() - P_Random()) << 18; - } - - this.LineAttack(mo, angle, MISSILERANGE, targ.bulletslope, damage); - } - - /** - * P_LineAttack If damage == 0, it is just a test trace that will leave linetarget set. - * - * @param t1 - * @param angle angle_t - * @param distance fixed_t - * @param slope fixed_t - * @param damage - */ - default void LineAttack(mobj_t t1, @angle_t long angle, @fixed_t int distance, @fixed_t int slope, int damage) { - final Spawn targ = contextRequire(KEY_SPAWN); - int x2, y2; - - targ.shootthing = t1; - targ.la_damage = damage; - x2 = t1.x + (distance >> FRACBITS) * finecosine(angle); - y2 = t1.y + (distance >> FRACBITS) * finesine(angle); - targ.shootz = t1.z + (t1.height >> 1) + 8 * FRACUNIT; - targ.attackrange = distance; - targ.aimslope = slope; - - PathTraverse(t1.x, t1.y, x2, y2, PT_ADDLINES | PT_ADDTHINGS, this::ShootTraverse); - } - - // - // RADIUS ATTACK - // - /** - * P_RadiusAttack Source is the creature that caused the explosion at spot. - */ - default void RadiusAttack(mobj_t spot, mobj_t source, int damage) { - final AbstractLevelLoader ll = levelLoader(); - final Attacks att = contextRequire(KEY_ATTACKS); - - int x; - int y; - - int xl; - int xh; - int yl; - int yh; - - @fixed_t - int dist; - - dist = (damage + MAXRADIUS) << FRACBITS; - yh = ll.getSafeBlockY(spot.y + dist - ll.bmaporgy); - yl = ll.getSafeBlockY(spot.y - dist - ll.bmaporgy); - xh = ll.getSafeBlockX(spot.x + dist - ll.bmaporgx); - xl = ll.getSafeBlockX(spot.x - dist - ll.bmaporgx); - att.bombspot = spot; - att.bombsource = source; - att.bombdamage = damage; - - for (y = yl; y <= yh; y++) { - for (x = xl; x <= xh; x++) { - BlockThingsIterator(x, y, this::RadiusAttack); - } - } - } - - ///////////////////// PIT AND PTR FUNCTIONS ////////////////// - /** - * PIT_VileCheck Detect a corpse that could be raised. - */ - @P_Enemy.C(PIT_VileCheck) - default boolean VileCheck(mobj_t thing) { - final Attacks att = contextRequire(KEY_ATTACKS); - - int maxdist; - boolean check; - - if (!eval(thing.flags & MF_CORPSE)) { - return true; // not a monster - } - if (thing.mobj_tics != -1) { - return true; // not lying still yet - } - if (thing.info.raisestate == statenum_t.S_NULL) { - return true; // monster doesn't have a raise state - } - maxdist = thing.info.radius + mobjinfo[mobjtype_t.MT_VILE.ordinal()].radius; - - if (Math.abs(thing.x - att.vileTryX) > maxdist - || Math.abs(thing.y - att.vileTryY) > maxdist) { - return true; // not actually touching - } - - att.vileCorpseHit = thing; - att.vileCorpseHit.momx = att.vileCorpseHit.momy = 0; - att.vileCorpseHit.height <<= 2; - check = CheckPosition(att.vileCorpseHit, att.vileCorpseHit.x, att.vileCorpseHit.y); - att.vileCorpseHit.height >>= 2; - - // check it doesn't fit here, or stop checking - return !check; - } - - /** - * PIT_RadiusAttack "bombsource" is the creature that caused the explosion at "bombspot". - */ - @P_Map.C(PIT_RadiusAttack) - default boolean RadiusAttack(mobj_t thing) { - final Attacks att = contextRequire(KEY_ATTACKS); - @fixed_t - int dx, dy, dist; - - if (!eval(thing.flags & MF_SHOOTABLE)) { - return true; - } - - // Boss spider and cyborg - // take no damage from concussion. - if (thing.type == mobjtype_t.MT_CYBORG || thing.type == mobjtype_t.MT_SPIDER) { - return true; - } - - dx = Math.abs(thing.x - att.bombspot.x); - dy = Math.abs(thing.y - att.bombspot.y); - - dist = dx > dy ? dx : dy; - dist = (dist - thing.radius) >> FRACBITS; - - if (dist < 0) { - dist = 0; - } - - if (dist >= att.bombdamage) { - return true; // out of range - } - if (CheckSight(thing, att.bombspot)) { - // must be in direct path - DamageMobj(thing, att.bombspot, att.bombsource, att.bombdamage - dist); - } - - return true; - } - - ; - - /** - * PTR_ShootTraverse - * - * 9/5/2011: Accepted _D_'s fix - */ - @P_Map.C(PTR_ShootTraverse) - default boolean ShootTraverse(intercept_t in) { - final Spawn targ = contextRequire(KEY_SPAWN); - final Movement mov = contextRequire(KEY_MOVEMENT); - @fixed_t - int x, y, z, frac; - line_t li; - mobj_t th; - - @fixed_t - int slope, dist, thingtopslope, thingbottomslope; - - if (in.isaline) { - li = (line_t) in.d(); - - if (li.special != 0) { - ShootSpecialLine(targ.shootthing, li); - } - - if (!eval(li.flags & ML_TWOSIDED)) { - return gotoHitLine(in, li); - } - - // crosses a two sided line - LineOpening(li); - - dist = FixedMul(targ.attackrange, in.frac); - - if (li.frontsector.floorheight != li.backsector.floorheight) { - slope = FixedDiv(mov.openbottom - targ.shootz, dist); - if (slope > targ.aimslope) { - return gotoHitLine(in, li); - } - } - - if (li.frontsector.ceilingheight != li.backsector.ceilingheight) { - slope = FixedDiv(mov.opentop - targ.shootz, dist); - if (slope < targ.aimslope) { - return gotoHitLine(in, li); - } - } - - // shot continues - return true; - - } - - // shoot a thing - th = (mobj_t) in.d(); - if (th == targ.shootthing) { - return true; // can't shoot self - } - if (!eval(th.flags & MF_SHOOTABLE)) { - return true; // corpse or something - } - // check angles to see if the thing can be aimed at - dist = FixedMul(targ.attackrange, in.frac); - thingtopslope = FixedDiv(th.z + th.height - targ.shootz, dist); - - if (thingtopslope < targ.aimslope) { - return true; // shot over the thing - } - thingbottomslope = FixedDiv(th.z - targ.shootz, dist); - - if (thingbottomslope > targ.aimslope) { - return true; // shot under the thing - } - - // hit thing - // position a bit closer - frac = in.frac - FixedDiv(10 * FRACUNIT, targ.attackrange); - - x = targ.trace.x + FixedMul(targ.trace.dx, frac); - y = targ.trace.y + FixedMul(targ.trace.dy, frac); - z = targ.shootz + FixedMul(targ.aimslope, FixedMul(frac, targ.attackrange)); - - // Spawn bullet puffs or blod spots, - // depending on target type. - if (eval(((mobj_t) in.d()).flags & MF_NOBLOOD)) { - SpawnPuff(x, y, z); - } else { - SpawnBlood(x, y, z, targ.la_damage); - } - - if (targ.la_damage != 0) { - DamageMobj(th, targ.shootthing, targ.shootthing, targ.la_damage); - } - - // don't go any farther - return false; - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import static data.Defines.MISSILERANGE; +import static data.Defines.PT_ADDLINES; +import static data.Defines.PT_ADDTHINGS; +import static data.Limits.MAXRADIUS; +import static data.Tables.finecosine; +import static data.Tables.finesine; +import static data.info.mobjinfo; +import data.mobjtype_t; +import defines.statenum_t; +import doom.SourceCode.P_Enemy; +import static doom.SourceCode.P_Enemy.PIT_VileCheck; +import doom.SourceCode.P_Map; +import static doom.SourceCode.P_Map.PIT_RadiusAttack; +import static doom.SourceCode.P_Map.PTR_ShootTraverse; +import doom.SourceCode.angle_t; +import doom.SourceCode.fixed_t; +import static m.fixed_t.FRACBITS; +import static m.fixed_t.FRACUNIT; +import static m.fixed_t.FixedDiv; +import static m.fixed_t.FixedMul; +import p.AbstractLevelLoader; +import p.intercept_t; +import p.mobj_t; +import static p.mobj_t.MF_CORPSE; +import static p.mobj_t.MF_NOBLOOD; +import static p.mobj_t.MF_SHOOTABLE; +import rr.line_t; +import static rr.line_t.ML_TWOSIDED; +import static utils.C2JUtils.eval; +import utils.TraitFactory.ContextKey; + +public interface ActionsAttacks extends ActionsAim, ActionsMobj, ActionsSight, ActionsShootEvents { + + ContextKey KEY_ATTACKS = ACTION_KEY_CHAIN.newKey(ActionsAttacks.class, Attacks::new); + + final class Attacks { + + // + // RADIUS ATTACK + // + public mobj_t bombsource; + public mobj_t bombspot; + public int bombdamage; + ///////////////////// PIT AND PTR FUNCTIONS ////////////////// + /** + * PIT_VileCheck Detect a corpse that could be raised. + */ + public mobj_t vileCorpseHit; + public mobj_t vileObj; + public int vileTryX; + public int vileTryY; + } + + // + // P_GunShot + // + default void P_GunShot(mobj_t mo, boolean accurate) { + final Spawn targ = contextRequire(KEY_SPAWN); + long angle; + int damage; + + damage = 5 * (P_Random() % 3 + 1); + angle = mo.angle; + + if (!accurate) { + angle += (P_Random() - P_Random()) << 18; + } + + this.LineAttack(mo, angle, MISSILERANGE, targ.bulletslope, damage); + } + + /** + * P_LineAttack If damage == 0, it is just a test trace that will leave linetarget set. + * + * @param t1 + * @param angle angle_t + * @param distance fixed_t + * @param slope fixed_t + * @param damage + */ + default void LineAttack(mobj_t t1, @angle_t long angle, @fixed_t int distance, @fixed_t int slope, int damage) { + final Spawn targ = contextRequire(KEY_SPAWN); + int x2, y2; + + targ.shootthing = t1; + targ.la_damage = damage; + x2 = t1.x + (distance >> FRACBITS) * finecosine(angle); + y2 = t1.y + (distance >> FRACBITS) * finesine(angle); + targ.shootz = t1.z + (t1.height >> 1) + 8 * FRACUNIT; + targ.attackrange = distance; + targ.aimslope = slope; + + PathTraverse(t1.x, t1.y, x2, y2, PT_ADDLINES | PT_ADDTHINGS, this::ShootTraverse); + } + + // + // RADIUS ATTACK + // + /** + * P_RadiusAttack Source is the creature that caused the explosion at spot. + */ + default void RadiusAttack(mobj_t spot, mobj_t source, int damage) { + final AbstractLevelLoader ll = levelLoader(); + final Attacks att = contextRequire(KEY_ATTACKS); + + int x; + int y; + + int xl; + int xh; + int yl; + int yh; + + @fixed_t + int dist; + + dist = (damage + MAXRADIUS) << FRACBITS; + yh = ll.getSafeBlockY(spot.y + dist - ll.bmaporgy); + yl = ll.getSafeBlockY(spot.y - dist - ll.bmaporgy); + xh = ll.getSafeBlockX(spot.x + dist - ll.bmaporgx); + xl = ll.getSafeBlockX(spot.x - dist - ll.bmaporgx); + att.bombspot = spot; + att.bombsource = source; + att.bombdamage = damage; + + for (y = yl; y <= yh; y++) { + for (x = xl; x <= xh; x++) { + BlockThingsIterator(x, y, this::RadiusAttack); + } + } + } + + ///////////////////// PIT AND PTR FUNCTIONS ////////////////// + /** + * PIT_VileCheck Detect a corpse that could be raised. + */ + @P_Enemy.C(PIT_VileCheck) + default boolean VileCheck(mobj_t thing) { + final Attacks att = contextRequire(KEY_ATTACKS); + + int maxdist; + boolean check; + + if (!eval(thing.flags & MF_CORPSE)) { + return true; // not a monster + } + if (thing.mobj_tics != -1) { + return true; // not lying still yet + } + if (thing.info.raisestate == statenum_t.S_NULL) { + return true; // monster doesn't have a raise state + } + maxdist = thing.info.radius + mobjinfo[mobjtype_t.MT_VILE.ordinal()].radius; + + if (Math.abs(thing.x - att.vileTryX) > maxdist + || Math.abs(thing.y - att.vileTryY) > maxdist) { + return true; // not actually touching + } + + att.vileCorpseHit = thing; + att.vileCorpseHit.momx = att.vileCorpseHit.momy = 0; + att.vileCorpseHit.height <<= 2; + check = CheckPosition(att.vileCorpseHit, att.vileCorpseHit.x, att.vileCorpseHit.y); + att.vileCorpseHit.height >>= 2; + + // check it doesn't fit here, or stop checking + return !check; + } + + /** + * PIT_RadiusAttack "bombsource" is the creature that caused the explosion at "bombspot". + */ + @P_Map.C(PIT_RadiusAttack) + default boolean RadiusAttack(mobj_t thing) { + final Attacks att = contextRequire(KEY_ATTACKS); + @fixed_t + int dx, dy, dist; + + if (!eval(thing.flags & MF_SHOOTABLE)) { + return true; + } + + // Boss spider and cyborg + // take no damage from concussion. + if (thing.type == mobjtype_t.MT_CYBORG || thing.type == mobjtype_t.MT_SPIDER) { + return true; + } + + dx = Math.abs(thing.x - att.bombspot.x); + dy = Math.abs(thing.y - att.bombspot.y); + + dist = dx > dy ? dx : dy; + dist = (dist - thing.radius) >> FRACBITS; + + if (dist < 0) { + dist = 0; + } + + if (dist >= att.bombdamage) { + return true; // out of range + } + if (CheckSight(thing, att.bombspot)) { + // must be in direct path + DamageMobj(thing, att.bombspot, att.bombsource, att.bombdamage - dist); + } + + return true; + } + + ; + + /** + * PTR_ShootTraverse + * + * 9/5/2011: Accepted _D_'s fix + */ + @P_Map.C(PTR_ShootTraverse) + default boolean ShootTraverse(intercept_t in) { + final Spawn targ = contextRequire(KEY_SPAWN); + final Movement mov = contextRequire(KEY_MOVEMENT); + @fixed_t + int x, y, z, frac; + line_t li; + mobj_t th; + + @fixed_t + int slope, dist, thingtopslope, thingbottomslope; + + if (in.isaline) { + li = (line_t) in.d(); + + if (li.special != 0) { + ShootSpecialLine(targ.shootthing, li); + } + + if (!eval(li.flags & ML_TWOSIDED)) { + return gotoHitLine(in, li); + } + + // crosses a two sided line + LineOpening(li); + + dist = FixedMul(targ.attackrange, in.frac); + + if (li.frontsector.floorheight != li.backsector.floorheight) { + slope = FixedDiv(mov.openbottom - targ.shootz, dist); + if (slope > targ.aimslope) { + return gotoHitLine(in, li); + } + } + + if (li.frontsector.ceilingheight != li.backsector.ceilingheight) { + slope = FixedDiv(mov.opentop - targ.shootz, dist); + if (slope < targ.aimslope) { + return gotoHitLine(in, li); + } + } + + // shot continues + return true; + + } + + // shoot a thing + th = (mobj_t) in.d(); + if (th == targ.shootthing) { + return true; // can't shoot self + } + if (!eval(th.flags & MF_SHOOTABLE)) { + return true; // corpse or something + } + // check angles to see if the thing can be aimed at + dist = FixedMul(targ.attackrange, in.frac); + thingtopslope = FixedDiv(th.z + th.height - targ.shootz, dist); + + if (thingtopslope < targ.aimslope) { + return true; // shot over the thing + } + thingbottomslope = FixedDiv(th.z - targ.shootz, dist); + + if (thingbottomslope > targ.aimslope) { + return true; // shot under the thing + } + + // hit thing + // position a bit closer + frac = in.frac - FixedDiv(10 * FRACUNIT, targ.attackrange); + + x = targ.trace.x + FixedMul(targ.trace.dx, frac); + y = targ.trace.y + FixedMul(targ.trace.dy, frac); + z = targ.shootz + FixedMul(targ.aimslope, FixedMul(frac, targ.attackrange)); + + // Spawn bullet puffs or blod spots, + // depending on target type. + if (eval(((mobj_t) in.d()).flags & MF_NOBLOOD)) { + SpawnPuff(x, y, z); + } else { + SpawnBlood(x, y, z, targ.la_damage); + } + + if (targ.la_damage != 0) { + DamageMobj(th, targ.shootthing, targ.shootthing, targ.la_damage); + } + + // don't go any farther + return false; + } +} \ No newline at end of file diff --git a/src/p/Actions/ActionsCeilings.java b/src/p/Actions/ActionsCeilings.java index 671e60e..2eb40fc 100644 --- a/src/p/Actions/ActionsCeilings.java +++ b/src/p/Actions/ActionsCeilings.java @@ -1,298 +1,298 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import static data.Limits.CEILSPEED; -import static data.Limits.MAXCEILINGS; -import data.sounds; -import doom.SourceCode.P_Ceiling; -import static doom.SourceCode.P_Ceiling.EV_DoCeiling; -import doom.thinker_t; -import static m.fixed_t.FRACUNIT; -import p.ActiveStates; -import p.ceiling_e; -import p.ceiling_t; -import p.result_e; -import rr.line_t; -import rr.sector_t; -import utils.C2JUtils; -import static utils.C2JUtils.eval; -import utils.TraitFactory.ContextKey; - -public interface ActionsCeilings extends ActionsMoveEvents, ActionsUseEvents { - - ContextKey KEY_CEILINGS = ACTION_KEY_CHAIN.newKey(ActionsCeilings.class, Ceilings::new); - - void RemoveThinker(thinker_t activeCeiling); - - result_e MovePlane(sector_t sector, int speed, int bottomheight, boolean crush, int i, int direction); - - int FindSectorFromLineTag(line_t line, int secnum); - - final class Ceilings { - - ceiling_t[] activeceilings = new ceiling_t[MAXCEILINGS]; - } - - /** - * This needs to be called before loading, otherwise crushers won't be able to be restarted. - */ - default void ClearCeilingsBeforeLoading() { - contextRequire(KEY_CEILINGS).activeceilings = new ceiling_t[MAXCEILINGS]; - } - - /** - * T_MoveCeiling - */ - default void MoveCeiling(ceiling_t ceiling) { - result_e res; - - switch (ceiling.direction) { - case 0: - // IN STASIS - break; - case 1: - // UP - res = MovePlane(ceiling.sector, ceiling.speed, ceiling.topheight, false, 1, ceiling.direction); - - if (!eval(LevelTime() & 7)) { - switch (ceiling.type) { - case silentCrushAndRaise: - break; - default: - StartSound(ceiling.sector.soundorg, sounds.sfxenum_t.sfx_stnmov); - } - } - - if (res == result_e.pastdest) { - switch (ceiling.type) { - case raiseToHighest: - this.RemoveActiveCeiling(ceiling); - break; - case silentCrushAndRaise: - StartSound(ceiling.sector.soundorg, sounds.sfxenum_t.sfx_pstop); - case fastCrushAndRaise: - case crushAndRaise: - ceiling.direction = -1; - default: - break; - } - } - break; - - case -1: - // DOWN - res = MovePlane(ceiling.sector, ceiling.speed, ceiling.bottomheight, ceiling.crush, 1, ceiling.direction); - - if (!eval(LevelTime() & 7)) { - switch (ceiling.type) { - case silentCrushAndRaise: - break; - default: - StartSound(ceiling.sector.soundorg, sounds.sfxenum_t.sfx_stnmov); - } - } - - if (res == result_e.pastdest) { - switch (ceiling.type) { - case silentCrushAndRaise: - StartSound(ceiling.sector.soundorg, sounds.sfxenum_t.sfx_pstop); - case crushAndRaise: - ceiling.speed = CEILSPEED; - case fastCrushAndRaise: - ceiling.direction = 1; - break; - case lowerAndCrush: - case lowerToFloor: - RemoveActiveCeiling(ceiling); - break; - default: - break; - } - } else { // ( res != result_e.pastdest ) - if (res == result_e.crushed) { - switch (ceiling.type) { - case silentCrushAndRaise: - case crushAndRaise: - case lowerAndCrush: - ceiling.speed = CEILSPEED / 8; - break; - default: - break; - } - } - } - } - } - - // - // EV.DoCeiling - // Move a ceiling up/down and all around! - // - @Override - @P_Ceiling.C(EV_DoCeiling) - default boolean DoCeiling(line_t line, ceiling_e type) { - int secnum = -1; - boolean rtn = false; - sector_t sec; - ceiling_t ceiling; - - // Reactivate in-stasis ceilings...for certain types. - switch (type) { - case fastCrushAndRaise: - case silentCrushAndRaise: - case crushAndRaise: - ActivateInStasisCeiling(line); - default: - break; - } - - while ((secnum = FindSectorFromLineTag(line, secnum)) >= 0) { - sec = levelLoader().sectors[secnum]; - if (sec.specialdata != null) { - continue; - } - - // new door thinker - rtn = true; - ceiling = new ceiling_t(); - sec.specialdata = ceiling; - ceiling.thinkerFunction = ActiveStates.T_MoveCeiling; - AddThinker(ceiling); - ceiling.sector = sec; - ceiling.crush = false; - - switch (type) { - case fastCrushAndRaise: - ceiling.crush = true; - ceiling.topheight = sec.ceilingheight; - ceiling.bottomheight = sec.floorheight + (8 * FRACUNIT); - ceiling.direction = -1; - ceiling.speed = CEILSPEED * 2; - break; - - case silentCrushAndRaise: - case crushAndRaise: - ceiling.crush = true; - ceiling.topheight = sec.ceilingheight; - case lowerAndCrush: - case lowerToFloor: - ceiling.bottomheight = sec.floorheight; - if (type != ceiling_e.lowerToFloor) { - ceiling.bottomheight += 8 * FRACUNIT; - } - ceiling.direction = -1; - ceiling.speed = CEILSPEED; - break; - - case raiseToHighest: - ceiling.topheight = sec.FindHighestCeilingSurrounding(); - ceiling.direction = 1; - ceiling.speed = CEILSPEED; - break; - } - - ceiling.tag = sec.tag; - ceiling.type = type; - AddActiveCeiling(ceiling); - } - return rtn; - } - - // - // Add an active ceiling - // - default void AddActiveCeiling(ceiling_t c) { - final ceiling_t[] activeCeilings = getActiveCeilings(); - for (int i = 0; i < activeCeilings.length; ++i) { - if (activeCeilings[i] == null) { - activeCeilings[i] = c; - return; - } - } - // Needs rezising - setActiveceilings(C2JUtils.resize(c, activeCeilings, 2 * activeCeilings.length)); - } - - // - // Remove a ceiling's thinker - // - default void RemoveActiveCeiling(ceiling_t c) { - final ceiling_t[] activeCeilings = getActiveCeilings(); - for (int i = 0; i < activeCeilings.length; ++i) { - if (activeCeilings[i] == c) { - activeCeilings[i].sector.specialdata = null; - RemoveThinker(activeCeilings[i]); - activeCeilings[i] = null; - break; - } - } - } - - // - // Restart a ceiling that's in-stasis - // - default void ActivateInStasisCeiling(line_t line) { - final ceiling_t[] activeCeilings = getActiveCeilings(); - for (int i = 0; i < activeCeilings.length; ++i) { - if (activeCeilings[i] != null - && (activeCeilings[i].tag == line.tag) - && (activeCeilings[i].direction == 0)) { - activeCeilings[i].direction = activeCeilings[i].olddirection; - activeCeilings[i].thinkerFunction = ActiveStates.T_MoveCeiling; - } - } - } - - // - // EV_CeilingCrushStop - // Stop a ceiling from crushing! - // - @Override - default int CeilingCrushStop(line_t line) { - int i; - int rtn; - - rtn = 0; - final ceiling_t[] activeCeilings = getActiveCeilings(); - for (i = 0; i < activeCeilings.length; ++i) { - if (activeCeilings[i] != null - && (activeCeilings[i].tag == line.tag) - && (activeCeilings[i].direction != 0)) { - activeCeilings[i].olddirection = activeCeilings[i].direction; - activeCeilings[i].thinkerFunction = ActiveStates.NOP; - activeCeilings[i].direction = 0; // in-stasis - rtn = 1; - } - } - - return rtn; - } - - default void setActiveceilings(ceiling_t[] activeceilings) { - contextRequire(KEY_CEILINGS).activeceilings = activeceilings; - } - - default ceiling_t[] getActiveCeilings() { - return contextRequire(KEY_CEILINGS).activeceilings; - } - - default int getMaxCeilings() { - return contextRequire(KEY_CEILINGS).activeceilings.length; - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import static data.Limits.CEILSPEED; +import static data.Limits.MAXCEILINGS; +import data.sounds; +import doom.SourceCode.P_Ceiling; +import static doom.SourceCode.P_Ceiling.EV_DoCeiling; +import doom.thinker_t; +import static m.fixed_t.FRACUNIT; +import p.ActiveStates; +import p.ceiling_e; +import p.ceiling_t; +import p.result_e; +import rr.line_t; +import rr.sector_t; +import utils.C2JUtils; +import static utils.C2JUtils.eval; +import utils.TraitFactory.ContextKey; + +public interface ActionsCeilings extends ActionsMoveEvents, ActionsUseEvents { + + ContextKey KEY_CEILINGS = ACTION_KEY_CHAIN.newKey(ActionsCeilings.class, Ceilings::new); + + void RemoveThinker(thinker_t activeCeiling); + + result_e MovePlane(sector_t sector, int speed, int bottomheight, boolean crush, int i, int direction); + + int FindSectorFromLineTag(line_t line, int secnum); + + final class Ceilings { + + ceiling_t[] activeceilings = new ceiling_t[MAXCEILINGS]; + } + + /** + * This needs to be called before loading, otherwise crushers won't be able to be restarted. + */ + default void ClearCeilingsBeforeLoading() { + contextRequire(KEY_CEILINGS).activeceilings = new ceiling_t[MAXCEILINGS]; + } + + /** + * T_MoveCeiling + */ + default void MoveCeiling(ceiling_t ceiling) { + result_e res; + + switch (ceiling.direction) { + case 0: + // IN STASIS + break; + case 1: + // UP + res = MovePlane(ceiling.sector, ceiling.speed, ceiling.topheight, false, 1, ceiling.direction); + + if (!eval(LevelTime() & 7)) { + switch (ceiling.type) { + case silentCrushAndRaise: + break; + default: + StartSound(ceiling.sector.soundorg, sounds.sfxenum_t.sfx_stnmov); + } + } + + if (res == result_e.pastdest) { + switch (ceiling.type) { + case raiseToHighest: + this.RemoveActiveCeiling(ceiling); + break; + case silentCrushAndRaise: + StartSound(ceiling.sector.soundorg, sounds.sfxenum_t.sfx_pstop); + case fastCrushAndRaise: + case crushAndRaise: + ceiling.direction = -1; + default: + break; + } + } + break; + + case -1: + // DOWN + res = MovePlane(ceiling.sector, ceiling.speed, ceiling.bottomheight, ceiling.crush, 1, ceiling.direction); + + if (!eval(LevelTime() & 7)) { + switch (ceiling.type) { + case silentCrushAndRaise: + break; + default: + StartSound(ceiling.sector.soundorg, sounds.sfxenum_t.sfx_stnmov); + } + } + + if (res == result_e.pastdest) { + switch (ceiling.type) { + case silentCrushAndRaise: + StartSound(ceiling.sector.soundorg, sounds.sfxenum_t.sfx_pstop); + case crushAndRaise: + ceiling.speed = CEILSPEED; + case fastCrushAndRaise: + ceiling.direction = 1; + break; + case lowerAndCrush: + case lowerToFloor: + RemoveActiveCeiling(ceiling); + break; + default: + break; + } + } else { // ( res != result_e.pastdest ) + if (res == result_e.crushed) { + switch (ceiling.type) { + case silentCrushAndRaise: + case crushAndRaise: + case lowerAndCrush: + ceiling.speed = CEILSPEED / 8; + break; + default: + break; + } + } + } + } + } + + // + // EV.DoCeiling + // Move a ceiling up/down and all around! + // + @Override + @P_Ceiling.C(EV_DoCeiling) + default boolean DoCeiling(line_t line, ceiling_e type) { + int secnum = -1; + boolean rtn = false; + sector_t sec; + ceiling_t ceiling; + + // Reactivate in-stasis ceilings...for certain types. + switch (type) { + case fastCrushAndRaise: + case silentCrushAndRaise: + case crushAndRaise: + ActivateInStasisCeiling(line); + default: + break; + } + + while ((secnum = FindSectorFromLineTag(line, secnum)) >= 0) { + sec = levelLoader().sectors[secnum]; + if (sec.specialdata != null) { + continue; + } + + // new door thinker + rtn = true; + ceiling = new ceiling_t(); + sec.specialdata = ceiling; + ceiling.thinkerFunction = ActiveStates.T_MoveCeiling; + AddThinker(ceiling); + ceiling.sector = sec; + ceiling.crush = false; + + switch (type) { + case fastCrushAndRaise: + ceiling.crush = true; + ceiling.topheight = sec.ceilingheight; + ceiling.bottomheight = sec.floorheight + (8 * FRACUNIT); + ceiling.direction = -1; + ceiling.speed = CEILSPEED * 2; + break; + + case silentCrushAndRaise: + case crushAndRaise: + ceiling.crush = true; + ceiling.topheight = sec.ceilingheight; + case lowerAndCrush: + case lowerToFloor: + ceiling.bottomheight = sec.floorheight; + if (type != ceiling_e.lowerToFloor) { + ceiling.bottomheight += 8 * FRACUNIT; + } + ceiling.direction = -1; + ceiling.speed = CEILSPEED; + break; + + case raiseToHighest: + ceiling.topheight = sec.FindHighestCeilingSurrounding(); + ceiling.direction = 1; + ceiling.speed = CEILSPEED; + break; + } + + ceiling.tag = sec.tag; + ceiling.type = type; + AddActiveCeiling(ceiling); + } + return rtn; + } + + // + // Add an active ceiling + // + default void AddActiveCeiling(ceiling_t c) { + final ceiling_t[] activeCeilings = getActiveCeilings(); + for (int i = 0; i < activeCeilings.length; ++i) { + if (activeCeilings[i] == null) { + activeCeilings[i] = c; + return; + } + } + // Needs rezising + setActiveceilings(C2JUtils.resize(c, activeCeilings, 2 * activeCeilings.length)); + } + + // + // Remove a ceiling's thinker + // + default void RemoveActiveCeiling(ceiling_t c) { + final ceiling_t[] activeCeilings = getActiveCeilings(); + for (int i = 0; i < activeCeilings.length; ++i) { + if (activeCeilings[i] == c) { + activeCeilings[i].sector.specialdata = null; + RemoveThinker(activeCeilings[i]); + activeCeilings[i] = null; + break; + } + } + } + + // + // Restart a ceiling that's in-stasis + // + default void ActivateInStasisCeiling(line_t line) { + final ceiling_t[] activeCeilings = getActiveCeilings(); + for (int i = 0; i < activeCeilings.length; ++i) { + if (activeCeilings[i] != null + && (activeCeilings[i].tag == line.tag) + && (activeCeilings[i].direction == 0)) { + activeCeilings[i].direction = activeCeilings[i].olddirection; + activeCeilings[i].thinkerFunction = ActiveStates.T_MoveCeiling; + } + } + } + + // + // EV_CeilingCrushStop + // Stop a ceiling from crushing! + // + @Override + default int CeilingCrushStop(line_t line) { + int i; + int rtn; + + rtn = 0; + final ceiling_t[] activeCeilings = getActiveCeilings(); + for (i = 0; i < activeCeilings.length; ++i) { + if (activeCeilings[i] != null + && (activeCeilings[i].tag == line.tag) + && (activeCeilings[i].direction != 0)) { + activeCeilings[i].olddirection = activeCeilings[i].direction; + activeCeilings[i].thinkerFunction = ActiveStates.NOP; + activeCeilings[i].direction = 0; // in-stasis + rtn = 1; + } + } + + return rtn; + } + + default void setActiveceilings(ceiling_t[] activeceilings) { + contextRequire(KEY_CEILINGS).activeceilings = activeceilings; + } + + default ceiling_t[] getActiveCeilings() { + return contextRequire(KEY_CEILINGS).activeceilings; + } + + default int getMaxCeilings() { + return contextRequire(KEY_CEILINGS).activeceilings.length; + } +} \ No newline at end of file diff --git a/src/p/Actions/ActionsDoors.java b/src/p/Actions/ActionsDoors.java index b742022..920e462 100644 --- a/src/p/Actions/ActionsDoors.java +++ b/src/p/Actions/ActionsDoors.java @@ -1,493 +1,493 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import data.sounds; -import defines.card_t; -import doom.SourceCode; -import doom.SourceCode.P_Doors; -import static doom.SourceCode.P_Doors.P_SpawnDoorCloseIn30; -import static doom.SourceCode.P_Doors.P_SpawnDoorRaiseIn5Mins; -import static doom.englsh.PD_BLUEK; -import static doom.englsh.PD_BLUEO; -import static doom.englsh.PD_REDK; -import static doom.englsh.PD_REDO; -import static doom.englsh.PD_YELLOWK; -import static doom.englsh.PD_YELLOWO; -import doom.player_t; -import doom.thinker_t; -import static m.fixed_t.FRACUNIT; -import p.ActiveStates; -import static p.ActiveStates.T_VerticalDoor; -import static p.DoorDefines.VDOORSPEED; -import static p.DoorDefines.VDOORWAIT; -import p.mobj_t; -import p.plat_t; -import p.result_e; -import p.vldoor_e; -import p.vldoor_t; -import rr.line_t; -import rr.sector_t; -import static utils.C2JUtils.eval; - -public interface ActionsDoors extends ActionsMoveEvents, ActionsUseEvents { - - result_e MovePlane(sector_t sector, int speed, int floorheight, boolean b, int i, int direction); - - void RemoveThinker(thinker_t door); - - int FindSectorFromLineTag(line_t line, int secnum); - - // - // VERTICAL DOORS - // - /** - * T_VerticalDoor - */ - default void VerticalDoor(vldoor_t door) { - switch (door.direction) { - case 0: - // WAITING - if (!eval(--door.topcountdown)) { - switch (door.type) { - case blazeRaise: - door.direction = -1; // time to go back down - StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_bdcls); - break; - case normal: - door.direction = -1; // time to go back down - StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_dorcls); - break; - case close30ThenOpen: - door.direction = 1; - StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_doropn); - break; - default: - break; - } - } - break; - - case 2: - // INITIAL WAIT - if (!eval(--door.topcountdown)) { - switch (door.type) { - case raiseIn5Mins: - door.direction = 1; - door.type = vldoor_e.normal; - StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_doropn); - break; - default: - break; - } - } - break; - - case -1: { - // DOWN - final result_e res = MovePlane(door.sector, door.speed, door.sector.floorheight, false, 1, door.direction); - if (res == result_e.pastdest) { - switch (door.type) { - case blazeRaise: - case blazeClose: - door.sector.specialdata = null; - RemoveThinker(door); // unlink and free - StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_bdcls); - break; - case normal: - case close: - door.sector.specialdata = null; - RemoveThinker(door); // unlink and free - break; - case close30ThenOpen: - door.direction = 0; - door.topcountdown = 35 * 30; - break; - default: - break; - } - } else if (res == result_e.crushed) { - switch (door.type) { - case blazeClose: - case close: // DO NOT GO BACK UP! - break; - default: - door.direction = 1; - StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_doropn); - } - } - break; - } - case 1: { - // UP - final result_e res = this.MovePlane(door.sector, door.speed, door.topheight, false, 1, door.direction); - - if (res == result_e.pastdest) { - switch (door.type) { - case blazeRaise: - case normal: - door.direction = 0; // wait at top - door.topcountdown = door.topwait; - break; - case close30ThenOpen: - case blazeOpen: - case open: - door.sector.specialdata = null; - RemoveThinker(door); // unlink and free - break; - default: - break; - } - } - break; - } - } - } - - /** - * EV_DoLockedDoor Move a locked door up/down - */ - @Override - default boolean DoLockedDoor(line_t line, vldoor_e type, mobj_t thing) { - player_t p; - - p = thing.player; - - if (p == null) { - return false; - } - - switch (line.special) { - case 99: // Blue Lock - case 133: - /* if ( p==null ) - return false; */ - if (!p.cards[card_t.it_bluecard.ordinal()] && !p.cards[card_t.it_blueskull.ordinal()]) { - p.message = PD_BLUEO; - StartSound(null, sounds.sfxenum_t.sfx_oof); - return false; - } - break; - - case 134: // Red Lock - case 135: - /* if ( p==null ) - return false; */ - if (!p.cards[card_t.it_redcard.ordinal()] && !p.cards[card_t.it_redskull.ordinal()]) { - p.message = PD_REDO; - StartSound(null, sounds.sfxenum_t.sfx_oof); - return false; - } - break; - - case 136: // Yellow Lock - case 137: - /* if ( p==null ) - return false; */ - if (!p.cards[card_t.it_yellowcard.ordinal()] - && !p.cards[card_t.it_yellowskull.ordinal()]) { - p.message = PD_YELLOWO; - StartSound(null, sounds.sfxenum_t.sfx_oof); - return false; - } - break; - } - - return DoDoor(line, type); - } - - @Override - default boolean DoDoor(line_t line, vldoor_e type) { - int secnum; - boolean rtn = false; - sector_t sec; - vldoor_t door; - - secnum = -1; - - while ((secnum = FindSectorFromLineTag(line, secnum)) >= 0) { - sec = levelLoader().sectors[secnum]; - if (sec.specialdata != null) { - continue; - } - - // new door thinker - rtn = true; - door = new vldoor_t(); - sec.specialdata = door; - door.thinkerFunction = ActiveStates.T_VerticalDoor; - AddThinker(door); - door.sector = sec; - door.type = type; - door.topwait = VDOORWAIT; - door.speed = VDOORSPEED; - - switch (type) { - case blazeClose: - door.topheight = sec.FindLowestCeilingSurrounding(); - door.topheight -= 4 * FRACUNIT; - door.direction = -1; - door.speed = VDOORSPEED * 4; - StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_bdcls); - break; - case close: - door.topheight = sec.FindLowestCeilingSurrounding(); - door.topheight -= 4 * FRACUNIT; - door.direction = -1; - StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_dorcls); - break; - case close30ThenOpen: - door.topheight = sec.ceilingheight; - door.direction = -1; - StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_dorcls); - break; - case blazeRaise: - case blazeOpen: - door.direction = 1; - door.topheight = sec.FindLowestCeilingSurrounding(); - door.topheight -= 4 * FRACUNIT; - door.speed = VDOORSPEED * 4; - if (door.topheight != sec.ceilingheight) { - StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_bdopn); - } - break; - case normal: - case open: - door.direction = 1; - door.topheight = sec.FindLowestCeilingSurrounding(); - door.topheight -= 4 * FRACUNIT; - if (door.topheight != sec.ceilingheight) { - StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_doropn); - } - default: - break; - } - - } - return rtn; - } - - /** - * EV_VerticalDoor : open a door manually, no tag value - */ - @Override - default void VerticalDoor(line_t line, mobj_t thing) { - player_t player; - //int secnum; - sector_t sec; - vldoor_t door; - int side; - - side = 0; // only front sides can be used - - // Check for locks - player = thing.player; - - switch (line.special) { - case 26: // Blue Lock - case 32: - if (player == null) { - return; - } - - if (!player.cards[card_t.it_bluecard.ordinal()] && !player.cards[card_t.it_blueskull.ordinal()]) { - player.message = PD_BLUEK; - StartSound(null, sounds.sfxenum_t.sfx_oof); - return; - } - break; - - case 27: // Yellow Lock - case 34: - if (player == null) { - return; - } - - if (!player.cards[card_t.it_yellowcard.ordinal()] && !player.cards[card_t.it_yellowskull.ordinal()]) { - player.message = PD_YELLOWK; - StartSound(null, sounds.sfxenum_t.sfx_oof); - return; - } - break; - - case 28: // Red Lock - case 33: - if (player == null) { - return; - } - - if (!player.cards[card_t.it_redcard.ordinal()] && !player.cards[card_t.it_redskull.ordinal()]) { - player.message = PD_REDK; - StartSound(null, sounds.sfxenum_t.sfx_oof); - return; - } - break; - } - - // if the sector has an active thinker, use it - sec = levelLoader().sides[line.sidenum[side ^ 1]].sector; - // secnum = sec.id; - - if (sec.specialdata != null) { - if (sec.specialdata instanceof plat_t) { - /** - * [MAES]: demo sync for e1nm0646: emulates active plat_t interpreted - * as door. TODO: add our own overflow handling class. - */ - door = ((plat_t) sec.specialdata).asVlDoor(levelLoader().sectors); - } else { - door = (vldoor_t) sec.specialdata; - } - switch (line.special) { - case 1: // ONLY FOR "RAISE" DOORS, NOT "OPEN"s - case 26: - case 27: - case 28: - case 117: - if (door.direction == -1) { - door.direction = 1; // go back up - } else { - if (thing.player == null) { - return; // JDC: bad guys never close doors - } - door.direction = -1; // start going down immediately - } - return; - } - } - - // for proper sound - switch (line.special) { - case 117: // BLAZING DOOR RAISE - case 118: // BLAZING DOOR OPEN - StartSound(sec.soundorg, sounds.sfxenum_t.sfx_bdopn); - break; - - case 1: // NORMAL DOOR SOUND - case 31: - StartSound(sec.soundorg, sounds.sfxenum_t.sfx_doropn); - break; - - default: // LOCKED DOOR SOUND - StartSound(sec.soundorg, sounds.sfxenum_t.sfx_doropn); - break; - } - - // new door thinker - door = new vldoor_t(); - sec.specialdata = door; - door.thinkerFunction = ActiveStates.T_VerticalDoor; - AddThinker(door); - door.sector = sec; - door.direction = 1; - door.speed = VDOORSPEED; - door.topwait = VDOORWAIT; - - switch (line.special) { - case 1: - case 26: - case 27: - case 28: - door.type = vldoor_e.normal; - break; - case 31: - case 32: - case 33: - case 34: - door.type = vldoor_e.open; - line.special = 0; - break; - case 117: // blazing door raise - door.type = vldoor_e.blazeRaise; - door.speed = VDOORSPEED * 4; - break; - case 118: // blazing door open - door.type = vldoor_e.blazeOpen; - line.special = 0; - door.speed = VDOORSPEED * 4; - } - - // find the top and bottom of the movement range - door.topheight = sec.FindLowestCeilingSurrounding(); - door.topheight -= 4 * FRACUNIT; - } - - // - // Spawn a door that closes after 30 seconds - // - @SourceCode.Exact - @P_Doors.C(P_SpawnDoorCloseIn30) - default void SpawnDoorCloseIn30(sector_t sector) { - vldoor_t door; - - Z_Malloc: - { - door = new vldoor_t(); - } - - P_AddThinker: - { - AddThinker(door); - } - - sector.specialdata = door; - sector.special = 0; - - door.thinkerFunction = T_VerticalDoor; - door.sector = sector; - door.direction = 0; - door.type = vldoor_e.normal; - door.speed = VDOORSPEED; - door.topcountdown = 30 * 35; - } - - /** - * Spawn a door that opens after 5 minutes - */ - @SourceCode.Exact - @P_Doors.C(P_SpawnDoorRaiseIn5Mins) - default void SpawnDoorRaiseIn5Mins(sector_t sector, int secnum) { - vldoor_t door; - - Z_Malloc: - { - door = new vldoor_t(); - } - - P_AddThinker: - { - AddThinker(door); - } - - sector.specialdata = door; - sector.special = 0; - - door.thinkerFunction = T_VerticalDoor; - door.sector = sector; - door.direction = 2; - door.type = vldoor_e.raiseIn5Mins; - door.speed = VDOORSPEED; - P_FindLowestCeilingSurrounding: - { - door.topheight = sector.FindLowestCeilingSurrounding(); - } - door.topheight -= 4 * FRACUNIT; - door.topwait = VDOORWAIT; - door.topcountdown = 5 * 60 * 35; - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import data.sounds; +import defines.card_t; +import doom.SourceCode; +import doom.SourceCode.P_Doors; +import static doom.SourceCode.P_Doors.P_SpawnDoorCloseIn30; +import static doom.SourceCode.P_Doors.P_SpawnDoorRaiseIn5Mins; +import static doom.englsh.PD_BLUEK; +import static doom.englsh.PD_BLUEO; +import static doom.englsh.PD_REDK; +import static doom.englsh.PD_REDO; +import static doom.englsh.PD_YELLOWK; +import static doom.englsh.PD_YELLOWO; +import doom.player_t; +import doom.thinker_t; +import static m.fixed_t.FRACUNIT; +import p.ActiveStates; +import static p.ActiveStates.T_VerticalDoor; +import static p.DoorDefines.VDOORSPEED; +import static p.DoorDefines.VDOORWAIT; +import p.mobj_t; +import p.plat_t; +import p.result_e; +import p.vldoor_e; +import p.vldoor_t; +import rr.line_t; +import rr.sector_t; +import static utils.C2JUtils.eval; + +public interface ActionsDoors extends ActionsMoveEvents, ActionsUseEvents { + + result_e MovePlane(sector_t sector, int speed, int floorheight, boolean b, int i, int direction); + + void RemoveThinker(thinker_t door); + + int FindSectorFromLineTag(line_t line, int secnum); + + // + // VERTICAL DOORS + // + /** + * T_VerticalDoor + */ + default void VerticalDoor(vldoor_t door) { + switch (door.direction) { + case 0: + // WAITING + if (!eval(--door.topcountdown)) { + switch (door.type) { + case blazeRaise: + door.direction = -1; // time to go back down + StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_bdcls); + break; + case normal: + door.direction = -1; // time to go back down + StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_dorcls); + break; + case close30ThenOpen: + door.direction = 1; + StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_doropn); + break; + default: + break; + } + } + break; + + case 2: + // INITIAL WAIT + if (!eval(--door.topcountdown)) { + switch (door.type) { + case raiseIn5Mins: + door.direction = 1; + door.type = vldoor_e.normal; + StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_doropn); + break; + default: + break; + } + } + break; + + case -1: { + // DOWN + final result_e res = MovePlane(door.sector, door.speed, door.sector.floorheight, false, 1, door.direction); + if (res == result_e.pastdest) { + switch (door.type) { + case blazeRaise: + case blazeClose: + door.sector.specialdata = null; + RemoveThinker(door); // unlink and free + StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_bdcls); + break; + case normal: + case close: + door.sector.specialdata = null; + RemoveThinker(door); // unlink and free + break; + case close30ThenOpen: + door.direction = 0; + door.topcountdown = 35 * 30; + break; + default: + break; + } + } else if (res == result_e.crushed) { + switch (door.type) { + case blazeClose: + case close: // DO NOT GO BACK UP! + break; + default: + door.direction = 1; + StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_doropn); + } + } + break; + } + case 1: { + // UP + final result_e res = this.MovePlane(door.sector, door.speed, door.topheight, false, 1, door.direction); + + if (res == result_e.pastdest) { + switch (door.type) { + case blazeRaise: + case normal: + door.direction = 0; // wait at top + door.topcountdown = door.topwait; + break; + case close30ThenOpen: + case blazeOpen: + case open: + door.sector.specialdata = null; + RemoveThinker(door); // unlink and free + break; + default: + break; + } + } + break; + } + } + } + + /** + * EV_DoLockedDoor Move a locked door up/down + */ + @Override + default boolean DoLockedDoor(line_t line, vldoor_e type, mobj_t thing) { + player_t p; + + p = thing.player; + + if (p == null) { + return false; + } + + switch (line.special) { + case 99: // Blue Lock + case 133: + /* if ( p==null ) + return false; */ + if (!p.cards[card_t.it_bluecard.ordinal()] && !p.cards[card_t.it_blueskull.ordinal()]) { + p.message = PD_BLUEO; + StartSound(null, sounds.sfxenum_t.sfx_oof); + return false; + } + break; + + case 134: // Red Lock + case 135: + /* if ( p==null ) + return false; */ + if (!p.cards[card_t.it_redcard.ordinal()] && !p.cards[card_t.it_redskull.ordinal()]) { + p.message = PD_REDO; + StartSound(null, sounds.sfxenum_t.sfx_oof); + return false; + } + break; + + case 136: // Yellow Lock + case 137: + /* if ( p==null ) + return false; */ + if (!p.cards[card_t.it_yellowcard.ordinal()] + && !p.cards[card_t.it_yellowskull.ordinal()]) { + p.message = PD_YELLOWO; + StartSound(null, sounds.sfxenum_t.sfx_oof); + return false; + } + break; + } + + return DoDoor(line, type); + } + + @Override + default boolean DoDoor(line_t line, vldoor_e type) { + int secnum; + boolean rtn = false; + sector_t sec; + vldoor_t door; + + secnum = -1; + + while ((secnum = FindSectorFromLineTag(line, secnum)) >= 0) { + sec = levelLoader().sectors[secnum]; + if (sec.specialdata != null) { + continue; + } + + // new door thinker + rtn = true; + door = new vldoor_t(); + sec.specialdata = door; + door.thinkerFunction = ActiveStates.T_VerticalDoor; + AddThinker(door); + door.sector = sec; + door.type = type; + door.topwait = VDOORWAIT; + door.speed = VDOORSPEED; + + switch (type) { + case blazeClose: + door.topheight = sec.FindLowestCeilingSurrounding(); + door.topheight -= 4 * FRACUNIT; + door.direction = -1; + door.speed = VDOORSPEED * 4; + StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_bdcls); + break; + case close: + door.topheight = sec.FindLowestCeilingSurrounding(); + door.topheight -= 4 * FRACUNIT; + door.direction = -1; + StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_dorcls); + break; + case close30ThenOpen: + door.topheight = sec.ceilingheight; + door.direction = -1; + StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_dorcls); + break; + case blazeRaise: + case blazeOpen: + door.direction = 1; + door.topheight = sec.FindLowestCeilingSurrounding(); + door.topheight -= 4 * FRACUNIT; + door.speed = VDOORSPEED * 4; + if (door.topheight != sec.ceilingheight) { + StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_bdopn); + } + break; + case normal: + case open: + door.direction = 1; + door.topheight = sec.FindLowestCeilingSurrounding(); + door.topheight -= 4 * FRACUNIT; + if (door.topheight != sec.ceilingheight) { + StartSound(door.sector.soundorg, sounds.sfxenum_t.sfx_doropn); + } + default: + break; + } + + } + return rtn; + } + + /** + * EV_VerticalDoor : open a door manually, no tag value + */ + @Override + default void VerticalDoor(line_t line, mobj_t thing) { + player_t player; + //int secnum; + sector_t sec; + vldoor_t door; + int side; + + side = 0; // only front sides can be used + + // Check for locks + player = thing.player; + + switch (line.special) { + case 26: // Blue Lock + case 32: + if (player == null) { + return; + } + + if (!player.cards[card_t.it_bluecard.ordinal()] && !player.cards[card_t.it_blueskull.ordinal()]) { + player.message = PD_BLUEK; + StartSound(null, sounds.sfxenum_t.sfx_oof); + return; + } + break; + + case 27: // Yellow Lock + case 34: + if (player == null) { + return; + } + + if (!player.cards[card_t.it_yellowcard.ordinal()] && !player.cards[card_t.it_yellowskull.ordinal()]) { + player.message = PD_YELLOWK; + StartSound(null, sounds.sfxenum_t.sfx_oof); + return; + } + break; + + case 28: // Red Lock + case 33: + if (player == null) { + return; + } + + if (!player.cards[card_t.it_redcard.ordinal()] && !player.cards[card_t.it_redskull.ordinal()]) { + player.message = PD_REDK; + StartSound(null, sounds.sfxenum_t.sfx_oof); + return; + } + break; + } + + // if the sector has an active thinker, use it + sec = levelLoader().sides[line.sidenum[side ^ 1]].sector; + // secnum = sec.id; + + if (sec.specialdata != null) { + if (sec.specialdata instanceof plat_t) { + /** + * [MAES]: demo sync for e1nm0646: emulates active plat_t interpreted + * as door. TODO: add our own overflow handling class. + */ + door = ((plat_t) sec.specialdata).asVlDoor(levelLoader().sectors); + } else { + door = (vldoor_t) sec.specialdata; + } + switch (line.special) { + case 1: // ONLY FOR "RAISE" DOORS, NOT "OPEN"s + case 26: + case 27: + case 28: + case 117: + if (door.direction == -1) { + door.direction = 1; // go back up + } else { + if (thing.player == null) { + return; // JDC: bad guys never close doors + } + door.direction = -1; // start going down immediately + } + return; + } + } + + // for proper sound + switch (line.special) { + case 117: // BLAZING DOOR RAISE + case 118: // BLAZING DOOR OPEN + StartSound(sec.soundorg, sounds.sfxenum_t.sfx_bdopn); + break; + + case 1: // NORMAL DOOR SOUND + case 31: + StartSound(sec.soundorg, sounds.sfxenum_t.sfx_doropn); + break; + + default: // LOCKED DOOR SOUND + StartSound(sec.soundorg, sounds.sfxenum_t.sfx_doropn); + break; + } + + // new door thinker + door = new vldoor_t(); + sec.specialdata = door; + door.thinkerFunction = ActiveStates.T_VerticalDoor; + AddThinker(door); + door.sector = sec; + door.direction = 1; + door.speed = VDOORSPEED; + door.topwait = VDOORWAIT; + + switch (line.special) { + case 1: + case 26: + case 27: + case 28: + door.type = vldoor_e.normal; + break; + case 31: + case 32: + case 33: + case 34: + door.type = vldoor_e.open; + line.special = 0; + break; + case 117: // blazing door raise + door.type = vldoor_e.blazeRaise; + door.speed = VDOORSPEED * 4; + break; + case 118: // blazing door open + door.type = vldoor_e.blazeOpen; + line.special = 0; + door.speed = VDOORSPEED * 4; + } + + // find the top and bottom of the movement range + door.topheight = sec.FindLowestCeilingSurrounding(); + door.topheight -= 4 * FRACUNIT; + } + + // + // Spawn a door that closes after 30 seconds + // + @SourceCode.Exact + @P_Doors.C(P_SpawnDoorCloseIn30) + default void SpawnDoorCloseIn30(sector_t sector) { + vldoor_t door; + + Z_Malloc: + { + door = new vldoor_t(); + } + + P_AddThinker: + { + AddThinker(door); + } + + sector.specialdata = door; + sector.special = 0; + + door.thinkerFunction = T_VerticalDoor; + door.sector = sector; + door.direction = 0; + door.type = vldoor_e.normal; + door.speed = VDOORSPEED; + door.topcountdown = 30 * 35; + } + + /** + * Spawn a door that opens after 5 minutes + */ + @SourceCode.Exact + @P_Doors.C(P_SpawnDoorRaiseIn5Mins) + default void SpawnDoorRaiseIn5Mins(sector_t sector, int secnum) { + vldoor_t door; + + Z_Malloc: + { + door = new vldoor_t(); + } + + P_AddThinker: + { + AddThinker(door); + } + + sector.specialdata = door; + sector.special = 0; + + door.thinkerFunction = T_VerticalDoor; + door.sector = sector; + door.direction = 2; + door.type = vldoor_e.raiseIn5Mins; + door.speed = VDOORSPEED; + P_FindLowestCeilingSurrounding: + { + door.topheight = sector.FindLowestCeilingSurrounding(); + } + door.topheight -= 4 * FRACUNIT; + door.topwait = VDOORWAIT; + door.topcountdown = 5 * 60 * 35; + } +} \ No newline at end of file diff --git a/src/p/Actions/ActionsEnemies.java b/src/p/Actions/ActionsEnemies.java index 019ffb7..3f76bc1 100644 --- a/src/p/Actions/ActionsEnemies.java +++ b/src/p/Actions/ActionsEnemies.java @@ -1,284 +1,284 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import static data.Defines.MELEERANGE; -import static data.Limits.MAXSPECIALCROSS; -import static data.Tables.ANG270; -import static data.Tables.ANG90; -import static data.Tables.BITS32; -import data.mobjtype_t; -import defines.statenum_t; -import doom.SourceCode.fixed_t; -import static doom.items.weaponinfo; -import doom.player_t; -import static m.fixed_t.FRACUNIT; -import static p.MapUtils.AproxDistance; -import static p.MobjFlags.MF_JUSTHIT; -import p.mobj_t; -import rr.SceneRenderer; -import rr.line_t; -import static rr.line_t.ML_SOUNDBLOCK; -import static rr.line_t.ML_TWOSIDED; -import rr.sector_t; -import rr.side_t; -import utils.TraitFactory.ContextKey; - -public interface ActionsEnemies extends ActionsSight, ActionsSpawns { - - ContextKey KEY_ENEMIES = ACTION_KEY_CHAIN.newKey(ActionsEnemies.class, Enemies::new); - - class Enemies { - - mobj_t soundtarget; - // Peg to map movement - line_t[] spechitp = new line_t[MAXSPECIALCROSS]; - int numspechit; - } - - // - // ENEMY THINKING - // Enemies are allways spawned - // with targetplayer = -1, threshold = 0 - // Most monsters are spawned unaware of all players, - // but some can be made preaware - // - /** - * P_CheckMeleeRange - */ - default boolean CheckMeleeRange(mobj_t actor) { - mobj_t pl; - @fixed_t - int dist; - - if (actor.target == null) { - return false; - } - - pl = actor.target; - dist = AproxDistance(pl.x - actor.x, pl.y - actor.y); - - if (dist >= MELEERANGE - 20 * FRACUNIT + pl.info.radius) { - return false; - } - - return CheckSight(actor, actor.target); - } - - /** - * P_CheckMissileRange - */ - default boolean CheckMissileRange(mobj_t actor) { - @fixed_t - int dist; - - if (!CheckSight(actor, actor.target)) { - return false; - } - - if ((actor.flags & MF_JUSTHIT) != 0) { - // the target just hit the enemy, - // so fight back! - actor.flags &= ~MF_JUSTHIT; - return true; - } - - if (actor.reactiontime != 0) { - return false; // do not attack yet - } - - // OPTIMIZE: get this from a global checksight - dist = AproxDistance(actor.x - actor.target.x, actor.y - actor.target.y) - 64 * FRACUNIT; - - // [SYNC}: Major desync cause of desyncs. - // DO NOT compare with null! - if (actor.info.meleestate == statenum_t.S_NULL) { - dist -= 128 * FRACUNIT; // no melee attack, so fire more - } - - dist >>= 16; - - if (actor.type == mobjtype_t.MT_VILE) { - if (dist > 14 * 64) { - return false; // too far away - } - } - - if (actor.type == mobjtype_t.MT_UNDEAD) { - if (dist < 196) { - return false; // close for fist attack - } - dist >>= 1; - } - - if (actor.type == mobjtype_t.MT_CYBORG || actor.type == mobjtype_t.MT_SPIDER || actor.type == mobjtype_t.MT_SKULL) { - dist >>= 1; - } - - if (dist > 200) { - dist = 200; - } - - if (actor.type == mobjtype_t.MT_CYBORG && dist > 160) { - dist = 160; - } - - return P_Random() >= dist; - } - - // - // Called by P_NoiseAlert. - // Recursively traverse adjacent sectors, - // sound blocking lines cut off traversal. - // - default void RecursiveSound(sector_t sec, int soundblocks) { - final SceneRenderer sr = sceneRenderer(); - final Enemies en = contextRequire(KEY_ENEMIES); - final Movement mov = contextRequire(KEY_MOVEMENT); - int i; - line_t check; - sector_t other; - - // wake up all monsters in this sector - if (sec.validcount == sr.getValidCount() && sec.soundtraversed <= soundblocks + 1) { - return; // already flooded - } - - sec.validcount = sr.getValidCount(); - sec.soundtraversed = soundblocks + 1; - sec.soundtarget = en.soundtarget; - - // "peg" to the level loader for syntactic sugar - side_t[] sides = levelLoader().sides; - - for (i = 0; i < sec.linecount; i++) { - check = sec.lines[i]; - - if ((check.flags & ML_TWOSIDED) == 0) { - continue; - } - - LineOpening(check); - - if (mov.openrange <= 0) { - continue; // closed door - } - - if (sides[check.sidenum[0]].sector == sec) { - other = sides[check.sidenum[1]].sector; - } else { - other = sides[check.sidenum[0]].sector; - } - - if ((check.flags & ML_SOUNDBLOCK) != 0) { - if (soundblocks == 0) { - RecursiveSound(other, 1); - } - } else { - RecursiveSound(other, soundblocks); - } - } - } - - /** - * P_NoiseAlert - * If a monster yells at a player, - * it will alert other monsters to the player. - */ - default void NoiseAlert(mobj_t target, mobj_t emmiter) { - final Enemies en = contextRequire(KEY_ENEMIES); - en.soundtarget = target; - sceneRenderer().increaseValidCount(1); - RecursiveSound(emmiter.subsector.sector, 0); - } - - /** - * P_FireWeapon. Originally in pspr - */ - default void FireWeapon(player_t player) { - statenum_t newstate; - - if (!player.CheckAmmo()) { - return; - } - - player.mo.SetMobjState(statenum_t.S_PLAY_ATK1); - newstate = weaponinfo[player.readyweapon.ordinal()].atkstate; - player.SetPsprite(player_t.ps_weapon, newstate); - NoiseAlert(player.mo, player.mo); - } - - /** - * P_LookForPlayers If allaround is false, only look 180 degrees in - * front. Returns true if a player is targeted. - */ - default boolean LookForPlayers(mobj_t actor, boolean allaround) { - final SceneRenderer sr = sceneRenderer(); - - int c; - int stop; - player_t player; - // sector_t sector; - long an; // angle - int dist; // fixed - - // sector = actor.subsector.sector; - c = 0; - stop = (actor.lastlook - 1) & 3; - - for (;; actor.lastlook = (actor.lastlook + 1) & 3) { - if (!PlayerInGame(actor.lastlook)) { - continue; - } - - if (c++ == 2 || actor.lastlook == stop) { - // done looking - return false; - } - - player = getPlayer(actor.lastlook); - - if (player.health[0] <= 0) { - continue; // dead - } - - if (!CheckSight(actor, player.mo)) { - continue; // out of sight - } - - if (!allaround) { - an = (sr.PointToAngle2(actor.x, actor.y, player.mo.x, player.mo.y) - actor.angle) & BITS32; - - if (an > ANG90 && an < ANG270) { - dist = AproxDistance(player.mo.x - actor.x, player.mo.y - actor.y); - - // if real close, react anyway - if (dist > MELEERANGE) { - continue; // behind back - } - } - } - - actor.target = player.mo; - return true; - } - // The compiler complains that this is unreachable - // return false; - } - -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import static data.Defines.MELEERANGE; +import static data.Limits.MAXSPECIALCROSS; +import static data.Tables.ANG270; +import static data.Tables.ANG90; +import static data.Tables.BITS32; +import data.mobjtype_t; +import defines.statenum_t; +import doom.SourceCode.fixed_t; +import static doom.items.weaponinfo; +import doom.player_t; +import static m.fixed_t.FRACUNIT; +import static p.MapUtils.AproxDistance; +import static p.MobjFlags.MF_JUSTHIT; +import p.mobj_t; +import rr.SceneRenderer; +import rr.line_t; +import static rr.line_t.ML_SOUNDBLOCK; +import static rr.line_t.ML_TWOSIDED; +import rr.sector_t; +import rr.side_t; +import utils.TraitFactory.ContextKey; + +public interface ActionsEnemies extends ActionsSight, ActionsSpawns { + + ContextKey KEY_ENEMIES = ACTION_KEY_CHAIN.newKey(ActionsEnemies.class, Enemies::new); + + class Enemies { + + mobj_t soundtarget; + // Peg to map movement + line_t[] spechitp = new line_t[MAXSPECIALCROSS]; + int numspechit; + } + + // + // ENEMY THINKING + // Enemies are allways spawned + // with targetplayer = -1, threshold = 0 + // Most monsters are spawned unaware of all players, + // but some can be made preaware + // + /** + * P_CheckMeleeRange + */ + default boolean CheckMeleeRange(mobj_t actor) { + mobj_t pl; + @fixed_t + int dist; + + if (actor.target == null) { + return false; + } + + pl = actor.target; + dist = AproxDistance(pl.x - actor.x, pl.y - actor.y); + + if (dist >= MELEERANGE - 20 * FRACUNIT + pl.info.radius) { + return false; + } + + return CheckSight(actor, actor.target); + } + + /** + * P_CheckMissileRange + */ + default boolean CheckMissileRange(mobj_t actor) { + @fixed_t + int dist; + + if (!CheckSight(actor, actor.target)) { + return false; + } + + if ((actor.flags & MF_JUSTHIT) != 0) { + // the target just hit the enemy, + // so fight back! + actor.flags &= ~MF_JUSTHIT; + return true; + } + + if (actor.reactiontime != 0) { + return false; // do not attack yet + } + + // OPTIMIZE: get this from a global checksight + dist = AproxDistance(actor.x - actor.target.x, actor.y - actor.target.y) - 64 * FRACUNIT; + + // [SYNC}: Major desync cause of desyncs. + // DO NOT compare with null! + if (actor.info.meleestate == statenum_t.S_NULL) { + dist -= 128 * FRACUNIT; // no melee attack, so fire more + } + + dist >>= 16; + + if (actor.type == mobjtype_t.MT_VILE) { + if (dist > 14 * 64) { + return false; // too far away + } + } + + if (actor.type == mobjtype_t.MT_UNDEAD) { + if (dist < 196) { + return false; // close for fist attack + } + dist >>= 1; + } + + if (actor.type == mobjtype_t.MT_CYBORG || actor.type == mobjtype_t.MT_SPIDER || actor.type == mobjtype_t.MT_SKULL) { + dist >>= 1; + } + + if (dist > 200) { + dist = 200; + } + + if (actor.type == mobjtype_t.MT_CYBORG && dist > 160) { + dist = 160; + } + + return P_Random() >= dist; + } + + // + // Called by P_NoiseAlert. + // Recursively traverse adjacent sectors, + // sound blocking lines cut off traversal. + // + default void RecursiveSound(sector_t sec, int soundblocks) { + final SceneRenderer sr = sceneRenderer(); + final Enemies en = contextRequire(KEY_ENEMIES); + final Movement mov = contextRequire(KEY_MOVEMENT); + int i; + line_t check; + sector_t other; + + // wake up all monsters in this sector + if (sec.validcount == sr.getValidCount() && sec.soundtraversed <= soundblocks + 1) { + return; // already flooded + } + + sec.validcount = sr.getValidCount(); + sec.soundtraversed = soundblocks + 1; + sec.soundtarget = en.soundtarget; + + // "peg" to the level loader for syntactic sugar + side_t[] sides = levelLoader().sides; + + for (i = 0; i < sec.linecount; i++) { + check = sec.lines[i]; + + if ((check.flags & ML_TWOSIDED) == 0) { + continue; + } + + LineOpening(check); + + if (mov.openrange <= 0) { + continue; // closed door + } + + if (sides[check.sidenum[0]].sector == sec) { + other = sides[check.sidenum[1]].sector; + } else { + other = sides[check.sidenum[0]].sector; + } + + if ((check.flags & ML_SOUNDBLOCK) != 0) { + if (soundblocks == 0) { + RecursiveSound(other, 1); + } + } else { + RecursiveSound(other, soundblocks); + } + } + } + + /** + * P_NoiseAlert + * If a monster yells at a player, + * it will alert other monsters to the player. + */ + default void NoiseAlert(mobj_t target, mobj_t emmiter) { + final Enemies en = contextRequire(KEY_ENEMIES); + en.soundtarget = target; + sceneRenderer().increaseValidCount(1); + RecursiveSound(emmiter.subsector.sector, 0); + } + + /** + * P_FireWeapon. Originally in pspr + */ + default void FireWeapon(player_t player) { + statenum_t newstate; + + if (!player.CheckAmmo()) { + return; + } + + player.mo.SetMobjState(statenum_t.S_PLAY_ATK1); + newstate = weaponinfo[player.readyweapon.ordinal()].atkstate; + player.SetPsprite(player_t.ps_weapon, newstate); + NoiseAlert(player.mo, player.mo); + } + + /** + * P_LookForPlayers If allaround is false, only look 180 degrees in + * front. Returns true if a player is targeted. + */ + default boolean LookForPlayers(mobj_t actor, boolean allaround) { + final SceneRenderer sr = sceneRenderer(); + + int c; + int stop; + player_t player; + // sector_t sector; + long an; // angle + int dist; // fixed + + // sector = actor.subsector.sector; + c = 0; + stop = (actor.lastlook - 1) & 3; + + for (;; actor.lastlook = (actor.lastlook + 1) & 3) { + if (!PlayerInGame(actor.lastlook)) { + continue; + } + + if (c++ == 2 || actor.lastlook == stop) { + // done looking + return false; + } + + player = getPlayer(actor.lastlook); + + if (player.health[0] <= 0) { + continue; // dead + } + + if (!CheckSight(actor, player.mo)) { + continue; // out of sight + } + + if (!allaround) { + an = (sr.PointToAngle2(actor.x, actor.y, player.mo.x, player.mo.y) - actor.angle) & BITS32; + + if (an > ANG90 && an < ANG270) { + dist = AproxDistance(player.mo.x - actor.x, player.mo.y - actor.y); + + // if real close, react anyway + if (dist > MELEERANGE) { + continue; // behind back + } + } + } + + actor.target = player.mo; + return true; + } + // The compiler complains that this is unreachable + // return false; + } + +} \ No newline at end of file diff --git a/src/p/Actions/ActionsFloors.java b/src/p/Actions/ActionsFloors.java index 540a286..f25b890 100644 --- a/src/p/Actions/ActionsFloors.java +++ b/src/p/Actions/ActionsFloors.java @@ -1,422 +1,422 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import static data.Limits.MAXINT; -import data.sounds; -import m.fixed_t; -import static m.fixed_t.FRACUNIT; -import p.ActiveStates; -import p.floor_e; -import p.floormove_t; -import p.plat_e; -import p.plat_t; -import p.plattype_e; -import p.result_e; -import p.stair_e; -import rr.line_t; -import static rr.line_t.ML_TWOSIDED; -import rr.sector_t; -import rr.side_t; -import static utils.C2JUtils.eval; - -public interface ActionsFloors extends ActionsPlats { - - result_e MovePlane(sector_t sector, int speed, int floordestheight, boolean crush, int i, int direction); - - boolean twoSided(int secnum, int i); - - side_t getSide(int secnum, int i, int s); - - sector_t getSector(int secnum, int i, int i0); - - // - // FLOORS - // - int FLOORSPEED = fixed_t.MAPFRACUNIT; - - /** - * MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN) - */ - default void MoveFloor(floormove_t floor) { - final result_e res = MovePlane(floor.sector, floor.speed, floor.floordestheight, floor.crush, 0, floor.direction); - - if (!eval(LevelTime() & 7)) { - StartSound(floor.sector.soundorg, sounds.sfxenum_t.sfx_stnmov); - } - - if (res == result_e.pastdest) { - floor.sector.specialdata = null; - - if (floor.direction == 1) { - switch (floor.type) { - case donutRaise: - floor.sector.special = (short) floor.newspecial; - floor.sector.floorpic = floor.texture; - default: - break; - } - } else if (floor.direction == -1) { - switch (floor.type) //TODO: check if a null floor.type is valid or a bug - // MAES: actually, type should always be set to something. - // In C, this means "zero" or "null". In Java, we must make sure - // it's actually set to something all the time. - { - case lowerAndChange: - floor.sector.special = (short) floor.newspecial; - floor.sector.floorpic = floor.texture; - default: - break; - } - } - - RemoveThinker(floor); - StartSound(floor.sector.soundorg, sounds.sfxenum_t.sfx_pstop); - } - } - - // - // HANDLE FLOOR TYPES - // - @Override - default boolean DoFloor(line_t line, floor_e floortype) { - int secnum = -1; - boolean rtn = false; - sector_t sec; - floormove_t floor; - - while ((secnum = FindSectorFromLineTag(line, secnum)) >= 0) { - sec = levelLoader().sectors[secnum]; - - // ALREADY MOVING? IF SO, KEEP GOING... - if (sec.specialdata != null) { - continue; - } - - // new floor thinker - rtn = true; - floor = new floormove_t(); - sec.specialdata = floor; - floor.thinkerFunction = ActiveStates.T_MoveFloor; - AddThinker(floor); - floor.type = floortype; - floor.crush = false; - - switch (floortype) { - case lowerFloor: - floor.direction = -1; - floor.sector = sec; - floor.speed = FLOORSPEED; - floor.floordestheight = sec.FindHighestFloorSurrounding(); - break; - - case lowerFloorToLowest: - floor.direction = -1; - floor.sector = sec; - floor.speed = FLOORSPEED; - floor.floordestheight = sec.FindLowestFloorSurrounding(); - break; - - case turboLower: - floor.direction = -1; - floor.sector = sec; - floor.speed = FLOORSPEED * 4; - floor.floordestheight = sec.FindHighestFloorSurrounding(); - if (floor.floordestheight != sec.floorheight) { - floor.floordestheight += 8 * FRACUNIT; - } - break; - - case raiseFloorCrush: - floor.crush = true; - case raiseFloor: - floor.direction = 1; - floor.sector = sec; - floor.speed = FLOORSPEED; - floor.floordestheight = sec.FindLowestCeilingSurrounding(); - if (floor.floordestheight > sec.ceilingheight) { - floor.floordestheight = sec.ceilingheight; - } - floor.floordestheight -= (8 * FRACUNIT) - * eval(floortype == floor_e.raiseFloorCrush); - break; - - case raiseFloorTurbo: - floor.direction = 1; - floor.sector = sec; - floor.speed = FLOORSPEED * 4; - floor.floordestheight = sec.FindNextHighestFloor(sec.floorheight); - break; - - case raiseFloorToNearest: - floor.direction = 1; - floor.sector = sec; - floor.speed = FLOORSPEED; - floor.floordestheight = sec.FindNextHighestFloor(sec.floorheight); - break; - - case raiseFloor24: - floor.direction = 1; - floor.sector = sec; - floor.speed = FLOORSPEED; - floor.floordestheight = floor.sector.floorheight + 24 * FRACUNIT; - break; - case raiseFloor512: - floor.direction = 1; - floor.sector = sec; - floor.speed = FLOORSPEED; - floor.floordestheight = floor.sector.floorheight + 512 * FRACUNIT; - break; - - case raiseFloor24AndChange: - floor.direction = 1; - floor.sector = sec; - floor.speed = FLOORSPEED; - floor.floordestheight = floor.sector.floorheight + 24 * FRACUNIT; - sec.floorpic = line.frontsector.floorpic; - sec.special = line.frontsector.special; - break; - - case raiseToTexture: { - int minsize = MAXINT; - side_t side; - - floor.direction = 1; - floor.sector = sec; - floor.speed = FLOORSPEED; - for (int i = 0; i < sec.linecount; ++i) { - if (twoSided(secnum, i)) { - for (int s = 0; s < 2; ++s) { - side = getSide(secnum, i, s); - if (side.bottomtexture >= 0) { - if (DOOM().textureManager.getTextureheight(side.bottomtexture) < minsize) { - minsize = DOOM().textureManager.getTextureheight(side.bottomtexture); - } - } - } - } - } - floor.floordestheight = floor.sector.floorheight + minsize; - } - break; - - case lowerAndChange: - floor.direction = -1; - floor.sector = sec; - floor.speed = FLOORSPEED; - floor.floordestheight = sec.FindLowestFloorSurrounding(); - floor.texture = sec.floorpic; - - for (int i = 0; i < sec.linecount; i++) { - if (twoSided(secnum, i)) { - if (getSide(secnum, i, 0).sector.id == secnum) { - sec = getSector(secnum, i, 1); - if (sec.floorheight == floor.floordestheight) { - floor.texture = sec.floorpic; - floor.newspecial = sec.special; - break; - } - } else { - sec = getSector(secnum, i, 0); - if (sec.floorheight == floor.floordestheight) { - floor.texture = sec.floorpic; - floor.newspecial = sec.special; - break; - } - } - } - } - default: - break; - } - } - return rtn; - } - - /** - * BUILD A STAIRCASE! - */ - @Override - default boolean BuildStairs(line_t line, stair_e type) { - int secnum; - int height; - int i; - int newsecnum; - int texture; - boolean ok; - boolean rtn; - - sector_t sec; - sector_t tsec; - - floormove_t floor; - - int stairsize = 0; - int speed = 0; // shut up compiler - - secnum = -1; - rtn = false; - while ((secnum = FindSectorFromLineTag(line, secnum)) >= 0) { - sec = levelLoader().sectors[secnum]; - - // ALREADY MOVING? IF SO, KEEP GOING... - if (sec.specialdata != null) { - continue; - } - - // new floor thinker - rtn = true; - floor = new floormove_t(); - sec.specialdata = floor; - floor.thinkerFunction = ActiveStates.T_MoveFloor; - AddThinker(floor); - floor.direction = 1; - floor.sector = sec; - switch (type) { - case build8: - speed = FLOORSPEED / 4; - stairsize = 8 * FRACUNIT; - break; - case turbo16: - speed = FLOORSPEED * 4; - stairsize = 16 * FRACUNIT; - break; - } - floor.speed = speed; - height = sec.floorheight + stairsize; - floor.floordestheight = height; - - texture = sec.floorpic; - - // Find next sector to raise - // 1. Find 2-sided line with same sector side[0] - // 2. Other side is the next sector to raise - do { - ok = false; - for (i = 0; i < sec.linecount; i++) { - if (!eval((sec.lines[i]).flags & ML_TWOSIDED)) { - continue; - } - - tsec = (sec.lines[i]).frontsector; - newsecnum = tsec.id; - - if (secnum != newsecnum) { - continue; - } - - tsec = (sec.lines[i]).backsector; - newsecnum = tsec.id; - - if (tsec.floorpic != texture) { - continue; - } - - height += stairsize; - - if (tsec.specialdata != null) { - continue; - } - - sec = tsec; - secnum = newsecnum; - floor = new floormove_t(); - sec.specialdata = floor; - floor.thinkerFunction = ActiveStates.T_MoveFloor; - AddThinker(floor); - floor.direction = 1; - floor.sector = sec; - floor.speed = speed; - floor.floordestheight = height; - ok = true; - break; - } - } while (ok); - } - return rtn; - } - - /** - * Move a plat up and down - */ - default void PlatRaise(plat_t plat) { - result_e res; - - switch (plat.status) { - case up: - res = MovePlane(plat.sector, plat.speed, plat.high, plat.crush, 0, 1); - - if (plat.type == plattype_e.raiseAndChange - || plat.type == plattype_e.raiseToNearestAndChange) { - if (!eval(LevelTime() & 7)) { - StartSound(plat.sector.soundorg, sounds.sfxenum_t.sfx_stnmov); - } - } - - if (res == result_e.crushed && (!plat.crush)) { - plat.count = plat.wait; - plat.status = plat_e.down; - StartSound(plat.sector.soundorg, sounds.sfxenum_t.sfx_pstart); - } else { - if (res == result_e.pastdest) { - plat.count = plat.wait; - plat.status = plat_e.waiting; - StartSound(plat.sector.soundorg, sounds.sfxenum_t.sfx_pstop); - - switch (plat.type) { - case blazeDWUS: - case downWaitUpStay: - RemoveActivePlat(plat); - break; - - case raiseAndChange: - case raiseToNearestAndChange: - RemoveActivePlat(plat); - break; - - default: - break; - } - } - } - break; - - case down: - res = MovePlane(plat.sector, plat.speed, plat.low, false, 0, -1); - - if (res == result_e.pastdest) { - plat.count = plat.wait; - plat.status = plat_e.waiting; - StartSound(plat.sector.soundorg, sounds.sfxenum_t.sfx_pstop); - } - break; - - case waiting: - if (--plat.count == 0) { - if (plat.sector.floorheight == plat.low) { - plat.status = plat_e.up; - } else { - plat.status = plat_e.down; - } - StartSound(plat.sector.soundorg, sounds.sfxenum_t.sfx_pstart); - } - case in_stasis: - break; - } - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import static data.Limits.MAXINT; +import data.sounds; +import m.fixed_t; +import static m.fixed_t.FRACUNIT; +import p.ActiveStates; +import p.floor_e; +import p.floormove_t; +import p.plat_e; +import p.plat_t; +import p.plattype_e; +import p.result_e; +import p.stair_e; +import rr.line_t; +import static rr.line_t.ML_TWOSIDED; +import rr.sector_t; +import rr.side_t; +import static utils.C2JUtils.eval; + +public interface ActionsFloors extends ActionsPlats { + + result_e MovePlane(sector_t sector, int speed, int floordestheight, boolean crush, int i, int direction); + + boolean twoSided(int secnum, int i); + + side_t getSide(int secnum, int i, int s); + + sector_t getSector(int secnum, int i, int i0); + + // + // FLOORS + // + int FLOORSPEED = fixed_t.MAPFRACUNIT; + + /** + * MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN) + */ + default void MoveFloor(floormove_t floor) { + final result_e res = MovePlane(floor.sector, floor.speed, floor.floordestheight, floor.crush, 0, floor.direction); + + if (!eval(LevelTime() & 7)) { + StartSound(floor.sector.soundorg, sounds.sfxenum_t.sfx_stnmov); + } + + if (res == result_e.pastdest) { + floor.sector.specialdata = null; + + if (floor.direction == 1) { + switch (floor.type) { + case donutRaise: + floor.sector.special = (short) floor.newspecial; + floor.sector.floorpic = floor.texture; + default: + break; + } + } else if (floor.direction == -1) { + switch (floor.type) //TODO: check if a null floor.type is valid or a bug + // MAES: actually, type should always be set to something. + // In C, this means "zero" or "null". In Java, we must make sure + // it's actually set to something all the time. + { + case lowerAndChange: + floor.sector.special = (short) floor.newspecial; + floor.sector.floorpic = floor.texture; + default: + break; + } + } + + RemoveThinker(floor); + StartSound(floor.sector.soundorg, sounds.sfxenum_t.sfx_pstop); + } + } + + // + // HANDLE FLOOR TYPES + // + @Override + default boolean DoFloor(line_t line, floor_e floortype) { + int secnum = -1; + boolean rtn = false; + sector_t sec; + floormove_t floor; + + while ((secnum = FindSectorFromLineTag(line, secnum)) >= 0) { + sec = levelLoader().sectors[secnum]; + + // ALREADY MOVING? IF SO, KEEP GOING... + if (sec.specialdata != null) { + continue; + } + + // new floor thinker + rtn = true; + floor = new floormove_t(); + sec.specialdata = floor; + floor.thinkerFunction = ActiveStates.T_MoveFloor; + AddThinker(floor); + floor.type = floortype; + floor.crush = false; + + switch (floortype) { + case lowerFloor: + floor.direction = -1; + floor.sector = sec; + floor.speed = FLOORSPEED; + floor.floordestheight = sec.FindHighestFloorSurrounding(); + break; + + case lowerFloorToLowest: + floor.direction = -1; + floor.sector = sec; + floor.speed = FLOORSPEED; + floor.floordestheight = sec.FindLowestFloorSurrounding(); + break; + + case turboLower: + floor.direction = -1; + floor.sector = sec; + floor.speed = FLOORSPEED * 4; + floor.floordestheight = sec.FindHighestFloorSurrounding(); + if (floor.floordestheight != sec.floorheight) { + floor.floordestheight += 8 * FRACUNIT; + } + break; + + case raiseFloorCrush: + floor.crush = true; + case raiseFloor: + floor.direction = 1; + floor.sector = sec; + floor.speed = FLOORSPEED; + floor.floordestheight = sec.FindLowestCeilingSurrounding(); + if (floor.floordestheight > sec.ceilingheight) { + floor.floordestheight = sec.ceilingheight; + } + floor.floordestheight -= (8 * FRACUNIT) + * eval(floortype == floor_e.raiseFloorCrush); + break; + + case raiseFloorTurbo: + floor.direction = 1; + floor.sector = sec; + floor.speed = FLOORSPEED * 4; + floor.floordestheight = sec.FindNextHighestFloor(sec.floorheight); + break; + + case raiseFloorToNearest: + floor.direction = 1; + floor.sector = sec; + floor.speed = FLOORSPEED; + floor.floordestheight = sec.FindNextHighestFloor(sec.floorheight); + break; + + case raiseFloor24: + floor.direction = 1; + floor.sector = sec; + floor.speed = FLOORSPEED; + floor.floordestheight = floor.sector.floorheight + 24 * FRACUNIT; + break; + case raiseFloor512: + floor.direction = 1; + floor.sector = sec; + floor.speed = FLOORSPEED; + floor.floordestheight = floor.sector.floorheight + 512 * FRACUNIT; + break; + + case raiseFloor24AndChange: + floor.direction = 1; + floor.sector = sec; + floor.speed = FLOORSPEED; + floor.floordestheight = floor.sector.floorheight + 24 * FRACUNIT; + sec.floorpic = line.frontsector.floorpic; + sec.special = line.frontsector.special; + break; + + case raiseToTexture: { + int minsize = MAXINT; + side_t side; + + floor.direction = 1; + floor.sector = sec; + floor.speed = FLOORSPEED; + for (int i = 0; i < sec.linecount; ++i) { + if (twoSided(secnum, i)) { + for (int s = 0; s < 2; ++s) { + side = getSide(secnum, i, s); + if (side.bottomtexture >= 0) { + if (DOOM().textureManager.getTextureheight(side.bottomtexture) < minsize) { + minsize = DOOM().textureManager.getTextureheight(side.bottomtexture); + } + } + } + } + } + floor.floordestheight = floor.sector.floorheight + minsize; + } + break; + + case lowerAndChange: + floor.direction = -1; + floor.sector = sec; + floor.speed = FLOORSPEED; + floor.floordestheight = sec.FindLowestFloorSurrounding(); + floor.texture = sec.floorpic; + + for (int i = 0; i < sec.linecount; i++) { + if (twoSided(secnum, i)) { + if (getSide(secnum, i, 0).sector.id == secnum) { + sec = getSector(secnum, i, 1); + if (sec.floorheight == floor.floordestheight) { + floor.texture = sec.floorpic; + floor.newspecial = sec.special; + break; + } + } else { + sec = getSector(secnum, i, 0); + if (sec.floorheight == floor.floordestheight) { + floor.texture = sec.floorpic; + floor.newspecial = sec.special; + break; + } + } + } + } + default: + break; + } + } + return rtn; + } + + /** + * BUILD A STAIRCASE! + */ + @Override + default boolean BuildStairs(line_t line, stair_e type) { + int secnum; + int height; + int i; + int newsecnum; + int texture; + boolean ok; + boolean rtn; + + sector_t sec; + sector_t tsec; + + floormove_t floor; + + int stairsize = 0; + int speed = 0; // shut up compiler + + secnum = -1; + rtn = false; + while ((secnum = FindSectorFromLineTag(line, secnum)) >= 0) { + sec = levelLoader().sectors[secnum]; + + // ALREADY MOVING? IF SO, KEEP GOING... + if (sec.specialdata != null) { + continue; + } + + // new floor thinker + rtn = true; + floor = new floormove_t(); + sec.specialdata = floor; + floor.thinkerFunction = ActiveStates.T_MoveFloor; + AddThinker(floor); + floor.direction = 1; + floor.sector = sec; + switch (type) { + case build8: + speed = FLOORSPEED / 4; + stairsize = 8 * FRACUNIT; + break; + case turbo16: + speed = FLOORSPEED * 4; + stairsize = 16 * FRACUNIT; + break; + } + floor.speed = speed; + height = sec.floorheight + stairsize; + floor.floordestheight = height; + + texture = sec.floorpic; + + // Find next sector to raise + // 1. Find 2-sided line with same sector side[0] + // 2. Other side is the next sector to raise + do { + ok = false; + for (i = 0; i < sec.linecount; i++) { + if (!eval((sec.lines[i]).flags & ML_TWOSIDED)) { + continue; + } + + tsec = (sec.lines[i]).frontsector; + newsecnum = tsec.id; + + if (secnum != newsecnum) { + continue; + } + + tsec = (sec.lines[i]).backsector; + newsecnum = tsec.id; + + if (tsec.floorpic != texture) { + continue; + } + + height += stairsize; + + if (tsec.specialdata != null) { + continue; + } + + sec = tsec; + secnum = newsecnum; + floor = new floormove_t(); + sec.specialdata = floor; + floor.thinkerFunction = ActiveStates.T_MoveFloor; + AddThinker(floor); + floor.direction = 1; + floor.sector = sec; + floor.speed = speed; + floor.floordestheight = height; + ok = true; + break; + } + } while (ok); + } + return rtn; + } + + /** + * Move a plat up and down + */ + default void PlatRaise(plat_t plat) { + result_e res; + + switch (plat.status) { + case up: + res = MovePlane(plat.sector, plat.speed, plat.high, plat.crush, 0, 1); + + if (plat.type == plattype_e.raiseAndChange + || plat.type == plattype_e.raiseToNearestAndChange) { + if (!eval(LevelTime() & 7)) { + StartSound(plat.sector.soundorg, sounds.sfxenum_t.sfx_stnmov); + } + } + + if (res == result_e.crushed && (!plat.crush)) { + plat.count = plat.wait; + plat.status = plat_e.down; + StartSound(plat.sector.soundorg, sounds.sfxenum_t.sfx_pstart); + } else { + if (res == result_e.pastdest) { + plat.count = plat.wait; + plat.status = plat_e.waiting; + StartSound(plat.sector.soundorg, sounds.sfxenum_t.sfx_pstop); + + switch (plat.type) { + case blazeDWUS: + case downWaitUpStay: + RemoveActivePlat(plat); + break; + + case raiseAndChange: + case raiseToNearestAndChange: + RemoveActivePlat(plat); + break; + + default: + break; + } + } + } + break; + + case down: + res = MovePlane(plat.sector, plat.speed, plat.low, false, 0, -1); + + if (res == result_e.pastdest) { + plat.count = plat.wait; + plat.status = plat_e.waiting; + StartSound(plat.sector.soundorg, sounds.sfxenum_t.sfx_pstop); + } + break; + + case waiting: + if (--plat.count == 0) { + if (plat.sector.floorheight == plat.low) { + plat.status = plat_e.up; + } else { + plat.status = plat_e.down; + } + StartSound(plat.sector.soundorg, sounds.sfxenum_t.sfx_pstart); + } + case in_stasis: + break; + } + } +} \ No newline at end of file diff --git a/src/p/Actions/ActionsLights.java b/src/p/Actions/ActionsLights.java index b3386b7..67643e9 100644 --- a/src/p/Actions/ActionsLights.java +++ b/src/p/Actions/ActionsLights.java @@ -1,365 +1,365 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import doom.SourceCode; -import doom.SourceCode.P_Lights; -import static doom.SourceCode.P_Lights.P_SpawnFireFlicker; -import static doom.SourceCode.P_Lights.P_SpawnGlowingLight; -import static doom.SourceCode.P_Lights.P_SpawnLightFlash; -import static doom.SourceCode.P_Lights.P_SpawnStrobeFlash; -import doom.SourceCode.P_Spec; -import static doom.SourceCode.P_Spec.P_FindMinSurroundingLight; -import java.io.DataInputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import p.AbstractLevelLoader; -import static p.ActiveStates.T_FireFlicker; -import static p.ActiveStates.T_Glow; -import static p.ActiveStates.T_LightFlash; -import static p.ActiveStates.T_StrobeFlash; -import static p.DoorDefines.SLOWDARK; -import static p.DoorDefines.STROBEBRIGHT; -import p.strobe_t; -import rr.SectorAction; -import rr.line_t; -import rr.sector_t; -import w.DoomIO; - -public interface ActionsLights extends ActionsMoveEvents, ActionsUseEvents { - - int FindSectorFromLineTag(line_t line, int secnum); - - // - // P_LIGHTS - // - public class fireflicker_t extends SectorAction { - - public int count; - public int maxlight; - public int minlight; - } - - // - // BROKEN LIGHT EFFECT - // - public class lightflash_t extends SectorAction { - - public int count; - public int maxlight; - public int minlight; - public int maxtime; - public int mintime; - - @Override - public void read(DataInputStream f) throws IOException { - super.read(f); // Call thinker reader first - super.sectorid = DoomIO.readLEInt(f); // Sector index - count = DoomIO.readLEInt(f); - maxlight = DoomIO.readLEInt(f); - minlight = DoomIO.readLEInt(f); - maxtime = DoomIO.readLEInt(f); - mintime = DoomIO.readLEInt(f); - } - - @Override - public void pack(ByteBuffer b) throws IOException { - super.pack(b); //12 - b.putInt(super.sectorid); // 16 - b.putInt(count); //20 - b.putInt(maxlight);//24 - b.putInt(minlight);//28 - b.putInt(maxtime);//32 - b.putInt(mintime);//36 - } - } - - public class glow_t extends SectorAction { - - public int minlight; - public int maxlight; - public int direction; - - @Override - public void read(DataInputStream f) throws IOException { - - super.read(f); // Call thinker reader first - super.sectorid = DoomIO.readLEInt(f); // Sector index - minlight = DoomIO.readLEInt(f); - maxlight = DoomIO.readLEInt(f); - direction = DoomIO.readLEInt(f); - } - - @Override - public void pack(ByteBuffer b) throws IOException { - super.pack(b); //12 - b.putInt(super.sectorid); // 16 - b.putInt(minlight);//20 - b.putInt(maxlight);//24 - b.putInt(direction);//38 - } - } - - // - // Find minimum light from an adjacent sector - // - @SourceCode.Exact - @P_Spec.C(P_FindMinSurroundingLight) - default int FindMinSurroundingLight(sector_t sector, int max) { - int min; - line_t line; - sector_t check; - - min = max; - for (int i = 0; i < sector.linecount; i++) { - line = sector.lines[i]; - getNextSector: - { - check = line.getNextSector(sector); - } - - if (check == null) { - continue; - } - - if (check.lightlevel < min) { - min = check.lightlevel; - } - } - return min; - } - - /** - * P_SpawnLightFlash After the map has been loaded, scan each sector for - * specials that spawn thinkers - */ - @SourceCode.Exact - @P_Lights.C(P_SpawnLightFlash) - default void SpawnLightFlash(sector_t sector) { - lightflash_t flash; - - // nothing special about it during gameplay - sector.special = 0; - - Z_Malloc: - { - flash = new lightflash_t(); - } - - P_AddThinker: - { - AddThinker(flash); - } - - flash.thinkerFunction = T_LightFlash; - flash.sector = sector; - flash.maxlight = sector.lightlevel; - - flash.minlight = FindMinSurroundingLight(sector, sector.lightlevel); - flash.maxtime = 64; - flash.mintime = 7; - flash.count = (P_Random() & flash.maxtime) + 1; - } - - // - // P_SpawnStrobeFlash - // After the map has been loaded, scan each sector - // for specials that spawn thinkers - // - @SourceCode.Exact - @P_Lights.C(P_SpawnStrobeFlash) - default void SpawnStrobeFlash(sector_t sector, int fastOrSlow, int inSync) { - strobe_t flash; - - Z_Malloc: - { - flash = new strobe_t(); - } - - P_AddThinker: - { - AddThinker(flash); - } - - flash.sector = sector; - flash.darktime = fastOrSlow; - flash.brighttime = STROBEBRIGHT; - flash.thinkerFunction = T_StrobeFlash; - flash.maxlight = sector.lightlevel; - flash.minlight = FindMinSurroundingLight(sector, sector.lightlevel); - - if (flash.minlight == flash.maxlight) { - flash.minlight = 0; - } - - // nothing special about it during gameplay - sector.special = 0; - - if (inSync == 0) { - flash.count = (P_Random() & 7) + 1; - } else { - flash.count = 1; - } - } - - @SourceCode.Exact - @P_Lights.C(P_SpawnGlowingLight) - default void SpawnGlowingLight(sector_t sector) { - glow_t g; - - Z_Malloc: - { - g = new glow_t(); - } - - P_AddThinker: - { - AddThinker(g); - } - - g.sector = sector; - P_FindMinSurroundingLight: - { - g.minlight = FindMinSurroundingLight(sector, sector.lightlevel); - } - g.maxlight = sector.lightlevel; - g.thinkerFunction = T_Glow; - g.direction = -1; - - sector.special = 0; - } - - // - // Start strobing lights (usually from a trigger) - // - @Override - default void StartLightStrobing(line_t line) { - final AbstractLevelLoader ll = levelLoader(); - - int secnum; - sector_t sec; - - secnum = -1; - while ((secnum = FindSectorFromLineTag(line, secnum)) >= 0) { - sec = ll.sectors[secnum]; - if (sec.specialdata != null) { - continue; - } - - SpawnStrobeFlash(sec, SLOWDARK, 0); - } - } - - // - // P_SpawnFireFlicker - // - @SourceCode.Exact - @P_Lights.C(P_SpawnFireFlicker) - default void SpawnFireFlicker(sector_t sector) { - fireflicker_t flick; - - // Note that we are resetting sector attributes. - // Nothing special about it during gameplay. - sector.special = 0; - - Z_Malloc: - { - flick = new fireflicker_t(); - } - - P_AddThinker: - { - AddThinker(flick); - } - - flick.thinkerFunction = T_FireFlicker; - flick.sector = sector; - flick.maxlight = sector.lightlevel; - flick.minlight = FindMinSurroundingLight(sector, sector.lightlevel) + 16; - flick.count = 4; - } - - // - // TURN LINE'S TAG LIGHTS OFF - // - @Override - default void TurnTagLightsOff(line_t line) { - final AbstractLevelLoader ll = levelLoader(); - - int i; - int min; - sector_t sector; - sector_t tsec; - line_t templine; - - for (int j = 0; j < ll.numsectors; j++) { - sector = ll.sectors[j]; - if (sector.tag == line.tag) { - - min = sector.lightlevel; - for (i = 0; i < sector.linecount; i++) { - templine = sector.lines[i]; - tsec = templine.getNextSector(sector); - if (tsec == null) { - continue; - } - if (tsec.lightlevel < min) { - min = tsec.lightlevel; - } - } - sector.lightlevel = (short) min; - } - } - } - - // - // TURN LINE'S TAG LIGHTS ON - // - @Override - default void LightTurnOn(line_t line, int bright) { - final AbstractLevelLoader ll = levelLoader(); - - sector_t sector; - sector_t temp; - line_t templine; - - for (int i = 0; i < ll.numsectors; i++) { - sector = ll.sectors[i]; - if (sector.tag == line.tag) { - // bright = 0 means to search - // for highest light level - // surrounding sector - if (bright == 0) { - for (int j = 0; j < sector.linecount; j++) { - templine = sector.lines[j]; - temp = templine.getNextSector(sector); - - if (temp == null) { - continue; - } - - if (temp.lightlevel > bright) { - bright = temp.lightlevel; - } - } - } - sector.lightlevel = (short) bright; - } - } - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import doom.SourceCode; +import doom.SourceCode.P_Lights; +import static doom.SourceCode.P_Lights.P_SpawnFireFlicker; +import static doom.SourceCode.P_Lights.P_SpawnGlowingLight; +import static doom.SourceCode.P_Lights.P_SpawnLightFlash; +import static doom.SourceCode.P_Lights.P_SpawnStrobeFlash; +import doom.SourceCode.P_Spec; +import static doom.SourceCode.P_Spec.P_FindMinSurroundingLight; +import java.io.DataInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import p.AbstractLevelLoader; +import static p.ActiveStates.T_FireFlicker; +import static p.ActiveStates.T_Glow; +import static p.ActiveStates.T_LightFlash; +import static p.ActiveStates.T_StrobeFlash; +import static p.DoorDefines.SLOWDARK; +import static p.DoorDefines.STROBEBRIGHT; +import p.strobe_t; +import rr.SectorAction; +import rr.line_t; +import rr.sector_t; +import w.DoomIO; + +public interface ActionsLights extends ActionsMoveEvents, ActionsUseEvents { + + int FindSectorFromLineTag(line_t line, int secnum); + + // + // P_LIGHTS + // + public class fireflicker_t extends SectorAction { + + public int count; + public int maxlight; + public int minlight; + } + + // + // BROKEN LIGHT EFFECT + // + public class lightflash_t extends SectorAction { + + public int count; + public int maxlight; + public int minlight; + public int maxtime; + public int mintime; + + @Override + public void read(DataInputStream f) throws IOException { + super.read(f); // Call thinker reader first + super.sectorid = DoomIO.readLEInt(f); // Sector index + count = DoomIO.readLEInt(f); + maxlight = DoomIO.readLEInt(f); + minlight = DoomIO.readLEInt(f); + maxtime = DoomIO.readLEInt(f); + mintime = DoomIO.readLEInt(f); + } + + @Override + public void pack(ByteBuffer b) throws IOException { + super.pack(b); //12 + b.putInt(super.sectorid); // 16 + b.putInt(count); //20 + b.putInt(maxlight);//24 + b.putInt(minlight);//28 + b.putInt(maxtime);//32 + b.putInt(mintime);//36 + } + } + + public class glow_t extends SectorAction { + + public int minlight; + public int maxlight; + public int direction; + + @Override + public void read(DataInputStream f) throws IOException { + + super.read(f); // Call thinker reader first + super.sectorid = DoomIO.readLEInt(f); // Sector index + minlight = DoomIO.readLEInt(f); + maxlight = DoomIO.readLEInt(f); + direction = DoomIO.readLEInt(f); + } + + @Override + public void pack(ByteBuffer b) throws IOException { + super.pack(b); //12 + b.putInt(super.sectorid); // 16 + b.putInt(minlight);//20 + b.putInt(maxlight);//24 + b.putInt(direction);//38 + } + } + + // + // Find minimum light from an adjacent sector + // + @SourceCode.Exact + @P_Spec.C(P_FindMinSurroundingLight) + default int FindMinSurroundingLight(sector_t sector, int max) { + int min; + line_t line; + sector_t check; + + min = max; + for (int i = 0; i < sector.linecount; i++) { + line = sector.lines[i]; + getNextSector: + { + check = line.getNextSector(sector); + } + + if (check == null) { + continue; + } + + if (check.lightlevel < min) { + min = check.lightlevel; + } + } + return min; + } + + /** + * P_SpawnLightFlash After the map has been loaded, scan each sector for + * specials that spawn thinkers + */ + @SourceCode.Exact + @P_Lights.C(P_SpawnLightFlash) + default void SpawnLightFlash(sector_t sector) { + lightflash_t flash; + + // nothing special about it during gameplay + sector.special = 0; + + Z_Malloc: + { + flash = new lightflash_t(); + } + + P_AddThinker: + { + AddThinker(flash); + } + + flash.thinkerFunction = T_LightFlash; + flash.sector = sector; + flash.maxlight = sector.lightlevel; + + flash.minlight = FindMinSurroundingLight(sector, sector.lightlevel); + flash.maxtime = 64; + flash.mintime = 7; + flash.count = (P_Random() & flash.maxtime) + 1; + } + + // + // P_SpawnStrobeFlash + // After the map has been loaded, scan each sector + // for specials that spawn thinkers + // + @SourceCode.Exact + @P_Lights.C(P_SpawnStrobeFlash) + default void SpawnStrobeFlash(sector_t sector, int fastOrSlow, int inSync) { + strobe_t flash; + + Z_Malloc: + { + flash = new strobe_t(); + } + + P_AddThinker: + { + AddThinker(flash); + } + + flash.sector = sector; + flash.darktime = fastOrSlow; + flash.brighttime = STROBEBRIGHT; + flash.thinkerFunction = T_StrobeFlash; + flash.maxlight = sector.lightlevel; + flash.minlight = FindMinSurroundingLight(sector, sector.lightlevel); + + if (flash.minlight == flash.maxlight) { + flash.minlight = 0; + } + + // nothing special about it during gameplay + sector.special = 0; + + if (inSync == 0) { + flash.count = (P_Random() & 7) + 1; + } else { + flash.count = 1; + } + } + + @SourceCode.Exact + @P_Lights.C(P_SpawnGlowingLight) + default void SpawnGlowingLight(sector_t sector) { + glow_t g; + + Z_Malloc: + { + g = new glow_t(); + } + + P_AddThinker: + { + AddThinker(g); + } + + g.sector = sector; + P_FindMinSurroundingLight: + { + g.minlight = FindMinSurroundingLight(sector, sector.lightlevel); + } + g.maxlight = sector.lightlevel; + g.thinkerFunction = T_Glow; + g.direction = -1; + + sector.special = 0; + } + + // + // Start strobing lights (usually from a trigger) + // + @Override + default void StartLightStrobing(line_t line) { + final AbstractLevelLoader ll = levelLoader(); + + int secnum; + sector_t sec; + + secnum = -1; + while ((secnum = FindSectorFromLineTag(line, secnum)) >= 0) { + sec = ll.sectors[secnum]; + if (sec.specialdata != null) { + continue; + } + + SpawnStrobeFlash(sec, SLOWDARK, 0); + } + } + + // + // P_SpawnFireFlicker + // + @SourceCode.Exact + @P_Lights.C(P_SpawnFireFlicker) + default void SpawnFireFlicker(sector_t sector) { + fireflicker_t flick; + + // Note that we are resetting sector attributes. + // Nothing special about it during gameplay. + sector.special = 0; + + Z_Malloc: + { + flick = new fireflicker_t(); + } + + P_AddThinker: + { + AddThinker(flick); + } + + flick.thinkerFunction = T_FireFlicker; + flick.sector = sector; + flick.maxlight = sector.lightlevel; + flick.minlight = FindMinSurroundingLight(sector, sector.lightlevel) + 16; + flick.count = 4; + } + + // + // TURN LINE'S TAG LIGHTS OFF + // + @Override + default void TurnTagLightsOff(line_t line) { + final AbstractLevelLoader ll = levelLoader(); + + int i; + int min; + sector_t sector; + sector_t tsec; + line_t templine; + + for (int j = 0; j < ll.numsectors; j++) { + sector = ll.sectors[j]; + if (sector.tag == line.tag) { + + min = sector.lightlevel; + for (i = 0; i < sector.linecount; i++) { + templine = sector.lines[i]; + tsec = templine.getNextSector(sector); + if (tsec == null) { + continue; + } + if (tsec.lightlevel < min) { + min = tsec.lightlevel; + } + } + sector.lightlevel = (short) min; + } + } + } + + // + // TURN LINE'S TAG LIGHTS ON + // + @Override + default void LightTurnOn(line_t line, int bright) { + final AbstractLevelLoader ll = levelLoader(); + + sector_t sector; + sector_t temp; + line_t templine; + + for (int i = 0; i < ll.numsectors; i++) { + sector = ll.sectors[i]; + if (sector.tag == line.tag) { + // bright = 0 means to search + // for highest light level + // surrounding sector + if (bright == 0) { + for (int j = 0; j < sector.linecount; j++) { + templine = sector.lines[j]; + temp = templine.getNextSector(sector); + + if (temp == null) { + continue; + } + + if (temp.lightlevel > bright) { + bright = temp.lightlevel; + } + } + } + sector.lightlevel = (short) bright; + } + } + } +} \ No newline at end of file diff --git a/src/p/Actions/ActionsMissiles.java b/src/p/Actions/ActionsMissiles.java index 69d8940..35052fb 100644 --- a/src/p/Actions/ActionsMissiles.java +++ b/src/p/Actions/ActionsMissiles.java @@ -1,177 +1,177 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import static data.Tables.BITS32; -import static data.Tables.finecosine; -import static data.Tables.finesine; -import static data.info.mobjinfo; -import data.mobjtype_t; -import doom.SourceCode.angle_t; -import static m.fixed_t.FRACBITS; -import static m.fixed_t.FRACUNIT; -import static m.fixed_t.FixedMul; -import static p.MapUtils.AproxDistance; -import p.mobj_t; -import static p.mobj_t.MF_MISSILE; -import static p.mobj_t.MF_SHADOW; -import static utils.C2JUtils.eval; - -public interface ActionsMissiles extends ActionsMobj { - - int AimLineAttack(mobj_t source, long an, int i); - - /** - * P_CheckMissileSpawn Moves the missile forward a bit and possibly explodes it right there. - * - * @param th - */ - default void CheckMissileSpawn(mobj_t th) { - th.mobj_tics -= P_Random() & 3; - if (th.mobj_tics < 1) { - th.mobj_tics = 1; - } - - // move a little forward so an angle can - // be computed if it immediately explodes - th.x += (th.momx >> 1); - th.y += (th.momy >> 1); - th.z += (th.momz >> 1); - - if (!TryMove(th, th.x, th.y)) { - ExplodeMissile(th); - } - } - - /** - * P_SpawnMissile - */ - default mobj_t SpawnMissile(mobj_t source, mobj_t dest, mobjtype_t type) { - mobj_t th; - @angle_t - long an; - int dist; - - th = SpawnMobj(source.x, source.y, source.z + 4 * 8 * FRACUNIT, type); - - if (th.info.seesound != null) { - StartSound(th, th.info.seesound); - } - - th.target = source; // where it came from - an = sceneRenderer().PointToAngle2(source.x, source.y, dest.x, dest.y) & BITS32; - - // fuzzy player - if (eval(dest.flags & MF_SHADOW)) { - an += (P_Random() - P_Random()) << 20; - } - - th.angle = an & BITS32; - //an >>= ANGLETOFINESHIFT; - th.momx = FixedMul(th.info.speed, finecosine(an)); - th.momy = FixedMul(th.info.speed, finesine(an)); - - dist = AproxDistance(dest.x - source.x, dest.y - source.y); - dist /= th.info.speed; - - if (dist < 1) { - dist = 1; - } - - th.momz = (dest.z - source.z) / dist; - CheckMissileSpawn(th); - - return th; - } - - /** - * P_SpawnPlayerMissile Tries to aim at a nearby monster - */ - default void SpawnPlayerMissile(mobj_t source, mobjtype_t type) { - final Spawn targ = contextRequire(KEY_SPAWN); - - mobj_t th; - @angle_t - long an; - int x, y, z, slope; // ActionFunction - - // see which target is to be aimed at - an = source.angle; - slope = AimLineAttack(source, an, 16 * 64 * FRACUNIT); - - if (targ.linetarget == null) { - an += 1 << 26; - an &= BITS32; - slope = AimLineAttack(source, an, 16 * 64 * FRACUNIT); - - if (targ.linetarget == null) { - an -= 2 << 26; - an &= BITS32; - slope = AimLineAttack(source, an, 16 * 64 * FRACUNIT); - } - - if (targ.linetarget == null) { - an = source.angle & BITS32; - // angle should be "sane"..right? - // Just this line allows freelook. - slope = ((source.player.lookdir) << FRACBITS) / 173; - } - } - - x = source.x; - y = source.y; - z = source.z + 4 * 8 * FRACUNIT + slope; - - th = this.SpawnMobj(x, y, z, type); - - if (th.info.seesound != null) { - StartSound(th, th.info.seesound); - } - - th.target = source; - th.angle = an; - th.momx = FixedMul(th.info.speed, finecosine(an)); - th.momy = FixedMul(th.info.speed, finesine(an)); - th.momz = FixedMul(th.info.speed, slope); - - CheckMissileSpawn(th); - } - - /** - * P_ExplodeMissile - */ - @Override - default void ExplodeMissile(mobj_t mo) { - mo.momx = mo.momy = mo.momz = 0; - - // MAES 9/5/2011: using mobj code for that. - mo.SetMobjState(mobjinfo[mo.type.ordinal()].deathstate); - - mo.mobj_tics -= P_Random() & 3; - - if (mo.mobj_tics < 1) { - mo.mobj_tics = 1; - } - - mo.flags &= ~MF_MISSILE; - - if (mo.info.deathsound != null) { - StartSound(mo, mo.info.deathsound); - } - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import static data.Tables.BITS32; +import static data.Tables.finecosine; +import static data.Tables.finesine; +import static data.info.mobjinfo; +import data.mobjtype_t; +import doom.SourceCode.angle_t; +import static m.fixed_t.FRACBITS; +import static m.fixed_t.FRACUNIT; +import static m.fixed_t.FixedMul; +import static p.MapUtils.AproxDistance; +import p.mobj_t; +import static p.mobj_t.MF_MISSILE; +import static p.mobj_t.MF_SHADOW; +import static utils.C2JUtils.eval; + +public interface ActionsMissiles extends ActionsMobj { + + int AimLineAttack(mobj_t source, long an, int i); + + /** + * P_CheckMissileSpawn Moves the missile forward a bit and possibly explodes it right there. + * + * @param th + */ + default void CheckMissileSpawn(mobj_t th) { + th.mobj_tics -= P_Random() & 3; + if (th.mobj_tics < 1) { + th.mobj_tics = 1; + } + + // move a little forward so an angle can + // be computed if it immediately explodes + th.x += (th.momx >> 1); + th.y += (th.momy >> 1); + th.z += (th.momz >> 1); + + if (!TryMove(th, th.x, th.y)) { + ExplodeMissile(th); + } + } + + /** + * P_SpawnMissile + */ + default mobj_t SpawnMissile(mobj_t source, mobj_t dest, mobjtype_t type) { + mobj_t th; + @angle_t + long an; + int dist; + + th = SpawnMobj(source.x, source.y, source.z + 4 * 8 * FRACUNIT, type); + + if (th.info.seesound != null) { + StartSound(th, th.info.seesound); + } + + th.target = source; // where it came from + an = sceneRenderer().PointToAngle2(source.x, source.y, dest.x, dest.y) & BITS32; + + // fuzzy player + if (eval(dest.flags & MF_SHADOW)) { + an += (P_Random() - P_Random()) << 20; + } + + th.angle = an & BITS32; + //an >>= ANGLETOFINESHIFT; + th.momx = FixedMul(th.info.speed, finecosine(an)); + th.momy = FixedMul(th.info.speed, finesine(an)); + + dist = AproxDistance(dest.x - source.x, dest.y - source.y); + dist /= th.info.speed; + + if (dist < 1) { + dist = 1; + } + + th.momz = (dest.z - source.z) / dist; + CheckMissileSpawn(th); + + return th; + } + + /** + * P_SpawnPlayerMissile Tries to aim at a nearby monster + */ + default void SpawnPlayerMissile(mobj_t source, mobjtype_t type) { + final Spawn targ = contextRequire(KEY_SPAWN); + + mobj_t th; + @angle_t + long an; + int x, y, z, slope; // ActionFunction + + // see which target is to be aimed at + an = source.angle; + slope = AimLineAttack(source, an, 16 * 64 * FRACUNIT); + + if (targ.linetarget == null) { + an += 1 << 26; + an &= BITS32; + slope = AimLineAttack(source, an, 16 * 64 * FRACUNIT); + + if (targ.linetarget == null) { + an -= 2 << 26; + an &= BITS32; + slope = AimLineAttack(source, an, 16 * 64 * FRACUNIT); + } + + if (targ.linetarget == null) { + an = source.angle & BITS32; + // angle should be "sane"..right? + // Just this line allows freelook. + slope = ((source.player.lookdir) << FRACBITS) / 173; + } + } + + x = source.x; + y = source.y; + z = source.z + 4 * 8 * FRACUNIT + slope; + + th = this.SpawnMobj(x, y, z, type); + + if (th.info.seesound != null) { + StartSound(th, th.info.seesound); + } + + th.target = source; + th.angle = an; + th.momx = FixedMul(th.info.speed, finecosine(an)); + th.momy = FixedMul(th.info.speed, finesine(an)); + th.momz = FixedMul(th.info.speed, slope); + + CheckMissileSpawn(th); + } + + /** + * P_ExplodeMissile + */ + @Override + default void ExplodeMissile(mobj_t mo) { + mo.momx = mo.momy = mo.momz = 0; + + // MAES 9/5/2011: using mobj code for that. + mo.SetMobjState(mobjinfo[mo.type.ordinal()].deathstate); + + mo.mobj_tics -= P_Random() & 3; + + if (mo.mobj_tics < 1) { + mo.mobj_tics = 1; + } + + mo.flags &= ~MF_MISSILE; + + if (mo.info.deathsound != null) { + StartSound(mo, mo.info.deathsound); + } + } +} \ No newline at end of file diff --git a/src/p/Actions/ActionsMobj.java b/src/p/Actions/ActionsMobj.java index 955ee2e..c7cc6e6 100644 --- a/src/p/Actions/ActionsMobj.java +++ b/src/p/Actions/ActionsMobj.java @@ -1,386 +1,386 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import static data.Defines.BASETHRESHOLD; -import static data.Defines.ITEMQUESIZE; -import static data.Defines.ONFLOORZ; -import static data.Defines.PST_DEAD; -import static data.Defines.pw_invulnerability; -import static data.Tables.ANG180; -import static data.Tables.BITS32; -import static data.Tables.finecosine; -import static data.Tables.finesine; -import static data.info.states; -import data.mobjtype_t; -import defines.skill_t; -import defines.statenum_t; -import doom.SourceCode; -import doom.SourceCode.P_MapUtl; -import static doom.SourceCode.P_MapUtl.P_UnsetThingPosition; -import static doom.SourceCode.P_Mobj.P_RemoveMobj; -import doom.player_t; -import doom.weapontype_t; -import static m.fixed_t.FRACUNIT; -import static m.fixed_t.FixedMul; -import static m.fixed_t.MAPFRACUNIT; -import p.AbstractLevelLoader; -import static p.MobjFlags.MF_DROPPED; -import static p.MobjFlags.MF_NOBLOCKMAP; -import static p.MobjFlags.MF_NOSECTOR; -import static p.MobjFlags.MF_SPECIAL; -import p.mobj_t; -import static p.mobj_t.MF_CORPSE; -import static p.mobj_t.MF_COUNTKILL; -import static p.mobj_t.MF_DROPOFF; -import static p.mobj_t.MF_FLOAT; -import static p.mobj_t.MF_JUSTHIT; -import static p.mobj_t.MF_NOCLIP; -import static p.mobj_t.MF_NOGRAVITY; -import static p.mobj_t.MF_SHOOTABLE; -import static p.mobj_t.MF_SKULLFLY; -import static p.mobj_t.MF_SOLID; -import static utils.C2JUtils.eval; - -public interface ActionsMobj extends ActionsThings, ActionsMovement, ActionsTeleportation { - - // - // P_DamageMobj - // Damages both enemies and players - // "inflictor" is the thing that caused the damage - // creature or missile, can be NULL (slime, etc) - // "source" is the thing to target after taking damage - // creature or NULL - // Source and inflictor are the same for melee attacks. - // Source can be NULL for slime, barrel explosions - // and other environmental stuff. - // - @Override - default void DamageMobj(mobj_t target, mobj_t inflictor, mobj_t source, int damage) { - long ang; // unsigned - int saved; - player_t player; - @SourceCode.fixed_t - int thrust; - int temp; - - if (!eval(target.flags & MF_SHOOTABLE)) { - return; // shouldn't happen... - } - if (target.health <= 0) { - return; - } - - if (eval(target.flags & MF_SKULLFLY)) { - target.momx = target.momy = target.momz = 0; - } - - player = target.player; - if ((player != null) && getGameSkill() == skill_t.sk_baby) { - damage >>= 1; // take half damage in trainer mode - } - - // Some close combat weapons should not - // inflict thrust and push the victim out of reach, - // thus kick away unless using the chainsaw. - if ((inflictor != null) - && !eval(target.flags & MF_NOCLIP) - && (source == null - || source.player == null - || source.player.readyweapon != weapontype_t.wp_chainsaw)) { - ang = sceneRenderer().PointToAngle2(inflictor.x, - inflictor.y, - target.x, - target.y) & BITS32; - - thrust = damage * (MAPFRACUNIT >> 3) * 100 / target.info.mass; - - // make fall forwards sometimes - if ((damage < 40) - && (damage > target.health) - && (target.z - inflictor.z > 64 * FRACUNIT) - && eval(P_Random() & 1)) { - ang += ANG180; - thrust *= 4; - } - - //ang >>= ANGLETOFINESHIFT; - target.momx += FixedMul(thrust, finecosine(ang)); - target.momy += FixedMul(thrust, finesine(ang)); - } - - // player specific - if (player != null) { - // end of game hell hack - if (target.subsector.sector.special == 11 - && damage >= target.health) { - damage = target.health - 1; - } - - // Below certain threshold, - // ignore damage in GOD mode, or with INVUL power. - if (damage < 1000 - && (eval(player.cheats & player_t.CF_GODMODE)) - || player.powers[pw_invulnerability] != 0) { - return; - } - - if (player.armortype != 0) { - if (player.armortype == 1) { - saved = damage / 3; - } else { - saved = damage / 2; - } - - if (player.armorpoints[0] <= saved) { - // armor is used up - saved = player.armorpoints[0]; - player.armortype = 0; - } - player.armorpoints[0] -= saved; - damage -= saved; - } - player.health[0] -= damage; // mirror mobj health here for Dave - if (player.health[0] < 0) { - player.health[0] = 0; - } - - player.attacker = source; - player.damagecount += damage; // add damage after armor / invuln - - if (player.damagecount > 100) { - player.damagecount = 100; // teleport stomp does 10k points... - } - temp = damage < 100 ? damage : 100; - - if (player == getPlayer(ConsolePlayerNumber())) { - doomSystem().Tactile(40, 10, 40 + temp * 2); - } - } - - // do the damage - target.health -= damage; - if (target.health <= 0) { - this.KillMobj(source, target); - return; - } - - if ((P_Random() < target.info.painchance) - && !eval(target.flags & MF_SKULLFLY)) { - target.flags |= MF_JUSTHIT; // fight back! - - target.SetMobjState(target.info.painstate); - } - - target.reactiontime = 0; // we're awake now... - - if (((target.threshold == 0) || (target.type == mobjtype_t.MT_VILE)) - && (source != null) && (source != target) - && (source.type != mobjtype_t.MT_VILE)) { - // if not intent on another player, - // chase after this one - target.target = source; - target.threshold = BASETHRESHOLD; - if (target.mobj_state == states[target.info.spawnstate.ordinal()] - && target.info.seestate != statenum_t.S_NULL) { - target.SetMobjState(target.info.seestate); - } - } - - } - - // - // KillMobj - // - default void KillMobj(mobj_t source, mobj_t target) { - mobjtype_t item; - mobj_t mo; - - // Maes: this seems necessary in order for barrel damage - // to propagate inflictors. - target.target = source; - - target.flags &= ~(MF_SHOOTABLE | MF_FLOAT | MF_SKULLFLY); - - if (target.type != mobjtype_t.MT_SKULL) { - target.flags &= ~MF_NOGRAVITY; - } - - target.flags |= MF_CORPSE | MF_DROPOFF; - target.height >>= 2; - - if (source != null && source.player != null) { - // count for intermission - if ((target.flags & MF_COUNTKILL) != 0) { - source.player.killcount++; - } - - if (target.player != null) //; <-- _D_: that semicolon caused a bug! - { - source.player.frags[target.player.identify()]++; - } - // It's probably intended to increment the frags of source player vs target player. Lookup? - } else if (!IsNetGame() && ((target.flags & MF_COUNTKILL) != 0)) { - // count all monster deaths, - // even those caused by other monsters - getPlayer(0).killcount++; - } - - if (target.player != null) { - // count environment kills against you - if (source == null) // TODO: some way to indentify which one of the - // four possiblelayers is the current player - { - target.player.frags[target.player.identify()]++; - } - - target.flags &= ~MF_SOLID; - target.player.playerstate = PST_DEAD; - target.player.DropWeapon(); // in PSPR - - if (target.player == getPlayer(ConsolePlayerNumber()) && IsAutoMapActive()) { - // don't die in auto map, - // switch view prior to dying - autoMap().Stop(); - } - - } - - if (target.health < -target.info.spawnhealth && target.info.xdeathstate != statenum_t.S_NULL) { - target.SetMobjState(target.info.xdeathstate); - } else { - target.SetMobjState(target.info.deathstate); - } - target.mobj_tics -= P_Random() & 3; - - if (target.mobj_tics < 1) { - target.mobj_tics = 1; - } - - // I_StartSound (&actor.r, actor.info.deathsound); - // Drop stuff. - // This determines the kind of object spawned - // during the death frame of a thing. - switch (target.type) { - case MT_WOLFSS: - case MT_POSSESSED: - item = mobjtype_t.MT_CLIP; - break; - - case MT_SHOTGUY: - item = mobjtype_t.MT_SHOTGUN; - break; - - case MT_CHAINGUY: - item = mobjtype_t.MT_CHAINGUN; - break; - - default: - return; - } - - mo = SpawnMobj(target.x, target.y, ONFLOORZ, item); - mo.flags |= MF_DROPPED; // special versions of items - } - - @Override - @SourceCode.Exact - @SourceCode.P_Mobj.C(P_RemoveMobj) - default void RemoveMobj(mobj_t mobj) { - if (eval(mobj.flags & MF_SPECIAL) - && !eval(mobj.flags & MF_DROPPED) - && (mobj.type != mobjtype_t.MT_INV) - && (mobj.type != mobjtype_t.MT_INS)) { - final RespawnQueue resp = contextRequire(KEY_RESP_QUEUE); - resp.itemrespawnque[resp.iquehead] = mobj.spawnpoint; - resp.itemrespawntime[resp.iquehead] = LevelTime(); - resp.iquehead = (resp.iquehead + 1) & (ITEMQUESIZE - 1); - - // lose one off the end? - if (resp.iquehead == resp.iquetail) { - resp.iquetail = (resp.iquetail + 1) & (ITEMQUESIZE - 1); - } - } - - // unlink from sector and block lists - P_UnsetThingPosition: - { - UnsetThingPosition(mobj); - } - - // stop any playing sound - S_StopSound: - { - StopSound(mobj); - } - - // free block - P_RemoveThinker: - { - RemoveThinker(mobj); - } - } - - /** - * P_UnsetThingPosition Unlinks a thing from block map and sectors. On each - * position change, BLOCKMAP and other lookups maintaining lists ot things - * inside these structures need to be updated. - */ - @Override - @SourceCode.Exact - @P_MapUtl.C(P_UnsetThingPosition) - default void UnsetThingPosition(mobj_t thing) { - final AbstractLevelLoader ll = levelLoader(); - final int blockx; - final int blocky; - - if (!eval(thing.flags & MF_NOSECTOR)) { - // inert things don't need to be in blockmap? - // unlink from subsector - if (thing.snext != null) { - ((mobj_t) thing.snext).sprev = thing.sprev; - } - - if (thing.sprev != null) { - ((mobj_t) thing.sprev).snext = thing.snext; - } else { - thing.subsector.sector.thinglist = (mobj_t) thing.snext; - } - } - - if (!eval(thing.flags & MF_NOBLOCKMAP)) { - // inert things don't need to be in blockmap - // unlink from block map - if (thing.bnext != null) { - ((mobj_t) thing.bnext).bprev = thing.bprev; - } - - if (thing.bprev != null) { - ((mobj_t) thing.bprev).bnext = thing.bnext; - } else { - blockx = ll.getSafeBlockX(thing.x - ll.bmaporgx); - blocky = ll.getSafeBlockY(thing.y - ll.bmaporgy); - - if (blockx >= 0 && blockx < ll.bmapwidth - && blocky >= 0 && blocky < ll.bmapheight) { - ll.blocklinks[blocky * ll.bmapwidth + blockx] = (mobj_t) thing.bnext; - } - } - } - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import static data.Defines.BASETHRESHOLD; +import static data.Defines.ITEMQUESIZE; +import static data.Defines.ONFLOORZ; +import static data.Defines.PST_DEAD; +import static data.Defines.pw_invulnerability; +import static data.Tables.ANG180; +import static data.Tables.BITS32; +import static data.Tables.finecosine; +import static data.Tables.finesine; +import static data.info.states; +import data.mobjtype_t; +import defines.skill_t; +import defines.statenum_t; +import doom.SourceCode; +import doom.SourceCode.P_MapUtl; +import static doom.SourceCode.P_MapUtl.P_UnsetThingPosition; +import static doom.SourceCode.P_Mobj.P_RemoveMobj; +import doom.player_t; +import doom.weapontype_t; +import static m.fixed_t.FRACUNIT; +import static m.fixed_t.FixedMul; +import static m.fixed_t.MAPFRACUNIT; +import p.AbstractLevelLoader; +import static p.MobjFlags.MF_DROPPED; +import static p.MobjFlags.MF_NOBLOCKMAP; +import static p.MobjFlags.MF_NOSECTOR; +import static p.MobjFlags.MF_SPECIAL; +import p.mobj_t; +import static p.mobj_t.MF_CORPSE; +import static p.mobj_t.MF_COUNTKILL; +import static p.mobj_t.MF_DROPOFF; +import static p.mobj_t.MF_FLOAT; +import static p.mobj_t.MF_JUSTHIT; +import static p.mobj_t.MF_NOCLIP; +import static p.mobj_t.MF_NOGRAVITY; +import static p.mobj_t.MF_SHOOTABLE; +import static p.mobj_t.MF_SKULLFLY; +import static p.mobj_t.MF_SOLID; +import static utils.C2JUtils.eval; + +public interface ActionsMobj extends ActionsThings, ActionsMovement, ActionsTeleportation { + + // + // P_DamageMobj + // Damages both enemies and players + // "inflictor" is the thing that caused the damage + // creature or missile, can be NULL (slime, etc) + // "source" is the thing to target after taking damage + // creature or NULL + // Source and inflictor are the same for melee attacks. + // Source can be NULL for slime, barrel explosions + // and other environmental stuff. + // + @Override + default void DamageMobj(mobj_t target, mobj_t inflictor, mobj_t source, int damage) { + long ang; // unsigned + int saved; + player_t player; + @SourceCode.fixed_t + int thrust; + int temp; + + if (!eval(target.flags & MF_SHOOTABLE)) { + return; // shouldn't happen... + } + if (target.health <= 0) { + return; + } + + if (eval(target.flags & MF_SKULLFLY)) { + target.momx = target.momy = target.momz = 0; + } + + player = target.player; + if ((player != null) && getGameSkill() == skill_t.sk_baby) { + damage >>= 1; // take half damage in trainer mode + } + + // Some close combat weapons should not + // inflict thrust and push the victim out of reach, + // thus kick away unless using the chainsaw. + if ((inflictor != null) + && !eval(target.flags & MF_NOCLIP) + && (source == null + || source.player == null + || source.player.readyweapon != weapontype_t.wp_chainsaw)) { + ang = sceneRenderer().PointToAngle2(inflictor.x, + inflictor.y, + target.x, + target.y) & BITS32; + + thrust = damage * (MAPFRACUNIT >> 3) * 100 / target.info.mass; + + // make fall forwards sometimes + if ((damage < 40) + && (damage > target.health) + && (target.z - inflictor.z > 64 * FRACUNIT) + && eval(P_Random() & 1)) { + ang += ANG180; + thrust *= 4; + } + + //ang >>= ANGLETOFINESHIFT; + target.momx += FixedMul(thrust, finecosine(ang)); + target.momy += FixedMul(thrust, finesine(ang)); + } + + // player specific + if (player != null) { + // end of game hell hack + if (target.subsector.sector.special == 11 + && damage >= target.health) { + damage = target.health - 1; + } + + // Below certain threshold, + // ignore damage in GOD mode, or with INVUL power. + if (damage < 1000 + && (eval(player.cheats & player_t.CF_GODMODE)) + || player.powers[pw_invulnerability] != 0) { + return; + } + + if (player.armortype != 0) { + if (player.armortype == 1) { + saved = damage / 3; + } else { + saved = damage / 2; + } + + if (player.armorpoints[0] <= saved) { + // armor is used up + saved = player.armorpoints[0]; + player.armortype = 0; + } + player.armorpoints[0] -= saved; + damage -= saved; + } + player.health[0] -= damage; // mirror mobj health here for Dave + if (player.health[0] < 0) { + player.health[0] = 0; + } + + player.attacker = source; + player.damagecount += damage; // add damage after armor / invuln + + if (player.damagecount > 100) { + player.damagecount = 100; // teleport stomp does 10k points... + } + temp = damage < 100 ? damage : 100; + + if (player == getPlayer(ConsolePlayerNumber())) { + doomSystem().Tactile(40, 10, 40 + temp * 2); + } + } + + // do the damage + target.health -= damage; + if (target.health <= 0) { + this.KillMobj(source, target); + return; + } + + if ((P_Random() < target.info.painchance) + && !eval(target.flags & MF_SKULLFLY)) { + target.flags |= MF_JUSTHIT; // fight back! + + target.SetMobjState(target.info.painstate); + } + + target.reactiontime = 0; // we're awake now... + + if (((target.threshold == 0) || (target.type == mobjtype_t.MT_VILE)) + && (source != null) && (source != target) + && (source.type != mobjtype_t.MT_VILE)) { + // if not intent on another player, + // chase after this one + target.target = source; + target.threshold = BASETHRESHOLD; + if (target.mobj_state == states[target.info.spawnstate.ordinal()] + && target.info.seestate != statenum_t.S_NULL) { + target.SetMobjState(target.info.seestate); + } + } + + } + + // + // KillMobj + // + default void KillMobj(mobj_t source, mobj_t target) { + mobjtype_t item; + mobj_t mo; + + // Maes: this seems necessary in order for barrel damage + // to propagate inflictors. + target.target = source; + + target.flags &= ~(MF_SHOOTABLE | MF_FLOAT | MF_SKULLFLY); + + if (target.type != mobjtype_t.MT_SKULL) { + target.flags &= ~MF_NOGRAVITY; + } + + target.flags |= MF_CORPSE | MF_DROPOFF; + target.height >>= 2; + + if (source != null && source.player != null) { + // count for intermission + if ((target.flags & MF_COUNTKILL) != 0) { + source.player.killcount++; + } + + if (target.player != null) //; <-- _D_: that semicolon caused a bug! + { + source.player.frags[target.player.identify()]++; + } + // It's probably intended to increment the frags of source player vs target player. Lookup? + } else if (!IsNetGame() && ((target.flags & MF_COUNTKILL) != 0)) { + // count all monster deaths, + // even those caused by other monsters + getPlayer(0).killcount++; + } + + if (target.player != null) { + // count environment kills against you + if (source == null) // TODO: some way to indentify which one of the + // four possiblelayers is the current player + { + target.player.frags[target.player.identify()]++; + } + + target.flags &= ~MF_SOLID; + target.player.playerstate = PST_DEAD; + target.player.DropWeapon(); // in PSPR + + if (target.player == getPlayer(ConsolePlayerNumber()) && IsAutoMapActive()) { + // don't die in auto map, + // switch view prior to dying + autoMap().Stop(); + } + + } + + if (target.health < -target.info.spawnhealth && target.info.xdeathstate != statenum_t.S_NULL) { + target.SetMobjState(target.info.xdeathstate); + } else { + target.SetMobjState(target.info.deathstate); + } + target.mobj_tics -= P_Random() & 3; + + if (target.mobj_tics < 1) { + target.mobj_tics = 1; + } + + // I_StartSound (&actor.r, actor.info.deathsound); + // Drop stuff. + // This determines the kind of object spawned + // during the death frame of a thing. + switch (target.type) { + case MT_WOLFSS: + case MT_POSSESSED: + item = mobjtype_t.MT_CLIP; + break; + + case MT_SHOTGUY: + item = mobjtype_t.MT_SHOTGUN; + break; + + case MT_CHAINGUY: + item = mobjtype_t.MT_CHAINGUN; + break; + + default: + return; + } + + mo = SpawnMobj(target.x, target.y, ONFLOORZ, item); + mo.flags |= MF_DROPPED; // special versions of items + } + + @Override + @SourceCode.Exact + @SourceCode.P_Mobj.C(P_RemoveMobj) + default void RemoveMobj(mobj_t mobj) { + if (eval(mobj.flags & MF_SPECIAL) + && !eval(mobj.flags & MF_DROPPED) + && (mobj.type != mobjtype_t.MT_INV) + && (mobj.type != mobjtype_t.MT_INS)) { + final RespawnQueue resp = contextRequire(KEY_RESP_QUEUE); + resp.itemrespawnque[resp.iquehead] = mobj.spawnpoint; + resp.itemrespawntime[resp.iquehead] = LevelTime(); + resp.iquehead = (resp.iquehead + 1) & (ITEMQUESIZE - 1); + + // lose one off the end? + if (resp.iquehead == resp.iquetail) { + resp.iquetail = (resp.iquetail + 1) & (ITEMQUESIZE - 1); + } + } + + // unlink from sector and block lists + P_UnsetThingPosition: + { + UnsetThingPosition(mobj); + } + + // stop any playing sound + S_StopSound: + { + StopSound(mobj); + } + + // free block + P_RemoveThinker: + { + RemoveThinker(mobj); + } + } + + /** + * P_UnsetThingPosition Unlinks a thing from block map and sectors. On each + * position change, BLOCKMAP and other lookups maintaining lists ot things + * inside these structures need to be updated. + */ + @Override + @SourceCode.Exact + @P_MapUtl.C(P_UnsetThingPosition) + default void UnsetThingPosition(mobj_t thing) { + final AbstractLevelLoader ll = levelLoader(); + final int blockx; + final int blocky; + + if (!eval(thing.flags & MF_NOSECTOR)) { + // inert things don't need to be in blockmap? + // unlink from subsector + if (thing.snext != null) { + ((mobj_t) thing.snext).sprev = thing.sprev; + } + + if (thing.sprev != null) { + ((mobj_t) thing.sprev).snext = thing.snext; + } else { + thing.subsector.sector.thinglist = (mobj_t) thing.snext; + } + } + + if (!eval(thing.flags & MF_NOBLOCKMAP)) { + // inert things don't need to be in blockmap + // unlink from block map + if (thing.bnext != null) { + ((mobj_t) thing.bnext).bprev = thing.bprev; + } + + if (thing.bprev != null) { + ((mobj_t) thing.bprev).bnext = thing.bnext; + } else { + blockx = ll.getSafeBlockX(thing.x - ll.bmaporgx); + blocky = ll.getSafeBlockY(thing.y - ll.bmaporgy); + + if (blockx >= 0 && blockx < ll.bmapwidth + && blocky >= 0 && blocky < ll.bmapheight) { + ll.blocklinks[blocky * ll.bmapwidth + blockx] = (mobj_t) thing.bnext; + } + } + } + } +} \ No newline at end of file diff --git a/src/p/Actions/ActionsMoveEvents.java b/src/p/Actions/ActionsMoveEvents.java index 6236fb4..35b3442 100644 --- a/src/p/Actions/ActionsMoveEvents.java +++ b/src/p/Actions/ActionsMoveEvents.java @@ -1,513 +1,513 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import p.ceiling_e; -import p.floor_e; -import p.mobj_t; -import p.plattype_e; -import p.stair_e; -import p.vldoor_e; -import rr.line_t; - -public interface ActionsMoveEvents extends ActionTrait { - - boolean DoDoor(line_t line, vldoor_e type); - - boolean DoFloor(line_t line, floor_e floor_e); - - boolean DoPlat(line_t line, plattype_e plattype_e, int i); - - boolean BuildStairs(line_t line, stair_e stair_e); - - boolean DoCeiling(line_t line, ceiling_e ceiling_e); - - void StopPlat(line_t line); - - void LightTurnOn(line_t line, int i); - - void StartLightStrobing(line_t line); - - void TurnTagLightsOff(line_t line); - - int Teleport(line_t line, int side, mobj_t thing); - - int CeilingCrushStop(line_t line); - - // - //EVENTS - //Events are operations triggered by using, crossing, - //or shooting special lines, or by timed thinkers. - // - /** - * P_CrossSpecialLine - TRIGGER Called every time a thing origin is about to cross a line with a non 0 special. - */ - default void CrossSpecialLine(line_t line, int side, mobj_t thing) { - //line_t line; - boolean ok; - - //line = LL.lines[linenum]; - // Triggers that other things can activate - if (thing.player == null) { - // Things that should NOT trigger specials... - switch (thing.type) { - case MT_ROCKET: - case MT_PLASMA: - case MT_BFG: - case MT_TROOPSHOT: - case MT_HEADSHOT: - case MT_BRUISERSHOT: - return; - // break; - - default: - break; - } - - ok = false; - switch (line.special) { - case 39: // TELEPORT TRIGGER - case 97: // TELEPORT RETRIGGER - case 125: // TELEPORT MONSTERONLY TRIGGER - case 126: // TELEPORT MONSTERONLY RETRIGGER - case 4: // RAISE DOOR - case 10: // PLAT DOWN-WAIT-UP-STAY TRIGGER - case 88: // PLAT DOWN-WAIT-UP-STAY RETRIGGER - ok = true; - break; - } - if (!ok) { - return; - } - } - - // TODO: enum! - // Note: could use some const's here. - switch (line.special) { - // TRIGGERS. - // All from here to RETRIGGERS. - case 2: - // Open Door - DoDoor(line, vldoor_e.open); - line.special = 0; - break; - - case 3: - // Close Door - DoDoor(line, vldoor_e.close); - line.special = 0; - break; - - case 4: - // Raise Door - DoDoor(line, vldoor_e.normal); - line.special = 0; - break; - - case 5: - // Raise Floor - DoFloor(line, floor_e.raiseFloor); - line.special = 0; - break; - - case 6: - // Fast Ceiling Crush & Raise - DoCeiling(line, ceiling_e.fastCrushAndRaise); - line.special = 0; - break; - - case 8: - // Build Stairs - BuildStairs(line, stair_e.build8); - line.special = 0; - break; - - case 10: - // PlatDownWaitUp - DoPlat(line, plattype_e.downWaitUpStay, 0); - line.special = 0; - break; - - case 12: - // Light Turn On - brightest near - LightTurnOn(line, 0); - line.special = 0; - break; - - case 13: - // Light Turn On 255 - LightTurnOn(line, 255); - line.special = 0; - break; - - case 16: - // Close Door 30 - DoDoor(line, vldoor_e.close30ThenOpen); - line.special = 0; - break; - - case 17: - // Start Light Strobing - StartLightStrobing(line); - line.special = 0; - break; - - case 19: - // Lower Floor - DoFloor(line, floor_e.lowerFloor); - line.special = 0; - break; - - case 22: - // Raise floor to nearest height and change texture - DoPlat(line, plattype_e.raiseToNearestAndChange, 0); - line.special = 0; - break; - - case 25: - // Ceiling Crush and Raise - DoCeiling(line, ceiling_e.crushAndRaise); - line.special = 0; - break; - - case 30: - // Raise floor to shortest texture height - // on either side of lines. - DoFloor(line, floor_e.raiseToTexture); - line.special = 0; - break; - - case 35: - // Lights Very Dark - LightTurnOn(line, 35); - line.special = 0; - break; - - case 36: - // Lower Floor (TURBO) - DoFloor(line, floor_e.turboLower); - line.special = 0; - break; - - case 37: - // LowerAndChange - DoFloor(line, floor_e.lowerAndChange); - line.special = 0; - break; - - case 38: - // Lower Floor To Lowest - DoFloor(line, floor_e.lowerFloorToLowest); - line.special = 0; - break; - - case 39: - // TELEPORT! - Teleport(line, side, thing); - line.special = 0; - break; - - case 40: - // RaiseCeilingLowerFloor - DoCeiling(line, ceiling_e.raiseToHighest); - DoFloor(line, floor_e.lowerFloorToLowest); - line.special = 0; - break; - - case 44: - // Ceiling Crush - DoCeiling(line, ceiling_e.lowerAndCrush); - line.special = 0; - break; - - case 52: - // EXIT! - DOOM().ExitLevel(); - break; - - case 53: - // Perpetual Platform Raise - DoPlat(line, plattype_e.perpetualRaise, 0); - line.special = 0; - break; - - case 54: - // Platform Stop - StopPlat(line); - line.special = 0; - break; - - case 56: - // Raise Floor Crush - DoFloor(line, floor_e.raiseFloorCrush); - line.special = 0; - break; - - case 57: - // Ceiling Crush Stop - CeilingCrushStop(line); - line.special = 0; - break; - - case 58: - // Raise Floor 24 - DoFloor(line, floor_e.raiseFloor24); - line.special = 0; - break; - - case 59: - // Raise Floor 24 And Change - DoFloor(line, floor_e.raiseFloor24AndChange); - line.special = 0; - break; - - case 104: - // Turn lights off in sector(tag) - TurnTagLightsOff(line); - line.special = 0; - break; - - case 108: - // Blazing Door Raise (faster than TURBO!) - DoDoor(line, vldoor_e.blazeRaise); - line.special = 0; - break; - - case 109: - // Blazing Door Open (faster than TURBO!) - DoDoor(line, vldoor_e.blazeOpen); - line.special = 0; - break; - - case 100: - // Build Stairs Turbo 16 - BuildStairs(line, stair_e.turbo16); - line.special = 0; - break; - - case 110: - // Blazing Door Close (faster than TURBO!) - DoDoor(line, vldoor_e.blazeClose); - line.special = 0; - break; - - case 119: - // Raise floor to nearest surr. floor - DoFloor(line, floor_e.raiseFloorToNearest); - line.special = 0; - break; - - case 121: - // Blazing PlatDownWaitUpStay - DoPlat(line, plattype_e.blazeDWUS, 0); - line.special = 0; - break; - - case 124: - // Secret EXIT - DOOM().SecretExitLevel(); - break; - - case 125: - // TELEPORT MonsterONLY - if (thing.player == null) { - Teleport(line, side, thing); - line.special = 0; - } - break; - - case 130: - // Raise Floor Turbo - DoFloor(line, floor_e.raiseFloorTurbo); - line.special = 0; - break; - - case 141: - // Silent Ceiling Crush & Raise - DoCeiling(line, ceiling_e.silentCrushAndRaise); - line.special = 0; - break; - - // RETRIGGERS. All from here till end. - case 72: - // Ceiling Crush - DoCeiling(line, ceiling_e.lowerAndCrush); - break; - - case 73: - // Ceiling Crush and Raise - DoCeiling(line, ceiling_e.crushAndRaise); - break; - - case 74: - // Ceiling Crush Stop - CeilingCrushStop(line); - break; - - case 75: - // Close Door - DoDoor(line, vldoor_e.close); - break; - - case 76: - // Close Door 30 - DoDoor(line, vldoor_e.close30ThenOpen); - break; - - case 77: - // Fast Ceiling Crush & Raise - DoCeiling(line, ceiling_e.fastCrushAndRaise); - break; - - case 79: - // Lights Very Dark - LightTurnOn(line, 35); - break; - - case 80: - // Light Turn On - brightest near - LightTurnOn(line, 0); - break; - - case 81: - // Light Turn On 255 - LightTurnOn(line, 255); - break; - - case 82: - // Lower Floor To Lowest - DoFloor(line, floor_e.lowerFloorToLowest); - break; - - case 83: - // Lower Floor - DoFloor(line, floor_e.lowerFloor); - break; - - case 84: - // LowerAndChange - DoFloor(line, floor_e.lowerAndChange); - break; - - case 86: - // Open Door - DoDoor(line, vldoor_e.open); - break; - - case 87: - // Perpetual Platform Raise - DoPlat(line, plattype_e.perpetualRaise, 0); - break; - - case 88: - // PlatDownWaitUp - DoPlat(line, plattype_e.downWaitUpStay, 0); - break; - - case 89: - // Platform Stop - StopPlat(line); - break; - - case 90: - // Raise Door - DoDoor(line, vldoor_e.normal); - break; - - case 91: - // Raise Floor - DoFloor(line, floor_e.raiseFloor); - break; - - case 92: - // Raise Floor 24 - DoFloor(line, floor_e.raiseFloor24); - break; - - case 93: - // Raise Floor 24 And Change - DoFloor(line, floor_e.raiseFloor24AndChange); - break; - - case 94: - // Raise Floor Crush - DoFloor(line, floor_e.raiseFloorCrush); - break; - - case 95: - // Raise floor to nearest height - // and change texture. - DoPlat(line, plattype_e.raiseToNearestAndChange, 0); - break; - - case 96: - // Raise floor to shortest texture height - // on either side of lines. - DoFloor(line, floor_e.raiseToTexture); - break; - - case 97: - // TELEPORT! - Teleport(line, side, thing); - break; - - case 98: - // Lower Floor (TURBO) - DoFloor(line, floor_e.turboLower); - break; - - case 105: - // Blazing Door Raise (faster than TURBO!) - DoDoor(line, vldoor_e.blazeRaise); - break; - - case 106: - // Blazing Door Open (faster than TURBO!) - DoDoor(line, vldoor_e.blazeOpen); - break; - - case 107: - // Blazing Door Close (faster than TURBO!) - DoDoor(line, vldoor_e.blazeClose); - break; - - case 120: - // Blazing PlatDownWaitUpStay. - DoPlat(line, plattype_e.blazeDWUS, 0); - break; - - case 126: - // TELEPORT MonsterONLY. - if (thing.player == null) { - Teleport(line, side, thing); - } - break; - - case 128: - // Raise To Nearest Floor - DoFloor(line, floor_e.raiseFloorToNearest); - break; - - case 129: - // Raise Floor Turbo - DoFloor(line, floor_e.raiseFloorTurbo); - break; - } - } - -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import p.ceiling_e; +import p.floor_e; +import p.mobj_t; +import p.plattype_e; +import p.stair_e; +import p.vldoor_e; +import rr.line_t; + +public interface ActionsMoveEvents extends ActionTrait { + + boolean DoDoor(line_t line, vldoor_e type); + + boolean DoFloor(line_t line, floor_e floor_e); + + boolean DoPlat(line_t line, plattype_e plattype_e, int i); + + boolean BuildStairs(line_t line, stair_e stair_e); + + boolean DoCeiling(line_t line, ceiling_e ceiling_e); + + void StopPlat(line_t line); + + void LightTurnOn(line_t line, int i); + + void StartLightStrobing(line_t line); + + void TurnTagLightsOff(line_t line); + + int Teleport(line_t line, int side, mobj_t thing); + + int CeilingCrushStop(line_t line); + + // + //EVENTS + //Events are operations triggered by using, crossing, + //or shooting special lines, or by timed thinkers. + // + /** + * P_CrossSpecialLine - TRIGGER Called every time a thing origin is about to cross a line with a non 0 special. + */ + default void CrossSpecialLine(line_t line, int side, mobj_t thing) { + //line_t line; + boolean ok; + + //line = LL.lines[linenum]; + // Triggers that other things can activate + if (thing.player == null) { + // Things that should NOT trigger specials... + switch (thing.type) { + case MT_ROCKET: + case MT_PLASMA: + case MT_BFG: + case MT_TROOPSHOT: + case MT_HEADSHOT: + case MT_BRUISERSHOT: + return; + // break; + + default: + break; + } + + ok = false; + switch (line.special) { + case 39: // TELEPORT TRIGGER + case 97: // TELEPORT RETRIGGER + case 125: // TELEPORT MONSTERONLY TRIGGER + case 126: // TELEPORT MONSTERONLY RETRIGGER + case 4: // RAISE DOOR + case 10: // PLAT DOWN-WAIT-UP-STAY TRIGGER + case 88: // PLAT DOWN-WAIT-UP-STAY RETRIGGER + ok = true; + break; + } + if (!ok) { + return; + } + } + + // TODO: enum! + // Note: could use some const's here. + switch (line.special) { + // TRIGGERS. + // All from here to RETRIGGERS. + case 2: + // Open Door + DoDoor(line, vldoor_e.open); + line.special = 0; + break; + + case 3: + // Close Door + DoDoor(line, vldoor_e.close); + line.special = 0; + break; + + case 4: + // Raise Door + DoDoor(line, vldoor_e.normal); + line.special = 0; + break; + + case 5: + // Raise Floor + DoFloor(line, floor_e.raiseFloor); + line.special = 0; + break; + + case 6: + // Fast Ceiling Crush & Raise + DoCeiling(line, ceiling_e.fastCrushAndRaise); + line.special = 0; + break; + + case 8: + // Build Stairs + BuildStairs(line, stair_e.build8); + line.special = 0; + break; + + case 10: + // PlatDownWaitUp + DoPlat(line, plattype_e.downWaitUpStay, 0); + line.special = 0; + break; + + case 12: + // Light Turn On - brightest near + LightTurnOn(line, 0); + line.special = 0; + break; + + case 13: + // Light Turn On 255 + LightTurnOn(line, 255); + line.special = 0; + break; + + case 16: + // Close Door 30 + DoDoor(line, vldoor_e.close30ThenOpen); + line.special = 0; + break; + + case 17: + // Start Light Strobing + StartLightStrobing(line); + line.special = 0; + break; + + case 19: + // Lower Floor + DoFloor(line, floor_e.lowerFloor); + line.special = 0; + break; + + case 22: + // Raise floor to nearest height and change texture + DoPlat(line, plattype_e.raiseToNearestAndChange, 0); + line.special = 0; + break; + + case 25: + // Ceiling Crush and Raise + DoCeiling(line, ceiling_e.crushAndRaise); + line.special = 0; + break; + + case 30: + // Raise floor to shortest texture height + // on either side of lines. + DoFloor(line, floor_e.raiseToTexture); + line.special = 0; + break; + + case 35: + // Lights Very Dark + LightTurnOn(line, 35); + line.special = 0; + break; + + case 36: + // Lower Floor (TURBO) + DoFloor(line, floor_e.turboLower); + line.special = 0; + break; + + case 37: + // LowerAndChange + DoFloor(line, floor_e.lowerAndChange); + line.special = 0; + break; + + case 38: + // Lower Floor To Lowest + DoFloor(line, floor_e.lowerFloorToLowest); + line.special = 0; + break; + + case 39: + // TELEPORT! + Teleport(line, side, thing); + line.special = 0; + break; + + case 40: + // RaiseCeilingLowerFloor + DoCeiling(line, ceiling_e.raiseToHighest); + DoFloor(line, floor_e.lowerFloorToLowest); + line.special = 0; + break; + + case 44: + // Ceiling Crush + DoCeiling(line, ceiling_e.lowerAndCrush); + line.special = 0; + break; + + case 52: + // EXIT! + DOOM().ExitLevel(); + break; + + case 53: + // Perpetual Platform Raise + DoPlat(line, plattype_e.perpetualRaise, 0); + line.special = 0; + break; + + case 54: + // Platform Stop + StopPlat(line); + line.special = 0; + break; + + case 56: + // Raise Floor Crush + DoFloor(line, floor_e.raiseFloorCrush); + line.special = 0; + break; + + case 57: + // Ceiling Crush Stop + CeilingCrushStop(line); + line.special = 0; + break; + + case 58: + // Raise Floor 24 + DoFloor(line, floor_e.raiseFloor24); + line.special = 0; + break; + + case 59: + // Raise Floor 24 And Change + DoFloor(line, floor_e.raiseFloor24AndChange); + line.special = 0; + break; + + case 104: + // Turn lights off in sector(tag) + TurnTagLightsOff(line); + line.special = 0; + break; + + case 108: + // Blazing Door Raise (faster than TURBO!) + DoDoor(line, vldoor_e.blazeRaise); + line.special = 0; + break; + + case 109: + // Blazing Door Open (faster than TURBO!) + DoDoor(line, vldoor_e.blazeOpen); + line.special = 0; + break; + + case 100: + // Build Stairs Turbo 16 + BuildStairs(line, stair_e.turbo16); + line.special = 0; + break; + + case 110: + // Blazing Door Close (faster than TURBO!) + DoDoor(line, vldoor_e.blazeClose); + line.special = 0; + break; + + case 119: + // Raise floor to nearest surr. floor + DoFloor(line, floor_e.raiseFloorToNearest); + line.special = 0; + break; + + case 121: + // Blazing PlatDownWaitUpStay + DoPlat(line, plattype_e.blazeDWUS, 0); + line.special = 0; + break; + + case 124: + // Secret EXIT + DOOM().SecretExitLevel(); + break; + + case 125: + // TELEPORT MonsterONLY + if (thing.player == null) { + Teleport(line, side, thing); + line.special = 0; + } + break; + + case 130: + // Raise Floor Turbo + DoFloor(line, floor_e.raiseFloorTurbo); + line.special = 0; + break; + + case 141: + // Silent Ceiling Crush & Raise + DoCeiling(line, ceiling_e.silentCrushAndRaise); + line.special = 0; + break; + + // RETRIGGERS. All from here till end. + case 72: + // Ceiling Crush + DoCeiling(line, ceiling_e.lowerAndCrush); + break; + + case 73: + // Ceiling Crush and Raise + DoCeiling(line, ceiling_e.crushAndRaise); + break; + + case 74: + // Ceiling Crush Stop + CeilingCrushStop(line); + break; + + case 75: + // Close Door + DoDoor(line, vldoor_e.close); + break; + + case 76: + // Close Door 30 + DoDoor(line, vldoor_e.close30ThenOpen); + break; + + case 77: + // Fast Ceiling Crush & Raise + DoCeiling(line, ceiling_e.fastCrushAndRaise); + break; + + case 79: + // Lights Very Dark + LightTurnOn(line, 35); + break; + + case 80: + // Light Turn On - brightest near + LightTurnOn(line, 0); + break; + + case 81: + // Light Turn On 255 + LightTurnOn(line, 255); + break; + + case 82: + // Lower Floor To Lowest + DoFloor(line, floor_e.lowerFloorToLowest); + break; + + case 83: + // Lower Floor + DoFloor(line, floor_e.lowerFloor); + break; + + case 84: + // LowerAndChange + DoFloor(line, floor_e.lowerAndChange); + break; + + case 86: + // Open Door + DoDoor(line, vldoor_e.open); + break; + + case 87: + // Perpetual Platform Raise + DoPlat(line, plattype_e.perpetualRaise, 0); + break; + + case 88: + // PlatDownWaitUp + DoPlat(line, plattype_e.downWaitUpStay, 0); + break; + + case 89: + // Platform Stop + StopPlat(line); + break; + + case 90: + // Raise Door + DoDoor(line, vldoor_e.normal); + break; + + case 91: + // Raise Floor + DoFloor(line, floor_e.raiseFloor); + break; + + case 92: + // Raise Floor 24 + DoFloor(line, floor_e.raiseFloor24); + break; + + case 93: + // Raise Floor 24 And Change + DoFloor(line, floor_e.raiseFloor24AndChange); + break; + + case 94: + // Raise Floor Crush + DoFloor(line, floor_e.raiseFloorCrush); + break; + + case 95: + // Raise floor to nearest height + // and change texture. + DoPlat(line, plattype_e.raiseToNearestAndChange, 0); + break; + + case 96: + // Raise floor to shortest texture height + // on either side of lines. + DoFloor(line, floor_e.raiseToTexture); + break; + + case 97: + // TELEPORT! + Teleport(line, side, thing); + break; + + case 98: + // Lower Floor (TURBO) + DoFloor(line, floor_e.turboLower); + break; + + case 105: + // Blazing Door Raise (faster than TURBO!) + DoDoor(line, vldoor_e.blazeRaise); + break; + + case 106: + // Blazing Door Open (faster than TURBO!) + DoDoor(line, vldoor_e.blazeOpen); + break; + + case 107: + // Blazing Door Close (faster than TURBO!) + DoDoor(line, vldoor_e.blazeClose); + break; + + case 120: + // Blazing PlatDownWaitUpStay. + DoPlat(line, plattype_e.blazeDWUS, 0); + break; + + case 126: + // TELEPORT MonsterONLY. + if (thing.player == null) { + Teleport(line, side, thing); + } + break; + + case 128: + // Raise To Nearest Floor + DoFloor(line, floor_e.raiseFloorToNearest); + break; + + case 129: + // Raise Floor Turbo + DoFloor(line, floor_e.raiseFloorTurbo); + break; + } + } + +} \ No newline at end of file diff --git a/src/p/Actions/ActionsMovement.java b/src/p/Actions/ActionsMovement.java index 50a25f7..999e540 100644 --- a/src/p/Actions/ActionsMovement.java +++ b/src/p/Actions/ActionsMovement.java @@ -1,681 +1,681 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import static data.Defines.FLOATSPEED; -import static data.Defines.PT_ADDLINES; -import static data.Limits.MAXMOVE; -import static data.Tables.ANG180; -import static data.Tables.BITS32; -import static data.Tables.finecosine; -import static data.Tables.finesine; -import defines.slopetype_t; -import defines.statenum_t; -import doom.SourceCode; -import static doom.SourceCode.P_Map.PTR_SlideTraverse; -import doom.SourceCode.fixed_t; -import doom.player_t; -import static m.fixed_t.FRACUNIT; -import static m.fixed_t.FixedMul; -import static p.ChaseDirections.DI_EAST; -import static p.ChaseDirections.DI_NODIR; -import static p.ChaseDirections.DI_NORTH; -import static p.ChaseDirections.DI_SOUTH; -import static p.ChaseDirections.DI_SOUTHEAST; -import static p.ChaseDirections.DI_WEST; -import static p.ChaseDirections.diags; -import static p.ChaseDirections.opposite; -import static p.ChaseDirections.xspeed; -import static p.ChaseDirections.yspeed; -import static p.MapUtils.AproxDistance; -import p.intercept_t; -import p.mobj_t; -import static p.mobj_t.MF_CORPSE; -import static p.mobj_t.MF_DROPOFF; -import static p.mobj_t.MF_FLOAT; -import static p.mobj_t.MF_INFLOAT; -import static p.mobj_t.MF_MISSILE; -import static p.mobj_t.MF_NOCLIP; -import static p.mobj_t.MF_SKULLFLY; -import static p.mobj_t.MF_TELEPORT; -import rr.SceneRenderer; -import rr.line_t; -import static rr.line_t.ML_TWOSIDED; -import static utils.C2JUtils.eval; -import utils.TraitFactory.ContextKey; - -public interface ActionsMovement extends ActionsPathTraverse { - - ContextKey KEY_DIRTYPE = ACTION_KEY_CHAIN.newKey(ActionsMovement.class, DirType::new); - - // - // P_XYMovement - // - int STOPSPEED = 4096; - int FRICTION = 59392; - int FUDGE = 2048; ///(FRACUNIT/MAPFRACUNIT); - - void UnsetThingPosition(mobj_t thing); - - void ExplodeMissile(mobj_t mo); - - final class DirType { - - //dirtype - int d1; - int d2; - } - - ///////////////// MOVEMENT'S ACTIONS //////////////////////// - /** - * If "floatok" true, move would be ok if within "tmfloorz - tmceilingz". - */ - // - // P_Move - // Move in the current direction, - // returns false if the move is blocked. - // - default boolean Move(mobj_t actor) { - final Movement mov = contextRequire(KEY_MOVEMENT); - final Spechits sp = contextRequire(KEY_SPECHITS); - - @fixed_t - int tryx, tryy; - line_t ld; - - // warning: 'catch', 'throw', and 'try' - // are all C++ reserved words - boolean try_ok; - boolean good; - - if (actor.movedir == DI_NODIR) { - return false; - } - - if (actor.movedir >= 8) { - doomSystem().Error("Weird actor.movedir!"); - } - - tryx = actor.x + actor.info.speed * xspeed[actor.movedir]; - tryy = actor.y + actor.info.speed * yspeed[actor.movedir]; - - try_ok = this.TryMove(actor, tryx, tryy); - - if (!try_ok) { - // open any specials - if (eval(actor.flags & MF_FLOAT) && mov.floatok) { - // must adjust height - if (actor.z < mov.tmfloorz) { - actor.z += FLOATSPEED; - } else { - actor.z -= FLOATSPEED; - } - - actor.flags |= MF_INFLOAT; - return true; - } - - if (sp.numspechit == 0) { - return false; - } - - actor.movedir = DI_NODIR; - good = false; - while ((sp.numspechit--) > 0) { - ld = sp.spechit[sp.numspechit]; - // if the special is not a door - // that can be opened, - // return false - if (UseSpecialLine(actor, ld, false)) { - good = true; - } - } - return good; - } else { - actor.flags &= ~MF_INFLOAT; - } - - if (!eval(actor.flags & MF_FLOAT)) { - actor.z = actor.floorz; - } - return true; - } - - /** - * // P_TryMove // Attempt to move to a new position, // crossing special lines unless MF_TELEPORT is set. - * - * @param x fixed_t - * @param y fixed_t - * - */ - default boolean TryMove(mobj_t thing, @fixed_t int x, @fixed_t int y) { - final Movement mov = contextRequire(KEY_MOVEMENT); - final Spechits sp = contextRequire(KEY_SPECHITS); - - @fixed_t - int oldx, oldy; - boolean side, oldside; // both were int - line_t ld; - - mov.floatok = false; - if (!this.CheckPosition(thing, x, y)) { - return false; // solid wall or thing - } - if (!eval(thing.flags & MF_NOCLIP)) { - if (mov.tmceilingz - mov.tmfloorz < thing.height) { - return false; // doesn't fit - } - mov.floatok = true; - - if (!eval(thing.flags & MF_TELEPORT) && mov.tmceilingz - thing.z < thing.height) { - return false; // mobj must lower itself to fit - } - if (!eval(thing.flags & MF_TELEPORT) && mov.tmfloorz - thing.z > 24 * FRACUNIT) { - return false; // too big a step up - } - if (!eval(thing.flags & (MF_DROPOFF | MF_FLOAT)) && mov.tmfloorz - mov.tmdropoffz > 24 * FRACUNIT) { - return false; // don't stand over a dropoff - } - } - - // the move is ok, - // so link the thing into its new position - UnsetThingPosition(thing); - - oldx = thing.x; - oldy = thing.y; - thing.floorz = mov.tmfloorz; - thing.ceilingz = mov.tmceilingz; - thing.x = x; - thing.y = y; - - levelLoader().SetThingPosition(thing); - - // if any special lines were hit, do the effect - if (!eval(thing.flags & (MF_TELEPORT | MF_NOCLIP))) { - while (sp.numspechit-- > 0) { - // see if the line was crossed - ld = sp.spechit[sp.numspechit]; - side = ld.PointOnLineSide(thing.x, thing.y); - oldside = ld.PointOnLineSide(oldx, oldy); - if (side != oldside) { - if (ld.special != 0) { - CrossSpecialLine(ld, oldside ? 1 : 0, thing); - } - } - } - } - - return true; - } - - default void NewChaseDir(mobj_t actor) { - final DirType dirtype = contextRequire(KEY_DIRTYPE); - - @fixed_t - int deltax, deltay; - - int tdir; - int olddir; - // dirtypes - int turnaround; - - if (actor.target == null) { - doomSystem().Error("P_NewChaseDir: called with no target"); - } - - olddir = actor.movedir; - turnaround = opposite[olddir]; - - deltax = actor.target.x - actor.x; - deltay = actor.target.y - actor.y; - - if (deltax > 10 * FRACUNIT) { - dirtype.d1 = DI_EAST; - } else if (deltax < -10 * FRACUNIT) { - dirtype.d1 = DI_WEST; - } else { - dirtype.d1 = DI_NODIR; - } - - if (deltay < -10 * FRACUNIT) { - dirtype.d2 = DI_SOUTH; - } else if (deltay > 10 * FRACUNIT) { - dirtype.d2 = DI_NORTH; - } else { - dirtype.d2 = DI_NODIR; - } - - // try direct route - if (dirtype.d1 != DI_NODIR && dirtype.d2 != DI_NODIR) { - actor.movedir = diags[(eval(deltay < 0) << 1) + eval(deltax > 0)]; - if (actor.movedir != turnaround && this.TryWalk(actor)) { - return; - } - } - - // try other directions - if (P_Random() > 200 || Math.abs(deltay) > Math.abs(deltax)) { - tdir = dirtype.d1; - dirtype.d1 = dirtype.d2; - dirtype.d2 = tdir; - } - - if (dirtype.d1 == turnaround) { - dirtype.d1 = DI_NODIR; - } - - if (dirtype.d2 == turnaround) { - dirtype.d2 = DI_NODIR; - } - - if (dirtype.d1 != DI_NODIR) { - actor.movedir = dirtype.d1; - if (this.TryWalk(actor)) { - // either moved forward or attacked - return; - } - } - - if (dirtype.d2 != DI_NODIR) { - actor.movedir = dirtype.d2; - - if (this.TryWalk(actor)) { - return; - } - } - - // there is no direct path to the player, - // so pick another direction. - if (olddir != DI_NODIR) { - actor.movedir = olddir; - - if (this.TryWalk(actor)) { - return; - } - } - - // randomly determine direction of search - if (eval(P_Random() & 1)) { - for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++) { - if (tdir != turnaround) { - actor.movedir = tdir; - - if (TryWalk(actor)) { - return; - } - } - } - } else { - for (tdir = DI_SOUTHEAST; tdir != (DI_EAST - 1); tdir--) { - if (tdir != turnaround) { - actor.movedir = tdir; - - if (TryWalk(actor)) { - return; - } - } - } - } - - if (turnaround != DI_NODIR) { - actor.movedir = turnaround; - if (TryWalk(actor)) { - return; - } - } - - actor.movedir = DI_NODIR; // can not move - } - - /** - * TryWalk Attempts to move actor on in its current (ob.moveangle) direction. If blocked by either a wall or an - * actor returns FALSE If move is either clear or blocked only by a door, returns TRUE and sets... If a door is in - * the way, an OpenDoor call is made to start it opening. - */ - default boolean TryWalk(mobj_t actor) { - if (!Move(actor)) { - return false; - } - - actor.movecount = P_Random() & 15; - return true; - } - - // - // P_HitSlideLine - // Adjusts the xmove / ymove - // so that the next move will slide along the wall. - // - default void HitSlideLine(line_t ld) { - final SceneRenderer sr = sceneRenderer(); - final SlideMove slideMove = contextRequire(KEY_SLIDEMOVE); - boolean side; - - // all angles - long lineangle, moveangle, deltaangle; - - @fixed_t - int movelen, newlen; - - if (ld.slopetype == slopetype_t.ST_HORIZONTAL) { - slideMove.tmymove = 0; - return; - } - - if (ld.slopetype == slopetype_t.ST_VERTICAL) { - slideMove.tmxmove = 0; - return; - } - - side = ld.PointOnLineSide(slideMove.slidemo.x, slideMove.slidemo.y); - - lineangle = sr.PointToAngle2(0, 0, ld.dx, ld.dy); - - if (side == true) { - lineangle += ANG180; - } - - moveangle = sr.PointToAngle2(0, 0, slideMove.tmxmove, slideMove.tmymove); - deltaangle = (moveangle - lineangle) & BITS32; - - if (deltaangle > ANG180) { - deltaangle += ANG180; - } - // system.Error ("SlideLine: ang>ANG180"); - - //lineangle >>>= ANGLETOFINESHIFT; - //deltaangle >>>= ANGLETOFINESHIFT; - movelen = AproxDistance(slideMove.tmxmove, slideMove.tmymove); - newlen = FixedMul(movelen, finecosine(deltaangle)); - - slideMove.tmxmove = FixedMul(newlen, finecosine(lineangle)); - slideMove.tmymove = FixedMul(newlen, finesine(lineangle)); - } - - ///(FRACUNIT/MAPFRACUNIT); - // - // P_SlideMove - // The momx / momy move is bad, so try to slide - // along a wall. - // Find the first line hit, move flush to it, - // and slide along it - // - // This is a kludgy mess. - // - default void SlideMove(mobj_t mo) { - final SlideMove slideMove = contextRequire(KEY_SLIDEMOVE); - @fixed_t - int leadx, leady, trailx, traily, newx, newy; - int hitcount; - - slideMove.slidemo = mo; - hitcount = 0; - - do { - if (++hitcount == 3) { - // goto stairstep - this.stairstep(mo); - return; - } // don't loop forever - - // trace along the three leading corners - if (mo.momx > 0) { - leadx = mo.x + mo.radius; - trailx = mo.x - mo.radius; - } else { - leadx = mo.x - mo.radius; - trailx = mo.x + mo.radius; - } - - if (mo.momy > 0) { - leady = mo.y + mo.radius; - traily = mo.y - mo.radius; - } else { - leady = mo.y - mo.radius; - traily = mo.y + mo.radius; - } - - slideMove.bestslidefrac = FRACUNIT + 1; - - PathTraverse(leadx, leady, leadx + mo.momx, leady + mo.momy, PT_ADDLINES, this::SlideTraverse); - PathTraverse(trailx, leady, trailx + mo.momx, leady + mo.momy, PT_ADDLINES, this::SlideTraverse); - PathTraverse(leadx, traily, leadx + mo.momx, traily + mo.momy, PT_ADDLINES, this::SlideTraverse); - - // move up to the wall - if (slideMove.bestslidefrac == FRACUNIT + 1) { - // the move most have hit the middle, so stairstep - this.stairstep(mo); - return; - } // don't loop forever - - // fudge a bit to make sure it doesn't hit - slideMove.bestslidefrac -= FUDGE; - if (slideMove.bestslidefrac > 0) { - newx = FixedMul(mo.momx, slideMove.bestslidefrac); - newy = FixedMul(mo.momy, slideMove.bestslidefrac); - - if (!this.TryMove(mo, mo.x + newx, mo.y + newy)) { - // goto stairstep - this.stairstep(mo); - return; - } // don't loop forever - } - - // Now continue along the wall. - // First calculate remainder. - slideMove.bestslidefrac = FRACUNIT - (slideMove.bestslidefrac + FUDGE); - - if (slideMove.bestslidefrac > FRACUNIT) { - slideMove.bestslidefrac = FRACUNIT; - } - - if (slideMove.bestslidefrac <= 0) { - return; - } - - slideMove.tmxmove = FixedMul(mo.momx, slideMove.bestslidefrac); - slideMove.tmymove = FixedMul(mo.momy, slideMove.bestslidefrac); - - HitSlideLine(slideMove.bestslideline); // clip the moves - - mo.momx = slideMove.tmxmove; - mo.momy = slideMove.tmymove; - - } // goto retry - while (!TryMove(mo, mo.x + slideMove.tmxmove, mo.y + slideMove.tmymove)); - } - - /** - * Fugly "goto stairstep" simulation - * - * @param mo - */ - default void stairstep(mobj_t mo) { - if (!TryMove(mo, mo.x, mo.y + mo.momy)) { - TryMove(mo, mo.x + mo.momx, mo.y); - } - } - - // - // P_XYMovement - // - default void XYMovement(mobj_t mo) { - final Movement mv = contextRequire(KEY_MOVEMENT); - - @fixed_t - int ptryx, ptryy; // pointers to fixed_t ??? - @fixed_t - int xmove, ymove; - player_t player; - - if ((mo.momx == 0) && (mo.momy == 0)) { - if ((mo.flags & MF_SKULLFLY) != 0) { - // the skull slammed into something - mo.flags &= ~MF_SKULLFLY; - mo.momx = mo.momy = mo.momz = 0; - - mo.SetMobjState(mo.info.spawnstate); - } - return; - } - - player = mo.player; - - if (mo.momx > MAXMOVE) { - mo.momx = MAXMOVE; - } else if (mo.momx < -MAXMOVE) { - mo.momx = -MAXMOVE; - } - - if (mo.momy > MAXMOVE) { - mo.momy = MAXMOVE; - } else if (mo.momy < -MAXMOVE) { - mo.momy = -MAXMOVE; - } - - xmove = mo.momx; - ymove = mo.momy; - - do { - if (xmove > MAXMOVE / 2 || ymove > MAXMOVE / 2) { - ptryx = mo.x + xmove / 2; - ptryy = mo.y + ymove / 2; - xmove >>= 1; - ymove >>= 1; - } else { - ptryx = mo.x + xmove; - ptryy = mo.y + ymove; - xmove = ymove = 0; - } - - if (!TryMove(mo, ptryx, ptryy)) { - // blocked move - if (mo.player != null) { // try to slide along it - SlideMove(mo); - } else if (eval(mo.flags & MF_MISSILE)) { - // explode a missile - if (mv.ceilingline != null && mv.ceilingline.backsector != null - && mv.ceilingline.backsector.ceilingpic == DOOM().textureManager.getSkyFlatNum()) { - // Hack to prevent missiles exploding - // against the sky. - // Does not handle sky floors. - RemoveMobj(mo); - return; - } - ExplodeMissile(mo); - } else { - mo.momx = mo.momy = 0; - } - } - } while ((xmove | ymove) != 0); - - // slow down - if (player != null && eval(player.cheats & player_t.CF_NOMOMENTUM)) { - // debug option for no sliding at all - mo.momx = mo.momy = 0; - return; - } - - if (eval(mo.flags & (MF_MISSILE | MF_SKULLFLY))) { - return; // no friction for missiles ever - } - if (mo.z > mo.floorz) { - return; // no friction when airborne - } - if (eval(mo.flags & MF_CORPSE)) { - // do not stop sliding - // if halfway off a step with some momentum - if (mo.momx > FRACUNIT / 4 - || mo.momx < -FRACUNIT / 4 - || mo.momy > FRACUNIT / 4 - || mo.momy < -FRACUNIT / 4) { - if (mo.floorz != mo.subsector.sector.floorheight) { - return; - } - } - } - - if (mo.momx > -STOPSPEED && mo.momx < STOPSPEED && mo.momy > -STOPSPEED && mo.momy < STOPSPEED - && (player == null || (player.cmd.forwardmove == 0 && player.cmd.sidemove == 0))) { - // if in a walking frame, stop moving - // TODO: we need a way to get state indexed inside of states[], to sim pointer arithmetic. - // FIX: added an "id" field. - if (player != null && player.mo.mobj_state.id - statenum_t.S_PLAY_RUN1.ordinal() < 4) { - player.mo.SetMobjState(statenum_t.S_PLAY); - } - - mo.momx = 0; - mo.momy = 0; - } else { - mo.momx = FixedMul(mo.momx, FRICTION); - mo.momy = FixedMul(mo.momy, FRICTION); - } - } - - // - // SLIDE MOVE - // Allows the player to slide along any angled walls. - // - // fixed - // - // PTR_SlideTraverse - // - @SourceCode.P_Map.C(PTR_SlideTraverse) - default boolean SlideTraverse(intercept_t in) { - final SlideMove slideMove = contextRequire(KEY_SLIDEMOVE); - final Movement ma = contextRequire(KEY_MOVEMENT); - line_t li; - - if (!in.isaline) { - doomSystem().Error("PTR_SlideTraverse: not a line?"); - } - - li = (line_t) in.d(); - - if (!eval(li.flags & ML_TWOSIDED)) { - if (li.PointOnLineSide(slideMove.slidemo.x, slideMove.slidemo.y)) { - // don't hit the back side - return true; - } - return this.isblocking(in, li); - } - - // set openrange, opentop, openbottom - LineOpening(li); - - if ((ma.openrange < slideMove.slidemo.height) - || // doesn't fit - (ma.opentop - slideMove.slidemo.z < slideMove.slidemo.height) - || // mobj is too high - (ma.openbottom - slideMove.slidemo.z > 24 * FRACUNIT)) // too big a step up - { - if (in.frac < slideMove.bestslidefrac) { - slideMove.secondslidefrac = slideMove.bestslidefrac; - slideMove.secondslideline = slideMove.bestslideline; - slideMove.bestslidefrac = in.frac; - slideMove.bestslideline = li; - } - - return false; // stop - } else { // this line doesn't block movement - return true; - } - } -; -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import static data.Defines.FLOATSPEED; +import static data.Defines.PT_ADDLINES; +import static data.Limits.MAXMOVE; +import static data.Tables.ANG180; +import static data.Tables.BITS32; +import static data.Tables.finecosine; +import static data.Tables.finesine; +import defines.slopetype_t; +import defines.statenum_t; +import doom.SourceCode; +import static doom.SourceCode.P_Map.PTR_SlideTraverse; +import doom.SourceCode.fixed_t; +import doom.player_t; +import static m.fixed_t.FRACUNIT; +import static m.fixed_t.FixedMul; +import static p.ChaseDirections.DI_EAST; +import static p.ChaseDirections.DI_NODIR; +import static p.ChaseDirections.DI_NORTH; +import static p.ChaseDirections.DI_SOUTH; +import static p.ChaseDirections.DI_SOUTHEAST; +import static p.ChaseDirections.DI_WEST; +import static p.ChaseDirections.diags; +import static p.ChaseDirections.opposite; +import static p.ChaseDirections.xspeed; +import static p.ChaseDirections.yspeed; +import static p.MapUtils.AproxDistance; +import p.intercept_t; +import p.mobj_t; +import static p.mobj_t.MF_CORPSE; +import static p.mobj_t.MF_DROPOFF; +import static p.mobj_t.MF_FLOAT; +import static p.mobj_t.MF_INFLOAT; +import static p.mobj_t.MF_MISSILE; +import static p.mobj_t.MF_NOCLIP; +import static p.mobj_t.MF_SKULLFLY; +import static p.mobj_t.MF_TELEPORT; +import rr.SceneRenderer; +import rr.line_t; +import static rr.line_t.ML_TWOSIDED; +import static utils.C2JUtils.eval; +import utils.TraitFactory.ContextKey; + +public interface ActionsMovement extends ActionsPathTraverse { + + ContextKey KEY_DIRTYPE = ACTION_KEY_CHAIN.newKey(ActionsMovement.class, DirType::new); + + // + // P_XYMovement + // + int STOPSPEED = 4096; + int FRICTION = 59392; + int FUDGE = 2048; ///(FRACUNIT/MAPFRACUNIT); + + void UnsetThingPosition(mobj_t thing); + + void ExplodeMissile(mobj_t mo); + + final class DirType { + + //dirtype + int d1; + int d2; + } + + ///////////////// MOVEMENT'S ACTIONS //////////////////////// + /** + * If "floatok" true, move would be ok if within "tmfloorz - tmceilingz". + */ + // + // P_Move + // Move in the current direction, + // returns false if the move is blocked. + // + default boolean Move(mobj_t actor) { + final Movement mov = contextRequire(KEY_MOVEMENT); + final Spechits sp = contextRequire(KEY_SPECHITS); + + @fixed_t + int tryx, tryy; + line_t ld; + + // warning: 'catch', 'throw', and 'try' + // are all C++ reserved words + boolean try_ok; + boolean good; + + if (actor.movedir == DI_NODIR) { + return false; + } + + if (actor.movedir >= 8) { + doomSystem().Error("Weird actor.movedir!"); + } + + tryx = actor.x + actor.info.speed * xspeed[actor.movedir]; + tryy = actor.y + actor.info.speed * yspeed[actor.movedir]; + + try_ok = this.TryMove(actor, tryx, tryy); + + if (!try_ok) { + // open any specials + if (eval(actor.flags & MF_FLOAT) && mov.floatok) { + // must adjust height + if (actor.z < mov.tmfloorz) { + actor.z += FLOATSPEED; + } else { + actor.z -= FLOATSPEED; + } + + actor.flags |= MF_INFLOAT; + return true; + } + + if (sp.numspechit == 0) { + return false; + } + + actor.movedir = DI_NODIR; + good = false; + while ((sp.numspechit--) > 0) { + ld = sp.spechit[sp.numspechit]; + // if the special is not a door + // that can be opened, + // return false + if (UseSpecialLine(actor, ld, false)) { + good = true; + } + } + return good; + } else { + actor.flags &= ~MF_INFLOAT; + } + + if (!eval(actor.flags & MF_FLOAT)) { + actor.z = actor.floorz; + } + return true; + } + + /** + * // P_TryMove // Attempt to move to a new position, // crossing special lines unless MF_TELEPORT is set. + * + * @param x fixed_t + * @param y fixed_t + * + */ + default boolean TryMove(mobj_t thing, @fixed_t int x, @fixed_t int y) { + final Movement mov = contextRequire(KEY_MOVEMENT); + final Spechits sp = contextRequire(KEY_SPECHITS); + + @fixed_t + int oldx, oldy; + boolean side, oldside; // both were int + line_t ld; + + mov.floatok = false; + if (!this.CheckPosition(thing, x, y)) { + return false; // solid wall or thing + } + if (!eval(thing.flags & MF_NOCLIP)) { + if (mov.tmceilingz - mov.tmfloorz < thing.height) { + return false; // doesn't fit + } + mov.floatok = true; + + if (!eval(thing.flags & MF_TELEPORT) && mov.tmceilingz - thing.z < thing.height) { + return false; // mobj must lower itself to fit + } + if (!eval(thing.flags & MF_TELEPORT) && mov.tmfloorz - thing.z > 24 * FRACUNIT) { + return false; // too big a step up + } + if (!eval(thing.flags & (MF_DROPOFF | MF_FLOAT)) && mov.tmfloorz - mov.tmdropoffz > 24 * FRACUNIT) { + return false; // don't stand over a dropoff + } + } + + // the move is ok, + // so link the thing into its new position + UnsetThingPosition(thing); + + oldx = thing.x; + oldy = thing.y; + thing.floorz = mov.tmfloorz; + thing.ceilingz = mov.tmceilingz; + thing.x = x; + thing.y = y; + + levelLoader().SetThingPosition(thing); + + // if any special lines were hit, do the effect + if (!eval(thing.flags & (MF_TELEPORT | MF_NOCLIP))) { + while (sp.numspechit-- > 0) { + // see if the line was crossed + ld = sp.spechit[sp.numspechit]; + side = ld.PointOnLineSide(thing.x, thing.y); + oldside = ld.PointOnLineSide(oldx, oldy); + if (side != oldside) { + if (ld.special != 0) { + CrossSpecialLine(ld, oldside ? 1 : 0, thing); + } + } + } + } + + return true; + } + + default void NewChaseDir(mobj_t actor) { + final DirType dirtype = contextRequire(KEY_DIRTYPE); + + @fixed_t + int deltax, deltay; + + int tdir; + int olddir; + // dirtypes + int turnaround; + + if (actor.target == null) { + doomSystem().Error("P_NewChaseDir: called with no target"); + } + + olddir = actor.movedir; + turnaround = opposite[olddir]; + + deltax = actor.target.x - actor.x; + deltay = actor.target.y - actor.y; + + if (deltax > 10 * FRACUNIT) { + dirtype.d1 = DI_EAST; + } else if (deltax < -10 * FRACUNIT) { + dirtype.d1 = DI_WEST; + } else { + dirtype.d1 = DI_NODIR; + } + + if (deltay < -10 * FRACUNIT) { + dirtype.d2 = DI_SOUTH; + } else if (deltay > 10 * FRACUNIT) { + dirtype.d2 = DI_NORTH; + } else { + dirtype.d2 = DI_NODIR; + } + + // try direct route + if (dirtype.d1 != DI_NODIR && dirtype.d2 != DI_NODIR) { + actor.movedir = diags[(eval(deltay < 0) << 1) + eval(deltax > 0)]; + if (actor.movedir != turnaround && this.TryWalk(actor)) { + return; + } + } + + // try other directions + if (P_Random() > 200 || Math.abs(deltay) > Math.abs(deltax)) { + tdir = dirtype.d1; + dirtype.d1 = dirtype.d2; + dirtype.d2 = tdir; + } + + if (dirtype.d1 == turnaround) { + dirtype.d1 = DI_NODIR; + } + + if (dirtype.d2 == turnaround) { + dirtype.d2 = DI_NODIR; + } + + if (dirtype.d1 != DI_NODIR) { + actor.movedir = dirtype.d1; + if (this.TryWalk(actor)) { + // either moved forward or attacked + return; + } + } + + if (dirtype.d2 != DI_NODIR) { + actor.movedir = dirtype.d2; + + if (this.TryWalk(actor)) { + return; + } + } + + // there is no direct path to the player, + // so pick another direction. + if (olddir != DI_NODIR) { + actor.movedir = olddir; + + if (this.TryWalk(actor)) { + return; + } + } + + // randomly determine direction of search + if (eval(P_Random() & 1)) { + for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++) { + if (tdir != turnaround) { + actor.movedir = tdir; + + if (TryWalk(actor)) { + return; + } + } + } + } else { + for (tdir = DI_SOUTHEAST; tdir != (DI_EAST - 1); tdir--) { + if (tdir != turnaround) { + actor.movedir = tdir; + + if (TryWalk(actor)) { + return; + } + } + } + } + + if (turnaround != DI_NODIR) { + actor.movedir = turnaround; + if (TryWalk(actor)) { + return; + } + } + + actor.movedir = DI_NODIR; // can not move + } + + /** + * TryWalk Attempts to move actor on in its current (ob.moveangle) direction. If blocked by either a wall or an + * actor returns FALSE If move is either clear or blocked only by a door, returns TRUE and sets... If a door is in + * the way, an OpenDoor call is made to start it opening. + */ + default boolean TryWalk(mobj_t actor) { + if (!Move(actor)) { + return false; + } + + actor.movecount = P_Random() & 15; + return true; + } + + // + // P_HitSlideLine + // Adjusts the xmove / ymove + // so that the next move will slide along the wall. + // + default void HitSlideLine(line_t ld) { + final SceneRenderer sr = sceneRenderer(); + final SlideMove slideMove = contextRequire(KEY_SLIDEMOVE); + boolean side; + + // all angles + long lineangle, moveangle, deltaangle; + + @fixed_t + int movelen, newlen; + + if (ld.slopetype == slopetype_t.ST_HORIZONTAL) { + slideMove.tmymove = 0; + return; + } + + if (ld.slopetype == slopetype_t.ST_VERTICAL) { + slideMove.tmxmove = 0; + return; + } + + side = ld.PointOnLineSide(slideMove.slidemo.x, slideMove.slidemo.y); + + lineangle = sr.PointToAngle2(0, 0, ld.dx, ld.dy); + + if (side == true) { + lineangle += ANG180; + } + + moveangle = sr.PointToAngle2(0, 0, slideMove.tmxmove, slideMove.tmymove); + deltaangle = (moveangle - lineangle) & BITS32; + + if (deltaangle > ANG180) { + deltaangle += ANG180; + } + // system.Error ("SlideLine: ang>ANG180"); + + //lineangle >>>= ANGLETOFINESHIFT; + //deltaangle >>>= ANGLETOFINESHIFT; + movelen = AproxDistance(slideMove.tmxmove, slideMove.tmymove); + newlen = FixedMul(movelen, finecosine(deltaangle)); + + slideMove.tmxmove = FixedMul(newlen, finecosine(lineangle)); + slideMove.tmymove = FixedMul(newlen, finesine(lineangle)); + } + + ///(FRACUNIT/MAPFRACUNIT); + // + // P_SlideMove + // The momx / momy move is bad, so try to slide + // along a wall. + // Find the first line hit, move flush to it, + // and slide along it + // + // This is a kludgy mess. + // + default void SlideMove(mobj_t mo) { + final SlideMove slideMove = contextRequire(KEY_SLIDEMOVE); + @fixed_t + int leadx, leady, trailx, traily, newx, newy; + int hitcount; + + slideMove.slidemo = mo; + hitcount = 0; + + do { + if (++hitcount == 3) { + // goto stairstep + this.stairstep(mo); + return; + } // don't loop forever + + // trace along the three leading corners + if (mo.momx > 0) { + leadx = mo.x + mo.radius; + trailx = mo.x - mo.radius; + } else { + leadx = mo.x - mo.radius; + trailx = mo.x + mo.radius; + } + + if (mo.momy > 0) { + leady = mo.y + mo.radius; + traily = mo.y - mo.radius; + } else { + leady = mo.y - mo.radius; + traily = mo.y + mo.radius; + } + + slideMove.bestslidefrac = FRACUNIT + 1; + + PathTraverse(leadx, leady, leadx + mo.momx, leady + mo.momy, PT_ADDLINES, this::SlideTraverse); + PathTraverse(trailx, leady, trailx + mo.momx, leady + mo.momy, PT_ADDLINES, this::SlideTraverse); + PathTraverse(leadx, traily, leadx + mo.momx, traily + mo.momy, PT_ADDLINES, this::SlideTraverse); + + // move up to the wall + if (slideMove.bestslidefrac == FRACUNIT + 1) { + // the move most have hit the middle, so stairstep + this.stairstep(mo); + return; + } // don't loop forever + + // fudge a bit to make sure it doesn't hit + slideMove.bestslidefrac -= FUDGE; + if (slideMove.bestslidefrac > 0) { + newx = FixedMul(mo.momx, slideMove.bestslidefrac); + newy = FixedMul(mo.momy, slideMove.bestslidefrac); + + if (!this.TryMove(mo, mo.x + newx, mo.y + newy)) { + // goto stairstep + this.stairstep(mo); + return; + } // don't loop forever + } + + // Now continue along the wall. + // First calculate remainder. + slideMove.bestslidefrac = FRACUNIT - (slideMove.bestslidefrac + FUDGE); + + if (slideMove.bestslidefrac > FRACUNIT) { + slideMove.bestslidefrac = FRACUNIT; + } + + if (slideMove.bestslidefrac <= 0) { + return; + } + + slideMove.tmxmove = FixedMul(mo.momx, slideMove.bestslidefrac); + slideMove.tmymove = FixedMul(mo.momy, slideMove.bestslidefrac); + + HitSlideLine(slideMove.bestslideline); // clip the moves + + mo.momx = slideMove.tmxmove; + mo.momy = slideMove.tmymove; + + } // goto retry + while (!TryMove(mo, mo.x + slideMove.tmxmove, mo.y + slideMove.tmymove)); + } + + /** + * Fugly "goto stairstep" simulation + * + * @param mo + */ + default void stairstep(mobj_t mo) { + if (!TryMove(mo, mo.x, mo.y + mo.momy)) { + TryMove(mo, mo.x + mo.momx, mo.y); + } + } + + // + // P_XYMovement + // + default void XYMovement(mobj_t mo) { + final Movement mv = contextRequire(KEY_MOVEMENT); + + @fixed_t + int ptryx, ptryy; // pointers to fixed_t ??? + @fixed_t + int xmove, ymove; + player_t player; + + if ((mo.momx == 0) && (mo.momy == 0)) { + if ((mo.flags & MF_SKULLFLY) != 0) { + // the skull slammed into something + mo.flags &= ~MF_SKULLFLY; + mo.momx = mo.momy = mo.momz = 0; + + mo.SetMobjState(mo.info.spawnstate); + } + return; + } + + player = mo.player; + + if (mo.momx > MAXMOVE) { + mo.momx = MAXMOVE; + } else if (mo.momx < -MAXMOVE) { + mo.momx = -MAXMOVE; + } + + if (mo.momy > MAXMOVE) { + mo.momy = MAXMOVE; + } else if (mo.momy < -MAXMOVE) { + mo.momy = -MAXMOVE; + } + + xmove = mo.momx; + ymove = mo.momy; + + do { + if (xmove > MAXMOVE / 2 || ymove > MAXMOVE / 2) { + ptryx = mo.x + xmove / 2; + ptryy = mo.y + ymove / 2; + xmove >>= 1; + ymove >>= 1; + } else { + ptryx = mo.x + xmove; + ptryy = mo.y + ymove; + xmove = ymove = 0; + } + + if (!TryMove(mo, ptryx, ptryy)) { + // blocked move + if (mo.player != null) { // try to slide along it + SlideMove(mo); + } else if (eval(mo.flags & MF_MISSILE)) { + // explode a missile + if (mv.ceilingline != null && mv.ceilingline.backsector != null + && mv.ceilingline.backsector.ceilingpic == DOOM().textureManager.getSkyFlatNum()) { + // Hack to prevent missiles exploding + // against the sky. + // Does not handle sky floors. + RemoveMobj(mo); + return; + } + ExplodeMissile(mo); + } else { + mo.momx = mo.momy = 0; + } + } + } while ((xmove | ymove) != 0); + + // slow down + if (player != null && eval(player.cheats & player_t.CF_NOMOMENTUM)) { + // debug option for no sliding at all + mo.momx = mo.momy = 0; + return; + } + + if (eval(mo.flags & (MF_MISSILE | MF_SKULLFLY))) { + return; // no friction for missiles ever + } + if (mo.z > mo.floorz) { + return; // no friction when airborne + } + if (eval(mo.flags & MF_CORPSE)) { + // do not stop sliding + // if halfway off a step with some momentum + if (mo.momx > FRACUNIT / 4 + || mo.momx < -FRACUNIT / 4 + || mo.momy > FRACUNIT / 4 + || mo.momy < -FRACUNIT / 4) { + if (mo.floorz != mo.subsector.sector.floorheight) { + return; + } + } + } + + if (mo.momx > -STOPSPEED && mo.momx < STOPSPEED && mo.momy > -STOPSPEED && mo.momy < STOPSPEED + && (player == null || (player.cmd.forwardmove == 0 && player.cmd.sidemove == 0))) { + // if in a walking frame, stop moving + // TODO: we need a way to get state indexed inside of states[], to sim pointer arithmetic. + // FIX: added an "id" field. + if (player != null && player.mo.mobj_state.id - statenum_t.S_PLAY_RUN1.ordinal() < 4) { + player.mo.SetMobjState(statenum_t.S_PLAY); + } + + mo.momx = 0; + mo.momy = 0; + } else { + mo.momx = FixedMul(mo.momx, FRICTION); + mo.momy = FixedMul(mo.momy, FRICTION); + } + } + + // + // SLIDE MOVE + // Allows the player to slide along any angled walls. + // + // fixed + // + // PTR_SlideTraverse + // + @SourceCode.P_Map.C(PTR_SlideTraverse) + default boolean SlideTraverse(intercept_t in) { + final SlideMove slideMove = contextRequire(KEY_SLIDEMOVE); + final Movement ma = contextRequire(KEY_MOVEMENT); + line_t li; + + if (!in.isaline) { + doomSystem().Error("PTR_SlideTraverse: not a line?"); + } + + li = (line_t) in.d(); + + if (!eval(li.flags & ML_TWOSIDED)) { + if (li.PointOnLineSide(slideMove.slidemo.x, slideMove.slidemo.y)) { + // don't hit the back side + return true; + } + return this.isblocking(in, li); + } + + // set openrange, opentop, openbottom + LineOpening(li); + + if ((ma.openrange < slideMove.slidemo.height) + || // doesn't fit + (ma.opentop - slideMove.slidemo.z < slideMove.slidemo.height) + || // mobj is too high + (ma.openbottom - slideMove.slidemo.z > 24 * FRACUNIT)) // too big a step up + { + if (in.frac < slideMove.bestslidefrac) { + slideMove.secondslidefrac = slideMove.bestslidefrac; + slideMove.secondslideline = slideMove.bestslideline; + slideMove.bestslidefrac = in.frac; + slideMove.bestslideline = li; + } + + return false; // stop + } else { // this line doesn't block movement + return true; + } + } +; +} \ No newline at end of file diff --git a/src/p/Actions/ActionsPathTraverse.java b/src/p/Actions/ActionsPathTraverse.java index 03b6660..456d10f 100644 --- a/src/p/Actions/ActionsPathTraverse.java +++ b/src/p/Actions/ActionsPathTraverse.java @@ -1,386 +1,386 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import static data.Defines.MAPBLOCKSHIFT; -import static data.Defines.MAPBLOCKSIZE; -import static data.Defines.MAPBTOFRAC; -import static data.Defines.PT_ADDLINES; -import static data.Defines.PT_ADDTHINGS; -import static data.Defines.PT_EARLYOUT; -import static data.Limits.MAXINT; -import static data.Limits.MAXINTERCEPTS; -import doom.SourceCode.P_MapUtl; -import static doom.SourceCode.P_MapUtl.P_PathTraverse; -import doom.SourceCode.fixed_t; -import java.util.function.Predicate; -import static m.fixed_t.FRACBITS; -import static m.fixed_t.FRACUNIT; -import static m.fixed_t.FixedDiv; -import static m.fixed_t.FixedMul; -import p.AbstractLevelLoader; -import static p.MapUtils.InterceptVector; -import p.divline_t; -import p.intercept_t; -import p.mobj_t; -import rr.line_t; -import utils.C2JUtils; -import static utils.C2JUtils.eval; -import static utils.GenericCopy.malloc; -import utils.TraitFactory.ContextKey; - -public interface ActionsPathTraverse extends ActionsSectors { - - ContextKey KEY_TRAVERSE = ACTION_KEY_CHAIN.newKey(ActionsPathTraverse.class, Traverse::new); - - final class Traverse { - //////////////// PIT FUNCTION OBJECTS /////////////////// - - // - // PIT_AddLineIntercepts. - // Looks for lines in the given block - // that intercept the given trace - // to add to the intercepts list. - // - // A line is crossed if its endpoints - // are on opposite sides of the trace. - // Returns true if earlyout and a solid line hit. - // - divline_t addLineDivLine = new divline_t(); - - // - // PIT_AddThingIntercepts - // - // maybe make this a shared instance variable? - divline_t thingInterceptDivLine = new divline_t(); - - boolean earlyout; - - int intercept_p; - - // - // INTERCEPT ROUTINES - // - intercept_t[] intercepts = malloc(intercept_t::new, intercept_t[]::new, MAXINTERCEPTS); - - void ResizeIntercepts() { - intercepts = C2JUtils.resize(intercepts[0], intercepts, intercepts.length * 2); - } - } - - /** - * P_PathTraverse Traces a line from x1,y1 to x2,y2, calling the traverser function for each. Returns true if the - * traverser function returns true for all lines. - */ - @Override - @P_MapUtl.C(P_PathTraverse) - default boolean PathTraverse(int x1, int y1, int x2, int y2, int flags, Predicate trav) { - final AbstractLevelLoader ll = levelLoader(); - final Spawn sp = contextRequire(KEY_SPAWN); - final Traverse tr = contextRequire(KEY_TRAVERSE); - - // System.out.println("Pathtraverse "+x1+" , " +y1+" to "+x2 +" , " - // +y2); - final int xt1, yt1; - final int xt2, yt2; - final long _x1, _x2, _y1, _y2; - final int mapx1, mapy1; - final int xstep, ystep; - - int partial; - - int xintercept, yintercept; - - int mapx; - int mapy; - - int mapxstep; - int mapystep; - - int count; - - tr.earlyout = eval(flags & PT_EARLYOUT); - - sceneRenderer().increaseValidCount(1); - tr.intercept_p = 0; - - if (((x1 - ll.bmaporgx) & (MAPBLOCKSIZE - 1)) == 0) { - x1 += FRACUNIT; // don't side exactly on a line - } - if (((y1 - ll.bmaporgy) & (MAPBLOCKSIZE - 1)) == 0) { - y1 += FRACUNIT; // don't side exactly on a line - } - sp.trace.x = x1; - sp.trace.y = y1; - sp.trace.dx = x2 - x1; - sp.trace.dy = y2 - y1; - - // Code developed in common with entryway - // for prBoom+ - _x1 = (long) x1 - ll.bmaporgx; - _y1 = (long) y1 - ll.bmaporgy; - xt1 = (int) (_x1 >> MAPBLOCKSHIFT); - yt1 = (int) (_y1 >> MAPBLOCKSHIFT); - - mapx1 = (int) (_x1 >> MAPBTOFRAC); - mapy1 = (int) (_y1 >> MAPBTOFRAC); - - _x2 = (long) x2 - ll.bmaporgx; - _y2 = (long) y2 - ll.bmaporgy; - xt2 = (int) (_x2 >> MAPBLOCKSHIFT); - yt2 = (int) (_y2 >> MAPBLOCKSHIFT); - - x1 -= ll.bmaporgx; - y1 -= ll.bmaporgy; - x2 -= ll.bmaporgx; - y2 -= ll.bmaporgy; - - if (xt2 > xt1) { - mapxstep = 1; - partial = FRACUNIT - (mapx1 & (FRACUNIT - 1)); - ystep = FixedDiv(y2 - y1, Math.abs(x2 - x1)); - } else if (xt2 < xt1) { - mapxstep = -1; - partial = mapx1 & (FRACUNIT - 1); - ystep = FixedDiv(y2 - y1, Math.abs(x2 - x1)); - } else { - mapxstep = 0; - partial = FRACUNIT; - ystep = 256 * FRACUNIT; - } - - yintercept = mapy1 + FixedMul(partial, ystep); - - if (yt2 > yt1) { - mapystep = 1; - partial = FRACUNIT - (mapy1 & (FRACUNIT - 1)); - xstep = FixedDiv(x2 - x1, Math.abs(y2 - y1)); - } else if (yt2 < yt1) { - mapystep = -1; - partial = mapy1 & (FRACUNIT - 1); - xstep = FixedDiv(x2 - x1, Math.abs(y2 - y1)); - } else { - mapystep = 0; - partial = FRACUNIT; - xstep = 256 * FRACUNIT; - } - xintercept = mapx1 + FixedMul(partial, xstep); - - // Step through map blocks. - // Count is present to prevent a round off error - // from skipping the break. - mapx = xt1; - mapy = yt1; - - for (count = 0; count < 64; count++) { - if (eval(flags & PT_ADDLINES)) { - if (!this.BlockLinesIterator(mapx, mapy, this::AddLineIntercepts)) { - return false; // early out - } - } - - if (eval(flags & PT_ADDTHINGS)) { - if (!this.BlockThingsIterator(mapx, mapy, this::AddThingIntercepts)) { - return false; // early out - } - } - - if (mapx == xt2 - && mapy == yt2) { - break; - } - - boolean changeX = (yintercept >> FRACBITS) == mapy; - boolean changeY = (xintercept >> FRACBITS) == mapx; - if (changeX) { - yintercept += ystep; - mapx += mapxstep; - } else //[MAES]: this fixed sync issues. Lookup linuxdoom - if (changeY) { - xintercept += xstep; - mapy += mapystep; - } - - } - // go through the sorted list - //System.out.println("Some intercepts found"); - return TraverseIntercept(trav, FRACUNIT); - } // end method - - default boolean AddLineIntercepts(line_t ld) { - final Spawn sp = contextRequire(KEY_SPAWN); - final Traverse tr = contextRequire(KEY_TRAVERSE); - - boolean s1; - boolean s2; - @fixed_t - int frac; - - // avoid precision problems with two routines - if (sp.trace.dx > FRACUNIT * 16 || sp.trace.dy > FRACUNIT * 16 - || sp.trace.dx < -FRACUNIT * 16 || sp.trace.dy < -FRACUNIT * 16) { - s1 = sp.trace.PointOnDivlineSide(ld.v1x, ld.v1y); - s2 = sp.trace.PointOnDivlineSide(ld.v2x, ld.v2y); - //s1 = obs.trace.DivlineSide(ld.v1x, ld.v1.y); - //s2 = obs.trace.DivlineSide(ld.v2x, ld.v2y); - } else { - s1 = ld.PointOnLineSide(sp.trace.x, sp.trace.y); - s2 = ld.PointOnLineSide(sp.trace.x + sp.trace.dx, sp.trace.y + sp.trace.dy); - //s1 = new divline_t(ld).DivlineSide(obs.trace.x, obs.trace.y); - //s2 = new divline_t(ld).DivlineSide(obs.trace.x + obs.trace.dx, obs.trace.y + obs.trace.dy); - } - - if (s1 == s2) { - return true; // line isn't crossed - } - // hit the line - tr.addLineDivLine.MakeDivline(ld); - frac = InterceptVector(sp.trace, tr.addLineDivLine); - - if (frac < 0) { - return true; // behind source - } - // try to early out the check - if (tr.earlyout && frac < FRACUNIT && ld.backsector == null) { - return false; // stop checking - } - - // "create" a new intercept in the static intercept pool. - if (tr.intercept_p >= tr.intercepts.length) { - tr.ResizeIntercepts(); - } - - tr.intercepts[tr.intercept_p].frac = frac; - tr.intercepts[tr.intercept_p].isaline = true; - tr.intercepts[tr.intercept_p].line = ld; - tr.intercept_p++; - - return true; // continue - } - - ; - - default boolean AddThingIntercepts(mobj_t thing) { - final Spawn sp = contextRequire(KEY_SPAWN); - final Traverse tr = contextRequire(KEY_TRAVERSE); - - @fixed_t - int x1, y1, x2, y2; - boolean s1, s2; - boolean tracepositive; - @fixed_t - int frac; - - tracepositive = (sp.trace.dx ^ sp.trace.dy) > 0; - - // check a corner to corner crossection for hit - if (tracepositive) { - x1 = thing.x - thing.radius; - y1 = thing.y + thing.radius; - - x2 = thing.x + thing.radius; - y2 = thing.y - thing.radius; - } else { - x1 = thing.x - thing.radius; - y1 = thing.y - thing.radius; - - x2 = thing.x + thing.radius; - y2 = thing.y + thing.radius; - } - - s1 = sp.trace.PointOnDivlineSide(x1, y1); - s2 = sp.trace.PointOnDivlineSide(x2, y2); - - if (s1 == s2) { - return true; // line isn't crossed - } - - tr.thingInterceptDivLine.x = x1; - tr.thingInterceptDivLine.y = y1; - tr.thingInterceptDivLine.dx = x2 - x1; - tr.thingInterceptDivLine.dy = y2 - y1; - - frac = InterceptVector(sp.trace, tr.thingInterceptDivLine); - - if (frac < 0) { - return true; // behind source - } - - // "create" a new intercept in the static intercept pool. - if (tr.intercept_p >= tr.intercepts.length) { - tr.ResizeIntercepts(); - } - - tr.intercepts[tr.intercept_p].frac = frac; - tr.intercepts[tr.intercept_p].isaline = false; - tr.intercepts[tr.intercept_p].thing = thing; - tr.intercept_p++; - - return true; // keep going - } - - ; - - // - //P_TraverseIntercepts - //Returns true if the traverser function returns true - //for all lines. - // - default boolean TraverseIntercept(Predicate func, int maxfrac) { - final Traverse tr = contextRequire(KEY_TRAVERSE); - - int count; - @fixed_t - int dist; - intercept_t in = null; // shut up compiler warning - - count = tr.intercept_p; - - while (count-- > 0) { - dist = MAXINT; - for (int scan = 0; scan < tr.intercept_p; scan++) { - if (tr.intercepts[scan].frac < dist) { - dist = tr.intercepts[scan].frac; - in = tr.intercepts[scan]; - } - } - - if (dist > maxfrac) { - return true; // checked everything in range - } - /* // UNUSED - { - // don't check these yet, there may be others inserted - in = scan = intercepts; - for ( scan = intercepts ; scan maxfrac) - *in++ = *scan; - intercept_p = in; - return false; - } - */ - - if (!func.test(in)) { - return false; // don't bother going farther - } - in.frac = MAXINT; - } - - return true; // everything was traversed - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import static data.Defines.MAPBLOCKSHIFT; +import static data.Defines.MAPBLOCKSIZE; +import static data.Defines.MAPBTOFRAC; +import static data.Defines.PT_ADDLINES; +import static data.Defines.PT_ADDTHINGS; +import static data.Defines.PT_EARLYOUT; +import static data.Limits.MAXINT; +import static data.Limits.MAXINTERCEPTS; +import doom.SourceCode.P_MapUtl; +import static doom.SourceCode.P_MapUtl.P_PathTraverse; +import doom.SourceCode.fixed_t; +import java.util.function.Predicate; +import static m.fixed_t.FRACBITS; +import static m.fixed_t.FRACUNIT; +import static m.fixed_t.FixedDiv; +import static m.fixed_t.FixedMul; +import p.AbstractLevelLoader; +import static p.MapUtils.InterceptVector; +import p.divline_t; +import p.intercept_t; +import p.mobj_t; +import rr.line_t; +import utils.C2JUtils; +import static utils.C2JUtils.eval; +import static utils.GenericCopy.malloc; +import utils.TraitFactory.ContextKey; + +public interface ActionsPathTraverse extends ActionsSectors { + + ContextKey KEY_TRAVERSE = ACTION_KEY_CHAIN.newKey(ActionsPathTraverse.class, Traverse::new); + + final class Traverse { + //////////////// PIT FUNCTION OBJECTS /////////////////// + + // + // PIT_AddLineIntercepts. + // Looks for lines in the given block + // that intercept the given trace + // to add to the intercepts list. + // + // A line is crossed if its endpoints + // are on opposite sides of the trace. + // Returns true if earlyout and a solid line hit. + // + divline_t addLineDivLine = new divline_t(); + + // + // PIT_AddThingIntercepts + // + // maybe make this a shared instance variable? + divline_t thingInterceptDivLine = new divline_t(); + + boolean earlyout; + + int intercept_p; + + // + // INTERCEPT ROUTINES + // + intercept_t[] intercepts = malloc(intercept_t::new, intercept_t[]::new, MAXINTERCEPTS); + + void ResizeIntercepts() { + intercepts = C2JUtils.resize(intercepts[0], intercepts, intercepts.length * 2); + } + } + + /** + * P_PathTraverse Traces a line from x1,y1 to x2,y2, calling the traverser function for each. Returns true if the + * traverser function returns true for all lines. + */ + @Override + @P_MapUtl.C(P_PathTraverse) + default boolean PathTraverse(int x1, int y1, int x2, int y2, int flags, Predicate trav) { + final AbstractLevelLoader ll = levelLoader(); + final Spawn sp = contextRequire(KEY_SPAWN); + final Traverse tr = contextRequire(KEY_TRAVERSE); + + // System.out.println("Pathtraverse "+x1+" , " +y1+" to "+x2 +" , " + // +y2); + final int xt1, yt1; + final int xt2, yt2; + final long _x1, _x2, _y1, _y2; + final int mapx1, mapy1; + final int xstep, ystep; + + int partial; + + int xintercept, yintercept; + + int mapx; + int mapy; + + int mapxstep; + int mapystep; + + int count; + + tr.earlyout = eval(flags & PT_EARLYOUT); + + sceneRenderer().increaseValidCount(1); + tr.intercept_p = 0; + + if (((x1 - ll.bmaporgx) & (MAPBLOCKSIZE - 1)) == 0) { + x1 += FRACUNIT; // don't side exactly on a line + } + if (((y1 - ll.bmaporgy) & (MAPBLOCKSIZE - 1)) == 0) { + y1 += FRACUNIT; // don't side exactly on a line + } + sp.trace.x = x1; + sp.trace.y = y1; + sp.trace.dx = x2 - x1; + sp.trace.dy = y2 - y1; + + // Code developed in common with entryway + // for prBoom+ + _x1 = (long) x1 - ll.bmaporgx; + _y1 = (long) y1 - ll.bmaporgy; + xt1 = (int) (_x1 >> MAPBLOCKSHIFT); + yt1 = (int) (_y1 >> MAPBLOCKSHIFT); + + mapx1 = (int) (_x1 >> MAPBTOFRAC); + mapy1 = (int) (_y1 >> MAPBTOFRAC); + + _x2 = (long) x2 - ll.bmaporgx; + _y2 = (long) y2 - ll.bmaporgy; + xt2 = (int) (_x2 >> MAPBLOCKSHIFT); + yt2 = (int) (_y2 >> MAPBLOCKSHIFT); + + x1 -= ll.bmaporgx; + y1 -= ll.bmaporgy; + x2 -= ll.bmaporgx; + y2 -= ll.bmaporgy; + + if (xt2 > xt1) { + mapxstep = 1; + partial = FRACUNIT - (mapx1 & (FRACUNIT - 1)); + ystep = FixedDiv(y2 - y1, Math.abs(x2 - x1)); + } else if (xt2 < xt1) { + mapxstep = -1; + partial = mapx1 & (FRACUNIT - 1); + ystep = FixedDiv(y2 - y1, Math.abs(x2 - x1)); + } else { + mapxstep = 0; + partial = FRACUNIT; + ystep = 256 * FRACUNIT; + } + + yintercept = mapy1 + FixedMul(partial, ystep); + + if (yt2 > yt1) { + mapystep = 1; + partial = FRACUNIT - (mapy1 & (FRACUNIT - 1)); + xstep = FixedDiv(x2 - x1, Math.abs(y2 - y1)); + } else if (yt2 < yt1) { + mapystep = -1; + partial = mapy1 & (FRACUNIT - 1); + xstep = FixedDiv(x2 - x1, Math.abs(y2 - y1)); + } else { + mapystep = 0; + partial = FRACUNIT; + xstep = 256 * FRACUNIT; + } + xintercept = mapx1 + FixedMul(partial, xstep); + + // Step through map blocks. + // Count is present to prevent a round off error + // from skipping the break. + mapx = xt1; + mapy = yt1; + + for (count = 0; count < 64; count++) { + if (eval(flags & PT_ADDLINES)) { + if (!this.BlockLinesIterator(mapx, mapy, this::AddLineIntercepts)) { + return false; // early out + } + } + + if (eval(flags & PT_ADDTHINGS)) { + if (!this.BlockThingsIterator(mapx, mapy, this::AddThingIntercepts)) { + return false; // early out + } + } + + if (mapx == xt2 + && mapy == yt2) { + break; + } + + boolean changeX = (yintercept >> FRACBITS) == mapy; + boolean changeY = (xintercept >> FRACBITS) == mapx; + if (changeX) { + yintercept += ystep; + mapx += mapxstep; + } else //[MAES]: this fixed sync issues. Lookup linuxdoom + if (changeY) { + xintercept += xstep; + mapy += mapystep; + } + + } + // go through the sorted list + //System.out.println("Some intercepts found"); + return TraverseIntercept(trav, FRACUNIT); + } // end method + + default boolean AddLineIntercepts(line_t ld) { + final Spawn sp = contextRequire(KEY_SPAWN); + final Traverse tr = contextRequire(KEY_TRAVERSE); + + boolean s1; + boolean s2; + @fixed_t + int frac; + + // avoid precision problems with two routines + if (sp.trace.dx > FRACUNIT * 16 || sp.trace.dy > FRACUNIT * 16 + || sp.trace.dx < -FRACUNIT * 16 || sp.trace.dy < -FRACUNIT * 16) { + s1 = sp.trace.PointOnDivlineSide(ld.v1x, ld.v1y); + s2 = sp.trace.PointOnDivlineSide(ld.v2x, ld.v2y); + //s1 = obs.trace.DivlineSide(ld.v1x, ld.v1.y); + //s2 = obs.trace.DivlineSide(ld.v2x, ld.v2y); + } else { + s1 = ld.PointOnLineSide(sp.trace.x, sp.trace.y); + s2 = ld.PointOnLineSide(sp.trace.x + sp.trace.dx, sp.trace.y + sp.trace.dy); + //s1 = new divline_t(ld).DivlineSide(obs.trace.x, obs.trace.y); + //s2 = new divline_t(ld).DivlineSide(obs.trace.x + obs.trace.dx, obs.trace.y + obs.trace.dy); + } + + if (s1 == s2) { + return true; // line isn't crossed + } + // hit the line + tr.addLineDivLine.MakeDivline(ld); + frac = InterceptVector(sp.trace, tr.addLineDivLine); + + if (frac < 0) { + return true; // behind source + } + // try to early out the check + if (tr.earlyout && frac < FRACUNIT && ld.backsector == null) { + return false; // stop checking + } + + // "create" a new intercept in the static intercept pool. + if (tr.intercept_p >= tr.intercepts.length) { + tr.ResizeIntercepts(); + } + + tr.intercepts[tr.intercept_p].frac = frac; + tr.intercepts[tr.intercept_p].isaline = true; + tr.intercepts[tr.intercept_p].line = ld; + tr.intercept_p++; + + return true; // continue + } + + ; + + default boolean AddThingIntercepts(mobj_t thing) { + final Spawn sp = contextRequire(KEY_SPAWN); + final Traverse tr = contextRequire(KEY_TRAVERSE); + + @fixed_t + int x1, y1, x2, y2; + boolean s1, s2; + boolean tracepositive; + @fixed_t + int frac; + + tracepositive = (sp.trace.dx ^ sp.trace.dy) > 0; + + // check a corner to corner crossection for hit + if (tracepositive) { + x1 = thing.x - thing.radius; + y1 = thing.y + thing.radius; + + x2 = thing.x + thing.radius; + y2 = thing.y - thing.radius; + } else { + x1 = thing.x - thing.radius; + y1 = thing.y - thing.radius; + + x2 = thing.x + thing.radius; + y2 = thing.y + thing.radius; + } + + s1 = sp.trace.PointOnDivlineSide(x1, y1); + s2 = sp.trace.PointOnDivlineSide(x2, y2); + + if (s1 == s2) { + return true; // line isn't crossed + } + + tr.thingInterceptDivLine.x = x1; + tr.thingInterceptDivLine.y = y1; + tr.thingInterceptDivLine.dx = x2 - x1; + tr.thingInterceptDivLine.dy = y2 - y1; + + frac = InterceptVector(sp.trace, tr.thingInterceptDivLine); + + if (frac < 0) { + return true; // behind source + } + + // "create" a new intercept in the static intercept pool. + if (tr.intercept_p >= tr.intercepts.length) { + tr.ResizeIntercepts(); + } + + tr.intercepts[tr.intercept_p].frac = frac; + tr.intercepts[tr.intercept_p].isaline = false; + tr.intercepts[tr.intercept_p].thing = thing; + tr.intercept_p++; + + return true; // keep going + } + + ; + + // + //P_TraverseIntercepts + //Returns true if the traverser function returns true + //for all lines. + // + default boolean TraverseIntercept(Predicate func, int maxfrac) { + final Traverse tr = contextRequire(KEY_TRAVERSE); + + int count; + @fixed_t + int dist; + intercept_t in = null; // shut up compiler warning + + count = tr.intercept_p; + + while (count-- > 0) { + dist = MAXINT; + for (int scan = 0; scan < tr.intercept_p; scan++) { + if (tr.intercepts[scan].frac < dist) { + dist = tr.intercepts[scan].frac; + in = tr.intercepts[scan]; + } + } + + if (dist > maxfrac) { + return true; // checked everything in range + } + /* // UNUSED + { + // don't check these yet, there may be others inserted + in = scan = intercepts; + for ( scan = intercepts ; scan maxfrac) + *in++ = *scan; + intercept_p = in; + return false; + } + */ + + if (!func.test(in)) { + return false; // don't bother going farther + } + in.frac = MAXINT; + } + + return true; // everything was traversed + } +} \ No newline at end of file diff --git a/src/p/Actions/ActionsPlats.java b/src/p/Actions/ActionsPlats.java index feb7697..e479166 100644 --- a/src/p/Actions/ActionsPlats.java +++ b/src/p/Actions/ActionsPlats.java @@ -1,252 +1,252 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import static data.Limits.MAXPLATS; -import static data.Limits.PLATSPEED; -import static data.Limits.PLATWAIT; -import data.sounds; -import doom.thinker_t; -import java.util.logging.Level; -import java.util.logging.Logger; -import m.Settings; -import static m.fixed_t.FRACUNIT; -import mochadoom.Engine; -import mochadoom.Loggers; -import p.AbstractLevelLoader; -import static p.ActiveStates.NOP; -import static p.ActiveStates.T_PlatRaise; -import p.plat_e; -import p.plat_t; -import p.plattype_e; -import rr.line_t; -import rr.sector_t; -import utils.C2JUtils; -import utils.TraitFactory.ContextKey; - -public interface ActionsPlats extends ActionsMoveEvents, ActionsUseEvents { - - ContextKey KEY_PLATS = ACTION_KEY_CHAIN.newKey(ActionsPlats.class, Plats::new); - - int FindSectorFromLineTag(line_t line, int secnum); - - void RemoveThinker(thinker_t activeplat); - - final class Plats { - - static final Logger LOGGER = Loggers.getLogger(ActionsPlats.class.getName()); - - // activeplats is just a placeholder. Plat objects aren't - // actually reused, so we don't need an initialized array. - // Same rule when resizing. - plat_t[] activeplats = new plat_t[MAXPLATS]; - } - - // - // Do Platforms - // "amount" is only used for SOME platforms. - // - @Override - default boolean DoPlat(line_t line, plattype_e type, int amount) { - final AbstractLevelLoader ll = levelLoader(); - - plat_t plat; - int secnum = -1; - boolean rtn = false; - sector_t sec; - - // Activate all plats that are in_stasis - switch (type) { - case perpetualRaise: - ActivateInStasis(line.tag); - break; - - default: - break; - } - - while ((secnum = FindSectorFromLineTag(line, secnum)) >= 0) { - sec = ll.sectors[secnum]; - - if (sec.specialdata != null) { - continue; - } - - // Find lowest & highest floors around sector - rtn = true; - plat = new plat_t(); - - plat.type = type; - plat.sector = sec; - plat.sector.specialdata = plat; - plat.thinkerFunction = T_PlatRaise; - AddThinker(plat); - plat.crush = false; - plat.tag = line.tag; - - switch (type) { - case raiseToNearestAndChange: - plat.speed = PLATSPEED / 2; - sec.floorpic = ll.sides[line.sidenum[0]].sector.floorpic; - plat.high = sec.FindNextHighestFloor(sec.floorheight); - plat.wait = 0; - plat.status = plat_e.up; - // NO MORE DAMAGE, IF APPLICABLE - sec.special = 0; - - StartSound(sec.soundorg, sounds.sfxenum_t.sfx_stnmov); - break; - - case raiseAndChange: - plat.speed = PLATSPEED / 2; - sec.floorpic = ll.sides[line.sidenum[0]].sector.floorpic; - plat.high = sec.floorheight + amount * FRACUNIT; - plat.wait = 0; - plat.status = plat_e.up; - - StartSound(sec.soundorg, sounds.sfxenum_t.sfx_stnmov); - break; - - case downWaitUpStay: - plat.speed = PLATSPEED * 4; - plat.low = sec.FindLowestFloorSurrounding(); - - if (plat.low > sec.floorheight) { - plat.low = sec.floorheight; - } - - plat.high = sec.floorheight; - plat.wait = 35 * PLATWAIT; - plat.status = plat_e.down; - StartSound(sec.soundorg, sounds.sfxenum_t.sfx_pstart); - break; - - case blazeDWUS: - plat.speed = PLATSPEED * 8; - plat.low = sec.FindLowestFloorSurrounding(); - - if (plat.low > sec.floorheight) { - plat.low = sec.floorheight; - } - - plat.high = sec.floorheight; - plat.wait = 35 * PLATWAIT; - plat.status = plat_e.down; - StartSound(sec.soundorg, sounds.sfxenum_t.sfx_pstart); - break; - - case perpetualRaise: - plat.speed = PLATSPEED; - plat.low = sec.FindLowestFloorSurrounding(); - - if (plat.low > sec.floorheight) { - plat.low = sec.floorheight; - } - - plat.high = sec.FindHighestFloorSurrounding(); - - if (plat.high < sec.floorheight) { - plat.high = sec.floorheight; - } - - plat.wait = 35 * PLATWAIT; - // Guaranteed to be 0 or 1. - plat.status = plat_e.values()[P_Random() & 1]; - - StartSound(sec.soundorg, sounds.sfxenum_t.sfx_pstart); - break; - } - AddActivePlat(plat); - } - return rtn; - } - - default void ActivateInStasis(int tag) { - final Plats plats = contextRequire(KEY_PLATS); - - for (final plat_t activeplat : plats.activeplats) { - if (activeplat != null && activeplat.tag == tag && activeplat.status == plat_e.in_stasis) { - activeplat.status = activeplat.oldstatus; - activeplat.thinkerFunction = T_PlatRaise; - } - } - } - - @Override - default void StopPlat(line_t line) { - final Plats plats = contextRequire(KEY_PLATS); - - for (final plat_t activeplat : plats.activeplats) { - if (activeplat != null && activeplat.status != plat_e.in_stasis && activeplat.tag == line.tag) { - activeplat.oldstatus = (activeplat).status; - activeplat.status = plat_e.in_stasis; - activeplat.thinkerFunction = NOP; - } - } - } - - default void AddActivePlat(plat_t plat) { - final Plats plats = contextRequire(KEY_PLATS); - - for (int i = 0; i < plats.activeplats.length; i++) { - if (plats.activeplats[i] == null) { - plats.activeplats[i] = plat; - return; - } - } - - /** - * Added option to turn off the resize - * - Good Sign 2017/04/26 - */ - // Uhh... lemme guess. Needs to resize? - // Resize but leave extra items empty. - if (Engine.getConfig().equals(Settings.extend_plats_limit, Boolean.TRUE)) { - plats.activeplats = C2JUtils.resizeNoAutoInit(plats.activeplats, 2 * plats.activeplats.length); - AddActivePlat(plat); - } else { - Plats.LOGGER.log(Level.SEVERE, "P_AddActivePlat: no more plats!"); - System.exit(1); - } - } - - default void RemoveActivePlat(plat_t plat) { - final Plats plats = contextRequire(KEY_PLATS); - - for (int i = 0; i < plats.activeplats.length; i++) { - if (plat == plats.activeplats[i]) { - (plats.activeplats[i]).sector.specialdata = null; - RemoveThinker(plats.activeplats[i]); - plats.activeplats[i] = null; - - return; - } - } - - Plats.LOGGER.log(Level.SEVERE, "P_RemoveActivePlat: can't find plat!"); - System.exit(1); - } - - default void ClearPlatsBeforeLoading() { - final Plats plats = contextRequire(KEY_PLATS); - - for (int i = 0; i < plats.activeplats.length; i++) { - plats.activeplats[i] = null; - } - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import static data.Limits.MAXPLATS; +import static data.Limits.PLATSPEED; +import static data.Limits.PLATWAIT; +import data.sounds; +import doom.thinker_t; +import java.util.logging.Level; +import java.util.logging.Logger; +import m.Settings; +import static m.fixed_t.FRACUNIT; +import mochadoom.Engine; +import mochadoom.Loggers; +import p.AbstractLevelLoader; +import static p.ActiveStates.NOP; +import static p.ActiveStates.T_PlatRaise; +import p.plat_e; +import p.plat_t; +import p.plattype_e; +import rr.line_t; +import rr.sector_t; +import utils.C2JUtils; +import utils.TraitFactory.ContextKey; + +public interface ActionsPlats extends ActionsMoveEvents, ActionsUseEvents { + + ContextKey KEY_PLATS = ACTION_KEY_CHAIN.newKey(ActionsPlats.class, Plats::new); + + int FindSectorFromLineTag(line_t line, int secnum); + + void RemoveThinker(thinker_t activeplat); + + final class Plats { + + static final Logger LOGGER = Loggers.getLogger(ActionsPlats.class.getName()); + + // activeplats is just a placeholder. Plat objects aren't + // actually reused, so we don't need an initialized array. + // Same rule when resizing. + plat_t[] activeplats = new plat_t[MAXPLATS]; + } + + // + // Do Platforms + // "amount" is only used for SOME platforms. + // + @Override + default boolean DoPlat(line_t line, plattype_e type, int amount) { + final AbstractLevelLoader ll = levelLoader(); + + plat_t plat; + int secnum = -1; + boolean rtn = false; + sector_t sec; + + // Activate all plats that are in_stasis + switch (type) { + case perpetualRaise: + ActivateInStasis(line.tag); + break; + + default: + break; + } + + while ((secnum = FindSectorFromLineTag(line, secnum)) >= 0) { + sec = ll.sectors[secnum]; + + if (sec.specialdata != null) { + continue; + } + + // Find lowest & highest floors around sector + rtn = true; + plat = new plat_t(); + + plat.type = type; + plat.sector = sec; + plat.sector.specialdata = plat; + plat.thinkerFunction = T_PlatRaise; + AddThinker(plat); + plat.crush = false; + plat.tag = line.tag; + + switch (type) { + case raiseToNearestAndChange: + plat.speed = PLATSPEED / 2; + sec.floorpic = ll.sides[line.sidenum[0]].sector.floorpic; + plat.high = sec.FindNextHighestFloor(sec.floorheight); + plat.wait = 0; + plat.status = plat_e.up; + // NO MORE DAMAGE, IF APPLICABLE + sec.special = 0; + + StartSound(sec.soundorg, sounds.sfxenum_t.sfx_stnmov); + break; + + case raiseAndChange: + plat.speed = PLATSPEED / 2; + sec.floorpic = ll.sides[line.sidenum[0]].sector.floorpic; + plat.high = sec.floorheight + amount * FRACUNIT; + plat.wait = 0; + plat.status = plat_e.up; + + StartSound(sec.soundorg, sounds.sfxenum_t.sfx_stnmov); + break; + + case downWaitUpStay: + plat.speed = PLATSPEED * 4; + plat.low = sec.FindLowestFloorSurrounding(); + + if (plat.low > sec.floorheight) { + plat.low = sec.floorheight; + } + + plat.high = sec.floorheight; + plat.wait = 35 * PLATWAIT; + plat.status = plat_e.down; + StartSound(sec.soundorg, sounds.sfxenum_t.sfx_pstart); + break; + + case blazeDWUS: + plat.speed = PLATSPEED * 8; + plat.low = sec.FindLowestFloorSurrounding(); + + if (plat.low > sec.floorheight) { + plat.low = sec.floorheight; + } + + plat.high = sec.floorheight; + plat.wait = 35 * PLATWAIT; + plat.status = plat_e.down; + StartSound(sec.soundorg, sounds.sfxenum_t.sfx_pstart); + break; + + case perpetualRaise: + plat.speed = PLATSPEED; + plat.low = sec.FindLowestFloorSurrounding(); + + if (plat.low > sec.floorheight) { + plat.low = sec.floorheight; + } + + plat.high = sec.FindHighestFloorSurrounding(); + + if (plat.high < sec.floorheight) { + plat.high = sec.floorheight; + } + + plat.wait = 35 * PLATWAIT; + // Guaranteed to be 0 or 1. + plat.status = plat_e.values()[P_Random() & 1]; + + StartSound(sec.soundorg, sounds.sfxenum_t.sfx_pstart); + break; + } + AddActivePlat(plat); + } + return rtn; + } + + default void ActivateInStasis(int tag) { + final Plats plats = contextRequire(KEY_PLATS); + + for (final plat_t activeplat : plats.activeplats) { + if (activeplat != null && activeplat.tag == tag && activeplat.status == plat_e.in_stasis) { + activeplat.status = activeplat.oldstatus; + activeplat.thinkerFunction = T_PlatRaise; + } + } + } + + @Override + default void StopPlat(line_t line) { + final Plats plats = contextRequire(KEY_PLATS); + + for (final plat_t activeplat : plats.activeplats) { + if (activeplat != null && activeplat.status != plat_e.in_stasis && activeplat.tag == line.tag) { + activeplat.oldstatus = (activeplat).status; + activeplat.status = plat_e.in_stasis; + activeplat.thinkerFunction = NOP; + } + } + } + + default void AddActivePlat(plat_t plat) { + final Plats plats = contextRequire(KEY_PLATS); + + for (int i = 0; i < plats.activeplats.length; i++) { + if (plats.activeplats[i] == null) { + plats.activeplats[i] = plat; + return; + } + } + + /** + * Added option to turn off the resize + * - Good Sign 2017/04/26 + */ + // Uhh... lemme guess. Needs to resize? + // Resize but leave extra items empty. + if (Engine.getConfig().equals(Settings.extend_plats_limit, Boolean.TRUE)) { + plats.activeplats = C2JUtils.resizeNoAutoInit(plats.activeplats, 2 * plats.activeplats.length); + AddActivePlat(plat); + } else { + Plats.LOGGER.log(Level.SEVERE, "P_AddActivePlat: no more plats!"); + System.exit(1); + } + } + + default void RemoveActivePlat(plat_t plat) { + final Plats plats = contextRequire(KEY_PLATS); + + for (int i = 0; i < plats.activeplats.length; i++) { + if (plat == plats.activeplats[i]) { + (plats.activeplats[i]).sector.specialdata = null; + RemoveThinker(plats.activeplats[i]); + plats.activeplats[i] = null; + + return; + } + } + + Plats.LOGGER.log(Level.SEVERE, "P_RemoveActivePlat: can't find plat!"); + System.exit(1); + } + + default void ClearPlatsBeforeLoading() { + final Plats plats = contextRequire(KEY_PLATS); + + for (int i = 0; i < plats.activeplats.length; i++) { + plats.activeplats[i] = null; + } + } +} \ No newline at end of file diff --git a/src/p/Actions/ActionsSectors.java b/src/p/Actions/ActionsSectors.java index 51fbcaa..0302533 100644 --- a/src/p/Actions/ActionsSectors.java +++ b/src/p/Actions/ActionsSectors.java @@ -1,475 +1,475 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import static data.Defines.ITEMQUESIZE; -import static data.Defines.MELEERANGE; -import data.mapthing_t; -import data.mobjtype_t; -import defines.statenum_t; -import doom.SourceCode.P_Map; -import static doom.SourceCode.P_Map.PIT_ChangeSector; -import doom.SourceCode.fixed_t; -import java.util.logging.Logger; -import static m.BBox.BOXBOTTOM; -import static m.BBox.BOXLEFT; -import static m.BBox.BOXRIGHT; -import static m.BBox.BOXTOP; -import mochadoom.Loggers; -import p.AbstractLevelLoader; -import p.ActiveStates; -import p.divline_t; -import p.floor_e; -import p.floormove_t; -import p.mobj_t; -import static p.mobj_t.MF_DROPPED; -import static p.mobj_t.MF_SHOOTABLE; -import static p.mobj_t.MF_SOLID; -import p.result_e; -import rr.line_t; -import static rr.line_t.ML_TWOSIDED; -import rr.sector_t; -import rr.side_t; -import static utils.C2JUtils.eval; -import utils.TraitFactory.ContextKey; - -public interface ActionsSectors extends ActionsLights, ActionsFloors, ActionsDoors, ActionsCeilings, ActionsSlideDoors { - - ContextKey KEY_RESP_QUEUE = ACTION_KEY_CHAIN.newKey(ActionsSectors.class, RespawnQueue::new); - ContextKey KEY_SPAWN = ACTION_KEY_CHAIN.newKey(ActionsSectors.class, Spawn::new); - ContextKey KEY_CRUSHES = ACTION_KEY_CHAIN.newKey(ActionsSectors.class, Crushes::new); - - void RemoveMobj(mobj_t thing); - - void DamageMobj(mobj_t thing, mobj_t tmthing, mobj_t tmthing0, int damage); - - mobj_t SpawnMobj(@fixed_t int x, @fixed_t int y, @fixed_t int z, mobjtype_t type); - - final class Crushes { - - boolean crushchange; - boolean nofit; - } - - final class RespawnQueue { - - // - // P_RemoveMobj - // - mapthing_t[] itemrespawnque = new mapthing_t[ITEMQUESIZE]; - int[] itemrespawntime = new int[ITEMQUESIZE]; - int iquehead; - int iquetail; - } - - final class Spawn { - - final static Logger LOGGER = Loggers.getLogger(ActionsSectors.class.getName()); - - /** - * who got hit (or NULL) - */ - public mobj_t linetarget; - - @fixed_t - public int attackrange; - - public mobj_t shootthing; - // Height if not aiming up or down - // ???: use slope for monsters? - @fixed_t - public int shootz; - - public int la_damage; - - @fixed_t - public int aimslope; - - public divline_t trace = new divline_t(); - - public int topslope, bottomslope; // slopes to top and bottom of target - - // - // P_BulletSlope - // Sets a slope so a near miss is at aproximately - // the height of the intended target - // - public int bulletslope; - - boolean isMeleeRange() { - return attackrange == MELEERANGE; - } - } - - // - // P_ChangeSector - // - // - // SECTOR HEIGHT CHANGING - // After modifying a sectors floor or ceiling height, - // call this routine to adjust the positions - // of all things that touch the sector. - // - // If anything doesn't fit anymore, true will be returned. - // If crunch is true, they will take damage - // as they are being crushed. - // If Crunch is false, you should set the sector height back - // the way it was and call P_ChangeSector again - // to undo the changes. - // - default boolean ChangeSector(sector_t sector, boolean crunch) { - final Crushes cr = contextRequire(KEY_CRUSHES); - int x; - int y; - - cr.nofit = false; - cr.crushchange = crunch; - - // re-check heights for all things near the moving sector - for (x = sector.blockbox[BOXLEFT]; x <= sector.blockbox[BOXRIGHT]; x++) { - for (y = sector.blockbox[BOXBOTTOM]; y <= sector.blockbox[BOXTOP]; y++) { - this.BlockThingsIterator(x, y, this::ChangeSector); - } - } - - return cr.nofit; - } - - /** - * PIT_ChangeSector - */ - @P_Map.C(PIT_ChangeSector) - default boolean ChangeSector(mobj_t thing) { - final Crushes cr = contextRequire(KEY_CRUSHES); - mobj_t mo; - - if (ThingHeightClip(thing)) { - // keep checking - return true; - } - - // crunch bodies to giblets - if (thing.health <= 0) { - thing.SetMobjState(statenum_t.S_GIBS); - - thing.flags &= ~MF_SOLID; - thing.height = 0; - thing.radius = 0; - - // keep checking - return true; - } - - // crunch dropped items - if (eval(thing.flags & MF_DROPPED)) { - RemoveMobj(thing); - - // keep checking - return true; - } - - if (!eval(thing.flags & MF_SHOOTABLE)) { - // assume it is bloody gibs or something - return true; - } - - cr.nofit = true; - - if (cr.crushchange && !eval(LevelTime() & 3)) { - DamageMobj(thing, null, null, 10); - - // spray blood in a random direction - mo = SpawnMobj(thing.x, thing.y, thing.z + thing.height / 2, mobjtype_t.MT_BLOOD); - - mo.momx = (P_Random() - P_Random()) << 12; - mo.momy = (P_Random() - P_Random()) << 12; - } - - // keep checking (crush other things) - return true; - } - - ; - - /** - * Move a plane (floor or ceiling) and check for crushing - * - * @param sector - * @param speed fixed - * @param dest fixed - * @param crush - * @param floorOrCeiling - * @param direction - */ - @Override - default result_e MovePlane(sector_t sector, int speed, int dest, boolean crush, int floorOrCeiling, int direction) { - boolean flag; - @fixed_t - int lastpos; - - switch (floorOrCeiling) { - case 0: - // FLOOR - switch (direction) { - case -1: - // DOWN - if (sector.floorheight - speed < dest) { - lastpos = sector.floorheight; - sector.floorheight = dest; - flag = ChangeSector(sector, crush); - if (flag == true) { - sector.floorheight = lastpos; - ChangeSector(sector, crush); - //return crushed; - } - return result_e.pastdest; - } else { - lastpos = sector.floorheight; - sector.floorheight -= speed; - flag = ChangeSector(sector, crush); - if (flag == true) { - sector.floorheight = lastpos; - ChangeSector(sector, crush); - return result_e.crushed; - } - } - break; - - case 1: - // UP - if (sector.floorheight + speed > dest) { - lastpos = sector.floorheight; - sector.floorheight = dest; - flag = ChangeSector(sector, crush); - if (flag == true) { - sector.floorheight = lastpos; - ChangeSector(sector, crush); - //return crushed; - } - return result_e.pastdest; - } else { - // COULD GET CRUSHED - lastpos = sector.floorheight; - sector.floorheight += speed; - flag = ChangeSector(sector, crush); - if (flag == true) { - if (crush == true) { - return result_e.crushed; - } - sector.floorheight = lastpos; - ChangeSector(sector, crush); - return result_e.crushed; - } - } - break; - } - break; - - case 1: - // CEILING - switch (direction) { - case -1: - // DOWN - if (sector.ceilingheight - speed < dest) { - lastpos = sector.ceilingheight; - sector.ceilingheight = dest; - flag = ChangeSector(sector, crush); - - if (flag == true) { - sector.ceilingheight = lastpos; - ChangeSector(sector, crush); - //return crushed; - } - return result_e.pastdest; - } else { - // COULD GET CRUSHED - lastpos = sector.ceilingheight; - sector.ceilingheight -= speed; - flag = ChangeSector(sector, crush); - - if (flag == true) { - if (crush == true) { - return result_e.crushed; - } - sector.ceilingheight = lastpos; - ChangeSector(sector, crush); - return result_e.crushed; - } - } - break; - - case 1: - // UP - if (sector.ceilingheight + speed > dest) { - lastpos = sector.ceilingheight; - sector.ceilingheight = dest; - flag = ChangeSector(sector, crush); - if (flag == true) { - sector.ceilingheight = lastpos; - ChangeSector(sector, crush); - //return crushed; - } - return result_e.pastdest; - } else { - lastpos = sector.ceilingheight; - sector.ceilingheight += speed; - flag = ChangeSector(sector, crush); - // UNUSED - /* - if (flag == true) - { - sector.ceilingheight = lastpos; - P_ChangeSector(sector,crush); - return crushed; - } - */ - } - break; - } - break; - - } - return result_e.ok; - } - - /** - * Special Stuff that can not be categorized - * - * (I'm sure it has something to do with John Romero's obsession with fucking stuff and making them his bitches). - * - * @param line - * - */ - @Override - default boolean DoDonut(line_t line) { - sector_t s1; - sector_t s2; - sector_t s3; - int secnum; - boolean rtn; - int i; - floormove_t floor; - - secnum = -1; - rtn = false; - while ((secnum = FindSectorFromLineTag(line, secnum)) >= 0) { - s1 = levelLoader().sectors[secnum]; - - // ALREADY MOVING? IF SO, KEEP GOING... - if (s1.specialdata != null) { - continue; - } - - rtn = true; - s2 = s1.lines[0].getNextSector(s1); - for (i = 0; i < s2.linecount; i++) { - if ((!eval(s2.lines[i].flags & ML_TWOSIDED)) || (s2.lines[i].backsector == s1)) { - continue; - } - s3 = s2.lines[i].backsector; - - // Spawn rising slime - floor = new floormove_t(); - s2.specialdata = floor; - floor.thinkerFunction = ActiveStates.T_MoveFloor; - AddThinker(floor); - floor.type = floor_e.donutRaise; - floor.crush = false; - floor.direction = 1; - floor.sector = s2; - floor.speed = FLOORSPEED / 2; - floor.texture = s3.floorpic; - floor.newspecial = 0; - floor.floordestheight = s3.floorheight; - - // Spawn lowering donut-hole - floor = new floormove_t(); - s1.specialdata = floor; - floor.thinkerFunction = ActiveStates.T_MoveFloor; - AddThinker(floor); - floor.type = floor_e.lowerFloor; - floor.crush = false; - floor.direction = -1; - floor.sector = s1; - floor.speed = FLOORSPEED / 2; - floor.floordestheight = s3.floorheight; - break; - } - } - return rtn; - } - - /** - * RETURN NEXT SECTOR # THAT LINE TAG REFERS TO - */ - @Override - default int FindSectorFromLineTag(line_t line, int start) { - final AbstractLevelLoader ll = levelLoader(); - - for (int i = start + 1; i < ll.numsectors; i++) { - if (ll.sectors[i].tag == line.tag) { - return i; - } - } - - return -1; - } - - // - // UTILITIES - // - // - // getSide() - // Will return a side_t* - // given the number of the current sector, - // the line number, and the side (0/1) that you want. - // - @Override - default side_t getSide(int currentSector, int line, int side) { - final AbstractLevelLoader ll = levelLoader(); - return ll.sides[(ll.sectors[currentSector].lines[line]).sidenum[side]]; - } - - /** - * getSector() - * Will return a sector_t - * given the number of the current sector, - * the line number and the side (0/1) that you want. - */ - @Override - default sector_t getSector(int currentSector, int line, int side) { - final AbstractLevelLoader ll = levelLoader(); - return ll.sides[(ll.sectors[currentSector].lines[line]).sidenum[side]].sector; - } - - /** - * twoSided() - * Given the sector number and the line number, - * it will tell you whether the line is two-sided or not. - */ - @Override - default boolean twoSided(int sector, int line) { - return eval((levelLoader().sectors[sector].lines[line]).flags & ML_TWOSIDED); - } - - default void ClearRespawnQueue() { - // clear special respawning que - final RespawnQueue rq = contextRequire(KEY_RESP_QUEUE); - rq.iquehead = rq.iquetail = 0; - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import static data.Defines.ITEMQUESIZE; +import static data.Defines.MELEERANGE; +import data.mapthing_t; +import data.mobjtype_t; +import defines.statenum_t; +import doom.SourceCode.P_Map; +import static doom.SourceCode.P_Map.PIT_ChangeSector; +import doom.SourceCode.fixed_t; +import java.util.logging.Logger; +import static m.BBox.BOXBOTTOM; +import static m.BBox.BOXLEFT; +import static m.BBox.BOXRIGHT; +import static m.BBox.BOXTOP; +import mochadoom.Loggers; +import p.AbstractLevelLoader; +import p.ActiveStates; +import p.divline_t; +import p.floor_e; +import p.floormove_t; +import p.mobj_t; +import static p.mobj_t.MF_DROPPED; +import static p.mobj_t.MF_SHOOTABLE; +import static p.mobj_t.MF_SOLID; +import p.result_e; +import rr.line_t; +import static rr.line_t.ML_TWOSIDED; +import rr.sector_t; +import rr.side_t; +import static utils.C2JUtils.eval; +import utils.TraitFactory.ContextKey; + +public interface ActionsSectors extends ActionsLights, ActionsFloors, ActionsDoors, ActionsCeilings, ActionsSlideDoors { + + ContextKey KEY_RESP_QUEUE = ACTION_KEY_CHAIN.newKey(ActionsSectors.class, RespawnQueue::new); + ContextKey KEY_SPAWN = ACTION_KEY_CHAIN.newKey(ActionsSectors.class, Spawn::new); + ContextKey KEY_CRUSHES = ACTION_KEY_CHAIN.newKey(ActionsSectors.class, Crushes::new); + + void RemoveMobj(mobj_t thing); + + void DamageMobj(mobj_t thing, mobj_t tmthing, mobj_t tmthing0, int damage); + + mobj_t SpawnMobj(@fixed_t int x, @fixed_t int y, @fixed_t int z, mobjtype_t type); + + final class Crushes { + + boolean crushchange; + boolean nofit; + } + + final class RespawnQueue { + + // + // P_RemoveMobj + // + mapthing_t[] itemrespawnque = new mapthing_t[ITEMQUESIZE]; + int[] itemrespawntime = new int[ITEMQUESIZE]; + int iquehead; + int iquetail; + } + + final class Spawn { + + final static Logger LOGGER = Loggers.getLogger(ActionsSectors.class.getName()); + + /** + * who got hit (or NULL) + */ + public mobj_t linetarget; + + @fixed_t + public int attackrange; + + public mobj_t shootthing; + // Height if not aiming up or down + // ???: use slope for monsters? + @fixed_t + public int shootz; + + public int la_damage; + + @fixed_t + public int aimslope; + + public divline_t trace = new divline_t(); + + public int topslope, bottomslope; // slopes to top and bottom of target + + // + // P_BulletSlope + // Sets a slope so a near miss is at aproximately + // the height of the intended target + // + public int bulletslope; + + boolean isMeleeRange() { + return attackrange == MELEERANGE; + } + } + + // + // P_ChangeSector + // + // + // SECTOR HEIGHT CHANGING + // After modifying a sectors floor or ceiling height, + // call this routine to adjust the positions + // of all things that touch the sector. + // + // If anything doesn't fit anymore, true will be returned. + // If crunch is true, they will take damage + // as they are being crushed. + // If Crunch is false, you should set the sector height back + // the way it was and call P_ChangeSector again + // to undo the changes. + // + default boolean ChangeSector(sector_t sector, boolean crunch) { + final Crushes cr = contextRequire(KEY_CRUSHES); + int x; + int y; + + cr.nofit = false; + cr.crushchange = crunch; + + // re-check heights for all things near the moving sector + for (x = sector.blockbox[BOXLEFT]; x <= sector.blockbox[BOXRIGHT]; x++) { + for (y = sector.blockbox[BOXBOTTOM]; y <= sector.blockbox[BOXTOP]; y++) { + this.BlockThingsIterator(x, y, this::ChangeSector); + } + } + + return cr.nofit; + } + + /** + * PIT_ChangeSector + */ + @P_Map.C(PIT_ChangeSector) + default boolean ChangeSector(mobj_t thing) { + final Crushes cr = contextRequire(KEY_CRUSHES); + mobj_t mo; + + if (ThingHeightClip(thing)) { + // keep checking + return true; + } + + // crunch bodies to giblets + if (thing.health <= 0) { + thing.SetMobjState(statenum_t.S_GIBS); + + thing.flags &= ~MF_SOLID; + thing.height = 0; + thing.radius = 0; + + // keep checking + return true; + } + + // crunch dropped items + if (eval(thing.flags & MF_DROPPED)) { + RemoveMobj(thing); + + // keep checking + return true; + } + + if (!eval(thing.flags & MF_SHOOTABLE)) { + // assume it is bloody gibs or something + return true; + } + + cr.nofit = true; + + if (cr.crushchange && !eval(LevelTime() & 3)) { + DamageMobj(thing, null, null, 10); + + // spray blood in a random direction + mo = SpawnMobj(thing.x, thing.y, thing.z + thing.height / 2, mobjtype_t.MT_BLOOD); + + mo.momx = (P_Random() - P_Random()) << 12; + mo.momy = (P_Random() - P_Random()) << 12; + } + + // keep checking (crush other things) + return true; + } + + ; + + /** + * Move a plane (floor or ceiling) and check for crushing + * + * @param sector + * @param speed fixed + * @param dest fixed + * @param crush + * @param floorOrCeiling + * @param direction + */ + @Override + default result_e MovePlane(sector_t sector, int speed, int dest, boolean crush, int floorOrCeiling, int direction) { + boolean flag; + @fixed_t + int lastpos; + + switch (floorOrCeiling) { + case 0: + // FLOOR + switch (direction) { + case -1: + // DOWN + if (sector.floorheight - speed < dest) { + lastpos = sector.floorheight; + sector.floorheight = dest; + flag = ChangeSector(sector, crush); + if (flag == true) { + sector.floorheight = lastpos; + ChangeSector(sector, crush); + //return crushed; + } + return result_e.pastdest; + } else { + lastpos = sector.floorheight; + sector.floorheight -= speed; + flag = ChangeSector(sector, crush); + if (flag == true) { + sector.floorheight = lastpos; + ChangeSector(sector, crush); + return result_e.crushed; + } + } + break; + + case 1: + // UP + if (sector.floorheight + speed > dest) { + lastpos = sector.floorheight; + sector.floorheight = dest; + flag = ChangeSector(sector, crush); + if (flag == true) { + sector.floorheight = lastpos; + ChangeSector(sector, crush); + //return crushed; + } + return result_e.pastdest; + } else { + // COULD GET CRUSHED + lastpos = sector.floorheight; + sector.floorheight += speed; + flag = ChangeSector(sector, crush); + if (flag == true) { + if (crush == true) { + return result_e.crushed; + } + sector.floorheight = lastpos; + ChangeSector(sector, crush); + return result_e.crushed; + } + } + break; + } + break; + + case 1: + // CEILING + switch (direction) { + case -1: + // DOWN + if (sector.ceilingheight - speed < dest) { + lastpos = sector.ceilingheight; + sector.ceilingheight = dest; + flag = ChangeSector(sector, crush); + + if (flag == true) { + sector.ceilingheight = lastpos; + ChangeSector(sector, crush); + //return crushed; + } + return result_e.pastdest; + } else { + // COULD GET CRUSHED + lastpos = sector.ceilingheight; + sector.ceilingheight -= speed; + flag = ChangeSector(sector, crush); + + if (flag == true) { + if (crush == true) { + return result_e.crushed; + } + sector.ceilingheight = lastpos; + ChangeSector(sector, crush); + return result_e.crushed; + } + } + break; + + case 1: + // UP + if (sector.ceilingheight + speed > dest) { + lastpos = sector.ceilingheight; + sector.ceilingheight = dest; + flag = ChangeSector(sector, crush); + if (flag == true) { + sector.ceilingheight = lastpos; + ChangeSector(sector, crush); + //return crushed; + } + return result_e.pastdest; + } else { + lastpos = sector.ceilingheight; + sector.ceilingheight += speed; + flag = ChangeSector(sector, crush); + // UNUSED + /* + if (flag == true) + { + sector.ceilingheight = lastpos; + P_ChangeSector(sector,crush); + return crushed; + } + */ + } + break; + } + break; + + } + return result_e.ok; + } + + /** + * Special Stuff that can not be categorized + * + * (I'm sure it has something to do with John Romero's obsession with fucking stuff and making them his bitches). + * + * @param line + * + */ + @Override + default boolean DoDonut(line_t line) { + sector_t s1; + sector_t s2; + sector_t s3; + int secnum; + boolean rtn; + int i; + floormove_t floor; + + secnum = -1; + rtn = false; + while ((secnum = FindSectorFromLineTag(line, secnum)) >= 0) { + s1 = levelLoader().sectors[secnum]; + + // ALREADY MOVING? IF SO, KEEP GOING... + if (s1.specialdata != null) { + continue; + } + + rtn = true; + s2 = s1.lines[0].getNextSector(s1); + for (i = 0; i < s2.linecount; i++) { + if ((!eval(s2.lines[i].flags & ML_TWOSIDED)) || (s2.lines[i].backsector == s1)) { + continue; + } + s3 = s2.lines[i].backsector; + + // Spawn rising slime + floor = new floormove_t(); + s2.specialdata = floor; + floor.thinkerFunction = ActiveStates.T_MoveFloor; + AddThinker(floor); + floor.type = floor_e.donutRaise; + floor.crush = false; + floor.direction = 1; + floor.sector = s2; + floor.speed = FLOORSPEED / 2; + floor.texture = s3.floorpic; + floor.newspecial = 0; + floor.floordestheight = s3.floorheight; + + // Spawn lowering donut-hole + floor = new floormove_t(); + s1.specialdata = floor; + floor.thinkerFunction = ActiveStates.T_MoveFloor; + AddThinker(floor); + floor.type = floor_e.lowerFloor; + floor.crush = false; + floor.direction = -1; + floor.sector = s1; + floor.speed = FLOORSPEED / 2; + floor.floordestheight = s3.floorheight; + break; + } + } + return rtn; + } + + /** + * RETURN NEXT SECTOR # THAT LINE TAG REFERS TO + */ + @Override + default int FindSectorFromLineTag(line_t line, int start) { + final AbstractLevelLoader ll = levelLoader(); + + for (int i = start + 1; i < ll.numsectors; i++) { + if (ll.sectors[i].tag == line.tag) { + return i; + } + } + + return -1; + } + + // + // UTILITIES + // + // + // getSide() + // Will return a side_t* + // given the number of the current sector, + // the line number, and the side (0/1) that you want. + // + @Override + default side_t getSide(int currentSector, int line, int side) { + final AbstractLevelLoader ll = levelLoader(); + return ll.sides[(ll.sectors[currentSector].lines[line]).sidenum[side]]; + } + + /** + * getSector() + * Will return a sector_t + * given the number of the current sector, + * the line number and the side (0/1) that you want. + */ + @Override + default sector_t getSector(int currentSector, int line, int side) { + final AbstractLevelLoader ll = levelLoader(); + return ll.sides[(ll.sectors[currentSector].lines[line]).sidenum[side]].sector; + } + + /** + * twoSided() + * Given the sector number and the line number, + * it will tell you whether the line is two-sided or not. + */ + @Override + default boolean twoSided(int sector, int line) { + return eval((levelLoader().sectors[sector].lines[line]).flags & ML_TWOSIDED); + } + + default void ClearRespawnQueue() { + // clear special respawning que + final RespawnQueue rq = contextRequire(KEY_RESP_QUEUE); + rq.iquehead = rq.iquetail = 0; + } +} \ No newline at end of file diff --git a/src/p/Actions/ActionsShootEvents.java b/src/p/Actions/ActionsShootEvents.java index b3c1cc9..f485dd7 100644 --- a/src/p/Actions/ActionsShootEvents.java +++ b/src/p/Actions/ActionsShootEvents.java @@ -1,104 +1,104 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import static m.fixed_t.FRACUNIT; -import static m.fixed_t.FixedDiv; -import static m.fixed_t.FixedMul; -import p.UnifiedGameMap.Switches; -import p.floor_e; -import p.intercept_t; -import p.mobj_t; -import p.plattype_e; -import p.vldoor_e; -import rr.line_t; - -public interface ActionsShootEvents extends ActionsSpawns { - - /** - * P_ShootSpecialLine - IMPACT SPECIALS Called when a thing shoots a special line. - */ - default void ShootSpecialLine(mobj_t thing, line_t line) { - final Switches sw = getSwitches(); - boolean ok; - - // Impacts that other things can activate. - if (thing.player == null) { - ok = false; - switch (line.special) { - case 46: - // OPEN DOOR IMPACT - ok = true; - break; - } - if (!ok) { - return; - } - } - - switch (line.special) { - case 24: - // RAISE FLOOR - DoFloor(line, floor_e.raiseFloor); - sw.ChangeSwitchTexture(line, false); - break; - - case 46: - // OPEN DOOR - DoDoor(line, vldoor_e.open); - sw.ChangeSwitchTexture(line, true); - break; - - case 47: - // RAISE FLOOR NEAR AND CHANGE - DoPlat(line, plattype_e.raiseToNearestAndChange, 0); - sw.ChangeSwitchTexture(line, false); - break; - } - } - - //_D_: NOTE: this function was added, because replacing a goto by a boolean flag caused a bug if shooting a single sided line - default boolean gotoHitLine(intercept_t in, line_t li) { - final Spawn targ = contextRequire(KEY_SPAWN); - int x, y, z, frac; - - // position a bit closer - frac = in.frac - FixedDiv(4 * FRACUNIT, targ.attackrange); - x = targ.trace.x + FixedMul(targ.trace.dx, frac); - y = targ.trace.y + FixedMul(targ.trace.dy, frac); - z = targ.shootz + FixedMul(targ.aimslope, FixedMul(frac, targ.attackrange)); - - if (li.frontsector.ceilingpic == DOOM().textureManager.getSkyFlatNum()) { - // don't shoot the sky! - if (z > li.frontsector.ceilingheight) { - return false; - } - - // it's a sky hack wall - if (li.backsector != null && li.backsector.ceilingpic == DOOM().textureManager.getSkyFlatNum()) { - return false; - } - } - - // Spawn bullet puffs. - this.SpawnPuff(x, y, z); - - // don't go any farther - return false; - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import static m.fixed_t.FRACUNIT; +import static m.fixed_t.FixedDiv; +import static m.fixed_t.FixedMul; +import p.UnifiedGameMap.Switches; +import p.floor_e; +import p.intercept_t; +import p.mobj_t; +import p.plattype_e; +import p.vldoor_e; +import rr.line_t; + +public interface ActionsShootEvents extends ActionsSpawns { + + /** + * P_ShootSpecialLine - IMPACT SPECIALS Called when a thing shoots a special line. + */ + default void ShootSpecialLine(mobj_t thing, line_t line) { + final Switches sw = getSwitches(); + boolean ok; + + // Impacts that other things can activate. + if (thing.player == null) { + ok = false; + switch (line.special) { + case 46: + // OPEN DOOR IMPACT + ok = true; + break; + } + if (!ok) { + return; + } + } + + switch (line.special) { + case 24: + // RAISE FLOOR + DoFloor(line, floor_e.raiseFloor); + sw.ChangeSwitchTexture(line, false); + break; + + case 46: + // OPEN DOOR + DoDoor(line, vldoor_e.open); + sw.ChangeSwitchTexture(line, true); + break; + + case 47: + // RAISE FLOOR NEAR AND CHANGE + DoPlat(line, plattype_e.raiseToNearestAndChange, 0); + sw.ChangeSwitchTexture(line, false); + break; + } + } + + //_D_: NOTE: this function was added, because replacing a goto by a boolean flag caused a bug if shooting a single sided line + default boolean gotoHitLine(intercept_t in, line_t li) { + final Spawn targ = contextRequire(KEY_SPAWN); + int x, y, z, frac; + + // position a bit closer + frac = in.frac - FixedDiv(4 * FRACUNIT, targ.attackrange); + x = targ.trace.x + FixedMul(targ.trace.dx, frac); + y = targ.trace.y + FixedMul(targ.trace.dy, frac); + z = targ.shootz + FixedMul(targ.aimslope, FixedMul(frac, targ.attackrange)); + + if (li.frontsector.ceilingpic == DOOM().textureManager.getSkyFlatNum()) { + // don't shoot the sky! + if (z > li.frontsector.ceilingheight) { + return false; + } + + // it's a sky hack wall + if (li.backsector != null && li.backsector.ceilingpic == DOOM().textureManager.getSkyFlatNum()) { + return false; + } + } + + // Spawn bullet puffs. + this.SpawnPuff(x, y, z); + + // don't go any farther + return false; + } +} \ No newline at end of file diff --git a/src/p/Actions/ActionsSight.java b/src/p/Actions/ActionsSight.java index 35f82db..59a0218 100644 --- a/src/p/Actions/ActionsSight.java +++ b/src/p/Actions/ActionsSight.java @@ -1,276 +1,276 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import static data.Defines.NF_SUBSECTOR; -import static data.Defines.RANGECHECK; -import doom.SourceCode.fixed_t; -import static m.fixed_t.FixedDiv; -import p.AbstractLevelLoader; -import p.MapUtils; -import p.divline_t; -import p.mobj_t; -import rr.SceneRenderer; -import rr.line_t; -import static rr.line_t.ML_TWOSIDED; -import rr.node_t; -import rr.sector_t; -import rr.subsector_t; -import static utils.C2JUtils.eval; -import static utils.C2JUtils.flags; -import utils.TraitFactory.ContextKey; - -public interface ActionsSight extends ActionsSectors { - - ContextKey KEY_SIGHT = ACTION_KEY_CHAIN.newKey(ActionsSight.class, Sight::new); - - class Sight { - - int sightzstart; // eye z of looker - divline_t strace = new divline_t(); - ; // from t1 to t2 - int t2x, t2y; - int[] sightcounts = new int[2]; - } - - /** - * P_CheckSight Returns true if a straight line between t1 and t2 is - * unobstructed. Uses REJECT. - */ - default boolean CheckSight(mobj_t t1, mobj_t t2) { - final AbstractLevelLoader ll = levelLoader(); - final Sight sight = contextRequire(KEY_SIGHT); - final Spawn spawn = contextRequire(KEY_SPAWN); - - int s1; - int s2; - int pnum; - int bytenum; - int bitnum; - - // First check for trivial rejection. - // Determine subsector entries in REJECT table. - s1 = t1.subsector.sector.id; // (t1.subsector.sector - sectors); - s2 = t2.subsector.sector.id;// - sectors); - pnum = s1 * ll.numsectors + s2; - bytenum = pnum >> 3; - bitnum = 1 << (pnum & 7); - - // Check in REJECT table. - if (eval(ll.rejectmatrix[bytenum] & bitnum)) { - sight.sightcounts[0]++; - - // can't possibly be connected - return false; - } - - // An unobstructed LOS is possible. - // Now look from eyes of t1 to any part of t2. - sight.sightcounts[1]++; - - sceneRenderer().increaseValidCount(1); - - sight.sightzstart = t1.z + t1.height - (t1.height >> 2); - spawn.topslope = (t2.z + t2.height) - sight.sightzstart; - spawn.bottomslope = (t2.z) - sight.sightzstart; - - sight.strace.x = t1.x; - sight.strace.y = t1.y; - sight.t2x = t2.x; - sight.t2y = t2.y; - sight.strace.dx = t2.x - t1.x; - sight.strace.dy = t2.y - t1.y; - - // the head node is the last node output - return CrossBSPNode(ll.numnodes - 1); - } - - /** - * P_CrossSubsector Returns true if strace crosses the given subsector - * successfully. - */ - default boolean CrossSubsector(int num) { - final SceneRenderer sr = sceneRenderer(); - final AbstractLevelLoader ll = levelLoader(); - final Spawn spawn = contextRequire(KEY_SPAWN); - final Sight sight = contextRequire(KEY_SIGHT); - - int seg; // pointer inside segs - line_t line; - int s1; - int s2; - int count; - subsector_t sub; - sector_t front; - sector_t back; - @fixed_t - int opentop; - int openbottom; - divline_t divl = new divline_t(); - //vertex_t v1; - //vertex_t v2; - @fixed_t - int frac; - int slope; - - if (RANGECHECK) { - if (num >= ll.numsubsectors) { - doomSystem().Error("P_CrossSubsector: ss %d with numss = %d", num, ll.numsubsectors); - } - } - - sub = ll.subsectors[num]; - - // check lines - count = sub.numlines; - seg = sub.firstline;// LL.segs[sub.firstline]; - - for (; count > 0; seg++, count--) { - line = ll.segs[seg].linedef; - - // allready checked other side? - if (line.validcount == sr.getValidCount()) { - continue; - } - - line.validcount = sr.getValidCount(); - - //v1 = line.v1; - //v2 = line.v2; - s1 = sight.strace.DivlineSide(line.v1x, line.v1y); - s2 = sight.strace.DivlineSide(line.v2x, line.v2y); - - // line isn't crossed? - if (s1 == s2) { - continue; - } - - divl.x = line.v1x; - divl.y = line.v1y; - divl.dx = line.v2x - line.v1x; - divl.dy = line.v2y - line.v1y; - s1 = divl.DivlineSide(sight.strace.x, sight.strace.y); - s2 = divl.DivlineSide(sight.t2x, sight.t2y); - - // line isn't crossed? - if (s1 == s2) { - continue; - } - - // stop because it is not two sided anyway - // might do this after updating validcount? - if (!flags(line.flags, ML_TWOSIDED)) { - return false; - } - - // crosses a two sided line - front = ll.segs[seg].frontsector; - back = ll.segs[seg].backsector; - - // no wall to block sight with? - if (front.floorheight == back.floorheight - && front.ceilingheight == back.ceilingheight) { - continue; - } - - // possible occluder - // because of ceiling height differences - if (front.ceilingheight < back.ceilingheight) { - opentop = front.ceilingheight; - } else { - opentop = back.ceilingheight; - } - - // because of ceiling height differences - if (front.floorheight > back.floorheight) { - openbottom = front.floorheight; - } else { - openbottom = back.floorheight; - } - - // quick test for totally closed doors - if (openbottom >= opentop) { - return false; // stop - } - - frac = MapUtils.P_InterceptVector(sight.strace, divl); - - if (front.floorheight != back.floorheight) { - slope = FixedDiv(openbottom - sight.sightzstart, frac); - if (slope > spawn.bottomslope) { - spawn.bottomslope = slope; - } - } - - if (front.ceilingheight != back.ceilingheight) { - slope = FixedDiv(opentop - sight.sightzstart, frac); - if (slope < spawn.topslope) { - spawn.topslope = slope; - } - } - - if (spawn.topslope <= spawn.bottomslope) { - return false; // stop - } - } - // passed the subsector ok - return true; - } - - /** - * P_CrossBSPNode Returns true if strace crosses the given node - * successfully. - */ - default boolean CrossBSPNode(int bspnum) { - final AbstractLevelLoader ll = levelLoader(); - final Sight sight = contextRequire(KEY_SIGHT); - - node_t bsp; - int side; - - if (eval(bspnum & NF_SUBSECTOR)) { - if (bspnum == -1) { - return CrossSubsector(0); - } else { - return CrossSubsector(bspnum & (~NF_SUBSECTOR)); - } - } - - bsp = ll.nodes[bspnum]; - - // decide which side the start point is on - side = bsp.DivlineSide(sight.strace.x, sight.strace.y); - if (side == 2) { - side = 0; // an "on" should cross both sides - } - - // cross the starting side - if (!CrossBSPNode(bsp.children[side])) { - return false; - } - - // the partition plane is crossed here - if (side == bsp.DivlineSide(sight.t2x, sight.t2y)) { - // the line doesn't touch the other side - return true; - } - - // cross the ending side - return CrossBSPNode(bsp.children[side ^ 1]); - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import static data.Defines.NF_SUBSECTOR; +import static data.Defines.RANGECHECK; +import doom.SourceCode.fixed_t; +import static m.fixed_t.FixedDiv; +import p.AbstractLevelLoader; +import p.MapUtils; +import p.divline_t; +import p.mobj_t; +import rr.SceneRenderer; +import rr.line_t; +import static rr.line_t.ML_TWOSIDED; +import rr.node_t; +import rr.sector_t; +import rr.subsector_t; +import static utils.C2JUtils.eval; +import static utils.C2JUtils.flags; +import utils.TraitFactory.ContextKey; + +public interface ActionsSight extends ActionsSectors { + + ContextKey KEY_SIGHT = ACTION_KEY_CHAIN.newKey(ActionsSight.class, Sight::new); + + class Sight { + + int sightzstart; // eye z of looker + divline_t strace = new divline_t(); + ; // from t1 to t2 + int t2x, t2y; + int[] sightcounts = new int[2]; + } + + /** + * P_CheckSight Returns true if a straight line between t1 and t2 is + * unobstructed. Uses REJECT. + */ + default boolean CheckSight(mobj_t t1, mobj_t t2) { + final AbstractLevelLoader ll = levelLoader(); + final Sight sight = contextRequire(KEY_SIGHT); + final Spawn spawn = contextRequire(KEY_SPAWN); + + int s1; + int s2; + int pnum; + int bytenum; + int bitnum; + + // First check for trivial rejection. + // Determine subsector entries in REJECT table. + s1 = t1.subsector.sector.id; // (t1.subsector.sector - sectors); + s2 = t2.subsector.sector.id;// - sectors); + pnum = s1 * ll.numsectors + s2; + bytenum = pnum >> 3; + bitnum = 1 << (pnum & 7); + + // Check in REJECT table. + if (eval(ll.rejectmatrix[bytenum] & bitnum)) { + sight.sightcounts[0]++; + + // can't possibly be connected + return false; + } + + // An unobstructed LOS is possible. + // Now look from eyes of t1 to any part of t2. + sight.sightcounts[1]++; + + sceneRenderer().increaseValidCount(1); + + sight.sightzstart = t1.z + t1.height - (t1.height >> 2); + spawn.topslope = (t2.z + t2.height) - sight.sightzstart; + spawn.bottomslope = (t2.z) - sight.sightzstart; + + sight.strace.x = t1.x; + sight.strace.y = t1.y; + sight.t2x = t2.x; + sight.t2y = t2.y; + sight.strace.dx = t2.x - t1.x; + sight.strace.dy = t2.y - t1.y; + + // the head node is the last node output + return CrossBSPNode(ll.numnodes - 1); + } + + /** + * P_CrossSubsector Returns true if strace crosses the given subsector + * successfully. + */ + default boolean CrossSubsector(int num) { + final SceneRenderer sr = sceneRenderer(); + final AbstractLevelLoader ll = levelLoader(); + final Spawn spawn = contextRequire(KEY_SPAWN); + final Sight sight = contextRequire(KEY_SIGHT); + + int seg; // pointer inside segs + line_t line; + int s1; + int s2; + int count; + subsector_t sub; + sector_t front; + sector_t back; + @fixed_t + int opentop; + int openbottom; + divline_t divl = new divline_t(); + //vertex_t v1; + //vertex_t v2; + @fixed_t + int frac; + int slope; + + if (RANGECHECK) { + if (num >= ll.numsubsectors) { + doomSystem().Error("P_CrossSubsector: ss %d with numss = %d", num, ll.numsubsectors); + } + } + + sub = ll.subsectors[num]; + + // check lines + count = sub.numlines; + seg = sub.firstline;// LL.segs[sub.firstline]; + + for (; count > 0; seg++, count--) { + line = ll.segs[seg].linedef; + + // allready checked other side? + if (line.validcount == sr.getValidCount()) { + continue; + } + + line.validcount = sr.getValidCount(); + + //v1 = line.v1; + //v2 = line.v2; + s1 = sight.strace.DivlineSide(line.v1x, line.v1y); + s2 = sight.strace.DivlineSide(line.v2x, line.v2y); + + // line isn't crossed? + if (s1 == s2) { + continue; + } + + divl.x = line.v1x; + divl.y = line.v1y; + divl.dx = line.v2x - line.v1x; + divl.dy = line.v2y - line.v1y; + s1 = divl.DivlineSide(sight.strace.x, sight.strace.y); + s2 = divl.DivlineSide(sight.t2x, sight.t2y); + + // line isn't crossed? + if (s1 == s2) { + continue; + } + + // stop because it is not two sided anyway + // might do this after updating validcount? + if (!flags(line.flags, ML_TWOSIDED)) { + return false; + } + + // crosses a two sided line + front = ll.segs[seg].frontsector; + back = ll.segs[seg].backsector; + + // no wall to block sight with? + if (front.floorheight == back.floorheight + && front.ceilingheight == back.ceilingheight) { + continue; + } + + // possible occluder + // because of ceiling height differences + if (front.ceilingheight < back.ceilingheight) { + opentop = front.ceilingheight; + } else { + opentop = back.ceilingheight; + } + + // because of ceiling height differences + if (front.floorheight > back.floorheight) { + openbottom = front.floorheight; + } else { + openbottom = back.floorheight; + } + + // quick test for totally closed doors + if (openbottom >= opentop) { + return false; // stop + } + + frac = MapUtils.P_InterceptVector(sight.strace, divl); + + if (front.floorheight != back.floorheight) { + slope = FixedDiv(openbottom - sight.sightzstart, frac); + if (slope > spawn.bottomslope) { + spawn.bottomslope = slope; + } + } + + if (front.ceilingheight != back.ceilingheight) { + slope = FixedDiv(opentop - sight.sightzstart, frac); + if (slope < spawn.topslope) { + spawn.topslope = slope; + } + } + + if (spawn.topslope <= spawn.bottomslope) { + return false; // stop + } + } + // passed the subsector ok + return true; + } + + /** + * P_CrossBSPNode Returns true if strace crosses the given node + * successfully. + */ + default boolean CrossBSPNode(int bspnum) { + final AbstractLevelLoader ll = levelLoader(); + final Sight sight = contextRequire(KEY_SIGHT); + + node_t bsp; + int side; + + if (eval(bspnum & NF_SUBSECTOR)) { + if (bspnum == -1) { + return CrossSubsector(0); + } else { + return CrossSubsector(bspnum & (~NF_SUBSECTOR)); + } + } + + bsp = ll.nodes[bspnum]; + + // decide which side the start point is on + side = bsp.DivlineSide(sight.strace.x, sight.strace.y); + if (side == 2) { + side = 0; // an "on" should cross both sides + } + + // cross the starting side + if (!CrossBSPNode(bsp.children[side])) { + return false; + } + + // the partition plane is crossed here + if (side == bsp.DivlineSide(sight.t2x, sight.t2y)) { + // the line doesn't touch the other side + return true; + } + + // cross the ending side + return CrossBSPNode(bsp.children[side ^ 1]); + } +} \ No newline at end of file diff --git a/src/p/Actions/ActionsSlideDoors.java b/src/p/Actions/ActionsSlideDoors.java index d1ede47..0e3665c 100644 --- a/src/p/Actions/ActionsSlideDoors.java +++ b/src/p/Actions/ActionsSlideDoors.java @@ -1,234 +1,234 @@ -package p.Actions; - -import doom.thinker_t; -import java.util.logging.Level; -import java.util.logging.Logger; -import mochadoom.Loggers; -import p.AbstractLevelLoader; -import static p.ActiveStates.T_SlidingDoor; -import p.mobj_t; -import p.sd_e; -import p.sdt_e; -import p.slidedoor_t; -import p.slideframe_t; -import p.slidename_t; -import rr.TextureManager; -import rr.line_t; -import static rr.line_t.ML_BLOCKING; -import rr.sector_t; -import static utils.GenericCopy.malloc; -import utils.TraitFactory.ContextKey; - -public interface ActionsSlideDoors extends ActionTrait { - - static final Logger LOGGER = Loggers.getLogger(ActionsSlideDoors.class.getName()); - - ContextKey KEY_SLIDEDOORS = ACTION_KEY_CHAIN.newKey(ActionsSlideDoors.class, SlideDoors::new); - - void RemoveThinker(thinker_t t); - - // UNUSED - // Separate into p_slidoor.c? - // ABANDONED TO THE MISTS OF TIME!!! - // - // EV_SlidingDoor : slide a door horizontally - // (animate midtexture, then set noblocking line) - // - int MAXSLIDEDOORS = 5; - // how many frames of animation - int SNUMFRAMES = 4; - - int SDOORWAIT = 35 * 3; - int SWAITTICS = 4; - - slidename_t[] slideFrameNames = { - new slidename_t( - "GDOORF1", "GDOORF2", "GDOORF3", "GDOORF4", // front - "GDOORB1", "GDOORB2", "GDOORB3", "GDOORB4" // back - ), - new slidename_t(), new slidename_t(), new slidename_t(), new slidename_t() - }; - - final class SlideDoors { - - slideframe_t[] slideFrames = malloc(slideframe_t::new, slideframe_t[]::new, MAXSLIDEDOORS); - } - - default void SlidingDoor(slidedoor_t door) { - final AbstractLevelLoader ll = levelLoader(); - final SlideDoors sd = contextRequire(KEY_SLIDEDOORS); - switch (door.status) { - case sd_opening: - if (door.timer-- == 0) { - if (++door.frame == ActionsSlideDoors.SNUMFRAMES) { - // IF DOOR IS DONE OPENING... - ll.sides[door.line.sidenum[0]].midtexture = 0; - ll.sides[door.line.sidenum[1]].midtexture = 0; - door.line.flags &= ML_BLOCKING ^ 0xff; - - if (door.type == sdt_e.sdt_openOnly) { - door.frontsector.specialdata = null; - RemoveThinker(door); - break; - } - - door.timer = ActionsSlideDoors.SDOORWAIT; - door.status = sd_e.sd_waiting; - } else { - // IF DOOR NEEDS TO ANIMATE TO NEXT FRAME... - door.timer = ActionsSlideDoors.SWAITTICS; - - ll.sides[door.line.sidenum[0]].midtexture = (short) sd.slideFrames[door.whichDoorIndex].frontFrames[door.frame]; - ll.sides[door.line.sidenum[1]].midtexture = (short) sd.slideFrames[door.whichDoorIndex].backFrames[door.frame]; - } - } - break; - - case sd_waiting: - // IF DOOR IS DONE WAITING... - if (door.timer-- == 0) { - // CAN DOOR CLOSE? - if (door.frontsector.thinglist != null - || door.backsector.thinglist != null) { - door.timer = ActionsSlideDoors.SDOORWAIT; - break; - } - - // door.frame = SNUMFRAMES-1; - door.status = sd_e.sd_closing; - door.timer = ActionsSlideDoors.SWAITTICS; - } - break; - - case sd_closing: - if (door.timer-- == 0) { - if (--door.frame < 0) { - // IF DOOR IS DONE CLOSING... - door.line.flags |= ML_BLOCKING; - door.frontsector.specialdata = null; - RemoveThinker(door); - break; - } else { - // IF DOOR NEEDS TO ANIMATE TO NEXT FRAME... - door.timer = ActionsSlideDoors.SWAITTICS; - - ll.sides[door.line.sidenum[0]].midtexture = (short) sd.slideFrames[door.whichDoorIndex].frontFrames[door.frame]; - ll.sides[door.line.sidenum[1]].midtexture = (short) sd.slideFrames[door.whichDoorIndex].backFrames[door.frame]; - } - } - break; - } - } - - default void P_InitSlidingDoorFrames() { - final TextureManager tm = DOOM().textureManager; - final SlideDoors sd = contextRequire(KEY_SLIDEDOORS); - - int i; - int f1; - int f2; - int f3; - int f4; - - // DOOM II ONLY... - if (!DOOM().isCommercial()) { - return; - } - - for (i = 0; i < MAXSLIDEDOORS; i++) { - if (slideFrameNames[i].frontFrame1 == null) { - break; - } - - f1 = tm.TextureNumForName(slideFrameNames[i].frontFrame1); - f2 = tm.TextureNumForName(slideFrameNames[i].frontFrame2); - f3 = tm.TextureNumForName(slideFrameNames[i].frontFrame3); - f4 = tm.TextureNumForName(slideFrameNames[i].frontFrame4); - - sd.slideFrames[i].frontFrames[0] = f1; - sd.slideFrames[i].frontFrames[1] = f2; - sd.slideFrames[i].frontFrames[2] = f3; - sd.slideFrames[i].frontFrames[3] = f4; - - f1 = tm.TextureNumForName(slideFrameNames[i].backFrame1); - f2 = tm.TextureNumForName(slideFrameNames[i].backFrame2); - f3 = tm.TextureNumForName(slideFrameNames[i].backFrame3); - f4 = tm.TextureNumForName(slideFrameNames[i].backFrame4); - - sd.slideFrames[i].backFrames[0] = f1; - sd.slideFrames[i].backFrames[1] = f2; - sd.slideFrames[i].backFrames[2] = f3; - sd.slideFrames[i].backFrames[3] = f4; - } - } - - // - // Return index into "slideFrames" array - // for which door type to use - // - default int P_FindSlidingDoorType(line_t line) { - final AbstractLevelLoader ll = levelLoader(); - final SlideDoors sd = contextRequire(KEY_SLIDEDOORS); - - for (int i = 0; i < MAXSLIDEDOORS; i++) { - int val = ll.sides[line.sidenum[0]].midtexture; - if (val == sd.slideFrames[i].frontFrames[0]) { - return i; - } - } - - return -1; - } - - default void EV_SlidingDoor(line_t line, mobj_t thing) { - sector_t sec; - slidedoor_t door; - - // DOOM II ONLY... - if (!DOOM().isCommercial()) { - return; - } - - LOGGER.log(Level.WARNING, "EV_SlidingDoor"); - - // Make sure door isn't already being animated - sec = line.frontsector; - door = null; - if (sec.specialdata != null) { - if (thing.player == null) { - return; - } - - door = (slidedoor_t) sec.specialdata; - if (door.type == sdt_e.sdt_openAndClose) { - if (door.status == sd_e.sd_waiting) { - door.status = sd_e.sd_closing; - } - } else { - return; - } - } - - // Init sliding door vars - if (door == null) { - door = new slidedoor_t(); - AddThinker(door); - sec.specialdata = door; - - door.type = sdt_e.sdt_openAndClose; - door.status = sd_e.sd_opening; - door.whichDoorIndex = P_FindSlidingDoorType(line); - - if (door.whichDoorIndex < 0) { - doomSystem().Error("EV_SlidingDoor: Can't use texture for sliding door!"); - } - - door.frontsector = sec; - door.backsector = line.backsector; - door.thinkerFunction = T_SlidingDoor; - door.timer = SWAITTICS; - door.frame = 0; - door.line = line; - } - } -} +package p.Actions; + +import doom.thinker_t; +import java.util.logging.Level; +import java.util.logging.Logger; +import mochadoom.Loggers; +import p.AbstractLevelLoader; +import static p.ActiveStates.T_SlidingDoor; +import p.mobj_t; +import p.sd_e; +import p.sdt_e; +import p.slidedoor_t; +import p.slideframe_t; +import p.slidename_t; +import rr.TextureManager; +import rr.line_t; +import static rr.line_t.ML_BLOCKING; +import rr.sector_t; +import static utils.GenericCopy.malloc; +import utils.TraitFactory.ContextKey; + +public interface ActionsSlideDoors extends ActionTrait { + + static final Logger LOGGER = Loggers.getLogger(ActionsSlideDoors.class.getName()); + + ContextKey KEY_SLIDEDOORS = ACTION_KEY_CHAIN.newKey(ActionsSlideDoors.class, SlideDoors::new); + + void RemoveThinker(thinker_t t); + + // UNUSED + // Separate into p_slidoor.c? + // ABANDONED TO THE MISTS OF TIME!!! + // + // EV_SlidingDoor : slide a door horizontally + // (animate midtexture, then set noblocking line) + // + int MAXSLIDEDOORS = 5; + // how many frames of animation + int SNUMFRAMES = 4; + + int SDOORWAIT = 35 * 3; + int SWAITTICS = 4; + + slidename_t[] slideFrameNames = { + new slidename_t( + "GDOORF1", "GDOORF2", "GDOORF3", "GDOORF4", // front + "GDOORB1", "GDOORB2", "GDOORB3", "GDOORB4" // back + ), + new slidename_t(), new slidename_t(), new slidename_t(), new slidename_t() + }; + + final class SlideDoors { + + slideframe_t[] slideFrames = malloc(slideframe_t::new, slideframe_t[]::new, MAXSLIDEDOORS); + } + + default void SlidingDoor(slidedoor_t door) { + final AbstractLevelLoader ll = levelLoader(); + final SlideDoors sd = contextRequire(KEY_SLIDEDOORS); + switch (door.status) { + case sd_opening: + if (door.timer-- == 0) { + if (++door.frame == ActionsSlideDoors.SNUMFRAMES) { + // IF DOOR IS DONE OPENING... + ll.sides[door.line.sidenum[0]].midtexture = 0; + ll.sides[door.line.sidenum[1]].midtexture = 0; + door.line.flags &= ML_BLOCKING ^ 0xff; + + if (door.type == sdt_e.sdt_openOnly) { + door.frontsector.specialdata = null; + RemoveThinker(door); + break; + } + + door.timer = ActionsSlideDoors.SDOORWAIT; + door.status = sd_e.sd_waiting; + } else { + // IF DOOR NEEDS TO ANIMATE TO NEXT FRAME... + door.timer = ActionsSlideDoors.SWAITTICS; + + ll.sides[door.line.sidenum[0]].midtexture = (short) sd.slideFrames[door.whichDoorIndex].frontFrames[door.frame]; + ll.sides[door.line.sidenum[1]].midtexture = (short) sd.slideFrames[door.whichDoorIndex].backFrames[door.frame]; + } + } + break; + + case sd_waiting: + // IF DOOR IS DONE WAITING... + if (door.timer-- == 0) { + // CAN DOOR CLOSE? + if (door.frontsector.thinglist != null + || door.backsector.thinglist != null) { + door.timer = ActionsSlideDoors.SDOORWAIT; + break; + } + + // door.frame = SNUMFRAMES-1; + door.status = sd_e.sd_closing; + door.timer = ActionsSlideDoors.SWAITTICS; + } + break; + + case sd_closing: + if (door.timer-- == 0) { + if (--door.frame < 0) { + // IF DOOR IS DONE CLOSING... + door.line.flags |= ML_BLOCKING; + door.frontsector.specialdata = null; + RemoveThinker(door); + break; + } else { + // IF DOOR NEEDS TO ANIMATE TO NEXT FRAME... + door.timer = ActionsSlideDoors.SWAITTICS; + + ll.sides[door.line.sidenum[0]].midtexture = (short) sd.slideFrames[door.whichDoorIndex].frontFrames[door.frame]; + ll.sides[door.line.sidenum[1]].midtexture = (short) sd.slideFrames[door.whichDoorIndex].backFrames[door.frame]; + } + } + break; + } + } + + default void P_InitSlidingDoorFrames() { + final TextureManager tm = DOOM().textureManager; + final SlideDoors sd = contextRequire(KEY_SLIDEDOORS); + + int i; + int f1; + int f2; + int f3; + int f4; + + // DOOM II ONLY... + if (!DOOM().isCommercial()) { + return; + } + + for (i = 0; i < MAXSLIDEDOORS; i++) { + if (slideFrameNames[i].frontFrame1 == null) { + break; + } + + f1 = tm.TextureNumForName(slideFrameNames[i].frontFrame1); + f2 = tm.TextureNumForName(slideFrameNames[i].frontFrame2); + f3 = tm.TextureNumForName(slideFrameNames[i].frontFrame3); + f4 = tm.TextureNumForName(slideFrameNames[i].frontFrame4); + + sd.slideFrames[i].frontFrames[0] = f1; + sd.slideFrames[i].frontFrames[1] = f2; + sd.slideFrames[i].frontFrames[2] = f3; + sd.slideFrames[i].frontFrames[3] = f4; + + f1 = tm.TextureNumForName(slideFrameNames[i].backFrame1); + f2 = tm.TextureNumForName(slideFrameNames[i].backFrame2); + f3 = tm.TextureNumForName(slideFrameNames[i].backFrame3); + f4 = tm.TextureNumForName(slideFrameNames[i].backFrame4); + + sd.slideFrames[i].backFrames[0] = f1; + sd.slideFrames[i].backFrames[1] = f2; + sd.slideFrames[i].backFrames[2] = f3; + sd.slideFrames[i].backFrames[3] = f4; + } + } + + // + // Return index into "slideFrames" array + // for which door type to use + // + default int P_FindSlidingDoorType(line_t line) { + final AbstractLevelLoader ll = levelLoader(); + final SlideDoors sd = contextRequire(KEY_SLIDEDOORS); + + for (int i = 0; i < MAXSLIDEDOORS; i++) { + int val = ll.sides[line.sidenum[0]].midtexture; + if (val == sd.slideFrames[i].frontFrames[0]) { + return i; + } + } + + return -1; + } + + default void EV_SlidingDoor(line_t line, mobj_t thing) { + sector_t sec; + slidedoor_t door; + + // DOOM II ONLY... + if (!DOOM().isCommercial()) { + return; + } + + LOGGER.log(Level.WARNING, "EV_SlidingDoor"); + + // Make sure door isn't already being animated + sec = line.frontsector; + door = null; + if (sec.specialdata != null) { + if (thing.player == null) { + return; + } + + door = (slidedoor_t) sec.specialdata; + if (door.type == sdt_e.sdt_openAndClose) { + if (door.status == sd_e.sd_waiting) { + door.status = sd_e.sd_closing; + } + } else { + return; + } + } + + // Init sliding door vars + if (door == null) { + door = new slidedoor_t(); + AddThinker(door); + sec.specialdata = door; + + door.type = sdt_e.sdt_openAndClose; + door.status = sd_e.sd_opening; + door.whichDoorIndex = P_FindSlidingDoorType(line); + + if (door.whichDoorIndex < 0) { + doomSystem().Error("EV_SlidingDoor: Can't use texture for sliding door!"); + } + + door.frontsector = sec; + door.backsector = line.backsector; + door.thinkerFunction = T_SlidingDoor; + door.timer = SWAITTICS; + door.frame = 0; + door.line = line; + } + } +} \ No newline at end of file diff --git a/src/p/Actions/ActionsSpawns.java b/src/p/Actions/ActionsSpawns.java index dc86ca2..46a5a6d 100644 --- a/src/p/Actions/ActionsSpawns.java +++ b/src/p/Actions/ActionsSpawns.java @@ -1,451 +1,451 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import static data.Defines.MTF_AMBUSH; -import static data.Defines.NUMCARDS; -import static data.Defines.ONCEILINGZ; -import static data.Defines.ONFLOORZ; -import static data.Defines.PST_LIVE; -import static data.Defines.PST_REBORN; -import static data.Defines.VIEWHEIGHT; -import static data.Limits.MAXPLAYERS; -import static data.Limits.NUMMOBJTYPES; -import static data.Tables.ANG45; -import static data.info.mobjinfo; -import static data.info.states; -import data.mapthing_t; -import data.mobjinfo_t; -import data.mobjtype_t; -import data.sounds; -import data.state_t; -import defines.skill_t; -import defines.statenum_t; -import doom.DoomMain; -import doom.SourceCode; -import doom.SourceCode.P_Mobj; -import static doom.SourceCode.P_Mobj.P_SpawnMobj; -import static doom.SourceCode.P_Mobj.P_SpawnPlayer; -import doom.SourceCode.fixed_t; -import doom.player_t; -import java.util.logging.Level; -import static m.fixed_t.FRACBITS; -import static m.fixed_t.FRACUNIT; -import p.ActiveStates; -import p.mobj_t; -import static p.mobj_t.MF_AMBUSH; -import static p.mobj_t.MF_COUNTITEM; -import static p.mobj_t.MF_COUNTKILL; -import static p.mobj_t.MF_NOTDMATCH; -import static p.mobj_t.MF_SPAWNCEILING; -import static p.mobj_t.MF_TRANSSHIFT; -import rr.subsector_t; -import static utils.C2JUtils.eval; -import v.graphics.Palettes; - -public interface ActionsSpawns extends ActionsSectors { - - /** - * P_NightmareRespawn - */ - default void NightmareRespawn(mobj_t mobj) { - int x, y, z; // fixed - subsector_t ss; - mobj_t mo; - mapthing_t mthing; - - x = mobj.spawnpoint.x << FRACBITS; - y = mobj.spawnpoint.y << FRACBITS; - - // somthing is occupying it's position? - if (!CheckPosition(mobj, x, y)) { - return; // no respwan - } - // spawn a teleport fog at old spot - // because of removal of the body? - mo = SpawnMobj(mobj.x, mobj.y, mobj.subsector.sector.floorheight, mobjtype_t.MT_TFOG); - - // initiate teleport sound - StartSound(mo, sounds.sfxenum_t.sfx_telept); - - // spawn a teleport fog at the new spot - ss = levelLoader().PointInSubsector(x, y); - - mo = SpawnMobj(x, y, ss.sector.floorheight, mobjtype_t.MT_TFOG); - - StartSound(mo, sounds.sfxenum_t.sfx_telept); - - // spawn the new monster - mthing = mobj.spawnpoint; - - // spawn it - if (eval(mobj.info.flags & MF_SPAWNCEILING)) { - z = ONCEILINGZ; - } else { - z = ONFLOORZ; - } - - // inherit attributes from deceased one - mo = SpawnMobj(x, y, z, mobj.type); - mo.spawnpoint = mobj.spawnpoint; - mo.angle = ANG45 * (mthing.angle / 45); - - if (eval(mthing.options & MTF_AMBUSH)) { - mo.flags |= MF_AMBUSH; - } - - mo.reactiontime = 18; - - // remove the old monster, - RemoveMobj(mobj); - } - - /** - * P_SpawnMobj - * - * @param x fixed - * @param y fixed - * @param z fixed - * @param type - * @return - */ - @Override - @SourceCode.Exact - @P_Mobj.C(P_SpawnMobj) - default mobj_t SpawnMobj(@fixed_t int x, @fixed_t int y, @fixed_t int z, mobjtype_t type) { - mobj_t mobj; - state_t st; - mobjinfo_t info; - - Z_Malloc: - { - mobj = createMobj(); - } - info = mobjinfo[type.ordinal()]; - - mobj.type = type; - mobj.info = info; - mobj.x = x; - mobj.y = y; - mobj.radius = info.radius; - mobj.height = info.height; - mobj.flags = info.flags; - mobj.health = info.spawnhealth; - - if (getGameSkill() != skill_t.sk_nightmare) { - mobj.reactiontime = info.reactiontime; - } - - P_Random: - { - mobj.lastlook = P_Random() % MAXPLAYERS; - } - // do not set the state with P_SetMobjState, - // because action routines can not be called yet - st = states[info.spawnstate.ordinal()]; - - mobj.mobj_state = st; - mobj.mobj_tics = st.tics; - mobj.mobj_sprite = st.sprite; - mobj.mobj_frame = st.frame; - - // set subsector and/or block links - P_SetThingPosition: - { - SetThingPosition(mobj); - } - - mobj.floorz = mobj.subsector.sector.floorheight; - mobj.ceilingz = mobj.subsector.sector.ceilingheight; - - if (z == ONFLOORZ) { - mobj.z = mobj.floorz; - } else if (z == ONCEILINGZ) { - mobj.z = mobj.ceilingz - mobj.info.height; - } else { - mobj.z = z; - } - - mobj.thinkerFunction = ActiveStates.P_MobjThinker; - P_AddThinker: - { - AddThinker(mobj); - } - - return mobj; - } - - /** - * P_SpawnPlayer - * Called when a player is spawned on the level. - * Most of the player structure stays unchanged - * between levels. - */ - @SourceCode.Exact - @P_Mobj.C(P_SpawnPlayer) - default void SpawnPlayer(mapthing_t mthing) { - player_t p; - @fixed_t - int x, y, z; - mobj_t mobj; - - // not playing? - if (!PlayerInGame(mthing.type - 1)) { - return; - } - - p = getPlayer(mthing.type - 1); - - if (p.playerstate == PST_REBORN) { - G_PlayerReborn: - { - p.PlayerReborn(); - } - } - //DM.PlayerReborn (mthing.type-1); - - x = mthing.x << FRACBITS; - y = mthing.y << FRACBITS; - z = ONFLOORZ; - P_SpawnMobj: - { - mobj = this.SpawnMobj(x, y, z, mobjtype_t.MT_PLAYER); - } - - // set color translations for player sprites - if (mthing.type > 1) { - mobj.flags |= (mthing.type - 1) << MF_TRANSSHIFT; - } - - mobj.angle = ANG45 * (mthing.angle / 45); - mobj.player = p; - mobj.health = p.health[0]; - - p.mo = mobj; - p.playerstate = PST_LIVE; - p.refire = 0; - p.message = null; - p.damagecount = 0; - p.bonuscount = 0; - p.extralight = 0; - p.fixedcolormap = Palettes.COLORMAP_FIXED; - p.viewheight = VIEWHEIGHT; - - // setup gun psprite - P_SetupPsprites: - { - p.SetupPsprites(); - } - - // give all cards in death match mode - if (IsDeathMatch()) { - for (int i = 0; i < NUMCARDS; i++) { - p.cards[i] = true; - } - } - - if (mthing.type - 1 == ConsolePlayerNumber()) { - // wake up the status bar - ST_Start: - { - statusBar().Start(); - } - // wake up the heads up text - HU_Start: - { - headsUp().Start(); - } - } - } - - /** - * P_SpawnMapThing The fields of the mapthing should already be in host byte order. - */ - default mobj_t SpawnMapThing(mapthing_t mthing) { - final DoomMain D = DOOM(); - int i; - int bit; - mobj_t mobj; - int x; - int y; - int z; - - // count deathmatch start positions - if (mthing.type == 11) { - if (D.deathmatch_p < 10/*DM.deathmatchstarts[10]*/) { - // memcpy (deathmatch_p, mthing, sizeof(*mthing)); - D.deathmatchstarts[D.deathmatch_p] = new mapthing_t(mthing); - D.deathmatch_p++; - } - return null; - } - - if (mthing.type <= 0) { - // Ripped from Chocolate Doom :-p - // Thing type 0 is actually "player -1 start". - // For some reason, Vanilla Doom accepts/ignores this. - // MAES: no kidding. - - return null; - } - - // check for players specially - if (mthing.type <= 4 && mthing.type > 0) // killough 2/26/98 -- fix crashes - { - // save spots for respawning in network games - D.playerstarts[mthing.type - 1] = new mapthing_t(mthing); - if (!IsDeathMatch()) { - this.SpawnPlayer(mthing); - } - - return null; - } - - // check for apropriate skill level - if (!IsNetGame() && eval(mthing.options & 16)) { - return null; - } - - switch (getGameSkill()) { - case sk_baby: - bit = 1; - break; - case sk_nightmare: - bit = 4; - break; - default: - bit = 1 << (getGameSkill().ordinal() - 1); - break; - } - - if (!eval(mthing.options & bit)) { - return null; - } - - // find which type to spawn - for (i = 0; i < NUMMOBJTYPES; i++) { - if (mthing.type == mobjinfo[i].doomednum) { - break; - } - } - - // phares 5/16/98: - // Do not abort because of an unknown thing. Ignore it, but post a - // warning message for the player. - if (i == NUMMOBJTYPES) { - Spawn.LOGGER.log(Level.WARNING, - String.format("P_SpawnMapThing: Unknown type %d at (%d, %d)", mthing.type, mthing.x, mthing.y)); - return null; - } - - // don't spawn keycards and players in deathmatch - if (IsDeathMatch() && eval(mobjinfo[i].flags & MF_NOTDMATCH)) { - return null; - } - - // don't spawn any monsters if -nomonsters - if (D.nomonsters && (i == mobjtype_t.MT_SKULL.ordinal() || eval(mobjinfo[i].flags & MF_COUNTKILL))) { - return null; - } - - // spawn it - x = mthing.x << FRACBITS; - y = mthing.y << FRACBITS; - - if (eval(mobjinfo[i].flags & MF_SPAWNCEILING)) { - z = ONCEILINGZ; - } else { - z = ONFLOORZ; - } - - mobj = this.SpawnMobj(x, y, z, mobjtype_t.values()[i]); - mobj.spawnpoint.copyFrom(mthing); - - if (mobj.mobj_tics > 0) { - mobj.mobj_tics = 1 + (P_Random() % mobj.mobj_tics); - } - if (eval(mobj.flags & MF_COUNTKILL)) { - D.totalkills++; - } - if (eval(mobj.flags & MF_COUNTITEM)) { - D.totalitems++; - } - - mobj.angle = ANG45 * (mthing.angle / 45); - if (eval(mthing.options & MTF_AMBUSH)) { - mobj.flags |= MF_AMBUSH; - } - - return mobj; - - } - - /** - * P_SpawnBlood - * - * @param x fixed - * @param y fixed - * @param z fixed - * @param damage - */ - default void SpawnBlood(int x, int y, int z, int damage) { - mobj_t th; - - z += ((P_Random() - P_Random()) << 10); - th = this.SpawnMobj(x, y, z, mobjtype_t.MT_BLOOD); - th.momz = FRACUNIT * 2; - th.mobj_tics -= P_Random() & 3; - - if (th.mobj_tics < 1) { - th.mobj_tics = 1; - } - - if (damage <= 12 && damage >= 9) { - th.SetMobjState(statenum_t.S_BLOOD2); - } else if (damage < 9) { - th.SetMobjState(statenum_t.S_BLOOD3); - } - } - - /** - * P_SpawnPuff - * - * @param x fixed - * @param y fixed - * @param z fixed - * - */ - default void SpawnPuff(int x, int y, int z) { - mobj_t th; - - z += ((P_Random() - P_Random()) << 10); - - th = this.SpawnMobj(x, y, z, mobjtype_t.MT_PUFF); - th.momz = FRACUNIT; - th.mobj_tics -= P_Random() & 3; - - if (th.mobj_tics < 1) { - th.mobj_tics = 1; - } - - // don't make punches spark on the wall - if (contextTest(KEY_SPAWN, Spawn::isMeleeRange)) { - th.SetMobjState(statenum_t.S_PUFF3); - } - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import static data.Defines.MTF_AMBUSH; +import static data.Defines.NUMCARDS; +import static data.Defines.ONCEILINGZ; +import static data.Defines.ONFLOORZ; +import static data.Defines.PST_LIVE; +import static data.Defines.PST_REBORN; +import static data.Defines.VIEWHEIGHT; +import static data.Limits.MAXPLAYERS; +import static data.Limits.NUMMOBJTYPES; +import static data.Tables.ANG45; +import static data.info.mobjinfo; +import static data.info.states; +import data.mapthing_t; +import data.mobjinfo_t; +import data.mobjtype_t; +import data.sounds; +import data.state_t; +import defines.skill_t; +import defines.statenum_t; +import doom.DoomMain; +import doom.SourceCode; +import doom.SourceCode.P_Mobj; +import static doom.SourceCode.P_Mobj.P_SpawnMobj; +import static doom.SourceCode.P_Mobj.P_SpawnPlayer; +import doom.SourceCode.fixed_t; +import doom.player_t; +import java.util.logging.Level; +import static m.fixed_t.FRACBITS; +import static m.fixed_t.FRACUNIT; +import p.ActiveStates; +import p.mobj_t; +import static p.mobj_t.MF_AMBUSH; +import static p.mobj_t.MF_COUNTITEM; +import static p.mobj_t.MF_COUNTKILL; +import static p.mobj_t.MF_NOTDMATCH; +import static p.mobj_t.MF_SPAWNCEILING; +import static p.mobj_t.MF_TRANSSHIFT; +import rr.subsector_t; +import static utils.C2JUtils.eval; +import v.graphics.Palettes; + +public interface ActionsSpawns extends ActionsSectors { + + /** + * P_NightmareRespawn + */ + default void NightmareRespawn(mobj_t mobj) { + int x, y, z; // fixed + subsector_t ss; + mobj_t mo; + mapthing_t mthing; + + x = mobj.spawnpoint.x << FRACBITS; + y = mobj.spawnpoint.y << FRACBITS; + + // somthing is occupying it's position? + if (!CheckPosition(mobj, x, y)) { + return; // no respwan + } + // spawn a teleport fog at old spot + // because of removal of the body? + mo = SpawnMobj(mobj.x, mobj.y, mobj.subsector.sector.floorheight, mobjtype_t.MT_TFOG); + + // initiate teleport sound + StartSound(mo, sounds.sfxenum_t.sfx_telept); + + // spawn a teleport fog at the new spot + ss = levelLoader().PointInSubsector(x, y); + + mo = SpawnMobj(x, y, ss.sector.floorheight, mobjtype_t.MT_TFOG); + + StartSound(mo, sounds.sfxenum_t.sfx_telept); + + // spawn the new monster + mthing = mobj.spawnpoint; + + // spawn it + if (eval(mobj.info.flags & MF_SPAWNCEILING)) { + z = ONCEILINGZ; + } else { + z = ONFLOORZ; + } + + // inherit attributes from deceased one + mo = SpawnMobj(x, y, z, mobj.type); + mo.spawnpoint = mobj.spawnpoint; + mo.angle = ANG45 * (mthing.angle / 45); + + if (eval(mthing.options & MTF_AMBUSH)) { + mo.flags |= MF_AMBUSH; + } + + mo.reactiontime = 18; + + // remove the old monster, + RemoveMobj(mobj); + } + + /** + * P_SpawnMobj + * + * @param x fixed + * @param y fixed + * @param z fixed + * @param type + * @return + */ + @Override + @SourceCode.Exact + @P_Mobj.C(P_SpawnMobj) + default mobj_t SpawnMobj(@fixed_t int x, @fixed_t int y, @fixed_t int z, mobjtype_t type) { + mobj_t mobj; + state_t st; + mobjinfo_t info; + + Z_Malloc: + { + mobj = createMobj(); + } + info = mobjinfo[type.ordinal()]; + + mobj.type = type; + mobj.info = info; + mobj.x = x; + mobj.y = y; + mobj.radius = info.radius; + mobj.height = info.height; + mobj.flags = info.flags; + mobj.health = info.spawnhealth; + + if (getGameSkill() != skill_t.sk_nightmare) { + mobj.reactiontime = info.reactiontime; + } + + P_Random: + { + mobj.lastlook = P_Random() % MAXPLAYERS; + } + // do not set the state with P_SetMobjState, + // because action routines can not be called yet + st = states[info.spawnstate.ordinal()]; + + mobj.mobj_state = st; + mobj.mobj_tics = st.tics; + mobj.mobj_sprite = st.sprite; + mobj.mobj_frame = st.frame; + + // set subsector and/or block links + P_SetThingPosition: + { + SetThingPosition(mobj); + } + + mobj.floorz = mobj.subsector.sector.floorheight; + mobj.ceilingz = mobj.subsector.sector.ceilingheight; + + if (z == ONFLOORZ) { + mobj.z = mobj.floorz; + } else if (z == ONCEILINGZ) { + mobj.z = mobj.ceilingz - mobj.info.height; + } else { + mobj.z = z; + } + + mobj.thinkerFunction = ActiveStates.P_MobjThinker; + P_AddThinker: + { + AddThinker(mobj); + } + + return mobj; + } + + /** + * P_SpawnPlayer + * Called when a player is spawned on the level. + * Most of the player structure stays unchanged + * between levels. + */ + @SourceCode.Exact + @P_Mobj.C(P_SpawnPlayer) + default void SpawnPlayer(mapthing_t mthing) { + player_t p; + @fixed_t + int x, y, z; + mobj_t mobj; + + // not playing? + if (!PlayerInGame(mthing.type - 1)) { + return; + } + + p = getPlayer(mthing.type - 1); + + if (p.playerstate == PST_REBORN) { + G_PlayerReborn: + { + p.PlayerReborn(); + } + } + //DM.PlayerReborn (mthing.type-1); + + x = mthing.x << FRACBITS; + y = mthing.y << FRACBITS; + z = ONFLOORZ; + P_SpawnMobj: + { + mobj = this.SpawnMobj(x, y, z, mobjtype_t.MT_PLAYER); + } + + // set color translations for player sprites + if (mthing.type > 1) { + mobj.flags |= (mthing.type - 1) << MF_TRANSSHIFT; + } + + mobj.angle = ANG45 * (mthing.angle / 45); + mobj.player = p; + mobj.health = p.health[0]; + + p.mo = mobj; + p.playerstate = PST_LIVE; + p.refire = 0; + p.message = null; + p.damagecount = 0; + p.bonuscount = 0; + p.extralight = 0; + p.fixedcolormap = Palettes.COLORMAP_FIXED; + p.viewheight = VIEWHEIGHT; + + // setup gun psprite + P_SetupPsprites: + { + p.SetupPsprites(); + } + + // give all cards in death match mode + if (IsDeathMatch()) { + for (int i = 0; i < NUMCARDS; i++) { + p.cards[i] = true; + } + } + + if (mthing.type - 1 == ConsolePlayerNumber()) { + // wake up the status bar + ST_Start: + { + statusBar().Start(); + } + // wake up the heads up text + HU_Start: + { + headsUp().Start(); + } + } + } + + /** + * P_SpawnMapThing The fields of the mapthing should already be in host byte order. + */ + default mobj_t SpawnMapThing(mapthing_t mthing) { + final DoomMain D = DOOM(); + int i; + int bit; + mobj_t mobj; + int x; + int y; + int z; + + // count deathmatch start positions + if (mthing.type == 11) { + if (D.deathmatch_p < 10/*DM.deathmatchstarts[10]*/) { + // memcpy (deathmatch_p, mthing, sizeof(*mthing)); + D.deathmatchstarts[D.deathmatch_p] = new mapthing_t(mthing); + D.deathmatch_p++; + } + return null; + } + + if (mthing.type <= 0) { + // Ripped from Chocolate Doom :-p + // Thing type 0 is actually "player -1 start". + // For some reason, Vanilla Doom accepts/ignores this. + // MAES: no kidding. + + return null; + } + + // check for players specially + if (mthing.type <= 4 && mthing.type > 0) // killough 2/26/98 -- fix crashes + { + // save spots for respawning in network games + D.playerstarts[mthing.type - 1] = new mapthing_t(mthing); + if (!IsDeathMatch()) { + this.SpawnPlayer(mthing); + } + + return null; + } + + // check for apropriate skill level + if (!IsNetGame() && eval(mthing.options & 16)) { + return null; + } + + switch (getGameSkill()) { + case sk_baby: + bit = 1; + break; + case sk_nightmare: + bit = 4; + break; + default: + bit = 1 << (getGameSkill().ordinal() - 1); + break; + } + + if (!eval(mthing.options & bit)) { + return null; + } + + // find which type to spawn + for (i = 0; i < NUMMOBJTYPES; i++) { + if (mthing.type == mobjinfo[i].doomednum) { + break; + } + } + + // phares 5/16/98: + // Do not abort because of an unknown thing. Ignore it, but post a + // warning message for the player. + if (i == NUMMOBJTYPES) { + Spawn.LOGGER.log(Level.WARNING, + String.format("P_SpawnMapThing: Unknown type %d at (%d, %d)", mthing.type, mthing.x, mthing.y)); + return null; + } + + // don't spawn keycards and players in deathmatch + if (IsDeathMatch() && eval(mobjinfo[i].flags & MF_NOTDMATCH)) { + return null; + } + + // don't spawn any monsters if -nomonsters + if (D.nomonsters && (i == mobjtype_t.MT_SKULL.ordinal() || eval(mobjinfo[i].flags & MF_COUNTKILL))) { + return null; + } + + // spawn it + x = mthing.x << FRACBITS; + y = mthing.y << FRACBITS; + + if (eval(mobjinfo[i].flags & MF_SPAWNCEILING)) { + z = ONCEILINGZ; + } else { + z = ONFLOORZ; + } + + mobj = this.SpawnMobj(x, y, z, mobjtype_t.values()[i]); + mobj.spawnpoint.copyFrom(mthing); + + if (mobj.mobj_tics > 0) { + mobj.mobj_tics = 1 + (P_Random() % mobj.mobj_tics); + } + if (eval(mobj.flags & MF_COUNTKILL)) { + D.totalkills++; + } + if (eval(mobj.flags & MF_COUNTITEM)) { + D.totalitems++; + } + + mobj.angle = ANG45 * (mthing.angle / 45); + if (eval(mthing.options & MTF_AMBUSH)) { + mobj.flags |= MF_AMBUSH; + } + + return mobj; + + } + + /** + * P_SpawnBlood + * + * @param x fixed + * @param y fixed + * @param z fixed + * @param damage + */ + default void SpawnBlood(int x, int y, int z, int damage) { + mobj_t th; + + z += ((P_Random() - P_Random()) << 10); + th = this.SpawnMobj(x, y, z, mobjtype_t.MT_BLOOD); + th.momz = FRACUNIT * 2; + th.mobj_tics -= P_Random() & 3; + + if (th.mobj_tics < 1) { + th.mobj_tics = 1; + } + + if (damage <= 12 && damage >= 9) { + th.SetMobjState(statenum_t.S_BLOOD2); + } else if (damage < 9) { + th.SetMobjState(statenum_t.S_BLOOD3); + } + } + + /** + * P_SpawnPuff + * + * @param x fixed + * @param y fixed + * @param z fixed + * + */ + default void SpawnPuff(int x, int y, int z) { + mobj_t th; + + z += ((P_Random() - P_Random()) << 10); + + th = this.SpawnMobj(x, y, z, mobjtype_t.MT_PUFF); + th.momz = FRACUNIT; + th.mobj_tics -= P_Random() & 3; + + if (th.mobj_tics < 1) { + th.mobj_tics = 1; + } + + // don't make punches spark on the wall + if (contextTest(KEY_SPAWN, Spawn::isMeleeRange)) { + th.SetMobjState(statenum_t.S_PUFF3); + } + } +} \ No newline at end of file diff --git a/src/p/Actions/ActionsTeleportation.java b/src/p/Actions/ActionsTeleportation.java index cfd1ab3..562b1ca 100644 --- a/src/p/Actions/ActionsTeleportation.java +++ b/src/p/Actions/ActionsTeleportation.java @@ -1,201 +1,201 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import static data.Limits.MAXRADIUS; -import data.Tables; -import static data.Tables.finecosine; -import static data.Tables.finesine; -import data.mobjtype_t; -import data.sounds; -import doom.SourceCode.fixed_t; -import doom.thinker_t; -import static m.BBox.BOXBOTTOM; -import static m.BBox.BOXLEFT; -import static m.BBox.BOXRIGHT; -import static m.BBox.BOXTOP; -import p.AbstractLevelLoader; -import p.ActiveStates; -import p.mobj_t; -import static p.mobj_t.MF_MISSILE; -import rr.line_t; -import rr.sector_t; -import rr.subsector_t; - -public interface ActionsTeleportation extends ActionsSectors { - - void UnsetThingPosition(mobj_t mobj); - - // - // TELEPORTATION - // - @Override - default int Teleport(line_t line, int side, mobj_t thing) { - int i; - int tag; - mobj_t m; - mobj_t fog; - int an; - thinker_t thinker; - sector_t sector; - @fixed_t - int oldx, oldy, oldz; - - // don't teleport missiles - if ((thing.flags & MF_MISSILE) != 0) { - return 0; - } - - // Don't teleport if hit back of line, - // so you can get out of teleporter. - if (side == 1) { - return 0; - } - - tag = line.tag; - for (i = 0; i < levelLoader().numsectors; i++) { - if (levelLoader().sectors[i].tag == tag) { - //thinker = thinkercap.next; - for (thinker = getThinkerCap().next; thinker != getThinkerCap(); thinker = thinker.next) { - // not a mobj - if (thinker.thinkerFunction != ActiveStates.P_MobjThinker) { - continue; - } - - m = (mobj_t) thinker; - - // not a teleportman - if (m.type != mobjtype_t.MT_TELEPORTMAN) { - continue; - } - - sector = m.subsector.sector; - // wrong sector - if (sector.id != i) { - continue; - } - - oldx = thing.x; - oldy = thing.y; - oldz = thing.z; - - if (!TeleportMove(thing, m.x, m.y)) { - return 0; - } - - thing.z = thing.floorz; //fixme: not needed? - if (thing.player != null) { - thing.player.viewz = thing.z + thing.player.viewheight; - thing.player.lookdir = 0; // Reset lookdir - } - - // spawn teleport fog at source and destination - fog = SpawnMobj(oldx, oldy, oldz, mobjtype_t.MT_TFOG); - StartSound(fog, sounds.sfxenum_t.sfx_telept); - an = Tables.toBAMIndex(m.angle); - fog = SpawnMobj(m.x + 20 * finecosine[an], m.y + 20 * finesine[an], thing.z, mobjtype_t.MT_TFOG); - - // emit sound, where? - StartSound(fog, sounds.sfxenum_t.sfx_telept); - - // don't move for a bit - if (thing.player != null) { - thing.reactiontime = 18; - } - - thing.angle = m.angle; - thing.momx = thing.momy = thing.momz = 0; - return 1; - } - } - } - return 0; - } - - // - // TELEPORT MOVE - // - // - // P_TeleportMove - // - default boolean TeleportMove(mobj_t thing, int x, /*fixed*/ int y) { - final Spechits spechits = contextRequire(KEY_SPECHITS); - final AbstractLevelLoader ll = levelLoader(); - final Movement ma = contextRequire(KEY_MOVEMENT); - int xl; - int xh; - int yl; - int yh; - int bx; - int by; - - subsector_t newsubsec; - - // kill anything occupying the position - ma.tmthing = thing; - ma.tmflags = thing.flags; - - ma.tmx = x; - ma.tmy = y; - - ma.tmbbox[BOXTOP] = y + ma.tmthing.radius; - ma.tmbbox[BOXBOTTOM] = y - ma.tmthing.radius; - ma.tmbbox[BOXRIGHT] = x + ma.tmthing.radius; - ma.tmbbox[BOXLEFT] = x - ma.tmthing.radius; - - newsubsec = ll.PointInSubsector(x, y); - ma.ceilingline = null; - - // The base floor/ceiling is from the subsector - // that contains the point. - // Any contacted lines the step closer together - // will adjust them. - ma.tmfloorz = ma.tmdropoffz = newsubsec.sector.floorheight; - ma.tmceilingz = newsubsec.sector.ceilingheight; - - sceneRenderer().increaseValidCount(1); // This is r_main's ? - spechits.numspechit = 0; - - // stomp on any things contacted - xl = ll.getSafeBlockX(ma.tmbbox[BOXLEFT] - ll.bmaporgx - MAXRADIUS); - xh = ll.getSafeBlockX(ma.tmbbox[BOXRIGHT] - ll.bmaporgx + MAXRADIUS); - yl = ll.getSafeBlockY(ma.tmbbox[BOXBOTTOM] - ll.bmaporgy - MAXRADIUS); - yh = ll.getSafeBlockY(ma.tmbbox[BOXTOP] - ll.bmaporgy + MAXRADIUS); - - for (bx = xl; bx <= xh; bx++) { - for (by = yl; by <= yh; by++) { - if (!BlockThingsIterator(bx, by, this::StompThing)) { - return false; - } - } - } - - // the move is ok, - // so link the thing into its new position - UnsetThingPosition(thing); - - thing.floorz = ma.tmfloorz; - thing.ceilingz = ma.tmceilingz; - thing.x = x; - thing.y = y; - - ll.SetThingPosition(thing); - - return true; - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import static data.Limits.MAXRADIUS; +import data.Tables; +import static data.Tables.finecosine; +import static data.Tables.finesine; +import data.mobjtype_t; +import data.sounds; +import doom.SourceCode.fixed_t; +import doom.thinker_t; +import static m.BBox.BOXBOTTOM; +import static m.BBox.BOXLEFT; +import static m.BBox.BOXRIGHT; +import static m.BBox.BOXTOP; +import p.AbstractLevelLoader; +import p.ActiveStates; +import p.mobj_t; +import static p.mobj_t.MF_MISSILE; +import rr.line_t; +import rr.sector_t; +import rr.subsector_t; + +public interface ActionsTeleportation extends ActionsSectors { + + void UnsetThingPosition(mobj_t mobj); + + // + // TELEPORTATION + // + @Override + default int Teleport(line_t line, int side, mobj_t thing) { + int i; + int tag; + mobj_t m; + mobj_t fog; + int an; + thinker_t thinker; + sector_t sector; + @fixed_t + int oldx, oldy, oldz; + + // don't teleport missiles + if ((thing.flags & MF_MISSILE) != 0) { + return 0; + } + + // Don't teleport if hit back of line, + // so you can get out of teleporter. + if (side == 1) { + return 0; + } + + tag = line.tag; + for (i = 0; i < levelLoader().numsectors; i++) { + if (levelLoader().sectors[i].tag == tag) { + //thinker = thinkercap.next; + for (thinker = getThinkerCap().next; thinker != getThinkerCap(); thinker = thinker.next) { + // not a mobj + if (thinker.thinkerFunction != ActiveStates.P_MobjThinker) { + continue; + } + + m = (mobj_t) thinker; + + // not a teleportman + if (m.type != mobjtype_t.MT_TELEPORTMAN) { + continue; + } + + sector = m.subsector.sector; + // wrong sector + if (sector.id != i) { + continue; + } + + oldx = thing.x; + oldy = thing.y; + oldz = thing.z; + + if (!TeleportMove(thing, m.x, m.y)) { + return 0; + } + + thing.z = thing.floorz; //fixme: not needed? + if (thing.player != null) { + thing.player.viewz = thing.z + thing.player.viewheight; + thing.player.lookdir = 0; // Reset lookdir + } + + // spawn teleport fog at source and destination + fog = SpawnMobj(oldx, oldy, oldz, mobjtype_t.MT_TFOG); + StartSound(fog, sounds.sfxenum_t.sfx_telept); + an = Tables.toBAMIndex(m.angle); + fog = SpawnMobj(m.x + 20 * finecosine[an], m.y + 20 * finesine[an], thing.z, mobjtype_t.MT_TFOG); + + // emit sound, where? + StartSound(fog, sounds.sfxenum_t.sfx_telept); + + // don't move for a bit + if (thing.player != null) { + thing.reactiontime = 18; + } + + thing.angle = m.angle; + thing.momx = thing.momy = thing.momz = 0; + return 1; + } + } + } + return 0; + } + + // + // TELEPORT MOVE + // + // + // P_TeleportMove + // + default boolean TeleportMove(mobj_t thing, int x, /*fixed*/ int y) { + final Spechits spechits = contextRequire(KEY_SPECHITS); + final AbstractLevelLoader ll = levelLoader(); + final Movement ma = contextRequire(KEY_MOVEMENT); + int xl; + int xh; + int yl; + int yh; + int bx; + int by; + + subsector_t newsubsec; + + // kill anything occupying the position + ma.tmthing = thing; + ma.tmflags = thing.flags; + + ma.tmx = x; + ma.tmy = y; + + ma.tmbbox[BOXTOP] = y + ma.tmthing.radius; + ma.tmbbox[BOXBOTTOM] = y - ma.tmthing.radius; + ma.tmbbox[BOXRIGHT] = x + ma.tmthing.radius; + ma.tmbbox[BOXLEFT] = x - ma.tmthing.radius; + + newsubsec = ll.PointInSubsector(x, y); + ma.ceilingline = null; + + // The base floor/ceiling is from the subsector + // that contains the point. + // Any contacted lines the step closer together + // will adjust them. + ma.tmfloorz = ma.tmdropoffz = newsubsec.sector.floorheight; + ma.tmceilingz = newsubsec.sector.ceilingheight; + + sceneRenderer().increaseValidCount(1); // This is r_main's ? + spechits.numspechit = 0; + + // stomp on any things contacted + xl = ll.getSafeBlockX(ma.tmbbox[BOXLEFT] - ll.bmaporgx - MAXRADIUS); + xh = ll.getSafeBlockX(ma.tmbbox[BOXRIGHT] - ll.bmaporgx + MAXRADIUS); + yl = ll.getSafeBlockY(ma.tmbbox[BOXBOTTOM] - ll.bmaporgy - MAXRADIUS); + yh = ll.getSafeBlockY(ma.tmbbox[BOXTOP] - ll.bmaporgy + MAXRADIUS); + + for (bx = xl; bx <= xh; bx++) { + for (by = yl; by <= yh; by++) { + if (!BlockThingsIterator(bx, by, this::StompThing)) { + return false; + } + } + } + + // the move is ok, + // so link the thing into its new position + UnsetThingPosition(thing); + + thing.floorz = ma.tmfloorz; + thing.ceilingz = ma.tmceilingz; + thing.x = x; + thing.y = y; + + ll.SetThingPosition(thing); + + return true; + } +} \ No newline at end of file diff --git a/src/p/Actions/ActionsThings.java b/src/p/Actions/ActionsThings.java index 7ba6c59..3acaaf1 100644 --- a/src/p/Actions/ActionsThings.java +++ b/src/p/Actions/ActionsThings.java @@ -1,567 +1,567 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import static data.Defines.NUMAMMO; -import static data.Defines.pw_allmap; -import static data.Defines.pw_infrared; -import static data.Defines.pw_invisibility; -import static data.Defines.pw_invulnerability; -import static data.Defines.pw_ironfeet; -import static data.Defines.pw_strength; -import data.mobjtype_t; -import data.sounds.sfxenum_t; -import defines.ammotype_t; -import defines.card_t; -import doom.DoomMain; -import doom.SourceCode.P_Map; -import static doom.SourceCode.P_Map.PIT_CheckThing; -import static doom.SourceCode.P_Map.PIT_StompThing; -import doom.SourceCode.fixed_t; -import static doom.englsh.*; -import doom.player_t; -import doom.weapontype_t; -import m.Settings; -import static m.fixed_t.FRACUNIT; -import p.mobj_t; -import static p.mobj_t.MF_COUNTITEM; -import static p.mobj_t.MF_DROPPED; -import static p.mobj_t.MF_MISSILE; -import static p.mobj_t.MF_PICKUP; -import static p.mobj_t.MF_SHOOTABLE; -import static p.mobj_t.MF_SKULLFLY; -import static p.mobj_t.MF_SOLID; -import static p.mobj_t.MF_SPECIAL; -import static utils.C2JUtils.eval; - -public interface ActionsThings extends ActionTrait { - - void DamageMobj(mobj_t thing, mobj_t tmthing, mobj_t tmthing0, int damage); - - void RemoveMobj(mobj_t special); - - /** - * PIT_CheckThing - */ - @Override - @P_Map.C(PIT_CheckThing) - default boolean CheckThing(mobj_t thing) { - final Movement movm = contextRequire(KEY_MOVEMENT); - @fixed_t - int blockdist; - boolean solid; - int damage; - - if ((thing.flags & (MF_SOLID | MF_SPECIAL | MF_SHOOTABLE)) == 0) { - return true; - } - - blockdist = thing.radius + movm.tmthing.radius; - - if (Math.abs(thing.x - movm.tmx) >= blockdist - || Math.abs(thing.y - movm.tmy) >= blockdist) { - // didn't hit it - return true; - } - - // don't clip against self - if (thing == movm.tmthing) { - return true; - } - - // check for skulls slamming into things - if ((movm.tmthing.flags & MF_SKULLFLY) != 0) { - damage = ((P_Random() % 8) + 1) * movm.tmthing.info.damage; - - DamageMobj(thing, movm.tmthing, movm.tmthing, damage); - - movm.tmthing.flags &= ~MF_SKULLFLY; - movm.tmthing.momx = movm.tmthing.momy = movm.tmthing.momz = 0; - - movm.tmthing.SetMobjState(movm.tmthing.info.spawnstate); - - return false; // stop moving - } - - // missiles can hit other things - if (eval(movm.tmthing.flags & MF_MISSILE)) { - // see if it went over / under - if (movm.tmthing.z > thing.z + thing.height) { - return true; // overhead - } - if (movm.tmthing.z + movm.tmthing.height < thing.z) { - return true; // underneath - } - if (movm.tmthing.target != null && (movm.tmthing.target.type == thing.type - || (movm.tmthing.target.type == mobjtype_t.MT_KNIGHT && thing.type == mobjtype_t.MT_BRUISER) - || (movm.tmthing.target.type == mobjtype_t.MT_BRUISER && thing.type == mobjtype_t.MT_KNIGHT))) { - // Don't hit same species as originator. - if (thing == movm.tmthing.target) { - return true; - } - - if (thing.type != mobjtype_t.MT_PLAYER) { - // Explode, but do no damage. - // Let players missile other players. - return false; - } - } - - if (!eval(thing.flags & MF_SHOOTABLE)) { - // didn't do any damage - return !eval(thing.flags & MF_SOLID); - } - - // damage / explode - damage = ((P_Random() % 8) + 1) * movm.tmthing.info.damage; - DamageMobj(thing, movm.tmthing, movm.tmthing.target, damage); - - // don't traverse any more - return false; - } - - // check for special pickup - if (eval(thing.flags & MF_SPECIAL)) { - solid = eval(thing.flags & MF_SOLID); - if (eval(movm.tmflags & MF_PICKUP)) { - // can remove thing - TouchSpecialThing(thing, movm.tmthing); - } - return !solid; - } - - return !eval(thing.flags & MF_SOLID); - } - - ; - - /** - * P_TouchSpecialThing LIKE ROMERO's ASS!!! - */ - default void TouchSpecialThing(mobj_t special, mobj_t toucher) { - final DoomMain DOOM = DOOM(); - player_t player; - int i; - @fixed_t - int delta; - sfxenum_t sound; - - delta = special.z - toucher.z; - - if (delta > toucher.height || delta < -8 * FRACUNIT) { - // out of reach - return; - } - - sound = sfxenum_t.sfx_itemup; - player = toucher.player; - - // Dead thing touching. - // Can happen with a sliding player corpse. - if (toucher.health <= 0) { - return; - } - - // Identify by sprite. - switch (special.mobj_sprite) { - // armor - case SPR_ARM1: - if (!player.GiveArmor(1)) { - return; - } - player.message = GOTARMOR; - break; - - case SPR_ARM2: - if (!player.GiveArmor(2)) { - return; - } - player.message = GOTMEGA; - break; - - // bonus items - case SPR_BON1: - player.health[0]++; // can go over 100% - if (player.health[0] > 200) { - player.health[0] = 200; - } - player.mo.health = player.health[0]; - player.message = GOTHTHBONUS; - break; - - case SPR_BON2: - player.armorpoints[0]++; // can go over 100% - if (player.armorpoints[0] > 200) { - player.armorpoints[0] = 200; - } - if (player.armortype == 0) { - player.armortype = 1; - } - player.message = GOTARMBONUS; - break; - - case SPR_SOUL: - player.health[0] += 100; - if (player.health[0] > 200) { - player.health[0] = 200; - } - player.mo.health = player.health[0]; - player.message = GOTSUPER; - sound = sfxenum_t.sfx_getpow; - break; - - case SPR_MEGA: - if (!DOOM.isCommercial()) { - return; - } - player.health[0] = 200; - player.mo.health = player.health[0]; - player.GiveArmor(2); - player.message = GOTMSPHERE; - sound = sfxenum_t.sfx_getpow; - break; - - // cards - // leave cards for everyone - case SPR_BKEY: - if (!player.cards[card_t.it_bluecard.ordinal()]) { - player.message = GOTBLUECARD; - } - player.GiveCard(card_t.it_bluecard); - if (!DOOM.netgame) { - break; - } - return; - - case SPR_YKEY: - if (!player.cards[card_t.it_yellowcard.ordinal()]) { - player.message = GOTYELWCARD; - } - player.GiveCard(card_t.it_yellowcard); - if (!DOOM.netgame) { - break; - } - return; - - case SPR_RKEY: - if (!player.cards[card_t.it_redcard.ordinal()]) { - player.message = GOTREDCARD; - } - player.GiveCard(card_t.it_redcard); - if (!DOOM.netgame) { - break; - } - return; - - case SPR_BSKU: - if (!player.cards[card_t.it_blueskull.ordinal()]) { - player.message = GOTBLUESKUL; - } - player.GiveCard(card_t.it_blueskull); - if (!DOOM.netgame) { - break; - } - return; - - case SPR_YSKU: - if (!player.cards[card_t.it_yellowskull.ordinal()]) { - player.message = GOTYELWSKUL; - } - player.GiveCard(card_t.it_yellowskull); - if (!DOOM.netgame) { - break; - } - return; - - case SPR_RSKU: - if (!player.cards[card_t.it_redskull.ordinal()]) { - player.message = GOTREDSKULL; - } - player.GiveCard(card_t.it_redskull); - if (!DOOM.netgame) { - break; - } - return; - - // medikits, heals - case SPR_STIM: - if (!player.GiveBody(10)) { - return; - } - player.message = GOTSTIM; - break; - - case SPR_MEDI: - /** - * Another fix with switchable option to enable - * - Good Sign 2017/04/03 - */ - boolean need = player.health[0] < 25; - - if (!player.GiveBody(25)) { - return; - } - - if (DOOM.CM.equals(Settings.fix_medi_need, Boolean.FALSE)) // default behavior - with bug - { - player.message = player.health[0] < 25 ? GOTMEDINEED : GOTMEDIKIT; - } else //proper behavior - { - player.message = need ? GOTMEDINEED : GOTMEDIKIT; - } - - break; - - // power ups - case SPR_PINV: - if (!player.GivePower(pw_invulnerability)) { - return; - } - player.message = GOTINVUL; - sound = sfxenum_t.sfx_getpow; - break; - - case SPR_PSTR: - if (!player.GivePower(pw_strength)) { - return; - } - player.message = GOTBERSERK; - if (player.readyweapon != weapontype_t.wp_fist) { - player.pendingweapon = weapontype_t.wp_fist; - } - sound = sfxenum_t.sfx_getpow; - break; - - case SPR_PINS: - if (!player.GivePower(pw_invisibility)) { - return; - } - player.message = GOTINVIS; - sound = sfxenum_t.sfx_getpow; - break; - - case SPR_SUIT: - if (!player.GivePower(pw_ironfeet)) { - return; - } - player.message = GOTSUIT; - sound = sfxenum_t.sfx_getpow; - break; - - case SPR_PMAP: - if (!player.GivePower(pw_allmap)) { - return; - } - player.message = GOTMAP; - sound = sfxenum_t.sfx_getpow; - break; - - case SPR_PVIS: - if (!player.GivePower(pw_infrared)) { - return; - } - player.message = GOTVISOR; - sound = sfxenum_t.sfx_getpow; - break; - - // ammo - case SPR_CLIP: - if ((special.flags & MF_DROPPED) != 0) { - if (!player.GiveAmmo(ammotype_t.am_clip, 0)) { - return; - } - } else { - if (!player.GiveAmmo(ammotype_t.am_clip, 1)) { - return; - } - } - player.message = GOTCLIP; - break; - - case SPR_AMMO: - if (!player.GiveAmmo(ammotype_t.am_clip, 5)) { - return; - } - player.message = GOTCLIPBOX; - break; - - case SPR_ROCK: - if (!player.GiveAmmo(ammotype_t.am_misl, 1)) { - return; - } - player.message = GOTROCKET; - break; - - case SPR_BROK: - if (!player.GiveAmmo(ammotype_t.am_misl, 5)) { - return; - } - player.message = GOTROCKBOX; - break; - - case SPR_CELL: - if (!player.GiveAmmo(ammotype_t.am_cell, 1)) { - return; - } - player.message = GOTCELL; - break; - - case SPR_CELP: - if (!player.GiveAmmo(ammotype_t.am_cell, 5)) { - return; - } - player.message = GOTCELLBOX; - break; - - case SPR_SHEL: - if (!player.GiveAmmo(ammotype_t.am_shell, 1)) { - return; - } - player.message = GOTSHELLS; - break; - - case SPR_SBOX: - if (!player.GiveAmmo(ammotype_t.am_shell, 5)) { - return; - } - player.message = GOTSHELLBOX; - break; - - case SPR_BPAK: - if (!player.backpack) { - for (i = 0; i < NUMAMMO; i++) { - player.maxammo[i] *= 2; - } - player.backpack = true; - } - for (i = 0; i < NUMAMMO; i++) { - player.GiveAmmo(ammotype_t.values()[i], 1); - } - player.message = GOTBACKPACK; - break; - - // weapons - case SPR_BFUG: - if (!player.GiveWeapon(weapontype_t.wp_bfg, false)) { - return; - } - player.message = GOTBFG9000; - sound = sfxenum_t.sfx_wpnup; - break; - - case SPR_MGUN: - if (!player.GiveWeapon(weapontype_t.wp_chaingun, - (special.flags & MF_DROPPED) != 0)) { - return; - } - player.message = GOTCHAINGUN; - sound = sfxenum_t.sfx_wpnup; - break; - - case SPR_CSAW: - if (!player.GiveWeapon(weapontype_t.wp_chainsaw, false)) { - return; - } - player.message = GOTCHAINSAW; - sound = sfxenum_t.sfx_wpnup; - break; - - case SPR_LAUN: - if (!player.GiveWeapon(weapontype_t.wp_missile, false)) { - return; - } - player.message = GOTLAUNCHER; - sound = sfxenum_t.sfx_wpnup; - break; - - case SPR_PLAS: - if (!player.GiveWeapon(weapontype_t.wp_plasma, false)) { - return; - } - player.message = GOTPLASMA; - sound = sfxenum_t.sfx_wpnup; - break; - - case SPR_SHOT: - if (!player.GiveWeapon(weapontype_t.wp_shotgun, - (special.flags & MF_DROPPED) != 0)) { - return; - } - player.message = GOTSHOTGUN; - sound = sfxenum_t.sfx_wpnup; - break; - - case SPR_SGN2: - if (!player.GiveWeapon(weapontype_t.wp_supershotgun, - (special.flags & MF_DROPPED) != 0)) { - return; - } - player.message = GOTSHOTGUN2; - sound = sfxenum_t.sfx_wpnup; - break; - - default: - DOOM.doomSystem.Error("P_SpecialThing: Unknown gettable thing"); - } - - if ((special.flags & MF_COUNTITEM) != 0) { - player.itemcount++; - } - RemoveMobj(special); - player.bonuscount += player_t.BONUSADD; - if (player == DOOM.players[DOOM.consoleplayer]) { - DOOM.doomSound.StartSound(null, sound); - } - } - - /** - * PIT_StompThing - */ - @Override - @P_Map.C(PIT_StompThing) - default boolean StompThing(mobj_t thing) { - final Movement mov = contextRequire(KEY_MOVEMENT); - @fixed_t - int blockdist; - - if ((thing.flags & MF_SHOOTABLE) == 0) { - return true; - } - - blockdist = thing.radius + mov.tmthing.radius; - - if (Math.abs(thing.x - mov.tmx) >= blockdist || Math.abs(thing.y - mov.tmy) >= blockdist) { - // didn't hit it - return true; - } - - // don't clip against self - if (thing == mov.tmthing) { - return true; - } - - // monsters don't stomp things except on boss level - if ((mov.tmthing.player == null) && (MapNumber() != 30)) { - return false; - } - - DamageMobj(thing, mov.tmthing, mov.tmthing, 10000); // in interaction - return true; - } -; -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import static data.Defines.NUMAMMO; +import static data.Defines.pw_allmap; +import static data.Defines.pw_infrared; +import static data.Defines.pw_invisibility; +import static data.Defines.pw_invulnerability; +import static data.Defines.pw_ironfeet; +import static data.Defines.pw_strength; +import data.mobjtype_t; +import data.sounds.sfxenum_t; +import defines.ammotype_t; +import defines.card_t; +import doom.DoomMain; +import doom.SourceCode.P_Map; +import static doom.SourceCode.P_Map.PIT_CheckThing; +import static doom.SourceCode.P_Map.PIT_StompThing; +import doom.SourceCode.fixed_t; +import static doom.englsh.*; +import doom.player_t; +import doom.weapontype_t; +import m.Settings; +import static m.fixed_t.FRACUNIT; +import p.mobj_t; +import static p.mobj_t.MF_COUNTITEM; +import static p.mobj_t.MF_DROPPED; +import static p.mobj_t.MF_MISSILE; +import static p.mobj_t.MF_PICKUP; +import static p.mobj_t.MF_SHOOTABLE; +import static p.mobj_t.MF_SKULLFLY; +import static p.mobj_t.MF_SOLID; +import static p.mobj_t.MF_SPECIAL; +import static utils.C2JUtils.eval; + +public interface ActionsThings extends ActionTrait { + + void DamageMobj(mobj_t thing, mobj_t tmthing, mobj_t tmthing0, int damage); + + void RemoveMobj(mobj_t special); + + /** + * PIT_CheckThing + */ + @Override + @P_Map.C(PIT_CheckThing) + default boolean CheckThing(mobj_t thing) { + final Movement movm = contextRequire(KEY_MOVEMENT); + @fixed_t + int blockdist; + boolean solid; + int damage; + + if ((thing.flags & (MF_SOLID | MF_SPECIAL | MF_SHOOTABLE)) == 0) { + return true; + } + + blockdist = thing.radius + movm.tmthing.radius; + + if (Math.abs(thing.x - movm.tmx) >= blockdist + || Math.abs(thing.y - movm.tmy) >= blockdist) { + // didn't hit it + return true; + } + + // don't clip against self + if (thing == movm.tmthing) { + return true; + } + + // check for skulls slamming into things + if ((movm.tmthing.flags & MF_SKULLFLY) != 0) { + damage = ((P_Random() % 8) + 1) * movm.tmthing.info.damage; + + DamageMobj(thing, movm.tmthing, movm.tmthing, damage); + + movm.tmthing.flags &= ~MF_SKULLFLY; + movm.tmthing.momx = movm.tmthing.momy = movm.tmthing.momz = 0; + + movm.tmthing.SetMobjState(movm.tmthing.info.spawnstate); + + return false; // stop moving + } + + // missiles can hit other things + if (eval(movm.tmthing.flags & MF_MISSILE)) { + // see if it went over / under + if (movm.tmthing.z > thing.z + thing.height) { + return true; // overhead + } + if (movm.tmthing.z + movm.tmthing.height < thing.z) { + return true; // underneath + } + if (movm.tmthing.target != null && (movm.tmthing.target.type == thing.type + || (movm.tmthing.target.type == mobjtype_t.MT_KNIGHT && thing.type == mobjtype_t.MT_BRUISER) + || (movm.tmthing.target.type == mobjtype_t.MT_BRUISER && thing.type == mobjtype_t.MT_KNIGHT))) { + // Don't hit same species as originator. + if (thing == movm.tmthing.target) { + return true; + } + + if (thing.type != mobjtype_t.MT_PLAYER) { + // Explode, but do no damage. + // Let players missile other players. + return false; + } + } + + if (!eval(thing.flags & MF_SHOOTABLE)) { + // didn't do any damage + return !eval(thing.flags & MF_SOLID); + } + + // damage / explode + damage = ((P_Random() % 8) + 1) * movm.tmthing.info.damage; + DamageMobj(thing, movm.tmthing, movm.tmthing.target, damage); + + // don't traverse any more + return false; + } + + // check for special pickup + if (eval(thing.flags & MF_SPECIAL)) { + solid = eval(thing.flags & MF_SOLID); + if (eval(movm.tmflags & MF_PICKUP)) { + // can remove thing + TouchSpecialThing(thing, movm.tmthing); + } + return !solid; + } + + return !eval(thing.flags & MF_SOLID); + } + + ; + + /** + * P_TouchSpecialThing LIKE ROMERO's ASS!!! + */ + default void TouchSpecialThing(mobj_t special, mobj_t toucher) { + final DoomMain DOOM = DOOM(); + player_t player; + int i; + @fixed_t + int delta; + sfxenum_t sound; + + delta = special.z - toucher.z; + + if (delta > toucher.height || delta < -8 * FRACUNIT) { + // out of reach + return; + } + + sound = sfxenum_t.sfx_itemup; + player = toucher.player; + + // Dead thing touching. + // Can happen with a sliding player corpse. + if (toucher.health <= 0) { + return; + } + + // Identify by sprite. + switch (special.mobj_sprite) { + // armor + case SPR_ARM1: + if (!player.GiveArmor(1)) { + return; + } + player.message = GOTARMOR; + break; + + case SPR_ARM2: + if (!player.GiveArmor(2)) { + return; + } + player.message = GOTMEGA; + break; + + // bonus items + case SPR_BON1: + player.health[0]++; // can go over 100% + if (player.health[0] > 200) { + player.health[0] = 200; + } + player.mo.health = player.health[0]; + player.message = GOTHTHBONUS; + break; + + case SPR_BON2: + player.armorpoints[0]++; // can go over 100% + if (player.armorpoints[0] > 200) { + player.armorpoints[0] = 200; + } + if (player.armortype == 0) { + player.armortype = 1; + } + player.message = GOTARMBONUS; + break; + + case SPR_SOUL: + player.health[0] += 100; + if (player.health[0] > 200) { + player.health[0] = 200; + } + player.mo.health = player.health[0]; + player.message = GOTSUPER; + sound = sfxenum_t.sfx_getpow; + break; + + case SPR_MEGA: + if (!DOOM.isCommercial()) { + return; + } + player.health[0] = 200; + player.mo.health = player.health[0]; + player.GiveArmor(2); + player.message = GOTMSPHERE; + sound = sfxenum_t.sfx_getpow; + break; + + // cards + // leave cards for everyone + case SPR_BKEY: + if (!player.cards[card_t.it_bluecard.ordinal()]) { + player.message = GOTBLUECARD; + } + player.GiveCard(card_t.it_bluecard); + if (!DOOM.netgame) { + break; + } + return; + + case SPR_YKEY: + if (!player.cards[card_t.it_yellowcard.ordinal()]) { + player.message = GOTYELWCARD; + } + player.GiveCard(card_t.it_yellowcard); + if (!DOOM.netgame) { + break; + } + return; + + case SPR_RKEY: + if (!player.cards[card_t.it_redcard.ordinal()]) { + player.message = GOTREDCARD; + } + player.GiveCard(card_t.it_redcard); + if (!DOOM.netgame) { + break; + } + return; + + case SPR_BSKU: + if (!player.cards[card_t.it_blueskull.ordinal()]) { + player.message = GOTBLUESKUL; + } + player.GiveCard(card_t.it_blueskull); + if (!DOOM.netgame) { + break; + } + return; + + case SPR_YSKU: + if (!player.cards[card_t.it_yellowskull.ordinal()]) { + player.message = GOTYELWSKUL; + } + player.GiveCard(card_t.it_yellowskull); + if (!DOOM.netgame) { + break; + } + return; + + case SPR_RSKU: + if (!player.cards[card_t.it_redskull.ordinal()]) { + player.message = GOTREDSKULL; + } + player.GiveCard(card_t.it_redskull); + if (!DOOM.netgame) { + break; + } + return; + + // medikits, heals + case SPR_STIM: + if (!player.GiveBody(10)) { + return; + } + player.message = GOTSTIM; + break; + + case SPR_MEDI: + /** + * Another fix with switchable option to enable + * - Good Sign 2017/04/03 + */ + boolean need = player.health[0] < 25; + + if (!player.GiveBody(25)) { + return; + } + + if (DOOM.CM.equals(Settings.fix_medi_need, Boolean.FALSE)) // default behavior - with bug + { + player.message = player.health[0] < 25 ? GOTMEDINEED : GOTMEDIKIT; + } else //proper behavior + { + player.message = need ? GOTMEDINEED : GOTMEDIKIT; + } + + break; + + // power ups + case SPR_PINV: + if (!player.GivePower(pw_invulnerability)) { + return; + } + player.message = GOTINVUL; + sound = sfxenum_t.sfx_getpow; + break; + + case SPR_PSTR: + if (!player.GivePower(pw_strength)) { + return; + } + player.message = GOTBERSERK; + if (player.readyweapon != weapontype_t.wp_fist) { + player.pendingweapon = weapontype_t.wp_fist; + } + sound = sfxenum_t.sfx_getpow; + break; + + case SPR_PINS: + if (!player.GivePower(pw_invisibility)) { + return; + } + player.message = GOTINVIS; + sound = sfxenum_t.sfx_getpow; + break; + + case SPR_SUIT: + if (!player.GivePower(pw_ironfeet)) { + return; + } + player.message = GOTSUIT; + sound = sfxenum_t.sfx_getpow; + break; + + case SPR_PMAP: + if (!player.GivePower(pw_allmap)) { + return; + } + player.message = GOTMAP; + sound = sfxenum_t.sfx_getpow; + break; + + case SPR_PVIS: + if (!player.GivePower(pw_infrared)) { + return; + } + player.message = GOTVISOR; + sound = sfxenum_t.sfx_getpow; + break; + + // ammo + case SPR_CLIP: + if ((special.flags & MF_DROPPED) != 0) { + if (!player.GiveAmmo(ammotype_t.am_clip, 0)) { + return; + } + } else { + if (!player.GiveAmmo(ammotype_t.am_clip, 1)) { + return; + } + } + player.message = GOTCLIP; + break; + + case SPR_AMMO: + if (!player.GiveAmmo(ammotype_t.am_clip, 5)) { + return; + } + player.message = GOTCLIPBOX; + break; + + case SPR_ROCK: + if (!player.GiveAmmo(ammotype_t.am_misl, 1)) { + return; + } + player.message = GOTROCKET; + break; + + case SPR_BROK: + if (!player.GiveAmmo(ammotype_t.am_misl, 5)) { + return; + } + player.message = GOTROCKBOX; + break; + + case SPR_CELL: + if (!player.GiveAmmo(ammotype_t.am_cell, 1)) { + return; + } + player.message = GOTCELL; + break; + + case SPR_CELP: + if (!player.GiveAmmo(ammotype_t.am_cell, 5)) { + return; + } + player.message = GOTCELLBOX; + break; + + case SPR_SHEL: + if (!player.GiveAmmo(ammotype_t.am_shell, 1)) { + return; + } + player.message = GOTSHELLS; + break; + + case SPR_SBOX: + if (!player.GiveAmmo(ammotype_t.am_shell, 5)) { + return; + } + player.message = GOTSHELLBOX; + break; + + case SPR_BPAK: + if (!player.backpack) { + for (i = 0; i < NUMAMMO; i++) { + player.maxammo[i] *= 2; + } + player.backpack = true; + } + for (i = 0; i < NUMAMMO; i++) { + player.GiveAmmo(ammotype_t.values()[i], 1); + } + player.message = GOTBACKPACK; + break; + + // weapons + case SPR_BFUG: + if (!player.GiveWeapon(weapontype_t.wp_bfg, false)) { + return; + } + player.message = GOTBFG9000; + sound = sfxenum_t.sfx_wpnup; + break; + + case SPR_MGUN: + if (!player.GiveWeapon(weapontype_t.wp_chaingun, + (special.flags & MF_DROPPED) != 0)) { + return; + } + player.message = GOTCHAINGUN; + sound = sfxenum_t.sfx_wpnup; + break; + + case SPR_CSAW: + if (!player.GiveWeapon(weapontype_t.wp_chainsaw, false)) { + return; + } + player.message = GOTCHAINSAW; + sound = sfxenum_t.sfx_wpnup; + break; + + case SPR_LAUN: + if (!player.GiveWeapon(weapontype_t.wp_missile, false)) { + return; + } + player.message = GOTLAUNCHER; + sound = sfxenum_t.sfx_wpnup; + break; + + case SPR_PLAS: + if (!player.GiveWeapon(weapontype_t.wp_plasma, false)) { + return; + } + player.message = GOTPLASMA; + sound = sfxenum_t.sfx_wpnup; + break; + + case SPR_SHOT: + if (!player.GiveWeapon(weapontype_t.wp_shotgun, + (special.flags & MF_DROPPED) != 0)) { + return; + } + player.message = GOTSHOTGUN; + sound = sfxenum_t.sfx_wpnup; + break; + + case SPR_SGN2: + if (!player.GiveWeapon(weapontype_t.wp_supershotgun, + (special.flags & MF_DROPPED) != 0)) { + return; + } + player.message = GOTSHOTGUN2; + sound = sfxenum_t.sfx_wpnup; + break; + + default: + DOOM.doomSystem.Error("P_SpecialThing: Unknown gettable thing"); + } + + if ((special.flags & MF_COUNTITEM) != 0) { + player.itemcount++; + } + RemoveMobj(special); + player.bonuscount += player_t.BONUSADD; + if (player == DOOM.players[DOOM.consoleplayer]) { + DOOM.doomSound.StartSound(null, sound); + } + } + + /** + * PIT_StompThing + */ + @Override + @P_Map.C(PIT_StompThing) + default boolean StompThing(mobj_t thing) { + final Movement mov = contextRequire(KEY_MOVEMENT); + @fixed_t + int blockdist; + + if ((thing.flags & MF_SHOOTABLE) == 0) { + return true; + } + + blockdist = thing.radius + mov.tmthing.radius; + + if (Math.abs(thing.x - mov.tmx) >= blockdist || Math.abs(thing.y - mov.tmy) >= blockdist) { + // didn't hit it + return true; + } + + // don't clip against self + if (thing == mov.tmthing) { + return true; + } + + // monsters don't stomp things except on boss level + if ((mov.tmthing.player == null) && (MapNumber() != 30)) { + return false; + } + + DamageMobj(thing, mov.tmthing, mov.tmthing, 10000); // in interaction + return true; + } +; +} \ No newline at end of file diff --git a/src/p/Actions/ActionsThinkers.java b/src/p/Actions/ActionsThinkers.java index 5baa4e6..01c41b7 100644 --- a/src/p/Actions/ActionsThinkers.java +++ b/src/p/Actions/ActionsThinkers.java @@ -1,347 +1,347 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import static data.Defines.ITEMQUESIZE; -import static data.Defines.ONCEILINGZ; -import static data.Defines.ONFLOORZ; -import static data.Limits.MAXPLAYERS; -import static data.Tables.ANG45; -import static data.info.mobjinfo; -import data.mapthing_t; -import data.mobjtype_t; -import data.sounds; -import doom.CommandVariable; -import doom.DoomMain; -import doom.SourceCode; -import doom.SourceCode.CauseOfDesyncProbability; -import doom.SourceCode.P_Spec; -import static doom.SourceCode.P_Spec.P_SpawnSpecials; -import static doom.SourceCode.P_Tick.P_RemoveThinker; -import doom.thinker_t; -import static m.fixed_t.FRACBITS; -import p.AbstractLevelLoader; -import p.ActiveStates; -import p.ActiveStates.MobjConsumer; -import p.ActiveStates.ThinkerConsumer; -import static p.DoorDefines.FASTDARK; -import static p.DoorDefines.SLOWDARK; -import p.RemoveState; -import p.ThinkerList; -import p.UnifiedGameMap; -import p.mobj_t; -import static p.mobj_t.MF_SPAWNCEILING; -import rr.sector_t; -import rr.subsector_t; -import static utils.C2JUtils.eval; - -public interface ActionsThinkers extends ActionsSectors, ThinkerList { - - // - // P_RemoveThinker - // Deallocation is lazy -- it will not actually be freed - // until its thinking turn comes up. - // - // - // killough 4/25/98: - // - // Instead of marking the function with -1 value cast to a function pointer, - // set the function to P_RemoveThinkerDelayed(), so that later, it will be - // removed automatically as part of the thinker process. - // - @Override - @SourceCode.Compatible("thinker->function.acv = (actionf_v)(-1)") - @SourceCode.P_Tick.C(P_RemoveThinker) - default void RemoveThinker(thinker_t thinker) { - thinker.thinkerFunction = RemoveState.REMOVE; - } - - /** - * P_SpawnSpecials After the map has been loaded, scan for specials that spawn thinkers - */ - @SourceCode.Suspicious(CauseOfDesyncProbability.LOW) - @P_Spec.C(P_SpawnSpecials) - default void SpawnSpecials() { - final DoomMain D = DOOM(); - final AbstractLevelLoader ll = levelLoader(); - final UnifiedGameMap.Specials sp = getSpecials(); - sector_t sector; - - /*int episode; - - episode = 1; - if (W.CheckNumForName("texture2") >= 0) - episode = 2; - */ - // See if -TIMER needs to be used. - sp.levelTimer = false; - - if (D.cVarManager.bool(CommandVariable.AVG) && IsDeathMatch()) { - sp.levelTimer = true; - sp.levelTimeCount = 20 * 60 * 35; - } - - if (IsDeathMatch()) { - D.cVarManager.with(CommandVariable.TIMER, 0, (Integer i) -> { - sp.levelTimer = true; - sp.levelTimeCount = i * 60 * 35; - }); - } - - // Init special SECTORs. - //sector = LL.sectors; - for (int i = 0; i < ll.numsectors; i++) { - sector = ll.sectors[i]; - if (!eval(sector.special)) { - continue; - } - - switch (sector.special) { - case 1: - // FLICKERING LIGHTS - P_SpawnLightFlash: - { - SpawnLightFlash(sector); - } - break; - - case 2: - // STROBE FAST - P_SpawnStrobeFlash: - { - SpawnStrobeFlash(sector, FASTDARK, 0); - } - break; - - case 3: - // STROBE SLOW - P_SpawnStrobeFlash: - { - SpawnStrobeFlash(sector, SLOWDARK, 0); - } - break; - - case 4: - // STROBE FAST/DEATH SLIME - P_SpawnStrobeFlash: - { - SpawnStrobeFlash(sector, FASTDARK, 0); - } - sector.special = 4; - break; - - case 8: - // GLOWING LIGHT - P_SpawnGlowingLight: - { - SpawnGlowingLight(sector); - } - break; - case 9: - // SECRET SECTOR - D.totalsecret++; - break; - - case 10: - // DOOR CLOSE IN 30 SECONDS - SpawnDoorCloseIn30: - { - SpawnDoorCloseIn30(sector); - } - break; - - case 12: - // SYNC STROBE SLOW - P_SpawnStrobeFlash: - { - SpawnStrobeFlash(sector, SLOWDARK, 1); - } - break; - - case 13: - // SYNC STROBE FAST - P_SpawnStrobeFlash: - { - SpawnStrobeFlash(sector, FASTDARK, 1); - } - break; - - case 14: - // DOOR RAISE IN 5 MINUTES - P_SpawnDoorRaiseIn5Mins: - { - SpawnDoorRaiseIn5Mins(sector, i); - } - break; - - case 17: - P_SpawnFireFlicker: - { - SpawnFireFlicker(sector); - } - break; - } - } - - // Init line EFFECTs - sp.numlinespecials = 0; - for (int i = 0; i < ll.numlines; i++) { - switch (ll.lines[i].special) { - case 48: - // EFFECT FIRSTCOL SCROLL+ - // Maes 6/4/2012: removed length limit. - if (sp.numlinespecials == sp.linespeciallist.length) { - sp.resizeLinesSpecialList(); - } - sp.linespeciallist[sp.numlinespecials] = ll.lines[i]; - sp.numlinespecials++; - break; - } - } - - // Init other misc stuff - for (int i = 0; i < this.getMaxCeilings(); i++) { - this.getActiveCeilings()[i] = null; - } - - getSwitches().initButtonList(); - - // UNUSED: no horizonal sliders. - // if (SL!=null) { - // SL.updateStatus(DM); - // SL.P_InitSlidingDoorFrames(); - //} - } - - /** - * P_RespawnSpecials - */ - default void RespawnSpecials() { - final RespawnQueue resp = contextRequire(KEY_RESP_QUEUE); - int x, y, z; // fixed - - subsector_t ss; - mobj_t mo; - mapthing_t mthing; - - int i; - - // only respawn items in deathmatch (deathmatch!=2) - if (!DOOM().altdeath) { - return; // - } - // nothing left to respawn? - if (resp.iquehead == resp.iquetail) { - return; - } - - // wait at least 30 seconds - if (LevelTime() - resp.itemrespawntime[resp.iquetail] < 30 * 35) { - return; - } - - mthing = resp.itemrespawnque[resp.iquetail]; - - x = mthing.x << FRACBITS; - y = mthing.y << FRACBITS; - - // spawn a teleport fog at the new spot - ss = levelLoader().PointInSubsector(x, y); - mo = SpawnMobj(x, y, ss.sector.floorheight, mobjtype_t.MT_IFOG); - StartSound(mo, sounds.sfxenum_t.sfx_itmbk); - - // find which type to spawn - for (i = 0; i < mobjtype_t.NUMMOBJTYPES.ordinal(); i++) { - if (mthing.type == mobjinfo[i].doomednum) { - break; - } - } - - // spawn it - if (eval(mobjinfo[i].flags & MF_SPAWNCEILING)) { - z = ONCEILINGZ; - } else { - z = ONFLOORZ; - } - - mo = SpawnMobj(x, y, z, mobjtype_t.values()[i]); - mo.spawnpoint = mthing; - mo.angle = ANG45 * (mthing.angle / 45); - - // pull it from the que - resp.iquetail = (resp.iquetail + 1) & (ITEMQUESIZE - 1); - } - - // - // P_AllocateThinker - // Allocates memory and adds a new thinker at the end of the list. - // - //public void AllocateThinker(thinker_t thinker) {; - // UNUSED - //} - // - // P_RunThinkers - // - default void RunThinkers() { - thinker_t thinker = getThinkerCap().next; - while (thinker != getThinkerCap()) { - if (thinker.thinkerFunction == RemoveState.REMOVE) { - // time to remove it - thinker.next.prev = thinker.prev; - thinker.prev.next = thinker.next; - // Z_Free (currentthinker); - } else { - ActiveStates thinkerFunction = (ActiveStates) thinker.thinkerFunction; - if (thinkerFunction.isParamType(MobjConsumer.class)) { - thinkerFunction.fun(MobjConsumer.class).accept(DOOM().actions, (mobj_t) thinker); - } else if (thinkerFunction.isParamType(ThinkerConsumer.class)) { - thinkerFunction.fun(ThinkerConsumer.class).accept(DOOM().actions, thinker); - } - } - thinker = thinker.next; - } - } - - // - //P_Ticker - // - default void Ticker() { - // run the tic - if (IsPaused()) { - return; - } - - // pause if in menu and at least one tic has been run - if (!IsNetGame() && IsMenuActive() && !IsDemoPlayback() && getPlayer(ConsolePlayerNumber()).viewz != 1) { - return; - } - - for (int i = 0; i < MAXPLAYERS; i++) { - if (PlayerInGame(i)) { - getPlayer(i).PlayerThink(); - } - } - - RunThinkers(); - getSpecials().UpdateSpecials(); // In specials. Merge? - RespawnSpecials(); - - // for par times - DOOM().leveltime++; - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import static data.Defines.ITEMQUESIZE; +import static data.Defines.ONCEILINGZ; +import static data.Defines.ONFLOORZ; +import static data.Limits.MAXPLAYERS; +import static data.Tables.ANG45; +import static data.info.mobjinfo; +import data.mapthing_t; +import data.mobjtype_t; +import data.sounds; +import doom.CommandVariable; +import doom.DoomMain; +import doom.SourceCode; +import doom.SourceCode.CauseOfDesyncProbability; +import doom.SourceCode.P_Spec; +import static doom.SourceCode.P_Spec.P_SpawnSpecials; +import static doom.SourceCode.P_Tick.P_RemoveThinker; +import doom.thinker_t; +import static m.fixed_t.FRACBITS; +import p.AbstractLevelLoader; +import p.ActiveStates; +import p.ActiveStates.MobjConsumer; +import p.ActiveStates.ThinkerConsumer; +import static p.DoorDefines.FASTDARK; +import static p.DoorDefines.SLOWDARK; +import p.RemoveState; +import p.ThinkerList; +import p.UnifiedGameMap; +import p.mobj_t; +import static p.mobj_t.MF_SPAWNCEILING; +import rr.sector_t; +import rr.subsector_t; +import static utils.C2JUtils.eval; + +public interface ActionsThinkers extends ActionsSectors, ThinkerList { + + // + // P_RemoveThinker + // Deallocation is lazy -- it will not actually be freed + // until its thinking turn comes up. + // + // + // killough 4/25/98: + // + // Instead of marking the function with -1 value cast to a function pointer, + // set the function to P_RemoveThinkerDelayed(), so that later, it will be + // removed automatically as part of the thinker process. + // + @Override + @SourceCode.Compatible("thinker->function.acv = (actionf_v)(-1)") + @SourceCode.P_Tick.C(P_RemoveThinker) + default void RemoveThinker(thinker_t thinker) { + thinker.thinkerFunction = RemoveState.REMOVE; + } + + /** + * P_SpawnSpecials After the map has been loaded, scan for specials that spawn thinkers + */ + @SourceCode.Suspicious(CauseOfDesyncProbability.LOW) + @P_Spec.C(P_SpawnSpecials) + default void SpawnSpecials() { + final DoomMain D = DOOM(); + final AbstractLevelLoader ll = levelLoader(); + final UnifiedGameMap.Specials sp = getSpecials(); + sector_t sector; + + /*int episode; + + episode = 1; + if (W.CheckNumForName("texture2") >= 0) + episode = 2; + */ + // See if -TIMER needs to be used. + sp.levelTimer = false; + + if (D.cVarManager.bool(CommandVariable.AVG) && IsDeathMatch()) { + sp.levelTimer = true; + sp.levelTimeCount = 20 * 60 * 35; + } + + if (IsDeathMatch()) { + D.cVarManager.with(CommandVariable.TIMER, 0, (Integer i) -> { + sp.levelTimer = true; + sp.levelTimeCount = i * 60 * 35; + }); + } + + // Init special SECTORs. + //sector = LL.sectors; + for (int i = 0; i < ll.numsectors; i++) { + sector = ll.sectors[i]; + if (!eval(sector.special)) { + continue; + } + + switch (sector.special) { + case 1: + // FLICKERING LIGHTS + P_SpawnLightFlash: + { + SpawnLightFlash(sector); + } + break; + + case 2: + // STROBE FAST + P_SpawnStrobeFlash: + { + SpawnStrobeFlash(sector, FASTDARK, 0); + } + break; + + case 3: + // STROBE SLOW + P_SpawnStrobeFlash: + { + SpawnStrobeFlash(sector, SLOWDARK, 0); + } + break; + + case 4: + // STROBE FAST/DEATH SLIME + P_SpawnStrobeFlash: + { + SpawnStrobeFlash(sector, FASTDARK, 0); + } + sector.special = 4; + break; + + case 8: + // GLOWING LIGHT + P_SpawnGlowingLight: + { + SpawnGlowingLight(sector); + } + break; + case 9: + // SECRET SECTOR + D.totalsecret++; + break; + + case 10: + // DOOR CLOSE IN 30 SECONDS + SpawnDoorCloseIn30: + { + SpawnDoorCloseIn30(sector); + } + break; + + case 12: + // SYNC STROBE SLOW + P_SpawnStrobeFlash: + { + SpawnStrobeFlash(sector, SLOWDARK, 1); + } + break; + + case 13: + // SYNC STROBE FAST + P_SpawnStrobeFlash: + { + SpawnStrobeFlash(sector, FASTDARK, 1); + } + break; + + case 14: + // DOOR RAISE IN 5 MINUTES + P_SpawnDoorRaiseIn5Mins: + { + SpawnDoorRaiseIn5Mins(sector, i); + } + break; + + case 17: + P_SpawnFireFlicker: + { + SpawnFireFlicker(sector); + } + break; + } + } + + // Init line EFFECTs + sp.numlinespecials = 0; + for (int i = 0; i < ll.numlines; i++) { + switch (ll.lines[i].special) { + case 48: + // EFFECT FIRSTCOL SCROLL+ + // Maes 6/4/2012: removed length limit. + if (sp.numlinespecials == sp.linespeciallist.length) { + sp.resizeLinesSpecialList(); + } + sp.linespeciallist[sp.numlinespecials] = ll.lines[i]; + sp.numlinespecials++; + break; + } + } + + // Init other misc stuff + for (int i = 0; i < this.getMaxCeilings(); i++) { + this.getActiveCeilings()[i] = null; + } + + getSwitches().initButtonList(); + + // UNUSED: no horizonal sliders. + // if (SL!=null) { + // SL.updateStatus(DM); + // SL.P_InitSlidingDoorFrames(); + //} + } + + /** + * P_RespawnSpecials + */ + default void RespawnSpecials() { + final RespawnQueue resp = contextRequire(KEY_RESP_QUEUE); + int x, y, z; // fixed + + subsector_t ss; + mobj_t mo; + mapthing_t mthing; + + int i; + + // only respawn items in deathmatch (deathmatch!=2) + if (!DOOM().altdeath) { + return; // + } + // nothing left to respawn? + if (resp.iquehead == resp.iquetail) { + return; + } + + // wait at least 30 seconds + if (LevelTime() - resp.itemrespawntime[resp.iquetail] < 30 * 35) { + return; + } + + mthing = resp.itemrespawnque[resp.iquetail]; + + x = mthing.x << FRACBITS; + y = mthing.y << FRACBITS; + + // spawn a teleport fog at the new spot + ss = levelLoader().PointInSubsector(x, y); + mo = SpawnMobj(x, y, ss.sector.floorheight, mobjtype_t.MT_IFOG); + StartSound(mo, sounds.sfxenum_t.sfx_itmbk); + + // find which type to spawn + for (i = 0; i < mobjtype_t.NUMMOBJTYPES.ordinal(); i++) { + if (mthing.type == mobjinfo[i].doomednum) { + break; + } + } + + // spawn it + if (eval(mobjinfo[i].flags & MF_SPAWNCEILING)) { + z = ONCEILINGZ; + } else { + z = ONFLOORZ; + } + + mo = SpawnMobj(x, y, z, mobjtype_t.values()[i]); + mo.spawnpoint = mthing; + mo.angle = ANG45 * (mthing.angle / 45); + + // pull it from the que + resp.iquetail = (resp.iquetail + 1) & (ITEMQUESIZE - 1); + } + + // + // P_AllocateThinker + // Allocates memory and adds a new thinker at the end of the list. + // + //public void AllocateThinker(thinker_t thinker) {; + // UNUSED + //} + // + // P_RunThinkers + // + default void RunThinkers() { + thinker_t thinker = getThinkerCap().next; + while (thinker != getThinkerCap()) { + if (thinker.thinkerFunction == RemoveState.REMOVE) { + // time to remove it + thinker.next.prev = thinker.prev; + thinker.prev.next = thinker.next; + // Z_Free (currentthinker); + } else { + ActiveStates thinkerFunction = (ActiveStates) thinker.thinkerFunction; + if (thinkerFunction.isParamType(MobjConsumer.class)) { + thinkerFunction.fun(MobjConsumer.class).accept(DOOM().actions, (mobj_t) thinker); + } else if (thinkerFunction.isParamType(ThinkerConsumer.class)) { + thinkerFunction.fun(ThinkerConsumer.class).accept(DOOM().actions, thinker); + } + } + thinker = thinker.next; + } + } + + // + //P_Ticker + // + default void Ticker() { + // run the tic + if (IsPaused()) { + return; + } + + // pause if in menu and at least one tic has been run + if (!IsNetGame() && IsMenuActive() && !IsDemoPlayback() && getPlayer(ConsolePlayerNumber()).viewz != 1) { + return; + } + + for (int i = 0; i < MAXPLAYERS; i++) { + if (PlayerInGame(i)) { + getPlayer(i).PlayerThink(); + } + } + + RunThinkers(); + getSpecials().UpdateSpecials(); // In specials. Merge? + RespawnSpecials(); + + // for par times + DOOM().leveltime++; + } +} \ No newline at end of file diff --git a/src/p/Actions/ActionsUseEvents.java b/src/p/Actions/ActionsUseEvents.java index c65553e..3a4566b 100644 --- a/src/p/Actions/ActionsUseEvents.java +++ b/src/p/Actions/ActionsUseEvents.java @@ -1,539 +1,539 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions; - -import static data.Defines.PT_ADDLINES; -import static data.Defines.USERANGE; -import data.Tables; -import static data.Tables.finecosine; -import static data.Tables.finesine; -import data.sounds; -import doom.SourceCode.P_Map; -import static doom.SourceCode.P_Map.PTR_UseTraverse; -import doom.player_t; -import java.util.function.Predicate; -import static m.fixed_t.FRACBITS; -import p.ceiling_e; -import p.floor_e; -import p.intercept_t; -import p.mobj_t; -import p.plattype_e; -import p.stair_e; -import p.vldoor_e; -import rr.line_t; -import static rr.line_t.ML_SECRET; -import static utils.C2JUtils.eval; - -public interface ActionsUseEvents extends ActionTrait { - - void VerticalDoor(line_t line, mobj_t thing); - - void LightTurnOn(line_t line, int i); - - boolean BuildStairs(line_t line, stair_e stair_e); - - boolean DoDonut(line_t line); - - boolean DoFloor(line_t line, floor_e floor_e); - - boolean DoDoor(line_t line, vldoor_e vldoor_e); - - boolean DoPlat(line_t line, plattype_e plattype_e, int i); - - boolean DoCeiling(line_t line, ceiling_e ceiling_e); - - boolean DoLockedDoor(line_t line, vldoor_e vldoor_e, mobj_t thing); - - boolean PathTraverse(int x1, int y1, int x2, int y2, int flags, Predicate trav); - - /** - * P_UseSpecialLine Called when a thing uses a special line. Only the front sides of lines are usable. - */ - default boolean UseSpecialLine(mobj_t thing, line_t line, boolean side) { - // Err... - // Use the back sides of VERY SPECIAL lines... - if (side) { - switch (line.special) { - case 124: - // Sliding door open&close - // SL.EV_SlidingDoor(line, thing); - break; - - default: - return false; - //break; - } - } - - // Switches that other things can activate. - //_D_: little bug fixed here, see linuxdoom source - if (thing.player ==/*!=*/ null) { - // never open secret doors - if (eval(line.flags & ML_SECRET)) { - return false; - } - - switch (line.special) { - case 1: // MANUAL DOOR RAISE - case 32: // MANUAL BLUE - case 33: // MANUAL RED - case 34: // MANUAL YELLOW - break; - - default: - return false; - //break; - } - } - - // do something - switch (line.special) { - // MANUALS - case 1: // Vertical Door - case 26: // Blue Door/Locked - case 27: // Yellow Door /Locked - case 28: // Red Door /Locked - - case 31: // Manual door open - case 32: // Blue locked door open - case 33: // Red locked door open - case 34: // Yellow locked door open - - case 117: // Blazing door raise - case 118: // Blazing door open - VerticalDoor(line, thing); - break; - - //UNUSED - Door Slide Open&Close - case 124: - // NOTE: clashes with secret level exit. - //SL.EV_SlidingDoor (line, thing); - break; - - // SWITCHES - case 7: - // Build Stairs - if (BuildStairs(line, stair_e.build8)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 9: - // Change Donut - if (DoDonut(line)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 11: - // Exit level - getSwitches().ChangeSwitchTexture(line, false); - DOOM().ExitLevel(); - break; - - case 14: - // Raise Floor 32 and change texture - if (DoPlat(line, plattype_e.raiseAndChange, 32)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 15: - // Raise Floor 24 and change texture - if (DoPlat(line, plattype_e.raiseAndChange, 24)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 18: - // Raise Floor to next highest floor - if (DoFloor(line, floor_e.raiseFloorToNearest)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 20: - // Raise Plat next highest floor and change texture - if (DoPlat(line, plattype_e.raiseToNearestAndChange, 0)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 21: - // PlatDownWaitUpStay - if (DoPlat(line, plattype_e.downWaitUpStay, 0)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 23: - // Lower Floor to Lowest - if (DoFloor(line, floor_e.lowerFloorToLowest)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 29: - // Raise Door - if (DoDoor(line, vldoor_e.normal)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 41: - // Lower Ceiling to Floor - if (DoCeiling(line, ceiling_e.lowerToFloor)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 71: - // Turbo Lower Floor - if (DoFloor(line, floor_e.turboLower)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 49: - // Ceiling Crush And Raise - if (DoCeiling(line, ceiling_e.crushAndRaise)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 50: - // Close Door - if (DoDoor(line, vldoor_e.close)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 51: - // Secret EXIT - getSwitches().ChangeSwitchTexture(line, false); - DOOM().SecretExitLevel(); - break; - - case 55: - // Raise Floor Crush - if (DoFloor(line, floor_e.raiseFloorCrush)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 101: - // Raise Floor - if (DoFloor(line, floor_e.raiseFloor)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 102: - // Lower Floor to Surrounding floor height - if (DoFloor(line, floor_e.lowerFloor)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 103: - // Open Door - if (DoDoor(line, vldoor_e.open)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 111: - // Blazing Door Raise (faster than TURBO!) - if (DoDoor(line, vldoor_e.blazeRaise)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 112: - // Blazing Door Open (faster than TURBO!) - if (DoDoor(line, vldoor_e.blazeOpen)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 113: - // Blazing Door Close (faster than TURBO!) - if (DoDoor(line, vldoor_e.blazeClose)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 122: - // Blazing PlatDownWaitUpStay - if (DoPlat(line, plattype_e.blazeDWUS, 0)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 127: - // Build Stairs Turbo 16 - if (this.BuildStairs(line, stair_e.turbo16)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 131: - // Raise Floor Turbo - if (DoFloor(line, floor_e.raiseFloorTurbo)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 133: - // BlzOpenDoor BLUE - case 135: - // BlzOpenDoor RED - case 137: - // BlzOpenDoor YELLOW - if (DoLockedDoor(line, vldoor_e.blazeOpen, thing)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - case 140: - // Raise Floor 512 - if (DoFloor(line, floor_e.raiseFloor512)) { - getSwitches().ChangeSwitchTexture(line, false); - } - break; - - // BUTTONS - case 42: - // Close Door - if (DoDoor(line, vldoor_e.close)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 43: - // Lower Ceiling to Floor - if (this.DoCeiling(line, ceiling_e.lowerToFloor)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 45: - // Lower Floor to Surrounding floor height - if (DoFloor(line, floor_e.lowerFloor)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 60: - // Lower Floor to Lowest - if (DoFloor(line, floor_e.lowerFloorToLowest)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 61: - // Open Door - if (DoDoor(line, vldoor_e.open)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 62: - // PlatDownWaitUpStay - if (DoPlat(line, plattype_e.downWaitUpStay, 1)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 63: - // Raise Door - if (DoDoor(line, vldoor_e.normal)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 64: - // Raise Floor to ceiling - if (DoFloor(line, floor_e.raiseFloor)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 66: - // Raise Floor 24 and change texture - if (DoPlat(line, plattype_e.raiseAndChange, 24)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 67: - // Raise Floor 32 and change texture - if (DoPlat(line, plattype_e.raiseAndChange, 32)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 65: - // Raise Floor Crush - if (DoFloor(line, floor_e.raiseFloorCrush)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 68: - // Raise Plat to next highest floor and change texture - if (DoPlat(line, plattype_e.raiseToNearestAndChange, 0)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 69: - // Raise Floor to next highest floor - if (DoFloor(line, floor_e.raiseFloorToNearest)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 70: - // Turbo Lower Floor - if (DoFloor(line, floor_e.turboLower)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 114: - // Blazing Door Raise (faster than TURBO!) - if (DoDoor(line, vldoor_e.blazeRaise)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 115: - // Blazing Door Open (faster than TURBO!) - if (DoDoor(line, vldoor_e.blazeOpen)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 116: - // Blazing Door Close (faster than TURBO!) - if (DoDoor(line, vldoor_e.blazeClose)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 123: - // Blazing PlatDownWaitUpStay - if (DoPlat(line, plattype_e.blazeDWUS, 0)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 132: - // Raise Floor Turbo - if (DoFloor(line, floor_e.raiseFloorTurbo)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 99: - // BlzOpenDoor BLUE - case 134: - // BlzOpenDoor RED - case 136: - // BlzOpenDoor YELLOW - if (this.DoLockedDoor(line, vldoor_e.blazeOpen, thing)) { - getSwitches().ChangeSwitchTexture(line, true); - } - break; - - case 138: - // Light Turn On - LightTurnOn(line, 255); - getSwitches().ChangeSwitchTexture(line, true); - break; - - case 139: - // Light Turn Off - LightTurnOn(line, 35); - getSwitches().ChangeSwitchTexture(line, true); - break; - - } - - return true; - } - - /** - * P_UseLines Looks for special lines in front of the player to activate. - */ - default void UseLines(player_t player) { - final Spechits sp = contextRequire(KEY_SPECHITS); - int angle; - int x1, y1, x2, y2; - //System.out.println("Uselines"); - sp.usething = player.mo; - - // Normally this shouldn't cause problems? - angle = Tables.toBAMIndex(player.mo.angle); - - x1 = player.mo.x; - y1 = player.mo.y; - x2 = x1 + (USERANGE >> FRACBITS) * finecosine[angle]; - y2 = y1 + (USERANGE >> FRACBITS) * finesine[angle]; - - PathTraverse(x1, y1, x2, y2, PT_ADDLINES, this::UseTraverse); - } - - // - // USE LINES - // - @P_Map.C(PTR_UseTraverse) - default boolean UseTraverse(intercept_t in) { - final Movement mov = contextRequire(KEY_MOVEMENT); - final Spechits sp = contextRequire(KEY_SPECHITS); - - boolean side; - // FIXME: some sanity check here? - line_t line = (line_t) in.d(); - - if (line.special == 0) { - LineOpening(line); - if (mov.openrange <= 0) { - StartSound(sp.usething, sounds.sfxenum_t.sfx_noway); - - // can't use through a wall - return false; - } - // not a special line, but keep checking - return true; - } - - side = false; - if (line.PointOnLineSide(sp.usething.x, sp.usething.y)) { - side = true; - } - - // return false; // don't use back side - UseSpecialLine(sp.usething, line, side); - - // can't use for than one special line in a row - return false; - } -; -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions; + +import static data.Defines.PT_ADDLINES; +import static data.Defines.USERANGE; +import data.Tables; +import static data.Tables.finecosine; +import static data.Tables.finesine; +import data.sounds; +import doom.SourceCode.P_Map; +import static doom.SourceCode.P_Map.PTR_UseTraverse; +import doom.player_t; +import java.util.function.Predicate; +import static m.fixed_t.FRACBITS; +import p.ceiling_e; +import p.floor_e; +import p.intercept_t; +import p.mobj_t; +import p.plattype_e; +import p.stair_e; +import p.vldoor_e; +import rr.line_t; +import static rr.line_t.ML_SECRET; +import static utils.C2JUtils.eval; + +public interface ActionsUseEvents extends ActionTrait { + + void VerticalDoor(line_t line, mobj_t thing); + + void LightTurnOn(line_t line, int i); + + boolean BuildStairs(line_t line, stair_e stair_e); + + boolean DoDonut(line_t line); + + boolean DoFloor(line_t line, floor_e floor_e); + + boolean DoDoor(line_t line, vldoor_e vldoor_e); + + boolean DoPlat(line_t line, plattype_e plattype_e, int i); + + boolean DoCeiling(line_t line, ceiling_e ceiling_e); + + boolean DoLockedDoor(line_t line, vldoor_e vldoor_e, mobj_t thing); + + boolean PathTraverse(int x1, int y1, int x2, int y2, int flags, Predicate trav); + + /** + * P_UseSpecialLine Called when a thing uses a special line. Only the front sides of lines are usable. + */ + default boolean UseSpecialLine(mobj_t thing, line_t line, boolean side) { + // Err... + // Use the back sides of VERY SPECIAL lines... + if (side) { + switch (line.special) { + case 124: + // Sliding door open&close + // SL.EV_SlidingDoor(line, thing); + break; + + default: + return false; + //break; + } + } + + // Switches that other things can activate. + //_D_: little bug fixed here, see linuxdoom source + if (thing.player ==/*!=*/ null) { + // never open secret doors + if (eval(line.flags & ML_SECRET)) { + return false; + } + + switch (line.special) { + case 1: // MANUAL DOOR RAISE + case 32: // MANUAL BLUE + case 33: // MANUAL RED + case 34: // MANUAL YELLOW + break; + + default: + return false; + //break; + } + } + + // do something + switch (line.special) { + // MANUALS + case 1: // Vertical Door + case 26: // Blue Door/Locked + case 27: // Yellow Door /Locked + case 28: // Red Door /Locked + + case 31: // Manual door open + case 32: // Blue locked door open + case 33: // Red locked door open + case 34: // Yellow locked door open + + case 117: // Blazing door raise + case 118: // Blazing door open + VerticalDoor(line, thing); + break; + + //UNUSED - Door Slide Open&Close + case 124: + // NOTE: clashes with secret level exit. + //SL.EV_SlidingDoor (line, thing); + break; + + // SWITCHES + case 7: + // Build Stairs + if (BuildStairs(line, stair_e.build8)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 9: + // Change Donut + if (DoDonut(line)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 11: + // Exit level + getSwitches().ChangeSwitchTexture(line, false); + DOOM().ExitLevel(); + break; + + case 14: + // Raise Floor 32 and change texture + if (DoPlat(line, plattype_e.raiseAndChange, 32)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 15: + // Raise Floor 24 and change texture + if (DoPlat(line, plattype_e.raiseAndChange, 24)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 18: + // Raise Floor to next highest floor + if (DoFloor(line, floor_e.raiseFloorToNearest)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 20: + // Raise Plat next highest floor and change texture + if (DoPlat(line, plattype_e.raiseToNearestAndChange, 0)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 21: + // PlatDownWaitUpStay + if (DoPlat(line, plattype_e.downWaitUpStay, 0)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 23: + // Lower Floor to Lowest + if (DoFloor(line, floor_e.lowerFloorToLowest)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 29: + // Raise Door + if (DoDoor(line, vldoor_e.normal)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 41: + // Lower Ceiling to Floor + if (DoCeiling(line, ceiling_e.lowerToFloor)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 71: + // Turbo Lower Floor + if (DoFloor(line, floor_e.turboLower)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 49: + // Ceiling Crush And Raise + if (DoCeiling(line, ceiling_e.crushAndRaise)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 50: + // Close Door + if (DoDoor(line, vldoor_e.close)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 51: + // Secret EXIT + getSwitches().ChangeSwitchTexture(line, false); + DOOM().SecretExitLevel(); + break; + + case 55: + // Raise Floor Crush + if (DoFloor(line, floor_e.raiseFloorCrush)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 101: + // Raise Floor + if (DoFloor(line, floor_e.raiseFloor)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 102: + // Lower Floor to Surrounding floor height + if (DoFloor(line, floor_e.lowerFloor)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 103: + // Open Door + if (DoDoor(line, vldoor_e.open)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 111: + // Blazing Door Raise (faster than TURBO!) + if (DoDoor(line, vldoor_e.blazeRaise)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 112: + // Blazing Door Open (faster than TURBO!) + if (DoDoor(line, vldoor_e.blazeOpen)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 113: + // Blazing Door Close (faster than TURBO!) + if (DoDoor(line, vldoor_e.blazeClose)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 122: + // Blazing PlatDownWaitUpStay + if (DoPlat(line, plattype_e.blazeDWUS, 0)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 127: + // Build Stairs Turbo 16 + if (this.BuildStairs(line, stair_e.turbo16)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 131: + // Raise Floor Turbo + if (DoFloor(line, floor_e.raiseFloorTurbo)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 133: + // BlzOpenDoor BLUE + case 135: + // BlzOpenDoor RED + case 137: + // BlzOpenDoor YELLOW + if (DoLockedDoor(line, vldoor_e.blazeOpen, thing)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + case 140: + // Raise Floor 512 + if (DoFloor(line, floor_e.raiseFloor512)) { + getSwitches().ChangeSwitchTexture(line, false); + } + break; + + // BUTTONS + case 42: + // Close Door + if (DoDoor(line, vldoor_e.close)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 43: + // Lower Ceiling to Floor + if (this.DoCeiling(line, ceiling_e.lowerToFloor)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 45: + // Lower Floor to Surrounding floor height + if (DoFloor(line, floor_e.lowerFloor)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 60: + // Lower Floor to Lowest + if (DoFloor(line, floor_e.lowerFloorToLowest)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 61: + // Open Door + if (DoDoor(line, vldoor_e.open)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 62: + // PlatDownWaitUpStay + if (DoPlat(line, plattype_e.downWaitUpStay, 1)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 63: + // Raise Door + if (DoDoor(line, vldoor_e.normal)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 64: + // Raise Floor to ceiling + if (DoFloor(line, floor_e.raiseFloor)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 66: + // Raise Floor 24 and change texture + if (DoPlat(line, plattype_e.raiseAndChange, 24)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 67: + // Raise Floor 32 and change texture + if (DoPlat(line, plattype_e.raiseAndChange, 32)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 65: + // Raise Floor Crush + if (DoFloor(line, floor_e.raiseFloorCrush)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 68: + // Raise Plat to next highest floor and change texture + if (DoPlat(line, plattype_e.raiseToNearestAndChange, 0)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 69: + // Raise Floor to next highest floor + if (DoFloor(line, floor_e.raiseFloorToNearest)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 70: + // Turbo Lower Floor + if (DoFloor(line, floor_e.turboLower)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 114: + // Blazing Door Raise (faster than TURBO!) + if (DoDoor(line, vldoor_e.blazeRaise)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 115: + // Blazing Door Open (faster than TURBO!) + if (DoDoor(line, vldoor_e.blazeOpen)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 116: + // Blazing Door Close (faster than TURBO!) + if (DoDoor(line, vldoor_e.blazeClose)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 123: + // Blazing PlatDownWaitUpStay + if (DoPlat(line, plattype_e.blazeDWUS, 0)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 132: + // Raise Floor Turbo + if (DoFloor(line, floor_e.raiseFloorTurbo)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 99: + // BlzOpenDoor BLUE + case 134: + // BlzOpenDoor RED + case 136: + // BlzOpenDoor YELLOW + if (this.DoLockedDoor(line, vldoor_e.blazeOpen, thing)) { + getSwitches().ChangeSwitchTexture(line, true); + } + break; + + case 138: + // Light Turn On + LightTurnOn(line, 255); + getSwitches().ChangeSwitchTexture(line, true); + break; + + case 139: + // Light Turn Off + LightTurnOn(line, 35); + getSwitches().ChangeSwitchTexture(line, true); + break; + + } + + return true; + } + + /** + * P_UseLines Looks for special lines in front of the player to activate. + */ + default void UseLines(player_t player) { + final Spechits sp = contextRequire(KEY_SPECHITS); + int angle; + int x1, y1, x2, y2; + //System.out.println("Uselines"); + sp.usething = player.mo; + + // Normally this shouldn't cause problems? + angle = Tables.toBAMIndex(player.mo.angle); + + x1 = player.mo.x; + y1 = player.mo.y; + x2 = x1 + (USERANGE >> FRACBITS) * finecosine[angle]; + y2 = y1 + (USERANGE >> FRACBITS) * finesine[angle]; + + PathTraverse(x1, y1, x2, y2, PT_ADDLINES, this::UseTraverse); + } + + // + // USE LINES + // + @P_Map.C(PTR_UseTraverse) + default boolean UseTraverse(intercept_t in) { + final Movement mov = contextRequire(KEY_MOVEMENT); + final Spechits sp = contextRequire(KEY_SPECHITS); + + boolean side; + // FIXME: some sanity check here? + line_t line = (line_t) in.d(); + + if (line.special == 0) { + LineOpening(line); + if (mov.openrange <= 0) { + StartSound(sp.usething, sounds.sfxenum_t.sfx_noway); + + // can't use through a wall + return false; + } + // not a special line, but keep checking + return true; + } + + side = false; + if (line.PointOnLineSide(sp.usething.x, sp.usething.y)) { + side = true; + } + + // return false; // don't use back side + UseSpecialLine(sp.usething, line, side); + + // can't use for than one special line in a row + return false; + } +; +} \ No newline at end of file diff --git a/src/p/Actions/ActiveStates/Ai.java b/src/p/Actions/ActiveStates/Ai.java index 0606b91..eaec2ea 100644 --- a/src/p/Actions/ActiveStates/Ai.java +++ b/src/p/Actions/ActiveStates/Ai.java @@ -1,295 +1,295 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions.ActiveStates; - -import static data.Tables.ANG45; -import static data.Tables.BITS32; -import data.mobjtype_t; -import data.sounds; -import defines.skill_t; -import defines.statenum_t; -import p.mobj_t; -import static p.mobj_t.MF_AMBUSH; -import static p.mobj_t.MF_COUNTKILL; -import static p.mobj_t.MF_JUSTATTACKED; -import static p.mobj_t.MF_SHADOW; -import static p.mobj_t.MF_SHOOTABLE; -import static p.mobj_t.MF_SKULLFLY; -import static p.mobj_t.MF_SOLID; -import static utils.C2JUtils.eval; - -public interface Ai extends Monsters, Sounds { - - // - // A_Look - // Stay in state until a player is sighted. - // - default void A_Look(mobj_t actor) { - mobj_t targ; - boolean seeyou = false; // to avoid the fugly goto - - actor.threshold = 0; // any shot will wake up - targ = actor.subsector.sector.soundtarget; - - if (targ != null - && eval(targ.flags & MF_SHOOTABLE)) { - actor.target = targ; - - if (eval(actor.flags & MF_AMBUSH)) { - seeyou = getEnemies().CheckSight(actor, actor.target); - } else { - seeyou = true; - } - } - if (!seeyou) { - if (!getEnemies().LookForPlayers(actor, false)) { - return; - } - } - - // go into chase state - seeyou: - if (actor.info.seesound != null && actor.info.seesound != sounds.sfxenum_t.sfx_None) { - int sound; - - switch (actor.info.seesound) { - case sfx_posit1: - case sfx_posit2: - case sfx_posit3: - sound = sounds.sfxenum_t.sfx_posit1.ordinal() + P_Random() % 3; - break; - - case sfx_bgsit1: - case sfx_bgsit2: - sound = sounds.sfxenum_t.sfx_bgsit1.ordinal() + P_Random() % 2; - break; - - default: - sound = actor.info.seesound.ordinal(); - break; - } - - if (actor.type == mobjtype_t.MT_SPIDER || actor.type == mobjtype_t.MT_CYBORG) { - // full volume - StartSound(null, sound); - } else { - StartSound(actor, sound); - } - } - - actor.SetMobjState(actor.info.seestate); - } - - /** - * A_Chase - * Actor has a melee attack, - * so it tries to close as fast as possible - */ - @Override - default void A_Chase(mobj_t actor) { - int delta; - boolean nomissile = false; // for the fugly goto - - if (actor.reactiontime != 0) { - actor.reactiontime--; - } - - // modify target threshold - if (actor.threshold != 0) { - if (actor.target == null || actor.target.health <= 0) { - actor.threshold = 0; - } else { - actor.threshold--; - } - } - - // turn towards movement direction if not there yet - if (actor.movedir < 8) { - actor.angle &= (7 << 29); - actor.angle &= BITS32; - // Nice problem, here! - delta = (int) (actor.angle - (actor.movedir << 29)); - - if (delta > 0) { - actor.angle -= ANG45; - } else if (delta < 0) { - actor.angle += ANG45; - } - - actor.angle &= BITS32; - } - - if (actor.target == null || !eval(actor.target.flags & MF_SHOOTABLE)) { - // look for a new target - if (getEnemies().LookForPlayers(actor, true)) { - return; // got a new target - } - actor.SetMobjState(actor.info.spawnstate); - return; - } - - // do not attack twice in a row - if (eval(actor.flags & MF_JUSTATTACKED)) { - actor.flags &= ~MF_JUSTATTACKED; - if (getGameSkill() != skill_t.sk_nightmare && !IsFastParm()) { - getAttacks().NewChaseDir(actor); - } - return; - } - - // check for melee attack - if (actor.info.meleestate != statenum_t.S_NULL && getEnemies().CheckMeleeRange(actor)) { - if (actor.info.attacksound != null) { - StartSound(actor, actor.info.attacksound); - } - actor.SetMobjState(actor.info.meleestate); - return; - } - - // check for missile attack - if (actor.info.missilestate != statenum_t.S_NULL) { //_D_: this caused a bug where Demon for example were disappearing - // Assume that a missile attack is possible - if (getGameSkill().ordinal() < skill_t.sk_nightmare.ordinal() && !IsFastParm() && actor.movecount != 0) { - // Uhm....no. - nomissile = true; - } else if (!getEnemies().CheckMissileRange(actor)) { - nomissile = true; // Out of range - } - if (!nomissile) { - // Perform the attack - actor.SetMobjState(actor.info.missilestate); - actor.flags |= MF_JUSTATTACKED; - return; - } - } - - // This should be executed always, if not averted by returns. - // possibly choose another target - if (IsNetGame() && actor.threshold == 0 && !getEnemies().CheckSight(actor, actor.target)) { - if (getEnemies().LookForPlayers(actor, true)) { - return; // got a new target - } - } - - // chase towards player - if (--actor.movecount < 0 || !getAttacks().Move(actor)) { - getAttacks().NewChaseDir(actor); - } - - // make active sound - if (actor.info.activesound != null && P_Random() < 3) { - StartSound(actor, actor.info.activesound); - } - } - - @Override - default void A_Fall(mobj_t actor) { - // actor is on ground, it can be walked over - actor.flags &= ~MF_SOLID; - - // So change this if corpse objects - // are meant to be obstacles. - } - - /** - * Causes object to move and perform obs. - * Can only be called through the Actions dispatcher. - * - * @param mobj - */ - // - //P_MobjThinker - // - default void P_MobjThinker(mobj_t mobj) { - // momentum movement - if (mobj.momx != 0 || mobj.momy != 0 || (eval(mobj.flags & MF_SKULLFLY))) { - getAttacks().XYMovement(mobj); - - if (mobj.thinkerFunction.ordinal() == 0) { - return; // mobj was removed or nop - } - } - if ((mobj.z != mobj.floorz) || mobj.momz != 0) { - mobj.ZMovement(); - - if (mobj.thinkerFunction.ordinal() == 0) { - return; // mobj was removed or nop - } - } - - // cycle through states, - // calling action functions at transitions - if (mobj.mobj_tics != -1) { - mobj.mobj_tics--; - - // you can cycle through multiple states in a tic - if (!eval(mobj.mobj_tics)) { - if (!mobj.SetMobjState(mobj.mobj_state.nextstate)) { - // freed itself - } - } - } else { - // check for nightmare respawn - if (!eval(mobj.flags & MF_COUNTKILL)) { - return; - } - - if (!DOOM().respawnmonsters) { - return; - } - - mobj.movecount++; - - if (mobj.movecount < 12 * 35) { - return; - } - - if (eval(LevelTime() & 31)) { - return; - } - - if (P_Random() > 4) { - return; - } - - getEnemies().NightmareRespawn(mobj); - } - } - - // - // A_FaceTarget - // - @Override - default void A_FaceTarget(mobj_t actor) { - if (actor.target == null) { - return; - } - - actor.flags &= ~MF_AMBUSH; - - actor.angle = sceneRenderer().PointToAngle2(actor.x, - actor.y, - actor.target.x, - actor.target.y) & BITS32; - - if (eval(actor.target.flags & MF_SHADOW)) { - actor.angle += (P_Random() - P_Random()) << 21; - } - actor.angle &= BITS32; - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions.ActiveStates; + +import static data.Tables.ANG45; +import static data.Tables.BITS32; +import data.mobjtype_t; +import data.sounds; +import defines.skill_t; +import defines.statenum_t; +import p.mobj_t; +import static p.mobj_t.MF_AMBUSH; +import static p.mobj_t.MF_COUNTKILL; +import static p.mobj_t.MF_JUSTATTACKED; +import static p.mobj_t.MF_SHADOW; +import static p.mobj_t.MF_SHOOTABLE; +import static p.mobj_t.MF_SKULLFLY; +import static p.mobj_t.MF_SOLID; +import static utils.C2JUtils.eval; + +public interface Ai extends Monsters, Sounds { + + // + // A_Look + // Stay in state until a player is sighted. + // + default void A_Look(mobj_t actor) { + mobj_t targ; + boolean seeyou = false; // to avoid the fugly goto + + actor.threshold = 0; // any shot will wake up + targ = actor.subsector.sector.soundtarget; + + if (targ != null + && eval(targ.flags & MF_SHOOTABLE)) { + actor.target = targ; + + if (eval(actor.flags & MF_AMBUSH)) { + seeyou = getEnemies().CheckSight(actor, actor.target); + } else { + seeyou = true; + } + } + if (!seeyou) { + if (!getEnemies().LookForPlayers(actor, false)) { + return; + } + } + + // go into chase state + seeyou: + if (actor.info.seesound != null && actor.info.seesound != sounds.sfxenum_t.sfx_None) { + int sound; + + switch (actor.info.seesound) { + case sfx_posit1: + case sfx_posit2: + case sfx_posit3: + sound = sounds.sfxenum_t.sfx_posit1.ordinal() + P_Random() % 3; + break; + + case sfx_bgsit1: + case sfx_bgsit2: + sound = sounds.sfxenum_t.sfx_bgsit1.ordinal() + P_Random() % 2; + break; + + default: + sound = actor.info.seesound.ordinal(); + break; + } + + if (actor.type == mobjtype_t.MT_SPIDER || actor.type == mobjtype_t.MT_CYBORG) { + // full volume + StartSound(null, sound); + } else { + StartSound(actor, sound); + } + } + + actor.SetMobjState(actor.info.seestate); + } + + /** + * A_Chase + * Actor has a melee attack, + * so it tries to close as fast as possible + */ + @Override + default void A_Chase(mobj_t actor) { + int delta; + boolean nomissile = false; // for the fugly goto + + if (actor.reactiontime != 0) { + actor.reactiontime--; + } + + // modify target threshold + if (actor.threshold != 0) { + if (actor.target == null || actor.target.health <= 0) { + actor.threshold = 0; + } else { + actor.threshold--; + } + } + + // turn towards movement direction if not there yet + if (actor.movedir < 8) { + actor.angle &= (7 << 29); + actor.angle &= BITS32; + // Nice problem, here! + delta = (int) (actor.angle - (actor.movedir << 29)); + + if (delta > 0) { + actor.angle -= ANG45; + } else if (delta < 0) { + actor.angle += ANG45; + } + + actor.angle &= BITS32; + } + + if (actor.target == null || !eval(actor.target.flags & MF_SHOOTABLE)) { + // look for a new target + if (getEnemies().LookForPlayers(actor, true)) { + return; // got a new target + } + actor.SetMobjState(actor.info.spawnstate); + return; + } + + // do not attack twice in a row + if (eval(actor.flags & MF_JUSTATTACKED)) { + actor.flags &= ~MF_JUSTATTACKED; + if (getGameSkill() != skill_t.sk_nightmare && !IsFastParm()) { + getAttacks().NewChaseDir(actor); + } + return; + } + + // check for melee attack + if (actor.info.meleestate != statenum_t.S_NULL && getEnemies().CheckMeleeRange(actor)) { + if (actor.info.attacksound != null) { + StartSound(actor, actor.info.attacksound); + } + actor.SetMobjState(actor.info.meleestate); + return; + } + + // check for missile attack + if (actor.info.missilestate != statenum_t.S_NULL) { //_D_: this caused a bug where Demon for example were disappearing + // Assume that a missile attack is possible + if (getGameSkill().ordinal() < skill_t.sk_nightmare.ordinal() && !IsFastParm() && actor.movecount != 0) { + // Uhm....no. + nomissile = true; + } else if (!getEnemies().CheckMissileRange(actor)) { + nomissile = true; // Out of range + } + if (!nomissile) { + // Perform the attack + actor.SetMobjState(actor.info.missilestate); + actor.flags |= MF_JUSTATTACKED; + return; + } + } + + // This should be executed always, if not averted by returns. + // possibly choose another target + if (IsNetGame() && actor.threshold == 0 && !getEnemies().CheckSight(actor, actor.target)) { + if (getEnemies().LookForPlayers(actor, true)) { + return; // got a new target + } + } + + // chase towards player + if (--actor.movecount < 0 || !getAttacks().Move(actor)) { + getAttacks().NewChaseDir(actor); + } + + // make active sound + if (actor.info.activesound != null && P_Random() < 3) { + StartSound(actor, actor.info.activesound); + } + } + + @Override + default void A_Fall(mobj_t actor) { + // actor is on ground, it can be walked over + actor.flags &= ~MF_SOLID; + + // So change this if corpse objects + // are meant to be obstacles. + } + + /** + * Causes object to move and perform obs. + * Can only be called through the Actions dispatcher. + * + * @param mobj + */ + // + //P_MobjThinker + // + default void P_MobjThinker(mobj_t mobj) { + // momentum movement + if (mobj.momx != 0 || mobj.momy != 0 || (eval(mobj.flags & MF_SKULLFLY))) { + getAttacks().XYMovement(mobj); + + if (mobj.thinkerFunction.ordinal() == 0) { + return; // mobj was removed or nop + } + } + if ((mobj.z != mobj.floorz) || mobj.momz != 0) { + mobj.ZMovement(); + + if (mobj.thinkerFunction.ordinal() == 0) { + return; // mobj was removed or nop + } + } + + // cycle through states, + // calling action functions at transitions + if (mobj.mobj_tics != -1) { + mobj.mobj_tics--; + + // you can cycle through multiple states in a tic + if (!eval(mobj.mobj_tics)) { + if (!mobj.SetMobjState(mobj.mobj_state.nextstate)) { + // freed itself + } + } + } else { + // check for nightmare respawn + if (!eval(mobj.flags & MF_COUNTKILL)) { + return; + } + + if (!DOOM().respawnmonsters) { + return; + } + + mobj.movecount++; + + if (mobj.movecount < 12 * 35) { + return; + } + + if (eval(LevelTime() & 31)) { + return; + } + + if (P_Random() > 4) { + return; + } + + getEnemies().NightmareRespawn(mobj); + } + } + + // + // A_FaceTarget + // + @Override + default void A_FaceTarget(mobj_t actor) { + if (actor.target == null) { + return; + } + + actor.flags &= ~MF_AMBUSH; + + actor.angle = sceneRenderer().PointToAngle2(actor.x, + actor.y, + actor.target.x, + actor.target.y) & BITS32; + + if (eval(actor.target.flags & MF_SHADOW)) { + actor.angle += (P_Random() - P_Random()) << 21; + } + actor.angle &= BITS32; + } +} \ No newline at end of file diff --git a/src/p/Actions/ActiveStates/Attacks.java b/src/p/Actions/ActiveStates/Attacks.java index 9e014b2..ac1694e 100644 --- a/src/p/Actions/ActiveStates/Attacks.java +++ b/src/p/Actions/ActiveStates/Attacks.java @@ -1,314 +1,314 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions.ActiveStates; - -import static data.Defines.MELEERANGE; -import static data.Defines.MISSILERANGE; -import static data.Defines.pw_strength; -import static data.Tables.ANG180; -import static data.Tables.ANG90; -import static data.Tables.BITS32; -import data.mobjtype_t; -import data.sounds; -import defines.statenum_t; -import doom.SourceCode.angle_t; -import static doom.items.weaponinfo; -import doom.player_t; -import static doom.player_t.ps_flash; -import static m.fixed_t.FRACUNIT; -import static p.Actions.ActionsSectors.KEY_SPAWN; -import p.Actions.ActionsSectors.Spawn; -import p.mobj_t; -import static p.mobj_t.MF_JUSTATTACKED; -import p.pspdef_t; -import static utils.C2JUtils.eval; - -public interface Attacks extends Monsters { - - // plasma cells for a bfg attack - // IDEA: make action functions partially parametrizable? - int BFGCELLS = 40; - - // - // A_FirePistol - // - default void A_FirePistol(player_t player, pspdef_t psp) { - StartSound(player.mo, sounds.sfxenum_t.sfx_pistol); - - player.mo.SetMobjState(statenum_t.S_PLAY_ATK2); - player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()]--; - - player.SetPsprite( - ps_flash, - weaponinfo[player.readyweapon.ordinal()].flashstate); - - getAttacks().P_BulletSlope(player.mo); - getAttacks().P_GunShot(player.mo, !eval(player.refire)); - } - - // - // A_FireShotgun - // - default void A_FireShotgun(player_t player, pspdef_t psp) { - int i; - - StartSound(player.mo, sounds.sfxenum_t.sfx_shotgn); - player.mo.SetMobjState(statenum_t.S_PLAY_ATK2); - - player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()]--; - - player.SetPsprite( - ps_flash, - weaponinfo[player.readyweapon.ordinal()].flashstate); - - getAttacks().P_BulletSlope(player.mo); - - for (i = 0; i < 7; i++) { - getAttacks().P_GunShot(player.mo, false); - } - } - - /** - * A_FireShotgun2 - */ - default void A_FireShotgun2(player_t player, pspdef_t psp) { - final Spawn sp = getEnemies().contextRequire(KEY_SPAWN); - long angle; - int damage; - - StartSound(player.mo, sounds.sfxenum_t.sfx_dshtgn); - player.mo.SetMobjState(statenum_t.S_PLAY_ATK2); - - player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()] -= 2; - - player.SetPsprite( - ps_flash, - weaponinfo[player.readyweapon.ordinal()].flashstate); - - getAttacks().P_BulletSlope(player.mo); - - for (int i = 0; i < 20; i++) { - damage = 5 * (P_Random() % 3 + 1); - angle = player.mo.angle; - angle += (P_Random() - P_Random()) << 19; - getAttacks().LineAttack(player.mo, angle, MISSILERANGE, sp.bulletslope + ((P_Random() - P_Random()) << 5), damage); - } - } - - // - // A_Punch - // - default void A_Punch(player_t player, pspdef_t psp) { - final Spawn sp = contextRequire(KEY_SPAWN); - @angle_t - long angle; - int damage; - int slope; - - damage = (P_Random() % 10 + 1) << 1; - - if (eval(player.powers[pw_strength])) { - damage *= 10; - } - - angle = player.mo.angle; - //angle = (angle+(RND.P_Random()-RND.P_Random())<<18)/*&BITS32*/; - // _D_: for some reason, punch didnt work until I change this - // I think it's because of "+" VS "<<" prioritys... - angle += (P_Random() - P_Random()) << 18; - slope = getAttacks().AimLineAttack(player.mo, angle, MELEERANGE); - getAttacks().LineAttack(player.mo, angle, MELEERANGE, slope, damage); - - // turn to face target - if (eval(sp.linetarget)) { - StartSound(player.mo, sounds.sfxenum_t.sfx_punch); - player.mo.angle = sceneRenderer().PointToAngle2( - player.mo.x, - player.mo.y, - sp.linetarget.x, - sp.linetarget.y - ) & BITS32; - } - } - - // - // A_Saw - // - default void A_Saw(player_t player, pspdef_t psp) { - final Spawn sp = contextRequire(KEY_SPAWN); - @angle_t - long angle; - int damage; - int slope; - - damage = 2 * (P_Random() % 10 + 1); - angle = player.mo.angle; - angle += (P_Random() - P_Random()) << 18; - angle &= BITS32; - - // use meleerange + 1 se the puff doesn't skip the flash - slope = getAttacks().AimLineAttack(player.mo, angle, MELEERANGE + 1); - getAttacks().LineAttack(player.mo, angle, MELEERANGE + 1, slope, damage); - - if (!eval(sp.linetarget)) { - StartSound(player.mo, sounds.sfxenum_t.sfx_sawful); - return; - } - StartSound(player.mo, sounds.sfxenum_t.sfx_sawhit); - - // turn to face target - angle = sceneRenderer().PointToAngle2(player.mo.x, player.mo.y, - sp.linetarget.x, sp.linetarget.y) & BITS32; - /* FIXME: this comparison is going to fail.... or not? - If e.g. angle = 359 degrees (which will be mapped to a small negative number), - and player.mo.angle = 160 degrees (a large, positive value), the result will be a - large negative value, which will still be "greater" than ANG180. - - It seems that *differences* between angles will always compare correctly, but - not direct inequalities. - - */ - - // Yet another screwy place where unsigned BAM angles are used as SIGNED comparisons. - long dangle = (angle - player.mo.angle); - dangle &= BITS32; - if (dangle > ANG180) { - if ((int) dangle < -ANG90 / 20) { - player.mo.angle = angle + ANG90 / 21; - } else { - player.mo.angle -= ANG90 / 20; - } - } else { - if (dangle > ANG90 / 20) { - player.mo.angle = angle - ANG90 / 21; - } else { - player.mo.angle += ANG90 / 20; - } - } - player.mo.angle &= BITS32; - player.mo.flags |= MF_JUSTATTACKED; - } - - // - // A_FireMissile - // - default void A_FireMissile(player_t player, pspdef_t psp) { - player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()]--; - getAttacks().SpawnPlayerMissile(player.mo, mobjtype_t.MT_ROCKET); - } - - // - // A_FireBFG - // - default void A_FireBFG(player_t player, pspdef_t psp) { - player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()] -= BFGCELLS; - getAttacks().SpawnPlayerMissile(player.mo, mobjtype_t.MT_BFG); - } - - // - // A_FireCGun - // - default void A_FireCGun(player_t player, pspdef_t psp) { - // For convenience. - int readyweap = player.readyweapon.ordinal(); - int flashstate = weaponinfo[readyweap].flashstate.ordinal(); - int current_state = psp.state.id; - - StartSound(player.mo, sounds.sfxenum_t.sfx_pistol); - if (!eval(player.ammo[weaponinfo[readyweap].ammo.ordinal()])) { - return; - } - - player.mo.SetMobjState(statenum_t.S_PLAY_ATK2); - player.ammo[weaponinfo[readyweap].ammo.ordinal()]--; - - // MAES: Code to alternate between two different gun flashes - // needed a clear rewrite, as it was way too messy. - // We know that the flash states are a certain amount away from - // the firing states. This amount is two frames. - player.SetPsprite(ps_flash, statenum_t.values()[flashstate + current_state - statenum_t.S_CHAIN1.ordinal()] - ); - - getAttacks().P_BulletSlope(player.mo); - getAttacks().P_GunShot(player.mo, !eval(player.refire)); - } - - // - // A_FirePlasma - // - default void A_FirePlasma(player_t player, pspdef_t psp) { - player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()]--; - - player.SetPsprite( - ps_flash, - weaponinfo[player.readyweapon.ordinal()].flashstate); - - getAttacks().SpawnPlayerMissile(player.mo, mobjtype_t.MT_PLASMA); - } - - default void A_XScream(mobj_t actor) { - StartSound(actor, sounds.sfxenum_t.sfx_slop); - } - - default void A_Pain(mobj_t actor) { - if (actor.info.painsound != null) { - StartSound(actor, actor.info.painsound); - } - } - - // - // A_Explode - // - default void A_Explode(mobj_t thingy) { - getAttacks().RadiusAttack(thingy, thingy.target, 128); - } - - // - // A_BFGSpray - // Spawn a BFG explosion on every monster in view - // - default void A_BFGSpray(mobj_t mo) { - final Spawn sp = contextRequire(KEY_SPAWN); - - int damage; - long an; // angle_t - - // offset angles from its attack angle - for (int i = 0; i < 40; i++) { - an = (mo.angle - ANG90 / 2 + ANG90 / 40 * i) & BITS32; - - // mo.target is the originator (player) - // of the missile - getAttacks().AimLineAttack(mo.target, an, 16 * 64 * FRACUNIT); - - if (!eval(sp.linetarget)) { - continue; - } - - getEnemies().SpawnMobj(sp.linetarget.x, sp.linetarget.y, sp.linetarget.z + (sp.linetarget.height >> 2), mobjtype_t.MT_EXTRABFG); - - damage = 0; - for (int j = 0; j < 15; j++) { - damage += (P_Random() & 7) + 1; - } - - getEnemies().DamageMobj(sp.linetarget, mo.target, mo.target, damage); - } - } - -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions.ActiveStates; + +import static data.Defines.MELEERANGE; +import static data.Defines.MISSILERANGE; +import static data.Defines.pw_strength; +import static data.Tables.ANG180; +import static data.Tables.ANG90; +import static data.Tables.BITS32; +import data.mobjtype_t; +import data.sounds; +import defines.statenum_t; +import doom.SourceCode.angle_t; +import static doom.items.weaponinfo; +import doom.player_t; +import static doom.player_t.ps_flash; +import static m.fixed_t.FRACUNIT; +import static p.Actions.ActionsSectors.KEY_SPAWN; +import p.Actions.ActionsSectors.Spawn; +import p.mobj_t; +import static p.mobj_t.MF_JUSTATTACKED; +import p.pspdef_t; +import static utils.C2JUtils.eval; + +public interface Attacks extends Monsters { + + // plasma cells for a bfg attack + // IDEA: make action functions partially parametrizable? + int BFGCELLS = 40; + + // + // A_FirePistol + // + default void A_FirePistol(player_t player, pspdef_t psp) { + StartSound(player.mo, sounds.sfxenum_t.sfx_pistol); + + player.mo.SetMobjState(statenum_t.S_PLAY_ATK2); + player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()]--; + + player.SetPsprite( + ps_flash, + weaponinfo[player.readyweapon.ordinal()].flashstate); + + getAttacks().P_BulletSlope(player.mo); + getAttacks().P_GunShot(player.mo, !eval(player.refire)); + } + + // + // A_FireShotgun + // + default void A_FireShotgun(player_t player, pspdef_t psp) { + int i; + + StartSound(player.mo, sounds.sfxenum_t.sfx_shotgn); + player.mo.SetMobjState(statenum_t.S_PLAY_ATK2); + + player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()]--; + + player.SetPsprite( + ps_flash, + weaponinfo[player.readyweapon.ordinal()].flashstate); + + getAttacks().P_BulletSlope(player.mo); + + for (i = 0; i < 7; i++) { + getAttacks().P_GunShot(player.mo, false); + } + } + + /** + * A_FireShotgun2 + */ + default void A_FireShotgun2(player_t player, pspdef_t psp) { + final Spawn sp = getEnemies().contextRequire(KEY_SPAWN); + long angle; + int damage; + + StartSound(player.mo, sounds.sfxenum_t.sfx_dshtgn); + player.mo.SetMobjState(statenum_t.S_PLAY_ATK2); + + player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()] -= 2; + + player.SetPsprite( + ps_flash, + weaponinfo[player.readyweapon.ordinal()].flashstate); + + getAttacks().P_BulletSlope(player.mo); + + for (int i = 0; i < 20; i++) { + damage = 5 * (P_Random() % 3 + 1); + angle = player.mo.angle; + angle += (P_Random() - P_Random()) << 19; + getAttacks().LineAttack(player.mo, angle, MISSILERANGE, sp.bulletslope + ((P_Random() - P_Random()) << 5), damage); + } + } + + // + // A_Punch + // + default void A_Punch(player_t player, pspdef_t psp) { + final Spawn sp = contextRequire(KEY_SPAWN); + @angle_t + long angle; + int damage; + int slope; + + damage = (P_Random() % 10 + 1) << 1; + + if (eval(player.powers[pw_strength])) { + damage *= 10; + } + + angle = player.mo.angle; + //angle = (angle+(RND.P_Random()-RND.P_Random())<<18)/*&BITS32*/; + // _D_: for some reason, punch didnt work until I change this + // I think it's because of "+" VS "<<" prioritys... + angle += (P_Random() - P_Random()) << 18; + slope = getAttacks().AimLineAttack(player.mo, angle, MELEERANGE); + getAttacks().LineAttack(player.mo, angle, MELEERANGE, slope, damage); + + // turn to face target + if (eval(sp.linetarget)) { + StartSound(player.mo, sounds.sfxenum_t.sfx_punch); + player.mo.angle = sceneRenderer().PointToAngle2( + player.mo.x, + player.mo.y, + sp.linetarget.x, + sp.linetarget.y + ) & BITS32; + } + } + + // + // A_Saw + // + default void A_Saw(player_t player, pspdef_t psp) { + final Spawn sp = contextRequire(KEY_SPAWN); + @angle_t + long angle; + int damage; + int slope; + + damage = 2 * (P_Random() % 10 + 1); + angle = player.mo.angle; + angle += (P_Random() - P_Random()) << 18; + angle &= BITS32; + + // use meleerange + 1 se the puff doesn't skip the flash + slope = getAttacks().AimLineAttack(player.mo, angle, MELEERANGE + 1); + getAttacks().LineAttack(player.mo, angle, MELEERANGE + 1, slope, damage); + + if (!eval(sp.linetarget)) { + StartSound(player.mo, sounds.sfxenum_t.sfx_sawful); + return; + } + StartSound(player.mo, sounds.sfxenum_t.sfx_sawhit); + + // turn to face target + angle = sceneRenderer().PointToAngle2(player.mo.x, player.mo.y, + sp.linetarget.x, sp.linetarget.y) & BITS32; + /* FIXME: this comparison is going to fail.... or not? + If e.g. angle = 359 degrees (which will be mapped to a small negative number), + and player.mo.angle = 160 degrees (a large, positive value), the result will be a + large negative value, which will still be "greater" than ANG180. + + It seems that *differences* between angles will always compare correctly, but + not direct inequalities. + + */ + + // Yet another screwy place where unsigned BAM angles are used as SIGNED comparisons. + long dangle = (angle - player.mo.angle); + dangle &= BITS32; + if (dangle > ANG180) { + if ((int) dangle < -ANG90 / 20) { + player.mo.angle = angle + ANG90 / 21; + } else { + player.mo.angle -= ANG90 / 20; + } + } else { + if (dangle > ANG90 / 20) { + player.mo.angle = angle - ANG90 / 21; + } else { + player.mo.angle += ANG90 / 20; + } + } + player.mo.angle &= BITS32; + player.mo.flags |= MF_JUSTATTACKED; + } + + // + // A_FireMissile + // + default void A_FireMissile(player_t player, pspdef_t psp) { + player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()]--; + getAttacks().SpawnPlayerMissile(player.mo, mobjtype_t.MT_ROCKET); + } + + // + // A_FireBFG + // + default void A_FireBFG(player_t player, pspdef_t psp) { + player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()] -= BFGCELLS; + getAttacks().SpawnPlayerMissile(player.mo, mobjtype_t.MT_BFG); + } + + // + // A_FireCGun + // + default void A_FireCGun(player_t player, pspdef_t psp) { + // For convenience. + int readyweap = player.readyweapon.ordinal(); + int flashstate = weaponinfo[readyweap].flashstate.ordinal(); + int current_state = psp.state.id; + + StartSound(player.mo, sounds.sfxenum_t.sfx_pistol); + if (!eval(player.ammo[weaponinfo[readyweap].ammo.ordinal()])) { + return; + } + + player.mo.SetMobjState(statenum_t.S_PLAY_ATK2); + player.ammo[weaponinfo[readyweap].ammo.ordinal()]--; + + // MAES: Code to alternate between two different gun flashes + // needed a clear rewrite, as it was way too messy. + // We know that the flash states are a certain amount away from + // the firing states. This amount is two frames. + player.SetPsprite(ps_flash, statenum_t.values()[flashstate + current_state - statenum_t.S_CHAIN1.ordinal()] + ); + + getAttacks().P_BulletSlope(player.mo); + getAttacks().P_GunShot(player.mo, !eval(player.refire)); + } + + // + // A_FirePlasma + // + default void A_FirePlasma(player_t player, pspdef_t psp) { + player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()]--; + + player.SetPsprite( + ps_flash, + weaponinfo[player.readyweapon.ordinal()].flashstate); + + getAttacks().SpawnPlayerMissile(player.mo, mobjtype_t.MT_PLASMA); + } + + default void A_XScream(mobj_t actor) { + StartSound(actor, sounds.sfxenum_t.sfx_slop); + } + + default void A_Pain(mobj_t actor) { + if (actor.info.painsound != null) { + StartSound(actor, actor.info.painsound); + } + } + + // + // A_Explode + // + default void A_Explode(mobj_t thingy) { + getAttacks().RadiusAttack(thingy, thingy.target, 128); + } + + // + // A_BFGSpray + // Spawn a BFG explosion on every monster in view + // + default void A_BFGSpray(mobj_t mo) { + final Spawn sp = contextRequire(KEY_SPAWN); + + int damage; + long an; // angle_t + + // offset angles from its attack angle + for (int i = 0; i < 40; i++) { + an = (mo.angle - ANG90 / 2 + ANG90 / 40 * i) & BITS32; + + // mo.target is the originator (player) + // of the missile + getAttacks().AimLineAttack(mo.target, an, 16 * 64 * FRACUNIT); + + if (!eval(sp.linetarget)) { + continue; + } + + getEnemies().SpawnMobj(sp.linetarget.x, sp.linetarget.y, sp.linetarget.z + (sp.linetarget.height >> 2), mobjtype_t.MT_EXTRABFG); + + damage = 0; + for (int j = 0; j < 15; j++) { + damage += (P_Random() & 7) + 1; + } + + getEnemies().DamageMobj(sp.linetarget, mo.target, mo.target, damage); + } + } + +} \ No newline at end of file diff --git a/src/p/Actions/ActiveStates/MonsterStates/Bosses.java b/src/p/Actions/ActiveStates/MonsterStates/Bosses.java index a538da4..c236c5f 100644 --- a/src/p/Actions/ActiveStates/MonsterStates/Bosses.java +++ b/src/p/Actions/ActiveStates/MonsterStates/Bosses.java @@ -1,214 +1,214 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions.ActiveStates.MonsterStates; - -import static data.Limits.MAXPLAYERS; -import data.mobjtype_t; -import doom.DoomMain; -import doom.thinker_t; -import p.Actions.ActionTrait; -import p.ActiveStates; -import p.floor_e; -import p.mobj_t; -import p.vldoor_e; -import rr.line_t; - -public interface Bosses extends ActionTrait { - - void A_Fall(mobj_t mo); - - /** - * A_BossDeath - * Possibly trigger special effects - * if on first boss level - * - * TODO: find out how Plutonia/TNT does cope with this. - * Special clauses? - * - */ - default void A_BossDeath(mobj_t mo) { - final DoomMain D = DOOM(); - thinker_t th; - mobj_t mo2; - line_t junk = new line_t(); - int i; - - if (D.isCommercial()) { - if (D.gamemap != 7) { - return; - } - - if ((mo.type != mobjtype_t.MT_FATSO) - && (mo.type != mobjtype_t.MT_BABY)) { - return; - } - } else { - switch (D.gameepisode) { - case 1: - if (D.gamemap != 8) { - return; - } - - if (mo.type != mobjtype_t.MT_BRUISER) { - return; - } - break; - - case 2: - if (D.gamemap != 8) { - return; - } - - if (mo.type != mobjtype_t.MT_CYBORG) { - return; - } - break; - - case 3: - if (D.gamemap != 8) { - return; - } - - if (mo.type != mobjtype_t.MT_SPIDER) { - return; - } - - break; - - case 4: - switch (D.gamemap) { - case 6: - if (mo.type != mobjtype_t.MT_CYBORG) { - return; - } - break; - - case 8: - if (mo.type != mobjtype_t.MT_SPIDER) { - return; - } - break; - - default: - return; - } - break; - - default: - if (D.gamemap != 8) { - return; - } - break; - } - - } - - // make sure there is a player alive for victory - for (i = 0; i < MAXPLAYERS; i++) { - if (D.playeringame[i] && D.players[i].health[0] > 0) { - break; - } - } - - if (i == MAXPLAYERS) { - return; // no one left alive, so do not end game - } - // scan the remaining thinkers to see - // if all bosses are dead - for (th = getThinkerCap().next; th != getThinkerCap(); th = th.next) { - if (th.thinkerFunction != ActiveStates.P_MobjThinker) { - continue; - } - - mo2 = (mobj_t) th; - if (mo2 != mo - && mo2.type == mo.type - && mo2.health > 0) { - // other boss not dead - return; - } - } - - // victory! - if (D.isCommercial()) { - if (D.gamemap == 7) { - if (mo.type == mobjtype_t.MT_FATSO) { - junk.tag = 666; - getThinkers().DoFloor(junk, floor_e.lowerFloorToLowest); - return; - } - - if (mo.type == mobjtype_t.MT_BABY) { - junk.tag = 667; - getThinkers().DoFloor(junk, floor_e.raiseToTexture); - return; - } - } - } else { - switch (D.gameepisode) { - case 1: - junk.tag = 666; - getThinkers().DoFloor(junk, floor_e.lowerFloorToLowest); - return; - - case 4: - switch (D.gamemap) { - case 6: - junk.tag = 666; - getThinkers().DoDoor(junk, vldoor_e.blazeOpen); - return; - - case 8: - junk.tag = 666; - getThinkers().DoFloor(junk, floor_e.lowerFloorToLowest); - return; - } - } - } - - D.ExitLevel(); - } - - default void A_KeenDie(mobj_t mo) { - thinker_t th; - mobj_t mo2; - line_t junk = new line_t(); // MAES: fixed null 21/5/2011 - - A_Fall(mo); - - // scan the remaining thinkers - // to see if all Keens are dead - for (th = getThinkerCap().next; th != getThinkerCap(); th = th.next) { - if (th.thinkerFunction != ActiveStates.P_MobjThinker) { - continue; - } - - mo2 = (mobj_t) th; - if (mo2 != mo - && mo2.type == mo.type - && mo2.health > 0) { - // other Keen not dead - return; - } - } - - junk.tag = 666; - getThinkers().DoDoor(junk, vldoor_e.open); - } - -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions.ActiveStates.MonsterStates; + +import static data.Limits.MAXPLAYERS; +import data.mobjtype_t; +import doom.DoomMain; +import doom.thinker_t; +import p.Actions.ActionTrait; +import p.ActiveStates; +import p.floor_e; +import p.mobj_t; +import p.vldoor_e; +import rr.line_t; + +public interface Bosses extends ActionTrait { + + void A_Fall(mobj_t mo); + + /** + * A_BossDeath + * Possibly trigger special effects + * if on first boss level + * + * TODO: find out how Plutonia/TNT does cope with this. + * Special clauses? + * + */ + default void A_BossDeath(mobj_t mo) { + final DoomMain D = DOOM(); + thinker_t th; + mobj_t mo2; + line_t junk = new line_t(); + int i; + + if (D.isCommercial()) { + if (D.gamemap != 7) { + return; + } + + if ((mo.type != mobjtype_t.MT_FATSO) + && (mo.type != mobjtype_t.MT_BABY)) { + return; + } + } else { + switch (D.gameepisode) { + case 1: + if (D.gamemap != 8) { + return; + } + + if (mo.type != mobjtype_t.MT_BRUISER) { + return; + } + break; + + case 2: + if (D.gamemap != 8) { + return; + } + + if (mo.type != mobjtype_t.MT_CYBORG) { + return; + } + break; + + case 3: + if (D.gamemap != 8) { + return; + } + + if (mo.type != mobjtype_t.MT_SPIDER) { + return; + } + + break; + + case 4: + switch (D.gamemap) { + case 6: + if (mo.type != mobjtype_t.MT_CYBORG) { + return; + } + break; + + case 8: + if (mo.type != mobjtype_t.MT_SPIDER) { + return; + } + break; + + default: + return; + } + break; + + default: + if (D.gamemap != 8) { + return; + } + break; + } + + } + + // make sure there is a player alive for victory + for (i = 0; i < MAXPLAYERS; i++) { + if (D.playeringame[i] && D.players[i].health[0] > 0) { + break; + } + } + + if (i == MAXPLAYERS) { + return; // no one left alive, so do not end game + } + // scan the remaining thinkers to see + // if all bosses are dead + for (th = getThinkerCap().next; th != getThinkerCap(); th = th.next) { + if (th.thinkerFunction != ActiveStates.P_MobjThinker) { + continue; + } + + mo2 = (mobj_t) th; + if (mo2 != mo + && mo2.type == mo.type + && mo2.health > 0) { + // other boss not dead + return; + } + } + + // victory! + if (D.isCommercial()) { + if (D.gamemap == 7) { + if (mo.type == mobjtype_t.MT_FATSO) { + junk.tag = 666; + getThinkers().DoFloor(junk, floor_e.lowerFloorToLowest); + return; + } + + if (mo.type == mobjtype_t.MT_BABY) { + junk.tag = 667; + getThinkers().DoFloor(junk, floor_e.raiseToTexture); + return; + } + } + } else { + switch (D.gameepisode) { + case 1: + junk.tag = 666; + getThinkers().DoFloor(junk, floor_e.lowerFloorToLowest); + return; + + case 4: + switch (D.gamemap) { + case 6: + junk.tag = 666; + getThinkers().DoDoor(junk, vldoor_e.blazeOpen); + return; + + case 8: + junk.tag = 666; + getThinkers().DoFloor(junk, floor_e.lowerFloorToLowest); + return; + } + } + } + + D.ExitLevel(); + } + + default void A_KeenDie(mobj_t mo) { + thinker_t th; + mobj_t mo2; + line_t junk = new line_t(); // MAES: fixed null 21/5/2011 + + A_Fall(mo); + + // scan the remaining thinkers + // to see if all Keens are dead + for (th = getThinkerCap().next; th != getThinkerCap(); th = th.next) { + if (th.thinkerFunction != ActiveStates.P_MobjThinker) { + continue; + } + + mo2 = (mobj_t) th; + if (mo2 != mo + && mo2.type == mo.type + && mo2.health > 0) { + // other Keen not dead + return; + } + } + + junk.tag = 666; + getThinkers().DoDoor(junk, vldoor_e.open); + } + +} \ No newline at end of file diff --git a/src/p/Actions/ActiveStates/MonsterStates/Demonspawns.java b/src/p/Actions/ActiveStates/MonsterStates/Demonspawns.java index a5cb2e7..04ee5e2 100644 --- a/src/p/Actions/ActiveStates/MonsterStates/Demonspawns.java +++ b/src/p/Actions/ActiveStates/MonsterStates/Demonspawns.java @@ -1,110 +1,110 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions.ActiveStates.MonsterStates; - -import data.mobjtype_t; -import data.sounds; -import p.Actions.ActionTrait; -import p.mobj_t; - -public interface Demonspawns extends ActionTrait { - - void A_FaceTarget(mobj_t actor); - - // - // A_TroopAttack - // - default void A_TroopAttack(mobj_t actor) { - int damage; - - if (actor.target == null) { - return; - } - - A_FaceTarget(actor); - if (getEnemies().CheckMeleeRange(actor)) { - StartSound(actor, sounds.sfxenum_t.sfx_claw); - damage = (P_Random() % 8 + 1) * 3; - getAttacks().DamageMobj(actor.target, actor, actor, damage); - return; - } - - // launch a missile - getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_TROOPSHOT); - } - - default void A_SargAttack(mobj_t actor) { - int damage; - - if (actor.target == null) { - return; - } - - A_FaceTarget(actor); - if (getEnemies().CheckMeleeRange(actor)) { - damage = ((P_Random() % 10) + 1) * 4; - getAttacks().DamageMobj(actor.target, actor, actor, damage); - } - } - - default void A_HeadAttack(mobj_t actor) { - int damage; - - if (actor.target == null) { - return; - } - - A_FaceTarget(actor); - if (getEnemies().CheckMeleeRange(actor)) { - damage = (P_Random() % 6 + 1) * 10; - getAttacks().DamageMobj(actor.target, actor, actor, damage); - return; - } - - // launch a missile - getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_HEADSHOT); - } - - default void A_CyberAttack(mobj_t actor) { - if (actor.target == null) { - return; - } - - A_FaceTarget(actor); - getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_ROCKET); - } - - default void A_BruisAttack(mobj_t actor) { - int damage; - - if (actor.target == null) { - return; - } - - if (getEnemies().CheckMeleeRange(actor)) { - StartSound(actor, sounds.sfxenum_t.sfx_claw); - damage = (P_Random() % 8 + 1) * 10; - getAttacks().DamageMobj(actor.target, actor, actor, damage); - return; - } - - // launch a missile - getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_BRUISERSHOT); - } - -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions.ActiveStates.MonsterStates; + +import data.mobjtype_t; +import data.sounds; +import p.Actions.ActionTrait; +import p.mobj_t; + +public interface Demonspawns extends ActionTrait { + + void A_FaceTarget(mobj_t actor); + + // + // A_TroopAttack + // + default void A_TroopAttack(mobj_t actor) { + int damage; + + if (actor.target == null) { + return; + } + + A_FaceTarget(actor); + if (getEnemies().CheckMeleeRange(actor)) { + StartSound(actor, sounds.sfxenum_t.sfx_claw); + damage = (P_Random() % 8 + 1) * 3; + getAttacks().DamageMobj(actor.target, actor, actor, damage); + return; + } + + // launch a missile + getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_TROOPSHOT); + } + + default void A_SargAttack(mobj_t actor) { + int damage; + + if (actor.target == null) { + return; + } + + A_FaceTarget(actor); + if (getEnemies().CheckMeleeRange(actor)) { + damage = ((P_Random() % 10) + 1) * 4; + getAttacks().DamageMobj(actor.target, actor, actor, damage); + } + } + + default void A_HeadAttack(mobj_t actor) { + int damage; + + if (actor.target == null) { + return; + } + + A_FaceTarget(actor); + if (getEnemies().CheckMeleeRange(actor)) { + damage = (P_Random() % 6 + 1) * 10; + getAttacks().DamageMobj(actor.target, actor, actor, damage); + return; + } + + // launch a missile + getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_HEADSHOT); + } + + default void A_CyberAttack(mobj_t actor) { + if (actor.target == null) { + return; + } + + A_FaceTarget(actor); + getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_ROCKET); + } + + default void A_BruisAttack(mobj_t actor) { + int damage; + + if (actor.target == null) { + return; + } + + if (getEnemies().CheckMeleeRange(actor)) { + StartSound(actor, sounds.sfxenum_t.sfx_claw); + damage = (P_Random() % 8 + 1) * 10; + getAttacks().DamageMobj(actor.target, actor, actor, damage); + return; + } + + // launch a missile + getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_BRUISERSHOT); + } + +} \ No newline at end of file diff --git a/src/p/Actions/ActiveStates/MonsterStates/HorrendousVisages.java b/src/p/Actions/ActiveStates/MonsterStates/HorrendousVisages.java index 995f5c3..81382e0 100644 --- a/src/p/Actions/ActiveStates/MonsterStates/HorrendousVisages.java +++ b/src/p/Actions/ActiveStates/MonsterStates/HorrendousVisages.java @@ -1,202 +1,202 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions.ActiveStates.MonsterStates; - -import data.Limits; -import data.mobjtype_t; -import data.sounds; -import defines.skill_t; -import defines.statenum_t; -import doom.thinker_t; -import static m.fixed_t.FRACUNIT; -import p.Actions.ActiveStates.Sounds; -import p.ActiveStates; -import p.mobj_t; -import utils.TraitFactory.ContextKey; - -public interface HorrendousVisages extends Sounds { - - ContextKey KEY_BRAIN = ACTION_KEY_CHAIN.newKey(HorrendousVisages.class, Brain::new); - - final class Brain { - - // Brain status - mobj_t[] braintargets = new mobj_t[Limits.NUMBRAINTARGETS]; - int numbraintargets; - int braintargeton; - int easy = 0; - } - - default void A_BrainAwake(mobj_t mo) { - final Brain brain = contextRequire(KEY_BRAIN); - thinker_t thinker; - mobj_t m; - - // find all the target spots - brain.numbraintargets = 0; - brain.braintargeton = 0; - - //thinker = obs.thinkercap.next; - for (thinker = getThinkerCap().next; thinker != getThinkerCap(); thinker = thinker.next) { - if (thinker.thinkerFunction != ActiveStates.P_MobjThinker) { - continue; // not a mobj - } - m = (mobj_t) thinker; - - if (m.type == mobjtype_t.MT_BOSSTARGET) { - brain.braintargets[brain.numbraintargets] = m; - brain.numbraintargets++; - } - } - - StartSound(null, sounds.sfxenum_t.sfx_bossit); - } - - default void A_BrainScream(mobj_t mo) { - int x; - int y; - int z; - mobj_t th; - - for (x = mo.x - 196 * FRACUNIT; x < mo.x + 320 * FRACUNIT; x += FRACUNIT * 8) { - y = mo.y - 320 * FRACUNIT; - z = 128 + P_Random() * 2 * FRACUNIT; - th = getEnemies().SpawnMobj(x, y, z, mobjtype_t.MT_ROCKET); - th.momz = P_Random() * 512; - - th.SetMobjState(statenum_t.S_BRAINEXPLODE1); - - th.mobj_tics -= P_Random() & 7; - if (th.mobj_tics < 1) { - th.mobj_tics = 1; - } - } - - StartSound(null, sounds.sfxenum_t.sfx_bosdth); - } - - default void A_BrainExplode(mobj_t mo) { - int x; - int y; - int z; - mobj_t th; - - x = mo.x + (P_Random() - P_Random()) * 2048; - y = mo.y; - z = 128 + P_Random() * 2 * FRACUNIT; - th = getEnemies().SpawnMobj(x, y, z, mobjtype_t.MT_ROCKET); - th.momz = P_Random() * 512; - - th.SetMobjState(statenum_t.S_BRAINEXPLODE1); - - th.mobj_tics -= P_Random() & 7; - if (th.mobj_tics < 1) { - th.mobj_tics = 1; - } - } - - default void A_BrainDie(mobj_t mo) { - DOOM().ExitLevel(); - } - - default void A_BrainSpit(mobj_t mo) { - final Brain brain = contextRequire(KEY_BRAIN); - mobj_t targ; - mobj_t newmobj; - - brain.easy ^= 1; - if (getGameSkill().ordinal() <= skill_t.sk_easy.ordinal() && (brain.easy == 0)) { - return; - } - - // shoot a cube at current target - targ = brain.braintargets[brain.braintargeton]; - - // Load-time fix: awake on zero numbrain targets, if A_BrainSpit is called. - if (brain.numbraintargets == 0) { - A_BrainAwake(mo); - return; - } - brain.braintargeton = (brain.braintargeton + 1) % brain.numbraintargets; - - // spawn brain missile - newmobj = getAttacks().SpawnMissile(mo, targ, mobjtype_t.MT_SPAWNSHOT); - newmobj.target = targ; - newmobj.reactiontime = ((targ.y - mo.y) / newmobj.momy) / newmobj.mobj_state.tics; - - StartSound(null, sounds.sfxenum_t.sfx_bospit); - } - - @Override - default void A_SpawnFly(mobj_t mo) { - mobj_t newmobj; - mobj_t fog; - mobj_t targ; - int r; - mobjtype_t type; - - if (--mo.reactiontime != 0) { - return; // still flying - } - targ = mo.target; - - // First spawn teleport fog. - fog = getEnemies().SpawnMobj(targ.x, targ.y, targ.z, mobjtype_t.MT_SPAWNFIRE); - StartSound(fog, sounds.sfxenum_t.sfx_telept); - - // Randomly select monster to spawn. - r = P_Random(); - - // Probability distribution (kind of :), - // decreasing likelihood. - if (r < 50) { - type = mobjtype_t.MT_TROOP; - } else if (r < 90) { - type = mobjtype_t.MT_SERGEANT; - } else if (r < 120) { - type = mobjtype_t.MT_SHADOWS; - } else if (r < 130) { - type = mobjtype_t.MT_PAIN; - } else if (r < 160) { - type = mobjtype_t.MT_HEAD; - } else if (r < 162) { - type = mobjtype_t.MT_VILE; - } else if (r < 172) { - type = mobjtype_t.MT_UNDEAD; - } else if (r < 192) { - type = mobjtype_t.MT_BABY; - } else if (r < 222) { - type = mobjtype_t.MT_FATSO; - } else if (r < 246) { - type = mobjtype_t.MT_KNIGHT; - } else { - type = mobjtype_t.MT_BRUISER; - } - - newmobj = getEnemies().SpawnMobj(targ.x, targ.y, targ.z, type); - if (getEnemies().LookForPlayers(newmobj, true)) { - newmobj.SetMobjState(newmobj.info.seestate); - } - - // telefrag anything in this spot - getAttacks().TeleportMove(newmobj, newmobj.x, newmobj.y); - - // remove self (i.e., cube). - getEnemies().RemoveMobj(mo); - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions.ActiveStates.MonsterStates; + +import data.Limits; +import data.mobjtype_t; +import data.sounds; +import defines.skill_t; +import defines.statenum_t; +import doom.thinker_t; +import static m.fixed_t.FRACUNIT; +import p.Actions.ActiveStates.Sounds; +import p.ActiveStates; +import p.mobj_t; +import utils.TraitFactory.ContextKey; + +public interface HorrendousVisages extends Sounds { + + ContextKey KEY_BRAIN = ACTION_KEY_CHAIN.newKey(HorrendousVisages.class, Brain::new); + + final class Brain { + + // Brain status + mobj_t[] braintargets = new mobj_t[Limits.NUMBRAINTARGETS]; + int numbraintargets; + int braintargeton; + int easy = 0; + } + + default void A_BrainAwake(mobj_t mo) { + final Brain brain = contextRequire(KEY_BRAIN); + thinker_t thinker; + mobj_t m; + + // find all the target spots + brain.numbraintargets = 0; + brain.braintargeton = 0; + + //thinker = obs.thinkercap.next; + for (thinker = getThinkerCap().next; thinker != getThinkerCap(); thinker = thinker.next) { + if (thinker.thinkerFunction != ActiveStates.P_MobjThinker) { + continue; // not a mobj + } + m = (mobj_t) thinker; + + if (m.type == mobjtype_t.MT_BOSSTARGET) { + brain.braintargets[brain.numbraintargets] = m; + brain.numbraintargets++; + } + } + + StartSound(null, sounds.sfxenum_t.sfx_bossit); + } + + default void A_BrainScream(mobj_t mo) { + int x; + int y; + int z; + mobj_t th; + + for (x = mo.x - 196 * FRACUNIT; x < mo.x + 320 * FRACUNIT; x += FRACUNIT * 8) { + y = mo.y - 320 * FRACUNIT; + z = 128 + P_Random() * 2 * FRACUNIT; + th = getEnemies().SpawnMobj(x, y, z, mobjtype_t.MT_ROCKET); + th.momz = P_Random() * 512; + + th.SetMobjState(statenum_t.S_BRAINEXPLODE1); + + th.mobj_tics -= P_Random() & 7; + if (th.mobj_tics < 1) { + th.mobj_tics = 1; + } + } + + StartSound(null, sounds.sfxenum_t.sfx_bosdth); + } + + default void A_BrainExplode(mobj_t mo) { + int x; + int y; + int z; + mobj_t th; + + x = mo.x + (P_Random() - P_Random()) * 2048; + y = mo.y; + z = 128 + P_Random() * 2 * FRACUNIT; + th = getEnemies().SpawnMobj(x, y, z, mobjtype_t.MT_ROCKET); + th.momz = P_Random() * 512; + + th.SetMobjState(statenum_t.S_BRAINEXPLODE1); + + th.mobj_tics -= P_Random() & 7; + if (th.mobj_tics < 1) { + th.mobj_tics = 1; + } + } + + default void A_BrainDie(mobj_t mo) { + DOOM().ExitLevel(); + } + + default void A_BrainSpit(mobj_t mo) { + final Brain brain = contextRequire(KEY_BRAIN); + mobj_t targ; + mobj_t newmobj; + + brain.easy ^= 1; + if (getGameSkill().ordinal() <= skill_t.sk_easy.ordinal() && (brain.easy == 0)) { + return; + } + + // shoot a cube at current target + targ = brain.braintargets[brain.braintargeton]; + + // Load-time fix: awake on zero numbrain targets, if A_BrainSpit is called. + if (brain.numbraintargets == 0) { + A_BrainAwake(mo); + return; + } + brain.braintargeton = (brain.braintargeton + 1) % brain.numbraintargets; + + // spawn brain missile + newmobj = getAttacks().SpawnMissile(mo, targ, mobjtype_t.MT_SPAWNSHOT); + newmobj.target = targ; + newmobj.reactiontime = ((targ.y - mo.y) / newmobj.momy) / newmobj.mobj_state.tics; + + StartSound(null, sounds.sfxenum_t.sfx_bospit); + } + + @Override + default void A_SpawnFly(mobj_t mo) { + mobj_t newmobj; + mobj_t fog; + mobj_t targ; + int r; + mobjtype_t type; + + if (--mo.reactiontime != 0) { + return; // still flying + } + targ = mo.target; + + // First spawn teleport fog. + fog = getEnemies().SpawnMobj(targ.x, targ.y, targ.z, mobjtype_t.MT_SPAWNFIRE); + StartSound(fog, sounds.sfxenum_t.sfx_telept); + + // Randomly select monster to spawn. + r = P_Random(); + + // Probability distribution (kind of :), + // decreasing likelihood. + if (r < 50) { + type = mobjtype_t.MT_TROOP; + } else if (r < 90) { + type = mobjtype_t.MT_SERGEANT; + } else if (r < 120) { + type = mobjtype_t.MT_SHADOWS; + } else if (r < 130) { + type = mobjtype_t.MT_PAIN; + } else if (r < 160) { + type = mobjtype_t.MT_HEAD; + } else if (r < 162) { + type = mobjtype_t.MT_VILE; + } else if (r < 172) { + type = mobjtype_t.MT_UNDEAD; + } else if (r < 192) { + type = mobjtype_t.MT_BABY; + } else if (r < 222) { + type = mobjtype_t.MT_FATSO; + } else if (r < 246) { + type = mobjtype_t.MT_KNIGHT; + } else { + type = mobjtype_t.MT_BRUISER; + } + + newmobj = getEnemies().SpawnMobj(targ.x, targ.y, targ.z, type); + if (getEnemies().LookForPlayers(newmobj, true)) { + newmobj.SetMobjState(newmobj.info.seestate); + } + + // telefrag anything in this spot + getAttacks().TeleportMove(newmobj, newmobj.x, newmobj.y); + + // remove self (i.e., cube). + getEnemies().RemoveMobj(mo); + } +} \ No newline at end of file diff --git a/src/p/Actions/ActiveStates/MonsterStates/Mancubi.java b/src/p/Actions/ActiveStates/MonsterStates/Mancubi.java index 50eea28..ecaff33 100644 --- a/src/p/Actions/ActiveStates/MonsterStates/Mancubi.java +++ b/src/p/Actions/ActiveStates/MonsterStates/Mancubi.java @@ -1,97 +1,97 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions.ActiveStates.MonsterStates; - -import data.Tables; -import static data.Tables.finecosine; -import static data.Tables.finesine; -import data.mobjtype_t; -import data.sounds; -import static m.fixed_t.FixedMul; -import p.Actions.ActionTrait; -import p.mobj_t; - -public interface Mancubi extends ActionTrait { - - static final long FATSPREAD = Tables.ANG90 / 8; - - void A_FaceTarget(mobj_t actor); - - // - // Mancubus attack, - // firing three missiles (bruisers) - // in three different directions? - // Doesn't look like it. - // - default void A_FatRaise(mobj_t actor) { - A_FaceTarget(actor); - StartSound(actor, sounds.sfxenum_t.sfx_manatk); - } - - default void A_FatAttack1(mobj_t actor) { - mobj_t mo; - int an; - - A_FaceTarget(actor); - // Change direction to ... - actor.angle += FATSPREAD; - getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_FATSHOT); - - mo = getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_FATSHOT); - mo.angle += FATSPREAD; - an = Tables.toBAMIndex(mo.angle); - mo.momx = FixedMul(mo.info.speed, finecosine[an]); - mo.momy = FixedMul(mo.info.speed, finesine[an]); - } - - default void A_FatAttack2(mobj_t actor) { - mobj_t mo; - int an; - - A_FaceTarget(actor); - // Now here choose opposite deviation. - actor.angle -= FATSPREAD; - getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_FATSHOT); - - mo = getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_FATSHOT); - mo.angle -= FATSPREAD * 2; - an = Tables.toBAMIndex(mo.angle); - mo.momx = FixedMul(mo.info.speed, finecosine[an]); - mo.momy = FixedMul(mo.info.speed, finesine[an]); - } - - default void A_FatAttack3(mobj_t actor) { - mobj_t mo; - int an; - - A_FaceTarget(actor); - - mo = getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_FATSHOT); - mo.angle -= FATSPREAD / 2; - an = Tables.toBAMIndex(mo.angle); - mo.momx = FixedMul(mo.info.speed, finecosine[an]); - mo.momy = FixedMul(mo.info.speed, finesine[an]); - - mo = getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_FATSHOT); - mo.angle += FATSPREAD / 2; - an = Tables.toBAMIndex(mo.angle); - mo.momx = FixedMul(mo.info.speed, finecosine[an]); - mo.momy = FixedMul(mo.info.speed, finesine[an]); - } - -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions.ActiveStates.MonsterStates; + +import data.Tables; +import static data.Tables.finecosine; +import static data.Tables.finesine; +import data.mobjtype_t; +import data.sounds; +import static m.fixed_t.FixedMul; +import p.Actions.ActionTrait; +import p.mobj_t; + +public interface Mancubi extends ActionTrait { + + static final long FATSPREAD = Tables.ANG90 / 8; + + void A_FaceTarget(mobj_t actor); + + // + // Mancubus attack, + // firing three missiles (bruisers) + // in three different directions? + // Doesn't look like it. + // + default void A_FatRaise(mobj_t actor) { + A_FaceTarget(actor); + StartSound(actor, sounds.sfxenum_t.sfx_manatk); + } + + default void A_FatAttack1(mobj_t actor) { + mobj_t mo; + int an; + + A_FaceTarget(actor); + // Change direction to ... + actor.angle += FATSPREAD; + getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_FATSHOT); + + mo = getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_FATSHOT); + mo.angle += FATSPREAD; + an = Tables.toBAMIndex(mo.angle); + mo.momx = FixedMul(mo.info.speed, finecosine[an]); + mo.momy = FixedMul(mo.info.speed, finesine[an]); + } + + default void A_FatAttack2(mobj_t actor) { + mobj_t mo; + int an; + + A_FaceTarget(actor); + // Now here choose opposite deviation. + actor.angle -= FATSPREAD; + getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_FATSHOT); + + mo = getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_FATSHOT); + mo.angle -= FATSPREAD * 2; + an = Tables.toBAMIndex(mo.angle); + mo.momx = FixedMul(mo.info.speed, finecosine[an]); + mo.momy = FixedMul(mo.info.speed, finesine[an]); + } + + default void A_FatAttack3(mobj_t actor) { + mobj_t mo; + int an; + + A_FaceTarget(actor); + + mo = getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_FATSHOT); + mo.angle -= FATSPREAD / 2; + an = Tables.toBAMIndex(mo.angle); + mo.momx = FixedMul(mo.info.speed, finecosine[an]); + mo.momy = FixedMul(mo.info.speed, finesine[an]); + + mo = getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_FATSHOT); + mo.angle += FATSPREAD / 2; + an = Tables.toBAMIndex(mo.angle); + mo.momx = FixedMul(mo.info.speed, finecosine[an]); + mo.momy = FixedMul(mo.info.speed, finesine[an]); + } + +} \ No newline at end of file diff --git a/src/p/Actions/ActiveStates/MonsterStates/PainsSouls.java b/src/p/Actions/ActiveStates/MonsterStates/PainsSouls.java index 436fb46..6be5cb5 100644 --- a/src/p/Actions/ActiveStates/MonsterStates/PainsSouls.java +++ b/src/p/Actions/ActiveStates/MonsterStates/PainsSouls.java @@ -1,159 +1,159 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions.ActiveStates.MonsterStates; - -import static data.Limits.MAXSKULLS; -import data.Tables; -import static data.Tables.ANG180; -import static data.Tables.ANG270; -import static data.Tables.ANG90; -import static data.Tables.finecosine; -import static data.Tables.finesine; -import static data.info.mobjinfo; -import data.mobjtype_t; -import doom.SourceCode.angle_t; -import doom.SourceCode.fixed_t; -import doom.thinker_t; -import static m.fixed_t.FRACUNIT; -import static m.fixed_t.FixedMul; -import p.Actions.ActionTrait; -import p.ActiveStates; -import static p.MapUtils.AproxDistance; -import p.mobj_t; -import static p.mobj_t.MF_SKULLFLY; - -public interface PainsSouls extends ActionTrait { - - static final int SKULLSPEED = 20 * m.fixed_t.MAPFRACUNIT; - - void A_FaceTarget(mobj_t actor); - - void A_Fall(mobj_t actor); - - /** - * SkullAttack - * Fly at the player like a missile. - */ - default void A_SkullAttack(mobj_t actor) { - mobj_t dest; - int an; - int dist; - - if (actor.target == null) { - return; - } - - dest = actor.target; - actor.flags |= MF_SKULLFLY; - - StartSound(actor, actor.info.attacksound); - A_FaceTarget(actor); - an = Tables.toBAMIndex(actor.angle); - actor.momx = FixedMul(SKULLSPEED, finecosine[an]); - actor.momy = FixedMul(SKULLSPEED, finesine[an]); - dist = AproxDistance(dest.x - actor.x, dest.y - actor.y); - dist /= SKULLSPEED; - - if (dist < 1) { - dist = 1; - } - actor.momz = (dest.z + (dest.height >> 1) - actor.z) / dist; - } - - /** - * A_PainShootSkull - * Spawn a lost soul and launch it at the target - * It's not a valid callback like the others, actually. - * No idea if some DEH patch does use it to cause - * mayhem though. - * - */ - default void A_PainShootSkull(mobj_t actor, Long angle) { - @fixed_t - int x, y, z; - - mobj_t newmobj; - @angle_t - int an; - int prestep; - int count; - thinker_t currentthinker; - - // count total number of skull currently on the level - count = 0; - - currentthinker = getThinkerCap().next; - while (currentthinker != getThinkerCap()) { - if ((currentthinker.thinkerFunction == ActiveStates.P_MobjThinker) - && ((mobj_t) currentthinker).type == mobjtype_t.MT_SKULL) { - count++; - } - currentthinker = currentthinker.next; - } - - // if there are allready 20 skulls on the level, - // don't spit another one - if (count > MAXSKULLS) { - return; - } - - // okay, there's playe for another one - an = Tables.toBAMIndex(angle); - - prestep - = 4 * FRACUNIT - + 3 * (actor.info.radius + mobjinfo[mobjtype_t.MT_SKULL.ordinal()].radius) / 2; - - x = actor.x + FixedMul(prestep, finecosine[an]); - y = actor.y + FixedMul(prestep, finesine[an]); - z = actor.z + 8 * FRACUNIT; - - newmobj = getAttacks().SpawnMobj(x, y, z, mobjtype_t.MT_SKULL); - - // Check for movements. - if (!getAttacks().TryMove(newmobj, newmobj.x, newmobj.y)) { - // kill it immediately - getAttacks().DamageMobj(newmobj, actor, actor, 10000); - return; - } - - newmobj.target = actor.target; - A_SkullAttack(newmobj); - } - - // - // A_PainAttack - // Spawn a lost soul and launch it at the target - // - default void A_PainAttack(mobj_t actor) { - if (actor.target == null) { - return; - } - - A_FaceTarget(actor); - A_PainShootSkull(actor, actor.angle); - } - - default void A_PainDie(mobj_t actor) { - A_Fall(actor); - A_PainShootSkull(actor, actor.angle + ANG90); - A_PainShootSkull(actor, actor.angle + ANG180); - A_PainShootSkull(actor, actor.angle + ANG270); - } - -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions.ActiveStates.MonsterStates; + +import static data.Limits.MAXSKULLS; +import data.Tables; +import static data.Tables.ANG180; +import static data.Tables.ANG270; +import static data.Tables.ANG90; +import static data.Tables.finecosine; +import static data.Tables.finesine; +import static data.info.mobjinfo; +import data.mobjtype_t; +import doom.SourceCode.angle_t; +import doom.SourceCode.fixed_t; +import doom.thinker_t; +import static m.fixed_t.FRACUNIT; +import static m.fixed_t.FixedMul; +import p.Actions.ActionTrait; +import p.ActiveStates; +import static p.MapUtils.AproxDistance; +import p.mobj_t; +import static p.mobj_t.MF_SKULLFLY; + +public interface PainsSouls extends ActionTrait { + + static final int SKULLSPEED = 20 * m.fixed_t.MAPFRACUNIT; + + void A_FaceTarget(mobj_t actor); + + void A_Fall(mobj_t actor); + + /** + * SkullAttack + * Fly at the player like a missile. + */ + default void A_SkullAttack(mobj_t actor) { + mobj_t dest; + int an; + int dist; + + if (actor.target == null) { + return; + } + + dest = actor.target; + actor.flags |= MF_SKULLFLY; + + StartSound(actor, actor.info.attacksound); + A_FaceTarget(actor); + an = Tables.toBAMIndex(actor.angle); + actor.momx = FixedMul(SKULLSPEED, finecosine[an]); + actor.momy = FixedMul(SKULLSPEED, finesine[an]); + dist = AproxDistance(dest.x - actor.x, dest.y - actor.y); + dist /= SKULLSPEED; + + if (dist < 1) { + dist = 1; + } + actor.momz = (dest.z + (dest.height >> 1) - actor.z) / dist; + } + + /** + * A_PainShootSkull + * Spawn a lost soul and launch it at the target + * It's not a valid callback like the others, actually. + * No idea if some DEH patch does use it to cause + * mayhem though. + * + */ + default void A_PainShootSkull(mobj_t actor, Long angle) { + @fixed_t + int x, y, z; + + mobj_t newmobj; + @angle_t + int an; + int prestep; + int count; + thinker_t currentthinker; + + // count total number of skull currently on the level + count = 0; + + currentthinker = getThinkerCap().next; + while (currentthinker != getThinkerCap()) { + if ((currentthinker.thinkerFunction == ActiveStates.P_MobjThinker) + && ((mobj_t) currentthinker).type == mobjtype_t.MT_SKULL) { + count++; + } + currentthinker = currentthinker.next; + } + + // if there are allready 20 skulls on the level, + // don't spit another one + if (count > MAXSKULLS) { + return; + } + + // okay, there's playe for another one + an = Tables.toBAMIndex(angle); + + prestep + = 4 * FRACUNIT + + 3 * (actor.info.radius + mobjinfo[mobjtype_t.MT_SKULL.ordinal()].radius) / 2; + + x = actor.x + FixedMul(prestep, finecosine[an]); + y = actor.y + FixedMul(prestep, finesine[an]); + z = actor.z + 8 * FRACUNIT; + + newmobj = getAttacks().SpawnMobj(x, y, z, mobjtype_t.MT_SKULL); + + // Check for movements. + if (!getAttacks().TryMove(newmobj, newmobj.x, newmobj.y)) { + // kill it immediately + getAttacks().DamageMobj(newmobj, actor, actor, 10000); + return; + } + + newmobj.target = actor.target; + A_SkullAttack(newmobj); + } + + // + // A_PainAttack + // Spawn a lost soul and launch it at the target + // + default void A_PainAttack(mobj_t actor) { + if (actor.target == null) { + return; + } + + A_FaceTarget(actor); + A_PainShootSkull(actor, actor.angle); + } + + default void A_PainDie(mobj_t actor) { + A_Fall(actor); + A_PainShootSkull(actor, actor.angle + ANG90); + A_PainShootSkull(actor, actor.angle + ANG180); + A_PainShootSkull(actor, actor.angle + ANG270); + } + +} \ No newline at end of file diff --git a/src/p/Actions/ActiveStates/MonsterStates/Skels.java b/src/p/Actions/ActiveStates/MonsterStates/Skels.java index 73127f2..0f9e2f3 100644 --- a/src/p/Actions/ActiveStates/MonsterStates/Skels.java +++ b/src/p/Actions/ActiveStates/MonsterStates/Skels.java @@ -1,146 +1,146 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions.ActiveStates.MonsterStates; - -import data.Tables; -import static data.Tables.ANG180; -import static data.Tables.BITS32; -import static data.Tables.finecosine; -import static data.Tables.finesine; -import data.mobjtype_t; -import data.sounds; -import static m.fixed_t.FRACUNIT; -import static m.fixed_t.FixedMul; -import static m.fixed_t.MAPFRACUNIT; -import p.Actions.ActionTrait; -import static p.MapUtils.AproxDistance; -import p.mobj_t; -import static utils.C2JUtils.eval; - -public interface Skels extends ActionTrait { - - int TRACEANGLE = 0xC_00_00_00; - - // - // A_SkelMissile - // - default void A_SkelMissile(mobj_t actor) { - mobj_t mo; - - if (actor.target == null) { - return; - } - - A_FaceTarget(actor); - actor.z += 16 * FRACUNIT; // so missile spawns higher - mo = getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_TRACER); - actor.z -= 16 * FRACUNIT; // back to normal - - mo.x += mo.momx; - mo.y += mo.momy; - mo.tracer = actor.target; - } - - default void A_SkelWhoosh(mobj_t actor) { - if (actor.target == null) { - return; - } - A_FaceTarget(actor); - StartSound(actor, sounds.sfxenum_t.sfx_skeswg); - } - - default void A_SkelFist(mobj_t actor) { - int damage; - - if (actor.target == null) { - return; - } - - A_FaceTarget(actor); - - if (getEnemies().CheckMeleeRange(actor)) { - damage = ((P_Random() % 10) + 1) * 6; - StartSound(actor, sounds.sfxenum_t.sfx_skepch); - getAttacks().DamageMobj(actor.target, actor, actor, damage); - } - } - - default void A_Tracer(mobj_t actor) { - long exact; //angle_t - int dist, slope; // fixed - mobj_t dest; - mobj_t th; - if (eval(DOOM().gametic & 3)) { - return; - } - // spawn a puff of smoke behind the rocket - getAttacks().SpawnPuff(actor.x, actor.y, actor.z); - th = getEnemies().SpawnMobj(actor.x - actor.momx, actor.y - actor.momy, actor.z, mobjtype_t.MT_SMOKE); - th.momz = MAPFRACUNIT; - th.mobj_tics -= P_Random() & 3; - if (th.mobj_tics < 1) { - th.mobj_tics = 1; - } - - // adjust direction - dest = actor.tracer; - if (dest == null || dest.health <= 0) { - return; - } - - // change angle - exact = sceneRenderer().PointToAngle2(actor.x, actor.y, dest.x, dest.y) & BITS32; - - // MAES: let's analyze the logic here... - // So exact is the angle between the missile and its target. - if (exact != actor.angle) { // missile is already headed there dead-on. - if (exact - actor.angle > ANG180) { - actor.angle -= TRACEANGLE; - actor.angle &= BITS32; - if (((exact - actor.angle) & BITS32) < ANG180) { - actor.angle = exact; - } - } else { - actor.angle += TRACEANGLE; - actor.angle &= BITS32; - if (((exact - actor.angle) & BITS32) > ANG180) { - actor.angle = exact; - } - } - } - // MAES: fixed and sped up. - int exact2 = Tables.toBAMIndex(actor.angle); - actor.momx = FixedMul(actor.info.speed, finecosine[exact2]); - actor.momy = FixedMul(actor.info.speed, finesine[exact2]); - // change slope - dist = AproxDistance(dest.x - actor.x, dest.y - actor.y); - dist /= actor.info.speed; - if (dist < 1) { - dist = 1; - } - slope = (dest.z + 40 * FRACUNIT - actor.z) / dist; - if (slope < actor.momz) { - actor.momz -= FRACUNIT / 8; - } else { - actor.momz += FRACUNIT / 8; - } - } - - public void A_FaceTarget(mobj_t actor); - -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions.ActiveStates.MonsterStates; + +import data.Tables; +import static data.Tables.ANG180; +import static data.Tables.BITS32; +import static data.Tables.finecosine; +import static data.Tables.finesine; +import data.mobjtype_t; +import data.sounds; +import static m.fixed_t.FRACUNIT; +import static m.fixed_t.FixedMul; +import static m.fixed_t.MAPFRACUNIT; +import p.Actions.ActionTrait; +import static p.MapUtils.AproxDistance; +import p.mobj_t; +import static utils.C2JUtils.eval; + +public interface Skels extends ActionTrait { + + int TRACEANGLE = 0xC_00_00_00; + + // + // A_SkelMissile + // + default void A_SkelMissile(mobj_t actor) { + mobj_t mo; + + if (actor.target == null) { + return; + } + + A_FaceTarget(actor); + actor.z += 16 * FRACUNIT; // so missile spawns higher + mo = getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_TRACER); + actor.z -= 16 * FRACUNIT; // back to normal + + mo.x += mo.momx; + mo.y += mo.momy; + mo.tracer = actor.target; + } + + default void A_SkelWhoosh(mobj_t actor) { + if (actor.target == null) { + return; + } + A_FaceTarget(actor); + StartSound(actor, sounds.sfxenum_t.sfx_skeswg); + } + + default void A_SkelFist(mobj_t actor) { + int damage; + + if (actor.target == null) { + return; + } + + A_FaceTarget(actor); + + if (getEnemies().CheckMeleeRange(actor)) { + damage = ((P_Random() % 10) + 1) * 6; + StartSound(actor, sounds.sfxenum_t.sfx_skepch); + getAttacks().DamageMobj(actor.target, actor, actor, damage); + } + } + + default void A_Tracer(mobj_t actor) { + long exact; //angle_t + int dist, slope; // fixed + mobj_t dest; + mobj_t th; + if (eval(DOOM().gametic & 3)) { + return; + } + // spawn a puff of smoke behind the rocket + getAttacks().SpawnPuff(actor.x, actor.y, actor.z); + th = getEnemies().SpawnMobj(actor.x - actor.momx, actor.y - actor.momy, actor.z, mobjtype_t.MT_SMOKE); + th.momz = MAPFRACUNIT; + th.mobj_tics -= P_Random() & 3; + if (th.mobj_tics < 1) { + th.mobj_tics = 1; + } + + // adjust direction + dest = actor.tracer; + if (dest == null || dest.health <= 0) { + return; + } + + // change angle + exact = sceneRenderer().PointToAngle2(actor.x, actor.y, dest.x, dest.y) & BITS32; + + // MAES: let's analyze the logic here... + // So exact is the angle between the missile and its target. + if (exact != actor.angle) { // missile is already headed there dead-on. + if (exact - actor.angle > ANG180) { + actor.angle -= TRACEANGLE; + actor.angle &= BITS32; + if (((exact - actor.angle) & BITS32) < ANG180) { + actor.angle = exact; + } + } else { + actor.angle += TRACEANGLE; + actor.angle &= BITS32; + if (((exact - actor.angle) & BITS32) > ANG180) { + actor.angle = exact; + } + } + } + // MAES: fixed and sped up. + int exact2 = Tables.toBAMIndex(actor.angle); + actor.momx = FixedMul(actor.info.speed, finecosine[exact2]); + actor.momy = FixedMul(actor.info.speed, finesine[exact2]); + // change slope + dist = AproxDistance(dest.x - actor.x, dest.y - actor.y); + dist /= actor.info.speed; + if (dist < 1) { + dist = 1; + } + slope = (dest.z + 40 * FRACUNIT - actor.z) / dist; + if (slope < actor.momz) { + actor.momz -= FRACUNIT / 8; + } else { + actor.momz += FRACUNIT / 8; + } + } + + public void A_FaceTarget(mobj_t actor); + +} \ No newline at end of file diff --git a/src/p/Actions/ActiveStates/MonsterStates/Spiders.java b/src/p/Actions/ActiveStates/MonsterStates/Spiders.java index faae843..59a0130 100644 --- a/src/p/Actions/ActiveStates/MonsterStates/Spiders.java +++ b/src/p/Actions/ActiveStates/MonsterStates/Spiders.java @@ -1,51 +1,51 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions.ActiveStates.MonsterStates; - -import data.mobjtype_t; -import p.Actions.ActionTrait; -import p.mobj_t; - -public interface Spiders extends ActionTrait { - - void A_FaceTarget(mobj_t actor); - - default void A_SpidRefire(mobj_t actor) { - // keep firing unless target got out of sight - A_FaceTarget(actor); - - if (P_Random() < 10) { - return; - } - - if (actor.target == null || actor.target.health <= 0 || !getEnemies().CheckSight(actor, actor.target)) { - actor.SetMobjState(actor.info.seestate); - } - } - - default void A_BspiAttack(mobj_t actor) { - if (actor.target == null) { - return; - } - - A_FaceTarget(actor); - - // launch a missile - getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_ARACHPLAZ); - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions.ActiveStates.MonsterStates; + +import data.mobjtype_t; +import p.Actions.ActionTrait; +import p.mobj_t; + +public interface Spiders extends ActionTrait { + + void A_FaceTarget(mobj_t actor); + + default void A_SpidRefire(mobj_t actor) { + // keep firing unless target got out of sight + A_FaceTarget(actor); + + if (P_Random() < 10) { + return; + } + + if (actor.target == null || actor.target.health <= 0 || !getEnemies().CheckSight(actor, actor.target)) { + actor.SetMobjState(actor.info.seestate); + } + } + + default void A_BspiAttack(mobj_t actor) { + if (actor.target == null) { + return; + } + + A_FaceTarget(actor); + + // launch a missile + getAttacks().SpawnMissile(actor, actor.target, mobjtype_t.MT_ARACHPLAZ); + } +} \ No newline at end of file diff --git a/src/p/Actions/ActiveStates/MonsterStates/Viles.java b/src/p/Actions/ActiveStates/MonsterStates/Viles.java index dbb3814..a0ad731 100644 --- a/src/p/Actions/ActiveStates/MonsterStates/Viles.java +++ b/src/p/Actions/ActiveStates/MonsterStates/Viles.java @@ -1,206 +1,206 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions.ActiveStates.MonsterStates; - -import static data.Limits.MAXRADIUS; -import static data.Tables.finecosine; -import static data.Tables.finesine; -import data.mobjinfo_t; -import data.mobjtype_t; -import data.sounds; -import defines.statenum_t; -import static m.fixed_t.FRACUNIT; -import static m.fixed_t.FixedMul; -import static m.fixed_t.MAPFRACUNIT; -import p.AbstractLevelLoader; -import p.Actions.ActionTrait; -import p.Actions.ActionsAttacks; -import p.Actions.ActionsAttacks.Attacks; -import static p.Actions.ActionsAttacks.KEY_ATTACKS; -import static p.ChaseDirections.DI_NODIR; -import static p.ChaseDirections.xspeed; -import static p.ChaseDirections.yspeed; -import p.mobj_t; - -public interface Viles extends ActionTrait { - - void A_FaceTarget(mobj_t actor); - - void A_Chase(mobj_t actor); - - // - // A_VileChase - // Check for ressurecting a body - // - default void A_VileChase(mobj_t actor) { - final AbstractLevelLoader ll = levelLoader(); - final ActionsAttacks actionsAttacks = getAttacks(); - final Attacks att = actionsAttacks.contextRequire(KEY_ATTACKS); - - int xl; - int xh; - int yl; - int yh; - - int bx; - int by; - - mobjinfo_t info; - mobj_t temp; - - if (actor.movedir != DI_NODIR) { - // check for corpses to raise - att.vileTryX = actor.x + actor.info.speed * xspeed[actor.movedir]; - att.vileTryY = actor.y + actor.info.speed * yspeed[actor.movedir]; - - xl = ll.getSafeBlockX(att.vileTryX - ll.bmaporgx - MAXRADIUS * 2); - xh = ll.getSafeBlockX(att.vileTryX - ll.bmaporgx + MAXRADIUS * 2); - yl = ll.getSafeBlockY(att.vileTryY - ll.bmaporgy - MAXRADIUS * 2); - yh = ll.getSafeBlockY(att.vileTryY - ll.bmaporgy + MAXRADIUS * 2); - - att.vileObj = actor; - for (bx = xl; bx <= xh; bx++) { - for (by = yl; by <= yh; by++) { - // Call PIT_VileCheck to check - // whether object is a corpse - // that can be raised. - if (!BlockThingsIterator(bx, by, actionsAttacks::VileCheck)) { - // got one! - temp = actor.target; - actor.target = att.vileCorpseHit; - A_FaceTarget(actor); - actor.target = temp; - - actor.SetMobjState(statenum_t.S_VILE_HEAL1); - StartSound(att.vileCorpseHit, sounds.sfxenum_t.sfx_slop); - info = att.vileCorpseHit.info; - - att.vileCorpseHit.SetMobjState(info.raisestate); - att.vileCorpseHit.height <<= 2; - att.vileCorpseHit.flags = info.flags; - att.vileCorpseHit.health = info.spawnhealth; - att.vileCorpseHit.target = null; - - return; - } - } - } - } - - // Return to normal attack. - A_Chase(actor); - } - - // - // A_VileStart - // - default void A_VileStart(mobj_t actor) { - StartSound(actor, sounds.sfxenum_t.sfx_vilatk); - } - - // - // A_Fire - // Keep fire in front of player unless out of sight - // - default void A_StartFire(mobj_t actor) { - StartSound(actor, sounds.sfxenum_t.sfx_flamst); - A_Fire(actor); - } - - default void A_FireCrackle(mobj_t actor) { - StartSound(actor, sounds.sfxenum_t.sfx_flame); - A_Fire(actor); - } - - default void A_Fire(mobj_t actor) { - mobj_t dest; - //long an; - - dest = actor.tracer; - if (dest == null) { - return; - } - - // don't move it if the vile lost sight - if (!getEnemies().CheckSight(actor.target, dest)) { - return; - } - - // an = dest.angle >>> ANGLETOFINESHIFT; - getAttacks().UnsetThingPosition(actor); - actor.x = dest.x + FixedMul(24 * FRACUNIT, finecosine(dest.angle)); - actor.y = dest.y + FixedMul(24 * FRACUNIT, finesine(dest.angle)); - actor.z = dest.z; - SetThingPosition(actor); - } - - // - // A_VileTarget - // Spawn the hellfire - // - default void A_VileTarget(mobj_t actor) { - mobj_t fog; - - if (actor.target == null) { - return; - } - - A_FaceTarget(actor); - - fog = getEnemies().SpawnMobj(actor.target.x, actor.target.y, actor.target.z, mobjtype_t.MT_FIRE); - - actor.tracer = fog; - fog.target = actor; - fog.tracer = actor.target; - A_Fire(fog); - } - - // - // A_VileAttack - // - default void A_VileAttack(mobj_t actor) { - mobj_t fire; - //int an; - - if (actor.target == null) { - return; - } - - A_FaceTarget(actor); - - if (!getEnemies().CheckSight(actor, actor.target)) { - return; - } - - StartSound(actor, sounds.sfxenum_t.sfx_barexp); - getAttacks().DamageMobj(actor.target, actor, actor, 20); - actor.target.momz = 1000 * MAPFRACUNIT / actor.target.info.mass; - - // an = actor.angle >> ANGLETOFINESHIFT; - fire = actor.tracer; - - if (fire == null) { - return; - } - - // move the fire between the vile and the player - fire.x = actor.target.x - FixedMul(24 * FRACUNIT, finecosine(actor.angle)); - fire.y = actor.target.y - FixedMul(24 * FRACUNIT, finesine(actor.angle)); - getAttacks().RadiusAttack(fire, actor, 70); - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions.ActiveStates.MonsterStates; + +import static data.Limits.MAXRADIUS; +import static data.Tables.finecosine; +import static data.Tables.finesine; +import data.mobjinfo_t; +import data.mobjtype_t; +import data.sounds; +import defines.statenum_t; +import static m.fixed_t.FRACUNIT; +import static m.fixed_t.FixedMul; +import static m.fixed_t.MAPFRACUNIT; +import p.AbstractLevelLoader; +import p.Actions.ActionTrait; +import p.Actions.ActionsAttacks; +import p.Actions.ActionsAttacks.Attacks; +import static p.Actions.ActionsAttacks.KEY_ATTACKS; +import static p.ChaseDirections.DI_NODIR; +import static p.ChaseDirections.xspeed; +import static p.ChaseDirections.yspeed; +import p.mobj_t; + +public interface Viles extends ActionTrait { + + void A_FaceTarget(mobj_t actor); + + void A_Chase(mobj_t actor); + + // + // A_VileChase + // Check for ressurecting a body + // + default void A_VileChase(mobj_t actor) { + final AbstractLevelLoader ll = levelLoader(); + final ActionsAttacks actionsAttacks = getAttacks(); + final Attacks att = actionsAttacks.contextRequire(KEY_ATTACKS); + + int xl; + int xh; + int yl; + int yh; + + int bx; + int by; + + mobjinfo_t info; + mobj_t temp; + + if (actor.movedir != DI_NODIR) { + // check for corpses to raise + att.vileTryX = actor.x + actor.info.speed * xspeed[actor.movedir]; + att.vileTryY = actor.y + actor.info.speed * yspeed[actor.movedir]; + + xl = ll.getSafeBlockX(att.vileTryX - ll.bmaporgx - MAXRADIUS * 2); + xh = ll.getSafeBlockX(att.vileTryX - ll.bmaporgx + MAXRADIUS * 2); + yl = ll.getSafeBlockY(att.vileTryY - ll.bmaporgy - MAXRADIUS * 2); + yh = ll.getSafeBlockY(att.vileTryY - ll.bmaporgy + MAXRADIUS * 2); + + att.vileObj = actor; + for (bx = xl; bx <= xh; bx++) { + for (by = yl; by <= yh; by++) { + // Call PIT_VileCheck to check + // whether object is a corpse + // that can be raised. + if (!BlockThingsIterator(bx, by, actionsAttacks::VileCheck)) { + // got one! + temp = actor.target; + actor.target = att.vileCorpseHit; + A_FaceTarget(actor); + actor.target = temp; + + actor.SetMobjState(statenum_t.S_VILE_HEAL1); + StartSound(att.vileCorpseHit, sounds.sfxenum_t.sfx_slop); + info = att.vileCorpseHit.info; + + att.vileCorpseHit.SetMobjState(info.raisestate); + att.vileCorpseHit.height <<= 2; + att.vileCorpseHit.flags = info.flags; + att.vileCorpseHit.health = info.spawnhealth; + att.vileCorpseHit.target = null; + + return; + } + } + } + } + + // Return to normal attack. + A_Chase(actor); + } + + // + // A_VileStart + // + default void A_VileStart(mobj_t actor) { + StartSound(actor, sounds.sfxenum_t.sfx_vilatk); + } + + // + // A_Fire + // Keep fire in front of player unless out of sight + // + default void A_StartFire(mobj_t actor) { + StartSound(actor, sounds.sfxenum_t.sfx_flamst); + A_Fire(actor); + } + + default void A_FireCrackle(mobj_t actor) { + StartSound(actor, sounds.sfxenum_t.sfx_flame); + A_Fire(actor); + } + + default void A_Fire(mobj_t actor) { + mobj_t dest; + //long an; + + dest = actor.tracer; + if (dest == null) { + return; + } + + // don't move it if the vile lost sight + if (!getEnemies().CheckSight(actor.target, dest)) { + return; + } + + // an = dest.angle >>> ANGLETOFINESHIFT; + getAttacks().UnsetThingPosition(actor); + actor.x = dest.x + FixedMul(24 * FRACUNIT, finecosine(dest.angle)); + actor.y = dest.y + FixedMul(24 * FRACUNIT, finesine(dest.angle)); + actor.z = dest.z; + SetThingPosition(actor); + } + + // + // A_VileTarget + // Spawn the hellfire + // + default void A_VileTarget(mobj_t actor) { + mobj_t fog; + + if (actor.target == null) { + return; + } + + A_FaceTarget(actor); + + fog = getEnemies().SpawnMobj(actor.target.x, actor.target.y, actor.target.z, mobjtype_t.MT_FIRE); + + actor.tracer = fog; + fog.target = actor; + fog.tracer = actor.target; + A_Fire(fog); + } + + // + // A_VileAttack + // + default void A_VileAttack(mobj_t actor) { + mobj_t fire; + //int an; + + if (actor.target == null) { + return; + } + + A_FaceTarget(actor); + + if (!getEnemies().CheckSight(actor, actor.target)) { + return; + } + + StartSound(actor, sounds.sfxenum_t.sfx_barexp); + getAttacks().DamageMobj(actor.target, actor, actor, 20); + actor.target.momz = 1000 * MAPFRACUNIT / actor.target.info.mass; + + // an = actor.angle >> ANGLETOFINESHIFT; + fire = actor.tracer; + + if (fire == null) { + return; + } + + // move the fire between the vile and the player + fire.x = actor.target.x - FixedMul(24 * FRACUNIT, finecosine(actor.angle)); + fire.y = actor.target.y - FixedMul(24 * FRACUNIT, finesine(actor.angle)); + getAttacks().RadiusAttack(fire, actor, 70); + } +} \ No newline at end of file diff --git a/src/p/Actions/ActiveStates/MonsterStates/Zombies.java b/src/p/Actions/ActiveStates/MonsterStates/Zombies.java index 0dae0f2..e7bb6a8 100644 --- a/src/p/Actions/ActiveStates/MonsterStates/Zombies.java +++ b/src/p/Actions/ActiveStates/MonsterStates/Zombies.java @@ -1,106 +1,106 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions.ActiveStates.MonsterStates; - -import static data.Defines.MISSILERANGE; -import data.sounds; -import p.Actions.ActionTrait; -import p.mobj_t; - -public interface Zombies extends ActionTrait { - - void A_FaceTarget(mobj_t actor); - - // - // A_PosAttack - // - default void A_PosAttack(mobj_t actor) { - int angle; - int damage; - int slope; - - if (actor.target == null) { - return; - } - A_FaceTarget(actor); - angle = (int) actor.angle; - slope = getAttacks().AimLineAttack(actor, angle, MISSILERANGE); - - StartSound(actor, sounds.sfxenum_t.sfx_pistol); - angle += (P_Random() - P_Random()) << 20; - damage = ((P_Random() % 5) + 1) * 3; - getAttacks().LineAttack(actor, angle, MISSILERANGE, slope, damage); - } - - default void A_SPosAttack(mobj_t actor) { - int i; - long angle; - long bangle; - int damage; - int slope; - - if (actor.target == null) { - return; - } - - StartSound(actor, sounds.sfxenum_t.sfx_shotgn); - A_FaceTarget(actor); - bangle = actor.angle; - slope = getAttacks().AimLineAttack(actor, bangle, MISSILERANGE); - - for (i = 0; i < 3; i++) { - angle = bangle + ((P_Random() - P_Random()) << 20); - damage = ((P_Random() % 5) + 1) * 3; - getAttacks().LineAttack(actor, angle, MISSILERANGE, slope, damage); - } - } - - default void A_CPosAttack(mobj_t actor) { - long angle; - long bangle; - int damage; - int slope; - - if (actor.target == null) { - return; - } - - StartSound(actor, sounds.sfxenum_t.sfx_shotgn); - A_FaceTarget(actor); - bangle = actor.angle; - slope = getAttacks().AimLineAttack(actor, bangle, MISSILERANGE); - - angle = bangle + ((P_Random() - P_Random()) << 20); - damage = ((P_Random() % 5) + 1) * 3; - getAttacks().LineAttack(actor, angle, MISSILERANGE, slope, damage); - } - - default void A_CPosRefire(mobj_t actor) { - // keep firing unless target got out of sight - A_FaceTarget(actor); - - if (P_Random() < 40) { - return; - } - - if (actor.target == null || actor.target.health <= 0 || !getEnemies().CheckSight(actor, actor.target)) { - actor.SetMobjState(actor.info.seestate); - } - } - -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions.ActiveStates.MonsterStates; + +import static data.Defines.MISSILERANGE; +import data.sounds; +import p.Actions.ActionTrait; +import p.mobj_t; + +public interface Zombies extends ActionTrait { + + void A_FaceTarget(mobj_t actor); + + // + // A_PosAttack + // + default void A_PosAttack(mobj_t actor) { + int angle; + int damage; + int slope; + + if (actor.target == null) { + return; + } + A_FaceTarget(actor); + angle = (int) actor.angle; + slope = getAttacks().AimLineAttack(actor, angle, MISSILERANGE); + + StartSound(actor, sounds.sfxenum_t.sfx_pistol); + angle += (P_Random() - P_Random()) << 20; + damage = ((P_Random() % 5) + 1) * 3; + getAttacks().LineAttack(actor, angle, MISSILERANGE, slope, damage); + } + + default void A_SPosAttack(mobj_t actor) { + int i; + long angle; + long bangle; + int damage; + int slope; + + if (actor.target == null) { + return; + } + + StartSound(actor, sounds.sfxenum_t.sfx_shotgn); + A_FaceTarget(actor); + bangle = actor.angle; + slope = getAttacks().AimLineAttack(actor, bangle, MISSILERANGE); + + for (i = 0; i < 3; i++) { + angle = bangle + ((P_Random() - P_Random()) << 20); + damage = ((P_Random() % 5) + 1) * 3; + getAttacks().LineAttack(actor, angle, MISSILERANGE, slope, damage); + } + } + + default void A_CPosAttack(mobj_t actor) { + long angle; + long bangle; + int damage; + int slope; + + if (actor.target == null) { + return; + } + + StartSound(actor, sounds.sfxenum_t.sfx_shotgn); + A_FaceTarget(actor); + bangle = actor.angle; + slope = getAttacks().AimLineAttack(actor, bangle, MISSILERANGE); + + angle = bangle + ((P_Random() - P_Random()) << 20); + damage = ((P_Random() % 5) + 1) * 3; + getAttacks().LineAttack(actor, angle, MISSILERANGE, slope, damage); + } + + default void A_CPosRefire(mobj_t actor) { + // keep firing unless target got out of sight + A_FaceTarget(actor); + + if (P_Random() < 40) { + return; + } + + if (actor.target == null || actor.target.health <= 0 || !getEnemies().CheckSight(actor, actor.target)) { + actor.SetMobjState(actor.info.seestate); + } + } + +} \ No newline at end of file diff --git a/src/p/Actions/ActiveStates/Monsters.java b/src/p/Actions/ActiveStates/Monsters.java index bee4624..1eafebc 100644 --- a/src/p/Actions/ActiveStates/Monsters.java +++ b/src/p/Actions/ActiveStates/Monsters.java @@ -1,45 +1,45 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions.ActiveStates; - -import p.Actions.ActiveStates.MonsterStates.Bosses; -import p.Actions.ActiveStates.MonsterStates.Demonspawns; -import p.Actions.ActiveStates.MonsterStates.HorrendousVisages; -import p.Actions.ActiveStates.MonsterStates.Mancubi; -import p.Actions.ActiveStates.MonsterStates.PainsSouls; -import p.Actions.ActiveStates.MonsterStates.Skels; -import p.Actions.ActiveStates.MonsterStates.Spiders; -import p.Actions.ActiveStates.MonsterStates.Viles; -import p.Actions.ActiveStates.MonsterStates.Zombies; - -/** - * Include all from Monsters package - * - * @author Good Sign - */ -public interface Monsters extends - Bosses, - Demonspawns, - HorrendousVisages, - Mancubi, - PainsSouls, - Skels, - Spiders, - Viles, - Zombies { - -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions.ActiveStates; + +import p.Actions.ActiveStates.MonsterStates.Bosses; +import p.Actions.ActiveStates.MonsterStates.Demonspawns; +import p.Actions.ActiveStates.MonsterStates.HorrendousVisages; +import p.Actions.ActiveStates.MonsterStates.Mancubi; +import p.Actions.ActiveStates.MonsterStates.PainsSouls; +import p.Actions.ActiveStates.MonsterStates.Skels; +import p.Actions.ActiveStates.MonsterStates.Spiders; +import p.Actions.ActiveStates.MonsterStates.Viles; +import p.Actions.ActiveStates.MonsterStates.Zombies; + +/** + * Include all from Monsters package + * + * @author Good Sign + */ +public interface Monsters extends + Bosses, + Demonspawns, + HorrendousVisages, + Mancubi, + PainsSouls, + Skels, + Spiders, + Viles, + Zombies { + +} \ No newline at end of file diff --git a/src/p/Actions/ActiveStates/Sounds.java b/src/p/Actions/ActiveStates/Sounds.java index af7d08f..b505a06 100644 --- a/src/p/Actions/ActiveStates/Sounds.java +++ b/src/p/Actions/ActiveStates/Sounds.java @@ -1,126 +1,126 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions.ActiveStates; - -import data.mobjtype_t; -import data.sounds; -import doom.player_t; -import p.Actions.ActionTrait; -import p.mobj_t; -import p.pspdef_t; - -public interface Sounds extends ActionTrait { - - void A_Chase(mobj_t mo); - - void A_ReFire(player_t player, pspdef_t psp); - - void A_SpawnFly(mobj_t mo); - - default void A_Scream(mobj_t actor) { - int sound; - - switch (actor.info.deathsound) { - case sfx_None: - return; - - case sfx_podth1: - case sfx_podth2: - case sfx_podth3: - sound = sounds.sfxenum_t.sfx_podth1.ordinal() + P_Random() % 3; - break; - - case sfx_bgdth1: - case sfx_bgdth2: - sound = sounds.sfxenum_t.sfx_bgdth1.ordinal() + P_Random() % 2; - break; - - default: - sound = actor.info.deathsound.ordinal(); - break; - } - - // Check for bosses. - if (actor.type == mobjtype_t.MT_SPIDER - || actor.type == mobjtype_t.MT_CYBORG) { - // full volume - StartSound(null, sound); - } else { - StartSound(actor, sound); - } - } - - default void A_Hoof(mobj_t mo) { - StartSound(mo, sounds.sfxenum_t.sfx_hoof); - A_Chase(mo); - } - - // - // A_BFGsound - // - default void A_BFGsound(player_t player, pspdef_t psp) { - StartSound(player.mo, sounds.sfxenum_t.sfx_bfg); - } - - default void A_OpenShotgun2(player_t player, pspdef_t psp) { - StartSound(player.mo, sounds.sfxenum_t.sfx_dbopn); - } - - default void A_LoadShotgun2(player_t player, pspdef_t psp) { - StartSound(player.mo, sounds.sfxenum_t.sfx_dbload); - } - - default void A_CloseShotgun2(player_t player, pspdef_t psp) { - StartSound(player.mo, sounds.sfxenum_t.sfx_dbcls); - A_ReFire(player, psp); - } - - default void A_BrainPain(mobj_t mo) { - StartSound(null, sounds.sfxenum_t.sfx_bospn); - } - - default void A_Metal(mobj_t mo) { - StartSound(mo, sounds.sfxenum_t.sfx_metal); - A_Chase(mo); - } - - default void A_BabyMetal(mobj_t mo) { - StartSound(mo, sounds.sfxenum_t.sfx_bspwlk); - A_Chase(mo); - } - - // travelling cube sound - default void A_SpawnSound(mobj_t mo) { - StartSound(mo, sounds.sfxenum_t.sfx_boscub); - A_SpawnFly(mo); - } - - default void A_PlayerScream(mobj_t actor) { - // Default death sound. - sounds.sfxenum_t sound = sounds.sfxenum_t.sfx_pldeth; - - if (DOOM().isCommercial() && (actor.health < -50)) { - // IF THE PLAYER DIES - // LESS THAN -50% WITHOUT GIBBING - sound = sounds.sfxenum_t.sfx_pdiehi; - } - - StartSound(actor, sound); - } - -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions.ActiveStates; + +import data.mobjtype_t; +import data.sounds; +import doom.player_t; +import p.Actions.ActionTrait; +import p.mobj_t; +import p.pspdef_t; + +public interface Sounds extends ActionTrait { + + void A_Chase(mobj_t mo); + + void A_ReFire(player_t player, pspdef_t psp); + + void A_SpawnFly(mobj_t mo); + + default void A_Scream(mobj_t actor) { + int sound; + + switch (actor.info.deathsound) { + case sfx_None: + return; + + case sfx_podth1: + case sfx_podth2: + case sfx_podth3: + sound = sounds.sfxenum_t.sfx_podth1.ordinal() + P_Random() % 3; + break; + + case sfx_bgdth1: + case sfx_bgdth2: + sound = sounds.sfxenum_t.sfx_bgdth1.ordinal() + P_Random() % 2; + break; + + default: + sound = actor.info.deathsound.ordinal(); + break; + } + + // Check for bosses. + if (actor.type == mobjtype_t.MT_SPIDER + || actor.type == mobjtype_t.MT_CYBORG) { + // full volume + StartSound(null, sound); + } else { + StartSound(actor, sound); + } + } + + default void A_Hoof(mobj_t mo) { + StartSound(mo, sounds.sfxenum_t.sfx_hoof); + A_Chase(mo); + } + + // + // A_BFGsound + // + default void A_BFGsound(player_t player, pspdef_t psp) { + StartSound(player.mo, sounds.sfxenum_t.sfx_bfg); + } + + default void A_OpenShotgun2(player_t player, pspdef_t psp) { + StartSound(player.mo, sounds.sfxenum_t.sfx_dbopn); + } + + default void A_LoadShotgun2(player_t player, pspdef_t psp) { + StartSound(player.mo, sounds.sfxenum_t.sfx_dbload); + } + + default void A_CloseShotgun2(player_t player, pspdef_t psp) { + StartSound(player.mo, sounds.sfxenum_t.sfx_dbcls); + A_ReFire(player, psp); + } + + default void A_BrainPain(mobj_t mo) { + StartSound(null, sounds.sfxenum_t.sfx_bospn); + } + + default void A_Metal(mobj_t mo) { + StartSound(mo, sounds.sfxenum_t.sfx_metal); + A_Chase(mo); + } + + default void A_BabyMetal(mobj_t mo) { + StartSound(mo, sounds.sfxenum_t.sfx_bspwlk); + A_Chase(mo); + } + + // travelling cube sound + default void A_SpawnSound(mobj_t mo) { + StartSound(mo, sounds.sfxenum_t.sfx_boscub); + A_SpawnFly(mo); + } + + default void A_PlayerScream(mobj_t actor) { + // Default death sound. + sounds.sfxenum_t sound = sounds.sfxenum_t.sfx_pldeth; + + if (DOOM().isCommercial() && (actor.health < -50)) { + // IF THE PLAYER DIES + // LESS THAN -50% WITHOUT GIBBING + sound = sounds.sfxenum_t.sfx_pdiehi; + } + + StartSound(actor, sound); + } + +} \ No newline at end of file diff --git a/src/p/Actions/ActiveStates/Thinkers.java b/src/p/Actions/ActiveStates/Thinkers.java index da9aafc..c0c1520 100644 --- a/src/p/Actions/ActiveStates/Thinkers.java +++ b/src/p/Actions/ActiveStates/Thinkers.java @@ -1,139 +1,139 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions.ActiveStates; - -import doom.SourceCode; -import doom.SourceCode.P_Lights; -import static doom.SourceCode.P_Lights.T_FireFlicker; -import static doom.SourceCode.P_Lights.T_Glow; -import static doom.SourceCode.P_Lights.T_LightFlash; -import doom.thinker_t; -import p.Actions.ActionTrait; -import p.Actions.ActionsLights.fireflicker_t; -import p.Actions.ActionsLights.glow_t; -import p.Actions.ActionsLights.lightflash_t; -import static p.DoorDefines.GLOWSPEED; -import p.ceiling_t; -import p.floormove_t; -import p.plat_t; -import p.slidedoor_t; -import p.strobe_t; -import p.vldoor_t; - -public interface Thinkers extends ActionTrait { - - // - // T_FireFlicker - // - @SourceCode.Exact - @P_Lights.C(T_FireFlicker) - default void T_FireFlicker(thinker_t f) { - final fireflicker_t flick = (fireflicker_t) f; - int amount; - - if (--flick.count != 0) { - return; - } - - amount = (P_Random() & 3) * 16; - - if (flick.sector.lightlevel - amount < flick.minlight) { - flick.sector.lightlevel = (short) flick.minlight; - } else { - flick.sector.lightlevel = (short) (flick.maxlight - amount); - } - - flick.count = 4; - } - - /** - * T_LightFlash - * Do flashing lights. - */ - @SourceCode.Exact - @P_Lights.C(T_LightFlash) - default void T_LightFlash(thinker_t l) { - final lightflash_t flash = (lightflash_t) l; - if (--flash.count != 0) { - return; - } - - if (flash.sector.lightlevel == flash.maxlight) { - flash.sector.lightlevel = (short) flash.minlight; - flash.count = (P_Random() & flash.mintime) + 1; - } else { - flash.sector.lightlevel = (short) flash.maxlight; - flash.count = (P_Random() & flash.maxtime) + 1; - } - } - - default void T_StrobeFlash(thinker_t s) { - ((strobe_t) s).StrobeFlash(); - } - - // - // Spawn glowing light - // - @SourceCode.Exact - @P_Lights.C(T_Glow) - default void T_Glow(thinker_t t) { - glow_t g = (glow_t) t; - switch (g.direction) { - case -1: - // DOWN - g.sector.lightlevel -= GLOWSPEED; - if (g.sector.lightlevel <= g.minlight) { - g.sector.lightlevel += GLOWSPEED; - g.direction = 1; - } - break; - - case 1: - // UP - g.sector.lightlevel += GLOWSPEED; - if (g.sector.lightlevel >= g.maxlight) { - g.sector.lightlevel -= GLOWSPEED; - g.direction = -1; - } - break; - - default: - break; - } - } - - default void T_MoveCeiling(thinker_t c) { - getThinkers().MoveCeiling((ceiling_t) c); - } - - default void T_MoveFloor(thinker_t f) { - getThinkers().MoveFloor((floormove_t) f); - } - - default void T_VerticalDoor(thinker_t v) { - getThinkers().VerticalDoor((vldoor_t) v); - } - - default void T_SlidingDoor(thinker_t door) { - getThinkers().SlidingDoor((slidedoor_t) door); - } - - default void T_PlatRaise(thinker_t p) { - getThinkers().PlatRaise((plat_t) p); - } -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions.ActiveStates; + +import doom.SourceCode; +import doom.SourceCode.P_Lights; +import static doom.SourceCode.P_Lights.T_FireFlicker; +import static doom.SourceCode.P_Lights.T_Glow; +import static doom.SourceCode.P_Lights.T_LightFlash; +import doom.thinker_t; +import p.Actions.ActionTrait; +import p.Actions.ActionsLights.fireflicker_t; +import p.Actions.ActionsLights.glow_t; +import p.Actions.ActionsLights.lightflash_t; +import static p.DoorDefines.GLOWSPEED; +import p.ceiling_t; +import p.floormove_t; +import p.plat_t; +import p.slidedoor_t; +import p.strobe_t; +import p.vldoor_t; + +public interface Thinkers extends ActionTrait { + + // + // T_FireFlicker + // + @SourceCode.Exact + @P_Lights.C(T_FireFlicker) + default void T_FireFlicker(thinker_t f) { + final fireflicker_t flick = (fireflicker_t) f; + int amount; + + if (--flick.count != 0) { + return; + } + + amount = (P_Random() & 3) * 16; + + if (flick.sector.lightlevel - amount < flick.minlight) { + flick.sector.lightlevel = (short) flick.minlight; + } else { + flick.sector.lightlevel = (short) (flick.maxlight - amount); + } + + flick.count = 4; + } + + /** + * T_LightFlash + * Do flashing lights. + */ + @SourceCode.Exact + @P_Lights.C(T_LightFlash) + default void T_LightFlash(thinker_t l) { + final lightflash_t flash = (lightflash_t) l; + if (--flash.count != 0) { + return; + } + + if (flash.sector.lightlevel == flash.maxlight) { + flash.sector.lightlevel = (short) flash.minlight; + flash.count = (P_Random() & flash.mintime) + 1; + } else { + flash.sector.lightlevel = (short) flash.maxlight; + flash.count = (P_Random() & flash.maxtime) + 1; + } + } + + default void T_StrobeFlash(thinker_t s) { + ((strobe_t) s).StrobeFlash(); + } + + // + // Spawn glowing light + // + @SourceCode.Exact + @P_Lights.C(T_Glow) + default void T_Glow(thinker_t t) { + glow_t g = (glow_t) t; + switch (g.direction) { + case -1: + // DOWN + g.sector.lightlevel -= GLOWSPEED; + if (g.sector.lightlevel <= g.minlight) { + g.sector.lightlevel += GLOWSPEED; + g.direction = 1; + } + break; + + case 1: + // UP + g.sector.lightlevel += GLOWSPEED; + if (g.sector.lightlevel >= g.maxlight) { + g.sector.lightlevel -= GLOWSPEED; + g.direction = -1; + } + break; + + default: + break; + } + } + + default void T_MoveCeiling(thinker_t c) { + getThinkers().MoveCeiling((ceiling_t) c); + } + + default void T_MoveFloor(thinker_t f) { + getThinkers().MoveFloor((floormove_t) f); + } + + default void T_VerticalDoor(thinker_t v) { + getThinkers().VerticalDoor((vldoor_t) v); + } + + default void T_SlidingDoor(thinker_t door) { + getThinkers().SlidingDoor((slidedoor_t) door); + } + + default void T_PlatRaise(thinker_t p) { + getThinkers().PlatRaise((plat_t) p); + } +} \ No newline at end of file diff --git a/src/p/Actions/ActiveStates/Weapons.java b/src/p/Actions/ActiveStates/Weapons.java index ec7892d..e81db06 100644 --- a/src/p/Actions/ActiveStates/Weapons.java +++ b/src/p/Actions/ActiveStates/Weapons.java @@ -1,208 +1,208 @@ -/* - * Copyright (C) 1993-1996 by id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p.Actions.ActiveStates; - -import static data.Defines.BT_ATTACK; -import static data.Defines.PST_DEAD; -import static data.Tables.FINEANGLES; -import static data.Tables.FINEMASK; -import static data.Tables.finecosine; -import static data.Tables.finesine; -import static data.info.states; -import data.sounds; -import defines.statenum_t; -import static doom.items.weaponinfo; -import doom.player_t; -import static doom.player_t.LOWERSPEED; -import static doom.player_t.RAISESPEED; -import static doom.player_t.WEAPONBOTTOM; -import static doom.player_t.WEAPONTOP; -import static doom.player_t.ps_flash; -import static doom.player_t.ps_weapon; -import doom.weapontype_t; -import static m.fixed_t.FRACUNIT; -import static m.fixed_t.FixedMul; -import p.pspdef_t; -import static utils.C2JUtils.eval; - -public interface Weapons extends Sounds { - - /** - * A_WeaponReady - * The player can fire the weapon - * or change to another weapon at this time. - * Follows after getting weapon up, - * or after previous attack/fire sequence. - */ - default void A_WeaponReady(player_t player, pspdef_t psp) { - statenum_t newstate; - int angle; - - // get out of attack state - if (player.mo.mobj_state == states[statenum_t.S_PLAY_ATK1.ordinal()] - || player.mo.mobj_state == states[statenum_t.S_PLAY_ATK2.ordinal()]) { - player.mo.SetMobjState(statenum_t.S_PLAY); - } - - if (player.readyweapon == weapontype_t.wp_chainsaw - && psp.state == states[statenum_t.S_SAW.ordinal()]) { - StartSound(player.mo, sounds.sfxenum_t.sfx_sawidl); - } - - // check for change - // if player is dead, put the weapon away - if (player.pendingweapon != weapontype_t.wp_nochange || !eval(player.health[0])) { - // change weapon - // (pending weapon should allready be validated) - newstate = weaponinfo[player.readyweapon.ordinal()].downstate; - player.SetPsprite(player_t.ps_weapon, newstate); - return; - } - - // check for fire - // the missile launcher and bfg do not auto fire - if (eval(player.cmd.buttons & BT_ATTACK)) { - if (!player.attackdown - || (player.readyweapon != weapontype_t.wp_missile - && player.readyweapon != weapontype_t.wp_bfg)) { - player.attackdown = true; - getEnemies().FireWeapon(player); - return; - } - } else { - player.attackdown = false; - } - - // bob the weapon based on movement speed - angle = (128 * LevelTime()) & FINEMASK; - psp.sx = FRACUNIT + FixedMul(player.bob, finecosine[angle]); - angle &= FINEANGLES / 2 - 1; - psp.sy = player_t.WEAPONTOP + FixedMul(player.bob, finesine[angle]); - } - - // - // A_Raise - // - default void A_Raise(player_t player, pspdef_t psp) { - statenum_t newstate; - - //System.out.println("Trying to raise weapon"); - //System.out.println(player.readyweapon + " height: "+psp.sy); - psp.sy -= RAISESPEED; - - if (psp.sy > WEAPONTOP) { - //System.out.println("Not on top yet, exit and repeat."); - return; - } - - psp.sy = WEAPONTOP; - - // The weapon has been raised all the way, - // so change to the ready state. - newstate = weaponinfo[player.readyweapon.ordinal()].readystate; - //System.out.println("Weapon raised, setting new state."); - - player.SetPsprite(ps_weapon, newstate); - } - - // - // A_ReFire - // The player can re-fire the weapon - // without lowering it entirely. - // - @Override - default void A_ReFire(player_t player, pspdef_t psp) { - // check for fire - // (if a weaponchange is pending, let it go through instead) - if (eval(player.cmd.buttons & BT_ATTACK) - && player.pendingweapon == weapontype_t.wp_nochange - && eval(player.health[0])) { - player.refire++; - getEnemies().FireWeapon(player); - } else { - player.refire = 0; - player.CheckAmmo(); - } - } - - // - // A_GunFlash - // - default void A_GunFlash(player_t player, pspdef_t psp) { - player.mo.SetMobjState(statenum_t.S_PLAY_ATK2); - player.SetPsprite(ps_flash, weaponinfo[player.readyweapon.ordinal()].flashstate); - } - - // - // ? - // - default void A_Light0(player_t player, pspdef_t psp) { - player.extralight = 0; - } - - default void A_Light1(player_t player, pspdef_t psp) { - player.extralight = 1; - } - - default void A_Light2(player_t player, pspdef_t psp) { - player.extralight = 2; - } - - // - // A_Lower - // Lowers current weapon, - // and changes weapon at bottom. - // - default void A_Lower(player_t player, pspdef_t psp) { - psp.sy += LOWERSPEED; - - // Is already down. - if (psp.sy < WEAPONBOTTOM) { - return; - } - - // Player is dead. - if (player.playerstate == PST_DEAD) { - psp.sy = WEAPONBOTTOM; - - // don't bring weapon back up - return; - } - - // The old weapon has been lowered off the screen, - // so change the weapon and start raising it - if (!eval(player.health[0])) { - // Player is dead, so keep the weapon off screen. - player.SetPsprite(ps_weapon, statenum_t.S_NULL); - return; - } - - player.readyweapon = player.pendingweapon; - - player.BringUpWeapon(); - } - - default void A_CheckReload(player_t player, pspdef_t psp) { - player.CheckAmmo(); - /* - if (player.ammo[am_shell]<2) - P_SetPsprite (player, ps_weapon, S_DSNR1); - */ - } - -} +/* + * Copyright (C) 1993-1996 by id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p.Actions.ActiveStates; + +import static data.Defines.BT_ATTACK; +import static data.Defines.PST_DEAD; +import static data.Tables.FINEANGLES; +import static data.Tables.FINEMASK; +import static data.Tables.finecosine; +import static data.Tables.finesine; +import static data.info.states; +import data.sounds; +import defines.statenum_t; +import static doom.items.weaponinfo; +import doom.player_t; +import static doom.player_t.LOWERSPEED; +import static doom.player_t.RAISESPEED; +import static doom.player_t.WEAPONBOTTOM; +import static doom.player_t.WEAPONTOP; +import static doom.player_t.ps_flash; +import static doom.player_t.ps_weapon; +import doom.weapontype_t; +import static m.fixed_t.FRACUNIT; +import static m.fixed_t.FixedMul; +import p.pspdef_t; +import static utils.C2JUtils.eval; + +public interface Weapons extends Sounds { + + /** + * A_WeaponReady + * The player can fire the weapon + * or change to another weapon at this time. + * Follows after getting weapon up, + * or after previous attack/fire sequence. + */ + default void A_WeaponReady(player_t player, pspdef_t psp) { + statenum_t newstate; + int angle; + + // get out of attack state + if (player.mo.mobj_state == states[statenum_t.S_PLAY_ATK1.ordinal()] + || player.mo.mobj_state == states[statenum_t.S_PLAY_ATK2.ordinal()]) { + player.mo.SetMobjState(statenum_t.S_PLAY); + } + + if (player.readyweapon == weapontype_t.wp_chainsaw + && psp.state == states[statenum_t.S_SAW.ordinal()]) { + StartSound(player.mo, sounds.sfxenum_t.sfx_sawidl); + } + + // check for change + // if player is dead, put the weapon away + if (player.pendingweapon != weapontype_t.wp_nochange || !eval(player.health[0])) { + // change weapon + // (pending weapon should allready be validated) + newstate = weaponinfo[player.readyweapon.ordinal()].downstate; + player.SetPsprite(player_t.ps_weapon, newstate); + return; + } + + // check for fire + // the missile launcher and bfg do not auto fire + if (eval(player.cmd.buttons & BT_ATTACK)) { + if (!player.attackdown + || (player.readyweapon != weapontype_t.wp_missile + && player.readyweapon != weapontype_t.wp_bfg)) { + player.attackdown = true; + getEnemies().FireWeapon(player); + return; + } + } else { + player.attackdown = false; + } + + // bob the weapon based on movement speed + angle = (128 * LevelTime()) & FINEMASK; + psp.sx = FRACUNIT + FixedMul(player.bob, finecosine[angle]); + angle &= FINEANGLES / 2 - 1; + psp.sy = player_t.WEAPONTOP + FixedMul(player.bob, finesine[angle]); + } + + // + // A_Raise + // + default void A_Raise(player_t player, pspdef_t psp) { + statenum_t newstate; + + //System.out.println("Trying to raise weapon"); + //System.out.println(player.readyweapon + " height: "+psp.sy); + psp.sy -= RAISESPEED; + + if (psp.sy > WEAPONTOP) { + //System.out.println("Not on top yet, exit and repeat."); + return; + } + + psp.sy = WEAPONTOP; + + // The weapon has been raised all the way, + // so change to the ready state. + newstate = weaponinfo[player.readyweapon.ordinal()].readystate; + //System.out.println("Weapon raised, setting new state."); + + player.SetPsprite(ps_weapon, newstate); + } + + // + // A_ReFire + // The player can re-fire the weapon + // without lowering it entirely. + // + @Override + default void A_ReFire(player_t player, pspdef_t psp) { + // check for fire + // (if a weaponchange is pending, let it go through instead) + if (eval(player.cmd.buttons & BT_ATTACK) + && player.pendingweapon == weapontype_t.wp_nochange + && eval(player.health[0])) { + player.refire++; + getEnemies().FireWeapon(player); + } else { + player.refire = 0; + player.CheckAmmo(); + } + } + + // + // A_GunFlash + // + default void A_GunFlash(player_t player, pspdef_t psp) { + player.mo.SetMobjState(statenum_t.S_PLAY_ATK2); + player.SetPsprite(ps_flash, weaponinfo[player.readyweapon.ordinal()].flashstate); + } + + // + // ? + // + default void A_Light0(player_t player, pspdef_t psp) { + player.extralight = 0; + } + + default void A_Light1(player_t player, pspdef_t psp) { + player.extralight = 1; + } + + default void A_Light2(player_t player, pspdef_t psp) { + player.extralight = 2; + } + + // + // A_Lower + // Lowers current weapon, + // and changes weapon at bottom. + // + default void A_Lower(player_t player, pspdef_t psp) { + psp.sy += LOWERSPEED; + + // Is already down. + if (psp.sy < WEAPONBOTTOM) { + return; + } + + // Player is dead. + if (player.playerstate == PST_DEAD) { + psp.sy = WEAPONBOTTOM; + + // don't bring weapon back up + return; + } + + // The old weapon has been lowered off the screen, + // so change the weapon and start raising it + if (!eval(player.health[0])) { + // Player is dead, so keep the weapon off screen. + player.SetPsprite(ps_weapon, statenum_t.S_NULL); + return; + } + + player.readyweapon = player.pendingweapon; + + player.BringUpWeapon(); + } + + default void A_CheckReload(player_t player, pspdef_t psp) { + player.CheckAmmo(); + /* + if (player.ammo[am_shell]<2) + P_SetPsprite (player, ps_weapon, S_DSNR1); + */ + } + +} \ No newline at end of file diff --git a/src/p/ActiveStates.java b/src/p/ActiveStates.java index 9cf5af2..4e3ec8c 100644 --- a/src/p/ActiveStates.java +++ b/src/p/ActiveStates.java @@ -1,225 +1,225 @@ -/* - * Copyright (C) 1993-1996 Id Software, Inc. - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package p; - -import doom.SourceCode.D_Think; -import doom.SourceCode.D_Think.actionf_t; -import doom.SourceCode.actionf_p1; -import doom.SourceCode.actionf_p2; -import doom.SourceCode.actionf_v; -import doom.player_t; -import doom.thinker_t; -import java.util.logging.Level; -import java.util.logging.Logger; -import mochadoom.Loggers; - -/** - * In vanilla doom there is union called actionf_t that can hold - * one of the three types: actionf_p1, actionf_v and actionf_p2 - * - * typedef union - * { - * actionf_p1 acp1; - * actionf_v acv; - * actionf_p2 acp2; - * - * } actionf_t; - * - * For those unfamiliar with C, the union can have only one value - * assigned with all the values combined solving the behavior of - * logical and of all of them) - * - * actionf_p1, actionf_v and actionf_p2 are defined as these: - * - * typedef void (*actionf_v)(); - * typedef void (*actionf_p1)( void* ); - * typedef void (*actionf_p2)( void*, void* ); - * - * As you can see, they are pointers, so they all occupy the same space - * in the union: the length of the memory pointer. - * - * Effectively, this means that you can write to any of the three fields - * the pointer to the function correspoding to the field, and - * it will completely overwrite any other function assigned in other - * two fields. Even more: the other fields will have the same pointer, - * just with wrong type. - * - * In Mocha Doom, this were addressed differently. A special helper enum - * was created to hold possible names of the functions, and they were checked - * by name, not by equality of the objects (object == object if point the same) - * assigned to one of three fields. But, not understanding the true nature - * of C's unions, in Mocha Doom all three fields were preserved and threated - * like they can hold some different information at the same time. - * - * I present hereby the solution that will both simplify the definition - * and usage of the action functions, and provide a way to achieve the - * exact same behavior as would be in C: if you assign the function, - * you will replace the old one (virtually, "all the three fields") - * and you can call any function with 0 to 2 arguments. - * - * Also to store the functions in the same place where we declare them, - * an Command pattern is implemented, requiring the function caller - * to provide himself or any sufficient class that implements the Client - * contract to provide the information needed for holding the state - * of action functions. - * - * - Good Sign 2017/04/28 - * - * Thinkers can either have one parameter of type (mobj_t), - * Or otherwise be sector specials, flickering lights etc. - * Those are atypical and need special handling. - */ -public enum ActiveStates implements ThinkerStates { - NOP(ActiveStates::nop, ThinkerConsumer.class), - A_Light0(ActionFunctions::A_Light0, PlayerSpriteConsumer.class), - A_WeaponReady(ActionFunctions::A_WeaponReady, PlayerSpriteConsumer.class), - A_Lower(ActionFunctions::A_Lower, PlayerSpriteConsumer.class), - A_Raise(ActionFunctions::A_Raise, PlayerSpriteConsumer.class), - A_Punch(ActionFunctions::A_Punch, PlayerSpriteConsumer.class), - A_ReFire(ActionFunctions::A_ReFire, PlayerSpriteConsumer.class), - A_FirePistol(ActionFunctions::A_FirePistol, PlayerSpriteConsumer.class), - A_Light1(ActionFunctions::A_Light1, PlayerSpriteConsumer.class), - A_FireShotgun(ActionFunctions::A_FireShotgun, PlayerSpriteConsumer.class), - A_Light2(ActionFunctions::A_Light2, PlayerSpriteConsumer.class), - A_FireShotgun2(ActionFunctions::A_FireShotgun2, PlayerSpriteConsumer.class), - A_CheckReload(ActionFunctions::A_CheckReload, PlayerSpriteConsumer.class), - A_OpenShotgun2(ActionFunctions::A_OpenShotgun2, PlayerSpriteConsumer.class), - A_LoadShotgun2(ActionFunctions::A_LoadShotgun2, PlayerSpriteConsumer.class), - A_CloseShotgun2(ActionFunctions::A_CloseShotgun2, PlayerSpriteConsumer.class), - A_FireCGun(ActionFunctions::A_FireCGun, PlayerSpriteConsumer.class), - A_GunFlash(ActionFunctions::A_GunFlash, PlayerSpriteConsumer.class), - A_FireMissile(ActionFunctions::A_FireMissile, PlayerSpriteConsumer.class), - A_Saw(ActionFunctions::A_Saw, PlayerSpriteConsumer.class), - A_FirePlasma(ActionFunctions::A_FirePlasma, PlayerSpriteConsumer.class), - A_BFGsound(ActionFunctions::A_BFGsound, PlayerSpriteConsumer.class), - A_FireBFG(ActionFunctions::A_FireBFG, PlayerSpriteConsumer.class), - A_BFGSpray(ActionFunctions::A_BFGSpray, MobjConsumer.class), - A_Explode(ActionFunctions::A_Explode, MobjConsumer.class), - A_Pain(ActionFunctions::A_Pain, MobjConsumer.class), - A_PlayerScream(ActionFunctions::A_PlayerScream, MobjConsumer.class), - A_Fall(ActionFunctions::A_Fall, MobjConsumer.class), - A_XScream(ActionFunctions::A_XScream, MobjConsumer.class), - A_Look(ActionFunctions::A_Look, MobjConsumer.class), - A_Chase(ActionFunctions::A_Chase, MobjConsumer.class), - A_FaceTarget(ActionFunctions::A_FaceTarget, MobjConsumer.class), - A_PosAttack(ActionFunctions::A_PosAttack, MobjConsumer.class), - A_Scream(ActionFunctions::A_Scream, MobjConsumer.class), - A_SPosAttack(ActionFunctions::A_SPosAttack, MobjConsumer.class), - A_VileChase(ActionFunctions::A_VileChase, MobjConsumer.class), - A_VileStart(ActionFunctions::A_VileStart, MobjConsumer.class), - A_VileTarget(ActionFunctions::A_VileTarget, MobjConsumer.class), - A_VileAttack(ActionFunctions::A_VileAttack, MobjConsumer.class), - A_StartFire(ActionFunctions::A_StartFire, MobjConsumer.class), - A_Fire(ActionFunctions::A_Fire, MobjConsumer.class), - A_FireCrackle(ActionFunctions::A_FireCrackle, MobjConsumer.class), - A_Tracer(ActionFunctions::A_Tracer, MobjConsumer.class), - A_SkelWhoosh(ActionFunctions::A_SkelWhoosh, MobjConsumer.class), - A_SkelFist(ActionFunctions::A_SkelFist, MobjConsumer.class), - A_SkelMissile(ActionFunctions::A_SkelMissile, MobjConsumer.class), - A_FatRaise(ActionFunctions::A_FatRaise, MobjConsumer.class), - A_FatAttack1(ActionFunctions::A_FatAttack1, MobjConsumer.class), - A_FatAttack2(ActionFunctions::A_FatAttack2, MobjConsumer.class), - A_FatAttack3(ActionFunctions::A_FatAttack3, MobjConsumer.class), - A_BossDeath(ActionFunctions::A_BossDeath, MobjConsumer.class), - A_CPosAttack(ActionFunctions::A_CPosAttack, MobjConsumer.class), - A_CPosRefire(ActionFunctions::A_CPosRefire, MobjConsumer.class), - A_TroopAttack(ActionFunctions::A_TroopAttack, MobjConsumer.class), - A_SargAttack(ActionFunctions::A_SargAttack, MobjConsumer.class), - A_HeadAttack(ActionFunctions::A_HeadAttack, MobjConsumer.class), - A_BruisAttack(ActionFunctions::A_BruisAttack, MobjConsumer.class), - A_SkullAttack(ActionFunctions::A_SkullAttack, MobjConsumer.class), - A_Metal(ActionFunctions::A_Metal, MobjConsumer.class), - A_SpidRefire(ActionFunctions::A_SpidRefire, MobjConsumer.class), - A_BabyMetal(ActionFunctions::A_BabyMetal, MobjConsumer.class), - A_BspiAttack(ActionFunctions::A_BspiAttack, MobjConsumer.class), - A_Hoof(ActionFunctions::A_Hoof, MobjConsumer.class), - A_CyberAttack(ActionFunctions::A_CyberAttack, MobjConsumer.class), - A_PainAttack(ActionFunctions::A_PainAttack, MobjConsumer.class), - A_PainDie(ActionFunctions::A_PainDie, MobjConsumer.class), - A_KeenDie(ActionFunctions::A_KeenDie, MobjConsumer.class), - A_BrainPain(ActionFunctions::A_BrainPain, MobjConsumer.class), - A_BrainScream(ActionFunctions::A_BrainScream, MobjConsumer.class), - A_BrainDie(ActionFunctions::A_BrainDie, MobjConsumer.class), - A_BrainAwake(ActionFunctions::A_BrainAwake, MobjConsumer.class), - A_BrainSpit(ActionFunctions::A_BrainSpit, MobjConsumer.class), - A_SpawnSound(ActionFunctions::A_SpawnSound, MobjConsumer.class), - A_SpawnFly(ActionFunctions::A_SpawnFly, MobjConsumer.class), - A_BrainExplode(ActionFunctions::A_BrainExplode, MobjConsumer.class), - P_MobjThinker(ActionFunctions::P_MobjThinker, MobjConsumer.class), - T_FireFlicker(ActionFunctions::T_FireFlicker, ThinkerConsumer.class), - T_LightFlash(ActionFunctions::T_LightFlash, ThinkerConsumer.class), - T_StrobeFlash(ActionFunctions::T_StrobeFlash, ThinkerConsumer.class), - T_Glow(ActionFunctions::T_Glow, ThinkerConsumer.class), - T_MoveCeiling(ActionFunctions::T_MoveCeiling, ThinkerConsumer.class), - T_MoveFloor(ActionFunctions::T_MoveFloor, ThinkerConsumer.class), - T_VerticalDoor(ActionFunctions::T_VerticalDoor, ThinkerConsumer.class), - T_PlatRaise(ActionFunctions::T_PlatRaise, ThinkerConsumer.class), - T_SlidingDoor(ActionFunctions::T_SlidingDoor, ThinkerConsumer.class); - - private final static Logger LOGGER = Loggers.getLogger(ActiveStates.class.getName()); - - private final ParamClass actionFunction; - private final Class> paramType; - - private > ActiveStates(final T actionFunction, final Class paramType) { - this.actionFunction = actionFunction; - this.paramType = paramType; - } - - private static void nop(Object... o) { - } - - @actionf_p1 - @D_Think.C(actionf_t.acp1) - public interface MobjConsumer extends ParamClass { - - void accept(ActionFunctions a, mobj_t m); - } - - @actionf_v - @D_Think.C(actionf_t.acv) - public interface ThinkerConsumer extends ParamClass { - - void accept(ActionFunctions a, thinker_t t); - } - - @actionf_p2 - @D_Think.C(actionf_t.acp2) - public interface PlayerSpriteConsumer extends ParamClass { - - void accept(ActionFunctions a, player_t p, pspdef_t s); - } - - private interface ParamClass> { - } - - public boolean isParamType(final Class paramType) { - return this.paramType == paramType; - } - - @SuppressWarnings("unchecked") - public > T fun(final Class paramType) { - if (this.paramType != paramType) { - LOGGER.log(Level.WARNING, "Wrong paramType for state: {0}", this); - return null; - } - - // don't believe, it's checked - return (T) this.actionFunction; - } -} +/* + * Copyright (C) 1993-1996 Id Software, Inc. + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package p; + +import doom.SourceCode.D_Think; +import doom.SourceCode.D_Think.actionf_t; +import doom.SourceCode.actionf_p1; +import doom.SourceCode.actionf_p2; +import doom.SourceCode.actionf_v; +import doom.player_t; +import doom.thinker_t; +import java.util.logging.Level; +import java.util.logging.Logger; +import mochadoom.Loggers; + +/** + * In vanilla doom there is union called actionf_t that can hold + * one of the three types: actionf_p1, actionf_v and actionf_p2 + * + * typedef union + * { + * actionf_p1 acp1; + * actionf_v acv; + * actionf_p2 acp2; + * + * } actionf_t; + * + * For those unfamiliar with C, the union can have only one value + * assigned with all the values combined solving the behavior of + * logical and of all of them) + * + * actionf_p1, actionf_v and actionf_p2 are defined as these: + * + * typedef void (*actionf_v)(); + * typedef void (*actionf_p1)( void* ); + * typedef void (*actionf_p2)( void*, void* ); + * + * As you can see, they are pointers, so they all occupy the same space + * in the union: the length of the memory pointer. + * + * Effectively, this means that you can write to any of the three fields + * the pointer to the function correspoding to the field, and + * it will completely overwrite any other function assigned in other + * two fields. Even more: the other fields will have the same pointer, + * just with wrong type. + * + * In Mocha Doom, this were addressed differently. A special helper enum + * was created to hold possible names of the functions, and they were checked + * by name, not by equality of the objects (object == object if point the same) + * assigned to one of three fields. But, not understanding the true nature + * of C's unions, in Mocha Doom all three fields were preserved and threated + * like they can hold some different information at the same time. + * + * I present hereby the solution that will both simplify the definition + * and usage of the action functions, and provide a way to achieve the + * exact same behavior as would be in C: if you assign the function, + * you will replace the old one (virtually, "all the three fields") + * and you can call any function with 0 to 2 arguments. + * + * Also to store the functions in the same place where we declare them, + * an Command pattern is implemented, requiring the function caller + * to provide himself or any sufficient class that implements the Client + * contract to provide the information needed for holding the state + * of action functions. + * + * - Good Sign 2017/04/28 + * + * Thinkers can either have one parameter of type (mobj_t), + * Or otherwise be sector specials, flickering lights etc. + * Those are atypical and need special handling. + */ +public enum ActiveStates implements ThinkerStates { + NOP(ActiveStates::nop, ThinkerConsumer.class), + A_Light0(ActionFunctions::A_Light0, PlayerSpriteConsumer.class), + A_WeaponReady(ActionFunctions::A_WeaponReady, PlayerSpriteConsumer.class), + A_Lower(ActionFunctions::A_Lower, PlayerSpriteConsumer.class), + A_Raise(ActionFunctions::A_Raise, PlayerSpriteConsumer.class), + A_Punch(ActionFunctions::A_Punch, PlayerSpriteConsumer.class), + A_ReFire(ActionFunctions::A_ReFire, PlayerSpriteConsumer.class), + A_FirePistol(ActionFunctions::A_FirePistol, PlayerSpriteConsumer.class), + A_Light1(ActionFunctions::A_Light1, PlayerSpriteConsumer.class), + A_FireShotgun(ActionFunctions::A_FireShotgun, PlayerSpriteConsumer.class), + A_Light2(ActionFunctions::A_Light2, PlayerSpriteConsumer.class), + A_FireShotgun2(ActionFunctions::A_FireShotgun2, PlayerSpriteConsumer.class), + A_CheckReload(ActionFunctions::A_CheckReload, PlayerSpriteConsumer.class), + A_OpenShotgun2(ActionFunctions::A_OpenShotgun2, PlayerSpriteConsumer.class), + A_LoadShotgun2(ActionFunctions::A_LoadShotgun2, PlayerSpriteConsumer.class), + A_CloseShotgun2(ActionFunctions::A_CloseShotgun2, PlayerSpriteConsumer.class), + A_FireCGun(ActionFunctions::A_FireCGun, PlayerSpriteConsumer.class), + A_GunFlash(ActionFunctions::A_GunFlash, PlayerSpriteConsumer.class), + A_FireMissile(ActionFunctions::A_FireMissile, PlayerSpriteConsumer.class), + A_Saw(ActionFunctions::A_Saw, PlayerSpriteConsumer.class), + A_FirePlasma(ActionFunctions::A_FirePlasma, PlayerSpriteConsumer.class), + A_BFGsound(ActionFunctions::A_BFGsound, PlayerSpriteConsumer.class), + A_FireBFG(ActionFunctions::A_FireBFG, PlayerSpriteConsumer.class), + A_BFGSpray(ActionFunctions::A_BFGSpray, MobjConsumer.class), + A_Explode(ActionFunctions::A_Explode, MobjConsumer.class), + A_Pain(ActionFunctions::A_Pain, MobjConsumer.class), + A_PlayerScream(ActionFunctions::A_PlayerScream, MobjConsumer.class), + A_Fall(ActionFunctions::A_Fall, MobjConsumer.class), + A_XScream(ActionFunctions::A_XScream, MobjConsumer.class), + A_Look(ActionFunctions::A_Look, MobjConsumer.class), + A_Chase(ActionFunctions::A_Chase, MobjConsumer.class), + A_FaceTarget(ActionFunctions::A_FaceTarget, MobjConsumer.class), + A_PosAttack(ActionFunctions::A_PosAttack, MobjConsumer.class), + A_Scream(ActionFunctions::A_Scream, MobjConsumer.class), + A_SPosAttack(ActionFunctions::A_SPosAttack, MobjConsumer.class), + A_VileChase(ActionFunctions::A_VileChase, MobjConsumer.class), + A_VileStart(ActionFunctions::A_VileStart, MobjConsumer.class), + A_VileTarget(ActionFunctions::A_VileTarget, MobjConsumer.class), + A_VileAttack(ActionFunctions::A_VileAttack, MobjConsumer.class), + A_StartFire(ActionFunctions::A_StartFire, MobjConsumer.class), + A_Fire(ActionFunctions::A_Fire, MobjConsumer.class), + A_FireCrackle(ActionFunctions::A_FireCrackle, MobjConsumer.class), + A_Tracer(ActionFunctions::A_Tracer, MobjConsumer.class), + A_SkelWhoosh(ActionFunctions::A_SkelWhoosh, MobjConsumer.class), + A_SkelFist(ActionFunctions::A_SkelFist, MobjConsumer.class), + A_SkelMissile(ActionFunctions::A_SkelMissile, MobjConsumer.class), + A_FatRaise(ActionFunctions::A_FatRaise, MobjConsumer.class), + A_FatAttack1(ActionFunctions::A_FatAttack1, MobjConsumer.class), + A_FatAttack2(ActionFunctions::A_FatAttack2, MobjConsumer.class), + A_FatAttack3(ActionFunctions::A_FatAttack3, MobjConsumer.class), + A_BossDeath(ActionFunctions::A_BossDeath, MobjConsumer.class), + A_CPosAttack(ActionFunctions::A_CPosAttack, MobjConsumer.class), + A_CPosRefire(ActionFunctions::A_CPosRefire, MobjConsumer.class), + A_TroopAttack(ActionFunctions::A_TroopAttack, MobjConsumer.class), + A_SargAttack(ActionFunctions::A_SargAttack, MobjConsumer.class), + A_HeadAttack(ActionFunctions::A_HeadAttack, MobjConsumer.class), + A_BruisAttack(ActionFunctions::A_BruisAttack, MobjConsumer.class), + A_SkullAttack(ActionFunctions::A_SkullAttack, MobjConsumer.class), + A_Metal(ActionFunctions::A_Metal, MobjConsumer.class), + A_SpidRefire(ActionFunctions::A_SpidRefire, MobjConsumer.class), + A_BabyMetal(ActionFunctions::A_BabyMetal, MobjConsumer.class), + A_BspiAttack(ActionFunctions::A_BspiAttack, MobjConsumer.class), + A_Hoof(ActionFunctions::A_Hoof, MobjConsumer.class), + A_CyberAttack(ActionFunctions::A_CyberAttack, MobjConsumer.class), + A_PainAttack(ActionFunctions::A_PainAttack, MobjConsumer.class), + A_PainDie(ActionFunctions::A_PainDie, MobjConsumer.class), + A_KeenDie(ActionFunctions::A_KeenDie, MobjConsumer.class), + A_BrainPain(ActionFunctions::A_BrainPain, MobjConsumer.class), + A_BrainScream(ActionFunctions::A_BrainScream, MobjConsumer.class), + A_BrainDie(ActionFunctions::A_BrainDie, MobjConsumer.class), + A_BrainAwake(ActionFunctions::A_BrainAwake, MobjConsumer.class), + A_BrainSpit(ActionFunctions::A_BrainSpit, MobjConsumer.class), + A_SpawnSound(ActionFunctions::A_SpawnSound, MobjConsumer.class), + A_SpawnFly(ActionFunctions::A_SpawnFly, MobjConsumer.class), + A_BrainExplode(ActionFunctions::A_BrainExplode, MobjConsumer.class), + P_MobjThinker(ActionFunctions::P_MobjThinker, MobjConsumer.class), + T_FireFlicker(ActionFunctions::T_FireFlicker, ThinkerConsumer.class), + T_LightFlash(ActionFunctions::T_LightFlash, ThinkerConsumer.class), + T_StrobeFlash(ActionFunctions::T_StrobeFlash, ThinkerConsumer.class), + T_Glow(ActionFunctions::T_Glow, ThinkerConsumer.class), + T_MoveCeiling(ActionFunctions::T_MoveCeiling, ThinkerConsumer.class), + T_MoveFloor(ActionFunctions::T_MoveFloor, ThinkerConsumer.class), + T_VerticalDoor(ActionFunctions::T_VerticalDoor, ThinkerConsumer.class), + T_PlatRaise(ActionFunctions::T_PlatRaise, ThinkerConsumer.class), + T_SlidingDoor(ActionFunctions::T_SlidingDoor, ThinkerConsumer.class); + + private final static Logger LOGGER = Loggers.getLogger(ActiveStates.class.getName()); + + private final ParamClass actionFunction; + private final Class> paramType; + + private > ActiveStates(final T actionFunction, final Class paramType) { + this.actionFunction = actionFunction; + this.paramType = paramType; + } + + private static void nop(Object... o) { + } + + @actionf_p1 + @D_Think.C(actionf_t.acp1) + public interface MobjConsumer extends ParamClass { + + void accept(ActionFunctions a, mobj_t m); + } + + @actionf_v + @D_Think.C(actionf_t.acv) + public interface ThinkerConsumer extends ParamClass { + + void accept(ActionFunctions a, thinker_t t); + } + + @actionf_p2 + @D_Think.C(actionf_t.acp2) + public interface PlayerSpriteConsumer extends ParamClass { + + void accept(ActionFunctions a, player_t p, pspdef_t s); + } + + private interface ParamClass> { + } + + public boolean isParamType(final Class paramType) { + return this.paramType == paramType; + } + + @SuppressWarnings("unchecked") + public > T fun(final Class paramType) { + if (this.paramType != paramType) { + LOGGER.log(Level.WARNING, "Wrong paramType for state: {0}", this); + return null; + } + + // don't believe, it's checked + return (T) this.actionFunction; + } +} \ No newline at end of file diff --git a/src/p/BoomLevelLoader.java b/src/p/BoomLevelLoader.java index 64666f0..92123d5 100644 --- a/src/p/BoomLevelLoader.java +++ b/src/p/BoomLevelLoader.java @@ -1,2237 +1,2237 @@ -package p; - -import static boom.Compatibility.prboom_2_compatibility; -import boom.DeepBSPNodesV4; -import static boom.E6Y.NO_INDEX; -import boom.mapglvertex_t; -import boom.mapnode_v4_t; -import boom.mapnode_znod_t; -import boom.mapseg_v4_t; -import boom.mapseg_znod_t; -import boom.mapsubsector_v4_t; -import boom.mapsubsector_znod_t; -import static data.Defines.NF_SUBSECTOR; -import static data.Defines.NF_SUBSECTOR_CLASSIC; -import static data.Defines.PU_LEVEL; -import data.Limits; -import data.maplinedef_t; -import data.mapnode_t; -import data.mapsector_t; -import data.mapseg_t; -import data.mapsidedef_t; -import data.mapsubsector_t; -import data.mapthing_t; -import data.mapvertex_t; -import defines.skill_t; -import defines.slopetype_t; -import doom.CommandVariable; -import doom.DoomMain; -import doom.DoomStatus; -import doom.SourceCode; -import doom.SourceCode.CauseOfDesyncProbability; -import doom.SourceCode.P_Setup; -import static doom.SourceCode.P_Setup.P_LoadThings; -import static doom.SourceCode.P_Setup.P_SetupLevel; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Arrays; -import java.util.function.IntFunction; -import java.util.logging.Level; -import java.util.logging.Logger; -import m.BBox; -import static m.BBox.BOXBOTTOM; -import static m.BBox.BOXLEFT; -import static m.BBox.BOXRIGHT; -import static m.BBox.BOXTOP; -import m.fixed_t; -import static m.fixed_t.FRACBITS; -import static m.fixed_t.FRACUNIT; -import mochadoom.Loggers; -import rr.RendererState; -import rr.line_t; -import static rr.line_t.ML_TWOSIDED; -import rr.node_t; -import rr.sector_t; -import rr.seg_t; -import rr.side_t; -import rr.subsector_t; -import rr.vertex_t; -import rr.z_vertex_t; -import s.degenmobj_t; -import utils.C2JUtils; -import static utils.C2JUtils.flags; -import static utils.C2JUtils.unsigned; -import utils.GenericCopy.ArraySupplier; -import static utils.GenericCopy.malloc; -import w.CacheableDoomObjectContainer; -import w.DoomBuffer; -import w.wadfile_info_t; - -/* - * Emacs style mode select -*- Java -*- - * ----------------------------------------------------------------------------- - * PrBoom: a Doom port merged with LxDoom and LSDLDoom based on BOOM, a modified - * and improved DOOM engine Copyright (C) 1999 by id Software, Chi Hoang, Lee - * Killough, Jim Flynn, Rand Phares, Ty Halderman Copyright (C) 1999-2000 by - * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze Copyright 2005, - * 2006 by Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko This - * program is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. DESCRIPTION: Do all - * the WAD I/O, get map description, set up initial state and misc. LUTs. - * - * MAES 30/9/2011: This is a direct translation of prBoom+'s 2.5.0.8 p_setup.c - * and p_setup.h. - * - * - * - * ----------------------------------------------------------------------------- - */ -public class BoomLevelLoader extends AbstractLevelLoader { - - private static final Logger LOGGER = Loggers.getLogger(BoomLevelLoader.class.getName()); - - public BoomLevelLoader(DoomMain DM) { - super(DM); - // TODO Auto-generated constructor stub - } - - // OpenGL related. - byte[] map_subsectors; - - // ////////////////////////////////////////////////////////////////////////////////////////// - // figgi 08/21/00 -- finalants and globals for glBsp support - public static final int gNd2 = 0x32644E67; // figgi -- suppport for new - // GL_VERT format v2.0 - - public static final int gNd3 = 0x33644E67; - - public static final int gNd4 = 0x34644E67; - - public static final int gNd5 = 0x35644E67; - - public static final int ZNOD = 0x444F4E5A; - - public static final int ZGLN = 0x4E4C475A; - - public static final int GL_VERT_OFFSET = 4; - - int firstglvertex = 0; - - int nodesVersion = 0; - - boolean forceOldBsp = false; - - // figgi 08/21/00 -- glSegs - class glseg_t { - - char v1; // start vertex (16 bit) - - char v2; // end vertex (16 bit) - - char linedef; // linedef, or -1 for minisegs - - short side; // side on linedef: 0 for right, 1 for left - - short partner; // corresponding partner seg, or -1 on one-sided walls - } - - public static final int ML_GL_LABEL = 0; // A separator name, GL_ExMx or - // GL_MAPxx - - public static final int ML_GL_VERTS = 1; // Extra Vertices - - public static final int ML_GL_SEGS = 2; // Segs, from linedefs & minisegs - - public static final int ML_GL_SSECT = 3; // SubSectors, list of segs - - public static final int ML_GL_NODES = 4; // GL BSP nodes - - // ////////////////////////////////////////////////////////////////////////////////////////// - // - // REJECT - // For fast sight rejection. - // Speeds up enemy AI by skipping detailed - // LineOf Sight calculation. - // Without the special effect, this could - // be used as a PVS lookup as well. - // - private int rejectlump = -1;// cph - store reject lump num if cached - - private int current_episode = -1; - - private int current_map = -1; - - private int current_nodesVersion = -1; - - private boolean samelevel = false; - - /** - * e6y: Smart malloc Used by P_SetupLevel() for smart data loading. Do - * nothing if level is the same. Passing a null array forces allocation. - * - * @param p - * generically typed array to consider - * @param numstuff - * elements to realloc - */ - private T[] malloc_IfSameLevel(T[] p, int numstuff, ArraySupplier supplier, IntFunction generator) { - if (!samelevel || (p == null)) { - return malloc(supplier, generator, numstuff); - } - return p; - } - - // e6y: Smart calloc - // Used by P_SetupLevel() for smart data loading - // Clear the memory without allocation if level is the same - private T[] calloc_IfSameLevel(T[] p, int numstuff, ArraySupplier supplier, IntFunction generator) { - if (!samelevel) { - return malloc(supplier, generator, numstuff); - } else { - // TODO: stuff should be resetted! - C2JUtils.resetAll(p); - return p; - } - } - - // - // P_CheckForZDoomNodes - // - private boolean P_CheckForZDoomNodes(int lumpnum, int gl_lumpnum) { - byte[] data; - int check; - - data = DOOM.wadLoader.CacheLumpNumAsRawBytes(lumpnum + ML_NODES, 0); - check = ByteBuffer.wrap(data).getInt(); - - if (check == ZNOD) { - DOOM.doomSystem.Error("P_CheckForZDoomNodes: ZDoom nodes not supported yet"); - } - - data = DOOM.wadLoader.CacheLumpNumAsRawBytes(lumpnum + ML_SSECTORS, 0); - check = ByteBuffer.wrap(data).getInt(); - - if (check == ZGLN) { - DOOM.doomSystem.Error("P_CheckForZDoomNodes: ZDoom GL nodes not supported yet"); - } - - // Unlock them to force different buffering interpretation. - DOOM.wadLoader.UnlockLumpNum(lumpnum + ML_NODES); - DOOM.wadLoader.UnlockLumpNum(lumpnum + ML_SSECTORS); - - return false; - } - - // - // P_CheckForDeePBSPv4Nodes - // http://www.sbsoftware.com/files/DeePBSPV4specs.txt - // - private boolean P_CheckForDeePBSPv4Nodes(int lumpnum, int gl_lumpnum) { - byte[] data; - boolean result = false; - - data = DOOM.wadLoader.CacheLumpNumAsRawBytes(lumpnum + ML_NODES, 0); - byte[] compare = Arrays.copyOfRange(data, 0, 7); - - if (Arrays.equals(compare, DeepBSPNodesV4.DeepBSPHeader)) { - LOGGER.log(Level.INFO, "P_CheckForDeePBSPv4Nodes: DeePBSP v4 Extended nodes are detected"); - result = true; - } - - DOOM.wadLoader.UnlockLumpNum(lumpnum + ML_NODES); - - return result; - } - - // - // P_CheckForZDoomUncompressedNodes - // http://zdoom.org/wiki/ZDBSP#Compressed_Nodes - // - private static final int XNOD = 0x584e4f44; - - private boolean P_CheckForZDoomUncompressedNodes(int lumpnum, int gl_lumpnum) { - byte[] data; - int wrapper; - boolean result = false; - - data = DOOM.wadLoader.CacheLumpNumAsRawBytes(lumpnum + ML_NODES, 0); - wrapper = ByteBuffer.wrap(data).getInt(); - - if (wrapper == XNOD) { - LOGGER.log(Level.INFO, "P_CheckForZDoomUncompressedNodes: ZDoom uncompressed normal nodes are detected"); - result = true; - } - - DOOM.wadLoader.UnlockLumpNum(lumpnum + ML_NODES); - - return result; - } - - // - // P_GetNodesVersion - // - public void P_GetNodesVersion(int lumpnum, int gl_lumpnum) { - int ver = -1; - nodesVersion = 0; - - if ((gl_lumpnum > lumpnum) && (forceOldBsp == false) - && (DoomStatus.compatibility_level >= prboom_2_compatibility)) { - - byte[] data = DOOM.wadLoader.CacheLumpNumAsRawBytes(gl_lumpnum + ML_GL_VERTS, 0); - int wrapper = ByteBuffer.wrap(data).getInt(); - if (wrapper == gNd2) { - data = DOOM.wadLoader.CacheLumpNumAsRawBytes(gl_lumpnum + ML_GL_SEGS, 0); - wrapper = ByteBuffer.wrap(data).getInt(); - if (wrapper == gNd3) { - ver = 3; - } else { - nodesVersion = gNd2; - LOGGER.log(Level.INFO, "P_GetNodesVersion: found version 2 nodes"); - } - } - if (wrapper == gNd4) { - ver = 4; - } - if (wrapper == gNd5) { - ver = 5; - } - // e6y: unknown gl nodes will be ignored - if (nodesVersion == 0 && ver != -1) { - LOGGER.log(Level.INFO, String.format("P_GetNodesVersion: found version %d nodes", ver)); - LOGGER.log(Level.INFO, String.format("P_GetNodesVersion: version %d nodes not supported", ver)); - } - } else { - nodesVersion = 0; - LOGGER.log(Level.INFO, "P_GetNodesVersion: using normal BSP nodes"); - if (P_CheckForZDoomNodes(lumpnum, gl_lumpnum)) { - DOOM.doomSystem.Error("P_GetNodesVersion: ZDoom nodes not supported yet"); - } - } - } - - // - // P_LoadVertexes - // - // killough 5/3/98: reformatted, cleaned up - // - private void P_LoadVertexes(int lump) { - final mapvertex_t[] data; // cph - final - - // Determine number of lumps: - // total lump length / vertex record length. - numvertexes = DOOM.wadLoader.LumpLength(lump) / mapvertex_t.sizeOf(); - - // Allocate zone memory for buffer. - vertexes = calloc_IfSameLevel(vertexes, numvertexes, vertex_t::new, vertex_t[]::new); - - // Load data into cache. - // cph 2006/07/29 - cast to mapvertex_t here, making the loop below much - // neater - data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numvertexes, mapvertex_t::new, mapvertex_t[]::new); - - // Copy and convert vertex coordinates, - // internal representation as fixed. - for (int i = 0; i < numvertexes; i++) { - vertexes[i].x = data[i].x << m.fixed_t.FRACBITS; - vertexes[i].y = data[i].y << FRACBITS; - } - - // Free buffer memory. - DOOM.wadLoader.UnlockLumpNum(lump); - } - - /******************************************* - * Name : P_LoadVertexes2 * modified : 09/18/00, adapted for PrBoom * author - * : figgi * what : support for gl nodes - * - * @throws IOException - * * - *******************************************/ - // figgi -- FIXME: Automap showes wrong zoom boundaries when starting game - // when P_LoadVertexes2 is used with classic BSP nodes. - private void P_LoadVertexes2(int lump, int gllump) throws IOException { - final ByteBuffer gldata; - mapvertex_t[] ml; - - // GL vertexes come after regular ones. - firstglvertex = DOOM.wadLoader.LumpLength(lump) / mapvertex_t.sizeOf(); - numvertexes = DOOM.wadLoader.LumpLength(lump) / mapvertex_t.sizeOf(); - - if (gllump >= 0) { // check for glVertices - // Read GL lump into buffer. This allows some flexibility - gldata = DOOM.wadLoader.CacheLumpNumAsDoomBuffer(gllump).getBuffer(); - - if (nodesVersion == gNd2) { // 32 bit GL_VERT format (16.16 fixed) - // These vertexes are double in size than regular Doom vertexes. - // Furthermore, we have to skip the first 4 bytes - // (GL_VERT_OFFSET) - // of the gl lump. - numvertexes += (DOOM.wadLoader.LumpLength(gllump) - GL_VERT_OFFSET) / mapglvertex_t.sizeOf(); - - // Vertexes size accomodates both normal and GL nodes. - vertexes = malloc_IfSameLevel(vertexes, numvertexes, vertex_t::new, vertex_t[]::new); - - final mapglvertex_t mgl[] = malloc(mapglvertex_t::new, mapglvertex_t[]::new, numvertexes - firstglvertex); - - // Get lump and skip first 4 bytes - gldata.rewind(); - gldata.position(GL_VERT_OFFSET); - - CacheableDoomObjectContainer.unpack(gldata, mgl); - - int mgl_count = 0; - - for (int i = firstglvertex; i < numvertexes; i++) { - vertexes[i].x = mgl[mgl_count].x; - vertexes[i].y = mgl[mgl_count].y; - mgl_count++; - } - } else { - // Vertexes size accomodates both normal and GL nodes. - numvertexes += DOOM.wadLoader.LumpLength(gllump) / mapvertex_t.sizeOf(); - vertexes = malloc_IfSameLevel(vertexes, numvertexes, vertex_t::new, vertex_t[]::new); - - ml = malloc(mapvertex_t::new, mapvertex_t[]::new, numvertexes - firstglvertex); - - // We can read this "directly" because no skipping is involved. - gldata.rewind(); - CacheableDoomObjectContainer.unpack(gldata, ml); - // ml = W.CacheLumpNumIntoArray(gllump, - // numvertexes-firstglvertex,mapvertex_t.class); - int ml_count = 0; - - for (int i = firstglvertex; i < numvertexes; i++) { - vertexes[i].x = ml[ml_count].x; - vertexes[i].y = ml[ml_count].y; - ml_count++; - } - } - DOOM.wadLoader.UnlockLumpNum(gllump); - } - - // Loading of regular lumps (sheesh!) - ml = DOOM.wadLoader.CacheLumpNumIntoArray(lump, firstglvertex, mapvertex_t::new, mapvertex_t[]::new); - - for (int i = 0; i < firstglvertex; i++) { - vertexes[i].x = ml[i].x; - vertexes[i].y = ml[i].y; - } - - DOOM.wadLoader.UnlockLumpNum(lump); - - } - - /******************************************* - * created : 08/13/00 * modified : 09/18/00, adapted for PrBoom * author : - * figgi * what : basic functions needed for * computing gl nodes * - *******************************************/ - public int checkGLVertex(int num) { - if ((num & 0x8000) != 0) { - num = (num & 0x7FFF) + firstglvertex; - } - return num; - } - - public static float GetDistance(int dx, int dy) { - float fx = (float) (dx) / FRACUNIT, fy = (float) (dy) / FRACUNIT; - return (float) Math.sqrt(fx * fx + fy * fy); - } - - public static float GetTexelDistance(int dx, int dy) { - // return (float)((int)(GetDistance(dx, dy) + 0.5f)); - float fx = (float) (dx) / FRACUNIT, fy = (float) (dy) / FRACUNIT; - return ((int) (0.5f + (float) Math.sqrt(fx * fx + fy * fy))); - } - - public static int GetOffset(vertex_t v1, vertex_t v2) { - float a, b; - int r; - a = (v1.x - v2.x) / (float) FRACUNIT; - b = (v1.y - v2.y) / (float) FRACUNIT; - r = (int) (Math.sqrt(a * a + b * b) * FRACUNIT); - return r; - } - - // - // P_LoadSegs - // - // killough 5/3/98: reformatted, cleaned up - private void P_LoadSegs(int lump) { - final mapseg_t[] data; // cph - final - - numsegs = DOOM.wadLoader.LumpLength(lump) / mapseg_t.sizeOf(); - segs = calloc_IfSameLevel(segs, numsegs, seg_t::new, seg_t[]::new); - - data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numsegs, mapseg_t::new, mapseg_t[]::new); // cph - - // wad - // lump - // handling - // updated - - if ((data == null) || (numsegs == 0)) { - DOOM.doomSystem.Error("P_LoadSegs: no segs in level"); - } - - for (int i = 0; i < numsegs; i++) { - seg_t li = segs[i]; - final mapseg_t ml = data[i]; - char v1, v2; - - int side, linedef; - line_t ldef; - - li.iSegID = i; // proff 11/05/2000: needed for OpenGL - - v1 = ml.v1; - v2 = ml.v2; - - // e6y - // moved down for additional checks to avoid overflow - // if wrong vertexe's indexes are in SEGS lump - // see below for more detailed information - // li.v1 = &vertexes[v1]; - // li.v2 = &vertexes[v2]; - li.miniseg = false; // figgi -- there are no minisegs in classic BSP - // nodes - - // e6y: moved down, see below - // li.length = GetDistance(li.v2.x - li.v1.x, li.v2.y - li.v1.y); - li.angle = ml.angle << 16; - li.offset = ml.offset << 16; - linedef = ml.linedef; - - // e6y: check for wrong indexes - if (linedef >= numlines) { - DOOM.doomSystem.Error("P_LoadSegs: seg %d references a non-existent linedef %d", i, linedef); - } - - ldef = lines[linedef]; - li.linedef = ldef; - side = ml.side; - - // e6y: fix wrong side index - if (side != 0 && side != 1) { - LOGGER.log(Level.WARNING, String.format("P_LoadSegs: seg %d contains wrong side index %d. Replaced with 1.", i, side)); - side = 1; - } - - // e6y: check for wrong indexes - if (ldef.sidenum[side] >= (char) numsides) { - DOOM.doomSystem.Error( - "P_LoadSegs: linedef %d for seg %d references a non-existent sidedef %d", - linedef, i, ldef.sidenum[side] - ); - } - - li.sidedef = sides[ldef.sidenum[side]]; - - /* - * cph 2006/09/30 - our frontsector can be the second side of the - * linedef, so must check for NO_INDEX in case we are incorrectly - * referencing the back of a 1S line - */ - if (ldef.sidenum[side] != NO_INDEX) { - li.frontsector = sides[ldef.sidenum[side]].sector; - } else { - li.frontsector = null; - LOGGER.log(Level.INFO, String.format("P_LoadSegs: front of seg %d has no sidedef", i)); - } - - if (flags(ldef.flags, ML_TWOSIDED) && ldef.sidenum[side ^ 1] != NO_INDEX) { - li.backsector = sides[ldef.sidenum[side ^ 1]].sector; - } else { - li.backsector = null; - } - - // e6y - // check and fix wrong references to non-existent vertexes - // see e1m9 @ NIVELES.WAD - // http://www.doomworld.com/idgames/index.php?id=12647 - if (v1 >= numvertexes || v2 >= numvertexes) { - String str = "P_LoadSegs: compatibility loss - seg %d references a non-existent vertex %d\n"; - - if (DOOM.demorecording) { - DOOM.doomSystem.Error( - str + "Demo recording on levels with invalid nodes is not allowed", - i, (v1 >= numvertexes ? v1 : v2) - ); - } - - if (v1 >= numvertexes) { - LOGGER.log(Level.WARNING, String.format(str, i, v1)); - } - if (v2 >= numvertexes) { - LOGGER.log(Level.WARNING, String.format(str, i, v2)); - } - - if (li.sidedef == sides[li.linedef.sidenum[0]]) { - li.v1 = lines[ml.linedef].v1; - li.v2 = lines[ml.linedef].v2; - } else { - li.v1 = lines[ml.linedef].v2; - li.v2 = lines[ml.linedef].v1; - } - } else { - li.v1 = vertexes[v1]; - li.v2 = vertexes[v2]; - } - - li.assignVertexValues(); - - // e6y: now we can calculate it - li.length = GetDistance(li.v2x - li.v1x, li.v2y - li.v1y); - - // Recalculate seg offsets that are sometimes incorrect - // with certain nodebuilders. Fixes among others, line 20365 - // of DV.wad, map 5 - li.offset = GetOffset(li.v1, (ml.side != 0 ? ldef.v2 : ldef.v1)); - } - - DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the data - } - - private void P_LoadSegs_V4(int lump) { - int i; - mapseg_v4_t[] data; - - numsegs = DOOM.wadLoader.LumpLength(lump) / mapseg_v4_t.sizeOf(); - segs = calloc_IfSameLevel(segs, numsegs, seg_t::new, seg_t[]::new); - data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numsegs, mapseg_v4_t::new, mapseg_v4_t[]::new); - - if ((data == null) || (numsegs == 0)) { - DOOM.doomSystem.Error("P_LoadSegs_V4: no segs in level"); - } - - for (i = 0; i < numsegs; i++) { - seg_t li = segs[i]; - final mapseg_v4_t ml = data[i]; - int v1, v2; - - int side, linedef; - line_t ldef; - - li.iSegID = i; // proff 11/05/2000: needed for OpenGL - - v1 = ml.v1; - v2 = ml.v2; - - li.miniseg = false; // figgi -- there are no minisegs in classic BSP - // nodes - - li.angle = ml.angle << 16; - li.offset = ml.offset << 16; - linedef = ml.linedef; - - // e6y: check for wrong indexes - if (unsigned(linedef) >= unsigned(numlines)) { - DOOM.doomSystem.Error( - "P_LoadSegs_V4: seg %d references a non-existent linedef %d", - i, unsigned(linedef)); - } - - ldef = lines[linedef]; - li.linedef = ldef; - side = ml.side; - - // e6y: fix wrong side index - if (side != 0 && side != 1) { - LOGGER.log(Level.WARNING, String.format("P_LoadSegs_V4: seg %d contains wrong side index %d. Replaced with 1.", i, side)); - side = 1; - } - - // e6y: check for wrong indexes - if (unsigned(ldef.sidenum[side]) >= unsigned(numsides)) { - DOOM.doomSystem.Error( - "P_LoadSegs_V4: linedef %d for seg %d references a non-existent sidedef %d", - linedef, i, unsigned(ldef.sidenum[side]) - ); - } - - li.sidedef = sides[ldef.sidenum[side]]; - - /* - * cph 2006/09/30 - our frontsector can be the second side of the - * linedef, so must check for NO_INDEX in case we are incorrectly - * referencing the back of a 1S line - */ - if (ldef.sidenum[side] != NO_INDEX) { - li.frontsector = sides[ldef.sidenum[side]].sector; - } else { - li.frontsector = null; - LOGGER.log(Level.WARNING, String.format("P_LoadSegs_V4: front of seg %d has no sidedef", i)); - } - - if (flags(ldef.flags, ML_TWOSIDED) - && ldef.sidenum[side ^ 1] != NO_INDEX) { - li.backsector = sides[ldef.sidenum[side ^ 1]].sector; - } else { - li.backsector = null; - } - - // e6y - // check and fix wrong references to non-existent vertexes - // see e1m9 @ NIVELES.WAD - // http://www.doomworld.com/idgames/index.php?id=12647 - if (v1 >= numvertexes || v2 >= numvertexes) { - String str = "P_LoadSegs_V4: compatibility loss - seg %d references a non-existent vertex %d\n"; - - if (DOOM.demorecording) { - DOOM.doomSystem.Error( - (str + "Demo recording on levels with invalid nodes is not allowed"), - i, (v1 >= numvertexes ? v1 : v2) - ); - } - - if (v1 >= numvertexes) { - LOGGER.log(Level.WARNING, String.format(str, i, v1)); - } - if (v2 >= numvertexes) { - LOGGER.log(Level.WARNING, String.format(str, i, v2)); - } - - if (li.sidedef == sides[li.linedef.sidenum[0]]) { - li.v1 = lines[ml.linedef].v1; - li.v2 = lines[ml.linedef].v2; - } else { - li.v1 = lines[ml.linedef].v2; - li.v2 = lines[ml.linedef].v1; - } - } else { - li.v1 = vertexes[v1]; - li.v2 = vertexes[v2]; - } - - // e6y: now we can calculate it - li.length = GetDistance(li.v2.x - li.v1.x, li.v2.y - li.v1.y); - - // Recalculate seg offsets that are sometimes incorrect - // with certain nodebuilders. Fixes among others, line 20365 - // of DV.wad, map 5 - li.offset = GetOffset(li.v1, (ml.side != 0 ? ldef.v2 : ldef.v1)); - } - - DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the data - } - - /******************************************* - * Name : P_LoadGLSegs * created : 08/13/00 * modified : 09/18/00, adapted - * for PrBoom * author : figgi * what : support for gl nodes * - *******************************************/ - /* - * private void P_LoadGLSegs(int lump) { int i; final glseg_t ml; line_t - * ldef; numsegs = W.LumpLength(lump) / sizeof(glseg_t); segs = - * malloc_IfSameLevel(segs, numsegs * sizeof(seg_t)); memset(segs, 0, - * numsegs * sizeof(seg_t)); ml = (final glseg_t*)W.CacheLumpNum(lump); if - * ((!ml) || (!numsegs)) I_Error("P_LoadGLSegs: no glsegs in level"); for(i - * = 0; i < numsegs; i++) { // check for gl-vertices segs[i].v1 = - * &vertexes[checkGLVertex(LittleShort(ml.v1))]; segs[i].v2 = - * &vertexes[checkGLVertex(LittleShort(ml.v2))]; segs[i].iSegID = i; - * if(ml.linedef != (unsigned short)-1) // skip minisegs { ldef = - * &lines[ml.linedef]; segs[i].linedef = ldef; segs[i].miniseg = false; - * segs[i].angle = - * R_PointToAngle2(segs[i].v1.x,segs[i].v1.y,segs[i].v2.x,segs[i].v2.y); - * segs[i].sidedef = &sides[ldef.sidenum[ml.side]]; segs[i].length = - * GetDistance(segs[i].v2.x - segs[i].v1.x, segs[i].v2.y - segs[i].v1.y); - * segs[i].frontsector = sides[ldef.sidenum[ml.side]].sector; if (ldef.flags - * & ML_TWOSIDED) segs[i].backsector = - * sides[ldef.sidenum[ml.side^1]].sector; else segs[i].backsector = 0; if - * (ml.side) segs[i].offset = GetOffset(segs[i].v1, ldef.v2); else - * segs[i].offset = GetOffset(segs[i].v1, ldef.v1); } else { segs[i].miniseg - * = true; segs[i].angle = 0; segs[i].offset = 0; segs[i].length = 0; - * segs[i].linedef = NULL; segs[i].sidedef = NULL; segs[i].frontsector = - * NULL; segs[i].backsector = NULL; } ml++; } W.UnlockLumpNum(lump); } - */ - // - // P_LoadSubsectors - // - // killough 5/3/98: reformatted, cleaned up - private void P_LoadSubsectors(int lump) { - /* - * cph 2006/07/29 - make data a final mapsubsector_t *, so the loop - * below is simpler & gives no finalness warnings - */ - final mapsubsector_t[] data; - - numsubsectors = DOOM.wadLoader.LumpLength(lump) / mapsubsector_t.sizeOf(); - subsectors = calloc_IfSameLevel(subsectors, numsubsectors, subsector_t::new, subsector_t[]::new); - data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numsubsectors, mapsubsector_t::new, mapsubsector_t[]::new); - - if ((data == null) || (numsubsectors == 0)) { - DOOM.doomSystem.Error("P_LoadSubsectors: no subsectors in level"); - } - - for (int i = 0; i < numsubsectors; i++) { - // e6y: support for extended nodes - subsectors[i].numlines = data[i].numsegs; - subsectors[i].firstline = data[i].firstseg; - } - - DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the data - } - - private void P_LoadSubsectors_V4(int lump) { - /* - * cph 2006/07/29 - make data a final mapsubsector_t *, so the loop - * below is simpler & gives no finalness warnings - */ - final mapsubsector_v4_t[] data; - - numsubsectors = DOOM.wadLoader.LumpLength(lump) / mapsubsector_v4_t.sizeOf(); - subsectors = calloc_IfSameLevel(subsectors, numsubsectors, subsector_t::new, subsector_t[]::new); - data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numsubsectors, mapsubsector_v4_t::new, mapsubsector_v4_t[]::new); - - if ((data == null) || (numsubsectors == 0)) { - DOOM.doomSystem.Error("P_LoadSubsectors_V4: no subsectors in level"); - } - - for (int i = 0; i < numsubsectors; i++) { - subsectors[i].numlines = data[i].numsegs; - subsectors[i].firstline = data[i].firstseg; - } - - DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the data - } - - // - // P_LoadSectors - // - // killough 5/3/98: reformatted, cleaned up - private void P_LoadSectors(int lump) { - final mapsector_t[] data; // cph - final* - - numsectors = DOOM.wadLoader.LumpLength(lump) / mapsector_t.sizeOf(); - sectors = calloc_IfSameLevel(sectors, numsectors, sector_t::new, sector_t[]::new); - data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numsectors, mapsector_t::new, mapsector_t[]::new); // cph - // - - // wad - // lump - // handling - // updated - - for (int i = 0; i < numsectors; i++) { - sector_t ss = sectors[i]; - final mapsector_t ms = data[i]; - - ss.id = i; // proff 04/05/2000: needed for OpenGL - ss.floorheight = ms.floorheight << FRACBITS; - ss.ceilingheight = ms.ceilingheight << FRACBITS; - ss.floorpic = (short) DOOM.textureManager.FlatNumForName(ms.floorpic); - ss.ceilingpic = (short) DOOM.textureManager.FlatNumForName(ms.ceilingpic); - ss.lightlevel = ms.lightlevel; - ss.special = ms.special; - // ss.oldspecial = ms.special; huh? - ss.tag = ms.tag; - ss.thinglist = null; - // MAES: link to thinker list and RNG - ss.TL = this.DOOM.actions; - ss.RND = this.DOOM.random; - - // ss.touching_thinglist = null; // phares 3/14/98 - // ss.nextsec = -1; //jff 2/26/98 add fields to support locking out - // ss.prevsec = -1; // stair retriggering until build completes - // killough 3/7/98: - // ss.floor_xoffs = 0; - // ss.floor_yoffs = 0; // floor and ceiling flats offsets - // ss.ceiling_xoffs = 0; - // ss.ceiling_yoffs = 0; - // ss.heightsec = -1; // sector used to get floor and ceiling height - // ss.floorlightsec = -1; // sector used to get floor lighting - // killough 3/7/98: end changes - // killough 4/11/98 sector used to get ceiling lighting: - // ss.ceilinglightsec = -1; - // killough 4/4/98: colormaps: - // ss.bottommap = ss.midmap = ss.topmap = 0; - // killough 10/98: sky textures coming from sidedefs: - // ss.sky = 0; - } - - DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the data - } - - // - // P_LoadNodes - // - // killough 5/3/98: reformatted, cleaned up - private void P_LoadNodes(int lump) { - final mapnode_t[] data; // cph - final* - - numnodes = DOOM.wadLoader.LumpLength(lump) / mapnode_t.sizeOf(); - nodes = malloc_IfSameLevel(nodes, numnodes, node_t::new, node_t[]::new); - data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numnodes, mapnode_t::new, mapnode_t[]::new); // cph - // - - // wad - // lump - // handling - // updated - - if ((data == null) || (numnodes == 0)) { - // allow trivial maps - if (numsubsectors == 1) { - LOGGER.log(Level.INFO, "P_LoadNodes: trivial map (no nodes, one subsector)"); - } else { - DOOM.doomSystem.Error("P_LoadNodes: no nodes in level"); - } - } - - for (int i = 0; i < numnodes; i++) { - node_t no = nodes[i]; - final mapnode_t mn = data[i]; - - no.x = mn.x << FRACBITS; - no.y = mn.y << FRACBITS; - no.dx = mn.dx << FRACBITS; - no.dy = mn.dy << FRACBITS; - - for (int j = 0; j < 2; j++) { - // e6y: support for extended nodes - no.children[j] = mn.children[j]; - - // e6y: support for extended nodes - if (no.children[j] == 0xFFFF) { - no.children[j] = 0xFFFFFFFF; - } else if (flags(no.children[j], NF_SUBSECTOR_CLASSIC)) { - // Convert to extended type - no.children[j] &= ~NF_SUBSECTOR_CLASSIC; - - // haleyjd 11/06/10: check for invalid subsector reference - if (no.children[j] >= numsubsectors) { - LOGGER.log(Level.WARNING, String.format("P_LoadNodes: BSP tree references invalid subsector %d.", no.children[j])); - no.children[j] = 0; - } - - no.children[j] |= NF_SUBSECTOR; - } - - for (int k = 0; k < 4; k++) { - no.bbox[j].set(k, mn.bbox[j][k] << FRACBITS); - } - } - } - - DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the data - } - - private void P_LoadNodes_V4(int lump) { - final DeepBSPNodesV4 data; // cph - final* - - numnodes = (DOOM.wadLoader.LumpLength(lump) - 8) / mapnode_v4_t.sizeOf(); - nodes = malloc_IfSameLevel(nodes, numnodes, node_t::new, node_t[]::new); - data = DOOM.wadLoader.CacheLumpNum(lump, 0, DeepBSPNodesV4.class); // cph - // - - // wad - // lump - // handling - // updated - - if ((data == null) || (numnodes == 0)) { - // allow trivial maps - if (numsubsectors == 1) { - LOGGER.log(Level.INFO, "P_LoadNodes_V4: trivial map (no nodes, one subsector)\n"); - } else { - DOOM.doomSystem.Error("P_LoadNodes_V4: no nodes in level"); - } - } - - for (int i = 0; i < numnodes; i++) { - node_t no = nodes[i]; - final mapnode_v4_t mn = data.getNodes()[i]; - - no.x = mn.x << FRACBITS; - no.y = mn.y << FRACBITS; - no.dx = mn.dx << FRACBITS; - no.dy = mn.dy << FRACBITS; - - for (int j = 0; j < 2; j++) { - no.children[j] = mn.children[j]; - - for (int k = 0; k < 4; k++) { - no.bbox[j].bbox[k] = mn.bbox[j][k] << FRACBITS; - } - } - } - - DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the data - } - - private void P_LoadZSegs(ByteBuffer data) throws IOException { - final mapseg_znod_t nodes[] = malloc(mapseg_znod_t::new, mapseg_znod_t[]::new, numsegs); - CacheableDoomObjectContainer.unpack(data, nodes); - - for (int i = 0; i < numsegs; i++) { - line_t ldef; - int v1, v2; - int linedef; - char side; - seg_t li = segs[i]; - final mapseg_znod_t ml = nodes[i]; - - v1 = ml.v1; - v2 = ml.v2; - - li.iSegID = i; // proff 11/05/2000: needed for OpenGL - li.miniseg = false; - - linedef = ml.linedef; - - // e6y: check for wrong indexes - if (unsigned(linedef) >= unsigned(numlines)) { - DOOM.doomSystem.Error( - "P_LoadZSegs: seg %d references a non-existent linedef %d", - i, unsigned(linedef) - ); - } - - ldef = lines[linedef]; - li.linedef = ldef; - side = (char) ml.side; - - // e6y: fix wrong side index - if (side != 0 && side != 1) { - LOGGER.log(Level.WARNING, String.format("P_LoadZSegs: seg %d contains wrong side index %d. Replaced with 1.", i, (int) side)); - side = 1; - } - - // e6y: check for wrong indexes - if (unsigned(ldef.sidenum[side]) >= unsigned(numsides)) { - DOOM.doomSystem.Error( - "P_LoadZSegs: linedef %d for seg %d references a non-existent sidedef %d", - linedef, i, unsigned(ldef.sidenum[side]) - ); - } - - li.sidedef = sides[ldef.sidenum[side]]; - - /* - * cph 2006/09/30 - our frontsector can be the second side of the - * linedef, so must check for NO_INDEX in case we are incorrectly - * referencing the back of a 1S line - */ - if (ldef.sidenum[side] != NO_INDEX) { - li.frontsector = sides[ldef.sidenum[side]].sector; - } else { - li.frontsector = null; - LOGGER.log(Level.WARNING, String.format("P_LoadZSegs: front of seg %d has no sidedef", i)); - } - - if (flags(ldef.flags, ML_TWOSIDED) && (ldef.sidenum[side ^ 1] != NO_INDEX)) { - li.backsector = sides[ldef.sidenum[side ^ 1]].sector; - } else { - li.backsector = null; - } - - li.v1 = vertexes[v1]; - li.v2 = vertexes[v2]; - - li.length = GetDistance(li.v2.x - li.v1.x, li.v2.y - li.v1.y); - li.offset = GetOffset(li.v1, (side != 0 ? ldef.v2 : ldef.v1)); - li.angle = RendererState.PointToAngle(segs[i].v1.x, segs[i].v1.y, segs[i].v2.x, segs[i].v2.y); - // li.angle = (int)((float)atan2(li.v2.y - li.v1.y,li.v2.x - - // li.v1.x) * (ANG180 / M_PI)); - } - } - - private int CheckZNodesOverflow(int size, int count) { - size -= count; - - if (size < 0) { - DOOM.doomSystem.Error("P_LoadZNodes: incorrect nodes"); - } - - return size; - } - - private void P_LoadZNodes(int lump, int glnodes) throws IOException { - ByteBuffer data; - int len; - int header; // for debugging - - int orgVerts, newVerts; - int numSubs, currSeg; - int numSegs; - int numNodes; - vertex_t[] newvertarray = null; - - data = DOOM.wadLoader.CacheLumpNumAsDoomBuffer(lump).getBuffer(); - data.order(ByteOrder.LITTLE_ENDIAN); - len = DOOM.wadLoader.LumpLength(lump); - - // skip header - len = CheckZNodesOverflow(len, 4); - header = data.getInt(); - - // Read extra vertices added during node building - len = CheckZNodesOverflow(len, 4); - orgVerts = data.getInt(); - - len = CheckZNodesOverflow(len, 4); - newVerts = data.getInt(); - - if (!samelevel) { - if (orgVerts + newVerts == numvertexes) { - newvertarray = vertexes; - } else { - newvertarray = new vertex_t[orgVerts + newVerts]; - // TODO: avoid creating new objects that will be rewritten instantly - Good Sign 2017/05/07 - Arrays.setAll(newvertarray, ii -> new vertex_t()); - System.arraycopy(vertexes, 0, newvertarray, 0, orgVerts); - } - - //(sizeof(newvertarray[0].x) + sizeof(newvertarray[0].y)) - len = CheckZNodesOverflow(len, newVerts * vertex_t.sizeOf()); - z_vertex_t tmp = new z_vertex_t(); - - for (int i = 0; i < newVerts; i++) { - tmp.unpack(data); - newvertarray[i + orgVerts].x = tmp.x; - newvertarray[i + orgVerts].y = tmp.y; - } - - // Extra vertexes read in - if (vertexes != newvertarray) { - for (int i = 0; i < numlines; i++) { - //lines[i].v1 = lines[i].v1 - vertexes + newvertarray; - //lines[i].v2 = lines[i].v2 - vertexes + newvertarray; - // Find indexes of v1 & v2 inside old vertexes array - // (.v1-vertexes) and use that index to re-point inside newvertarray - lines[i].v1 = newvertarray[C2JUtils.indexOf(vertexes, lines[i].v1)]; - lines[i].v2 = newvertarray[C2JUtils.indexOf(vertexes, lines[i].v2)]; - } - // free(vertexes); - vertexes = newvertarray; - numvertexes = orgVerts + newVerts; - } - } else { - // Skip the reading of all these new vertices and the expensive indexOf searches. - int size = newVerts * z_vertex_t.sizeOf(); - len = CheckZNodesOverflow(len, size); - data.position(data.position() + size); - } - - // Read the subsectors - len = CheckZNodesOverflow(len, 4); - numSubs = data.getInt(); - - numsubsectors = numSubs; - if (numsubsectors <= 0) { - DOOM.doomSystem.Error("P_LoadZNodes: no subsectors in level"); - } - subsectors = calloc_IfSameLevel(subsectors, numsubsectors, subsector_t::new, subsector_t[]::new); - - len = CheckZNodesOverflow(len, numSubs * mapsubsector_znod_t.sizeOf()); - final mapsubsector_znod_t mseg = new mapsubsector_znod_t(); - for (int i = currSeg = 0; i < numSubs; i++) { - mseg.unpack(data); - - subsectors[i].firstline = currSeg; - subsectors[i].numlines = (int) mseg.numsegs; - currSeg += mseg.numsegs; - } - - // Read the segs - len = CheckZNodesOverflow(len, 4); - numSegs = data.getInt(); - - // The number of segs stored should match the number of - // segs used by subsectors. - if (numSegs != currSeg) { - DOOM.doomSystem.Error("P_LoadZNodes: Incorrect number of segs in nodes."); - } - - numsegs = numSegs; - segs = calloc_IfSameLevel(segs, numsegs, seg_t::new, seg_t[]::new); - - if (glnodes == 0) { - len = CheckZNodesOverflow(len, numsegs * mapseg_znod_t.sizeOf()); - P_LoadZSegs(data); - } else { - //P_LoadGLZSegs (data, glnodes); - DOOM.doomSystem.Error("P_LoadZNodes: GL segs are not supported."); - } - - // Read nodes - len = CheckZNodesOverflow(len, 4); - numNodes = data.getInt(); - - numnodes = numNodes; - nodes = calloc_IfSameLevel(nodes, numNodes, node_t::new, node_t[]::new); - - len = CheckZNodesOverflow(len, numNodes * mapnode_znod_t.sizeOf()); - - mapnode_znod_t znodes[] = malloc(mapnode_znod_t::new, mapnode_znod_t[]::new, numNodes); - CacheableDoomObjectContainer.unpack(data, znodes); - - for (int i = 0; i < numNodes; i++) { - int j, k; - node_t no = nodes[i]; - final mapnode_znod_t mn = znodes[i]; - - no.x = mn.x << FRACBITS; - no.y = mn.y << FRACBITS; - no.dx = mn.dx << FRACBITS; - no.dy = mn.dy << FRACBITS; - - for (j = 0; j < 2; j++) { - no.children[j] = mn.children[j]; - - for (k = 0; k < 4; k++) { - no.bbox[j].bbox[k] = mn.bbox[j][k] << FRACBITS; - } - } - } - - DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the data - } - - private boolean no_overlapped_sprites; - - private int GETXY(mobj_t mobj) { - return (mobj.x + (mobj.y >> 16)); - } - - private int dicmp_sprite_by_pos(final Object a, final Object b) { - mobj_t m1 = (mobj_t) a; - mobj_t m2 = (mobj_t) b; - - int res = GETXY(m2) - GETXY(m1); - no_overlapped_sprites = no_overlapped_sprites && (res != 0); - return res; - } - - /* - * P_LoadThings killough 5/3/98: reformatted, cleaned up cph 2001/07/07 - - * don't write into the lump cache, especially non-idepotent changes like - * byte order reversals. Take a copy to edit. - */ - @SourceCode.Suspicious(CauseOfDesyncProbability.LOW) - @P_Setup.C(P_LoadThings) - private void P_LoadThings(int lump) { - int numthings = DOOM.wadLoader.LumpLength(lump) / mapthing_t.sizeOf(); - final mapthing_t[] data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numthings, mapthing_t::new, mapthing_t[]::new); - - mobj_t mobj; - int mobjcount = 0; - mobj_t[] mobjlist = new mobj_t[numthings]; - Arrays.setAll(mobjlist, j -> mobj_t.createOn(DOOM)); - - if ((data == null) || (numthings == 0)) { - DOOM.doomSystem.Error("P_LoadThings: no things in level"); - } - - for (int i = 0; i < numthings; i++) { - mapthing_t mt = data[i]; - - /* - * Not needed. Handled during unmarshaling. mt.x = - * LittleShort(mt.x); mt.y = LittleShort(mt.y); mt.angle = - * LittleShort(mt.angle); mt.type = LittleShort(mt.type); mt.options - * = LittleShort(mt.options); - */ - if (!P_IsDoomnumAllowed(mt.type)) { - continue; - } - - // Do spawn all other stuff. - mobj = DOOM.actions.SpawnMapThing(mt/* , i */); - if (mobj != null && mobj.info.speed == 0) { - mobjlist[mobjcount++] = mobj; - } - } - - DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the data - /* - * #ifdef GL_DOOM if (V_GetMode() == VID_MODEGL) { no_overlapped_sprites - * = true; qsort(mobjlist, mobjcount, sizeof(mobjlist[0]), - * dicmp_sprite_by_pos); if (!no_overlapped_sprites) { i = 1; while (i < - * mobjcount) { mobj_t *m1 = mobjlist[i - 1]; mobj_t *m2 = mobjlist[i - - * 0]; if (GETXY(m1) == GETXY(m2)) { mobj_t *mo = (m1.index < m2.index ? - * m1 : m2); i++; while (i < mobjcount && GETXY(mobjlist[i]) == - * GETXY(m1)) { if (mobjlist[i].index < mo.index) { mo = mobjlist[i]; } - * i++; } // 'nearest' mo.flags |= MF_FOREGROUND; } i++; } } } #endif - */ - - } - - /* - * P_IsDoomnumAllowed() Based on code taken from P_LoadThings() in - * src/p_setup.c Return TRUE if the thing in question is expected to be - * available in the gamemode used. - */ - boolean P_IsDoomnumAllowed(int doomnum) { - // Do not spawn cool, new monsters if !commercial - if (!DOOM.isCommercial()) { - switch (doomnum) { - case 64: // Archvile - case 65: // Former Human Commando - case 66: // Revenant - case 67: // Mancubus - case 68: // Arachnotron - case 69: // Hell Knight - case 71: // Pain Elemental - case 84: // Wolf SS - case 88: // Boss Brain - case 89: // Boss Shooter - return false; - } - } - - return true; - } - - // - // P_LoadLineDefs - // Also counts secret lines for intermissions. - // ^^^ - // ??? killough ??? - // Does this mean secrets used to be linedef-based, rather than - // sector-based? - // - // killough 4/4/98: split into two functions, to allow sidedef overloading - // - // killough 5/3/98: reformatted, cleaned up - private void P_LoadLineDefs(int lump) { - final maplinedef_t[] data; // cph - final* - - numlines = DOOM.wadLoader.LumpLength(lump) / maplinedef_t.sizeOf(); - lines = calloc_IfSameLevel(lines, numlines, line_t::new, line_t[]::new); - data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numlines, maplinedef_t::new, maplinedef_t[]::new); // cph - // - - // wad - // lump - // handling - // updated - - for (int i = 0; i < numlines; i++) { - final maplinedef_t mld = data[i]; - line_t ld = lines[i]; - ld.id = i; - vertex_t v1, v2; - - ld.flags = mld.flags; - ld.special = mld.special; - ld.tag = mld.tag; - v1 = ld.v1 = vertexes[mld.v1]; - v2 = ld.v2 = vertexes[mld.v2]; - ld.dx = v2.x - v1.x; - ld.dy = v2.y - v1.y; - // Maes: map value semantics. - ld.assignVertexValues(); - - /* - * #ifdef GL_DOOM // e6y // Rounding the wall length to the nearest - * integer // when determining length instead of always rounding - * down // There is no more glitches on seams between identical - * textures. ld.texel_length = GetTexelDistance(ld.dx, ld.dy); - * #endif - */ - ld.tranlump = -1; // killough 4/11/98: no translucency by default - - ld.slopetype = (ld.dx == 0) - ? slopetype_t.ST_VERTICAL - : (ld.dy == 0) - ? slopetype_t.ST_HORIZONTAL - : fixed_t.FixedDiv(ld.dy, ld.dx) > 0 - ? slopetype_t.ST_POSITIVE - : slopetype_t.ST_NEGATIVE; - - if (v1.x < v2.x) { - ld.bbox[BBox.BOXLEFT] = v1.x; - ld.bbox[BBox.BOXRIGHT] = v2.x; - } else { - ld.bbox[BBox.BOXLEFT] = v2.x; - ld.bbox[BBox.BOXRIGHT] = v1.x; - } - if (v1.y < v2.y) { - ld.bbox[BBox.BOXBOTTOM] = v1.y; - ld.bbox[BBox.BOXTOP] = v2.y; - } else { - ld.bbox[BBox.BOXBOTTOM] = v2.y; - ld.bbox[BBox.BOXTOP] = v1.y; - } - - /* calculate sound origin of line to be its midpoint */ - // e6y: fix sound origin for large levels - // no need for comp_sound test, these are only used when comp_sound - // = 0 - ld.soundorg = new degenmobj_t( - ld.bbox[BBox.BOXLEFT] / 2 - + ld.bbox[BBox.BOXRIGHT] / 2, ld.bbox[BBox.BOXTOP] / 2 - + ld.bbox[BBox.BOXBOTTOM] / 2, 0 - ); - - // TODO - // ld.iLineID=i; // proff 04/05/2000: needed for OpenGL - ld.sidenum[0] = mld.sidenum[0]; - ld.sidenum[1] = mld.sidenum[1]; - - { - /* - * cph 2006/09/30 - fix sidedef errors right away. cph - * 2002/07/20 - these errors are fatal if not fixed, so apply - * them in compatibility mode - a desync is better than a crash! - */ - for (int j = 0; j < 2; j++) { - if (ld.sidenum[j] != NO_INDEX && ld.sidenum[j] >= numsides) { - ld.sidenum[j] = NO_INDEX; - LOGGER.log(Level.WARNING, String.format( - "P_LoadLineDefs: linedef %d has out-of-range sidedef number", - numlines - i - 1) - ); - } - } - - // killough 11/98: fix common wad errors (missing sidedefs): - if (ld.sidenum[0] == NO_INDEX) { - ld.sidenum[0] = 0; // Substitute dummy sidedef for missing - // right side - // cph - print a warning about the bug - LOGGER.log(Level.WARNING, String.format("P_LoadLineDefs: linedef %d missing first sidedef\n", numlines - i - 1)); - } - - if ((ld.sidenum[1] == NO_INDEX) && flags(ld.flags, ML_TWOSIDED)) { - // e6y - // ML_TWOSIDED flag shouldn't be cleared for compatibility - // purposes - // see CLNJ-506.LMP at http://doomedsda.us/wad1005.html - // TODO: we don't really care, but still... - // if (!demo_compatibility || - // !overflows[OVERFLOW.MISSEDBACKSIDE].emulate) - // { - ld.flags &= ~ML_TWOSIDED; // Clear 2s flag for missing left - // side - // } - // Mark such lines and do not draw them only in - // demo_compatibility, - // because Boom's behaviour is different - // See OTTAWAU.WAD E1M1, sectors 226 and 300 - // http://www.doomworld.com/idgames/index.php?id=1651 - // TODO ehhh? - // ld.r_flags = RF_IGNORE_COMPAT; - // cph - print a warning about the bug - LOGGER.log(Level.WARNING, String.format( - "P_LoadLineDefs: linedef %d has two-sided flag set, but no second sidedef\n", - numlines - i - 1) - ); - } - } - - // killough 4/4/98: support special sidedef interpretation below - // TODO: - // if (ld.sidenum[0] != NO_INDEX && ld.special!=0) - // sides[(ld.sidenum[0]<<16)& (0x0000FFFF&ld.sidenum[1])].special = - // ld.special; - } - - DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the lump - } - - // killough 4/4/98: delay using sidedefs until they are loaded - // killough 5/3/98: reformatted, cleaned up - private void P_LoadLineDefs2(int lump) { - line_t ld; - - for (int i = 0; i < numlines; i++) { - ld = lines[i]; - ld.frontsector = sides[ld.sidenum[0]].sector; // e6y: Can't be - // NO_INDEX here - ld.backsector - = ld.sidenum[1] != NO_INDEX ? sides[ld.sidenum[1]].sector : null; - switch (ld.special) { // killough 4/11/98: handle special types - case 260: // killough 4/11/98: translucent 2s textures - // TODO: transparentpresent = true;//e6y - // int lmp = sides[ld.getSpecialSidenum()].special; // - // translucency from sidedef - // if (!ld.tag) // if tag==0, - // ld.tranlump = lmp; // affect this linedef only - // else - // for (int j=0;j= numsectors) { - LOGGER.log(Level.WARNING, String.format("P_LoadSideDefs2: sidedef %d has out-of-range sector num %d", i, (int) sector_num)); - sector_num = 0; - } - sd.sector = sec = sectors[sector_num]; - } - - // killough 4/4/98: allow sidedef texture names to be overloaded - // killough 4/11/98: refined to allow colormaps to work as wall - // textures if invalid as colormaps but valid as textures. - switch (sd.special) { - case 242: // variable colormap via 242 linedef - /* - * sd.bottomtexture = (sec.bottommap = - * R.ColormapNumForName(msd.bottomtexture)) < 0 ? sec.bottommap - * = 0, R.TextureNumForName(msd.bottomtexture): 0 ; - * sd.midtexture = (sec.midmap = - * R.ColormapNumForName(msd.midtexture)) < 0 ? sec.midmap = 0, - * R.TextureNumForName(msd.midtexture) : 0 ; sd.toptexture = - * (sec.topmap = R.ColormapNumForName(msd.toptexture)) < 0 ? - * sec.topmap = 0, R.TextureNumForName(msd.toptexture) : 0 ; - */ - - break; - - case 260: // killough 4/11/98: apply translucency to 2s normal texture - if (msd.midtexture.compareToIgnoreCase("TRANMAP") == 0) { - if ((sd.special = DOOM.wadLoader.CheckNumForName(msd.midtexture)) < 0 - || DOOM.wadLoader.LumpLength(sd.special) != 65536) { - sd.special = 0; - sd.midtexture = (short) DOOM.textureManager.TextureNumForName(msd.midtexture); - } else { - sd.special++; - sd.midtexture = 0; - } - } else { - sd.midtexture = (short) (sd.special = 0); - } - sd.toptexture = (short) DOOM.textureManager.TextureNumForName(msd.toptexture); - sd.bottomtexture = (short) DOOM.textureManager.TextureNumForName(msd.bottomtexture); - break; - - /* - * #ifdef GL_DOOM case 271: case 272: if - * (R_CheckTextureNumForName(msd.toptexture) == -1) { - * sd.skybox_index = R_BoxSkyboxNumForName(msd.toptexture); } #endif - */ - default: // normal cases - // TODO: Boom uses "SafeTextureNumForName" here. Find out what - // it does. - sd.midtexture = (short) DOOM.textureManager.CheckTextureNumForName(msd.midtexture); - sd.toptexture = (short) DOOM.textureManager.CheckTextureNumForName(msd.toptexture); - sd.bottomtexture = (short) DOOM.textureManager.CheckTextureNumForName(msd.bottomtexture); - break; - } - } - - DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the lump - } - - // - // P_LoadBlockMap - // - // killough 3/1/98: substantially modified to work - // towards removing blockmap limit (a wad limitation) - // - // killough 3/30/98: Rewritten to remove blockmap limit, - // though current algorithm is brute-force and unoptimal. - // - private void P_LoadBlockMap(int lump) throws IOException { - int count = 0; - - if (DOOM.cVarManager.bool(CommandVariable.BLOCKMAP) - || DOOM.wadLoader.LumpLength(lump) < 8 - || (count = DOOM.wadLoader.LumpLength(lump) / 2) >= 0x10000) // e6y - { - CreateBlockMap(); - } else { - // cph - final*, wad lump handling updated - final char[] wadblockmaplump; - - DoomBuffer data = DOOM.wadLoader.CacheLumpNum(lump, PU_LEVEL, DoomBuffer.class); - count = DOOM.wadLoader.LumpLength(lump) / 2; - wadblockmaplump = new char[count]; - - data.setOrder(ByteOrder.LITTLE_ENDIAN); - data.rewind(); - data.readCharArray(wadblockmaplump, count); - - if (!samelevel) // Reallocate if required. - { - blockmaplump = new int[count]; - } - - // killough 3/1/98: Expand wad blockmap into larger internal one, - // by treating all offsets except -1 as unsigned and zero-extending - // them. This potentially doubles the size of blockmaps allowed, - // because Doom originally considered the offsets as always signed. - blockmaplump[0] = wadblockmaplump[0]; - blockmaplump[1] = wadblockmaplump[1]; - blockmaplump[2] = wadblockmaplump[2] & 0xffff; - blockmaplump[3] = wadblockmaplump[3] & 0xffff; - - for (int i = 4; i < count; i++) { - short t = (short) wadblockmaplump[i]; // killough 3/1/98 - blockmaplump[i] = (int) (t == -1 ? -1l : t & 0xffff); - } - - DOOM.wadLoader.UnlockLumpNum(lump); // cph - unlock the lump - - bmaporgx = blockmaplump[0] << FRACBITS; - bmaporgy = blockmaplump[1] << FRACBITS; - bmapwidth = blockmaplump[2]; - bmapheight = blockmaplump[3]; - - // haleyjd 03/04/10: check for blockmap problems - // http://www.doomworld.com/idgames/index.php?id=12935 - if (!VerifyBlockMap(count)) { - LOGGER.log(Level.WARNING, String.format("P_LoadBlockMap: erroneous BLOCKMAP lump may cause crashes.")); - LOGGER.log(Level.WARNING, String.format("P_LoadBlockMap: use \"-blockmap\" command line switch for rebuilding")); - } - } - - // MAES: blockmap was generated, rather than loaded. - if (count == 0) { - count = blockmaplump.length - 4; - } - - // clear out mobj chains - CPhipps - use calloc - // blocklinks = calloc_IfSameLevel(blocklinks, bmapwidth * - // bmapheight.mobj_t.); - // clear out mobj chains - // ATTENTION! BUG!!! - // If blocklinks are "cleared" to void -but instantiated- objects, - // very bad bugs happen, especially the second time a level is - // re-instantiated. - // Probably caused other bugs as well, as an extra object would appear - // in iterators. - if (blocklinks != null && samelevel) { - for (int i = 0; i < bmapwidth * bmapheight; i++) { - blocklinks[i] = null; - } - } else { - blocklinks = new mobj_t[bmapwidth * bmapheight]; - } - - // IMPORTANT MODIFICATION: no need to have both blockmaplump AND - // blockmap. - // If the offsets in the lump are OK, then we can modify them (remove 4) - // and copy the rest of the data in one single data array. This avoids - // reserving memory for two arrays (we can't simply alias one in Java) - blockmap = new int[blockmaplump.length - 4]; - count = bmapwidth * bmapheight; - // Offsets are relative to START OF BLOCKMAP, and IN SHORTS, not bytes. - for (int i = 0; i < blockmaplump.length - 4; i++) { - // Modify indexes so that we don't need two different lumps. - // Can probably be further optimized if we simply shift everything - // backwards. - // and reuse the same memory space. - if (i < count) { - blockmaplump[i] = blockmaplump[i + 4] - 4; - } else { - blockmaplump[i] = blockmaplump[i + 4]; - } - } - - // MAES: set blockmapxneg and blockmapyneg - // E.g. for a full 512x512 map, they should be both - // -1. For a 257*257, they should be both -255 etc. - if (bmapwidth > 255) { - blockmapxneg = bmapwidth - 512; - } - if (bmapheight > 255) { - blockmapyneg = bmapheight - 512; - } - - blockmap = blockmaplump; - - } - - // - // P_LoadReject - load the reject table - // - private void P_LoadReject(int lumpnum, int totallines) { - // dump any old cached reject lump, then cache the new one - if (rejectlump != -1) { - DOOM.wadLoader.UnlockLumpNum(rejectlump); - } - rejectlump = lumpnum + ML_REJECT; - rejectmatrix = DOOM.wadLoader.CacheLumpNumAsRawBytes(rejectlump, 0); - - // e6y: check for overflow - // TODO: g.Overflow.RejectOverrun(rejectlump, rejectmatrix, - // totallines,numsectors); - } - - // - // P_GroupLines - // Builds sector line lists and subsector sector numbers. - // Finds block bounding boxes for sectors. - // - // killough 5/3/98: reformatted, cleaned up - // cph 18/8/99: rewritten to avoid O(numlines * numsectors) section - // It makes things more complicated, but saves seconds on big levels - // figgi 09/18/00 -- adapted for gl-nodes - // modified to return totallines (needed by P_LoadReject) - private int P_GroupLines() { - line_t li; - sector_t sector; - int total = numlines; - - // figgi - for (int i = 0; i < numsubsectors; i++) { - int seg = subsectors[i].firstline; - subsectors[i].sector = null; - for (int j = 0; j < subsectors[i].numlines; j++) { - if (segs[seg].sidedef != null) { - subsectors[i].sector = segs[seg].sidedef.sector; - break; - } - seg++; - } - if (subsectors[i].sector == null) { - DOOM.doomSystem.Error("P_GroupLines: Subsector a part of no sector!\n"); - } - } - - // count number of lines in each sector - for (int i = 0; i < numlines; i++) { - li = lines[i]; - li.frontsector.linecount++; - if (li.backsector != null && (li.backsector != li.frontsector)) { - li.backsector.linecount++; - total++; - } - } - - // allocate line tables for each sector - // e6y: REJECT overrun emulation code - // moved to P_LoadReject - for (int i = 0; i < numsectors; i++) { - sector = sectors[i]; - sector.lines = malloc(line_t::new, line_t[]::new, sector.linecount); - // linebuffer += sector.linecount; - sector.linecount = 0; - BBox.ClearBox(sector.blockbox); - } - - // Enter those lines - for (int i = 0; i < numlines; i++) { - li = lines[i]; - AddLineToSector(li, li.frontsector); - if (li.backsector != null && li.backsector != li.frontsector) { - AddLineToSector(li, li.backsector); - } - } - - for (int i = 0; i < numsectors; i++) { - sector = sectors[i]; - int[] bbox = sector.blockbox; // cph - For convenience, so - // I can sue the old code unchanged - int block; - - // set the degenmobj_t to the middle of the bounding box - // TODO - if (true/* comp[comp_sound] */) { - sector.soundorg = new degenmobj_t((bbox[BOXRIGHT] + bbox[BOXLEFT]) / 2, (bbox[BOXTOP] + bbox[BOXBOTTOM]) / 2); - } else { - // e6y: fix sound origin for large levels - sector.soundorg = new degenmobj_t((bbox[BOXRIGHT] / 2 + bbox[BOXLEFT] / 2), bbox[BOXTOP] / 2 + bbox[BOXBOTTOM] / 2); - } - - // adjust bounding box to map blocks - block = getSafeBlockY(bbox[BOXTOP] - bmaporgy + Limits.MAXRADIUS); - block = block >= bmapheight ? bmapheight - 1 : block; - sector.blockbox[BOXTOP] = block; - - block = getSafeBlockY(bbox[BOXBOTTOM] - bmaporgy - Limits.MAXRADIUS); - block = block < 0 ? 0 : block; - sector.blockbox[BOXBOTTOM] = block; - - block = getSafeBlockX(bbox[BOXRIGHT] - bmaporgx + Limits.MAXRADIUS); - block = block >= bmapwidth ? bmapwidth - 1 : block; - sector.blockbox[BOXRIGHT] = block; - - block = getSafeBlockX(bbox[BOXLEFT] - bmaporgx - Limits.MAXRADIUS); - block = block < 0 ? 0 : block; - sector.blockbox[BOXLEFT] = block; - } - - return total; // this value is needed by the reject overrun emulation - // code - } - - // - // killough 10/98 - // - // Remove slime trails. - // - // Slime trails are inherent to Doom's coordinate system -- i.e. there is - // nothing that a node builder can do to prevent slime trails ALL of the - // time, - // because it's a product of the integer coodinate system, and just because - // two lines pass through exact integer coordinates, doesn't necessarily - // mean - // that they will intersect at integer coordinates. Thus we must allow for - // fractional coordinates if we are to be able to split segs with node - // lines, - // as a node builder must do when creating a BSP tree. - // - // A wad file does not allow fractional coordinates, so node builders are - // out - // of luck except that they can try to limit the number of splits (they - // might - // also be able to detect the degree of roundoff error and try to avoid - // splits - // with a high degree of roundoff error). But we can use fractional - // coordinates - // here, inside the engine. It's like the difference between square inches - // and - // square miles, in terms of granularity. - // - // For each vertex of every seg, check to see whether it's also a vertex of - // the linedef associated with the seg (i.e, it's an endpoint). If it's not - // an endpoint, and it wasn't already moved, move the vertex towards the - // linedef by projecting it using the law of cosines. Formula: - // - // 2 2 2 2 - // dx x0 + dy x1 + dx dy (y0 - y1) dy y0 + dx y1 + dx dy (x0 - x1) - // {---------------------------------, ---------------------------------} - // 2 2 2 2 - // dx + dy dx + dy - // - // (x0,y0) is the vertex being moved, and (x1,y1)-(x1+dx,y1+dy) is the - // reference linedef. - // - // Segs corresponding to orthogonal linedefs (exactly vertical or horizontal - // linedefs), which comprise at least half of all linedefs in most wads, - // don't - // need to be considered, because they almost never contribute to slime - // trails - // (because then any roundoff error is parallel to the linedef, which - // doesn't - // cause slime). Skipping simple orthogonal lines lets the code finish - // quicker. - // - // Please note: This section of code is not interchangable with TeamTNT's - // code which attempts to fix the same problem. - // - // Firelines (TM) is a Rezistered Trademark of MBF Productions - // - private void P_RemoveSlimeTrails() { // killough 10/98 - // Hitlist for vertices - boolean[] hit = new boolean[numvertexes]; - - // Searchlist for - for (int i = 0; i < numsegs; i++) { // Go through each seg - final line_t l; - - if (segs[i].miniseg == true) { // figgi -- skip minisegs - return; - } - - l = segs[i].linedef; // The parent linedef - if (l.dx != 0 && l.dy != 0) { // We can ignore orthogonal lines - vertex_t v = segs[i].v1; - do { - int index = C2JUtils.indexOf(vertexes, v); - if (!hit[index]) { // If we haven't processed vertex - hit[index] = true; // Mark this vertex as processed - if (v != l.v1 && v != l.v2) { // Exclude endpoints of linedefs - // Project the vertex back onto the parent linedef - long dx2 = (l.dx >> FRACBITS) * (l.dx >> FRACBITS); - long dy2 = (l.dy >> FRACBITS) * (l.dy >> FRACBITS); - long dxy = (l.dx >> FRACBITS) * (l.dy >> FRACBITS); - long s = dx2 + dy2; - int x0 = v.x, y0 = v.y, x1 = l.v1.x, y1 = l.v1.y; - v.x = (int) ((dx2 * x0 + dy2 * x1 + dxy * (y0 - y1)) / s); - v.y = (int) ((dy2 * y0 + dx2 * y1 + dxy * (x0 - x1)) / s); - } - } // Obsfucated C contest entry: :) - } while ((v != segs[i].v2) && ((v = segs[i].v2) != null)); - } - // Assign modified vertex values. - l.assignVertexValues(); - } - } - - // - // P_CheckLumpsForSameSource - // - // Are these lumps in the same wad file? - // - boolean P_CheckLumpsForSameSource(int lump1, int lump2) { - int wad1_index, wad2_index; - wadfile_info_t wad1, wad2; - - if ((unsigned(lump1) >= unsigned(DOOM.wadLoader.NumLumps())) - || (unsigned(lump2) >= unsigned(DOOM.wadLoader.NumLumps()))) { - return false; - } - - wad1 = DOOM.wadLoader.GetLumpInfo(lump1).wadfile; - wad2 = DOOM.wadLoader.GetLumpInfo(lump2).wadfile; - - if (wad1 == null || wad2 == null) { - return false; - } - - wad1_index = DOOM.wadLoader.GetWadfileIndex(wad1); - wad2_index = DOOM.wadLoader.GetWadfileIndex(wad2); - - if (wad1_index != wad2_index) { - return false; - } - - if ((wad1_index < 0) || (wad1_index >= DOOM.wadLoader.GetNumWadfiles())) { - return false; - } - - return !((wad2_index < 0) || (wad2_index >= DOOM.wadLoader.GetNumWadfiles())); - } - - private static final String[] ml_labels = { - "ML_LABEL", // A separator, name, ExMx or MAPxx - "ML_THINGS", // Monsters, items.. - "ML_LINEDEFS", // LineDefs, from editing - "ML_SIDEDEFS", // SideDefs, from editing - "ML_VERTEXES", // Vertices, edited and BSP splits generated - "ML_SEGS", // LineSegs, from LineDefs split by BSP - "ML_SSECTORS", // SubSectors, list of LineSegs - "ML_NODES", // BSP nodes - "ML_SECTORS", // Sectors, from editing - "ML_REJECT", // LUT, sector-sector visibility - "ML_BLOCKMAP", // LUT, motion clipping, walls/grid element - }; - - private static final boolean GL_DOOM = false; - - // - // P_CheckLevelFormat - // - // Checking for presence of necessary lumps - // - void P_CheckLevelWadStructure(final String mapname) { - int i, lumpnum; - - if (mapname == null) { - DOOM.doomSystem.Error("P_SetupLevel: Wrong map name"); - throw new NullPointerException(); - } - - lumpnum = DOOM.wadLoader.CheckNumForName(mapname.toUpperCase()); - - if (lumpnum < 0) { - DOOM.doomSystem.Error("P_SetupLevel: There is no %s map.", mapname); - } - - for (i = ML_THINGS + 1; i <= ML_SECTORS; i++) { - if (!P_CheckLumpsForSameSource(lumpnum, lumpnum + i)) { - DOOM.doomSystem.Error( - "P_SetupLevel: Level wad structure is incomplete. There is no %s lump. (%s)", - ml_labels[i], DOOM.wadLoader.GetNameForLump(lumpnum)); - } - } - - // refuse to load Hexen-format maps, avoid segfaults - i = lumpnum + ML_BLOCKMAP + 1; - if (P_CheckLumpsForSameSource(lumpnum, i)) { - if (DOOM.wadLoader.GetLumpInfo(i).name.compareToIgnoreCase("BEHAVIOR") == 0) { - DOOM.doomSystem.Error("P_SetupLevel: %s: Hexen format not supported", mapname); - } - } - } - - // - // P_SetupLevel - // - // killough 5/3/98: reformatted, cleaned up - @Override - @SourceCode.Suspicious(CauseOfDesyncProbability.LOW) - @P_Setup.C(P_SetupLevel) - public void SetupLevel(int episode, int map, int playermask, skill_t skill) throws IOException { - String lumpname; - int lumpnum; - - String gl_lumpname; - int gl_lumpnum; - - // e6y - DOOM.totallive = 0; - // TODO: transparentpresent = false; - - // R_StopAllInterpolations(); - DOOM.totallive = DOOM.totalkills = DOOM.totalitems = DOOM.totalsecret = DOOM.wminfo.maxfrags = 0; - DOOM.wminfo.partime = 180; - - for (int i = 0; i < Limits.MAXPLAYERS; i++) { - DOOM.players[i].killcount = DOOM.players[i].secretcount = DOOM.players[i].itemcount = 0; - // TODO DM.players[i].resurectedkillcount = 0;//e6y - } - - // Initial height of PointOfView - // will be set by player think. - DOOM.players[DOOM.consoleplayer].viewz = 1; - - // Make sure all sounds are stopped before Z_FreeTags. - S_Start: - { - DOOM.doomSound.Start(); - } - - Z_FreeTags: - ; // Z_FreeTags(PU_LEVEL, PU_PURGELEVEL-1); - - if (rejectlump != -1) { // cph - unlock the reject table - DOOM.wadLoader.UnlockLumpNum(rejectlump); - rejectlump = -1; - } - - P_InitThinkers: - { - DOOM.actions.InitThinkers(); - } - - // if working with a devlopment map, reload it - W_Reload: - ; // killough 1/31/98: W.Reload obsolete - - // find map name - if (DOOM.isCommercial()) { - lumpname = String.format("map%02d", map); // killough 1/24/98: - // simplify - gl_lumpname = String.format("gl_map%02d", map); // figgi - } else { - lumpname = String.format("E%dM%d", episode, map); // killough - // 1/24/98: - // simplify - gl_lumpname = String.format("GL_E%dM%d", episode, map); // figgi - } - - W_GetNumForName: - { - lumpnum = DOOM.wadLoader.GetNumForName(lumpname); - gl_lumpnum = DOOM.wadLoader.CheckNumForName(gl_lumpname); // figgi - } - - // e6y - // Refuse to load a map with incomplete pwad structure. - // Avoid segfaults on levels without nodes. - P_CheckLevelWadStructure(lumpname); - - DOOM.leveltime = 0; - DOOM.totallive = 0; - - // note: most of this ordering is important - // killough 3/1/98: P_LoadBlockMap call moved down to below - // killough 4/4/98: split load of sidedefs into two parts, - // to allow texture names to be used in special linedefs - // figgi 10/19/00 -- check for gl lumps and load them - P_GetNodesVersion(lumpnum, gl_lumpnum); - - // e6y: speedup of level reloading - // Most of level's structures now are allocated with PU_STATIC instead - // of PU_LEVEL - // It is important for OpenGL, because in case of the same data in - // memory - // we can skip recalculation of much stuff - samelevel = (map == current_map) && (episode == current_episode) && (nodesVersion == current_nodesVersion); - - current_episode = episode; - current_map = map; - current_nodesVersion = nodesVersion; - - if (!samelevel) { - - /* - * if (GL_DOOM){ // proff 11/99: clean the memory from textures etc. - * gld_CleanMemory(); } - */ - // free(segs); - // free(nodes); - // free(subsectors); - /* - * #ifdef GL_DOOM free(map_subsectors); #endif - */ - // free(blocklinks); - // free(blockmaplump); - // free(lines); - // free(sides); - // free(sectors); - // free(vertexes); - } - - if (nodesVersion > 0) { - this.P_LoadVertexes2(lumpnum + ML_VERTEXES, gl_lumpnum + ML_GL_VERTS); - } else { - P_LoadVertexes(lumpnum + ML_VERTEXES); - } - - P_LoadSectors(lumpnum + ML_SECTORS); - P_LoadSideDefs(lumpnum + ML_SIDEDEFS); - P_LoadLineDefs(lumpnum + ML_LINEDEFS); - P_LoadSideDefs2(lumpnum + ML_SIDEDEFS); - P_LoadLineDefs2(lumpnum + ML_LINEDEFS); - - // e6y: speedup of level reloading - // Do not reload BlockMap for same level, - // because in case of big level P_CreateBlockMap eats much time - if (!samelevel) { - P_LoadBlockMap(lumpnum + ML_BLOCKMAP); - } else { - // clear out mobj chains - if (blocklinks != null && blocklinks.length == bmapwidth * bmapheight) { - for (int i = 0; i < bmapwidth * bmapheight; i++) { - blocklinks[i] = null; - } - } else { - blocklinks = new mobj_t[bmapwidth * bmapheight]; - Arrays.setAll(blocklinks, i -> mobj_t.createOn(DOOM)); - } - } - - if (nodesVersion > 0) { - P_LoadSubsectors(gl_lumpnum + ML_GL_SSECT); - P_LoadNodes(gl_lumpnum + ML_GL_NODES); - // TODO: P_LoadGLSegs(gl_lumpnum + ML_GL_SEGS); - } else { - if (P_CheckForZDoomUncompressedNodes(lumpnum, gl_lumpnum)) { - P_LoadZNodes(lumpnum + ML_NODES, 0); - } else if (P_CheckForDeePBSPv4Nodes(lumpnum, gl_lumpnum)) { - P_LoadSubsectors_V4(lumpnum + ML_SSECTORS); - P_LoadNodes_V4(lumpnum + ML_NODES); - P_LoadSegs_V4(lumpnum + ML_SEGS); - } else { - P_LoadSubsectors(lumpnum + ML_SSECTORS); - P_LoadNodes(lumpnum + ML_NODES); - P_LoadSegs(lumpnum + ML_SEGS); - } - } - - /* - * if (GL_DOOM){ map_subsectors = calloc_IfSameLevel(map_subsectors, - * numsubsectors); } - */ - // reject loading and underflow padding separated out into new function - // P_GroupLines modified to return a number the underflow padding needs - // P_LoadReject(lumpnum, P_GroupLines()); - P_GroupLines(); - super.LoadReject(lumpnum + ML_REJECT); - - /** - * TODO: try to fix, since it seems it doesn't work - * - Good Sign 2017/05/07 - */ - // e6y - // Correction of desync on dv04-423.lmp/dv.wad - // http://www.doomworld.com/vb/showthread.php?s=&postid=627257#post627257 - // if (DoomStatus.compatibility_level>=lxdoom_1_compatibility || - // Compatibility.prboom_comp[PC.PC_REMOVE_SLIME_TRAILS.ordinal()].state) - P_RemoveSlimeTrails(); // killough 10/98: remove slime trails from wad - - // Note: you don't need to clear player queue slots -- - // a much simpler fix is in g_game.c -- killough 10/98 - DOOM.bodyqueslot = 0; - - /* cph - reset all multiplayer starts */ - for (int i = 0; i < playerstarts.length; i++) { - DOOM.playerstarts[i] = null; - } - - deathmatch_p = 0; - - for (int i = 0; i < Limits.MAXPLAYERS; i++) { - DOOM.players[i].mo = null; - } - // TODO: TracerClearStarts(); - - // Hmm? P_MapStart(); - P_LoadThings: - { - P_LoadThings(lumpnum + ML_THINGS); - } - - // if deathmatch, randomly spawn the active players - if (DOOM.deathmatch) { - for (int i = 0; i < Limits.MAXPLAYERS; i++) { - if (DOOM.playeringame[i]) { - DOOM.players[i].mo = null; // not needed? - done before P_LoadThings - G_DeathMatchSpawnPlayer: - { - DOOM.DeathMatchSpawnPlayer(i); - } - } - } - } else { // if !deathmatch, check all necessary player starts actually exist - for (int i = 0; i < Limits.MAXPLAYERS; i++) { - if (DOOM.playeringame[i] && !C2JUtils.eval(DOOM.players[i].mo)) { - DOOM.doomSystem.Error("P_SetupLevel: missing player %d start\n", i + 1); - } - } - } - - // killough 3/26/98: Spawn icon landings: - // TODO: if (DM.isCommercial()) - // P.SpawnBrainTargets(); - if (!DOOM.isShareware()) { - // TODO: S.ParseMusInfo(lumpname); - } - - // clear special respawning que - DOOM.actions.ClearRespawnQueue(); - - // set up world state - P_SpawnSpecials: - { - DOOM.actions.SpawnSpecials(); - } - - // TODO: P.MapEnd(); - // preload graphics - if (DOOM.precache) { - /* @SourceCode.Compatible if together */ - R_PrecacheLevel: - { - DOOM.textureManager.PrecacheLevel(); - - // MAES: thinkers are separate than texture management. Maybe split - // sprite management as well? - DOOM.sceneRenderer.PreCacheThinkers(); - } - } - - /* - * if (GL_DOOM){ if (V_GetMode() == VID_MODEGL) { // e6y // Do not - * preprocess GL data during skipping, // because it potentially will - * not be used. // But preprocessing must be called immediately after - * stop of skipping. if (!doSkip) { // proff 11/99: calculate all OpenGL - * specific tables etc. gld_PreprocessLevel(); } } } - */ - // e6y - // TODO P_SyncWalkcam(true, true); - // TODO R_SmoothPlaying_Reset(NULL); - } - -} +package p; + +import static boom.Compatibility.prboom_2_compatibility; +import boom.DeepBSPNodesV4; +import static boom.E6Y.NO_INDEX; +import boom.mapglvertex_t; +import boom.mapnode_v4_t; +import boom.mapnode_znod_t; +import boom.mapseg_v4_t; +import boom.mapseg_znod_t; +import boom.mapsubsector_v4_t; +import boom.mapsubsector_znod_t; +import static data.Defines.NF_SUBSECTOR; +import static data.Defines.NF_SUBSECTOR_CLASSIC; +import static data.Defines.PU_LEVEL; +import data.Limits; +import data.maplinedef_t; +import data.mapnode_t; +import data.mapsector_t; +import data.mapseg_t; +import data.mapsidedef_t; +import data.mapsubsector_t; +import data.mapthing_t; +import data.mapvertex_t; +import defines.skill_t; +import defines.slopetype_t; +import doom.CommandVariable; +import doom.DoomMain; +import doom.DoomStatus; +import doom.SourceCode; +import doom.SourceCode.CauseOfDesyncProbability; +import doom.SourceCode.P_Setup; +import static doom.SourceCode.P_Setup.P_LoadThings; +import static doom.SourceCode.P_Setup.P_SetupLevel; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Arrays; +import java.util.function.IntFunction; +import java.util.logging.Level; +import java.util.logging.Logger; +import m.BBox; +import static m.BBox.BOXBOTTOM; +import static m.BBox.BOXLEFT; +import static m.BBox.BOXRIGHT; +import static m.BBox.BOXTOP; +import m.fixed_t; +import static m.fixed_t.FRACBITS; +import static m.fixed_t.FRACUNIT; +import mochadoom.Loggers; +import rr.RendererState; +import rr.line_t; +import static rr.line_t.ML_TWOSIDED; +import rr.node_t; +import rr.sector_t; +import rr.seg_t; +import rr.side_t; +import rr.subsector_t; +import rr.vertex_t; +import rr.z_vertex_t; +import s.degenmobj_t; +import utils.C2JUtils; +import static utils.C2JUtils.flags; +import static utils.C2JUtils.unsigned; +import utils.GenericCopy.ArraySupplier; +import static utils.GenericCopy.malloc; +import w.CacheableDoomObjectContainer; +import w.DoomBuffer; +import w.wadfile_info_t; + +/* + * Emacs style mode select -*- Java -*- + * ----------------------------------------------------------------------------- + * PrBoom: a Doom port merged with LxDoom and LSDLDoom based on BOOM, a modified + * and improved DOOM engine Copyright (C) 1999 by id Software, Chi Hoang, Lee + * Killough, Jim Flynn, Rand Phares, Ty Halderman Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze Copyright 2005, + * 2006 by Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko This + * program is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. DESCRIPTION: Do all + * the WAD I/O, get map description, set up initial state and misc. LUTs. + * + * MAES 30/9/2011: This is a direct translation of prBoom+'s 2.5.0.8 p_setup.c + * and p_setup.h. + * + * + * + * ----------------------------------------------------------------------------- + */ +public class BoomLevelLoader extends AbstractLevelLoader { + + private static final Logger LOGGER = Loggers.getLogger(BoomLevelLoader.class.getName()); + + public BoomLevelLoader(DoomMain DM) { + super(DM); + // TODO Auto-generated constructor stub + } + + // OpenGL related. + byte[] map_subsectors; + + // ////////////////////////////////////////////////////////////////////////////////////////// + // figgi 08/21/00 -- finalants and globals for glBsp support + public static final int gNd2 = 0x32644E67; // figgi -- suppport for new + // GL_VERT format v2.0 + + public static final int gNd3 = 0x33644E67; + + public static final int gNd4 = 0x34644E67; + + public static final int gNd5 = 0x35644E67; + + public static final int ZNOD = 0x444F4E5A; + + public static final int ZGLN = 0x4E4C475A; + + public static final int GL_VERT_OFFSET = 4; + + int firstglvertex = 0; + + int nodesVersion = 0; + + boolean forceOldBsp = false; + + // figgi 08/21/00 -- glSegs + class glseg_t { + + char v1; // start vertex (16 bit) + + char v2; // end vertex (16 bit) + + char linedef; // linedef, or -1 for minisegs + + short side; // side on linedef: 0 for right, 1 for left + + short partner; // corresponding partner seg, or -1 on one-sided walls + } + + public static final int ML_GL_LABEL = 0; // A separator name, GL_ExMx or + // GL_MAPxx + + public static final int ML_GL_VERTS = 1; // Extra Vertices + + public static final int ML_GL_SEGS = 2; // Segs, from linedefs & minisegs + + public static final int ML_GL_SSECT = 3; // SubSectors, list of segs + + public static final int ML_GL_NODES = 4; // GL BSP nodes + + // ////////////////////////////////////////////////////////////////////////////////////////// + // + // REJECT + // For fast sight rejection. + // Speeds up enemy AI by skipping detailed + // LineOf Sight calculation. + // Without the special effect, this could + // be used as a PVS lookup as well. + // + private int rejectlump = -1;// cph - store reject lump num if cached + + private int current_episode = -1; + + private int current_map = -1; + + private int current_nodesVersion = -1; + + private boolean samelevel = false; + + /** + * e6y: Smart malloc Used by P_SetupLevel() for smart data loading. Do + * nothing if level is the same. Passing a null array forces allocation. + * + * @param p + * generically typed array to consider + * @param numstuff + * elements to realloc + */ + private T[] malloc_IfSameLevel(T[] p, int numstuff, ArraySupplier supplier, IntFunction generator) { + if (!samelevel || (p == null)) { + return malloc(supplier, generator, numstuff); + } + return p; + } + + // e6y: Smart calloc + // Used by P_SetupLevel() for smart data loading + // Clear the memory without allocation if level is the same + private T[] calloc_IfSameLevel(T[] p, int numstuff, ArraySupplier supplier, IntFunction generator) { + if (!samelevel) { + return malloc(supplier, generator, numstuff); + } else { + // TODO: stuff should be resetted! + C2JUtils.resetAll(p); + return p; + } + } + + // + // P_CheckForZDoomNodes + // + private boolean P_CheckForZDoomNodes(int lumpnum, int gl_lumpnum) { + byte[] data; + int check; + + data = DOOM.wadLoader.CacheLumpNumAsRawBytes(lumpnum + ML_NODES, 0); + check = ByteBuffer.wrap(data).getInt(); + + if (check == ZNOD) { + DOOM.doomSystem.Error("P_CheckForZDoomNodes: ZDoom nodes not supported yet"); + } + + data = DOOM.wadLoader.CacheLumpNumAsRawBytes(lumpnum + ML_SSECTORS, 0); + check = ByteBuffer.wrap(data).getInt(); + + if (check == ZGLN) { + DOOM.doomSystem.Error("P_CheckForZDoomNodes: ZDoom GL nodes not supported yet"); + } + + // Unlock them to force different buffering interpretation. + DOOM.wadLoader.UnlockLumpNum(lumpnum + ML_NODES); + DOOM.wadLoader.UnlockLumpNum(lumpnum + ML_SSECTORS); + + return false; + } + + // + // P_CheckForDeePBSPv4Nodes + // http://www.sbsoftware.com/files/DeePBSPV4specs.txt + // + private boolean P_CheckForDeePBSPv4Nodes(int lumpnum, int gl_lumpnum) { + byte[] data; + boolean result = false; + + data = DOOM.wadLoader.CacheLumpNumAsRawBytes(lumpnum + ML_NODES, 0); + byte[] compare = Arrays.copyOfRange(data, 0, 7); + + if (Arrays.equals(compare, DeepBSPNodesV4.DeepBSPHeader)) { + LOGGER.log(Level.INFO, "P_CheckForDeePBSPv4Nodes: DeePBSP v4 Extended nodes are detected"); + result = true; + } + + DOOM.wadLoader.UnlockLumpNum(lumpnum + ML_NODES); + + return result; + } + + // + // P_CheckForZDoomUncompressedNodes + // http://zdoom.org/wiki/ZDBSP#Compressed_Nodes + // + private static final int XNOD = 0x584e4f44; + + private boolean P_CheckForZDoomUncompressedNodes(int lumpnum, int gl_lumpnum) { + byte[] data; + int wrapper; + boolean result = false; + + data = DOOM.wadLoader.CacheLumpNumAsRawBytes(lumpnum + ML_NODES, 0); + wrapper = ByteBuffer.wrap(data).getInt(); + + if (wrapper == XNOD) { + LOGGER.log(Level.INFO, "P_CheckForZDoomUncompressedNodes: ZDoom uncompressed normal nodes are detected"); + result = true; + } + + DOOM.wadLoader.UnlockLumpNum(lumpnum + ML_NODES); + + return result; + } + + // + // P_GetNodesVersion + // + public void P_GetNodesVersion(int lumpnum, int gl_lumpnum) { + int ver = -1; + nodesVersion = 0; + + if ((gl_lumpnum > lumpnum) && (forceOldBsp == false) + && (DoomStatus.compatibility_level >= prboom_2_compatibility)) { + + byte[] data = DOOM.wadLoader.CacheLumpNumAsRawBytes(gl_lumpnum + ML_GL_VERTS, 0); + int wrapper = ByteBuffer.wrap(data).getInt(); + if (wrapper == gNd2) { + data = DOOM.wadLoader.CacheLumpNumAsRawBytes(gl_lumpnum + ML_GL_SEGS, 0); + wrapper = ByteBuffer.wrap(data).getInt(); + if (wrapper == gNd3) { + ver = 3; + } else { + nodesVersion = gNd2; + LOGGER.log(Level.INFO, "P_GetNodesVersion: found version 2 nodes"); + } + } + if (wrapper == gNd4) { + ver = 4; + } + if (wrapper == gNd5) { + ver = 5; + } + // e6y: unknown gl nodes will be ignored + if (nodesVersion == 0 && ver != -1) { + LOGGER.log(Level.INFO, String.format("P_GetNodesVersion: found version %d nodes", ver)); + LOGGER.log(Level.INFO, String.format("P_GetNodesVersion: version %d nodes not supported", ver)); + } + } else { + nodesVersion = 0; + LOGGER.log(Level.INFO, "P_GetNodesVersion: using normal BSP nodes"); + if (P_CheckForZDoomNodes(lumpnum, gl_lumpnum)) { + DOOM.doomSystem.Error("P_GetNodesVersion: ZDoom nodes not supported yet"); + } + } + } + + // + // P_LoadVertexes + // + // killough 5/3/98: reformatted, cleaned up + // + private void P_LoadVertexes(int lump) { + final mapvertex_t[] data; // cph - final + + // Determine number of lumps: + // total lump length / vertex record length. + numvertexes = DOOM.wadLoader.LumpLength(lump) / mapvertex_t.sizeOf(); + + // Allocate zone memory for buffer. + vertexes = calloc_IfSameLevel(vertexes, numvertexes, vertex_t::new, vertex_t[]::new); + + // Load data into cache. + // cph 2006/07/29 - cast to mapvertex_t here, making the loop below much + // neater + data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numvertexes, mapvertex_t::new, mapvertex_t[]::new); + + // Copy and convert vertex coordinates, + // internal representation as fixed. + for (int i = 0; i < numvertexes; i++) { + vertexes[i].x = data[i].x << m.fixed_t.FRACBITS; + vertexes[i].y = data[i].y << FRACBITS; + } + + // Free buffer memory. + DOOM.wadLoader.UnlockLumpNum(lump); + } + + /******************************************* + * Name : P_LoadVertexes2 * modified : 09/18/00, adapted for PrBoom * author + * : figgi * what : support for gl nodes + * + * @throws IOException + * * + *******************************************/ + // figgi -- FIXME: Automap showes wrong zoom boundaries when starting game + // when P_LoadVertexes2 is used with classic BSP nodes. + private void P_LoadVertexes2(int lump, int gllump) throws IOException { + final ByteBuffer gldata; + mapvertex_t[] ml; + + // GL vertexes come after regular ones. + firstglvertex = DOOM.wadLoader.LumpLength(lump) / mapvertex_t.sizeOf(); + numvertexes = DOOM.wadLoader.LumpLength(lump) / mapvertex_t.sizeOf(); + + if (gllump >= 0) { // check for glVertices + // Read GL lump into buffer. This allows some flexibility + gldata = DOOM.wadLoader.CacheLumpNumAsDoomBuffer(gllump).getBuffer(); + + if (nodesVersion == gNd2) { // 32 bit GL_VERT format (16.16 fixed) + // These vertexes are double in size than regular Doom vertexes. + // Furthermore, we have to skip the first 4 bytes + // (GL_VERT_OFFSET) + // of the gl lump. + numvertexes += (DOOM.wadLoader.LumpLength(gllump) - GL_VERT_OFFSET) / mapglvertex_t.sizeOf(); + + // Vertexes size accomodates both normal and GL nodes. + vertexes = malloc_IfSameLevel(vertexes, numvertexes, vertex_t::new, vertex_t[]::new); + + final mapglvertex_t mgl[] = malloc(mapglvertex_t::new, mapglvertex_t[]::new, numvertexes - firstglvertex); + + // Get lump and skip first 4 bytes + gldata.rewind(); + gldata.position(GL_VERT_OFFSET); + + CacheableDoomObjectContainer.unpack(gldata, mgl); + + int mgl_count = 0; + + for (int i = firstglvertex; i < numvertexes; i++) { + vertexes[i].x = mgl[mgl_count].x; + vertexes[i].y = mgl[mgl_count].y; + mgl_count++; + } + } else { + // Vertexes size accomodates both normal and GL nodes. + numvertexes += DOOM.wadLoader.LumpLength(gllump) / mapvertex_t.sizeOf(); + vertexes = malloc_IfSameLevel(vertexes, numvertexes, vertex_t::new, vertex_t[]::new); + + ml = malloc(mapvertex_t::new, mapvertex_t[]::new, numvertexes - firstglvertex); + + // We can read this "directly" because no skipping is involved. + gldata.rewind(); + CacheableDoomObjectContainer.unpack(gldata, ml); + // ml = W.CacheLumpNumIntoArray(gllump, + // numvertexes-firstglvertex,mapvertex_t.class); + int ml_count = 0; + + for (int i = firstglvertex; i < numvertexes; i++) { + vertexes[i].x = ml[ml_count].x; + vertexes[i].y = ml[ml_count].y; + ml_count++; + } + } + DOOM.wadLoader.UnlockLumpNum(gllump); + } + + // Loading of regular lumps (sheesh!) + ml = DOOM.wadLoader.CacheLumpNumIntoArray(lump, firstglvertex, mapvertex_t::new, mapvertex_t[]::new); + + for (int i = 0; i < firstglvertex; i++) { + vertexes[i].x = ml[i].x; + vertexes[i].y = ml[i].y; + } + + DOOM.wadLoader.UnlockLumpNum(lump); + + } + + /******************************************* + * created : 08/13/00 * modified : 09/18/00, adapted for PrBoom * author : + * figgi * what : basic functions needed for * computing gl nodes * + *******************************************/ + public int checkGLVertex(int num) { + if ((num & 0x8000) != 0) { + num = (num & 0x7FFF) + firstglvertex; + } + return num; + } + + public static float GetDistance(int dx, int dy) { + float fx = (float) (dx) / FRACUNIT, fy = (float) (dy) / FRACUNIT; + return (float) Math.sqrt(fx * fx + fy * fy); + } + + public static float GetTexelDistance(int dx, int dy) { + // return (float)((int)(GetDistance(dx, dy) + 0.5f)); + float fx = (float) (dx) / FRACUNIT, fy = (float) (dy) / FRACUNIT; + return ((int) (0.5f + (float) Math.sqrt(fx * fx + fy * fy))); + } + + public static int GetOffset(vertex_t v1, vertex_t v2) { + float a, b; + int r; + a = (v1.x - v2.x) / (float) FRACUNIT; + b = (v1.y - v2.y) / (float) FRACUNIT; + r = (int) (Math.sqrt(a * a + b * b) * FRACUNIT); + return r; + } + + // + // P_LoadSegs + // + // killough 5/3/98: reformatted, cleaned up + private void P_LoadSegs(int lump) { + final mapseg_t[] data; // cph - final + + numsegs = DOOM.wadLoader.LumpLength(lump) / mapseg_t.sizeOf(); + segs = calloc_IfSameLevel(segs, numsegs, seg_t::new, seg_t[]::new); + + data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numsegs, mapseg_t::new, mapseg_t[]::new); // cph - + // wad + // lump + // handling + // updated + + if ((data == null) || (numsegs == 0)) { + DOOM.doomSystem.Error("P_LoadSegs: no segs in level"); + } + + for (int i = 0; i < numsegs; i++) { + seg_t li = segs[i]; + final mapseg_t ml = data[i]; + char v1, v2; + + int side, linedef; + line_t ldef; + + li.iSegID = i; // proff 11/05/2000: needed for OpenGL + + v1 = ml.v1; + v2 = ml.v2; + + // e6y + // moved down for additional checks to avoid overflow + // if wrong vertexe's indexes are in SEGS lump + // see below for more detailed information + // li.v1 = &vertexes[v1]; + // li.v2 = &vertexes[v2]; + li.miniseg = false; // figgi -- there are no minisegs in classic BSP + // nodes + + // e6y: moved down, see below + // li.length = GetDistance(li.v2.x - li.v1.x, li.v2.y - li.v1.y); + li.angle = ml.angle << 16; + li.offset = ml.offset << 16; + linedef = ml.linedef; + + // e6y: check for wrong indexes + if (linedef >= numlines) { + DOOM.doomSystem.Error("P_LoadSegs: seg %d references a non-existent linedef %d", i, linedef); + } + + ldef = lines[linedef]; + li.linedef = ldef; + side = ml.side; + + // e6y: fix wrong side index + if (side != 0 && side != 1) { + LOGGER.log(Level.WARNING, String.format("P_LoadSegs: seg %d contains wrong side index %d. Replaced with 1.", i, side)); + side = 1; + } + + // e6y: check for wrong indexes + if (ldef.sidenum[side] >= (char) numsides) { + DOOM.doomSystem.Error( + "P_LoadSegs: linedef %d for seg %d references a non-existent sidedef %d", + linedef, i, ldef.sidenum[side] + ); + } + + li.sidedef = sides[ldef.sidenum[side]]; + + /* + * cph 2006/09/30 - our frontsector can be the second side of the + * linedef, so must check for NO_INDEX in case we are incorrectly + * referencing the back of a 1S line + */ + if (ldef.sidenum[side] != NO_INDEX) { + li.frontsector = sides[ldef.sidenum[side]].sector; + } else { + li.frontsector = null; + LOGGER.log(Level.INFO, String.format("P_LoadSegs: front of seg %d has no sidedef", i)); + } + + if (flags(ldef.flags, ML_TWOSIDED) && ldef.sidenum[side ^ 1] != NO_INDEX) { + li.backsector = sides[ldef.sidenum[side ^ 1]].sector; + } else { + li.backsector = null; + } + + // e6y + // check and fix wrong references to non-existent vertexes + // see e1m9 @ NIVELES.WAD + // http://www.doomworld.com/idgames/index.php?id=12647 + if (v1 >= numvertexes || v2 >= numvertexes) { + String str = "P_LoadSegs: compatibility loss - seg %d references a non-existent vertex %d\n"; + + if (DOOM.demorecording) { + DOOM.doomSystem.Error( + str + "Demo recording on levels with invalid nodes is not allowed", + i, (v1 >= numvertexes ? v1 : v2) + ); + } + + if (v1 >= numvertexes) { + LOGGER.log(Level.WARNING, String.format(str, i, v1)); + } + if (v2 >= numvertexes) { + LOGGER.log(Level.WARNING, String.format(str, i, v2)); + } + + if (li.sidedef == sides[li.linedef.sidenum[0]]) { + li.v1 = lines[ml.linedef].v1; + li.v2 = lines[ml.linedef].v2; + } else { + li.v1 = lines[ml.linedef].v2; + li.v2 = lines[ml.linedef].v1; + } + } else { + li.v1 = vertexes[v1]; + li.v2 = vertexes[v2]; + } + + li.assignVertexValues(); + + // e6y: now we can calculate it + li.length = GetDistance(li.v2x - li.v1x, li.v2y - li.v1y); + + // Recalculate seg offsets that are sometimes incorrect + // with certain nodebuilders. Fixes among others, line 20365 + // of DV.wad, map 5 + li.offset = GetOffset(li.v1, (ml.side != 0 ? ldef.v2 : ldef.v1)); + } + + DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the data + } + + private void P_LoadSegs_V4(int lump) { + int i; + mapseg_v4_t[] data; + + numsegs = DOOM.wadLoader.LumpLength(lump) / mapseg_v4_t.sizeOf(); + segs = calloc_IfSameLevel(segs, numsegs, seg_t::new, seg_t[]::new); + data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numsegs, mapseg_v4_t::new, mapseg_v4_t[]::new); + + if ((data == null) || (numsegs == 0)) { + DOOM.doomSystem.Error("P_LoadSegs_V4: no segs in level"); + } + + for (i = 0; i < numsegs; i++) { + seg_t li = segs[i]; + final mapseg_v4_t ml = data[i]; + int v1, v2; + + int side, linedef; + line_t ldef; + + li.iSegID = i; // proff 11/05/2000: needed for OpenGL + + v1 = ml.v1; + v2 = ml.v2; + + li.miniseg = false; // figgi -- there are no minisegs in classic BSP + // nodes + + li.angle = ml.angle << 16; + li.offset = ml.offset << 16; + linedef = ml.linedef; + + // e6y: check for wrong indexes + if (unsigned(linedef) >= unsigned(numlines)) { + DOOM.doomSystem.Error( + "P_LoadSegs_V4: seg %d references a non-existent linedef %d", + i, unsigned(linedef)); + } + + ldef = lines[linedef]; + li.linedef = ldef; + side = ml.side; + + // e6y: fix wrong side index + if (side != 0 && side != 1) { + LOGGER.log(Level.WARNING, String.format("P_LoadSegs_V4: seg %d contains wrong side index %d. Replaced with 1.", i, side)); + side = 1; + } + + // e6y: check for wrong indexes + if (unsigned(ldef.sidenum[side]) >= unsigned(numsides)) { + DOOM.doomSystem.Error( + "P_LoadSegs_V4: linedef %d for seg %d references a non-existent sidedef %d", + linedef, i, unsigned(ldef.sidenum[side]) + ); + } + + li.sidedef = sides[ldef.sidenum[side]]; + + /* + * cph 2006/09/30 - our frontsector can be the second side of the + * linedef, so must check for NO_INDEX in case we are incorrectly + * referencing the back of a 1S line + */ + if (ldef.sidenum[side] != NO_INDEX) { + li.frontsector = sides[ldef.sidenum[side]].sector; + } else { + li.frontsector = null; + LOGGER.log(Level.WARNING, String.format("P_LoadSegs_V4: front of seg %d has no sidedef", i)); + } + + if (flags(ldef.flags, ML_TWOSIDED) + && ldef.sidenum[side ^ 1] != NO_INDEX) { + li.backsector = sides[ldef.sidenum[side ^ 1]].sector; + } else { + li.backsector = null; + } + + // e6y + // check and fix wrong references to non-existent vertexes + // see e1m9 @ NIVELES.WAD + // http://www.doomworld.com/idgames/index.php?id=12647 + if (v1 >= numvertexes || v2 >= numvertexes) { + String str = "P_LoadSegs_V4: compatibility loss - seg %d references a non-existent vertex %d\n"; + + if (DOOM.demorecording) { + DOOM.doomSystem.Error( + (str + "Demo recording on levels with invalid nodes is not allowed"), + i, (v1 >= numvertexes ? v1 : v2) + ); + } + + if (v1 >= numvertexes) { + LOGGER.log(Level.WARNING, String.format(str, i, v1)); + } + if (v2 >= numvertexes) { + LOGGER.log(Level.WARNING, String.format(str, i, v2)); + } + + if (li.sidedef == sides[li.linedef.sidenum[0]]) { + li.v1 = lines[ml.linedef].v1; + li.v2 = lines[ml.linedef].v2; + } else { + li.v1 = lines[ml.linedef].v2; + li.v2 = lines[ml.linedef].v1; + } + } else { + li.v1 = vertexes[v1]; + li.v2 = vertexes[v2]; + } + + // e6y: now we can calculate it + li.length = GetDistance(li.v2.x - li.v1.x, li.v2.y - li.v1.y); + + // Recalculate seg offsets that are sometimes incorrect + // with certain nodebuilders. Fixes among others, line 20365 + // of DV.wad, map 5 + li.offset = GetOffset(li.v1, (ml.side != 0 ? ldef.v2 : ldef.v1)); + } + + DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the data + } + + /******************************************* + * Name : P_LoadGLSegs * created : 08/13/00 * modified : 09/18/00, adapted + * for PrBoom * author : figgi * what : support for gl nodes * + *******************************************/ + /* + * private void P_LoadGLSegs(int lump) { int i; final glseg_t ml; line_t + * ldef; numsegs = W.LumpLength(lump) / sizeof(glseg_t); segs = + * malloc_IfSameLevel(segs, numsegs * sizeof(seg_t)); memset(segs, 0, + * numsegs * sizeof(seg_t)); ml = (final glseg_t*)W.CacheLumpNum(lump); if + * ((!ml) || (!numsegs)) I_Error("P_LoadGLSegs: no glsegs in level"); for(i + * = 0; i < numsegs; i++) { // check for gl-vertices segs[i].v1 = + * &vertexes[checkGLVertex(LittleShort(ml.v1))]; segs[i].v2 = + * &vertexes[checkGLVertex(LittleShort(ml.v2))]; segs[i].iSegID = i; + * if(ml.linedef != (unsigned short)-1) // skip minisegs { ldef = + * &lines[ml.linedef]; segs[i].linedef = ldef; segs[i].miniseg = false; + * segs[i].angle = + * R_PointToAngle2(segs[i].v1.x,segs[i].v1.y,segs[i].v2.x,segs[i].v2.y); + * segs[i].sidedef = &sides[ldef.sidenum[ml.side]]; segs[i].length = + * GetDistance(segs[i].v2.x - segs[i].v1.x, segs[i].v2.y - segs[i].v1.y); + * segs[i].frontsector = sides[ldef.sidenum[ml.side]].sector; if (ldef.flags + * & ML_TWOSIDED) segs[i].backsector = + * sides[ldef.sidenum[ml.side^1]].sector; else segs[i].backsector = 0; if + * (ml.side) segs[i].offset = GetOffset(segs[i].v1, ldef.v2); else + * segs[i].offset = GetOffset(segs[i].v1, ldef.v1); } else { segs[i].miniseg + * = true; segs[i].angle = 0; segs[i].offset = 0; segs[i].length = 0; + * segs[i].linedef = NULL; segs[i].sidedef = NULL; segs[i].frontsector = + * NULL; segs[i].backsector = NULL; } ml++; } W.UnlockLumpNum(lump); } + */ + // + // P_LoadSubsectors + // + // killough 5/3/98: reformatted, cleaned up + private void P_LoadSubsectors(int lump) { + /* + * cph 2006/07/29 - make data a final mapsubsector_t *, so the loop + * below is simpler & gives no finalness warnings + */ + final mapsubsector_t[] data; + + numsubsectors = DOOM.wadLoader.LumpLength(lump) / mapsubsector_t.sizeOf(); + subsectors = calloc_IfSameLevel(subsectors, numsubsectors, subsector_t::new, subsector_t[]::new); + data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numsubsectors, mapsubsector_t::new, mapsubsector_t[]::new); + + if ((data == null) || (numsubsectors == 0)) { + DOOM.doomSystem.Error("P_LoadSubsectors: no subsectors in level"); + } + + for (int i = 0; i < numsubsectors; i++) { + // e6y: support for extended nodes + subsectors[i].numlines = data[i].numsegs; + subsectors[i].firstline = data[i].firstseg; + } + + DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the data + } + + private void P_LoadSubsectors_V4(int lump) { + /* + * cph 2006/07/29 - make data a final mapsubsector_t *, so the loop + * below is simpler & gives no finalness warnings + */ + final mapsubsector_v4_t[] data; + + numsubsectors = DOOM.wadLoader.LumpLength(lump) / mapsubsector_v4_t.sizeOf(); + subsectors = calloc_IfSameLevel(subsectors, numsubsectors, subsector_t::new, subsector_t[]::new); + data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numsubsectors, mapsubsector_v4_t::new, mapsubsector_v4_t[]::new); + + if ((data == null) || (numsubsectors == 0)) { + DOOM.doomSystem.Error("P_LoadSubsectors_V4: no subsectors in level"); + } + + for (int i = 0; i < numsubsectors; i++) { + subsectors[i].numlines = data[i].numsegs; + subsectors[i].firstline = data[i].firstseg; + } + + DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the data + } + + // + // P_LoadSectors + // + // killough 5/3/98: reformatted, cleaned up + private void P_LoadSectors(int lump) { + final mapsector_t[] data; // cph - final* + + numsectors = DOOM.wadLoader.LumpLength(lump) / mapsector_t.sizeOf(); + sectors = calloc_IfSameLevel(sectors, numsectors, sector_t::new, sector_t[]::new); + data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numsectors, mapsector_t::new, mapsector_t[]::new); // cph + // - + // wad + // lump + // handling + // updated + + for (int i = 0; i < numsectors; i++) { + sector_t ss = sectors[i]; + final mapsector_t ms = data[i]; + + ss.id = i; // proff 04/05/2000: needed for OpenGL + ss.floorheight = ms.floorheight << FRACBITS; + ss.ceilingheight = ms.ceilingheight << FRACBITS; + ss.floorpic = (short) DOOM.textureManager.FlatNumForName(ms.floorpic); + ss.ceilingpic = (short) DOOM.textureManager.FlatNumForName(ms.ceilingpic); + ss.lightlevel = ms.lightlevel; + ss.special = ms.special; + // ss.oldspecial = ms.special; huh? + ss.tag = ms.tag; + ss.thinglist = null; + // MAES: link to thinker list and RNG + ss.TL = this.DOOM.actions; + ss.RND = this.DOOM.random; + + // ss.touching_thinglist = null; // phares 3/14/98 + // ss.nextsec = -1; //jff 2/26/98 add fields to support locking out + // ss.prevsec = -1; // stair retriggering until build completes + // killough 3/7/98: + // ss.floor_xoffs = 0; + // ss.floor_yoffs = 0; // floor and ceiling flats offsets + // ss.ceiling_xoffs = 0; + // ss.ceiling_yoffs = 0; + // ss.heightsec = -1; // sector used to get floor and ceiling height + // ss.floorlightsec = -1; // sector used to get floor lighting + // killough 3/7/98: end changes + // killough 4/11/98 sector used to get ceiling lighting: + // ss.ceilinglightsec = -1; + // killough 4/4/98: colormaps: + // ss.bottommap = ss.midmap = ss.topmap = 0; + // killough 10/98: sky textures coming from sidedefs: + // ss.sky = 0; + } + + DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the data + } + + // + // P_LoadNodes + // + // killough 5/3/98: reformatted, cleaned up + private void P_LoadNodes(int lump) { + final mapnode_t[] data; // cph - final* + + numnodes = DOOM.wadLoader.LumpLength(lump) / mapnode_t.sizeOf(); + nodes = malloc_IfSameLevel(nodes, numnodes, node_t::new, node_t[]::new); + data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numnodes, mapnode_t::new, mapnode_t[]::new); // cph + // - + // wad + // lump + // handling + // updated + + if ((data == null) || (numnodes == 0)) { + // allow trivial maps + if (numsubsectors == 1) { + LOGGER.log(Level.INFO, "P_LoadNodes: trivial map (no nodes, one subsector)"); + } else { + DOOM.doomSystem.Error("P_LoadNodes: no nodes in level"); + } + } + + for (int i = 0; i < numnodes; i++) { + node_t no = nodes[i]; + final mapnode_t mn = data[i]; + + no.x = mn.x << FRACBITS; + no.y = mn.y << FRACBITS; + no.dx = mn.dx << FRACBITS; + no.dy = mn.dy << FRACBITS; + + for (int j = 0; j < 2; j++) { + // e6y: support for extended nodes + no.children[j] = mn.children[j]; + + // e6y: support for extended nodes + if (no.children[j] == 0xFFFF) { + no.children[j] = 0xFFFFFFFF; + } else if (flags(no.children[j], NF_SUBSECTOR_CLASSIC)) { + // Convert to extended type + no.children[j] &= ~NF_SUBSECTOR_CLASSIC; + + // haleyjd 11/06/10: check for invalid subsector reference + if (no.children[j] >= numsubsectors) { + LOGGER.log(Level.WARNING, String.format("P_LoadNodes: BSP tree references invalid subsector %d.", no.children[j])); + no.children[j] = 0; + } + + no.children[j] |= NF_SUBSECTOR; + } + + for (int k = 0; k < 4; k++) { + no.bbox[j].set(k, mn.bbox[j][k] << FRACBITS); + } + } + } + + DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the data + } + + private void P_LoadNodes_V4(int lump) { + final DeepBSPNodesV4 data; // cph - final* + + numnodes = (DOOM.wadLoader.LumpLength(lump) - 8) / mapnode_v4_t.sizeOf(); + nodes = malloc_IfSameLevel(nodes, numnodes, node_t::new, node_t[]::new); + data = DOOM.wadLoader.CacheLumpNum(lump, 0, DeepBSPNodesV4.class); // cph + // - + // wad + // lump + // handling + // updated + + if ((data == null) || (numnodes == 0)) { + // allow trivial maps + if (numsubsectors == 1) { + LOGGER.log(Level.INFO, "P_LoadNodes_V4: trivial map (no nodes, one subsector)\n"); + } else { + DOOM.doomSystem.Error("P_LoadNodes_V4: no nodes in level"); + } + } + + for (int i = 0; i < numnodes; i++) { + node_t no = nodes[i]; + final mapnode_v4_t mn = data.getNodes()[i]; + + no.x = mn.x << FRACBITS; + no.y = mn.y << FRACBITS; + no.dx = mn.dx << FRACBITS; + no.dy = mn.dy << FRACBITS; + + for (int j = 0; j < 2; j++) { + no.children[j] = mn.children[j]; + + for (int k = 0; k < 4; k++) { + no.bbox[j].bbox[k] = mn.bbox[j][k] << FRACBITS; + } + } + } + + DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the data + } + + private void P_LoadZSegs(ByteBuffer data) throws IOException { + final mapseg_znod_t nodes[] = malloc(mapseg_znod_t::new, mapseg_znod_t[]::new, numsegs); + CacheableDoomObjectContainer.unpack(data, nodes); + + for (int i = 0; i < numsegs; i++) { + line_t ldef; + int v1, v2; + int linedef; + char side; + seg_t li = segs[i]; + final mapseg_znod_t ml = nodes[i]; + + v1 = ml.v1; + v2 = ml.v2; + + li.iSegID = i; // proff 11/05/2000: needed for OpenGL + li.miniseg = false; + + linedef = ml.linedef; + + // e6y: check for wrong indexes + if (unsigned(linedef) >= unsigned(numlines)) { + DOOM.doomSystem.Error( + "P_LoadZSegs: seg %d references a non-existent linedef %d", + i, unsigned(linedef) + ); + } + + ldef = lines[linedef]; + li.linedef = ldef; + side = (char) ml.side; + + // e6y: fix wrong side index + if (side != 0 && side != 1) { + LOGGER.log(Level.WARNING, String.format("P_LoadZSegs: seg %d contains wrong side index %d. Replaced with 1.", i, (int) side)); + side = 1; + } + + // e6y: check for wrong indexes + if (unsigned(ldef.sidenum[side]) >= unsigned(numsides)) { + DOOM.doomSystem.Error( + "P_LoadZSegs: linedef %d for seg %d references a non-existent sidedef %d", + linedef, i, unsigned(ldef.sidenum[side]) + ); + } + + li.sidedef = sides[ldef.sidenum[side]]; + + /* + * cph 2006/09/30 - our frontsector can be the second side of the + * linedef, so must check for NO_INDEX in case we are incorrectly + * referencing the back of a 1S line + */ + if (ldef.sidenum[side] != NO_INDEX) { + li.frontsector = sides[ldef.sidenum[side]].sector; + } else { + li.frontsector = null; + LOGGER.log(Level.WARNING, String.format("P_LoadZSegs: front of seg %d has no sidedef", i)); + } + + if (flags(ldef.flags, ML_TWOSIDED) && (ldef.sidenum[side ^ 1] != NO_INDEX)) { + li.backsector = sides[ldef.sidenum[side ^ 1]].sector; + } else { + li.backsector = null; + } + + li.v1 = vertexes[v1]; + li.v2 = vertexes[v2]; + + li.length = GetDistance(li.v2.x - li.v1.x, li.v2.y - li.v1.y); + li.offset = GetOffset(li.v1, (side != 0 ? ldef.v2 : ldef.v1)); + li.angle = RendererState.PointToAngle(segs[i].v1.x, segs[i].v1.y, segs[i].v2.x, segs[i].v2.y); + // li.angle = (int)((float)atan2(li.v2.y - li.v1.y,li.v2.x - + // li.v1.x) * (ANG180 / M_PI)); + } + } + + private int CheckZNodesOverflow(int size, int count) { + size -= count; + + if (size < 0) { + DOOM.doomSystem.Error("P_LoadZNodes: incorrect nodes"); + } + + return size; + } + + private void P_LoadZNodes(int lump, int glnodes) throws IOException { + ByteBuffer data; + int len; + int header; // for debugging + + int orgVerts, newVerts; + int numSubs, currSeg; + int numSegs; + int numNodes; + vertex_t[] newvertarray = null; + + data = DOOM.wadLoader.CacheLumpNumAsDoomBuffer(lump).getBuffer(); + data.order(ByteOrder.LITTLE_ENDIAN); + len = DOOM.wadLoader.LumpLength(lump); + + // skip header + len = CheckZNodesOverflow(len, 4); + header = data.getInt(); + + // Read extra vertices added during node building + len = CheckZNodesOverflow(len, 4); + orgVerts = data.getInt(); + + len = CheckZNodesOverflow(len, 4); + newVerts = data.getInt(); + + if (!samelevel) { + if (orgVerts + newVerts == numvertexes) { + newvertarray = vertexes; + } else { + newvertarray = new vertex_t[orgVerts + newVerts]; + // TODO: avoid creating new objects that will be rewritten instantly - Good Sign 2017/05/07 + Arrays.setAll(newvertarray, ii -> new vertex_t()); + System.arraycopy(vertexes, 0, newvertarray, 0, orgVerts); + } + + //(sizeof(newvertarray[0].x) + sizeof(newvertarray[0].y)) + len = CheckZNodesOverflow(len, newVerts * vertex_t.sizeOf()); + z_vertex_t tmp = new z_vertex_t(); + + for (int i = 0; i < newVerts; i++) { + tmp.unpack(data); + newvertarray[i + orgVerts].x = tmp.x; + newvertarray[i + orgVerts].y = tmp.y; + } + + // Extra vertexes read in + if (vertexes != newvertarray) { + for (int i = 0; i < numlines; i++) { + //lines[i].v1 = lines[i].v1 - vertexes + newvertarray; + //lines[i].v2 = lines[i].v2 - vertexes + newvertarray; + // Find indexes of v1 & v2 inside old vertexes array + // (.v1-vertexes) and use that index to re-point inside newvertarray + lines[i].v1 = newvertarray[C2JUtils.indexOf(vertexes, lines[i].v1)]; + lines[i].v2 = newvertarray[C2JUtils.indexOf(vertexes, lines[i].v2)]; + } + // free(vertexes); + vertexes = newvertarray; + numvertexes = orgVerts + newVerts; + } + } else { + // Skip the reading of all these new vertices and the expensive indexOf searches. + int size = newVerts * z_vertex_t.sizeOf(); + len = CheckZNodesOverflow(len, size); + data.position(data.position() + size); + } + + // Read the subsectors + len = CheckZNodesOverflow(len, 4); + numSubs = data.getInt(); + + numsubsectors = numSubs; + if (numsubsectors <= 0) { + DOOM.doomSystem.Error("P_LoadZNodes: no subsectors in level"); + } + subsectors = calloc_IfSameLevel(subsectors, numsubsectors, subsector_t::new, subsector_t[]::new); + + len = CheckZNodesOverflow(len, numSubs * mapsubsector_znod_t.sizeOf()); + final mapsubsector_znod_t mseg = new mapsubsector_znod_t(); + for (int i = currSeg = 0; i < numSubs; i++) { + mseg.unpack(data); + + subsectors[i].firstline = currSeg; + subsectors[i].numlines = (int) mseg.numsegs; + currSeg += mseg.numsegs; + } + + // Read the segs + len = CheckZNodesOverflow(len, 4); + numSegs = data.getInt(); + + // The number of segs stored should match the number of + // segs used by subsectors. + if (numSegs != currSeg) { + DOOM.doomSystem.Error("P_LoadZNodes: Incorrect number of segs in nodes."); + } + + numsegs = numSegs; + segs = calloc_IfSameLevel(segs, numsegs, seg_t::new, seg_t[]::new); + + if (glnodes == 0) { + len = CheckZNodesOverflow(len, numsegs * mapseg_znod_t.sizeOf()); + P_LoadZSegs(data); + } else { + //P_LoadGLZSegs (data, glnodes); + DOOM.doomSystem.Error("P_LoadZNodes: GL segs are not supported."); + } + + // Read nodes + len = CheckZNodesOverflow(len, 4); + numNodes = data.getInt(); + + numnodes = numNodes; + nodes = calloc_IfSameLevel(nodes, numNodes, node_t::new, node_t[]::new); + + len = CheckZNodesOverflow(len, numNodes * mapnode_znod_t.sizeOf()); + + mapnode_znod_t znodes[] = malloc(mapnode_znod_t::new, mapnode_znod_t[]::new, numNodes); + CacheableDoomObjectContainer.unpack(data, znodes); + + for (int i = 0; i < numNodes; i++) { + int j, k; + node_t no = nodes[i]; + final mapnode_znod_t mn = znodes[i]; + + no.x = mn.x << FRACBITS; + no.y = mn.y << FRACBITS; + no.dx = mn.dx << FRACBITS; + no.dy = mn.dy << FRACBITS; + + for (j = 0; j < 2; j++) { + no.children[j] = mn.children[j]; + + for (k = 0; k < 4; k++) { + no.bbox[j].bbox[k] = mn.bbox[j][k] << FRACBITS; + } + } + } + + DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the data + } + + private boolean no_overlapped_sprites; + + private int GETXY(mobj_t mobj) { + return (mobj.x + (mobj.y >> 16)); + } + + private int dicmp_sprite_by_pos(final Object a, final Object b) { + mobj_t m1 = (mobj_t) a; + mobj_t m2 = (mobj_t) b; + + int res = GETXY(m2) - GETXY(m1); + no_overlapped_sprites = no_overlapped_sprites && (res != 0); + return res; + } + + /* + * P_LoadThings killough 5/3/98: reformatted, cleaned up cph 2001/07/07 - + * don't write into the lump cache, especially non-idepotent changes like + * byte order reversals. Take a copy to edit. + */ + @SourceCode.Suspicious(CauseOfDesyncProbability.LOW) + @P_Setup.C(P_LoadThings) + private void P_LoadThings(int lump) { + int numthings = DOOM.wadLoader.LumpLength(lump) / mapthing_t.sizeOf(); + final mapthing_t[] data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numthings, mapthing_t::new, mapthing_t[]::new); + + mobj_t mobj; + int mobjcount = 0; + mobj_t[] mobjlist = new mobj_t[numthings]; + Arrays.setAll(mobjlist, j -> mobj_t.createOn(DOOM)); + + if ((data == null) || (numthings == 0)) { + DOOM.doomSystem.Error("P_LoadThings: no things in level"); + } + + for (int i = 0; i < numthings; i++) { + mapthing_t mt = data[i]; + + /* + * Not needed. Handled during unmarshaling. mt.x = + * LittleShort(mt.x); mt.y = LittleShort(mt.y); mt.angle = + * LittleShort(mt.angle); mt.type = LittleShort(mt.type); mt.options + * = LittleShort(mt.options); + */ + if (!P_IsDoomnumAllowed(mt.type)) { + continue; + } + + // Do spawn all other stuff. + mobj = DOOM.actions.SpawnMapThing(mt/* , i */); + if (mobj != null && mobj.info.speed == 0) { + mobjlist[mobjcount++] = mobj; + } + } + + DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the data + /* + * #ifdef GL_DOOM if (V_GetMode() == VID_MODEGL) { no_overlapped_sprites + * = true; qsort(mobjlist, mobjcount, sizeof(mobjlist[0]), + * dicmp_sprite_by_pos); if (!no_overlapped_sprites) { i = 1; while (i < + * mobjcount) { mobj_t *m1 = mobjlist[i - 1]; mobj_t *m2 = mobjlist[i - + * 0]; if (GETXY(m1) == GETXY(m2)) { mobj_t *mo = (m1.index < m2.index ? + * m1 : m2); i++; while (i < mobjcount && GETXY(mobjlist[i]) == + * GETXY(m1)) { if (mobjlist[i].index < mo.index) { mo = mobjlist[i]; } + * i++; } // 'nearest' mo.flags |= MF_FOREGROUND; } i++; } } } #endif + */ + + } + + /* + * P_IsDoomnumAllowed() Based on code taken from P_LoadThings() in + * src/p_setup.c Return TRUE if the thing in question is expected to be + * available in the gamemode used. + */ + boolean P_IsDoomnumAllowed(int doomnum) { + // Do not spawn cool, new monsters if !commercial + if (!DOOM.isCommercial()) { + switch (doomnum) { + case 64: // Archvile + case 65: // Former Human Commando + case 66: // Revenant + case 67: // Mancubus + case 68: // Arachnotron + case 69: // Hell Knight + case 71: // Pain Elemental + case 84: // Wolf SS + case 88: // Boss Brain + case 89: // Boss Shooter + return false; + } + } + + return true; + } + + // + // P_LoadLineDefs + // Also counts secret lines for intermissions. + // ^^^ + // ??? killough ??? + // Does this mean secrets used to be linedef-based, rather than + // sector-based? + // + // killough 4/4/98: split into two functions, to allow sidedef overloading + // + // killough 5/3/98: reformatted, cleaned up + private void P_LoadLineDefs(int lump) { + final maplinedef_t[] data; // cph - final* + + numlines = DOOM.wadLoader.LumpLength(lump) / maplinedef_t.sizeOf(); + lines = calloc_IfSameLevel(lines, numlines, line_t::new, line_t[]::new); + data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numlines, maplinedef_t::new, maplinedef_t[]::new); // cph + // - + // wad + // lump + // handling + // updated + + for (int i = 0; i < numlines; i++) { + final maplinedef_t mld = data[i]; + line_t ld = lines[i]; + ld.id = i; + vertex_t v1, v2; + + ld.flags = mld.flags; + ld.special = mld.special; + ld.tag = mld.tag; + v1 = ld.v1 = vertexes[mld.v1]; + v2 = ld.v2 = vertexes[mld.v2]; + ld.dx = v2.x - v1.x; + ld.dy = v2.y - v1.y; + // Maes: map value semantics. + ld.assignVertexValues(); + + /* + * #ifdef GL_DOOM // e6y // Rounding the wall length to the nearest + * integer // when determining length instead of always rounding + * down // There is no more glitches on seams between identical + * textures. ld.texel_length = GetTexelDistance(ld.dx, ld.dy); + * #endif + */ + ld.tranlump = -1; // killough 4/11/98: no translucency by default + + ld.slopetype = (ld.dx == 0) + ? slopetype_t.ST_VERTICAL + : (ld.dy == 0) + ? slopetype_t.ST_HORIZONTAL + : fixed_t.FixedDiv(ld.dy, ld.dx) > 0 + ? slopetype_t.ST_POSITIVE + : slopetype_t.ST_NEGATIVE; + + if (v1.x < v2.x) { + ld.bbox[BBox.BOXLEFT] = v1.x; + ld.bbox[BBox.BOXRIGHT] = v2.x; + } else { + ld.bbox[BBox.BOXLEFT] = v2.x; + ld.bbox[BBox.BOXRIGHT] = v1.x; + } + if (v1.y < v2.y) { + ld.bbox[BBox.BOXBOTTOM] = v1.y; + ld.bbox[BBox.BOXTOP] = v2.y; + } else { + ld.bbox[BBox.BOXBOTTOM] = v2.y; + ld.bbox[BBox.BOXTOP] = v1.y; + } + + /* calculate sound origin of line to be its midpoint */ + // e6y: fix sound origin for large levels + // no need for comp_sound test, these are only used when comp_sound + // = 0 + ld.soundorg = new degenmobj_t( + ld.bbox[BBox.BOXLEFT] / 2 + + ld.bbox[BBox.BOXRIGHT] / 2, ld.bbox[BBox.BOXTOP] / 2 + + ld.bbox[BBox.BOXBOTTOM] / 2, 0 + ); + + // TODO + // ld.iLineID=i; // proff 04/05/2000: needed for OpenGL + ld.sidenum[0] = mld.sidenum[0]; + ld.sidenum[1] = mld.sidenum[1]; + + { + /* + * cph 2006/09/30 - fix sidedef errors right away. cph + * 2002/07/20 - these errors are fatal if not fixed, so apply + * them in compatibility mode - a desync is better than a crash! + */ + for (int j = 0; j < 2; j++) { + if (ld.sidenum[j] != NO_INDEX && ld.sidenum[j] >= numsides) { + ld.sidenum[j] = NO_INDEX; + LOGGER.log(Level.WARNING, String.format( + "P_LoadLineDefs: linedef %d has out-of-range sidedef number", + numlines - i - 1) + ); + } + } + + // killough 11/98: fix common wad errors (missing sidedefs): + if (ld.sidenum[0] == NO_INDEX) { + ld.sidenum[0] = 0; // Substitute dummy sidedef for missing + // right side + // cph - print a warning about the bug + LOGGER.log(Level.WARNING, String.format("P_LoadLineDefs: linedef %d missing first sidedef\n", numlines - i - 1)); + } + + if ((ld.sidenum[1] == NO_INDEX) && flags(ld.flags, ML_TWOSIDED)) { + // e6y + // ML_TWOSIDED flag shouldn't be cleared for compatibility + // purposes + // see CLNJ-506.LMP at http://doomedsda.us/wad1005.html + // TODO: we don't really care, but still... + // if (!demo_compatibility || + // !overflows[OVERFLOW.MISSEDBACKSIDE].emulate) + // { + ld.flags &= ~ML_TWOSIDED; // Clear 2s flag for missing left + // side + // } + // Mark such lines and do not draw them only in + // demo_compatibility, + // because Boom's behaviour is different + // See OTTAWAU.WAD E1M1, sectors 226 and 300 + // http://www.doomworld.com/idgames/index.php?id=1651 + // TODO ehhh? + // ld.r_flags = RF_IGNORE_COMPAT; + // cph - print a warning about the bug + LOGGER.log(Level.WARNING, String.format( + "P_LoadLineDefs: linedef %d has two-sided flag set, but no second sidedef\n", + numlines - i - 1) + ); + } + } + + // killough 4/4/98: support special sidedef interpretation below + // TODO: + // if (ld.sidenum[0] != NO_INDEX && ld.special!=0) + // sides[(ld.sidenum[0]<<16)& (0x0000FFFF&ld.sidenum[1])].special = + // ld.special; + } + + DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the lump + } + + // killough 4/4/98: delay using sidedefs until they are loaded + // killough 5/3/98: reformatted, cleaned up + private void P_LoadLineDefs2(int lump) { + line_t ld; + + for (int i = 0; i < numlines; i++) { + ld = lines[i]; + ld.frontsector = sides[ld.sidenum[0]].sector; // e6y: Can't be + // NO_INDEX here + ld.backsector + = ld.sidenum[1] != NO_INDEX ? sides[ld.sidenum[1]].sector : null; + switch (ld.special) { // killough 4/11/98: handle special types + case 260: // killough 4/11/98: translucent 2s textures + // TODO: transparentpresent = true;//e6y + // int lmp = sides[ld.getSpecialSidenum()].special; // + // translucency from sidedef + // if (!ld.tag) // if tag==0, + // ld.tranlump = lmp; // affect this linedef only + // else + // for (int j=0;j= numsectors) { + LOGGER.log(Level.WARNING, String.format("P_LoadSideDefs2: sidedef %d has out-of-range sector num %d", i, (int) sector_num)); + sector_num = 0; + } + sd.sector = sec = sectors[sector_num]; + } + + // killough 4/4/98: allow sidedef texture names to be overloaded + // killough 4/11/98: refined to allow colormaps to work as wall + // textures if invalid as colormaps but valid as textures. + switch (sd.special) { + case 242: // variable colormap via 242 linedef + /* + * sd.bottomtexture = (sec.bottommap = + * R.ColormapNumForName(msd.bottomtexture)) < 0 ? sec.bottommap + * = 0, R.TextureNumForName(msd.bottomtexture): 0 ; + * sd.midtexture = (sec.midmap = + * R.ColormapNumForName(msd.midtexture)) < 0 ? sec.midmap = 0, + * R.TextureNumForName(msd.midtexture) : 0 ; sd.toptexture = + * (sec.topmap = R.ColormapNumForName(msd.toptexture)) < 0 ? + * sec.topmap = 0, R.TextureNumForName(msd.toptexture) : 0 ; + */ + + break; + + case 260: // killough 4/11/98: apply translucency to 2s normal texture + if (msd.midtexture.compareToIgnoreCase("TRANMAP") == 0) { + if ((sd.special = DOOM.wadLoader.CheckNumForName(msd.midtexture)) < 0 + || DOOM.wadLoader.LumpLength(sd.special) != 65536) { + sd.special = 0; + sd.midtexture = (short) DOOM.textureManager.TextureNumForName(msd.midtexture); + } else { + sd.special++; + sd.midtexture = 0; + } + } else { + sd.midtexture = (short) (sd.special = 0); + } + sd.toptexture = (short) DOOM.textureManager.TextureNumForName(msd.toptexture); + sd.bottomtexture = (short) DOOM.textureManager.TextureNumForName(msd.bottomtexture); + break; + + /* + * #ifdef GL_DOOM case 271: case 272: if + * (R_CheckTextureNumForName(msd.toptexture) == -1) { + * sd.skybox_index = R_BoxSkyboxNumForName(msd.toptexture); } #endif + */ + default: // normal cases + // TODO: Boom uses "SafeTextureNumForName" here. Find out what + // it does. + sd.midtexture = (short) DOOM.textureManager.CheckTextureNumForName(msd.midtexture); + sd.toptexture = (short) DOOM.textureManager.CheckTextureNumForName(msd.toptexture); + sd.bottomtexture = (short) DOOM.textureManager.CheckTextureNumForName(msd.bottomtexture); + break; + } + } + + DOOM.wadLoader.UnlockLumpNum(lump); // cph - release the lump + } + + // + // P_LoadBlockMap + // + // killough 3/1/98: substantially modified to work + // towards removing blockmap limit (a wad limitation) + // + // killough 3/30/98: Rewritten to remove blockmap limit, + // though current algorithm is brute-force and unoptimal. + // + private void P_LoadBlockMap(int lump) throws IOException { + int count = 0; + + if (DOOM.cVarManager.bool(CommandVariable.BLOCKMAP) + || DOOM.wadLoader.LumpLength(lump) < 8 + || (count = DOOM.wadLoader.LumpLength(lump) / 2) >= 0x10000) // e6y + { + CreateBlockMap(); + } else { + // cph - final*, wad lump handling updated + final char[] wadblockmaplump; + + DoomBuffer data = DOOM.wadLoader.CacheLumpNum(lump, PU_LEVEL, DoomBuffer.class); + count = DOOM.wadLoader.LumpLength(lump) / 2; + wadblockmaplump = new char[count]; + + data.setOrder(ByteOrder.LITTLE_ENDIAN); + data.rewind(); + data.readCharArray(wadblockmaplump, count); + + if (!samelevel) // Reallocate if required. + { + blockmaplump = new int[count]; + } + + // killough 3/1/98: Expand wad blockmap into larger internal one, + // by treating all offsets except -1 as unsigned and zero-extending + // them. This potentially doubles the size of blockmaps allowed, + // because Doom originally considered the offsets as always signed. + blockmaplump[0] = wadblockmaplump[0]; + blockmaplump[1] = wadblockmaplump[1]; + blockmaplump[2] = wadblockmaplump[2] & 0xffff; + blockmaplump[3] = wadblockmaplump[3] & 0xffff; + + for (int i = 4; i < count; i++) { + short t = (short) wadblockmaplump[i]; // killough 3/1/98 + blockmaplump[i] = (int) (t == -1 ? -1l : t & 0xffff); + } + + DOOM.wadLoader.UnlockLumpNum(lump); // cph - unlock the lump + + bmaporgx = blockmaplump[0] << FRACBITS; + bmaporgy = blockmaplump[1] << FRACBITS; + bmapwidth = blockmaplump[2]; + bmapheight = blockmaplump[3]; + + // haleyjd 03/04/10: check for blockmap problems + // http://www.doomworld.com/idgames/index.php?id=12935 + if (!VerifyBlockMap(count)) { + LOGGER.log(Level.WARNING, String.format("P_LoadBlockMap: erroneous BLOCKMAP lump may cause crashes.")); + LOGGER.log(Level.WARNING, String.format("P_LoadBlockMap: use \"-blockmap\" command line switch for rebuilding")); + } + } + + // MAES: blockmap was generated, rather than loaded. + if (count == 0) { + count = blockmaplump.length - 4; + } + + // clear out mobj chains - CPhipps - use calloc + // blocklinks = calloc_IfSameLevel(blocklinks, bmapwidth * + // bmapheight.mobj_t.); + // clear out mobj chains + // ATTENTION! BUG!!! + // If blocklinks are "cleared" to void -but instantiated- objects, + // very bad bugs happen, especially the second time a level is + // re-instantiated. + // Probably caused other bugs as well, as an extra object would appear + // in iterators. + if (blocklinks != null && samelevel) { + for (int i = 0; i < bmapwidth * bmapheight; i++) { + blocklinks[i] = null; + } + } else { + blocklinks = new mobj_t[bmapwidth * bmapheight]; + } + + // IMPORTANT MODIFICATION: no need to have both blockmaplump AND + // blockmap. + // If the offsets in the lump are OK, then we can modify them (remove 4) + // and copy the rest of the data in one single data array. This avoids + // reserving memory for two arrays (we can't simply alias one in Java) + blockmap = new int[blockmaplump.length - 4]; + count = bmapwidth * bmapheight; + // Offsets are relative to START OF BLOCKMAP, and IN SHORTS, not bytes. + for (int i = 0; i < blockmaplump.length - 4; i++) { + // Modify indexes so that we don't need two different lumps. + // Can probably be further optimized if we simply shift everything + // backwards. + // and reuse the same memory space. + if (i < count) { + blockmaplump[i] = blockmaplump[i + 4] - 4; + } else { + blockmaplump[i] = blockmaplump[i + 4]; + } + } + + // MAES: set blockmapxneg and blockmapyneg + // E.g. for a full 512x512 map, they should be both + // -1. For a 257*257, they should be both -255 etc. + if (bmapwidth > 255) { + blockmapxneg = bmapwidth - 512; + } + if (bmapheight > 255) { + blockmapyneg = bmapheight - 512; + } + + blockmap = blockmaplump; + + } + + // + // P_LoadReject - load the reject table + // + private void P_LoadReject(int lumpnum, int totallines) { + // dump any old cached reject lump, then cache the new one + if (rejectlump != -1) { + DOOM.wadLoader.UnlockLumpNum(rejectlump); + } + rejectlump = lumpnum + ML_REJECT; + rejectmatrix = DOOM.wadLoader.CacheLumpNumAsRawBytes(rejectlump, 0); + + // e6y: check for overflow + // TODO: g.Overflow.RejectOverrun(rejectlump, rejectmatrix, + // totallines,numsectors); + } + + // + // P_GroupLines + // Builds sector line lists and subsector sector numbers. + // Finds block bounding boxes for sectors. + // + // killough 5/3/98: reformatted, cleaned up + // cph 18/8/99: rewritten to avoid O(numlines * numsectors) section + // It makes things more complicated, but saves seconds on big levels + // figgi 09/18/00 -- adapted for gl-nodes + // modified to return totallines (needed by P_LoadReject) + private int P_GroupLines() { + line_t li; + sector_t sector; + int total = numlines; + + // figgi + for (int i = 0; i < numsubsectors; i++) { + int seg = subsectors[i].firstline; + subsectors[i].sector = null; + for (int j = 0; j < subsectors[i].numlines; j++) { + if (segs[seg].sidedef != null) { + subsectors[i].sector = segs[seg].sidedef.sector; + break; + } + seg++; + } + if (subsectors[i].sector == null) { + DOOM.doomSystem.Error("P_GroupLines: Subsector a part of no sector!\n"); + } + } + + // count number of lines in each sector + for (int i = 0; i < numlines; i++) { + li = lines[i]; + li.frontsector.linecount++; + if (li.backsector != null && (li.backsector != li.frontsector)) { + li.backsector.linecount++; + total++; + } + } + + // allocate line tables for each sector + // e6y: REJECT overrun emulation code + // moved to P_LoadReject + for (int i = 0; i < numsectors; i++) { + sector = sectors[i]; + sector.lines = malloc(line_t::new, line_t[]::new, sector.linecount); + // linebuffer += sector.linecount; + sector.linecount = 0; + BBox.ClearBox(sector.blockbox); + } + + // Enter those lines + for (int i = 0; i < numlines; i++) { + li = lines[i]; + AddLineToSector(li, li.frontsector); + if (li.backsector != null && li.backsector != li.frontsector) { + AddLineToSector(li, li.backsector); + } + } + + for (int i = 0; i < numsectors; i++) { + sector = sectors[i]; + int[] bbox = sector.blockbox; // cph - For convenience, so + // I can sue the old code unchanged + int block; + + // set the degenmobj_t to the middle of the bounding box + // TODO + if (true/* comp[comp_sound] */) { + sector.soundorg = new degenmobj_t((bbox[BOXRIGHT] + bbox[BOXLEFT]) / 2, (bbox[BOXTOP] + bbox[BOXBOTTOM]) / 2); + } else { + // e6y: fix sound origin for large levels + sector.soundorg = new degenmobj_t((bbox[BOXRIGHT] / 2 + bbox[BOXLEFT] / 2), bbox[BOXTOP] / 2 + bbox[BOXBOTTOM] / 2); + } + + // adjust bounding box to map blocks + block = getSafeBlockY(bbox[BOXTOP] - bmaporgy + Limits.MAXRADIUS); + block = block >= bmapheight ? bmapheight - 1 : block; + sector.blockbox[BOXTOP] = block; + + block = getSafeBlockY(bbox[BOXBOTTOM] - bmaporgy - Limits.MAXRADIUS); + block = block < 0 ? 0 : block; + sector.blockbox[BOXBOTTOM] = block; + + block = getSafeBlockX(bbox[BOXRIGHT] - bmaporgx + Limits.MAXRADIUS); + block = block >= bmapwidth ? bmapwidth - 1 : block; + sector.blockbox[BOXRIGHT] = block; + + block = getSafeBlockX(bbox[BOXLEFT] - bmaporgx - Limits.MAXRADIUS); + block = block < 0 ? 0 : block; + sector.blockbox[BOXLEFT] = block; + } + + return total; // this value is needed by the reject overrun emulation + // code + } + + // + // killough 10/98 + // + // Remove slime trails. + // + // Slime trails are inherent to Doom's coordinate system -- i.e. there is + // nothing that a node builder can do to prevent slime trails ALL of the + // time, + // because it's a product of the integer coodinate system, and just because + // two lines pass through exact integer coordinates, doesn't necessarily + // mean + // that they will intersect at integer coordinates. Thus we must allow for + // fractional coordinates if we are to be able to split segs with node + // lines, + // as a node builder must do when creating a BSP tree. + // + // A wad file does not allow fractional coordinates, so node builders are + // out + // of luck except that they can try to limit the number of splits (they + // might + // also be able to detect the degree of roundoff error and try to avoid + // splits + // with a high degree of roundoff error). But we can use fractional + // coordinates + // here, inside the engine. It's like the difference between square inches + // and + // square miles, in terms of granularity. + // + // For each vertex of every seg, check to see whether it's also a vertex of + // the linedef associated with the seg (i.e, it's an endpoint). If it's not + // an endpoint, and it wasn't already moved, move the vertex towards the + // linedef by projecting it using the law of cosines. Formula: + // + // 2 2 2 2 + // dx x0 + dy x1 + dx dy (y0 - y1) dy y0 + dx y1 + dx dy (x0 - x1) + // {---------------------------------, ---------------------------------} + // 2 2 2 2 + // dx + dy dx + dy + // + // (x0,y0) is the vertex being moved, and (x1,y1)-(x1+dx,y1+dy) is the + // reference linedef. + // + // Segs corresponding to orthogonal linedefs (exactly vertical or horizontal + // linedefs), which comprise at least half of all linedefs in most wads, + // don't + // need to be considered, because they almost never contribute to slime + // trails + // (because then any roundoff error is parallel to the linedef, which + // doesn't + // cause slime). Skipping simple orthogonal lines lets the code finish + // quicker. + // + // Please note: This section of code is not interchangable with TeamTNT's + // code which attempts to fix the same problem. + // + // Firelines (TM) is a Rezistered Trademark of MBF Productions + // + private void P_RemoveSlimeTrails() { // killough 10/98 + // Hitlist for vertices + boolean[] hit = new boolean[numvertexes]; + + // Searchlist for + for (int i = 0; i < numsegs; i++) { // Go through each seg + final line_t l; + + if (segs[i].miniseg == true) { // figgi -- skip minisegs + return; + } + + l = segs[i].linedef; // The parent linedef + if (l.dx != 0 && l.dy != 0) { // We can ignore orthogonal lines + vertex_t v = segs[i].v1; + do { + int index = C2JUtils.indexOf(vertexes, v); + if (!hit[index]) { // If we haven't processed vertex + hit[index] = true; // Mark this vertex as processed + if (v != l.v1 && v != l.v2) { // Exclude endpoints of linedefs + // Project the vertex back onto the parent linedef + long dx2 = (l.dx >> FRACBITS) * (l.dx >> FRACBITS); + long dy2 = (l.dy >> FRACBITS) * (l.dy >> FRACBITS); + long dxy = (l.dx >> FRACBITS) * (l.dy >> FRACBITS); + long s = dx2 + dy2; + int x0 = v.x, y0 = v.y, x1 = l.v1.x, y1 = l.v1.y; + v.x = (int) ((dx2 * x0 + dy2 * x1 + dxy * (y0 - y1)) / s); + v.y = (int) ((dy2 * y0 + dx2 * y1 + dxy * (x0 - x1)) / s); + } + } // Obsfucated C contest entry: :) + } while ((v != segs[i].v2) && ((v = segs[i].v2) != null)); + } + // Assign modified vertex values. + l.assignVertexValues(); + } + } + + // + // P_CheckLumpsForSameSource + // + // Are these lumps in the same wad file? + // + boolean P_CheckLumpsForSameSource(int lump1, int lump2) { + int wad1_index, wad2_index; + wadfile_info_t wad1, wad2; + + if ((unsigned(lump1) >= unsigned(DOOM.wadLoader.NumLumps())) + || (unsigned(lump2) >= unsigned(DOOM.wadLoader.NumLumps()))) { + return false; + } + + wad1 = DOOM.wadLoader.GetLumpInfo(lump1).wadfile; + wad2 = DOOM.wadLoader.GetLumpInfo(lump2).wadfile; + + if (wad1 == null || wad2 == null) { + return false; + } + + wad1_index = DOOM.wadLoader.GetWadfileIndex(wad1); + wad2_index = DOOM.wadLoader.GetWadfileIndex(wad2); + + if (wad1_index != wad2_index) { + return false; + } + + if ((wad1_index < 0) || (wad1_index >= DOOM.wadLoader.GetNumWadfiles())) { + return false; + } + + return !((wad2_index < 0) || (wad2_index >= DOOM.wadLoader.GetNumWadfiles())); + } + + private static final String[] ml_labels = { + "ML_LABEL", // A separator, name, ExMx or MAPxx + "ML_THINGS", // Monsters, items.. + "ML_LINEDEFS", // LineDefs, from editing + "ML_SIDEDEFS", // SideDefs, from editing + "ML_VERTEXES", // Vertices, edited and BSP splits generated + "ML_SEGS", // LineSegs, from LineDefs split by BSP + "ML_SSECTORS", // SubSectors, list of LineSegs + "ML_NODES", // BSP nodes + "ML_SECTORS", // Sectors, from editing + "ML_REJECT", // LUT, sector-sector visibility + "ML_BLOCKMAP", // LUT, motion clipping, walls/grid element + }; + + private static final boolean GL_DOOM = false; + + // + // P_CheckLevelFormat + // + // Checking for presence of necessary lumps + // + void P_CheckLevelWadStructure(final String mapname) { + int i, lumpnum; + + if (mapname == null) { + DOOM.doomSystem.Error("P_SetupLevel: Wrong map name"); + throw new NullPointerException(); + } + + lumpnum = DOOM.wadLoader.CheckNumForName(mapname.toUpperCase()); + + if (lumpnum < 0) { + DOOM.doomSystem.Error("P_SetupLevel: There is no %s map.", mapname); + } + + for (i = ML_THINGS + 1; i <= ML_SECTORS; i++) { + if (!P_CheckLumpsForSameSource(lumpnum, lumpnum + i)) { + DOOM.doomSystem.Error( + "P_SetupLevel: Level wad structure is incomplete. There is no %s lump. (%s)", + ml_labels[i], DOOM.wadLoader.GetNameForLump(lumpnum)); + } + } + + // refuse to load Hexen-format maps, avoid segfaults + i = lumpnum + ML_BLOCKMAP + 1; + if (P_CheckLumpsForSameSource(lumpnum, i)) { + if (DOOM.wadLoader.GetLumpInfo(i).name.compareToIgnoreCase("BEHAVIOR") == 0) { + DOOM.doomSystem.Error("P_SetupLevel: %s: Hexen format not supported", mapname); + } + } + } + + // + // P_SetupLevel + // + // killough 5/3/98: reformatted, cleaned up + @Override + @SourceCode.Suspicious(CauseOfDesyncProbability.LOW) + @P_Setup.C(P_SetupLevel) + public void SetupLevel(int episode, int map, int playermask, skill_t skill) throws IOException { + String lumpname; + int lumpnum; + + String gl_lumpname; + int gl_lumpnum; + + // e6y + DOOM.totallive = 0; + // TODO: transparentpresent = false; + + // R_StopAllInterpolations(); + DOOM.totallive = DOOM.totalkills = DOOM.totalitems = DOOM.totalsecret = DOOM.wminfo.maxfrags = 0; + DOOM.wminfo.partime = 180; + + for (int i = 0; i < Limits.MAXPLAYERS; i++) { + DOOM.players[i].killcount = DOOM.players[i].secretcount = DOOM.players[i].itemcount = 0; + // TODO DM.players[i].resurectedkillcount = 0;//e6y + } + + // Initial height of PointOfView + // will be set by player think. + DOOM.players[DOOM.consoleplayer].viewz = 1; + + // Make sure all sounds are stopped before Z_FreeTags. + S_Start: + { + DOOM.doomSound.Start(); + } + + Z_FreeTags: + ; // Z_FreeTags(PU_LEVEL, PU_PURGELEVEL-1); + + if (rejectlump != -1) { // cph - unlock the reject table + DOOM.wadLoader.UnlockLumpNum(rejectlump); + rejectlump = -1; + } + + P_InitThinkers: + { + DOOM.actions.InitThinkers(); + } + + // if working with a devlopment map, reload it + W_Reload: + ; // killough 1/31/98: W.Reload obsolete + + // find map name + if (DOOM.isCommercial()) { + lumpname = String.format("map%02d", map); // killough 1/24/98: + // simplify + gl_lumpname = String.format("gl_map%02d", map); // figgi + } else { + lumpname = String.format("E%dM%d", episode, map); // killough + // 1/24/98: + // simplify + gl_lumpname = String.format("GL_E%dM%d", episode, map); // figgi + } + + W_GetNumForName: + { + lumpnum = DOOM.wadLoader.GetNumForName(lumpname); + gl_lumpnum = DOOM.wadLoader.CheckNumForName(gl_lumpname); // figgi + } + + // e6y + // Refuse to load a map with incomplete pwad structure. + // Avoid segfaults on levels without nodes. + P_CheckLevelWadStructure(lumpname); + + DOOM.leveltime = 0; + DOOM.totallive = 0; + + // note: most of this ordering is important + // killough 3/1/98: P_LoadBlockMap call moved down to below + // killough 4/4/98: split load of sidedefs into two parts, + // to allow texture names to be used in special linedefs + // figgi 10/19/00 -- check for gl lumps and load them + P_GetNodesVersion(lumpnum, gl_lumpnum); + + // e6y: speedup of level reloading + // Most of level's structures now are allocated with PU_STATIC instead + // of PU_LEVEL + // It is important for OpenGL, because in case of the same data in + // memory + // we can skip recalculation of much stuff + samelevel = (map == current_map) && (episode == current_episode) && (nodesVersion == current_nodesVersion); + + current_episode = episode; + current_map = map; + current_nodesVersion = nodesVersion; + + if (!samelevel) { + + /* + * if (GL_DOOM){ // proff 11/99: clean the memory from textures etc. + * gld_CleanMemory(); } + */ + // free(segs); + // free(nodes); + // free(subsectors); + /* + * #ifdef GL_DOOM free(map_subsectors); #endif + */ + // free(blocklinks); + // free(blockmaplump); + // free(lines); + // free(sides); + // free(sectors); + // free(vertexes); + } + + if (nodesVersion > 0) { + this.P_LoadVertexes2(lumpnum + ML_VERTEXES, gl_lumpnum + ML_GL_VERTS); + } else { + P_LoadVertexes(lumpnum + ML_VERTEXES); + } + + P_LoadSectors(lumpnum + ML_SECTORS); + P_LoadSideDefs(lumpnum + ML_SIDEDEFS); + P_LoadLineDefs(lumpnum + ML_LINEDEFS); + P_LoadSideDefs2(lumpnum + ML_SIDEDEFS); + P_LoadLineDefs2(lumpnum + ML_LINEDEFS); + + // e6y: speedup of level reloading + // Do not reload BlockMap for same level, + // because in case of big level P_CreateBlockMap eats much time + if (!samelevel) { + P_LoadBlockMap(lumpnum + ML_BLOCKMAP); + } else { + // clear out mobj chains + if (blocklinks != null && blocklinks.length == bmapwidth * bmapheight) { + for (int i = 0; i < bmapwidth * bmapheight; i++) { + blocklinks[i] = null; + } + } else { + blocklinks = new mobj_t[bmapwidth * bmapheight]; + Arrays.setAll(blocklinks, i -> mobj_t.createOn(DOOM)); + } + } + + if (nodesVersion > 0) { + P_LoadSubsectors(gl_lumpnum + ML_GL_SSECT); + P_LoadNodes(gl_lumpnum + ML_GL_NODES); + // TODO: P_LoadGLSegs(gl_lumpnum + ML_GL_SEGS); + } else { + if (P_CheckForZDoomUncompressedNodes(lumpnum, gl_lumpnum)) { + P_LoadZNodes(lumpnum + ML_NODES, 0); + } else if (P_CheckForDeePBSPv4Nodes(lumpnum, gl_lumpnum)) { + P_LoadSubsectors_V4(lumpnum + ML_SSECTORS); + P_LoadNodes_V4(lumpnum + ML_NODES); + P_LoadSegs_V4(lumpnum + ML_SEGS); + } else { + P_LoadSubsectors(lumpnum + ML_SSECTORS); + P_LoadNodes(lumpnum + ML_NODES); + P_LoadSegs(lumpnum + ML_SEGS); + } + } + + /* + * if (GL_DOOM){ map_subsectors = calloc_IfSameLevel(map_subsectors, + * numsubsectors); } + */ + // reject loading and underflow padding separated out into new function + // P_GroupLines modified to return a number the underflow padding needs + // P_LoadReject(lumpnum, P_GroupLines()); + P_GroupLines(); + super.LoadReject(lumpnum + ML_REJECT); + + /** + * TODO: try to fix, since it seems it doesn't work + * - Good Sign 2017/05/07 + */ + // e6y + // Correction of desync on dv04-423.lmp/dv.wad + // http://www.doomworld.com/vb/showthread.php?s=&postid=627257#post627257 + // if (DoomStatus.compatibility_level>=lxdoom_1_compatibility || + // Compatibility.prboom_comp[PC.PC_REMOVE_SLIME_TRAILS.ordinal()].state) + P_RemoveSlimeTrails(); // killough 10/98: remove slime trails from wad + + // Note: you don't need to clear player queue slots -- + // a much simpler fix is in g_game.c -- killough 10/98 + DOOM.bodyqueslot = 0; + + /* cph - reset all multiplayer starts */ + for (int i = 0; i < playerstarts.length; i++) { + DOOM.playerstarts[i] = null; + } + + deathmatch_p = 0; + + for (int i = 0; i < Limits.MAXPLAYERS; i++) { + DOOM.players[i].mo = null; + } + // TODO: TracerClearStarts(); + + // Hmm? P_MapStart(); + P_LoadThings: + { + P_LoadThings(lumpnum + ML_THINGS); + } + + // if deathmatch, randomly spawn the active players + if (DOOM.deathmatch) { + for (int i = 0; i < Limits.MAXPLAYERS; i++) { + if (DOOM.playeringame[i]) { + DOOM.players[i].mo = null; // not needed? - done before P_LoadThings + G_DeathMatchSpawnPlayer: + { + DOOM.DeathMatchSpawnPlayer(i); + } + } + } + } else { // if !deathmatch, check all necessary player starts actually exist + for (int i = 0; i < Limits.MAXPLAYERS; i++) { + if (DOOM.playeringame[i] && !C2JUtils.eval(DOOM.players[i].mo)) { + DOOM.doomSystem.Error("P_SetupLevel: missing player %d start\n", i + 1); + } + } + } + + // killough 3/26/98: Spawn icon landings: + // TODO: if (DM.isCommercial()) + // P.SpawnBrainTargets(); + if (!DOOM.isShareware()) { + // TODO: S.ParseMusInfo(lumpname); + } + + // clear special respawning que + DOOM.actions.ClearRespawnQueue(); + + // set up world state + P_SpawnSpecials: + { + DOOM.actions.SpawnSpecials(); + } + + // TODO: P.MapEnd(); + // preload graphics + if (DOOM.precache) { + /* @SourceCode.Compatible if together */ + R_PrecacheLevel: + { + DOOM.textureManager.PrecacheLevel(); + + // MAES: thinkers are separate than texture management. Maybe split + // sprite management as well? + DOOM.sceneRenderer.PreCacheThinkers(); + } + } + + /* + * if (GL_DOOM){ if (V_GetMode() == VID_MODEGL) { // e6y // Do not + * preprocess GL data during skipping, // because it potentially will + * not be used. // But preprocessing must be called immediately after + * stop of skipping. if (!doSkip) { // proff 11/99: calculate all OpenGL + * specific tables etc. gld_PreprocessLevel(); } } } + */ + // e6y + // TODO P_SyncWalkcam(true, true); + // TODO R_SmoothPlaying_Reset(NULL); + } + +} \ No newline at end of file diff --git a/src/p/ChaseDirections.java b/src/p/ChaseDirections.java index d461f7d..af7cc71 100644 --- a/src/p/ChaseDirections.java +++ b/src/p/ChaseDirections.java @@ -43,4 +43,4 @@ public final class ChaseDirections { = {0, 47000 / TIC_MUL, MAPFRACUNIT, 47000 / TIC_MUL, 0, -47000 / TIC_MUL, -MAPFRACUNIT, -47000 / TIC_MUL}; // all // fixed -} +} \ No newline at end of file diff --git a/src/p/DoomPlayer.java b/src/p/DoomPlayer.java index 9c61628..5dd6485 100644 --- a/src/p/DoomPlayer.java +++ b/src/p/DoomPlayer.java @@ -30,4 +30,4 @@ public interface DoomPlayer { mobj_t inflictor, mobj_t source, int damage); -} +} \ No newline at end of file diff --git a/src/p/DoorDefines.java b/src/p/DoorDefines.java index 7e0bc71..f26adee 100644 --- a/src/p/DoorDefines.java +++ b/src/p/DoorDefines.java @@ -17,4 +17,4 @@ public final class DoorDefines { public static final int SLOWDARK = 35; -} +} \ No newline at end of file diff --git a/src/p/ILevelLoader.java b/src/p/ILevelLoader.java index d3b96b4..6ff34d8 100644 --- a/src/p/ILevelLoader.java +++ b/src/p/ILevelLoader.java @@ -50,13 +50,13 @@ public interface ILevelLoader { "VERTEXES", "SEGS", "SSECTORS", "NODES", "SECTORS", "REJECT", "BLOCKMAP"}; - /** P_SetupLevel - * + /** P_SetupLevel + * * @param episode * @param map * @param playermask * @param skill - * @throws IOException + * @throws IOException */ @P_Setup.C(P_SetupLevel) void SetupLevel(int episode, int map, int playermask, skill_t skill) throws IOException; @@ -65,20 +65,20 @@ public interface ILevelLoader { * P_SetThingPosition Links a thing into both a block and a subsector based * on it's x y. Sets thing.subsector properly * - * + * * @param thing */ void SetThingPosition(mobj_t thing); /** * R_PointInSubsector - * + * * MAES: it makes more sense to have this here. - * + * * @param x fixed * @param y fixed - * + * */ subsector_t PointInSubsector(int x, int y); -} +} \ No newline at end of file diff --git a/src/p/ISightChecker.java b/src/p/ISightChecker.java index c81e477..51894d5 100644 --- a/src/p/ISightChecker.java +++ b/src/p/ISightChecker.java @@ -8,4 +8,4 @@ public interface ISightChecker { public boolean CrossBSPNode(int bspnum); -} +} \ No newline at end of file diff --git a/src/p/Interceptable.java b/src/p/Interceptable.java index bc15ed7..819b6f3 100644 --- a/src/p/Interceptable.java +++ b/src/p/Interceptable.java @@ -2,4 +2,4 @@ public interface Interceptable { -} +} \ No newline at end of file diff --git a/src/p/LevelLoader.java b/src/p/LevelLoader.java index c2faa30..a3af585 100644 --- a/src/p/LevelLoader.java +++ b/src/p/LevelLoader.java @@ -1,943 +1,943 @@ -package p; - -import static data.Defines.MAPBLOCKSHIFT; -import static data.Defines.NF_SUBSECTOR; -import static data.Defines.NF_SUBSECTOR_CLASSIC; -import static data.Defines.PU_LEVEL; -import static data.Limits.MAXPLAYERS; -import static data.Limits.MAXRADIUS; -import data.maplinedef_t; -import data.mapnode_t; -import data.mapsector_t; -import data.mapseg_t; -import data.mapsidedef_t; -import data.mapsubsector_t; -import data.mapthing_t; -import data.mapvertex_t; -import defines.skill_t; -import defines.slopetype_t; -import doom.CommandVariable; -import doom.DoomMain; -import java.io.IOException; -import java.nio.ByteOrder; -import java.util.logging.Level; -import java.util.logging.Logger; -import m.BBox; -import static m.BBox.BOXBOTTOM; -import static m.BBox.BOXLEFT; -import static m.BBox.BOXRIGHT; -import static m.BBox.BOXTOP; -import static m.fixed_t.FRACBITS; -import static m.fixed_t.FixedDiv; -import mochadoom.Loggers; -import rr.line_t; -import static rr.line_t.ML_TWOSIDED; -import rr.node_t; -import rr.sector_t; -import rr.seg_t; -import rr.side_t; -import rr.subsector_t; -import rr.vertex_t; -import s.degenmobj_t; -import static utils.C2JUtils.flags; -import static utils.GenericCopy.malloc; -import w.DoomBuffer; - -//Emacs style mode select -*- Java -*- -//----------------------------------------------------------------------------- -// -// $Id: LevelLoader.java,v 1.44 2012/09/24 17:16:23 velktron Exp $ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// DESCRIPTION: -// Do all the WAD I/O, get map description, -// set up initial state and misc. LUTs. -// -//----------------------------------------------------------------------------- -public class LevelLoader extends AbstractLevelLoader { - - private static final Logger LOGGER = Loggers.getLogger(LevelLoader.class.getName()); - - public static final String rcsid = "$Id: LevelLoader.java,v 1.44 2012/09/24 17:16:23 velktron Exp $"; - - public LevelLoader(DoomMain DM) { - super(DM); - // Traditional loader sets limit. - deathmatchstarts = new mapthing_t[MAX_DEATHMATCH_STARTS]; - } - - /** - * P_LoadVertexes - * - * @throws IOException - */ - public void LoadVertexes(int lump) throws IOException { - // Make a lame-ass attempt at loading some vertexes. - - // Determine number of lumps: - // total lump length / vertex record length. - numvertexes = DOOM.wadLoader.LumpLength(lump) / mapvertex_t.sizeOf(); - - // Load data into cache. - // MAES: we now have a mismatch between memory/disk: in memory, we need an array. - // On disk, we have a single lump/blob. Thus, we need to find a way to deserialize this... - vertexes = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numvertexes, vertex_t::new, vertex_t[]::new); - - // Copy and convert vertex coordinates, - // MAES: not needed. Intermediate mapvertex_t struct skipped. - } - - /** - * P_LoadSegs - * - * @throws IOException - */ - public void LoadSegs(int lump) throws IOException { - - mapseg_t[] data; - mapseg_t ml; - seg_t li; - line_t ldef; - int linedef; - int side; - - // Another disparity between disk/memory. Treat it the same as VERTEXES. - numsegs = DOOM.wadLoader.LumpLength(lump) / mapseg_t.sizeOf(); - segs = malloc(seg_t::new, seg_t[]::new, numsegs); - data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numsegs, mapseg_t::new, mapseg_t[]::new); - - // We're not done yet! - for (int i = 0; i < numsegs; i++) { - li = segs[i]; - ml = data[i]; - li.v1 = vertexes[ml.v1]; - li.v2 = vertexes[ml.v2]; - li.assignVertexValues(); - - li.angle = ((ml.angle) << 16) & 0xFFFFFFFFL; - li.offset = (ml.offset) << 16; - linedef = ml.linedef; - li.linedef = ldef = lines[linedef]; - side = ml.side; - li.sidedef = sides[ldef.sidenum[side]]; - li.frontsector = sides[ldef.sidenum[side]].sector; - if (flags(ldef.flags, ML_TWOSIDED)) { - // MAES: Fix double sided without back side. E.g. Linedef 16103 in Europe.wad - if (ldef.sidenum[side ^ 1] != line_t.NO_INDEX) { - li.backsector = sides[ldef.sidenum[side ^ 1]].sector; - } - // Fix two-sided with no back side. - //else { - //li.backsector=null; - //ldef.flags^=ML_TWOSIDED; - //} - } else { - li.backsector = null; - } - } - - } - - /** - * P_LoadSubsectors - * - * @throws IOException - */ - public void LoadSubsectors(int lump) throws IOException { - mapsubsector_t ms; - subsector_t ss; - mapsubsector_t[] data; - - numsubsectors = DOOM.wadLoader.LumpLength(lump) / mapsubsector_t.sizeOf(); - subsectors = malloc(subsector_t::new, subsector_t[]::new, numsubsectors); - - // Read "mapsubsectors" - data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numsubsectors, mapsubsector_t::new, mapsubsector_t[]::new); - - for (int i = 0; i < numsubsectors; i++) { - ms = data[i]; - ss = subsectors[i]; - ss.numlines = ms.numsegs; - ss.firstline = ms.firstseg; - } - - } - - /** - * P_LoadSectors - * - * @throws IOException - */ - public void LoadSectors(int lump) throws IOException { - mapsector_t[] data; - mapsector_t ms; - sector_t ss; - - numsectors = DOOM.wadLoader.LumpLength(lump) / mapsector_t.sizeOf(); - sectors = malloc(sector_t::new, sector_t[]::new, numsectors); - - // Read "mapsectors" - data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numsectors, mapsector_t::new, mapsector_t[]::new); - - for (int i = 0; i < numsectors; i++) { - ms = data[i]; - ss = sectors[i]; - ss.floorheight = ms.floorheight << FRACBITS; - ss.ceilingheight = ms.ceilingheight << FRACBITS; - ss.floorpic = (short) DOOM.textureManager.FlatNumForName(ms.floorpic); - ss.ceilingpic = (short) DOOM.textureManager.FlatNumForName(ms.ceilingpic); - ss.lightlevel = ms.lightlevel; - ss.special = ms.special; - ss.tag = ms.tag; - ss.thinglist = null; - ss.id = i; - ss.TL = this.DOOM.actions; - ss.RND = this.DOOM.random; - } - - } - - /** - * P_LoadNodes - * - * @throws IOException - */ - public void LoadNodes(int lump) throws IOException { - mapnode_t[] data; - int i; - int j; - int k; - mapnode_t mn; - node_t no; - - numnodes = DOOM.wadLoader.LumpLength(lump) / mapnode_t.sizeOf(); - nodes = malloc(node_t::new, node_t[]::new, numnodes); - - // Read "mapnodes" - data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numnodes, mapnode_t::new, mapnode_t[]::new); - - for (i = 0; i < numnodes; i++) { - mn = data[i]; - no = nodes[i]; - - no.x = mn.x << FRACBITS; - no.y = mn.y << FRACBITS; - no.dx = mn.dx << FRACBITS; - no.dy = mn.dy << FRACBITS; - for (j = 0; j < 2; j++) { - // e6y: support for extended nodes - no.children[j] = (char) mn.children[j]; - - // e6y: support for extended nodes - if (no.children[j] == 0xFFFF) { - no.children[j] = 0xFFFFFFFF; - } else if (flags(no.children[j], NF_SUBSECTOR_CLASSIC)) { - // Convert to extended type - no.children[j] &= ~NF_SUBSECTOR_CLASSIC; - - // haleyjd 11/06/10: check for invalid subsector reference - if (no.children[j] >= numsubsectors) { - LOGGER.log(Level.WARNING, String.format( - "P_LoadNodes: BSP tree references invalid subsector %d.", - no.children[j])); - no.children[j] = 0; - } - - no.children[j] |= NF_SUBSECTOR; - } - - for (k = 0; k < 4; k++) { - no.bbox[j].set(k, mn.bbox[j][k] << FRACBITS); - } - } - } - - } - - /** - * P_LoadThings - * - * @throws IOException - */ - public void LoadThings(int lump) throws IOException { - mapthing_t[] data; - mapthing_t mt; - int numthings; - boolean spawn; - - numthings = DOOM.wadLoader.LumpLength(lump) / mapthing_t.sizeOf(); - // VERY IMPORTANT: since now caching is near-absolute, - // the mapthing_t instances must be CLONED rather than just - // referenced, otherwise missing mobj bugs start happening. - - data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numthings, mapthing_t::new, mapthing_t[]::new); - - for (int i = 0; i < numthings; i++) { - mt = data[i]; - spawn = true; - - // Do not spawn cool, new monsters if !commercial - if (!DOOM.isCommercial()) { - switch (mt.type) { - case 68: // Arachnotron - case 64: // Archvile - case 88: // Boss Brain - case 89: // Boss Shooter - case 69: // Hell Knight - case 67: // Mancubus - case 71: // Pain Elemental - case 65: // Former Human Commando - case 66: // Revenant - case 84: // Wolf SS - spawn = false; - break; - } - } - if (spawn == false) { - break; - } - - // Do spawn all other stuff. - // MAES: we have loaded the shit with the proper endianness, so no fucking around, bitch. - /*mt.x = SHORT(mt.x); - mt.y = SHORT(mt.y); - mt.angle = SHORT(mt.angle); - mt.type = SHORT(mt.type); - mt.options = SHORT(mt.options);*/ - //System.out.printf("Spawning %d %s\n",i,mt.type); - DOOM.actions.SpawnMapThing(mt); - } - - // Status may have changed. It's better to release the resources anyway - //W.UnlockLumpNum(lump); - } - - /** - * P_LoadLineDefs - * Also counts secret lines for intermissions. - * - * @throws IOException - */ - public void LoadLineDefs(int lump) throws IOException { - maplinedef_t[] data; - maplinedef_t mld; - line_t ld; - vertex_t v1; - vertex_t v2; - - numlines = DOOM.wadLoader.LumpLength(lump) / maplinedef_t.sizeOf(); - lines = malloc(line_t::new, line_t[]::new, numlines); - - // Check those actually used in sectors, later on. - used_lines = new boolean[numlines]; - - // read "maplinedefs" - data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numlines, maplinedef_t::new, maplinedef_t[]::new); - - for (int i = 0; i < numlines; i++) { - mld = data[i]; - ld = lines[i]; - - ld.flags = mld.flags; - ld.special = mld.special; - ld.tag = mld.tag; - v1 = ld.v1 = vertexes[(char) mld.v1]; - v2 = ld.v2 = vertexes[(char) mld.v2]; - ld.dx = v2.x - v1.x; - ld.dy = v2.y - v1.y; - // Map value semantics. - ld.assignVertexValues(); - - if (ld.dx == 0) { - ld.slopetype = slopetype_t.ST_VERTICAL; - } else if (ld.dy == 0) { - ld.slopetype = slopetype_t.ST_HORIZONTAL; - } else { - if (FixedDiv(ld.dy, ld.dx) > 0) { - ld.slopetype = slopetype_t.ST_POSITIVE; - } else { - ld.slopetype = slopetype_t.ST_NEGATIVE; - } - } - - if (v1.x < v2.x) { - ld.bbox[BOXLEFT] = v1.x; - ld.bbox[BOXRIGHT] = v2.x; - } else { - ld.bbox[BOXLEFT] = v2.x; - ld.bbox[BOXRIGHT] = v1.x; - } - - if (v1.y < v2.y) { - ld.bbox[BOXBOTTOM] = v1.y; - ld.bbox[BOXTOP] = v2.y; - } else { - ld.bbox[BOXBOTTOM] = v2.y; - ld.bbox[BOXTOP] = v1.y; - } - - ld.sidenum[0] = mld.sidenum[0]; - ld.sidenum[1] = mld.sidenum[1]; - - // Sanity check for two-sided without two valid sides. - if (flags(ld.flags, ML_TWOSIDED)) { - if ((ld.sidenum[0] == line_t.NO_INDEX) || (ld.sidenum[1] == line_t.NO_INDEX)) { - // Well, dat ain't so tu-sided now, ey esse? - ld.flags ^= ML_TWOSIDED; - } - } - - // Front side defined without a valid frontsector. - if (ld.sidenum[0] != line_t.NO_INDEX) { - ld.frontsector = sides[ld.sidenum[0]].sector; - if (ld.frontsector == null) { // // Still null? Bad map. Map to dummy. - ld.frontsector = dummy_sector; - } - - } else { - ld.frontsector = null; - } - - // back side defined without a valid backsector. - if (ld.sidenum[1] != line_t.NO_INDEX) { - ld.backsector = sides[ld.sidenum[1]].sector; - if (ld.backsector == null) { // Still null? Bad map. Map to dummy. - ld.backsector = dummy_sector; - } - } else { - ld.backsector = null; - } - - // If at least one valid sector is defined, then it's not null. - if (ld.frontsector != null || ld.backsector != null) { - this.used_lines[i] = true; - } - - } - - } - - /** - * P_LoadSideDefs - */ - public void LoadSideDefs(int lump) throws IOException { - mapsidedef_t[] data; - mapsidedef_t msd; - side_t sd; - - numsides = DOOM.wadLoader.LumpLength(lump) / mapsidedef_t.sizeOf(); - sides = malloc(side_t::new, side_t[]::new, numsides); - - data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numsides, mapsidedef_t::new, mapsidedef_t[]::new); - - for (int i = 0; i < numsides; i++) { - msd = data[i]; - sd = sides[i]; - - sd.textureoffset = (msd.textureoffset) << FRACBITS; - sd.rowoffset = (msd.rowoffset) << FRACBITS; - sd.toptexture = (short) DOOM.textureManager.TextureNumForName(msd.toptexture); - sd.bottomtexture = (short) DOOM.textureManager.TextureNumForName(msd.bottomtexture); - sd.midtexture = (short) DOOM.textureManager.TextureNumForName(msd.midtexture); - if (msd.sector < 0) { - sd.sector = dummy_sector; - } else { - sd.sector = sectors[msd.sector]; - } - } - } - - // MAES 22/5/2011 This hack added for PHOBOS2.WAD, in order to - // accomodate for some linedefs having a sector number of "-1". - // Any negative sector will get rewired to this dummy sector. - // PROBABLY, this will handle unused sector/linedefes cleanly? - sector_t dummy_sector = new sector_t(); - - /** - * P_LoadBlockMap - * - * @throws IOException - * - * TODO: generate BLOCKMAP dynamically to - * handle missing cases and increase accuracy. - * - */ - public void LoadBlockMap(int lump) throws IOException { - int count = 0; - - if (DOOM.cVarManager.bool(CommandVariable.BLOCKMAP) || DOOM.wadLoader.LumpLength(lump) < 8 - || (count = DOOM.wadLoader.LumpLength(lump) / 2) >= 0x10000) // e6y - { - CreateBlockMap(); - } else { - - DoomBuffer data = (DoomBuffer) DOOM.wadLoader.CacheLumpNum(lump, PU_LEVEL, DoomBuffer.class); - count = DOOM.wadLoader.LumpLength(lump) / 2; - blockmaplump = new int[count]; - - data.setOrder(ByteOrder.LITTLE_ENDIAN); - data.rewind(); - data.readCharArray(blockmaplump, count); - - // Maes: first four shorts are header data. - bmaporgx = blockmaplump[0] << FRACBITS; - bmaporgy = blockmaplump[1] << FRACBITS; - bmapwidth = blockmaplump[2]; - bmapheight = blockmaplump[3]; - - // MAES: use killough's code to convert terminators to -1 beforehand - for (int i = 4; i < count; i++) { - short t = (short) blockmaplump[i]; // killough 3/1/98 - blockmaplump[i] = (int) (t == -1 ? -1l : t & 0xffff); - } - - // haleyjd 03/04/10: check for blockmap problems - // http://www.doomworld.com/idgames/index.php?id=12935 - if (!VerifyBlockMap(count)) { - LOGGER.log(Level.WARNING, "P_LoadBlockMap: erroneous BLOCKMAP lump may cause crashes.\n"); - LOGGER.log(Level.WARNING, "P_LoadBlockMap: use \"-blockmap\" command line switch for rebuilding\n"); - } - - } - count = bmapwidth * bmapheight; - - // IMPORTANT MODIFICATION: no need to have both blockmaplump AND blockmap. - // If the offsets in the lump are OK, then we can modify them (remove 4) - // and copy the rest of the data in one single data array. This avoids - // reserving memory for two arrays (we can't simply alias one in Java) - blockmap = new int[blockmaplump.length - 4]; - - // Offsets are relative to START OF BLOCKMAP, and IN SHORTS, not bytes. - for (int i = 0; i < blockmaplump.length - 4; i++) { - // Modify indexes so that we don't need two different lumps. - // Can probably be further optimized if we simply shift everything backwards. - // and reuse the same memory space. - if (i < count) { - blockmaplump[i] = blockmaplump[i + 4] - 4; - } else { - // Make terminators definitively -1, different that 0xffff - short t = (short) blockmaplump[i + 4]; // killough 3/1/98 - blockmaplump[i] = (int) (t == -1 ? -1l : t & 0xffff); - } - } - - // clear out mobj chains - // ATTENTION! BUG!!! - // If blocklinks are "cleared" to void -but instantiated- objects, - // very bad bugs happen, especially the second time a level is re-instantiated. - // Probably caused other bugs as well, as an extra object would appear in iterators. - if (blocklinks != null && blocklinks.length == count) { - for (int i = 0; i < count; i++) { - blocklinks[i] = null; - } - } else { - blocklinks = new mobj_t[count]; - } - - // Bye bye. Not needed. - blockmap = blockmaplump; - } - - /** - * P_GroupLines - * Builds sector line lists and subsector sector numbers. - * Finds block bounding boxes for sectors. - */ - public void GroupLines() { - int total; - line_t li; - sector_t sector; - subsector_t ss; - seg_t seg; - int[] bbox = new int[4]; - int block; - - // look up sector number for each subsector - for (int i = 0; i < numsubsectors; i++) { - ss = subsectors[i]; - seg = segs[ss.firstline]; - ss.sector = seg.sidedef.sector; - } - - //linebuffer=new line_t[numsectors][0]; - // count number of lines in each sector - total = 0; - - for (int i = 0; i < numlines; i++) { - li = lines[i]; - total++; - li.frontsector.linecount++; - - if ((li.backsector != null) && (li.backsector != li.frontsector)) { - li.backsector.linecount++; - total++; - } - - } - - // build line tables for each sector - // MAES: we don't really need this in Java. - // linebuffer = new line_t[total]; - // int linebuffercount=0; - // We scan through ALL sectors. - for (int i = 0; i < numsectors; i++) { - sector = sectors[i]; - BBox.ClearBox(bbox); - //sector->lines = linebuffer; - // We can just construct line tables of the correct size - // for each sector. - int countlines = 0; - // We scan through ALL lines.... - - // System.out.println(i+ ": looking for sector -> "+sector); - for (int j = 0; j < numlines; j++) { - li = lines[j]; - - //System.out.println(j+ " front "+li.frontsector+ " back "+li.backsector); - if (li.frontsector == sector || li.backsector == sector) { - // This sector will have one more line. - countlines++; - // Expand bounding box... - BBox.AddToBox(bbox, li.v1.x, li.v1.y); - BBox.AddToBox(bbox, li.v2.x, li.v2.y); - } - } - - // So, this sector must have that many lines. - sector.lines = new line_t[countlines]; - - int addedlines = 0; - int pointline = 0; - - // Add actual lines into sectors. - for (int j = 0; j < numlines; j++) { - li = lines[j]; - // If - if (li.frontsector == sector || li.backsector == sector) { - // This sector will have one more line. - sectors[i].lines[pointline++] = lines[j]; - addedlines++; - } - } - - if (addedlines != sector.linecount) { - DOOM.doomSystem.Error("P_GroupLines: miscounted"); - } - - // set the degenmobj_t to the middle of the bounding box - sector.soundorg = new degenmobj_t(((bbox[BOXRIGHT] + bbox[BOXLEFT]) / 2), - ((bbox[BOXTOP] + bbox[BOXBOTTOM]) / 2), (sector.ceilingheight - sector.floorheight) / 2); - - // adjust bounding box to map blocks - block = (bbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; - block = block >= bmapheight ? bmapheight - 1 : block; - sector.blockbox[BOXTOP] = block; - - block = (bbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; - block = block < 0 ? 0 : block; - sector.blockbox[BOXBOTTOM] = block; - - block = (bbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; - block = block >= bmapwidth ? bmapwidth - 1 : block; - sector.blockbox[BOXRIGHT] = block; - - block = (bbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; - block = block < 0 ? 0 : block; - sector.blockbox[BOXLEFT] = block; - } - - } - - @Override - public void - SetupLevel(int episode, - int map, - int playermask, - skill_t skill) { - int i; - String lumpname; - int lumpnum; - - try { - DOOM.totalkills = DOOM.totalitems = DOOM.totalsecret = DOOM.wminfo.maxfrags = 0; - DOOM.wminfo.partime = 180; - for (i = 0; i < MAXPLAYERS; i++) { - DOOM.players[i].killcount = DOOM.players[i].secretcount - = DOOM.players[i].itemcount = 0; - } - - // Initial height of PointOfView - // will be set by player think. - DOOM.players[DOOM.consoleplayer].viewz = 1; - - // Make sure all sounds are stopped before Z_FreeTags. - DOOM.doomSound.Start(); - - /* - #if 0 // UNUSED - if (debugfile) - { - Z_FreeTags (PU_LEVEL, MAXINT); - Z_FileDumpHeap (debugfile); - } - else - #endif - */ - // Z_FreeTags (PU_LEVEL, PU_PURGELEVEL-1); - // UNUSED W_Profile (); - DOOM.actions.InitThinkers(); - - // if working with a development map, reload it - DOOM.wadLoader.Reload(); - - // find map name - if (DOOM.isCommercial()) { - if (map < 10) { - lumpname = "MAP0" + map; - } else { - lumpname = "MAP" + map; - } - } else { - lumpname = ("E" - + (char) ('0' + episode) - + "M" - + (char) ('0' + map)); - } - - lumpnum = DOOM.wadLoader.GetNumForName(lumpname); - - DOOM.leveltime = 0; - - if (!DOOM.wadLoader.verifyLumpName(lumpnum + ML_BLOCKMAP, LABELS[ML_BLOCKMAP])) { - LOGGER.log(Level.WARNING, "Blockmap missing!"); - } - - // note: most of this ordering is important - this.LoadVertexes(lumpnum + ML_VERTEXES); - this.LoadSectors(lumpnum + ML_SECTORS); - this.LoadSideDefs(lumpnum + ML_SIDEDEFS); - this.LoadLineDefs(lumpnum + ML_LINEDEFS); - this.LoadSubsectors(lumpnum + ML_SSECTORS); - this.LoadNodes(lumpnum + ML_NODES); - this.LoadSegs(lumpnum + ML_SEGS); - - // MAES: in order to apply optimizations and rebuilding, order must be changed. - this.LoadBlockMap(lumpnum + ML_BLOCKMAP); - //this.SanitizeBlockmap(); - //this.getMapBoundingBox(); - - this.LoadReject(lumpnum + ML_REJECT); - - this.GroupLines(); - - DOOM.bodyqueslot = 0; - // Reset to "deathmatch starts" - DOOM.deathmatch_p = 0; - this.LoadThings(lumpnum + ML_THINGS); - - // if deathmatch, randomly spawn the active players - if (DOOM.deathmatch) { - for (i = 0; i < MAXPLAYERS; i++) { - if (DOOM.playeringame[i]) { - DOOM.players[i].mo = null; - DOOM.DeathMatchSpawnPlayer(i); - } - } - - } - - // clear special respawning que - DOOM.actions.ClearRespawnQueue(); - - // set up world state - DOOM.actions.SpawnSpecials(); - - // build subsector connect matrix - // UNUSED P_ConnectSubsectors (); - // preload graphics - if (DOOM.precache) { - DOOM.textureManager.PrecacheLevel(); - // MAES: thinkers are separate than texture management. Maybe split sprite management as well? - DOOM.sceneRenderer.PreCacheThinkers(); - - } - - } catch (Exception e) { - LOGGER.log(Level.SEVERE, "Error while loading level", e); - } - } - -} - -//$Log: LevelLoader.java,v $ -//Revision 1.44 2012/09/24 17:16:23 velktron -//Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD. -// -//Revision 1.43.2.2 2012/09/24 16:57:16 velktron -//Addressed generics warnings. -// -//Revision 1.43.2.1 2012/03/26 09:53:44 velktron -//Use line_t.NO_INDEX for good measure, when possible. -// -//Revision 1.43 2011/11/03 15:19:51 velktron -//Adapted to using ISpriteManager -// -//Revision 1.42 2011/10/07 16:05:52 velktron -//Now using line_t for ML_* definitions. -// -//Revision 1.41 2011/10/06 16:44:32 velktron -//Proper support for extended nodes, made reject loading into a separate method. -// -//Revision 1.40 2011/09/30 15:20:24 velktron -//Very modified, useless SanitizeBlockmap method ditched. -//Common utility methods moved to superclass. Shares blockmap checking and generation -//with Boom-derived code. Now capable of running Europe.wad. -//TODO: Blockmap generation can be really slow on large levels. -//Optimize better for Java, or parallelize. -// -//Revision 1.39 2011/09/29 17:22:08 velktron -//Blockchain terminators are now -1 (extended) -// -//Revision 1.38 2011/09/29 17:11:32 velktron -//Blockmap optimizations. -// -//Revision 1.37 2011/09/29 15:17:48 velktron -//SetupLevel can propagate exceptions. -// -//Revision 1.36 2011/09/29 13:28:01 velktron -//Extends AbstractLevelLoader -// -//Revision 1.35 2011/09/27 18:04:36 velktron -//Fixed major blockmap bug -// -//Revision 1.34 2011/09/27 16:00:20 velktron -//Minor blockmap stuff. -// -//Revision 1.33 2011/08/24 15:52:04 velktron -//Sets proper ISoundOrigin for sectors (height, too) -// -//Revision 1.32 2011/08/24 15:00:34 velktron -//Improved version, now using createArrayOfObjects. Much better syntax. -// -//Revision 1.31 2011/08/23 16:17:22 velktron -//Got rid of Z remnants. -// -//Revision 1.30 2011/07/27 21:26:19 velktron -//Quieted down debugging for v1.5 release -// -//Revision 1.29 2011/07/25 19:56:53 velktron -//reject matrix size bugfix, fron danmaku branch. -// -//Revision 1.28 2011/07/22 15:37:52 velktron -//Began blockmap autogen code...still WIP -// -//Revision 1.27 2011/07/20 16:14:45 velktron -//Bullet-proofing vs missing or corrupt REJECT table. TODO: built-in system to re-compute it. -// -//Revision 1.26 2011/06/18 23:25:33 velktron -//Removed debugginess -// -//Revision 1.25 2011/06/18 23:21:26 velktron -//-id -// -//Revision 1.24 2011/06/18 23:18:24 velktron -//Added sanitization for broken two-sided sidedefs, and semi-support for extended blockmaps. -// -//Revision 1.23 2011/05/24 11:31:47 velktron -//Adapted to IDoomStatusBar -// -//Revision 1.22 2011/05/22 21:09:34 velktron -//Added spechit overflow handling, and unused linedefs (with -1 sector) handling. -// -//Revision 1.21 2011/05/21 14:53:57 velktron -//Adapted to use new gamemode system. -// -//Revision 1.20 2011/05/20 14:52:23 velktron -//Moved several function from the Renderer and Action code in here, since it made more sense. -// -//Revision 1.19 2011/05/18 16:55:44 velktron -//TEMPORARY TESTING VERSION, DO NOT USE -// -//Revision 1.18 2011/05/17 16:51:20 velktron -//Switched to DoomStatus -// -//Revision 1.17 2011/05/10 10:39:18 velktron -//Semi-playable Techdemo v1.3 milestone -// -//Revision 1.16 2011/05/05 17:24:22 velktron -//Started merging more of _D_'s changes. -// -//Revision 1.15 2010/12/20 17:15:08 velktron -//Made the renderer more OO -> TextureManager and other changes as well. -// -//Revision 1.14 2010/11/22 21:41:22 velktron -//Parallel rendering...sort of.It works, but either the barriers are broken or it's simply not worthwhile at this point :-/ -// -//Revision 1.13 2010/11/22 14:54:53 velktron -//Greater objectification of sectors etc. -// -//Revision 1.12 2010/11/22 01:17:16 velktron -//Fixed blockmap (for the most part), some actions implemented and functional, ambient animation/lighting functional. -// -//Revision 1.11 2010/11/14 20:00:21 velktron -//Bleeding floor bug fixed! -// -//Revision 1.10 2010/11/03 16:48:04 velktron -//"Bling" view angles fixed (perhaps related to the "bleeding line bug"?) -// -//Revision 1.9 2010/09/27 02:27:29 velktron -//BEASTLY update -// -//Revision 1.8 2010/09/23 20:36:45 velktron -//*** empty log message *** -// -//Revision 1.7 2010/09/23 15:11:57 velktron -//A bit closer... -// -//Revision 1.6 2010/09/22 16:40:02 velktron -//MASSIVE changes in the status passing model. -//DoomMain and DoomGame unified. -//Doomstat merged into DoomMain (now status and game functions are one). -// -//Most of DoomMain implemented. Possible to attempt a "classic type" start but will stop when reading sprites. -// -//Revision 1.5 2010/09/21 15:53:37 velktron -//Split the Map ...somewhat... -// -//Revision 1.4 2010/09/14 15:34:01 velktron -//The enormity of this commit is incredible (pun intended) -// -//Revision 1.3 2010/09/08 15:22:18 velktron -//x,y coords in some structs as value semantics. Possible speed increase? -// -//Revision 1.2 2010/09/02 15:56:54 velktron -//Bulk of unified renderer copyediting done. -// -//Some changes like e.g. global separate limits class and instance methods for seg_t and node_t introduced. -// -//Revision 1.1 2010/09/01 15:53:42 velktron -//Graphics data loader implemented....still need to figure out how column caching works, though. -// -//Revision 1.4 2010/08/19 23:14:49 velktron -//Automap -// -//Revision 1.3 2010/08/13 14:06:36 velktron -//Endlevel screen fully functional! -// -//Revision 1.2 2010/08/11 16:31:34 velktron -//Map loading works! Check out LevelLoaderTester for more. -// -//Revision 1.1 2010/08/10 16:41:57 velktron -//Threw some work into map loading. -// +package p; + +import static data.Defines.MAPBLOCKSHIFT; +import static data.Defines.NF_SUBSECTOR; +import static data.Defines.NF_SUBSECTOR_CLASSIC; +import static data.Defines.PU_LEVEL; +import static data.Limits.MAXPLAYERS; +import static data.Limits.MAXRADIUS; +import data.maplinedef_t; +import data.mapnode_t; +import data.mapsector_t; +import data.mapseg_t; +import data.mapsidedef_t; +import data.mapsubsector_t; +import data.mapthing_t; +import data.mapvertex_t; +import defines.skill_t; +import defines.slopetype_t; +import doom.CommandVariable; +import doom.DoomMain; +import java.io.IOException; +import java.nio.ByteOrder; +import java.util.logging.Level; +import java.util.logging.Logger; +import m.BBox; +import static m.BBox.BOXBOTTOM; +import static m.BBox.BOXLEFT; +import static m.BBox.BOXRIGHT; +import static m.BBox.BOXTOP; +import static m.fixed_t.FRACBITS; +import static m.fixed_t.FixedDiv; +import mochadoom.Loggers; +import rr.line_t; +import static rr.line_t.ML_TWOSIDED; +import rr.node_t; +import rr.sector_t; +import rr.seg_t; +import rr.side_t; +import rr.subsector_t; +import rr.vertex_t; +import s.degenmobj_t; +import static utils.C2JUtils.flags; +import static utils.GenericCopy.malloc; +import w.DoomBuffer; + +//Emacs style mode select -*- Java -*- +//----------------------------------------------------------------------------- +// +// $Id: LevelLoader.java,v 1.44 2012/09/24 17:16:23 velktron Exp $ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// DESCRIPTION: +// Do all the WAD I/O, get map description, +// set up initial state and misc. LUTs. +// +//----------------------------------------------------------------------------- +public class LevelLoader extends AbstractLevelLoader { + + private static final Logger LOGGER = Loggers.getLogger(LevelLoader.class.getName()); + + public static final String rcsid = "$Id: LevelLoader.java,v 1.44 2012/09/24 17:16:23 velktron Exp $"; + + public LevelLoader(DoomMain DM) { + super(DM); + // Traditional loader sets limit. + deathmatchstarts = new mapthing_t[MAX_DEATHMATCH_STARTS]; + } + + /** + * P_LoadVertexes + * + * @throws IOException + */ + public void LoadVertexes(int lump) throws IOException { + // Make a lame-ass attempt at loading some vertexes. + + // Determine number of lumps: + // total lump length / vertex record length. + numvertexes = DOOM.wadLoader.LumpLength(lump) / mapvertex_t.sizeOf(); + + // Load data into cache. + // MAES: we now have a mismatch between memory/disk: in memory, we need an array. + // On disk, we have a single lump/blob. Thus, we need to find a way to deserialize this... + vertexes = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numvertexes, vertex_t::new, vertex_t[]::new); + + // Copy and convert vertex coordinates, + // MAES: not needed. Intermediate mapvertex_t struct skipped. + } + + /** + * P_LoadSegs + * + * @throws IOException + */ + public void LoadSegs(int lump) throws IOException { + + mapseg_t[] data; + mapseg_t ml; + seg_t li; + line_t ldef; + int linedef; + int side; + + // Another disparity between disk/memory. Treat it the same as VERTEXES. + numsegs = DOOM.wadLoader.LumpLength(lump) / mapseg_t.sizeOf(); + segs = malloc(seg_t::new, seg_t[]::new, numsegs); + data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numsegs, mapseg_t::new, mapseg_t[]::new); + + // We're not done yet! + for (int i = 0; i < numsegs; i++) { + li = segs[i]; + ml = data[i]; + li.v1 = vertexes[ml.v1]; + li.v2 = vertexes[ml.v2]; + li.assignVertexValues(); + + li.angle = ((ml.angle) << 16) & 0xFFFFFFFFL; + li.offset = (ml.offset) << 16; + linedef = ml.linedef; + li.linedef = ldef = lines[linedef]; + side = ml.side; + li.sidedef = sides[ldef.sidenum[side]]; + li.frontsector = sides[ldef.sidenum[side]].sector; + if (flags(ldef.flags, ML_TWOSIDED)) { + // MAES: Fix double sided without back side. E.g. Linedef 16103 in Europe.wad + if (ldef.sidenum[side ^ 1] != line_t.NO_INDEX) { + li.backsector = sides[ldef.sidenum[side ^ 1]].sector; + } + // Fix two-sided with no back side. + //else { + //li.backsector=null; + //ldef.flags^=ML_TWOSIDED; + //} + } else { + li.backsector = null; + } + } + + } + + /** + * P_LoadSubsectors + * + * @throws IOException + */ + public void LoadSubsectors(int lump) throws IOException { + mapsubsector_t ms; + subsector_t ss; + mapsubsector_t[] data; + + numsubsectors = DOOM.wadLoader.LumpLength(lump) / mapsubsector_t.sizeOf(); + subsectors = malloc(subsector_t::new, subsector_t[]::new, numsubsectors); + + // Read "mapsubsectors" + data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numsubsectors, mapsubsector_t::new, mapsubsector_t[]::new); + + for (int i = 0; i < numsubsectors; i++) { + ms = data[i]; + ss = subsectors[i]; + ss.numlines = ms.numsegs; + ss.firstline = ms.firstseg; + } + + } + + /** + * P_LoadSectors + * + * @throws IOException + */ + public void LoadSectors(int lump) throws IOException { + mapsector_t[] data; + mapsector_t ms; + sector_t ss; + + numsectors = DOOM.wadLoader.LumpLength(lump) / mapsector_t.sizeOf(); + sectors = malloc(sector_t::new, sector_t[]::new, numsectors); + + // Read "mapsectors" + data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numsectors, mapsector_t::new, mapsector_t[]::new); + + for (int i = 0; i < numsectors; i++) { + ms = data[i]; + ss = sectors[i]; + ss.floorheight = ms.floorheight << FRACBITS; + ss.ceilingheight = ms.ceilingheight << FRACBITS; + ss.floorpic = (short) DOOM.textureManager.FlatNumForName(ms.floorpic); + ss.ceilingpic = (short) DOOM.textureManager.FlatNumForName(ms.ceilingpic); + ss.lightlevel = ms.lightlevel; + ss.special = ms.special; + ss.tag = ms.tag; + ss.thinglist = null; + ss.id = i; + ss.TL = this.DOOM.actions; + ss.RND = this.DOOM.random; + } + + } + + /** + * P_LoadNodes + * + * @throws IOException + */ + public void LoadNodes(int lump) throws IOException { + mapnode_t[] data; + int i; + int j; + int k; + mapnode_t mn; + node_t no; + + numnodes = DOOM.wadLoader.LumpLength(lump) / mapnode_t.sizeOf(); + nodes = malloc(node_t::new, node_t[]::new, numnodes); + + // Read "mapnodes" + data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numnodes, mapnode_t::new, mapnode_t[]::new); + + for (i = 0; i < numnodes; i++) { + mn = data[i]; + no = nodes[i]; + + no.x = mn.x << FRACBITS; + no.y = mn.y << FRACBITS; + no.dx = mn.dx << FRACBITS; + no.dy = mn.dy << FRACBITS; + for (j = 0; j < 2; j++) { + // e6y: support for extended nodes + no.children[j] = (char) mn.children[j]; + + // e6y: support for extended nodes + if (no.children[j] == 0xFFFF) { + no.children[j] = 0xFFFFFFFF; + } else if (flags(no.children[j], NF_SUBSECTOR_CLASSIC)) { + // Convert to extended type + no.children[j] &= ~NF_SUBSECTOR_CLASSIC; + + // haleyjd 11/06/10: check for invalid subsector reference + if (no.children[j] >= numsubsectors) { + LOGGER.log(Level.WARNING, String.format( + "P_LoadNodes: BSP tree references invalid subsector %d.", + no.children[j])); + no.children[j] = 0; + } + + no.children[j] |= NF_SUBSECTOR; + } + + for (k = 0; k < 4; k++) { + no.bbox[j].set(k, mn.bbox[j][k] << FRACBITS); + } + } + } + + } + + /** + * P_LoadThings + * + * @throws IOException + */ + public void LoadThings(int lump) throws IOException { + mapthing_t[] data; + mapthing_t mt; + int numthings; + boolean spawn; + + numthings = DOOM.wadLoader.LumpLength(lump) / mapthing_t.sizeOf(); + // VERY IMPORTANT: since now caching is near-absolute, + // the mapthing_t instances must be CLONED rather than just + // referenced, otherwise missing mobj bugs start happening. + + data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numthings, mapthing_t::new, mapthing_t[]::new); + + for (int i = 0; i < numthings; i++) { + mt = data[i]; + spawn = true; + + // Do not spawn cool, new monsters if !commercial + if (!DOOM.isCommercial()) { + switch (mt.type) { + case 68: // Arachnotron + case 64: // Archvile + case 88: // Boss Brain + case 89: // Boss Shooter + case 69: // Hell Knight + case 67: // Mancubus + case 71: // Pain Elemental + case 65: // Former Human Commando + case 66: // Revenant + case 84: // Wolf SS + spawn = false; + break; + } + } + if (spawn == false) { + break; + } + + // Do spawn all other stuff. + // MAES: we have loaded the shit with the proper endianness, so no fucking around, bitch. + /*mt.x = SHORT(mt.x); + mt.y = SHORT(mt.y); + mt.angle = SHORT(mt.angle); + mt.type = SHORT(mt.type); + mt.options = SHORT(mt.options);*/ + //System.out.printf("Spawning %d %s\n",i,mt.type); + DOOM.actions.SpawnMapThing(mt); + } + + // Status may have changed. It's better to release the resources anyway + //W.UnlockLumpNum(lump); + } + + /** + * P_LoadLineDefs + * Also counts secret lines for intermissions. + * + * @throws IOException + */ + public void LoadLineDefs(int lump) throws IOException { + maplinedef_t[] data; + maplinedef_t mld; + line_t ld; + vertex_t v1; + vertex_t v2; + + numlines = DOOM.wadLoader.LumpLength(lump) / maplinedef_t.sizeOf(); + lines = malloc(line_t::new, line_t[]::new, numlines); + + // Check those actually used in sectors, later on. + used_lines = new boolean[numlines]; + + // read "maplinedefs" + data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numlines, maplinedef_t::new, maplinedef_t[]::new); + + for (int i = 0; i < numlines; i++) { + mld = data[i]; + ld = lines[i]; + + ld.flags = mld.flags; + ld.special = mld.special; + ld.tag = mld.tag; + v1 = ld.v1 = vertexes[(char) mld.v1]; + v2 = ld.v2 = vertexes[(char) mld.v2]; + ld.dx = v2.x - v1.x; + ld.dy = v2.y - v1.y; + // Map value semantics. + ld.assignVertexValues(); + + if (ld.dx == 0) { + ld.slopetype = slopetype_t.ST_VERTICAL; + } else if (ld.dy == 0) { + ld.slopetype = slopetype_t.ST_HORIZONTAL; + } else { + if (FixedDiv(ld.dy, ld.dx) > 0) { + ld.slopetype = slopetype_t.ST_POSITIVE; + } else { + ld.slopetype = slopetype_t.ST_NEGATIVE; + } + } + + if (v1.x < v2.x) { + ld.bbox[BOXLEFT] = v1.x; + ld.bbox[BOXRIGHT] = v2.x; + } else { + ld.bbox[BOXLEFT] = v2.x; + ld.bbox[BOXRIGHT] = v1.x; + } + + if (v1.y < v2.y) { + ld.bbox[BOXBOTTOM] = v1.y; + ld.bbox[BOXTOP] = v2.y; + } else { + ld.bbox[BOXBOTTOM] = v2.y; + ld.bbox[BOXTOP] = v1.y; + } + + ld.sidenum[0] = mld.sidenum[0]; + ld.sidenum[1] = mld.sidenum[1]; + + // Sanity check for two-sided without two valid sides. + if (flags(ld.flags, ML_TWOSIDED)) { + if ((ld.sidenum[0] == line_t.NO_INDEX) || (ld.sidenum[1] == line_t.NO_INDEX)) { + // Well, dat ain't so tu-sided now, ey esse? + ld.flags ^= ML_TWOSIDED; + } + } + + // Front side defined without a valid frontsector. + if (ld.sidenum[0] != line_t.NO_INDEX) { + ld.frontsector = sides[ld.sidenum[0]].sector; + if (ld.frontsector == null) { // // Still null? Bad map. Map to dummy. + ld.frontsector = dummy_sector; + } + + } else { + ld.frontsector = null; + } + + // back side defined without a valid backsector. + if (ld.sidenum[1] != line_t.NO_INDEX) { + ld.backsector = sides[ld.sidenum[1]].sector; + if (ld.backsector == null) { // Still null? Bad map. Map to dummy. + ld.backsector = dummy_sector; + } + } else { + ld.backsector = null; + } + + // If at least one valid sector is defined, then it's not null. + if (ld.frontsector != null || ld.backsector != null) { + this.used_lines[i] = true; + } + + } + + } + + /** + * P_LoadSideDefs + */ + public void LoadSideDefs(int lump) throws IOException { + mapsidedef_t[] data; + mapsidedef_t msd; + side_t sd; + + numsides = DOOM.wadLoader.LumpLength(lump) / mapsidedef_t.sizeOf(); + sides = malloc(side_t::new, side_t[]::new, numsides); + + data = DOOM.wadLoader.CacheLumpNumIntoArray(lump, numsides, mapsidedef_t::new, mapsidedef_t[]::new); + + for (int i = 0; i < numsides; i++) { + msd = data[i]; + sd = sides[i]; + + sd.textureoffset = (msd.textureoffset) << FRACBITS; + sd.rowoffset = (msd.rowoffset) << FRACBITS; + sd.toptexture = (short) DOOM.textureManager.TextureNumForName(msd.toptexture); + sd.bottomtexture = (short) DOOM.textureManager.TextureNumForName(msd.bottomtexture); + sd.midtexture = (short) DOOM.textureManager.TextureNumForName(msd.midtexture); + if (msd.sector < 0) { + sd.sector = dummy_sector; + } else { + sd.sector = sectors[msd.sector]; + } + } + } + + // MAES 22/5/2011 This hack added for PHOBOS2.WAD, in order to + // accomodate for some linedefs having a sector number of "-1". + // Any negative sector will get rewired to this dummy sector. + // PROBABLY, this will handle unused sector/linedefes cleanly? + sector_t dummy_sector = new sector_t(); + + /** + * P_LoadBlockMap + * + * @throws IOException + * + * TODO: generate BLOCKMAP dynamically to + * handle missing cases and increase accuracy. + * + */ + public void LoadBlockMap(int lump) throws IOException { + int count = 0; + + if (DOOM.cVarManager.bool(CommandVariable.BLOCKMAP) || DOOM.wadLoader.LumpLength(lump) < 8 + || (count = DOOM.wadLoader.LumpLength(lump) / 2) >= 0x10000) // e6y + { + CreateBlockMap(); + } else { + + DoomBuffer data = (DoomBuffer) DOOM.wadLoader.CacheLumpNum(lump, PU_LEVEL, DoomBuffer.class); + count = DOOM.wadLoader.LumpLength(lump) / 2; + blockmaplump = new int[count]; + + data.setOrder(ByteOrder.LITTLE_ENDIAN); + data.rewind(); + data.readCharArray(blockmaplump, count); + + // Maes: first four shorts are header data. + bmaporgx = blockmaplump[0] << FRACBITS; + bmaporgy = blockmaplump[1] << FRACBITS; + bmapwidth = blockmaplump[2]; + bmapheight = blockmaplump[3]; + + // MAES: use killough's code to convert terminators to -1 beforehand + for (int i = 4; i < count; i++) { + short t = (short) blockmaplump[i]; // killough 3/1/98 + blockmaplump[i] = (int) (t == -1 ? -1l : t & 0xffff); + } + + // haleyjd 03/04/10: check for blockmap problems + // http://www.doomworld.com/idgames/index.php?id=12935 + if (!VerifyBlockMap(count)) { + LOGGER.log(Level.WARNING, "P_LoadBlockMap: erroneous BLOCKMAP lump may cause crashes.\n"); + LOGGER.log(Level.WARNING, "P_LoadBlockMap: use \"-blockmap\" command line switch for rebuilding\n"); + } + + } + count = bmapwidth * bmapheight; + + // IMPORTANT MODIFICATION: no need to have both blockmaplump AND blockmap. + // If the offsets in the lump are OK, then we can modify them (remove 4) + // and copy the rest of the data in one single data array. This avoids + // reserving memory for two arrays (we can't simply alias one in Java) + blockmap = new int[blockmaplump.length - 4]; + + // Offsets are relative to START OF BLOCKMAP, and IN SHORTS, not bytes. + for (int i = 0; i < blockmaplump.length - 4; i++) { + // Modify indexes so that we don't need two different lumps. + // Can probably be further optimized if we simply shift everything backwards. + // and reuse the same memory space. + if (i < count) { + blockmaplump[i] = blockmaplump[i + 4] - 4; + } else { + // Make terminators definitively -1, different that 0xffff + short t = (short) blockmaplump[i + 4]; // killough 3/1/98 + blockmaplump[i] = (int) (t == -1 ? -1l : t & 0xffff); + } + } + + // clear out mobj chains + // ATTENTION! BUG!!! + // If blocklinks are "cleared" to void -but instantiated- objects, + // very bad bugs happen, especially the second time a level is re-instantiated. + // Probably caused other bugs as well, as an extra object would appear in iterators. + if (blocklinks != null && blocklinks.length == count) { + for (int i = 0; i < count; i++) { + blocklinks[i] = null; + } + } else { + blocklinks = new mobj_t[count]; + } + + // Bye bye. Not needed. + blockmap = blockmaplump; + } + + /** + * P_GroupLines + * Builds sector line lists and subsector sector numbers. + * Finds block bounding boxes for sectors. + */ + public void GroupLines() { + int total; + line_t li; + sector_t sector; + subsector_t ss; + seg_t seg; + int[] bbox = new int[4]; + int block; + + // look up sector number for each subsector + for (int i = 0; i < numsubsectors; i++) { + ss = subsectors[i]; + seg = segs[ss.firstline]; + ss.sector = seg.sidedef.sector; + } + + //linebuffer=new line_t[numsectors][0]; + // count number of lines in each sector + total = 0; + + for (int i = 0; i < numlines; i++) { + li = lines[i]; + total++; + li.frontsector.linecount++; + + if ((li.backsector != null) && (li.backsector != li.frontsector)) { + li.backsector.linecount++; + total++; + } + + } + + // build line tables for each sector + // MAES: we don't really need this in Java. + // linebuffer = new line_t[total]; + // int linebuffercount=0; + // We scan through ALL sectors. + for (int i = 0; i < numsectors; i++) { + sector = sectors[i]; + BBox.ClearBox(bbox); + //sector->lines = linebuffer; + // We can just construct line tables of the correct size + // for each sector. + int countlines = 0; + // We scan through ALL lines.... + + // System.out.println(i+ ": looking for sector -> "+sector); + for (int j = 0; j < numlines; j++) { + li = lines[j]; + + //System.out.println(j+ " front "+li.frontsector+ " back "+li.backsector); + if (li.frontsector == sector || li.backsector == sector) { + // This sector will have one more line. + countlines++; + // Expand bounding box... + BBox.AddToBox(bbox, li.v1.x, li.v1.y); + BBox.AddToBox(bbox, li.v2.x, li.v2.y); + } + } + + // So, this sector must have that many lines. + sector.lines = new line_t[countlines]; + + int addedlines = 0; + int pointline = 0; + + // Add actual lines into sectors. + for (int j = 0; j < numlines; j++) { + li = lines[j]; + // If + if (li.frontsector == sector || li.backsector == sector) { + // This sector will have one more line. + sectors[i].lines[pointline++] = lines[j]; + addedlines++; + } + } + + if (addedlines != sector.linecount) { + DOOM.doomSystem.Error("P_GroupLines: miscounted"); + } + + // set the degenmobj_t to the middle of the bounding box + sector.soundorg = new degenmobj_t(((bbox[BOXRIGHT] + bbox[BOXLEFT]) / 2), + ((bbox[BOXTOP] + bbox[BOXBOTTOM]) / 2), (sector.ceilingheight - sector.floorheight) / 2); + + // adjust bounding box to map blocks + block = (bbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; + block = block >= bmapheight ? bmapheight - 1 : block; + sector.blockbox[BOXTOP] = block; + + block = (bbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; + block = block < 0 ? 0 : block; + sector.blockbox[BOXBOTTOM] = block; + + block = (bbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; + block = block >= bmapwidth ? bmapwidth - 1 : block; + sector.blockbox[BOXRIGHT] = block; + + block = (bbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; + block = block < 0 ? 0 : block; + sector.blockbox[BOXLEFT] = block; + } + + } + + @Override + public void + SetupLevel(int episode, + int map, + int playermask, + skill_t skill) { + int i; + String lumpname; + int lumpnum; + + try { + DOOM.totalkills = DOOM.totalitems = DOOM.totalsecret = DOOM.wminfo.maxfrags = 0; + DOOM.wminfo.partime = 180; + for (i = 0; i < MAXPLAYERS; i++) { + DOOM.players[i].killcount = DOOM.players[i].secretcount + = DOOM.players[i].itemcount = 0; + } + + // Initial height of PointOfView + // will be set by player think. + DOOM.players[DOOM.consoleplayer].viewz = 1; + + // Make sure all sounds are stopped before Z_FreeTags. + DOOM.doomSound.Start(); + + /* + #if 0 // UNUSED + if (debugfile) + { + Z_FreeTags (PU_LEVEL, MAXINT); + Z_FileDumpHeap (debugfile); + } + else + #endif + */ + // Z_FreeTags (PU_LEVEL, PU_PURGELEVEL-1); + // UNUSED W_Profile (); + DOOM.actions.InitThinkers(); + + // if working with a development map, reload it + DOOM.wadLoader.Reload(); + + // find map name + if (DOOM.isCommercial()) { + if (map < 10) { + lumpname = "MAP0" + map; + } else { + lumpname = "MAP" + map; + } + } else { + lumpname = ("E" + + (char) ('0' + episode) + + "M" + + (char) ('0' + map)); + } + + lumpnum = DOOM.wadLoader.GetNumForName(lumpname); + + DOOM.leveltime = 0; + + if (!DOOM.wadLoader.verifyLumpName(lumpnum + ML_BLOCKMAP, LABELS[ML_BLOCKMAP])) { + LOGGER.log(Level.WARNING, "Blockmap missing!"); + } + + // note: most of this ordering is important + this.LoadVertexes(lumpnum + ML_VERTEXES); + this.LoadSectors(lumpnum + ML_SECTORS); + this.LoadSideDefs(lumpnum + ML_SIDEDEFS); + this.LoadLineDefs(lumpnum + ML_LINEDEFS); + this.LoadSubsectors(lumpnum + ML_SSECTORS); + this.LoadNodes(lumpnum + ML_NODES); + this.LoadSegs(lumpnum + ML_SEGS); + + // MAES: in order to apply optimizations and rebuilding, order must be changed. + this.LoadBlockMap(lumpnum + ML_BLOCKMAP); + //this.SanitizeBlockmap(); + //this.getMapBoundingBox(); + + this.LoadReject(lumpnum + ML_REJECT); + + this.GroupLines(); + + DOOM.bodyqueslot = 0; + // Reset to "deathmatch starts" + DOOM.deathmatch_p = 0; + this.LoadThings(lumpnum + ML_THINGS); + + // if deathmatch, randomly spawn the active players + if (DOOM.deathmatch) { + for (i = 0; i < MAXPLAYERS; i++) { + if (DOOM.playeringame[i]) { + DOOM.players[i].mo = null; + DOOM.DeathMatchSpawnPlayer(i); + } + } + + } + + // clear special respawning que + DOOM.actions.ClearRespawnQueue(); + + // set up world state + DOOM.actions.SpawnSpecials(); + + // build subsector connect matrix + // UNUSED P_ConnectSubsectors (); + // preload graphics + if (DOOM.precache) { + DOOM.textureManager.PrecacheLevel(); + // MAES: thinkers are separate than texture management. Maybe split sprite management as well? + DOOM.sceneRenderer.PreCacheThinkers(); + + } + + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error while loading level", e); + } + } + +} + +//$Log: LevelLoader.java,v $ +//Revision 1.44 2012/09/24 17:16:23 velktron +//Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD. +// +//Revision 1.43.2.2 2012/09/24 16:57:16 velktron +//Addressed generics warnings. +// +//Revision 1.43.2.1 2012/03/26 09:53:44 velktron +//Use line_t.NO_INDEX for good measure, when possible. +// +//Revision 1.43 2011/11/03 15:19:51 velktron +//Adapted to using ISpriteManager +// +//Revision 1.42 2011/10/07 16:05:52 velktron +//Now using line_t for ML_* definitions. +// +//Revision 1.41 2011/10/06 16:44:32 velktron +//Proper support for extended nodes, made reject loading into a separate method. +// +//Revision 1.40 2011/09/30 15:20:24 velktron +//Very modified, useless SanitizeBlockmap method ditched. +//Common utility methods moved to superclass. Shares blockmap checking and generation +//with Boom-derived code. Now capable of running Europe.wad. +//TODO: Blockmap generation can be really slow on large levels. +//Optimize better for Java, or parallelize. +// +//Revision 1.39 2011/09/29 17:22:08 velktron +//Blockchain terminators are now -1 (extended) +// +//Revision 1.38 2011/09/29 17:11:32 velktron +//Blockmap optimizations. +// +//Revision 1.37 2011/09/29 15:17:48 velktron +//SetupLevel can propagate exceptions. +// +//Revision 1.36 2011/09/29 13:28:01 velktron +//Extends AbstractLevelLoader +// +//Revision 1.35 2011/09/27 18:04:36 velktron +//Fixed major blockmap bug +// +//Revision 1.34 2011/09/27 16:00:20 velktron +//Minor blockmap stuff. +// +//Revision 1.33 2011/08/24 15:52:04 velktron +//Sets proper ISoundOrigin for sectors (height, too) +// +//Revision 1.32 2011/08/24 15:00:34 velktron +//Improved version, now using createArrayOfObjects. Much better syntax. +// +//Revision 1.31 2011/08/23 16:17:22 velktron +//Got rid of Z remnants. +// +//Revision 1.30 2011/07/27 21:26:19 velktron +//Quieted down debugging for v1.5 release +// +//Revision 1.29 2011/07/25 19:56:53 velktron +//reject matrix size bugfix, fron danmaku branch. +// +//Revision 1.28 2011/07/22 15:37:52 velktron +//Began blockmap autogen code...still WIP +// +//Revision 1.27 2011/07/20 16:14:45 velktron +//Bullet-proofing vs missing or corrupt REJECT table. TODO: built-in system to re-compute it. +// +//Revision 1.26 2011/06/18 23:25:33 velktron +//Removed debugginess +// +//Revision 1.25 2011/06/18 23:21:26 velktron +//-id +// +//Revision 1.24 2011/06/18 23:18:24 velktron +//Added sanitization for broken two-sided sidedefs, and semi-support for extended blockmaps. +// +//Revision 1.23 2011/05/24 11:31:47 velktron +//Adapted to IDoomStatusBar +// +//Revision 1.22 2011/05/22 21:09:34 velktron +//Added spechit overflow handling, and unused linedefs (with -1 sector) handling. +// +//Revision 1.21 2011/05/21 14:53:57 velktron +//Adapted to use new gamemode system. +// +//Revision 1.20 2011/05/20 14:52:23 velktron +//Moved several function from the Renderer and Action code in here, since it made more sense. +// +//Revision 1.19 2011/05/18 16:55:44 velktron +//TEMPORARY TESTING VERSION, DO NOT USE +// +//Revision 1.18 2011/05/17 16:51:20 velktron +//Switched to DoomStatus +// +//Revision 1.17 2011/05/10 10:39:18 velktron +//Semi-playable Techdemo v1.3 milestone +// +//Revision 1.16 2011/05/05 17:24:22 velktron +//Started merging more of _D_'s changes. +// +//Revision 1.15 2010/12/20 17:15:08 velktron +//Made the renderer more OO -> TextureManager and other changes as well. +// +//Revision 1.14 2010/11/22 21:41:22 velktron +//Parallel rendering...sort of.It works, but either the barriers are broken or it's simply not worthwhile at this point :-/ +// +//Revision 1.13 2010/11/22 14:54:53 velktron +//Greater objectification of sectors etc. +// +//Revision 1.12 2010/11/22 01:17:16 velktron +//Fixed blockmap (for the most part), some actions implemented and functional, ambient animation/lighting functional. +// +//Revision 1.11 2010/11/14 20:00:21 velktron +//Bleeding floor bug fixed! +// +//Revision 1.10 2010/11/03 16:48:04 velktron +//"Bling" view angles fixed (perhaps related to the "bleeding line bug"?) +// +//Revision 1.9 2010/09/27 02:27:29 velktron +//BEASTLY update +// +//Revision 1.8 2010/09/23 20:36:45 velktron +//*** empty log message *** +// +//Revision 1.7 2010/09/23 15:11:57 velktron +//A bit closer... +// +//Revision 1.6 2010/09/22 16:40:02 velktron +//MASSIVE changes in the status passing model. +//DoomMain and DoomGame unified. +//Doomstat merged into DoomMain (now status and game functions are one). +// +//Most of DoomMain implemented. Possible to attempt a "classic type" start but will stop when reading sprites. +// +//Revision 1.5 2010/09/21 15:53:37 velktron +//Split the Map ...somewhat... +// +//Revision 1.4 2010/09/14 15:34:01 velktron +//The enormity of this commit is incredible (pun intended) +// +//Revision 1.3 2010/09/08 15:22:18 velktron +//x,y coords in some structs as value semantics. Possible speed increase? +// +//Revision 1.2 2010/09/02 15:56:54 velktron +//Bulk of unified renderer copyediting done. +// +//Some changes like e.g. global separate limits class and instance methods for seg_t and node_t introduced. +// +//Revision 1.1 2010/09/01 15:53:42 velktron +//Graphics data loader implemented....still need to figure out how column caching works, though. +// +//Revision 1.4 2010/08/19 23:14:49 velktron +//Automap +// +//Revision 1.3 2010/08/13 14:06:36 velktron +//Endlevel screen fully functional! +// +//Revision 1.2 2010/08/11 16:31:34 velktron +//Map loading works! Check out LevelLoaderTester for more. +// +//Revision 1.1 2010/08/10 16:41:57 velktron +//Threw some work into map loading. +// \ No newline at end of file diff --git a/src/p/MapUtils.java b/src/p/MapUtils.java index 5a36723..896afdd 100644 --- a/src/p/MapUtils.java +++ b/src/p/MapUtils.java @@ -32,7 +32,7 @@ public class MapUtils { * along the first divline. * This is only called by the addthings * and addlines traversers. - * + * * @return int to be treated as fixed_t */ public static int @@ -76,12 +76,12 @@ public class MapUtils { v2y = (float)v2.y/FRACUNIT; v2dx = (float)v2.dx/FRACUNIT; v2dy = (float)v2.dy/FRACUNIT; - + den = v1dy*v2dx - v1dx*v2dy; if (den == 0) return 0; // parallel - + num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx; frac = num / den; @@ -101,7 +101,7 @@ private static final int P_InterceptVector2(final divline_t v2, final divline_t } /** Used by CrossSubSector - * + * * @param v2 * @param v1 * @return @@ -124,7 +124,7 @@ public static final int P_InterceptVector(final divline_t v2, final divline_t v1 * P_InterceptVector2 Returns the fractional intercept point along the * first divline. This is only called by the addthings and addlines * traversers. - * + * * @param v2 * @param v1 * @returnP_InterceptVector2 @@ -149,4 +149,4 @@ public static final int InterceptVector2(divline_t v2, divline_t v1) { return frac; } -} +} \ No newline at end of file diff --git a/src/p/MobjFlags.java b/src/p/MobjFlags.java index 59df55d..e12f048 100644 --- a/src/p/MobjFlags.java +++ b/src/p/MobjFlags.java @@ -104,4 +104,4 @@ public interface MobjFlags { public static final long MF_NO_DEPTH_TEST = (0x0000002000000000L); public static final long MF_FOREGROUND = (0x0000004000000000L); -} +} \ No newline at end of file diff --git a/src/p/RemoveState.java b/src/p/RemoveState.java index d50afbf..cd0824f 100644 --- a/src/p/RemoveState.java +++ b/src/p/RemoveState.java @@ -2,4 +2,4 @@ public enum RemoveState implements ThinkerStates { REMOVE; -} +} \ No newline at end of file diff --git a/src/p/Resettable.java b/src/p/Resettable.java index aea1478..e4fd3aa 100644 --- a/src/p/Resettable.java +++ b/src/p/Resettable.java @@ -5,4 +5,4 @@ public interface Resettable { public void reset(); -} +} \ No newline at end of file diff --git a/src/p/Specials.java b/src/p/Specials.java index 08d956d..b748355 100644 --- a/src/p/Specials.java +++ b/src/p/Specials.java @@ -148,10 +148,10 @@ public interface Specials { // 4 players, 4 buttons each at once, max. public static final int MAXBUTTONS = 16; - // 1 second, in ticks. + // 1 second, in ticks. public static final int BUTTONTIME = 35; -//extern button_t buttonlist[MAXBUTTONS]; +//extern button_t buttonlist[MAXBUTTONS]; public void P_ChangeSwitchTexture(line_t line, int useAgain); @@ -258,7 +258,7 @@ public interface Specials { char backFrame2[9]; char backFrame3[9]; char backFrame4[9]; - + } slidename_t; */ @@ -281,7 +281,7 @@ public interface Specials { #define SWAITTICS 4 // how many diff. types of anims -#define MAXSLIDEDOORS 5 +#define MAXSLIDEDOORS 5 void P_InitSlidingDoorFrames(void); @@ -339,4 +339,4 @@ public interface Specials { ( line_t* line, int side, mobj_t* thing ); - */ + */ \ No newline at end of file diff --git a/src/p/ThinkerList.java b/src/p/ThinkerList.java index 7cee641..11d4f40 100644 --- a/src/p/ThinkerList.java +++ b/src/p/ThinkerList.java @@ -20,4 +20,4 @@ public interface ThinkerList { thinker_t getRandomThinker(); thinker_t getThinkerCap(); -} +} \ No newline at end of file diff --git a/src/p/ThinkerStates.java b/src/p/ThinkerStates.java index 5cb01a9..5c59dc6 100644 --- a/src/p/ThinkerStates.java +++ b/src/p/ThinkerStates.java @@ -3,4 +3,4 @@ public interface ThinkerStates { int ordinal(); -} +} \ No newline at end of file diff --git a/src/p/UnifiedGameMap.java b/src/p/UnifiedGameMap.java index 8fc6847..648116f 100644 --- a/src/p/UnifiedGameMap.java +++ b/src/p/UnifiedGameMap.java @@ -1,702 +1,702 @@ -package p; - -import data.Limits; -import static data.Limits.BUTTONTIME; -import static data.Limits.MAXANIMS; -import static data.Limits.MAXBUTTONS; -import static data.Limits.MAXSWITCHES; -import data.sounds.sfxenum_t; -import doom.DoomMain; -import doom.SourceCode; -import doom.SourceCode.CauseOfDesyncProbability; -import doom.SourceCode.P_Tick; -import static doom.SourceCode.P_Tick.P_AddThinker; -import static doom.SourceCode.P_Tick.P_InitThinkers; -import doom.thinker_t; -import java.util.Arrays; -import java.util.logging.Level; -import java.util.logging.Logger; -import m.Settings; -import static m.fixed_t.MAPFRACUNIT; -import mochadoom.Engine; -import mochadoom.Loggers; -import rr.ISpriteManager; -import rr.line_t; -import utils.C2JUtils; -import static utils.C2JUtils.eval; -import static utils.GenericCopy.malloc; - -// // FROM SIGHT -public abstract class UnifiedGameMap implements ThinkerList { - - private static final Logger LOGGER = Loggers.getLogger(UnifiedGameMap.class.getName()); - - /** - * killough's code for thinkers seems to be totally broken in M.D, - * so commented it out and will not probably restore, but may invent - * something new in future - * - Good Sign 2017/05/1 - */ - public UnifiedGameMap(DoomMain DOOM) { - this.SW = new Switches(); - this.SPECS = new Specials(); - this.thinkercap = new thinker_t(); - /*for (int i=0; i DOOM; - - // //////////// Internal singletons ////////////// - public ActionFunctions A; - - Specials SPECS; - - Switches SW; - - // //////////////////////////////////////////// - // - // THING POSITION SETTING - // - // - // BLOCK MAP ITERATORS - // For each line/thing in the given mapblock, - // call the passed PIT_* function. - // If the function returns false, - // exit with false without checking anything else. - // - int ptflags; - - /** - * killough's code for thinkers seems to be totally broken in M.D, - * this method is unused - */ - /*protected void UpdateThinker(thinker_t thinker) { - thinker_t th; - // find the class the thinker belongs to - - th_class cls = thinker.thinkerFunction == NOP - ? th_class.th_delete - : (thinker.thinkerFunction == P_MobjThinker - && ((mobj_t) thinker).health > 0 - && (eval((((mobj_t) thinker).flags) & MF_COUNTKILL) - || ((mobj_t) thinker).type == mobjtype_t.MT_SKULL) - ? ( - eval((((mobj_t) thinker).flags) & MF_FRIEND) - ? th_class.th_friends - : th_class.th_enemies - ) : th_class.th_misc - ); - - { - /* Remove from current thread, if in one */ - /*if ((th = thinker.cnext) != null) { - (th.cprev = thinker.cprev).cnext = th; - } - } - - // Add to appropriate thread - th = thinkerclasscap[cls.ordinal()]; - th.cprev.cnext = thinker; - thinker.cnext = th; - thinker.cprev = th.cprev; - th.cprev = thinker; - } - - protected final thinker_t[] thinkerclasscap=new thinker_t[th_class.NUMTHCLASS];*/ - public boolean sight_debug; - - // - // P_InitPicAnims - // - /** - * Floor/ceiling animation sequences, defined by first and last frame, i.e. - * the flat (64x64 tile) name to be used. The full animation sequence is - * given using all the flats between the start and end entry, in the order - * found in the WAD file. - */ - private final animdef_t[] animdefs = { - new animdef_t(false, "NUKAGE3", "NUKAGE1", 8), - new animdef_t(false, "FWATER4", "FWATER1", 8), - new animdef_t(false, "SWATER4", "SWATER1", 8), - new animdef_t(false, "LAVA4", "LAVA1", 8), - new animdef_t(false, "BLOOD3", "BLOOD1", 8), - // DOOM II flat animations. - new animdef_t(false, "RROCK08", "RROCK05", 8), - new animdef_t(false, "SLIME04", "SLIME01", 8), - new animdef_t(false, "SLIME08", "SLIME05", 8), - new animdef_t(false, "SLIME12", "SLIME09", 8), - new animdef_t(true, "BLODGR4", "BLODGR1", 8), - new animdef_t(true, "SLADRIP3", "SLADRIP1", 8), - new animdef_t(true, "BLODRIP4", "BLODRIP1", 8), - new animdef_t(true, "FIREWALL", "FIREWALA", 8), - new animdef_t(true, "GSTFONT3", "GSTFONT1", 8), - new animdef_t(true, "FIRELAVA", "FIRELAV3", 8), - new animdef_t(true, "FIREMAG3", "FIREMAG1", 8), - new animdef_t(true, "FIREBLU2", "FIREBLU1", 8), - new animdef_t(true, "ROCKRED3", "ROCKRED1", 8), - new animdef_t(true, "BFALL4", "BFALL1", 8), - new animdef_t(true, "SFALL4", "SFALL1", 8), - new animdef_t(true, "WFALL4", "WFALL1", 8), - new animdef_t(true, "DBRAIN4", "DBRAIN1", 8) - }; - - // MAES: this was a cheap trick to mark the end of the sequence - // with a value of "-1". - // It won't work in Java, so just use animdefs.length-1 - // new animdef_t(false, "", "", 0) }; - // - // SPECIAL SPAWNING - // - public class Specials { - - public static final int OK = 0, CRUSHED = 1, PASTDEST = 2; - - public line_t[] linespeciallist = new line_t[Limits.MAXLINEANIMS]; - public short numlinespecials; - - /** - * These are NOT the same anims found in defines. Dunno why they fucked up - * this one so badly. Even the type has the same name, but is entirely - * different. No way they could be overlapped/unionized either. So WTF. - * Really. WTF. - */ - public anim_t[] anims = new anim_t[MAXANIMS]; - - // MAES: was a pointer - public int lastanim; - - // - // P_UpdateSpecials - // Animate planes, scroll walls, etc. - // - public boolean levelTimer; - - public int levelTimeCount; - - private Specials() { - - } - - public void UpdateSpecials() { - int pic; - line_t line; - anim_t anim; - - // LEVEL TIMER - if (levelTimer == true) { - levelTimeCount--; - if (levelTimeCount == 0) { - DOOM.ExitLevel(); - } - } - - // ANIMATE FLATS AND TEXTURES GLOBALLY - for (int j = 0; j < lastanim; j++) { - anim = anims[j]; - - for (int i = anim.basepic; i < anim.basepic + anim.numpics; i++) { - pic - = anim.basepic - + ((DOOM.leveltime / anim.speed + i) % anim.numpics); - if (anim.istexture) { - DOOM.textureManager.setTextureTranslation(i, pic); - } else { - DOOM.textureManager.setFlatTranslation(i, pic); - } - } - } - - // ANIMATE LINE SPECIALS - for (int i = 0; i < numlinespecials; i++) { - line = linespeciallist[i]; - switch (line.special) { - case 48: - // EFFECT FIRSTCOL SCROLL + - DOOM.levelLoader.sides[line.sidenum[0]].textureoffset += MAPFRACUNIT; - break; - } - } - - // DO BUTTONS - SW.doButtons(); - } - - public void InitPicAnims() { - Arrays.setAll(anims, i -> new anim_t()); - anim_t lstanim; - // Init animation. MAES: sneaky base pointer conversion ;-) - this.lastanim = 0; - // MAES: for (i=0 ; animdefs[i].istexture != -1 ; i++) - for (int i = 0; i < animdefs.length - 1; i++) { - lstanim = anims[this.lastanim]; - if (animdefs[i].istexture) { - // different episode ? - if (DOOM.textureManager.CheckTextureNumForName(animdefs[i].startname) == -1) { - continue; - } - // So, if it IS a valid texture, it goes straight into anims. - lstanim.picnum = DOOM.textureManager.TextureNumForName(animdefs[i].endname); - lstanim.basepic = DOOM.textureManager.TextureNumForName(animdefs[i].startname); - } else { // If not a texture, it's a flat. - if (DOOM.wadLoader.CheckNumForName(animdefs[i].startname) == -1) { - continue; - } - LOGGER.log(Level.FINER, animdefs[i]::toString); - // Otherwise, lstanim seems to go nowhere :-/ - lstanim.picnum = DOOM.textureManager.FlatNumForName(animdefs[i].endname); - lstanim.basepic = DOOM.textureManager.FlatNumForName(animdefs[i].startname); - } - - lstanim.istexture = animdefs[i].istexture; - lstanim.numpics = lstanim.picnum - lstanim.basepic + 1; - - if (lstanim.numpics < 2) { - DOOM.doomSystem.Error("P_InitPicAnims: bad cycle from %s to %s", - animdefs[i].startname, animdefs[i].endname); - } - - lstanim.speed = animdefs[i].speed; - this.lastanim++; - } - } - - public final void resizeLinesSpecialList() { - linespeciallist = C2JUtils.resize(linespeciallist[0], linespeciallist, linespeciallist.length * 2); - } - - } - - public class Switches { - - private Switches() { - switchlist = new int[MAXSWITCHES]; - initButtonList(); - } - - public void doButtons() { - for (final button_t buttonlist1 : buttonlist) { - if (eval(buttonlist1.btimer)) { - buttonlist1.btimer--; - if (!eval(buttonlist1.btimer)) { - switch (buttonlist1.where) { - case top: - DOOM.levelLoader.sides[buttonlist1.line.sidenum[0]].toptexture = (short) buttonlist1.btexture; - break; - case middle: - DOOM.levelLoader.sides[buttonlist1.line.sidenum[0]].midtexture = (short) buttonlist1.btexture; - break; - case bottom: - DOOM.levelLoader.sides[buttonlist1.line.sidenum[0]].bottomtexture = (short) buttonlist1.btexture; - break; - } - DOOM.doomSound.StartSound(buttonlist1.soundorg, sfxenum_t.sfx_swtchn); - buttonlist1.reset(); - } - } - } - } - - // - // CHANGE THE TEXTURE OF A WALL SWITCH TO ITS OPPOSITE - // - switchlist_t[] alphSwitchList = { - // Doom shareware episode 1 switches - new switchlist_t("SW1BRCOM", "SW2BRCOM", 1), - new switchlist_t("SW1BRN1", "SW2BRN1", 1), - new switchlist_t("SW1BRN2", "SW2BRN2", 1), - new switchlist_t("SW1BRNGN", "SW2BRNGN", 1), - new switchlist_t("SW1BROWN", "SW2BROWN", 1), - new switchlist_t("SW1COMM", "SW2COMM", 1), - new switchlist_t("SW1COMP", "SW2COMP", 1), - new switchlist_t("SW1DIRT", "SW2DIRT", 1), - new switchlist_t("SW1EXIT", "SW2EXIT", 1), - new switchlist_t("SW1GRAY", "SW2GRAY", 1), - new switchlist_t("SW1GRAY1", "SW2GRAY1", 1), - new switchlist_t("SW1METAL", "SW2METAL", 1), - new switchlist_t("SW1PIPE", "SW2PIPE", 1), - new switchlist_t("SW1SLAD", "SW2SLAD", 1), - new switchlist_t("SW1STARG", "SW2STARG", 1), - new switchlist_t("SW1STON1", "SW2STON1", 1), - new switchlist_t("SW1STON2", "SW2STON2", 1), - new switchlist_t("SW1STONE", "SW2STONE", 1), - new switchlist_t("SW1STRTN", "SW2STRTN", 1), - // Doom registered episodes 2&3 switches - new switchlist_t("SW1BLUE", "SW2BLUE", 2), - new switchlist_t("SW1CMT", "SW2CMT", 2), - new switchlist_t("SW1GARG", "SW2GARG", 2), - new switchlist_t("SW1GSTON", "SW2GSTON", 2), - new switchlist_t("SW1HOT", "SW2HOT", 2), - new switchlist_t("SW1LION", "SW2LION", 2), - new switchlist_t("SW1SATYR", "SW2SATYR", 2), - new switchlist_t("SW1SKIN", "SW2SKIN", 2), - new switchlist_t("SW1VINE", "SW2VINE", 2), - new switchlist_t("SW1WOOD", "SW2WOOD", 2), - // Doom II switches - new switchlist_t("SW1PANEL", "SW2PANEL", 3), - new switchlist_t("SW1ROCK", "SW2ROCK", 3), - new switchlist_t("SW1MET2", "SW2MET2", 3), - new switchlist_t("SW1WDMET", "SW2WDMET", 3), - new switchlist_t("SW1BRIK", "SW2BRIK", 3), - new switchlist_t("SW1MOD1", "SW2MOD1", 3), - new switchlist_t("SW1ZIM", "SW2ZIM", 3), - new switchlist_t("SW1STON6", "SW2STON6", 3), - new switchlist_t("SW1TEK", "SW2TEK", 3), - new switchlist_t("SW1MARB", "SW2MARB", 3), - new switchlist_t("SW1SKULL", "SW2SKULL", 3), - new switchlist_t("\0", "\0", 0) - }; - - /** A (runtime generated) list of the KNOWN button types */ - int[] switchlist; - int numswitches; - button_t[] buttonlist; - - // - // P_InitSwitchList - // Only called at game initialization. - // - public void InitSwitchList() { - int i; - int index; - int episode; - - episode = 1; - - // MAES: if this isn't changed Ultimate Doom's switches - // won't work visually. - if (DOOM.isRegistered()) { - episode = 2; - } else if (DOOM.isCommercial()) { - episode = 3; - } - - for (index = 0, i = 0; i < MAXSWITCHES; i++) { - if (index >= switchlist.length) { - // Remove limit - switchlist = Arrays.copyOf(switchlist, switchlist.length > 0 ? switchlist.length * 2 : 8); - } - - // Trickery. Looks for "end of list" marker - // Since the list has pairs of switches, the - // actual number of distinct switches is index/2 - if (alphSwitchList[i].episode == 0) { - numswitches = index / 2; - switchlist[index] = -1; - break; - } - - if (alphSwitchList[i].episode <= episode) { - /* - * // UNUSED - debug? int value; if - * (R_CheckTextureNumForName(alphSwitchList[i].name1) < 0) { - * system.Error("Can't find switch texture '%s'!", - * alphSwitchList[i].name1); continue; } value = - * R_TextureNumForName(alphSwitchList[i].name1); - */ - switchlist[index++] = DOOM.textureManager.TextureNumForName(alphSwitchList[i].name1); - switchlist[index++] = DOOM.textureManager.TextureNumForName(alphSwitchList[i].name2); - } - } - } - - // - // Start a button counting down till it turns off. - // - public final void StartButton(line_t line, bwhere_e w, int texture, int time) { - // See if button is already pressed - for (button_t buttonlist1 : buttonlist) { - if (buttonlist1.btimer != 0 && buttonlist1.line == line) { - return; - } - } - - // At this point, it may mean that THE button of that particular - // line was not active, or simply that there were not enough - // buttons in buttonlist to support an additional entry. - // Search for a free button slot. - for (button_t buttonlist1 : buttonlist) { - if (buttonlist1.btimer == 0) { - buttonlist1.line = line; - buttonlist1.where = w; - buttonlist1.btexture = texture; - buttonlist1.btimer = time; - buttonlist1.soundorg = line.soundorg; - return; - } - } - - /** - * Added config option to disable resize - * - Good Sign 2017/04/26 - */ - // Extremely rare event, We must be able to push more than MAXBUTTONS buttons - // in one tic, which can't normally happen except in really pathological maps. - // In any case, resizing should solve this problem. - if (Engine.getConfig().equals(Settings.extend_button_slots_limit, Boolean.TRUE)) { - buttonlist = C2JUtils.resize(buttonlist[0], buttonlist, buttonlist.length * 2); - // Try again - StartButton(line, w, texture, time); - } else { - LOGGER.log(Level.SEVERE, "P_StartButton: no button slots left!"); - System.exit(1); - } - } - - // - // Function that changes wall texture. - // Tell it if switch is ok to use again (true=yes, it's a button). - // - public void ChangeSwitchTexture(line_t line, boolean useAgain) { - int texTop; - int texMid; - int texBot; - int sound; - - if (!useAgain) { - line.special = 0; - } - - texTop = DOOM.levelLoader.sides[line.sidenum[0]].toptexture; - texMid = DOOM.levelLoader.sides[line.sidenum[0]].midtexture; - texBot = DOOM.levelLoader.sides[line.sidenum[0]].bottomtexture; - - sound = sfxenum_t.sfx_swtchn.ordinal(); - - // EXIT SWITCH? - if (line.special == 11) { - sound = sfxenum_t.sfx_swtchx.ordinal(); - } - - for (int i = 0; i < numswitches * 2; i++) { - if (switchlist[i] == texTop) { - DOOM.doomSound.StartSound(buttonlist[0].soundorg, sound); - DOOM.levelLoader.sides[line.sidenum[0]].toptexture = (short) switchlist[i ^ 1]; - - if (useAgain) { - StartButton(line, bwhere_e.top, switchlist[i], BUTTONTIME); - } - - return; - } else { - if (switchlist[i] == texMid) { - DOOM.doomSound.StartSound(buttonlist[0].soundorg, sound); - DOOM.levelLoader.sides[line.sidenum[0]].midtexture = (short) switchlist[i ^ 1]; - - if (useAgain) { - StartButton(line, bwhere_e.middle, switchlist[i], BUTTONTIME); - } - - return; - } else { - if (switchlist[i] == texBot) { - DOOM.doomSound.StartSound(buttonlist[0].soundorg, sound); - DOOM.levelLoader.sides[line.sidenum[0]].bottomtexture = (short) switchlist[i ^ 1]; - - if (useAgain) { - StartButton(line, bwhere_e.bottom, switchlist[i], BUTTONTIME); - } - - return; - } - } - } - } - } - - public final void initButtonList() { - // Unlike plats, buttonlist needs statically allocated and reusable - // objects. The MAXBUTTONS limit actually applied to buttons PRESSED - // or ACTIVE at once, not how many there can actually be in a map. - - buttonlist = malloc(button_t::new, button_t[]::new, MAXBUTTONS); - } - } - - /* enum PTR { - SlideTraverse, - AimTraverse, - ShootTraverse, - UseTraverse - } */ - /////////// BEGIN MAP OBJECT CODE, USE AS BASIC - // //////////////////////////////// THINKER CODE, GLOBALLY VISIBLE - // ///////////////// - // - // THINKERS - // All thinkers should be allocated by Z_Malloc - // so they can be operated on uniformly. - // The actual structures will vary in size, - // but the first element must be thinker_t. - // - /** Both the head and the tail of the thinkers list */ - public thinker_t thinkercap; - - /** - * killough's code for thinkers seems to be totally broken in M.D, - * so commented it out and will not probably restore, but may invent - * something new in future - * - Good Sign 2017/05/1 - * - * P_InitThinkers - */ - @Override - @SourceCode.Suspicious(CauseOfDesyncProbability.MEDIUM) - @P_Tick.C(P_InitThinkers) - public void InitThinkers() { - - /*for (int i=0; i{ - //@Override - //public void accept(thinker_t thinker) { - /* - try { - System.err.printf("Delete: %s %d<= %s %d => %s %d\n", - ((mobj_t)thinker.prev).type,((mobj_t)thinker.prev).thingnum, - ((mobj_t)thinker).type,((mobj_t)thinker).thingnum, - ((mobj_t)thinker.next).type,((mobj_t)thinker.next).thingnum); - } catch (ClassCastException e){ - - } */ - // Unlike Boom, if we reach here it gets zapped anyway - //if (!thinker->references) - //{ - //{ /* Remove from main thinker list */ - //thinker_t next = thinker.next; - /* Note that currentthinker is guaranteed to point to us, - * and since we're freeing our memory, we had better change that. So - * point it to thinker->prev, so the iterator will correctly move on to - * thinker->prev->next = thinker->next */ - //(next.prev = currentthinker = thinker.prev).next = next; - //thinker.next=thinker.prev=null; - //try { - // System.err.printf("Delete: %s %d <==> %s %d\n", - // ((mobj_t)currentthinker.prev).type,((mobj_t)currentthinker.prev).thingnum, - // ((mobj_t)currentthinker.next).type,((mobj_t)currentthinker.next).thingnum); - //} catch (ClassCastException e){ - //} - //} - //{ - /* Remove from current thinker class list */ - //thinker_t th = thinker.cnext; - //(th.cprev = thinker.cprev).cnext = th; - //thinker.cnext=thinker.cprev=null; - //} - //} - //} -} // End unified map +package p; + +import data.Limits; +import static data.Limits.BUTTONTIME; +import static data.Limits.MAXANIMS; +import static data.Limits.MAXBUTTONS; +import static data.Limits.MAXSWITCHES; +import data.sounds.sfxenum_t; +import doom.DoomMain; +import doom.SourceCode; +import doom.SourceCode.CauseOfDesyncProbability; +import doom.SourceCode.P_Tick; +import static doom.SourceCode.P_Tick.P_AddThinker; +import static doom.SourceCode.P_Tick.P_InitThinkers; +import doom.thinker_t; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; +import m.Settings; +import static m.fixed_t.MAPFRACUNIT; +import mochadoom.Engine; +import mochadoom.Loggers; +import rr.ISpriteManager; +import rr.line_t; +import utils.C2JUtils; +import static utils.C2JUtils.eval; +import static utils.GenericCopy.malloc; + +// // FROM SIGHT +public abstract class UnifiedGameMap implements ThinkerList { + + private static final Logger LOGGER = Loggers.getLogger(UnifiedGameMap.class.getName()); + + /** + * killough's code for thinkers seems to be totally broken in M.D, + * so commented it out and will not probably restore, but may invent + * something new in future + * - Good Sign 2017/05/1 + */ + public UnifiedGameMap(DoomMain DOOM) { + this.SW = new Switches(); + this.SPECS = new Specials(); + this.thinkercap = new thinker_t(); + /*for (int i=0; i DOOM; + + // //////////// Internal singletons ////////////// + public ActionFunctions A; + + Specials SPECS; + + Switches SW; + + // //////////////////////////////////////////// + // + // THING POSITION SETTING + // + // + // BLOCK MAP ITERATORS + // For each line/thing in the given mapblock, + // call the passed PIT_* function. + // If the function returns false, + // exit with false without checking anything else. + // + int ptflags; + + /** + * killough's code for thinkers seems to be totally broken in M.D, + * this method is unused + */ + /*protected void UpdateThinker(thinker_t thinker) { + thinker_t th; + // find the class the thinker belongs to + + th_class cls = thinker.thinkerFunction == NOP + ? th_class.th_delete + : (thinker.thinkerFunction == P_MobjThinker + && ((mobj_t) thinker).health > 0 + && (eval((((mobj_t) thinker).flags) & MF_COUNTKILL) + || ((mobj_t) thinker).type == mobjtype_t.MT_SKULL) + ? ( + eval((((mobj_t) thinker).flags) & MF_FRIEND) + ? th_class.th_friends + : th_class.th_enemies + ) : th_class.th_misc + ); + + { + /* Remove from current thread, if in one */ + /*if ((th = thinker.cnext) != null) { + (th.cprev = thinker.cprev).cnext = th; + } + } + + // Add to appropriate thread + th = thinkerclasscap[cls.ordinal()]; + th.cprev.cnext = thinker; + thinker.cnext = th; + thinker.cprev = th.cprev; + th.cprev = thinker; + } + + protected final thinker_t[] thinkerclasscap=new thinker_t[th_class.NUMTHCLASS];*/ + public boolean sight_debug; + + // + // P_InitPicAnims + // + /** + * Floor/ceiling animation sequences, defined by first and last frame, i.e. + * the flat (64x64 tile) name to be used. The full animation sequence is + * given using all the flats between the start and end entry, in the order + * found in the WAD file. + */ + private final animdef_t[] animdefs = { + new animdef_t(false, "NUKAGE3", "NUKAGE1", 8), + new animdef_t(false, "FWATER4", "FWATER1", 8), + new animdef_t(false, "SWATER4", "SWATER1", 8), + new animdef_t(false, "LAVA4", "LAVA1", 8), + new animdef_t(false, "BLOOD3", "BLOOD1", 8), + // DOOM II flat animations. + new animdef_t(false, "RROCK08", "RROCK05", 8), + new animdef_t(false, "SLIME04", "SLIME01", 8), + new animdef_t(false, "SLIME08", "SLIME05", 8), + new animdef_t(false, "SLIME12", "SLIME09", 8), + new animdef_t(true, "BLODGR4", "BLODGR1", 8), + new animdef_t(true, "SLADRIP3", "SLADRIP1", 8), + new animdef_t(true, "BLODRIP4", "BLODRIP1", 8), + new animdef_t(true, "FIREWALL", "FIREWALA", 8), + new animdef_t(true, "GSTFONT3", "GSTFONT1", 8), + new animdef_t(true, "FIRELAVA", "FIRELAV3", 8), + new animdef_t(true, "FIREMAG3", "FIREMAG1", 8), + new animdef_t(true, "FIREBLU2", "FIREBLU1", 8), + new animdef_t(true, "ROCKRED3", "ROCKRED1", 8), + new animdef_t(true, "BFALL4", "BFALL1", 8), + new animdef_t(true, "SFALL4", "SFALL1", 8), + new animdef_t(true, "WFALL4", "WFALL1", 8), + new animdef_t(true, "DBRAIN4", "DBRAIN1", 8) + }; + + // MAES: this was a cheap trick to mark the end of the sequence + // with a value of "-1". + // It won't work in Java, so just use animdefs.length-1 + // new animdef_t(false, "", "", 0) }; + // + // SPECIAL SPAWNING + // + public class Specials { + + public static final int OK = 0, CRUSHED = 1, PASTDEST = 2; + + public line_t[] linespeciallist = new line_t[Limits.MAXLINEANIMS]; + public short numlinespecials; + + /** + * These are NOT the same anims found in defines. Dunno why they fucked up + * this one so badly. Even the type has the same name, but is entirely + * different. No way they could be overlapped/unionized either. So WTF. + * Really. WTF. + */ + public anim_t[] anims = new anim_t[MAXANIMS]; + + // MAES: was a pointer + public int lastanim; + + // + // P_UpdateSpecials + // Animate planes, scroll walls, etc. + // + public boolean levelTimer; + + public int levelTimeCount; + + private Specials() { + + } + + public void UpdateSpecials() { + int pic; + line_t line; + anim_t anim; + + // LEVEL TIMER + if (levelTimer == true) { + levelTimeCount--; + if (levelTimeCount == 0) { + DOOM.ExitLevel(); + } + } + + // ANIMATE FLATS AND TEXTURES GLOBALLY + for (int j = 0; j < lastanim; j++) { + anim = anims[j]; + + for (int i = anim.basepic; i < anim.basepic + anim.numpics; i++) { + pic + = anim.basepic + + ((DOOM.leveltime / anim.speed + i) % anim.numpics); + if (anim.istexture) { + DOOM.textureManager.setTextureTranslation(i, pic); + } else { + DOOM.textureManager.setFlatTranslation(i, pic); + } + } + } + + // ANIMATE LINE SPECIALS + for (int i = 0; i < numlinespecials; i++) { + line = linespeciallist[i]; + switch (line.special) { + case 48: + // EFFECT FIRSTCOL SCROLL + + DOOM.levelLoader.sides[line.sidenum[0]].textureoffset += MAPFRACUNIT; + break; + } + } + + // DO BUTTONS + SW.doButtons(); + } + + public void InitPicAnims() { + Arrays.setAll(anims, i -> new anim_t()); + anim_t lstanim; + // Init animation. MAES: sneaky base pointer conversion ;-) + this.lastanim = 0; + // MAES: for (i=0 ; animdefs[i].istexture != -1 ; i++) + for (int i = 0; i < animdefs.length - 1; i++) { + lstanim = anims[this.lastanim]; + if (animdefs[i].istexture) { + // different episode ? + if (DOOM.textureManager.CheckTextureNumForName(animdefs[i].startname) == -1) { + continue; + } + // So, if it IS a valid texture, it goes straight into anims. + lstanim.picnum = DOOM.textureManager.TextureNumForName(animdefs[i].endname); + lstanim.basepic = DOOM.textureManager.TextureNumForName(animdefs[i].startname); + } else { // If not a texture, it's a flat. + if (DOOM.wadLoader.CheckNumForName(animdefs[i].startname) == -1) { + continue; + } + LOGGER.log(Level.FINER, animdefs[i]::toString); + // Otherwise, lstanim seems to go nowhere :-/ + lstanim.picnum = DOOM.textureManager.FlatNumForName(animdefs[i].endname); + lstanim.basepic = DOOM.textureManager.FlatNumForName(animdefs[i].startname); + } + + lstanim.istexture = animdefs[i].istexture; + lstanim.numpics = lstanim.picnum - lstanim.basepic + 1; + + if (lstanim.numpics < 2) { + DOOM.doomSystem.Error("P_InitPicAnims: bad cycle from %s to %s", + animdefs[i].startname, animdefs[i].endname); + } + + lstanim.speed = animdefs[i].speed; + this.lastanim++; + } + } + + public final void resizeLinesSpecialList() { + linespeciallist = C2JUtils.resize(linespeciallist[0], linespeciallist, linespeciallist.length * 2); + } + + } + + public class Switches { + + private Switches() { + switchlist = new int[MAXSWITCHES]; + initButtonList(); + } + + public void doButtons() { + for (final button_t buttonlist1 : buttonlist) { + if (eval(buttonlist1.btimer)) { + buttonlist1.btimer--; + if (!eval(buttonlist1.btimer)) { + switch (buttonlist1.where) { + case top: + DOOM.levelLoader.sides[buttonlist1.line.sidenum[0]].toptexture = (short) buttonlist1.btexture; + break; + case middle: + DOOM.levelLoader.sides[buttonlist1.line.sidenum[0]].midtexture = (short) buttonlist1.btexture; + break; + case bottom: + DOOM.levelLoader.sides[buttonlist1.line.sidenum[0]].bottomtexture = (short) buttonlist1.btexture; + break; + } + DOOM.doomSound.StartSound(buttonlist1.soundorg, sfxenum_t.sfx_swtchn); + buttonlist1.reset(); + } + } + } + } + + // + // CHANGE THE TEXTURE OF A WALL SWITCH TO ITS OPPOSITE + // + switchlist_t[] alphSwitchList = { + // Doom shareware episode 1 switches + new switchlist_t("SW1BRCOM", "SW2BRCOM", 1), + new switchlist_t("SW1BRN1", "SW2BRN1", 1), + new switchlist_t("SW1BRN2", "SW2BRN2", 1), + new switchlist_t("SW1BRNGN", "SW2BRNGN", 1), + new switchlist_t("SW1BROWN", "SW2BROWN", 1), + new switchlist_t("SW1COMM", "SW2COMM", 1), + new switchlist_t("SW1COMP", "SW2COMP", 1), + new switchlist_t("SW1DIRT", "SW2DIRT", 1), + new switchlist_t("SW1EXIT", "SW2EXIT", 1), + new switchlist_t("SW1GRAY", "SW2GRAY", 1), + new switchlist_t("SW1GRAY1", "SW2GRAY1", 1), + new switchlist_t("SW1METAL", "SW2METAL", 1), + new switchlist_t("SW1PIPE", "SW2PIPE", 1), + new switchlist_t("SW1SLAD", "SW2SLAD", 1), + new switchlist_t("SW1STARG", "SW2STARG", 1), + new switchlist_t("SW1STON1", "SW2STON1", 1), + new switchlist_t("SW1STON2", "SW2STON2", 1), + new switchlist_t("SW1STONE", "SW2STONE", 1), + new switchlist_t("SW1STRTN", "SW2STRTN", 1), + // Doom registered episodes 2&3 switches + new switchlist_t("SW1BLUE", "SW2BLUE", 2), + new switchlist_t("SW1CMT", "SW2CMT", 2), + new switchlist_t("SW1GARG", "SW2GARG", 2), + new switchlist_t("SW1GSTON", "SW2GSTON", 2), + new switchlist_t("SW1HOT", "SW2HOT", 2), + new switchlist_t("SW1LION", "SW2LION", 2), + new switchlist_t("SW1SATYR", "SW2SATYR", 2), + new switchlist_t("SW1SKIN", "SW2SKIN", 2), + new switchlist_t("SW1VINE", "SW2VINE", 2), + new switchlist_t("SW1WOOD", "SW2WOOD", 2), + // Doom II switches + new switchlist_t("SW1PANEL", "SW2PANEL", 3), + new switchlist_t("SW1ROCK", "SW2ROCK", 3), + new switchlist_t("SW1MET2", "SW2MET2", 3), + new switchlist_t("SW1WDMET", "SW2WDMET", 3), + new switchlist_t("SW1BRIK", "SW2BRIK", 3), + new switchlist_t("SW1MOD1", "SW2MOD1", 3), + new switchlist_t("SW1ZIM", "SW2ZIM", 3), + new switchlist_t("SW1STON6", "SW2STON6", 3), + new switchlist_t("SW1TEK", "SW2TEK", 3), + new switchlist_t("SW1MARB", "SW2MARB", 3), + new switchlist_t("SW1SKULL", "SW2SKULL", 3), + new switchlist_t("\0", "\0", 0) + }; + + /** A (runtime generated) list of the KNOWN button types */ + int[] switchlist; + int numswitches; + button_t[] buttonlist; + + // + // P_InitSwitchList + // Only called at game initialization. + // + public void InitSwitchList() { + int i; + int index; + int episode; + + episode = 1; + + // MAES: if this isn't changed Ultimate Doom's switches + // won't work visually. + if (DOOM.isRegistered()) { + episode = 2; + } else if (DOOM.isCommercial()) { + episode = 3; + } + + for (index = 0, i = 0; i < MAXSWITCHES; i++) { + if (index >= switchlist.length) { + // Remove limit + switchlist = Arrays.copyOf(switchlist, switchlist.length > 0 ? switchlist.length * 2 : 8); + } + + // Trickery. Looks for "end of list" marker + // Since the list has pairs of switches, the + // actual number of distinct switches is index/2 + if (alphSwitchList[i].episode == 0) { + numswitches = index / 2; + switchlist[index] = -1; + break; + } + + if (alphSwitchList[i].episode <= episode) { + /* + * // UNUSED - debug? int value; if + * (R_CheckTextureNumForName(alphSwitchList[i].name1) < 0) { + * system.Error("Can't find switch texture '%s'!", + * alphSwitchList[i].name1); continue; } value = + * R_TextureNumForName(alphSwitchList[i].name1); + */ + switchlist[index++] = DOOM.textureManager.TextureNumForName(alphSwitchList[i].name1); + switchlist[index++] = DOOM.textureManager.TextureNumForName(alphSwitchList[i].name2); + } + } + } + + // + // Start a button counting down till it turns off. + // + public final void StartButton(line_t line, bwhere_e w, int texture, int time) { + // See if button is already pressed + for (button_t buttonlist1 : buttonlist) { + if (buttonlist1.btimer != 0 && buttonlist1.line == line) { + return; + } + } + + // At this point, it may mean that THE button of that particular + // line was not active, or simply that there were not enough + // buttons in buttonlist to support an additional entry. + // Search for a free button slot. + for (button_t buttonlist1 : buttonlist) { + if (buttonlist1.btimer == 0) { + buttonlist1.line = line; + buttonlist1.where = w; + buttonlist1.btexture = texture; + buttonlist1.btimer = time; + buttonlist1.soundorg = line.soundorg; + return; + } + } + + /** + * Added config option to disable resize + * - Good Sign 2017/04/26 + */ + // Extremely rare event, We must be able to push more than MAXBUTTONS buttons + // in one tic, which can't normally happen except in really pathological maps. + // In any case, resizing should solve this problem. + if (Engine.getConfig().equals(Settings.extend_button_slots_limit, Boolean.TRUE)) { + buttonlist = C2JUtils.resize(buttonlist[0], buttonlist, buttonlist.length * 2); + // Try again + StartButton(line, w, texture, time); + } else { + LOGGER.log(Level.SEVERE, "P_StartButton: no button slots left!"); + System.exit(1); + } + } + + // + // Function that changes wall texture. + // Tell it if switch is ok to use again (true=yes, it's a button). + // + public void ChangeSwitchTexture(line_t line, boolean useAgain) { + int texTop; + int texMid; + int texBot; + int sound; + + if (!useAgain) { + line.special = 0; + } + + texTop = DOOM.levelLoader.sides[line.sidenum[0]].toptexture; + texMid = DOOM.levelLoader.sides[line.sidenum[0]].midtexture; + texBot = DOOM.levelLoader.sides[line.sidenum[0]].bottomtexture; + + sound = sfxenum_t.sfx_swtchn.ordinal(); + + // EXIT SWITCH? + if (line.special == 11) { + sound = sfxenum_t.sfx_swtchx.ordinal(); + } + + for (int i = 0; i < numswitches * 2; i++) { + if (switchlist[i] == texTop) { + DOOM.doomSound.StartSound(buttonlist[0].soundorg, sound); + DOOM.levelLoader.sides[line.sidenum[0]].toptexture = (short) switchlist[i ^ 1]; + + if (useAgain) { + StartButton(line, bwhere_e.top, switchlist[i], BUTTONTIME); + } + + return; + } else { + if (switchlist[i] == texMid) { + DOOM.doomSound.StartSound(buttonlist[0].soundorg, sound); + DOOM.levelLoader.sides[line.sidenum[0]].midtexture = (short) switchlist[i ^ 1]; + + if (useAgain) { + StartButton(line, bwhere_e.middle, switchlist[i], BUTTONTIME); + } + + return; + } else { + if (switchlist[i] == texBot) { + DOOM.doomSound.StartSound(buttonlist[0].soundorg, sound); + DOOM.levelLoader.sides[line.sidenum[0]].bottomtexture = (short) switchlist[i ^ 1]; + + if (useAgain) { + StartButton(line, bwhere_e.bottom, switchlist[i], BUTTONTIME); + } + + return; + } + } + } + } + } + + public final void initButtonList() { + // Unlike plats, buttonlist needs statically allocated and reusable + // objects. The MAXBUTTONS limit actually applied to buttons PRESSED + // or ACTIVE at once, not how many there can actually be in a map. + + buttonlist = malloc(button_t::new, button_t[]::new, MAXBUTTONS); + } + } + + /* enum PTR { + SlideTraverse, + AimTraverse, + ShootTraverse, + UseTraverse + } */ + /////////// BEGIN MAP OBJECT CODE, USE AS BASIC + // //////////////////////////////// THINKER CODE, GLOBALLY VISIBLE + // ///////////////// + // + // THINKERS + // All thinkers should be allocated by Z_Malloc + // so they can be operated on uniformly. + // The actual structures will vary in size, + // but the first element must be thinker_t. + // + /** Both the head and the tail of the thinkers list */ + public thinker_t thinkercap; + + /** + * killough's code for thinkers seems to be totally broken in M.D, + * so commented it out and will not probably restore, but may invent + * something new in future + * - Good Sign 2017/05/1 + * + * P_InitThinkers + */ + @Override + @SourceCode.Suspicious(CauseOfDesyncProbability.MEDIUM) + @P_Tick.C(P_InitThinkers) + public void InitThinkers() { + + /*for (int i=0; i{ + //@Override + //public void accept(thinker_t thinker) { + /* + try { + System.err.printf("Delete: %s %d<= %s %d => %s %d\n", + ((mobj_t)thinker.prev).type,((mobj_t)thinker.prev).thingnum, + ((mobj_t)thinker).type,((mobj_t)thinker).thingnum, + ((mobj_t)thinker.next).type,((mobj_t)thinker.next).thingnum); + } catch (ClassCastException e){ + + } */ + // Unlike Boom, if we reach here it gets zapped anyway + //if (!thinker->references) + //{ + //{ /* Remove from main thinker list */ + //thinker_t next = thinker.next; + /* Note that currentthinker is guaranteed to point to us, + * and since we're freeing our memory, we had better change that. So + * point it to thinker->prev, so the iterator will correctly move on to + * thinker->prev->next = thinker->next */ + //(next.prev = currentthinker = thinker.prev).next = next; + //thinker.next=thinker.prev=null; + //try { + // System.err.printf("Delete: %s %d <==> %s %d\n", + // ((mobj_t)currentthinker.prev).type,((mobj_t)currentthinker.prev).thingnum, + // ((mobj_t)currentthinker.next).type,((mobj_t)currentthinker.next).thingnum); + //} catch (ClassCastException e){ + //} + //} + //{ + /* Remove from current thinker class list */ + //thinker_t th = thinker.cnext; + //(th.cprev = thinker.cprev).cnext = th; + //thinker.cnext=thinker.cprev=null; + //} + //} + //} +} // End unified map \ No newline at end of file diff --git a/src/p/anim_t.java b/src/p/anim_t.java index 106fa65..db5cc08 100644 --- a/src/p/anim_t.java +++ b/src/p/anim_t.java @@ -2,7 +2,7 @@ /** Animating textures and planes * There is another anim_t used in wi_stuff, unrelated. - * + * * @author admin * */ @@ -27,4 +27,4 @@ public anim_t(boolean istexture, int picnum, int basepic, int numpics, public int numpics; public int speed; -} +} \ No newline at end of file diff --git a/src/p/animdef_t.java b/src/p/animdef_t.java index b65ca7a..c4e8636 100644 --- a/src/p/animdef_t.java +++ b/src/p/animdef_t.java @@ -8,8 +8,8 @@ /** * Source animation definition. Made readable for compatibility with Boom's - * SWANTBLS system. - * + * SWANTBLS system. + * * @author velktron */ public class animdef_t @@ -32,11 +32,11 @@ public animdef_t(boolean istexture, String endname, String startname, * use "flats on walls functionality of course. */ public boolean istexture; - /** The END name and START name of a texture, given in this order when reading a lump - * The animation system is agnostic to the actual names of of the "in-between" + /** The END name and START name of a texture, given in this order when reading a lump + * The animation system is agnostic to the actual names of of the "in-between" * frames, it's purely pointer based, and only the start/end are constant. It only * counts the actual number of existing textures during initialization time. - * + * */ public String endname, startname; @@ -62,4 +62,4 @@ public static int size() { return 23; } -} +} \ No newline at end of file diff --git a/src/p/button_t.java b/src/p/button_t.java index 1da91ec..ac958b3 100644 --- a/src/p/button_t.java +++ b/src/p/button_t.java @@ -26,4 +26,4 @@ public void reset() { } -} +} \ No newline at end of file diff --git a/src/p/bwhere_e.java b/src/p/bwhere_e.java index 901e3b3..492d2ff 100644 --- a/src/p/bwhere_e.java +++ b/src/p/bwhere_e.java @@ -4,4 +4,4 @@ public enum bwhere_e { top, middle, bottom -} +} \ No newline at end of file diff --git a/src/p/ceiling_e.java b/src/p/ceiling_e.java index a36eecf..72bfb17 100644 --- a/src/p/ceiling_e.java +++ b/src/p/ceiling_e.java @@ -12,4 +12,4 @@ public enum ceiling_e { fastCrushAndRaise, silentCrushAndRaise; -} +} \ No newline at end of file diff --git a/src/p/ceiling_t.java b/src/p/ceiling_t.java index ee39e4a..261e7f6 100644 --- a/src/p/ceiling_t.java +++ b/src/p/ceiling_t.java @@ -48,8 +48,8 @@ public void read(DataInputStream f) throws IOException { @Override public void pack(ByteBuffer b) throws IOException { b.order(ByteOrder.LITTLE_ENDIAN); - super.pack(b); //12 - b.putInt(type.ordinal()); // 16 + super.pack(b); //12 + b.putInt(type.ordinal()); // 16 b.putInt(super.sectorid); // 20 b.putInt(bottomheight); b.putInt(topheight); // 28 @@ -76,4 +76,4 @@ public void unpack(ByteBuffer b) throws IOException { } private static final ByteBuffer readbuffer = ByteBuffer.allocate(48); -} +} \ No newline at end of file diff --git a/src/p/divline_t.java b/src/p/divline_t.java index 4b7d6ba..48a0d57 100644 --- a/src/p/divline_t.java +++ b/src/p/divline_t.java @@ -42,12 +42,12 @@ int PUREFUNC P_PointOnDivlineSide(fixed_t x, fixed_t y, const divline_t *line) int dy; int left; int right; - + if (this.dx==0) { if (x <= this.x) return this.dy > 0; - + return this.dy < 0; } if (this.dy==0) @@ -57,10 +57,10 @@ int PUREFUNC P_PointOnDivlineSide(fixed_t x, fixed_t y, const divline_t *line) return this.dx > 0; } - + dx = (x - this.x); dy = (y - this.y); - + // try to quickly decide by looking at sign bits if ( ((this.dy ^ this.dx ^ dx ^ dy)&0x80000000) !=0) { @@ -68,10 +68,10 @@ int PUREFUNC P_PointOnDivlineSide(fixed_t x, fixed_t y, const divline_t *line) return true; // (left is negative) return false; } - + left = FixedMul ( this.dy>>8, dx>>8 ); right = FixedMul ( dy>>8 , this.dx>>8 ); - + if (right < left) return false; // front side return true; // back side @@ -119,21 +119,21 @@ public divline_t() { < (left = ((x - this.x) >> FRACBITS) * (this.dy >> FRACBITS)) ? 0 : right == left ? 2 : 1; - /* - + /* + int left,right,dx,dy; if (this.dx==0) { if (x==this.x) return 2; - + if (x <= this.x) return eval(this.dy > 0); return eval(this.y < 0); } - + if (this.dy==0) { if (x==this.y) @@ -144,16 +144,16 @@ public divline_t() { return eval(this.dx > 0); } - + dx = (x - this.x); dy = (y - this.y); left = (this.dy>>FRACBITS) * (dx>>FRACBITS); right = (dy>>FRACBITS) * (this.dx>>FRACBITS); - + if (right < left) return 0; // front side - + if (left == right) return 2; return 1; // back side @@ -162,4 +162,4 @@ public divline_t() { private static final boolean olddemo = true; -} +} \ No newline at end of file diff --git a/src/p/floor_e.java b/src/p/floor_e.java index d8cf5b9..787ef6e 100644 --- a/src/p/floor_e.java +++ b/src/p/floor_e.java @@ -24,4 +24,4 @@ public enum floor_e { donutRaise, raiseFloor512 -} +} \ No newline at end of file diff --git a/src/p/floormove_t.java b/src/p/floormove_t.java index 8399971..a15f052 100644 --- a/src/p/floormove_t.java +++ b/src/p/floormove_t.java @@ -29,7 +29,7 @@ public floormove_t() { @Override public void read(DataInputStream f) throws IOException { - super.read(f); // Call thinker reader first + super.read(f); // Call thinker reader first type = floor_e.values()[DoomIO.readLEInt(f)]; crush = DoomIO.readIntBoolean(f); super.sectorid = DoomIO.readLEInt(f); // Sector index (or pointer?) @@ -42,7 +42,7 @@ public void read(DataInputStream f) throws IOException { @Override public void pack(ByteBuffer b) throws IOException { - super.pack(b); //12 + super.pack(b); //12 b.putInt(type.ordinal()); // 16 b.putInt(crush ? 1 : 0); //20 b.putInt(super.sectorid); // 24 @@ -53,4 +53,4 @@ public void pack(ByteBuffer b) throws IOException { b.putInt(speed); // 42 } -} +} \ No newline at end of file diff --git a/src/p/intercept_t.java b/src/p/intercept_t.java index 79a6cba..7620198 100644 --- a/src/p/intercept_t.java +++ b/src/p/intercept_t.java @@ -45,4 +45,4 @@ public Interceptable d() { return (isaline) ? line : thing; } -} +} \ No newline at end of file diff --git a/src/p/mobj_t.java b/src/p/mobj_t.java index 0ef9c50..75b750b 100644 --- a/src/p/mobj_t.java +++ b/src/p/mobj_t.java @@ -1,584 +1,584 @@ -package p; - -import static data.Defines.FLOATSPEED; -import static data.Defines.GRAVITY; -import static data.Defines.VIEWHEIGHT; -import data.Tables; -import static data.info.states; -import data.mapthing_t; -import data.mobjinfo_t; -import data.mobjtype_t; -import data.sounds.sfxenum_t; -import data.spritenum_t; -import data.state_t; -import defines.statenum_t; -import doom.DoomMain; -import doom.SourceCode.fixed_t; -import doom.player_t; -import doom.thinker_t; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.logging.Level; -import java.util.logging.Logger; -import mochadoom.Loggers; -import p.ActiveStates.MobjConsumer; -import static p.MapUtils.AproxDistance; -import rr.subsector_t; -import s.ISoundOrigin; -import static utils.C2JUtils.eval; -import static utils.C2JUtils.pointer; -import w.IPackableDoomObject; -import w.IReadableDoomObject; -import w.IWritableDoomObject; - -/** - * - * NOTES: mobj_t - * - * mobj_ts are used to tell the refresh where to draw an image, tell the world - * simulation when objects are contacted, and tell the sound driver how to - * position a sound. - * - * The refresh uses the next and prev links to follow lists of things in sectors - * as they are being drawn. The sprite, frame, and angle elements determine - * which patch_t is used to draw the sprite if it is visible. The sprite and - * frame values are allmost allways set from state_t structures. The - * statescr.exe utility generates the states.h and states.c files that contain - * the sprite/frame numbers from the statescr.txt source file. The xyz origin - * point represents a point at the bottom middle of the sprite (between the feet - * of a biped). This is the default origin position for patch_ts grabbed with - * lumpy.exe. A walking creature will have its z equal to the floor it is - * standing on. - * - * The sound code uses the x,y, and subsector fields to do stereo positioning of - * any sound effited by the mobj_t. - * - * The play simulation uses the blocklinks, x,y,z, radius, height to determine - * when mobj_ts are touching each other, touching lines in the map, or hit by - * trace lines (gunshots, lines of sight, etc). The mobj_t->flags element has - * various bit flags used by the simulation. - * - * Every mobj_t is linked into a single sector based on its origin coordinates. - * The subsector_t is found with R_PointInSubsector(x,y), and the sector_t can - * be found with subsector->sector. The sector links are only used by the - * rendering code, the play simulation does not care about them at all. - * - * Any mobj_t that needs to be acted upon by something else in the play world - * (block movement, be shot, etc) will also need to be linked into the blockmap. - * If the thing has the MF_NOBLOCK flag set, it will not use the block links. It - * can still interact with other things, but only as the instigator (missiles - * will run into other things, but nothing can run into a missile). Each block - * in the grid is 128*128 units, and knows about every line_t that it contains a - * piece of, and every interactable mobj_t that has its origin contained. - * - * A valid mobj_t is a mobj_t that has the proper subsector_t filled in for its - * xy coordinates and is linked into the sector from which the subsector was - * made, or has the MF_NOSECTOR flag set (the subsector_t needs to be valid even - * if MF_NOSECTOR is set), and is linked into a blockmap block or has the - * MF_NOBLOCKMAP flag set. Links should only be modified by the - * P_[Un]SetThingPosition() functions. Do not change the MF_NO? flags while a - * thing is valid. - * - * Any questions? - * - * @author admin - * - */ -public class mobj_t extends thinker_t implements ISoundOrigin, Interceptable, - IWritableDoomObject, IPackableDoomObject, IReadableDoomObject { - - private static final Logger LOGGER = Loggers.getLogger(mobj_t.class.getName()); - - public final ActionFunctions A; - - public static mobj_t createOn(final DoomMain context) { - if (eval(context.actions)) { - return new mobj_t(context.actions); - } - - return new mobj_t(); - } - - private mobj_t() { - this.spawnpoint = new mapthing_t(); - this.A = null; - } - - private mobj_t(final ActionFunctions A) { - this.spawnpoint = new mapthing_t(); - this.A = A; - // A mobj_t is ALSO a thinker, as it always contains the struct. - // Don't fall for C's trickery ;-) - // this.thinker=new thinker_t(); - } - - /* List: thinker links. */ - // public thinker_t thinker; - /** Info for drawing: position. */ - @fixed_t - public int x, y, z; - - /** More list: links in sector (if needed) */ - public thinker_t snext, sprev; - - // More drawing info: to determine current sprite. - /** - * orientation. This needs to be long or else certain checks will fail...but - * I need to see it working in order to confirm - */ - public long angle; - - /** used to find patch_t and flip value */ - public spritenum_t mobj_sprite; - /** might be ORed with FF_FULLBRIGHT */ - public int mobj_frame; - - /** Interaction info, by BLOCKMAP. Links in blocks (if needed). */ - public thinker_t bnext, bprev; - - /** MAES: was actually a pointer to a struct subsector_s */ - public subsector_t subsector; - - /** The closest interval over all contacted Sectors. */ - @fixed_t - public int floorz, ceilingz; - - /** For movement checking. */ - @fixed_t - public int radius, height; - - /** Momentums, used to update position. */ - @fixed_t - public int momx, momy, momz; - - /** If == validcount, already checked. */ - public int validcount; - - public mobjtype_t type; - // MAES: was a pointer - public mobjinfo_t info; // &mobjinfo[mobj.type] - - public long mobj_tics; // state tic counter - // MAES: was a pointer - public state_t mobj_state; - public long flags; - public int health; - - /** Movement direction, movement generation (zig-zagging). */ - public int movedir; // 0-7 - public int movecount; // when 0, select a new dir - - /** - * Thing being chased/attacked (or NULL), also the originator for missiles. - * MAES: was a pointer - */ - public mobj_t target; - public int p_target; // for savegames - - /** - * Reaction time: if non 0, don't attack yet. Used by player to freeze a bit - * after teleporting. - */ - public int reactiontime; - - /** - * If >0, the target will be chased no matter what (even if shot) - */ - public int threshold; - - /** - * Additional info record for player avatars only. Only valid if type == - * MT_PLAYER struct player_s* player; - */ - public player_t player; - - /** Player number last looked for. */ - public int lastlook; - - /** For nightmare respawn. */ - public mapthing_t spawnpoint; // struct - - /** Thing being chased/attacked for tracers. */ - public mobj_t tracer; // MAES: was a pointer - - // // MF_ flags for mobjs. - // Call P_SpecialThing when touched. - public static final int MF_SPECIAL = 1; - // Blocks. - public static final int MF_SOLID = 2; - // Can be hit. - public static final int MF_SHOOTABLE = 4; - // Don't use the sector links (invisible but touchable). - public static final int MF_NOSECTOR = 8; - // Don't use the blocklinks (inert but displayable) - public static final int MF_NOBLOCKMAP = 16; - - // Not to be activated by sound, deaf monster. - public static final int MF_AMBUSH = 32; - // Will try to attack right back. - public static final int MF_JUSTHIT = 64; - // Will take at least one step before attacking. - public static final int MF_JUSTATTACKED = 128; - // On level spawning (initial position), - // hang from ceiling instead of stand on floor. - public static final int MF_SPAWNCEILING = 256; - // Don't apply gravity (every tic), - // that is, object will float, keeping current height - // or changing it actively. - public static final int MF_NOGRAVITY = 512; - - // Movement flags. - // This allows jumps from high places. - public static final int MF_DROPOFF = 0x400; - // For players, will pick up items. - public static final int MF_PICKUP = 0x800; - // Player cheat. ??? - public static final int MF_NOCLIP = 0x1000; - // Player: keep info about sliding along walls. - public static final int MF_SLIDE = 0x2000; - // Allow moves to any height, no gravity. - // For active floaters, e.g. cacodemons, pain elementals. - public static final int MF_FLOAT = 0x4000; - // Don't cross lines - // ??? or look at heights on teleport. - public static final int MF_TELEPORT = 0x8000; - // Don't hit same species, explode on block. - // Player missiles as well as fireballs of various kinds. - public static final int MF_MISSILE = 0x10000; - // Dropped by a demon, not level spawned. - // E.g. ammo clips dropped by dying former humans. - public static final int MF_DROPPED = 0x20000; - // Use fuzzy draw (shadow demons or spectres), - // temporary player invisibility powerup. - public static final int MF_SHADOW = 0x40000; - // Flag: don't bleed when shot (use puff), - // barrels and shootable furniture shall not bleed. - public static final int MF_NOBLOOD = 0x80000; - // Don't stop moving halfway off a step, - // that is, have dead bodies slide down all the way. - public static final int MF_CORPSE = 0x100000; - // Floating to a height for a move, ??? - // don't auto float to target's height. - public static final int MF_INFLOAT = 0x200000; - - // On kill, count this enemy object - // towards intermission kill total. - // Happy gathering. - public static final int MF_COUNTKILL = 0x400000; - - // On picking up, count this item object - // towards intermission item total. - public static final int MF_COUNTITEM = 0x800000; - - // Special handling: skull in flight. - // Neither a cacodemon nor a missile. - public static final int MF_SKULLFLY = 0x1000000; - - // Don't spawn this object - // in death match mode (e.g. key cards). - public static final int MF_NOTDMATCH = 0x2000000; - - // Player sprites in multiplayer modes are modified - // using an internal color lookup table for re-indexing. - // If 0x4 0x8 or 0xc, - // use a translation table for player colormaps - public static final int MF_TRANSLATION = 0xc000000; - // Hmm ???. - public static final int MF_TRANSSHIFT = 26; - - /* - * The following methods were for the most part "contextless" and - * instance-specific, so they were implemented here rather that being - * scattered all over the package. - */ - /** - * P_SetMobjState Returns true if the mobj is still present. - */ - public boolean SetMobjState(statenum_t state) { - state_t st; - - do { - if (state == statenum_t.S_NULL) { - mobj_state = null; - // MAES/_D_: uncommented this as it should work by now (?). - A.RemoveMobj(this); - return false; - } - - st = states[state.ordinal()]; - mobj_state = st; - mobj_tics = st.tics; - mobj_sprite = st.sprite; - mobj_frame = st.frame; - - // Modified handling. - // Call action functions when the state is set - // TODO: try find a bug - if (st.action.isParamType(MobjConsumer.class)) { - st.action.fun(MobjConsumer.class).accept(A, this); - } - - state = st.nextstate; - } while (!eval(mobj_tics)); - - return true; - } - - /** - * P_ZMovement - */ - public void ZMovement() { - @fixed_t - int dist, delta; - - // check for smooth step up - if ((player != null) && z < floorz) { - player.viewheight -= floorz - z; - - player.deltaviewheight = (VIEWHEIGHT - player.viewheight) >> 3; - } - - // adjust height - z += momz; - - if (((flags & MF_FLOAT) != 0) && target != null) { - // float down towards target if too close - if ((flags & MF_SKULLFLY) == 0 && (flags & MF_INFLOAT) == 0) { - dist = AproxDistance(x - target.x, y - target.y); - - delta = (target.z + (height >> 1)) - z; - - if (delta < 0 && dist < -(delta * 3)) { - z -= FLOATSPEED; - } else if (delta > 0 && dist < (delta * 3)) { - z += FLOATSPEED; - } - } - - } - - // clip movement - if (z <= floorz) { - // hit the floor - - // Note (id): - // somebody left this after the setting momz to 0, - // kinda useless there. - if ((flags & MF_SKULLFLY) != 0) { - // the skull slammed into something - momz = -momz; - } - - if (momz < 0) { - if (player != null && (momz < -GRAVITY * 8)) { - // Squat down. - // Decrease viewheight for a moment - // after hitting the ground (hard), - // and utter appropriate sound. - player.deltaviewheight = momz >> 3; - A.DOOM.doomSound.StartSound(this, sfxenum_t.sfx_oof); - } - momz = 0; - } - z = floorz; - - if ((flags & MF_MISSILE) != 0 && (flags & MF_NOCLIP) == 0) { - A.ExplodeMissile(this); - return; - } - } else if ((flags & MF_NOGRAVITY) == 0) { - if (momz == 0) { - momz = -GRAVITY * 2; - } else { - momz -= GRAVITY; - } - } - - if (z + height > ceilingz) { - // hit the ceiling - if (momz > 0) { - momz = 0; - } - { - z = ceilingz - height; - } - - if ((flags & MF_SKULLFLY) != 0) { // the skull slammed into - // something - momz = -momz; - } - - if ((flags & MF_MISSILE) != 0 && (flags & MF_NOCLIP) == 0) { - A.ExplodeMissile(this); - } - } - } - - public int eflags; // DOOM LEGACY - - // Fields used only during DSG unmarshalling - public int stateid; - public int playerid; - public int p_tracer; - - /** Unique thing id, used during sync debugging */ - public int thingnum; - - public void clear() { - fastclear.rewind(); - try { - this.unpack(mobj_t.fastclear); - } catch (IOException e) { - LOGGER.log(Level.SEVERE, "clear failure", e); - } - } - - // _D_: to permit this object to save/load - @Override - public void read(DataInputStream f) throws IOException { - // More efficient, avoids duplicating code and - // handles little endian better. - buffer.position(0); - buffer.order(ByteOrder.LITTLE_ENDIAN); - f.read(buffer.array()); - this.unpack(buffer); - } - - @Override - public void write(DataOutputStream f) throws IOException { - - // More efficient, avoids duplicating code and - // handles little endian better. - buffer.position(0); - buffer.order(ByteOrder.LITTLE_ENDIAN); - this.pack(buffer); - f.write(buffer.array()); - - } - - @Override - public void pack(ByteBuffer b) throws IOException { - b.order(ByteOrder.LITTLE_ENDIAN); - super.pack(b); // Pack the head thinker. - b.putInt(x); - b.putInt(y); - b.putInt(z); - b.putInt(pointer(snext)); - b.putInt(pointer(sprev)); - b.putInt((int) (this.angle & Tables.BITS32)); - b.putInt(this.mobj_sprite.ordinal()); - b.putInt(this.mobj_frame); - b.putInt(pointer(bnext)); - b.putInt(pointer(bprev)); - b.putInt(pointer(subsector)); - b.putInt(floorz); - b.putInt(ceilingz); - b.putInt(radius); - b.putInt(height); - b.putInt(momx); - b.putInt(momy); - b.putInt(momz); - b.putInt(validcount); - b.putInt(type.ordinal()); - b.putInt(pointer(info)); // TODO: mobjinfo - b.putInt((int) (this.mobj_tics & Tables.BITS32)); - b.putInt(this.mobj_state.id); // TODO: state OK? - b.putInt((int) this.flags); // truncate - b.putInt(this.health); - b.putInt(this.movedir); - b.putInt(this.movecount); - b.putInt(pointer(target)); // TODO: p_target? - b.putInt(this.reactiontime); - b.putInt(this.threshold); - // Check for player. - if (this.player != null) { - b.putInt(1 + this.player.identify()); - - // System.out.printf("Mobj with hashcode %d is player %d",pointer(this),1+this.player.identify()); - } else { - b.putInt(0); - } - b.putInt(lastlook); - spawnpoint.pack(b); - b.putInt(pointer(tracer)); // tracer pointer stored. - - } - - @Override - public void unpack(ByteBuffer b) throws IOException { - b.order(ByteOrder.LITTLE_ENDIAN); - super.unpack(b); // 12 Read the head thinker. - this.x = b.getInt(); // 16 - this.y = b.getInt(); // 20 - this.z = b.getInt(); // 24 - b.getLong(); // TODO: snext, sprev. When are those set? 32 - this.angle = Tables.BITS32 & b.getInt(); // 36 - this.mobj_sprite = spritenum_t.values()[b.getInt()]; // 40 - this.mobj_frame = b.getInt(); // 44 - b.getLong(); // TODO: bnext, bprev. When are those set? 52 - b.getInt(); // TODO: subsector 56 - this.floorz = b.getInt(); // 60 - this.ceilingz = b.getInt(); // 64 - this.radius = b.getInt(); // 68 - this.height = b.getInt(); // 72 - this.momx = b.getInt(); // 76 - this.momy = b.getInt(); // 80 - this.momz = b.getInt(); // 84 - this.validcount = b.getInt(); // 88 - this.type = mobjtype_t.values()[b.getInt()]; // 92 - b.getInt(); // TODO: mobjinfo (deduced from type) //96 - this.mobj_tics = Tables.BITS32 & b.getInt(); // 100 - // System.out.println("State"+f.readLEInt()); - this.stateid = b.getInt(); // TODO: state OK? - this.flags = b.getInt() & Tables.BITS32; // Only 32-bit flags can be restored - this.health = b.getInt(); - this.movedir = b.getInt(); - this.movecount = b.getInt(); - this.p_target = b.getInt(); - this.reactiontime = b.getInt(); - this.threshold = b.getInt(); - this.playerid = b.getInt(); // TODO: player. Non null should mean that - // it IS a player. - this.lastlook = b.getInt(); - spawnpoint.unpack(b); - this.p_tracer = b.getInt(); // TODO: tracer - } - - private static ByteBuffer buffer = ByteBuffer.allocate(154); - private static ByteBuffer fastclear = ByteBuffer.allocate(154); - - /* - * @Override protected void finalize(){ count++; if (count%100==0) - * System.err - * .printf("Total %d Mobj %s@%d finalized free memory: %d\n",count, - * this.type.name(),this.hashCode(),Runtime.getRuntime().freeMemory()); } - */ - protected static int count = 0; - - // TODO: a linked list of sectors where this object appears - // public msecnode_t touching_sectorlist; - // Sound origin stuff - @Override - public final int getX() { - return x; - } - - @Override - public final int getY() { - return y; - } - - @Override - public final int getZ() { - return z; - } - - @Override - public String toString() { - return String.format("%s %d", this.type, this.thingnum); - } - -} +package p; + +import static data.Defines.FLOATSPEED; +import static data.Defines.GRAVITY; +import static data.Defines.VIEWHEIGHT; +import data.Tables; +import static data.info.states; +import data.mapthing_t; +import data.mobjinfo_t; +import data.mobjtype_t; +import data.sounds.sfxenum_t; +import data.spritenum_t; +import data.state_t; +import defines.statenum_t; +import doom.DoomMain; +import doom.SourceCode.fixed_t; +import doom.player_t; +import doom.thinker_t; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.logging.Level; +import java.util.logging.Logger; +import mochadoom.Loggers; +import p.ActiveStates.MobjConsumer; +import static p.MapUtils.AproxDistance; +import rr.subsector_t; +import s.ISoundOrigin; +import static utils.C2JUtils.eval; +import static utils.C2JUtils.pointer; +import w.IPackableDoomObject; +import w.IReadableDoomObject; +import w.IWritableDoomObject; + +/** + * + * NOTES: mobj_t + * + * mobj_ts are used to tell the refresh where to draw an image, tell the world + * simulation when objects are contacted, and tell the sound driver how to + * position a sound. + * + * The refresh uses the next and prev links to follow lists of things in sectors + * as they are being drawn. The sprite, frame, and angle elements determine + * which patch_t is used to draw the sprite if it is visible. The sprite and + * frame values are allmost allways set from state_t structures. The + * statescr.exe utility generates the states.h and states.c files that contain + * the sprite/frame numbers from the statescr.txt source file. The xyz origin + * point represents a point at the bottom middle of the sprite (between the feet + * of a biped). This is the default origin position for patch_ts grabbed with + * lumpy.exe. A walking creature will have its z equal to the floor it is + * standing on. + * + * The sound code uses the x,y, and subsector fields to do stereo positioning of + * any sound effited by the mobj_t. + * + * The play simulation uses the blocklinks, x,y,z, radius, height to determine + * when mobj_ts are touching each other, touching lines in the map, or hit by + * trace lines (gunshots, lines of sight, etc). The mobj_t->flags element has + * various bit flags used by the simulation. + * + * Every mobj_t is linked into a single sector based on its origin coordinates. + * The subsector_t is found with R_PointInSubsector(x,y), and the sector_t can + * be found with subsector->sector. The sector links are only used by the + * rendering code, the play simulation does not care about them at all. + * + * Any mobj_t that needs to be acted upon by something else in the play world + * (block movement, be shot, etc) will also need to be linked into the blockmap. + * If the thing has the MF_NOBLOCK flag set, it will not use the block links. It + * can still interact with other things, but only as the instigator (missiles + * will run into other things, but nothing can run into a missile). Each block + * in the grid is 128*128 units, and knows about every line_t that it contains a + * piece of, and every interactable mobj_t that has its origin contained. + * + * A valid mobj_t is a mobj_t that has the proper subsector_t filled in for its + * xy coordinates and is linked into the sector from which the subsector was + * made, or has the MF_NOSECTOR flag set (the subsector_t needs to be valid even + * if MF_NOSECTOR is set), and is linked into a blockmap block or has the + * MF_NOBLOCKMAP flag set. Links should only be modified by the + * P_[Un]SetThingPosition() functions. Do not change the MF_NO? flags while a + * thing is valid. + * + * Any questions? + * + * @author admin + * + */ +public class mobj_t extends thinker_t implements ISoundOrigin, Interceptable, + IWritableDoomObject, IPackableDoomObject, IReadableDoomObject { + + private static final Logger LOGGER = Loggers.getLogger(mobj_t.class.getName()); + + public final ActionFunctions A; + + public static mobj_t createOn(final DoomMain context) { + if (eval(context.actions)) { + return new mobj_t(context.actions); + } + + return new mobj_t(); + } + + private mobj_t() { + this.spawnpoint = new mapthing_t(); + this.A = null; + } + + private mobj_t(final ActionFunctions A) { + this.spawnpoint = new mapthing_t(); + this.A = A; + // A mobj_t is ALSO a thinker, as it always contains the struct. + // Don't fall for C's trickery ;-) + // this.thinker=new thinker_t(); + } + + /* List: thinker links. */ + // public thinker_t thinker; + /** Info for drawing: position. */ + @fixed_t + public int x, y, z; + + /** More list: links in sector (if needed) */ + public thinker_t snext, sprev; + + // More drawing info: to determine current sprite. + /** + * orientation. This needs to be long or else certain checks will fail...but + * I need to see it working in order to confirm + */ + public long angle; + + /** used to find patch_t and flip value */ + public spritenum_t mobj_sprite; + /** might be ORed with FF_FULLBRIGHT */ + public int mobj_frame; + + /** Interaction info, by BLOCKMAP. Links in blocks (if needed). */ + public thinker_t bnext, bprev; + + /** MAES: was actually a pointer to a struct subsector_s */ + public subsector_t subsector; + + /** The closest interval over all contacted Sectors. */ + @fixed_t + public int floorz, ceilingz; + + /** For movement checking. */ + @fixed_t + public int radius, height; + + /** Momentums, used to update position. */ + @fixed_t + public int momx, momy, momz; + + /** If == validcount, already checked. */ + public int validcount; + + public mobjtype_t type; + // MAES: was a pointer + public mobjinfo_t info; // &mobjinfo[mobj.type] + + public long mobj_tics; // state tic counter + // MAES: was a pointer + public state_t mobj_state; + public long flags; + public int health; + + /** Movement direction, movement generation (zig-zagging). */ + public int movedir; // 0-7 + public int movecount; // when 0, select a new dir + + /** + * Thing being chased/attacked (or NULL), also the originator for missiles. + * MAES: was a pointer + */ + public mobj_t target; + public int p_target; // for savegames + + /** + * Reaction time: if non 0, don't attack yet. Used by player to freeze a bit + * after teleporting. + */ + public int reactiontime; + + /** + * If >0, the target will be chased no matter what (even if shot) + */ + public int threshold; + + /** + * Additional info record for player avatars only. Only valid if type == + * MT_PLAYER struct player_s* player; + */ + public player_t player; + + /** Player number last looked for. */ + public int lastlook; + + /** For nightmare respawn. */ + public mapthing_t spawnpoint; // struct + + /** Thing being chased/attacked for tracers. */ + public mobj_t tracer; // MAES: was a pointer + + // // MF_ flags for mobjs. + // Call P_SpecialThing when touched. + public static final int MF_SPECIAL = 1; + // Blocks. + public static final int MF_SOLID = 2; + // Can be hit. + public static final int MF_SHOOTABLE = 4; + // Don't use the sector links (invisible but touchable). + public static final int MF_NOSECTOR = 8; + // Don't use the blocklinks (inert but displayable) + public static final int MF_NOBLOCKMAP = 16; + + // Not to be activated by sound, deaf monster. + public static final int MF_AMBUSH = 32; + // Will try to attack right back. + public static final int MF_JUSTHIT = 64; + // Will take at least one step before attacking. + public static final int MF_JUSTATTACKED = 128; + // On level spawning (initial position), + // hang from ceiling instead of stand on floor. + public static final int MF_SPAWNCEILING = 256; + // Don't apply gravity (every tic), + // that is, object will float, keeping current height + // or changing it actively. + public static final int MF_NOGRAVITY = 512; + + // Movement flags. + // This allows jumps from high places. + public static final int MF_DROPOFF = 0x400; + // For players, will pick up items. + public static final int MF_PICKUP = 0x800; + // Player cheat. ??? + public static final int MF_NOCLIP = 0x1000; + // Player: keep info about sliding along walls. + public static final int MF_SLIDE = 0x2000; + // Allow moves to any height, no gravity. + // For active floaters, e.g. cacodemons, pain elementals. + public static final int MF_FLOAT = 0x4000; + // Don't cross lines + // ??? or look at heights on teleport. + public static final int MF_TELEPORT = 0x8000; + // Don't hit same species, explode on block. + // Player missiles as well as fireballs of various kinds. + public static final int MF_MISSILE = 0x10000; + // Dropped by a demon, not level spawned. + // E.g. ammo clips dropped by dying former humans. + public static final int MF_DROPPED = 0x20000; + // Use fuzzy draw (shadow demons or spectres), + // temporary player invisibility powerup. + public static final int MF_SHADOW = 0x40000; + // Flag: don't bleed when shot (use puff), + // barrels and shootable furniture shall not bleed. + public static final int MF_NOBLOOD = 0x80000; + // Don't stop moving halfway off a step, + // that is, have dead bodies slide down all the way. + public static final int MF_CORPSE = 0x100000; + // Floating to a height for a move, ??? + // don't auto float to target's height. + public static final int MF_INFLOAT = 0x200000; + + // On kill, count this enemy object + // towards intermission kill total. + // Happy gathering. + public static final int MF_COUNTKILL = 0x400000; + + // On picking up, count this item object + // towards intermission item total. + public static final int MF_COUNTITEM = 0x800000; + + // Special handling: skull in flight. + // Neither a cacodemon nor a missile. + public static final int MF_SKULLFLY = 0x1000000; + + // Don't spawn this object + // in death match mode (e.g. key cards). + public static final int MF_NOTDMATCH = 0x2000000; + + // Player sprites in multiplayer modes are modified + // using an internal color lookup table for re-indexing. + // If 0x4 0x8 or 0xc, + // use a translation table for player colormaps + public static final int MF_TRANSLATION = 0xc000000; + // Hmm ???. + public static final int MF_TRANSSHIFT = 26; + + /* + * The following methods were for the most part "contextless" and + * instance-specific, so they were implemented here rather that being + * scattered all over the package. + */ + /** + * P_SetMobjState Returns true if the mobj is still present. + */ + public boolean SetMobjState(statenum_t state) { + state_t st; + + do { + if (state == statenum_t.S_NULL) { + mobj_state = null; + // MAES/_D_: uncommented this as it should work by now (?). + A.RemoveMobj(this); + return false; + } + + st = states[state.ordinal()]; + mobj_state = st; + mobj_tics = st.tics; + mobj_sprite = st.sprite; + mobj_frame = st.frame; + + // Modified handling. + // Call action functions when the state is set + // TODO: try find a bug + if (st.action.isParamType(MobjConsumer.class)) { + st.action.fun(MobjConsumer.class).accept(A, this); + } + + state = st.nextstate; + } while (!eval(mobj_tics)); + + return true; + } + + /** + * P_ZMovement + */ + public void ZMovement() { + @fixed_t + int dist, delta; + + // check for smooth step up + if ((player != null) && z < floorz) { + player.viewheight -= floorz - z; + + player.deltaviewheight = (VIEWHEIGHT - player.viewheight) >> 3; + } + + // adjust height + z += momz; + + if (((flags & MF_FLOAT) != 0) && target != null) { + // float down towards target if too close + if ((flags & MF_SKULLFLY) == 0 && (flags & MF_INFLOAT) == 0) { + dist = AproxDistance(x - target.x, y - target.y); + + delta = (target.z + (height >> 1)) - z; + + if (delta < 0 && dist < -(delta * 3)) { + z -= FLOATSPEED; + } else if (delta > 0 && dist < (delta * 3)) { + z += FLOATSPEED; + } + } + + } + + // clip movement + if (z <= floorz) { + // hit the floor + + // Note (id): + // somebody left this after the setting momz to 0, + // kinda useless there. + if ((flags & MF_SKULLFLY) != 0) { + // the skull slammed into something + momz = -momz; + } + + if (momz < 0) { + if (player != null && (momz < -GRAVITY * 8)) { + // Squat down. + // Decrease viewheight for a moment + // after hitting the ground (hard), + // and utter appropriate sound. + player.deltaviewheight = momz >> 3; + A.DOOM.doomSound.StartSound(this, sfxenum_t.sfx_oof); + } + momz = 0; + } + z = floorz; + + if ((flags & MF_MISSILE) != 0 && (flags & MF_NOCLIP) == 0) { + A.ExplodeMissile(this); + return; + } + } else if ((flags & MF_NOGRAVITY) == 0) { + if (momz == 0) { + momz = -GRAVITY * 2; + } else { + momz -= GRAVITY; + } + } + + if (z + height > ceilingz) { + // hit the ceiling + if (momz > 0) { + momz = 0; + } + { + z = ceilingz - height; + } + + if ((flags & MF_SKULLFLY) != 0) { // the skull slammed into + // something + momz = -momz; + } + + if ((flags & MF_MISSILE) != 0 && (flags & MF_NOCLIP) == 0) { + A.ExplodeMissile(this); + } + } + } + + public int eflags; // DOOM LEGACY + + // Fields used only during DSG unmarshalling + public int stateid; + public int playerid; + public int p_tracer; + + /** Unique thing id, used during sync debugging */ + public int thingnum; + + public void clear() { + fastclear.rewind(); + try { + this.unpack(mobj_t.fastclear); + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "clear failure", e); + } + } + + // _D_: to permit this object to save/load + @Override + public void read(DataInputStream f) throws IOException { + // More efficient, avoids duplicating code and + // handles little endian better. + buffer.position(0); + buffer.order(ByteOrder.LITTLE_ENDIAN); + f.read(buffer.array()); + this.unpack(buffer); + } + + @Override + public void write(DataOutputStream f) throws IOException { + + // More efficient, avoids duplicating code and + // handles little endian better. + buffer.position(0); + buffer.order(ByteOrder.LITTLE_ENDIAN); + this.pack(buffer); + f.write(buffer.array()); + + } + + @Override + public void pack(ByteBuffer b) throws IOException { + b.order(ByteOrder.LITTLE_ENDIAN); + super.pack(b); // Pack the head thinker. + b.putInt(x); + b.putInt(y); + b.putInt(z); + b.putInt(pointer(snext)); + b.putInt(pointer(sprev)); + b.putInt((int) (this.angle & Tables.BITS32)); + b.putInt(this.mobj_sprite.ordinal()); + b.putInt(this.mobj_frame); + b.putInt(pointer(bnext)); + b.putInt(pointer(bprev)); + b.putInt(pointer(subsector)); + b.putInt(floorz); + b.putInt(ceilingz); + b.putInt(radius); + b.putInt(height); + b.putInt(momx); + b.putInt(momy); + b.putInt(momz); + b.putInt(validcount); + b.putInt(type.ordinal()); + b.putInt(pointer(info)); // TODO: mobjinfo + b.putInt((int) (this.mobj_tics & Tables.BITS32)); + b.putInt(this.mobj_state.id); // TODO: state OK? + b.putInt((int) this.flags); // truncate + b.putInt(this.health); + b.putInt(this.movedir); + b.putInt(this.movecount); + b.putInt(pointer(target)); // TODO: p_target? + b.putInt(this.reactiontime); + b.putInt(this.threshold); + // Check for player. + if (this.player != null) { + b.putInt(1 + this.player.identify()); + + // System.out.printf("Mobj with hashcode %d is player %d",pointer(this),1+this.player.identify()); + } else { + b.putInt(0); + } + b.putInt(lastlook); + spawnpoint.pack(b); + b.putInt(pointer(tracer)); // tracer pointer stored. + + } + + @Override + public void unpack(ByteBuffer b) throws IOException { + b.order(ByteOrder.LITTLE_ENDIAN); + super.unpack(b); // 12 Read the head thinker. + this.x = b.getInt(); // 16 + this.y = b.getInt(); // 20 + this.z = b.getInt(); // 24 + b.getLong(); // TODO: snext, sprev. When are those set? 32 + this.angle = Tables.BITS32 & b.getInt(); // 36 + this.mobj_sprite = spritenum_t.values()[b.getInt()]; // 40 + this.mobj_frame = b.getInt(); // 44 + b.getLong(); // TODO: bnext, bprev. When are those set? 52 + b.getInt(); // TODO: subsector 56 + this.floorz = b.getInt(); // 60 + this.ceilingz = b.getInt(); // 64 + this.radius = b.getInt(); // 68 + this.height = b.getInt(); // 72 + this.momx = b.getInt(); // 76 + this.momy = b.getInt(); // 80 + this.momz = b.getInt(); // 84 + this.validcount = b.getInt(); // 88 + this.type = mobjtype_t.values()[b.getInt()]; // 92 + b.getInt(); // TODO: mobjinfo (deduced from type) //96 + this.mobj_tics = Tables.BITS32 & b.getInt(); // 100 + // System.out.println("State"+f.readLEInt()); + this.stateid = b.getInt(); // TODO: state OK? + this.flags = b.getInt() & Tables.BITS32; // Only 32-bit flags can be restored + this.health = b.getInt(); + this.movedir = b.getInt(); + this.movecount = b.getInt(); + this.p_target = b.getInt(); + this.reactiontime = b.getInt(); + this.threshold = b.getInt(); + this.playerid = b.getInt(); // TODO: player. Non null should mean that + // it IS a player. + this.lastlook = b.getInt(); + spawnpoint.unpack(b); + this.p_tracer = b.getInt(); // TODO: tracer + } + + private static ByteBuffer buffer = ByteBuffer.allocate(154); + private static ByteBuffer fastclear = ByteBuffer.allocate(154); + + /* + * @Override protected void finalize(){ count++; if (count%100==0) + * System.err + * .printf("Total %d Mobj %s@%d finalized free memory: %d\n",count, + * this.type.name(),this.hashCode(),Runtime.getRuntime().freeMemory()); } + */ + protected static int count = 0; + + // TODO: a linked list of sectors where this object appears + // public msecnode_t touching_sectorlist; + // Sound origin stuff + @Override + public final int getX() { + return x; + } + + @Override + public final int getY() { + return y; + } + + @Override + public final int getZ() { + return z; + } + + @Override + public String toString() { + return String.format("%s %d", this.type, this.thingnum); + } + +} \ No newline at end of file diff --git a/src/p/plat_e.java b/src/p/plat_e.java index 5ffa986..b618a83 100644 --- a/src/p/plat_e.java +++ b/src/p/plat_e.java @@ -8,4 +8,4 @@ public enum plat_e { down, waiting, in_stasis -} +} \ No newline at end of file diff --git a/src/p/plat_t.java b/src/p/plat_t.java index c0dcdff..6303a91 100644 --- a/src/p/plat_t.java +++ b/src/p/plat_t.java @@ -32,7 +32,7 @@ public plat_t() { @Override public void read(DataInputStream f) throws IOException { - super.read(f); // Call thinker reader first + super.read(f); // Call thinker reader first super.sectorid = DoomIO.readLEInt(f); // Sector index speed = DoomIO.readLEInt(f); low = DoomIO.readLEInt(f); @@ -50,7 +50,7 @@ public void read(DataInputStream f) throws IOException { @Override public void pack(ByteBuffer b) throws IOException { - super.pack(b); //12 + super.pack(b); //12 b.putInt(super.sectorid); // 16 b.putInt(speed);//20 b.putInt(low); // 24 @@ -78,13 +78,13 @@ public vldoor_t asVlDoor(sector_t[] sectors) { // 1 = up, 0 = waiting at top, -1 = down int direction; - + // tics to wait at the top int topwait; // (keep in case a door going down is reset) // when it reaches 0, start going down int topcountdown; - + } vldoor_t; */ @@ -102,4 +102,4 @@ public vldoor_t asVlDoor(sector_t[] sectors) { return tmp; } -} +} \ No newline at end of file diff --git a/src/p/plattype_e.java b/src/p/plattype_e.java index 1c3a3e5..76da5b7 100644 --- a/src/p/plattype_e.java +++ b/src/p/plattype_e.java @@ -7,4 +7,4 @@ public enum plattype_e { raiseToNearestAndChange, blazeDWUS -} +} \ No newline at end of file diff --git a/src/p/pspdef_t.java b/src/p/pspdef_t.java index 81ce85d..5c3d54b 100644 --- a/src/p/pspdef_t.java +++ b/src/p/pspdef_t.java @@ -43,4 +43,4 @@ public void pack(ByteBuffer f) throws IOException { f.putInt(sy); } -} +} \ No newline at end of file diff --git a/src/p/result_e.java b/src/p/result_e.java index 7c3e6ce..a74331c 100644 --- a/src/p/result_e.java +++ b/src/p/result_e.java @@ -4,4 +4,4 @@ public enum result_e { ok, crushed, pastdest -} +} \ No newline at end of file diff --git a/src/p/sd_e.java b/src/p/sd_e.java index 2addc4a..41ca544 100644 --- a/src/p/sd_e.java +++ b/src/p/sd_e.java @@ -4,4 +4,4 @@ public enum sd_e { sd_opening, sd_waiting, sd_closing -} +} \ No newline at end of file diff --git a/src/p/sdt_e.java b/src/p/sdt_e.java index 95282ed..ce4e7f2 100644 --- a/src/p/sdt_e.java +++ b/src/p/sdt_e.java @@ -4,4 +4,4 @@ public enum sdt_e { sdt_openOnly, sdt_closeOnly, sdt_openAndClose -} +} \ No newline at end of file diff --git a/src/p/slidedoor_t.java b/src/p/slidedoor_t.java index 027bb89..37cd6c7 100644 --- a/src/p/slidedoor_t.java +++ b/src/p/slidedoor_t.java @@ -1,24 +1,24 @@ -package p; - -import static p.ActiveStates.T_SlidingDoor; -import rr.SectorAction; -import rr.line_t; -import rr.sector_t; - -public class slidedoor_t extends SectorAction { - - public sdt_e type; - public line_t line; - public int frame; - public int whichDoorIndex; - public int timer; - public sector_t frontsector; - public sector_t backsector; - public sd_e status; - - public slidedoor_t() { - type = sdt_e.sdt_closeOnly; - status = sd_e.sd_closing; - thinkerFunction = T_SlidingDoor; - } -} +package p; + +import static p.ActiveStates.T_SlidingDoor; +import rr.SectorAction; +import rr.line_t; +import rr.sector_t; + +public class slidedoor_t extends SectorAction { + + public sdt_e type; + public line_t line; + public int frame; + public int whichDoorIndex; + public int timer; + public sector_t frontsector; + public sector_t backsector; + public sd_e status; + + public slidedoor_t() { + type = sdt_e.sdt_closeOnly; + status = sd_e.sd_closing; + thinkerFunction = T_SlidingDoor; + } +} \ No newline at end of file diff --git a/src/p/slideframe_t.java b/src/p/slideframe_t.java index 6d89536..1076ec8 100644 --- a/src/p/slideframe_t.java +++ b/src/p/slideframe_t.java @@ -4,4 +4,4 @@ public class slideframe_t { public int[] frontFrames = new int[4]; public int[] backFrames = new int[4]; -} +} \ No newline at end of file diff --git a/src/p/slidename_t.java b/src/p/slidename_t.java index 2d9000f..4cdb80d 100644 --- a/src/p/slidename_t.java +++ b/src/p/slidename_t.java @@ -28,4 +28,4 @@ public slidename_t(String frontFrame1, String frontFrame2, public String backFrame3; public String backFrame4; -} +} \ No newline at end of file diff --git a/src/p/stair_e.java b/src/p/stair_e.java index 132a052..7fe40af 100644 --- a/src/p/stair_e.java +++ b/src/p/stair_e.java @@ -2,5 +2,5 @@ public enum stair_e { build8, // slowly build by 8 - turbo16 // quickly build by 16 -} + turbo16 // quickly build by 16 +} \ No newline at end of file diff --git a/src/p/strobe_t.java b/src/p/strobe_t.java index da11fdb..c70ad06 100644 --- a/src/p/strobe_t.java +++ b/src/p/strobe_t.java @@ -35,7 +35,7 @@ public void StrobeFlash() { @Override public void read(DataInputStream f) throws IOException { - super.read(f); // Call thinker reader first + super.read(f); // Call thinker reader first super.sectorid = DoomIO.readLEInt(f); // Sector index count = DoomIO.readLEInt(f); maxlight = DoomIO.readLEInt(f); @@ -46,7 +46,7 @@ public void read(DataInputStream f) throws IOException { @Override public void pack(ByteBuffer b) throws IOException { - super.pack(b); //12 + super.pack(b); //12 b.putInt(super.sectorid); // 16 b.putInt(count); //20 b.putInt(maxlight);//24 @@ -54,4 +54,4 @@ public void pack(ByteBuffer b) throws IOException { b.putInt(darktime);//32 b.putInt(brighttime);//36 } -}; +}; \ No newline at end of file diff --git a/src/p/switchlist_t.java b/src/p/switchlist_t.java index a0541cb..ef26320 100644 --- a/src/p/switchlist_t.java +++ b/src/p/switchlist_t.java @@ -44,4 +44,4 @@ public final static int size() { public String toString() { return String.format("%s %s %d", name1, name2, episode); } -} +} \ No newline at end of file diff --git a/src/p/vldoor_e.java b/src/p/vldoor_e.java index 5bebde3..2bb9f6b 100644 --- a/src/p/vldoor_e.java +++ b/src/p/vldoor_e.java @@ -14,4 +14,4 @@ public enum vldoor_e { blazeClose; public static final int VALUES = vldoor_e.values().length; -} +} \ No newline at end of file diff --git a/src/p/vldoor_t.java b/src/p/vldoor_t.java index dc97dbe..049ef70 100644 --- a/src/p/vldoor_t.java +++ b/src/p/vldoor_t.java @@ -26,7 +26,7 @@ public class vldoor_t extends SectorAction implements IReadableDoomObject { @Override public void read(DataInputStream f) throws IOException { - super.read(f); // Call thinker reader first + super.read(f); // Call thinker reader first type = vldoor_e.values()[DoomIO.readLEInt(f)]; super.sectorid = DoomIO.readLEInt(f); // Sector index (or pointer?) topheight = DoomIO.readLEInt(f); @@ -38,7 +38,7 @@ public void read(DataInputStream f) throws IOException { @Override public void pack(ByteBuffer b) throws IOException { - super.pack(b); //12 + super.pack(b); //12 b.putInt(type.ordinal()); // 16 b.putInt(super.sectorid); // 20 b.putInt(topheight); // 24 @@ -48,4 +48,4 @@ public void pack(ByteBuffer b) throws IOException { b.putInt(topcountdown); //40 } -} +} \ No newline at end of file diff --git a/src/pooling/AudioChunkPool.java b/src/pooling/AudioChunkPool.java index 6b8bf35..3323f4e 100644 --- a/src/pooling/AudioChunkPool.java +++ b/src/pooling/AudioChunkPool.java @@ -23,4 +23,4 @@ public boolean validate(AudioChunk o) { return o.free; } -} +} \ No newline at end of file diff --git a/src/pooling/GenericIntMap.java b/src/pooling/GenericIntMap.java index 7e61c96..c864e2d 100644 --- a/src/pooling/GenericIntMap.java +++ b/src/pooling/GenericIntMap.java @@ -7,7 +7,7 @@ public abstract class GenericIntMap { protected static final int DEFAULT_CAPACITY = 16; /** Concrete implementations must allocate patches - * + * */ GenericIntMap() { @@ -69,4 +69,4 @@ protected int indexOf(int lump) { protected int[] lumps; protected int numEntries; protected K[] patches; -} +} \ No newline at end of file diff --git a/src/pooling/ObjectPool.java b/src/pooling/ObjectPool.java index d2f9d5f..b2cb3a3 100644 --- a/src/pooling/ObjectPool.java +++ b/src/pooling/ObjectPool.java @@ -10,7 +10,7 @@ /** A convenient object pooling class. Currently used for AudioChunks, but * could be reused for UI events and other such things. Perhaps reusing it * for mobj_t's is possible, but risky. - * + * */ public abstract class ObjectPool { @@ -86,4 +86,4 @@ public synchronized void checkIn(K t) { private long expirationTime; protected Hashtable locked; private Hashtable unlocked; -} +} \ No newline at end of file diff --git a/src/pooling/ObjectQueuePool.java b/src/pooling/ObjectQueuePool.java index 5cef27f..dde91bd 100644 --- a/src/pooling/ObjectQueuePool.java +++ b/src/pooling/ObjectQueuePool.java @@ -7,10 +7,10 @@ import p.mobj_t; /** A convenient object pooling class, derived from the stock ObjectPool. - * + * * It's about 50% faster than calling new, and MUCH faster than ObjectPool * because it doesn't do that bullshit object cleanup every so often. - * + * */ public abstract class ObjectQueuePool { @@ -56,4 +56,4 @@ public void checkIn(K t) { protected Stack locked; // private Hashtable unlocked; -} +} \ No newline at end of file diff --git a/src/pooling/RoguePatchMap.java b/src/pooling/RoguePatchMap.java index c396069..41bf6d2 100644 --- a/src/pooling/RoguePatchMap.java +++ b/src/pooling/RoguePatchMap.java @@ -6,4 +6,4 @@ public RoguePatchMap() { super(); patches = new byte[DEFAULT_CAPACITY][][]; } -} +} \ No newline at end of file diff --git a/src/pooling/RoguePatchMap2.java b/src/pooling/RoguePatchMap2.java index b70a42c..6b840dc 100644 --- a/src/pooling/RoguePatchMap2.java +++ b/src/pooling/RoguePatchMap2.java @@ -59,4 +59,4 @@ private int indexOf(int lump) { private int[] lumps; private int numEntries; private byte[][][] patches; -} +} \ No newline at end of file diff --git a/src/rr/AbstractThings.java b/src/rr/AbstractThings.java index 5de7072..89ef98a 100644 --- a/src/rr/AbstractThings.java +++ b/src/rr/AbstractThings.java @@ -26,13 +26,13 @@ * class is the base for all implementations, and contains the gory clipping * and priority stuff. It can terminate by drawing directly, or by buffering * into a pipeline for parallelized drawing. - * + * * It need to be aware of almost everything in the renderer, which means that - * it's a PITA to keep "disembodied". Then again, this probably means it's more + * it's a PITA to keep "disembodied". Then again, this probably means it's more * extensible... - * - * - * + * + * + * */ public abstract class AbstractThings implements IMaskedDrawer { @@ -167,7 +167,7 @@ protected void DrawVisSprite(vissprite_t vis) { /** * R_RenderMaskedSegRange - * + * * @param ds * @param x1 * @param x2 @@ -768,7 +768,7 @@ protected final void DrawMaskedColumn(column_t column) { * Used for sprites and masked mid textures. * Masked means: partly transparent, i.e. stored * in posts/runs of opaque pixels. - * + * * NOTE: this version accepts raw bytes, in case you know what you're doing. * NOTE: this is a legacy function. Do not reactivate unless * REALLY needed. @@ -782,12 +782,12 @@ protected final void DrawMaskedColumn (byte[] column) int basetexturemid; // fixed_t int topdelta; int length; - + basetexturemid = dc_texturemid; // That's true for the whole column. dc_source = column; int pointer=0; - + // for each post... while((topdelta=0xFF&column[pointer])!=0xFF) { @@ -799,10 +799,10 @@ protected final void DrawMaskedColumn (byte[] column) dc_yl = (topscreen+FRACUNIT-1)>>FRACBITS; dc_yh = (bottomscreen-1)>>FRACBITS; - + if (dc_yh >= mfloorclip[p_mfloorclip+dc_x]) dc_yh = mfloorclip[p_mfloorclip+dc_x]-1; - + if (dc_yl <= mceilingclip[p_mceilingclip+dc_x]) dc_yl = mceilingclip[p_mceilingclip+dc_x]+1; @@ -810,19 +810,19 @@ protected final void DrawMaskedColumn (byte[] column) if (dc_yl <= dc_yh && dc_yh < viewheight) { // Set pointer inside column to current post's data - // Rremember, it goes {postlen}{postdelta}{pad}[data]{pad} + // Rremember, it goes {postlen}{postdelta}{pad}[data]{pad} dc_source_ofs = pointer+3; dc_texturemid = basetexturemid - (topdelta< { T GetCachedColumn(int tex, int col); -} +} \ No newline at end of file diff --git a/src/rr/IGetColumn.java b/src/rr/IGetColumn.java index b936b2b..a931b21 100644 --- a/src/rr/IGetColumn.java +++ b/src/rr/IGetColumn.java @@ -2,7 +2,7 @@ /** An interface used to ease the use of the GetCachedColumn by part * of parallelized renderers. - * + * * @author Maes * */ @@ -10,4 +10,4 @@ public interface IGetColumn { T GetColumn(int tex, int col); -} +} \ No newline at end of file diff --git a/src/rr/ILimitResettable.java b/src/rr/ILimitResettable.java index 2b32ac8..e108d93 100644 --- a/src/rr/ILimitResettable.java +++ b/src/rr/ILimitResettable.java @@ -1,13 +1,13 @@ package rr; /** Resets limit-removing stuff back to their initial values, - * either for initialization reasons or to regain memory - * e.g. playing MAP02 after nuts.wad should free up some vissprite buffers. - * + * either for initialization reasons or to regain memory + * e.g. playing MAP02 after nuts.wad should free up some vissprite buffers. + * * @author admin * */ public interface ILimitResettable { public void resetLimits(); -} +} \ No newline at end of file diff --git a/src/rr/IMaskedDrawer.java b/src/rr/IMaskedDrawer.java index 5a16fd1..c46f8bb 100644 --- a/src/rr/IMaskedDrawer.java +++ b/src/rr/IMaskedDrawer.java @@ -20,4 +20,4 @@ public interface IMaskedDrawer extends IDetailAware { * HINT: you need to discern between masked and non-masked draws. */ void completeColumn(); -} +} \ No newline at end of file diff --git a/src/rr/ISpriteManager.java b/src/rr/ISpriteManager.java index 353f2d8..dfc6192 100644 --- a/src/rr/ISpriteManager.java +++ b/src/rr/ISpriteManager.java @@ -2,7 +2,7 @@ /** Interface for sprite managers. Handles loading sprites, fixing * rotations etc. and helping retrieving spritedefs when required. - * + * * @author velktron. * */ @@ -50,4 +50,4 @@ public interface ISpriteManager { void InitSprites(String[] namelist); -} +} \ No newline at end of file diff --git a/src/rr/IVisSpriteManagement.java b/src/rr/IVisSpriteManagement.java index 4cf5202..d7ac922 100644 --- a/src/rr/IVisSpriteManagement.java +++ b/src/rr/IVisSpriteManagement.java @@ -3,7 +3,7 @@ /** A sprite manager does everything but drawing the sprites. It creates lists * of sprites-per-sector, sorts them, and stuff like that. * that gory visibiliy - * + * * @author velkton * * @param @@ -23,4 +23,4 @@ public interface IVisSpriteManagement extends ILimitResettable { void ClearSprites(); -} +} \ No newline at end of file diff --git a/src/rr/MultiPatchSynthesizer.java b/src/rr/MultiPatchSynthesizer.java index 5a678bc..61939f1 100644 --- a/src/rr/MultiPatchSynthesizer.java +++ b/src/rr/MultiPatchSynthesizer.java @@ -8,8 +8,8 @@ /** * Utilities to synthesize patch_t format images from multiple patches * (with transparency). - * - * + * + * * @author velktron * */ @@ -78,7 +78,7 @@ public static column_t getColumnStream(byte[] pixels, boolean[] solid, int heigh List ranges = new ArrayList<>(); - // Scan column for continuous pixel ranges + // Scan column for continuous pixel ranges for (int i = 0; i < height; i++) { // Encountered solid start. @@ -120,12 +120,12 @@ public static column_t getColumnStream(byte[] pixels, boolean[] solid, int heigh for (int i = 0; i < n; i++) { PixelRange pr = ranges.get(i); - topdelta = pr.start; // cumulative top delta + topdelta = pr.start; // cumulative top delta // Precomputed column data postofs[i] = (short) file.size() + 3; // Last written post +3, pointing at first pixel of data. topdeltas[i] = (short) topdelta; - postlens[i] = (short) (pr.getLength()); // Post lengths are at net of padding + postlens[i] = (short) (pr.getLength()); // Post lengths are at net of padding file.write(topdeltas[i]); file.write(postlens[i]); @@ -150,11 +150,11 @@ public static column_t getColumnStream(byte[] pixels, boolean[] solid, int heigh public static patch_t synthesize(byte[][] pixels, boolean[][] solid, int width, int height, int picture_top, int picture_left){ // Ideal for this use, since we don't know how big the patch is going to be a-priori ByteArrayOutputStream file=new ByteArrayOutputStream(); - + int offset; - + int[] columnofs=new int[width]; - + // Patch header file.write(width); file.write(height); @@ -174,21 +174,21 @@ public static patch_t synthesize(byte[][] pixels, boolean[][] solid, int width, boolean operator = true; int pixel_count = 0; - + while (y < height){ byte val=pixels[x][y]; boolean transparent=!solid[x][y]; - - + + // Pixel is transparent if (transparent && !operator ) { - dummy_value = 0; + dummy_value = 0; file.write(dummy_value); operator = true; } else //Pixel not transparent, and operator condition set. if (!transparent && operator){ - int row_start = y; + int row_start = y; pixel_count = 0; dummy_value = 0; // write above post data to memory buffer @@ -196,7 +196,7 @@ public static patch_t synthesize(byte[][] pixels, boolean[][] solid, int width, offset = file.size(); //current post position in memory buffer operator = false; - } else + } else if (!transparent && !operator){ pixel_count++; // increment current post pixel_count } @@ -210,7 +210,7 @@ public static patch_t synthesize(byte[][] pixels, boolean[][] solid, int width, seek back to previous_offset end block - + write pixel to memory buffer end block @@ -224,7 +224,7 @@ public static patch_t synthesize(byte[][] pixels, boolean[][] solid, int width, write Pixel to memory buffer rowstart = 255 - + write rowstart to memory buffer end block @@ -255,4 +255,4 @@ public static patch_t synthesize(byte[][] pixels, boolean[][] solid, int width, write memory buffer to file } } */ -} +} \ No newline at end of file diff --git a/src/rr/PlaneDrawer.java b/src/rr/PlaneDrawer.java index 016a37a..33954a6 100644 --- a/src/rr/PlaneDrawer.java +++ b/src/rr/PlaneDrawer.java @@ -74,15 +74,15 @@ protected PlaneDrawer(DoomMain DOOM, SceneRenderer R) { /** * R_MapPlane - * + * * Called only by R_MakeSpans. - * + * * This is where the actual span drawing function is called. - * + * * Uses global vars: planeheight ds_source -> flat data has already been * set. basexscale -> actual drawing angle and position is computed from * these baseyscale viewx viewy - * + * * BASIC PRIMITIVE */ public void MapPlane(int y, int x1, int x2) { @@ -141,13 +141,13 @@ protected final void rangeCheck(int x1, int x2, int y) { /** * R_MakeSpans - * + * * Called only by DrawPlanes. If you wondered where the actual * boundaries for the visplane flood-fill are laid out, this is it. - * + * * The system of coords seems to be defining a sort of cone. - * - * + * + * * @param x * Horizontal position * @param t1 @@ -158,7 +158,7 @@ protected final void rangeCheck(int x1, int x2, int y) { * Top-right y coord ? * @param b2 * Bottom-right y coord ? - * + * */ protected void MakeSpans(int x, int t1, int b1, int t2, int b2) { @@ -229,4 +229,4 @@ public int[] getDistScale() { return distscale; } -} +} \ No newline at end of file diff --git a/src/rr/RendererState.java b/src/rr/RendererState.java index 1b63476..051c64e 100644 --- a/src/rr/RendererState.java +++ b/src/rr/RendererState.java @@ -1,3113 +1,3113 @@ -package rr; - -import data.Defines; -import static data.Defines.ANGLETOSKYSHIFT; -import static data.Defines.NF_SUBSECTOR; -import static data.Defines.PU_CACHE; -import static data.Defines.SIL_BOTH; -import static data.Defines.SIL_BOTTOM; -import static data.Defines.SIL_TOP; -import static data.Limits.MAXHEIGHT; -import static data.Limits.MAXSEGS; -import static data.Limits.MAXWIDTH; -import data.Tables; -import static data.Tables.ANG180; -import static data.Tables.ANG270; -import static data.Tables.ANG90; -import static data.Tables.ANGLETOFINESHIFT; -import static data.Tables.BITS32; -import static data.Tables.DBITS; -import static data.Tables.FINEANGLES; -import static data.Tables.QUARTERMARK; -import static data.Tables.SlopeDiv; -import static data.Tables.addAngles; -import static data.Tables.finecosine; -import static data.Tables.finesine; -import static data.Tables.finetangent; -import static data.Tables.tantoangle; -import doom.CommandVariable; -import doom.DoomMain; -import doom.SourceCode.R_Draw; -import static doom.SourceCode.R_Draw.R_FillBackScreen; -import doom.player_t; -import doom.thinker_t; -import i.IDoomSystem; -import java.awt.Rectangle; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -import static m.BBox.BOXBOTTOM; -import static m.BBox.BOXLEFT; -import static m.BBox.BOXRIGHT; -import static m.BBox.BOXTOP; -import m.IDoomMenu; -import m.MenuMisc; -import m.Settings; -import static m.fixed_t.FRACBITS; -import static m.fixed_t.FRACUNIT; -import static m.fixed_t.FixedDiv; -import static m.fixed_t.FixedMul; -import mochadoom.Engine; -import mochadoom.Loggers; -import static p.ActiveStates.P_MobjThinker; -import p.mobj_t; -import rr.drawfuns.ColFuncs; -import rr.drawfuns.ColVars; -import rr.drawfuns.DoomColumnFunction; -import rr.drawfuns.DoomSpanFunction; -import rr.drawfuns.SpanVars; -import static rr.line_t.ML_DONTPEGBOTTOM; -import static rr.line_t.ML_DONTPEGTOP; -import static rr.line_t.ML_MAPPED; -import utils.C2JUtils; -import static utils.GenericCopy.malloc; -import static v.DoomGraphicSystem.V_NOSCALEOFFSET; -import static v.DoomGraphicSystem.V_NOSCALEPATCH; -import static v.DoomGraphicSystem.V_NOSCALESTART; -import v.graphics.Palettes; -import v.renderers.DoomScreen; -import static v.renderers.DoomScreen.BG; -import static v.renderers.DoomScreen.FG; -import v.tables.BlurryTable; -import v.tables.LightsAndColors; -import w.IWadLoader; - -/** - * Most shared -essential- status information, methods and classes related to - * the software rendering subsystem are found here, shared between the various - * implementations of the Doom's renderer. Not the cleanest or more OO way - * possible, but still a good way to avoid duplicating common code. Some stuff - * like Texture, Flat and Sprite management are also found -or at least - * implemented temporarily- here, until a cleaner split can be made. This is a - * kind of "Jack of all trades" class, but hopefully not for long. - * - * @author velktron - */ -public abstract class RendererState implements SceneRenderer, ILimitResettable { - - private static final Logger LOGGER = Loggers.getLogger(RendererState.class.getName()); - - protected static final boolean DEBUG = false; - protected static final boolean DEBUG2 = false; - - // ///////////////////// STATUS //////////////////////// - protected DoomMain DOOM; - protected ISegDrawer MySegs; - protected IDoomMenu Menu; - protected BSP MyBSP; - protected PlaneDrawer MyPlanes; - protected IMaskedDrawer MyThings; - public IVisSpriteManagement VIS; - protected TextureManager TexMan; - public ViewVars view; - public LightsAndColors colormaps; - public SegVars seg_vars; - public Visplanes vp_vars; - - // Rendering subsystems that are detailshift-aware - protected List detailaware; - - // The only reason to query scaledviewwidth from outside the renderer, is - // this. - @Override - public boolean isFullHeight() { - return (view.height == DOOM.vs.getScreenHeight()); - } - - public boolean isFullWidth() { - return (view.scaledwidth == DOOM.vs.getScreenWidth()); - } - - @Override - public boolean isFullScreen() { - return isFullWidth() && isFullHeight(); - } - - /** - * Increment every time a check is made For some reason, this needs to be - * visible even by enemies thinking :-S - */ - protected int validcount = 1; - - /** - * Who can set this? A: The Menu. - */ - protected boolean setsizeneeded; - protected int setblocks; - protected int setdetail; - - // private BSPVars bspvars; - /** - * R_SetViewSize Do not really change anything here, because it might be in - * the middle of a refresh. The change will take effect next refresh. - * - * @param blocks - * 11 is full screen, 9 default. - * @param detail - * 0= high, 1 =low. - */ - @Override - public void SetViewSize(int blocks, int detail) { - // System.out.println("SetViewSize"); - setsizeneeded = true; - setblocks = blocks; - setdetail = detail; - - detailaware.forEach((d) -> { - d.setDetail(setdetail); - }); - } - - /** - * R_SetupFrame - */ - public void SetupFrame(player_t player) { - view.player = player; - view.x = player.mo.x; - view.y = player.mo.y; - // viewangle = addAngles(player.mo.angle , viewangleoffset); - view.angle = player.mo.angle & BITS32; - // With 32 colormaps, a bump of 1 or 2 is normal. - // With more than 32, it should be obviously higher. - - int bumplight = Math.max(colormaps.lightBits() - 5, 0); - // Be a bit more generous, otherwise the effect is not - // as evident with truecolor maps. - bumplight += (bumplight > 0) ? 1 : 0; - - colormaps.extralight = player.extralight << bumplight; - - view.z = player.viewz; - view.lookdir = player.lookdir; - int tempCentery; - - // MAES: hacks based on Heretic. Weapon movement needs to be compensated - if (setblocks == 11) { - tempCentery = (view.height / 2) + (int) (view.lookdir * DOOM.vs.getScreenMul() * setblocks) / 11; - } else { - tempCentery = (view.height / 2) + (int) (view.lookdir * DOOM.vs.getScreenMul() * setblocks) / 10; - } - - if (view.centery != tempCentery) { - view.centery = tempCentery; - view.centeryfrac = view.centery << FRACBITS; - int yslope[] = vp_vars.yslope; - for (int i = 0; i < view.height; i++) { - yslope[i] = FixedDiv( - (view.width << view.detailshift) / 2 * FRACUNIT, - Math.abs(((i - view.centery) << FRACBITS) + FRACUNIT / 2) - ); - } - - skydcvars.centery = maskedcvars.centery = dcvars.centery = view.centery; - } - - view.sin = Tables.finesine(view.angle); - view.cos = Tables.finecosine(view.angle); - - sscount = 0; - - if (player.fixedcolormap != Palettes.COLORMAP_FIXED) { - colormaps.fixedcolormap = colormaps.getFixedColormap(player); - // Offset by fixedcolomap - // pfixedcolormap =player.fixedcolormap*256; - - colormaps.walllights = colormaps.scalelightfixed; - - for (int i = 0; i < colormaps.maxLightScale(); i++) { - colormaps.scalelightfixed[i] = colormaps.fixedcolormap; - } - } else { - colormaps.fixedcolormap = null; - } - - framecount++; - validcount++; - } - - /** - * R_SetupFrame for a particular actor. - */ - public void SetupFrame(mobj_t actor) { - - // viewplayer = player; - view.x = actor.x; - view.y = actor.y; - // viewangle = addAngles(player.mo.angle , viewangleoffset); - view.angle = actor.angle & BITS32; - // extralight = actor.extralight; - - view.z = actor.z + actor.height; - - view.sin = finesine(view.angle); - view.cos = finecosine(view.angle); - - sscount = 0; - - framecount++; - validcount++; - } - - public RendererState(DoomMain DOOM) { - this.DOOM = DOOM; - - // These don't change between implementations, yet. - this.MyBSP = new BSP(); - - this.view = new ViewVars(DOOM.vs); - this.seg_vars = new SegVars(); - this.dcvars = new ColVars<>(); - this.dsvars = new SpanVars<>(); - this.maskedcvars = new ColVars<>(); - this.skydcvars = new ColVars<>(); - this.colfunclow = new ColFuncs<>(); - this.colfunchi = new ColFuncs<>(); - - this.detailaware = new ArrayList<>(); - this.colormaps = new LightsAndColors<>(DOOM); - // It's better to construct this here - @SuppressWarnings("unchecked") - final TextureManager tm = (TextureManager) new SimpleTextureManager(DOOM); - this.TexMan = tm; - - // Visplane variables - this.vp_vars = new Visplanes(DOOM.vs, view, TexMan); - - // Set rendering functions only after screen sizes - // and stuff have been set. - this.MyPlanes = new Planes(DOOM, this); - this.VIS = new VisSprites<>(this); - this.MyThings = new SimpleThings<>(DOOM.vs, this); - } - - // ////////////////////////////// THINGS //////////////////////////////// - protected final class BSP extends BSPVars { - - /** - * newend is one past the last valid seg (cliprange_t) - */ - int newend; - - cliprange_t[] solidsegs; - - BSP() { - solidsegs = malloc(cliprange_t::new, cliprange_t[]::new, MAXSEGS + 1); - } - - /** - * R_ClipSolidWallSegment Does handle solid walls, single sided LineDefs - * (middle texture) that entirely block the view VERTICALLY. Handles - * "clipranges" for a solid wall, aka where it blocks the view. - * - * @param first - * starting y coord? - * @param last - * ending y coord? - */ - private void ClipSolidWallSegment(int first, int last) { - - int next; - int start; - // int maxlast=Integer.MIN_VALUE; - - start = 0; // within solidsegs - - // Find the first cliprange that touches the range. - // Actually, the first one not completely hiding it (its last must - // be lower than first. - while (solidsegs[start].last < first - 1) { - start++; - } - - // If the post begins above the lastly found cliprange... - if (first < solidsegs[start].first) { - // ..and ends above it, too (no overlapping) - if (last < solidsegs[start].first - 1) { - // ... then the post is entirely visible (above start), - // so insert a new clippost. Calling this function - // tells the renderer that there is an obstruction. - MySegs.StoreWallRange(first, last); - - // Newend should have a value of 2 if we are at the - // beginning of a new frame. - next = newend; - newend++; - - if (next >= solidsegs.length) { - ResizeSolidSegs(); - } - while (next != start) { - // *next=*(next-1); - /* - * MAES: I think this is supposed to copy the structs - * solidsegs[next] = solidsegs[next-1].clone(); OK, so - * basically the last solidseg copies its previous, and - * so on until we reach the start. This means that at - * some point, the value of the start solidseg is - * duplicated. - */ - - solidsegs[next].copy(solidsegs[next - 1]); - - next--; - } - - // At this point, next points at start. - // Therefore, start - solidsegs[next].first = first; - solidsegs[next].last = last; - return; - } - - // There is a fragment above *start. This can occur if it a - // post does start before another, but its lower edge overlaps - // (partial, upper occlusion) - MySegs.StoreWallRange(first, solidsegs[start].first - 1); - // Now adjust the clip size. - solidsegs[start].first = first; - } - - // We can reach this only if a post starts AFTER another - // Bottom contained in start? Obviously it won't be visible. - if (last <= solidsegs[start].last) { - return; - } - - next = start; - while (last >= solidsegs[(next + 1)].first - 1) { - // There is a fragment between two posts. - MySegs.StoreWallRange(solidsegs[next].last + 1, - solidsegs[next + 1].first - 1); - next++; - - if (last <= solidsegs[next].last) { - // Bottom is contained in next. - // Adjust the clip size. - solidsegs[start].last = solidsegs[next].last; - // goto crunch; - - { // crunch code - if (next == start) { - // Post just extended past the bottom of one post. - return; - } - - while (next++ != newend) { - // Remove a post. - // MAES: this is a struct copy. - if (next >= solidsegs.length) { - ResizeSolidSegs(); - } - solidsegs[++start].copy(solidsegs[next]); - } - - newend = start + 1; - return; - } - } - } - - // There is a fragment after *next. - MySegs.StoreWallRange(solidsegs[next].last + 1, last); - // Adjust the clip size. - solidsegs[start].last = last; - - // Remove start+1 to next from the clip list, - // because start now covers their area. - { // crunch code - if (next == start) { - // Post just extended past the bottom of one post. - return; - } - - while (next++ != newend) { - // Remove a post. - // MAES: this is a struct copy. - // MAES: this can overflow, breaking e.g. MAP30 of Final - // Doom. - if (next >= solidsegs.length) { - ResizeSolidSegs(); - } - solidsegs[++start].copy(solidsegs[next]); - } - - newend = start + 1; - } - } - - void ResizeSolidSegs() { - solidsegs = C2JUtils.resize(solidsegs, solidsegs.length * 2); - } - - // - // R_ClipPassWallSegment - // Clips the given range of columns, - // but does not includes it in the clip list. - // Does handle windows, - // e.g. LineDefs with upper and lower texture. - // - private void ClipPassWallSegment(int first, int last) { - - // Find the first range that touches the range - // (adjacent pixels are touching). - int start = 0; - - while (solidsegs[start].last < first - 1) { - start++; - } - - if (first < solidsegs[start].first) { - if (last < solidsegs[start].first - 1) { - // Post is entirely visible (above start). - MySegs.StoreWallRange(first, last); - return; - } - - // There is a fragment above *start. - MySegs.StoreWallRange(first, solidsegs[start].first - 1); - } - - // Bottom contained in start? - if (last <= solidsegs[start].last) { - return; - } - - // MAES: Java absolutely can't do without a sanity check here. - // if (startptr>=MAXSEGS-2) return; - while (last >= solidsegs[start + 1].first - 1) { - // There is a fragment between two posts. - MySegs.StoreWallRange(solidsegs[start].last + 1, - solidsegs[start + 1].first - 1); - start++; - // if (startptr>=MAXSEGS-2) return; - // start=solidsegs[startptr]; - - if (last <= solidsegs[start].last) { - return; - } - } - - // There is a fragment after *next. - MySegs.StoreWallRange(solidsegs[start].last + 1, last); - } - - /** - * R_ClearClipSegs Clears the clipping segs list. The list is actually - * fixed size for efficiency reasons, so it just tells Doom to use the - * first two solidsegs, which are "neutered". It's interesting to note - * how the solidsegs begin and end just "outside" the visible borders of - * the screen. - */ - public void ClearClipSegs() { - solidsegs[0].first = -0x7fffffff; - solidsegs[0].last = -1; - solidsegs[1].first = view.width; - solidsegs[1].last = 0x7fffffff; - newend = 2; // point so solidsegs[2]; - } - - /** - * R_AddLine Called after a SubSector BSP trasversal ends up in a - * "final" subsector. Clips the given segment and adds any visible - * pieces to the line list. It also determines what kind of boundary - * (line) visplane clipping should be performed. E.g. window, final - * 1-sided line, closed door etc.) CAREFUL: was the source of much - * frustration with visplanes... - */ - private void AddLine(seg_t line) { - if (DEBUG) { - LOGGER.log(Level.FINE, String.format("Entered AddLine for %s", String.valueOf(line))); - } - int x1; - int x2; - long angle1; - long angle2; - long span; - long tspan; - - curline = line; - - // OPTIMIZE: quickly reject orthogonal back sides. - angle1 = view.PointToAngle(line.v1x, line.v1y); - angle2 = view.PointToAngle(line.v2x, line.v2y); - - // Clip to view edges. - // OPTIMIZE: make constant out of 2*clipangle (FIELDOFVIEW). - span = addAngles(angle1, -angle2); - - // Back side? I.e. backface culling? - if (span >= ANG180) { - return; - } - - // Global angle needed by segcalc. - MySegs.setGlobalAngle(angle1); - angle1 -= view.angle; - angle2 -= view.angle; - - angle1 &= BITS32; - angle2 &= BITS32; - - tspan = addAngles(angle1, clipangle); - - if (tspan > CLIPANGLE2) { - tspan -= CLIPANGLE2; - tspan &= BITS32; - - // Totally off the left edge? - if (tspan >= span) { - return; - } - - angle1 = clipangle; - } - tspan = addAngles(clipangle, -angle2); - - if (tspan > CLIPANGLE2) { - tspan -= CLIPANGLE2; - tspan &= BITS32; - - // Totally off the left edge? - if (tspan >= span) { - return; - } - angle2 = -clipangle; - angle2 &= BITS32; - } - - // The seg is in the view range, - // but not necessarily visible. - angle1 = ((angle1 + ANG90) & BITS32) >>> ANGLETOFINESHIFT; - angle2 = ((angle2 + ANG90) & BITS32) >>> ANGLETOFINESHIFT; - x1 = viewangletox[(int) angle1]; - x2 = viewangletox[(int) angle2]; - - // Does not cross a pixel? - if (x1 == x2) { - return; - } - - backsector = line.backsector; - - // Single sided line? - if (backsector == null) { - if (DEBUG) { - LOGGER.log(Level.FINE, String.format("Entering ClipSolidWallSegment SS with params %d %d", x1, (x2 - 1))); - } - ClipSolidWallSegment(x1, x2 - 1); // to clipsolid - if (DEBUG) { - LOGGER.log(Level.FINE, "Exiting ClipSolidWallSegment"); - } - return; - } - - // Closed door. - if (backsector.ceilingheight <= frontsector.floorheight - || backsector.floorheight >= frontsector.ceilingheight) { - if (DEBUG) { - LOGGER.log(Level.FINE, String.format("Entering ClipSolidWallSegment Closed door with params %d %d", x1, (x2 - 1))); - } - ClipSolidWallSegment(x1, x2 - 1); - // to clipsolid - return; - } - - // Window. This includes same-level floors with different textures - if (backsector.ceilingheight != frontsector.ceilingheight - || backsector.floorheight != frontsector.floorheight) { - if (DEBUG) { - LOGGER.log(Level.FINE, String.format("Entering ClipSolidWallSegment window with params %d %d", x1, (x2 - 1))); - } - ClipPassWallSegment(x1, x2 - 1); // to clippass - return; - } - - // Reject empty lines used for triggers - // and special events. - // Identical floor and ceiling on both sides, - // identical light levels on both sides, - // and no middle texture. - if (backsector.ceilingpic == frontsector.ceilingpic - && backsector.floorpic == frontsector.floorpic - && backsector.lightlevel == frontsector.lightlevel - && curline.sidedef.midtexture == 0) { - return; - } - - // If nothing of the previous holds, then we are - // treating the case of same-level, differently - // textured floors. ACHTUNG, this caused the "bleeding floor" - // bug, which is now fixed. - // Fucking GOTOs.... - ClipPassWallSegment(x1, x2 - 1); // to clippass - if (DEBUG) { - LOGGER.log(Level.FINE, String.format("Exiting AddLine for %s", String.valueOf(line))); - } - } - - // - // R_CheckBBox - // Checks BSP node/subtree bounding box. - // Returns true - // if some part of the bbox might be visible. - // - private final int[][] checkcoord = { - {3, 0, 2, 1}, - {3, 0, 2, 0}, - {3, 1, 2, 0}, - {0}, - {2, 0, 2, 1}, - {0, 0, 0, 0}, - {3, 1, 3, 0}, - {0}, - {2, 0, 3, 1}, - {2, 1, 3, 1}, - {2, 1, 3, 0} - }; - - /** - * @param bspcoord - * (fixed_t* as bbox) - * @return - */ - public boolean CheckBBox(int[] bspcoord) { - int boxx; - int boxy; - int boxpos; - - // fixed_t - int x1; - int y1; - int x2; - int y2; - - // angle_t - long angle1; - long angle2; - long span; - long tspan; - - cliprange_t start; - - int sx1; - int sx2; - - // Find the corners of the box - // that define the edges from current viewpoint. - if (view.x <= bspcoord[BOXLEFT]) { - boxx = 0; - } else if (view.x < bspcoord[BOXRIGHT]) { - boxx = 1; - } else { - boxx = 2; - } - - if (view.y >= bspcoord[BOXTOP]) { - boxy = 0; - } else if (view.y > bspcoord[BOXBOTTOM]) { - boxy = 1; - } else { - boxy = 2; - } - - boxpos = (boxy << 2) + boxx; - if (boxpos == 5) { - return true; - } - - x1 = bspcoord[checkcoord[boxpos][0]]; - y1 = bspcoord[checkcoord[boxpos][1]]; - x2 = bspcoord[checkcoord[boxpos][2]]; - y2 = bspcoord[checkcoord[boxpos][3]]; - - // check clip list for an open space - angle1 = view.PointToAngle(x1, y1) - view.angle; - angle2 = view.PointToAngle(x2, y2) - view.angle; - - angle1 &= BITS32; - angle2 &= BITS32; - - span = angle1 - angle2; - - span &= BITS32; - - // Sitting on a line? - if (span >= ANG180) { - return true; - } - - tspan = angle1 + clipangle; - tspan &= BITS32; - - if (tspan > CLIPANGLE2) { - tspan -= CLIPANGLE2; - tspan &= BITS32; - // Totally off the left edge? - if (tspan >= span) { - return false; - } - - angle1 = clipangle; - } - tspan = (clipangle - angle2) & BITS32; - if (tspan > CLIPANGLE2) { - tspan -= CLIPANGLE2; - tspan &= BITS32; - - // Totally off the left edge? - if (tspan >= span) { - return false; - } - - angle2 = -clipangle; - angle2 &= BITS32; - } - - // Find the first clippost - // that touches the source post - // (adjacent pixels are touching). - angle1 = ((angle1 + ANG90) & BITS32) >>> ANGLETOFINESHIFT; - angle2 = ((angle2 + ANG90) & BITS32) >>> ANGLETOFINESHIFT; - sx1 = viewangletox[(int) angle1]; - sx2 = viewangletox[(int) angle2]; - - // Does not cross a pixel. - if (sx1 == sx2) { - return false; - } - sx2--; - - int pstart = 0; - start = solidsegs[pstart]; - // FIXME: possible solidseg overflow here overflows - while (start.last < sx2 && pstart < MAXSEGS) { - start = solidsegs[pstart++]; - } - - return !(sx1 >= start.first && sx2 <= start.last); - } - - /** - * R_Subsector Determine floor/ceiling planes. Add sprites of things in - * sector. Draw one or more line segments. It also alters the visplane - * list! - * - * @param num - * Subsector from subsector_t list in Lever Loader. - */ - private void Subsector(int num) { - if (DEBUG) { - LOGGER.log(Level.FINE, String.format("SubSector %d to render", num)); - } - int count; - int line; // pointer into a list of segs instead of seg_t - subsector_t sub; - - if (RANGECHECK) { - if (num >= DOOM.levelLoader.numsubsectors) { - DOOM.doomSystem.Error("R_Subsector: ss %d with numss = %d", num, - DOOM.levelLoader.numsubsectors); - } - } - - sscount++; - sub = DOOM.levelLoader.subsectors[num]; - - frontsector = sub.sector; - if (DEBUG) { - LOGGER.log(Level.FINE, String.format("Frontsector to render: %s", String.valueOf(frontsector))); - } - count = sub.numlines; - // line = LL.segs[sub.firstline]; - line = sub.firstline; - - if (DEBUG) { - LOGGER.log(Level.FINE, "Trying to find an existing FLOOR visplane..."); - } - if (frontsector.floorheight < view.z) { - vp_vars.floorplane - = vp_vars.FindPlane(frontsector.floorheight, - frontsector.floorpic, frontsector.lightlevel); - } else { - // FIXME: unclear what would happen with a null visplane used - // It's never checked explicitly for either condition, just - // called straight. - vp_vars.floorplane = -1; // in lieu of NULL - } - - // System.out.println("Trying to find an existing CEILING visplane..."); - if (frontsector.ceilingheight > view.z - || frontsector.ceilingpic == TexMan.getSkyFlatNum()) { - vp_vars.ceilingplane - = vp_vars.FindPlane(frontsector.ceilingheight, - frontsector.ceilingpic, frontsector.lightlevel); - } else { - vp_vars.ceilingplane = -1; // In lieu of NULL. Will bomb if - // actually - // used. - } - - VIS.AddSprites(frontsector); - - if (DEBUG) { - LOGGER.log(Level.FINE, String.format("Enter Addline for SubSector %d count %d", num, count)); - } - while (count-- > 0) { - AddLine(DOOM.levelLoader.segs[line]); - line++; - } - if (DEBUG) { - LOGGER.log(Level.FINE, String.format("Exit Addline for SubSector %d", num)); - } - } - - /** - * RenderBSPNode Renders all subsectors below a given node, traversing - * subtree recursively. Just call with BSP root. - */ - public void RenderBSPNode(int bspnum) { - if (DEBUG) { - LOGGER.log(Level.FINE, String.format("Processing BSP Node %d", bspnum)); - } - - node_t bsp; - int side; - - // Found a subsector? Then further decisions are taken, in, well, - // SubSector. - if (C2JUtils.flags(bspnum, NF_SUBSECTOR)) { - if (DEBUG) { - LOGGER.log(Level.FINE, "Subsector found."); - } - if (bspnum == -1) { - Subsector(0); - } else { - Subsector(bspnum & (~NF_SUBSECTOR)); - } - return; - } - - bsp = DOOM.levelLoader.nodes[bspnum]; - - // Decide which side the view point is on. - side = bsp.PointOnSide(view.x, view.y); - if (DEBUG) { - LOGGER.log(Level.FINE, String.format("View side: %d", side)); - } - - // Recursively divide front space. - if (DEBUG) { - LOGGER.log(Level.FINE, String.format("Enter Front space of %d", bspnum)); - } - RenderBSPNode(bsp.children[side]); - if (DEBUG) { - LOGGER.log(Level.FINE, String.format("Return Front space of %d", bspnum)); - } - - // Possibly divide back space. - if (CheckBBox(bsp.bbox[side ^ 1].bbox)) { - if (DEBUG) { - LOGGER.log(Level.FINE, String.format("Enter Back space of %d", bspnum)); - } - RenderBSPNode(bsp.children[side ^ 1]); - if (DEBUG) { - LOGGER.log(Level.FINE, String.format("Return Back space of %d", bspnum)); - } - } - } - - } - - protected abstract class SegDrawer implements ISegDrawer { - - protected static final int HEIGHTBITS = 12; - protected static final int HEIGHTUNIT = (1 << HEIGHTBITS); - protected final Visplanes vp_vars; - protected final SegVars seg_vars; - - // Fast blanking buffers. - protected short[] BLANKFLOORCLIP; - protected short[] BLANKCEILINGCLIP; - - @Override - public short[] getBLANKFLOORCLIP() { - return BLANKFLOORCLIP; - } - - @Override - public short[] getBLANKCEILINGCLIP() { - return BLANKCEILINGCLIP; - } - - /** - * fixed_t - */ - protected int pixhigh, pixlow, pixhighstep, pixlowstep, topfrac, topstep, bottomfrac, bottomstep; - protected int worldtop, worldbottom, worldhigh, worldlow; - - /** - * True if any of the segs textures might be visible. - */ - protected boolean segtextured; - - /** - * Clip values are the solid pixel bounding the range. floorclip starts - * out vs.getScreenHeight() ceilingclip starts out -1 - */ - protected short[] floorclip, ceilingclip; - - @Override - public final short[] getFloorClip() { - return floorclip; - } - - @Override - public short[] getCeilingClip() { - return ceilingclip; - } - - /** - * False if the back side is the same plane. - */ - protected boolean markfloor, markceiling; - - protected boolean maskedtexture; - - protected int toptexture; - - protected int bottomtexture; - - protected int midtexture; - - /** - * angle_t, used after adding ANG90 in StoreWallRange - */ - protected long rw_normalangle; - - /** - * angle to line origin - */ - protected long rw_angle1; - - // - // regular wall - // - protected int rw_x; - - protected int rw_stopx; - - protected long rw_centerangle; // angle_t - - /** - * fixed_t - */ - protected int rw_offset, rw_distance, rw_scale, rw_scalestep, - rw_midtexturemid, rw_toptexturemid, rw_bottomtexturemid; - - @Override - public void resetLimits() { - drawseg_t[] tmp = new drawseg_t[seg_vars.MAXDRAWSEGS]; - System.arraycopy(seg_vars.drawsegs, 0, tmp, 0, seg_vars.MAXDRAWSEGS); - - // Now, that was quite a haircut!. - seg_vars.drawsegs = tmp; - - // System.out.println("Drawseg buffer cut back to original limit of "+MAXDRAWSEGS); - } - - @Override - public void sync() { - // Nothing required if serial. - } - - /** - * R_StoreWallRange A wall segment will be drawn between start and stop - * pixels (inclusive). This is the only place where - * markceiling/markfloor can be set. Can only be called from - * ClipSolidWallSegment and ClipPassWallSegment. - * - * @throws IOException - */ - @Override - public void StoreWallRange(int start, int stop) { - - if (DEBUG2) { - LOGGER.log(Level.FINER, String.format("Storewallrange called between %d and %d", start, stop)); - } - - int hyp; // fixed_t - int sineval; // fixed_t - int distangle; - long offsetangle; // angle_t - int vtop; // fixed_t - int lightnum; - drawseg_t seg; - - // don't overflow and crash - if (seg_vars.ds_p == seg_vars.drawsegs.length) { - seg_vars.ResizeDrawsegs(); - } - - if (RANGECHECK) { - if (start >= view.width || start > stop) { - DOOM.doomSystem.Error("Bad R_RenderWallRange: %d to %d", start, stop); - } - } - - seg = seg_vars.drawsegs[seg_vars.ds_p]; - - MyBSP.sidedef = MyBSP.curline.sidedef; - MyBSP.linedef = MyBSP.curline.linedef; - - // mark the segment as visible for auto map - MyBSP.linedef.flags |= ML_MAPPED; - - // calculate rw_distance for scale calculation - rw_normalangle = addAngles(MyBSP.curline.angle, ANG90); - - /* - * MAES: ok, this is a tricky spot. angle_t's are supposed to be - * always positive 32-bit unsigned integers, so a subtraction should - * be always positive by definition, right? WRONG: this fucking spot - * caused "blind spots" at certain angles because ONLY HERE angles - * are supposed to be treated as SIGNED and result in differences - * <180 degrees -_- The only way to coerce this behavior is to cast - * both as signed ints. - */ - offsetangle = Math.abs((int) rw_normalangle - (int) rw_angle1); - - if (offsetangle > ANG90) { - offsetangle = ANG90; - } - - // It should fit even in a signed int, by now. - distangle = (int) (ANG90 - offsetangle); - hyp = PointToDist(MyBSP.curline.v1x, MyBSP.curline.v1y); - sineval = finesine(distangle); - rw_distance = FixedMul(hyp, sineval); - - seg.x1 = rw_x = start; - seg.x2 = stop; - seg.curline = MyBSP.curline; - /* - * This is the only place it's ever explicitly assigned. Therefore - * it always starts at stop+1. - */ - rw_stopx = stop + 1; - - // calculate scale at both ends and step - // this is the ONLY place where rw_scale is set. - seg.scale1 - = rw_scale - = ScaleFromGlobalAngle((view.angle + view.xtoviewangle[start])); - - if (stop > start) { - seg.scale2 - = ScaleFromGlobalAngle(view.angle + view.xtoviewangle[stop]); - seg.scalestep - = rw_scalestep = (seg.scale2 - rw_scale) / (stop - start); - } else { - // UNUSED: try to fix the stretched line bug - /* - * #if 0 if (rw_distance < FRACUNIT/2) { fixed_t trx,try; - * fixed_t gxt,gyt; trx = curline.v1.x - viewx; try = - * curline.v1.y - viewy; gxt = FixedMul(trx,viewcos); gyt = - * -FixedMul(try,viewsin); seg.scale1 = FixedDiv(projection, - * gxt-gyt)< MyBSP.backsector.floorheight) { - seg.silhouette = SIL_BOTTOM; - seg.bsilheight = MyBSP.frontsector.floorheight; - } else if (MyBSP.backsector.floorheight > view.z) { - seg.silhouette = SIL_BOTTOM; - seg.bsilheight = Integer.MAX_VALUE; - // seg.sprbottomclip = negonearray; - } - - if (MyBSP.frontsector.ceilingheight < MyBSP.backsector.ceilingheight) { - seg.silhouette |= SIL_TOP; - seg.tsilheight = MyBSP.frontsector.ceilingheight; - } else if (MyBSP.backsector.ceilingheight < view.z) { - seg.silhouette |= SIL_TOP; - seg.tsilheight = Integer.MIN_VALUE; - // seg.sprtopclip = screenheightarray; - } - - if (MyBSP.backsector.ceilingheight <= MyBSP.frontsector.floorheight) { - seg.setSprBottomClip(view.negonearray, 0); - seg.bsilheight = Integer.MAX_VALUE; - seg.silhouette |= SIL_BOTTOM; - } - - if (MyBSP.backsector.floorheight >= MyBSP.frontsector.ceilingheight) { - seg.setSprTopClip(view.screenheightarray, 0); - seg.tsilheight = Integer.MIN_VALUE; - seg.silhouette |= SIL_TOP; - } - - worldhigh = MyBSP.backsector.ceilingheight - view.z; - worldlow = MyBSP.backsector.floorheight - view.z; - - // hack to allow height changes in outdoor areas - if (MyBSP.frontsector.ceilingpic == TexMan.getSkyFlatNum() - && MyBSP.backsector.ceilingpic == TexMan - .getSkyFlatNum()) { - worldtop = worldhigh; - } - - markfloor = worldlow != worldbottom - || MyBSP.backsector.floorpic != MyBSP.frontsector.floorpic - || MyBSP.backsector.lightlevel != MyBSP.frontsector.lightlevel; // same plane on both sides - markceiling = worldhigh != worldtop - || MyBSP.backsector.ceilingpic != MyBSP.frontsector.ceilingpic - || MyBSP.backsector.lightlevel != MyBSP.frontsector.lightlevel; // same plane on both sides - - if (MyBSP.backsector.ceilingheight <= MyBSP.frontsector.floorheight - || MyBSP.backsector.floorheight >= MyBSP.frontsector.ceilingheight) { - // closed door - markceiling = markfloor = true; - } - - if (worldhigh < worldtop) { - // top texture - toptexture - = TexMan.getTextureTranslation(MyBSP.sidedef.toptexture); - if ((MyBSP.linedef.flags & ML_DONTPEGTOP) != 0) { - // top of texture at top - rw_toptexturemid = worldtop; - } else { - vtop - = MyBSP.backsector.ceilingheight - + TexMan.getTextureheight(MyBSP.sidedef.toptexture); - - // bottom of texture - rw_toptexturemid = vtop - view.z; - } - } - if (worldlow > worldbottom) { - // bottom texture - bottomtexture - = TexMan.getTextureTranslation(MyBSP.sidedef.bottomtexture); - - if ((MyBSP.linedef.flags & ML_DONTPEGBOTTOM) != 0) { - // bottom of texture at bottom - // top of texture at top - rw_bottomtexturemid = worldtop; - } else { - // top of texture at top - rw_bottomtexturemid = worldlow; - } - } - rw_toptexturemid += MyBSP.sidedef.rowoffset; - rw_bottomtexturemid += MyBSP.sidedef.rowoffset; - - // allocate space for masked texture tables - if (MyBSP.sidedef.midtexture != 0) { - // masked midtexture - maskedtexture = true; - seg_vars.maskedtexturecol = vp_vars.openings; - seg_vars.pmaskedtexturecol = vp_vars.lastopening - rw_x; - seg.setMaskedTextureCol(seg_vars.maskedtexturecol, - seg_vars.pmaskedtexturecol); - vp_vars.lastopening += rw_stopx - rw_x; - } - } - - // calculate rw_offset (only needed for textured lines) - segtextured - = (((midtexture | toptexture | bottomtexture) != 0) | maskedtexture); - - if (segtextured) { - offsetangle = addAngles(rw_normalangle, -rw_angle1); - - // Another "tricky spot": negative of an unsigned number? - if (offsetangle > ANG180) { - offsetangle = (-(int) offsetangle) & BITS32; - } - - if (offsetangle > ANG90) { - offsetangle = ANG90; - } - - sineval = finesine(offsetangle); - rw_offset = FixedMul(hyp, sineval); - - // Another bug: we CAN'T assume that the result won't wrap - // around. - // If that assumption is made, then texture alignment issues - // appear - if (((rw_normalangle - rw_angle1) & BITS32) < ANG180) { - rw_offset = -rw_offset; - } - - rw_offset += MyBSP.sidedef.textureoffset + MyBSP.curline.offset; - // This is OK, however: we can add as much shit as we want, - // as long as we trim it to the 32 LSB. Proof as to why - // this is always true is left as an exercise to the reader. - rw_centerangle = (ANG90 + view.angle - rw_normalangle) & BITS32; - - // calculate light table - // use different light tables - // for horizontal / vertical / diagonal - // OPTIMIZE: get rid of LIGHTSEGSHIFT globally - if (colormaps.fixedcolormap == null) { - lightnum - = (MyBSP.frontsector.lightlevel >> colormaps.lightSegShift()) - + colormaps.extralight; - - if (MyBSP.curline.v1y == MyBSP.curline.v2y) { - lightnum--; - } else if (MyBSP.curline.v1x == MyBSP.curline.v2x) { - lightnum++; - } - - if (lightnum < 0) { - colormaps.walllights = colormaps.scalelight[0]; - } else if (lightnum >= colormaps.lightLevels()) { - colormaps.walllights - = colormaps.scalelight[colormaps.lightLevels() - 1]; - } else { - colormaps.walllights = colormaps.scalelight[lightnum]; - } - } - } - - // if a floor / ceiling plane is on the wrong side - // of the view plane, it is definitely invisible - // and doesn't need to be marked. - if (MyBSP.frontsector.floorheight >= view.z) { - // above view plane - markfloor = false; - } - - if (MyBSP.frontsector.ceilingheight <= view.z - && MyBSP.frontsector.ceilingpic != TexMan.getSkyFlatNum()) { - // below view plane - markceiling = false; - } - - // calculate incremental stepping values for texture edges - worldtop >>= 4; - worldbottom >>= 4; - - topstep = -FixedMul(rw_scalestep, worldtop); - topfrac = (view.centeryfrac >> 4) - FixedMul(worldtop, rw_scale); - - bottomstep = -FixedMul(rw_scalestep, worldbottom); - bottomfrac - = (view.centeryfrac >> 4) - FixedMul(worldbottom, rw_scale); - - if (MyBSP.backsector != null) { - worldhigh >>= 4; - worldlow >>= 4; - - if (worldhigh < worldtop) { - pixhigh - = (view.centeryfrac >> 4) - FixedMul(worldhigh, rw_scale); - pixhighstep = -FixedMul(rw_scalestep, worldhigh); - } - - if (worldlow > worldbottom) { - pixlow - = (view.centeryfrac >> 4) - FixedMul(worldlow, rw_scale); - pixlowstep = -FixedMul(rw_scalestep, worldlow); - } - } - - // render it - if (markceiling) { - // System.out.println("Markceiling"); - vp_vars.ceilingplane - = vp_vars.CheckPlane(vp_vars.ceilingplane, rw_x, rw_stopx - 1); - } - - if (markfloor) { - // System.out.println("Markfloor"); - vp_vars.floorplane - = vp_vars.CheckPlane(vp_vars.floorplane, rw_x, rw_stopx - 1); - } - - RenderSegLoop(); - - // After rendering is actually performed, clipping is set. - // save sprite clipping info ... no top clipping? - if ((C2JUtils.flags(seg.silhouette, SIL_TOP) || maskedtexture) - && seg.nullSprTopClip()) { - - // memcpy (lastopening, ceilingclip+start, 2*(rw_stopx-start)); - System.arraycopy(ceilingclip, start, vp_vars.openings, - vp_vars.lastopening, rw_stopx - start); - - seg.setSprTopClip(vp_vars.openings, vp_vars.lastopening - start); - // seg.setSprTopClipPointer(); - vp_vars.lastopening += rw_stopx - start; - } - // no floor clipping? - if ((C2JUtils.flags(seg.silhouette, SIL_BOTTOM) || maskedtexture) - && seg.nullSprBottomClip()) { - // memcpy (lastopening, floorclip+start, 2*(rw_stopx-start)); - System.arraycopy(floorclip, start, vp_vars.openings, - vp_vars.lastopening, rw_stopx - start); - seg.setSprBottomClip(vp_vars.openings, vp_vars.lastopening - - start); - vp_vars.lastopening += rw_stopx - start; - } - - if (maskedtexture && C2JUtils.flags(seg.silhouette, SIL_TOP)) { - seg.silhouette |= SIL_TOP; - seg.tsilheight = Integer.MIN_VALUE; - } - if (maskedtexture && (seg.silhouette & SIL_BOTTOM) == 0) { - seg.silhouette |= SIL_BOTTOM; - seg.bsilheight = Integer.MAX_VALUE; - } - seg_vars.ds_p++; - } - - /** - * R_RenderSegLoop Draws zero, one, or two textures (and possibly a - * masked texture) for walls. Can draw or mark the starting pixel of - * floor and ceiling textures. Also sets the actual sprite clipping info - * (where sprites should be cut) Since rw_x ranges are non-overlapping, - * rendering all walls means completing the clipping list as well. The - * only difference between the parallel and the non-parallel version is - * that the parallel doesn't draw immediately but rather, generates - * RWIs. This can surely be unified to avoid replicating code. CALLED: - * CORE LOOPING ROUTINE. - */ - protected void RenderSegLoop() { - int angle; // angle_t - int index; - int yl; // low - int yh; // hight - int mid; - int texturecolumn = 0; // fixed_t - int top; - int bottom; - - for (; rw_x < rw_stopx; rw_x++) { - // mark floor / ceiling areas - yl = (topfrac + HEIGHTUNIT - 1) >> HEIGHTBITS; - - // no space above wall? - if (yl < ceilingclip[rw_x] + 1) { - yl = ceilingclip[rw_x] + 1; - } - - if (markceiling) { - top = ceilingclip[rw_x] + 1; - bottom = yl - 1; - - if (bottom >= floorclip[rw_x]) { - bottom = floorclip[rw_x] - 1; - } - - if (top <= bottom) { - vp_vars.visplanes[vp_vars.ceilingplane].setTop(rw_x, - (char) top); - vp_vars.visplanes[vp_vars.ceilingplane].setBottom(rw_x, - (char) bottom); - } - } - - yh = bottomfrac >> HEIGHTBITS; - - if (yh >= floorclip[rw_x]) { - yh = floorclip[rw_x] - 1; - } - - // A particular seg has been identified as a floor marker. - if (markfloor) { - top = yh + 1; - bottom = floorclip[rw_x] - 1; - if (top <= ceilingclip[rw_x]) { - top = ceilingclip[rw_x] + 1; - } - if (top <= bottom) { - vp_vars.visplanes[vp_vars.floorplane].setTop(rw_x, - (char) top); - vp_vars.visplanes[vp_vars.floorplane].setBottom(rw_x, - (char) bottom); - } - } - - // texturecolumn and lighting are independent of wall tiers - if (segtextured) { - // calculate texture offset - - // CAREFUL: a VERY anomalous point in the code. Their sum is - // supposed - // to give an angle not exceeding 45 degrees (or an index of - // 0x0FFF after - // shifting). If added with pure unsigned rules, this - // doesn't hold anymore, - // not even if accounting for overflow. - angle - = Tables.toBAMIndex(rw_centerangle - + (int) view.xtoviewangle[rw_x]); - - // FIXME: We are accessing finetangent here, the code seems - // pretty confident in that angle won't exceed 4K no matter - // what. - // But xtoviewangle alone can yield 8K when shifted. - // This usually only overflows if we idclip and look at - // certain directions (probably angles get fucked up), - // however it seems rare - // enough to just "swallow" the exception. You can eliminate - // it by anding - // with 0x1FFF if you're so inclined. - // FIXED by allowing overflow. See Tables for details. - texturecolumn - = rw_offset - FixedMul(finetangent[angle], rw_distance); - texturecolumn >>= FRACBITS; - // calculate lighting - index = rw_scale >> colormaps.lightScaleShift(); - - if (index >= colormaps.maxLightScale()) { - index = colormaps.maxLightScale() - 1; - } - - dcvars.dc_colormap = colormaps.walllights[index]; - dcvars.dc_x = rw_x; - dcvars.dc_iscale = (int) (0xffffffffL / rw_scale); - } - - // draw the wall tiers - if (midtexture != 0) { - // single sided line - dcvars.dc_yl = yl; - dcvars.dc_yh = yh; - dcvars.dc_texheight - = TexMan.getTextureheight(midtexture) >> FRACBITS; // killough - dcvars.dc_texturemid = rw_midtexturemid; - dcvars.dc_source_ofs = 0; - dcvars.dc_source - = TexMan.GetCachedColumn(midtexture, texturecolumn); - CompleteColumn(); - ceilingclip[rw_x] = (short) view.height; - floorclip[rw_x] = -1; - } else { - // two sided line - if (toptexture != 0) { - // top wall - mid = pixhigh >> HEIGHTBITS; - pixhigh += pixhighstep; - - if (mid >= floorclip[rw_x]) { - mid = floorclip[rw_x] - 1; - } - - if (mid >= yl) { - dcvars.dc_yl = yl; - dcvars.dc_yh = mid; - dcvars.dc_texturemid = rw_toptexturemid; - dcvars.dc_texheight = TexMan.getTextureheight(toptexture) >> FRACBITS; - dcvars.dc_source = TexMan.GetCachedColumn(toptexture, texturecolumn); - dcvars.dc_source_ofs = 0; - if (dcvars.dc_colormap == null) { - LOGGER.log(Level.FINE, "Two-sided"); - } - CompleteColumn(); - ceilingclip[rw_x] = (short) mid; - } else { - ceilingclip[rw_x] = (short) (yl - 1); - } - } else { - // no top wall - if (markceiling) { - ceilingclip[rw_x] = (short) (yl - 1); - } - } - - if (bottomtexture != 0) { - // bottom wall - mid = (pixlow + HEIGHTUNIT - 1) >> HEIGHTBITS; - pixlow += pixlowstep; - - // no space above wall? - if (mid <= ceilingclip[rw_x]) { - mid = ceilingclip[rw_x] + 1; - } - - if (mid <= yh) { - dcvars.dc_yl = mid; - dcvars.dc_yh = yh; - dcvars.dc_texturemid = rw_bottomtexturemid; - dcvars.dc_texheight = TexMan.getTextureheight(bottomtexture) >> FRACBITS; - dcvars.dc_source = TexMan.GetCachedColumn(bottomtexture, texturecolumn); - dcvars.dc_source_ofs = 0; - CompleteColumn(); - - floorclip[rw_x] = (short) mid; - } else { - floorclip[rw_x] = (short) (yh + 1); - } - } else { - // no bottom wall - if (markfloor) { - floorclip[rw_x] = (short) (yh + 1); - } - } - - if (maskedtexture) { - // save texturecol - // for backdrawing of masked mid texture - seg_vars.maskedtexturecol[seg_vars.pmaskedtexturecol + rw_x] = (short) texturecolumn; - } - } - - rw_scale += rw_scalestep; - topfrac += topstep; - bottomfrac += bottomstep; - } - } - - @Override - public void ClearClips() { - System.arraycopy(BLANKFLOORCLIP, 0, floorclip, 0, view.width); - System.arraycopy(BLANKCEILINGCLIP, 0, ceilingclip, 0, view.width); - } - - /** - * Called from RenderSegLoop. This should either invoke the column - * function, or store a wall rendering instruction in the parallel - * version. It's the only difference between the parallel and serial - * renderer, BTW. So override and implement accordingly. - */ - protected abstract void CompleteColumn(); - - @Override - public void ExecuteSetViewSize(int viewwidth) { - for (int i = 0; i < viewwidth; i++) { - BLANKFLOORCLIP[i] = (short) view.height; - BLANKCEILINGCLIP[i] = -1; - } - } - - @Override - public void CompleteRendering() { - // Nothing to do for serial. - } - - protected column_t col; - - public SegDrawer(SceneRenderer R) { - this.vp_vars = R.getVPVars(); - this.seg_vars = R.getSegVars(); - col = new column_t(); - seg_vars.drawsegs = malloc(drawseg_t::new, drawseg_t[]::new, seg_vars.MAXDRAWSEGS); - this.floorclip = new short[DOOM.vs.getScreenWidth()]; - this.ceilingclip = new short[DOOM.vs.getScreenWidth()]; - BLANKFLOORCLIP = new short[DOOM.vs.getScreenWidth()]; - BLANKCEILINGCLIP = new short[DOOM.vs.getScreenWidth()]; - } - - /** - * R_ScaleFromGlobalAngle Returns the texture mapping scale for the - * current line (horizontal span) at the given angle. rw_distance must - * be calculated first. - */ - protected int ScaleFromGlobalAngle(long visangle) { - int scale; // fixed_t - long anglea; - long angleb; - int sinea; - int sineb; - int num; // fixed_t - int den; - - // UNUSED - /* - * { fixed_t dist; fixed_t z; fixed_t sinv; fixed_t cosv; sinv = - * finesine[(visangle-rw_normalangle)>>ANGLETOFINESHIFT]; dist = - * FixedDiv (rw_distance, sinv); cosv = - * finecosine[(viewangle-visangle)>>ANGLETOFINESHIFT]; z = - * abs(FixedMul (dist, cosv)); scale = FixedDiv(projection, z); - * return scale; } - */ - anglea = (ANG90 + visangle - view.angle) & BITS32; - angleb = (ANG90 + visangle - rw_normalangle) & BITS32; - - // both sines are allways positive - sinea = finesine(anglea); - sineb = finesine(angleb); - num = FixedMul(view.projection, sineb) << view.detailshift; - den = FixedMul(rw_distance, sinea); - - if (den > num >> 16) { - scale = FixedDiv(num, den); - - if (scale > 64 * FRACUNIT) { - scale = 64 * FRACUNIT; - } else if (scale < 256) { - scale = 256; - } - } else { - scale = 64 * FRACUNIT; - } - - return scale; - } - - @Override - public void setGlobalAngle(long angle) { - this.rw_angle1 = angle; - } - } - - protected interface IPlaneDrawer { - - void InitPlanes(); - - void MapPlane(int y, int x1, int x2); - - void DrawPlanes(); - - int[] getDistScale(); - - /** - * Sync up in case there's concurrent planes/walls rendering - */ - void sync(); - } - - protected interface ISegDrawer extends ILimitResettable { - - void ClearClips(); - - short[] getBLANKCEILINGCLIP(); - - short[] getBLANKFLOORCLIP(); - - short[] getFloorClip(); - - short[] getCeilingClip(); - - void ExecuteSetViewSize(int viewwidth); - - void setGlobalAngle(long angle1); - - void StoreWallRange(int first, int last); - - /** - * If there is anything to do beyond the BPS traversal, - * e.g. parallel rendering - */ - void CompleteRendering(); - - /** - * Sync up in case there's concurrent planes/walls rendering - */ - void sync(); - } - - protected class Planes extends PlaneDrawer { - - Planes(DoomMain DOOM, RendererState R) { - super(DOOM, R); - } - - /** - * R_DrawPlanes At the end of each frame. This also means that visplanes - * must have been set BEFORE we called this function. Therefore, look - * for errors behind. - * - * @throws IOException - */ - @Override - public void DrawPlanes() { - if (DEBUG) { - LOGGER.log(Level.FINE, String.format("DrawPlanes: %d", vp_vars.lastvisplane)); - } - visplane_t pln; // visplane_t - int light; - int x; - int stop; - int angle; - - if (RANGECHECK) { - rangeCheckErrors(); - } - - for (int pl = 0; pl < vp_vars.lastvisplane; pl++) { - pln = vp_vars.visplanes[pl]; - if (DEBUG2) { - LOGGER.log(Level.FINER, String.valueOf(pln)); - } - - if (pln.minx > pln.maxx) { - continue; - } - // sky flat - if (pln.picnum == TexMan.getSkyFlatNum()) { - // Cache skytexture stuff here. They aren't going to change - // while - // being drawn, after all, are they? - int skytexture = TexMan.getSkyTexture(); - skydcvars.dc_texheight - = TexMan.getTextureheight(skytexture) >> FRACBITS; - skydcvars.dc_iscale - = vpvars.getSkyScale() >> view.detailshift; - - /** - * Sky is allways drawn full bright, i.e. colormaps[0] is - * used. Because of this hack, sky is not affected by INVUL - * inverse mapping. - * Settings.fixskypalette handles the fix - */ - if (DOOM.CM.equals(Settings.fix_sky_palette, Boolean.TRUE) && colormap.fixedcolormap != null) { - skydcvars.dc_colormap = colormap.fixedcolormap; - } else { - skydcvars.dc_colormap = colormap.colormaps[Palettes.COLORMAP_FIXED]; - } - skydcvars.dc_texturemid = TexMan.getSkyTextureMid(); - for (x = pln.minx; x <= pln.maxx; x++) { - - skydcvars.dc_yl = pln.getTop(x); - skydcvars.dc_yh = pln.getBottom(x); - - if (skydcvars.dc_yl <= skydcvars.dc_yh) { - angle - = (int) (addAngles(view.angle, view.xtoviewangle[x]) >>> ANGLETOSKYSHIFT); - skydcvars.dc_x = x; - // Optimized: texheight is going to be the same - // during normal skies drawing...right? - skydcvars.dc_source - = TexMan.GetCachedColumn(skytexture, angle); - colfunc.sky.invoke(); - } - } - continue; - } - - // regular flat - dsvars.ds_source = TexMan.getSafeFlat(pln.picnum); - - planeheight = Math.abs(pln.height - view.z); - light = (pln.lightlevel >> colormap.lightSegShift()) + colormap.extralight; - - if (light >= colormap.lightLevels()) { - light = colormap.lightLevels() - 1; - } - - if (light < 0) { - light = 0; - } - - planezlight = colormap.zlight[light]; - - // We set those values at the border of a plane's top to a - // "sentinel" value...ok. - pln.setTop(pln.maxx + 1, visplane_t.SENTINEL); - pln.setTop(pln.minx - 1, visplane_t.SENTINEL); - - stop = pln.maxx + 1; - - for (x = pln.minx; x <= stop; x++) { - MakeSpans(x, pln.getTop(x - 1), pln.getBottom(x - 1), pln.getTop(x), pln.getBottom(x)); - } - - // Z_ChangeTag (ds_source, PU_CACHE); - } - } - - } // End Plane class - - // /////////////////////// LIGHTS, POINTERS, COLORMAPS ETC. //////////////// - // /// FROM R_DATA, R_MAIN , R_DRAW ////////// - /** - * OK< this is supposed to "peg" into screen buffer 0. It will work AS LONG - * AS SOMEONE FUCKING ACTUALLY SETS IT !!!! - */ - protected V screen; - - protected static final boolean RANGECHECK = false; - - /** - * These are actually offsets inside screen 0 (or any screen). Therefore - * anything using them should "draw" inside screen 0 - */ - protected int[] ylookup = new int[MAXHEIGHT]; - - /** - * Columns offset to set where?! - */ - protected int[] columnofs = new int[MAXWIDTH]; - - /** - * General purpose. Used for solid walls and as an intermediary for - * threading - */ - protected ColVars dcvars; - - /** - * Used for spans - */ - protected SpanVars dsvars; - - // Used for sky drawer, to avoid clashing with shared dcvars - protected ColVars skydcvars; - - /** - * Masked drawing functions get "pegged" to this set of dcvars, passed upon - * initialization. However, multi-threaded vars are better off carrying each - * their own ones. - */ - protected ColVars maskedcvars; - - /** - * e6y: wide-res Borrowed from PrBoom+; - */ - - /* - * protected int wide_centerx, wide_ratio, wide_offsetx, wide_offset2x, - * wide_offsety, wide_offset2y; protected final base_ratio_t[] - * BaseRatioSizes = { new base_ratio_t(960, 600, 0, 48, 1.333333f), // 4:3 - * new base_ratio_t(1280, 450, 0, 48 * 3 / 4, 1.777777f), // 16:9 new - * base_ratio_t(1152, 500, 0, 48 * 5 / 6, 1.6f), // 16:10 new - * base_ratio_t(960, 600, 0, 48, 1.333333f), new base_ratio_t(960, 640, - * (int) (6.5 * FRACUNIT), 48 * 15 / 16, 1.25f) // 5:4 }; - */ - /** - * just for profiling purposes - */ - protected int framecount; - protected int sscount; - protected int linecount; - protected int loopcount; - - // - // precalculated math tables - // - protected long clipangle; - - // Set to 2*clipangle later. - protected long CLIPANGLE2; - - // The viewangletox[viewangle + FINEANGLES/4] lookup - // maps the visible view angles to screen X coordinates, - // flattening the arc to a flat projection plane. - // There will be many angles mapped to the same X. - protected final int[] viewangletox = new int[FINEANGLES / 2]; - - /** - * The xtoviewangle[] table maps a screen pixel to the lowest viewangle that - * maps back to x ranges from clipangle to -clipangle. - * - * @see view.xtoviewangle - */ - //protected long[] view.xtoviewangle;// MAES: to resize - // UNUSED. - // The finetangentgent[angle+FINEANGLES/4] table - // holds the fixed_t tangent values for view angles, - // ranging from MININT to 0 to MAXINT. - // fixed_t finetangent[FINEANGLES/2]; - // fixed_t finesine[5*FINEANGLES/4]; - // MAES: uh oh. So now all these ints must become finesines? fuck that. - // Also wtf @ this hack....this points to approx 1/4th of the finesine - // table, but what happens if I read past it? - // int[] finecosine = finesine[FINEANGLES/4]; - - /* - * MAES: what's going on with light tables here. OK...so these should be - * "unsigned bytes", since, after all, they'll be used as pointers inside an - * array to finally pick a color, so they should be expanded to shorts. - */ - // //////////// SOME UTILITY METHODS ///////////// - /** - * Assigns a point of view before calling PointToAngle CAREFUL: this isn't a - * pure function, as it alters the renderer's state! - */ - @Override - public final long PointToAngle2(int x1, int y1, int x2, int y2) { - // Careful with assignments... - view.x = x1; - view.y = y1; - - return view.PointToAngle(x2, y2); - } - - // - // R_InitPointToAngle - // - /* - * protected final void InitPointToAngle () { // UNUSED - now getting from - * tables.c if (false){ int i; long t; float f; // // slope (tangent) to - * angle lookup // for (i=0 ; i<=SLOPERANGE ; i++) { f = (float) Math.atan( - * (double)(i/SLOPERANGE )/(3.141592657*2)); t = (long) (0xffffffffL*f); - * tantoangle[i] = (int) t; } } } - */ - /** - * Public, static, stateless version of PointToAngle2. Call this one when - * "renderless" use of PointToAngle2 is required. - */ - public static long PointToAngle(int viewx, int viewy, int x, int y) { - // MAES: note how we don't use &BITS32 here. That is because - // we know that the maximum possible value of tantoangle is angle - // This way, we are actually working with vectors emanating - // from our current position. - x -= viewx; - y -= viewy; - - if ((x == 0) && (y == 0)) { - return 0; - } - - if (x >= 0) { - // x >=0 - if (y >= 0) { - // y>= 0 - - if (x > y) { - // octant 0 - return tantoangle[SlopeDiv(y, x)]; - } else { - // octant 1 - return (ANG90 - 1 - tantoangle[SlopeDiv(x, y)]); - } - } else { - // y<0 - y = -y; - - if (x > y) { - // octant 8 - return (-tantoangle[SlopeDiv(y, x)]); - } else { - // octant 7 - return (ANG270 + tantoangle[SlopeDiv(x, y)]); - } - } - } else { - // x<0 - x = -x; - - if (y >= 0) { - // y>= 0 - if (x > y) { - // octant 3 - return (ANG180 - 1 - tantoangle[SlopeDiv(y, x)]); - } else { - // octant 2 - return (ANG90 + tantoangle[SlopeDiv(x, y)]); - } - } else { - // y<0 - y = -y; - - if (x > y) { - // octant 4 - return (ANG180 + tantoangle[SlopeDiv(y, x)]); - } else { - // octant 5 - return (ANG270 - 1 - tantoangle[SlopeDiv(x, y)]); - } - } - } - // This is actually unreachable. - // return 0; - } - - // - // R_InitTables - // - protected void InitTables() { - // UNUSED: now getting from tables.c - /* - * int i; float a; float fv; int t; // viewangle tangent table for (i=0 - * ; i dx) { - temp = dx; - dx = dy; - dy = temp; - } - - // If one or both of the distances are *exactly* zero at this point, - // then this means that the wall is in your face anyway, plus we want to - // avoid a division by zero. So you get zero. - if (dx == 0) { - return 0; - } - - /* - * If dx is zero, this is going to bomb. Fixeddiv will return MAXINT aka - * 7FFFFFFF, >> DBITS will make it 3FFFFFF, which is more than enough to - * break tantoangle[]. In the original C code, this probably didn't - * matter: there would probably be garbage orientations thrown all - * around. However this is unacceptable in Java. OK, so the safeguard - * above prevents that. Still... this method is only called once per - * visible wall per frame, so one check more or less at this point won't - * change much. It's better to be safe than sorry. - */ - // This effectively limits the angle to - // angle = Math.max(FixedDiv(dy, dx), 2048) >> DBITS; - angle = (FixedDiv(dy, dx) & 0x1FFFF) >> DBITS; - - // Since the division will be 0xFFFF at most, DBITS will restrict - // the maximum angle index to 7FF, about 45, so adding ANG90 with - // no other safeguards is OK. - angle = (int) ((tantoangle[angle] + ANG90) >> ANGLETOFINESHIFT); - - // use as cosine - dist = FixedDiv(dx, finesine[angle]); - - return dist; - } - - // //////////// COMMON RENDERING GLOBALS //////////////// - // //////////////// COLUMN AND SPAN FUNCTIONS ////////////// - protected ColFuncs colfunc; - - // Keep two sets of functions. - protected ColFuncs colfunchi; - - protected ColFuncs colfunclow; - - protected void setHiColFuns() { - colfunchi.main = colfunchi.base = DrawColumn; - colfunchi.masked = DrawColumnMasked; - colfunchi.fuzz = DrawFuzzColumn; - colfunchi.trans = DrawTranslatedColumn; - colfunchi.glass = DrawTLColumn; - colfunchi.player = DrawColumnPlayer; - colfunchi.sky = DrawColumnSkies; - } - - protected void setLowColFuns() { - colfunclow.main = colfunclow.base = DrawColumnLow; - colfunclow.masked = DrawColumnMaskedLow; - colfunclow.fuzz = DrawFuzzColumnLow; - colfunclow.trans = DrawTranslatedColumnLow; - colfunclow.glass = DrawTLColumn; - colfunclow.player = DrawColumnMaskedLow; - colfunclow.sky = DrawColumnSkiesLow; - } - - @Override - public ColFuncs getColFuncsHi() { - return this.colfunchi; - } - - @Override - public ColFuncs getColFuncsLow() { - return this.colfunclow; - } - - @Override - public ColVars getMaskedDCVars() { - return this.maskedcvars; - } - - // These column functions are "fixed" for a given renderer, and are - // not used directly, but only after passing them to colfuncs - protected DoomColumnFunction DrawTranslatedColumn; - protected DoomColumnFunction DrawTranslatedColumnLow; - protected DoomColumnFunction DrawColumnPlayer; - protected DoomColumnFunction DrawColumnSkies; - protected DoomColumnFunction DrawColumnSkiesLow; - protected DoomColumnFunction DrawFuzzColumn; - protected DoomColumnFunction DrawFuzzColumnLow; - protected DoomColumnFunction DrawColumn; - protected DoomColumnFunction DrawColumnLow; - protected DoomColumnFunction DrawColumnMasked; - protected DoomColumnFunction DrawColumnMaskedLow; - protected DoomColumnFunction DrawTLColumn; - - /** - * to be set in UnifiedRenderer - */ - protected DoomSpanFunction DrawSpan, DrawSpanLow; - - // ////////////// r_draw methods ////////////// - /** - * R_DrawViewBorder Draws the border around the view for different size windows - * Made use of CopyRect there - * - Good Sign 2017/04/06 - */ - @Override - public void DrawViewBorder() { - if (view.scaledwidth == DOOM.vs.getScreenWidth()) { - return; - } - - final int top = ((DOOM.vs.getScreenHeight() - DOOM.statusBar.getHeight()) - view.height) / 2; - final int side = (DOOM.vs.getScreenWidth() - view.scaledwidth) / 2; - final Rectangle rect; - // copy top - rect = new Rectangle(0, 0, DOOM.vs.getScreenWidth(), top); - DOOM.graphicSystem.CopyRect(BG, rect, FG); - // copy left side - rect.setBounds(0, top, side, view.height); - DOOM.graphicSystem.CopyRect(BG, rect, FG); - // copy right side - rect.x = side + view.scaledwidth; - DOOM.graphicSystem.CopyRect(BG, rect, FG); - // copy bottom - rect.setBounds(0, top + view.height, DOOM.vs.getScreenWidth(), top); - DOOM.graphicSystem.CopyRect(BG, rect, FG); - } - - @Override - public void ExecuteSetViewSize() { - int cosadj; - int dy; - int level; - int startmap; - - setsizeneeded = false; - - // 11 Blocks means "full screen" - if (setblocks == 11) { - view.scaledwidth = DOOM.vs.getScreenWidth(); - view.height = DOOM.vs.getScreenHeight(); - } else if (DOOM.CM.equals(Settings.scale_screen_tiles, Boolean.TRUE)) { - /** - * Make it exactly as in vanilla DOOM - * - Good Sign 2017/05/08 - */ - view.scaledwidth = (setblocks * 32) * DOOM.vs.getScalingX(); - view.height = ((setblocks * 168 / 10) & ~7) * DOOM.vs.getScalingY(); - } else { // Mocha Doom formula looks better for non-scaled tiles - view.scaledwidth = setblocks * (DOOM.vs.getScreenWidth() / 10); - // Height can only be a multiple of 8. - view.height = (short) ((setblocks * (DOOM.vs.getScreenHeight() - DOOM.statusBar.getHeight()) / 10) & ~7); - } - - skydcvars.viewheight - = maskedcvars.viewheight = dcvars.viewheight = view.height; - - view.detailshift = setdetail; - view.width = view.scaledwidth >> view.detailshift; - - view.centery = view.height / 2; - view.centerx = view.width / 2; - view.centerxfrac = (view.centerx << FRACBITS); - view.centeryfrac = (view.centery << FRACBITS); - view.projection = view.centerxfrac; - - skydcvars.centery = maskedcvars.centery = dcvars.centery = view.centery; - - // High detail - if (view.detailshift == 0) { - - colfunc = colfunchi; - dsvars.spanfunc = DrawSpan; - } else { - // Low detail - colfunc = colfunclow; - dsvars.spanfunc = DrawSpanLow; - - } - - InitBuffer(view.scaledwidth, view.height); - - InitTextureMapping(); - - // psprite scales - // pspritescale = FRACUNIT*viewwidth/vs.getScreenWidth(); - // pspriteiscale = FRACUNIT*vs.getScreenWidth()/viewwidth; - MyThings.setPspriteScale((int) (FRACUNIT * (DOOM.vs.getScreenMul() * view.width) / DOOM.vs.getScreenWidth())); - MyThings.setPspriteIscale((int) (FRACUNIT * (DOOM.vs.getScreenWidth() / (view.width * DOOM.vs.getScreenMul())))); - vp_vars.setSkyScale((int) (FRACUNIT * (DOOM.vs.getScreenWidth() / (view.width * DOOM.vs.getScreenMul())))); - - view.BOBADJUST = this.DOOM.vs.getSafeScaling() << 15; - view.WEAPONADJUST = (int) ((DOOM.vs.getScreenWidth() / (2 * DOOM.vs.getScreenMul())) * FRACUNIT); - - // thing clipping - for (int i = 0; i < view.width; i++) { - view.screenheightarray[i] = (short) view.height; - } - - // planes - for (int i = 0; i < view.height; i++) { - dy = ((i - view.height / 2) << FRACBITS) + FRACUNIT / 2; - dy = Math.abs(dy); - vp_vars.yslope[i] = FixedDiv((view.width << view.detailshift) / 2 * FRACUNIT, dy); - // MyPlanes.yslopef[i] = ((viewwidth<= colormaps.numColorMaps()) { - level = colormaps.numColorMaps() - 1; - } - colormaps.scalelight[i][j] = colormaps.colormaps[level]; - } - } - - MySegs.ExecuteSetViewSize(view.width); - } - - private final Rectangle backScreenRect = new Rectangle(); - private final Rectangle tilePatchRect = new Rectangle(); - - /** - * R_FillBackScreen Fills the back screen with a pattern for variable screen - * sizes Also draws a beveled edge. This is actually stored in screen 1, and - * is only OCCASIONALLY written to screen 0 (the visible one) by calling - * R_VideoErase. - */ - @Override - @R_Draw.C(R_FillBackScreen) - public void FillBackScreen() { - final boolean scaleSetting = Engine.getConfig().equals(Settings.scale_screen_tiles, Boolean.TRUE); - flat_t src; - DoomScreen dest; - int x; - int y; - patch_t patch; - - // DOOM border patch. - String name1 = "FLOOR7_2"; - - // DOOM II border patch. - String name2 = "GRNROCK"; - - String name; - - if (view.scaledwidth == DOOM.vs.getScreenWidth()) { - return; - } - - if (DOOM.isCommercial()) { - name = name2; - } else { - name = name1; - } - - /* This is a flat we're reading here */ - src = DOOM.wadLoader.CacheLumpName(name, PU_CACHE, flat_t.class); - dest = BG; - - /** - * TODO: cache it? - * This part actually draws the border itself, without bevels - * - * MAES: - * improved drawing routine for extended bit-depth compatibility. - * - * Now supports configurable vanilla-like scaling of tiles - * - Good Sign 2017/04/09 - * - * @SourceCode.Compatible - */ - Tiling: - { - this.backScreenRect.setBounds(0, 0, DOOM.vs.getScreenWidth(), DOOM.vs.getScreenHeight() - DOOM.statusBar.getHeight()); - this.tilePatchRect.setBounds(0, 0, 64, 64); - V block = DOOM.graphicSystem.convertPalettedBlock(src.data); - if (scaleSetting) { - block = DOOM.graphicSystem.ScaleBlock(block, DOOM.vs, tilePatchRect.width, tilePatchRect.height); - this.tilePatchRect.width *= DOOM.graphicSystem.getScalingX(); - this.tilePatchRect.height *= DOOM.graphicSystem.getScalingY(); - } - DOOM.graphicSystem.TileScreenArea(dest, backScreenRect, block, tilePatchRect); - } - - final int scaleFlags = V_NOSCALESTART | (scaleSetting ? 0 : V_NOSCALEOFFSET | V_NOSCALEPATCH); - final int stepX = scaleSetting ? DOOM.graphicSystem.getScalingX() << 3 : 8; - final int stepY = scaleSetting ? DOOM.graphicSystem.getScalingY() << 3 : 8; - - patch = DOOM.wadLoader.CachePatchName("BRDR_T", PU_CACHE); - for (x = 0; x < view.scaledwidth; x += stepX) { - DOOM.graphicSystem.DrawPatchScaled(BG, patch, DOOM.vs, view.windowx + x, view.windowy - stepY, scaleFlags); - } - - patch = DOOM.wadLoader.CachePatchName("BRDR_B", PU_CACHE); - for (x = 0; x < view.scaledwidth; x += stepX) { - DOOM.graphicSystem.DrawPatchScaled(BG, patch, DOOM.vs, view.windowx + x, view.windowy + view.height, scaleFlags); - } - - patch = DOOM.wadLoader.CachePatchName("BRDR_L", PU_CACHE); - for (y = 0; y < view.height; y += stepY) { - DOOM.graphicSystem.DrawPatchScaled(BG, patch, DOOM.vs, view.windowx - stepX, view.windowy + y, scaleFlags); - } - - patch = DOOM.wadLoader.CachePatchName("BRDR_R", PU_CACHE); - for (y = 0; y < view.height; y += stepY) { - DOOM.graphicSystem.DrawPatchScaled(BG, patch, DOOM.vs, view.windowx + view.scaledwidth, view.windowy + y, scaleFlags); - } - - // Draw beveled edge. Top-left - patch = DOOM.wadLoader.CachePatchName("BRDR_TL", PU_CACHE); - DOOM.graphicSystem.DrawPatchScaled(BG, patch, DOOM.vs, view.windowx - stepX, view.windowy - stepY, scaleFlags); - - // Top-right. - patch = DOOM.wadLoader.CachePatchName("BRDR_TR", PU_CACHE); - DOOM.graphicSystem.DrawPatchScaled(BG, patch, DOOM.vs, view.windowx + view.scaledwidth, view.windowy - stepY, scaleFlags); - - // Bottom-left - patch = DOOM.wadLoader.CachePatchName("BRDR_BL", PU_CACHE); - DOOM.graphicSystem.DrawPatchScaled(BG, patch, DOOM.vs, view.windowx - stepX, view.windowy + view.height, scaleFlags); - - // Bottom-right. - patch = DOOM.wadLoader.CachePatchName("BRDR_BR", PU_CACHE); - DOOM.graphicSystem.DrawPatchScaled(BG, patch, DOOM.vs, view.windowx + view.width, view.windowy + view.height, scaleFlags); - } - - /** - * R_Init - */ - @Override - public void Init() { - // Any good reason for this to be here? - // drawsegs=new drawseg_t[MAXDRAWSEGS]; - // C2JUtils.initArrayOfObjects(drawsegs); - - // DON'T FORGET ABOUT MEEEEEE!!!11!!! - this.screen = this.DOOM.graphicSystem.getScreen(FG); - - LOGGER.log(Level.INFO, "R_InitData"); - InitData(); - // InitPointToAngle (); - LOGGER.log(Level.INFO, "R_InitPointToAngle"); - - // ds.DM.viewwidth / ds.viewheight / detailLevel are set by the defaults - LOGGER.log(Level.INFO, "R_InitTables"); - InitTables(); - - SetViewSize(DOOM.menu.getScreenBlocks(), DOOM.menu.getDetailLevel()); - - LOGGER.log(Level.INFO, "R_InitPlanes"); - MyPlanes.InitPlanes(); - - LOGGER.log(Level.INFO, "R_InitLightTables"); - InitLightTables(); - - int initSkyMap = TexMan.InitSkyMap(); - LOGGER.log(Level.INFO, String.format("R_InitSkyMap: %d", initSkyMap)); - - LOGGER.log(Level.INFO, "R_InitTranslationsTables"); - InitTranslationTables(); - - LOGGER.log(Level.INFO, "R_InitTranMap"); - R_InitTranMap(0); - - LOGGER.log(Level.INFO, "R_InitDrawingFunctions"); - R_InitDrawingFunctions(); - - framecount = 0; - } - - /** - * R_InitBuffer Creates lookup tables that avoid multiplies and other - * hazzles for getting the framebuffer address of a pixel to draw. MAES: - * this is "pinned" to screen[0] of a Video Renderer. We will handle this - * differently elsewhere... - */ - protected void InitBuffer(int width, int height) { - int i; - - // Handle resize, - // e.g. smaller view windows - // with border and/or status bar. - view.windowx = (DOOM.vs.getScreenWidth() - width) >> 1; - - // Column offset. For windows. - for (i = 0; i < width; i++) { - columnofs[i] = view.windowx + i; - } - - // SamE with base row offset. - if (width == DOOM.vs.getScreenWidth()) { - view.windowy = 0; - } else { - view.windowy = (DOOM.vs.getScreenHeight() - DOOM.statusBar.getHeight() - height) >> 1; - } - - // Preclaculate all row offsets. - for (i = 0; i < height; i++) { - ylookup[i] = /* screens[0] + */ (i + view.windowy) * DOOM.vs.getScreenWidth(); - } - } - - /** - * R_InitTextureMapping Not moved into the TextureManager because it's - * tighly coupled to the visuals, rather than textures. Perhaps the name is - * not the most appropriate. - */ - protected void InitTextureMapping() { - int i, x, t; - int focallength; // fixed_t - int fov = FIELDOFVIEW; - - // For widescreen displays, increase the FOV so that the middle part of - // the - // screen that would be visible on a 4:3 display has the requested FOV. - /* - * UNUSED if (wide_centerx != centerx) { // wide_centerx is what centerx - * would be // if the display was not widescreen fov = (int) - * (Math.atan((double) centerx Math.tan((double) fov * Math.PI / - * FINEANGLES) / (double) wide_centerx) FINEANGLES / Math.PI); if (fov > - * 130 * FINEANGLES / 360) fov = 130 * FINEANGLES / 360; } - */ - // Use tangent table to generate viewangletox: - // viewangletox will give the next greatest x - // after the view angle. - // - // Calc focallength - // so FIELDOFVIEW angles covers vs.getScreenWidth(). - focallength - = FixedDiv(view.centerxfrac, finetangent[QUARTERMARK + FIELDOFVIEW - / 2]); - - for (i = 0; i < FINEANGLES / 2; i++) { - if (finetangent[i] > FRACUNIT * 2) { - t = -1; - } else if (finetangent[i] < -FRACUNIT * 2) { - t = view.width + 1; - } else { - t = FixedMul(finetangent[i], focallength); - t = (view.centerxfrac - t + FRACUNIT - 1) >> FRACBITS; - - if (t < -1) { - t = -1; - } else if (t > view.width + 1) { - t = view.width + 1; - } - } - viewangletox[i] = t; - } - - // Scan viewangletox[] to generate xtoviewangle[]: - // xtoviewangle will give the smallest view angle - // that maps to x. - for (x = 0; x <= view.width; x++) { - i = 0; - while (viewangletox[i] > x) { - i++; - } - view.xtoviewangle[x] = addAngles((i << ANGLETOFINESHIFT), -ANG90); - } - - // Take out the fencepost cases from viewangletox. - for (i = 0; i < FINEANGLES / 2; i++) { - t = FixedMul(finetangent[i], focallength); - t = view.centerx - t; - - if (viewangletox[i] == -1) { - viewangletox[i] = 0; - } else if (viewangletox[i] == view.width + 1) { - viewangletox[i] = view.width; - } - } - - clipangle = view.xtoviewangle[0]; - // OPTIMIZE: assign constant for optimization. - CLIPANGLE2 = (2 * clipangle) & BITS32; - } - - // - // R_InitLightTables - // Only inits the zlight table, - // because the scalelight table changes with view size. - // - protected final static int DISTMAP = 2; - - protected void InitLightTables() { - int i; - int j; - int startmap; - int scale; - - // Calculate the light levels to use - // for each level / distance combination. - for (i = 0; i < colormaps.lightLevels(); i++) { - startmap = ((colormaps.lightLevels() - colormaps.lightBright() - i) * 2) * colormaps.numColorMaps() / colormaps.lightLevels(); - for (j = 0; j < colormaps.maxLightZ(); j++) { - // CPhipps - use 320 here instead of vs.getScreenWidth(), otherwise hires is - // brighter than normal res - - scale = FixedDiv((320 / 2 * FRACUNIT), (j + 1) << colormaps.lightZShift()); - int t, level = startmap - (scale >>= colormaps.lightScaleShift()) / DISTMAP; - - if (level < 0) { - level = 0; - } - - if (level >= colormaps.numColorMaps()) { - level = colormaps.numColorMaps() - 1; - } - - // zlight[i][j] = colormaps + level*256; - colormaps.zlight[i][j] = colormaps.colormaps[level]; - } - } - } - - protected static final int TSC = 12; - - /** - * number of fixed point digits in - * filter percent - */ - byte[] main_tranmap; - - /** - * A faster implementation of the tranmap calculations. Almost 10x faster - * than the old one! - * - * @param progress - */ - protected void R_InitTranMap(int progress) { - int lump = DOOM.wadLoader.CheckNumForName("TRANMAP"); - - long ta = System.nanoTime(); - - // PRIORITY: a map file has been specified from commandline. Try to read - // it. If OK, this trumps even those specified in lumps. - DOOM.cVarManager.with(CommandVariable.TRANMAP, 0, (String tranmap) -> { - if (C2JUtils.testReadAccess(tranmap)) { - LOGGER.log(Level.INFO, - String.format("Translucency map file %s specified in -tranmap arg. Attempting to use...", tranmap)); - main_tranmap = new byte[256 * 256]; // killough 4/11/98 - int result = MenuMisc.ReadFile(tranmap, main_tranmap); - if (result > 0) { - return; - } - - LOGGER.log(Level.SEVERE, "R_InitTranMap: translucency map failure"); - } - }); - - // Next, if a tranlucency filter map lump is present, use it - if (lump != -1) { // Set a pointer to the translucency filter maps. - LOGGER.log(Level.INFO, "Translucency map found in lump. Attempting to use..."); - // main_tranmap=new byte[256*256]; // killough 4/11/98 - main_tranmap = DOOM.wadLoader.CacheLumpNumAsRawBytes(lump, Defines.PU_STATIC); // killough - // 4/11/98 - // Tolerate 64K or more. - if (main_tranmap.length >= 0x10000) { - return; - } - LOGGER.log(Level.SEVERE, "R_InitTranMap: tranlucency filter map failure"); // Not good, try something else. - } - - // A default map file already exists. Try to read it. - if (C2JUtils.testReadAccess("tranmap.dat")) { - LOGGER.log(Level.INFO, "Translucency map found in default tranmap.dat file. Attempting to use..."); - main_tranmap = new byte[256 * 256]; // killough 4/11/98 - int result = MenuMisc.ReadFile("tranmap.dat", main_tranmap); - if (result > 0) { - return; // Something went wrong, so fuck that. - } - } - - // Nothing to do, so we must synthesize it from scratch. And, boy, is it - // slooow. - { // Compose a default transparent filter map based on PLAYPAL. - LOGGER.log(Level.INFO, "Computing translucency map from scratch...that's gonna be SLOW..."); - byte[] playpal = DOOM.wadLoader.CacheLumpNameAsRawBytes("PLAYPAL", Defines.PU_STATIC); - main_tranmap = new byte[256 * 256]; // killough 4/11/98 - int[] basepal = new int[3 * 256]; - int[] mixedpal = new int[3 * 256 * 256]; - - main_tranmap = new byte[256 * 256]; - - // Init array of base colors. - for (int i = 0; i < 256; i++) { - basepal[3 * i] = 0Xff & playpal[i * 3]; - basepal[1 + 3 * i] = 0Xff & playpal[1 + i * 3]; - basepal[2 + 3 * i] = 0Xff & playpal[2 + i * 3]; - } - - // Init array of mixed colors. These are true RGB. - // The diagonal of this array will be the original colors. - for (int i = 0; i < 256 * 3; i += 3) { - for (int j = 0; j < 256 * 3; j += 3) { - mixColors(basepal, basepal, mixedpal, i, j, j * 256 + i); - } - } - - // Init distance map. Every original palette colour has a - // certain distance from all the others. The diagonal is zero. - // The interpretation is that e.g. the mixture of color 2 and 8 will - // have a RGB value, which is closest to euclidean distance to - // e.g. original color 9. Therefore we should put "9" in the (2,8) - // and (8,2) cells of the tranmap. - final float[] tmpdist = new float[256]; - - for (int a = 0; a < 256; a++) { - for (int b = a; b < 256; b++) { - // We evaluate the mixture of a and b - // Construct distance table vs all of the ORIGINAL colors. - for (int k = 0; k < 256; k++) { - tmpdist[k] = colorDistance(mixedpal, basepal, 3 * (a + b * 256), k * 3); - } - - main_tranmap[(a << 8) | b] = (byte) findMin(tmpdist); - main_tranmap[(b << 8) | a] = main_tranmap[(a << 8) | b]; - } - } - LOGGER.log(Level.INFO, "R_InitTranMap: done"); - if (MenuMisc.WriteFile("tranmap.dat", main_tranmap, - main_tranmap.length)) { - LOGGER.log(Level.INFO, "TRANMAP.DAT saved to disk for your convenience! Next time will be faster."); - } - } - - long b = System.nanoTime(); - LOGGER.log(Level.INFO, String.format("Tranmap %d", (b - ta) / 1000000)); - } - - /** - * Mixes two RGB colors. Nuff said - */ - protected void mixColors(int[] a, int[] b, int[] c, int pa, int pb, - int pc) { - c[pc] = (a[pa] + b[pb]) / 2; - c[pc + 1] = (a[pa + 1] + b[pb + 1]) / 2; - c[pc + 2] = (a[pa + 2] + b[pb + 2]) / 2; - - } - - /** - * Returns the euclidean distance of two RGB colors. Nuff said - */ - protected float colorDistance(int[] a, int[] b, int pa, int pb) { - return (float) Math.sqrt((a[pa] - b[pb]) * (a[pa] - b[pb]) - + (a[pa + 1] - b[pb + 1]) * (a[pa + 1] - b[pb + 1]) - + (a[pa + 2] - b[pb + 2]) * (a[pa + 2] - b[pb + 2])); - } - - /** - * Stuff that is trivially initializable, even with generics, - * but is only safe to do after all constructors have completed. - */ - protected void completeInit() { - this.detailaware.add(MyThings); - } - - protected int findMin(float[] a) { - int minindex = 0; - float min = Float.POSITIVE_INFINITY; - - for (int i = 0; i < a.length; i++) { - if (a[i] < min) { - min = a[i]; - minindex = i; - } - } - - return minindex; - - } - - /** - * R_DrawMaskedColumnSinglePost. Used to handle some special cases where - * cached columns get used as "masked" middle textures. Will be treated as a - * single-run post of capped length. - */ - - /* - * protected final void DrawCompositeColumnPost(byte[] column) { int - * topscreen; int bottomscreen; int basetexturemid; // fixed_t int - * topdelta=0; // Fixed value int length; basetexturemid = dc_texturemid; // - * That's true for the whole column. dc_source = column; // for each post... - * while (topdelta==0) { // calculate unclipped screen coordinates // for - * post topscreen = sprtopscreen + spryscale * 0; length = column.length; - * bottomscreen = topscreen + spryscale * length; dc_yl = (topscreen + - * FRACUNIT - 1) >> FRACBITS; dc_yh = (bottomscreen - 1) >> FRACBITS; if - * (dc_yh >= mfloorclip[p_mfloorclip + dc_x]) dc_yh = - * mfloorclip[p_mfloorclip + dc_x] - 1; if (dc_yl <= - * mceilingclip[p_mceilingclip + dc_x]) dc_yl = mceilingclip[p_mceilingclip - * + dc_x] + 1; // killough 3/2/98, 3/27/98: Failsafe against - * overflow/crash: if (dc_yl <= dc_yh && dc_yh < viewheight) { // Set - * pointer inside column to current post's data // Rremember, it goes - * {postlen}{postdelta}{pad}[data]{pad} dc_source_ofs = 0; // pointer + 3; - * dc_texturemid = basetexturemid - (topdelta << FRACBITS); // Drawn by - * either R_DrawColumn // or (SHADOW) R_DrawFuzzColumn. dc_texheight=0; // - * Killough try { maskedcolfunc.invoke(); } catch (Exception e){ - * System.err.printf("Error rendering %d %d %d\n", dc_yl,dc_yh,dc_yh-dc_yl); - * } } topdelta--; } dc_texturemid = basetexturemid; } - */ - protected abstract void InitColormaps() throws IOException; - - // Only used by Fuzz effect - protected BlurryTable BLURRY_MAP; - - /** - * R_InitData Locates all the lumps that will be used by all views Must be - * called after W_Init. - */ - public void InitData() { - try { - LOGGER.log(Level.INFO, "Init Texture and Flat Manager"); - TexMan = this.DOOM.textureManager; - LOGGER.log(Level.INFO, "InitTextures"); - TexMan.InitTextures(); - LOGGER.log(Level.INFO, "InitFlats"); - TexMan.InitFlats(); - LOGGER.log(Level.INFO, "InitSprites"); - DOOM.spriteManager.InitSpriteLumps(); - MyThings.cacheSpriteManager(DOOM.spriteManager); - VIS.cacheSpriteManager(DOOM.spriteManager); - LOGGER.log(Level.INFO, "InitColormaps"); - InitColormaps(); - - } catch (IOException e) { - LOGGER.log(Level.SEVERE, "Error: InitData failure", e); - } - - } - - protected int spritememory; - - /** - * To be called right after PrecacheLevel from SetupLevel in LevelLoader. - * It's an ugly hack, in that it must communicate with the "Game map" class - * and determine what kinds of monsters are actually in the level and - * whether it should load their graphics or not. Whenever we implement it, - * it's going to be ugly and not neatly separated anyway. - * - * @return - */ - @Override - public void PreCacheThinkers() { - - boolean[] spritepresent; - thinker_t th; - spriteframe_t sf; - int lump; - - final spritedef_t[] sprites = DOOM.spriteManager.getSprites(); - final int numsprites = DOOM.spriteManager.getNumSprites(); - - spritepresent = new boolean[numsprites]; - - for (th = DOOM.actions.getThinkerCap().next; th != DOOM.actions.getThinkerCap(); th = th.next) { - if (th.thinkerFunction == P_MobjThinker) { - spritepresent[((mobj_t) th).mobj_sprite.ordinal()] = true; - } - } - - spritememory = 0; - for (int i = 0; i < numsprites; i++) { - if (!spritepresent[i]) { - continue; - } - - for (int j = 0; j < sprites[i].numframes; j++) { - sf = sprites[i].spriteframes[j]; - for (int k = 0; k < 8; k++) { - lump = DOOM.spriteManager.getFirstSpriteLump() + sf.lump[k]; - spritememory += DOOM.wadLoader.GetLumpInfo(lump).size; - DOOM.wadLoader.CacheLumpNum(lump, PU_CACHE, patch_t.class); - } - } - } - } - - /** - * R_InitTranslationTables Creates the translation tables to map the green - * color ramp to gray, brown, red. Assumes a given structure of the PLAYPAL. - * Could be read from a lump instead. - */ - protected void InitTranslationTables() { - int i; - - final int TR_COLORS = 28; - - // translationtables = Z_Malloc (256*3+255, PU_STATIC, 0); - // translationtables = (byte *)(( (int)translationtables + 255 )& ~255); - byte[][] translationtables - = colormaps.translationtables = new byte[TR_COLORS][256]; - - // translate just the 16 green colors - for (i = 0; i < 256; i++) { - translationtables[0][i] = (byte) i; - - if (i >= 0x70 && i <= 0x7f) { - // Remap green range to other ranges. - translationtables[1][i] = (byte) (0x60 + (i & 0xf)); // gray - translationtables[2][i] = (byte) (0x40 + (i & 0xf)); // brown - translationtables[3][i] = (byte) (0x20 + (i & 0xf)); // red - translationtables[4][i] = (byte) (0x10 + (i & 0xf)); // pink - translationtables[5][i] = (byte) (0x30 + (i & 0xf)); // skin - translationtables[6][i] = (byte) (0x50 + (i & 0xf)); // metal - translationtables[7][i] = (byte) (0x80 + (i & 0xf)); // copper - translationtables[8][i] = (byte) (0xB0 + (i & 0xf)); // b.red - translationtables[9][i] = (byte) (0xC0 + (i & 0xf)); // electric - // blue - translationtables[10][i] = (byte) (0xD0 + (i & 0xf)); // guantanamo - // "Halfhue" colors for which there are only 8 distinct hues - translationtables[11][i] = (byte) (0x90 + (i & 0xf) / 2); // brown2 - translationtables[12][i] = (byte) (0x98 + (i & 0xf) / 2); // gray2 - translationtables[13][i] = (byte) (0xA0 + (i & 0xf) / 2); // piss - translationtables[14][i] = (byte) (0xA8 + (i & 0xf) / 2); // gay - translationtables[15][i] = (byte) (0xE0 + (i & 0xf) / 2); // yellow - translationtables[16][i] = (byte) (0xE8 + (i & 0xf) / 2); // turd - translationtables[17][i] = (byte) (0xF0 + (i & 0xf) / 2); // compblue - translationtables[18][i] = (byte) (0xF8 + (i & 0xf) / 2); // whore - translationtables[19][i] = (byte) (0x05 + (i & 0xf) / 2); // nigga - // "Pimped up" colors, using mixed hues. - translationtables[20][i] = (byte) (0x90 + (i & 0xf)); // soldier - translationtables[21][i] = (byte) (0xA0 + (i & 0xf)); // drag - // queen - translationtables[22][i] = (byte) (0xE0 + (i & 0xf)); // shit & - // piss - translationtables[23][i] = (byte) (0xF0 + (i & 0xf)); // raver - translationtables[24][i] = (byte) (0x70 + (0xf - i & 0xf)); // inv.marine - translationtables[25][i] = (byte) (0xF0 + (0xf - i & 0xf)); // inv.raver - translationtables[26][i] = (byte) (0xE0 + (0xf - i & 0xf)); // piss - // & - // shit - translationtables[27][i] = (byte) (0xA0 + (i & 0xf)); // shitty - // gay - } else { - for (int j = 1; j < TR_COLORS; j++) { - // Keep all other colors as is. - translationtables[j][i] = (byte) i; - } - } - } - } - - // ///////////////// Generic rendering methods ///////////////////// - public IMaskedDrawer getThings() { - return this.MyThings; - } - - /** - * e6y: this is a precalculated value for more precise flats drawing (see - * R_MapPlane) "Borrowed" from PrBoom+ - */ - protected float viewfocratio; - - protected int projectiony; - - // Some more isolation methods.... - @Override - public int getValidCount() { - return validcount; - } - - @Override - public void increaseValidCount(int amount) { - validcount += amount; - } - - @Override - public boolean getSetSizeNeeded() { - return setsizeneeded; - } - - @Override - public TextureManager getTextureManager() { - return TexMan; - } - - @Override - public PlaneDrawer getPlaneDrawer() { - return this.MyPlanes; - } - - @Override - public ViewVars getView() { - return this.view; - } - - @Override - public SpanVars getDSVars() { - return this.dsvars; - } - - @Override - public LightsAndColors getColorMap() { - return this.colormaps; - } - - @Override - public IDoomSystem getDoomSystem() { - return this.DOOM.doomSystem; - } - - @Override - public Visplanes getVPVars() { - return this.vp_vars; - } - - @Override - public SegVars getSegVars() { - return this.seg_vars; - } - - @Override - public IWadLoader getWadLoader() { - return this.DOOM.wadLoader; - } - - @Override - public ISpriteManager getSpriteManager() { - return this.DOOM.spriteManager; - } - - @Override - public BSPVars getBSPVars() { - return this.MyBSP; - } - - @Override - public IVisSpriteManagement getVisSpriteManager() { - return this.VIS; - } - - /** - * Initializes the various drawing functions. They are all "pegged" to the - * same dcvars/dsvars object. Any initializations of e.g. parallel renderers - * and their supporting subsystems should occur here. - */ - protected void R_InitDrawingFunctions() { - this.setHiColFuns(); - this.setLowColFuns(); - } - - // //////////////////////////// LIMIT RESETTING ////////////////// - @Override - public void resetLimits() { - // Call it only at the beginning of new levels. - VIS.resetLimits(); - MySegs.resetLimits(); - } - - /** - * R_RenderView As you can guess, this renders the player view of a - * particular player object. In practice, it could render the view of any - * mobj too, provided you adapt the SetupFrame method (where the viewing - * variables are set). This is the "vanilla" implementation which just works - * for most cases. - */ - @Override - public void RenderPlayerView(player_t player) { - - // Viewing variables are set according to the player's mobj. Interesting - // hacks like - // free cameras or monster views can be done. - SetupFrame(player); - - // Clear buffers. - MyBSP.ClearClipSegs(); - seg_vars.ClearDrawSegs(); - vp_vars.ClearPlanes(); - MySegs.ClearClips(); - VIS.ClearSprites(); - - // Check for new console commands. - DOOM.gameNetworking.NetUpdate(); - - // The head node is the last node output. - MyBSP.RenderBSPNode(DOOM.levelLoader.numnodes - 1); - - // Check for new console commands. - DOOM.gameNetworking.NetUpdate(); - - // FIXME: "Warped floor" fixed, now to fix same-height visplane - // bleeding. - MyPlanes.DrawPlanes(); - - // Check for new console commands. - DOOM.gameNetworking.NetUpdate(); - - MyThings.DrawMasked(); - - colfunc.main = colfunc.base; - - // Check for new console commands. - DOOM.gameNetworking.NetUpdate(); - } -} +package rr; + +import data.Defines; +import static data.Defines.ANGLETOSKYSHIFT; +import static data.Defines.NF_SUBSECTOR; +import static data.Defines.PU_CACHE; +import static data.Defines.SIL_BOTH; +import static data.Defines.SIL_BOTTOM; +import static data.Defines.SIL_TOP; +import static data.Limits.MAXHEIGHT; +import static data.Limits.MAXSEGS; +import static data.Limits.MAXWIDTH; +import data.Tables; +import static data.Tables.ANG180; +import static data.Tables.ANG270; +import static data.Tables.ANG90; +import static data.Tables.ANGLETOFINESHIFT; +import static data.Tables.BITS32; +import static data.Tables.DBITS; +import static data.Tables.FINEANGLES; +import static data.Tables.QUARTERMARK; +import static data.Tables.SlopeDiv; +import static data.Tables.addAngles; +import static data.Tables.finecosine; +import static data.Tables.finesine; +import static data.Tables.finetangent; +import static data.Tables.tantoangle; +import doom.CommandVariable; +import doom.DoomMain; +import doom.SourceCode.R_Draw; +import static doom.SourceCode.R_Draw.R_FillBackScreen; +import doom.player_t; +import doom.thinker_t; +import i.IDoomSystem; +import java.awt.Rectangle; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import static m.BBox.BOXBOTTOM; +import static m.BBox.BOXLEFT; +import static m.BBox.BOXRIGHT; +import static m.BBox.BOXTOP; +import m.IDoomMenu; +import m.MenuMisc; +import m.Settings; +import static m.fixed_t.FRACBITS; +import static m.fixed_t.FRACUNIT; +import static m.fixed_t.FixedDiv; +import static m.fixed_t.FixedMul; +import mochadoom.Engine; +import mochadoom.Loggers; +import static p.ActiveStates.P_MobjThinker; +import p.mobj_t; +import rr.drawfuns.ColFuncs; +import rr.drawfuns.ColVars; +import rr.drawfuns.DoomColumnFunction; +import rr.drawfuns.DoomSpanFunction; +import rr.drawfuns.SpanVars; +import static rr.line_t.ML_DONTPEGBOTTOM; +import static rr.line_t.ML_DONTPEGTOP; +import static rr.line_t.ML_MAPPED; +import utils.C2JUtils; +import static utils.GenericCopy.malloc; +import static v.DoomGraphicSystem.V_NOSCALEOFFSET; +import static v.DoomGraphicSystem.V_NOSCALEPATCH; +import static v.DoomGraphicSystem.V_NOSCALESTART; +import v.graphics.Palettes; +import v.renderers.DoomScreen; +import static v.renderers.DoomScreen.BG; +import static v.renderers.DoomScreen.FG; +import v.tables.BlurryTable; +import v.tables.LightsAndColors; +import w.IWadLoader; + +/** + * Most shared -essential- status information, methods and classes related to + * the software rendering subsystem are found here, shared between the various + * implementations of the Doom's renderer. Not the cleanest or more OO way + * possible, but still a good way to avoid duplicating common code. Some stuff + * like Texture, Flat and Sprite management are also found -or at least + * implemented temporarily- here, until a cleaner split can be made. This is a + * kind of "Jack of all trades" class, but hopefully not for long. + * + * @author velktron + */ +public abstract class RendererState implements SceneRenderer, ILimitResettable { + + private static final Logger LOGGER = Loggers.getLogger(RendererState.class.getName()); + + protected static final boolean DEBUG = false; + protected static final boolean DEBUG2 = false; + + // ///////////////////// STATUS //////////////////////// + protected DoomMain DOOM; + protected ISegDrawer MySegs; + protected IDoomMenu Menu; + protected BSP MyBSP; + protected PlaneDrawer MyPlanes; + protected IMaskedDrawer MyThings; + public IVisSpriteManagement VIS; + protected TextureManager TexMan; + public ViewVars view; + public LightsAndColors colormaps; + public SegVars seg_vars; + public Visplanes vp_vars; + + // Rendering subsystems that are detailshift-aware + protected List detailaware; + + // The only reason to query scaledviewwidth from outside the renderer, is + // this. + @Override + public boolean isFullHeight() { + return (view.height == DOOM.vs.getScreenHeight()); + } + + public boolean isFullWidth() { + return (view.scaledwidth == DOOM.vs.getScreenWidth()); + } + + @Override + public boolean isFullScreen() { + return isFullWidth() && isFullHeight(); + } + + /** + * Increment every time a check is made For some reason, this needs to be + * visible even by enemies thinking :-S + */ + protected int validcount = 1; + + /** + * Who can set this? A: The Menu. + */ + protected boolean setsizeneeded; + protected int setblocks; + protected int setdetail; + + // private BSPVars bspvars; + /** + * R_SetViewSize Do not really change anything here, because it might be in + * the middle of a refresh. The change will take effect next refresh. + * + * @param blocks + * 11 is full screen, 9 default. + * @param detail + * 0= high, 1 =low. + */ + @Override + public void SetViewSize(int blocks, int detail) { + // System.out.println("SetViewSize"); + setsizeneeded = true; + setblocks = blocks; + setdetail = detail; + + detailaware.forEach((d) -> { + d.setDetail(setdetail); + }); + } + + /** + * R_SetupFrame + */ + public void SetupFrame(player_t player) { + view.player = player; + view.x = player.mo.x; + view.y = player.mo.y; + // viewangle = addAngles(player.mo.angle , viewangleoffset); + view.angle = player.mo.angle & BITS32; + // With 32 colormaps, a bump of 1 or 2 is normal. + // With more than 32, it should be obviously higher. + + int bumplight = Math.max(colormaps.lightBits() - 5, 0); + // Be a bit more generous, otherwise the effect is not + // as evident with truecolor maps. + bumplight += (bumplight > 0) ? 1 : 0; + + colormaps.extralight = player.extralight << bumplight; + + view.z = player.viewz; + view.lookdir = player.lookdir; + int tempCentery; + + // MAES: hacks based on Heretic. Weapon movement needs to be compensated + if (setblocks == 11) { + tempCentery = (view.height / 2) + (int) (view.lookdir * DOOM.vs.getScreenMul() * setblocks) / 11; + } else { + tempCentery = (view.height / 2) + (int) (view.lookdir * DOOM.vs.getScreenMul() * setblocks) / 10; + } + + if (view.centery != tempCentery) { + view.centery = tempCentery; + view.centeryfrac = view.centery << FRACBITS; + int yslope[] = vp_vars.yslope; + for (int i = 0; i < view.height; i++) { + yslope[i] = FixedDiv( + (view.width << view.detailshift) / 2 * FRACUNIT, + Math.abs(((i - view.centery) << FRACBITS) + FRACUNIT / 2) + ); + } + + skydcvars.centery = maskedcvars.centery = dcvars.centery = view.centery; + } + + view.sin = Tables.finesine(view.angle); + view.cos = Tables.finecosine(view.angle); + + sscount = 0; + + if (player.fixedcolormap != Palettes.COLORMAP_FIXED) { + colormaps.fixedcolormap = colormaps.getFixedColormap(player); + // Offset by fixedcolomap + // pfixedcolormap =player.fixedcolormap*256; + + colormaps.walllights = colormaps.scalelightfixed; + + for (int i = 0; i < colormaps.maxLightScale(); i++) { + colormaps.scalelightfixed[i] = colormaps.fixedcolormap; + } + } else { + colormaps.fixedcolormap = null; + } + + framecount++; + validcount++; + } + + /** + * R_SetupFrame for a particular actor. + */ + public void SetupFrame(mobj_t actor) { + + // viewplayer = player; + view.x = actor.x; + view.y = actor.y; + // viewangle = addAngles(player.mo.angle , viewangleoffset); + view.angle = actor.angle & BITS32; + // extralight = actor.extralight; + + view.z = actor.z + actor.height; + + view.sin = finesine(view.angle); + view.cos = finecosine(view.angle); + + sscount = 0; + + framecount++; + validcount++; + } + + public RendererState(DoomMain DOOM) { + this.DOOM = DOOM; + + // These don't change between implementations, yet. + this.MyBSP = new BSP(); + + this.view = new ViewVars(DOOM.vs); + this.seg_vars = new SegVars(); + this.dcvars = new ColVars<>(); + this.dsvars = new SpanVars<>(); + this.maskedcvars = new ColVars<>(); + this.skydcvars = new ColVars<>(); + this.colfunclow = new ColFuncs<>(); + this.colfunchi = new ColFuncs<>(); + + this.detailaware = new ArrayList<>(); + this.colormaps = new LightsAndColors<>(DOOM); + // It's better to construct this here + @SuppressWarnings("unchecked") + final TextureManager tm = (TextureManager) new SimpleTextureManager(DOOM); + this.TexMan = tm; + + // Visplane variables + this.vp_vars = new Visplanes(DOOM.vs, view, TexMan); + + // Set rendering functions only after screen sizes + // and stuff have been set. + this.MyPlanes = new Planes(DOOM, this); + this.VIS = new VisSprites<>(this); + this.MyThings = new SimpleThings<>(DOOM.vs, this); + } + + // ////////////////////////////// THINGS //////////////////////////////// + protected final class BSP extends BSPVars { + + /** + * newend is one past the last valid seg (cliprange_t) + */ + int newend; + + cliprange_t[] solidsegs; + + BSP() { + solidsegs = malloc(cliprange_t::new, cliprange_t[]::new, MAXSEGS + 1); + } + + /** + * R_ClipSolidWallSegment Does handle solid walls, single sided LineDefs + * (middle texture) that entirely block the view VERTICALLY. Handles + * "clipranges" for a solid wall, aka where it blocks the view. + * + * @param first + * starting y coord? + * @param last + * ending y coord? + */ + private void ClipSolidWallSegment(int first, int last) { + + int next; + int start; + // int maxlast=Integer.MIN_VALUE; + + start = 0; // within solidsegs + + // Find the first cliprange that touches the range. + // Actually, the first one not completely hiding it (its last must + // be lower than first. + while (solidsegs[start].last < first - 1) { + start++; + } + + // If the post begins above the lastly found cliprange... + if (first < solidsegs[start].first) { + // ..and ends above it, too (no overlapping) + if (last < solidsegs[start].first - 1) { + // ... then the post is entirely visible (above start), + // so insert a new clippost. Calling this function + // tells the renderer that there is an obstruction. + MySegs.StoreWallRange(first, last); + + // Newend should have a value of 2 if we are at the + // beginning of a new frame. + next = newend; + newend++; + + if (next >= solidsegs.length) { + ResizeSolidSegs(); + } + while (next != start) { + // *next=*(next-1); + /* + * MAES: I think this is supposed to copy the structs + * solidsegs[next] = solidsegs[next-1].clone(); OK, so + * basically the last solidseg copies its previous, and + * so on until we reach the start. This means that at + * some point, the value of the start solidseg is + * duplicated. + */ + + solidsegs[next].copy(solidsegs[next - 1]); + + next--; + } + + // At this point, next points at start. + // Therefore, start + solidsegs[next].first = first; + solidsegs[next].last = last; + return; + } + + // There is a fragment above *start. This can occur if it a + // post does start before another, but its lower edge overlaps + // (partial, upper occlusion) + MySegs.StoreWallRange(first, solidsegs[start].first - 1); + // Now adjust the clip size. + solidsegs[start].first = first; + } + + // We can reach this only if a post starts AFTER another + // Bottom contained in start? Obviously it won't be visible. + if (last <= solidsegs[start].last) { + return; + } + + next = start; + while (last >= solidsegs[(next + 1)].first - 1) { + // There is a fragment between two posts. + MySegs.StoreWallRange(solidsegs[next].last + 1, + solidsegs[next + 1].first - 1); + next++; + + if (last <= solidsegs[next].last) { + // Bottom is contained in next. + // Adjust the clip size. + solidsegs[start].last = solidsegs[next].last; + // goto crunch; + + { // crunch code + if (next == start) { + // Post just extended past the bottom of one post. + return; + } + + while (next++ != newend) { + // Remove a post. + // MAES: this is a struct copy. + if (next >= solidsegs.length) { + ResizeSolidSegs(); + } + solidsegs[++start].copy(solidsegs[next]); + } + + newend = start + 1; + return; + } + } + } + + // There is a fragment after *next. + MySegs.StoreWallRange(solidsegs[next].last + 1, last); + // Adjust the clip size. + solidsegs[start].last = last; + + // Remove start+1 to next from the clip list, + // because start now covers their area. + { // crunch code + if (next == start) { + // Post just extended past the bottom of one post. + return; + } + + while (next++ != newend) { + // Remove a post. + // MAES: this is a struct copy. + // MAES: this can overflow, breaking e.g. MAP30 of Final + // Doom. + if (next >= solidsegs.length) { + ResizeSolidSegs(); + } + solidsegs[++start].copy(solidsegs[next]); + } + + newend = start + 1; + } + } + + void ResizeSolidSegs() { + solidsegs = C2JUtils.resize(solidsegs, solidsegs.length * 2); + } + + // + // R_ClipPassWallSegment + // Clips the given range of columns, + // but does not includes it in the clip list. + // Does handle windows, + // e.g. LineDefs with upper and lower texture. + // + private void ClipPassWallSegment(int first, int last) { + + // Find the first range that touches the range + // (adjacent pixels are touching). + int start = 0; + + while (solidsegs[start].last < first - 1) { + start++; + } + + if (first < solidsegs[start].first) { + if (last < solidsegs[start].first - 1) { + // Post is entirely visible (above start). + MySegs.StoreWallRange(first, last); + return; + } + + // There is a fragment above *start. + MySegs.StoreWallRange(first, solidsegs[start].first - 1); + } + + // Bottom contained in start? + if (last <= solidsegs[start].last) { + return; + } + + // MAES: Java absolutely can't do without a sanity check here. + // if (startptr>=MAXSEGS-2) return; + while (last >= solidsegs[start + 1].first - 1) { + // There is a fragment between two posts. + MySegs.StoreWallRange(solidsegs[start].last + 1, + solidsegs[start + 1].first - 1); + start++; + // if (startptr>=MAXSEGS-2) return; + // start=solidsegs[startptr]; + + if (last <= solidsegs[start].last) { + return; + } + } + + // There is a fragment after *next. + MySegs.StoreWallRange(solidsegs[start].last + 1, last); + } + + /** + * R_ClearClipSegs Clears the clipping segs list. The list is actually + * fixed size for efficiency reasons, so it just tells Doom to use the + * first two solidsegs, which are "neutered". It's interesting to note + * how the solidsegs begin and end just "outside" the visible borders of + * the screen. + */ + public void ClearClipSegs() { + solidsegs[0].first = -0x7fffffff; + solidsegs[0].last = -1; + solidsegs[1].first = view.width; + solidsegs[1].last = 0x7fffffff; + newend = 2; // point so solidsegs[2]; + } + + /** + * R_AddLine Called after a SubSector BSP trasversal ends up in a + * "final" subsector. Clips the given segment and adds any visible + * pieces to the line list. It also determines what kind of boundary + * (line) visplane clipping should be performed. E.g. window, final + * 1-sided line, closed door etc.) CAREFUL: was the source of much + * frustration with visplanes... + */ + private void AddLine(seg_t line) { + if (DEBUG) { + LOGGER.log(Level.FINE, String.format("Entered AddLine for %s", String.valueOf(line))); + } + int x1; + int x2; + long angle1; + long angle2; + long span; + long tspan; + + curline = line; + + // OPTIMIZE: quickly reject orthogonal back sides. + angle1 = view.PointToAngle(line.v1x, line.v1y); + angle2 = view.PointToAngle(line.v2x, line.v2y); + + // Clip to view edges. + // OPTIMIZE: make constant out of 2*clipangle (FIELDOFVIEW). + span = addAngles(angle1, -angle2); + + // Back side? I.e. backface culling? + if (span >= ANG180) { + return; + } + + // Global angle needed by segcalc. + MySegs.setGlobalAngle(angle1); + angle1 -= view.angle; + angle2 -= view.angle; + + angle1 &= BITS32; + angle2 &= BITS32; + + tspan = addAngles(angle1, clipangle); + + if (tspan > CLIPANGLE2) { + tspan -= CLIPANGLE2; + tspan &= BITS32; + + // Totally off the left edge? + if (tspan >= span) { + return; + } + + angle1 = clipangle; + } + tspan = addAngles(clipangle, -angle2); + + if (tspan > CLIPANGLE2) { + tspan -= CLIPANGLE2; + tspan &= BITS32; + + // Totally off the left edge? + if (tspan >= span) { + return; + } + angle2 = -clipangle; + angle2 &= BITS32; + } + + // The seg is in the view range, + // but not necessarily visible. + angle1 = ((angle1 + ANG90) & BITS32) >>> ANGLETOFINESHIFT; + angle2 = ((angle2 + ANG90) & BITS32) >>> ANGLETOFINESHIFT; + x1 = viewangletox[(int) angle1]; + x2 = viewangletox[(int) angle2]; + + // Does not cross a pixel? + if (x1 == x2) { + return; + } + + backsector = line.backsector; + + // Single sided line? + if (backsector == null) { + if (DEBUG) { + LOGGER.log(Level.FINE, String.format("Entering ClipSolidWallSegment SS with params %d %d", x1, (x2 - 1))); + } + ClipSolidWallSegment(x1, x2 - 1); // to clipsolid + if (DEBUG) { + LOGGER.log(Level.FINE, "Exiting ClipSolidWallSegment"); + } + return; + } + + // Closed door. + if (backsector.ceilingheight <= frontsector.floorheight + || backsector.floorheight >= frontsector.ceilingheight) { + if (DEBUG) { + LOGGER.log(Level.FINE, String.format("Entering ClipSolidWallSegment Closed door with params %d %d", x1, (x2 - 1))); + } + ClipSolidWallSegment(x1, x2 - 1); + // to clipsolid + return; + } + + // Window. This includes same-level floors with different textures + if (backsector.ceilingheight != frontsector.ceilingheight + || backsector.floorheight != frontsector.floorheight) { + if (DEBUG) { + LOGGER.log(Level.FINE, String.format("Entering ClipSolidWallSegment window with params %d %d", x1, (x2 - 1))); + } + ClipPassWallSegment(x1, x2 - 1); // to clippass + return; + } + + // Reject empty lines used for triggers + // and special events. + // Identical floor and ceiling on both sides, + // identical light levels on both sides, + // and no middle texture. + if (backsector.ceilingpic == frontsector.ceilingpic + && backsector.floorpic == frontsector.floorpic + && backsector.lightlevel == frontsector.lightlevel + && curline.sidedef.midtexture == 0) { + return; + } + + // If nothing of the previous holds, then we are + // treating the case of same-level, differently + // textured floors. ACHTUNG, this caused the "bleeding floor" + // bug, which is now fixed. + // Fucking GOTOs.... + ClipPassWallSegment(x1, x2 - 1); // to clippass + if (DEBUG) { + LOGGER.log(Level.FINE, String.format("Exiting AddLine for %s", String.valueOf(line))); + } + } + + // + // R_CheckBBox + // Checks BSP node/subtree bounding box. + // Returns true + // if some part of the bbox might be visible. + // + private final int[][] checkcoord = { + {3, 0, 2, 1}, + {3, 0, 2, 0}, + {3, 1, 2, 0}, + {0}, + {2, 0, 2, 1}, + {0, 0, 0, 0}, + {3, 1, 3, 0}, + {0}, + {2, 0, 3, 1}, + {2, 1, 3, 1}, + {2, 1, 3, 0} + }; + + /** + * @param bspcoord + * (fixed_t* as bbox) + * @return + */ + public boolean CheckBBox(int[] bspcoord) { + int boxx; + int boxy; + int boxpos; + + // fixed_t + int x1; + int y1; + int x2; + int y2; + + // angle_t + long angle1; + long angle2; + long span; + long tspan; + + cliprange_t start; + + int sx1; + int sx2; + + // Find the corners of the box + // that define the edges from current viewpoint. + if (view.x <= bspcoord[BOXLEFT]) { + boxx = 0; + } else if (view.x < bspcoord[BOXRIGHT]) { + boxx = 1; + } else { + boxx = 2; + } + + if (view.y >= bspcoord[BOXTOP]) { + boxy = 0; + } else if (view.y > bspcoord[BOXBOTTOM]) { + boxy = 1; + } else { + boxy = 2; + } + + boxpos = (boxy << 2) + boxx; + if (boxpos == 5) { + return true; + } + + x1 = bspcoord[checkcoord[boxpos][0]]; + y1 = bspcoord[checkcoord[boxpos][1]]; + x2 = bspcoord[checkcoord[boxpos][2]]; + y2 = bspcoord[checkcoord[boxpos][3]]; + + // check clip list for an open space + angle1 = view.PointToAngle(x1, y1) - view.angle; + angle2 = view.PointToAngle(x2, y2) - view.angle; + + angle1 &= BITS32; + angle2 &= BITS32; + + span = angle1 - angle2; + + span &= BITS32; + + // Sitting on a line? + if (span >= ANG180) { + return true; + } + + tspan = angle1 + clipangle; + tspan &= BITS32; + + if (tspan > CLIPANGLE2) { + tspan -= CLIPANGLE2; + tspan &= BITS32; + // Totally off the left edge? + if (tspan >= span) { + return false; + } + + angle1 = clipangle; + } + tspan = (clipangle - angle2) & BITS32; + if (tspan > CLIPANGLE2) { + tspan -= CLIPANGLE2; + tspan &= BITS32; + + // Totally off the left edge? + if (tspan >= span) { + return false; + } + + angle2 = -clipangle; + angle2 &= BITS32; + } + + // Find the first clippost + // that touches the source post + // (adjacent pixels are touching). + angle1 = ((angle1 + ANG90) & BITS32) >>> ANGLETOFINESHIFT; + angle2 = ((angle2 + ANG90) & BITS32) >>> ANGLETOFINESHIFT; + sx1 = viewangletox[(int) angle1]; + sx2 = viewangletox[(int) angle2]; + + // Does not cross a pixel. + if (sx1 == sx2) { + return false; + } + sx2--; + + int pstart = 0; + start = solidsegs[pstart]; + // FIXME: possible solidseg overflow here overflows + while (start.last < sx2 && pstart < MAXSEGS) { + start = solidsegs[pstart++]; + } + + return !(sx1 >= start.first && sx2 <= start.last); + } + + /** + * R_Subsector Determine floor/ceiling planes. Add sprites of things in + * sector. Draw one or more line segments. It also alters the visplane + * list! + * + * @param num + * Subsector from subsector_t list in Lever Loader. + */ + private void Subsector(int num) { + if (DEBUG) { + LOGGER.log(Level.FINE, String.format("SubSector %d to render", num)); + } + int count; + int line; // pointer into a list of segs instead of seg_t + subsector_t sub; + + if (RANGECHECK) { + if (num >= DOOM.levelLoader.numsubsectors) { + DOOM.doomSystem.Error("R_Subsector: ss %d with numss = %d", num, + DOOM.levelLoader.numsubsectors); + } + } + + sscount++; + sub = DOOM.levelLoader.subsectors[num]; + + frontsector = sub.sector; + if (DEBUG) { + LOGGER.log(Level.FINE, String.format("Frontsector to render: %s", String.valueOf(frontsector))); + } + count = sub.numlines; + // line = LL.segs[sub.firstline]; + line = sub.firstline; + + if (DEBUG) { + LOGGER.log(Level.FINE, "Trying to find an existing FLOOR visplane..."); + } + if (frontsector.floorheight < view.z) { + vp_vars.floorplane + = vp_vars.FindPlane(frontsector.floorheight, + frontsector.floorpic, frontsector.lightlevel); + } else { + // FIXME: unclear what would happen with a null visplane used + // It's never checked explicitly for either condition, just + // called straight. + vp_vars.floorplane = -1; // in lieu of NULL + } + + // System.out.println("Trying to find an existing CEILING visplane..."); + if (frontsector.ceilingheight > view.z + || frontsector.ceilingpic == TexMan.getSkyFlatNum()) { + vp_vars.ceilingplane + = vp_vars.FindPlane(frontsector.ceilingheight, + frontsector.ceilingpic, frontsector.lightlevel); + } else { + vp_vars.ceilingplane = -1; // In lieu of NULL. Will bomb if + // actually + // used. + } + + VIS.AddSprites(frontsector); + + if (DEBUG) { + LOGGER.log(Level.FINE, String.format("Enter Addline for SubSector %d count %d", num, count)); + } + while (count-- > 0) { + AddLine(DOOM.levelLoader.segs[line]); + line++; + } + if (DEBUG) { + LOGGER.log(Level.FINE, String.format("Exit Addline for SubSector %d", num)); + } + } + + /** + * RenderBSPNode Renders all subsectors below a given node, traversing + * subtree recursively. Just call with BSP root. + */ + public void RenderBSPNode(int bspnum) { + if (DEBUG) { + LOGGER.log(Level.FINE, String.format("Processing BSP Node %d", bspnum)); + } + + node_t bsp; + int side; + + // Found a subsector? Then further decisions are taken, in, well, + // SubSector. + if (C2JUtils.flags(bspnum, NF_SUBSECTOR)) { + if (DEBUG) { + LOGGER.log(Level.FINE, "Subsector found."); + } + if (bspnum == -1) { + Subsector(0); + } else { + Subsector(bspnum & (~NF_SUBSECTOR)); + } + return; + } + + bsp = DOOM.levelLoader.nodes[bspnum]; + + // Decide which side the view point is on. + side = bsp.PointOnSide(view.x, view.y); + if (DEBUG) { + LOGGER.log(Level.FINE, String.format("View side: %d", side)); + } + + // Recursively divide front space. + if (DEBUG) { + LOGGER.log(Level.FINE, String.format("Enter Front space of %d", bspnum)); + } + RenderBSPNode(bsp.children[side]); + if (DEBUG) { + LOGGER.log(Level.FINE, String.format("Return Front space of %d", bspnum)); + } + + // Possibly divide back space. + if (CheckBBox(bsp.bbox[side ^ 1].bbox)) { + if (DEBUG) { + LOGGER.log(Level.FINE, String.format("Enter Back space of %d", bspnum)); + } + RenderBSPNode(bsp.children[side ^ 1]); + if (DEBUG) { + LOGGER.log(Level.FINE, String.format("Return Back space of %d", bspnum)); + } + } + } + + } + + protected abstract class SegDrawer implements ISegDrawer { + + protected static final int HEIGHTBITS = 12; + protected static final int HEIGHTUNIT = (1 << HEIGHTBITS); + protected final Visplanes vp_vars; + protected final SegVars seg_vars; + + // Fast blanking buffers. + protected short[] BLANKFLOORCLIP; + protected short[] BLANKCEILINGCLIP; + + @Override + public short[] getBLANKFLOORCLIP() { + return BLANKFLOORCLIP; + } + + @Override + public short[] getBLANKCEILINGCLIP() { + return BLANKCEILINGCLIP; + } + + /** + * fixed_t + */ + protected int pixhigh, pixlow, pixhighstep, pixlowstep, topfrac, topstep, bottomfrac, bottomstep; + protected int worldtop, worldbottom, worldhigh, worldlow; + + /** + * True if any of the segs textures might be visible. + */ + protected boolean segtextured; + + /** + * Clip values are the solid pixel bounding the range. floorclip starts + * out vs.getScreenHeight() ceilingclip starts out -1 + */ + protected short[] floorclip, ceilingclip; + + @Override + public final short[] getFloorClip() { + return floorclip; + } + + @Override + public short[] getCeilingClip() { + return ceilingclip; + } + + /** + * False if the back side is the same plane. + */ + protected boolean markfloor, markceiling; + + protected boolean maskedtexture; + + protected int toptexture; + + protected int bottomtexture; + + protected int midtexture; + + /** + * angle_t, used after adding ANG90 in StoreWallRange + */ + protected long rw_normalangle; + + /** + * angle to line origin + */ + protected long rw_angle1; + + // + // regular wall + // + protected int rw_x; + + protected int rw_stopx; + + protected long rw_centerangle; // angle_t + + /** + * fixed_t + */ + protected int rw_offset, rw_distance, rw_scale, rw_scalestep, + rw_midtexturemid, rw_toptexturemid, rw_bottomtexturemid; + + @Override + public void resetLimits() { + drawseg_t[] tmp = new drawseg_t[seg_vars.MAXDRAWSEGS]; + System.arraycopy(seg_vars.drawsegs, 0, tmp, 0, seg_vars.MAXDRAWSEGS); + + // Now, that was quite a haircut!. + seg_vars.drawsegs = tmp; + + // System.out.println("Drawseg buffer cut back to original limit of "+MAXDRAWSEGS); + } + + @Override + public void sync() { + // Nothing required if serial. + } + + /** + * R_StoreWallRange A wall segment will be drawn between start and stop + * pixels (inclusive). This is the only place where + * markceiling/markfloor can be set. Can only be called from + * ClipSolidWallSegment and ClipPassWallSegment. + * + * @throws IOException + */ + @Override + public void StoreWallRange(int start, int stop) { + + if (DEBUG2) { + LOGGER.log(Level.FINER, String.format("Storewallrange called between %d and %d", start, stop)); + } + + int hyp; // fixed_t + int sineval; // fixed_t + int distangle; + long offsetangle; // angle_t + int vtop; // fixed_t + int lightnum; + drawseg_t seg; + + // don't overflow and crash + if (seg_vars.ds_p == seg_vars.drawsegs.length) { + seg_vars.ResizeDrawsegs(); + } + + if (RANGECHECK) { + if (start >= view.width || start > stop) { + DOOM.doomSystem.Error("Bad R_RenderWallRange: %d to %d", start, stop); + } + } + + seg = seg_vars.drawsegs[seg_vars.ds_p]; + + MyBSP.sidedef = MyBSP.curline.sidedef; + MyBSP.linedef = MyBSP.curline.linedef; + + // mark the segment as visible for auto map + MyBSP.linedef.flags |= ML_MAPPED; + + // calculate rw_distance for scale calculation + rw_normalangle = addAngles(MyBSP.curline.angle, ANG90); + + /* + * MAES: ok, this is a tricky spot. angle_t's are supposed to be + * always positive 32-bit unsigned integers, so a subtraction should + * be always positive by definition, right? WRONG: this fucking spot + * caused "blind spots" at certain angles because ONLY HERE angles + * are supposed to be treated as SIGNED and result in differences + * <180 degrees -_- The only way to coerce this behavior is to cast + * both as signed ints. + */ + offsetangle = Math.abs((int) rw_normalangle - (int) rw_angle1); + + if (offsetangle > ANG90) { + offsetangle = ANG90; + } + + // It should fit even in a signed int, by now. + distangle = (int) (ANG90 - offsetangle); + hyp = PointToDist(MyBSP.curline.v1x, MyBSP.curline.v1y); + sineval = finesine(distangle); + rw_distance = FixedMul(hyp, sineval); + + seg.x1 = rw_x = start; + seg.x2 = stop; + seg.curline = MyBSP.curline; + /* + * This is the only place it's ever explicitly assigned. Therefore + * it always starts at stop+1. + */ + rw_stopx = stop + 1; + + // calculate scale at both ends and step + // this is the ONLY place where rw_scale is set. + seg.scale1 + = rw_scale + = ScaleFromGlobalAngle((view.angle + view.xtoviewangle[start])); + + if (stop > start) { + seg.scale2 + = ScaleFromGlobalAngle(view.angle + view.xtoviewangle[stop]); + seg.scalestep + = rw_scalestep = (seg.scale2 - rw_scale) / (stop - start); + } else { + // UNUSED: try to fix the stretched line bug + /* + * #if 0 if (rw_distance < FRACUNIT/2) { fixed_t trx,try; + * fixed_t gxt,gyt; trx = curline.v1.x - viewx; try = + * curline.v1.y - viewy; gxt = FixedMul(trx,viewcos); gyt = + * -FixedMul(try,viewsin); seg.scale1 = FixedDiv(projection, + * gxt-gyt)< MyBSP.backsector.floorheight) { + seg.silhouette = SIL_BOTTOM; + seg.bsilheight = MyBSP.frontsector.floorheight; + } else if (MyBSP.backsector.floorheight > view.z) { + seg.silhouette = SIL_BOTTOM; + seg.bsilheight = Integer.MAX_VALUE; + // seg.sprbottomclip = negonearray; + } + + if (MyBSP.frontsector.ceilingheight < MyBSP.backsector.ceilingheight) { + seg.silhouette |= SIL_TOP; + seg.tsilheight = MyBSP.frontsector.ceilingheight; + } else if (MyBSP.backsector.ceilingheight < view.z) { + seg.silhouette |= SIL_TOP; + seg.tsilheight = Integer.MIN_VALUE; + // seg.sprtopclip = screenheightarray; + } + + if (MyBSP.backsector.ceilingheight <= MyBSP.frontsector.floorheight) { + seg.setSprBottomClip(view.negonearray, 0); + seg.bsilheight = Integer.MAX_VALUE; + seg.silhouette |= SIL_BOTTOM; + } + + if (MyBSP.backsector.floorheight >= MyBSP.frontsector.ceilingheight) { + seg.setSprTopClip(view.screenheightarray, 0); + seg.tsilheight = Integer.MIN_VALUE; + seg.silhouette |= SIL_TOP; + } + + worldhigh = MyBSP.backsector.ceilingheight - view.z; + worldlow = MyBSP.backsector.floorheight - view.z; + + // hack to allow height changes in outdoor areas + if (MyBSP.frontsector.ceilingpic == TexMan.getSkyFlatNum() + && MyBSP.backsector.ceilingpic == TexMan + .getSkyFlatNum()) { + worldtop = worldhigh; + } + + markfloor = worldlow != worldbottom + || MyBSP.backsector.floorpic != MyBSP.frontsector.floorpic + || MyBSP.backsector.lightlevel != MyBSP.frontsector.lightlevel; // same plane on both sides + markceiling = worldhigh != worldtop + || MyBSP.backsector.ceilingpic != MyBSP.frontsector.ceilingpic + || MyBSP.backsector.lightlevel != MyBSP.frontsector.lightlevel; // same plane on both sides + + if (MyBSP.backsector.ceilingheight <= MyBSP.frontsector.floorheight + || MyBSP.backsector.floorheight >= MyBSP.frontsector.ceilingheight) { + // closed door + markceiling = markfloor = true; + } + + if (worldhigh < worldtop) { + // top texture + toptexture + = TexMan.getTextureTranslation(MyBSP.sidedef.toptexture); + if ((MyBSP.linedef.flags & ML_DONTPEGTOP) != 0) { + // top of texture at top + rw_toptexturemid = worldtop; + } else { + vtop + = MyBSP.backsector.ceilingheight + + TexMan.getTextureheight(MyBSP.sidedef.toptexture); + + // bottom of texture + rw_toptexturemid = vtop - view.z; + } + } + if (worldlow > worldbottom) { + // bottom texture + bottomtexture + = TexMan.getTextureTranslation(MyBSP.sidedef.bottomtexture); + + if ((MyBSP.linedef.flags & ML_DONTPEGBOTTOM) != 0) { + // bottom of texture at bottom + // top of texture at top + rw_bottomtexturemid = worldtop; + } else { + // top of texture at top + rw_bottomtexturemid = worldlow; + } + } + rw_toptexturemid += MyBSP.sidedef.rowoffset; + rw_bottomtexturemid += MyBSP.sidedef.rowoffset; + + // allocate space for masked texture tables + if (MyBSP.sidedef.midtexture != 0) { + // masked midtexture + maskedtexture = true; + seg_vars.maskedtexturecol = vp_vars.openings; + seg_vars.pmaskedtexturecol = vp_vars.lastopening - rw_x; + seg.setMaskedTextureCol(seg_vars.maskedtexturecol, + seg_vars.pmaskedtexturecol); + vp_vars.lastopening += rw_stopx - rw_x; + } + } + + // calculate rw_offset (only needed for textured lines) + segtextured + = (((midtexture | toptexture | bottomtexture) != 0) | maskedtexture); + + if (segtextured) { + offsetangle = addAngles(rw_normalangle, -rw_angle1); + + // Another "tricky spot": negative of an unsigned number? + if (offsetangle > ANG180) { + offsetangle = (-(int) offsetangle) & BITS32; + } + + if (offsetangle > ANG90) { + offsetangle = ANG90; + } + + sineval = finesine(offsetangle); + rw_offset = FixedMul(hyp, sineval); + + // Another bug: we CAN'T assume that the result won't wrap + // around. + // If that assumption is made, then texture alignment issues + // appear + if (((rw_normalangle - rw_angle1) & BITS32) < ANG180) { + rw_offset = -rw_offset; + } + + rw_offset += MyBSP.sidedef.textureoffset + MyBSP.curline.offset; + // This is OK, however: we can add as much shit as we want, + // as long as we trim it to the 32 LSB. Proof as to why + // this is always true is left as an exercise to the reader. + rw_centerangle = (ANG90 + view.angle - rw_normalangle) & BITS32; + + // calculate light table + // use different light tables + // for horizontal / vertical / diagonal + // OPTIMIZE: get rid of LIGHTSEGSHIFT globally + if (colormaps.fixedcolormap == null) { + lightnum + = (MyBSP.frontsector.lightlevel >> colormaps.lightSegShift()) + + colormaps.extralight; + + if (MyBSP.curline.v1y == MyBSP.curline.v2y) { + lightnum--; + } else if (MyBSP.curline.v1x == MyBSP.curline.v2x) { + lightnum++; + } + + if (lightnum < 0) { + colormaps.walllights = colormaps.scalelight[0]; + } else if (lightnum >= colormaps.lightLevels()) { + colormaps.walllights + = colormaps.scalelight[colormaps.lightLevels() - 1]; + } else { + colormaps.walllights = colormaps.scalelight[lightnum]; + } + } + } + + // if a floor / ceiling plane is on the wrong side + // of the view plane, it is definitely invisible + // and doesn't need to be marked. + if (MyBSP.frontsector.floorheight >= view.z) { + // above view plane + markfloor = false; + } + + if (MyBSP.frontsector.ceilingheight <= view.z + && MyBSP.frontsector.ceilingpic != TexMan.getSkyFlatNum()) { + // below view plane + markceiling = false; + } + + // calculate incremental stepping values for texture edges + worldtop >>= 4; + worldbottom >>= 4; + + topstep = -FixedMul(rw_scalestep, worldtop); + topfrac = (view.centeryfrac >> 4) - FixedMul(worldtop, rw_scale); + + bottomstep = -FixedMul(rw_scalestep, worldbottom); + bottomfrac + = (view.centeryfrac >> 4) - FixedMul(worldbottom, rw_scale); + + if (MyBSP.backsector != null) { + worldhigh >>= 4; + worldlow >>= 4; + + if (worldhigh < worldtop) { + pixhigh + = (view.centeryfrac >> 4) - FixedMul(worldhigh, rw_scale); + pixhighstep = -FixedMul(rw_scalestep, worldhigh); + } + + if (worldlow > worldbottom) { + pixlow + = (view.centeryfrac >> 4) - FixedMul(worldlow, rw_scale); + pixlowstep = -FixedMul(rw_scalestep, worldlow); + } + } + + // render it + if (markceiling) { + // System.out.println("Markceiling"); + vp_vars.ceilingplane + = vp_vars.CheckPlane(vp_vars.ceilingplane, rw_x, rw_stopx - 1); + } + + if (markfloor) { + // System.out.println("Markfloor"); + vp_vars.floorplane + = vp_vars.CheckPlane(vp_vars.floorplane, rw_x, rw_stopx - 1); + } + + RenderSegLoop(); + + // After rendering is actually performed, clipping is set. + // save sprite clipping info ... no top clipping? + if ((C2JUtils.flags(seg.silhouette, SIL_TOP) || maskedtexture) + && seg.nullSprTopClip()) { + + // memcpy (lastopening, ceilingclip+start, 2*(rw_stopx-start)); + System.arraycopy(ceilingclip, start, vp_vars.openings, + vp_vars.lastopening, rw_stopx - start); + + seg.setSprTopClip(vp_vars.openings, vp_vars.lastopening - start); + // seg.setSprTopClipPointer(); + vp_vars.lastopening += rw_stopx - start; + } + // no floor clipping? + if ((C2JUtils.flags(seg.silhouette, SIL_BOTTOM) || maskedtexture) + && seg.nullSprBottomClip()) { + // memcpy (lastopening, floorclip+start, 2*(rw_stopx-start)); + System.arraycopy(floorclip, start, vp_vars.openings, + vp_vars.lastopening, rw_stopx - start); + seg.setSprBottomClip(vp_vars.openings, vp_vars.lastopening + - start); + vp_vars.lastopening += rw_stopx - start; + } + + if (maskedtexture && C2JUtils.flags(seg.silhouette, SIL_TOP)) { + seg.silhouette |= SIL_TOP; + seg.tsilheight = Integer.MIN_VALUE; + } + if (maskedtexture && (seg.silhouette & SIL_BOTTOM) == 0) { + seg.silhouette |= SIL_BOTTOM; + seg.bsilheight = Integer.MAX_VALUE; + } + seg_vars.ds_p++; + } + + /** + * R_RenderSegLoop Draws zero, one, or two textures (and possibly a + * masked texture) for walls. Can draw or mark the starting pixel of + * floor and ceiling textures. Also sets the actual sprite clipping info + * (where sprites should be cut) Since rw_x ranges are non-overlapping, + * rendering all walls means completing the clipping list as well. The + * only difference between the parallel and the non-parallel version is + * that the parallel doesn't draw immediately but rather, generates + * RWIs. This can surely be unified to avoid replicating code. CALLED: + * CORE LOOPING ROUTINE. + */ + protected void RenderSegLoop() { + int angle; // angle_t + int index; + int yl; // low + int yh; // hight + int mid; + int texturecolumn = 0; // fixed_t + int top; + int bottom; + + for (; rw_x < rw_stopx; rw_x++) { + // mark floor / ceiling areas + yl = (topfrac + HEIGHTUNIT - 1) >> HEIGHTBITS; + + // no space above wall? + if (yl < ceilingclip[rw_x] + 1) { + yl = ceilingclip[rw_x] + 1; + } + + if (markceiling) { + top = ceilingclip[rw_x] + 1; + bottom = yl - 1; + + if (bottom >= floorclip[rw_x]) { + bottom = floorclip[rw_x] - 1; + } + + if (top <= bottom) { + vp_vars.visplanes[vp_vars.ceilingplane].setTop(rw_x, + (char) top); + vp_vars.visplanes[vp_vars.ceilingplane].setBottom(rw_x, + (char) bottom); + } + } + + yh = bottomfrac >> HEIGHTBITS; + + if (yh >= floorclip[rw_x]) { + yh = floorclip[rw_x] - 1; + } + + // A particular seg has been identified as a floor marker. + if (markfloor) { + top = yh + 1; + bottom = floorclip[rw_x] - 1; + if (top <= ceilingclip[rw_x]) { + top = ceilingclip[rw_x] + 1; + } + if (top <= bottom) { + vp_vars.visplanes[vp_vars.floorplane].setTop(rw_x, + (char) top); + vp_vars.visplanes[vp_vars.floorplane].setBottom(rw_x, + (char) bottom); + } + } + + // texturecolumn and lighting are independent of wall tiers + if (segtextured) { + // calculate texture offset + + // CAREFUL: a VERY anomalous point in the code. Their sum is + // supposed + // to give an angle not exceeding 45 degrees (or an index of + // 0x0FFF after + // shifting). If added with pure unsigned rules, this + // doesn't hold anymore, + // not even if accounting for overflow. + angle + = Tables.toBAMIndex(rw_centerangle + + (int) view.xtoviewangle[rw_x]); + + // FIXME: We are accessing finetangent here, the code seems + // pretty confident in that angle won't exceed 4K no matter + // what. + // But xtoviewangle alone can yield 8K when shifted. + // This usually only overflows if we idclip and look at + // certain directions (probably angles get fucked up), + // however it seems rare + // enough to just "swallow" the exception. You can eliminate + // it by anding + // with 0x1FFF if you're so inclined. + // FIXED by allowing overflow. See Tables for details. + texturecolumn + = rw_offset - FixedMul(finetangent[angle], rw_distance); + texturecolumn >>= FRACBITS; + // calculate lighting + index = rw_scale >> colormaps.lightScaleShift(); + + if (index >= colormaps.maxLightScale()) { + index = colormaps.maxLightScale() - 1; + } + + dcvars.dc_colormap = colormaps.walllights[index]; + dcvars.dc_x = rw_x; + dcvars.dc_iscale = (int) (0xffffffffL / rw_scale); + } + + // draw the wall tiers + if (midtexture != 0) { + // single sided line + dcvars.dc_yl = yl; + dcvars.dc_yh = yh; + dcvars.dc_texheight + = TexMan.getTextureheight(midtexture) >> FRACBITS; // killough + dcvars.dc_texturemid = rw_midtexturemid; + dcvars.dc_source_ofs = 0; + dcvars.dc_source + = TexMan.GetCachedColumn(midtexture, texturecolumn); + CompleteColumn(); + ceilingclip[rw_x] = (short) view.height; + floorclip[rw_x] = -1; + } else { + // two sided line + if (toptexture != 0) { + // top wall + mid = pixhigh >> HEIGHTBITS; + pixhigh += pixhighstep; + + if (mid >= floorclip[rw_x]) { + mid = floorclip[rw_x] - 1; + } + + if (mid >= yl) { + dcvars.dc_yl = yl; + dcvars.dc_yh = mid; + dcvars.dc_texturemid = rw_toptexturemid; + dcvars.dc_texheight = TexMan.getTextureheight(toptexture) >> FRACBITS; + dcvars.dc_source = TexMan.GetCachedColumn(toptexture, texturecolumn); + dcvars.dc_source_ofs = 0; + if (dcvars.dc_colormap == null) { + LOGGER.log(Level.FINE, "Two-sided"); + } + CompleteColumn(); + ceilingclip[rw_x] = (short) mid; + } else { + ceilingclip[rw_x] = (short) (yl - 1); + } + } else { + // no top wall + if (markceiling) { + ceilingclip[rw_x] = (short) (yl - 1); + } + } + + if (bottomtexture != 0) { + // bottom wall + mid = (pixlow + HEIGHTUNIT - 1) >> HEIGHTBITS; + pixlow += pixlowstep; + + // no space above wall? + if (mid <= ceilingclip[rw_x]) { + mid = ceilingclip[rw_x] + 1; + } + + if (mid <= yh) { + dcvars.dc_yl = mid; + dcvars.dc_yh = yh; + dcvars.dc_texturemid = rw_bottomtexturemid; + dcvars.dc_texheight = TexMan.getTextureheight(bottomtexture) >> FRACBITS; + dcvars.dc_source = TexMan.GetCachedColumn(bottomtexture, texturecolumn); + dcvars.dc_source_ofs = 0; + CompleteColumn(); + + floorclip[rw_x] = (short) mid; + } else { + floorclip[rw_x] = (short) (yh + 1); + } + } else { + // no bottom wall + if (markfloor) { + floorclip[rw_x] = (short) (yh + 1); + } + } + + if (maskedtexture) { + // save texturecol + // for backdrawing of masked mid texture + seg_vars.maskedtexturecol[seg_vars.pmaskedtexturecol + rw_x] = (short) texturecolumn; + } + } + + rw_scale += rw_scalestep; + topfrac += topstep; + bottomfrac += bottomstep; + } + } + + @Override + public void ClearClips() { + System.arraycopy(BLANKFLOORCLIP, 0, floorclip, 0, view.width); + System.arraycopy(BLANKCEILINGCLIP, 0, ceilingclip, 0, view.width); + } + + /** + * Called from RenderSegLoop. This should either invoke the column + * function, or store a wall rendering instruction in the parallel + * version. It's the only difference between the parallel and serial + * renderer, BTW. So override and implement accordingly. + */ + protected abstract void CompleteColumn(); + + @Override + public void ExecuteSetViewSize(int viewwidth) { + for (int i = 0; i < viewwidth; i++) { + BLANKFLOORCLIP[i] = (short) view.height; + BLANKCEILINGCLIP[i] = -1; + } + } + + @Override + public void CompleteRendering() { + // Nothing to do for serial. + } + + protected column_t col; + + public SegDrawer(SceneRenderer R) { + this.vp_vars = R.getVPVars(); + this.seg_vars = R.getSegVars(); + col = new column_t(); + seg_vars.drawsegs = malloc(drawseg_t::new, drawseg_t[]::new, seg_vars.MAXDRAWSEGS); + this.floorclip = new short[DOOM.vs.getScreenWidth()]; + this.ceilingclip = new short[DOOM.vs.getScreenWidth()]; + BLANKFLOORCLIP = new short[DOOM.vs.getScreenWidth()]; + BLANKCEILINGCLIP = new short[DOOM.vs.getScreenWidth()]; + } + + /** + * R_ScaleFromGlobalAngle Returns the texture mapping scale for the + * current line (horizontal span) at the given angle. rw_distance must + * be calculated first. + */ + protected int ScaleFromGlobalAngle(long visangle) { + int scale; // fixed_t + long anglea; + long angleb; + int sinea; + int sineb; + int num; // fixed_t + int den; + + // UNUSED + /* + * { fixed_t dist; fixed_t z; fixed_t sinv; fixed_t cosv; sinv = + * finesine[(visangle-rw_normalangle)>>ANGLETOFINESHIFT]; dist = + * FixedDiv (rw_distance, sinv); cosv = + * finecosine[(viewangle-visangle)>>ANGLETOFINESHIFT]; z = + * abs(FixedMul (dist, cosv)); scale = FixedDiv(projection, z); + * return scale; } + */ + anglea = (ANG90 + visangle - view.angle) & BITS32; + angleb = (ANG90 + visangle - rw_normalangle) & BITS32; + + // both sines are allways positive + sinea = finesine(anglea); + sineb = finesine(angleb); + num = FixedMul(view.projection, sineb) << view.detailshift; + den = FixedMul(rw_distance, sinea); + + if (den > num >> 16) { + scale = FixedDiv(num, den); + + if (scale > 64 * FRACUNIT) { + scale = 64 * FRACUNIT; + } else if (scale < 256) { + scale = 256; + } + } else { + scale = 64 * FRACUNIT; + } + + return scale; + } + + @Override + public void setGlobalAngle(long angle) { + this.rw_angle1 = angle; + } + } + + protected interface IPlaneDrawer { + + void InitPlanes(); + + void MapPlane(int y, int x1, int x2); + + void DrawPlanes(); + + int[] getDistScale(); + + /** + * Sync up in case there's concurrent planes/walls rendering + */ + void sync(); + } + + protected interface ISegDrawer extends ILimitResettable { + + void ClearClips(); + + short[] getBLANKCEILINGCLIP(); + + short[] getBLANKFLOORCLIP(); + + short[] getFloorClip(); + + short[] getCeilingClip(); + + void ExecuteSetViewSize(int viewwidth); + + void setGlobalAngle(long angle1); + + void StoreWallRange(int first, int last); + + /** + * If there is anything to do beyond the BPS traversal, + * e.g. parallel rendering + */ + void CompleteRendering(); + + /** + * Sync up in case there's concurrent planes/walls rendering + */ + void sync(); + } + + protected class Planes extends PlaneDrawer { + + Planes(DoomMain DOOM, RendererState R) { + super(DOOM, R); + } + + /** + * R_DrawPlanes At the end of each frame. This also means that visplanes + * must have been set BEFORE we called this function. Therefore, look + * for errors behind. + * + * @throws IOException + */ + @Override + public void DrawPlanes() { + if (DEBUG) { + LOGGER.log(Level.FINE, String.format("DrawPlanes: %d", vp_vars.lastvisplane)); + } + visplane_t pln; // visplane_t + int light; + int x; + int stop; + int angle; + + if (RANGECHECK) { + rangeCheckErrors(); + } + + for (int pl = 0; pl < vp_vars.lastvisplane; pl++) { + pln = vp_vars.visplanes[pl]; + if (DEBUG2) { + LOGGER.log(Level.FINER, String.valueOf(pln)); + } + + if (pln.minx > pln.maxx) { + continue; + } + // sky flat + if (pln.picnum == TexMan.getSkyFlatNum()) { + // Cache skytexture stuff here. They aren't going to change + // while + // being drawn, after all, are they? + int skytexture = TexMan.getSkyTexture(); + skydcvars.dc_texheight + = TexMan.getTextureheight(skytexture) >> FRACBITS; + skydcvars.dc_iscale + = vpvars.getSkyScale() >> view.detailshift; + + /** + * Sky is allways drawn full bright, i.e. colormaps[0] is + * used. Because of this hack, sky is not affected by INVUL + * inverse mapping. + * Settings.fixskypalette handles the fix + */ + if (DOOM.CM.equals(Settings.fix_sky_palette, Boolean.TRUE) && colormap.fixedcolormap != null) { + skydcvars.dc_colormap = colormap.fixedcolormap; + } else { + skydcvars.dc_colormap = colormap.colormaps[Palettes.COLORMAP_FIXED]; + } + skydcvars.dc_texturemid = TexMan.getSkyTextureMid(); + for (x = pln.minx; x <= pln.maxx; x++) { + + skydcvars.dc_yl = pln.getTop(x); + skydcvars.dc_yh = pln.getBottom(x); + + if (skydcvars.dc_yl <= skydcvars.dc_yh) { + angle + = (int) (addAngles(view.angle, view.xtoviewangle[x]) >>> ANGLETOSKYSHIFT); + skydcvars.dc_x = x; + // Optimized: texheight is going to be the same + // during normal skies drawing...right? + skydcvars.dc_source + = TexMan.GetCachedColumn(skytexture, angle); + colfunc.sky.invoke(); + } + } + continue; + } + + // regular flat + dsvars.ds_source = TexMan.getSafeFlat(pln.picnum); + + planeheight = Math.abs(pln.height - view.z); + light = (pln.lightlevel >> colormap.lightSegShift()) + colormap.extralight; + + if (light >= colormap.lightLevels()) { + light = colormap.lightLevels() - 1; + } + + if (light < 0) { + light = 0; + } + + planezlight = colormap.zlight[light]; + + // We set those values at the border of a plane's top to a + // "sentinel" value...ok. + pln.setTop(pln.maxx + 1, visplane_t.SENTINEL); + pln.setTop(pln.minx - 1, visplane_t.SENTINEL); + + stop = pln.maxx + 1; + + for (x = pln.minx; x <= stop; x++) { + MakeSpans(x, pln.getTop(x - 1), pln.getBottom(x - 1), pln.getTop(x), pln.getBottom(x)); + } + + // Z_ChangeTag (ds_source, PU_CACHE); + } + } + + } // End Plane class + + // /////////////////////// LIGHTS, POINTERS, COLORMAPS ETC. //////////////// + // /// FROM R_DATA, R_MAIN , R_DRAW ////////// + /** + * OK< this is supposed to "peg" into screen buffer 0. It will work AS LONG + * AS SOMEONE FUCKING ACTUALLY SETS IT !!!! + */ + protected V screen; + + protected static final boolean RANGECHECK = false; + + /** + * These are actually offsets inside screen 0 (or any screen). Therefore + * anything using them should "draw" inside screen 0 + */ + protected int[] ylookup = new int[MAXHEIGHT]; + + /** + * Columns offset to set where?! + */ + protected int[] columnofs = new int[MAXWIDTH]; + + /** + * General purpose. Used for solid walls and as an intermediary for + * threading + */ + protected ColVars dcvars; + + /** + * Used for spans + */ + protected SpanVars dsvars; + + // Used for sky drawer, to avoid clashing with shared dcvars + protected ColVars skydcvars; + + /** + * Masked drawing functions get "pegged" to this set of dcvars, passed upon + * initialization. However, multi-threaded vars are better off carrying each + * their own ones. + */ + protected ColVars maskedcvars; + + /** + * e6y: wide-res Borrowed from PrBoom+; + */ + + /* + * protected int wide_centerx, wide_ratio, wide_offsetx, wide_offset2x, + * wide_offsety, wide_offset2y; protected final base_ratio_t[] + * BaseRatioSizes = { new base_ratio_t(960, 600, 0, 48, 1.333333f), // 4:3 + * new base_ratio_t(1280, 450, 0, 48 * 3 / 4, 1.777777f), // 16:9 new + * base_ratio_t(1152, 500, 0, 48 * 5 / 6, 1.6f), // 16:10 new + * base_ratio_t(960, 600, 0, 48, 1.333333f), new base_ratio_t(960, 640, + * (int) (6.5 * FRACUNIT), 48 * 15 / 16, 1.25f) // 5:4 }; + */ + /** + * just for profiling purposes + */ + protected int framecount; + protected int sscount; + protected int linecount; + protected int loopcount; + + // + // precalculated math tables + // + protected long clipangle; + + // Set to 2*clipangle later. + protected long CLIPANGLE2; + + // The viewangletox[viewangle + FINEANGLES/4] lookup + // maps the visible view angles to screen X coordinates, + // flattening the arc to a flat projection plane. + // There will be many angles mapped to the same X. + protected final int[] viewangletox = new int[FINEANGLES / 2]; + + /** + * The xtoviewangle[] table maps a screen pixel to the lowest viewangle that + * maps back to x ranges from clipangle to -clipangle. + * + * @see view.xtoviewangle + */ + //protected long[] view.xtoviewangle;// MAES: to resize + // UNUSED. + // The finetangentgent[angle+FINEANGLES/4] table + // holds the fixed_t tangent values for view angles, + // ranging from MININT to 0 to MAXINT. + // fixed_t finetangent[FINEANGLES/2]; + // fixed_t finesine[5*FINEANGLES/4]; + // MAES: uh oh. So now all these ints must become finesines? fuck that. + // Also wtf @ this hack....this points to approx 1/4th of the finesine + // table, but what happens if I read past it? + // int[] finecosine = finesine[FINEANGLES/4]; + + /* + * MAES: what's going on with light tables here. OK...so these should be + * "unsigned bytes", since, after all, they'll be used as pointers inside an + * array to finally pick a color, so they should be expanded to shorts. + */ + // //////////// SOME UTILITY METHODS ///////////// + /** + * Assigns a point of view before calling PointToAngle CAREFUL: this isn't a + * pure function, as it alters the renderer's state! + */ + @Override + public final long PointToAngle2(int x1, int y1, int x2, int y2) { + // Careful with assignments... + view.x = x1; + view.y = y1; + + return view.PointToAngle(x2, y2); + } + + // + // R_InitPointToAngle + // + /* + * protected final void InitPointToAngle () { // UNUSED - now getting from + * tables.c if (false){ int i; long t; float f; // // slope (tangent) to + * angle lookup // for (i=0 ; i<=SLOPERANGE ; i++) { f = (float) Math.atan( + * (double)(i/SLOPERANGE )/(3.141592657*2)); t = (long) (0xffffffffL*f); + * tantoangle[i] = (int) t; } } } + */ + /** + * Public, static, stateless version of PointToAngle2. Call this one when + * "renderless" use of PointToAngle2 is required. + */ + public static long PointToAngle(int viewx, int viewy, int x, int y) { + // MAES: note how we don't use &BITS32 here. That is because + // we know that the maximum possible value of tantoangle is angle + // This way, we are actually working with vectors emanating + // from our current position. + x -= viewx; + y -= viewy; + + if ((x == 0) && (y == 0)) { + return 0; + } + + if (x >= 0) { + // x >=0 + if (y >= 0) { + // y>= 0 + + if (x > y) { + // octant 0 + return tantoangle[SlopeDiv(y, x)]; + } else { + // octant 1 + return (ANG90 - 1 - tantoangle[SlopeDiv(x, y)]); + } + } else { + // y<0 + y = -y; + + if (x > y) { + // octant 8 + return (-tantoangle[SlopeDiv(y, x)]); + } else { + // octant 7 + return (ANG270 + tantoangle[SlopeDiv(x, y)]); + } + } + } else { + // x<0 + x = -x; + + if (y >= 0) { + // y>= 0 + if (x > y) { + // octant 3 + return (ANG180 - 1 - tantoangle[SlopeDiv(y, x)]); + } else { + // octant 2 + return (ANG90 + tantoangle[SlopeDiv(x, y)]); + } + } else { + // y<0 + y = -y; + + if (x > y) { + // octant 4 + return (ANG180 + tantoangle[SlopeDiv(y, x)]); + } else { + // octant 5 + return (ANG270 - 1 - tantoangle[SlopeDiv(x, y)]); + } + } + } + // This is actually unreachable. + // return 0; + } + + // + // R_InitTables + // + protected void InitTables() { + // UNUSED: now getting from tables.c + /* + * int i; float a; float fv; int t; // viewangle tangent table for (i=0 + * ; i dx) { + temp = dx; + dx = dy; + dy = temp; + } + + // If one or both of the distances are *exactly* zero at this point, + // then this means that the wall is in your face anyway, plus we want to + // avoid a division by zero. So you get zero. + if (dx == 0) { + return 0; + } + + /* + * If dx is zero, this is going to bomb. Fixeddiv will return MAXINT aka + * 7FFFFFFF, >> DBITS will make it 3FFFFFF, which is more than enough to + * break tantoangle[]. In the original C code, this probably didn't + * matter: there would probably be garbage orientations thrown all + * around. However this is unacceptable in Java. OK, so the safeguard + * above prevents that. Still... this method is only called once per + * visible wall per frame, so one check more or less at this point won't + * change much. It's better to be safe than sorry. + */ + // This effectively limits the angle to + // angle = Math.max(FixedDiv(dy, dx), 2048) >> DBITS; + angle = (FixedDiv(dy, dx) & 0x1FFFF) >> DBITS; + + // Since the division will be 0xFFFF at most, DBITS will restrict + // the maximum angle index to 7FF, about 45, so adding ANG90 with + // no other safeguards is OK. + angle = (int) ((tantoangle[angle] + ANG90) >> ANGLETOFINESHIFT); + + // use as cosine + dist = FixedDiv(dx, finesine[angle]); + + return dist; + } + + // //////////// COMMON RENDERING GLOBALS //////////////// + // //////////////// COLUMN AND SPAN FUNCTIONS ////////////// + protected ColFuncs colfunc; + + // Keep two sets of functions. + protected ColFuncs colfunchi; + + protected ColFuncs colfunclow; + + protected void setHiColFuns() { + colfunchi.main = colfunchi.base = DrawColumn; + colfunchi.masked = DrawColumnMasked; + colfunchi.fuzz = DrawFuzzColumn; + colfunchi.trans = DrawTranslatedColumn; + colfunchi.glass = DrawTLColumn; + colfunchi.player = DrawColumnPlayer; + colfunchi.sky = DrawColumnSkies; + } + + protected void setLowColFuns() { + colfunclow.main = colfunclow.base = DrawColumnLow; + colfunclow.masked = DrawColumnMaskedLow; + colfunclow.fuzz = DrawFuzzColumnLow; + colfunclow.trans = DrawTranslatedColumnLow; + colfunclow.glass = DrawTLColumn; + colfunclow.player = DrawColumnMaskedLow; + colfunclow.sky = DrawColumnSkiesLow; + } + + @Override + public ColFuncs getColFuncsHi() { + return this.colfunchi; + } + + @Override + public ColFuncs getColFuncsLow() { + return this.colfunclow; + } + + @Override + public ColVars getMaskedDCVars() { + return this.maskedcvars; + } + + // These column functions are "fixed" for a given renderer, and are + // not used directly, but only after passing them to colfuncs + protected DoomColumnFunction DrawTranslatedColumn; + protected DoomColumnFunction DrawTranslatedColumnLow; + protected DoomColumnFunction DrawColumnPlayer; + protected DoomColumnFunction DrawColumnSkies; + protected DoomColumnFunction DrawColumnSkiesLow; + protected DoomColumnFunction DrawFuzzColumn; + protected DoomColumnFunction DrawFuzzColumnLow; + protected DoomColumnFunction DrawColumn; + protected DoomColumnFunction DrawColumnLow; + protected DoomColumnFunction DrawColumnMasked; + protected DoomColumnFunction DrawColumnMaskedLow; + protected DoomColumnFunction DrawTLColumn; + + /** + * to be set in UnifiedRenderer + */ + protected DoomSpanFunction DrawSpan, DrawSpanLow; + + // ////////////// r_draw methods ////////////// + /** + * R_DrawViewBorder Draws the border around the view for different size windows + * Made use of CopyRect there + * - Good Sign 2017/04/06 + */ + @Override + public void DrawViewBorder() { + if (view.scaledwidth == DOOM.vs.getScreenWidth()) { + return; + } + + final int top = ((DOOM.vs.getScreenHeight() - DOOM.statusBar.getHeight()) - view.height) / 2; + final int side = (DOOM.vs.getScreenWidth() - view.scaledwidth) / 2; + final Rectangle rect; + // copy top + rect = new Rectangle(0, 0, DOOM.vs.getScreenWidth(), top); + DOOM.graphicSystem.CopyRect(BG, rect, FG); + // copy left side + rect.setBounds(0, top, side, view.height); + DOOM.graphicSystem.CopyRect(BG, rect, FG); + // copy right side + rect.x = side + view.scaledwidth; + DOOM.graphicSystem.CopyRect(BG, rect, FG); + // copy bottom + rect.setBounds(0, top + view.height, DOOM.vs.getScreenWidth(), top); + DOOM.graphicSystem.CopyRect(BG, rect, FG); + } + + @Override + public void ExecuteSetViewSize() { + int cosadj; + int dy; + int level; + int startmap; + + setsizeneeded = false; + + // 11 Blocks means "full screen" + if (setblocks == 11) { + view.scaledwidth = DOOM.vs.getScreenWidth(); + view.height = DOOM.vs.getScreenHeight(); + } else if (DOOM.CM.equals(Settings.scale_screen_tiles, Boolean.TRUE)) { + /** + * Make it exactly as in vanilla DOOM + * - Good Sign 2017/05/08 + */ + view.scaledwidth = (setblocks * 32) * DOOM.vs.getScalingX(); + view.height = ((setblocks * 168 / 10) & ~7) * DOOM.vs.getScalingY(); + } else { // Mocha Doom formula looks better for non-scaled tiles + view.scaledwidth = setblocks * (DOOM.vs.getScreenWidth() / 10); + // Height can only be a multiple of 8. + view.height = (short) ((setblocks * (DOOM.vs.getScreenHeight() - DOOM.statusBar.getHeight()) / 10) & ~7); + } + + skydcvars.viewheight + = maskedcvars.viewheight = dcvars.viewheight = view.height; + + view.detailshift = setdetail; + view.width = view.scaledwidth >> view.detailshift; + + view.centery = view.height / 2; + view.centerx = view.width / 2; + view.centerxfrac = (view.centerx << FRACBITS); + view.centeryfrac = (view.centery << FRACBITS); + view.projection = view.centerxfrac; + + skydcvars.centery = maskedcvars.centery = dcvars.centery = view.centery; + + // High detail + if (view.detailshift == 0) { + + colfunc = colfunchi; + dsvars.spanfunc = DrawSpan; + } else { + // Low detail + colfunc = colfunclow; + dsvars.spanfunc = DrawSpanLow; + + } + + InitBuffer(view.scaledwidth, view.height); + + InitTextureMapping(); + + // psprite scales + // pspritescale = FRACUNIT*viewwidth/vs.getScreenWidth(); + // pspriteiscale = FRACUNIT*vs.getScreenWidth()/viewwidth; + MyThings.setPspriteScale((int) (FRACUNIT * (DOOM.vs.getScreenMul() * view.width) / DOOM.vs.getScreenWidth())); + MyThings.setPspriteIscale((int) (FRACUNIT * (DOOM.vs.getScreenWidth() / (view.width * DOOM.vs.getScreenMul())))); + vp_vars.setSkyScale((int) (FRACUNIT * (DOOM.vs.getScreenWidth() / (view.width * DOOM.vs.getScreenMul())))); + + view.BOBADJUST = this.DOOM.vs.getSafeScaling() << 15; + view.WEAPONADJUST = (int) ((DOOM.vs.getScreenWidth() / (2 * DOOM.vs.getScreenMul())) * FRACUNIT); + + // thing clipping + for (int i = 0; i < view.width; i++) { + view.screenheightarray[i] = (short) view.height; + } + + // planes + for (int i = 0; i < view.height; i++) { + dy = ((i - view.height / 2) << FRACBITS) + FRACUNIT / 2; + dy = Math.abs(dy); + vp_vars.yslope[i] = FixedDiv((view.width << view.detailshift) / 2 * FRACUNIT, dy); + // MyPlanes.yslopef[i] = ((viewwidth<= colormaps.numColorMaps()) { + level = colormaps.numColorMaps() - 1; + } + colormaps.scalelight[i][j] = colormaps.colormaps[level]; + } + } + + MySegs.ExecuteSetViewSize(view.width); + } + + private final Rectangle backScreenRect = new Rectangle(); + private final Rectangle tilePatchRect = new Rectangle(); + + /** + * R_FillBackScreen Fills the back screen with a pattern for variable screen + * sizes Also draws a beveled edge. This is actually stored in screen 1, and + * is only OCCASIONALLY written to screen 0 (the visible one) by calling + * R_VideoErase. + */ + @Override + @R_Draw.C(R_FillBackScreen) + public void FillBackScreen() { + final boolean scaleSetting = Engine.getConfig().equals(Settings.scale_screen_tiles, Boolean.TRUE); + flat_t src; + DoomScreen dest; + int x; + int y; + patch_t patch; + + // DOOM border patch. + String name1 = "FLOOR7_2"; + + // DOOM II border patch. + String name2 = "GRNROCK"; + + String name; + + if (view.scaledwidth == DOOM.vs.getScreenWidth()) { + return; + } + + if (DOOM.isCommercial()) { + name = name2; + } else { + name = name1; + } + + /* This is a flat we're reading here */ + src = DOOM.wadLoader.CacheLumpName(name, PU_CACHE, flat_t.class); + dest = BG; + + /** + * TODO: cache it? + * This part actually draws the border itself, without bevels + * + * MAES: + * improved drawing routine for extended bit-depth compatibility. + * + * Now supports configurable vanilla-like scaling of tiles + * - Good Sign 2017/04/09 + * + * @SourceCode.Compatible + */ + Tiling: + { + this.backScreenRect.setBounds(0, 0, DOOM.vs.getScreenWidth(), DOOM.vs.getScreenHeight() - DOOM.statusBar.getHeight()); + this.tilePatchRect.setBounds(0, 0, 64, 64); + V block = DOOM.graphicSystem.convertPalettedBlock(src.data); + if (scaleSetting) { + block = DOOM.graphicSystem.ScaleBlock(block, DOOM.vs, tilePatchRect.width, tilePatchRect.height); + this.tilePatchRect.width *= DOOM.graphicSystem.getScalingX(); + this.tilePatchRect.height *= DOOM.graphicSystem.getScalingY(); + } + DOOM.graphicSystem.TileScreenArea(dest, backScreenRect, block, tilePatchRect); + } + + final int scaleFlags = V_NOSCALESTART | (scaleSetting ? 0 : V_NOSCALEOFFSET | V_NOSCALEPATCH); + final int stepX = scaleSetting ? DOOM.graphicSystem.getScalingX() << 3 : 8; + final int stepY = scaleSetting ? DOOM.graphicSystem.getScalingY() << 3 : 8; + + patch = DOOM.wadLoader.CachePatchName("BRDR_T", PU_CACHE); + for (x = 0; x < view.scaledwidth; x += stepX) { + DOOM.graphicSystem.DrawPatchScaled(BG, patch, DOOM.vs, view.windowx + x, view.windowy - stepY, scaleFlags); + } + + patch = DOOM.wadLoader.CachePatchName("BRDR_B", PU_CACHE); + for (x = 0; x < view.scaledwidth; x += stepX) { + DOOM.graphicSystem.DrawPatchScaled(BG, patch, DOOM.vs, view.windowx + x, view.windowy + view.height, scaleFlags); + } + + patch = DOOM.wadLoader.CachePatchName("BRDR_L", PU_CACHE); + for (y = 0; y < view.height; y += stepY) { + DOOM.graphicSystem.DrawPatchScaled(BG, patch, DOOM.vs, view.windowx - stepX, view.windowy + y, scaleFlags); + } + + patch = DOOM.wadLoader.CachePatchName("BRDR_R", PU_CACHE); + for (y = 0; y < view.height; y += stepY) { + DOOM.graphicSystem.DrawPatchScaled(BG, patch, DOOM.vs, view.windowx + view.scaledwidth, view.windowy + y, scaleFlags); + } + + // Draw beveled edge. Top-left + patch = DOOM.wadLoader.CachePatchName("BRDR_TL", PU_CACHE); + DOOM.graphicSystem.DrawPatchScaled(BG, patch, DOOM.vs, view.windowx - stepX, view.windowy - stepY, scaleFlags); + + // Top-right. + patch = DOOM.wadLoader.CachePatchName("BRDR_TR", PU_CACHE); + DOOM.graphicSystem.DrawPatchScaled(BG, patch, DOOM.vs, view.windowx + view.scaledwidth, view.windowy - stepY, scaleFlags); + + // Bottom-left + patch = DOOM.wadLoader.CachePatchName("BRDR_BL", PU_CACHE); + DOOM.graphicSystem.DrawPatchScaled(BG, patch, DOOM.vs, view.windowx - stepX, view.windowy + view.height, scaleFlags); + + // Bottom-right. + patch = DOOM.wadLoader.CachePatchName("BRDR_BR", PU_CACHE); + DOOM.graphicSystem.DrawPatchScaled(BG, patch, DOOM.vs, view.windowx + view.width, view.windowy + view.height, scaleFlags); + } + + /** + * R_Init + */ + @Override + public void Init() { + // Any good reason for this to be here? + // drawsegs=new drawseg_t[MAXDRAWSEGS]; + // C2JUtils.initArrayOfObjects(drawsegs); + + // DON'T FORGET ABOUT MEEEEEE!!!11!!! + this.screen = this.DOOM.graphicSystem.getScreen(FG); + + LOGGER.log(Level.INFO, "R_InitData"); + InitData(); + // InitPointToAngle (); + LOGGER.log(Level.INFO, "R_InitPointToAngle"); + + // ds.DM.viewwidth / ds.viewheight / detailLevel are set by the defaults + LOGGER.log(Level.INFO, "R_InitTables"); + InitTables(); + + SetViewSize(DOOM.menu.getScreenBlocks(), DOOM.menu.getDetailLevel()); + + LOGGER.log(Level.INFO, "R_InitPlanes"); + MyPlanes.InitPlanes(); + + LOGGER.log(Level.INFO, "R_InitLightTables"); + InitLightTables(); + + int initSkyMap = TexMan.InitSkyMap(); + LOGGER.log(Level.INFO, String.format("R_InitSkyMap: %d", initSkyMap)); + + LOGGER.log(Level.INFO, "R_InitTranslationsTables"); + InitTranslationTables(); + + LOGGER.log(Level.INFO, "R_InitTranMap"); + R_InitTranMap(0); + + LOGGER.log(Level.INFO, "R_InitDrawingFunctions"); + R_InitDrawingFunctions(); + + framecount = 0; + } + + /** + * R_InitBuffer Creates lookup tables that avoid multiplies and other + * hazzles for getting the framebuffer address of a pixel to draw. MAES: + * this is "pinned" to screen[0] of a Video Renderer. We will handle this + * differently elsewhere... + */ + protected void InitBuffer(int width, int height) { + int i; + + // Handle resize, + // e.g. smaller view windows + // with border and/or status bar. + view.windowx = (DOOM.vs.getScreenWidth() - width) >> 1; + + // Column offset. For windows. + for (i = 0; i < width; i++) { + columnofs[i] = view.windowx + i; + } + + // SamE with base row offset. + if (width == DOOM.vs.getScreenWidth()) { + view.windowy = 0; + } else { + view.windowy = (DOOM.vs.getScreenHeight() - DOOM.statusBar.getHeight() - height) >> 1; + } + + // Preclaculate all row offsets. + for (i = 0; i < height; i++) { + ylookup[i] = /* screens[0] + */ (i + view.windowy) * DOOM.vs.getScreenWidth(); + } + } + + /** + * R_InitTextureMapping Not moved into the TextureManager because it's + * tighly coupled to the visuals, rather than textures. Perhaps the name is + * not the most appropriate. + */ + protected void InitTextureMapping() { + int i, x, t; + int focallength; // fixed_t + int fov = FIELDOFVIEW; + + // For widescreen displays, increase the FOV so that the middle part of + // the + // screen that would be visible on a 4:3 display has the requested FOV. + /* + * UNUSED if (wide_centerx != centerx) { // wide_centerx is what centerx + * would be // if the display was not widescreen fov = (int) + * (Math.atan((double) centerx Math.tan((double) fov * Math.PI / + * FINEANGLES) / (double) wide_centerx) FINEANGLES / Math.PI); if (fov > + * 130 * FINEANGLES / 360) fov = 130 * FINEANGLES / 360; } + */ + // Use tangent table to generate viewangletox: + // viewangletox will give the next greatest x + // after the view angle. + // + // Calc focallength + // so FIELDOFVIEW angles covers vs.getScreenWidth(). + focallength + = FixedDiv(view.centerxfrac, finetangent[QUARTERMARK + FIELDOFVIEW + / 2]); + + for (i = 0; i < FINEANGLES / 2; i++) { + if (finetangent[i] > FRACUNIT * 2) { + t = -1; + } else if (finetangent[i] < -FRACUNIT * 2) { + t = view.width + 1; + } else { + t = FixedMul(finetangent[i], focallength); + t = (view.centerxfrac - t + FRACUNIT - 1) >> FRACBITS; + + if (t < -1) { + t = -1; + } else if (t > view.width + 1) { + t = view.width + 1; + } + } + viewangletox[i] = t; + } + + // Scan viewangletox[] to generate xtoviewangle[]: + // xtoviewangle will give the smallest view angle + // that maps to x. + for (x = 0; x <= view.width; x++) { + i = 0; + while (viewangletox[i] > x) { + i++; + } + view.xtoviewangle[x] = addAngles((i << ANGLETOFINESHIFT), -ANG90); + } + + // Take out the fencepost cases from viewangletox. + for (i = 0; i < FINEANGLES / 2; i++) { + t = FixedMul(finetangent[i], focallength); + t = view.centerx - t; + + if (viewangletox[i] == -1) { + viewangletox[i] = 0; + } else if (viewangletox[i] == view.width + 1) { + viewangletox[i] = view.width; + } + } + + clipangle = view.xtoviewangle[0]; + // OPTIMIZE: assign constant for optimization. + CLIPANGLE2 = (2 * clipangle) & BITS32; + } + + // + // R_InitLightTables + // Only inits the zlight table, + // because the scalelight table changes with view size. + // + protected final static int DISTMAP = 2; + + protected void InitLightTables() { + int i; + int j; + int startmap; + int scale; + + // Calculate the light levels to use + // for each level / distance combination. + for (i = 0; i < colormaps.lightLevels(); i++) { + startmap = ((colormaps.lightLevels() - colormaps.lightBright() - i) * 2) * colormaps.numColorMaps() / colormaps.lightLevels(); + for (j = 0; j < colormaps.maxLightZ(); j++) { + // CPhipps - use 320 here instead of vs.getScreenWidth(), otherwise hires is + // brighter than normal res + + scale = FixedDiv((320 / 2 * FRACUNIT), (j + 1) << colormaps.lightZShift()); + int t, level = startmap - (scale >>= colormaps.lightScaleShift()) / DISTMAP; + + if (level < 0) { + level = 0; + } + + if (level >= colormaps.numColorMaps()) { + level = colormaps.numColorMaps() - 1; + } + + // zlight[i][j] = colormaps + level*256; + colormaps.zlight[i][j] = colormaps.colormaps[level]; + } + } + } + + protected static final int TSC = 12; + + /** + * number of fixed point digits in + * filter percent + */ + byte[] main_tranmap; + + /** + * A faster implementation of the tranmap calculations. Almost 10x faster + * than the old one! + * + * @param progress + */ + protected void R_InitTranMap(int progress) { + int lump = DOOM.wadLoader.CheckNumForName("TRANMAP"); + + long ta = System.nanoTime(); + + // PRIORITY: a map file has been specified from commandline. Try to read + // it. If OK, this trumps even those specified in lumps. + DOOM.cVarManager.with(CommandVariable.TRANMAP, 0, (String tranmap) -> { + if (C2JUtils.testReadAccess(tranmap)) { + LOGGER.log(Level.INFO, + String.format("Translucency map file %s specified in -tranmap arg. Attempting to use...", tranmap)); + main_tranmap = new byte[256 * 256]; // killough 4/11/98 + int result = MenuMisc.ReadFile(tranmap, main_tranmap); + if (result > 0) { + return; + } + + LOGGER.log(Level.SEVERE, "R_InitTranMap: translucency map failure"); + } + }); + + // Next, if a tranlucency filter map lump is present, use it + if (lump != -1) { // Set a pointer to the translucency filter maps. + LOGGER.log(Level.INFO, "Translucency map found in lump. Attempting to use..."); + // main_tranmap=new byte[256*256]; // killough 4/11/98 + main_tranmap = DOOM.wadLoader.CacheLumpNumAsRawBytes(lump, Defines.PU_STATIC); // killough + // 4/11/98 + // Tolerate 64K or more. + if (main_tranmap.length >= 0x10000) { + return; + } + LOGGER.log(Level.SEVERE, "R_InitTranMap: tranlucency filter map failure"); // Not good, try something else. + } + + // A default map file already exists. Try to read it. + if (C2JUtils.testReadAccess("tranmap.dat")) { + LOGGER.log(Level.INFO, "Translucency map found in default tranmap.dat file. Attempting to use..."); + main_tranmap = new byte[256 * 256]; // killough 4/11/98 + int result = MenuMisc.ReadFile("tranmap.dat", main_tranmap); + if (result > 0) { + return; // Something went wrong, so fuck that. + } + } + + // Nothing to do, so we must synthesize it from scratch. And, boy, is it + // slooow. + { // Compose a default transparent filter map based on PLAYPAL. + LOGGER.log(Level.INFO, "Computing translucency map from scratch...that's gonna be SLOW..."); + byte[] playpal = DOOM.wadLoader.CacheLumpNameAsRawBytes("PLAYPAL", Defines.PU_STATIC); + main_tranmap = new byte[256 * 256]; // killough 4/11/98 + int[] basepal = new int[3 * 256]; + int[] mixedpal = new int[3 * 256 * 256]; + + main_tranmap = new byte[256 * 256]; + + // Init array of base colors. + for (int i = 0; i < 256; i++) { + basepal[3 * i] = 0Xff & playpal[i * 3]; + basepal[1 + 3 * i] = 0Xff & playpal[1 + i * 3]; + basepal[2 + 3 * i] = 0Xff & playpal[2 + i * 3]; + } + + // Init array of mixed colors. These are true RGB. + // The diagonal of this array will be the original colors. + for (int i = 0; i < 256 * 3; i += 3) { + for (int j = 0; j < 256 * 3; j += 3) { + mixColors(basepal, basepal, mixedpal, i, j, j * 256 + i); + } + } + + // Init distance map. Every original palette colour has a + // certain distance from all the others. The diagonal is zero. + // The interpretation is that e.g. the mixture of color 2 and 8 will + // have a RGB value, which is closest to euclidean distance to + // e.g. original color 9. Therefore we should put "9" in the (2,8) + // and (8,2) cells of the tranmap. + final float[] tmpdist = new float[256]; + + for (int a = 0; a < 256; a++) { + for (int b = a; b < 256; b++) { + // We evaluate the mixture of a and b + // Construct distance table vs all of the ORIGINAL colors. + for (int k = 0; k < 256; k++) { + tmpdist[k] = colorDistance(mixedpal, basepal, 3 * (a + b * 256), k * 3); + } + + main_tranmap[(a << 8) | b] = (byte) findMin(tmpdist); + main_tranmap[(b << 8) | a] = main_tranmap[(a << 8) | b]; + } + } + LOGGER.log(Level.INFO, "R_InitTranMap: done"); + if (MenuMisc.WriteFile("tranmap.dat", main_tranmap, + main_tranmap.length)) { + LOGGER.log(Level.INFO, "TRANMAP.DAT saved to disk for your convenience! Next time will be faster."); + } + } + + long b = System.nanoTime(); + LOGGER.log(Level.INFO, String.format("Tranmap %d", (b - ta) / 1000000)); + } + + /** + * Mixes two RGB colors. Nuff said + */ + protected void mixColors(int[] a, int[] b, int[] c, int pa, int pb, + int pc) { + c[pc] = (a[pa] + b[pb]) / 2; + c[pc + 1] = (a[pa + 1] + b[pb + 1]) / 2; + c[pc + 2] = (a[pa + 2] + b[pb + 2]) / 2; + + } + + /** + * Returns the euclidean distance of two RGB colors. Nuff said + */ + protected float colorDistance(int[] a, int[] b, int pa, int pb) { + return (float) Math.sqrt((a[pa] - b[pb]) * (a[pa] - b[pb]) + + (a[pa + 1] - b[pb + 1]) * (a[pa + 1] - b[pb + 1]) + + (a[pa + 2] - b[pb + 2]) * (a[pa + 2] - b[pb + 2])); + } + + /** + * Stuff that is trivially initializable, even with generics, + * but is only safe to do after all constructors have completed. + */ + protected void completeInit() { + this.detailaware.add(MyThings); + } + + protected int findMin(float[] a) { + int minindex = 0; + float min = Float.POSITIVE_INFINITY; + + for (int i = 0; i < a.length; i++) { + if (a[i] < min) { + min = a[i]; + minindex = i; + } + } + + return minindex; + + } + + /** + * R_DrawMaskedColumnSinglePost. Used to handle some special cases where + * cached columns get used as "masked" middle textures. Will be treated as a + * single-run post of capped length. + */ + + /* + * protected final void DrawCompositeColumnPost(byte[] column) { int + * topscreen; int bottomscreen; int basetexturemid; // fixed_t int + * topdelta=0; // Fixed value int length; basetexturemid = dc_texturemid; // + * That's true for the whole column. dc_source = column; // for each post... + * while (topdelta==0) { // calculate unclipped screen coordinates // for + * post topscreen = sprtopscreen + spryscale * 0; length = column.length; + * bottomscreen = topscreen + spryscale * length; dc_yl = (topscreen + + * FRACUNIT - 1) >> FRACBITS; dc_yh = (bottomscreen - 1) >> FRACBITS; if + * (dc_yh >= mfloorclip[p_mfloorclip + dc_x]) dc_yh = + * mfloorclip[p_mfloorclip + dc_x] - 1; if (dc_yl <= + * mceilingclip[p_mceilingclip + dc_x]) dc_yl = mceilingclip[p_mceilingclip + * + dc_x] + 1; // killough 3/2/98, 3/27/98: Failsafe against + * overflow/crash: if (dc_yl <= dc_yh && dc_yh < viewheight) { // Set + * pointer inside column to current post's data // Rremember, it goes + * {postlen}{postdelta}{pad}[data]{pad} dc_source_ofs = 0; // pointer + 3; + * dc_texturemid = basetexturemid - (topdelta << FRACBITS); // Drawn by + * either R_DrawColumn // or (SHADOW) R_DrawFuzzColumn. dc_texheight=0; // + * Killough try { maskedcolfunc.invoke(); } catch (Exception e){ + * System.err.printf("Error rendering %d %d %d\n", dc_yl,dc_yh,dc_yh-dc_yl); + * } } topdelta--; } dc_texturemid = basetexturemid; } + */ + protected abstract void InitColormaps() throws IOException; + + // Only used by Fuzz effect + protected BlurryTable BLURRY_MAP; + + /** + * R_InitData Locates all the lumps that will be used by all views Must be + * called after W_Init. + */ + public void InitData() { + try { + LOGGER.log(Level.INFO, "Init Texture and Flat Manager"); + TexMan = this.DOOM.textureManager; + LOGGER.log(Level.INFO, "InitTextures"); + TexMan.InitTextures(); + LOGGER.log(Level.INFO, "InitFlats"); + TexMan.InitFlats(); + LOGGER.log(Level.INFO, "InitSprites"); + DOOM.spriteManager.InitSpriteLumps(); + MyThings.cacheSpriteManager(DOOM.spriteManager); + VIS.cacheSpriteManager(DOOM.spriteManager); + LOGGER.log(Level.INFO, "InitColormaps"); + InitColormaps(); + + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Error: InitData failure", e); + } + + } + + protected int spritememory; + + /** + * To be called right after PrecacheLevel from SetupLevel in LevelLoader. + * It's an ugly hack, in that it must communicate with the "Game map" class + * and determine what kinds of monsters are actually in the level and + * whether it should load their graphics or not. Whenever we implement it, + * it's going to be ugly and not neatly separated anyway. + * + * @return + */ + @Override + public void PreCacheThinkers() { + + boolean[] spritepresent; + thinker_t th; + spriteframe_t sf; + int lump; + + final spritedef_t[] sprites = DOOM.spriteManager.getSprites(); + final int numsprites = DOOM.spriteManager.getNumSprites(); + + spritepresent = new boolean[numsprites]; + + for (th = DOOM.actions.getThinkerCap().next; th != DOOM.actions.getThinkerCap(); th = th.next) { + if (th.thinkerFunction == P_MobjThinker) { + spritepresent[((mobj_t) th).mobj_sprite.ordinal()] = true; + } + } + + spritememory = 0; + for (int i = 0; i < numsprites; i++) { + if (!spritepresent[i]) { + continue; + } + + for (int j = 0; j < sprites[i].numframes; j++) { + sf = sprites[i].spriteframes[j]; + for (int k = 0; k < 8; k++) { + lump = DOOM.spriteManager.getFirstSpriteLump() + sf.lump[k]; + spritememory += DOOM.wadLoader.GetLumpInfo(lump).size; + DOOM.wadLoader.CacheLumpNum(lump, PU_CACHE, patch_t.class); + } + } + } + } + + /** + * R_InitTranslationTables Creates the translation tables to map the green + * color ramp to gray, brown, red. Assumes a given structure of the PLAYPAL. + * Could be read from a lump instead. + */ + protected void InitTranslationTables() { + int i; + + final int TR_COLORS = 28; + + // translationtables = Z_Malloc (256*3+255, PU_STATIC, 0); + // translationtables = (byte *)(( (int)translationtables + 255 )& ~255); + byte[][] translationtables + = colormaps.translationtables = new byte[TR_COLORS][256]; + + // translate just the 16 green colors + for (i = 0; i < 256; i++) { + translationtables[0][i] = (byte) i; + + if (i >= 0x70 && i <= 0x7f) { + // Remap green range to other ranges. + translationtables[1][i] = (byte) (0x60 + (i & 0xf)); // gray + translationtables[2][i] = (byte) (0x40 + (i & 0xf)); // brown + translationtables[3][i] = (byte) (0x20 + (i & 0xf)); // red + translationtables[4][i] = (byte) (0x10 + (i & 0xf)); // pink + translationtables[5][i] = (byte) (0x30 + (i & 0xf)); // skin + translationtables[6][i] = (byte) (0x50 + (i & 0xf)); // metal + translationtables[7][i] = (byte) (0x80 + (i & 0xf)); // copper + translationtables[8][i] = (byte) (0xB0 + (i & 0xf)); // b.red + translationtables[9][i] = (byte) (0xC0 + (i & 0xf)); // electric + // blue + translationtables[10][i] = (byte) (0xD0 + (i & 0xf)); // guantanamo + // "Halfhue" colors for which there are only 8 distinct hues + translationtables[11][i] = (byte) (0x90 + (i & 0xf) / 2); // brown2 + translationtables[12][i] = (byte) (0x98 + (i & 0xf) / 2); // gray2 + translationtables[13][i] = (byte) (0xA0 + (i & 0xf) / 2); // piss + translationtables[14][i] = (byte) (0xA8 + (i & 0xf) / 2); // gay + translationtables[15][i] = (byte) (0xE0 + (i & 0xf) / 2); // yellow + translationtables[16][i] = (byte) (0xE8 + (i & 0xf) / 2); // turd + translationtables[17][i] = (byte) (0xF0 + (i & 0xf) / 2); // compblue + translationtables[18][i] = (byte) (0xF8 + (i & 0xf) / 2); // whore + translationtables[19][i] = (byte) (0x05 + (i & 0xf) / 2); // nigga + // "Pimped up" colors, using mixed hues. + translationtables[20][i] = (byte) (0x90 + (i & 0xf)); // soldier + translationtables[21][i] = (byte) (0xA0 + (i & 0xf)); // drag + // queen + translationtables[22][i] = (byte) (0xE0 + (i & 0xf)); // shit & + // piss + translationtables[23][i] = (byte) (0xF0 + (i & 0xf)); // raver + translationtables[24][i] = (byte) (0x70 + (0xf - i & 0xf)); // inv.marine + translationtables[25][i] = (byte) (0xF0 + (0xf - i & 0xf)); // inv.raver + translationtables[26][i] = (byte) (0xE0 + (0xf - i & 0xf)); // piss + // & + // shit + translationtables[27][i] = (byte) (0xA0 + (i & 0xf)); // shitty + // gay + } else { + for (int j = 1; j < TR_COLORS; j++) { + // Keep all other colors as is. + translationtables[j][i] = (byte) i; + } + } + } + } + + // ///////////////// Generic rendering methods ///////////////////// + public IMaskedDrawer getThings() { + return this.MyThings; + } + + /** + * e6y: this is a precalculated value for more precise flats drawing (see + * R_MapPlane) "Borrowed" from PrBoom+ + */ + protected float viewfocratio; + + protected int projectiony; + + // Some more isolation methods.... + @Override + public int getValidCount() { + return validcount; + } + + @Override + public void increaseValidCount(int amount) { + validcount += amount; + } + + @Override + public boolean getSetSizeNeeded() { + return setsizeneeded; + } + + @Override + public TextureManager getTextureManager() { + return TexMan; + } + + @Override + public PlaneDrawer getPlaneDrawer() { + return this.MyPlanes; + } + + @Override + public ViewVars getView() { + return this.view; + } + + @Override + public SpanVars getDSVars() { + return this.dsvars; + } + + @Override + public LightsAndColors getColorMap() { + return this.colormaps; + } + + @Override + public IDoomSystem getDoomSystem() { + return this.DOOM.doomSystem; + } + + @Override + public Visplanes getVPVars() { + return this.vp_vars; + } + + @Override + public SegVars getSegVars() { + return this.seg_vars; + } + + @Override + public IWadLoader getWadLoader() { + return this.DOOM.wadLoader; + } + + @Override + public ISpriteManager getSpriteManager() { + return this.DOOM.spriteManager; + } + + @Override + public BSPVars getBSPVars() { + return this.MyBSP; + } + + @Override + public IVisSpriteManagement getVisSpriteManager() { + return this.VIS; + } + + /** + * Initializes the various drawing functions. They are all "pegged" to the + * same dcvars/dsvars object. Any initializations of e.g. parallel renderers + * and their supporting subsystems should occur here. + */ + protected void R_InitDrawingFunctions() { + this.setHiColFuns(); + this.setLowColFuns(); + } + + // //////////////////////////// LIMIT RESETTING ////////////////// + @Override + public void resetLimits() { + // Call it only at the beginning of new levels. + VIS.resetLimits(); + MySegs.resetLimits(); + } + + /** + * R_RenderView As you can guess, this renders the player view of a + * particular player object. In practice, it could render the view of any + * mobj too, provided you adapt the SetupFrame method (where the viewing + * variables are set). This is the "vanilla" implementation which just works + * for most cases. + */ + @Override + public void RenderPlayerView(player_t player) { + + // Viewing variables are set according to the player's mobj. Interesting + // hacks like + // free cameras or monster views can be done. + SetupFrame(player); + + // Clear buffers. + MyBSP.ClearClipSegs(); + seg_vars.ClearDrawSegs(); + vp_vars.ClearPlanes(); + MySegs.ClearClips(); + VIS.ClearSprites(); + + // Check for new console commands. + DOOM.gameNetworking.NetUpdate(); + + // The head node is the last node output. + MyBSP.RenderBSPNode(DOOM.levelLoader.numnodes - 1); + + // Check for new console commands. + DOOM.gameNetworking.NetUpdate(); + + // FIXME: "Warped floor" fixed, now to fix same-height visplane + // bleeding. + MyPlanes.DrawPlanes(); + + // Check for new console commands. + DOOM.gameNetworking.NetUpdate(); + + MyThings.DrawMasked(); + + colfunc.main = colfunc.base; + + // Check for new console commands. + DOOM.gameNetworking.NetUpdate(); + } +} \ No newline at end of file diff --git a/src/rr/SceneRenderer.java b/src/rr/SceneRenderer.java index b3b90c7..26302ee 100644 --- a/src/rr/SceneRenderer.java +++ b/src/rr/SceneRenderer.java @@ -95,4 +95,4 @@ public interface SceneRenderer { public ColVars getMaskedDCVars(); //public subsector_t PointInSubsector(int x, int y); -} +} \ No newline at end of file diff --git a/src/rr/SectorAction.java b/src/rr/SectorAction.java index 0f5783c..c45f319 100644 --- a/src/rr/SectorAction.java +++ b/src/rr/SectorAction.java @@ -2,11 +2,11 @@ import doom.thinker_t; -/** Used for special sector-based function for doors, ceilings +/** Used for special sector-based function for doors, ceilings * etc. that are treated as a thinker by the engine. The sector - * is part of the spec, so extending classes don't need to override + * is part of the spec, so extending classes don't need to override * it. Also, it extends thinker so futher extensions are thinkers too. - * + * */ public abstract class SectorAction extends thinker_t { @@ -16,4 +16,4 @@ public abstract class SectorAction extends thinker_t { * to their proper sector. */ public int sectorid; -} +} \ No newline at end of file diff --git a/src/rr/SegVars.java b/src/rr/SegVars.java index 3360bcd..3ac1158 100644 --- a/src/rr/SegVars.java +++ b/src/rr/SegVars.java @@ -18,9 +18,9 @@ public class SegVars { /** * R_ClearDrawSegs - * + * * The drawseg list is reset by pointing back at 0. - * + * */ public void ClearDrawSegs() { ds_p = 0; @@ -29,4 +29,4 @@ public void ClearDrawSegs() { public final void ResizeDrawsegs() { drawsegs = C2JUtils.resize(drawsegs[0], drawsegs, drawsegs.length * 2); } -} +} \ No newline at end of file diff --git a/src/rr/SimpleTextureManager.java b/src/rr/SimpleTextureManager.java index fffa7bb..b8a6255 100644 --- a/src/rr/SimpleTextureManager.java +++ b/src/rr/SimpleTextureManager.java @@ -29,9 +29,9 @@ /** An attempt to separate texture mapping functionality from * the rest of the rendering. Seems to work like a charm, and - * it makes it clearer what needs and what doesn't need to be + * it makes it clearer what needs and what doesn't need to be * exposed. - * + * * @author Maes * */ @@ -50,7 +50,7 @@ public class SimpleTextureManager implements TextureManager { // is stored in vertical runs of opaque pixels (posts). // A column is composed of zero or more posts, // a patch or sprite is composed of zero or more columns. - // + // protected int firstflat; protected int lastflat; protected int numflats; @@ -78,12 +78,12 @@ public class SimpleTextureManager implements TextureManager { /** Tells us which patch lump covers which column of which texture */ protected short[][] texturecolumnlump; - /** This is supposed to store indexes into a patch_t lump which point to the columns themselves + /** This is supposed to store indexes into a patch_t lump which point to the columns themselves * Instead, we're going to return indexes to columns inside a particular patch. * In the case of patches inside a non-cached multi-patch texture (e.g. those made of non-overlapping * patches), we're storing indexes INSIDE A PARTICULAR PATCH. E.g. for STARTAN1, which is made of two * 32-px wide patches, it should go something like 0, 1,2 ,3...31, 0,1,2,....31. - * + * * */ protected char[][] texturecolumnofs; @@ -180,7 +180,7 @@ public void InitTextures() throws IOException { int directory = 1; int texset = TEXTURE1; // Load the patch names from pnames.lmp. - //name[8] = 0; + //name[8] = 0; patchlookup = loadPatchNames("PNAMES"); // Load the map texture definitions from textures.lmp. @@ -201,7 +201,7 @@ public void InitTextures() throws IOException { numtextures = _numtextures[0] + _numtextures[1]; textures = new texture_t[numtextures]; - // MAES: Texture hashtable. + // MAES: Texture hashtable. TextureCache = new Hashtable<>(numtextures); texturecolumnlump = new short[numtextures][]; @@ -270,7 +270,7 @@ public void InitTextures() throws IOException { totalwidth += texture.width; } - // Precalculate whatever possible. + // Precalculate whatever possible. for (int i = 0; i < numtextures; i++) { GenerateLookup(i); } @@ -285,7 +285,7 @@ public void InitTextures() throws IOException { /** Assigns proper lumpnum to patch names. Check whether flats and patches of the same name coexist. * If yes, priority should go to patches. Otherwise, it's a "flats on walls" case. - * + * * @param pnames * @return * @throws IOException @@ -353,11 +353,11 @@ private patch_t retrievePatchSafe(int lump) { /** * R_GenerateLookup - * + * * Creates the lookup tables for a given texture (aka, where inside the texture cache * is the offset for particular column... I think. - * - * @throws IOException + * + * @throws IOException */ @Override public void GenerateLookup(int texnum) throws IOException { @@ -385,7 +385,7 @@ public void GenerateLookup(int texnum) throws IOException { collump = texturecolumnlump[texnum]; colofs = texturecolumnofs[texnum]; - /* Now count the number of columns that are covered by more + /* Now count the number of columns that are covered by more * than one patch. Fill in the lump / offset, so columns * with only a single patch are all done. */ @@ -413,7 +413,7 @@ public void GenerateLookup(int texnum) throws IOException { } for (; x < x2; x++) { /* Obviously, if a patch starts at x it does cover the x-th column - * of a texture, even if transparent. + * of a texture, even if transparent. */ patchcount[x]++; // Column "x" of composite texture "texnum" is covered by this patch. @@ -421,16 +421,16 @@ public void GenerateLookup(int texnum) throws IOException { /* This is supposed to be a raw pointer to the beginning of the column * data, as it appears inside the PATCH. - * + * * Instead, we can return the actual column index (x-x1) * As an example, the second patch of STARTAN1 (width 64) starts * at column 32. Therefore colofs should be something like * 0,1,2,...,31,0,1,....31, indicating that the 32-th column of * STARTAN1 is the 0-th column of the patch that is assigned to that column * (the latter can be looked up in texturecolumnlump[texnum]. - * + * * Any questions? - * + * */ colofs[x] = (char) (x - x1); // This implies that colofs[x] is 0 for a void column? @@ -468,12 +468,12 @@ public void GenerateLookup(int texnum) throws IOException { /** * R_GenerateComposite - * Using the texture definition, the composite texture is created + * Using the texture definition, the composite texture is created * from the patches and each column is cached. This method is "lazy" * aka it's only called when a cached/composite texture is needed. - * + * * @param texnum - * + * */ public void GenerateComposite(int texnum) { byte[][] block; @@ -544,24 +544,24 @@ public void GenerateComposite(int texnum) { /** * R_GenerateMaskedComposite - * + * * Generates a "masked composite texture": the result is a MASKED texture - * (with see-thru holes), but this time multiple patches can be used to + * (with see-thru holes), but this time multiple patches can be used to * assemble it, unlike standard Doom where this is not allowed. - * - * Called only if a request for a texture in the general purpose GetColumn - * method (used only for masked renders) turns out not to be pointing to a standard - * cached texture, nor to a disk lump(which is the standard Doom way of indicating a + * + * Called only if a request for a texture in the general purpose GetColumn + * method (used only for masked renders) turns out not to be pointing to a standard + * cached texture, nor to a disk lump(which is the standard Doom way of indicating a * composite single patch texture) but to a cached one which, however, is composite. - * + * * Confusing, huh? - * - * Normally, this results in a disaster, as the masked rendering methods + * + * Normally, this results in a disaster, as the masked rendering methods * don't expect cached/composite textures at all, and you get all sorts of nasty - * tutti frutti and medusa effects. Not anymore ;-) - * + * tutti frutti and medusa effects. Not anymore ;-) + * * @param texnum - * + * */ @Override public void GenerateMaskedComposite(int texnum) { @@ -626,29 +626,29 @@ public void GenerateMaskedComposite(int texnum) { } - // Patch drawn on cache, synthesize patch_t using it. + // Patch drawn on cache, synthesize patch_t using it. this.patchcomposite[texnum] = MultiPatchSynthesizer.synthesize(this.CheckTextureNameForNum(texnum), block, pixmap, texture.width, texture.height); } /** * R_DrawColumnInCache * Clip and draw a column from a patch into a cached post. - * + * * This means that columns are effectively "uncompressed" into cache, here, * and that composite textures are generally uncompressed...right? - * + * * Actually: "compressed" or "masked" textures are retrieved in the same way. * There are both "masked" and "unmasked" drawing methods. If a masked * column is passed to a method that expects a full, dense column...well, - * it will look fugly/overflow/crash. Vanilla Doom tolerated this, + * it will look fugly/overflow/crash. Vanilla Doom tolerated this, * we're probably going to have more problems. - * + * * @param patch Actually it's a single column to be drawn. May overdraw existing ones or void space. * @param cache the column cache itself. Actually it's the third level [texture][column]->data. * @param offset an offset inside the column cache (UNUSED) * @param originy vertical offset. Caution with masked stuff! * @param cacheheight the maximum height it's supposed to reach when drawing? - * + * */ public void DrawColumnInCache(column_t patch, byte[] cache, int offset, int originy, int cacheheight) { @@ -738,17 +738,17 @@ public void DrawColumnInCache(column_t patch, byte[] cache, * * Scans WADs for F_START/F_END lumps, and also any additional * F1_ and F2_ pairs. - * - * Correct behavior would be to detect F_START/F_END lumps, - * and skip any marker lumps sandwiched in between. If F_START and F_END are external, + * + * Correct behavior would be to detect F_START/F_END lumps, + * and skip any marker lumps sandwiched in between. If F_START and F_END are external, * use external override. - * - * Also, in the presence of external FF_START lumps, merge their contents + * + * Also, in the presence of external FF_START lumps, merge their contents * with those previously read. - * - * The method is COMPATIBLE with resource pre-coalesing, however it's not + * + * The method is COMPATIBLE with resource pre-coalesing, however it's not * trivial to change back to the naive code because of the "translationless" - * system used (all flats are assumed to lie in a linear space). This + * system used (all flats are assumed to lie in a linear space). This * speeds up lookups. * */ @@ -775,7 +775,7 @@ public final void InitFlats() { // priority, so its usefulness as an absolute end-index for regular flats // is dodgy at best. Gotta love the inconsistent mundo hacks! //int lastflatlump=W.GetNumForName(LUMPEND); - // + // int lump = firstflat; int seq = 0; String name; @@ -811,7 +811,7 @@ public final void InitFlats() { FlatNames.put(name, lump); // Remove old lump, but keep sequence. int oldseq = FlatCache.remove(removed); - // Put new lump number with old sequence. + // Put new lump number with old sequence. FlatCache.put(lump, oldseq); } else { // Add normally FlatCache.put(lump, seq); @@ -832,7 +832,7 @@ public final void InitFlats() { flatstorage = new int[numflats]; // MAJOR CHANGE: flattranslation stores absolute lump numbers. Adding - // firstlump is not necessary anymore. + // firstlump is not necessary anymore. // Now, we're pretty sure that we have a progressive value mapping. Enumeration stuff = FlatCache.keys(); while (stuff.hasMoreElements()) { @@ -845,7 +845,7 @@ public final void InitFlats() { for (int i = 0; i < numflats; i++) { flattranslation[i] = i; - // System.out.printf("Verification: flat[%d] is %s in lump %d\n",i,W.GetNameForNum(flattranslation[i]),flatstorage[i]); + // System.out.printf("Verification: flat[%d] is %s in lump %d\n",i,W.GetNameForNum(flattranslation[i]),flatstorage[i]); } } @@ -857,13 +857,13 @@ public final void InitFlats() { /** * R_PrecacheLevel * Preloads all relevant graphics for the level. - * + * * MAES: Everything except sprites. * A Texturemanager != sprite manager. * So the relevant functionality has been split into * PrecacheThinkers (found in common rendering code). - * - * + * + * */ int flatmemory; int texturememory; @@ -879,14 +879,14 @@ public void PrecacheLevel() throws IOException { // recache sprites. /* MAES: this code into PrecacheThinkers spritepresent = new boolean[numsprites]; - - + + for (th = P.thinkercap.next ; th != P.thinkercap ; th=th.next) { if (th.function==think_t.P_MobjThinker) spritepresent[((mobj_t )th).sprite.ordinal()] = true; } - + spritememory = 0; for (i=0 ; i { int offset; /** Its implicit position as indicated by the directory's ordering */ int entry; - /** Its MAXIMUM possible length, depending on what follows it. + /** Its MAXIMUM possible length, depending on what follows it. * Not trivial to compute without thoroughtly examining the entire lump */ int length; @@ -1200,9 +1200,9 @@ public byte[] getSafeFlat(int flatnum) { * Special version of GetColumn meant to be called concurrently by different * (MASKED) seg rendering threads, identfiex by index. This serves to avoid stomping * on mutual cached textures and causing crashes. - * + * * Returns column_t, so in theory it could be made data-agnostic. - * + * */ public column_t GetSmpColumn(int tex, int col, int id) { int lump, ofs; @@ -1247,7 +1247,7 @@ public column_t GetSmpColumn(int tex, int col, int id) { getMaskedComposite(tex).name, getMaskedComposite(tex).columns.length)); } - // Last resort. + // Last resort. smp_lastpatch[id] = getMaskedComposite(tex); smp_lasttex[id] = tex; smp_composite[id] = true; @@ -1267,13 +1267,13 @@ public column_t GetSmpColumn(int tex, int col, int id) { * R_GetColumn original version: returns raw pointers to byte-based column * data. Works for both masked and unmasked columns, but is not * tutti-frutti-safe. - * + * * Use GetCachedColumn instead, if rendering non-masked stuff, which is also * faster. - * + * * @throws IOException - * - * + * + * */ public byte[] GetColumn(int tex, int col) { int lump, ofs; @@ -1318,7 +1318,7 @@ public byte[] GetColumn(int tex, int col) { String.format("Composite patch %s %d", getMaskedComposite(tex).name, getMaskedComposite(tex).columns.length)); } - // Last resort. + // Last resort. lastpatch = getMaskedComposite(tex); lasttex = tex; composite = true; @@ -1329,17 +1329,17 @@ public byte[] GetColumn(int tex, int col) { /** * R_GetColumnStruct: returns actual pointers to columns. - * Agnostic of the underlying type. - * + * Agnostic of the underlying type. + * * Works for both masked and unmasked columns, but is not * tutti-frutti-safe. - * + * * Use GetCachedColumn instead, if rendering non-masked stuff, which is also * faster. - * + * * @throws IOException - * - * + * + * */ @Override public column_t GetColumnStruct(int tex, int col) { @@ -1384,7 +1384,7 @@ public column_t GetColumnStruct(int tex, int col) { String.format("Composite patch %s %d", getMaskedComposite(tex).name, getMaskedComposite(tex).columns.length)); } - // Last resort. + // Last resort. lastpatch = getMaskedComposite(tex); lasttex = tex; composite = true; @@ -1403,11 +1403,11 @@ public column_t GetColumnStruct(int tex, int col) { * R_GetColumn variation which is tutti-frutti proof. It only returns cached * columns, and even pre-caches single-patch textures intead of trashing the * WAD manager (should be faster, in theory). - * + * * Cannot be used for drawing masked textures, use classic GetColumn * instead. - * - * + * + * * @throws IOException */ @Override @@ -1442,7 +1442,7 @@ public void setSMPVars(int num_threads) { smp_composite = new boolean[num_threads];// = false; smp_lasttex = new int[num_threads];// = -1; smp_lastlump = new int[num_threads];// = -1; - smp_lastpatch = new patch_t[num_threads];// = null; + smp_lastpatch = new patch_t[num_threads];// = null; } -} +} \ No newline at end of file diff --git a/src/rr/SimpleThings.java b/src/rr/SimpleThings.java index 114ca5e..250c1de 100644 --- a/src/rr/SimpleThings.java +++ b/src/rr/SimpleThings.java @@ -5,7 +5,7 @@ /** * A very "simple" things class which just does serial rendering and uses all * the base methods from AbstractThings. - * + * * @author velktron * @param * @param @@ -20,4 +20,4 @@ public SimpleThings(VideoScale vs, SceneRenderer R) { public void completeColumn() { colfunc.invoke(); } -} +} \ No newline at end of file diff --git a/src/rr/SpriteManager.java b/src/rr/SpriteManager.java index 09ca996..75c7679 100644 --- a/src/rr/SpriteManager.java +++ b/src/rr/SpriteManager.java @@ -13,12 +13,12 @@ import static utils.GenericCopy.malloc; import w.lumpinfo_t; -/** An stand-alone sprite loader. Surprisingly, it is quite a - * separate concern from the renderer, and only needs to communicate +/** An stand-alone sprite loader. Surprisingly, it is quite a + * separate concern from the renderer, and only needs to communicate * occasionally through its getters with the rest of the stuff. - * + * * Helped lighten up the rendering code a lot, too. - * + * * @author Maes * */ @@ -248,9 +248,9 @@ public void InitSpriteLumps() { /** * R_InstallSpriteLump Local function for R_InitSprites. - * + * * Boom function, more suited to resource coalescing. - * + * */ public final void InstallSpriteLump(int lump, int frame, int rotation, boolean flipped) { @@ -287,7 +287,7 @@ public final void InstallSpriteLump(int lump, int frame, /** * R_InitSprites Called at program start. - * + * */ @Override public void InitSprites(String[] namelist) { @@ -352,7 +352,7 @@ public final int getSpriteTopOffset(int index) { // Some unused shit /* * R_InstallSpriteLump Local function for R_InitSprites. - * + * * Older function, closer to linuxdoom. Using Boom-derived one instead. */ /* @@ -378,7 +378,7 @@ protected final void InstallSpriteLump(int lump, int frame, // ?! if (rotation == 0) { - + // MAES: notice how comparisons are done with strict literals // (true and false) which are actually defined to be 0 and 1, // rather than assuming that true is "any nonzero value". This @@ -387,21 +387,21 @@ protected final void InstallSpriteLump(int lump, int frame, // "tri-state", and the comparison 0==false and // "anything else"==true was not good enough in this case. A // value of -1 doesn't yield either true or false here. - + // the lump should be used for all rotations if (sprtemp[frame].rotate == 0) { - + // MAES: Explanation: we stumbled upon this lump before, and // decided that this frame should have no more rotations, // hence we found an error and we bomb everything. - + I.Error("R_InitSprites: Sprite %s frame %c has multiple rot=0 lump", spritename, 'A' + frame); } // This should NEVER happen! if (sprtemp[frame].rotate == 1) { - + // MAES: This can only happen if we decided that a sprite's // frame was already decided to have rotations, but now we // stumble upon another occurence of "rotation 0". Or if you @@ -448,12 +448,12 @@ protected final void InstallSpriteLump(int lump, int frame, */ /* * OLDER, UNUSED VERSION - * + * * R_InitSpriteDefs Pass a null terminated list of sprite names (4 chars * exactly) to be used. Builds the sprite rotation matrixes to account * for horizontally flipped sprites. Will report an error if the lumps * are inconsistent. Only called at startup. - * + * * Sprite lump names are 4 characters for the actor, a letter for the * frame, and a number for the rotation. A sprite that is flippable will * have an additional letter/number appended. The rotation character can @@ -490,8 +490,8 @@ protected final void InstallSpriteLump(int lump, int frame, spritename = namelist[i]; // The original code actually set everything to "-1" - // here, including the "boolean" rotate value. The idea was - // to create a "tristate" of sorts, where -1 means a + // here, including the "boolean" rotate value. The idea was + // to create a "tristate" of sorts, where -1 means a // sprite of uncertain status. Goto InstallSpriteLumps // for more. for (int j = 0; j < sprtemp.length; j++) { @@ -590,4 +590,4 @@ protected final void InstallSpriteLump(int lump, int frame, } */ -} +} \ No newline at end of file diff --git a/src/rr/TextureManager.java b/src/rr/TextureManager.java index c19b217..6a90365 100644 --- a/src/rr/TextureManager.java +++ b/src/rr/TextureManager.java @@ -8,7 +8,7 @@ /** All texture, flat and sprite management operations should be handled * by an implementing class. As of now, the renderer does both, though it's * not really the most ideal. - * + * * @author Velktron * */ @@ -23,7 +23,7 @@ public interface TextureManager extends IGetColumn, IGetCachedColumn, I /**The "num" expected here is the internal flat number, * not the absolute lump number. So impement accordingly. - * + * * @param flatname * @return */ @@ -88,7 +88,7 @@ void GenerateLookup(int texnum) /** Return a "sanitized" patch. If data is insufficient, return * a default patch or attempt a partial draw. - * + * * @param patchnum * @return */ @@ -98,4 +98,4 @@ void GenerateLookup(int texnum) void setSMPVars(int nUMMASKEDTHREADS); -} +} \ No newline at end of file diff --git a/src/rr/UnifiedRenderer.java b/src/rr/UnifiedRenderer.java index d17f3af..63b533c 100644 --- a/src/rr/UnifiedRenderer.java +++ b/src/rr/UnifiedRenderer.java @@ -57,7 +57,7 @@ public static final class HiColor extends UnifiedRenderer { public HiColor(DoomMain DOOM) { super(DOOM); - // Init any video-output dependant stuff + // Init any video-output dependant stuff // Init light levels final int LIGHTLEVELS = colormaps.lightLevels(); final int MAXLIGHTSCALE = colormaps.maxLightScale(); @@ -153,7 +153,7 @@ protected void InitColormaps() throws IOException { colormaps.colormaps = DOOM.graphicSystem.getColorMap(); // MAES: blurry effect is hardcoded to this colormap. BLURRY_MAP = DOOM.graphicSystem.getBlurryTable(); - // colormaps = (byte *)( ((int)colormaps + 255)&~0xff); + // colormaps = (byte *)( ((int)colormaps + 255)&~0xff); } /** @@ -263,4 +263,4 @@ protected void R_InitDrawingFunctions() { } } -} +} \ No newline at end of file diff --git a/src/rr/ViewVars.java b/src/rr/ViewVars.java index c434088..d55614c 100644 --- a/src/rr/ViewVars.java +++ b/src/rr/ViewVars.java @@ -149,4 +149,4 @@ public final int getScaledViewHeight() { return height; } -} +} \ No newline at end of file diff --git a/src/rr/VisSprites.java b/src/rr/VisSprites.java index 573f923..3dfa054 100644 --- a/src/rr/VisSprites.java +++ b/src/rr/VisSprites.java @@ -21,7 +21,7 @@ /** Visualized sprite manager. Depends on: SpriteManager, DoomSystem, * Colormaps, Current View. - * + * * @author velktron * * @param @@ -95,7 +95,7 @@ public void AddSprites(sector_t sec) { /** * R_ProjectSprite Generates a vissprite for a thing if it might be visible. - * + * * @param thing */ protected final void ProjectSprite(mobj_t thing) { @@ -251,7 +251,7 @@ protected final void ProjectSprite(mobj_t thing) { * rendered but we have to copy data over it anyway. Would make more sense * to check for it specifically and avoiding copying data, which should be * more time consuming. Fixed by making this fully limit-removing. - * + * * @return */ protected final vissprite_t NewVisSprite() { @@ -319,4 +319,4 @@ public void resetLimits() { // Now, that was quite a haircut!. vissprites = tmp; } -} +} \ No newline at end of file diff --git a/src/rr/Visplanes.java b/src/rr/Visplanes.java index 38f6680..725d63f 100644 --- a/src/rr/Visplanes.java +++ b/src/rr/Visplanes.java @@ -14,7 +14,7 @@ import v.scale.VideoScale; /** Actual visplane data and methods are isolate here. - * This allows more encapsulation and some neat hacks like sharing + * This allows more encapsulation and some neat hacks like sharing * visplane data among parallel renderers, without duplicating them. */ public class Visplanes { @@ -111,14 +111,14 @@ public final void resizeVisplanes() { /** * R_FindPlane - * + * * Checks whether a visplane with the specified height, picnum and light * level exists among those already created. This looks like a half-assed * attempt at reusing already existing visplanes, rather than creating new * ones. The tricky part is understanding what happens if one DOESN'T exist. * Called only from within R_Subsector (so while we're still trasversing * stuff). - * + * * @param height * (fixed_t) * @param picnum @@ -169,7 +169,7 @@ public final int FindPlane(int height, int picnum, int lightlevel) { /** * R_ClearPlanes At begining of frame. - * + * */ public void ClearPlanes() { int angle; @@ -204,11 +204,11 @@ public void ClearPlanes() { /** * R_CheckPlane - * + * * Called from within StoreWallRange - * + * * Presumably decides if a visplane should be split or not? - * + * */ public int CheckPlane(int index, int start, int stop) { @@ -318,7 +318,7 @@ public int CheckPlane(int index, int start, int stop) { } /* - + /** * A hashtable used to retrieve planes with particular attributes faster * -hopefully-. The planes are still stored in the visplane array for @@ -389,4 +389,4 @@ protected final int FindPlane2(int height, int picnum, int lightlevel) { return checknum; } */ -} +} \ No newline at end of file diff --git a/src/rr/base_ratio_t.java b/src/rr/base_ratio_t.java index 00fc881..7ff8d2a 100644 --- a/src/rr/base_ratio_t.java +++ b/src/rr/base_ratio_t.java @@ -19,4 +19,4 @@ public base_ratio_t(int base_width, int base_height, int psprite_offset, public static final double RMUL = 1.6d / 1.333333d; -} +} \ No newline at end of file diff --git a/src/rr/cliprange_t.java b/src/rr/cliprange_t.java index b93ef08..c0aff9f 100644 --- a/src/rr/cliprange_t.java +++ b/src/rr/cliprange_t.java @@ -18,4 +18,4 @@ public void copy(cliprange_t from) { this.first = from.first; this.last = from.last; } -} +} \ No newline at end of file diff --git a/src/rr/column_t.java b/src/rr/column_t.java index a95a7dd..6f9325d 100644 --- a/src/rr/column_t.java +++ b/src/rr/column_t.java @@ -9,11 +9,11 @@ * typedef post_t column_t; * For the sake of efficiency, "column_t" will store raw data, however I added * some stuff to make my life easier. - * + * */ public class column_t implements CacheableDoomObject { - /** Static buffers used during I/O. + /** Static buffers used during I/O. * There's ABSO-FUCKING-LUTELY no reason to manipulate them externally!!! * I'M NOT KIDDING!!!11!! */ @@ -54,10 +54,10 @@ public void unpack(ByteBuffer buf) throws IOException { while ((topdelta = C2JUtils.toUnsignedByte(buf.get())) != 0xFF) { // From the wiki: - // A column's topdelta is compared to the previous column's topdelta - // (or to -1 if there is no previous column in this row). If the new + // A column's topdelta is compared to the previous column's topdelta + // (or to -1 if there is no previous column in this row). If the new // topdelta is lesser than the previous, it is interpreted as a tall - // patch and the two values are added together, the sum serving as the + // patch and the two values are added together, the sum serving as the // current column's actual offset. int tmp = topdelta; @@ -98,7 +98,7 @@ public void unpack(ByteBuffer buf) throws IOException { /** This -almost- completes reading, by filling in the header information * before the raw column data is read in. - * + * * @param skipped * @param colheight * @param postno @@ -151,4 +151,4 @@ public int getLength() { // // Revision 1.14 2011/06/08 16:11:13 velktron // IMPORTANT: postofs now skip delta,height and padding ENTIRELY (added +3). This eliminates the need to add +3 before accessing the data, saving some CPU cycles for each column. Of course, anything using column_t must take this into account. -// +// \ No newline at end of file diff --git a/src/rr/drawfuns/ColFuncs.java b/src/rr/drawfuns/ColFuncs.java index 483ce0b..2a39811 100644 --- a/src/rr/drawfuns/ColFuncs.java +++ b/src/rr/drawfuns/ColFuncs.java @@ -17,4 +17,4 @@ public class ColFuncs { public DoomColumnFunction player; public DoomColumnFunction sky; -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/ColVars.java b/src/rr/drawfuns/ColVars.java index 34afd65..2dc1e70 100644 --- a/src/rr/drawfuns/ColVars.java +++ b/src/rr/drawfuns/ColVars.java @@ -2,9 +2,9 @@ /** This is all the information needed to draw a particular column. Really. * So if we store all of this crap somewhere instead of drawing, we can do the - * drawing when it's more convenient, and since they are non-overlapping we can + * drawing when it's more convenient, and since they are non-overlapping we can * parallelize them. Any questions? - * + * */ public class ColVars { @@ -15,9 +15,9 @@ public class ColVars { public T dc_translation; public int viewheight; - /** Used by functions that accept transparency or other special + /** Used by functions that accept transparency or other special * remapping tables. - * + * */ public T tranmap; @@ -34,9 +34,9 @@ public class ColVars { /** * MAES: this was a typedef for unsigned bytes, called "lighttable_t". It - * makes more sense to make it generic and parametrize it to an array of - * primitives since it's performance-critical in the renderer. - * Now, whether this should be made bytes or shorts or chars or even ints + * makes more sense to make it generic and parametrize it to an array of + * primitives since it's performance-critical in the renderer. + * Now, whether this should be made bytes or shorts or chars or even ints * is debatable. */ public V dc_colormap; @@ -62,4 +62,4 @@ public void copyFrom(ColVars dcvars, int flags) { this.dc_flags = flags; } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/ColumnFunction.java b/src/rr/drawfuns/ColumnFunction.java index 88a3e17..96bb3bd 100644 --- a/src/rr/drawfuns/ColumnFunction.java +++ b/src/rr/drawfuns/ColumnFunction.java @@ -1,7 +1,7 @@ package rr.drawfuns; /** Either draws a column or a span - * + * * @author velktron * */ @@ -13,4 +13,4 @@ public interface ColumnFunction { /** A set of flags that help identifying the type of function */ public int getFlags(); -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/DcFlags.java b/src/rr/drawfuns/DcFlags.java index abf607a..161941e 100644 --- a/src/rr/drawfuns/DcFlags.java +++ b/src/rr/drawfuns/DcFlags.java @@ -1,8 +1,8 @@ package rr.drawfuns; -/** Flags used to mark column functions according to type, +/** Flags used to mark column functions according to type, * for quick type identification. - * + * * @author velktron * */ @@ -12,4 +12,4 @@ public final class DcFlags { public static final int TRANSLATED = 0x2; public static final int TRANSPARENT = 0x4; public static final int LOW_DETAIL = 0x8; -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/DoomColumnFunction.java b/src/rr/drawfuns/DoomColumnFunction.java index 26724fb..6f63753 100644 --- a/src/rr/drawfuns/DoomColumnFunction.java +++ b/src/rr/drawfuns/DoomColumnFunction.java @@ -3,8 +3,8 @@ import i.IDoomSystem; import v.tables.BlurryTable; -/** Prototype for - * +/** Prototype for + * * @author velktron * * @param @@ -53,10 +53,10 @@ protected final void performRangeCheck() { } /** - * + * * Use ylookup LUT to avoid multiply with ScreenWidth. * Use columnofs LUT for subwindows? - * + * * @return Framebuffer destination address. */ protected final int computeScreenDest() { @@ -82,4 +82,4 @@ public final int getFlags() { return this.flags; } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/DoomSpanFunction.java b/src/rr/drawfuns/DoomSpanFunction.java index c7bb483..2eca23c 100644 --- a/src/rr/drawfuns/DoomSpanFunction.java +++ b/src/rr/drawfuns/DoomSpanFunction.java @@ -37,4 +37,4 @@ public final void invoke(SpanVars dsvars) { invoke(); } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/R_DrawColumn.java b/src/rr/drawfuns/R_DrawColumn.java index fbbeac5..c45f7e7 100644 --- a/src/rr/drawfuns/R_DrawColumn.java +++ b/src/rr/drawfuns/R_DrawColumn.java @@ -6,9 +6,9 @@ /** * Adapted from Killough's Boom code. There are optimized as well as * low-detail versions of it. - * + * * @author admin - * + * */ /** * A column is a vertical slice/span from a wall texture that, given the @@ -68,7 +68,7 @@ public void invoke() { do { /* * Re-map color indices from wall texture column using a - * lighting/special effects LUT. + * lighting/special effects LUT. * Q: Where is "*dest"supposed to be pointing? * A: it's pointing inside screen[0] (set long before we came here) * dc_source is a pointer to a decompressed column's data. @@ -80,7 +80,7 @@ public void invoke() { /* * MAES: ok, so we have (from inside out): - * + * * frac is a fixed-point number representing a pointer inside a * column. It gets shifted to an integer, and AND-ed with 128 * (this causes vertical column tiling). @@ -90,4 +90,4 @@ public void invoke() { } while (count-- > 0); } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/R_DrawColumnBoom.java b/src/rr/drawfuns/R_DrawColumnBoom.java index de4a141..01f6ff9 100644 --- a/src/rr/drawfuns/R_DrawColumnBoom.java +++ b/src/rr/drawfuns/R_DrawColumnBoom.java @@ -9,7 +9,7 @@ /** * Adapted from Killough's Boom code. There are optimized as well as low-detail * versions of it. - * + * * @author admin */ public abstract class R_DrawColumnBoom @@ -378,4 +378,4 @@ public void invoke() { } } } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/R_DrawColumnBoomLow.java b/src/rr/drawfuns/R_DrawColumnBoomLow.java index d2163d4..02140a0 100644 --- a/src/rr/drawfuns/R_DrawColumnBoomLow.java +++ b/src/rr/drawfuns/R_DrawColumnBoomLow.java @@ -6,7 +6,7 @@ /** * Adapted from Killough's Boom code. Low-detail variation, no DC_SOURCE * optimization. - * + * * @author admin */ public abstract class R_DrawColumnBoomLow @@ -410,4 +410,4 @@ public void invoke() { } } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/R_DrawColumnBoomOpt.java b/src/rr/drawfuns/R_DrawColumnBoomOpt.java index 63c9458..9668d83 100644 --- a/src/rr/drawfuns/R_DrawColumnBoomOpt.java +++ b/src/rr/drawfuns/R_DrawColumnBoomOpt.java @@ -6,7 +6,7 @@ /** * Adapted from Killough's Boom code. Specially optimized version assuming that * dc_source_ofs is always 0. This eliminates it from expressions. - * + * * @author admin */ public abstract class R_DrawColumnBoomOpt @@ -351,4 +351,4 @@ public void invoke() { } } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/R_DrawColumnBoomOptLow.java b/src/rr/drawfuns/R_DrawColumnBoomOptLow.java index 3bc41d6..5b6d459 100644 --- a/src/rr/drawfuns/R_DrawColumnBoomOptLow.java +++ b/src/rr/drawfuns/R_DrawColumnBoomOptLow.java @@ -4,11 +4,11 @@ import static m.fixed_t.FRACBITS; /** - * Adapted from Killough's Boom code. Low-detail variation, with DC SOURCE + * Adapted from Killough's Boom code. Low-detail variation, with DC SOURCE * optimization. - * + * * @author admin - * + * */ public abstract class R_DrawColumnBoomOptLow extends DoomColumnFunction { @@ -346,4 +346,4 @@ public void invoke() { } } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/R_DrawColumnBoomSuperOpt.java b/src/rr/drawfuns/R_DrawColumnBoomSuperOpt.java index f3b855a..a1a3d04 100644 --- a/src/rr/drawfuns/R_DrawColumnBoomSuperOpt.java +++ b/src/rr/drawfuns/R_DrawColumnBoomSuperOpt.java @@ -7,9 +7,9 @@ * Adapted from Killough's Boom code. Specially super-optimized version assuming * that dc_source_ofs is always 0, AND that frac>>FRACBITS can be eliminated by * doing fracstep>>FRACBITS a-priori. Experimental/untested. - * + * * @author admin - * + * */ public final class R_DrawColumnBoomSuperOpt extends DoomColumnFunction { @@ -110,4 +110,4 @@ public void invoke() { } } } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/R_DrawColumnLow.java b/src/rr/drawfuns/R_DrawColumnLow.java index 565e297..cc6ca3d 100644 --- a/src/rr/drawfuns/R_DrawColumnLow.java +++ b/src/rr/drawfuns/R_DrawColumnLow.java @@ -53,4 +53,4 @@ public void invoke() { frac += fracstep; } while (count-- != 0); } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/R_DrawColumnUnrolled.java b/src/rr/drawfuns/R_DrawColumnUnrolled.java index 72432d3..c7964c6 100644 --- a/src/rr/drawfuns/R_DrawColumnUnrolled.java +++ b/src/rr/drawfuns/R_DrawColumnUnrolled.java @@ -4,10 +4,10 @@ /** * EI VITTU, this gives a clean 25% boost. Da fack... - * - * + * + * * @author admin - * + * */ public final class R_DrawColumnUnrolled extends DoomColumnFunction { @@ -83,4 +83,4 @@ public void invoke() { count--; } } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/R_DrawFuzzColumn.java b/src/rr/drawfuns/R_DrawFuzzColumn.java index e311d14..312aaf0 100644 --- a/src/rr/drawfuns/R_DrawFuzzColumn.java +++ b/src/rr/drawfuns/R_DrawFuzzColumn.java @@ -24,7 +24,7 @@ * fuzzMix was preserved, but moved to its own interface. * Implemented by BlurryTable if cfg option fuzz_mix is set * - Good Sign 2017/04/16 - * + * * Framebuffer postprocessing. Creates a fuzzy image by copying pixels from * adjacent ones to left and right. Used with an all black colormap, this * could create the SHADOW effect, i.e. spectres and invisible players. @@ -359,4 +359,4 @@ public void invoke() { } } } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/R_DrawFuzzColumnLow.java b/src/rr/drawfuns/R_DrawFuzzColumnLow.java index 4ff2df0..962c737 100644 --- a/src/rr/drawfuns/R_DrawFuzzColumnLow.java +++ b/src/rr/drawfuns/R_DrawFuzzColumnLow.java @@ -24,7 +24,7 @@ * fuzzMix was preserved, but moved to its own interface. * Implemented by BlurryTable if cfg option fuzz_mix is set * - Good Sign 2017/04/16 - * + * * Low detail version. Jesus. */ public abstract class R_DrawFuzzColumnLow extends DoomColumnFunction { @@ -387,4 +387,4 @@ public void invoke() { } } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/R_DrawSpan.java b/src/rr/drawfuns/R_DrawSpan.java index a32e020..f5b8bba 100644 --- a/src/rr/drawfuns/R_DrawSpan.java +++ b/src/rr/drawfuns/R_DrawSpan.java @@ -4,9 +4,9 @@ /** * Draws the actual span. - * + * * ds_frac, ds_yfrac, ds_x2, ds_x1, ds_xstep and ds_ystep must be set. - * + * */ public abstract class R_DrawSpan extends DoomSpanFunction { @@ -162,4 +162,4 @@ public void invoke() { } while (count-- > 0); } } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/R_DrawSpanLow.java b/src/rr/drawfuns/R_DrawSpanLow.java index 52a07be..f00c515 100644 --- a/src/rr/drawfuns/R_DrawSpanLow.java +++ b/src/rr/drawfuns/R_DrawSpanLow.java @@ -170,4 +170,4 @@ public void invoke() { } } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/R_DrawSpanUnrolled.java b/src/rr/drawfuns/R_DrawSpanUnrolled.java index 1303a51..baf5a34 100644 --- a/src/rr/drawfuns/R_DrawSpanUnrolled.java +++ b/src/rr/drawfuns/R_DrawSpanUnrolled.java @@ -6,7 +6,7 @@ * Drawspan loop unrolled by 4. However it has low rendering quality and bad * distortion. However it does actually does give a small speed boost (120 * -> 130 fps with a Mul of 3.0) - * + * */ public abstract class R_DrawSpanUnrolled extends DoomSpanFunction { @@ -248,4 +248,4 @@ public void invoke() { } } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/R_DrawSpanUnrolled2.java b/src/rr/drawfuns/R_DrawSpanUnrolled2.java index 89dd577..989ee50 100644 --- a/src/rr/drawfuns/R_DrawSpanUnrolled2.java +++ b/src/rr/drawfuns/R_DrawSpanUnrolled2.java @@ -90,4 +90,4 @@ public void invoke() { } } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/R_DrawTLColumn.java b/src/rr/drawfuns/R_DrawTLColumn.java index 71bd69c..1766011 100644 --- a/src/rr/drawfuns/R_DrawTLColumn.java +++ b/src/rr/drawfuns/R_DrawTLColumn.java @@ -119,4 +119,4 @@ public void invoke() { } } } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/R_DrawTranslatedColumn.java b/src/rr/drawfuns/R_DrawTranslatedColumn.java index adf4fd4..347eac6 100644 --- a/src/rr/drawfuns/R_DrawTranslatedColumn.java +++ b/src/rr/drawfuns/R_DrawTranslatedColumn.java @@ -312,4 +312,4 @@ public void invoke() { } } } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/R_DrawTranslatedColumnLow.java b/src/rr/drawfuns/R_DrawTranslatedColumnLow.java index 84db288..4056a3d 100644 --- a/src/rr/drawfuns/R_DrawTranslatedColumnLow.java +++ b/src/rr/drawfuns/R_DrawTranslatedColumnLow.java @@ -326,4 +326,4 @@ public void invoke() { } } -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/SpanFunction.java b/src/rr/drawfuns/SpanFunction.java index fbb5269..bd48f69 100644 --- a/src/rr/drawfuns/SpanFunction.java +++ b/src/rr/drawfuns/SpanFunction.java @@ -1,7 +1,7 @@ package rr.drawfuns; /** Either draws a column or a span - * + * * @author velktron * */ @@ -11,4 +11,4 @@ public interface SpanFunction { public void invoke(SpanVars dsvars); -} +} \ No newline at end of file diff --git a/src/rr/drawfuns/SpanVars.java b/src/rr/drawfuns/SpanVars.java index 82ee33e..6e44299 100644 --- a/src/rr/drawfuns/SpanVars.java +++ b/src/rr/drawfuns/SpanVars.java @@ -15,4 +15,4 @@ public class SpanVars { public int ds_ystep; public DoomSpanFunction spanfunc; -} +} \ No newline at end of file diff --git a/src/rr/drawseg_t.java b/src/rr/drawseg_t.java index 9109b46..93286f4 100644 --- a/src/rr/drawseg_t.java +++ b/src/rr/drawseg_t.java @@ -25,7 +25,7 @@ public drawseg_t() { /** do not clip sprites below this (fixed_t) */ public int tsilheight; - /** Indexes to lists for sprite clipping, + /** Indexes to lists for sprite clipping, all three adjusted so [x1] is first value. */ private int psprtopclip, psprbottomclip, pmaskedtexturecol; @@ -108,4 +108,4 @@ public boolean nullMaskedTextureCol() { return this.maskedtexturecol == null; } -} +} \ No newline at end of file diff --git a/src/rr/flat_t.java b/src/rr/flat_t.java index e6a7f5b..1b5dc93 100644 --- a/src/rr/flat_t.java +++ b/src/rr/flat_t.java @@ -28,4 +28,4 @@ public void unpack(ByteBuffer buf) } -} +} \ No newline at end of file diff --git a/src/rr/line_t.java b/src/rr/line_t.java index ff09cd0..915805e 100644 --- a/src/rr/line_t.java +++ b/src/rr/line_t.java @@ -107,7 +107,7 @@ public void assignVertexValues() { /** * P_PointOnLineSide - * + * * @param x * fixed_t * @param y @@ -150,7 +150,7 @@ public boolean PointOnLineSide(int x, int y) { * P_BoxOnLineSide Considers the line to be infinite Returns side 0 or 1, -1 * if box crosses the line. Doubles as a convenient check for whether a * bounding box crosses a line at all - * + * * @param tmbox * fixed_t[] */ @@ -204,7 +204,7 @@ public int BoxOnLineSide(int[] tmbox) { * Variant of P_BoxOnLineSide. Uses inclusive checks, so that even lines on * the border of a box will be considered crossing. This is more useful for * building blockmaps. - * + * * @param tmbox * fixed_t[] */ @@ -354,4 +354,4 @@ public void reset() { /** Set if already seen, thus drawn in automap. */ public static final int ML_MAPPED = 256; -} +} \ No newline at end of file diff --git a/src/rr/mappatch_t.java b/src/rr/mappatch_t.java index c70edfb..d7a9496 100644 --- a/src/rr/mappatch_t.java +++ b/src/rr/mappatch_t.java @@ -11,7 +11,7 @@ * with patches being lumps stored in the WAD. * The lumps are referenced by number, and patched * into the rectangular texture space using origin - * and possibly other attributes. + * and possibly other attributes. */ public class mappatch_t implements CacheableDoomObject { @@ -36,4 +36,4 @@ public static final int size() { return 10; } -}; +}; \ No newline at end of file diff --git a/src/rr/maptexture_t.java b/src/rr/maptexture_t.java index d42a891..84de86e 100644 --- a/src/rr/maptexture_t.java +++ b/src/rr/maptexture_t.java @@ -11,7 +11,7 @@ * A DOOM wall texture is a list of patches which are to be combined in a predefined order. * This is the ON-DISK structure, to be read from the TEXTURES1 and TEXTURES2 lumps. * In memory, this becomes texture_t. - * + * * @author MAES * */ @@ -20,7 +20,7 @@ public class maptexture_t implements CacheableDoomObject { public String name; public boolean masked; public short width; // was signed byte - public short height; // was + public short height; // was //void**t columndirectory; // OBSOLETE (yeah, but we must read a dummy integer here) public short patchcount; public mappatch_t[] patches; @@ -42,4 +42,4 @@ public void unpack(ByteBuffer buf) throws IOException { patches = malloc(mappatch_t::new, mappatch_t[]::new, patchcount); DoomBuffer.readObjectArray(buf, patches, patchcount); } -}; +}; \ No newline at end of file diff --git a/src/rr/maskdraw_t.java b/src/rr/maskdraw_t.java index 835e63d..650995a 100644 --- a/src/rr/maskdraw_t.java +++ b/src/rr/maskdraw_t.java @@ -6,8 +6,8 @@ * rendering subsystem, but discarded because of simplifications. * In theory it could be brought back one day if parallel sprite * drawing comes back.. just a thought ;-) - * - * + * + * * @author Maes * */ @@ -20,4 +20,4 @@ public class maskdraw_t { public int topclip; public int bottomclip; -} +} \ No newline at end of file diff --git a/src/rr/node_t.java b/src/rr/node_t.java index 3217a63..dfee24d 100644 --- a/src/rr/node_t.java +++ b/src/rr/node_t.java @@ -203,4 +203,4 @@ public void reset() { memset(children, 0, children.length); } -} +} \ No newline at end of file diff --git a/src/rr/pQuickSprite.java b/src/rr/pQuickSprite.java index 9f9cedf..c6fe151 100644 --- a/src/rr/pQuickSprite.java +++ b/src/rr/pQuickSprite.java @@ -73,4 +73,4 @@ public static final void sort(vissprite_t[] c) { } } } -} +} \ No newline at end of file diff --git a/src/rr/parallel/AbstractParallelRenderer.java b/src/rr/parallel/AbstractParallelRenderer.java index 387d1ac..5d61727 100644 --- a/src/rr/parallel/AbstractParallelRenderer.java +++ b/src/rr/parallel/AbstractParallelRenderer.java @@ -21,7 +21,7 @@ /** * Features and functionality which is common among parallel renderers - * + * * @author velktron */ public abstract class AbstractParallelRenderer extends RendererState implements RWI.Init { @@ -198,7 +198,7 @@ protected ParallelPlanes(DoomMain DOOM, SceneRenderer R) { * R_DrawPlanes At the end of each frame. This also means that visplanes * must have been set BEFORE we called this function. Therefore, look * for errors behind. - * + * * @throws IOException */ public void DrawPlanes() { @@ -464,7 +464,7 @@ protected ParallelPlanes2(DoomMain DOOM, SceneRenderer R) { * R_DrawPlanes At the end of each frame. This also means that visplanes * must have been set BEFORE we called this function. Therefore, look * for errors behind. - * + * * @throws IOException */ @Override @@ -554,4 +554,4 @@ public RenderWallExecutor[] InitRWIExecutors(int num, ColVars[] RWI) } RWI.Get RWIs; -} +} \ No newline at end of file diff --git a/src/rr/parallel/IGetSmpColumn.java b/src/rr/parallel/IGetSmpColumn.java index f11839d..1a8a217 100644 --- a/src/rr/parallel/IGetSmpColumn.java +++ b/src/rr/parallel/IGetSmpColumn.java @@ -4,7 +4,7 @@ /** An interface used to ease the use of the GetCachedColumn by part * of parallelized renderers. - * + * * @author Maes * */ @@ -12,10 +12,10 @@ * Special version of GetColumn meant to be called concurrently by different * seg rendering threads, identfiex by index. This serves to avoid stomping * on mutual cached textures and causing crashes. - * + * */ public interface IGetSmpColumn { column_t GetSmpColumn(int tex, int col, int id); -} +} \ No newline at end of file diff --git a/src/rr/parallel/MaskedWorker.java b/src/rr/parallel/MaskedWorker.java index 3d10217..10027b8 100644 --- a/src/rr/parallel/MaskedWorker.java +++ b/src/rr/parallel/MaskedWorker.java @@ -35,10 +35,10 @@ import v.scale.VideoScale; import v.tables.BlurryTable; -/** A "Masked Worker" draws sprites in a split-screen strategy. Used by +/** A "Masked Worker" draws sprites in a split-screen strategy. Used by * ParallelRenderer2. Each Masked Worker is essentially a complete Things * drawer, and reuses much of the serial methods. - * + * * @author velktron * * @param @@ -148,11 +148,11 @@ public TrueColor(VideoScale vs, SceneRenderer R, int id, /** * R_DrawVisSprite mfloorclip and mceilingclip should also be set. - * + * * Sprites are actually drawn here. Obviously overrides the serial * method, and only draws a portion of the sprite. - * - * + * + * */ @Override protected final void DrawVisSprite(vissprite_t vis) { @@ -218,7 +218,7 @@ protected final void DrawVisSprite(vissprite_t vis) { /** * R_RenderMaskedSegRange - * + * * @param ds * @param x1 * @param x2 @@ -334,12 +334,12 @@ protected final void RenderMaskedSegRange(drawseg_t ds, int x1, int x2) { /** * R_DrawPSprite - * + * * Draws a "player sprite" with slighly different rules than normal * sprites. This is actually a PITA, at best :-/ - * + * * Also different than normal implementation. - * + * */ @Override protected final void DrawPSprite(pspdef_t psp) { @@ -445,14 +445,14 @@ protected final void DrawPSprite(pspdef_t psp) { /** * R_DrawMasked - * + * * Sorts and draws vissprites (room for optimization in sorting func.) * Draws masked textures. Draws player weapons and overlays (psprites). - * + * * Sorting function can be swapped for almost anything, and it will work * better, in-place and be simpler to draw, too. - * - * + * + * */ @Override public void run() { @@ -460,7 +460,7 @@ public void run() { int ds; drawseg_t dss; - // Sprites should already be sorted for distance + // Sprites should already be sorted for distance colfunc = colfuncs.masked; // Sprites use fully-masked capable // function. @@ -505,4 +505,4 @@ public void run() { // TODO Auto-generated catch block } -} +} \ No newline at end of file diff --git a/src/rr/parallel/ParallelRenderer.java b/src/rr/parallel/ParallelRenderer.java index 0e94839..ce8eeca 100644 --- a/src/rr/parallel/ParallelRenderer.java +++ b/src/rr/parallel/ParallelRenderer.java @@ -30,7 +30,7 @@ * "Render Wall Instructions", and then rendered once they are all in place and * the can be parallelized between rendering threads. Rendering of sprites is * NOT parallelized yet (and probably not worth it, at this point). - * + * * @author admin */ public abstract class ParallelRenderer extends AbstractParallelRenderer { @@ -53,7 +53,7 @@ public ParallelRenderer(DoomMain DM, int wallthread, /** * Default constructor, 1 seg, 1 span and two masked threads. - * + * * @param DM */ public ParallelRenderer(DoomMain DM) { @@ -65,7 +65,7 @@ public ParallelRenderer(DoomMain DM) { * particular player object. In practice, it could render the view of any * mobj too, provided you adapt the SetupFrame method (where the viewing * variables are set). - * + * * @throws IOException */ public void RenderPlayerView(player_t player) { @@ -149,7 +149,7 @@ protected void InitColormaps() throws IOException { // MAES: blurry effect is hardcoded to this colormap. BLURRY_MAP = DOOM.graphicSystem.getBlurryTable(); - // colormaps = (byte *)( ((int)colormaps + 255)&~0xff); + // colormaps = (byte *)( ((int)colormaps + 255)&~0xff); } protected void R_InitDrawingFunctions() { @@ -464,4 +464,4 @@ public RenderWallExecutor[] InitRWIExecutors( } -} +} \ No newline at end of file diff --git a/src/rr/parallel/ParallelRenderer2.java b/src/rr/parallel/ParallelRenderer2.java index 9f51901..eccfa0b 100644 --- a/src/rr/parallel/ParallelRenderer2.java +++ b/src/rr/parallel/ParallelRenderer2.java @@ -27,7 +27,7 @@ * column-based. It does function, but is broken and has unsolved data dependencies. * It's therefore not used in official releases, and I chose to deprecate it. * If you still want to develop it, be my guest. - * + * * @author velktron * */ @@ -76,11 +76,11 @@ protected void InitParallelStuff() { ///////////////////////// The actual rendering calls /////////////////////// /** * R_RenderView - * + * * As you can guess, this renders the player view of a particular player object. * In practice, it could render the view of any mobj too, provided you adapt the * SetupFrame method (where the viewing variables are set). - * + * */ @Override @SuppressWarnings("unchecked") @@ -99,7 +99,7 @@ public void RenderPlayerView(player_t player) { } }*/ - // Clear buffers. + // Clear buffers. MyBSP.ClearClipSegs(); seg_vars.ClearDrawSegs(); vp_vars.ClearPlanes(); @@ -232,7 +232,7 @@ protected void InitColormaps() throws IOException { colormaps.colormaps = DOOM.graphicSystem.getColorMap(); // MAES: blurry effect is hardcoded to this colormap. BLURRY_MAP = DOOM.graphicSystem.getBlurryTable(); - // colormaps = (byte *)( ((int)colormaps + 255)&~0xff); + // colormaps = (byte *)( ((int)colormaps + 255)&~0xff); } @Override @@ -472,4 +472,4 @@ protected void R_InitDrawingFunctions() { super.R_InitDrawingFunctions(); } } -} +} \ No newline at end of file diff --git a/src/rr/parallel/ParallelThings.java b/src/rr/parallel/ParallelThings.java index 53d6357..261e208 100644 --- a/src/rr/parallel/ParallelThings.java +++ b/src/rr/parallel/ParallelThings.java @@ -20,7 +20,7 @@ * Parallel Things drawing class, column based, using RMI pipeline. * For N threads, each thread only draws those columns of sprites that * are in its own 1/N portion of the screen. - * + * * Overrides only the terminal drawing methods from things, using a * mechanism very similar to column-based wall threading. It's not very * efficient, since some of the really heavy parts (such as visibility @@ -35,7 +35,7 @@ * fps "Dummy" things renderer without ANY calculations: 90 fps. This means * that even a complete parallelization will likely have a quite limited * impact. - * + * * @author velktron */ public abstract class ParallelThings extends AbstractThings { @@ -191,4 +191,4 @@ protected void InitRMISubsystem(int[] columnofs, int[] ylookup, int[] screen, Cy } } -} +} \ No newline at end of file diff --git a/src/rr/parallel/ParallelThings2.java b/src/rr/parallel/ParallelThings2.java index dd83fe7..7bba83d 100644 --- a/src/rr/parallel/ParallelThings2.java +++ b/src/rr/parallel/ParallelThings2.java @@ -15,18 +15,18 @@ /** Alternate parallel sprite renderer using a split-screen strategy. * For N threads, each thread gets to render only the sprites that are entirely * in its own 1/Nth portion of the screen. - * + * * Sprites that span more than one section, are drawn partially. Each thread - * only has to worry with the priority of its own sprites. Similar to the + * only has to worry with the priority of its own sprites. Similar to the * split-seg parallel drawer. - * + * * Uses the "masked workers" subsystem, there is no column pipeline: workers * "tap" directly in the sprite sorted table and act accordingly (draw entirely, * draw nothing, draw partially). - * + * * It uses masked workers to perform the actual work, each of which is a complete - * Thing Drawer. - * + * Thing Drawer. + * * @author velktron * */ @@ -98,4 +98,4 @@ public void cacheSpriteManager(ISpriteManager SM) { } -} +} \ No newline at end of file diff --git a/src/rr/parallel/RWI.java b/src/rr/parallel/RWI.java index faf320f..18177bf 100644 --- a/src/rr/parallel/RWI.java +++ b/src/rr/parallel/RWI.java @@ -15,4 +15,4 @@ public interface Get { void setExecutors(RenderWallExecutor[] RWIExec); } -} +} \ No newline at end of file diff --git a/src/rr/parallel/RenderMaskedExecutor.java b/src/rr/parallel/RenderMaskedExecutor.java index 77e1d92..18b5686 100644 --- a/src/rr/parallel/RenderMaskedExecutor.java +++ b/src/rr/parallel/RenderMaskedExecutor.java @@ -21,7 +21,7 @@ /** * This is what actual executes the RenderWallInstruction. Essentially it's a * self-contained column rendering function. - * + * * @author velktron */ public abstract class RenderMaskedExecutor @@ -196,4 +196,4 @@ public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs, } -} +} \ No newline at end of file diff --git a/src/rr/parallel/RenderSegExecutor.java b/src/rr/parallel/RenderSegExecutor.java index 62b916a..3c05972 100644 --- a/src/rr/parallel/RenderSegExecutor.java +++ b/src/rr/parallel/RenderSegExecutor.java @@ -20,26 +20,26 @@ import v.tables.LightsAndColors; /** This is what actual executes the RenderSegInstructions. - * * + * * * Each thread actually operates on a FIXED PORTION OF THE SCREEN * (e.g. half-width, third-width etc.) and only renders the portions * of the RenderSegInstructions that are completely contained * within its own screen area. For this reason, all threads - * check out all RenderSegInstructions of the list, and render any + * check out all RenderSegInstructions of the list, and render any * and all portions that are within their responsability domain, so * to speak. - * - * FIXME there's a complex data dependency with ceilingclip/floorclip + * + * FIXME there's a complex data dependency with ceilingclip/floorclip * I was not quite able to fix yet. Practically, in the serial renderer, * calls to RenderSegLoop are done in a correct, non-overlapping order, * and certain parts are drawn before others in order to set current * floor/ceiling markers and visibility e.g. think of a wall visible * through windows. - * - * FIXME 7/6/2011 Data dependencies and per-thread clipping are now - * fixed, however there is still visible "jitter" or "noise" on some + * + * FIXME 7/6/2011 Data dependencies and per-thread clipping are now + * fixed, however there is still visible "jitter" or "noise" on some * of the walls, probably related to column offsets. - * + * * @author velktron * */ @@ -113,11 +113,11 @@ protected final void ProcessRSI(RenderSegInstruction rsi, int startx, int end int topstep = rsi.topstep; int texturecolumn = 0; // fixed_t final int bias; - // Well is entirely contained in our screen zone + // Well is entirely contained in our screen zone // (or the very least it starts in it). if (contained) { bias = 0; - } // We are continuing a wall that started in another + } // We are continuing a wall that started in another // screen zone. else { bias = (startx - rsi.rw_x); @@ -163,10 +163,10 @@ protected final void ProcessRSI(RenderSegInstruction rsi, int startx, int end // FIXME: We are accessing finetangent here, the code seems pretty confident // in that angle won't exceed 4K no matter what. But xtoviewangle // alone can yield 8K when shifted. - // This usually only overflows if we idclip and look at certain directions - // (probably angles get fucked up), however it seems rare enough to just + // This usually only overflows if we idclip and look at certain directions + // (probably angles get fucked up), however it seems rare enough to just // "swallow" the exception. You can eliminate it by anding with 0x1FFF - // if you're so inclined. + // if you're so inclined. texturecolumn = rsi.rw_offset - FixedMul(finetangent[angle], rsi.rw_distance); texturecolumn >>= FRACBITS; // calculate lighting @@ -259,7 +259,7 @@ protected final void ProcessRSI(RenderSegInstruction rsi, int startx, int end rw_scale += rw_scalestep; topfrac += topstep; bottomfrac += bottomstep; - } // end-rw + } // end-rw } // end-block } @@ -280,8 +280,8 @@ public void setScreenRange(int rwstart, int rwend) { /** How many instructions TOTAL are there to wade through. * Not all will be executed on one thread, except in some rare - * circumstances. - * + * circumstances. + * * @param rsiend */ public void setRSIEnd(int rsiend) { @@ -441,4 +441,4 @@ public Indexed(DoomMain DOOM, int id, // // Revision 1.3 2011/06/07 00:11:11 velktron // Fixed alternate parallel renderer (seg based). No longer deprecated. -// +// \ No newline at end of file diff --git a/src/rr/parallel/RenderSegInstruction.java b/src/rr/parallel/RenderSegInstruction.java index f12034c..3115054 100644 --- a/src/rr/parallel/RenderSegInstruction.java +++ b/src/rr/parallel/RenderSegInstruction.java @@ -3,7 +3,7 @@ /** This is all the information needed to draw a particular SEG. * It's quite a lot, actually, but much better than in testing * versions. - * + * */ public class RenderSegInstruction { @@ -20,4 +20,4 @@ public class RenderSegInstruction { V[] walllights; public int centery; -} +} \ No newline at end of file diff --git a/src/rr/parallel/RenderWallExecutor.java b/src/rr/parallel/RenderWallExecutor.java index cd89627..207c766 100644 --- a/src/rr/parallel/RenderWallExecutor.java +++ b/src/rr/parallel/RenderWallExecutor.java @@ -14,7 +14,7 @@ /** * This is what actual executes the RenderWallInstruction. Essentially it's a * self-contained column rendering function. - * + * * @author admin */ public class RenderWallExecutor @@ -137,4 +137,4 @@ public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs, } -} +} \ No newline at end of file diff --git a/src/rr/parallel/VisplaneWorker.java b/src/rr/parallel/VisplaneWorker.java index af66de0..08a14da 100644 --- a/src/rr/parallel/VisplaneWorker.java +++ b/src/rr/parallel/VisplaneWorker.java @@ -25,10 +25,10 @@ /** Visplane worker which shares work in an equal-visplane number strategy * with other workers. Might be unbalanced if one worker gets too large - * visplanes and others get smaller ones. Balancing strategy is applied in + * visplanes and others get smaller ones. Balancing strategy is applied in * run(), otherwise it's practically similar to a PlaneDrwer. - * - * + * + * * @author velktron * */ @@ -185,4 +185,4 @@ public void run() { } -} +} \ No newline at end of file diff --git a/src/rr/parallel/VisplaneWorker2.java b/src/rr/parallel/VisplaneWorker2.java index cf7b617..b7639e9 100644 --- a/src/rr/parallel/VisplaneWorker2.java +++ b/src/rr/parallel/VisplaneWorker2.java @@ -24,11 +24,11 @@ import v.graphics.Palettes; /** Visplane worker which shares work in an equal screen-portions strategy. - * + * * More balanced, but requires careful synchronization to avoid overdrawing and * stomping. - * - * + * + * * @author vepitrop. * * TODO: fix crashes @@ -132,7 +132,7 @@ public void run() { continue; } - // Reject non-visible + // Reject non-visible if (pln.minx > pln.maxx) { continue; } @@ -252,20 +252,20 @@ public void setDetail(int detailshift) { /** * R_MakeSpans - * + * * Called only by DrawPlanes. * If you wondered where the actual boundaries for the visplane * flood-fill are laid out, this is it. - * - * The system of coords seems to be defining a sort of cone. - * - * + * + * The system of coords seems to be defining a sort of cone. + * + * * @param x Horizontal position * @param t1 Top-left y coord? * @param b1 Bottom-left y coord? * @param t2 Top-right y coord ? * @param b2 Bottom-right y coord ? - * + * */ @Override protected final void MakeSpans(int x, int t1, int b1, int t2, int b2) { @@ -292,9 +292,9 @@ protected final void MakeSpans(int x, int t1, int b1, int t2, int b2) { * R_MapPlane * * Called only by R_MakeSpans. - * + * * This is where the actual span drawing function is called. - * + * * Uses global vars: * planeheight * ds_source -> flat data has already been set. @@ -318,7 +318,7 @@ protected final void MakeSpans(int x, int t1, int b1, int t2, int b2) { int distance; int length; int index; - + if (RANGECHECK){ if (x2 < x1 || x1<0 @@ -342,7 +342,7 @@ protected final void MakeSpans(int x, int t1, int b1, int t2, int b2) { vpw_dsvars.ds_xstep = cachedxstep[y]; vpw_dsvars.ds_ystep = cachedystep[y]; } - + length = FixedMul (distance,distscale[x1]); angle = (int)(((view.angle +xtoviewangle[x1])&BITS32)>>>ANGLETOFINESHIFT); vpw_dsvars.ds_xfrac = view.x + FixedMul(finecosine[angle], length); @@ -353,13 +353,13 @@ protected final void MakeSpans(int x, int t1, int b1, int t2, int b2) { else { index = distance >>> LIGHTZSHIFT; - + if (index >= MAXLIGHTZ ) index = MAXLIGHTZ-1; vpw_dsvars.ds_colormap = vpw_planezlight[index]; } - + vpw_dsvars.ds_y = y; vpw_dsvars.ds_x1 = x1; vpw_dsvars.ds_x2 = x2; @@ -368,9 +368,9 @@ protected final void MakeSpans(int x, int t1, int b1, int t2, int b2) { if (view.detailshift==0) vpw_spanfunc.invoke(); else - vpw_spanfunclow.invoke(); + vpw_spanfunclow.invoke(); } */ // Private to each thread. CyclicBarrier barrier; -} +} \ No newline at end of file diff --git a/src/rr/patch_t.java b/src/rr/patch_t.java index e00f3f5..a87acca 100644 --- a/src/rr/patch_t.java +++ b/src/rr/patch_t.java @@ -21,7 +21,7 @@ public class patch_t implements /*IReadableDoomObject,*/ CacheableDoomObject { public short leftoffset; /** pixels below the origin */ public short topoffset; - /** This used to be an implicit array pointing to raw posts of data. + /** This used to be an implicit array pointing to raw posts of data. * TODO: get rid of it? It's never used * only [width] used the [0] is &columnofs[width] */ public int[] columnofs; @@ -33,7 +33,7 @@ public class patch_t implements /*IReadableDoomObject,*/ CacheableDoomObject { /** Synthesizing constructor. * You have to provide the columns yourself, a-posteriori. - * + * * @param name * @param width * @param height @@ -64,7 +64,7 @@ public void read(DoomFile f) throws IOException{ this.columnofs=new int[this.width]; this.columns=new column_t[this.width]; C2JUtils.initArrayOfObjects( this.columns, column_t.class); - + // Read the column offsets. f.readIntArray(this.columnofs, this.columnofs.length, ByteOrder.LITTLE_ENDIAN); for (int i=0;i floor) { - floor = other.floorheight; - } - } - return floor; - } - - /** - * P_FindNextHighestFloor FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS - * Note: this should be doable w/o a fixed array. - * - * @param sec - * @param currentheight - * @return fixed - */ - public int FindNextHighestFloor(int currentheight) { - int i; - int h; - int min; - line_t check; - sector_t other; - int height = currentheight; - - int heightlist[] = new int[MAX_ADJOINING_SECTORS]; - - for (i = 0, h = 0; i < this.linecount; i++) { - check = this.lines[i]; - other = check.getNextSector(this); - - if (other == null) { - continue; - } - - if (other.floorheight > height) { - heightlist[h++] = other.floorheight; - } - - // Check for overflow. Exit. - if (h >= MAX_ADJOINING_SECTORS) { - LOGGER.log(Level.WARNING, - "Sector with more than 20 adjoining sectors"); - break; - } - } - - // Find lowest height in list - if (h == 0) { - return currentheight; - } - - min = heightlist[0]; - - // Range checking? - for (i = 1; i < h; i++) { - if (heightlist[i] < min) { - min = heightlist[i]; - } - } - - return min; - } - - // - // FIND LOWEST CEILING IN THE SURROUNDING SECTORS - // - @SourceCode.Exact - @P_Spec.C(P_FindLowestCeilingSurrounding) - public @fixed_t - int FindLowestCeilingSurrounding() { - line_t check; - sector_t other; - int height = MAXINT; - - for (int i = 0; i < this.linecount; i++) { - check = this.lines[i]; - getNextSector: - { - other = check.getNextSector(this); - } - - if (other == null) { - continue; - } - - if (other.ceilingheight < height) { - height = other.ceilingheight; - } - } - return height; - } - - // - // FIND HIGHEST CEILING IN THE SURROUNDING SECTORS - // - public int FindHighestCeilingSurrounding() { - int i; - line_t check; - sector_t other; - int height = 0; - - for (i = 0; i < this.linecount; i++) { - check = this.lines[i]; - other = check.getNextSector(this); - - if (other == null) { - continue; - } - - if (other.ceilingheight > height) { - height = other.ceilingheight; - } - } - return height; - } - - @Override - public void read(DataInputStream f) - throws IOException { - - // ACHTUNG: the only situation where we'd - // like to read memory-format sector_t's is from - // savegames, and in vanilla savegames, not all info - // is saved (or read) from disk. - this.floorheight = DoomIO.readLEShort(f) << FRACBITS; - this.ceilingheight = DoomIO.readLEShort(f) << FRACBITS; - // MAES: it may be necessary to apply a hack in order to - // read vanilla savegames. - this.floorpic = DoomIO.readLEShort(f); - this.ceilingpic = DoomIO.readLEShort(f); - // f.skipBytes(4); - this.lightlevel = DoomIO.readLEShort(f); - this.special = DoomIO.readLEShort(f); // needed? - this.tag = DoomIO.readLEShort(f); // needed? - } - - @Override - public void pack(ByteBuffer b) { - - b.putShort((short) (floorheight >> FRACBITS)); - b.putShort((short) (ceilingheight >> FRACBITS)); - // MAES: it may be necessary to apply a hack in order to - // read vanilla savegames. - b.putShort(floorpic); - b.putShort(ceilingpic); - // f.skipBytes(4); - b.putShort(lightlevel); - b.putShort(special); - b.putShort(tag); - } - - @Override - public void reset() { - floorheight = 0; - ceilingheight = 0; - floorpic = 0; - ceilingpic = 0; - lightlevel = 0; - special = 0; - tag = 0; - soundtraversed = 0; - soundtarget = null; - memset(blockbox, 0, blockbox.length); - soundorg = null; - validcount = 0; - thinglist = null; - specialdata = null; - linecount = 0; - lines = null; - id = -1; - - } -} +package rr; + +import static data.Limits.MAXINT; +import static data.Limits.MAX_ADJOINING_SECTORS; +import doom.SourceCode; +import doom.SourceCode.P_Spec; +import static doom.SourceCode.P_Spec.P_FindLowestCeilingSurrounding; +import doom.SourceCode.fixed_t; +import java.io.DataInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.logging.Level; +import java.util.logging.Logger; +import m.IRandom; +import static m.fixed_t.FRACBITS; +import static m.fixed_t.FRACUNIT; +import mochadoom.Loggers; +import p.Resettable; +import p.ThinkerList; +import p.mobj_t; +import s.degenmobj_t; +import static utils.C2JUtils.memset; +import w.DoomIO; +import w.IPackableDoomObject; +import w.IReadableDoomObject; + +/** + * The SECTORS record, at runtime. Stores things/mobjs. Can be + * archived/unarchived during savegames. + * + * @author Maes + */ +public class sector_t implements IReadableDoomObject, IPackableDoomObject, Resettable { + + private static final Logger LOGGER = Loggers.getLogger(sector_t.class.getName()); + + public ThinkerList TL; + + public IRandom RND; + + public sector_t() { + blockbox = new int[4]; + id = -1; + } + + /** (fixed_t) */ + public int floorheight, ceilingheight; + + public short floorpic; + + public short ceilingpic; + + public short lightlevel; + + public short special; + + public short tag; + + /** 0 = untraversed, 1,2 = sndlines -1 */ + public int soundtraversed; + + /** thing that made a sound (or null) (MAES: single pointer) */ + public mobj_t soundtarget; + + /** mapblock bounding box for height changes */ + public int[] blockbox; + + /** + * origin for any sounds played by the sector. Used to be degenmobj_t, but + * that's really a futile distinction. + */ + public degenmobj_t soundorg; + + /** if == validcount, already checked */ + public int validcount; + + /** list of mobjs in sector (MAES: it's used as a linked list) */ + public mobj_t thinglist; + + /** + * thinker_t for reversable actions. This actually was a void*, and in + * practice it could store doors, plats, floors and ceiling objects. + */ + public SectorAction specialdata; + + public int linecount; + + // struct line_s** lines; // [linecount] size + // MAES: make this line_t[] for now? + public line_t[] lines; + + /** Use for internal identification */ + public int id; + + /** killough 1/30/98: improves searches for tags. */ + public int nexttag, firsttag; + + @Override + public String toString() { + // needed? + + return String.format("Sector: %d %x %x %d %d %d %d %d", id, floorheight, + ceilingheight, floorpic, ceilingpic, lightlevel, special, // needed? + tag); + } + + // + // P_FindLowestFloorSurrounding() + // FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS + // + public int FindLowestFloorSurrounding() { + int i; + line_t check; + sector_t other; + int floor = this.floorheight; + + for (i = 0; i < this.linecount; i++) { + check = this.lines[i]; + other = check.getNextSector(this); + + if (other == null) { + continue; + } + + if (other.floorheight < floor) { + floor = other.floorheight; + } + } + return floor; + } + + /** + * P_FindHighestFloorSurrounding() FIND HIGHEST FLOOR HEIGHT IN SURROUNDING + * SECTORS Compatibility problem: apparently this is hardcoded for vanilla + * compatibility (instead of Integer.MIN_VALUE), but it will cause some + * "semi-Boom" maps not to work, since it won't be able to lower stuff below + * -500 units. The correct fix here would be to allow for -compatlevel style + * options. Maybe later. + * + * @param sec + */ + public int FindHighestFloorSurrounding() { + int i; + line_t check; + sector_t other; + + int floor = -500 * FRACUNIT; + + for (i = 0; i < this.linecount; i++) { + check = this.lines[i]; + other = check.getNextSector(this); + + // The compiler nagged about this being unreachable, with + // some older 1.6 JDKs, but that's obviously not true. + if (other == null) { + continue; + } + + if (other.floorheight > floor) { + floor = other.floorheight; + } + } + return floor; + } + + /** + * P_FindNextHighestFloor FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS + * Note: this should be doable w/o a fixed array. + * + * @param sec + * @param currentheight + * @return fixed + */ + public int FindNextHighestFloor(int currentheight) { + int i; + int h; + int min; + line_t check; + sector_t other; + int height = currentheight; + + int heightlist[] = new int[MAX_ADJOINING_SECTORS]; + + for (i = 0, h = 0; i < this.linecount; i++) { + check = this.lines[i]; + other = check.getNextSector(this); + + if (other == null) { + continue; + } + + if (other.floorheight > height) { + heightlist[h++] = other.floorheight; + } + + // Check for overflow. Exit. + if (h >= MAX_ADJOINING_SECTORS) { + LOGGER.log(Level.WARNING, + "Sector with more than 20 adjoining sectors"); + break; + } + } + + // Find lowest height in list + if (h == 0) { + return currentheight; + } + + min = heightlist[0]; + + // Range checking? + for (i = 1; i < h; i++) { + if (heightlist[i] < min) { + min = heightlist[i]; + } + } + + return min; + } + + // + // FIND LOWEST CEILING IN THE SURROUNDING SECTORS + // + @SourceCode.Exact + @P_Spec.C(P_FindLowestCeilingSurrounding) + public @fixed_t + int FindLowestCeilingSurrounding() { + line_t check; + sector_t other; + int height = MAXINT; + + for (int i = 0; i < this.linecount; i++) { + check = this.lines[i]; + getNextSector: + { + other = check.getNextSector(this); + } + + if (other == null) { + continue; + } + + if (other.ceilingheight < height) { + height = other.ceilingheight; + } + } + return height; + } + + // + // FIND HIGHEST CEILING IN THE SURROUNDING SECTORS + // + public int FindHighestCeilingSurrounding() { + int i; + line_t check; + sector_t other; + int height = 0; + + for (i = 0; i < this.linecount; i++) { + check = this.lines[i]; + other = check.getNextSector(this); + + if (other == null) { + continue; + } + + if (other.ceilingheight > height) { + height = other.ceilingheight; + } + } + return height; + } + + @Override + public void read(DataInputStream f) + throws IOException { + + // ACHTUNG: the only situation where we'd + // like to read memory-format sector_t's is from + // savegames, and in vanilla savegames, not all info + // is saved (or read) from disk. + this.floorheight = DoomIO.readLEShort(f) << FRACBITS; + this.ceilingheight = DoomIO.readLEShort(f) << FRACBITS; + // MAES: it may be necessary to apply a hack in order to + // read vanilla savegames. + this.floorpic = DoomIO.readLEShort(f); + this.ceilingpic = DoomIO.readLEShort(f); + // f.skipBytes(4); + this.lightlevel = DoomIO.readLEShort(f); + this.special = DoomIO.readLEShort(f); // needed? + this.tag = DoomIO.readLEShort(f); // needed? + } + + @Override + public void pack(ByteBuffer b) { + + b.putShort((short) (floorheight >> FRACBITS)); + b.putShort((short) (ceilingheight >> FRACBITS)); + // MAES: it may be necessary to apply a hack in order to + // read vanilla savegames. + b.putShort(floorpic); + b.putShort(ceilingpic); + // f.skipBytes(4); + b.putShort(lightlevel); + b.putShort(special); + b.putShort(tag); + } + + @Override + public void reset() { + floorheight = 0; + ceilingheight = 0; + floorpic = 0; + ceilingpic = 0; + lightlevel = 0; + special = 0; + tag = 0; + soundtraversed = 0; + soundtarget = null; + memset(blockbox, 0, blockbox.length); + soundorg = null; + validcount = 0; + thinglist = null; + specialdata = null; + linecount = 0; + lines = null; + id = -1; + + } +} \ No newline at end of file diff --git a/src/rr/seg_t.java b/src/rr/seg_t.java index 187adf9..3600391 100644 --- a/src/rr/seg_t.java +++ b/src/rr/seg_t.java @@ -6,7 +6,7 @@ /** * The LineSeg. Must be built from on-disk mapsegs_t, which are much simpler. - * + * * @author Maes */ public class seg_t @@ -53,7 +53,7 @@ public void assignVertexValues() { /** * R_PointOnSegSide - * + * * @param x * @param y * @param line @@ -115,7 +115,7 @@ public static int PointOnSegSide(int x, int y, seg_t line) { /** * R_PointOnSegSide - * + * * @param x * @param y * @param line @@ -195,4 +195,4 @@ public void reset() { length = 0; } -} +} \ No newline at end of file diff --git a/src/rr/side_t.java b/src/rr/side_t.java index 443cc41..4cb4590 100644 --- a/src/rr/side_t.java +++ b/src/rr/side_t.java @@ -11,7 +11,7 @@ /** * The SideDef. - * + * * @author admin */ public class side_t @@ -87,4 +87,4 @@ public void reset() { } -} +} \ No newline at end of file diff --git a/src/rr/spritedef_t.java b/src/rr/spritedef_t.java index a995fb0..7556578 100644 --- a/src/rr/spritedef_t.java +++ b/src/rr/spritedef_t.java @@ -26,7 +26,7 @@ public spritedef_t(spriteframe_t[] frames) { } } - /** Use this constructor, as we usually need less than 30 frames + /** Use this constructor, as we usually need less than 30 frames * It will actually clone the frames. */ public void copy(spriteframe_t[] from, int maxframes) { @@ -41,4 +41,4 @@ public void copy(spriteframe_t[] from, int maxframes) { public int numframes; public spriteframe_t[] spriteframes; -}; +}; \ No newline at end of file diff --git a/src/rr/spriteframe_t.java b/src/rr/spriteframe_t.java index df78077..2409e04 100644 --- a/src/rr/spriteframe_t.java +++ b/src/rr/spriteframe_t.java @@ -23,8 +23,8 @@ public spriteframe_t() { /** If false use 0 for any position. * Note: as eight entries are available, - * we might as well insert the same name eight times. - * + * we might as well insert the same name eight times. + * * FIXME: this is used as a tri-state. * 0= false * 1= true @@ -47,4 +47,4 @@ public spriteframe_t clone() { } -} +} \ No newline at end of file diff --git a/src/rr/subsector_t.java b/src/rr/subsector_t.java index b1ebab0..4a36538 100644 --- a/src/rr/subsector_t.java +++ b/src/rr/subsector_t.java @@ -3,11 +3,11 @@ import p.Resettable; /** - * + * * A SubSector. References a Sector. Basically, this is a list of LineSegs, * indicating the visible walls that define (all or some) sides of a convex BSP * leaf. - * + * * @author admin */ public class subsector_t implements Resettable { @@ -53,4 +53,4 @@ public void reset() { firstline = numlines = 0; } -} +} \ No newline at end of file diff --git a/src/rr/texpatch_t.java b/src/rr/texpatch_t.java index 6dbda2d..6b6e32b 100644 --- a/src/rr/texpatch_t.java +++ b/src/rr/texpatch_t.java @@ -21,4 +21,4 @@ public void copyFromMapPatch(mappatch_t mpp) { this.originy = mpp.originy; this.patch = mpp.patch; } -} +} \ No newline at end of file diff --git a/src/rr/texture_t.java b/src/rr/texture_t.java index 127c562..fd33914 100644 --- a/src/rr/texture_t.java +++ b/src/rr/texture_t.java @@ -3,9 +3,9 @@ /** A maptexturedef_t describes a rectangular texture, * which is composed of one or more mappatch_t structures * that arrange graphic patches. - * + * * This is the in-memory format, which is similar to maptexture_t (which is on-disk). - * + * * @author Maes * */ @@ -48,4 +48,4 @@ public String toString() { return sb.toString(); } -} +} \ No newline at end of file diff --git a/src/rr/vertex_t.java b/src/rr/vertex_t.java index 07b8f92..9a0263f 100644 --- a/src/rr/vertex_t.java +++ b/src/rr/vertex_t.java @@ -9,7 +9,7 @@ /** This is the vertex structure used IN MEMORY with fixed-point arithmetic. * It's DIFFERENT than the one used on disk, which has 16-bit signed shorts. - * However, it must be parsed. + * However, it must be parsed. * */ public class vertex_t implements CacheableDoomObject, Resettable { @@ -40,4 +40,4 @@ public static int sizeOf() { return 4; } -} +} \ No newline at end of file diff --git a/src/rr/visplane_t.java b/src/rr/visplane_t.java index a5ee4aa..0bdb550 100644 --- a/src/rr/visplane_t.java +++ b/src/rr/visplane_t.java @@ -21,9 +21,9 @@ public class visplane_t { public static int BOTTOMOFFSET; // Multithreading trickery (for strictly x-bounded drawers) - // The thread if is encoded in the upper 3 bits (puts an upper limit - // of 8 floor threads), and the stomped value is encoded in the next 12 - // bits (this puts an upper height limit of 4096 pixels). + // The thread if is encoded in the upper 3 bits (puts an upper limit + // of 8 floor threads), and the stomped value is encoded in the next 12 + // bits (this puts an upper height limit of 4096 pixels). // Not the cleanest system possible, but it's backwards compatible // TODO: expand visplane buffers to full-fledged ints? public static final char SENTINEL = 0x8000; @@ -160,4 +160,4 @@ public static void setVideoScale(VideoScale vs) { memset(clearvisplane, Character.MAX_VALUE, clearvisplane.length); } } -}; +}; \ No newline at end of file diff --git a/src/rr/vissprite_t.java b/src/rr/vissprite_t.java index d95fb97..fdb62cf 100644 --- a/src/rr/vissprite_t.java +++ b/src/rr/vissprite_t.java @@ -34,7 +34,7 @@ public class vissprite_t implements Comparable> { /** for color translation and shadow draw, * maxbright frames as well. - * + * * Use paired with pcolormap; */ public V colormap; @@ -46,7 +46,7 @@ public class vissprite_t implements Comparable> { /** visspites are sorted by scale */ @Override public final int compareTo(vissprite_t o) { - // We only really care if it's drawn before. + // We only really care if it's drawn before. if (this.scale > o.scale) { return 1; } @@ -60,4 +60,4 @@ public String toString() { return ("Effective drawing position x1: " + x1 + " x2: " + x2 + " scale " + (scale / 65535.0) + " iscale " + (xiscale / 65535.0)); } -} +} \ No newline at end of file diff --git a/src/rr/z_vertex_t.java b/src/rr/z_vertex_t.java index 60c6962..0401d72 100644 --- a/src/rr/z_vertex_t.java +++ b/src/rr/z_vertex_t.java @@ -25,4 +25,4 @@ public final static int sizeOf() { return 8; } -} +} \ No newline at end of file diff --git a/src/s/AbstractDoomAudio.java b/src/s/AbstractDoomAudio.java index c65782f..b07e538 100644 --- a/src/s/AbstractDoomAudio.java +++ b/src/s/AbstractDoomAudio.java @@ -20,12 +20,12 @@ /** Some stuff that is not implementation dependant * This includes channel management, sound priorities, - * positioning, distance attenuation etc. It's up to + * positioning, distance attenuation etc. It's up to * lower-level "drivers" to actually implements those. * This particular class needs not be a dummy itself, but - * the drivers it "talks" to might be. - * - * + * the drivers it "talks" to might be. + * + * * */ public class AbstractDoomAudio implements IDoomSound { @@ -40,7 +40,7 @@ public class AbstractDoomAudio implements IDoomSound { protected final static boolean D = false; /** the set of channels available. These are "soft" descriptor - channels, not to be confused with actual hardware audio + channels, not to be confused with actual hardware audio lines, which are an entirely different concern. */ @@ -166,7 +166,7 @@ public void Start() { } // HACK FOR COMMERCIAL - // if (commercial && mnum > mus_e3m9) + // if (commercial && mnum > mus_e3m9) // mnum -= mus_e3m9; ChangeMusic(mnum, true); @@ -311,9 +311,9 @@ public void Start() { // Assigns the handle to one of the channels in the // mix/output buffer. This is when things actually // become hard (pun intended). - // TODO: which channel? How do we know how the actual hardware + // TODO: which channel? How do we know how the actual hardware // ones map with the "soft" ones? - // Essentially we're begging to get an actual channel. + // Essentially we're begging to get an actual channel. channels[cnum].handle = ISND.StartSound(sfx_id, /*sfx->data,*/ volume, @@ -446,7 +446,7 @@ public void UpdateSounds(mobj_t listener) { // Clean up unused data. // This is currently not done for 16bit (sounds cached static). - // DOS 8bit remains. + // DOS 8bit remains. /*if (gametic.nextcleanup) { for (i=1 ; i>1); - + // The idea is that for low speeds, no doppler effect occurs. // For higher ones however, a shift occurs. We don't want this // to be annoying, so we'll only apply it for large speed differences // Then again, Doomguy can sprint like Carl Lewis... - + if (approx_dist>0x100000){ - + // Quickly decide sign of pitch based on speed vectors - + // angle of source (speed) to listener (speed) angle = rr.RendererState.PointToAngle(listener.momx, listener.momy, source.momx, source.momy); - + if ((0<=angle && angle<=Tables.ANG90)|| (180<=angle && angle<=Tables.ANG270)) vps.pitch+=(approx_dist>>16); @@ -810,7 +810,7 @@ protected int getChannel(ISoundOrigin origin, sfxinfo_t sfxinfo) { * and we can give it a handle proportional to the future tics * it should play until. Ofc, this means the minimum timeframe * for cutting a sound off is just 1 tic. - * + * * @param handle * @return */ @@ -821,4 +821,4 @@ public boolean SoundIsPlaying(int handle) // Ouch. return (DS.gametic < handle); } */ -} +} \ No newline at end of file diff --git a/src/s/AbstractSoundDriver.java b/src/s/AbstractSoundDriver.java index 5937f9b..8b5cef3 100644 --- a/src/s/AbstractSoundDriver.java +++ b/src/s/AbstractSoundDriver.java @@ -11,7 +11,7 @@ /** * Functionality and fields that are common among the various "sound drivers" * should go here. - * + * * @author Maes */ public abstract class AbstractSoundDriver implements ISoundDriver { @@ -27,9 +27,9 @@ public abstract class AbstractSoundDriver implements ISoundDriver { * channels are modifed and added, and stored in the buffer that is * submitted to the audio device. This is a 16-bit stereo signed PCM * mixbuffer. Memory order is LSB (?) and channel order is L-R-L-R... - * + * * Not all i - * + * */ protected byte[] mixbuffer;// = new byte[MIXBUFFERSIZE]; @@ -93,7 +93,7 @@ protected final void generateVolumeLUT() { * to 256K roughly, with the middle of the table being 64K, and presumably * representing unitary pitch. So the pitch variation can be quite extreme, * allowing -/+ 400% stepping :-S - * + * * @param steptablemid * @return */ @@ -107,7 +107,7 @@ protected void generateStepTable(int steptablemid) { /** Read a Doom-format sound effect from disk, leaving it in 8-bit mono format but * upsampling it to the target sample rate. - * + * * @param sfxname * @param len * @param index @@ -191,7 +191,7 @@ protected byte[] getsfx(String sfxname, int[] len, int index) { /** * Modified getsfx, which transforms samples into 16-bit, signed, stereo * beforehand, before being "fed" to the audio clips. - * + * * @param sfxname * @param index * @return @@ -283,7 +283,7 @@ public int StartSound(int id, int vol, int sep, int pitch, int priority) { * This function adds a sound to the list of currently active sounds, which * is maintained as a given number (eight, usually) of internal channels. * Returns a handle. - * + * * @param sfxid * @param volume * @param step @@ -318,8 +318,8 @@ public final int GetSfxLumpNum(sfxinfo_t sfx) { } /** - * Initialize - * + * Initialize + * * @return */ protected final void initMixBuffer() { @@ -363,9 +363,9 @@ protected final void initSound8() { /** * This is only the common part of InitSound that caches sound data in * 16-bit, stereo format (used by Audiolines). INTO sfxenum_t. - * + * * Only used by the Clip and David "drivers". - * + * */ protected final void initSound16() { int i; @@ -383,4 +383,4 @@ protected final void initSound16() { } } -} +} \ No newline at end of file diff --git a/src/s/AudioChunk.java b/src/s/AudioChunk.java index bccbfcd..7214e16 100644 --- a/src/s/AudioChunk.java +++ b/src/s/AudioChunk.java @@ -18,4 +18,4 @@ public void setStuff(int chunk, int time) { public byte[] buffer; public boolean free; -} +} \ No newline at end of file diff --git a/src/s/ClassicDoomSoundDriver.java b/src/s/ClassicDoomSoundDriver.java index a2518be..b686cba 100644 --- a/src/s/ClassicDoomSoundDriver.java +++ b/src/s/ClassicDoomSoundDriver.java @@ -17,16 +17,16 @@ /** * A close recreation of the classic linux doom sound mixer. - * + * * PROS: * a) Very faithful to pitch and stereo effects, and original * volume ramping. * b) Uses only one audioline and one playback thread - * + * * CONS: * a) May sound a bit off if production/consumption rates don't match * b) Sounds awful when mixing too many sounds together, just like the original. - * + * * @author Maes */ public class ClassicDoomSoundDriver extends AbstractSoundDriver { @@ -166,7 +166,7 @@ public void UpdateSound() { sample = 0x00FF & channels[chan][channel_pointer]; // Add left and right part for this channel (sound) - // to the current data. Adjust volume accordingly. + // to the current data. Adjust volume accordingly. // Q: could this be optimized by converting samples to 16-bit // at load time, while also allowing for stereo samples? // A: Only for the stereo part. You would still look a lookup @@ -583,7 +583,7 @@ public boolean SoundIsPlaying(int handle) { /** * Internal use. - * + * * @param handle * @return the channel that has the handle, or -2 if none has it. */ @@ -701,4 +701,4 @@ public String channelStatus() { protected final AudioChunk SILENT_CHUNK = new AudioChunk(); protected final AudioChunkPool audiochunkpool = new AudioChunkPool(); -} +} \ No newline at end of file diff --git a/src/s/ClipSFXModule.java b/src/s/ClipSFXModule.java index e800e93..c22257d 100644 --- a/src/s/ClipSFXModule.java +++ b/src/s/ClipSFXModule.java @@ -17,20 +17,20 @@ /** Experimental Clip based driver. It does work, but it has no * tangible advantages over the Audioline or Classic one. If the - * Audioline can be used, there's no reason to fall back to this + * Audioline can be used, there's no reason to fall back to this * one. - * + * * KNOWN ISSUES: - * - * a) Same general restrictions as audiolines (in fact, Clips ARE Audioline + * + * a) Same general restrictions as audiolines (in fact, Clips ARE Audioline * in disguise) * b) Multiple instances of the same sound require multiple clips, so * even caching them is a half-baked solution, and if you have e.g. 40 imps - * sound in a room.... - * - * + * sound in a room.... + * + * * Currently unused. - * + * * @author Velktron * */ @@ -90,7 +90,7 @@ public boolean InitSound() { /** Modified getsfx. The individual length of each sfx is not of interest. * However, they must be transformed into 16-bit, signed, stereo samples * beforehand, before being "fed" to the audio clips. - * + * * @param sfxname * @param index * @return @@ -245,7 +245,7 @@ private final void getClipForChannel(int c, int sfxid) { channels[c] = clip; // Control[] cs=clip.getControls(); - // + // // for (Control cc:cs){ // System.out.println("Control "+cc.getType().toString()); // } @@ -311,7 +311,7 @@ protected int addsfx(int sfxid, int volume, int pitch, int seperation) { // Okay, in the less recent channel, // we will handle the new SFX. // We need to decide whether we can reuse an existing clip - // or create a new one. In any case, when this method return + // or create a new one. In any case, when this method return // we should have a valid clip assigned to channel "slot". getClipForChannel(slot, sfxid); @@ -362,7 +362,7 @@ protected int addsfx(int sfxid, int volume, int pitch, int seperation) { } /** Accepts volume in "Doom" format (0-127). - * + * * @param volume */ public void setVolume(int chan, int volume) { @@ -424,8 +424,8 @@ public void UpdateSoundParams(int handle, int vol, int sep, int pitch) { } - /** Internal use. - * + /** Internal use. + * * @param handle * @return the channel that has the handle, or -2 if none has it. */ @@ -456,4 +456,4 @@ public String channelStatus() { } -} +} \ No newline at end of file diff --git a/src/s/DMXSound.java b/src/s/DMXSound.java index 8e48af0..425dfc9 100644 --- a/src/s/DMXSound.java +++ b/src/s/DMXSound.java @@ -33,4 +33,4 @@ public void unpack(ByteBuffer buf) buf.get(data); } -} +} \ No newline at end of file diff --git a/src/s/DSP.java b/src/s/DSP.java index e28a10d..06d4587 100644 --- a/src/s/DSP.java +++ b/src/s/DSP.java @@ -4,7 +4,7 @@ public class DSP { /** * QDSS Windowed Sinc ReSampling subroutine in Basic - * + * * @param x * new sample point location (relative to old indexes) (e.g. every * other integer for 0.5x decimation) @@ -60,7 +60,7 @@ public float resamp(float x, float[] indat, int alim, float fmax, * of the "results" is left as an exercise for the student. (consider this * code Open Source under a BSD style license) Some example filter * parameters: - * + * * @param fsr * = 44100 : rem set fsr = sample rate * @param fc @@ -217,4 +217,4 @@ public static void filter(double[] samples, int smp, double cutoff, int count) { } } -} +} \ No newline at end of file diff --git a/src/s/DavidMusicModule.java b/src/s/DavidMusicModule.java index cfebc5f..559bdf7 100644 --- a/src/s/DavidMusicModule.java +++ b/src/s/DavidMusicModule.java @@ -18,7 +18,7 @@ /** Concern separated from David Martel's MIDI & MUS player * for Mocha Doom. Greatly improved upon by finnw, perfecting volume changes * and MIDI device detection. - * + * * @author David Martel * @author velktron * @author finnw @@ -56,9 +56,9 @@ public void InitMusic() { } //System.out.printf("x %d y %d \n",x,y); - //--This sets the Sequencer and Synthesizer - //--The indices x and y correspond to the correct entries for the - //--default Sequencer and Synthesizer, as determined above + //--This sets the Sequencer and Synthesizer + //--The indices x and y correspond to the correct entries for the + //--default Sequencer and Synthesizer, as determined above if (x != -1) { sequencer = (Sequencer) MidiSystem.getMidiDevice(info[x]); } else { @@ -191,4 +191,4 @@ public void UnRegisterSong(int handle) { } -} +} \ No newline at end of file diff --git a/src/s/DavidSFXModule.java b/src/s/DavidSFXModule.java index caf3c73..e5e3634 100644 --- a/src/s/DavidSFXModule.java +++ b/src/s/DavidSFXModule.java @@ -16,11 +16,11 @@ import mochadoom.Loggers; /** David Martel's sound driver for Mocha Doom. Excellent work! - * + * * However, it's based on Java Audiolines, and as such has a number * of drawbacks: - * - * a) Sounds are forcibly blown to be stereo, 16-bit otherwise it's + * + * a) Sounds are forcibly blown to be stereo, 16-bit otherwise it's * impossible to get panning controls. * b) Volume, master gain, panning, pitch etc. controls are NOT guaranteed * to be granted across different OSes , and your mileage may vary. It's @@ -29,7 +29,7 @@ * c) Spawns as many threads as channels. Even if semaphore waiting it used, * that can be taxing for slower systems. - * + * * @author David * @author Velktron * @@ -152,7 +152,7 @@ public void SetChannels(int numChannels) { /** This one will only create datalines for common clip/audioline samples * directly. - * + * * @param c * @param sfxid */ @@ -219,7 +219,7 @@ private final void createDataLineForChannel(int c, int sfxid) { /* UNUSED version, designed to work on any type of sample (in theory). Requires a DoomSound container for separate format information. - + private final void createDataLineForChannel(int c, DoomSound sound){ if (channels[c].auline == null) { AudioFormat format = sound.ais.getFormat(); @@ -240,10 +240,10 @@ private final void createDataLineForChannel(int c, DoomSound sound){ if (channels[c].auline.isControlSupported(Type.VOLUME)) channels[c].vc=(FloatControl) channels[c].auline .getControl(Type.VOLUME); - else + else System.err.printf("VOLUME for channel %d NOT supported!\n",c); - } - + } + // Add individual pitch control. if (channels[c].auline.isControlSupported(Type.SAMPLE_RATE)){ @@ -251,15 +251,15 @@ private final void createDataLineForChannel(int c, DoomSound sound){ .getControl(Type.SAMPLE_RATE); } else { System.err.printf("SAMPLE_RATE for channel %d NOT supported!\n",c); - } - + } + // Add individual pan control (TODO: proper positioning). if (channels[c].auline.isControlSupported(Type.BALANCE)){ channels[c].bc=(FloatControl) channels[c].auline .getControl(FloatControl.Type.BALANCE); } else { System.err.printf("BALANCE for channel %d NOT supported!\n",c); - if (channels[c].auline.isControlSupported(Type.PAN)){ + if (channels[c].auline.isControlSupported(Type.PAN)){ channels[c].bc=(FloatControl) channels[c].auline .getControl(FloatControl.Type.PAN); } else { @@ -414,8 +414,8 @@ public void UpdateSoundParams(int handle, int vol, int sep, int pitch) { } - /** Internal use. - * + /** Internal use. + * * @param handle * @return the channel that has the handle, or -2 if none has it. */ @@ -431,9 +431,9 @@ private int getChannelFromHandle(int handle) { } /** A Thread for playing digital sound effects. - * + * * Obviously you need as many as channels? - * + * * In order not to end up in a hell of effects, * certain types of sounds must be limited to 1 per object. * @@ -478,7 +478,7 @@ public void addSound(byte[] ds, int handle) { } /** Accepts volume in "Doom" format (0-127). - * + * * @param volume */ public void setVolume(int volume) { @@ -505,7 +505,7 @@ public void setPanning(int sep) { /** Expects a steptable value between 16K and 256K, with * 64K being the middle. - * + * * @param pitch */ public void setPitch(int pitch) { @@ -590,4 +590,4 @@ public String channelStatus() { } -} +} \ No newline at end of file diff --git a/src/s/DoomIO.java b/src/s/DoomIO.java index 8871b92..6dc5578 100644 --- a/src/s/DoomIO.java +++ b/src/s/DoomIO.java @@ -304,4 +304,4 @@ public static int indexOfArray(Object[] a, Object o) { return -1; } -} +} \ No newline at end of file diff --git a/src/s/DoomSound.java b/src/s/DoomSound.java index 4ad63f6..1d5ed48 100644 --- a/src/s/DoomSound.java +++ b/src/s/DoomSound.java @@ -4,12 +4,12 @@ import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioFormat.Encoding; -/** A class representing a sample in memory +/** A class representing a sample in memory * Convenient for wrapping/mirroring it regardless of what it represents. * */ class DoomSound extends sfxinfo_t { - /** This audio format is the one used by internal samples (16 bit, 11KHz, Stereo) + /** This audio format is the one used by internal samples (16 bit, 11KHz, Stereo) * for Clips and AudioLines. Sure, it's not general enough... who cares though? */ public final static AudioFormat DEFAULT_SAMPLES_FORMAT = new AudioFormat(Encoding.PCM_SIGNED, ISoundDriver.SAMPLERATE, 16, 2, 4, ISoundDriver.SAMPLERATE, true); @@ -39,4 +39,4 @@ public DoomSound(sfxinfo_t sfx, AudioFormat format) { this.volume = sfx.volume; } -} +} \ No newline at end of file diff --git a/src/s/DoomToWave.java b/src/s/DoomToWave.java index d639644..087bfd6 100644 --- a/src/s/DoomToWave.java +++ b/src/s/DoomToWave.java @@ -262,4 +262,4 @@ void SNDsaveWave(InputStream is, OutputStream os, int speed, int size) throws IO } } -} +} \ No newline at end of file diff --git a/src/s/DummyMusic.java b/src/s/DummyMusic.java index e904c47..776d124 100644 --- a/src/s/DummyMusic.java +++ b/src/s/DummyMusic.java @@ -56,4 +56,4 @@ public void UnRegisterSong(int handle) { } -} +} \ No newline at end of file diff --git a/src/s/DummySFX.java b/src/s/DummySFX.java index 1ef16cd..7805056 100644 --- a/src/s/DummySFX.java +++ b/src/s/DummySFX.java @@ -64,4 +64,4 @@ public void SetChannels(int numChannels) { } -} +} \ No newline at end of file diff --git a/src/s/DummySoundDriver.java b/src/s/DummySoundDriver.java index 25b31bd..c55ae79 100644 --- a/src/s/DummySoundDriver.java +++ b/src/s/DummySoundDriver.java @@ -7,7 +7,7 @@ /** Does nothing. Just allows me to code without * commenting out ALL sound-related code. Hopefully * it will be superseded by a real sound driver one day. - * + * * @author Velktron * */ @@ -109,4 +109,4 @@ public void ChangeMusic(musicenum_t musicnum, boolean looping) { } -} +} \ No newline at end of file diff --git a/src/s/FinnwMusicModule.java b/src/s/FinnwMusicModule.java index c2cc75c..f4cac51 100644 --- a/src/s/FinnwMusicModule.java +++ b/src/s/FinnwMusicModule.java @@ -44,7 +44,7 @@ * This does not work well with a {@link Sequence} because changes to events * (e.g. channel volume change events) do not take effect while the sequencer * is running. - * + * * Disadvantages of this driver: *
  • Supports MUS lumps only (no MID, OGG etc.)
  • *
  • Creates its own thread
  • @@ -851,4 +851,4 @@ ByteBuffer getScoreBuffer() { /** Songs indexed by handle */ private final List songs; -} +} \ No newline at end of file diff --git a/src/s/IDoomSound.java b/src/s/IDoomSound.java index 33320b1..c20bc0c 100644 --- a/src/s/IDoomSound.java +++ b/src/s/IDoomSound.java @@ -117,7 +117,7 @@ class channel_t { /** * Start sound for thing at using from sounds.h * Convenience method using sfxenum_t instead. Delegated to int version. - * + * */ public void StartSound(ISoundOrigin origin, sfxenum_t sound_id); @@ -129,7 +129,7 @@ class channel_t { /** * Start music using from sounds.h, and set whether looping - * + * * @param musicnum * @param looping */ @@ -147,7 +147,7 @@ class channel_t { /** * Updates music & sounds - * + * * @param listener */ public void UpdateSounds(mobj_t listener); @@ -159,14 +159,14 @@ class channel_t { /** Start music using from sounds.h */ public void StartMusic(int music_id); - /** Start music using from sounds.h + /** Start music using from sounds.h * Convenience method using musicenum_t. */ public void StartMusic(musicenum_t music_id); // - // Internals. - // + // Internals. + // // MAES: these appear to be only of value for internal implementation, // and are never called externally. Thus, they might as well // not be part of the interface, even though it's convenient to reuse them. @@ -188,4 +188,4 @@ class channel_t { void S_StopChannel(int cnum); */ -} +} \ No newline at end of file diff --git a/src/s/IMusic.java b/src/s/IMusic.java index 416a802..d9045c2 100644 --- a/src/s/IMusic.java +++ b/src/s/IMusic.java @@ -20,9 +20,9 @@ public interface IMusic { void ResumeSong(int handle); - /** Registers a song handle to song data. + /** Registers a song handle to song data. * This should handle any conversions from MUS/MIDI/OPL/etc. - * + * * */ int RegisterSong(byte[] data); @@ -47,4 +47,4 @@ public static IMusic chooseModule(CVarManager CVM) { return new DavidMusicModule(); } } -} +} \ No newline at end of file diff --git a/src/s/ISoundDriver.java b/src/s/ISoundDriver.java index d68a71b..6d361aa 100644 --- a/src/s/ISoundDriver.java +++ b/src/s/ISoundDriver.java @@ -59,7 +59,7 @@ public interface ISoundDriver { public static final int SND_FRAME_RATE = 21; // Was 512, but if you mix that many samples per tic you will // eventually outrun the buffer :-/ I fail to see the mathematical - // justification behind this, unless they simply wanted the buffer to + // justification behind this, unless they simply wanted the buffer to // be a nice round number in size. public static final int SAMPLECOUNT = SAMPLERATE / SND_FRAME_RATE; public static final int MIXBUFFERSIZE = (SAMPLECOUNT * BUFMUL); @@ -71,7 +71,7 @@ public interface ISoundDriver { */ public static final int BUFFER_CHUNKS = 5; - /** Ths audio buffer size of the audioline itself. + /** Ths audio buffer size of the audioline itself. * Increasing this is the only effective way to combat output stuttering on * slower machines. */ @@ -105,9 +105,9 @@ public static ISoundDriver chooseModule(DoomMain DM, CVarManager CVM) { } /** Init at program start. Return false if device invalid, - * so that caller can decide best course of action. + * so that caller can decide best course of action. * The suggested one is to swap the sound "driver" for a dummy. - * + * * @return */ boolean InitSound(); @@ -140,7 +140,7 @@ int StartSound(int id, void StopSound(int handle); /** Called by S_*() functions to see if a channel is still playing. - Returns false if no longer playing, true if playing. This is + Returns false if no longer playing, true if playing. This is a relatively "high level" function, so its accuracy relies on what the "system specific" sound code reports back */ boolean SoundIsPlaying(int handle); @@ -152,4 +152,4 @@ void UpdateSoundParams(int handle, int sep, int pitch); -} +} \ No newline at end of file diff --git a/src/s/ISoundOrigin.java b/src/s/ISoundOrigin.java index 3e390c6..e3cde42 100644 --- a/src/s/ISoundOrigin.java +++ b/src/s/ISoundOrigin.java @@ -8,4 +8,4 @@ public interface ISoundOrigin { public int getY(); public int getZ(); -} +} \ No newline at end of file diff --git a/src/s/MusReader.java b/src/s/MusReader.java index dd33dc0..46103ca 100644 --- a/src/s/MusReader.java +++ b/src/s/MusReader.java @@ -18,7 +18,7 @@ /** * A MUS lump reader that loads directly to a Sequence. - * + * * Unlike QMusToMid, does not keep the MIDI version in a temporary file. * * @author finnw @@ -306,4 +306,4 @@ private void addShortMessage(int midiChan, int cmd, int data1, int data2) { private final List messages; } -} +} \ No newline at end of file diff --git a/src/s/QMusToMid.java b/src/s/QMusToMid.java index b088092..caecc5c 100644 --- a/src/s/QMusToMid.java +++ b/src/s/QMusToMid.java @@ -130,7 +130,7 @@ int ReadMUSheader(MUSheader MUSh, InputStream file) { return COMUSFILE; } - /*if( strncmp( MUSh->ID, MUSMAGIC, 4 ) ) + /*if( strncmp( MUSh->ID, MUSMAGIC, 4 ) ) return NOTMUSFILE ;*/ if ((MUSh.ScoreLength = DoomIO.freadint(file)) == -1) { return COMUSFILE; @@ -650,7 +650,7 @@ void PrintHeader() { void PrintSyntax() { // PrintHeader() ; -// System.out.println( +// System.out.println( // #ifdef MSDOG // "\nSyntax : QMUS2MID musfile1[.mus] {musfile2[.mus] ... | " // "midifile.mid} [options]\n" @@ -667,7 +667,7 @@ void PrintSyntax() { // " -nocomp : Don't compress !\n" // " -size ### : Set the track buffer size to ### (in KB). " // "Default = 64 KB\n" -// " -t ### : Ticks per quarter note. Default = 89\n" +// " -t ### : Ticks per quarter note. Default = 89\n" // ) ; } @@ -794,7 +794,7 @@ int main(int argc, char[] argv) { fflush( stdout ) ; do n = toupper( getxkey() ) ; - while( (n != 'Y') && (n != 'N') && (n != K_Return) + while( (n != 'Y') && (n != 'N') && (n != K_Return) && (n != 'A') && (n != 'Q')) ; switch( n ) { @@ -828,11 +828,11 @@ int main(int argc, char[] argv) { } if( !line && !nodisplay && !query ) System.out.println( "Sorry, there is no MUS file matching...\n" ) ; - + #else*/ //convert( mus, mid, nodisplay, div, size, nocomp, ow ) ; /* #endif*/ return 0; } -} +} \ No newline at end of file diff --git a/src/s/SpeakerDoomSoundDriver.java b/src/s/SpeakerDoomSoundDriver.java index 3300ef8..eb27e23 100644 --- a/src/s/SpeakerDoomSoundDriver.java +++ b/src/s/SpeakerDoomSoundDriver.java @@ -6,9 +6,9 @@ import mochadoom.Loggers; /** A variation of the Classic Sound Driver, decoding the DP-lumps - * instead of the DS. A better way would be to build-in an + * instead of the DS. A better way would be to build-in an * automatic "WAV to SPEAKER" conversion, but that can wait... - * + * * @author Maes * */ @@ -61,7 +61,7 @@ protected byte[] getsfx(String sfxname, size = sfx.length; // MAES: A-ha! So that's how they do it. - // SOund effects are padded to the highest multiple integer of + // SOund effects are padded to the highest multiple integer of // the mixing buffer's size (with silence) paddedsize = ((size - 8 + (SAMPLECOUNT - 1)) / SAMPLECOUNT) * SAMPLECOUNT; @@ -94,4 +94,4 @@ protected byte[] getsfx(String sfxname, return paddedsfx; } -} +} \ No newline at end of file diff --git a/src/s/SpeakerSound.java b/src/s/SpeakerSound.java index 1a84478..bb5dbe1 100644 --- a/src/s/SpeakerSound.java +++ b/src/s/SpeakerSound.java @@ -40,7 +40,7 @@ public class SpeakerSound implements CacheableDoomObject { 479, 465, 452}; /* From analysis of fraggle's PC Speaker timings, it was found - * that their natural logarithm had the following intercept + * that their natural logarithm had the following intercept * (starting at x=1) and slope. Therefore, it's possible * to go beyong the original 95 hardcoded values. */ @@ -61,13 +61,13 @@ public class SpeakerSound implements CacheableDoomObject { } } - /** Will return a very basic, 8-bit 11.025 KHz rendition of the sound + /** Will return a very basic, 8-bit 11.025 KHz rendition of the sound * This ain't no CuBase or MatLab, so if you were expecting perfect * sound and solid DSP, go elsewhere. - * + * */ public byte[] toRawSample() { - // Length is in 1/140th's of a second + // Length is in 1/140th's of a second byte[] chunk = new byte[this.length * 11025 / 140]; int counter = 0; @@ -112,4 +112,4 @@ public void unpack(ByteBuffer buf) } -} +} \ No newline at end of file diff --git a/src/s/SuperDoomSoundDriver.java b/src/s/SuperDoomSoundDriver.java index db94889..255f744 100644 --- a/src/s/SuperDoomSoundDriver.java +++ b/src/s/SuperDoomSoundDriver.java @@ -20,22 +20,22 @@ /** * A spiffy new sound system, based on the Classic sound driver. * It is entirely asynchronous (runs in its own thread) and even has its own timer. - * This allows it to continue mixing even when the main loop is not responding + * This allows it to continue mixing even when the main loop is not responding * (something which, arguably, could be achieved just with a timer calling * UpdateSound and SubmitSound). Uses message passing to deliver channel status * info, and mixed audio directly without using an intermediate buffer, * saving memory bandwidth. - * + * * PROS: * a) All those of ClassicSoundDriver plus: * b) Continues normal playback even under heavy CPU load, works smoother * even on lower powered CPUs. * c) More efficient due to less copying of audio blocks. * c) Fewer audio glitches compared to ClassicSoundDriver. - * + * * CONS: * a) All those of ClassicSoundDriver plus regarding timing accuracy. - * + * * @author Maes */ public class SuperDoomSoundDriver extends AbstractSoundDriver { @@ -76,7 +76,7 @@ public SuperDoomSoundDriver(DoomMain DM, int numChannels) { MIXTIMER.schedule(new SoundTimer(), 0, SOUND_PERIOD); } - /** These are still defined here to decouple them from the mixer's + /** These are still defined here to decouple them from the mixer's * ones, however they serve more as placeholders/status indicators; */ protected volatile boolean[] channels; @@ -163,7 +163,7 @@ public boolean InitSound() { SOUNDTHREAD = new Thread(SOUNDSRV); SOUNDTHREAD.setDaemon(true); SOUNDTHREAD.start(); - // Vroom! + // Vroom! MIXTHREAD = new Thread(MIXSRV); MIXTHREAD.setDaemon(true); MIXTHREAD.start(); @@ -431,7 +431,7 @@ public void run() { /** A single channel does carry a lot of crap, figuratively speaking. * Instead of making updates to ALL channel parameters, it makes more - * sense having a "mixing queue" with instructions that tell the + * sense having a "mixing queue" with instructions that tell the * mixer routine to do so-and-so with a certain channel. The mixer * will then "empty" the queue when it has completed a complete servicing * of all messages and mapped them to its internal status. @@ -442,8 +442,8 @@ protected class MixMessage { /** If this is set, the mixer considers that channel "muted" */ public boolean stop; - /** This signals an update of a currently active channel. - * Therefore pointer, remainder and data should remain untouched. + /** This signals an update of a currently active channel. + * Therefore pointer, remainder and data should remain untouched. * However volume and step of a particular channel can change. */ public boolean update; @@ -463,7 +463,7 @@ protected class MixMessage { /** Mixing thread. Mixing and submission must still go on even if * the engine lags behind due to excessive CPU load. - * + * * @author Maes * */ @@ -547,7 +547,7 @@ public void run() { // Thus, it must be flushed (p_mixbuffer=0) before reusing it. final int leftend = SAMPLECOUNT * step; - // Mix the next chunk, regardless of what the rest of the game is doing. + // Mix the next chunk, regardless of what the rest of the game is doing. while (!terminate) { // POINTERS to Left and right channel @@ -610,7 +610,7 @@ public void run() { sample = 0x00FF & channels[chan][channel_pointer]; // Add left and right part for this channel (sound) - // to the current data. Adjust volume accordingly. + // to the current data. Adjust volume accordingly. // Q: could this be optimized by converting samples to 16-bit // at load time, while also allowing for stereo samples? // A: Only for the stereo part. You would still look a lookup @@ -736,11 +736,11 @@ private final void submitSound() { } } - /** Drains message queue and applies to individual channels. + /** Drains message queue and applies to individual channels. * More recently enqueued messages will trump older ones. This method - * only changes the STATUS of channels, and actual message submissions - * can occur at most every sound frame. - * + * only changes the STATUS of channels, and actual message submissions + * can occur at most every sound frame. + * * @param messages */ private void drainAndApply(int messages) { @@ -811,7 +811,7 @@ public boolean SoundIsPlaying(int handle) { /** * Internal use. - * + * * @param handle * @return the channel that has the handle, or -2 if none has it. */ @@ -922,4 +922,4 @@ public void run() { protected final AudioChunk SILENT_CHUNK = new AudioChunk(); protected final AudioChunkPool audiochunkpool = new AudioChunkPool(); -} +} \ No newline at end of file diff --git a/src/s/VolumeScalingReceiver.java b/src/s/VolumeScalingReceiver.java index 48a4275..d5408c0 100644 --- a/src/s/VolumeScalingReceiver.java +++ b/src/s/VolumeScalingReceiver.java @@ -21,7 +21,7 @@ import mochadoom.Loggers; /** A {@link Receiver} that scales channel volumes. - * + * * Works by recognising channel volume change events and scaling the new volume * by the global music volume setting before forwarding the event to the * synthesizer. @@ -148,7 +148,7 @@ private float score(MidiDevice.Info info) { } /** Forward a message to the synthesizer. - * + * * If message is a volume change message, the volume is * first multiplied by the global volume. Otherwise, the message is * passed unmodified to the synthesizer. @@ -203,4 +203,4 @@ private int getVolumeChangeChannel(MidiMessage message) { private final Receiver synthReceiver; -} +} \ No newline at end of file diff --git a/src/s/channel_t.java b/src/s/channel_t.java index ae0789e..7df6c1d 100644 --- a/src/s/channel_t.java +++ b/src/s/channel_t.java @@ -27,4 +27,4 @@ public channel_t() { public int sfxVolume; SourceDataLine auline = null; -} +} \ No newline at end of file diff --git a/src/s/degenmobj_t.java b/src/s/degenmobj_t.java index 0075b9f..b632c90 100644 --- a/src/s/degenmobj_t.java +++ b/src/s/degenmobj_t.java @@ -32,4 +32,4 @@ public final int getZ() { return z; } -} +} \ No newline at end of file diff --git a/src/savegame/IDoomSaveGame.java b/src/savegame/IDoomSaveGame.java index 475a45b..506df44 100644 --- a/src/savegame/IDoomSaveGame.java +++ b/src/savegame/IDoomSaveGame.java @@ -15,4 +15,4 @@ public interface IDoomSaveGame { void setHeader(IDoomSaveGameHeader header); boolean doSave(DataOutputStream f); -} +} \ No newline at end of file diff --git a/src/savegame/IDoomSaveGameHeader.java b/src/savegame/IDoomSaveGameHeader.java index 9e5550d..724428a 100644 --- a/src/savegame/IDoomSaveGameHeader.java +++ b/src/savegame/IDoomSaveGameHeader.java @@ -2,10 +2,10 @@ import defines.skill_t; -/** A Save Game Header should be able to be loaded quickly and return +/** A Save Game Header should be able to be loaded quickly and return * some basic info about it (name, version, game time, etc.) in an unified * manner, no matter what actual format you use for saving. - * + * * @author admin * */ @@ -45,4 +45,4 @@ public interface IDoomSaveGameHeader { int getGamemap(); -} +} \ No newline at end of file diff --git a/src/savegame/ILoadSaveGame.java b/src/savegame/ILoadSaveGame.java index f5dea28..0e06499 100644 --- a/src/savegame/ILoadSaveGame.java +++ b/src/savegame/ILoadSaveGame.java @@ -9,4 +9,4 @@ public interface ILoadSaveGame { void doSave(ThinkerList li); void doLoad(ThinkerList li); -} +} \ No newline at end of file diff --git a/src/savegame/VanillaDSG.java b/src/savegame/VanillaDSG.java index 2b2193b..5d3f5b3 100644 --- a/src/savegame/VanillaDSG.java +++ b/src/savegame/VanillaDSG.java @@ -1,879 +1,879 @@ -package savegame; - -import static data.Limits.MAXCEILINGS; -import static data.Limits.MAXPLAYERS; -import data.info; -import doom.DoomMain; -import doom.SourceCode.P_SaveG; -import static doom.SourceCode.P_SaveG.P_ArchivePlayers; -import static doom.SourceCode.P_SaveG.P_ArchiveSpecials; -import static doom.SourceCode.P_SaveG.P_ArchiveThinkers; -import static doom.SourceCode.P_SaveG.P_ArchiveWorld; -import static doom.SourceCode.P_SaveG.P_UnArchivePlayers; -import static doom.SourceCode.P_SaveG.P_UnArchiveSpecials; -import static doom.SourceCode.P_SaveG.P_UnArchiveThinkers; -import static doom.SourceCode.P_SaveG.P_UnArchiveWorld; -import doom.player_t; -import doom.thinker_t; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -import m.Settings; -import mochadoom.Engine; -import mochadoom.Loggers; -import p.Actions.ActionsLights.glow_t; -import p.Actions.ActionsLights.lightflash_t; -import static p.ActiveStates.NOP; -import static p.ActiveStates.P_MobjThinker; -import static p.ActiveStates.T_Glow; -import static p.ActiveStates.T_LightFlash; -import static p.ActiveStates.T_MoveCeiling; -import static p.ActiveStates.T_MoveFloor; -import static p.ActiveStates.T_PlatRaise; -import static p.ActiveStates.T_StrobeFlash; -import static p.ActiveStates.T_VerticalDoor; -import p.ThinkerList; -import p.ceiling_t; -import p.floormove_t; -import p.mobj_t; -import p.plat_t; -import p.strobe_t; -import p.vldoor_t; -import rr.line_t; -import rr.sector_t; -import rr.side_t; -import utils.C2JUtils; - -public class VanillaDSG implements IDoomSaveGame { - - private static final Logger LOGGER = Loggers.getLogger(VanillaDSG.class.getName()); - - VanillaDSGHeader header; - final DoomMain DOOM; - - public VanillaDSG(DoomMain DOOM) { - this.DOOM = DOOM; - } - - @Override - public void setThinkerList(ThinkerList li) { - // TODO Auto-generated method stub - - } - - @Override - public IDoomSaveGameHeader getHeader() { - return header; - } - - @Override - public void setHeader(IDoomSaveGameHeader header) { - this.header = (VanillaDSGHeader) header; - - } - - private DataInputStream f; - private DataOutputStream fo; - private int maxsize; - - @Override - public boolean doLoad(DataInputStream f) { - try { - this.f = f; - maxsize = f.available(); - LOGGER.log(Level.FINE, String.format("Max size %d", maxsize)); - this.header = new VanillaDSGHeader(); - header.read(f); - UnArchivePlayers(); - UnArchiveWorld(); - UnArchiveThinkers(); - UnArchiveSpecials(); - byte terminator = f.readByte(); - return terminator == 0x1D; - } catch (IOException e) { - LOGGER.log(Level.WARNING, e, () - -> String.format("Error while loading savegame! Cause: %s", e.getMessage())); - return false; // Needed to shut up compiler. - } - - } - - /** - * P_UnArchivePlayers - * - * @throws IOException - */ - @P_SaveG.C(P_UnArchivePlayers) - protected void UnArchivePlayers() throws IOException { - int i; - int j; - - for (i = 0; i < MAXPLAYERS; i++) { - // Multiplayer savegames are different! - if (!DOOM.playeringame[i]) { - continue; - } - PADSAVEP(f, maxsize); // this will move us on the 52th byte, instead of 50th. - DOOM.players[i].read(f); - - //memcpy (&players[i],save_p, sizeof(player_t)); - //save_p += sizeof(player_t); - // will be set when unarc thinker - DOOM.players[i].mo = null; - DOOM.players[i].message = null; - DOOM.players[i].attacker = null; - - for (j = 0; j < player_t.NUMPSPRITES; j++) { - if (C2JUtils.eval(DOOM.players[i].psprites[j].state)) { - // MAES HACK to accomoadate state_t type punning a-posteriori - DOOM.players[i].psprites[j].state - = info.states[DOOM.players[i].psprites[j].readstate]; - } - } - } - } - - /** - * P_ArchivePlayers - * - * @throws IOException - */ - @P_SaveG.C(P_ArchivePlayers) - protected void ArchivePlayers() throws IOException { - for (int i = 0; i < MAXPLAYERS; i++) { - // Multiplayer savegames are different! - if (!DOOM.playeringame[i]) { - continue; - } - - PADSAVEP(fo); // this will move us on the 52th byte, instead of 50th. - - // State will have to be serialized when saving. - DOOM.players[i].write(fo); - - //System.out.printf("Player %d has mobj hashcode %d",(1+i),DS.players[i].mo.hashCode()); - } - } - - // - //P_ArchiveWorld - // - @P_SaveG.C(P_ArchiveWorld) - protected void ArchiveWorld() throws IOException { - int i; - int j; - sector_t sec; - line_t li; - side_t si; - - // do sectors (allocate 14 bytes per sector) - ByteBuffer buffer = ByteBuffer.allocate(DOOM.levelLoader.numsectors * 14); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - deAdaptSectors(); - for (i = 0; i < DOOM.levelLoader.numsectors; i++) { - sec = DOOM.levelLoader.sectors[i]; - // MAES: sectors are actually carefully - // marshalled, so we don't just read/write - // their entire memory footprint to disk. - sec.pack(buffer); - } - - adaptSectors(); - fo.write(buffer.array(), 0, buffer.position()); - - // do lines - // Allocate for the worst-case scenario (6+20 per line) - buffer = ByteBuffer.allocate(DOOM.levelLoader.numlines * (6 + 20)); - buffer.order(ByteOrder.LITTLE_ENDIAN); - buffer.position(0); - - //final side_t test1=new side_t(0x11111111,0x11111111,(short) 0x1111,(short)0x1111,(short)0x1111,null); - //final side_t test2=new side_t(0x22222222,0x22222222,(short) 0x2222,(short)0x2222,(short)0x2222,null); - for (i = 0; i < DOOM.levelLoader.numlines; i++) { - li = DOOM.levelLoader.lines[i]; - li.pack(buffer); - - for (j = 0; j < 2; j++) { - if (li.sidenum[j] == line_t.NO_INDEX) { - continue; - } - si = DOOM.levelLoader.sides[li.sidenum[j]]; - si.pack(buffer); - //if (j==0) test1.pack(buffer); - //else test2.pack(buffer); - - } - } - - int write = buffer.position(); - fo.write(buffer.array(), 0, write); - } - - // - //P_UnArchiveWorld - // - @P_SaveG.C(P_UnArchiveWorld) - protected final void UnArchiveWorld() throws IOException { - int i; - int j; - sector_t sec; - line_t li; - side_t si; - // short get; - //get = (short *)save_p; - - //List sectors=new ArrayList(); - // do sectors - for (i = 0; i < DOOM.levelLoader.numsectors; i++) { - sec = DOOM.levelLoader.sectors[i]; - // MAES: sectors were actually carefully - // unmarshalled, so we don't just read/write - // their entire memory footprint to disk. - sec.read(f); - sec.specialdata = null; - sec.soundtarget = null; - } - adaptSectors(); - // do lines - for (i = 0; i < DOOM.levelLoader.numlines; i++) { - li = DOOM.levelLoader.lines[i]; - // MAES: something similar occurs with lines, too. - li.read(f); - //System.out.println("Line "+i+": "+li); - //System.out.print(i+ " {"); - for (j = 0; j < 2; j++) { - // System.out.print(li.sidenum[j]); - // if (j<2) System.out.print(","); - // System.out.printf("Skipped sidenum %d for line %d\n",j,i); - if (li.sidenum[j] == line_t.NO_INDEX) { - // System.out.printf("Skipped sidenum %d for line %d\n",j,i); - continue; - } - // Similarly, sides also get a careful unmarshalling even - // in vanilla. No "dumb" block reads here. - si = DOOM.levelLoader.sides[li.sidenum[j]]; - si.read(f); - - } - //System.out.printf("Position at end of WORLD: %d\n",f.getFilePointer()); - } - - } - - /** - * Convert loaded sectors from vanilla savegames into the internal, - * continuous index progression, by intercepting breaks corresponding to markers. - */ - protected void adaptSectors() { - sector_t sec; - switch (DOOM.getGameMode()) { - case registered: - case shareware: - for (int i = 0; i < DOOM.levelLoader.numsectors; i++) { - sec = DOOM.levelLoader.sectors[i]; - // Between the F1_START and F1_END mark (in vanilla) - if (sec.floorpic <= 54) { - sec.floorpic -= 1; - } else { - // Between the F2_START and F2_END mark (in vanilla) - sec.floorpic -= 3; - } - if (sec.ceilingpic <= 54) { - sec.ceilingpic -= 1; - } else { - // Between the F2_START and F2_END mark (in vanilla) - sec.ceilingpic -= 3; - } - - } - break; - case commercial: - case pack_plut: - case pack_tnt: - for (int i = 0; i < DOOM.levelLoader.numsectors; i++) { - sec = DOOM.levelLoader.sectors[i]; - // Between the F1_START and F1_END mark (in vanilla) - if (sec.floorpic <= 54) { - sec.floorpic -= 1; - } else if (sec.floorpic <= 99) { - // Between the F2_START and F2_END mark (in vanilla) - sec.floorpic -= 3; - } else { - sec.floorpic -= 5; - } - - if (sec.ceilingpic <= 54) { - sec.ceilingpic -= 1; - } else if (sec.ceilingpic <= 99) { - // Between the F2_START and F2_END mark (in vanilla) - sec.ceilingpic -= 3; - } else { - sec.ceilingpic -= 5; - } - - } - default: - break; - } - } - - /** - * De-convert sectors from an absolute to a vanilla-like index - * progression, by adding proper skips - */ - protected void deAdaptSectors() { - sector_t sec; - switch (DOOM.getGameMode()) { - case registered: - case shareware: - for (int i = 0; i < DOOM.levelLoader.numsectors; i++) { - sec = DOOM.levelLoader.sectors[i]; - // Between the F1_START and F1_END mark (in vanilla) - if (sec.floorpic < 54) { - sec.floorpic += 1; - } else { - // Between the F2_START and F2_END mark (in vanilla) - sec.floorpic += 3; - } - if (sec.ceilingpic < 54) { - sec.ceilingpic += 1; - } else { - // Between the F2_START and F2_END mark (in vanilla) - sec.ceilingpic += 3; - } - - } - break; - case commercial: - case pack_plut: - case pack_tnt: - for (int i = 0; i < DOOM.levelLoader.numsectors; i++) { - sec = DOOM.levelLoader.sectors[i]; - // Between the F1_START and F1_END mark (in vanilla) - if (sec.floorpic < 54) { - sec.floorpic += 1; - } else if (sec.floorpic < 99) { - // Between the F2_START and F2_END mark (in vanilla) - sec.floorpic += 3; - } else { - sec.floorpic += 5; - } - - if (sec.ceilingpic < 54) { - sec.ceilingpic += 1; - } else if (sec.ceilingpic < 99) { - // Between the F2_START and F2_END mark (in vanilla) - sec.ceilingpic += 3; - } else { - sec.ceilingpic += 5; - } - - } - default: - break; - } - } - - // - //Thinkers - // - protected enum thinkerclass_t { - tc_end, - tc_mobj; - } - - List TL = new ArrayList<>(); - - // - //P_ArchiveThinkers - // - @P_SaveG.C(P_ArchiveThinkers) - protected void ArchiveThinkers() throws IOException { - thinker_t th; - mobj_t mobj; - - // save off the current thinkers - for (th = DOOM.actions.getThinkerCap().next; th != DOOM.actions.getThinkerCap(); th = th.next) { - if (th.thinkerFunction == P_MobjThinker) { - // Indicate valid thinker - fo.writeByte(thinkerclass_t.tc_mobj.ordinal()); - // Pad... - PADSAVEP(fo); - mobj = (mobj_t) th; - mobj.write(fo); - - // MAES: state is explicit in state.id - // save_p += sizeof(*mobj); - // mobj->state = (state_t *)(mobj->state - states); - // MAES: player is automatically generated at runtime and handled by the writer. - //if (mobj->player) - //mobj->player = (player_t *)((mobj->player-players) + 1); - } - - // I_Error ("P_ArchiveThinkers: Unknown thinker function"); - } - - // add a terminating marker - fo.writeByte(thinkerclass_t.tc_end.ordinal()); - - } - - // - //P_UnArchiveThinkers - // - @P_SaveG.C(P_UnArchiveThinkers) - protected void UnArchiveThinkers() throws IOException { - thinkerclass_t tclass; // was "byte", therefore unsigned - thinker_t currentthinker; - thinker_t next; - mobj_t mobj; - int id = 0; - - // remove all the current thinkers - currentthinker = DOOM.actions.getThinkerCap().next; - while (currentthinker != null && currentthinker != DOOM.actions.getThinkerCap()) { - next = currentthinker.next; - - if (currentthinker.thinkerFunction == P_MobjThinker) { - DOOM.actions.RemoveMobj((mobj_t) currentthinker); - }// else { - //currentthinker.next.prev=currentthinker.prev; - //currentthinker.prev.next=currentthinker.next; - //currentthinker = null; - //} - - currentthinker = next; - } - - DOOM.actions.InitThinkers(); - - // read in saved thinkers - boolean end = false; - while (!end) { - int tmp = f.readUnsignedByte(); - tclass = thinkerclass_t.values()[tmp]; - switch (tclass) { - case tc_end: - // That's how we know when to stop. - end = true; - break; // end of list - - case tc_mobj: - PADSAVEP(f, maxsize); - mobj = mobj_t.createOn(DOOM); - mobj.read(f); - mobj.id = ++id; - TL.add(mobj); - mobj.mobj_state = info.states[mobj.stateid]; - mobj.target = null; - if (mobj.playerid != 0) { - mobj.player = DOOM.players[mobj.playerid - 1]; - mobj.player.mo = mobj; - - } - DOOM.levelLoader.SetThingPosition(mobj); - mobj.info = info.mobjinfo[mobj.type.ordinal()]; - mobj.floorz = mobj.subsector.sector.floorheight; - mobj.ceilingz = mobj.subsector.sector.ceilingheight; - mobj.thinkerFunction = P_MobjThinker; - DOOM.actions.AddThinker(mobj); - break; - - default: - DOOM.doomSystem.Error("Unknown tclass %d in savegame", tclass); - } - } - - if (Engine.getConfig().equals(Settings.reconstruct_savegame_pointers, Boolean.TRUE)) { - reconstructPointers(); - rewirePointers(); - } - } - - final HashMap pointindex = new HashMap<>(); - - /** - * Allows reconstructing infighting targets from stored pointers/indices. - * Works even with vanilla savegames as long as whatever it is that you - * store is unique. A good choice would be progressive indices or hash values. - * - */ - protected void reconstructPointers() { - - int player = 0; - - for (mobj_t th : TL) { - - if (th.player != null) { - player = th.id; - // Player found, so that's our first key. - pointindex.put(th.player.p_mobj, th); - } - } - - if (player == 0) { - LOGGER.log(Level.WARNING, - "Player not found, cannot reconstruct pointers!"); - return; - } - - int curr; // next or prev index - - // We start from the player's index, if found. - // We subtract -1 so it matches that inside the thinkers list. - for (int i = (player - 1); i < TL.size() - 1; i++) { - // Get "next" pointer. - curr = TL.get(i).nextid; - pointindex.put(curr, TL.get(i + 1)); - } - - // We also search backwards, in case player wasn't first object - // (can this even happen, in vanilla?) - // -1 so it matches that of the TL list. - for (int i = (player - 1); i > 0; i--) { - // Get "prev" pointer. - curr = TL.get(i).previd; - pointindex.put(curr, TL.get(i - 1)); - } - - } - - /** - * Allows reconstructing infighting targets from stored pointers/indices from - * the hashtable created by reconstructPointers. - * - */ - protected void rewirePointers() { - TL.forEach(th -> { - if (th.p_target != 0) { - th.target = pointindex.get(th.p_target); - th.tracer = pointindex.get(th.p_tracer); - // System.out.printf("Object %s has target %s\n",th.type.toString(),th.target.type.toString()); - } - }); - } - - protected enum specials_e { - tc_ceiling, - tc_door, - tc_floor, - tc_plat, - tc_flash, - tc_strobe, - tc_glow, - tc_endspecials - - }; - - // - //P_ArchiveSpecials - // - @P_SaveG.C(P_ArchiveSpecials) - protected void ArchiveSpecials() throws IOException { - ceiling_t ceiling; - vldoor_t door; - floormove_t floor; - plat_t plat; - lightflash_t flash; - strobe_t strobe; - glow_t glow; - int i; - - // Most of these objects are quite hefty, but estimating 128 bytes tops - // for each should do (largest one is 56); - ByteBuffer buffer = ByteBuffer.allocate(128); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - // save off the current thinkers - for (thinker_t th = DOOM.actions.getThinkerCap().next; th != DOOM.actions.getThinkerCap(); th = th.next) { - - // Write out any pending objects. - if (buffer.position() > 0) { - fo.write(buffer.array(), 0, buffer.position()); - //System.out.println("Wrote out "+buffer.position()+" bytes"); - - } - - // Back to the beginning. - buffer.position(0); - - // So ceilings don't think? - if (th.thinkerFunction == NOP) { - // i maintains status between iterations - for (i = 0; i < DOOM.actions.getMaxCeilings(); i++) { - if ((th instanceof ceiling_t) && (DOOM.actions.getActiveCeilings()[i] == (ceiling_t) th)) { - break; - } - } - - if (i < MAXCEILINGS) { - fo.writeByte(specials_e.tc_ceiling.ordinal()); - PADSAVEP(fo); - // Set id for saving - ceiling = (ceiling_t) th; - ceiling.sectorid = ceiling.sector.id; - ceiling.pack(buffer); - } - continue; - } - - // Well, apparently some do. - if (th.thinkerFunction == T_MoveCeiling) { - - fo.writeByte(specials_e.tc_ceiling.ordinal()); - PADSAVEP(fo); - ceiling = (ceiling_t) th; - ceiling.sectorid = ceiling.sector.id; - ceiling.pack(buffer); - continue; - } - - // Well, apparently some do. - if (th.thinkerFunction == T_VerticalDoor) { - - fo.writeByte(specials_e.tc_door.ordinal()); - PADSAVEP(fo); - door = (vldoor_t) th; - door.sectorid = door.sector.id; - door.pack(buffer); - continue; - } - - // Well, apparently some do. - if (th.thinkerFunction == T_MoveFloor) { - fo.writeByte(specials_e.tc_floor.ordinal()); - PADSAVEP(fo); - floor = (floormove_t) th; - floor.sectorid = floor.sector.id; - floor.pack(buffer); - continue; - } - - // Well, apparently some do. - if (th.thinkerFunction == T_PlatRaise) { - fo.writeByte(specials_e.tc_plat.ordinal()); - PADSAVEP(fo); - plat = (plat_t) th; - plat.sectorid = plat.sector.id; - plat.pack(buffer); - continue; - } - - // Well, apparently some do. - if (th.thinkerFunction == T_LightFlash) { - fo.writeByte(specials_e.tc_flash.ordinal()); - PADSAVEP(fo); - flash = (lightflash_t) th; - flash.sectorid = flash.sector.id; - flash.pack(buffer); - continue; - } - - // Well, apparently some do. - if (th.thinkerFunction == T_StrobeFlash) { - fo.writeByte(specials_e.tc_strobe.ordinal()); - PADSAVEP(fo); - strobe = (strobe_t) th; - strobe.sectorid = strobe.sector.id; - strobe.pack(buffer); - continue; - } - - // Well, apparently some do. - if (th.thinkerFunction == T_Glow) { - fo.writeByte(specials_e.tc_glow.ordinal()); - PADSAVEP(fo); - glow = (glow_t) th; - glow.sectorid = glow.sector.id; - glow.pack(buffer); - } - } - - if (buffer.position() > 0) { - fo.write(buffer.array(), 0, buffer.position()); - } - - // Finito! - fo.writeByte((byte) specials_e.tc_endspecials.ordinal()); - } - - // - //P_UnArchiveSpecials - // - @P_SaveG.C(P_UnArchiveSpecials) - protected void UnArchiveSpecials() throws IOException { - specials_e tclass; - ceiling_t ceiling; - vldoor_t door; - floormove_t floor; - plat_t plat; - lightflash_t flash; - strobe_t strobe; - glow_t glow; - - //List A=new ArrayList(); - DOOM.actions.ClearPlatsBeforeLoading(); - DOOM.actions.ClearCeilingsBeforeLoading(); - - // read in saved thinkers - while (true) { - int tmp = f.readUnsignedByte(); - //tmp&=0x00ff; // To "unsigned byte" - tclass = specials_e.values()[tmp]; - switch (tclass) { - case tc_endspecials: - return; // end of list - - case tc_ceiling: - PADSAVEP(f, maxsize); - ceiling = new ceiling_t(); - ceiling.read(f); - ceiling.sector = DOOM.levelLoader.sectors[ceiling.sectorid]; - ceiling.sector.specialdata = ceiling; - - if (ceiling.functionid != 0) { - ceiling.thinkerFunction = T_MoveCeiling; - } - - DOOM.actions.AddThinker(ceiling); - DOOM.actions.AddActiveCeiling(ceiling); - break; - - case tc_door: - PADSAVEP(f, maxsize); - door = new vldoor_t(); - door.read(f); - door.sector = DOOM.levelLoader.sectors[door.sectorid]; - door.sector.specialdata = door; - door.thinkerFunction = T_VerticalDoor; - - DOOM.actions.AddThinker(door); - break; - - case tc_floor: - PADSAVEP(f, maxsize); - floor = new floormove_t(); - floor.read(f); - floor.sector = DOOM.levelLoader.sectors[floor.sectorid]; - floor.sector.specialdata = floor; - floor.thinkerFunction = T_MoveFloor; - - DOOM.actions.AddThinker(floor); - break; - - case tc_plat: - PADSAVEP(f, maxsize); - plat = new plat_t(); - plat.read(f); - plat.sector = DOOM.levelLoader.sectors[plat.sectorid]; - plat.sector.specialdata = plat; - - if (plat.functionid != 0) { - plat.thinkerFunction = T_PlatRaise; - } - - DOOM.actions.AddThinker(plat); - DOOM.actions.AddActivePlat(plat); - break; - - case tc_flash: - PADSAVEP(f, maxsize); - flash = new lightflash_t(); - flash.read(f); - flash.sector = DOOM.levelLoader.sectors[flash.sectorid]; - flash.thinkerFunction = T_LightFlash; - - DOOM.actions.AddThinker(flash); - break; - - case tc_strobe: - PADSAVEP(f, maxsize); - strobe = new strobe_t(); - strobe.read(f); - strobe.sector = DOOM.levelLoader.sectors[strobe.sectorid]; - strobe.thinkerFunction = T_StrobeFlash; - - DOOM.actions.AddThinker(strobe); - break; - - case tc_glow: - PADSAVEP(f, maxsize); - glow = new glow_t(); - glow.read(f); - glow.sector = DOOM.levelLoader.sectors[glow.sectorid]; - glow.thinkerFunction = T_Glow; - - DOOM.actions.AddThinker(glow); - break; - - default: - DOOM.doomSystem.Error("P_UnarchiveSpecials:Unknown tclass %d in savegame", tmp); - } - } - - } - - /** - * Pads save_p to a 4-byte boundary - * so that the load/save works on SGI&Gecko. - * - * @param save_p - */ - protected final int PADSAVEP(int save_p) { - return (save_p + ((4 - (save_p & 3)) & 3)); - } - - //protected final int PADSAVEP(ByteBuffer b, int save_p){ - // ByteBuffer - // return (save_p += (4 - ((int) save_p & 3)) & 3); - //} - protected final long PADSAVEP(DataInputStream f, int maxsize) throws IOException { - long save_p = maxsize - f.available(); - int padding = (4 - ((int) save_p & 3)) & 3; - // System.out.printf("Current position %d Padding by %d bytes %d\n",save_p,padding,maxsize); - f.skip(padding); - return padding; - } - - protected final long PADSAVEP(DataOutputStream f) throws IOException { - long save_p = f.size(); - int padding = (4 - ((int) save_p & 3)) & 3; - // System.out.printf("Current position %d Padding by %d bytes\n",save_p,padding); - for (int i = 0; i < padding; i++) { - f.write(0); - } - return padding; - } - - @Override - public boolean doSave(DataOutputStream f) { - try { - // The header must have been set, at this point. - this.fo = f; - //f.setLength(0); // Kill old info. - header.write(f); - - //header.read(f); - ArchivePlayers(); - ArchiveWorld(); - ArchiveThinkers(); - ArchiveSpecials(); - // TODO: the rest... - f.write(0x1D); - } catch (IOException e) { - LOGGER.log(Level.WARNING, e, () - -> String.format("Error while saving savegame! Cause: %s", e.getMessage())); - return false; // Needed to shut up compiler. - } - return true; - } - -} +package savegame; + +import static data.Limits.MAXCEILINGS; +import static data.Limits.MAXPLAYERS; +import data.info; +import doom.DoomMain; +import doom.SourceCode.P_SaveG; +import static doom.SourceCode.P_SaveG.P_ArchivePlayers; +import static doom.SourceCode.P_SaveG.P_ArchiveSpecials; +import static doom.SourceCode.P_SaveG.P_ArchiveThinkers; +import static doom.SourceCode.P_SaveG.P_ArchiveWorld; +import static doom.SourceCode.P_SaveG.P_UnArchivePlayers; +import static doom.SourceCode.P_SaveG.P_UnArchiveSpecials; +import static doom.SourceCode.P_SaveG.P_UnArchiveThinkers; +import static doom.SourceCode.P_SaveG.P_UnArchiveWorld; +import doom.player_t; +import doom.thinker_t; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import m.Settings; +import mochadoom.Engine; +import mochadoom.Loggers; +import p.Actions.ActionsLights.glow_t; +import p.Actions.ActionsLights.lightflash_t; +import static p.ActiveStates.NOP; +import static p.ActiveStates.P_MobjThinker; +import static p.ActiveStates.T_Glow; +import static p.ActiveStates.T_LightFlash; +import static p.ActiveStates.T_MoveCeiling; +import static p.ActiveStates.T_MoveFloor; +import static p.ActiveStates.T_PlatRaise; +import static p.ActiveStates.T_StrobeFlash; +import static p.ActiveStates.T_VerticalDoor; +import p.ThinkerList; +import p.ceiling_t; +import p.floormove_t; +import p.mobj_t; +import p.plat_t; +import p.strobe_t; +import p.vldoor_t; +import rr.line_t; +import rr.sector_t; +import rr.side_t; +import utils.C2JUtils; + +public class VanillaDSG implements IDoomSaveGame { + + private static final Logger LOGGER = Loggers.getLogger(VanillaDSG.class.getName()); + + VanillaDSGHeader header; + final DoomMain DOOM; + + public VanillaDSG(DoomMain DOOM) { + this.DOOM = DOOM; + } + + @Override + public void setThinkerList(ThinkerList li) { + // TODO Auto-generated method stub + + } + + @Override + public IDoomSaveGameHeader getHeader() { + return header; + } + + @Override + public void setHeader(IDoomSaveGameHeader header) { + this.header = (VanillaDSGHeader) header; + + } + + private DataInputStream f; + private DataOutputStream fo; + private int maxsize; + + @Override + public boolean doLoad(DataInputStream f) { + try { + this.f = f; + maxsize = f.available(); + LOGGER.log(Level.FINE, String.format("Max size %d", maxsize)); + this.header = new VanillaDSGHeader(); + header.read(f); + UnArchivePlayers(); + UnArchiveWorld(); + UnArchiveThinkers(); + UnArchiveSpecials(); + byte terminator = f.readByte(); + return terminator == 0x1D; + } catch (IOException e) { + LOGGER.log(Level.WARNING, e, () + -> String.format("Error while loading savegame! Cause: %s", e.getMessage())); + return false; // Needed to shut up compiler. + } + + } + + /** + * P_UnArchivePlayers + * + * @throws IOException + */ + @P_SaveG.C(P_UnArchivePlayers) + protected void UnArchivePlayers() throws IOException { + int i; + int j; + + for (i = 0; i < MAXPLAYERS; i++) { + // Multiplayer savegames are different! + if (!DOOM.playeringame[i]) { + continue; + } + PADSAVEP(f, maxsize); // this will move us on the 52th byte, instead of 50th. + DOOM.players[i].read(f); + + //memcpy (&players[i],save_p, sizeof(player_t)); + //save_p += sizeof(player_t); + // will be set when unarc thinker + DOOM.players[i].mo = null; + DOOM.players[i].message = null; + DOOM.players[i].attacker = null; + + for (j = 0; j < player_t.NUMPSPRITES; j++) { + if (C2JUtils.eval(DOOM.players[i].psprites[j].state)) { + // MAES HACK to accomoadate state_t type punning a-posteriori + DOOM.players[i].psprites[j].state + = info.states[DOOM.players[i].psprites[j].readstate]; + } + } + } + } + + /** + * P_ArchivePlayers + * + * @throws IOException + */ + @P_SaveG.C(P_ArchivePlayers) + protected void ArchivePlayers() throws IOException { + for (int i = 0; i < MAXPLAYERS; i++) { + // Multiplayer savegames are different! + if (!DOOM.playeringame[i]) { + continue; + } + + PADSAVEP(fo); // this will move us on the 52th byte, instead of 50th. + + // State will have to be serialized when saving. + DOOM.players[i].write(fo); + + //System.out.printf("Player %d has mobj hashcode %d",(1+i),DS.players[i].mo.hashCode()); + } + } + + // + //P_ArchiveWorld + // + @P_SaveG.C(P_ArchiveWorld) + protected void ArchiveWorld() throws IOException { + int i; + int j; + sector_t sec; + line_t li; + side_t si; + + // do sectors (allocate 14 bytes per sector) + ByteBuffer buffer = ByteBuffer.allocate(DOOM.levelLoader.numsectors * 14); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + deAdaptSectors(); + for (i = 0; i < DOOM.levelLoader.numsectors; i++) { + sec = DOOM.levelLoader.sectors[i]; + // MAES: sectors are actually carefully + // marshalled, so we don't just read/write + // their entire memory footprint to disk. + sec.pack(buffer); + } + + adaptSectors(); + fo.write(buffer.array(), 0, buffer.position()); + + // do lines + // Allocate for the worst-case scenario (6+20 per line) + buffer = ByteBuffer.allocate(DOOM.levelLoader.numlines * (6 + 20)); + buffer.order(ByteOrder.LITTLE_ENDIAN); + buffer.position(0); + + //final side_t test1=new side_t(0x11111111,0x11111111,(short) 0x1111,(short)0x1111,(short)0x1111,null); + //final side_t test2=new side_t(0x22222222,0x22222222,(short) 0x2222,(short)0x2222,(short)0x2222,null); + for (i = 0; i < DOOM.levelLoader.numlines; i++) { + li = DOOM.levelLoader.lines[i]; + li.pack(buffer); + + for (j = 0; j < 2; j++) { + if (li.sidenum[j] == line_t.NO_INDEX) { + continue; + } + si = DOOM.levelLoader.sides[li.sidenum[j]]; + si.pack(buffer); + //if (j==0) test1.pack(buffer); + //else test2.pack(buffer); + + } + } + + int write = buffer.position(); + fo.write(buffer.array(), 0, write); + } + + // + //P_UnArchiveWorld + // + @P_SaveG.C(P_UnArchiveWorld) + protected final void UnArchiveWorld() throws IOException { + int i; + int j; + sector_t sec; + line_t li; + side_t si; + // short get; + //get = (short *)save_p; + + //List sectors=new ArrayList(); + // do sectors + for (i = 0; i < DOOM.levelLoader.numsectors; i++) { + sec = DOOM.levelLoader.sectors[i]; + // MAES: sectors were actually carefully + // unmarshalled, so we don't just read/write + // their entire memory footprint to disk. + sec.read(f); + sec.specialdata = null; + sec.soundtarget = null; + } + adaptSectors(); + // do lines + for (i = 0; i < DOOM.levelLoader.numlines; i++) { + li = DOOM.levelLoader.lines[i]; + // MAES: something similar occurs with lines, too. + li.read(f); + //System.out.println("Line "+i+": "+li); + //System.out.print(i+ " {"); + for (j = 0; j < 2; j++) { + // System.out.print(li.sidenum[j]); + // if (j<2) System.out.print(","); + // System.out.printf("Skipped sidenum %d for line %d\n",j,i); + if (li.sidenum[j] == line_t.NO_INDEX) { + // System.out.printf("Skipped sidenum %d for line %d\n",j,i); + continue; + } + // Similarly, sides also get a careful unmarshalling even + // in vanilla. No "dumb" block reads here. + si = DOOM.levelLoader.sides[li.sidenum[j]]; + si.read(f); + + } + //System.out.printf("Position at end of WORLD: %d\n",f.getFilePointer()); + } + + } + + /** + * Convert loaded sectors from vanilla savegames into the internal, + * continuous index progression, by intercepting breaks corresponding to markers. + */ + protected void adaptSectors() { + sector_t sec; + switch (DOOM.getGameMode()) { + case registered: + case shareware: + for (int i = 0; i < DOOM.levelLoader.numsectors; i++) { + sec = DOOM.levelLoader.sectors[i]; + // Between the F1_START and F1_END mark (in vanilla) + if (sec.floorpic <= 54) { + sec.floorpic -= 1; + } else { + // Between the F2_START and F2_END mark (in vanilla) + sec.floorpic -= 3; + } + if (sec.ceilingpic <= 54) { + sec.ceilingpic -= 1; + } else { + // Between the F2_START and F2_END mark (in vanilla) + sec.ceilingpic -= 3; + } + + } + break; + case commercial: + case pack_plut: + case pack_tnt: + for (int i = 0; i < DOOM.levelLoader.numsectors; i++) { + sec = DOOM.levelLoader.sectors[i]; + // Between the F1_START and F1_END mark (in vanilla) + if (sec.floorpic <= 54) { + sec.floorpic -= 1; + } else if (sec.floorpic <= 99) { + // Between the F2_START and F2_END mark (in vanilla) + sec.floorpic -= 3; + } else { + sec.floorpic -= 5; + } + + if (sec.ceilingpic <= 54) { + sec.ceilingpic -= 1; + } else if (sec.ceilingpic <= 99) { + // Between the F2_START and F2_END mark (in vanilla) + sec.ceilingpic -= 3; + } else { + sec.ceilingpic -= 5; + } + + } + default: + break; + } + } + + /** + * De-convert sectors from an absolute to a vanilla-like index + * progression, by adding proper skips + */ + protected void deAdaptSectors() { + sector_t sec; + switch (DOOM.getGameMode()) { + case registered: + case shareware: + for (int i = 0; i < DOOM.levelLoader.numsectors; i++) { + sec = DOOM.levelLoader.sectors[i]; + // Between the F1_START and F1_END mark (in vanilla) + if (sec.floorpic < 54) { + sec.floorpic += 1; + } else { + // Between the F2_START and F2_END mark (in vanilla) + sec.floorpic += 3; + } + if (sec.ceilingpic < 54) { + sec.ceilingpic += 1; + } else { + // Between the F2_START and F2_END mark (in vanilla) + sec.ceilingpic += 3; + } + + } + break; + case commercial: + case pack_plut: + case pack_tnt: + for (int i = 0; i < DOOM.levelLoader.numsectors; i++) { + sec = DOOM.levelLoader.sectors[i]; + // Between the F1_START and F1_END mark (in vanilla) + if (sec.floorpic < 54) { + sec.floorpic += 1; + } else if (sec.floorpic < 99) { + // Between the F2_START and F2_END mark (in vanilla) + sec.floorpic += 3; + } else { + sec.floorpic += 5; + } + + if (sec.ceilingpic < 54) { + sec.ceilingpic += 1; + } else if (sec.ceilingpic < 99) { + // Between the F2_START and F2_END mark (in vanilla) + sec.ceilingpic += 3; + } else { + sec.ceilingpic += 5; + } + + } + default: + break; + } + } + + // + //Thinkers + // + protected enum thinkerclass_t { + tc_end, + tc_mobj; + } + + List TL = new ArrayList<>(); + + // + //P_ArchiveThinkers + // + @P_SaveG.C(P_ArchiveThinkers) + protected void ArchiveThinkers() throws IOException { + thinker_t th; + mobj_t mobj; + + // save off the current thinkers + for (th = DOOM.actions.getThinkerCap().next; th != DOOM.actions.getThinkerCap(); th = th.next) { + if (th.thinkerFunction == P_MobjThinker) { + // Indicate valid thinker + fo.writeByte(thinkerclass_t.tc_mobj.ordinal()); + // Pad... + PADSAVEP(fo); + mobj = (mobj_t) th; + mobj.write(fo); + + // MAES: state is explicit in state.id + // save_p += sizeof(*mobj); + // mobj->state = (state_t *)(mobj->state - states); + // MAES: player is automatically generated at runtime and handled by the writer. + //if (mobj->player) + //mobj->player = (player_t *)((mobj->player-players) + 1); + } + + // I_Error ("P_ArchiveThinkers: Unknown thinker function"); + } + + // add a terminating marker + fo.writeByte(thinkerclass_t.tc_end.ordinal()); + + } + + // + //P_UnArchiveThinkers + // + @P_SaveG.C(P_UnArchiveThinkers) + protected void UnArchiveThinkers() throws IOException { + thinkerclass_t tclass; // was "byte", therefore unsigned + thinker_t currentthinker; + thinker_t next; + mobj_t mobj; + int id = 0; + + // remove all the current thinkers + currentthinker = DOOM.actions.getThinkerCap().next; + while (currentthinker != null && currentthinker != DOOM.actions.getThinkerCap()) { + next = currentthinker.next; + + if (currentthinker.thinkerFunction == P_MobjThinker) { + DOOM.actions.RemoveMobj((mobj_t) currentthinker); + }// else { + //currentthinker.next.prev=currentthinker.prev; + //currentthinker.prev.next=currentthinker.next; + //currentthinker = null; + //} + + currentthinker = next; + } + + DOOM.actions.InitThinkers(); + + // read in saved thinkers + boolean end = false; + while (!end) { + int tmp = f.readUnsignedByte(); + tclass = thinkerclass_t.values()[tmp]; + switch (tclass) { + case tc_end: + // That's how we know when to stop. + end = true; + break; // end of list + + case tc_mobj: + PADSAVEP(f, maxsize); + mobj = mobj_t.createOn(DOOM); + mobj.read(f); + mobj.id = ++id; + TL.add(mobj); + mobj.mobj_state = info.states[mobj.stateid]; + mobj.target = null; + if (mobj.playerid != 0) { + mobj.player = DOOM.players[mobj.playerid - 1]; + mobj.player.mo = mobj; + + } + DOOM.levelLoader.SetThingPosition(mobj); + mobj.info = info.mobjinfo[mobj.type.ordinal()]; + mobj.floorz = mobj.subsector.sector.floorheight; + mobj.ceilingz = mobj.subsector.sector.ceilingheight; + mobj.thinkerFunction = P_MobjThinker; + DOOM.actions.AddThinker(mobj); + break; + + default: + DOOM.doomSystem.Error("Unknown tclass %d in savegame", tclass); + } + } + + if (Engine.getConfig().equals(Settings.reconstruct_savegame_pointers, Boolean.TRUE)) { + reconstructPointers(); + rewirePointers(); + } + } + + final HashMap pointindex = new HashMap<>(); + + /** + * Allows reconstructing infighting targets from stored pointers/indices. + * Works even with vanilla savegames as long as whatever it is that you + * store is unique. A good choice would be progressive indices or hash values. + * + */ + protected void reconstructPointers() { + + int player = 0; + + for (mobj_t th : TL) { + + if (th.player != null) { + player = th.id; + // Player found, so that's our first key. + pointindex.put(th.player.p_mobj, th); + } + } + + if (player == 0) { + LOGGER.log(Level.WARNING, + "Player not found, cannot reconstruct pointers!"); + return; + } + + int curr; // next or prev index + + // We start from the player's index, if found. + // We subtract -1 so it matches that inside the thinkers list. + for (int i = (player - 1); i < TL.size() - 1; i++) { + // Get "next" pointer. + curr = TL.get(i).nextid; + pointindex.put(curr, TL.get(i + 1)); + } + + // We also search backwards, in case player wasn't first object + // (can this even happen, in vanilla?) + // -1 so it matches that of the TL list. + for (int i = (player - 1); i > 0; i--) { + // Get "prev" pointer. + curr = TL.get(i).previd; + pointindex.put(curr, TL.get(i - 1)); + } + + } + + /** + * Allows reconstructing infighting targets from stored pointers/indices from + * the hashtable created by reconstructPointers. + * + */ + protected void rewirePointers() { + TL.forEach(th -> { + if (th.p_target != 0) { + th.target = pointindex.get(th.p_target); + th.tracer = pointindex.get(th.p_tracer); + // System.out.printf("Object %s has target %s\n",th.type.toString(),th.target.type.toString()); + } + }); + } + + protected enum specials_e { + tc_ceiling, + tc_door, + tc_floor, + tc_plat, + tc_flash, + tc_strobe, + tc_glow, + tc_endspecials + + }; + + // + //P_ArchiveSpecials + // + @P_SaveG.C(P_ArchiveSpecials) + protected void ArchiveSpecials() throws IOException { + ceiling_t ceiling; + vldoor_t door; + floormove_t floor; + plat_t plat; + lightflash_t flash; + strobe_t strobe; + glow_t glow; + int i; + + // Most of these objects are quite hefty, but estimating 128 bytes tops + // for each should do (largest one is 56); + ByteBuffer buffer = ByteBuffer.allocate(128); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // save off the current thinkers + for (thinker_t th = DOOM.actions.getThinkerCap().next; th != DOOM.actions.getThinkerCap(); th = th.next) { + + // Write out any pending objects. + if (buffer.position() > 0) { + fo.write(buffer.array(), 0, buffer.position()); + //System.out.println("Wrote out "+buffer.position()+" bytes"); + + } + + // Back to the beginning. + buffer.position(0); + + // So ceilings don't think? + if (th.thinkerFunction == NOP) { + // i maintains status between iterations + for (i = 0; i < DOOM.actions.getMaxCeilings(); i++) { + if ((th instanceof ceiling_t) && (DOOM.actions.getActiveCeilings()[i] == (ceiling_t) th)) { + break; + } + } + + if (i < MAXCEILINGS) { + fo.writeByte(specials_e.tc_ceiling.ordinal()); + PADSAVEP(fo); + // Set id for saving + ceiling = (ceiling_t) th; + ceiling.sectorid = ceiling.sector.id; + ceiling.pack(buffer); + } + continue; + } + + // Well, apparently some do. + if (th.thinkerFunction == T_MoveCeiling) { + + fo.writeByte(specials_e.tc_ceiling.ordinal()); + PADSAVEP(fo); + ceiling = (ceiling_t) th; + ceiling.sectorid = ceiling.sector.id; + ceiling.pack(buffer); + continue; + } + + // Well, apparently some do. + if (th.thinkerFunction == T_VerticalDoor) { + + fo.writeByte(specials_e.tc_door.ordinal()); + PADSAVEP(fo); + door = (vldoor_t) th; + door.sectorid = door.sector.id; + door.pack(buffer); + continue; + } + + // Well, apparently some do. + if (th.thinkerFunction == T_MoveFloor) { + fo.writeByte(specials_e.tc_floor.ordinal()); + PADSAVEP(fo); + floor = (floormove_t) th; + floor.sectorid = floor.sector.id; + floor.pack(buffer); + continue; + } + + // Well, apparently some do. + if (th.thinkerFunction == T_PlatRaise) { + fo.writeByte(specials_e.tc_plat.ordinal()); + PADSAVEP(fo); + plat = (plat_t) th; + plat.sectorid = plat.sector.id; + plat.pack(buffer); + continue; + } + + // Well, apparently some do. + if (th.thinkerFunction == T_LightFlash) { + fo.writeByte(specials_e.tc_flash.ordinal()); + PADSAVEP(fo); + flash = (lightflash_t) th; + flash.sectorid = flash.sector.id; + flash.pack(buffer); + continue; + } + + // Well, apparently some do. + if (th.thinkerFunction == T_StrobeFlash) { + fo.writeByte(specials_e.tc_strobe.ordinal()); + PADSAVEP(fo); + strobe = (strobe_t) th; + strobe.sectorid = strobe.sector.id; + strobe.pack(buffer); + continue; + } + + // Well, apparently some do. + if (th.thinkerFunction == T_Glow) { + fo.writeByte(specials_e.tc_glow.ordinal()); + PADSAVEP(fo); + glow = (glow_t) th; + glow.sectorid = glow.sector.id; + glow.pack(buffer); + } + } + + if (buffer.position() > 0) { + fo.write(buffer.array(), 0, buffer.position()); + } + + // Finito! + fo.writeByte((byte) specials_e.tc_endspecials.ordinal()); + } + + // + //P_UnArchiveSpecials + // + @P_SaveG.C(P_UnArchiveSpecials) + protected void UnArchiveSpecials() throws IOException { + specials_e tclass; + ceiling_t ceiling; + vldoor_t door; + floormove_t floor; + plat_t plat; + lightflash_t flash; + strobe_t strobe; + glow_t glow; + + //List A=new ArrayList(); + DOOM.actions.ClearPlatsBeforeLoading(); + DOOM.actions.ClearCeilingsBeforeLoading(); + + // read in saved thinkers + while (true) { + int tmp = f.readUnsignedByte(); + //tmp&=0x00ff; // To "unsigned byte" + tclass = specials_e.values()[tmp]; + switch (tclass) { + case tc_endspecials: + return; // end of list + + case tc_ceiling: + PADSAVEP(f, maxsize); + ceiling = new ceiling_t(); + ceiling.read(f); + ceiling.sector = DOOM.levelLoader.sectors[ceiling.sectorid]; + ceiling.sector.specialdata = ceiling; + + if (ceiling.functionid != 0) { + ceiling.thinkerFunction = T_MoveCeiling; + } + + DOOM.actions.AddThinker(ceiling); + DOOM.actions.AddActiveCeiling(ceiling); + break; + + case tc_door: + PADSAVEP(f, maxsize); + door = new vldoor_t(); + door.read(f); + door.sector = DOOM.levelLoader.sectors[door.sectorid]; + door.sector.specialdata = door; + door.thinkerFunction = T_VerticalDoor; + + DOOM.actions.AddThinker(door); + break; + + case tc_floor: + PADSAVEP(f, maxsize); + floor = new floormove_t(); + floor.read(f); + floor.sector = DOOM.levelLoader.sectors[floor.sectorid]; + floor.sector.specialdata = floor; + floor.thinkerFunction = T_MoveFloor; + + DOOM.actions.AddThinker(floor); + break; + + case tc_plat: + PADSAVEP(f, maxsize); + plat = new plat_t(); + plat.read(f); + plat.sector = DOOM.levelLoader.sectors[plat.sectorid]; + plat.sector.specialdata = plat; + + if (plat.functionid != 0) { + plat.thinkerFunction = T_PlatRaise; + } + + DOOM.actions.AddThinker(plat); + DOOM.actions.AddActivePlat(plat); + break; + + case tc_flash: + PADSAVEP(f, maxsize); + flash = new lightflash_t(); + flash.read(f); + flash.sector = DOOM.levelLoader.sectors[flash.sectorid]; + flash.thinkerFunction = T_LightFlash; + + DOOM.actions.AddThinker(flash); + break; + + case tc_strobe: + PADSAVEP(f, maxsize); + strobe = new strobe_t(); + strobe.read(f); + strobe.sector = DOOM.levelLoader.sectors[strobe.sectorid]; + strobe.thinkerFunction = T_StrobeFlash; + + DOOM.actions.AddThinker(strobe); + break; + + case tc_glow: + PADSAVEP(f, maxsize); + glow = new glow_t(); + glow.read(f); + glow.sector = DOOM.levelLoader.sectors[glow.sectorid]; + glow.thinkerFunction = T_Glow; + + DOOM.actions.AddThinker(glow); + break; + + default: + DOOM.doomSystem.Error("P_UnarchiveSpecials:Unknown tclass %d in savegame", tmp); + } + } + + } + + /** + * Pads save_p to a 4-byte boundary + * so that the load/save works on SGI&Gecko. + * + * @param save_p + */ + protected final int PADSAVEP(int save_p) { + return (save_p + ((4 - (save_p & 3)) & 3)); + } + + //protected final int PADSAVEP(ByteBuffer b, int save_p){ + // ByteBuffer + // return (save_p += (4 - ((int) save_p & 3)) & 3); + //} + protected final long PADSAVEP(DataInputStream f, int maxsize) throws IOException { + long save_p = maxsize - f.available(); + int padding = (4 - ((int) save_p & 3)) & 3; + // System.out.printf("Current position %d Padding by %d bytes %d\n",save_p,padding,maxsize); + f.skip(padding); + return padding; + } + + protected final long PADSAVEP(DataOutputStream f) throws IOException { + long save_p = f.size(); + int padding = (4 - ((int) save_p & 3)) & 3; + // System.out.printf("Current position %d Padding by %d bytes\n",save_p,padding); + for (int i = 0; i < padding; i++) { + f.write(0); + } + return padding; + } + + @Override + public boolean doSave(DataOutputStream f) { + try { + // The header must have been set, at this point. + this.fo = f; + //f.setLength(0); // Kill old info. + header.write(f); + + //header.read(f); + ArchivePlayers(); + ArchiveWorld(); + ArchiveThinkers(); + ArchiveSpecials(); + // TODO: the rest... + f.write(0x1D); + } catch (IOException e) { + LOGGER.log(Level.WARNING, e, () + -> String.format("Error while saving savegame! Cause: %s", e.getMessage())); + return false; // Needed to shut up compiler. + } + return true; + } + +} \ No newline at end of file diff --git a/src/savegame/VanillaDSGHeader.java b/src/savegame/VanillaDSGHeader.java index 12f2750..f29a55c 100644 --- a/src/savegame/VanillaDSGHeader.java +++ b/src/savegame/VanillaDSGHeader.java @@ -17,7 +17,7 @@ import w.IWritableDoomObject; /** The header of a vanilla savegame. - * + * * It contains a fixed-length, null-terminated string of 24 bytes max, in any case. * Then a 16-byte "version string", which normally reads "version 109". * Then bytes that record: @@ -26,10 +26,10 @@ * map +1 * players in game +4 * gametime +3 (as 24-bit big-endian) - * + * * So the header has an total size of *drum roll* 50 bytes. - * - * + * + * * @author admin * */ @@ -70,9 +70,9 @@ public void unpack(ByteBuffer buf) playeringame[i] = buf.get() != 0; } - // load a base level (this doesn't advance the pointer?) - //G_InitNew (gameskill, gameepisode, gamemap); - // get the times + // load a base level (this doesn't advance the pointer?) + //G_InitNew (gameskill, gameepisode, gamemap); + // get the times int a = C2JUtils.toUnsignedByte(buf.get()); int b = C2JUtils.toUnsignedByte(buf.get()); int c = C2JUtils.toUnsignedByte(buf.get()); @@ -101,9 +101,9 @@ public void write(DataOutputStream f) f.writeBoolean(playeringame[i]); } - // load a base level (this doesn't advance the pointer?) - //G_InitNew (gameskill, gameepisode, gamemap); - // get the times + // load a base level (this doesn't advance the pointer?) + //G_InitNew (gameskill, gameepisode, gamemap); + // get the times byte a = (byte) (0x0000FF & (leveltime >> 16)); byte b = (byte) (0x00FF & (leveltime >> 8)); byte c = (byte) (0x00FF & (leveltime)); @@ -128,7 +128,7 @@ public void read(DataInputStream f) playeringame[i] = f.readBoolean(); } - // get the times + // get the times int a = f.readUnsignedByte(); int b = f.readUnsignedByte(); int c = f.readUnsignedByte(); @@ -223,4 +223,4 @@ public boolean isProperend() { return properend; } -} +} \ No newline at end of file diff --git a/src/st/AbstractStatusBar.java b/src/st/AbstractStatusBar.java index a825236..6cf6ddf 100644 --- a/src/st/AbstractStatusBar.java +++ b/src/st/AbstractStatusBar.java @@ -9,4 +9,4 @@ public abstract class AbstractStatusBar implements IDoomStatusBar { public AbstractStatusBar(DoomMain DOOM) { this.DOOM = DOOM; } -} +} \ No newline at end of file diff --git a/src/st/IDoomStatusBar.java b/src/st/IDoomStatusBar.java index c3a05a5..c400cbb 100644 --- a/src/st/IDoomStatusBar.java +++ b/src/st/IDoomStatusBar.java @@ -52,7 +52,7 @@ public interface IDoomStatusBar { /** Called by startup code. */ public void Init(); - /** Used externally to determine window scaling. + /** Used externally to determine window scaling. * This means that drawing transparent status bars is possible, but * it will look fugly because of the solid windowing (and possibly * HOMS). @@ -65,4 +65,4 @@ public interface IDoomStatusBar { */ void forceRefresh(); -} +} \ No newline at end of file diff --git a/src/st/StatusBar.java b/src/st/StatusBar.java index 8437f37..a30c9f9 100644 --- a/src/st/StatusBar.java +++ b/src/st/StatusBar.java @@ -541,7 +541,7 @@ public void forceRefresh() { cheatseq_t cheat_tnthom = new cheatseq_t("tnthom", false); - // + // String[] mapnames; // @@ -1003,7 +1003,7 @@ public void updateFaceWidget() { && (plyr.attacker != plyr.mo)) { // being attacked priority = 7; - /** + /** * Another switchable fix of mine * - Good Sign 2017/04/02 */ @@ -1048,7 +1048,7 @@ public void updateFaceWidget() { if (priority < 7) { // getting hurt because of your own damn stupidity if (plyr.damagecount != 0) { - /** + /** * Another switchable fix of mine * - Good Sign 2017/04/02 */ @@ -1118,10 +1118,10 @@ public void updateFaceWidget() { * Clearly we can't do that in Java unless said variables are inside an * array and we provide both the array AND an index. For other cases, we * must simply build ad-hoc hacks. - * + * * In any case, only "status" updates are performed here. Actual visual * updates are performed by the Drawer. - * + * */ public void updateWidgets() { @@ -1395,7 +1395,7 @@ public void loadGraphics() { public void unloadGraphics() { - int i; // unload the numbers, tall and short + int i; // unload the numbers, tall and short for (i = 0; i < 10; i++) { DOOM.wadLoader.UnlockLumpNum(tallnum[i]); tallnum[i] = null; @@ -1407,10 +1407,10 @@ public void unloadGraphics() { DOOM.wadLoader.UnlockLumpNum(tallpercent); tallpercent = null; - // unload arms background + // unload arms background DOOM.wadLoader.UnlockLumpNum(armsbg); armsbg = null; - // unload gray #'s + // unload gray #'s for (i = 0; i < 6; i++) { DOOM.wadLoader.UnlockLumpNum(arms[i][0]); arms[i][0] = null; @@ -1581,10 +1581,10 @@ public void createWidgets() { } - /** Binary Icon widget + /** Binary Icon widget * This is used for stuff such as keys or weapons, which you either have * or you don't. - * + * * */ class st_binicon_t implements StatusBarWidget { @@ -1776,11 +1776,11 @@ class st_number_t int oldnum; /** - * Array in which to point with num. - * + * Array in which to point with num. + * * Fun fact: initially I tried to use Integer and Boolean, but those are * immutable -_-. Fuck that, Java. - * + * */ int[] numarray; @@ -1816,7 +1816,7 @@ public void init(int x, int y, patch_t[] pl, int[] numarray, int numindex, this.numindex = numindex; // _D_ fixed this bug } - // + // // A fairly efficient way to draw a number // based on differences from the old number. // Note: worth the trouble? @@ -2014,4 +2014,4 @@ public int getHeight() { //Adapted to use new gamemode system. // //Revision 1.25 2011/05/18 16:57:21 velktron -//Changed to DoomStatus +//Changed to DoomStatus \ No newline at end of file diff --git a/src/st/st_chatstateenum_t.java b/src/st/st_chatstateenum_t.java index d340858..b5756ad 100644 --- a/src/st/st_chatstateenum_t.java +++ b/src/st/st_chatstateenum_t.java @@ -4,4 +4,4 @@ enum st_chatstateenum_t { StartChatState, WaitDestState, GetChatState -} +} \ No newline at end of file diff --git a/src/st/st_enumstatcodes_t.java b/src/st/st_enumstatcodes_t.java index effc456..562896a 100644 --- a/src/st/st_enumstatcodes_t.java +++ b/src/st/st_enumstatcodes_t.java @@ -7,4 +7,4 @@ enum st_enumstatcodes_t { WaitDestState, GetChatState -} +} \ No newline at end of file diff --git a/src/st/st_stateenum_t.java b/src/st/st_stateenum_t.java index 5018c82..266d38f 100644 --- a/src/st/st_stateenum_t.java +++ b/src/st/st_stateenum_t.java @@ -6,4 +6,4 @@ public enum st_stateenum_t { AutomapState, FirstPersonState -} +} \ No newline at end of file diff --git a/src/timing/DelegateTicker.java b/src/timing/DelegateTicker.java index 2149bb5..18da59e 100644 --- a/src/timing/DelegateTicker.java +++ b/src/timing/DelegateTicker.java @@ -1,49 +1,49 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package timing; - -/** - * - * @author Good Sign - */ -public class DelegateTicker implements ITicker { - - private final FastTicker ft = new FastTicker(); - private final MilliTicker mt = new MilliTicker(); - private final NanoTicker nt = new NanoTicker(); - private ITicker currentTicker = ft; - - @Override - public int GetTime() { - return currentTicker.GetTime(); - } - - public void changeTicker() { - if (currentTicker == nt) { - currentTicker = mt; - ((MilliTicker) currentTicker).basetime = 0; - ((MilliTicker) currentTicker).oldtics = 0; - } else if (currentTicker == mt) { - currentTicker = ft; - ((FastTicker) currentTicker).fasttic = 0; - } else { - currentTicker = nt; - ((NanoTicker) currentTicker).basetime = 0; - ((NanoTicker) currentTicker).oldtics = 0; - } - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package timing; + +/** + * + * @author Good Sign + */ +public class DelegateTicker implements ITicker { + + private final FastTicker ft = new FastTicker(); + private final MilliTicker mt = new MilliTicker(); + private final NanoTicker nt = new NanoTicker(); + private ITicker currentTicker = ft; + + @Override + public int GetTime() { + return currentTicker.GetTime(); + } + + public void changeTicker() { + if (currentTicker == nt) { + currentTicker = mt; + ((MilliTicker) currentTicker).basetime = 0; + ((MilliTicker) currentTicker).oldtics = 0; + } else if (currentTicker == mt) { + currentTicker = ft; + ((FastTicker) currentTicker).fasttic = 0; + } else { + currentTicker = nt; + ((NanoTicker) currentTicker).basetime = 0; + ((NanoTicker) currentTicker).oldtics = 0; + } + } +} \ No newline at end of file diff --git a/src/timing/FastTicker.java b/src/timing/FastTicker.java index b8c3aa7..6714ec2 100644 --- a/src/timing/FastTicker.java +++ b/src/timing/FastTicker.java @@ -12,4 +12,4 @@ public int GetTime() { } protected volatile int fasttic = 0; -} +} \ No newline at end of file diff --git a/src/timing/ITicker.java b/src/timing/ITicker.java index 05b632b..8d56439 100644 --- a/src/timing/ITicker.java +++ b/src/timing/ITicker.java @@ -19,4 +19,4 @@ static ITicker createTicker(CVarManager CVM) { @I_IBM.C(I_GetTime) public int GetTime(); -} +} \ No newline at end of file diff --git a/src/timing/MilliTicker.java b/src/timing/MilliTicker.java index 9a4ca60..8fd684b 100644 --- a/src/timing/MilliTicker.java +++ b/src/timing/MilliTicker.java @@ -27,4 +27,4 @@ public int GetTime() { protected volatile int oldtics = 0; protected volatile int discrepancies; -} +} \ No newline at end of file diff --git a/src/timing/NanoTicker.java b/src/timing/NanoTicker.java index 8a8f61f..23acf23 100644 --- a/src/timing/NanoTicker.java +++ b/src/timing/NanoTicker.java @@ -38,4 +38,4 @@ public int GetTime() { protected volatile int oldtics = 0; protected volatile int discrepancies; -} +} \ No newline at end of file diff --git a/src/utils/BinarySearch.java b/src/utils/BinarySearch.java index d450bdd..7952d1f 100644 --- a/src/utils/BinarySearch.java +++ b/src/utils/BinarySearch.java @@ -1,886 +1,886 @@ -package utils; - -import java.util.Comparator; -import java.util.List; -import java.util.ListIterator; -import java.util.RandomAccess; -import java.util.function.Function; -import java.util.function.IntBinaryOperator; -import java.util.function.IntFunction; -import java.util.function.IntUnaryOperator; -import java.util.function.ToDoubleFunction; -import java.util.function.ToIntFunction; -import java.util.function.ToLongFunction; - -public enum BinarySearch { - ; - /** - * Binary search supporting search for one type of objects - * using object of another type, given from any object of one type - * a function can get an object of another type - * - * @param list of one type of objects - * @param converter from one type of objects to another - * @param key a value of another object type - * @return - */ - public static > int find(final List list, - final Function converter, - final E key) { - return find(list, converter, 0, list.size(), key); - } - - /** - * Binary search supporting search for one type of objects - * using object of another type, given from any object of one type - * a function can get an object of another type - * - * @param array of one type of objects - * @param converter from one type of objects to another - * @param key a value of another object type - * @return - */ - public static > int find(final T[] array, - final Function converter, - final E key) { - return find(array, converter, 0, array.length, key); - } - - /** - * Binary search supporting search for one type of objects - * using object of another type, given from any object of one type - * a function can get an object of another type - * - * @param list of one type of objects - * @param comparator - a comparator for objects of type E - * @param converter from one type of objects to another - * @param key a value of another object type - * @return - */ - public static int find(final List list, - final Function converter, - final Comparator comparator, - final E key) { - return find(list, converter, comparator, 0, list.size(), key); - } - - /** - * Binary search supporting search for one type of objects - * using object of another type, given from any object of one type - * a function can get an object of another type - * - * @param array of one type of objects - * @param comparator - a comparator for objects of type E - * @param converter from one type of objects to another - * @param key a value of another object type - * @return - */ - public static int find(final T[] array, - final Function converter, - final Comparator comparator, - final E key) { - return find(array, converter, comparator, 0, array.length, key); - } - - /** - * Binary search supporting search for one type of objects - * using primitive integer, given from any object - * of one type a function can get a primitive integer - * - * @param list of one type of objects - * @param converter from one type of objects to a primitive integer - * @param a primitive integer key value - * @return - */ - public static int findByInt(final List list, - final ToIntFunction converter, - final int key) { - return findByInt(list, converter, 0, list.size(), key); - } - - /** - * Binary search supporting search for one type of objects - * using primitive integer, given from any object - * of one type a function can get a primitive integer - * - * @param array of one type of objects - * @param converter from one type of objects to a primitive integer - * @param a primitive integer key value - * @return - */ - public static int findByInt(final T[] array, - final ToIntFunction converter, - final int key) { - return findByInt(array, converter, 0, array.length, key); - } - - /** - * Binary search supporting search for one type of objects - * using primitive integer, given from any object - * of one type a function can get a primitive integer - * - * @param list of one type of objects - * @param converter from one type of objects to a primitive integer - * @param comparator - a comparator for primitive integer values - * @param a primitive integer key value - * @return - */ - public static int findByInt(final List list, - final ToIntFunction converter, - final IntBinaryOperator comparator, - final int key) { - return findByInt(list, converter, comparator, 0, list.size(), key); - } - - /** - * Binary search supporting search for one type of objects - * using primitive integer, given from any object - * of one type a function can get a primitive integer - * - * @param array of one type of objects - * @param converter from one type of objects to a primitive integer - * @param comparator - a comparator for primitive integer values - * @param a primitive integer key value - * @return - */ - public static int findByInt(final T[] array, - final ToIntFunction converter, - final IntBinaryOperator comparator, - final int key) { - return findByInt(array, converter, comparator, 0, array.length, key); - } - - /** - * Binary search supporting search for one type of objects - * using primitive long, given from any object - * of one type a function can get a primitive long - * - * @param list of one type of objects - * @param converter from one type of objects to a primitive long - * @param a primitive long key value - * @return - */ - public static int findByLong(final List list, - final ToLongFunction converter, - final long key) { - return findByLong(list, converter, 0, list.size(), key); - } - - /** - * Binary search supporting search for one type of objects - * using primitive long, given from any object - * of one type a function can get a primitive long - * - * @param array of one type of objects - * @param converter from one type of objects to a primitive long - * @param a primitive long key value - * @return - */ - public static int findByLong(final T[] array, - final ToLongFunction converter, - final long key) { - return findByLong(array, converter, 0, array.length, key); - } - - /** - * Binary search supporting search for one type of objects - * using primitive long, given from any object - * of one type a function can get a primitive long - * - * @param list of one type of objects - * @param converter from one type of objects to a primitive long - * @param comparator - a comparator for primitive long values - * @param a primitive long key value - * @return - */ - public static int findByLong(final List list, - final ToLongFunction converter, - final LongComparator comparator, - final long key) { - return findByLong(list, converter, comparator, 0, list.size(), key); - } - - /** - * Binary search supporting search for one type of objects - * using primitive long, given from any object - * of one type a function can get a primitive long - * - * @param array of one type of objects - * @param converter from one type of objects to a primitive long - * @param comparator - a comparator for primitive long values - * @param a primitive long key value - * @return - */ - public static int findByLong(final T[] array, - final ToLongFunction converter, - final LongComparator comparator, - final long key) { - return findByLong(array, converter, comparator, 0, array.length, key); - } - - /** - * Binary search supporting search for one type of objects - * using primitive double, given from any object - * of one type a function can get a primitive double - * - * @param list of one type of objects - * @param converter from one type of objects to a primitive double - * @param a primitive double key value - * @return - */ - public static int findByDouble(final List list, - final ToDoubleFunction converter, - final double key) { - return findByDouble(list, converter, 0, list.size(), key); - } - - /** - * Binary search supporting search for one type of objects - * using primitive double, given from any object - * of one type a function can get a primitive double - * - * @param array of one type of objects - * @param converter from one type of objects to a primitive double - * @param a primitive double key value - * @return - */ - public static int findByDouble(final T[] array, - final ToDoubleFunction converter, - final double key) { - return findByDouble(array, converter, 0, array.length, key); - } - - /** - * Binary search supporting search for one type of objects - * using primitive double, given from any object - * of one type a function can get a primitive double - * - * @param list of one type of objects - * @param converter from one type of objects to a primitive double - * @param comparator - a comparator for primitive double values - * @param a primitive double key value - * @return - */ - public static int findByDouble(final List list, - final ToDoubleFunction converter, - final DoubleComparator comparator, - final double key) { - return findByDouble(list, converter, comparator, 0, list.size(), key); - } - - /** - * Binary search supporting search for one type of objects - * using primitive double, given from any object - * of one type a function can get a primitive double - * - * @param array of one type of objects - * @param converter from one type of objects to a primitive double - * @param comparator - a comparator for primitive double values - * @param a primitive double key value - * @return - */ - public static int findByDouble(final T[] array, - final ToDoubleFunction converter, - final DoubleComparator comparator, - final double key) { - return findByDouble(array, converter, comparator, 0, array.length, key); - } - - /** - * Binary search supporting search for one type of objects - * using object of another type, given from any object of one type - * a function can get an object of another type - * - * @param list of one type of objects - * @param converter from one type of objects to another - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param key a value of another object type - * @return - */ - public static > int find(final List list, - final Function converter, - final int from, final int to, final E key) { - final IntFunction getter = listGetter(list); - return findByIndex(i -> converter.apply(getter.apply(i)).compareTo(key), from, to); - } - - /** - * Binary search supporting search for one type of objects - * using object of another type, given from any object of one type - * a function can get an object of another type - * - * @param array of one type of objects - * @param converter from one type of objects to another - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param key a value of another object type - * @return - */ - public static > int find(final T[] array, - final Function converter, - final int from, final int to, final E key) { - rangeCheck(array.length, from, to); - return findByIndex(i -> converter.apply(array[i]).compareTo(key), from, to); - } - - /** - * Binary search supporting search for one type of objects - * using object of another type, given from any object of one type - * a function can get an object of another type - * - * @param list of one type of objects - * @param converter from one type of objects to another - * @param comparator - a comparator for objects of type E - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param key a value of another object type - * @return - */ - public static int find(final List list, - final Function converter, - final Comparator comparator, - final int from, final int to, final E key) { - final IntFunction getter = listGetter(list); - return findByIndex(i -> comparator.compare(converter.apply(getter.apply(i)), key), from, to); - } - - /** - * Binary search supporting search for one type of objects - * using object of another type, given from any object of one type - * a function can get an object of another type - * - * @param array of one type of objects - * @param converter from one type of objects to another - * @param comparator - a comparator for objects of type E - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param key a value of another object type - * @return - */ - public static int find(final T[] array, - final Function converter, - final Comparator comparator, - final int from, final int to, final E key) { - rangeCheck(array.length, from, to); - return findByIndex(i -> comparator.compare(converter.apply(array[i]), key), from, to); - } - - /** - * Binary search supporting search for one type of objects - * using primitive integer, given from any object - * of one type a function can get a primitive integer - * - * @param list of one type of objects - * @param converter from one type of objects to a primitive integer - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param a primitive integer key value - * @return - */ - public static int findByInt(final List list, - final ToIntFunction converter, - final int from, final int to, final int key) { - final IntFunction getter = listGetter(list); - return findByInt(i -> converter.applyAsInt(getter.apply(i)), from, to, key); - } - - /** - * Binary search supporting search for one type of objects - * using primitive integer, given from any object - * of one type a function can get a primitive integer - * - * @param array of one type of objects - * @param converter from one type of objects to a primitive integer - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param a primitive integer key value - * @return - */ - public static int findByInt(final T[] array, - final ToIntFunction converter, - final int from, final int to, final int key) { - rangeCheck(array.length, from, to); - return findByInt(i -> converter.applyAsInt(array[i]), from, to, key); - } - - /** - * Binary search supporting search for one type of objects - * using primitive integer, given from any object - * of one type a function can get a primitive integer - * - * @param list of one type of objects - * @param converter from one type of objects to a primitive integer - * @param comparator - a comparator for primitive integer values - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param a primitive integer key value - * @return - */ - public static int findByInt(final List list, - final ToIntFunction converter, - final IntBinaryOperator comparator, - final int from, final int to, final int key) { - final IntFunction getter = listGetter(list); - return findByIndex(i -> comparator.applyAsInt(converter.applyAsInt(getter.apply(i)), key), from, to); - } - - /** - * Binary search supporting search for one type of objects - * using primitive integer, given from any object - * of one type a function can get a primitive integer - * - * @param array of one type of objects - * @param converter from one type of objects to a primitive integer - * @param comparator - a comparator for primitive integer values - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param a primitive integer key value - * @return - */ - public static int findByInt(final T[] array, - final ToIntFunction converter, - final IntBinaryOperator comparator, - final int from, final int to, final int key) { - rangeCheck(array.length, from, to); - return findByIndex(i -> comparator.applyAsInt(converter.applyAsInt(array[i]), key), from, to); - } - - /** - * Binary search supporting search for one type of objects - * using primitive long, given from any object - * of one type a function can get a primitive long - * - * @param list of one type of objects - * @param converter from one type of objects to a primitive long - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param a primitive long key value - * @return - */ - public static int findByLong(final List list, - final ToLongFunction converter, - final int from, final int to, final long key) { - final IntFunction getter = listGetter(list); - return findByLong(i -> converter.applyAsLong(getter.apply(i)), from, to, key); - } - - /** - * Binary search supporting search for one type of objects - * using primitive long, given from any object - * of one type a function can get a primitive long - * - * @param array of one type of objects - * @param converter from one type of objects to a primitive long - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param a primitive long key value - * @return - */ - public static int findByLong(final T[] array, - final ToLongFunction converter, - final int from, final int to, final long key) { - rangeCheck(array.length, from, to); - return findByLong(i -> converter.applyAsLong(array[i]), from, to, key); - } - - /** - * Binary search supporting search for one type of objects - * using primitive long, given from any object - * of one type a function can get a primitive long - * - * @param list of one type of objects - * @param converter from one type of objects to a primitive long - * @param comparator - a comparator for primitive long values - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param a primitive long key value - * @return - */ - public static int findByLong(final List list, - final ToLongFunction converter, - final LongComparator comparator, - final int from, final int to, final long key) { - final IntFunction getter = listGetter(list); - return findByIndex(i -> comparator.compareAsLong(converter.applyAsLong(getter.apply(i)), key), from, to); - } - - /** - * Binary search supporting search for one type of objects - * using primitive long, given from any object - * of one type a function can get a primitive long - * - * @param array of one type of objects - * @param converter from one type of objects to a primitive long - * @param comparator - a comparator for primitive long values - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param a primitive long key value - * @return - */ - public static int findByLong(final T[] array, - final ToLongFunction converter, - final LongComparator comparator, - final int from, final int to, final long key) { - rangeCheck(array.length, from, to); - return findByIndex(i -> comparator.compareAsLong(converter.applyAsLong(array[i]), key), from, to); - } - - /** - * Binary search supporting search for one type of objects - * using primitive double, given from any object - * of one type a function can get a primitive double - * - * @param list of one type of objects - * @param converter from one type of objects to a primitive double - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param a primitive double key value - * @return - */ - public static int findByDouble(final List list, - final ToDoubleFunction converter, - final int from, final int to, final double key) { - final IntFunction getter = listGetter(list); - return findByDouble(i -> converter.applyAsDouble(getter.apply(i)), from, to, key); - } - - /** - * Binary search supporting search for one type of objects - * using primitive double, given from any object - * of one type a function can get a primitive double - * - * @param array of one type of objects - * @param converter from one type of objects to a primitive double - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param a primitive double key value - * @return - */ - public static int findByDouble(final T[] array, - final ToDoubleFunction converter, - final int from, final int to, final double key) { - rangeCheck(array.length, from, to); - return findByDouble(i -> converter.applyAsDouble(array[i]), from, to, key); - } - - /** - * Binary search supporting search for one type of objects - * using primitive double, given from any object - * of one type a function can get a primitive double - * - * @param list of one type of objects - * @param converter from one type of objects to a primitive double - * @param comparator - a comparator for primitive double values - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param a primitive double key value - * @return - */ - public static int findByDouble(final List list, - final ToDoubleFunction converter, - final DoubleComparator comparator, - final int from, final int to, final double key) { - final IntFunction getter = listGetter(list); - return findByIndex(i -> comparator.compareAsDouble(converter.applyAsDouble(getter.apply(i)), key), from, to); - } - - /** - * Binary search supporting search for one type of objects - * using primitive double, given from any object - * of one type a function can get a primitive double - * - * @param array of one type of objects - * @param converter from one type of objects to a primitive double - * @param comparator - a comparator for primitive double values - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param a primitive double key value - * @return - */ - public static int findByDouble(final T[] array, - final ToDoubleFunction converter, - final DoubleComparator comparator, - final int from, final int to, final double key) { - rangeCheck(array.length, from, to); - return findByIndex(i -> comparator.compareAsDouble(converter.applyAsDouble(array[i]), key), from, to); - } - - /** - * Blind binary search, presuming there is some sorted structure, - * whose sorting is someway ensured by some key object, using the getter - * who, given an index in the invisible structure, can produce a key - * object someway used to sort it. - * - * @param getter - a function accepting indexes, producing a key object used for sort - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param key - a key object - */ - public static > int find(final IntFunction getter, - final int from, final int to, final E key) { - return findByIndex(i -> getter.apply(i).compareTo(key), from, to); - } - - /** - * Blind binary search, presuming there is some sorted structure, - * whose sorting is someway ensured by some key object, using the getter - * who, given an index in the invisible structure, can produce a key - * object someway used to sort it. - * - * @param getter - a function accepting indexes, producing a key object used for sort - * @param comparator - a comparator for objects of type E - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param key - a key object - */ - public static int find(final IntFunction getter, - final Comparator comparator, - final int from, final int to, final E key) { - return findByIndex(i -> comparator.compare(getter.apply(i), key), from, to); - } - - /** - * Blind binary search, presuming there is some sorted structure, - * whose sorting is someway ensured by primitive integer key, - * using the getter who, given an index in the invisible structure, can produce - * the primitive integer key someway used to sort it. - * - * @param getter - a function accepting indexes, producing a primitive integer used for sort - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param key - a primitive integer key - */ - public static int findByInt(final IntUnaryOperator getter, - final int from, final int to, final int key) { - return findByInt(getter, Integer::compare, from, to, key); - } - - /** - * Blind binary search, presuming there is some sorted structure, - * whose sorting is someway ensured by primitive integer key, - * using the getter who, given an index in the invisible structure, can produce - * the primitive integer key someway used to sort it. - * - * @param getter - a function accepting indexes, producing a primitive integer used for sort - * @param comparator - a comparator for primitive integers - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param key - a primitive integer key - */ - public static int findByInt(final IntUnaryOperator getter, - final IntBinaryOperator comparator, - final int from, final int to, final int key) { - return findByIndex(i -> comparator.applyAsInt(getter.applyAsInt(i), key), from, to); - } - - /** - * Blind binary search, presuming there is some sorted structure, - * whose sorting is someway ensured by primitive long key, - * using the getter who, given an index in the invisible structure, can produce - * the primitive long key someway used to sort it. - * - * @param getter - a function accepting indexes, producing a primitive long used for sort - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param key - a primitive long key - */ - public static int findByLong(final LongGetter getter, - final int from, final int to, final long key) { - return findByLong(getter, Long::compare, from, to, key); - } - - /** - * Blind binary search, presuming there is some sorted structure, - * whose sorting is someway ensured by primitive long key, - * using the getter who, given an index in the invisible structure, can produce - * the primitive long key someway used to sort it. - * - * @param getter - a function accepting indexes, producing a primitive long used for sort - * @param comparator - a comparator for primitive long values - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param key - a primitive long key - */ - public static int findByLong(final LongGetter getter, - final LongComparator comparator, - final int from, final int to, final long key) { - return findByIndex(i -> comparator.compareAsLong(getter.getAsLong(i), key), from, to); - } - - /** - * Blind binary search, presuming there is some sorted structure, - * whose sorting is someway ensured by primitive double key, - * using the getter who, given an index in the invisible structure, can produce - * the primitive double key someway used to sort it. - * - * @param getter - a function accepting indexes, producing a primitive double used for sort - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param key - a primitive double key - */ - public static int findByDouble(final DoubleGetter getter, - final int from, final int to, final double key) { - return findByDouble(getter, Double::compare, from, to, key); - } - - /** - * Blind binary search, presuming there is some sorted structure, - * whose sorting is someway ensured by primitive double key, - * using the getter who, given an index in the invisible structure, can produce - * the primitive double key someway used to sort it. - * - * @param getter - a function accepting indexes, producing a primitive double used for sort - * @param comparator - a comparator for primitive double values - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - * @param key - a primitive double key - */ - public static int findByDouble(final DoubleGetter getter, - final DoubleComparator comparator, - final int from, final int to, final double key) { - return findByIndex(i -> comparator.compareAsDouble(getter.getAsDouble(i), key), from, to); - } - - /** - * Blind binary search applying array elements to matching function until it returns 0 - * @param list of one type of objects - * @param matcher - a matcher returning comparison result based on single list element - **/ - public static int findByMatch(final T[] array, - final ToIntFunction matcher) { - return findByMatch(array, matcher, 0, array.length); - } - - /** - * Blind binary search applying List elements to matching function until it returns 0 - * @param list of one type of objects - * @param matcher - a matcher returning comparison result based on single list element - **/ - public static int findByMatch(final List list, - final ToIntFunction matcher) { - return findByMatch(list, matcher, 0, list.size()); - } - - /** - * Blind binary search applying array elements to matching function until it returns 0 - * @param list of one type of objects - * @param matcher - a matcher returning comparison result based on single list element - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - **/ - public static int findByMatch(final T[] array, - final ToIntFunction matcher, - final int from, - final int to) { - rangeCheck(array.length, from, to); - return findByIndex(i -> matcher.applyAsInt(array[i]), from, to); - } - - /** - * Blind binary search applying List elements to matching function until it returns 0 - * @param list of one type of objects - * @param matcher - a matcher returning comparison result based on single list element - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - **/ - public static int findByMatch(final List list, - final ToIntFunction matcher, - final int from, - final int to) { - final IntFunction getter = listGetter(list); - return findByIndex(i -> matcher.applyAsInt(getter.apply(i)), from, to); - } - - /** - * Blind binary search applying index to comparison function until it returns 0 - * @param comparator - index-comparing function - * @param from - an index (inclusive) from which to start search - * @param to - an index (exclusive) from which to start search - **/ - public static int findByIndex(final IntUnaryOperator comparator, final int from, final int to) { - int low = from; - int high = to - 1; - - while (low <= high) { - int mid = (low + high) >>> 1; - int cmp = comparator.applyAsInt(mid); - - if (cmp < 0) { - low = mid + 1; - } else if (cmp > 0) { - high = mid - 1; - } else { - return mid; // key found - } - } - return -(low + 1); // key not found - } - - /** - * A copy of Arrays.rangeCheck private method from JDK - */ - private static void rangeCheck(int arrayLength, int fromIndex, int toIndex) { - if (fromIndex > toIndex) { - throw new IllegalArgumentException( - "fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); - } - if (fromIndex < 0) { - throw new ArrayIndexOutOfBoundsException(fromIndex); - } - if (toIndex > arrayLength) { - throw new ArrayIndexOutOfBoundsException(toIndex); - } - } - - /** - * A copy of Collections.get private method from JDK - */ - private static T get(ListIterator i, int index) { - T obj = null; - int pos = i.nextIndex(); - if (pos <= index) { - do { - obj = i.next(); - } while (pos++ < index); - } else { - do { - obj = i.previous(); - } while (--pos > index); - } - return obj; - } - - private static > IntFunction listGetter(final L list) { - if (list instanceof RandomAccess) { - return ((List) list)::get; - } - - final ListIterator it = list.listIterator(); - return i -> get(it, i); - } - - @FunctionalInterface - public interface LongComparator { - - int compareAsLong(long f1, long f2); - } - - @FunctionalInterface - public interface DoubleComparator { - - int compareAsDouble(double f1, double f2); - } - - @FunctionalInterface - public interface LongGetter { - - long getAsLong(int i); - } - - @FunctionalInterface - public interface DoubleGetter { - - double getAsDouble(int i); - } -} +package utils; + +import java.util.Comparator; +import java.util.List; +import java.util.ListIterator; +import java.util.RandomAccess; +import java.util.function.Function; +import java.util.function.IntBinaryOperator; +import java.util.function.IntFunction; +import java.util.function.IntUnaryOperator; +import java.util.function.ToDoubleFunction; +import java.util.function.ToIntFunction; +import java.util.function.ToLongFunction; + +public enum BinarySearch { + ; + /** + * Binary search supporting search for one type of objects + * using object of another type, given from any object of one type + * a function can get an object of another type + * + * @param list of one type of objects + * @param converter from one type of objects to another + * @param key a value of another object type + * @return + */ + public static > int find(final List list, + final Function converter, + final E key) { + return find(list, converter, 0, list.size(), key); + } + + /** + * Binary search supporting search for one type of objects + * using object of another type, given from any object of one type + * a function can get an object of another type + * + * @param array of one type of objects + * @param converter from one type of objects to another + * @param key a value of another object type + * @return + */ + public static > int find(final T[] array, + final Function converter, + final E key) { + return find(array, converter, 0, array.length, key); + } + + /** + * Binary search supporting search for one type of objects + * using object of another type, given from any object of one type + * a function can get an object of another type + * + * @param list of one type of objects + * @param comparator - a comparator for objects of type E + * @param converter from one type of objects to another + * @param key a value of another object type + * @return + */ + public static int find(final List list, + final Function converter, + final Comparator comparator, + final E key) { + return find(list, converter, comparator, 0, list.size(), key); + } + + /** + * Binary search supporting search for one type of objects + * using object of another type, given from any object of one type + * a function can get an object of another type + * + * @param array of one type of objects + * @param comparator - a comparator for objects of type E + * @param converter from one type of objects to another + * @param key a value of another object type + * @return + */ + public static int find(final T[] array, + final Function converter, + final Comparator comparator, + final E key) { + return find(array, converter, comparator, 0, array.length, key); + } + + /** + * Binary search supporting search for one type of objects + * using primitive integer, given from any object + * of one type a function can get a primitive integer + * + * @param list of one type of objects + * @param converter from one type of objects to a primitive integer + * @param a primitive integer key value + * @return + */ + public static int findByInt(final List list, + final ToIntFunction converter, + final int key) { + return findByInt(list, converter, 0, list.size(), key); + } + + /** + * Binary search supporting search for one type of objects + * using primitive integer, given from any object + * of one type a function can get a primitive integer + * + * @param array of one type of objects + * @param converter from one type of objects to a primitive integer + * @param a primitive integer key value + * @return + */ + public static int findByInt(final T[] array, + final ToIntFunction converter, + final int key) { + return findByInt(array, converter, 0, array.length, key); + } + + /** + * Binary search supporting search for one type of objects + * using primitive integer, given from any object + * of one type a function can get a primitive integer + * + * @param list of one type of objects + * @param converter from one type of objects to a primitive integer + * @param comparator - a comparator for primitive integer values + * @param a primitive integer key value + * @return + */ + public static int findByInt(final List list, + final ToIntFunction converter, + final IntBinaryOperator comparator, + final int key) { + return findByInt(list, converter, comparator, 0, list.size(), key); + } + + /** + * Binary search supporting search for one type of objects + * using primitive integer, given from any object + * of one type a function can get a primitive integer + * + * @param array of one type of objects + * @param converter from one type of objects to a primitive integer + * @param comparator - a comparator for primitive integer values + * @param a primitive integer key value + * @return + */ + public static int findByInt(final T[] array, + final ToIntFunction converter, + final IntBinaryOperator comparator, + final int key) { + return findByInt(array, converter, comparator, 0, array.length, key); + } + + /** + * Binary search supporting search for one type of objects + * using primitive long, given from any object + * of one type a function can get a primitive long + * + * @param list of one type of objects + * @param converter from one type of objects to a primitive long + * @param a primitive long key value + * @return + */ + public static int findByLong(final List list, + final ToLongFunction converter, + final long key) { + return findByLong(list, converter, 0, list.size(), key); + } + + /** + * Binary search supporting search for one type of objects + * using primitive long, given from any object + * of one type a function can get a primitive long + * + * @param array of one type of objects + * @param converter from one type of objects to a primitive long + * @param a primitive long key value + * @return + */ + public static int findByLong(final T[] array, + final ToLongFunction converter, + final long key) { + return findByLong(array, converter, 0, array.length, key); + } + + /** + * Binary search supporting search for one type of objects + * using primitive long, given from any object + * of one type a function can get a primitive long + * + * @param list of one type of objects + * @param converter from one type of objects to a primitive long + * @param comparator - a comparator for primitive long values + * @param a primitive long key value + * @return + */ + public static int findByLong(final List list, + final ToLongFunction converter, + final LongComparator comparator, + final long key) { + return findByLong(list, converter, comparator, 0, list.size(), key); + } + + /** + * Binary search supporting search for one type of objects + * using primitive long, given from any object + * of one type a function can get a primitive long + * + * @param array of one type of objects + * @param converter from one type of objects to a primitive long + * @param comparator - a comparator for primitive long values + * @param a primitive long key value + * @return + */ + public static int findByLong(final T[] array, + final ToLongFunction converter, + final LongComparator comparator, + final long key) { + return findByLong(array, converter, comparator, 0, array.length, key); + } + + /** + * Binary search supporting search for one type of objects + * using primitive double, given from any object + * of one type a function can get a primitive double + * + * @param list of one type of objects + * @param converter from one type of objects to a primitive double + * @param a primitive double key value + * @return + */ + public static int findByDouble(final List list, + final ToDoubleFunction converter, + final double key) { + return findByDouble(list, converter, 0, list.size(), key); + } + + /** + * Binary search supporting search for one type of objects + * using primitive double, given from any object + * of one type a function can get a primitive double + * + * @param array of one type of objects + * @param converter from one type of objects to a primitive double + * @param a primitive double key value + * @return + */ + public static int findByDouble(final T[] array, + final ToDoubleFunction converter, + final double key) { + return findByDouble(array, converter, 0, array.length, key); + } + + /** + * Binary search supporting search for one type of objects + * using primitive double, given from any object + * of one type a function can get a primitive double + * + * @param list of one type of objects + * @param converter from one type of objects to a primitive double + * @param comparator - a comparator for primitive double values + * @param a primitive double key value + * @return + */ + public static int findByDouble(final List list, + final ToDoubleFunction converter, + final DoubleComparator comparator, + final double key) { + return findByDouble(list, converter, comparator, 0, list.size(), key); + } + + /** + * Binary search supporting search for one type of objects + * using primitive double, given from any object + * of one type a function can get a primitive double + * + * @param array of one type of objects + * @param converter from one type of objects to a primitive double + * @param comparator - a comparator for primitive double values + * @param a primitive double key value + * @return + */ + public static int findByDouble(final T[] array, + final ToDoubleFunction converter, + final DoubleComparator comparator, + final double key) { + return findByDouble(array, converter, comparator, 0, array.length, key); + } + + /** + * Binary search supporting search for one type of objects + * using object of another type, given from any object of one type + * a function can get an object of another type + * + * @param list of one type of objects + * @param converter from one type of objects to another + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param key a value of another object type + * @return + */ + public static > int find(final List list, + final Function converter, + final int from, final int to, final E key) { + final IntFunction getter = listGetter(list); + return findByIndex(i -> converter.apply(getter.apply(i)).compareTo(key), from, to); + } + + /** + * Binary search supporting search for one type of objects + * using object of another type, given from any object of one type + * a function can get an object of another type + * + * @param array of one type of objects + * @param converter from one type of objects to another + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param key a value of another object type + * @return + */ + public static > int find(final T[] array, + final Function converter, + final int from, final int to, final E key) { + rangeCheck(array.length, from, to); + return findByIndex(i -> converter.apply(array[i]).compareTo(key), from, to); + } + + /** + * Binary search supporting search for one type of objects + * using object of another type, given from any object of one type + * a function can get an object of another type + * + * @param list of one type of objects + * @param converter from one type of objects to another + * @param comparator - a comparator for objects of type E + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param key a value of another object type + * @return + */ + public static int find(final List list, + final Function converter, + final Comparator comparator, + final int from, final int to, final E key) { + final IntFunction getter = listGetter(list); + return findByIndex(i -> comparator.compare(converter.apply(getter.apply(i)), key), from, to); + } + + /** + * Binary search supporting search for one type of objects + * using object of another type, given from any object of one type + * a function can get an object of another type + * + * @param array of one type of objects + * @param converter from one type of objects to another + * @param comparator - a comparator for objects of type E + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param key a value of another object type + * @return + */ + public static int find(final T[] array, + final Function converter, + final Comparator comparator, + final int from, final int to, final E key) { + rangeCheck(array.length, from, to); + return findByIndex(i -> comparator.compare(converter.apply(array[i]), key), from, to); + } + + /** + * Binary search supporting search for one type of objects + * using primitive integer, given from any object + * of one type a function can get a primitive integer + * + * @param list of one type of objects + * @param converter from one type of objects to a primitive integer + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param a primitive integer key value + * @return + */ + public static int findByInt(final List list, + final ToIntFunction converter, + final int from, final int to, final int key) { + final IntFunction getter = listGetter(list); + return findByInt(i -> converter.applyAsInt(getter.apply(i)), from, to, key); + } + + /** + * Binary search supporting search for one type of objects + * using primitive integer, given from any object + * of one type a function can get a primitive integer + * + * @param array of one type of objects + * @param converter from one type of objects to a primitive integer + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param a primitive integer key value + * @return + */ + public static int findByInt(final T[] array, + final ToIntFunction converter, + final int from, final int to, final int key) { + rangeCheck(array.length, from, to); + return findByInt(i -> converter.applyAsInt(array[i]), from, to, key); + } + + /** + * Binary search supporting search for one type of objects + * using primitive integer, given from any object + * of one type a function can get a primitive integer + * + * @param list of one type of objects + * @param converter from one type of objects to a primitive integer + * @param comparator - a comparator for primitive integer values + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param a primitive integer key value + * @return + */ + public static int findByInt(final List list, + final ToIntFunction converter, + final IntBinaryOperator comparator, + final int from, final int to, final int key) { + final IntFunction getter = listGetter(list); + return findByIndex(i -> comparator.applyAsInt(converter.applyAsInt(getter.apply(i)), key), from, to); + } + + /** + * Binary search supporting search for one type of objects + * using primitive integer, given from any object + * of one type a function can get a primitive integer + * + * @param array of one type of objects + * @param converter from one type of objects to a primitive integer + * @param comparator - a comparator for primitive integer values + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param a primitive integer key value + * @return + */ + public static int findByInt(final T[] array, + final ToIntFunction converter, + final IntBinaryOperator comparator, + final int from, final int to, final int key) { + rangeCheck(array.length, from, to); + return findByIndex(i -> comparator.applyAsInt(converter.applyAsInt(array[i]), key), from, to); + } + + /** + * Binary search supporting search for one type of objects + * using primitive long, given from any object + * of one type a function can get a primitive long + * + * @param list of one type of objects + * @param converter from one type of objects to a primitive long + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param a primitive long key value + * @return + */ + public static int findByLong(final List list, + final ToLongFunction converter, + final int from, final int to, final long key) { + final IntFunction getter = listGetter(list); + return findByLong(i -> converter.applyAsLong(getter.apply(i)), from, to, key); + } + + /** + * Binary search supporting search for one type of objects + * using primitive long, given from any object + * of one type a function can get a primitive long + * + * @param array of one type of objects + * @param converter from one type of objects to a primitive long + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param a primitive long key value + * @return + */ + public static int findByLong(final T[] array, + final ToLongFunction converter, + final int from, final int to, final long key) { + rangeCheck(array.length, from, to); + return findByLong(i -> converter.applyAsLong(array[i]), from, to, key); + } + + /** + * Binary search supporting search for one type of objects + * using primitive long, given from any object + * of one type a function can get a primitive long + * + * @param list of one type of objects + * @param converter from one type of objects to a primitive long + * @param comparator - a comparator for primitive long values + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param a primitive long key value + * @return + */ + public static int findByLong(final List list, + final ToLongFunction converter, + final LongComparator comparator, + final int from, final int to, final long key) { + final IntFunction getter = listGetter(list); + return findByIndex(i -> comparator.compareAsLong(converter.applyAsLong(getter.apply(i)), key), from, to); + } + + /** + * Binary search supporting search for one type of objects + * using primitive long, given from any object + * of one type a function can get a primitive long + * + * @param array of one type of objects + * @param converter from one type of objects to a primitive long + * @param comparator - a comparator for primitive long values + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param a primitive long key value + * @return + */ + public static int findByLong(final T[] array, + final ToLongFunction converter, + final LongComparator comparator, + final int from, final int to, final long key) { + rangeCheck(array.length, from, to); + return findByIndex(i -> comparator.compareAsLong(converter.applyAsLong(array[i]), key), from, to); + } + + /** + * Binary search supporting search for one type of objects + * using primitive double, given from any object + * of one type a function can get a primitive double + * + * @param list of one type of objects + * @param converter from one type of objects to a primitive double + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param a primitive double key value + * @return + */ + public static int findByDouble(final List list, + final ToDoubleFunction converter, + final int from, final int to, final double key) { + final IntFunction getter = listGetter(list); + return findByDouble(i -> converter.applyAsDouble(getter.apply(i)), from, to, key); + } + + /** + * Binary search supporting search for one type of objects + * using primitive double, given from any object + * of one type a function can get a primitive double + * + * @param array of one type of objects + * @param converter from one type of objects to a primitive double + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param a primitive double key value + * @return + */ + public static int findByDouble(final T[] array, + final ToDoubleFunction converter, + final int from, final int to, final double key) { + rangeCheck(array.length, from, to); + return findByDouble(i -> converter.applyAsDouble(array[i]), from, to, key); + } + + /** + * Binary search supporting search for one type of objects + * using primitive double, given from any object + * of one type a function can get a primitive double + * + * @param list of one type of objects + * @param converter from one type of objects to a primitive double + * @param comparator - a comparator for primitive double values + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param a primitive double key value + * @return + */ + public static int findByDouble(final List list, + final ToDoubleFunction converter, + final DoubleComparator comparator, + final int from, final int to, final double key) { + final IntFunction getter = listGetter(list); + return findByIndex(i -> comparator.compareAsDouble(converter.applyAsDouble(getter.apply(i)), key), from, to); + } + + /** + * Binary search supporting search for one type of objects + * using primitive double, given from any object + * of one type a function can get a primitive double + * + * @param array of one type of objects + * @param converter from one type of objects to a primitive double + * @param comparator - a comparator for primitive double values + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param a primitive double key value + * @return + */ + public static int findByDouble(final T[] array, + final ToDoubleFunction converter, + final DoubleComparator comparator, + final int from, final int to, final double key) { + rangeCheck(array.length, from, to); + return findByIndex(i -> comparator.compareAsDouble(converter.applyAsDouble(array[i]), key), from, to); + } + + /** + * Blind binary search, presuming there is some sorted structure, + * whose sorting is someway ensured by some key object, using the getter + * who, given an index in the invisible structure, can produce a key + * object someway used to sort it. + * + * @param getter - a function accepting indexes, producing a key object used for sort + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param key - a key object + */ + public static > int find(final IntFunction getter, + final int from, final int to, final E key) { + return findByIndex(i -> getter.apply(i).compareTo(key), from, to); + } + + /** + * Blind binary search, presuming there is some sorted structure, + * whose sorting is someway ensured by some key object, using the getter + * who, given an index in the invisible structure, can produce a key + * object someway used to sort it. + * + * @param getter - a function accepting indexes, producing a key object used for sort + * @param comparator - a comparator for objects of type E + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param key - a key object + */ + public static int find(final IntFunction getter, + final Comparator comparator, + final int from, final int to, final E key) { + return findByIndex(i -> comparator.compare(getter.apply(i), key), from, to); + } + + /** + * Blind binary search, presuming there is some sorted structure, + * whose sorting is someway ensured by primitive integer key, + * using the getter who, given an index in the invisible structure, can produce + * the primitive integer key someway used to sort it. + * + * @param getter - a function accepting indexes, producing a primitive integer used for sort + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param key - a primitive integer key + */ + public static int findByInt(final IntUnaryOperator getter, + final int from, final int to, final int key) { + return findByInt(getter, Integer::compare, from, to, key); + } + + /** + * Blind binary search, presuming there is some sorted structure, + * whose sorting is someway ensured by primitive integer key, + * using the getter who, given an index in the invisible structure, can produce + * the primitive integer key someway used to sort it. + * + * @param getter - a function accepting indexes, producing a primitive integer used for sort + * @param comparator - a comparator for primitive integers + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param key - a primitive integer key + */ + public static int findByInt(final IntUnaryOperator getter, + final IntBinaryOperator comparator, + final int from, final int to, final int key) { + return findByIndex(i -> comparator.applyAsInt(getter.applyAsInt(i), key), from, to); + } + + /** + * Blind binary search, presuming there is some sorted structure, + * whose sorting is someway ensured by primitive long key, + * using the getter who, given an index in the invisible structure, can produce + * the primitive long key someway used to sort it. + * + * @param getter - a function accepting indexes, producing a primitive long used for sort + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param key - a primitive long key + */ + public static int findByLong(final LongGetter getter, + final int from, final int to, final long key) { + return findByLong(getter, Long::compare, from, to, key); + } + + /** + * Blind binary search, presuming there is some sorted structure, + * whose sorting is someway ensured by primitive long key, + * using the getter who, given an index in the invisible structure, can produce + * the primitive long key someway used to sort it. + * + * @param getter - a function accepting indexes, producing a primitive long used for sort + * @param comparator - a comparator for primitive long values + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param key - a primitive long key + */ + public static int findByLong(final LongGetter getter, + final LongComparator comparator, + final int from, final int to, final long key) { + return findByIndex(i -> comparator.compareAsLong(getter.getAsLong(i), key), from, to); + } + + /** + * Blind binary search, presuming there is some sorted structure, + * whose sorting is someway ensured by primitive double key, + * using the getter who, given an index in the invisible structure, can produce + * the primitive double key someway used to sort it. + * + * @param getter - a function accepting indexes, producing a primitive double used for sort + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param key - a primitive double key + */ + public static int findByDouble(final DoubleGetter getter, + final int from, final int to, final double key) { + return findByDouble(getter, Double::compare, from, to, key); + } + + /** + * Blind binary search, presuming there is some sorted structure, + * whose sorting is someway ensured by primitive double key, + * using the getter who, given an index in the invisible structure, can produce + * the primitive double key someway used to sort it. + * + * @param getter - a function accepting indexes, producing a primitive double used for sort + * @param comparator - a comparator for primitive double values + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + * @param key - a primitive double key + */ + public static int findByDouble(final DoubleGetter getter, + final DoubleComparator comparator, + final int from, final int to, final double key) { + return findByIndex(i -> comparator.compareAsDouble(getter.getAsDouble(i), key), from, to); + } + + /** + * Blind binary search applying array elements to matching function until it returns 0 + * @param list of one type of objects + * @param matcher - a matcher returning comparison result based on single list element + **/ + public static int findByMatch(final T[] array, + final ToIntFunction matcher) { + return findByMatch(array, matcher, 0, array.length); + } + + /** + * Blind binary search applying List elements to matching function until it returns 0 + * @param list of one type of objects + * @param matcher - a matcher returning comparison result based on single list element + **/ + public static int findByMatch(final List list, + final ToIntFunction matcher) { + return findByMatch(list, matcher, 0, list.size()); + } + + /** + * Blind binary search applying array elements to matching function until it returns 0 + * @param list of one type of objects + * @param matcher - a matcher returning comparison result based on single list element + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + **/ + public static int findByMatch(final T[] array, + final ToIntFunction matcher, + final int from, + final int to) { + rangeCheck(array.length, from, to); + return findByIndex(i -> matcher.applyAsInt(array[i]), from, to); + } + + /** + * Blind binary search applying List elements to matching function until it returns 0 + * @param list of one type of objects + * @param matcher - a matcher returning comparison result based on single list element + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + **/ + public static int findByMatch(final List list, + final ToIntFunction matcher, + final int from, + final int to) { + final IntFunction getter = listGetter(list); + return findByIndex(i -> matcher.applyAsInt(getter.apply(i)), from, to); + } + + /** + * Blind binary search applying index to comparison function until it returns 0 + * @param comparator - index-comparing function + * @param from - an index (inclusive) from which to start search + * @param to - an index (exclusive) from which to start search + **/ + public static int findByIndex(final IntUnaryOperator comparator, final int from, final int to) { + int low = from; + int high = to - 1; + + while (low <= high) { + int mid = (low + high) >>> 1; + int cmp = comparator.applyAsInt(mid); + + if (cmp < 0) { + low = mid + 1; + } else if (cmp > 0) { + high = mid - 1; + } else { + return mid; // key found + } + } + return -(low + 1); // key not found + } + + /** + * A copy of Arrays.rangeCheck private method from JDK + */ + private static void rangeCheck(int arrayLength, int fromIndex, int toIndex) { + if (fromIndex > toIndex) { + throw new IllegalArgumentException( + "fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); + } + if (fromIndex < 0) { + throw new ArrayIndexOutOfBoundsException(fromIndex); + } + if (toIndex > arrayLength) { + throw new ArrayIndexOutOfBoundsException(toIndex); + } + } + + /** + * A copy of Collections.get private method from JDK + */ + private static T get(ListIterator i, int index) { + T obj = null; + int pos = i.nextIndex(); + if (pos <= index) { + do { + obj = i.next(); + } while (pos++ < index); + } else { + do { + obj = i.previous(); + } while (--pos > index); + } + return obj; + } + + private static > IntFunction listGetter(final L list) { + if (list instanceof RandomAccess) { + return ((List) list)::get; + } + + final ListIterator it = list.listIterator(); + return i -> get(it, i); + } + + @FunctionalInterface + public interface LongComparator { + + int compareAsLong(long f1, long f2); + } + + @FunctionalInterface + public interface DoubleComparator { + + int compareAsDouble(double f1, double f2); + } + + @FunctionalInterface + public interface LongGetter { + + long getAsLong(int i); + } + + @FunctionalInterface + public interface DoubleGetter { + + double getAsDouble(int i); + } +} \ No newline at end of file diff --git a/src/utils/C2JUtils.java b/src/utils/C2JUtils.java index f5fde18..8650a05 100644 --- a/src/utils/C2JUtils.java +++ b/src/utils/C2JUtils.java @@ -23,7 +23,7 @@ /** * Some utilities that emulate C stlib methods or provide convenient functions * to do repetitive system and memory related stuff. - * + * * @author Maes */ public final class C2JUtils { @@ -58,7 +58,7 @@ public static char[] strcpy(char[] s1, String s2) { /** Return a byte[] array from the string's chars, * ANDed to the lowest 8 bits. - * + * * @param str * @return */ @@ -80,7 +80,7 @@ public static byte[] toByteArray(String str) { * cache the result whenever possible. Consider adding an index or ID type * of field to the searched type if you require to use this property too * often. - * + * * @param array * @param key * @return @@ -99,7 +99,7 @@ public static int indexOf(Object[] array, Object key) { * Emulates C-style "string comparison". "Strings" are considered * null-terminated, and comparison is performed only up to the smaller of * the two. - * + * * @param s1 * @param s2 * @return @@ -121,7 +121,7 @@ public static boolean strcmp(char[] s1, String s2) { /** * C-like string length (null termination). - * + * * @param s1 * @return */ @@ -142,7 +142,7 @@ public static int strlen(char[] s1) { /** * Return a new String based on C-like null termination. - * + * * @param s * @return */ @@ -165,7 +165,7 @@ public static String nullTerminatedString(char[] s) { * Automatically "initializes" arrays of objects with their default * constuctor. It's better than doing it by hand, IMO. If you have a better * way, be my guest. - * + * * @param os * @param c * @throws Exception @@ -186,7 +186,7 @@ public static void initArrayOfObjects(T[] os, Class c) { * Automatically "initializes" arrays of objects with their default * constuctor. It's better than doing it by hand, IMO. If you have a better * way, be my guest. - * + * * @param os * @throws Exception * @throws @@ -211,12 +211,12 @@ public static void initArrayOfObjects(T[] os) { * causes performance loss. Use instead: * SomeType[] array = new SomeType[length]; * Arrays.setAll(array, i -> new SomeType()); - * + * * - Good Sign 2017/05/07 - * + * * Uses reflection to automatically create and initialize an array of * objects of the specified class. Does not require casting on "reception". - * + * * @param * @param c * @param num @@ -246,10 +246,10 @@ public static T[] createArrayOfObjects(Class c, int num) { * objects of the specified class. Does not require casting on "reception". * Requires an instance of the desired class. This allows getting around * determining the runtime type of parametrized types. - * - * + * + * * @param - * @param instance An instance of a particular class. + * @param instance An instance of a particular class. * @param num * @return * @return @@ -278,7 +278,7 @@ public static T[] createArrayOfObjects(T instance, int num) { * Automatically "initializes" arrays of objects with their default * constuctor. It's better than doing it by hand, IMO. If you have a better * way, be my guest. - * + * * @param os * @param startpos inclusive * @param endpos non-inclusive @@ -359,7 +359,7 @@ public static char unsigned(short num) { /** * Convenient alias for System.arraycopy(src, 0, dest, 0, length); - * + * * @param dest * @param src * @param length @@ -455,7 +455,7 @@ public static boolean testWriteAccess(String uri) { /** * Returns true if flags are included in arg. Synonymous with (flags & * arg)!=0 - * + * * @param flags * @param arg * @return @@ -472,7 +472,7 @@ public static boolean flags(long flags, long arg) { * Returns 1 for true and 0 for false. Useful, given the amount of * "arithmetic" logical functions in legacy code. Synonymous with * (expr?1:0); - * + * * @param flags * @param arg * @return @@ -485,7 +485,7 @@ public static int eval(boolean expr) { * Returns 1 for non-null and 0 for null objects. Useful, given the amount * of "existential" logical functions in legacy code. Synonymous with * (expr!=null); - * + * * @param flags * @param arg * @return @@ -496,7 +496,7 @@ public static boolean eval(Object expr) { /** * Returns true for expr!=0, false otherwise. - * + * * @param flags * @param arg * @return @@ -507,7 +507,7 @@ public static boolean eval(int expr) { /** * Returns true for expr!=0, false otherwise. - * + * * @param flags * @param arg * @return @@ -525,7 +525,7 @@ public static void resetAll(final Resettable[] r) { /** * Useful for unquoting strings, since StringTokenizer won't do it for us. * Returns null upon any failure. - * + * * @param s * @param c * @return @@ -570,9 +570,9 @@ public static String unquoteIfQuoted(String s, char c) { } /** - * Return either 0 or a hashcode - * - * @param o + * Return either 0 or a hashcode + * + * @param o */ public static int pointer(Object o) { if (o == null) { @@ -623,7 +623,7 @@ public static boolean checkForExtension(String filename, String ext) { /** Return the filename without extension, and stripped * of the path. - * + * * @param s * @return */ @@ -655,7 +655,7 @@ public static String removeExtension(String s) { * indicators. There's normally no need to enforce this behavior, as there's * nothing preventing the engine from INTERNALLY using lump names with >8 * chars. However, just to be sure... - * + * * @param path * @param limit Set to any value >0 to enforce a length limit * @param whole keep extension if set to true @@ -684,7 +684,7 @@ public static String extractFileBase(String path, int limit, boolean whole) { len -= src; } - // copy UP to the specific number of characters, or all + // copy UP to the specific number of characters, or all if (limit > 0) { len = Math.min(limit, len); } @@ -721,7 +721,7 @@ public static T[] resize(T[] oldarray, int newsize) { /** Generic array resizing method. Calls Arrays.copyOf but then also * uses initArrayOfObject for the "abundant" elements. - * + * * @param * @param instance * @param oldarray @@ -746,7 +746,7 @@ public static T[] resize(T instance, T[] oldarray, int newsize) { /** Resize an array without autoinitialization. Same as Arrays.copyOf(..), just * prints a message. - * + * * @param * @param oldarray * @param newsize @@ -797,7 +797,7 @@ public final static T[] getNewArray(Class c, int size) { /** * Try to guess whether a URI represents a local file, a network any of the * above but zipped. Returns - * + * * @param uri * @return an int with flags set according to InputStreamSugar */ @@ -853,4 +853,4 @@ public static int guessResourceType(String uri) { private C2JUtils() { } -} +} \ No newline at end of file diff --git a/src/utils/GenericCopy.java b/src/utils/GenericCopy.java index 67a11c6..107c950 100644 --- a/src/utils/GenericCopy.java +++ b/src/utils/GenericCopy.java @@ -1,169 +1,169 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package utils; - -import java.util.Arrays; -import java.util.function.IntFunction; -import java.util.function.Supplier; - -public class GenericCopy { - - private static final boolean[] BOOL_0 = {false}; - private static final byte[] BYTE_0 = {0}; - private static final short[] SHORT_0 = {0}; - private static final char[] CHAR_0 = {0}; - private static final int[] INT_0 = {0}; - private static final float[] FLOAT_0 = {0}; - private static final long[] LONG_0 = {0}; - private static final double[] DOUBLE_0 = {0}; - - public static void memset(long[] array, int start, int length, long... value) { - if (length > 0) { - if (value.length == 0) { - value = LONG_0; - } - System.arraycopy(value, 0, array, start, value.length); - - for (int i = value.length; i < length; i += i) { - System.arraycopy(array, start, array, start + i, ((length - i) < i) ? (length - i) : i); - } - } - } - - public static void memset(int[] array, int start, int length, int... value) { - if (length > 0) { - if (value.length == 0) { - value = INT_0; - } - System.arraycopy(value, 0, array, start, value.length); - - for (int i = value.length; i < length; i += i) { - System.arraycopy(array, start, array, start + i, ((length - i) < i) ? (length - i) : i); - } - } - } - - public static void memset(short[] array, int start, int length, short... value) { - if (length > 0) { - if (value.length == 0) { - value = SHORT_0; - } - System.arraycopy(value, 0, array, start, value.length); - - for (int i = value.length; i < length; i += i) { - System.arraycopy(array, start, array, start + i, ((length - i) < i) ? (length - i) : i); - } - } - } - - public static void memset(char[] array, int start, int length, char... value) { - if (length > 0) { - if (value.length == 0) { - value = CHAR_0; - } - System.arraycopy(value, 0, array, start, value.length); - - for (int i = value.length; i < length; i += i) { - System.arraycopy(array, start, array, start + i, ((length - i) < i) ? (length - i) : i); - } - } - } - - public static void memset(byte[] array, int start, int length, byte... value) { - if (length > 0) { - if (value.length == 0) { - value = BYTE_0; - } - System.arraycopy(value, 0, array, start, value.length); - - for (int i = value.length; i < length; i += i) { - System.arraycopy(array, start, array, start + i, ((length - i) < i) ? (length - i) : i); - } - } - } - - public static void memset(double[] array, int start, int length, double... value) { - if (length > 0) { - if (value.length == 0) { - value = DOUBLE_0; - } - System.arraycopy(value, 0, array, start, value.length); - - for (int i = value.length; i < length; i += i) { - System.arraycopy(array, start, array, start + i, ((length - i) < i) ? (length - i) : i); - } - } - } - - public static void memset(float[] array, int start, int length, float... value) { - if (length > 0) { - if (value.length == 0) { - value = FLOAT_0; - } - System.arraycopy(value, 0, array, start, value.length); - - for (int i = value.length; i < length; i += i) { - System.arraycopy(array, start, array, start + i, ((length - i) < i) ? (length - i) : i); - } - } - } - - public static void memset(boolean[] array, int start, int length, boolean... value) { - if (length > 0) { - if (value.length == 0) { - value = BOOL_0; - } - System.arraycopy(value, 0, array, start, value.length); - - for (int i = value.length; i < length; i += i) { - System.arraycopy(array, start, array, start + i, ((length - i) < i) ? (length - i) : i); - } - } - } - - @SuppressWarnings("SuspiciousSystemArraycopy") - public static void memset(T array, int start, int length, T value, int valueStart, int valueLength) { - if (length > 0 && valueLength > 0) { - System.arraycopy(value, valueStart, array, start, valueLength); - - for (int i = valueLength; i < length; i += i) { - System.arraycopy(array, start, array, start + i, ((length - i) < i) ? (length - i) : i); - } - } - } - - @SuppressWarnings("SuspiciousSystemArraycopy") - public static void memcpy(T srcArray, int srcStart, T dstArray, int dstStart, int length) { - System.arraycopy(srcArray, srcStart, dstArray, dstStart, length); - } - - public static T[] malloc(final ArraySupplier supplier, final IntFunction generator, final int length) { - final T[] array = generator.apply(length); - Arrays.setAll(array, supplier::getWithInt); - return array; - } - - public interface ArraySupplier extends Supplier { - - default T getWithInt(int ignoredInt) { - return get(); - } - } - - private GenericCopy() { - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package utils; + +import java.util.Arrays; +import java.util.function.IntFunction; +import java.util.function.Supplier; + +public class GenericCopy { + + private static final boolean[] BOOL_0 = {false}; + private static final byte[] BYTE_0 = {0}; + private static final short[] SHORT_0 = {0}; + private static final char[] CHAR_0 = {0}; + private static final int[] INT_0 = {0}; + private static final float[] FLOAT_0 = {0}; + private static final long[] LONG_0 = {0}; + private static final double[] DOUBLE_0 = {0}; + + public static void memset(long[] array, int start, int length, long... value) { + if (length > 0) { + if (value.length == 0) { + value = LONG_0; + } + System.arraycopy(value, 0, array, start, value.length); + + for (int i = value.length; i < length; i += i) { + System.arraycopy(array, start, array, start + i, ((length - i) < i) ? (length - i) : i); + } + } + } + + public static void memset(int[] array, int start, int length, int... value) { + if (length > 0) { + if (value.length == 0) { + value = INT_0; + } + System.arraycopy(value, 0, array, start, value.length); + + for (int i = value.length; i < length; i += i) { + System.arraycopy(array, start, array, start + i, ((length - i) < i) ? (length - i) : i); + } + } + } + + public static void memset(short[] array, int start, int length, short... value) { + if (length > 0) { + if (value.length == 0) { + value = SHORT_0; + } + System.arraycopy(value, 0, array, start, value.length); + + for (int i = value.length; i < length; i += i) { + System.arraycopy(array, start, array, start + i, ((length - i) < i) ? (length - i) : i); + } + } + } + + public static void memset(char[] array, int start, int length, char... value) { + if (length > 0) { + if (value.length == 0) { + value = CHAR_0; + } + System.arraycopy(value, 0, array, start, value.length); + + for (int i = value.length; i < length; i += i) { + System.arraycopy(array, start, array, start + i, ((length - i) < i) ? (length - i) : i); + } + } + } + + public static void memset(byte[] array, int start, int length, byte... value) { + if (length > 0) { + if (value.length == 0) { + value = BYTE_0; + } + System.arraycopy(value, 0, array, start, value.length); + + for (int i = value.length; i < length; i += i) { + System.arraycopy(array, start, array, start + i, ((length - i) < i) ? (length - i) : i); + } + } + } + + public static void memset(double[] array, int start, int length, double... value) { + if (length > 0) { + if (value.length == 0) { + value = DOUBLE_0; + } + System.arraycopy(value, 0, array, start, value.length); + + for (int i = value.length; i < length; i += i) { + System.arraycopy(array, start, array, start + i, ((length - i) < i) ? (length - i) : i); + } + } + } + + public static void memset(float[] array, int start, int length, float... value) { + if (length > 0) { + if (value.length == 0) { + value = FLOAT_0; + } + System.arraycopy(value, 0, array, start, value.length); + + for (int i = value.length; i < length; i += i) { + System.arraycopy(array, start, array, start + i, ((length - i) < i) ? (length - i) : i); + } + } + } + + public static void memset(boolean[] array, int start, int length, boolean... value) { + if (length > 0) { + if (value.length == 0) { + value = BOOL_0; + } + System.arraycopy(value, 0, array, start, value.length); + + for (int i = value.length; i < length; i += i) { + System.arraycopy(array, start, array, start + i, ((length - i) < i) ? (length - i) : i); + } + } + } + + @SuppressWarnings("SuspiciousSystemArraycopy") + public static void memset(T array, int start, int length, T value, int valueStart, int valueLength) { + if (length > 0 && valueLength > 0) { + System.arraycopy(value, valueStart, array, start, valueLength); + + for (int i = valueLength; i < length; i += i) { + System.arraycopy(array, start, array, start + i, ((length - i) < i) ? (length - i) : i); + } + } + } + + @SuppressWarnings("SuspiciousSystemArraycopy") + public static void memcpy(T srcArray, int srcStart, T dstArray, int dstStart, int length) { + System.arraycopy(srcArray, srcStart, dstArray, dstStart, length); + } + + public static T[] malloc(final ArraySupplier supplier, final IntFunction generator, final int length) { + final T[] array = generator.apply(length); + Arrays.setAll(array, supplier::getWithInt); + return array; + } + + public interface ArraySupplier extends Supplier { + + default T getWithInt(int ignoredInt) { + return get(); + } + } + + private GenericCopy() { + } +} \ No newline at end of file diff --git a/src/utils/OSValidator.java b/src/utils/OSValidator.java index 928c172..1795ac5 100644 --- a/src/utils/OSValidator.java +++ b/src/utils/OSValidator.java @@ -1,16 +1,16 @@ package utils; -/** Half-assed way of finding the OS we're running under, shamelessly +/** Half-assed way of finding the OS we're running under, shamelessly * ripped from: - * + * * http://www.mkyong.com/java/how-to-detect-os-in-java-systemgetpropertyosname/ * . * This is required, as some things in AWT don't work exactly consistently cross-OS * (AWT frame size is the first thing that goes wrong, but also mouse grabbing * behavior). - * + * * TODO: replace with Apache Commons library? - * + * * @author velktron * */ @@ -43,4 +43,4 @@ public static boolean isUnix() { public static boolean isUnknown() { return (!isWindows() && !isUnix() && !isMac()); } -} +} \ No newline at end of file diff --git a/src/utils/OrderedExecutor.java b/src/utils/OrderedExecutor.java index 9665a75..3a47e11 100644 --- a/src/utils/OrderedExecutor.java +++ b/src/utils/OrderedExecutor.java @@ -1,117 +1,117 @@ -package utils; - -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Map; -import java.util.Queue; -import java.util.concurrent.Executor; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.logging.Level; -import java.util.logging.Logger; -import mochadoom.Loggers; - -/** - * An executor that make sure tasks submitted with the same key - * will be executed in the same order as task submission - * (order of calling the {@link #submit(Object, Runnable)} method). - * - * Tasks submitted will be run in the given {@link Executor}. - * There is no restriction on how many threads in the given {@link Executor} - * needs to have (it can be single thread executor as well as a cached thread pool). - * - * If there are more than one thread in the given {@link Executor}, tasks - * submitted with different keys may be executed in parallel, but never - * for tasks submitted with the same key. - * - * * @param type of keys. - */ -public class OrderedExecutor { - - private static final Logger LOGGER = Loggers.getLogger(OrderedExecutor.class.getName()); - - private final Executor executor; - private final Map tasks; - - /** - * Constructs a {@code OrderedExecutor}. - * - * @param executor tasks will be run in this executor. - */ - public OrderedExecutor(Executor executor) { - this.executor = executor; - this.tasks = new HashMap<>(); - } - - /** - * Adds a new task to run for the given key. - * - * @param key the key for applying tasks ordering. - * @param runnable the task to run. - */ - public synchronized void submit(K key, Runnable runnable) { - Task task = tasks.get(key); - if (task == null) { - task = new Task(); - tasks.put(key, task); - } - task.add(runnable); - } - - /** - * Private inner class for running tasks for each key. - * Each key submitted will have one instance of this class. - */ - private class Task implements Runnable { - - private final Lock lock; - private final Queue queue; - - Task() { - this.lock = new ReentrantLock(); - this.queue = new LinkedList<>(); - } - - public void add(Runnable runnable) { - boolean runTask; - lock.lock(); - try { - // Run only if no job is running. - runTask = queue.isEmpty(); - queue.offer(runnable); - } finally { - lock.unlock(); - } - if (runTask) { - executor.execute(this); - } - } - - @Override - public void run() { - // Pick a task to run. - Runnable runnable; - lock.lock(); - try { - runnable = queue.peek(); - } finally { - lock.unlock(); - } - try { - runnable.run(); - } catch (Exception ex) { - LOGGER.log(Level.SEVERE, "OrderedExecutor run failure", ex); - } - // Check to see if there are queued task, if yes, submit for execution. - lock.lock(); - try { - queue.poll(); - if (!queue.isEmpty()) { - executor.execute(this); - } - } finally { - lock.unlock(); - } - } - } -} +package utils; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.Executor; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Level; +import java.util.logging.Logger; +import mochadoom.Loggers; + +/** + * An executor that make sure tasks submitted with the same key + * will be executed in the same order as task submission + * (order of calling the {@link #submit(Object, Runnable)} method). + * + * Tasks submitted will be run in the given {@link Executor}. + * There is no restriction on how many threads in the given {@link Executor} + * needs to have (it can be single thread executor as well as a cached thread pool). + * + * If there are more than one thread in the given {@link Executor}, tasks + * submitted with different keys may be executed in parallel, but never + * for tasks submitted with the same key. + * + * * @param type of keys. + */ +public class OrderedExecutor { + + private static final Logger LOGGER = Loggers.getLogger(OrderedExecutor.class.getName()); + + private final Executor executor; + private final Map tasks; + + /** + * Constructs a {@code OrderedExecutor}. + * + * @param executor tasks will be run in this executor. + */ + public OrderedExecutor(Executor executor) { + this.executor = executor; + this.tasks = new HashMap<>(); + } + + /** + * Adds a new task to run for the given key. + * + * @param key the key for applying tasks ordering. + * @param runnable the task to run. + */ + public synchronized void submit(K key, Runnable runnable) { + Task task = tasks.get(key); + if (task == null) { + task = new Task(); + tasks.put(key, task); + } + task.add(runnable); + } + + /** + * Private inner class for running tasks for each key. + * Each key submitted will have one instance of this class. + */ + private class Task implements Runnable { + + private final Lock lock; + private final Queue queue; + + Task() { + this.lock = new ReentrantLock(); + this.queue = new LinkedList<>(); + } + + public void add(Runnable runnable) { + boolean runTask; + lock.lock(); + try { + // Run only if no job is running. + runTask = queue.isEmpty(); + queue.offer(runnable); + } finally { + lock.unlock(); + } + if (runTask) { + executor.execute(this); + } + } + + @Override + public void run() { + // Pick a task to run. + Runnable runnable; + lock.lock(); + try { + runnable = queue.peek(); + } finally { + lock.unlock(); + } + try { + runnable.run(); + } catch (Exception ex) { + LOGGER.log(Level.SEVERE, "OrderedExecutor run failure", ex); + } + // Check to see if there are queued task, if yes, submit for execution. + lock.lock(); + try { + queue.poll(); + if (!queue.isEmpty()) { + executor.execute(this); + } + } finally { + lock.unlock(); + } + } + } +} \ No newline at end of file diff --git a/src/utils/ParseString.java b/src/utils/ParseString.java index b648c83..525398e 100644 --- a/src/utils/ParseString.java +++ b/src/utils/ParseString.java @@ -1,96 +1,96 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package utils; - -import java.util.Optional; - -/** - * @author Good Sign - */ -public enum ParseString { - ; - public static Object parseString(String stringSource) { - final Optional qt = QuoteType.getQuoteType(stringSource); - final boolean quoted = qt.isPresent(); - if (quoted) { - stringSource = qt.get().unQuote(stringSource); - } - - if (quoted && stringSource.length() == 1) { - final Character test = stringSource.charAt(0); - if (test >= 0 && test < 255) { - return test; - } - } - - Optional ret = checkInt(stringSource); - if (!ret.isPresent()) { - ret = checkDouble(stringSource); - if (!ret.isPresent()) { - ret = checkBoolean(stringSource); - if (!ret.isPresent()) { - return stringSource; - } - } - } - - return ret.get(); - } - - public static Optional checkInt(final String stringSource) { - Optional ret; - try { - long longRet = Long.parseLong(stringSource); - return longRet < Integer.MAX_VALUE - ? Optional.of((int) longRet) - : Optional.of(longRet); - } catch (NumberFormatException e) { - } - - try { - long longRet = Long.decode(stringSource); - return longRet < Integer.MAX_VALUE - ? Optional.of((int) longRet) - : Optional.of(longRet); - } catch (NumberFormatException e) { - } - - return Optional.empty(); - } - - public static Optional checkDouble(final String stringSource) { - try { - return Optional.of(Double.parseDouble(stringSource)); - } catch (NumberFormatException e) { - } - - return Optional.empty(); - } - - public static Optional checkBoolean(final String stringSource) { - try { - return Optional.of(Boolean.parseBoolean(stringSource)); - } catch (NumberFormatException e) { - } - - if ("false".compareToIgnoreCase(stringSource) == 0) { - return Optional.of(Boolean.FALSE); - } - - return Optional.empty(); - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package utils; + +import java.util.Optional; + +/** + * @author Good Sign + */ +public enum ParseString { + ; + public static Object parseString(String stringSource) { + final Optional qt = QuoteType.getQuoteType(stringSource); + final boolean quoted = qt.isPresent(); + if (quoted) { + stringSource = qt.get().unQuote(stringSource); + } + + if (quoted && stringSource.length() == 1) { + final Character test = stringSource.charAt(0); + if (test >= 0 && test < 255) { + return test; + } + } + + Optional ret = checkInt(stringSource); + if (!ret.isPresent()) { + ret = checkDouble(stringSource); + if (!ret.isPresent()) { + ret = checkBoolean(stringSource); + if (!ret.isPresent()) { + return stringSource; + } + } + } + + return ret.get(); + } + + public static Optional checkInt(final String stringSource) { + Optional ret; + try { + long longRet = Long.parseLong(stringSource); + return longRet < Integer.MAX_VALUE + ? Optional.of((int) longRet) + : Optional.of(longRet); + } catch (NumberFormatException e) { + } + + try { + long longRet = Long.decode(stringSource); + return longRet < Integer.MAX_VALUE + ? Optional.of((int) longRet) + : Optional.of(longRet); + } catch (NumberFormatException e) { + } + + return Optional.empty(); + } + + public static Optional checkDouble(final String stringSource) { + try { + return Optional.of(Double.parseDouble(stringSource)); + } catch (NumberFormatException e) { + } + + return Optional.empty(); + } + + public static Optional checkBoolean(final String stringSource) { + try { + return Optional.of(Boolean.parseBoolean(stringSource)); + } catch (NumberFormatException e) { + } + + if ("false".compareToIgnoreCase(stringSource) == 0) { + return Optional.of(Boolean.FALSE); + } + + return Optional.empty(); + } +} \ No newline at end of file diff --git a/src/utils/QuoteType.java b/src/utils/QuoteType.java index d39cd95..55c9086 100644 --- a/src/utils/QuoteType.java +++ b/src/utils/QuoteType.java @@ -48,4 +48,4 @@ public static Optional getQuoteType(final String stringSource) { return Optional.empty(); } -} +} \ No newline at end of file diff --git a/src/utils/ResourceIO.java b/src/utils/ResourceIO.java index 505fd59..63c34d8 100644 --- a/src/utils/ResourceIO.java +++ b/src/utils/ResourceIO.java @@ -1,98 +1,98 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package utils; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.OpenOption; -import java.nio.file.Path; -import java.util.function.Consumer; -import java.util.function.Supplier; -import java.util.logging.Level; -import java.util.logging.Logger; -import mochadoom.Loggers; - -/** - * Resource IO to automate read/write on configuration/resources - * - * @author Good Sign - */ -public class ResourceIO { - - private static final Logger LOGGER = Loggers.getLogger(ResourceIO.class.getName()); - - private final Path file; - private final Charset charset = Charset.forName("US-ASCII"); - - public ResourceIO(final File file) { - this.file = file.toPath(); - } - - public ResourceIO(final Path file) { - this.file = file; - } - - public ResourceIO(final String path) { - this.file = FileSystems.getDefault().getPath(path); - } - - public boolean exists() { - return Files.exists(file); - } - - public boolean readLines(final Consumer lineConsumer) { - if (Files.exists(file)) { - try ( BufferedReader reader = Files.newBufferedReader(file, charset)) { - String line; - while ((line = reader.readLine()) != null) { - lineConsumer.accept(line); - } - - return true; - } catch (IOException x) { - LOGGER.log(Level.WARNING, "ResourceIO read failure", x); - return false; - } - } - - return false; - } - - public boolean writeLines(final Supplier lineSupplier, final OpenOption... options) { - try ( BufferedWriter writer = Files.newBufferedWriter(file, charset, options)) { - String line; - while ((line = lineSupplier.get()) != null) { - writer.write(line, 0, line.length()); - writer.newLine(); - } - - return true; - } catch (IOException x) { - LOGGER.log(Level.WARNING, "ResourceIO write failure", x); - return false; - } - } - - public String getFileame() { - return file.toString(); - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package utils; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; +import mochadoom.Loggers; + +/** + * Resource IO to automate read/write on configuration/resources + * + * @author Good Sign + */ +public class ResourceIO { + + private static final Logger LOGGER = Loggers.getLogger(ResourceIO.class.getName()); + + private final Path file; + private final Charset charset = Charset.forName("US-ASCII"); + + public ResourceIO(final File file) { + this.file = file.toPath(); + } + + public ResourceIO(final Path file) { + this.file = file; + } + + public ResourceIO(final String path) { + this.file = FileSystems.getDefault().getPath(path); + } + + public boolean exists() { + return Files.exists(file); + } + + public boolean readLines(final Consumer lineConsumer) { + if (Files.exists(file)) { + try ( BufferedReader reader = Files.newBufferedReader(file, charset)) { + String line; + while ((line = reader.readLine()) != null) { + lineConsumer.accept(line); + } + + return true; + } catch (IOException x) { + LOGGER.log(Level.WARNING, "ResourceIO read failure", x); + return false; + } + } + + return false; + } + + public boolean writeLines(final Supplier lineSupplier, final OpenOption... options) { + try ( BufferedWriter writer = Files.newBufferedWriter(file, charset, options)) { + String line; + while ((line = lineSupplier.get()) != null) { + writer.write(line, 0, line.length()); + writer.newLine(); + } + + return true; + } catch (IOException x) { + LOGGER.log(Level.WARNING, "ResourceIO write failure", x); + return false; + } + } + + public String getFileame() { + return file.toString(); + } +} \ No newline at end of file diff --git a/src/utils/Throwers.java b/src/utils/Throwers.java index 31183a6..fb75762 100644 --- a/src/utils/Throwers.java +++ b/src/utils/Throwers.java @@ -1,308 +1,308 @@ -package utils; - -import java.io.PrintStream; -import java.io.PrintWriter; -import java.util.concurrent.Callable; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.BiPredicate; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; - -public enum Throwers { - ; - @SafeVarargs - public static Callable - callable(ThrowingCallable r, Class... cl) throws Throwed { - return () -> { - try { - return r.call(); - } catch (Throwable e) { - if (classifyMatching(e, cl)) { - throw doThrow(e); - } else { - throw doThrowE(e); - } - } - }; - } - - @SafeVarargs - public static Runnable - runnable(ThrowingRunnable r, Class... cl) throws Throwed { - return () -> { - try { - r.run(); - } catch (Throwable e) { - if (classifyMatching(e, cl)) { - throw doThrow(e); - } else { - throw doThrowE(e); - } - } - }; - } - - @SafeVarargs - public static Consumer - consumer(ThrowingConsumer c, Class... cl) throws Throwed { - return (t) -> { - try { - c.accept(t); - } catch (Throwable e) { - if (classifyMatching(e, cl)) { - throw doThrow(e); - } else { - throw doThrowE(e); - } - } - }; - } - - @SafeVarargs - public static BiConsumer - biConsumer(ThrowingBiConsumer c, Class... cl) throws Throwed { - return (t1, t2) -> { - try { - c.accept(t1, t2); - } catch (Throwable e) { - if (classifyMatching(e, cl)) { - throw doThrow(e); - } else { - throw doThrowE(e); - } - } - }; - } - - @SafeVarargs - public static Predicate - predicate(ThrowingPredicate p, Class... cl) throws Throwed { - return (t) -> { - try { - return p.test(t); - } catch (Throwable e) { - if (classifyMatching(e, cl)) { - throw doThrow(e); - } else { - throw doThrowE(e); - } - } - }; - } - - @SafeVarargs - public static BiPredicate - biPredicate(ThrowingBiPredicate p, Class... cl) throws Throwed { - return (t1, t2) -> { - try { - return p.test(t1, t2); - } catch (Throwable e) { - if (classifyMatching(e, cl)) { - throw doThrow(e); - } else { - throw doThrowE(e); - } - } - }; - } - - @SafeVarargs - public static Function - function(ThrowingFunction f, Class... cl) throws Throwed { - return (t) -> { - try { - return f.apply(t); - } catch (Throwable e) { - if (classifyMatching(e, cl)) { - throw doThrow(e); - } else { - throw doThrowE(e); - } - } - }; - } - - @SafeVarargs - public static BiFunction - biFunction(ThrowingBiFunction f, Class... cl) throws Throwed { - return (t1, t2) -> { - try { - return f.apply(t1, t2); - } catch (Throwable e) { - if (classifyMatching(e, cl)) { - throw doThrow(e); - } else { - throw doThrowE(e); - } - } - }; - } - - @SafeVarargs - public static Supplier - supplier(ThrowingSupplier s, Class... cl) throws Throwed { - return () -> { - try { - return s.get(); - } catch (Throwable e) { - if (classifyMatching(e, cl)) { - throw doThrow(e); - } else { - throw doThrowE(e); - } - } - }; - } - - public static class Throwed extends RuntimeException { - - private static final long serialVersionUID = 5802686109960804684L; - public final Throwable t; - - private Throwed(Throwable t) { - super(null, null, true, false); - this.t = t; - } - - @Override - public synchronized Throwable fillInStackTrace() { - return t.fillInStackTrace(); - } - - @Override - public synchronized Throwable getCause() { - return t.getCause(); - } - - @Override - public String getLocalizedMessage() { - return t.getLocalizedMessage(); - } - - @Override - public String getMessage() { - return t.getMessage(); - } - - @Override - public StackTraceElement[] getStackTrace() { - return t.getStackTrace(); - } - - @Override - public void setStackTrace(StackTraceElement[] stackTrace) { - t.setStackTrace(stackTrace); - } - - @Override - public synchronized Throwable initCause(Throwable cause) { - return t.initCause(cause); - } - - @Override - @SuppressWarnings("CallToPrintStackTrace") - public void printStackTrace() { - t.printStackTrace(); - } - - @Override - public void printStackTrace(PrintStream s) { - t.printStackTrace(s); - } - - @Override - public void printStackTrace(PrintWriter s) { - t.printStackTrace(s); - } - - @Override - public String toString() { - return t.toString(); - } - } - - public interface ThrowingCallable { - - T call() throws Throwable; - } - - public interface ThrowingRunnable { - - void run() throws Throwable; - } - - public interface ThrowingConsumer { - - void accept(T t) throws Throwable; - } - - public interface ThrowingBiConsumer { - - void accept(T1 t1, T2 t2) throws Throwable; - } - - public interface ThrowingPredicate { - - boolean test(T t) throws Throwable; - } - - public interface ThrowingBiPredicate { - - boolean test(T1 t1, T2 t2) throws Throwable; - } - - public interface ThrowingFunction { - - R apply(T t) throws Throwable; - } - - public interface ThrowingBiFunction { - - R apply(T1 t1, T2 t2) throws Throwable; - } - - public interface ThrowingSupplier { - - T get() throws Throwable; - } - - /** - * Throw checked exception as runtime exception preserving stack trace The class of exception will be changed so it - * will only trigger catch statements for new type - * - * @param e exception to be thrown - * @return impossible - * @throws Throwed - */ - public static RuntimeException doThrow(final Throwable e) throws Throwed { - throw new Throwed(e); - } - - /** - * Throw checked exception as runtime exception preserving stack trace The class of exception will not be changed. - * In example, an InterruptedException would then cause a Thread to be interrupted - * - * @param - * @param e exception to be thrown - * @return impossible - * @throws E (in runtime) - */ - @SuppressWarnings("unchecked") - private static RuntimeException doThrowE(final Throwable e) throws E { - throw (E) e; - } - - @SafeVarargs - private static boolean classifyMatching(Throwable ex, Class... options) { - for (Class o : options) { - if (o.isInstance(ex)) { - return true; - } - } - - return false; - } -} +package utils; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.concurrent.Callable; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +public enum Throwers { + ; + @SafeVarargs + public static Callable + callable(ThrowingCallable r, Class... cl) throws Throwed { + return () -> { + try { + return r.call(); + } catch (Throwable e) { + if (classifyMatching(e, cl)) { + throw doThrow(e); + } else { + throw doThrowE(e); + } + } + }; + } + + @SafeVarargs + public static Runnable + runnable(ThrowingRunnable r, Class... cl) throws Throwed { + return () -> { + try { + r.run(); + } catch (Throwable e) { + if (classifyMatching(e, cl)) { + throw doThrow(e); + } else { + throw doThrowE(e); + } + } + }; + } + + @SafeVarargs + public static Consumer + consumer(ThrowingConsumer c, Class... cl) throws Throwed { + return (t) -> { + try { + c.accept(t); + } catch (Throwable e) { + if (classifyMatching(e, cl)) { + throw doThrow(e); + } else { + throw doThrowE(e); + } + } + }; + } + + @SafeVarargs + public static BiConsumer + biConsumer(ThrowingBiConsumer c, Class... cl) throws Throwed { + return (t1, t2) -> { + try { + c.accept(t1, t2); + } catch (Throwable e) { + if (classifyMatching(e, cl)) { + throw doThrow(e); + } else { + throw doThrowE(e); + } + } + }; + } + + @SafeVarargs + public static Predicate + predicate(ThrowingPredicate p, Class... cl) throws Throwed { + return (t) -> { + try { + return p.test(t); + } catch (Throwable e) { + if (classifyMatching(e, cl)) { + throw doThrow(e); + } else { + throw doThrowE(e); + } + } + }; + } + + @SafeVarargs + public static BiPredicate + biPredicate(ThrowingBiPredicate p, Class... cl) throws Throwed { + return (t1, t2) -> { + try { + return p.test(t1, t2); + } catch (Throwable e) { + if (classifyMatching(e, cl)) { + throw doThrow(e); + } else { + throw doThrowE(e); + } + } + }; + } + + @SafeVarargs + public static Function + function(ThrowingFunction f, Class... cl) throws Throwed { + return (t) -> { + try { + return f.apply(t); + } catch (Throwable e) { + if (classifyMatching(e, cl)) { + throw doThrow(e); + } else { + throw doThrowE(e); + } + } + }; + } + + @SafeVarargs + public static BiFunction + biFunction(ThrowingBiFunction f, Class... cl) throws Throwed { + return (t1, t2) -> { + try { + return f.apply(t1, t2); + } catch (Throwable e) { + if (classifyMatching(e, cl)) { + throw doThrow(e); + } else { + throw doThrowE(e); + } + } + }; + } + + @SafeVarargs + public static Supplier + supplier(ThrowingSupplier s, Class... cl) throws Throwed { + return () -> { + try { + return s.get(); + } catch (Throwable e) { + if (classifyMatching(e, cl)) { + throw doThrow(e); + } else { + throw doThrowE(e); + } + } + }; + } + + public static class Throwed extends RuntimeException { + + private static final long serialVersionUID = 5802686109960804684L; + public final Throwable t; + + private Throwed(Throwable t) { + super(null, null, true, false); + this.t = t; + } + + @Override + public synchronized Throwable fillInStackTrace() { + return t.fillInStackTrace(); + } + + @Override + public synchronized Throwable getCause() { + return t.getCause(); + } + + @Override + public String getLocalizedMessage() { + return t.getLocalizedMessage(); + } + + @Override + public String getMessage() { + return t.getMessage(); + } + + @Override + public StackTraceElement[] getStackTrace() { + return t.getStackTrace(); + } + + @Override + public void setStackTrace(StackTraceElement[] stackTrace) { + t.setStackTrace(stackTrace); + } + + @Override + public synchronized Throwable initCause(Throwable cause) { + return t.initCause(cause); + } + + @Override + @SuppressWarnings("CallToPrintStackTrace") + public void printStackTrace() { + t.printStackTrace(); + } + + @Override + public void printStackTrace(PrintStream s) { + t.printStackTrace(s); + } + + @Override + public void printStackTrace(PrintWriter s) { + t.printStackTrace(s); + } + + @Override + public String toString() { + return t.toString(); + } + } + + public interface ThrowingCallable { + + T call() throws Throwable; + } + + public interface ThrowingRunnable { + + void run() throws Throwable; + } + + public interface ThrowingConsumer { + + void accept(T t) throws Throwable; + } + + public interface ThrowingBiConsumer { + + void accept(T1 t1, T2 t2) throws Throwable; + } + + public interface ThrowingPredicate { + + boolean test(T t) throws Throwable; + } + + public interface ThrowingBiPredicate { + + boolean test(T1 t1, T2 t2) throws Throwable; + } + + public interface ThrowingFunction { + + R apply(T t) throws Throwable; + } + + public interface ThrowingBiFunction { + + R apply(T1 t1, T2 t2) throws Throwable; + } + + public interface ThrowingSupplier { + + T get() throws Throwable; + } + + /** + * Throw checked exception as runtime exception preserving stack trace The class of exception will be changed so it + * will only trigger catch statements for new type + * + * @param e exception to be thrown + * @return impossible + * @throws Throwed + */ + public static RuntimeException doThrow(final Throwable e) throws Throwed { + throw new Throwed(e); + } + + /** + * Throw checked exception as runtime exception preserving stack trace The class of exception will not be changed. + * In example, an InterruptedException would then cause a Thread to be interrupted + * + * @param + * @param e exception to be thrown + * @return impossible + * @throws E (in runtime) + */ + @SuppressWarnings("unchecked") + private static RuntimeException doThrowE(final Throwable e) throws E { + throw (E) e; + } + + @SafeVarargs + private static boolean classifyMatching(Throwable ex, Class... options) { + for (Class o : options) { + if (o.isInstance(ex)) { + return true; + } + } + + return false; + } +} \ No newline at end of file diff --git a/src/utils/TraitFactory.java b/src/utils/TraitFactory.java index 0110479..9cc4d2f 100644 --- a/src/utils/TraitFactory.java +++ b/src/utils/TraitFactory.java @@ -1,299 +1,299 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package utils; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.logging.Logger; -import mochadoom.Loggers; - -/** - * Purpose of this pattern-interface: store Trait-specific class-wise context objects - * and be able to get them in constant time. - * - * Simple usage: - * You may read the theory below to understand, why and for what reason I wrote - * TraitFactory. However, the simplest use is: create an interface extending Trait, - * put there static final KeyChain object field, and declare some static ContextKey<> - * fields in descending classes and/or interfaces for your objects using KeyChain.newKey. - * - * Then to initialize everything, just call TraitFactory.build() and the result - * will be SharedContext to return on overriden method of Trait. - * - * TraitFactory.build utilizes (at the instantiation time, not in runtime) some - * black reflection magic to free you from need to look for every Trait in line, - * and call some registering function to add Objects to Keys in InsertConveyor. - * - * General contract of Trait: - * - * 0. In the constructor of object implementing the subset of Traits based - * on this Trait, you must call TraitFactory.build(this, idCapacity); - * Implementing this Trait otherwise means nothing. - * - * The result of TraitFactory.build(this, idCapacity); must be stored - * and the overriden method getContext() must return it. - * - * You can use some static non-final int[] field of deepest Trait dependency - * that is incremented by static initialization of all who depend on it, - * to determine idCapacity, or just guess big enough on your own. Also you can - * use helper object, KeyChain. - * - * 1. In a Trait of your subset, where you want to have some object in context, you - * must create static final ContextKey fild. During the static final ContextKey - * initialization, you can also hack into incrementing some static non-final - * somewhere, to be sure all who do the same produce unique fast ContextKeys. - * - * You can create several ContextKeys per Trait and store several contexts, - * and, if your preferedIds are unique, they will be still instant-fast. - * - * 2. You may want to be sure that all of your interfaces have created their context - * objects and put them into the InsertConveyor. To do that, you should have a - * method on the class using traits, that will descend into the top level traits, - * then lower and lower until the last of the traits. - * - * ContextKey does not override hashCode and is a final class. So the hashCode() - * method will be something like memory pointer, and uniqye per ContextKey. - * Default context storage (FactoryContext.class) does not check it until - * any new stored ContextKey have preferedId already taken, and reports different - * context Object Class. If such happen, all associated contexts are moved - * into HashMap and context acquisition will be since significantly slower. - * - * If your ContextKey does not overlap with another one, access to context Object - * would be the most instant of all possible. - * - * 3. In use, call contextGet(ContextKey) or some helper methods to get - * the Object from context. Alternatively, you can acquire the SharedContext. - * The helper methods are better in case you fear nulls. - * - * As the SharedContext is Shared, you can use it and objects from it in any - * descendants of the trait where you put this object into the context by key. - * - * If you made sure you never put two Objects of different type with two ContextKeys - * with matching preferedIds and Class'es, the cost of get(ContextKey) will be - * as negligible as one level of indirection + array access by int. - */ -public class TraitFactory { - - private final static Logger LOGGER = Loggers.getLogger(TraitFactory.class.getName()); - - public static SharedContext build(T traitUser, KeyChain usedChain) - throws IllegalArgumentException, IllegalAccessException { - return build(traitUser, usedChain.currentCapacity); - } - - public static SharedContext build(T traitUser, int idCapacity) - throws IllegalArgumentException, IllegalAccessException { - final FactoryContext c = new FactoryContext(idCapacity); - repeatRecursive(traitUser.getClass().getInterfaces(), c); - return c; - } - - private static void repeatRecursive(final Class[] traitUserInteraces, final FactoryContext c) - throws IllegalAccessException, SecurityException, IllegalArgumentException { - for (Class cls : traitUserInteraces) { - final Field[] declaredFields = cls.getDeclaredFields(); - for (final Field f : declaredFields) { - final int modifiers = f.getModifiers(); - if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) { - final Class fieldClass = f.getType(); - if (fieldClass == ContextKey.class) { - final ContextKey key = ContextKey.class.cast(f.get(null)); - c.put(key, key.contextConstructor); - LOGGER.fine(() -> String.format("%s for %s", c.get(key).getClass(), f.getDeclaringClass())); - } - } - } - - repeatRecursive(cls.getInterfaces(), c); - } - } - - public interface Trait { - - SharedContext getContext(); - - default T contextGet(ContextKey key, T defaultValue) { - final T got = getContext().get(key); - return got == null ? defaultValue : got; - } - - default T contextRequire(ContextKey key) { - final T got = getContext().get(key); - if (got == null) { - throw defaultException(key).get(); - } - - return got; - } - - default T contextRequire(ContextKey key, Supplier exceptionSupplier) throws E { - final T got = getContext().get(key); - if (got == null) { - throw exceptionSupplier.get(); - } - - return got; - } - - default boolean contextTest(ContextKey key, Predicate predicate) { - final T got = getContext().get(key); - return got == null ? false : predicate.test(got); - } - - default void contextWith(ContextKey key, Consumer consumer) { - final T got = getContext().get(key); - if (got != null) { - consumer.accept(got); - } - } - - default R contextMap(ContextKey key, Function mapper, R defaultValue) { - final T got = getContext().get(key); - if (got != null) { - return mapper.apply(got); - } else { - return defaultValue; - } - } - - default Supplier defaultException(ContextKey key) { - return () -> new SharedContextException(key, this.getClass()); - } - } - - public final static class ContextKey { - - final Class traitClass; - final int preferredId; - final Supplier contextConstructor; - - public ContextKey(final Class traitClass, int preferredId, Supplier contextConstructor) { - this.traitClass = traitClass; - this.preferredId = preferredId; - this.contextConstructor = contextConstructor; - } - - @Override - public String toString() { - return String.format("context in the Trait %s (preferred id: %d)", traitClass, preferredId); - } - } - - public final static class KeyChain { - - int currentCapacity; - - public ContextKey newKey(final Class traitClass, Supplier contextConstructor) { - return new ContextKey<>(traitClass, currentCapacity++, contextConstructor); - } - } - - public interface SharedContext { - - T get(ContextKey key); - } - - final static class FactoryContext implements InsertConveyor, SharedContext { - - private HashMap, Object> traitMap; - private ContextKey[] keys; - private Object[] contexts; - private boolean hasMap = false; - - private FactoryContext(final int idCapacity) { - this.keys = new ContextKey[idCapacity]; - this.contexts = new Object[idCapacity]; - } - - @Override - public void put(ContextKey key, Supplier context) { - if (!hasMap) { - if (key.preferredId >= 0 && key.preferredId < keys.length) { - // return in the case of duplicate initialization of trait - if (keys[key.preferredId] == key) { - LOGGER.finer(() -> "Already found, skipping: " + key); - return; - } else if (keys[key.preferredId] == null) { - keys[key.preferredId] = key; - contexts[key.preferredId] = context.get(); - return; - } - } - - hasMap = true; - for (int i = 0; i < keys.length; ++i) { - traitMap.put(keys[i], contexts[i]); - } - - keys = null; - contexts = null; - } - - traitMap.put(key, context.get()); - } - - @Override - @SuppressWarnings("unchecked") - public T get(ContextKey key) { - if (hasMap) { - return (T) traitMap.get(key); - } else if (key.preferredId >= 0 && key.preferredId < keys.length) { - return (T) contexts[key.preferredId]; - } - - return null; - } - } - - public interface InsertConveyor { - - void put(ContextKey key, Supplier context); - - default void putObj(ContextKey key, Object context) { - put(key, () -> context); - } - } - - private static class SharedContextException extends RuntimeException { - - private static final long serialVersionUID = 5356800492346200764L; - - SharedContextException(ContextKey key, Class topLevel) { - super(String.format("Trait context %s is not initialized when used by %s or" - + "is dereferencing a null pointer when required to do not", - key, topLevel)); - } - } - - private static Type[] getParameterizedTypes(Object object) { - Type superclassType = object.getClass().getGenericSuperclass(); - if (!ParameterizedType.class.isAssignableFrom(superclassType.getClass())) { - return null; - } - return ((ParameterizedType) superclassType).getActualTypeArguments(); - } - - private TraitFactory() { - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package utils; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.logging.Logger; +import mochadoom.Loggers; + +/** + * Purpose of this pattern-interface: store Trait-specific class-wise context objects + * and be able to get them in constant time. + * + * Simple usage: + * You may read the theory below to understand, why and for what reason I wrote + * TraitFactory. However, the simplest use is: create an interface extending Trait, + * put there static final KeyChain object field, and declare some static ContextKey<> + * fields in descending classes and/or interfaces for your objects using KeyChain.newKey. + * + * Then to initialize everything, just call TraitFactory.build() and the result + * will be SharedContext to return on overriden method of Trait. + * + * TraitFactory.build utilizes (at the instantiation time, not in runtime) some + * black reflection magic to free you from need to look for every Trait in line, + * and call some registering function to add Objects to Keys in InsertConveyor. + * + * General contract of Trait: + * + * 0. In the constructor of object implementing the subset of Traits based + * on this Trait, you must call TraitFactory.build(this, idCapacity); + * Implementing this Trait otherwise means nothing. + * + * The result of TraitFactory.build(this, idCapacity); must be stored + * and the overriden method getContext() must return it. + * + * You can use some static non-final int[] field of deepest Trait dependency + * that is incremented by static initialization of all who depend on it, + * to determine idCapacity, or just guess big enough on your own. Also you can + * use helper object, KeyChain. + * + * 1. In a Trait of your subset, where you want to have some object in context, you + * must create static final ContextKey fild. During the static final ContextKey + * initialization, you can also hack into incrementing some static non-final + * somewhere, to be sure all who do the same produce unique fast ContextKeys. + * + * You can create several ContextKeys per Trait and store several contexts, + * and, if your preferedIds are unique, they will be still instant-fast. + * + * 2. You may want to be sure that all of your interfaces have created their context + * objects and put them into the InsertConveyor. To do that, you should have a + * method on the class using traits, that will descend into the top level traits, + * then lower and lower until the last of the traits. + * + * ContextKey does not override hashCode and is a final class. So the hashCode() + * method will be something like memory pointer, and uniqye per ContextKey. + * Default context storage (FactoryContext.class) does not check it until + * any new stored ContextKey have preferedId already taken, and reports different + * context Object Class. If such happen, all associated contexts are moved + * into HashMap and context acquisition will be since significantly slower. + * + * If your ContextKey does not overlap with another one, access to context Object + * would be the most instant of all possible. + * + * 3. In use, call contextGet(ContextKey) or some helper methods to get + * the Object from context. Alternatively, you can acquire the SharedContext. + * The helper methods are better in case you fear nulls. + * + * As the SharedContext is Shared, you can use it and objects from it in any + * descendants of the trait where you put this object into the context by key. + * + * If you made sure you never put two Objects of different type with two ContextKeys + * with matching preferedIds and Class'es, the cost of get(ContextKey) will be + * as negligible as one level of indirection + array access by int. + */ +public class TraitFactory { + + private final static Logger LOGGER = Loggers.getLogger(TraitFactory.class.getName()); + + public static SharedContext build(T traitUser, KeyChain usedChain) + throws IllegalArgumentException, IllegalAccessException { + return build(traitUser, usedChain.currentCapacity); + } + + public static SharedContext build(T traitUser, int idCapacity) + throws IllegalArgumentException, IllegalAccessException { + final FactoryContext c = new FactoryContext(idCapacity); + repeatRecursive(traitUser.getClass().getInterfaces(), c); + return c; + } + + private static void repeatRecursive(final Class[] traitUserInteraces, final FactoryContext c) + throws IllegalAccessException, SecurityException, IllegalArgumentException { + for (Class cls : traitUserInteraces) { + final Field[] declaredFields = cls.getDeclaredFields(); + for (final Field f : declaredFields) { + final int modifiers = f.getModifiers(); + if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) { + final Class fieldClass = f.getType(); + if (fieldClass == ContextKey.class) { + final ContextKey key = ContextKey.class.cast(f.get(null)); + c.put(key, key.contextConstructor); + LOGGER.fine(() -> String.format("%s for %s", c.get(key).getClass(), f.getDeclaringClass())); + } + } + } + + repeatRecursive(cls.getInterfaces(), c); + } + } + + public interface Trait { + + SharedContext getContext(); + + default T contextGet(ContextKey key, T defaultValue) { + final T got = getContext().get(key); + return got == null ? defaultValue : got; + } + + default T contextRequire(ContextKey key) { + final T got = getContext().get(key); + if (got == null) { + throw defaultException(key).get(); + } + + return got; + } + + default T contextRequire(ContextKey key, Supplier exceptionSupplier) throws E { + final T got = getContext().get(key); + if (got == null) { + throw exceptionSupplier.get(); + } + + return got; + } + + default boolean contextTest(ContextKey key, Predicate predicate) { + final T got = getContext().get(key); + return got == null ? false : predicate.test(got); + } + + default void contextWith(ContextKey key, Consumer consumer) { + final T got = getContext().get(key); + if (got != null) { + consumer.accept(got); + } + } + + default R contextMap(ContextKey key, Function mapper, R defaultValue) { + final T got = getContext().get(key); + if (got != null) { + return mapper.apply(got); + } else { + return defaultValue; + } + } + + default Supplier defaultException(ContextKey key) { + return () -> new SharedContextException(key, this.getClass()); + } + } + + public final static class ContextKey { + + final Class traitClass; + final int preferredId; + final Supplier contextConstructor; + + public ContextKey(final Class traitClass, int preferredId, Supplier contextConstructor) { + this.traitClass = traitClass; + this.preferredId = preferredId; + this.contextConstructor = contextConstructor; + } + + @Override + public String toString() { + return String.format("context in the Trait %s (preferred id: %d)", traitClass, preferredId); + } + } + + public final static class KeyChain { + + int currentCapacity; + + public ContextKey newKey(final Class traitClass, Supplier contextConstructor) { + return new ContextKey<>(traitClass, currentCapacity++, contextConstructor); + } + } + + public interface SharedContext { + + T get(ContextKey key); + } + + final static class FactoryContext implements InsertConveyor, SharedContext { + + private HashMap, Object> traitMap; + private ContextKey[] keys; + private Object[] contexts; + private boolean hasMap = false; + + private FactoryContext(final int idCapacity) { + this.keys = new ContextKey[idCapacity]; + this.contexts = new Object[idCapacity]; + } + + @Override + public void put(ContextKey key, Supplier context) { + if (!hasMap) { + if (key.preferredId >= 0 && key.preferredId < keys.length) { + // return in the case of duplicate initialization of trait + if (keys[key.preferredId] == key) { + LOGGER.finer(() -> "Already found, skipping: " + key); + return; + } else if (keys[key.preferredId] == null) { + keys[key.preferredId] = key; + contexts[key.preferredId] = context.get(); + return; + } + } + + hasMap = true; + for (int i = 0; i < keys.length; ++i) { + traitMap.put(keys[i], contexts[i]); + } + + keys = null; + contexts = null; + } + + traitMap.put(key, context.get()); + } + + @Override + @SuppressWarnings("unchecked") + public T get(ContextKey key) { + if (hasMap) { + return (T) traitMap.get(key); + } else if (key.preferredId >= 0 && key.preferredId < keys.length) { + return (T) contexts[key.preferredId]; + } + + return null; + } + } + + public interface InsertConveyor { + + void put(ContextKey key, Supplier context); + + default void putObj(ContextKey key, Object context) { + put(key, () -> context); + } + } + + private static class SharedContextException extends RuntimeException { + + private static final long serialVersionUID = 5356800492346200764L; + + SharedContextException(ContextKey key, Class topLevel) { + super(String.format("Trait context %s is not initialized when used by %s or" + + "is dereferencing a null pointer when required to do not", + key, topLevel)); + } + } + + private static Type[] getParameterizedTypes(Object object) { + Type superclassType = object.getClass().getGenericSuperclass(); + if (!ParameterizedType.class.isAssignableFrom(superclassType.getClass())) { + return null; + } + return ((ParameterizedType) superclassType).getActualTypeArguments(); + } + + private TraitFactory() { + } +} \ No newline at end of file diff --git a/src/v/DoomGraphicSystem.java b/src/v/DoomGraphicSystem.java index ed4f85e..a59c4cf 100644 --- a/src/v/DoomGraphicSystem.java +++ b/src/v/DoomGraphicSystem.java @@ -30,27 +30,27 @@ import v.scale.VideoScale; import v.tables.BlurryTable; -/** +/** * Refactored a lot of it; most notable changes: - * + * * - 2d rendering methods are unified, standartized, generized, typehinted and incapsulated, * they are moved into separate interfaces and those interfaces to separate package - * + * * - Fixed buggy 2d alrorithms, such as scaling, rewritten and parallelized column drawing logic, * unified and simplified calculation of areas on column-major surface - * + * * - Renderer drivers are separated from drawing API and refactored a lot: fixed all issues with * improper gammas, lights and tinting, fixed delay before it applied on non-indexed render, * parallelized HiColor and TrueColor renderers. Only standard indexed 8-bit renderer is still * single-threaded, and he is very performant and is cool too! - * + * * -- Good Sign 2017/04/12 - * + * * Notes about method hiding: * - (A comment on the notes below) It would be only wonderful, if it also will make reflection-access * (that what happens when some lame Java developer cannot access something and he just use reflection to * set method public) harder on these methods. I hate lame Java developers. - * + * * Never trust clients. Never show them too much. So for those of you, who don't know something like that, * I introduce a good method of hiding interface methods. It is called Hiding By Complexity of Implementation. * Why I call it that? Because we strike a zombie sergeant using a shotgun. @@ -60,7 +60,7 @@ * That is why they are internal. A here it is the main contract: if you want to use internal methods, * you create all their environment properly by sub-contracts of concrete interfaces. * So we hide complexity of usage by complexity of implementation the usable case. And the sergeant falls. - * + * * A lot of interfaces with a lot of default methods. This is intended feature hiding mechanism. * Yes, it seems that a lot of PUBLIC default methods (default method is always public) * gains much access and power to one who use it... But actually, these interfaces restrict @@ -68,18 +68,18 @@ * to access any of these methods, and implementing these interfaces means implementing * a whole part of DoomGraphicsSystem. And I've thought out the interfaces contracts in the way * that if someone *implement* them on purpose, their methods will be safe and useful for him. - * + * * -- Good Sign 2017/04/14 - * + * * DoomVideoSystem is now an interface, that all "video drivers" (whether do screen, disk, etc.) - * must implement. - * + * must implement. + * * 23/10/2011: Made into a generic type, which affects the underlying raw screen data * type. This should make -in theory- true color or super-indexed (>8 bits) video modes * possible. The catch is that everything directly meddling with the renderer must also * be aware of the underlying implementation. E.g. the various screen arrays will not be * necessarily byte[]. - * + * * @author Maes */ public interface DoomGraphicSystem { @@ -94,10 +94,10 @@ public interface DoomGraphicSystem { final int V_SCALESTART = 0x00020000; // scale x,y, start coords final int V_SCALEPATCH = 0x00040000; // scale patch final int V_NOSCALEPATCH = 0x00080000; // don't scale patch - final int V_WHITEMAP = 0x00100000; // draw white (for v_drawstring) + final int V_WHITEMAP = 0x00100000; // draw white (for v_drawstring) final int V_FLIPPEDPATCH = 0x00200000; // flipped in y - final int V_TRANSLUCENTPATCH = 0x00400000; // draw patch translucent - final int V_PREDIVIDE = 0x00800000; // pre-divide by best x/y scale. + final int V_TRANSLUCENTPATCH = 0x00400000; // draw patch translucent + final int V_PREDIVIDE = 0x00800000; // pre-divide by best x/y scale. final int V_SCALEOFFSET = 0x01000000; // Scale the patch offset final int V_NOSCALEOFFSET = 0x02000000; // dont's cale patch offset final int V_SAFESCALE = 0x04000000; // scale only by minimal scale of x/y instead of both @@ -105,7 +105,7 @@ public interface DoomGraphicSystem { /** * Public API * See documentation in r2d package - * + * * These are only methods DoomGraphicSystem wants to share from the whole insanely big package r2d * Because using only these methods, it is minimal risk of breaking something. Actually, * the only problematic cases should be passing null instead of argument or invalid coordinates. @@ -181,7 +181,7 @@ public interface DoomGraphicSystem { void DrawBlock(DoomScreen dstScreen, V block, Rectangle sourceArea, int destinationPoint); - /** + /** * No matter how complex/weird/arcane palette manipulations you do internally, the AWT module * must always be able to "tap" into what's the current, "correct" screen after all manipulation and * color juju was applied. Call after a palette/gamma change. @@ -222,8 +222,8 @@ default Plotter createPlotter(DoomScreen screen) { BlurryTable getBlurryTable(); /** - * Indexed renderer needs to reset its image + * Indexed renderer needs to reset its image */ default void forcePalette() { } -} +} \ No newline at end of file diff --git a/src/v/graphics/Blocks.java b/src/v/graphics/Blocks.java index 399bc11..4cdd78e 100644 --- a/src/v/graphics/Blocks.java +++ b/src/v/graphics/Blocks.java @@ -1,135 +1,135 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.graphics; - -import java.awt.Rectangle; -import java.lang.reflect.Array; -import v.scale.VideoScale; - -/** - * Manipulating Blocks - * - * @author Good Sign - */ -public interface Blocks> extends Points, Palettes { - - /** - * Converts a block of paletted pixels into screen format pixels - * It is advised that implementation should both perform caching - * and be additionally optimized for 1-value src arrays - */ - V convertPalettedBlock(byte... src); - - /** - * Fills the whole dstScreen tiling the copies of block across it - */ - default void TileScreen(E dstScreen, V block, Rectangle blockArea) { - final int screenHeight = getScreenHeight(); - final int screenWidth = getScreenWidth(); - - for (int y = 0; y < screenHeight; y += blockArea.height) { - // Draw whole blocks. - for (int x = 0; x < screenWidth; x += blockArea.width) { - final int destination = point(x, y, screenWidth); - DrawBlock(dstScreen, block, blockArea, destination); - } - } - } - - /** - * Fills the rectangular part of dstScreen tiling the copies of block across it - */ - default void TileScreenArea(E dstScreen, Rectangle screenArea, V block, Rectangle blockArea) { - final int screenWidth = getScreenWidth(); - final int fiilLimitX = screenArea.x + screenArea.width; - final int fiilLimitY = screenArea.y + screenArea.height; - - for (int y = screenArea.y; y < fiilLimitY; y += blockArea.height) { - // Draw whole blocks. - for (int x = screenArea.x; x < fiilLimitX; x += blockArea.width) { - final int destination = point(x, y, screenWidth); - DrawBlock(dstScreen, block, blockArea, destination); - } - } - } - - /** - * Draws a linear block of pixels from the source buffer into screen buffer - * V_DrawBlock - */ - default void DrawBlock(E dstScreen, V block, Rectangle sourceArea, int destinationPoint) { - final V screen = getScreen(dstScreen); - final int bufferLength = Array.getLength(screen); - final int screenWidth = getScreenWidth(); - final Relocation rel = new Relocation( - point(sourceArea.x, sourceArea.y), - destinationPoint, - sourceArea.width); - - for (int h = sourceArea.height; h > 0; --h, rel.source += sourceArea.width, rel.destination += screenWidth) { - if (rel.destination + rel.length >= bufferLength) { - return; - } - screenCopy(block, screen, rel); - } - } - - default V ScaleBlock(V block, VideoScale vs, int width, int height) { - return ScaleBlock(block, width, height, vs.getScalingX(), vs.getScalingY()); - } - - default V ScaleBlock(V block, int width, int height, int dupX, int dupY) { - final int newWidth = width * dupX; - final int newHeight = height * dupY; - @SuppressWarnings("unchecked") - final V newBlock = (V) Array.newInstance(block.getClass().getComponentType(), newWidth * newHeight); - final Horizontal row = new Horizontal(0, dupX); - - for (int i = 0; i < width; ++i) { - for (int j = 0; j < height; ++j) { - final int pointSource = point(i, j, width); - final int pointDestination = point(i * dupX, j * dupY, newWidth); - row.start = pointDestination; - // Fill first line of rect - screenSet(block, pointSource, newBlock, row); - // Fill the rest of the rect - RepeatRow(newBlock, row, dupY - 1, newWidth); - } - } - - return newBlock; - } - - /** - * Given a row, repeats it down the screen - */ - default void RepeatRow(V screen, final Horizontal row, int times) { - RepeatRow(screen, row, times, getScreenWidth()); - } - - /** - * Given a row, repeats it down the screen - */ - default void RepeatRow(V block, final Horizontal row, int times, int blockWidth) { - if (times > 0) { - final Relocation rel = row.relocate(blockWidth); - for (; times > 0; --times, rel.shift(blockWidth)) { - screenCopy(block, block, rel); - } - } - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.graphics; + +import java.awt.Rectangle; +import java.lang.reflect.Array; +import v.scale.VideoScale; + +/** + * Manipulating Blocks + * + * @author Good Sign + */ +public interface Blocks> extends Points, Palettes { + + /** + * Converts a block of paletted pixels into screen format pixels + * It is advised that implementation should both perform caching + * and be additionally optimized for 1-value src arrays + */ + V convertPalettedBlock(byte... src); + + /** + * Fills the whole dstScreen tiling the copies of block across it + */ + default void TileScreen(E dstScreen, V block, Rectangle blockArea) { + final int screenHeight = getScreenHeight(); + final int screenWidth = getScreenWidth(); + + for (int y = 0; y < screenHeight; y += blockArea.height) { + // Draw whole blocks. + for (int x = 0; x < screenWidth; x += blockArea.width) { + final int destination = point(x, y, screenWidth); + DrawBlock(dstScreen, block, blockArea, destination); + } + } + } + + /** + * Fills the rectangular part of dstScreen tiling the copies of block across it + */ + default void TileScreenArea(E dstScreen, Rectangle screenArea, V block, Rectangle blockArea) { + final int screenWidth = getScreenWidth(); + final int fiilLimitX = screenArea.x + screenArea.width; + final int fiilLimitY = screenArea.y + screenArea.height; + + for (int y = screenArea.y; y < fiilLimitY; y += blockArea.height) { + // Draw whole blocks. + for (int x = screenArea.x; x < fiilLimitX; x += blockArea.width) { + final int destination = point(x, y, screenWidth); + DrawBlock(dstScreen, block, blockArea, destination); + } + } + } + + /** + * Draws a linear block of pixels from the source buffer into screen buffer + * V_DrawBlock + */ + default void DrawBlock(E dstScreen, V block, Rectangle sourceArea, int destinationPoint) { + final V screen = getScreen(dstScreen); + final int bufferLength = Array.getLength(screen); + final int screenWidth = getScreenWidth(); + final Relocation rel = new Relocation( + point(sourceArea.x, sourceArea.y), + destinationPoint, + sourceArea.width); + + for (int h = sourceArea.height; h > 0; --h, rel.source += sourceArea.width, rel.destination += screenWidth) { + if (rel.destination + rel.length >= bufferLength) { + return; + } + screenCopy(block, screen, rel); + } + } + + default V ScaleBlock(V block, VideoScale vs, int width, int height) { + return ScaleBlock(block, width, height, vs.getScalingX(), vs.getScalingY()); + } + + default V ScaleBlock(V block, int width, int height, int dupX, int dupY) { + final int newWidth = width * dupX; + final int newHeight = height * dupY; + @SuppressWarnings("unchecked") + final V newBlock = (V) Array.newInstance(block.getClass().getComponentType(), newWidth * newHeight); + final Horizontal row = new Horizontal(0, dupX); + + for (int i = 0; i < width; ++i) { + for (int j = 0; j < height; ++j) { + final int pointSource = point(i, j, width); + final int pointDestination = point(i * dupX, j * dupY, newWidth); + row.start = pointDestination; + // Fill first line of rect + screenSet(block, pointSource, newBlock, row); + // Fill the rest of the rect + RepeatRow(newBlock, row, dupY - 1, newWidth); + } + } + + return newBlock; + } + + /** + * Given a row, repeats it down the screen + */ + default void RepeatRow(V screen, final Horizontal row, int times) { + RepeatRow(screen, row, times, getScreenWidth()); + } + + /** + * Given a row, repeats it down the screen + */ + default void RepeatRow(V block, final Horizontal row, int times, int blockWidth) { + if (times > 0) { + final Relocation rel = row.relocate(blockWidth); + for (; times > 0; --times, rel.shift(blockWidth)) { + screenCopy(block, block, rel); + } + } + } +} \ No newline at end of file diff --git a/src/v/graphics/ColorTransform.java b/src/v/graphics/ColorTransform.java index 90c2dc5..bf60777 100644 --- a/src/v/graphics/ColorTransform.java +++ b/src/v/graphics/ColorTransform.java @@ -1,73 +1,73 @@ -/** - * Copyright (C) 1993-1996 Id Software, Inc. - * from f_wipe.c - * - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.graphics; - -import java.lang.reflect.Array; -import static utils.GenericCopy.memcpy; - -public interface ColorTransform { - - default boolean initTransform(Wipers.WiperImpl wiper) { - memcpy(wiper.wipeStartScr, 0, wiper.wipeEndScr, 0, Array.getLength(wiper.wipeEndScr)); - return false; - } - - default boolean colorTransformB(Wipers.WiperImpl wiper) { - byte[] w = wiper.wipeStartScr, e = wiper.wipeEndScr; - boolean changed = false; - for (int i = 0, newval; i < w.length; ++i) { - if (w[i] != e[i]) { - w[i] = w[i] > e[i] - ? (newval = w[i] - wiper.ticks) < e[i] ? e[i] : (byte) newval - : (newval = w[i] + wiper.ticks) > e[i] ? e[i] : (byte) newval; - changed = true; - } - } - return !changed; - } - - default boolean colorTransformS(Wipers.WiperImpl wiper) { - short[] w = wiper.wipeStartScr, e = wiper.wipeEndScr; - boolean changed = false; - for (int i = 0, newval; i < w.length; ++i) { - if (w[i] != e[i]) { - w[i] = w[i] > e[i] - ? (newval = w[i] - wiper.ticks) < e[i] ? e[i] : (byte) newval - : (newval = w[i] + wiper.ticks) > e[i] ? e[i] : (byte) newval; - changed = true; - } - } - return !changed; - } - - default boolean colorTransformI(Wipers.WiperImpl wiper) { - int[] w = wiper.wipeStartScr, e = wiper.wipeEndScr; - boolean changed = false; - for (int i = 0, newval; i < w.length; ++i) { - if (w[i] != e[i]) { - w[i] = w[i] > e[i] - ? (newval = w[i] - wiper.ticks) < e[i] ? e[i] : (byte) newval - : (newval = w[i] + wiper.ticks) > e[i] ? e[i] : (byte) newval; - changed = true; - } - } - return !changed; - } -} +/** + * Copyright (C) 1993-1996 Id Software, Inc. + * from f_wipe.c + * + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.graphics; + +import java.lang.reflect.Array; +import static utils.GenericCopy.memcpy; + +public interface ColorTransform { + + default boolean initTransform(Wipers.WiperImpl wiper) { + memcpy(wiper.wipeStartScr, 0, wiper.wipeEndScr, 0, Array.getLength(wiper.wipeEndScr)); + return false; + } + + default boolean colorTransformB(Wipers.WiperImpl wiper) { + byte[] w = wiper.wipeStartScr, e = wiper.wipeEndScr; + boolean changed = false; + for (int i = 0, newval; i < w.length; ++i) { + if (w[i] != e[i]) { + w[i] = w[i] > e[i] + ? (newval = w[i] - wiper.ticks) < e[i] ? e[i] : (byte) newval + : (newval = w[i] + wiper.ticks) > e[i] ? e[i] : (byte) newval; + changed = true; + } + } + return !changed; + } + + default boolean colorTransformS(Wipers.WiperImpl wiper) { + short[] w = wiper.wipeStartScr, e = wiper.wipeEndScr; + boolean changed = false; + for (int i = 0, newval; i < w.length; ++i) { + if (w[i] != e[i]) { + w[i] = w[i] > e[i] + ? (newval = w[i] - wiper.ticks) < e[i] ? e[i] : (byte) newval + : (newval = w[i] + wiper.ticks) > e[i] ? e[i] : (byte) newval; + changed = true; + } + } + return !changed; + } + + default boolean colorTransformI(Wipers.WiperImpl wiper) { + int[] w = wiper.wipeStartScr, e = wiper.wipeEndScr; + boolean changed = false; + for (int i = 0, newval; i < w.length; ++i) { + if (w[i] != e[i]) { + w[i] = w[i] > e[i] + ? (newval = w[i] - wiper.ticks) < e[i] ? e[i] : (byte) newval + : (newval = w[i] + wiper.ticks) > e[i] ? e[i] : (byte) newval; + changed = true; + } + } + return !changed; + } +} \ No newline at end of file diff --git a/src/v/graphics/Colors.java b/src/v/graphics/Colors.java index e981054..ea51c43 100644 --- a/src/v/graphics/Colors.java +++ b/src/v/graphics/Colors.java @@ -1,360 +1,360 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.graphics; - -import v.tables.ColorTint; - -/** - * Package containing individual color modification and transformation methods - */ -public interface Colors { - - /** - * Get alpha from packed argb long word. - * - * @param argb8888 - * @return - */ - default int getAlpha(int argb8888) { - return (argb8888 >>> 24) & 0xFF; - } - - /** - * Get red from packed argb long word. - * - * @param rgb888 - * @return - */ - default int getRed(int rgb888) { - return (0xFF0000 & rgb888) >> 16; - } - - /** - * Get red from packed rgb555 - * - * @param rgb555 - * @return - */ - default int getRed5(int rgb555) { - return (rgb555 >> 10) & 0x1F; - } - - /** - * Get green from packed argb long word. - * - * @param rgb888 - * @return - */ - default int getGreen(int rgb888) { - return (0xFF00 & rgb888) >> 8; - } - - /** - * Get green from packed rgb555 - * - * @param rgb555 - * @return - */ - default int getGreen5(int rgb555) { - return (rgb555 >> 5) & 0x1F; - } - - /** - * Get blue from packed argb long word. - * - * @param rgb888 - * @return - */ - default int getBlue(int rgb888) { - return 0xFF & rgb888; - } - - /** - * Get blue from packed rgb555 - * - * @param rgb555 - * @return - */ - default int getBlue5(int rgb555) { - return rgb555 & 0x1F; - } - - /** - * Get all four color channels into an array - */ - default int[] getARGB8888(int argb8888, int[] container) { - container[0] = getAlpha(argb8888); - container[1] = getRed(argb8888); - container[2] = getGreen(argb8888); - container[3] = getBlue(argb8888); - return container; - } - - /** - * Get all four color channels into an array - */ - default int[] getRGB888(int rgb888, int[] container) { - container[0] = getRed(rgb888); - container[1] = getGreen(rgb888); - container[2] = getBlue(rgb888); - return container; - } - - /** - * Get all three colors into an array - */ - default int[] getRGB555(int rgb555, int[] container) { - container[0] = getRed5(rgb555); - container[1] = getGreen5(rgb555); - container[2] = getBlue5(rgb555); - return container; - } - - /** - * Compose rgb888 color (opaque) - */ - default int toRGB888(int r, int g, int b) { - return 0xFF000000 + ((r & 0xFF) << 16) + ((g & 0xFF) << 8) + (b & 0xFF); - } - - /** - * Compose argb8888 color - */ - default int toARGB8888(int a, int r, int g, int b) { - return ((a & 0xFF) << 24) + ((r & 0xFF) << 16) + ((g & 0xFF) << 8) + (b & 0xFF); - } - - /** - * Compose rgb888 color - */ - default short toRGB555(int r, int g, int b) { - return (short) (((r & 0x1F) << 10) + ((g & 0x1F) << 5) + (b & 0x1F)); - } - - /** - * Alter rgb888 color by applying a tint to it - * @param int[] rgbInput an array containing rgb888 color components - */ - default int[] tintRGB888(final ColorTint tint, final int[] rgbInput, int[] rgbOutput) { - rgbOutput[0] = tint.tintRed8(rgbInput[0]); - rgbOutput[1] = tint.tintGreen8(rgbInput[1]); - rgbOutput[2] = tint.tintBlue8(rgbInput[2]); - return rgbOutput; - } - - /** - * Alter rgb555 color by applying a tint to it - * @param int[] rgbInput an array containing rgb555 color components - */ - default int[] tintRGB555(final ColorTint tint, final int[] rgbInput, int[] rgbOutput) { - rgbOutput[0] = tint.tintRed5(rgbInput[0]); - rgbOutput[1] = tint.tintGreen5(rgbInput[1]); - rgbOutput[2] = tint.tintBlue5(rgbInput[2]); - return rgbOutput; - } - - default double sigmoid(double r) { - return (1 / (1 + Math.pow(Math.E, (-1 * r)))); - } - - default int sigmoidGradient(int component1, int component2, float ratio) { - return (int) ((ratio * component1) + ((1 - ratio) * component2)); - } - - /** - * Tells which color is further by comparing distance between two packed rgb888 ints - */ - default int CompareColors888(int rgb888_1, int rgb888_2) { - final long distance = ColorDistance888(rgb888_1, rgb888_2); - return distance > 0 ? 1 : distance < 0 ? -1 : 0; - } - - /** - * Computes simplified Euclidean color distance (without extracting square root) between two packed rbg888 ints - */ - default long ColorDistance888(int rgb888_1, int rgb888_2) { - final int r1 = getRed(rgb888_1), - g1 = getGreen(rgb888_1), - b1 = getBlue(rgb888_1), - r2 = getRed(rgb888_2), - g2 = getGreen(rgb888_2), - b2 = getBlue(rgb888_2); - - final long dr = r1 - r2, dg = g1 - g2, db = b1 - b2; - return dr * dr + dg * dg + db * db; - } - - /** - * Tells which color is further by comparing hue, saturation, value distance between two packed rgb888 ints - */ - default int CompareColorsHSV888(int rgb888_1, int rgb888_2) { - final long distance = ColorDistanceHSV888(rgb888_1, rgb888_2); - return distance > 0 ? 1 : distance < 0 ? -1 : 0; - } - - /** - * Computes simplified Euclidean color distance (without extracting square root) between two packed rbg888 ints - * based on hue, saturation and value - */ - default long ColorDistanceHSV888(int rgb888_1, int rgb888_2) { - final int r1 = (int) (0.21 * getRed(rgb888_1)), - g1 = (int) (0.72 * getGreen(rgb888_1)), - b1 = (int) (0.07 * getBlue(rgb888_1)), - r2 = (int) (0.21 * getRed(rgb888_2)), - g2 = (int) (0.72 * getGreen(rgb888_2)), - b2 = (int) (0.07 * getBlue(rgb888_2)); - - final long dr = r1 - r2, dg = g1 - g2, db = b1 - b2; - return dr * dr + dg * dg + db * db; - } - - /** - * Tells which color is further by comparing distance between two packed rgb555 shorts - */ - default int CompareColors555(short rgb555_1, short rgb555_2) { - final long distance = ColorDistance555(rgb555_1, rgb555_2); - return distance > 0 ? 1 : distance < 0 ? -1 : 0; - } - - /** - * Computes simplified Euclidean color distance (without extracting square root) between two packed rbg555 shorts - */ - default long ColorDistance555(short rgb1, short rgb2) { - final int r1 = getRed5(rgb1), - g1 = getGreen5(rgb1), - b1 = getBlue5(rgb1), - r2 = getRed5(rgb2), - g2 = getGreen5(rgb2), - b2 = getBlue5(rgb2); - - final long dr = r1 - r2, dg = g1 - g2, db = b1 - b2; - return dr * dr + dg * dg + db * db; - } - - /** - * Tells which color is further by comparing hue, saturation, value distance between two packed rgb555 shorts - */ - default int CompareColorsHSV555(short rgb555_1, short rgb555_2) { - final long distance = ColorDistanceHSV555(rgb555_1, rgb555_2); - return distance > 0 ? 1 : distance < 0 ? -1 : 0; - } - - /** - * Computes simplified Euclidean color distance (without extracting square root) between two packed rbg888 ints - * based on hue, saturation and value - */ - default long ColorDistanceHSV555(short rgb555_1, int rgb555_2) { - final int r1 = (int) (0.21 * getRed5(rgb555_1)), - g1 = (int) (0.72 * getGreen5(rgb555_1)), - b1 = (int) (0.07 * getBlue5(rgb555_1)), - r2 = (int) (0.21 * getRed5(rgb555_2)), - g2 = (int) (0.72 * getGreen5(rgb555_2)), - b2 = (int) (0.07 * getBlue5(rgb555_2)); - - final long dr = r1 - r2, dg = g1 - g2, db = b1 - b2; - return dr * dr + dg * dg + db * db; - } - - default float[] ColorRatio(int[] rgb1, int[] rgb2, float[] out) { - for (int i = 0; i < 3; ++i) { - out[i] = rgb2[i] > 0 ? rgb1[i] / (float) rgb2[i] : 1.0f; - } - return out; - } - - /** - * Get ARGB_8888 from RGB_555, with proper higher-bit - * replication. - * - * @param rgb555 - * @return rgb888 packed int - * @author velktron - */ - default int rgb555to888(short rgb555) { - // .... .... .... .... - // 111 11 = 7C00 - // 11 111 = 03E0 - // 1F= 1 1111 - int ri = (0x7C00 & rgb555) >> 7; - int gi = (0x3E0 & rgb555) >> 2; - int bi = (0x1F & rgb555) << 3; - // replicate 3 higher bits - int bits = (ri & 224) >> 5; - ri += bits; - bits = (gi & 224) >> 5; - gi += bits; - bits = (bi & 224) >> 5; - bi += bits; - // ARGB 8888 packed - return toRGB888(ri, gi, bi); - } - - /** - * Get RGB_555 from packed ARGB_8888. - * - * @param argb - * @return rgb555 packed short - * @authoor velktron - */ - default short argb8888to555(int argb8888) { - int ri = (0xFF010000 & argb8888) >> 19; - int gi = (0xFF00 & argb8888) >> 11; - int bi = (0xFF & argb8888) >> 3; - return toRGB555(ri, gi, bi); - } - - /** - * Get packed RGB_555 word from individual 8-bit RGB components. - * - * WARNING: there's no sanity/overflow check for performance reasons. - * - * @param r - * @param g - * @param b - * @return rgb888 packed int - * @author velktron - */ - default short rgb888to555(int r, int g, int b) { - return toRGB555(r >> 3, g >> 3, b >> 3); - } - - /** - * Finds a color in the palette's range from rangel to rangeh closest to specified r, g, b - * by distortion, the lesst distorted color is the result. Used for rgb555 invulnerability colormap - */ - default int BestColor(int r, int g, int b, int[] palette, int rangel, int rangeh) { - /** - * let any color go to 0 as a last resort - */ - long bestdistortion = ((long) r * r + (long) g * g + (long) b * b) * 2; - int bestcolor = 0; - for (int i = rangel; i <= rangeh; i++) { - final long dr = r - getRed(palette[i]); - final long dg = g - getGreen(palette[i]); - final long db = b - getBlue(palette[i]); - final long distortion = dr * dr + dg * dg + db * db; - if (distortion < bestdistortion) { - if (distortion == 0) { - return i; // perfect match - } - bestdistortion = distortion; - bestcolor = i; - } - } - return bestcolor; - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.graphics; + +import v.tables.ColorTint; + +/** + * Package containing individual color modification and transformation methods + */ +public interface Colors { + + /** + * Get alpha from packed argb long word. + * + * @param argb8888 + * @return + */ + default int getAlpha(int argb8888) { + return (argb8888 >>> 24) & 0xFF; + } + + /** + * Get red from packed argb long word. + * + * @param rgb888 + * @return + */ + default int getRed(int rgb888) { + return (0xFF0000 & rgb888) >> 16; + } + + /** + * Get red from packed rgb555 + * + * @param rgb555 + * @return + */ + default int getRed5(int rgb555) { + return (rgb555 >> 10) & 0x1F; + } + + /** + * Get green from packed argb long word. + * + * @param rgb888 + * @return + */ + default int getGreen(int rgb888) { + return (0xFF00 & rgb888) >> 8; + } + + /** + * Get green from packed rgb555 + * + * @param rgb555 + * @return + */ + default int getGreen5(int rgb555) { + return (rgb555 >> 5) & 0x1F; + } + + /** + * Get blue from packed argb long word. + * + * @param rgb888 + * @return + */ + default int getBlue(int rgb888) { + return 0xFF & rgb888; + } + + /** + * Get blue from packed rgb555 + * + * @param rgb555 + * @return + */ + default int getBlue5(int rgb555) { + return rgb555 & 0x1F; + } + + /** + * Get all four color channels into an array + */ + default int[] getARGB8888(int argb8888, int[] container) { + container[0] = getAlpha(argb8888); + container[1] = getRed(argb8888); + container[2] = getGreen(argb8888); + container[3] = getBlue(argb8888); + return container; + } + + /** + * Get all four color channels into an array + */ + default int[] getRGB888(int rgb888, int[] container) { + container[0] = getRed(rgb888); + container[1] = getGreen(rgb888); + container[2] = getBlue(rgb888); + return container; + } + + /** + * Get all three colors into an array + */ + default int[] getRGB555(int rgb555, int[] container) { + container[0] = getRed5(rgb555); + container[1] = getGreen5(rgb555); + container[2] = getBlue5(rgb555); + return container; + } + + /** + * Compose rgb888 color (opaque) + */ + default int toRGB888(int r, int g, int b) { + return 0xFF000000 + ((r & 0xFF) << 16) + ((g & 0xFF) << 8) + (b & 0xFF); + } + + /** + * Compose argb8888 color + */ + default int toARGB8888(int a, int r, int g, int b) { + return ((a & 0xFF) << 24) + ((r & 0xFF) << 16) + ((g & 0xFF) << 8) + (b & 0xFF); + } + + /** + * Compose rgb888 color + */ + default short toRGB555(int r, int g, int b) { + return (short) (((r & 0x1F) << 10) + ((g & 0x1F) << 5) + (b & 0x1F)); + } + + /** + * Alter rgb888 color by applying a tint to it + * @param int[] rgbInput an array containing rgb888 color components + */ + default int[] tintRGB888(final ColorTint tint, final int[] rgbInput, int[] rgbOutput) { + rgbOutput[0] = tint.tintRed8(rgbInput[0]); + rgbOutput[1] = tint.tintGreen8(rgbInput[1]); + rgbOutput[2] = tint.tintBlue8(rgbInput[2]); + return rgbOutput; + } + + /** + * Alter rgb555 color by applying a tint to it + * @param int[] rgbInput an array containing rgb555 color components + */ + default int[] tintRGB555(final ColorTint tint, final int[] rgbInput, int[] rgbOutput) { + rgbOutput[0] = tint.tintRed5(rgbInput[0]); + rgbOutput[1] = tint.tintGreen5(rgbInput[1]); + rgbOutput[2] = tint.tintBlue5(rgbInput[2]); + return rgbOutput; + } + + default double sigmoid(double r) { + return (1 / (1 + Math.pow(Math.E, (-1 * r)))); + } + + default int sigmoidGradient(int component1, int component2, float ratio) { + return (int) ((ratio * component1) + ((1 - ratio) * component2)); + } + + /** + * Tells which color is further by comparing distance between two packed rgb888 ints + */ + default int CompareColors888(int rgb888_1, int rgb888_2) { + final long distance = ColorDistance888(rgb888_1, rgb888_2); + return distance > 0 ? 1 : distance < 0 ? -1 : 0; + } + + /** + * Computes simplified Euclidean color distance (without extracting square root) between two packed rbg888 ints + */ + default long ColorDistance888(int rgb888_1, int rgb888_2) { + final int r1 = getRed(rgb888_1), + g1 = getGreen(rgb888_1), + b1 = getBlue(rgb888_1), + r2 = getRed(rgb888_2), + g2 = getGreen(rgb888_2), + b2 = getBlue(rgb888_2); + + final long dr = r1 - r2, dg = g1 - g2, db = b1 - b2; + return dr * dr + dg * dg + db * db; + } + + /** + * Tells which color is further by comparing hue, saturation, value distance between two packed rgb888 ints + */ + default int CompareColorsHSV888(int rgb888_1, int rgb888_2) { + final long distance = ColorDistanceHSV888(rgb888_1, rgb888_2); + return distance > 0 ? 1 : distance < 0 ? -1 : 0; + } + + /** + * Computes simplified Euclidean color distance (without extracting square root) between two packed rbg888 ints + * based on hue, saturation and value + */ + default long ColorDistanceHSV888(int rgb888_1, int rgb888_2) { + final int r1 = (int) (0.21 * getRed(rgb888_1)), + g1 = (int) (0.72 * getGreen(rgb888_1)), + b1 = (int) (0.07 * getBlue(rgb888_1)), + r2 = (int) (0.21 * getRed(rgb888_2)), + g2 = (int) (0.72 * getGreen(rgb888_2)), + b2 = (int) (0.07 * getBlue(rgb888_2)); + + final long dr = r1 - r2, dg = g1 - g2, db = b1 - b2; + return dr * dr + dg * dg + db * db; + } + + /** + * Tells which color is further by comparing distance between two packed rgb555 shorts + */ + default int CompareColors555(short rgb555_1, short rgb555_2) { + final long distance = ColorDistance555(rgb555_1, rgb555_2); + return distance > 0 ? 1 : distance < 0 ? -1 : 0; + } + + /** + * Computes simplified Euclidean color distance (without extracting square root) between two packed rbg555 shorts + */ + default long ColorDistance555(short rgb1, short rgb2) { + final int r1 = getRed5(rgb1), + g1 = getGreen5(rgb1), + b1 = getBlue5(rgb1), + r2 = getRed5(rgb2), + g2 = getGreen5(rgb2), + b2 = getBlue5(rgb2); + + final long dr = r1 - r2, dg = g1 - g2, db = b1 - b2; + return dr * dr + dg * dg + db * db; + } + + /** + * Tells which color is further by comparing hue, saturation, value distance between two packed rgb555 shorts + */ + default int CompareColorsHSV555(short rgb555_1, short rgb555_2) { + final long distance = ColorDistanceHSV555(rgb555_1, rgb555_2); + return distance > 0 ? 1 : distance < 0 ? -1 : 0; + } + + /** + * Computes simplified Euclidean color distance (without extracting square root) between two packed rbg888 ints + * based on hue, saturation and value + */ + default long ColorDistanceHSV555(short rgb555_1, int rgb555_2) { + final int r1 = (int) (0.21 * getRed5(rgb555_1)), + g1 = (int) (0.72 * getGreen5(rgb555_1)), + b1 = (int) (0.07 * getBlue5(rgb555_1)), + r2 = (int) (0.21 * getRed5(rgb555_2)), + g2 = (int) (0.72 * getGreen5(rgb555_2)), + b2 = (int) (0.07 * getBlue5(rgb555_2)); + + final long dr = r1 - r2, dg = g1 - g2, db = b1 - b2; + return dr * dr + dg * dg + db * db; + } + + default float[] ColorRatio(int[] rgb1, int[] rgb2, float[] out) { + for (int i = 0; i < 3; ++i) { + out[i] = rgb2[i] > 0 ? rgb1[i] / (float) rgb2[i] : 1.0f; + } + return out; + } + + /** + * Get ARGB_8888 from RGB_555, with proper higher-bit + * replication. + * + * @param rgb555 + * @return rgb888 packed int + * @author velktron + */ + default int rgb555to888(short rgb555) { + // .... .... .... .... + // 111 11 = 7C00 + // 11 111 = 03E0 + // 1F= 1 1111 + int ri = (0x7C00 & rgb555) >> 7; + int gi = (0x3E0 & rgb555) >> 2; + int bi = (0x1F & rgb555) << 3; + // replicate 3 higher bits + int bits = (ri & 224) >> 5; + ri += bits; + bits = (gi & 224) >> 5; + gi += bits; + bits = (bi & 224) >> 5; + bi += bits; + // ARGB 8888 packed + return toRGB888(ri, gi, bi); + } + + /** + * Get RGB_555 from packed ARGB_8888. + * + * @param argb + * @return rgb555 packed short + * @authoor velktron + */ + default short argb8888to555(int argb8888) { + int ri = (0xFF010000 & argb8888) >> 19; + int gi = (0xFF00 & argb8888) >> 11; + int bi = (0xFF & argb8888) >> 3; + return toRGB555(ri, gi, bi); + } + + /** + * Get packed RGB_555 word from individual 8-bit RGB components. + * + * WARNING: there's no sanity/overflow check for performance reasons. + * + * @param r + * @param g + * @param b + * @return rgb888 packed int + * @author velktron + */ + default short rgb888to555(int r, int g, int b) { + return toRGB555(r >> 3, g >> 3, b >> 3); + } + + /** + * Finds a color in the palette's range from rangel to rangeh closest to specified r, g, b + * by distortion, the lesst distorted color is the result. Used for rgb555 invulnerability colormap + */ + default int BestColor(int r, int g, int b, int[] palette, int rangel, int rangeh) { + /** + * let any color go to 0 as a last resort + */ + long bestdistortion = ((long) r * r + (long) g * g + (long) b * b) * 2; + int bestcolor = 0; + for (int i = rangel; i <= rangeh; i++) { + final long dr = r - getRed(palette[i]); + final long dg = g - getGreen(palette[i]); + final long db = b - getBlue(palette[i]); + final long distortion = dr * dr + dg * dg + db * db; + if (distortion < bestdistortion) { + if (distortion == 0) { + return i; // perfect match + } + bestdistortion = distortion; + bestcolor = i; + } + } + return bestcolor; + } +} \ No newline at end of file diff --git a/src/v/graphics/Columns.java b/src/v/graphics/Columns.java index 151d24b..0bf5722 100644 --- a/src/v/graphics/Columns.java +++ b/src/v/graphics/Columns.java @@ -32,7 +32,7 @@ * Patch columns drawing. * The whole class is my custom hand-crafted code * - Good Sign 2017/04/03 - * + * * @author Good Sign */ public interface Columns> extends Blocks { @@ -47,7 +47,7 @@ default void DrawColumn(V screen, column_t col, Horizontal row, V data, int scrW final int fullRowShift = scrWidth * dupy; /** * For each post, j is the index of post. - * + * * A delta is a number of transparent rows to skip, if it is 0xFF then the whole column * is transparent, so if we have delta 0xFF, then we've done with column drawing. */ @@ -73,7 +73,7 @@ default void DrawColumn(V screen, column_t col, Horizontal row, V data, int scrW } /** - * Accepts patch columns drawing arguments (usually from Patches::DrawPatch method) + * Accepts patch columns drawing arguments (usually from Patches::DrawPatch method) * and submits the task to the local ForkJoinPool. The task iterates over patch columns in parallel. * We need to only iterate through real patch.width and perform scale in-loop */ @@ -89,7 +89,7 @@ default void DrawPatchColumns(V screen, patch_t patch, int x, int y, int dupx, i /** * As vanilla DOOM does not parallel column computation, we should have the option to turn off * the parallelism. Just set it to 0 in cfg:parallelism_patch_columns, and it will process columns in serial. - * + * * It will also prevent a crash on a dumb negative value set to this option. However, a value of 1000 is even * more dumb, but will probably not crash - just take hellion of megabytes memory and waste all the CPU time on * computing "what to process" instead of "what will be the result" @@ -113,4 +113,4 @@ class U { private U() { } } -} +} \ No newline at end of file diff --git a/src/v/graphics/Direction.java b/src/v/graphics/Direction.java index 284f24d..f1ce393 100644 --- a/src/v/graphics/Direction.java +++ b/src/v/graphics/Direction.java @@ -1,166 +1,166 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.graphics; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * - * @author Good Sign - */ -public enum Direction { - LEFT_UP, UP, RIGHT_UP, - /* \ || / */ - /* \ || / */ - /* \ || / */ - /* \ || / */ - LEFT,/*===*/ CENTER,/*===*/ RIGHT, - /* / || \ */ - /* / || \ */ - /* / || \ */ - /* / || \ */ - LEFT_DOWN, DOWN, RIGHT_DOWN; - - public static final List directions = Collections.unmodifiableList(Arrays.asList(values())); - - /** - * Categorization constants - */ - // LEFT_UP, UP, RIGHT_UP - public final boolean hasTop = ordinal() < 3; - // LEFT_UP, LEFT, LEFT_DOWN - public final boolean hasLeft = ordinal() % 3 == 0; - // RIGHT_UP, RIGHT_ RIGHT_DOWN - public final boolean hasRight = ordinal() % 3 == 2; - // LEFT_DOWN, DOWN, RIGHT_DOWN - public final boolean hasBottom = ordinal() > 5; - // UP, LEFT, RIGHT, DOWN - public final boolean straight = ordinal() % 2 != 0; - - public boolean isAdjacent(Direction dir) { - return this.straight ^ dir.straight; - } - - /** - * Conversions - */ - public Direction next() { - if (this == RIGHT_DOWN) { - return LEFT_UP; - } - - return directions.get(ordinal() + 1); - } - - public Direction opposite() { - switch (this) { - case LEFT_UP: - return RIGHT_DOWN; - case UP: - return DOWN; - case RIGHT_UP: - return LEFT_DOWN; - case LEFT: - return RIGHT; - default: // CENTER - return this; - case RIGHT: - return LEFT; - case LEFT_DOWN: - return RIGHT_UP; - case DOWN: - return UP; - case RIGHT_DOWN: - return LEFT_UP; - } - } - - public Direction rotationHor(int sign) { - if (sign == 0) { - return this; - } - - switch (this) { - case LEFT_UP: - return sign > 0 ? UP : LEFT; - case UP: - return sign > 0 ? RIGHT_UP : LEFT_UP; - case RIGHT_UP: - return sign > 0 ? RIGHT : UP; - case LEFT: - return sign > 0 ? CENTER : this; - default: // CENTER - return sign > 0 ? RIGHT : LEFT; - case RIGHT: - return sign > 0 ? CENTER : this; - case LEFT_DOWN: - return sign > 0 ? DOWN : LEFT; - case DOWN: - return sign > 0 ? RIGHT_DOWN : LEFT_DOWN; - case RIGHT_DOWN: - return sign > 0 ? RIGHT : DOWN; - } - } - - public Direction rotationVert(int sign) { - if (sign == 0) { - return this; - } - - switch (this) { - case LEFT_UP: - return sign > 0 ? LEFT : UP; - case UP: - return sign > 0 ? CENTER : this; - case RIGHT_UP: - return sign > 0 ? RIGHT : UP; - case LEFT: - return sign > 0 ? LEFT_DOWN : LEFT_UP; - default: // CENTER - return sign > 0 ? DOWN : UP; - case RIGHT: - return sign > 0 ? RIGHT_DOWN : RIGHT_UP; - case LEFT_DOWN: - return sign > 0 ? DOWN : LEFT; - case DOWN: - return sign < 0 ? CENTER : this; - case RIGHT_DOWN: - return sign > 0 ? DOWN : RIGHT; - } - } - - public Direction rotation(int signX, int signY) { - final Direction rotX = rotationHor(signX), rotY = rotationHor(signY); - - if (rotX.isAdjacent(rotY)) { - if (signX > 0 && signY > 0) { - return RIGHT_DOWN; - } else if (signX > 0 && signY < 0) { - return RIGHT_UP; - } else if (signX < 0 && signY > 0) { - return LEFT_DOWN; - } else if (signX < 0 && signY < 0) { - return LEFT_UP; - } - } - - // otherwise, 2nd takes precedence - return rotY; - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.graphics; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * + * @author Good Sign + */ +public enum Direction { + LEFT_UP, UP, RIGHT_UP, + /* \ || / */ + /* \ || / */ + /* \ || / */ + /* \ || / */ + LEFT,/*===*/ CENTER,/*===*/ RIGHT, + /* / || \ */ + /* / || \ */ + /* / || \ */ + /* / || \ */ + LEFT_DOWN, DOWN, RIGHT_DOWN; + + public static final List directions = Collections.unmodifiableList(Arrays.asList(values())); + + /** + * Categorization constants + */ + // LEFT_UP, UP, RIGHT_UP + public final boolean hasTop = ordinal() < 3; + // LEFT_UP, LEFT, LEFT_DOWN + public final boolean hasLeft = ordinal() % 3 == 0; + // RIGHT_UP, RIGHT_ RIGHT_DOWN + public final boolean hasRight = ordinal() % 3 == 2; + // LEFT_DOWN, DOWN, RIGHT_DOWN + public final boolean hasBottom = ordinal() > 5; + // UP, LEFT, RIGHT, DOWN + public final boolean straight = ordinal() % 2 != 0; + + public boolean isAdjacent(Direction dir) { + return this.straight ^ dir.straight; + } + + /** + * Conversions + */ + public Direction next() { + if (this == RIGHT_DOWN) { + return LEFT_UP; + } + + return directions.get(ordinal() + 1); + } + + public Direction opposite() { + switch (this) { + case LEFT_UP: + return RIGHT_DOWN; + case UP: + return DOWN; + case RIGHT_UP: + return LEFT_DOWN; + case LEFT: + return RIGHT; + default: // CENTER + return this; + case RIGHT: + return LEFT; + case LEFT_DOWN: + return RIGHT_UP; + case DOWN: + return UP; + case RIGHT_DOWN: + return LEFT_UP; + } + } + + public Direction rotationHor(int sign) { + if (sign == 0) { + return this; + } + + switch (this) { + case LEFT_UP: + return sign > 0 ? UP : LEFT; + case UP: + return sign > 0 ? RIGHT_UP : LEFT_UP; + case RIGHT_UP: + return sign > 0 ? RIGHT : UP; + case LEFT: + return sign > 0 ? CENTER : this; + default: // CENTER + return sign > 0 ? RIGHT : LEFT; + case RIGHT: + return sign > 0 ? CENTER : this; + case LEFT_DOWN: + return sign > 0 ? DOWN : LEFT; + case DOWN: + return sign > 0 ? RIGHT_DOWN : LEFT_DOWN; + case RIGHT_DOWN: + return sign > 0 ? RIGHT : DOWN; + } + } + + public Direction rotationVert(int sign) { + if (sign == 0) { + return this; + } + + switch (this) { + case LEFT_UP: + return sign > 0 ? LEFT : UP; + case UP: + return sign > 0 ? CENTER : this; + case RIGHT_UP: + return sign > 0 ? RIGHT : UP; + case LEFT: + return sign > 0 ? LEFT_DOWN : LEFT_UP; + default: // CENTER + return sign > 0 ? DOWN : UP; + case RIGHT: + return sign > 0 ? RIGHT_DOWN : RIGHT_UP; + case LEFT_DOWN: + return sign > 0 ? DOWN : LEFT; + case DOWN: + return sign < 0 ? CENTER : this; + case RIGHT_DOWN: + return sign > 0 ? DOWN : RIGHT; + } + } + + public Direction rotation(int signX, int signY) { + final Direction rotX = rotationHor(signX), rotY = rotationHor(signY); + + if (rotX.isAdjacent(rotY)) { + if (signX > 0 && signY > 0) { + return RIGHT_DOWN; + } else if (signX > 0 && signY < 0) { + return RIGHT_UP; + } else if (signX < 0 && signY > 0) { + return LEFT_DOWN; + } else if (signX < 0 && signY < 0) { + return LEFT_UP; + } + } + + // otherwise, 2nd takes precedence + return rotY; + } +} \ No newline at end of file diff --git a/src/v/graphics/Horizontal.java b/src/v/graphics/Horizontal.java index c3ebbc8..f26a332 100644 --- a/src/v/graphics/Horizontal.java +++ b/src/v/graphics/Horizontal.java @@ -17,8 +17,8 @@ package v.graphics; /** - * Horizontal represents a range from a screen buffer (byte or short or int array) - * + * Horizontal represents a range from a screen buffer (byte or short or int array) + * * @author Good Sign */ public class Horizontal { @@ -41,4 +41,4 @@ public Relocation relocate(int amount) { public void shift(int amount) { this.start += amount; } -} +} \ No newline at end of file diff --git a/src/v/graphics/Lights.java b/src/v/graphics/Lights.java index 21ef63c..12e898d 100644 --- a/src/v/graphics/Lights.java +++ b/src/v/graphics/Lights.java @@ -1,426 +1,426 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.graphics; - -import static v.graphics.Palettes.PAL_NUM_COLORS; -import v.tables.GreyscaleFilter; - -/** - * This package provides methods to dynamically generate lightmaps - * They are intended to be used instead of COLORMAP lump to - * compute sector brightness - * - * @author Good Sign - * @author John Carmack - * @author Velktron - */ -public interface Lights extends Colors { - - /** - * Light levels. Binded to the colormap subsystem - */ - final int COLORMAP_LIGHTS_15 = 1 << 5; - final int COLORMAP_LIGHTS_24 = 1 << 8; - - /** - * Standard lengths for colormaps - */ - final int COLORMAP_STD_LENGTH_15 = COLORMAP_LIGHTS_15 + 1; - final int COLORMAP_STD_LENGTH_24 = COLORMAP_LIGHTS_24 + 1; - - /** - * Default index of inverse colormap. Note that it will be shifted to the actual position - * in generated lights map by the difference in lights count between 5 and 8 bits lighting. - * I have discovered, that player_t.fixedcolormap property is *stored* by game when writing files, - * for example it could be included in savegame or demos. - * - * If we preshift inverse colormap, MochaDoom not in TrueColor bppMode or any Vanilla DOOM would crash - * when trying to load savegame made when under invulnerabilty in TrueColor bppMode. - * - Good Sign 2017/04/15 - */ - final int COLORMAP_INVERSE = 32; - - /** - * An index of of the lighted palette in colormap used for FUZZ effect and partial invisibility - */ - final int COLORMAP_BLURRY = 6; - - /** - * An index of of the most lighted palette in colormap - */ - final int COLORMAP_BULLBRIGHT = 1; - - /** - * An index of of palette0 in colormap which is not altered - */ - final int COLORMAP_FIXED = 0; - - /** - * A difference in percents between color multipliers of two adjacent light levels - * It took sometime to dig this out, and this could be possibly used to simplify - * BuildLight functions without decrease in their perfectness - * - * The formula to apply to a color will then be: - * float ratio = 1.0f - LIGHT_INCREMENT_RATIO_24 * lightLevel; - * color[0] = (int) (color[0] * ratio + 0.5) - * color[1] = (int) (color[1] * ratio + 0.5) - * color[2] = (int) (color[2] * ratio + 0.5) - * - * However, this one is untested, and existing formula in function AddLight8 does effectively the same, - * just a little slower. - * - * - Good Sign 2017/04/17 - */ - final float LIGHT_INCREMENT_RATIO_24 = 1.0f / COLORMAP_LIGHTS_24; - - /** - * Builds TrueColor lights based on standard COLORMAP lump in DOOM format - * Currently only supports lightmap manipulation, but does not change colors - * for hacked COLORMAP lumps - * - * Color indexes in colormaps on darker color levels point to less matching - * colors so only the direction of increase/decrease of lighting is actually - * used from COLORMAP lump. Everything else is computed based on PLAYPAL - * - * @param int[] palette A packed RGB888 256-entry int palette - * @param byete[][] colormap read from COLORMAP lump - * @author Good Sign - */ - default int[][] BuildLights24(int[] palette, byte[][] colormap) { - final int[][] targetColormap = new int[Math.max(colormap.length, COLORMAP_STD_LENGTH_15) - COLORMAP_LIGHTS_15 + COLORMAP_LIGHTS_24][PAL_NUM_COLORS]; - - // init operation containers - final int[] color0 = new int[3], color1 = new int[3], color2 = new int[3]; - final float[] ratio0 = new float[3]; - float weight = 0.0f; - - /** - * Fixed color map - just copy it, only translating palette to real color - * It is presumably the brightest colormap, but maybe not: we shall check weight of color ratios - */ - for (int i = 0; i < PAL_NUM_COLORS; ++i) { - targetColormap[0][i] = palette[colormap[0][i] & 0xFF]; - getRGB888(targetColormap[0][i], color0); - getRGB888(palette[i], color1); - // calculate color ratio - ColorRatio(color0, color1, ratio0); - // add average ratio to the weight - weight += GreyscaleFilter.component(ratio0[0], ratio0[1], ratio0[2]); - } - - // initialize ratio to relate weight with number of colors, with default PLAYPAL should always be 1.0f - float currentLightRatio = Math.min(weight / PAL_NUM_COLORS, 1.0f); - - // [1 .. 255]: all colormaps except 1 fixed, 1 inverse and 1 unused - for (int i = 1; i < COLORMAP_LIGHTS_24; ++i) { - // [1 .. 31] the index of the colormap to be target for gradations: max 31 of ceiling of i / 8 - final int div = (int) Math.ceil((double) i / 8); - final int target = Math.min(div, COLORMAP_LIGHTS_15 - 1); - final int remainder = div < COLORMAP_LIGHTS_15 ? i % 8 : 0; - final float gradient = 1.0f - remainder * 0.125f; - - // calculate weight again for each colormap - weight = 0.0f; - for (int j = 0; j < PAL_NUM_COLORS; ++j) { - // translated indexed color from wad-read colormap i at position j - getRGB888(palette[colormap[target][j] & 0xFF], color0); - // translated indexed color from our previous generated colormap at position j - getRGB888(targetColormap[i - 1][j], color1); - // calculate color ratio - ColorRatio(color0, color1, ratio0); - // add average ratio to the weight - weight += GreyscaleFilter.component(ratio0[0], ratio0[1], ratio0[2]); - // to detect which color we will use, get the fixed colormap one - getRGB888(targetColormap[0][j], color2); - - /** - * set our color using smooth TrueColor formula: we well use the brighter color as a base - * since the brighter color simply have more information not omitted - * if we are going up in brightness, not down, it will be compensated by ratio - */ - targetColormap[i][j] = toRGB888( - sigmoidGradient(color1[0], (int) (Math.max(color2[0], color0[0]) * currentLightRatio + 0.5), gradient), - sigmoidGradient(color1[1], (int) (Math.max(color2[1], color0[1]) * currentLightRatio + 0.5), gradient), - sigmoidGradient(color1[2], (int) (Math.max(color2[2], color0[2]) * currentLightRatio + 0.5), gradient) - ); - } - - // now detect if we are lightening or darkening - currentLightRatio += weight > PAL_NUM_COLORS ? LIGHT_INCREMENT_RATIO_24 : -LIGHT_INCREMENT_RATIO_24; - } - - // copy all other parts of colormap - for (int i = COLORMAP_LIGHTS_24, j = COLORMAP_LIGHTS_15; j < colormap.length; ++i, ++j) { - CopyMap24(targetColormap[i], palette, colormap[j]); - } - - return targetColormap; - } - - /** - * RF_BuildLights lifted from dcolors.c - * - * Used to compute extended-color colormaps even in absence of the - * COLORS15 lump. Must be recomputed if gamma levels change, since - * they actually modify the RGB envelopes. - * - * Variation that produces TrueColor lightmaps - * - * @param int[] palette A packed RGB888 256-entry int palette - */ - default int[][] BuildLights24(int[] palette) { - final int[][] targetColormap = new int[COLORMAP_STD_LENGTH_24][PAL_NUM_COLORS]; - final int[] palColor = new int[3]; - - // Don't repeat work more then necessary - loop first over colors, not lights - for (int c = 0; c < PAL_NUM_COLORS; ++c) { - getRGB888(palette[c], palColor); - for (int l = 0; l < COLORMAP_LIGHTS_24; ++l) { - // Full-quality truecolor. - targetColormap[l][c] = toRGB888( - AddLight8(palColor[0], l), // R - AddLight8(palColor[1], l), // G - AddLight8(palColor[2], l) // B - ); - } - - // Special map for invulnerability. Do not waste time, build it right now - BuildSpecials24(targetColormap[COLORMAP_LIGHTS_24], palColor, c); - } - - return targetColormap; - } - - /** - * RF_BuildLights lifted from dcolors.c - * - * Used to compute extended-color colormaps even in absence of the - * COLORS15 lump. Must be recomputed if gamma levels change, since - * they actually modify the RGB envelopes. - * - * @param int[] palette A packed RGB888 256-entry int palette - * @param byte[][] colormap, if supplied it will be used to translate the lights, - * the inverse colormap will be translated from it and all unused copied. - * - Good Sign 2017/04/17 - */ - default short[][] BuildLights15(int[] palette, byte[][] colormaps) { - final short[][] targetColormap = new short[Math.max(colormaps.length, COLORMAP_STD_LENGTH_15)][PAL_NUM_COLORS]; - - for (int c = 0; c < colormaps.length; ++c) { - CopyMap15(targetColormap[c], palette, colormaps[c]); - } - - return targetColormap; - } - - /** - * RF_BuildLights lifted from dcolors.c - * - * Used to compute extended-color colormaps even in absence of the - * COLORS15 lump. Must be recomputed if gamma levels change, since - * they actually modify the RGB envelopes. - * - * @param int[] palette A packed RGB888 256-entry int palette - */ - default short[][] BuildLights15(int[] palette) { - final short[][] targetColormap = new short[COLORMAP_STD_LENGTH_15][PAL_NUM_COLORS]; - final int[] palColor = new int[3]; - - // Don't repeat work more then necessary - loop first over colors, not lights - for (int c = 0; c < PAL_NUM_COLORS; ++c) { - getRGB888(palette[c], palColor); - for (int l = 0; l < COLORMAP_LIGHTS_15; ++l) { - // RGB555 for HiColor, eight times less smooth then TrueColor version - targetColormap[l][c] = toRGB555( - AddLight5(palColor[0], l), // R - AddLight5(palColor[1], l), // G - AddLight5(palColor[2], l) // B - ); - } - - // Special map for invulnerability. Do not waste time, build it right now - BuildSpecials15(targetColormap[COLORMAP_LIGHTS_15], palColor, c); - } - - return targetColormap; - } - - /** - * RF_BuildLights lifted from dcolors.c - * - * Used to compute extended-color colormaps even in absence of the - * COLORMAP lump. Must be recomputed if gamma levels change, since - * they actually modify the RGB envelopes. - * - * @param int[] palette A packed RGB888 256-entry int palette - * @return this concrete one builds Indexed colors. Maybe I would regret it - * - Good Sign 2017/04/19 - */ - default byte[][] BuildLightsI(int[] palette) { - final byte[][] targetColormap = new byte[COLORMAP_STD_LENGTH_15][PAL_NUM_COLORS]; - final int[] palColor = new int[3]; - - // Don't repeat work more then necessary - loop first over colors, not lights - for (int c = 0; c < PAL_NUM_COLORS; ++c) { - getRGB888(palette[c], palColor); - for (int l = 0; l < COLORMAP_LIGHTS_15; ++l) { - // RGB555 for HiColor, eight times less smooth then TrueColor version - targetColormap[l][c] = (byte) BestColor( - AddLightI(palColor[0], l), // R - AddLightI(palColor[1], l), // G - AddLightI(palColor[2], l), // B - palette, 0, PAL_NUM_COLORS - 1 - ); - } - - // Special map for invulnerability. Do not waste time, build it right now - BuildSpecialsI(targetColormap[COLORMAP_LIGHTS_15], palColor, palette, c); - } - - return targetColormap; - } - - /** - * @param c8 one rgb888 color component value - * @param light light level to add - * @return one rgb888 component value with added light level - */ - default int AddLight8(int c8, int light) { - return (int) (c8 * (1 - (float) light / COLORMAP_LIGHTS_24) + 0.5); - } - - /** - * @param c8 one rgb888 color component value (not a mistake - input is rgb888) - * @param light light level to add - * @return one rgb555 component value with added light level - */ - default int AddLight5(int c8, int light) { - return ((int) (c8 * (1 - (float) light / COLORMAP_LIGHTS_15) + 0.5)) >> 3; - } - - /** - * @param c8 one rgb888 color component value (not a mistake - input is rgb888) - * @param light light level to add - * @return one rgb555 component value with added light level - */ - default int AddLightI(int c8, int light) { - return (int) (c8 * (1 - (float) light / COLORMAP_LIGHTS_15) + 0.5); - } - - /** - * Decides the size of array for colormap and creates it - * @param hasColormap whether the array have lump-read colormap - * @param an array that can possibly have colormap read from COLORMAP lump - * @return empty array for colormap - */ - default int[][] AllocateColormap24(final boolean hasColormap, byte[][][] colormap) { - // if the lump-read COLORMAP is shorter, we must allocate enough - final int targetLength = hasColormap - ? COLORMAP_STD_LENGTH_24 + Math.max(0, colormap[0].length - COLORMAP_STD_LENGTH_15) - : COLORMAP_STD_LENGTH_24; - - final int[][] targetColormap = new int[targetLength][PAL_NUM_COLORS]; - return targetColormap; - } - - /** - * Decides the size of array for colormap and creates it - * @param hasColormap whether the array have lump-read colormap - * @param an array that can possibly have colormap read from COLORMAP lump - * @return empty array for colormap - */ - default short[][] AllocateColormap15(final boolean hasColormap, byte[][][] colormap) { - // if the lump-read COLORMAP is shorter, we must allocate enough - final int targetLength = hasColormap - ? Math.max(COLORMAP_STD_LENGTH_15, colormap[0].length) - : COLORMAP_STD_LENGTH_15; - - final short[][] targetColormap = new short[targetLength][PAL_NUM_COLORS]; - return targetColormap; - } - - /** - * Copy selected colormap from COLORMAP lump with respect to palette - * @param int[] stuff a 256-entry part of target colormap - * @param int[] palette A packed RGB888 256-entry int palette - * @param byte[] map a 256-entry part of COLORMAP lump to copy - */ - default void CopyMap24(int[] targetColormap, int[] palette, byte[] map) { - for (int c = 0; c < PAL_NUM_COLORS; ++c) { - targetColormap[c] = palette[map[c] & 0xFF]; - } - } - - /** - * Copy selected colormap from COLORMAP lump with respect to palette - * @param short[] stuff a 256-entry part of target colormap - * @param int[] palette A packed RGB888 256-entry int palette - * @param byte[] map a 256-entry part of COLORMAP lump to copy - */ - default void CopyMap15(short[] targetColormap, int[] palette, byte[] map) { - final int[] palColor = new int[3]; - for (int c = 0; c < PAL_NUM_COLORS; ++c) { - getRGB888(palette[map[c] & 0xFF], palColor); - targetColormap[c] = rgb888to555(palColor[0], palColor[1], palColor[2]); - } - } - - /** - * TrueColor invulnerability specials - * The key is: get the color, compute its luminance (or other method of grey if set in cfg) - * and substract it from white - * - * @param int[] stuff target array to set into - * @param int[] rgb unpacked color components - * @param index an index of the color int 256-entry int palette - */ - default void BuildSpecials24(int[] targetColormap, int[] rgb, int index) { - final float luminance = GreyscaleFilter.component((float) rgb[0], rgb[1], rgb[2]); - final int grey = (int) (255 * (1.0 - luminance / PAL_NUM_COLORS)); - targetColormap[index] = toRGB888(grey, grey, grey); - } - - /** - * HiColor invulnerability specials - * The key is: get the color, compute its luminance (or other method of grey if set in cfg) - * and substract it from white - * - * @param short[] stuff target array to set into - * @param int[] rgb unpacked color components - * @param index an index of the color int 256-entry int palette - */ - default void BuildSpecials15(short[] targetColormap, int[] rgb, int index) { - final float luminance = GreyscaleFilter.component((float) rgb[0], rgb[1], rgb[2]); - final int grey = (int) (255 * (1.0 - luminance / PAL_NUM_COLORS)); - targetColormap[index] = toRGB555(grey >> 3, grey >> 3, grey >> 3); - } - - /** - * Indexed invulnerability specials - * The key is: get the color, compute its luminance (or other method of grey if set in cfg) - * and substract it from white - * - * @param byte[] stuff target array to set into - * @param int[] rgb unpacked color components - * @param index an index of the color int 256-entry int palette - */ - default void BuildSpecialsI(byte[] targetColormap, int[] rgb, int[] palette, int index) { - final float luminance = GreyscaleFilter.component((float) rgb[0], rgb[1], rgb[2]); - final int grey = (int) (255 * (1.0 - luminance / PAL_NUM_COLORS)); - targetColormap[index] = (byte) BestColor(grey, grey, grey, palette, 0, PAL_NUM_COLORS - 1); - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.graphics; + +import static v.graphics.Palettes.PAL_NUM_COLORS; +import v.tables.GreyscaleFilter; + +/** + * This package provides methods to dynamically generate lightmaps + * They are intended to be used instead of COLORMAP lump to + * compute sector brightness + * + * @author Good Sign + * @author John Carmack + * @author Velktron + */ +public interface Lights extends Colors { + + /** + * Light levels. Binded to the colormap subsystem + */ + final int COLORMAP_LIGHTS_15 = 1 << 5; + final int COLORMAP_LIGHTS_24 = 1 << 8; + + /** + * Standard lengths for colormaps + */ + final int COLORMAP_STD_LENGTH_15 = COLORMAP_LIGHTS_15 + 1; + final int COLORMAP_STD_LENGTH_24 = COLORMAP_LIGHTS_24 + 1; + + /** + * Default index of inverse colormap. Note that it will be shifted to the actual position + * in generated lights map by the difference in lights count between 5 and 8 bits lighting. + * I have discovered, that player_t.fixedcolormap property is *stored* by game when writing files, + * for example it could be included in savegame or demos. + * + * If we preshift inverse colormap, MochaDoom not in TrueColor bppMode or any Vanilla DOOM would crash + * when trying to load savegame made when under invulnerabilty in TrueColor bppMode. + * - Good Sign 2017/04/15 + */ + final int COLORMAP_INVERSE = 32; + + /** + * An index of of the lighted palette in colormap used for FUZZ effect and partial invisibility + */ + final int COLORMAP_BLURRY = 6; + + /** + * An index of of the most lighted palette in colormap + */ + final int COLORMAP_BULLBRIGHT = 1; + + /** + * An index of of palette0 in colormap which is not altered + */ + final int COLORMAP_FIXED = 0; + + /** + * A difference in percents between color multipliers of two adjacent light levels + * It took sometime to dig this out, and this could be possibly used to simplify + * BuildLight functions without decrease in their perfectness + * + * The formula to apply to a color will then be: + * float ratio = 1.0f - LIGHT_INCREMENT_RATIO_24 * lightLevel; + * color[0] = (int) (color[0] * ratio + 0.5) + * color[1] = (int) (color[1] * ratio + 0.5) + * color[2] = (int) (color[2] * ratio + 0.5) + * + * However, this one is untested, and existing formula in function AddLight8 does effectively the same, + * just a little slower. + * + * - Good Sign 2017/04/17 + */ + final float LIGHT_INCREMENT_RATIO_24 = 1.0f / COLORMAP_LIGHTS_24; + + /** + * Builds TrueColor lights based on standard COLORMAP lump in DOOM format + * Currently only supports lightmap manipulation, but does not change colors + * for hacked COLORMAP lumps + * + * Color indexes in colormaps on darker color levels point to less matching + * colors so only the direction of increase/decrease of lighting is actually + * used from COLORMAP lump. Everything else is computed based on PLAYPAL + * + * @param int[] palette A packed RGB888 256-entry int palette + * @param byete[][] colormap read from COLORMAP lump + * @author Good Sign + */ + default int[][] BuildLights24(int[] palette, byte[][] colormap) { + final int[][] targetColormap = new int[Math.max(colormap.length, COLORMAP_STD_LENGTH_15) - COLORMAP_LIGHTS_15 + COLORMAP_LIGHTS_24][PAL_NUM_COLORS]; + + // init operation containers + final int[] color0 = new int[3], color1 = new int[3], color2 = new int[3]; + final float[] ratio0 = new float[3]; + float weight = 0.0f; + + /** + * Fixed color map - just copy it, only translating palette to real color + * It is presumably the brightest colormap, but maybe not: we shall check weight of color ratios + */ + for (int i = 0; i < PAL_NUM_COLORS; ++i) { + targetColormap[0][i] = palette[colormap[0][i] & 0xFF]; + getRGB888(targetColormap[0][i], color0); + getRGB888(palette[i], color1); + // calculate color ratio + ColorRatio(color0, color1, ratio0); + // add average ratio to the weight + weight += GreyscaleFilter.component(ratio0[0], ratio0[1], ratio0[2]); + } + + // initialize ratio to relate weight with number of colors, with default PLAYPAL should always be 1.0f + float currentLightRatio = Math.min(weight / PAL_NUM_COLORS, 1.0f); + + // [1 .. 255]: all colormaps except 1 fixed, 1 inverse and 1 unused + for (int i = 1; i < COLORMAP_LIGHTS_24; ++i) { + // [1 .. 31] the index of the colormap to be target for gradations: max 31 of ceiling of i / 8 + final int div = (int) Math.ceil((double) i / 8); + final int target = Math.min(div, COLORMAP_LIGHTS_15 - 1); + final int remainder = div < COLORMAP_LIGHTS_15 ? i % 8 : 0; + final float gradient = 1.0f - remainder * 0.125f; + + // calculate weight again for each colormap + weight = 0.0f; + for (int j = 0; j < PAL_NUM_COLORS; ++j) { + // translated indexed color from wad-read colormap i at position j + getRGB888(palette[colormap[target][j] & 0xFF], color0); + // translated indexed color from our previous generated colormap at position j + getRGB888(targetColormap[i - 1][j], color1); + // calculate color ratio + ColorRatio(color0, color1, ratio0); + // add average ratio to the weight + weight += GreyscaleFilter.component(ratio0[0], ratio0[1], ratio0[2]); + // to detect which color we will use, get the fixed colormap one + getRGB888(targetColormap[0][j], color2); + + /** + * set our color using smooth TrueColor formula: we well use the brighter color as a base + * since the brighter color simply have more information not omitted + * if we are going up in brightness, not down, it will be compensated by ratio + */ + targetColormap[i][j] = toRGB888( + sigmoidGradient(color1[0], (int) (Math.max(color2[0], color0[0]) * currentLightRatio + 0.5), gradient), + sigmoidGradient(color1[1], (int) (Math.max(color2[1], color0[1]) * currentLightRatio + 0.5), gradient), + sigmoidGradient(color1[2], (int) (Math.max(color2[2], color0[2]) * currentLightRatio + 0.5), gradient) + ); + } + + // now detect if we are lightening or darkening + currentLightRatio += weight > PAL_NUM_COLORS ? LIGHT_INCREMENT_RATIO_24 : -LIGHT_INCREMENT_RATIO_24; + } + + // copy all other parts of colormap + for (int i = COLORMAP_LIGHTS_24, j = COLORMAP_LIGHTS_15; j < colormap.length; ++i, ++j) { + CopyMap24(targetColormap[i], palette, colormap[j]); + } + + return targetColormap; + } + + /** + * RF_BuildLights lifted from dcolors.c + * + * Used to compute extended-color colormaps even in absence of the + * COLORS15 lump. Must be recomputed if gamma levels change, since + * they actually modify the RGB envelopes. + * + * Variation that produces TrueColor lightmaps + * + * @param int[] palette A packed RGB888 256-entry int palette + */ + default int[][] BuildLights24(int[] palette) { + final int[][] targetColormap = new int[COLORMAP_STD_LENGTH_24][PAL_NUM_COLORS]; + final int[] palColor = new int[3]; + + // Don't repeat work more then necessary - loop first over colors, not lights + for (int c = 0; c < PAL_NUM_COLORS; ++c) { + getRGB888(palette[c], palColor); + for (int l = 0; l < COLORMAP_LIGHTS_24; ++l) { + // Full-quality truecolor. + targetColormap[l][c] = toRGB888( + AddLight8(palColor[0], l), // R + AddLight8(palColor[1], l), // G + AddLight8(palColor[2], l) // B + ); + } + + // Special map for invulnerability. Do not waste time, build it right now + BuildSpecials24(targetColormap[COLORMAP_LIGHTS_24], palColor, c); + } + + return targetColormap; + } + + /** + * RF_BuildLights lifted from dcolors.c + * + * Used to compute extended-color colormaps even in absence of the + * COLORS15 lump. Must be recomputed if gamma levels change, since + * they actually modify the RGB envelopes. + * + * @param int[] palette A packed RGB888 256-entry int palette + * @param byte[][] colormap, if supplied it will be used to translate the lights, + * the inverse colormap will be translated from it and all unused copied. + * - Good Sign 2017/04/17 + */ + default short[][] BuildLights15(int[] palette, byte[][] colormaps) { + final short[][] targetColormap = new short[Math.max(colormaps.length, COLORMAP_STD_LENGTH_15)][PAL_NUM_COLORS]; + + for (int c = 0; c < colormaps.length; ++c) { + CopyMap15(targetColormap[c], palette, colormaps[c]); + } + + return targetColormap; + } + + /** + * RF_BuildLights lifted from dcolors.c + * + * Used to compute extended-color colormaps even in absence of the + * COLORS15 lump. Must be recomputed if gamma levels change, since + * they actually modify the RGB envelopes. + * + * @param int[] palette A packed RGB888 256-entry int palette + */ + default short[][] BuildLights15(int[] palette) { + final short[][] targetColormap = new short[COLORMAP_STD_LENGTH_15][PAL_NUM_COLORS]; + final int[] palColor = new int[3]; + + // Don't repeat work more then necessary - loop first over colors, not lights + for (int c = 0; c < PAL_NUM_COLORS; ++c) { + getRGB888(palette[c], palColor); + for (int l = 0; l < COLORMAP_LIGHTS_15; ++l) { + // RGB555 for HiColor, eight times less smooth then TrueColor version + targetColormap[l][c] = toRGB555( + AddLight5(palColor[0], l), // R + AddLight5(palColor[1], l), // G + AddLight5(palColor[2], l) // B + ); + } + + // Special map for invulnerability. Do not waste time, build it right now + BuildSpecials15(targetColormap[COLORMAP_LIGHTS_15], palColor, c); + } + + return targetColormap; + } + + /** + * RF_BuildLights lifted from dcolors.c + * + * Used to compute extended-color colormaps even in absence of the + * COLORMAP lump. Must be recomputed if gamma levels change, since + * they actually modify the RGB envelopes. + * + * @param int[] palette A packed RGB888 256-entry int palette + * @return this concrete one builds Indexed colors. Maybe I would regret it + * - Good Sign 2017/04/19 + */ + default byte[][] BuildLightsI(int[] palette) { + final byte[][] targetColormap = new byte[COLORMAP_STD_LENGTH_15][PAL_NUM_COLORS]; + final int[] palColor = new int[3]; + + // Don't repeat work more then necessary - loop first over colors, not lights + for (int c = 0; c < PAL_NUM_COLORS; ++c) { + getRGB888(palette[c], palColor); + for (int l = 0; l < COLORMAP_LIGHTS_15; ++l) { + // RGB555 for HiColor, eight times less smooth then TrueColor version + targetColormap[l][c] = (byte) BestColor( + AddLightI(palColor[0], l), // R + AddLightI(palColor[1], l), // G + AddLightI(palColor[2], l), // B + palette, 0, PAL_NUM_COLORS - 1 + ); + } + + // Special map for invulnerability. Do not waste time, build it right now + BuildSpecialsI(targetColormap[COLORMAP_LIGHTS_15], palColor, palette, c); + } + + return targetColormap; + } + + /** + * @param c8 one rgb888 color component value + * @param light light level to add + * @return one rgb888 component value with added light level + */ + default int AddLight8(int c8, int light) { + return (int) (c8 * (1 - (float) light / COLORMAP_LIGHTS_24) + 0.5); + } + + /** + * @param c8 one rgb888 color component value (not a mistake - input is rgb888) + * @param light light level to add + * @return one rgb555 component value with added light level + */ + default int AddLight5(int c8, int light) { + return ((int) (c8 * (1 - (float) light / COLORMAP_LIGHTS_15) + 0.5)) >> 3; + } + + /** + * @param c8 one rgb888 color component value (not a mistake - input is rgb888) + * @param light light level to add + * @return one rgb555 component value with added light level + */ + default int AddLightI(int c8, int light) { + return (int) (c8 * (1 - (float) light / COLORMAP_LIGHTS_15) + 0.5); + } + + /** + * Decides the size of array for colormap and creates it + * @param hasColormap whether the array have lump-read colormap + * @param an array that can possibly have colormap read from COLORMAP lump + * @return empty array for colormap + */ + default int[][] AllocateColormap24(final boolean hasColormap, byte[][][] colormap) { + // if the lump-read COLORMAP is shorter, we must allocate enough + final int targetLength = hasColormap + ? COLORMAP_STD_LENGTH_24 + Math.max(0, colormap[0].length - COLORMAP_STD_LENGTH_15) + : COLORMAP_STD_LENGTH_24; + + final int[][] targetColormap = new int[targetLength][PAL_NUM_COLORS]; + return targetColormap; + } + + /** + * Decides the size of array for colormap and creates it + * @param hasColormap whether the array have lump-read colormap + * @param an array that can possibly have colormap read from COLORMAP lump + * @return empty array for colormap + */ + default short[][] AllocateColormap15(final boolean hasColormap, byte[][][] colormap) { + // if the lump-read COLORMAP is shorter, we must allocate enough + final int targetLength = hasColormap + ? Math.max(COLORMAP_STD_LENGTH_15, colormap[0].length) + : COLORMAP_STD_LENGTH_15; + + final short[][] targetColormap = new short[targetLength][PAL_NUM_COLORS]; + return targetColormap; + } + + /** + * Copy selected colormap from COLORMAP lump with respect to palette + * @param int[] stuff a 256-entry part of target colormap + * @param int[] palette A packed RGB888 256-entry int palette + * @param byte[] map a 256-entry part of COLORMAP lump to copy + */ + default void CopyMap24(int[] targetColormap, int[] palette, byte[] map) { + for (int c = 0; c < PAL_NUM_COLORS; ++c) { + targetColormap[c] = palette[map[c] & 0xFF]; + } + } + + /** + * Copy selected colormap from COLORMAP lump with respect to palette + * @param short[] stuff a 256-entry part of target colormap + * @param int[] palette A packed RGB888 256-entry int palette + * @param byte[] map a 256-entry part of COLORMAP lump to copy + */ + default void CopyMap15(short[] targetColormap, int[] palette, byte[] map) { + final int[] palColor = new int[3]; + for (int c = 0; c < PAL_NUM_COLORS; ++c) { + getRGB888(palette[map[c] & 0xFF], palColor); + targetColormap[c] = rgb888to555(palColor[0], palColor[1], palColor[2]); + } + } + + /** + * TrueColor invulnerability specials + * The key is: get the color, compute its luminance (or other method of grey if set in cfg) + * and substract it from white + * + * @param int[] stuff target array to set into + * @param int[] rgb unpacked color components + * @param index an index of the color int 256-entry int palette + */ + default void BuildSpecials24(int[] targetColormap, int[] rgb, int index) { + final float luminance = GreyscaleFilter.component((float) rgb[0], rgb[1], rgb[2]); + final int grey = (int) (255 * (1.0 - luminance / PAL_NUM_COLORS)); + targetColormap[index] = toRGB888(grey, grey, grey); + } + + /** + * HiColor invulnerability specials + * The key is: get the color, compute its luminance (or other method of grey if set in cfg) + * and substract it from white + * + * @param short[] stuff target array to set into + * @param int[] rgb unpacked color components + * @param index an index of the color int 256-entry int palette + */ + default void BuildSpecials15(short[] targetColormap, int[] rgb, int index) { + final float luminance = GreyscaleFilter.component((float) rgb[0], rgb[1], rgb[2]); + final int grey = (int) (255 * (1.0 - luminance / PAL_NUM_COLORS)); + targetColormap[index] = toRGB555(grey >> 3, grey >> 3, grey >> 3); + } + + /** + * Indexed invulnerability specials + * The key is: get the color, compute its luminance (or other method of grey if set in cfg) + * and substract it from white + * + * @param byte[] stuff target array to set into + * @param int[] rgb unpacked color components + * @param index an index of the color int 256-entry int palette + */ + default void BuildSpecialsI(byte[] targetColormap, int[] rgb, int[] palette, int index) { + final float luminance = GreyscaleFilter.component((float) rgb[0], rgb[1], rgb[2]); + final int grey = (int) (255 * (1.0 - luminance / PAL_NUM_COLORS)); + targetColormap[index] = (byte) BestColor(grey, grey, grey, palette, 0, PAL_NUM_COLORS - 1); + } +} \ No newline at end of file diff --git a/src/v/graphics/Lines.java b/src/v/graphics/Lines.java index 6a42b00..0de72a3 100644 --- a/src/v/graphics/Lines.java +++ b/src/v/graphics/Lines.java @@ -1,83 +1,83 @@ -/** - * Copyright (C) 1993-1996 Id Software, Inc. - * from am_map.c - * - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.graphics; - -public interface Lines { - - /** - * Bresenham's line algorithm modified to use custom Plotter - * - * @param plotter - * @param x2 - * @param y2 - */ - default void drawLine(Plotter plotter, int x1, int x2) { - drawLine(plotter, x1, x2, 1, 1); - } - - default void drawLine(Plotter plotter, int x2, int y2, int dupX, int dupY) { - // delta of exact value and rounded value of the dependant variable - int d = 0, dy, dx, ix, iy; - - { - final int x = plotter.getX(), y = plotter.getY(); - - dy = Math.abs(y2 - y); - dx = Math.abs(x2 - x); - - ix = x < x2 ? 1 : -1; // increment direction - iy = y < y2 ? 1 : -1; - } - - int dy2 = (dy << 1); // slope scaling factors to avoid floating - int dx2 = (dx << 1); // point - - if (dy <= dx) { - for (;;) { - plotter.plot(); - if (plotter.getX() == x2) { - break; - } - d += dy2; - if (d > dx) { - plotter.shift(ix, iy); - d -= dx2; - } else { - plotter.shiftX(ix); - } - } - } else { - for (;;) { - plotter.plot(); - if (plotter.getY() == y2) { - break; - } - d += dx2; - if (d > dy) { - plotter.shift(ix, iy); - d -= dy2; - } else { - plotter.shiftY(iy); - } - } - } - } - -} +/** + * Copyright (C) 1993-1996 Id Software, Inc. + * from am_map.c + * + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.graphics; + +public interface Lines { + + /** + * Bresenham's line algorithm modified to use custom Plotter + * + * @param plotter + * @param x2 + * @param y2 + */ + default void drawLine(Plotter plotter, int x1, int x2) { + drawLine(plotter, x1, x2, 1, 1); + } + + default void drawLine(Plotter plotter, int x2, int y2, int dupX, int dupY) { + // delta of exact value and rounded value of the dependant variable + int d = 0, dy, dx, ix, iy; + + { + final int x = plotter.getX(), y = plotter.getY(); + + dy = Math.abs(y2 - y); + dx = Math.abs(x2 - x); + + ix = x < x2 ? 1 : -1; // increment direction + iy = y < y2 ? 1 : -1; + } + + int dy2 = (dy << 1); // slope scaling factors to avoid floating + int dx2 = (dx << 1); // point + + if (dy <= dx) { + for (;;) { + plotter.plot(); + if (plotter.getX() == x2) { + break; + } + d += dy2; + if (d > dx) { + plotter.shift(ix, iy); + d -= dx2; + } else { + plotter.shiftX(ix); + } + } + } else { + for (;;) { + plotter.plot(); + if (plotter.getY() == y2) { + break; + } + d += dx2; + if (d > dy) { + plotter.shift(ix, iy); + d -= dy2; + } else { + plotter.shiftY(iy); + } + } + } + } + +} \ No newline at end of file diff --git a/src/v/graphics/Melt.java b/src/v/graphics/Melt.java index aa26312..e30710b 100644 --- a/src/v/graphics/Melt.java +++ b/src/v/graphics/Melt.java @@ -1,173 +1,173 @@ -/** - * Copyright (C) 1993-1996 Id Software, Inc. - * from f_wipe.c - * - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.graphics; - -import static utils.C2JUtils.memcpy; - -public interface Melt extends ColorTransform { - - /** - * No more fucking column-major transpose! - * A funny fast thing for 1993, but able to make Intel i7 think hard in 2017 - * (well, at least, in energy saving mode :p) - * - Good Sign, 2017/04/10 - */ - default boolean initMeltScaled(Wipers.WiperImpl wiper) { - return initMelt(wiper, true); - } - - default boolean initMelt(Wipers.WiperImpl wiper) { - return initMelt(wiper, false); - } - - default boolean initMelt(Wipers.WiperImpl wiper, boolean scaled) { - // copy start screen to main screen - memcpy(wiper.wipeStartScr, wiper.wipeScr, wiper.screenWidth * wiper.screenHeight); - setupColumnPositions(wiper, scaled); - return false; - } - - /** - * setup initial column positions - * (y<0 => not ready to scroll yet) - */ - default void setupColumnPositions(Wipers.WiperImpl wiper, boolean scaled) { - final int lim = scaled ? wiper.screenWidth / wiper.dupy : wiper.screenWidth; - wiper.y = new int[lim]; - wiper.y[0] = -(wiper.random.M_Random() % 16); - for (int i = 1; i < lim; i++) { - final int r = (wiper.random.M_Random() % 3) - 1; - wiper.y[i] = wiper.y[i - 1] + r; - - if (wiper.y[i] > 0) { - wiper.y[i] = 0; - } else if (wiper.y[i] == -16) { - wiper.y[i] = -15; - } - } - } - - /** - * The only place where we cannot have generic code, because it is 1 pixel copy operation - * which to be called tens thousands times and will cause overhead on just literally any more intermediate function - * The "same object" comparison is actually comparison of two integers - pointers in memory, - so it is instant - * and branching is predictable, so a good cache will negate the class checks completely - * - Good Sign 2017/04/10 - */ - @SuppressWarnings("MismatchedReadAndWriteOfArray") - default void toScreen(Class bufType, Object src, Object dest, int width, int dy, int ps, int pd) { - if (bufType == int[].class) { - final int[] to = (int[]) src, from = (int[]) dest; - for (int i = 0; i < dy; ++i) { - final int iWidth = width * i; - to[pd + iWidth] = from[ps + iWidth]; - } - } else if (bufType == short[].class) { - final short[] to = (short[]) src, from = (short[]) dest; - for (int i = 0; i < dy; ++i) { - final int iWidth = width * i; - to[pd + iWidth] = from[ps + iWidth]; - } - } else if (bufType == byte[].class) { - final byte[] to = (byte[]) src, from = (byte[]) dest; - for (int i = 0; i < dy; ++i) { - final int iWidth = width * i; - to[pd + iWidth] = from[ps + iWidth]; - } - } else { - throw new UnsupportedOperationException("Do not have support for: " + bufType); - } - } - - /** - * Completely opposite of the previous method. Only performant when scaling is on. - * Stick to System.arraycopy since there is certainly several pixels to get and set. - * Also, it doesn't even need to check and cast to classes - * - Good Sign 2017/04/10 - */ - @SuppressWarnings("SuspiciousSystemArraycopy") - default void toScreenScaled(Wipers.WiperImpl wiper, Object from, int dy, int ps, int pd) { - for (int i = 0; i < dy; ++i) { - final int iWidth = wiper.screenWidth * i; - System.arraycopy(from, ps + iWidth, wiper.wipeScr, pd + iWidth, wiper.dupy); - } - } - - /** - * Scrolls down columns ready for scroll and those who aren't makes a bit more ready - * Finally no more shitty transpose! - * - Good Sign 2017/04/10 - */ - default boolean doMeltScaled(Wipers.WiperImpl wiper) { - return doMelt(wiper, true); - } - - default boolean doMelt(Wipers.WiperImpl wiper) { - return doMelt(wiper, false); - } - - default boolean doMelt(Wipers.WiperImpl wiper, boolean scaled) { - final int lim = scaled ? wiper.screenWidth / wiper.dupy : wiper.screenWidth; - boolean done = true; - - while (wiper.ticks-- > 0) { - for (int i = 0; i < lim; i++) { - // Column won't start yet. - if (wiper.y[i] < 0) { - wiper.y[i]++; - done = false; - } else if (wiper.y[i] < wiper.screenHeight) { - int dy = (wiper.y[i] < wiper.scaled_16) ? wiper.y[i] + (scaled ? wiper.dupy : 1) : wiper.scaled_8; - if (wiper.y[i] + dy >= wiper.screenHeight) { - dy = wiper.screenHeight - wiper.y[i]; - } - int pd = wiper.y[i] * wiper.screenWidth + (scaled ? i * wiper.dupx : i); - - // MAES: this part should draw the END SCREEN "behind" the melt. - if (scaled) { - toScreenScaled(wiper, wiper.wipeEndScr, dy, pd, pd); - } else { - toScreen(wiper.bufferType, wiper.wipeScr, wiper.wipeEndScr, wiper.screenWidth, dy, pd, pd); - } - - wiper.y[i] += dy; - pd += dy * wiper.screenWidth; - - // This draws a column shifted by y[i] - if (scaled) { - toScreenScaled(wiper, wiper.wipeStartScr, wiper.screenHeight - wiper.y[i], i * wiper.dupy, pd); - } else { - toScreen(wiper.bufferType, wiper.wipeScr, wiper.wipeStartScr, wiper.screenWidth, wiper.screenHeight - wiper.y[i], i, pd); - } - - done = false; - } - } - } - - return done; - } - - default boolean exitMelt(Wipers.WiperImpl wiper) { - wiper.y = null; //Z_Free(y); - wiper.ticks = 0; - return false; - } -} +/** + * Copyright (C) 1993-1996 Id Software, Inc. + * from f_wipe.c + * + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.graphics; + +import static utils.C2JUtils.memcpy; + +public interface Melt extends ColorTransform { + + /** + * No more fucking column-major transpose! + * A funny fast thing for 1993, but able to make Intel i7 think hard in 2017 + * (well, at least, in energy saving mode :p) + * - Good Sign, 2017/04/10 + */ + default boolean initMeltScaled(Wipers.WiperImpl wiper) { + return initMelt(wiper, true); + } + + default boolean initMelt(Wipers.WiperImpl wiper) { + return initMelt(wiper, false); + } + + default boolean initMelt(Wipers.WiperImpl wiper, boolean scaled) { + // copy start screen to main screen + memcpy(wiper.wipeStartScr, wiper.wipeScr, wiper.screenWidth * wiper.screenHeight); + setupColumnPositions(wiper, scaled); + return false; + } + + /** + * setup initial column positions + * (y<0 => not ready to scroll yet) + */ + default void setupColumnPositions(Wipers.WiperImpl wiper, boolean scaled) { + final int lim = scaled ? wiper.screenWidth / wiper.dupy : wiper.screenWidth; + wiper.y = new int[lim]; + wiper.y[0] = -(wiper.random.M_Random() % 16); + for (int i = 1; i < lim; i++) { + final int r = (wiper.random.M_Random() % 3) - 1; + wiper.y[i] = wiper.y[i - 1] + r; + + if (wiper.y[i] > 0) { + wiper.y[i] = 0; + } else if (wiper.y[i] == -16) { + wiper.y[i] = -15; + } + } + } + + /** + * The only place where we cannot have generic code, because it is 1 pixel copy operation + * which to be called tens thousands times and will cause overhead on just literally any more intermediate function + * The "same object" comparison is actually comparison of two integers - pointers in memory, - so it is instant + * and branching is predictable, so a good cache will negate the class checks completely + * - Good Sign 2017/04/10 + */ + @SuppressWarnings("MismatchedReadAndWriteOfArray") + default void toScreen(Class bufType, Object src, Object dest, int width, int dy, int ps, int pd) { + if (bufType == int[].class) { + final int[] to = (int[]) src, from = (int[]) dest; + for (int i = 0; i < dy; ++i) { + final int iWidth = width * i; + to[pd + iWidth] = from[ps + iWidth]; + } + } else if (bufType == short[].class) { + final short[] to = (short[]) src, from = (short[]) dest; + for (int i = 0; i < dy; ++i) { + final int iWidth = width * i; + to[pd + iWidth] = from[ps + iWidth]; + } + } else if (bufType == byte[].class) { + final byte[] to = (byte[]) src, from = (byte[]) dest; + for (int i = 0; i < dy; ++i) { + final int iWidth = width * i; + to[pd + iWidth] = from[ps + iWidth]; + } + } else { + throw new UnsupportedOperationException("Do not have support for: " + bufType); + } + } + + /** + * Completely opposite of the previous method. Only performant when scaling is on. + * Stick to System.arraycopy since there is certainly several pixels to get and set. + * Also, it doesn't even need to check and cast to classes + * - Good Sign 2017/04/10 + */ + @SuppressWarnings("SuspiciousSystemArraycopy") + default void toScreenScaled(Wipers.WiperImpl wiper, Object from, int dy, int ps, int pd) { + for (int i = 0; i < dy; ++i) { + final int iWidth = wiper.screenWidth * i; + System.arraycopy(from, ps + iWidth, wiper.wipeScr, pd + iWidth, wiper.dupy); + } + } + + /** + * Scrolls down columns ready for scroll and those who aren't makes a bit more ready + * Finally no more shitty transpose! + * - Good Sign 2017/04/10 + */ + default boolean doMeltScaled(Wipers.WiperImpl wiper) { + return doMelt(wiper, true); + } + + default boolean doMelt(Wipers.WiperImpl wiper) { + return doMelt(wiper, false); + } + + default boolean doMelt(Wipers.WiperImpl wiper, boolean scaled) { + final int lim = scaled ? wiper.screenWidth / wiper.dupy : wiper.screenWidth; + boolean done = true; + + while (wiper.ticks-- > 0) { + for (int i = 0; i < lim; i++) { + // Column won't start yet. + if (wiper.y[i] < 0) { + wiper.y[i]++; + done = false; + } else if (wiper.y[i] < wiper.screenHeight) { + int dy = (wiper.y[i] < wiper.scaled_16) ? wiper.y[i] + (scaled ? wiper.dupy : 1) : wiper.scaled_8; + if (wiper.y[i] + dy >= wiper.screenHeight) { + dy = wiper.screenHeight - wiper.y[i]; + } + int pd = wiper.y[i] * wiper.screenWidth + (scaled ? i * wiper.dupx : i); + + // MAES: this part should draw the END SCREEN "behind" the melt. + if (scaled) { + toScreenScaled(wiper, wiper.wipeEndScr, dy, pd, pd); + } else { + toScreen(wiper.bufferType, wiper.wipeScr, wiper.wipeEndScr, wiper.screenWidth, dy, pd, pd); + } + + wiper.y[i] += dy; + pd += dy * wiper.screenWidth; + + // This draws a column shifted by y[i] + if (scaled) { + toScreenScaled(wiper, wiper.wipeStartScr, wiper.screenHeight - wiper.y[i], i * wiper.dupy, pd); + } else { + toScreen(wiper.bufferType, wiper.wipeScr, wiper.wipeStartScr, wiper.screenWidth, wiper.screenHeight - wiper.y[i], i, pd); + } + + done = false; + } + } + } + + return done; + } + + default boolean exitMelt(Wipers.WiperImpl wiper) { + wiper.y = null; //Z_Free(y); + wiper.ticks = 0; + return false; + } +} \ No newline at end of file diff --git a/src/v/graphics/Palettes.java b/src/v/graphics/Palettes.java index ab273cd..9a01cf8 100644 --- a/src/v/graphics/Palettes.java +++ b/src/v/graphics/Palettes.java @@ -1,233 +1,233 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -package v.graphics; - -import java.awt.image.IndexColorModel; -import static v.tables.GammaTables.LUT; - -/** - * Refactored and included as the module of new software 2d graphics API - * - Good Sign 2017/04/14 - * - * Palettes & colormaps library - * - * @author Good Sign - * @author Maes - */ -public interface Palettes extends Lights { - - /** - * Maximum number of colors in palette - */ - static int PAL_NUM_COLORS = 256; - - /** - * There is 256 colors in standard PALYPAL lump, 3 bytes for each color (RGB value) - * totaling 256 * 3 = 768 bytes - */ - final int PAL_NUM_STRIDES = 3; - - /** - * Maximum number of palettes - * PLAYPAL length / (PAL_NUM_COLORS * PAL_NUM_STRIDES) - * - * TODO: think some way of support for future Hexen, Heretic, Strife palettes - */ - static int NUM_PALETTES = 14; - - /** - * Methods to be used by implementor - */ - /** - * Perform any action necessary so that palettes get modified according to specified gamma. - * Consider this a TIME CONSUMING operation, so don't call it unless really necessary. - * - * @param gammalevel - */ - void setUsegamma(int gammalevel); - - /** - * Getter for gamma level - * - * @return - */ - int getUsegamma(); - - /** - * Perform any action necessary so that the screen output uses the specified palette - * Consider this a TIME CONSUMING operation, so don't call it unless really necessary. - * - * @param palette - */ - void setPalette(int palette); - - /** - * Getter for palette - * - * @return - */ - int getPalette(); - - /** - * Get the value corresponding to a base color (0-255). - * Depending on the implementation this might be indexed, - * RGB etc. Use whenever you need "absolute" colors. - * - * @return int - */ - int getBaseColor(byte color); - - default int getBaseColor(int color) { - return getBaseColor((byte) color); - } - - /** - * Extracts RGB888 color from an index in the palette - * @param byte[] pal proper playpal - * @param int index and index of the color in the palette - * @return int packed opaque rgb888 pixel - */ - default int paletteToRGB888(byte[] pal, int index) { - return toRGB888(pal[index], pal[index + 1], pal[index + 2]); - } - - /** - * Extracts RGB555 color from an index in the palette - * @param byte[] pal proper playpal - * @param int index and index of the color in the palette - * @return int packed rgb555 pixel - */ - default short paletteToRGB555(byte[] pal, int index) { - return rgb888to555(pal[index], pal[index + 1], pal[index + 2]); - } - - /** - * Extracts RGB888 color components from an index in the palette to the container - * @param byte[] pal proper playpal - * @param byte index and index of the color in the palette - * @param int[] container to hold individual RGB color components - * @return int[] the populated container - */ - default int[] getPaletteRGB888(byte[] pal, int index, int[] container) { - container[0] = pal[index] & 0xFF; - container[1] = pal[index + 1] & 0xFF; - container[2] = pal[index + 2] & 0xFF; - return container; - } - - /** - * ColorShiftPalette - lifted from dcolors.c Operates on RGB888 palettes in - * separate bytes. at shift = 0, the colors are normal at shift = steps, the - * colors are all the given rgb - */ - default void ColorShiftPalette(byte[] inpal, byte[] outpal, int r, int g, int b, int shift, int steps) { - int in_p = 0; - int out_p = 0; - for (int i = 0; i < PAL_NUM_COLORS; i++) { - final int dr = r - inpal[in_p + 0]; - final int dg = g - inpal[in_p + 1]; - final int db = b - inpal[in_p + 2]; - outpal[out_p + 0] = (byte) (inpal[in_p + 0] + dr * shift / steps); - outpal[out_p + 1] = (byte) (inpal[in_p + 1] + dg * shift / steps); - outpal[out_p + 2] = (byte) (inpal[in_p + 2] + db * shift / steps); - in_p += 3; - out_p += 3; - } - } - - /** - * Given raw palette data, returns an array with proper TrueColor data - * @param byte[] pal proper palette - * @return int[] 32 bit Truecolor ARGB colormap - */ - default int[] paletteTrueColor(byte[] pal) { - final int pal888[] = new int[PAL_NUM_COLORS]; - - // Initial palette can be neutral or based upon "gamma 0", - // which is actually a bit biased and distorted - for (int x = 0; x < PAL_NUM_COLORS; ++x) { - pal888[x] = paletteToRGB888(pal, x * PAL_NUM_STRIDES); - } - - return pal888; - } - - /** - * Given raw palette data, returns an array with proper HiColor data - * @param byte[] pal proper palette - * @return short[] 16 bit HiColor RGB colormap - */ - default short[] paletteHiColor(byte[] pal) { - final short[] pal555 = new short[PAL_NUM_COLORS]; - - // Apply gammas a-posteriori, not a-priori. - // Initial palette can be neutral or based upon "gamma 0", - // which is actually a bit biased and distorted - for (int x = 0; x < PAL_NUM_COLORS; ++x) { - pal555[x] = paletteToRGB555(pal, x * PAL_NUM_STRIDES); - } - - return pal555; - } - - /** - * Given an array of certain length and raw palette data fills array - * with IndexColorModel's for each palette. Gammas are applied a-priori - * @param IndexColorModel[][] cmaps preallocated array, as it is often reconstructed for gamma, do not reallocate it - * @param byte[] pal proper palette - * @return the same araay as input, but all values set to new IndexColorModels - */ - default IndexColorModel[][] cmapIndexed(IndexColorModel icms[][], byte[] pal) { - final int colorsXstride = PAL_NUM_COLORS * PAL_NUM_STRIDES; - - // Now we have our palettes. - for (int i = 0; i < icms[0].length; ++i) { - //new IndexColorModel(8, PAL_NUM_COLORS, pal, i * colorsXstride, false); - icms[0][i] = createIndexColorModel(pal, i * colorsXstride); - } - - // Wire the others according to the gamma table. - final byte[] tmpcmap = new byte[colorsXstride]; - - // For each gamma value... - for (int j = 1; j < LUT.length; j++) { - // For each palette - for (int i = 0; i < NUM_PALETTES; i++) { - for (int k = 0; k < PAL_NUM_COLORS; ++k) { - final int iXcolorsXstride_plus_StrideXk = i * colorsXstride + PAL_NUM_STRIDES * k; - tmpcmap[3 * k/**/] = (byte) LUT[j][0xFF & pal[/**/iXcolorsXstride_plus_StrideXk]]; // R - tmpcmap[3 * k + 1] = (byte) LUT[j][0xFF & pal[1 + iXcolorsXstride_plus_StrideXk]]; // G - tmpcmap[3 * k + 2] = (byte) LUT[j][0xFF & pal[2 + iXcolorsXstride_plus_StrideXk]]; // B - } - - icms[j][i] = createIndexColorModel(tmpcmap, 0); - } - } - - return icms; - } - - /** - * @param byte[] cmap a colormap from which to make color model - * @param int start position in colormap from which to take PAL_NUM_COLORS - * @return IndexColorModel - */ - default IndexColorModel createIndexColorModel(byte cmap[], int start) { - return new IndexColorModel(8, PAL_NUM_COLORS, cmap, start, false); - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package v.graphics; + +import java.awt.image.IndexColorModel; +import static v.tables.GammaTables.LUT; + +/** + * Refactored and included as the module of new software 2d graphics API + * - Good Sign 2017/04/14 + * + * Palettes & colormaps library + * + * @author Good Sign + * @author Maes + */ +public interface Palettes extends Lights { + + /** + * Maximum number of colors in palette + */ + static int PAL_NUM_COLORS = 256; + + /** + * There is 256 colors in standard PALYPAL lump, 3 bytes for each color (RGB value) + * totaling 256 * 3 = 768 bytes + */ + final int PAL_NUM_STRIDES = 3; + + /** + * Maximum number of palettes + * PLAYPAL length / (PAL_NUM_COLORS * PAL_NUM_STRIDES) + * + * TODO: think some way of support for future Hexen, Heretic, Strife palettes + */ + static int NUM_PALETTES = 14; + + /** + * Methods to be used by implementor + */ + /** + * Perform any action necessary so that palettes get modified according to specified gamma. + * Consider this a TIME CONSUMING operation, so don't call it unless really necessary. + * + * @param gammalevel + */ + void setUsegamma(int gammalevel); + + /** + * Getter for gamma level + * + * @return + */ + int getUsegamma(); + + /** + * Perform any action necessary so that the screen output uses the specified palette + * Consider this a TIME CONSUMING operation, so don't call it unless really necessary. + * + * @param palette + */ + void setPalette(int palette); + + /** + * Getter for palette + * + * @return + */ + int getPalette(); + + /** + * Get the value corresponding to a base color (0-255). + * Depending on the implementation this might be indexed, + * RGB etc. Use whenever you need "absolute" colors. + * + * @return int + */ + int getBaseColor(byte color); + + default int getBaseColor(int color) { + return getBaseColor((byte) color); + } + + /** + * Extracts RGB888 color from an index in the palette + * @param byte[] pal proper playpal + * @param int index and index of the color in the palette + * @return int packed opaque rgb888 pixel + */ + default int paletteToRGB888(byte[] pal, int index) { + return toRGB888(pal[index], pal[index + 1], pal[index + 2]); + } + + /** + * Extracts RGB555 color from an index in the palette + * @param byte[] pal proper playpal + * @param int index and index of the color in the palette + * @return int packed rgb555 pixel + */ + default short paletteToRGB555(byte[] pal, int index) { + return rgb888to555(pal[index], pal[index + 1], pal[index + 2]); + } + + /** + * Extracts RGB888 color components from an index in the palette to the container + * @param byte[] pal proper playpal + * @param byte index and index of the color in the palette + * @param int[] container to hold individual RGB color components + * @return int[] the populated container + */ + default int[] getPaletteRGB888(byte[] pal, int index, int[] container) { + container[0] = pal[index] & 0xFF; + container[1] = pal[index + 1] & 0xFF; + container[2] = pal[index + 2] & 0xFF; + return container; + } + + /** + * ColorShiftPalette - lifted from dcolors.c Operates on RGB888 palettes in + * separate bytes. at shift = 0, the colors are normal at shift = steps, the + * colors are all the given rgb + */ + default void ColorShiftPalette(byte[] inpal, byte[] outpal, int r, int g, int b, int shift, int steps) { + int in_p = 0; + int out_p = 0; + for (int i = 0; i < PAL_NUM_COLORS; i++) { + final int dr = r - inpal[in_p + 0]; + final int dg = g - inpal[in_p + 1]; + final int db = b - inpal[in_p + 2]; + outpal[out_p + 0] = (byte) (inpal[in_p + 0] + dr * shift / steps); + outpal[out_p + 1] = (byte) (inpal[in_p + 1] + dg * shift / steps); + outpal[out_p + 2] = (byte) (inpal[in_p + 2] + db * shift / steps); + in_p += 3; + out_p += 3; + } + } + + /** + * Given raw palette data, returns an array with proper TrueColor data + * @param byte[] pal proper palette + * @return int[] 32 bit Truecolor ARGB colormap + */ + default int[] paletteTrueColor(byte[] pal) { + final int pal888[] = new int[PAL_NUM_COLORS]; + + // Initial palette can be neutral or based upon "gamma 0", + // which is actually a bit biased and distorted + for (int x = 0; x < PAL_NUM_COLORS; ++x) { + pal888[x] = paletteToRGB888(pal, x * PAL_NUM_STRIDES); + } + + return pal888; + } + + /** + * Given raw palette data, returns an array with proper HiColor data + * @param byte[] pal proper palette + * @return short[] 16 bit HiColor RGB colormap + */ + default short[] paletteHiColor(byte[] pal) { + final short[] pal555 = new short[PAL_NUM_COLORS]; + + // Apply gammas a-posteriori, not a-priori. + // Initial palette can be neutral or based upon "gamma 0", + // which is actually a bit biased and distorted + for (int x = 0; x < PAL_NUM_COLORS; ++x) { + pal555[x] = paletteToRGB555(pal, x * PAL_NUM_STRIDES); + } + + return pal555; + } + + /** + * Given an array of certain length and raw palette data fills array + * with IndexColorModel's for each palette. Gammas are applied a-priori + * @param IndexColorModel[][] cmaps preallocated array, as it is often reconstructed for gamma, do not reallocate it + * @param byte[] pal proper palette + * @return the same araay as input, but all values set to new IndexColorModels + */ + default IndexColorModel[][] cmapIndexed(IndexColorModel icms[][], byte[] pal) { + final int colorsXstride = PAL_NUM_COLORS * PAL_NUM_STRIDES; + + // Now we have our palettes. + for (int i = 0; i < icms[0].length; ++i) { + //new IndexColorModel(8, PAL_NUM_COLORS, pal, i * colorsXstride, false); + icms[0][i] = createIndexColorModel(pal, i * colorsXstride); + } + + // Wire the others according to the gamma table. + final byte[] tmpcmap = new byte[colorsXstride]; + + // For each gamma value... + for (int j = 1; j < LUT.length; j++) { + // For each palette + for (int i = 0; i < NUM_PALETTES; i++) { + for (int k = 0; k < PAL_NUM_COLORS; ++k) { + final int iXcolorsXstride_plus_StrideXk = i * colorsXstride + PAL_NUM_STRIDES * k; + tmpcmap[3 * k/**/] = (byte) LUT[j][0xFF & pal[/**/iXcolorsXstride_plus_StrideXk]]; // R + tmpcmap[3 * k + 1] = (byte) LUT[j][0xFF & pal[1 + iXcolorsXstride_plus_StrideXk]]; // G + tmpcmap[3 * k + 2] = (byte) LUT[j][0xFF & pal[2 + iXcolorsXstride_plus_StrideXk]]; // B + } + + icms[j][i] = createIndexColorModel(tmpcmap, 0); + } + } + + return icms; + } + + /** + * @param byte[] cmap a colormap from which to make color model + * @param int start position in colormap from which to take PAL_NUM_COLORS + * @return IndexColorModel + */ + default IndexColorModel createIndexColorModel(byte cmap[], int start) { + return new IndexColorModel(8, PAL_NUM_COLORS, cmap, start, false); + } +} \ No newline at end of file diff --git a/src/v/graphics/Patches.java b/src/v/graphics/Patches.java index d72188c..e7d1f86 100644 --- a/src/v/graphics/Patches.java +++ b/src/v/graphics/Patches.java @@ -1,226 +1,226 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.graphics; - -import java.util.logging.Level; -import java.util.logging.Logger; -import mochadoom.Loggers; -import rr.patch_t; -import utils.C2JUtils; -import static v.DoomGraphicSystem.V_FLIPPEDPATCH; -import static v.DoomGraphicSystem.V_NOSCALEOFFSET; -import static v.DoomGraphicSystem.V_NOSCALEPATCH; -import static v.DoomGraphicSystem.V_NOSCALESTART; -import static v.DoomGraphicSystem.V_PREDIVIDE; -import static v.DoomGraphicSystem.V_SAFESCALE; -import static v.DoomGraphicSystem.V_SCALEOFFSET; -import static v.DoomGraphicSystem.V_SCALEPATCH; -import static v.DoomGraphicSystem.V_SCALESTART; -import v.scale.VideoScale; - -/** - * Rewritten unified patch-drawing methods with parallelism (yeah, multithread!) - * Note that now most of the functions now support FLAGS now as a separate argument, I totally needed screen id safety. - * Reimplemented counter-flags, that work the next way: - * - there is a Default Behavior chose when flag is not present - * - if the flag is present Default Behavior is changed - * - if both the flag and the opposite flag are present, then the flag that restores Default Behavior takes precedence - * - * I tried my best to preserve all of the features done by prior contributors. - Good Sign 2017/04/02 - * - * @author Good Sign - * - * About all DrawPatch functions: - * It uses FLAGS (see above) (now as a separate - Good Sign 2017/04/04) parameter, to be - * parsed afterwards. Shamelessly ripped from Doom Legacy (for menus, etc) by _D_ ;-) - * - * added:05-02-98: default params : scale patch and scale start - * - * Iniially implemented for Mocha Doom by _D_ (shamelessly ripped from Eternity Engine ;-), adapted to scale based - * on a scaling info object (VSI). - * - * Unless overriden by flags, starting x and y are automatically scaled (implied V_SCALESTART) - */ -public interface Patches> extends Columns { - - static final Logger LOGGER = Loggers.getLogger(Patches.class.getName()); - - /** - * V_DrawPatch - * - * Draws a patch to the screen without centering or scaling - */ - default void DrawPatch(E screen, patch_t patch, int x, int y, int... flags) { - DrawPatchScaled(screen, patch, null, x, y, flags); - } - - /** - * V_DrawPatch - * - * Draws a patch to the screen without centering or scaling - */ - default void DrawPatchCentered(E screen, patch_t patch, int y, int... flags) { - Patches.this.DrawPatchCenteredScaled(screen, patch, null, y, flags); - } - - /** - * V_DrawScaledPatch like V_DrawPatch, but scaled with IVideoScale object scaling - * Centers the x coordinate on a screen based on patch width and offset - * I have completely reworked column drawing code, so it resides in another class, and supports parallelism - * - Good Sign 2017/04/04 - * - * It uses FLAGS (see above) (now as a separate - Good Sign 2017/04/04) parameter, to be - * parsed afterwards. Shamelessly ripped from Doom Legacy (for menus, etc) by _D_ ;-) - */ - default void DrawPatchCenteredScaled(E screen, patch_t patch, VideoScale vs, int y, int... flags) { - final int flagsV = flags.length > 0 ? flags[0] : 0; - int dupx, dupy; - if (vs != null) { - if (C2JUtils.flags(flagsV, V_SAFESCALE)) { - dupx = dupy = vs.getSafeScaling(); - } else { - dupx = vs.getScalingX(); - dupy = vs.getScalingY(); - } - } else { - dupx = dupy = 1; - } - final boolean predevide = C2JUtils.flags(flagsV, V_PREDIVIDE); - // By default we scale, if V_NOSCALEOFFSET we dont scale unless V_SCALEOFFSET (restores Default Behavior) - final boolean scaleOffset = !C2JUtils.flags(flagsV, V_NOSCALEOFFSET) || C2JUtils.flags(flagsV, V_SCALEOFFSET); - // By default we scale, if V_NOSCALESTART we dont scale unless V_SCALESTART (restores Default Behavior) - final boolean scaleStart = !C2JUtils.flags(flagsV, V_NOSCALESTART) || C2JUtils.flags(flagsV, V_SCALESTART); - // By default we do dup, if V_NOSCALEPATCH we dont dup unless V_SCALEPATCH (restores Default Behavior) - final boolean noScalePatch = C2JUtils.flags(flagsV, V_NOSCALEPATCH) && !C2JUtils.flags(flagsV, V_SCALEPATCH); - final boolean flip = C2JUtils.flags(flagsV, V_FLIPPEDPATCH); - final int halfWidth = noScalePatch ? patch.width / 2 : patch.width * dupx / 2; - int x = getScreenWidth() / 2 - halfWidth - (scaleOffset ? patch.leftoffset * dupx : patch.leftoffset); - y = applyScaling(y, patch.topoffset, dupy, predevide, scaleOffset, scaleStart); - - if (noScalePatch) { - dupx = dupy = 1; - } - - try { - doRangeCheck(x, y, patch, dupx, dupy); - DrawPatchColumns(getScreen(screen), patch, x, y, dupx, dupy, flip); - } catch (BadRangeException ex) { - printDebugPatchInfo(patch, x, y, predevide, scaleOffset, scaleStart, dupx, dupy); - } - } - - /** - * This method should help to debug bad patches or bad placement of them - * - Good Sign 2017/04/22 - */ - default void printDebugPatchInfo(patch_t patch, int x, int y, final boolean predevide, final boolean scaleOffset, final boolean scaleStart, int dupx, int dupy) { - LOGGER.log(Level.INFO, () -> String.format( - "V_DrawPatch: bad patch (ignored)\n" - + "Patch %s at %d, %d exceeds LFB\n" - + "\tpredevide: %s\n" - + "\tscaleOffset: %s\n" - + "\tscaleStart: %s\n" - + "\tdupx: %s, dupy: %s\n" - + "\tleftoffset: %s\n" - + "\ttopoffset: %s\n", - patch.name, x, y, - predevide, scaleOffset, scaleStart, dupx, dupy, patch.leftoffset, patch.topoffset - )); - } - - /** - * V_DrawPatch - * - * V_DrawScaledPatch like V_DrawPatch, but scaled with IVideoScale object scaling - * I have completely reworked column drawing code, so it resides in another class, and supports parallelism - * - Good Sign 2017/04/04 - */ - default void DrawPatchScaled(E screen, patch_t patch, VideoScale vs, int x, int y, int... flags) { - final int flagsV = flags.length > 0 ? flags[0] : 0; - int dupx, dupy; - if (vs != null) { - if (C2JUtils.flags(flagsV, V_SAFESCALE)) { - dupx = dupy = vs.getSafeScaling(); - } else { - dupx = vs.getScalingX(); - dupy = vs.getScalingY(); - } - } else { - dupx = dupy = 1; - } - final boolean predevide = C2JUtils.flags(flagsV, V_PREDIVIDE); - // By default we scale, if V_NOSCALEOFFSET we dont scale unless V_SCALEOFFSET (restores Default Behavior) - final boolean scaleOffset = !C2JUtils.flags(flagsV, V_NOSCALEOFFSET) || C2JUtils.flags(flagsV, V_SCALEOFFSET); - // By default we scale, if V_NOSCALESTART we dont scale unless V_SCALESTART (restores Default Behavior) - final boolean scaleStart = !C2JUtils.flags(flagsV, V_NOSCALESTART) || C2JUtils.flags(flagsV, V_SCALESTART); - // By default we do dup, if V_NOSCALEPATCH we dont dup unless V_SCALEPATCH (restores Default Behavior) - final boolean noScalePatch = C2JUtils.flags(flagsV, V_NOSCALEPATCH) && !C2JUtils.flags(flagsV, V_SCALEPATCH); - final boolean flip = C2JUtils.flags(flagsV, V_FLIPPEDPATCH); - x = applyScaling(x, patch.leftoffset, dupx, predevide, scaleOffset, scaleStart); - y = applyScaling(y, patch.topoffset, dupy, predevide, scaleOffset, scaleStart); - - if (noScalePatch) { - dupx = dupy = 1; - } - - try { - doRangeCheck(x, y, patch, dupx, dupy); - DrawPatchColumns(getScreen(screen), patch, x, y, dupx, dupy, flip); - } catch (BadRangeException ex) { - // Do not abort! - printDebugPatchInfo(patch, x, y, predevide, scaleOffset, scaleStart, dupx, dupy); - } - } - - /** - * Replaces DrawPatchCol for bunny scrolled in Finale. - * Also uses my reworked column code, but that one is not parallelized - * - Good Sign 2017/04/04 - */ - default void DrawPatchColScaled(E screen, patch_t patch, VideoScale vs, int x, int col) { - final int dupx = vs.getScalingX(), dupy = vs.getScalingY(); - x -= patch.leftoffset; - x *= dupx; - - DrawColumn( - getScreen(screen), - patch.columns[col], - new Horizontal(point(x, 0), dupx), - convertPalettedBlock(patch.columns[col].data), - getScreenWidth(), - dupy - ); - } - - default int applyScaling(int c, int offset, int dup, boolean predevide, boolean scaleOffset, boolean scaleStart) { - // A very common operation, eliminates the need to pre-divide. - if (predevide) { - c /= getScalingX(); - } - - // Scale start before offsetting, it seems right to do so - Good Sign 2017/04/04 - if (scaleStart) { - c *= dup; - } - - // MAES: added this fix so that non-zero patch offsets can be - // taken into account, regardless of whether we use pre-scaled - // coords or not. Only Doomguy's face needs this hack for now. - c -= scaleOffset ? offset * dup : offset; - return c; - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.graphics; + +import java.util.logging.Level; +import java.util.logging.Logger; +import mochadoom.Loggers; +import rr.patch_t; +import utils.C2JUtils; +import static v.DoomGraphicSystem.V_FLIPPEDPATCH; +import static v.DoomGraphicSystem.V_NOSCALEOFFSET; +import static v.DoomGraphicSystem.V_NOSCALEPATCH; +import static v.DoomGraphicSystem.V_NOSCALESTART; +import static v.DoomGraphicSystem.V_PREDIVIDE; +import static v.DoomGraphicSystem.V_SAFESCALE; +import static v.DoomGraphicSystem.V_SCALEOFFSET; +import static v.DoomGraphicSystem.V_SCALEPATCH; +import static v.DoomGraphicSystem.V_SCALESTART; +import v.scale.VideoScale; + +/** + * Rewritten unified patch-drawing methods with parallelism (yeah, multithread!) + * Note that now most of the functions now support FLAGS now as a separate argument, I totally needed screen id safety. + * Reimplemented counter-flags, that work the next way: + * - there is a Default Behavior chose when flag is not present + * - if the flag is present Default Behavior is changed + * - if both the flag and the opposite flag are present, then the flag that restores Default Behavior takes precedence + * + * I tried my best to preserve all of the features done by prior contributors. - Good Sign 2017/04/02 + * + * @author Good Sign + * + * About all DrawPatch functions: + * It uses FLAGS (see above) (now as a separate - Good Sign 2017/04/04) parameter, to be + * parsed afterwards. Shamelessly ripped from Doom Legacy (for menus, etc) by _D_ ;-) + * + * added:05-02-98: default params : scale patch and scale start + * + * Iniially implemented for Mocha Doom by _D_ (shamelessly ripped from Eternity Engine ;-), adapted to scale based + * on a scaling info object (VSI). + * + * Unless overriden by flags, starting x and y are automatically scaled (implied V_SCALESTART) + */ +public interface Patches> extends Columns { + + static final Logger LOGGER = Loggers.getLogger(Patches.class.getName()); + + /** + * V_DrawPatch + * + * Draws a patch to the screen without centering or scaling + */ + default void DrawPatch(E screen, patch_t patch, int x, int y, int... flags) { + DrawPatchScaled(screen, patch, null, x, y, flags); + } + + /** + * V_DrawPatch + * + * Draws a patch to the screen without centering or scaling + */ + default void DrawPatchCentered(E screen, patch_t patch, int y, int... flags) { + Patches.this.DrawPatchCenteredScaled(screen, patch, null, y, flags); + } + + /** + * V_DrawScaledPatch like V_DrawPatch, but scaled with IVideoScale object scaling + * Centers the x coordinate on a screen based on patch width and offset + * I have completely reworked column drawing code, so it resides in another class, and supports parallelism + * - Good Sign 2017/04/04 + * + * It uses FLAGS (see above) (now as a separate - Good Sign 2017/04/04) parameter, to be + * parsed afterwards. Shamelessly ripped from Doom Legacy (for menus, etc) by _D_ ;-) + */ + default void DrawPatchCenteredScaled(E screen, patch_t patch, VideoScale vs, int y, int... flags) { + final int flagsV = flags.length > 0 ? flags[0] : 0; + int dupx, dupy; + if (vs != null) { + if (C2JUtils.flags(flagsV, V_SAFESCALE)) { + dupx = dupy = vs.getSafeScaling(); + } else { + dupx = vs.getScalingX(); + dupy = vs.getScalingY(); + } + } else { + dupx = dupy = 1; + } + final boolean predevide = C2JUtils.flags(flagsV, V_PREDIVIDE); + // By default we scale, if V_NOSCALEOFFSET we dont scale unless V_SCALEOFFSET (restores Default Behavior) + final boolean scaleOffset = !C2JUtils.flags(flagsV, V_NOSCALEOFFSET) || C2JUtils.flags(flagsV, V_SCALEOFFSET); + // By default we scale, if V_NOSCALESTART we dont scale unless V_SCALESTART (restores Default Behavior) + final boolean scaleStart = !C2JUtils.flags(flagsV, V_NOSCALESTART) || C2JUtils.flags(flagsV, V_SCALESTART); + // By default we do dup, if V_NOSCALEPATCH we dont dup unless V_SCALEPATCH (restores Default Behavior) + final boolean noScalePatch = C2JUtils.flags(flagsV, V_NOSCALEPATCH) && !C2JUtils.flags(flagsV, V_SCALEPATCH); + final boolean flip = C2JUtils.flags(flagsV, V_FLIPPEDPATCH); + final int halfWidth = noScalePatch ? patch.width / 2 : patch.width * dupx / 2; + int x = getScreenWidth() / 2 - halfWidth - (scaleOffset ? patch.leftoffset * dupx : patch.leftoffset); + y = applyScaling(y, patch.topoffset, dupy, predevide, scaleOffset, scaleStart); + + if (noScalePatch) { + dupx = dupy = 1; + } + + try { + doRangeCheck(x, y, patch, dupx, dupy); + DrawPatchColumns(getScreen(screen), patch, x, y, dupx, dupy, flip); + } catch (BadRangeException ex) { + printDebugPatchInfo(patch, x, y, predevide, scaleOffset, scaleStart, dupx, dupy); + } + } + + /** + * This method should help to debug bad patches or bad placement of them + * - Good Sign 2017/04/22 + */ + default void printDebugPatchInfo(patch_t patch, int x, int y, final boolean predevide, final boolean scaleOffset, final boolean scaleStart, int dupx, int dupy) { + LOGGER.log(Level.INFO, () -> String.format( + "V_DrawPatch: bad patch (ignored)\n" + + "Patch %s at %d, %d exceeds LFB\n" + + "\tpredevide: %s\n" + + "\tscaleOffset: %s\n" + + "\tscaleStart: %s\n" + + "\tdupx: %s, dupy: %s\n" + + "\tleftoffset: %s\n" + + "\ttopoffset: %s\n", + patch.name, x, y, + predevide, scaleOffset, scaleStart, dupx, dupy, patch.leftoffset, patch.topoffset + )); + } + + /** + * V_DrawPatch + * + * V_DrawScaledPatch like V_DrawPatch, but scaled with IVideoScale object scaling + * I have completely reworked column drawing code, so it resides in another class, and supports parallelism + * - Good Sign 2017/04/04 + */ + default void DrawPatchScaled(E screen, patch_t patch, VideoScale vs, int x, int y, int... flags) { + final int flagsV = flags.length > 0 ? flags[0] : 0; + int dupx, dupy; + if (vs != null) { + if (C2JUtils.flags(flagsV, V_SAFESCALE)) { + dupx = dupy = vs.getSafeScaling(); + } else { + dupx = vs.getScalingX(); + dupy = vs.getScalingY(); + } + } else { + dupx = dupy = 1; + } + final boolean predevide = C2JUtils.flags(flagsV, V_PREDIVIDE); + // By default we scale, if V_NOSCALEOFFSET we dont scale unless V_SCALEOFFSET (restores Default Behavior) + final boolean scaleOffset = !C2JUtils.flags(flagsV, V_NOSCALEOFFSET) || C2JUtils.flags(flagsV, V_SCALEOFFSET); + // By default we scale, if V_NOSCALESTART we dont scale unless V_SCALESTART (restores Default Behavior) + final boolean scaleStart = !C2JUtils.flags(flagsV, V_NOSCALESTART) || C2JUtils.flags(flagsV, V_SCALESTART); + // By default we do dup, if V_NOSCALEPATCH we dont dup unless V_SCALEPATCH (restores Default Behavior) + final boolean noScalePatch = C2JUtils.flags(flagsV, V_NOSCALEPATCH) && !C2JUtils.flags(flagsV, V_SCALEPATCH); + final boolean flip = C2JUtils.flags(flagsV, V_FLIPPEDPATCH); + x = applyScaling(x, patch.leftoffset, dupx, predevide, scaleOffset, scaleStart); + y = applyScaling(y, patch.topoffset, dupy, predevide, scaleOffset, scaleStart); + + if (noScalePatch) { + dupx = dupy = 1; + } + + try { + doRangeCheck(x, y, patch, dupx, dupy); + DrawPatchColumns(getScreen(screen), patch, x, y, dupx, dupy, flip); + } catch (BadRangeException ex) { + // Do not abort! + printDebugPatchInfo(patch, x, y, predevide, scaleOffset, scaleStart, dupx, dupy); + } + } + + /** + * Replaces DrawPatchCol for bunny scrolled in Finale. + * Also uses my reworked column code, but that one is not parallelized + * - Good Sign 2017/04/04 + */ + default void DrawPatchColScaled(E screen, patch_t patch, VideoScale vs, int x, int col) { + final int dupx = vs.getScalingX(), dupy = vs.getScalingY(); + x -= patch.leftoffset; + x *= dupx; + + DrawColumn( + getScreen(screen), + patch.columns[col], + new Horizontal(point(x, 0), dupx), + convertPalettedBlock(patch.columns[col].data), + getScreenWidth(), + dupy + ); + } + + default int applyScaling(int c, int offset, int dup, boolean predevide, boolean scaleOffset, boolean scaleStart) { + // A very common operation, eliminates the need to pre-divide. + if (predevide) { + c /= getScalingX(); + } + + // Scale start before offsetting, it seems right to do so - Good Sign 2017/04/04 + if (scaleStart) { + c *= dup; + } + + // MAES: added this fix so that non-zero patch offsets can be + // taken into account, regardless of whether we use pre-scaled + // coords or not. Only Doomguy's face needs this hack for now. + c -= scaleOffset ? offset * dup : offset; + return c; + } +} \ No newline at end of file diff --git a/src/v/graphics/Plotter.java b/src/v/graphics/Plotter.java index c0c2b0e..d0b4ade 100644 --- a/src/v/graphics/Plotter.java +++ b/src/v/graphics/Plotter.java @@ -1,252 +1,252 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.graphics; - -import java.lang.reflect.Array; -import java.util.Objects; -import static utils.GenericCopy.memcpy; -import static utils.GenericCopy.memset; -import static v.graphics.Direction.CENTER; - -/** - * - * @author Good Sign - */ -public interface Plotter { - - default Plotter setColorSource(V colorSource) { - return setColorSource(colorSource, 0); - } - - Plotter setColorSource(V colorSource, int colorPos); - - Plotter setPosition(int x, int y); - - Plotter setThickness(int dupX, int dupY); - - Plotter plot(); - - Plotter shiftX(int shift); - - Plotter shiftY(int shift); - - int getX(); - - int getY(); - - enum Style { - Thin, Thick, Deep - } - - default Plotter shift(int shiftX, int shiftY) { - return shiftX(shiftX).shiftY(shiftY); - } - - /** - * Abstract plotter - without a Plot method - */ - abstract class Abstract implements Plotter { - - protected final V screen; - protected final int rowShift; - - protected Style style; - protected V colorSource; - protected int point; - protected int x; - protected int y; - - Abstract(V screen, int rowShift) { - this.screen = screen; - this.rowShift = rowShift; - } - - @Override - @SuppressWarnings("unchecked") - public Plotter setColorSource(V colorSource, int colorPos) { - Objects.requireNonNull(colorSource); - // cache only necessary part of the source - this.colorSource = (V) Array.newInstance(colorSource.getClass().getComponentType(), 1); - memcpy(colorSource, colorPos, this.colorSource, 0, 1); - return this; - } - - @Override - public Plotter setThickness(int dupX, int dupY) { - return this; - } - - @Override - public Plotter setPosition(int x, int y) { - this.point = y * rowShift + x; - this.x = x; - this.y = y; - return this; - } - - @Override - public Plotter shiftX(int shift) { - point += shift; - x += shift; - return this; - } - - @Override - public Plotter shiftY(int shift) { - if (shift > 0) { - point += rowShift; - ++y; - } else { - point -= rowShift; - --y; - } - - return this; - } - - @Override - public int getX() { - return x; - } - - @Override - public int getY() { - return y; - } - } - - class Thin extends Abstract { - - public Thin(V screen, int rowShift) { - super(screen, rowShift); - } - - @Override - public Plotter plot() { - memcpy(colorSource, 0, screen, point, 1); - return this; - } - } - - static int getThickness(int dupX) { - return Math.max(dupX >> 1, 1); - } - - /** - * You give it desired scaling level, it makes lines thicker - */ - class Thick extends Abstract { - - protected final int height; - protected int xThick; - protected int yThick; - - public Thick(V screen, int width, int height) { - super(screen, width); - this.height = height; - - // can overflow! - this.xThick = 1;//dupX >> 1; - this.yThick = 1;//dupX >> 1; - } - - @Override - public Plotter setThickness(int dupX, int dupY) { - this.xThick = dupX; - this.yThick = dupY; - return this; - } - - @Override - public Plotter plot() { - if (xThick == 0 || yThick == 0) { - memcpy(colorSource, 0, screen, point, 1); - return this; - } - return plotThick(xThick, yThick); - } - - protected Plotter plotThick(int modThickX, int modThickY) { - final int rows = y < modThickY ? y : (height < y + modThickY ? height - y : modThickY); - final int spaceLeft = x < modThickX ? 0 : modThickX; - final int spaceRight = rowShift < x + modThickX ? rowShift - x : modThickX; - - for (int row = -rows; row < rows; ++row) { - // color = colorSource[Math.abs(row)] - memset(screen, point - spaceLeft + rowShift * row, spaceLeft + spaceRight, colorSource, 0, 1); - } - - return this; - } - } - - /** - * Thick, but the direction of drawing is counted in - i.e., for round borders... - */ - class Deep extends Thick { - - protected Direction direction; - - public Deep(V screen, int width, int height) { - super(screen, width, height); - } - - @Override - public Plotter setPosition(int x, int y) { - direction = CENTER; - return super.setPosition(x, y); - } - - @Override - public Plotter shiftX(int shift) { - direction = direction.rotationHor(shift); - return super.shiftX(shift); - } - - @Override - public Plotter shiftY(int shift) { - direction = direction.rotationVert(shift); - return super.shiftY(shift); - } - - @Override - public Plotter shift(int shiftX, int shiftY) { - direction = direction.rotation(shiftX, shiftY); - return super.shift(shiftX, shiftY); - } - - @Override - public Plotter plot() { - if (xThick <= 1 || yThick <= 1) { - return super.plot(); - } - - int modThickX = xThick; - int modThickY = yThick; - - if (!direction.hasTop && !direction.hasBottom) { - modThickX >>= 1; - } - - if (!direction.hasLeft && !direction.hasRight) { - modThickY >>= 1; - } - - return plotThick(modThickX, modThickY); - } - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.graphics; + +import java.lang.reflect.Array; +import java.util.Objects; +import static utils.GenericCopy.memcpy; +import static utils.GenericCopy.memset; +import static v.graphics.Direction.CENTER; + +/** + * + * @author Good Sign + */ +public interface Plotter { + + default Plotter setColorSource(V colorSource) { + return setColorSource(colorSource, 0); + } + + Plotter setColorSource(V colorSource, int colorPos); + + Plotter setPosition(int x, int y); + + Plotter setThickness(int dupX, int dupY); + + Plotter plot(); + + Plotter shiftX(int shift); + + Plotter shiftY(int shift); + + int getX(); + + int getY(); + + enum Style { + Thin, Thick, Deep + } + + default Plotter shift(int shiftX, int shiftY) { + return shiftX(shiftX).shiftY(shiftY); + } + + /** + * Abstract plotter - without a Plot method + */ + abstract class Abstract implements Plotter { + + protected final V screen; + protected final int rowShift; + + protected Style style; + protected V colorSource; + protected int point; + protected int x; + protected int y; + + Abstract(V screen, int rowShift) { + this.screen = screen; + this.rowShift = rowShift; + } + + @Override + @SuppressWarnings("unchecked") + public Plotter setColorSource(V colorSource, int colorPos) { + Objects.requireNonNull(colorSource); + // cache only necessary part of the source + this.colorSource = (V) Array.newInstance(colorSource.getClass().getComponentType(), 1); + memcpy(colorSource, colorPos, this.colorSource, 0, 1); + return this; + } + + @Override + public Plotter setThickness(int dupX, int dupY) { + return this; + } + + @Override + public Plotter setPosition(int x, int y) { + this.point = y * rowShift + x; + this.x = x; + this.y = y; + return this; + } + + @Override + public Plotter shiftX(int shift) { + point += shift; + x += shift; + return this; + } + + @Override + public Plotter shiftY(int shift) { + if (shift > 0) { + point += rowShift; + ++y; + } else { + point -= rowShift; + --y; + } + + return this; + } + + @Override + public int getX() { + return x; + } + + @Override + public int getY() { + return y; + } + } + + class Thin extends Abstract { + + public Thin(V screen, int rowShift) { + super(screen, rowShift); + } + + @Override + public Plotter plot() { + memcpy(colorSource, 0, screen, point, 1); + return this; + } + } + + static int getThickness(int dupX) { + return Math.max(dupX >> 1, 1); + } + + /** + * You give it desired scaling level, it makes lines thicker + */ + class Thick extends Abstract { + + protected final int height; + protected int xThick; + protected int yThick; + + public Thick(V screen, int width, int height) { + super(screen, width); + this.height = height; + + // can overflow! + this.xThick = 1;//dupX >> 1; + this.yThick = 1;//dupX >> 1; + } + + @Override + public Plotter setThickness(int dupX, int dupY) { + this.xThick = dupX; + this.yThick = dupY; + return this; + } + + @Override + public Plotter plot() { + if (xThick == 0 || yThick == 0) { + memcpy(colorSource, 0, screen, point, 1); + return this; + } + return plotThick(xThick, yThick); + } + + protected Plotter plotThick(int modThickX, int modThickY) { + final int rows = y < modThickY ? y : (height < y + modThickY ? height - y : modThickY); + final int spaceLeft = x < modThickX ? 0 : modThickX; + final int spaceRight = rowShift < x + modThickX ? rowShift - x : modThickX; + + for (int row = -rows; row < rows; ++row) { + // color = colorSource[Math.abs(row)] + memset(screen, point - spaceLeft + rowShift * row, spaceLeft + spaceRight, colorSource, 0, 1); + } + + return this; + } + } + + /** + * Thick, but the direction of drawing is counted in - i.e., for round borders... + */ + class Deep extends Thick { + + protected Direction direction; + + public Deep(V screen, int width, int height) { + super(screen, width, height); + } + + @Override + public Plotter setPosition(int x, int y) { + direction = CENTER; + return super.setPosition(x, y); + } + + @Override + public Plotter shiftX(int shift) { + direction = direction.rotationHor(shift); + return super.shiftX(shift); + } + + @Override + public Plotter shiftY(int shift) { + direction = direction.rotationVert(shift); + return super.shiftY(shift); + } + + @Override + public Plotter shift(int shiftX, int shiftY) { + direction = direction.rotation(shiftX, shiftY); + return super.shift(shiftX, shiftY); + } + + @Override + public Plotter plot() { + if (xThick <= 1 || yThick <= 1) { + return super.plot(); + } + + int modThickX = xThick; + int modThickY = yThick; + + if (!direction.hasTop && !direction.hasBottom) { + modThickX >>= 1; + } + + if (!direction.hasLeft && !direction.hasRight) { + modThickY >>= 1; + } + + return plotThick(modThickX, modThickY); + } + } +} \ No newline at end of file diff --git a/src/v/graphics/Points.java b/src/v/graphics/Points.java index 85c5ea0..1770811 100644 --- a/src/v/graphics/Points.java +++ b/src/v/graphics/Points.java @@ -1,57 +1,57 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.graphics; - -import rr.patch_t; - -/** - * - * @author Good Sign - */ -public interface Points> extends Screens { - - default void doRangeCheck(int x, int y, int width, int height) throws BadRangeException { - if (x >= 0 && y >= 0) { - final int scrWidth = this.getScreenWidth(); - final int scrHeight = this.getScreenHeight(); - if (x + width > scrWidth || y + height > scrWidth) { - throw new BadRangeException(String.format( - "Coordinates overflow screen space: (%d, %d, %d, %d) on screen %dx%d", - x, y, x + width, y + height, scrWidth, scrHeight) - ); - } - } else { - throw new IllegalArgumentException(String.format("Invalid coordinates: (%d, %d)", x, y)); - } - } - - default void doRangeCheck(int x, int y, patch_t patch) throws BadRangeException { - doRangeCheck(x, y, patch.width, patch.height); - } - - default void doRangeCheck(int x, int y, patch_t patch, int dupx, int dupy) throws BadRangeException { - doRangeCheck(x, y, patch.width * dupx, patch.height * dupy); - } - - default int point(int x, int y) { - return y * getScreenWidth() + x; - } - - default int point(int x, int y, int width) { - return y * width + x; - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.graphics; + +import rr.patch_t; + +/** + * + * @author Good Sign + */ +public interface Points> extends Screens { + + default void doRangeCheck(int x, int y, int width, int height) throws BadRangeException { + if (x >= 0 && y >= 0) { + final int scrWidth = this.getScreenWidth(); + final int scrHeight = this.getScreenHeight(); + if (x + width > scrWidth || y + height > scrWidth) { + throw new BadRangeException(String.format( + "Coordinates overflow screen space: (%d, %d, %d, %d) on screen %dx%d", + x, y, x + width, y + height, scrWidth, scrHeight) + ); + } + } else { + throw new IllegalArgumentException(String.format("Invalid coordinates: (%d, %d)", x, y)); + } + } + + default void doRangeCheck(int x, int y, patch_t patch) throws BadRangeException { + doRangeCheck(x, y, patch.width, patch.height); + } + + default void doRangeCheck(int x, int y, patch_t patch, int dupx, int dupy) throws BadRangeException { + doRangeCheck(x, y, patch.width * dupx, patch.height * dupy); + } + + default int point(int x, int y) { + return y * getScreenWidth() + x; + } + + default int point(int x, int y, int width) { + return y * width + x; + } +} \ No newline at end of file diff --git a/src/v/graphics/Rectangles.java b/src/v/graphics/Rectangles.java index dd0d9fa..b9ce9b4 100644 --- a/src/v/graphics/Rectangles.java +++ b/src/v/graphics/Rectangles.java @@ -1,108 +1,108 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.graphics; - -import java.awt.Rectangle; - -/** - * Rectangles fill and copy - * - * TODO: range checks on Fill & Copy - * - * @author Good Sign - */ -public interface Rectangles> extends Blocks, Points { - - /** - * Computes a Horizontal with a row from the Rectangle at heightIndex - * @param rect - * @param heightIndex - * @return - */ - default Horizontal GetRectRow(Rectangle rect, int heightIndex) { - if (heightIndex < 0 || heightIndex > rect.height) { - throw new IndexOutOfBoundsException("Bad row index: " + heightIndex); - } - - return new Horizontal(point(rect.x, rect.y) + heightIndex * getScreenWidth(), rect.width); - } - - /** - * V_CopyRect - */ - default void CopyRect(E srcScreenType, Rectangle rectangle, E dstScreenType) { - final V srcScreen = getScreen(srcScreenType); - final V dstScreen = getScreen(dstScreenType); - final int screenWidth = getScreenWidth(); - final int point = point(rectangle.x, rectangle.y); - final Relocation rel = new Relocation(point, point, rectangle.width); - for (int h = rectangle.height; h > 0; --h, rel.shift(screenWidth)) { - screenCopy(srcScreen, dstScreen, rel); - } - } - - default void CopyRect(E srcScreenType, Rectangle rectangle, E dstScreenType, int dstPoint) { - final V srcScreen = getScreen(srcScreenType); - final V dstScreen = getScreen(dstScreenType); - final int screenWidth = getScreenWidth(); - final Relocation rel = new Relocation(point(rectangle.x, rectangle.y), dstPoint, rectangle.width); - for (int h = rectangle.height; h > 0; --h, rel.shift(screenWidth)) { - screenCopy(srcScreen, dstScreen, rel); - } - } - - /** - * V_FillRect - */ - default void FillRect(E screenType, Rectangle rectangle, V patternSrc, Horizontal pattern) { - final V screen = getScreen(screenType); - if (rectangle.height > 0) { - final Horizontal row = GetRectRow(rectangle, 0); - // Fill first line of rect - screenSet(patternSrc, pattern, screen, row); - // Fill the rest of the rect - RepeatRow(screen, row, rectangle.height - 1); - } - } - - default void FillRect(E screenType, Rectangle rectangle, V patternSrc, int point) { - final V screen = getScreen(screenType); - if (rectangle.height > 0) { - final Horizontal row = GetRectRow(rectangle, 0); - // Fill first line of rect - screenSet(patternSrc, point, screen, row); - // Fill the rest of the rect - RepeatRow(screen, row, rectangle.height - 1); - } - } - - default void FillRect(E screenType, Rectangle rectangle, int color) { - FillRect(screenType, rectangle, (byte) color); - } - - default void FillRect(E screenType, Rectangle rectangle, byte color) { - final V screen = getScreen(screenType); - if (rectangle.height > 0) { - final V filler = convertPalettedBlock(color); - final Horizontal row = GetRectRow(rectangle, 0); - // Fill first line of rect - screenSet(filler, 0, screen, row); - // Fill the rest of the rect - RepeatRow(screen, row, rectangle.height - 1); - } - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.graphics; + +import java.awt.Rectangle; + +/** + * Rectangles fill and copy + * + * TODO: range checks on Fill & Copy + * + * @author Good Sign + */ +public interface Rectangles> extends Blocks, Points { + + /** + * Computes a Horizontal with a row from the Rectangle at heightIndex + * @param rect + * @param heightIndex + * @return + */ + default Horizontal GetRectRow(Rectangle rect, int heightIndex) { + if (heightIndex < 0 || heightIndex > rect.height) { + throw new IndexOutOfBoundsException("Bad row index: " + heightIndex); + } + + return new Horizontal(point(rect.x, rect.y) + heightIndex * getScreenWidth(), rect.width); + } + + /** + * V_CopyRect + */ + default void CopyRect(E srcScreenType, Rectangle rectangle, E dstScreenType) { + final V srcScreen = getScreen(srcScreenType); + final V dstScreen = getScreen(dstScreenType); + final int screenWidth = getScreenWidth(); + final int point = point(rectangle.x, rectangle.y); + final Relocation rel = new Relocation(point, point, rectangle.width); + for (int h = rectangle.height; h > 0; --h, rel.shift(screenWidth)) { + screenCopy(srcScreen, dstScreen, rel); + } + } + + default void CopyRect(E srcScreenType, Rectangle rectangle, E dstScreenType, int dstPoint) { + final V srcScreen = getScreen(srcScreenType); + final V dstScreen = getScreen(dstScreenType); + final int screenWidth = getScreenWidth(); + final Relocation rel = new Relocation(point(rectangle.x, rectangle.y), dstPoint, rectangle.width); + for (int h = rectangle.height; h > 0; --h, rel.shift(screenWidth)) { + screenCopy(srcScreen, dstScreen, rel); + } + } + + /** + * V_FillRect + */ + default void FillRect(E screenType, Rectangle rectangle, V patternSrc, Horizontal pattern) { + final V screen = getScreen(screenType); + if (rectangle.height > 0) { + final Horizontal row = GetRectRow(rectangle, 0); + // Fill first line of rect + screenSet(patternSrc, pattern, screen, row); + // Fill the rest of the rect + RepeatRow(screen, row, rectangle.height - 1); + } + } + + default void FillRect(E screenType, Rectangle rectangle, V patternSrc, int point) { + final V screen = getScreen(screenType); + if (rectangle.height > 0) { + final Horizontal row = GetRectRow(rectangle, 0); + // Fill first line of rect + screenSet(patternSrc, point, screen, row); + // Fill the rest of the rect + RepeatRow(screen, row, rectangle.height - 1); + } + } + + default void FillRect(E screenType, Rectangle rectangle, int color) { + FillRect(screenType, rectangle, (byte) color); + } + + default void FillRect(E screenType, Rectangle rectangle, byte color) { + final V screen = getScreen(screenType); + if (rectangle.height > 0) { + final V filler = convertPalettedBlock(color); + final Horizontal row = GetRectRow(rectangle, 0); + // Fill first line of rect + screenSet(filler, 0, screen, row); + // Fill the rest of the rect + RepeatRow(screen, row, rectangle.height - 1); + } + } +} \ No newline at end of file diff --git a/src/v/graphics/Relocation.java b/src/v/graphics/Relocation.java index 2c191aa..294a9d0 100644 --- a/src/v/graphics/Relocation.java +++ b/src/v/graphics/Relocation.java @@ -19,7 +19,7 @@ /** * Relocation represents a move of a fixed length of bytes/shorts/ints * from one range in screen buffer to another range of the same size - * + * * @author Good Sign */ public final class Relocation { @@ -48,4 +48,4 @@ public Relocation retarget(int source, int destination) { this.destination = destination; return this; } -} +} \ No newline at end of file diff --git a/src/v/graphics/Screens.java b/src/v/graphics/Screens.java index 2a56b72..918b874 100644 --- a/src/v/graphics/Screens.java +++ b/src/v/graphics/Screens.java @@ -1,107 +1,107 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.graphics; - -import f.Wiper; -import java.lang.reflect.Array; -import m.IRandom; -import static utils.GenericCopy.memcpy; -import static utils.GenericCopy.memset; -import v.renderers.DoomScreen; - -/** - * Screen surface library - * - * @author Good Sign - */ -public interface Screens> { - - final int SCREENS_COUNT = DoomScreen.values().length; - - V getScreen(E screenType); - - int getScalingX(); - - int getScalingY(); - - int getScreenWidth(); - - int getScreenHeight(); - - Wiper createWiper(IRandom random); - - /** - * memset-like methods for screen surfaces - */ - /** - * Will fill destPortion on the screen with color of the specified point on it - * The point argument IS NOT a color to fill, only a POINTER to the pixel on the screen - */ - default void screenSet(V screen, int point, Horizontal destination) { - memset(screen, destination.start, destination.length, screen, point, 1); - } - - /** - * Will fill destPortion on the dstScreen by scrPortion pattern from srcScreen - */ - default void screenSet(V srcScreen, Horizontal pattern, V dstScreen, Horizontal destination) { - memset(dstScreen, destination.start, destination.length, srcScreen, pattern.start, pattern.length); - } - - /** - * Will fill destPortion on the dstScreen with color of the specified point on the srcScreen - * The point argument IS NOT a color to fill, only a POINTER to the pixel on the screen - */ - default void screenSet(V srcScreen, int point, V dstScreen, Horizontal destination) { - memset(dstScreen, destination.start, destination.length, srcScreen, point, 1); - } - - /** - * Will fill destPortion on the screen with srcPortion pattern from the same screen - */ - default void screenSet(V screen, Horizontal pattern, Horizontal destination) { - memset(screen, destination.start, destination.length, screen, pattern.start, pattern.length); - } - - /** - * memcpy-like method for screen surfaces - */ - default void screenCopy(V srcScreen, V dstScreen, Relocation relocation) { - memcpy(srcScreen, relocation.source, dstScreen, relocation.destination, relocation.length); - } - - default void screenCopy(E srcScreen, E dstScreen) { - final Object dstScreenObj = getScreen(dstScreen); - memcpy(getScreen(srcScreen), 0, dstScreenObj, 0, Array.getLength(dstScreenObj)); - } - - default Plotter createPlotter(E screen) { - return new Plotter.Thin<>(getScreen(screen), getScreenWidth()); - } - - class BadRangeException extends Exception { - - private static final long serialVersionUID = 2903441181162189295L; - - public BadRangeException(String m) { - super(m); - } - - public BadRangeException() { - } - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.graphics; + +import f.Wiper; +import java.lang.reflect.Array; +import m.IRandom; +import static utils.GenericCopy.memcpy; +import static utils.GenericCopy.memset; +import v.renderers.DoomScreen; + +/** + * Screen surface library + * + * @author Good Sign + */ +public interface Screens> { + + final int SCREENS_COUNT = DoomScreen.values().length; + + V getScreen(E screenType); + + int getScalingX(); + + int getScalingY(); + + int getScreenWidth(); + + int getScreenHeight(); + + Wiper createWiper(IRandom random); + + /** + * memset-like methods for screen surfaces + */ + /** + * Will fill destPortion on the screen with color of the specified point on it + * The point argument IS NOT a color to fill, only a POINTER to the pixel on the screen + */ + default void screenSet(V screen, int point, Horizontal destination) { + memset(screen, destination.start, destination.length, screen, point, 1); + } + + /** + * Will fill destPortion on the dstScreen by scrPortion pattern from srcScreen + */ + default void screenSet(V srcScreen, Horizontal pattern, V dstScreen, Horizontal destination) { + memset(dstScreen, destination.start, destination.length, srcScreen, pattern.start, pattern.length); + } + + /** + * Will fill destPortion on the dstScreen with color of the specified point on the srcScreen + * The point argument IS NOT a color to fill, only a POINTER to the pixel on the screen + */ + default void screenSet(V srcScreen, int point, V dstScreen, Horizontal destination) { + memset(dstScreen, destination.start, destination.length, srcScreen, point, 1); + } + + /** + * Will fill destPortion on the screen with srcPortion pattern from the same screen + */ + default void screenSet(V screen, Horizontal pattern, Horizontal destination) { + memset(screen, destination.start, destination.length, screen, pattern.start, pattern.length); + } + + /** + * memcpy-like method for screen surfaces + */ + default void screenCopy(V srcScreen, V dstScreen, Relocation relocation) { + memcpy(srcScreen, relocation.source, dstScreen, relocation.destination, relocation.length); + } + + default void screenCopy(E srcScreen, E dstScreen) { + final Object dstScreenObj = getScreen(dstScreen); + memcpy(getScreen(srcScreen), 0, dstScreenObj, 0, Array.getLength(dstScreenObj)); + } + + default Plotter createPlotter(E screen) { + return new Plotter.Thin<>(getScreen(screen), getScreenWidth()); + } + + class BadRangeException extends Exception { + + private static final long serialVersionUID = 2903441181162189295L; + + public BadRangeException(String m) { + super(m); + } + + public BadRangeException() { + } + } +} \ No newline at end of file diff --git a/src/v/graphics/Wipers.java b/src/v/graphics/Wipers.java index c5abae3..ad0543d 100644 --- a/src/v/graphics/Wipers.java +++ b/src/v/graphics/Wipers.java @@ -1,7 +1,7 @@ /** * Copyright (C) 1993-1996 Id Software, Inc. * from f_wipe.c - * + * * Copyright (C) 2017 Good Sign * * This program is free software: you can redistribute it and/or modify @@ -35,7 +35,7 @@ public class Wipers implements ColorTransform, Melt { /** * They are repeated thrice for a reason - they are overloads with different arguments * - Good Sign 2017/04/06 - * + * * ASS-WIPING functions */ public enum WipeFunc { @@ -203,4 +203,4 @@ public interface WipeType { private Wipers() { } -} +} \ No newline at end of file diff --git a/src/v/renderers/BppMode.java b/src/v/renderers/BppMode.java index eba1c6b..36bf9fb 100644 --- a/src/v/renderers/BppMode.java +++ b/src/v/renderers/BppMode.java @@ -92,4 +92,4 @@ interface ScenerGen extends Function, SceneRenderer> interface RenderGen extends Function, SoftwareGraphicsSystem> { } -} +} \ No newline at end of file diff --git a/src/v/renderers/BufferedRenderer.java b/src/v/renderers/BufferedRenderer.java index 6bc4d20..440417f 100644 --- a/src/v/renderers/BufferedRenderer.java +++ b/src/v/renderers/BufferedRenderer.java @@ -59,4 +59,4 @@ public final void forcePalette() { // //Revision 1.17.2.3 2012/09/24 16:56:06 velktron //New hierarchy, less code repetition. -// +// \ No newline at end of file diff --git a/src/v/renderers/BufferedRenderer16.java b/src/v/renderers/BufferedRenderer16.java index c776bb9..d8fb8bd 100644 --- a/src/v/renderers/BufferedRenderer16.java +++ b/src/v/renderers/BufferedRenderer16.java @@ -36,7 +36,7 @@ * It ulitizes now the same parallelization as 32-bit TrueColor renderer, * becasue it allows palettes and gammas to be applied properly on post-process. * The separate LUT's are generated for this renderer - * + * * Most likely, this renderer will be the least performant. * - Good Sign 2017/04/12 */ @@ -95,7 +95,7 @@ class BufferedRenderer16 extends SoftwareParallelVideoRenderer * As we use VolatileImage that can lose its contents, it must have special care. * doWriteScreen is called in the moment, when the VolatileImage is ready and * we can copy to it and post-process - * + * * If we use incompatible display, just draw our existing BufferedImage - it would be faster */ @Override @@ -216,4 +216,4 @@ public void run() { // // Revision 1.2.2.1 2011/11/14 00:27:11 velktron // A barely functional HiColor branch. Most stuff broken. DO NOT USE -// +// \ No newline at end of file diff --git a/src/v/renderers/BufferedRenderer32.java b/src/v/renderers/BufferedRenderer32.java index 4eb221c..59b561f 100644 --- a/src/v/renderers/BufferedRenderer32.java +++ b/src/v/renderers/BufferedRenderer32.java @@ -220,4 +220,4 @@ public void run() { // // Revision 1.2.2.1 2011/11/14 00:27:11 velktron // A barely functional HiColor branch. Most stuff broken. DO NOT USE -// +// \ No newline at end of file diff --git a/src/v/renderers/DoomScreen.java b/src/v/renderers/DoomScreen.java index 890fb66..a529eaf 100644 --- a/src/v/renderers/DoomScreen.java +++ b/src/v/renderers/DoomScreen.java @@ -1,38 +1,38 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.renderers; - -import java.lang.reflect.Array; -import java.util.Arrays; -import java.util.EnumMap; -import java.util.Map; - -/** - * - * @author Good Sign - */ -public enum DoomScreen { - FG, BG, WS, WE, SB; - - @SuppressWarnings("unchecked") - static Map mapScreensToBuffers(Class bufferType, int bufferLen) { - return Arrays.stream(values()) - .collect(() -> new EnumMap<>(DoomScreen.class), - (map, screen) -> map.put(screen, (V) Array.newInstance(bufferType.getComponentType(), bufferLen)), - EnumMap::putAll); - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.renderers; + +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.Map; + +/** + * + * @author Good Sign + */ +public enum DoomScreen { + FG, BG, WS, WE, SB; + + @SuppressWarnings("unchecked") + static Map mapScreensToBuffers(Class bufferType, int bufferLen) { + return Arrays.stream(values()) + .collect(() -> new EnumMap<>(DoomScreen.class), + (map, screen) -> map.put(screen, (V) Array.newInstance(bufferType.getComponentType(), bufferLen)), + EnumMap::putAll); + } +} \ No newline at end of file diff --git a/src/v/renderers/RendererFactory.java b/src/v/renderers/RendererFactory.java index a53c655..9fbd77a 100644 --- a/src/v/renderers/RendererFactory.java +++ b/src/v/renderers/RendererFactory.java @@ -1,117 +1,117 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.renderers; - -import java.util.Objects; -import v.DoomGraphicSystem; -import v.scale.VideoScale; -import w.IWadLoader; - -/** - * Renderer choice that depends on selected (or provided through command line) BppMode - * It also ensures you create it in right order and with right components. - * - * And see - no package interface shared to public - * @author Good Sign - */ -public class RendererFactory { - - private RendererFactory() { - } - - public static Clear newBuilder() { - return new Builder<>(); - } - - private static class Builder - implements Clear, WithVideoScale, WithBppMode, WithWadLoader { - - private IWadLoader wadLoader; - private VideoScale videoScale; - private BppMode bppMode; - - @Override - public WithVideoScale setVideoScale(VideoScale videoScale) { - this.videoScale = Objects.requireNonNull(videoScale); - return this; - } - - @Override - public WithBppMode setBppMode(BppMode bppMode) { - this.bppMode = Objects.requireNonNull(bppMode); - return this; - } - - @Override - public WithWadLoader setWadLoader(IWadLoader wadLoader) { - this.wadLoader = Objects.requireNonNull(wadLoader); - return this; - } - - @Override - public DoomGraphicSystem build() { - return bppMode.graphics(this); - } - - @Override - public BppMode getBppMode() { - return bppMode; - } - - @Override - public VideoScale getVideoScale() { - return videoScale; - } - - @Override - public IWadLoader getWadLoader() { - return wadLoader; - } - } - - public interface Clear { - - WithVideoScale setVideoScale(VideoScale videoScale); - } - - public interface WithVideoScale { - - WithBppMode setBppMode(BppMode bppMode); - - VideoScale getVideoScale(); - } - - public interface WithBppMode { - - WithWadLoader setWadLoader(IWadLoader wadLoader); - - VideoScale getVideoScale(); - - BppMode getBppMode(); - } - - public interface WithWadLoader { - - DoomGraphicSystem build(); - - VideoScale getVideoScale(); - - BppMode getBppMode(); - - IWadLoader getWadLoader(); - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.renderers; + +import java.util.Objects; +import v.DoomGraphicSystem; +import v.scale.VideoScale; +import w.IWadLoader; + +/** + * Renderer choice that depends on selected (or provided through command line) BppMode + * It also ensures you create it in right order and with right components. + * + * And see - no package interface shared to public + * @author Good Sign + */ +public class RendererFactory { + + private RendererFactory() { + } + + public static Clear newBuilder() { + return new Builder<>(); + } + + private static class Builder + implements Clear, WithVideoScale, WithBppMode, WithWadLoader { + + private IWadLoader wadLoader; + private VideoScale videoScale; + private BppMode bppMode; + + @Override + public WithVideoScale setVideoScale(VideoScale videoScale) { + this.videoScale = Objects.requireNonNull(videoScale); + return this; + } + + @Override + public WithBppMode setBppMode(BppMode bppMode) { + this.bppMode = Objects.requireNonNull(bppMode); + return this; + } + + @Override + public WithWadLoader setWadLoader(IWadLoader wadLoader) { + this.wadLoader = Objects.requireNonNull(wadLoader); + return this; + } + + @Override + public DoomGraphicSystem build() { + return bppMode.graphics(this); + } + + @Override + public BppMode getBppMode() { + return bppMode; + } + + @Override + public VideoScale getVideoScale() { + return videoScale; + } + + @Override + public IWadLoader getWadLoader() { + return wadLoader; + } + } + + public interface Clear { + + WithVideoScale setVideoScale(VideoScale videoScale); + } + + public interface WithVideoScale { + + WithBppMode setBppMode(BppMode bppMode); + + VideoScale getVideoScale(); + } + + public interface WithBppMode { + + WithWadLoader setWadLoader(IWadLoader wadLoader); + + VideoScale getVideoScale(); + + BppMode getBppMode(); + } + + public interface WithWadLoader { + + DoomGraphicSystem build(); + + VideoScale getVideoScale(); + + BppMode getBppMode(); + + IWadLoader getWadLoader(); + } +} \ No newline at end of file diff --git a/src/v/renderers/SceneRendererMode.java b/src/v/renderers/SceneRendererMode.java index 576c0e8..295f459 100644 --- a/src/v/renderers/SceneRendererMode.java +++ b/src/v/renderers/SceneRendererMode.java @@ -1,118 +1,118 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.renderers; - -import doom.CommandVariable; -import doom.DoomMain; -import java.util.function.Function; -import m.Settings; -import mochadoom.Engine; -import rr.SceneRenderer; -import rr.UnifiedRenderer; -import rr.parallel.ParallelRenderer; -import rr.parallel.ParallelRenderer2; - -/** - * This class helps to choose between scene renderers - */ -public enum SceneRendererMode { - Serial(UnifiedRenderer.Indexed::new, UnifiedRenderer.HiColor::new, UnifiedRenderer.TrueColor::new), - Parallel(SceneRendererMode::Parallel_8, SceneRendererMode::Parallel_16, SceneRendererMode::Parallel_32), - Parallel2(SceneRendererMode::Parallel2_8, SceneRendererMode::Parallel2_16, SceneRendererMode::Parallel2_32); - - private static final boolean cVarSerial = Engine.getCVM().bool(CommandVariable.SERIALRENDERER); - private static final boolean cVarParallel = Engine.getCVM().present(CommandVariable.PARALLELRENDERER); - private static final boolean cVarParallel2 = Engine.getCVM().present(CommandVariable.PARALLELRENDERER2); - private static final int[] threads = cVarSerial ? null : cVarParallel - ? parseSwitchConfig(CommandVariable.PARALLELRENDERER) - : cVarParallel2 - ? parseSwitchConfig(CommandVariable.PARALLELRENDERER2) - : new int[]{2, 2, 2}; - - final SG indexedGen; - final SG hicolorGen; - final SG truecolorGen; - - private SceneRendererMode(SG indexed, SG hi, SG truecolor) { - this.indexedGen = indexed; - this.hicolorGen = hi; - this.truecolorGen = truecolor; - } - - static int[] parseSwitchConfig(CommandVariable sw) { - // Try parsing walls, or default to 1 - final int walls = Engine.getCVM().get(sw, Integer.class, 0).orElse(1); - // Try parsing floors. If wall succeeded, but floors not, it will default to 1. - final int floors = Engine.getCVM().get(sw, Integer.class, 1).orElse(1); - // In the worst case, we will use the defaults. - final int masked = Engine.getCVM().get(sw, Integer.class, 2).orElse(2); - return new int[]{walls, floors, masked}; - } - - static SceneRendererMode getMode() { - if (cVarSerial) { - /** - * Serial renderer in command line argument will override everything else - */ - return Serial; - } else if (cVarParallel) { - /** - * The second-top priority switch is parallelrenderer (not 2) command line argument - */ - return Parallel; - } else if (cVarParallel2) { - /** - * If we have parallelrenderer2 on command line, it will still override config setting - */ - return Parallel2; - } - - /** - * We dont have overrides on command line - get mode from default.cfg (or whatever) - * Set default parallelism config in this case - * TODO: make able to choose in config, but on ONE line along with scene_renderer_mode, should be tricky! - */ - return Engine.getConfig().getValue(Settings.scene_renderer_mode, SceneRendererMode.class); - } - - private static SceneRenderer Parallel_8(DoomMain DOOM) { - return new ParallelRenderer.Indexed(DOOM, threads[0], threads[1], threads[2]); - } - - private static SceneRenderer Parallel_16(DoomMain DOOM) { - return new ParallelRenderer.HiColor(DOOM, threads[0], threads[1], threads[2]); - } - - private static SceneRenderer Parallel_32(DoomMain DOOM) { - return new ParallelRenderer.TrueColor(DOOM, threads[0], threads[1], threads[2]); - } - - private static SceneRenderer Parallel2_8(DoomMain DOOM) { - return new ParallelRenderer2.Indexed(DOOM, threads[0], threads[1], threads[2]); - } - - private static SceneRenderer Parallel2_16(DoomMain DOOM) { - return new ParallelRenderer2.HiColor(DOOM, threads[0], threads[1], threads[2]); - } - - private static SceneRenderer Parallel2_32(DoomMain DOOM) { - return new ParallelRenderer2.TrueColor(DOOM, threads[0], threads[1], threads[2]); - } - - interface SG extends Function, SceneRenderer> { - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.renderers; + +import doom.CommandVariable; +import doom.DoomMain; +import java.util.function.Function; +import m.Settings; +import mochadoom.Engine; +import rr.SceneRenderer; +import rr.UnifiedRenderer; +import rr.parallel.ParallelRenderer; +import rr.parallel.ParallelRenderer2; + +/** + * This class helps to choose between scene renderers + */ +public enum SceneRendererMode { + Serial(UnifiedRenderer.Indexed::new, UnifiedRenderer.HiColor::new, UnifiedRenderer.TrueColor::new), + Parallel(SceneRendererMode::Parallel_8, SceneRendererMode::Parallel_16, SceneRendererMode::Parallel_32), + Parallel2(SceneRendererMode::Parallel2_8, SceneRendererMode::Parallel2_16, SceneRendererMode::Parallel2_32); + + private static final boolean cVarSerial = Engine.getCVM().bool(CommandVariable.SERIALRENDERER); + private static final boolean cVarParallel = Engine.getCVM().present(CommandVariable.PARALLELRENDERER); + private static final boolean cVarParallel2 = Engine.getCVM().present(CommandVariable.PARALLELRENDERER2); + private static final int[] threads = cVarSerial ? null : cVarParallel + ? parseSwitchConfig(CommandVariable.PARALLELRENDERER) + : cVarParallel2 + ? parseSwitchConfig(CommandVariable.PARALLELRENDERER2) + : new int[]{2, 2, 2}; + + final SG indexedGen; + final SG hicolorGen; + final SG truecolorGen; + + private SceneRendererMode(SG indexed, SG hi, SG truecolor) { + this.indexedGen = indexed; + this.hicolorGen = hi; + this.truecolorGen = truecolor; + } + + static int[] parseSwitchConfig(CommandVariable sw) { + // Try parsing walls, or default to 1 + final int walls = Engine.getCVM().get(sw, Integer.class, 0).orElse(1); + // Try parsing floors. If wall succeeded, but floors not, it will default to 1. + final int floors = Engine.getCVM().get(sw, Integer.class, 1).orElse(1); + // In the worst case, we will use the defaults. + final int masked = Engine.getCVM().get(sw, Integer.class, 2).orElse(2); + return new int[]{walls, floors, masked}; + } + + static SceneRendererMode getMode() { + if (cVarSerial) { + /** + * Serial renderer in command line argument will override everything else + */ + return Serial; + } else if (cVarParallel) { + /** + * The second-top priority switch is parallelrenderer (not 2) command line argument + */ + return Parallel; + } else if (cVarParallel2) { + /** + * If we have parallelrenderer2 on command line, it will still override config setting + */ + return Parallel2; + } + + /** + * We dont have overrides on command line - get mode from default.cfg (or whatever) + * Set default parallelism config in this case + * TODO: make able to choose in config, but on ONE line along with scene_renderer_mode, should be tricky! + */ + return Engine.getConfig().getValue(Settings.scene_renderer_mode, SceneRendererMode.class); + } + + private static SceneRenderer Parallel_8(DoomMain DOOM) { + return new ParallelRenderer.Indexed(DOOM, threads[0], threads[1], threads[2]); + } + + private static SceneRenderer Parallel_16(DoomMain DOOM) { + return new ParallelRenderer.HiColor(DOOM, threads[0], threads[1], threads[2]); + } + + private static SceneRenderer Parallel_32(DoomMain DOOM) { + return new ParallelRenderer.TrueColor(DOOM, threads[0], threads[1], threads[2]); + } + + private static SceneRenderer Parallel2_8(DoomMain DOOM) { + return new ParallelRenderer2.Indexed(DOOM, threads[0], threads[1], threads[2]); + } + + private static SceneRenderer Parallel2_16(DoomMain DOOM) { + return new ParallelRenderer2.HiColor(DOOM, threads[0], threads[1], threads[2]); + } + + private static SceneRenderer Parallel2_32(DoomMain DOOM) { + return new ParallelRenderer2.TrueColor(DOOM, threads[0], threads[1], threads[2]); + } + + interface SG extends Function, SceneRenderer> { + } +} \ No newline at end of file diff --git a/src/v/renderers/SoftwareGraphicsSystem.java b/src/v/renderers/SoftwareGraphicsSystem.java index 378872d..722efdb 100644 --- a/src/v/renderers/SoftwareGraphicsSystem.java +++ b/src/v/renderers/SoftwareGraphicsSystem.java @@ -1,370 +1,370 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.renderers; - -import doom.CommandVariable; -import f.Wiper; -import java.awt.Image; -import java.awt.Rectangle; -import java.awt.image.DataBuffer; -import java.awt.image.DataBufferByte; -import java.awt.image.DataBufferInt; -import java.awt.image.DataBufferUShort; -import java.util.Map; -import m.IRandom; -import m.Settings; -import mochadoom.Engine; -import rr.patch_t; -import v.DoomGraphicSystem; -import v.graphics.Blocks; -import v.graphics.Horizontal; -import v.graphics.Lines; -import v.graphics.Palettes; -import v.graphics.Patches; -import v.graphics.Plotter; -import v.graphics.Rectangles; -import v.graphics.Relocation; -import v.graphics.Wipers; -import static v.renderers.DoomScreen.FG; -import static v.renderers.DoomScreen.WE; -import static v.renderers.DoomScreen.WS; -import static v.renderers.DoomScreen.mapScreensToBuffers; -import v.scale.VideoScale; -import v.tables.GammaTables; -import v.tables.Playpal; - -/** - * A package-protected hub, concentrating together public graphics APIs - * and support default methods from their interfaces - * - * Problems: we cannot change resolution on-fly because it will require re-creating buffers, rasters, etc - * TODO: decide what needs to be reset and implement resolution change methods (flushing buffers, expanding arrays, etc) - * (dont forget to run gc!) - * - * @author Good Sign - */ -abstract class SoftwareGraphicsSystem - implements DoomGraphicSystem, Rectangles, Blocks, Patches, Lines { - - /** - * Each screen is [SCREENWIDTH*SCREENHEIGHT]; This is what the various modules (menu, automap, renderer etc.) get to - * manipulate at the pixel level. To go beyond 8 bit displays, these must be extended - */ - protected final Map screens; - protected final VideoScale vs; - protected final Class bufferType; - - /** - * They are used in HiColor and TrueColor modes and are separated from tinting and gammas - * Colormaps are now part of the base software renderer. This allows some flexibility over manipulating them. - */ - protected final V[] liteColorMaps; - protected final V palette; - - /** - * Indexed renderer changes this property often when switching gammas and palettes - * For HiColor and TrueColor renderer it may change or not, depending on compatibility of - * graphics configuration: if VolatileImage is used, this changes as soon as it may invalidate - */ - protected Image currentscreen; - - /** - * Dynamic properties: - */ - protected int width; - protected int height; - protected int bufferLength; - protected int usegamma = 0; - protected int usepalette = 0; - - /** - * @param vs video scale info - * @param playpal palette - */ - SoftwareGraphicsSystem(RendererFactory.WithWadLoader rf, final Class bufferType) { - // Defaults - this.vs = rf.getVideoScale(); - this.width = vs.getScreenWidth(); - this.height = vs.getScreenHeight(); - this.bufferType = bufferType; - this.bufferLength = width * height; - this.screens = mapScreensToBuffers(bufferType, bufferLength); - this.palette = palette(rf); - this.liteColorMaps = colormap(rf); - } - - @SuppressWarnings("unchecked") - private V palette(RendererFactory.WithWadLoader rf) { - /*final byte[] */ - playpal - = Engine.getCVM().bool(CommandVariable.GREYPAL) - ? Playpal.greypal() - : Engine.getCVM().bool(CommandVariable.NOPLAYPAL) - ? Playpal.properPlaypal(null) - : rf.getWadLoader().LoadPlaypal(); - - /** - * In Indexed mode, read PLAYPAL lump can be used directly - */ - return bufferType == byte[].class - ? (V) playpal - /** - * In HiColor or TrueColor translate PLAYPAL to real colors - */ - : bufferType == short[].class - ? (V) paletteHiColor(playpal) - : (V) paletteTrueColor(playpal); - } - - private byte[] playpal; - - @SuppressWarnings("unchecked") - private V[] colormap(RendererFactory.WithWadLoader rf) { - final boolean colormapEnabled = !Engine.getCVM().bool(CommandVariable.NOCOLORMAP) - && Engine.getConfig().equals(Settings.enable_colormap_lump, Boolean.TRUE); - - return /** - * In Indexed mode, read COLORMAP lump can be used directly - */ - bufferType == byte[].class - ? colormapEnabled - ? (V[]) rf.getWadLoader().LoadColormap() - : (V[]) BuildLightsI(paletteTrueColor(playpal)) - /** - * In HiColor or TrueColor generate colormaps with lights - */ - : bufferType == short[].class - ? colormapEnabled // HiColor, check for cfg setting and command line argument -nocolormap - ? (V[]) BuildLights15(paletteTrueColor(playpal), rf.getWadLoader().LoadColormap()) - : (V[]) BuildLights15(paletteTrueColor(playpal)) - : colormapEnabled // TrueColor, check for cfg setting and command line argument -nocolormap - ? (V[]) BuildLights24((int[]) palette, rf.getWadLoader().LoadColormap()) - : (V[]) BuildLights24((int[]) palette); - } - - /** - * Getters - */ - @Override - public final int getUsegamma() { - return usegamma; - } - - @Override - public final int getPalette() { - return usepalette; - } - - @Override - public final int getScreenHeight() { - return this.height; - } - - @Override - public final int getScreenWidth() { - return this.width; - } - - @Override - public int getScalingX() { - return vs.getScalingX(); - } - - @Override - public int getScalingY() { - return vs.getScalingY(); - } - - @Override - public final V getScreen(DoomScreen screenType) { - return screens.get(screenType); - } - - @Override - public Image getScreenImage() { - return currentscreen; - /* may be null */ } - - /** - * API route delegating - */ - @Override - public void screenCopy(V srcScreen, V dstScreen, Relocation relocation) { - Rectangles.super.screenCopy(srcScreen, dstScreen, relocation); - } - - @Override - public void screenCopy(DoomScreen srcScreen, DoomScreen dstScreen) { - Rectangles.super.screenCopy(srcScreen, dstScreen); - } - - @Override - public int getBaseColor(int color) { - return Rectangles.super.getBaseColor(color); - } - - @Override - public int point(int x, int y) { - return Rectangles.super.point(x, y); - } - - @Override - public int point(int x, int y, int width) { - return Rectangles.super.point(x, y, width); - } - - @Override - public void drawLine(Plotter plotter, int x1, int x2) { - Lines.super.drawLine(plotter, x1, x2); - } - - @Override - public void DrawPatch(DoomScreen screen, patch_t patch, int x, int y, int... flags) { - Patches.super.DrawPatch(screen, patch, x, y, flags); - } - - @Override - public void DrawPatchCentered(DoomScreen screen, patch_t patch, int y, int... flags) { - Patches.super.DrawPatchCentered(screen, patch, y, flags); - } - - @Override - public void DrawPatchCenteredScaled(DoomScreen screen, patch_t patch, VideoScale vs, int y, int... flags) { - Patches.super.DrawPatchCenteredScaled(screen, patch, vs, y, flags); - } - - @Override - public void DrawPatchScaled(DoomScreen screen, patch_t patch, VideoScale vs, int x, int y, int... flags) { - Patches.super.DrawPatchScaled(screen, patch, vs, x, y, flags); - } - - @Override - public void DrawPatchColScaled(DoomScreen screen, patch_t patch, VideoScale vs, int x, int col) { - Patches.super.DrawPatchColScaled(screen, patch, vs, x, col); - } - - @Override - public void CopyRect(DoomScreen srcScreenType, Rectangle rectangle, DoomScreen dstScreenType) { - Rectangles.super.CopyRect(srcScreenType, rectangle, dstScreenType); - } - - @Override - public void CopyRect(DoomScreen srcScreenType, Rectangle rectangle, DoomScreen dstScreenType, int dstPoint) { - Rectangles.super.CopyRect(srcScreenType, rectangle, dstScreenType, dstPoint); - } - - @Override - public void FillRect(DoomScreen screenType, Rectangle rectangle, V patternSrc, Horizontal pattern) { - Rectangles.super.FillRect(screenType, rectangle, patternSrc, pattern); - } - - @Override - public void FillRect(DoomScreen screenType, Rectangle rectangle, V patternSrc, int point) { - Rectangles.super.FillRect(screenType, rectangle, patternSrc, point); - } - - @Override - public void FillRect(DoomScreen screenType, Rectangle rectangle, int color) { - Rectangles.super.FillRect(screenType, rectangle, color); - } - - @Override - public void FillRect(DoomScreen screenType, Rectangle rectangle, byte color) { - Rectangles.super.FillRect(screenType, rectangle, color); - } - - @Override - public V ScaleBlock(V block, VideoScale vs, int width, int height) { - return Rectangles.super.ScaleBlock(block, vs, width, height); - } - - @Override - public void TileScreen(DoomScreen dstScreen, V block, Rectangle blockArea) { - Rectangles.super.TileScreen(dstScreen, block, blockArea); - } - - @Override - public void TileScreenArea(DoomScreen dstScreen, Rectangle screenArea, V block, Rectangle blockArea) { - Rectangles.super.TileScreenArea(dstScreen, screenArea, block, blockArea); - } - - @Override - public void DrawBlock(DoomScreen dstScreen, V block, Rectangle sourceArea, int destinationPoint) { - Rectangles.super.DrawBlock(dstScreen, block, sourceArea, destinationPoint); - } - - @Override - public Plotter createPlotter(DoomScreen screen) { - return DoomGraphicSystem.super.createPlotter(screen); - } - - /** - * I_SetPalette - * - * Any bit-depth specific palette manipulation is performed by the VideoRenderer. It can range from simple - * (paintjob) to complex (multiple BufferedImages with locked data bits...) ugh! - * - * In order to change palette properly, we must invalidate - * the colormap cache if any, otherwise older colormaps will persist. - * The screen must be fully updated then - * - * @param palette index (normally between 0-14). - */ - @Override - public void setPalette(int palette) { - this.usepalette = palette % Palettes.NUM_PALETTES; - this.forcePalette(); - } - - @Override - public void setUsegamma(int gamma) { - this.usegamma = gamma % GammaTables.LUT.length; - - /** - * Because of switching gamma stops powerup palette except for invlunerablity - * Settings.fixgammapalette handles the fix - */ - if (Engine.getConfig().equals(Settings.fix_gamma_palette, Boolean.FALSE)) { - this.usepalette = 0; - } - - this.forcePalette(); - } - - @Override - public V[] getColorMap() { - return this.liteColorMaps; - } - - public DataBuffer newBuffer(DoomScreen screen) { - final V buffer = screens.get(screen); - if (buffer.getClass() == int[].class) { - return new DataBufferInt((int[]) buffer, ((int[]) buffer).length); - } else if (buffer.getClass() == short[].class) { - return new DataBufferUShort((short[]) buffer, ((short[]) buffer).length); - } else if (buffer.getClass() == byte[].class) { - return new DataBufferByte((byte[]) buffer, ((byte[]) buffer).length); - } - - throw new UnsupportedOperationException(String.format("SoftwareVideoRenderer does not support %s buffers", buffer.getClass())); - } - - @Override - public Wiper createWiper(IRandom random) { - return Wipers.createWiper(random, this, WS, WE, FG); - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.renderers; + +import doom.CommandVariable; +import f.Wiper; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.DataBufferInt; +import java.awt.image.DataBufferUShort; +import java.util.Map; +import m.IRandom; +import m.Settings; +import mochadoom.Engine; +import rr.patch_t; +import v.DoomGraphicSystem; +import v.graphics.Blocks; +import v.graphics.Horizontal; +import v.graphics.Lines; +import v.graphics.Palettes; +import v.graphics.Patches; +import v.graphics.Plotter; +import v.graphics.Rectangles; +import v.graphics.Relocation; +import v.graphics.Wipers; +import static v.renderers.DoomScreen.FG; +import static v.renderers.DoomScreen.WE; +import static v.renderers.DoomScreen.WS; +import static v.renderers.DoomScreen.mapScreensToBuffers; +import v.scale.VideoScale; +import v.tables.GammaTables; +import v.tables.Playpal; + +/** + * A package-protected hub, concentrating together public graphics APIs + * and support default methods from their interfaces + * + * Problems: we cannot change resolution on-fly because it will require re-creating buffers, rasters, etc + * TODO: decide what needs to be reset and implement resolution change methods (flushing buffers, expanding arrays, etc) + * (dont forget to run gc!) + * + * @author Good Sign + */ +abstract class SoftwareGraphicsSystem + implements DoomGraphicSystem, Rectangles, Blocks, Patches, Lines { + + /** + * Each screen is [SCREENWIDTH*SCREENHEIGHT]; This is what the various modules (menu, automap, renderer etc.) get to + * manipulate at the pixel level. To go beyond 8 bit displays, these must be extended + */ + protected final Map screens; + protected final VideoScale vs; + protected final Class bufferType; + + /** + * They are used in HiColor and TrueColor modes and are separated from tinting and gammas + * Colormaps are now part of the base software renderer. This allows some flexibility over manipulating them. + */ + protected final V[] liteColorMaps; + protected final V palette; + + /** + * Indexed renderer changes this property often when switching gammas and palettes + * For HiColor and TrueColor renderer it may change or not, depending on compatibility of + * graphics configuration: if VolatileImage is used, this changes as soon as it may invalidate + */ + protected Image currentscreen; + + /** + * Dynamic properties: + */ + protected int width; + protected int height; + protected int bufferLength; + protected int usegamma = 0; + protected int usepalette = 0; + + /** + * @param vs video scale info + * @param playpal palette + */ + SoftwareGraphicsSystem(RendererFactory.WithWadLoader rf, final Class bufferType) { + // Defaults + this.vs = rf.getVideoScale(); + this.width = vs.getScreenWidth(); + this.height = vs.getScreenHeight(); + this.bufferType = bufferType; + this.bufferLength = width * height; + this.screens = mapScreensToBuffers(bufferType, bufferLength); + this.palette = palette(rf); + this.liteColorMaps = colormap(rf); + } + + @SuppressWarnings("unchecked") + private V palette(RendererFactory.WithWadLoader rf) { + /*final byte[] */ + playpal + = Engine.getCVM().bool(CommandVariable.GREYPAL) + ? Playpal.greypal() + : Engine.getCVM().bool(CommandVariable.NOPLAYPAL) + ? Playpal.properPlaypal(null) + : rf.getWadLoader().LoadPlaypal(); + + /** + * In Indexed mode, read PLAYPAL lump can be used directly + */ + return bufferType == byte[].class + ? (V) playpal + /** + * In HiColor or TrueColor translate PLAYPAL to real colors + */ + : bufferType == short[].class + ? (V) paletteHiColor(playpal) + : (V) paletteTrueColor(playpal); + } + + private byte[] playpal; + + @SuppressWarnings("unchecked") + private V[] colormap(RendererFactory.WithWadLoader rf) { + final boolean colormapEnabled = !Engine.getCVM().bool(CommandVariable.NOCOLORMAP) + && Engine.getConfig().equals(Settings.enable_colormap_lump, Boolean.TRUE); + + return /** + * In Indexed mode, read COLORMAP lump can be used directly + */ + bufferType == byte[].class + ? colormapEnabled + ? (V[]) rf.getWadLoader().LoadColormap() + : (V[]) BuildLightsI(paletteTrueColor(playpal)) + /** + * In HiColor or TrueColor generate colormaps with lights + */ + : bufferType == short[].class + ? colormapEnabled // HiColor, check for cfg setting and command line argument -nocolormap + ? (V[]) BuildLights15(paletteTrueColor(playpal), rf.getWadLoader().LoadColormap()) + : (V[]) BuildLights15(paletteTrueColor(playpal)) + : colormapEnabled // TrueColor, check for cfg setting and command line argument -nocolormap + ? (V[]) BuildLights24((int[]) palette, rf.getWadLoader().LoadColormap()) + : (V[]) BuildLights24((int[]) palette); + } + + /** + * Getters + */ + @Override + public final int getUsegamma() { + return usegamma; + } + + @Override + public final int getPalette() { + return usepalette; + } + + @Override + public final int getScreenHeight() { + return this.height; + } + + @Override + public final int getScreenWidth() { + return this.width; + } + + @Override + public int getScalingX() { + return vs.getScalingX(); + } + + @Override + public int getScalingY() { + return vs.getScalingY(); + } + + @Override + public final V getScreen(DoomScreen screenType) { + return screens.get(screenType); + } + + @Override + public Image getScreenImage() { + return currentscreen; + /* may be null */ } + + /** + * API route delegating + */ + @Override + public void screenCopy(V srcScreen, V dstScreen, Relocation relocation) { + Rectangles.super.screenCopy(srcScreen, dstScreen, relocation); + } + + @Override + public void screenCopy(DoomScreen srcScreen, DoomScreen dstScreen) { + Rectangles.super.screenCopy(srcScreen, dstScreen); + } + + @Override + public int getBaseColor(int color) { + return Rectangles.super.getBaseColor(color); + } + + @Override + public int point(int x, int y) { + return Rectangles.super.point(x, y); + } + + @Override + public int point(int x, int y, int width) { + return Rectangles.super.point(x, y, width); + } + + @Override + public void drawLine(Plotter plotter, int x1, int x2) { + Lines.super.drawLine(plotter, x1, x2); + } + + @Override + public void DrawPatch(DoomScreen screen, patch_t patch, int x, int y, int... flags) { + Patches.super.DrawPatch(screen, patch, x, y, flags); + } + + @Override + public void DrawPatchCentered(DoomScreen screen, patch_t patch, int y, int... flags) { + Patches.super.DrawPatchCentered(screen, patch, y, flags); + } + + @Override + public void DrawPatchCenteredScaled(DoomScreen screen, patch_t patch, VideoScale vs, int y, int... flags) { + Patches.super.DrawPatchCenteredScaled(screen, patch, vs, y, flags); + } + + @Override + public void DrawPatchScaled(DoomScreen screen, patch_t patch, VideoScale vs, int x, int y, int... flags) { + Patches.super.DrawPatchScaled(screen, patch, vs, x, y, flags); + } + + @Override + public void DrawPatchColScaled(DoomScreen screen, patch_t patch, VideoScale vs, int x, int col) { + Patches.super.DrawPatchColScaled(screen, patch, vs, x, col); + } + + @Override + public void CopyRect(DoomScreen srcScreenType, Rectangle rectangle, DoomScreen dstScreenType) { + Rectangles.super.CopyRect(srcScreenType, rectangle, dstScreenType); + } + + @Override + public void CopyRect(DoomScreen srcScreenType, Rectangle rectangle, DoomScreen dstScreenType, int dstPoint) { + Rectangles.super.CopyRect(srcScreenType, rectangle, dstScreenType, dstPoint); + } + + @Override + public void FillRect(DoomScreen screenType, Rectangle rectangle, V patternSrc, Horizontal pattern) { + Rectangles.super.FillRect(screenType, rectangle, patternSrc, pattern); + } + + @Override + public void FillRect(DoomScreen screenType, Rectangle rectangle, V patternSrc, int point) { + Rectangles.super.FillRect(screenType, rectangle, patternSrc, point); + } + + @Override + public void FillRect(DoomScreen screenType, Rectangle rectangle, int color) { + Rectangles.super.FillRect(screenType, rectangle, color); + } + + @Override + public void FillRect(DoomScreen screenType, Rectangle rectangle, byte color) { + Rectangles.super.FillRect(screenType, rectangle, color); + } + + @Override + public V ScaleBlock(V block, VideoScale vs, int width, int height) { + return Rectangles.super.ScaleBlock(block, vs, width, height); + } + + @Override + public void TileScreen(DoomScreen dstScreen, V block, Rectangle blockArea) { + Rectangles.super.TileScreen(dstScreen, block, blockArea); + } + + @Override + public void TileScreenArea(DoomScreen dstScreen, Rectangle screenArea, V block, Rectangle blockArea) { + Rectangles.super.TileScreenArea(dstScreen, screenArea, block, blockArea); + } + + @Override + public void DrawBlock(DoomScreen dstScreen, V block, Rectangle sourceArea, int destinationPoint) { + Rectangles.super.DrawBlock(dstScreen, block, sourceArea, destinationPoint); + } + + @Override + public Plotter createPlotter(DoomScreen screen) { + return DoomGraphicSystem.super.createPlotter(screen); + } + + /** + * I_SetPalette + * + * Any bit-depth specific palette manipulation is performed by the VideoRenderer. It can range from simple + * (paintjob) to complex (multiple BufferedImages with locked data bits...) ugh! + * + * In order to change palette properly, we must invalidate + * the colormap cache if any, otherwise older colormaps will persist. + * The screen must be fully updated then + * + * @param palette index (normally between 0-14). + */ + @Override + public void setPalette(int palette) { + this.usepalette = palette % Palettes.NUM_PALETTES; + this.forcePalette(); + } + + @Override + public void setUsegamma(int gamma) { + this.usegamma = gamma % GammaTables.LUT.length; + + /** + * Because of switching gamma stops powerup palette except for invlunerablity + * Settings.fixgammapalette handles the fix + */ + if (Engine.getConfig().equals(Settings.fix_gamma_palette, Boolean.FALSE)) { + this.usepalette = 0; + } + + this.forcePalette(); + } + + @Override + public V[] getColorMap() { + return this.liteColorMaps; + } + + public DataBuffer newBuffer(DoomScreen screen) { + final V buffer = screens.get(screen); + if (buffer.getClass() == int[].class) { + return new DataBufferInt((int[]) buffer, ((int[]) buffer).length); + } else if (buffer.getClass() == short[].class) { + return new DataBufferUShort((short[]) buffer, ((short[]) buffer).length); + } else if (buffer.getClass() == byte[].class) { + return new DataBufferByte((byte[]) buffer, ((byte[]) buffer).length); + } + + throw new UnsupportedOperationException(String.format("SoftwareVideoRenderer does not support %s buffers", buffer.getClass())); + } + + @Override + public Wiper createWiper(IRandom random) { + return Wipers.createWiper(random, this, WS, WE, FG); + } +} \ No newline at end of file diff --git a/src/v/renderers/SoftwareIndexedVideoRenderer.java b/src/v/renderers/SoftwareIndexedVideoRenderer.java index 4199546..d9501eb 100644 --- a/src/v/renderers/SoftwareIndexedVideoRenderer.java +++ b/src/v/renderers/SoftwareIndexedVideoRenderer.java @@ -68,4 +68,4 @@ public boolean writeScreenShot(String name, DoomScreen screen) { MenuMisc.WritePNGfile(name, screens.get(screen), width, height, cmaps[usegamma][usepalette]); return true; } -} +} \ No newline at end of file diff --git a/src/v/renderers/SoftwareParallelVideoRenderer.java b/src/v/renderers/SoftwareParallelVideoRenderer.java index 3257a0d..1ed6171 100644 --- a/src/v/renderers/SoftwareParallelVideoRenderer.java +++ b/src/v/renderers/SoftwareParallelVideoRenderer.java @@ -31,7 +31,7 @@ /** * Base for HiColor and TrueColor parallel renderers - * + * * @author Good Sign * @author velktron */ @@ -69,7 +69,7 @@ static boolean checkConfigurationTruecolor() { /** * We do not need to clear caches anymore - pallettes are applied on post-process * - Good Sign 2017/04/12 - * + * * MEGA HACK FOR SUPER-8BIT MODES */ protected final HashMap colcache = new HashMap<>(); @@ -135,4 +135,4 @@ public V convertPalettedBlock(byte... data) { } return (V) (isShort ? new short[]{(short) getBaseColor(data[0])} : new int[]{getBaseColor(data[0])}); } -} +} \ No newline at end of file diff --git a/src/v/scale/VideoScale.java b/src/v/scale/VideoScale.java index 7341367..c5bcec7 100644 --- a/src/v/scale/VideoScale.java +++ b/src/v/scale/VideoScale.java @@ -64,4 +64,4 @@ public interface VideoScale { * @return */ boolean changed(); -} +} \ No newline at end of file diff --git a/src/v/scale/VideoScaleInfo.java b/src/v/scale/VideoScaleInfo.java index ea217de..c4b1e81 100644 --- a/src/v/scale/VideoScaleInfo.java +++ b/src/v/scale/VideoScaleInfo.java @@ -28,8 +28,8 @@ class VideoScaleInfo implements VideoScale { /** Scale is intended as a multiple of the base resolution, 320 x 200. * If changing the ratio is also desired, then keep in mind that * the base width is always considered fixed, while the base height - * is not. - * + * is not. + * * @param scale */ public VideoScaleInfo(float scale) { @@ -45,9 +45,9 @@ public VideoScaleInfo(float scale) { * that there are maximum width and height limits to take into account, * and that scaling of graphics etc. will be rather problematic. Default * ratio is 0.625, 0.75 will give a nice 4:3 ratio. - * + * * TODO: pretty lame... - * + * * @param scale * @param ratio */ @@ -96,4 +96,4 @@ public float getScreenMul() { return scale; } -} +} \ No newline at end of file diff --git a/src/v/scale/VisualSettings.java b/src/v/scale/VisualSettings.java index 504e619..ff4d71a 100644 --- a/src/v/scale/VisualSettings.java +++ b/src/v/scale/VisualSettings.java @@ -87,4 +87,4 @@ public final static VideoScale parse(CVarManager CVM, ConfigManager CM) { // In all other cases... return DEFAULT_SCALE; } -} +} \ No newline at end of file diff --git a/src/v/tables/BlurryTable.java b/src/v/tables/BlurryTable.java index d32d39c..5a7c903 100644 --- a/src/v/tables/BlurryTable.java +++ b/src/v/tables/BlurryTable.java @@ -1,228 +1,228 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.tables; - -import java.util.TreeMap; -import m.Settings; -import mochadoom.Engine; -import v.graphics.Colors; -import static v.graphics.Lights.COLORMAP_BLURRY; -import static v.graphics.Lights.COLORMAP_FIXED; -import static v.graphics.Palettes.PAL_NUM_COLORS; - -/** - * Colormap-friendly vanilla-like BlurryMap for HiColor && TrueColor modes - * (though it shares plain "BLURRYMAP" for Indexed too) - * - * DOOM's colormap #6 was deciphered to be actually applying greyscale averaging filter. - * So, the vanilla effect is something like "n% darker and greyscale", where n% varies - * I think I've succeeded in replicating it for real color modes - * - Good Sign 2017/04/15 - * - * Now should be 100%, I've accounted for shift of number of generated lights for 24 bit color - * - * @author Good Sign - */ -public class BlurryTable implements FuzzMix, Colors { - - /** - * Indexed LUT, e.g. classic "BLURRYMAP" (unaffected) - */ - private final byte[] LUT_idx; - - private final byte[] LUT_r8; - private final byte[] LUT_g8; - private final byte[] LUT_b8; - private final byte[] LUT_a8; - - private final byte[] LUT_r5; - private final byte[] LUT_g5; - private final byte[] LUT_b5; - - private final boolean semiTranslucent = Engine.getConfig().equals(Settings.semi_translucent_fuzz, Boolean.TRUE); - private final boolean fuzzMix = Engine.getConfig().equals(Settings.fuzz_mix, Boolean.TRUE); - - /** - * Only support indexed "BLURRYMAP" with indexed colorMap - * @param colorMap - */ - public BlurryTable(byte[][] colorMap) { - this.LUT_b5 = null; - this.LUT_g5 = null; - this.LUT_r5 = null; - this.LUT_b8 = null; - this.LUT_g8 = null; - this.LUT_r8 = null; - this.LUT_a8 = null; - this.LUT_idx = colorMap[COLORMAP_BLURRY]; - } - - /** - * HiColor BlurryTable will only support int[][] colormap - * @param liteColorMaps - */ - public BlurryTable(short[][] liteColorMaps) { - this.LUT_b5 = new byte[32]; - this.LUT_g5 = new byte[32]; - this.LUT_r5 = new byte[32]; - this.LUT_b8 = null; - this.LUT_g8 = null; - this.LUT_r8 = null; - this.LUT_a8 = null; - this.LUT_idx = null; - - /** - * Prepare to sort colors - we will be using the ratio that is next close to apply for current color - */ - final TreeMap sortedRatios = new TreeMap<>(this::CompareColors555); - - for (int i = 0; i < PAL_NUM_COLORS; ++i) { - // first get "BLURRYMAP" color components - final int[] blurryColor = getRGB555(liteColorMaps[COLORMAP_BLURRY][i], new int[3]); - // then gen color components from unmodified (fixed) palette - final int[] fixedColor = getRGB555(liteColorMaps[COLORMAP_FIXED][i], new int[3]); - // make grayscale avegrage (or what you set in cfg) colors out of these components - final short avgColor = GreyscaleFilter.grey555(blurryColor[0], blurryColor[1], blurryColor[2]); - final short avgOrig = GreyscaleFilter.grey555(fixedColor[0], fixedColor[1], fixedColor[2]); - // get grayscale color components - final int[] blurryAvg = getRGB555(avgColor, new int[3]); - final int[] fixedAvg = getRGB555(avgOrig, new int[3]); - - // now, calculate the ratios - final float ratioR = fixedAvg[0] > 0 ? blurryAvg[0] / (float) fixedAvg[0] : 0.0f, - ratioG = fixedAvg[1] > 0 ? blurryAvg[1] / (float) fixedAvg[1] : 0.0f, - ratioB = fixedAvg[2] > 0 ? blurryAvg[2] / (float) fixedAvg[2] : 0.0f; - - // best ratio is weighted towards red and blue, but should not be multiplied or it will be too dark - final float bestRatio = GreyscaleFilter.component(ratioR, ratioG, ratioB);//ratioR * ratioR * ratioG * ratioB * ratioB; - - // associate normal color from colormaps avegrage with this ratio - sortedRatios.put(avgOrig, bestRatio); - } - - // now we have built our sorted maps, time to calculate color component mappings - for (int i = 0; i <= 0x1F; ++i) { - final short rgb555 = toRGB555(i, i, i); - // now the best part - approximation. we just pick the closest grayscale color ratio - final float ratio = sortedRatios.floorEntry(rgb555).getValue(); - LUT_r5[i] = LUT_g5[i] = LUT_b5[i] = (byte) ((int) (i * ratio) & 0x1F); - } - // all done - } - - /** - * TrueColor BlurryTable will only support int[][] colormap - * @param liteColorMaps - */ - public BlurryTable(int[][] liteColorMaps) { - this.LUT_b5 = null; - this.LUT_g5 = null; - this.LUT_r5 = null; - this.LUT_a8 = new byte[256]; - this.LUT_b8 = new byte[256]; - this.LUT_g8 = new byte[256]; - this.LUT_r8 = new byte[256]; - this.LUT_idx = null; - - /** - * Prepare to sort colors - we will be using the ratio that is next close to apply for current color - */ - final TreeMap sortedRatios = new TreeMap<>(this::CompareColors888); - - for (int i = 0; i < PAL_NUM_COLORS; ++i) { - // first get "BLURRYMAP" color components. 24 bit lighting is richer (256 vs 32) so we need to multiply - final int[] blurryColor = getRGB888(liteColorMaps[COLORMAP_BLURRY << 3][i], new int[3]); - // then gen color components from unmodified (fixed) palette - final int[] fixedColor = getRGB888(liteColorMaps[COLORMAP_FIXED][i], new int[3]); - // make grayscale avegrage (or what you set in cfg) colors out of these components - final int avgColor = GreyscaleFilter.grey888(blurryColor[0], blurryColor[1], blurryColor[2]); - final int avgOrig = GreyscaleFilter.grey888(fixedColor[0], fixedColor[1], fixedColor[2]); - // get grayscale color components - final int[] blurryAvg = getRGB888(avgColor, new int[3]); - final int[] fixedAvg = getRGB888(avgOrig, new int[3]); - - // now, calculate the ratios - final float ratioR = fixedAvg[0] > 0 ? blurryAvg[0] / (float) fixedAvg[0] : 0.0f, - ratioG = fixedAvg[1] > 0 ? blurryAvg[1] / (float) fixedAvg[1] : 0.0f, - ratioB = fixedAvg[2] > 0 ? blurryAvg[2] / (float) fixedAvg[2] : 0.0f; - - // weight ratio towards red and blue and multiply to make darker - final float bestRatio = GreyscaleFilter.component(ratioR, ratioG, ratioB);//ratioR * ratioR * ratioG * ratioB * ratioB; - - // associate normal color from colormaps avegrage with this ratio - sortedRatios.put(avgOrig, bestRatio); - } - - // now we have built our sorted maps, time to calculate color component mappings - for (int i = 0; i <= 0xFF; ++i) { - final int rgb = toRGB888(i, i, i); - // now the best part - approximation. we just pick the closest grayscale color ratio - final float ratio = sortedRatios.floorEntry(rgb).getValue(); - LUT_r8[i] = LUT_g8[i] = LUT_b8[i] = (byte) ((int) (i * ratio) & 0xFF); - // for alpha it is different: we use the same ratio as for greyscale color, but the base alpha is min 50% - LUT_a8[i] = (byte) (ratio * (Math.max(i, 0x7F) / (float) 0xFF) * 0xFF); - } - // all done - } - - /** - * For indexes - */ - public byte computePixel(byte pixel) { - return LUT_idx[pixel & 0xFF]; - } - - /** - * For HiColor pixels - */ - public short computePixel(short pixel) { - if (fuzzMix) { // if blurry feature enabled, everything else does not apply - return fuzzMixHi(pixel); - } - final int rgb[] = getRGB555(pixel, new int[4]); - return toRGB555(LUT_r5[rgb[0]], LUT_g5[rgb[1]], LUT_b5[rgb[2]]); - } - - /** - * In high detail mode in AlphaTrueColor color mode will compute special greyscale-to-ratio translucency - */ - public int computePixel(int pixel) { - if (fuzzMix) { // if blurry feature enabled, everything else does not apply - return fuzzMixTrue(pixel); - } - - if (!semiTranslucent) { - return computePixelFast(pixel); - } - final int argb[] = getARGB8888(pixel, new int[4]); - // the alpha from previous frame would stay until the pixel will not belong to FUZZ holder - argb[0] = Math.min(argb[0], GreyscaleFilter.component(argb[1], argb[2], argb[3])); - return toARGB8888(LUT_a8[argb[0]], LUT_r8[argb[1]], LUT_g8[argb[2]], LUT_b8[argb[3]]); - } - - /** - * For low detail mode, do not compute translucency - */ - public int computePixelFast(int pixel) { - if (fuzzMix) { // if blurry feature enabled, everything else does not apply - return fuzzMixTrueLow(pixel); - } - - final int rgb[] = getRGB888(pixel, new int[3]); - return 0xFF000000 + (toRGB888(LUT_r8[rgb[0]], LUT_g8[rgb[1]], LUT_b8[rgb[2]]) & 0xFFFFFF); - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.tables; + +import java.util.TreeMap; +import m.Settings; +import mochadoom.Engine; +import v.graphics.Colors; +import static v.graphics.Lights.COLORMAP_BLURRY; +import static v.graphics.Lights.COLORMAP_FIXED; +import static v.graphics.Palettes.PAL_NUM_COLORS; + +/** + * Colormap-friendly vanilla-like BlurryMap for HiColor && TrueColor modes + * (though it shares plain "BLURRYMAP" for Indexed too) + * + * DOOM's colormap #6 was deciphered to be actually applying greyscale averaging filter. + * So, the vanilla effect is something like "n% darker and greyscale", where n% varies + * I think I've succeeded in replicating it for real color modes + * - Good Sign 2017/04/15 + * + * Now should be 100%, I've accounted for shift of number of generated lights for 24 bit color + * + * @author Good Sign + */ +public class BlurryTable implements FuzzMix, Colors { + + /** + * Indexed LUT, e.g. classic "BLURRYMAP" (unaffected) + */ + private final byte[] LUT_idx; + + private final byte[] LUT_r8; + private final byte[] LUT_g8; + private final byte[] LUT_b8; + private final byte[] LUT_a8; + + private final byte[] LUT_r5; + private final byte[] LUT_g5; + private final byte[] LUT_b5; + + private final boolean semiTranslucent = Engine.getConfig().equals(Settings.semi_translucent_fuzz, Boolean.TRUE); + private final boolean fuzzMix = Engine.getConfig().equals(Settings.fuzz_mix, Boolean.TRUE); + + /** + * Only support indexed "BLURRYMAP" with indexed colorMap + * @param colorMap + */ + public BlurryTable(byte[][] colorMap) { + this.LUT_b5 = null; + this.LUT_g5 = null; + this.LUT_r5 = null; + this.LUT_b8 = null; + this.LUT_g8 = null; + this.LUT_r8 = null; + this.LUT_a8 = null; + this.LUT_idx = colorMap[COLORMAP_BLURRY]; + } + + /** + * HiColor BlurryTable will only support int[][] colormap + * @param liteColorMaps + */ + public BlurryTable(short[][] liteColorMaps) { + this.LUT_b5 = new byte[32]; + this.LUT_g5 = new byte[32]; + this.LUT_r5 = new byte[32]; + this.LUT_b8 = null; + this.LUT_g8 = null; + this.LUT_r8 = null; + this.LUT_a8 = null; + this.LUT_idx = null; + + /** + * Prepare to sort colors - we will be using the ratio that is next close to apply for current color + */ + final TreeMap sortedRatios = new TreeMap<>(this::CompareColors555); + + for (int i = 0; i < PAL_NUM_COLORS; ++i) { + // first get "BLURRYMAP" color components + final int[] blurryColor = getRGB555(liteColorMaps[COLORMAP_BLURRY][i], new int[3]); + // then gen color components from unmodified (fixed) palette + final int[] fixedColor = getRGB555(liteColorMaps[COLORMAP_FIXED][i], new int[3]); + // make grayscale avegrage (or what you set in cfg) colors out of these components + final short avgColor = GreyscaleFilter.grey555(blurryColor[0], blurryColor[1], blurryColor[2]); + final short avgOrig = GreyscaleFilter.grey555(fixedColor[0], fixedColor[1], fixedColor[2]); + // get grayscale color components + final int[] blurryAvg = getRGB555(avgColor, new int[3]); + final int[] fixedAvg = getRGB555(avgOrig, new int[3]); + + // now, calculate the ratios + final float ratioR = fixedAvg[0] > 0 ? blurryAvg[0] / (float) fixedAvg[0] : 0.0f, + ratioG = fixedAvg[1] > 0 ? blurryAvg[1] / (float) fixedAvg[1] : 0.0f, + ratioB = fixedAvg[2] > 0 ? blurryAvg[2] / (float) fixedAvg[2] : 0.0f; + + // best ratio is weighted towards red and blue, but should not be multiplied or it will be too dark + final float bestRatio = GreyscaleFilter.component(ratioR, ratioG, ratioB);//ratioR * ratioR * ratioG * ratioB * ratioB; + + // associate normal color from colormaps avegrage with this ratio + sortedRatios.put(avgOrig, bestRatio); + } + + // now we have built our sorted maps, time to calculate color component mappings + for (int i = 0; i <= 0x1F; ++i) { + final short rgb555 = toRGB555(i, i, i); + // now the best part - approximation. we just pick the closest grayscale color ratio + final float ratio = sortedRatios.floorEntry(rgb555).getValue(); + LUT_r5[i] = LUT_g5[i] = LUT_b5[i] = (byte) ((int) (i * ratio) & 0x1F); + } + // all done + } + + /** + * TrueColor BlurryTable will only support int[][] colormap + * @param liteColorMaps + */ + public BlurryTable(int[][] liteColorMaps) { + this.LUT_b5 = null; + this.LUT_g5 = null; + this.LUT_r5 = null; + this.LUT_a8 = new byte[256]; + this.LUT_b8 = new byte[256]; + this.LUT_g8 = new byte[256]; + this.LUT_r8 = new byte[256]; + this.LUT_idx = null; + + /** + * Prepare to sort colors - we will be using the ratio that is next close to apply for current color + */ + final TreeMap sortedRatios = new TreeMap<>(this::CompareColors888); + + for (int i = 0; i < PAL_NUM_COLORS; ++i) { + // first get "BLURRYMAP" color components. 24 bit lighting is richer (256 vs 32) so we need to multiply + final int[] blurryColor = getRGB888(liteColorMaps[COLORMAP_BLURRY << 3][i], new int[3]); + // then gen color components from unmodified (fixed) palette + final int[] fixedColor = getRGB888(liteColorMaps[COLORMAP_FIXED][i], new int[3]); + // make grayscale avegrage (or what you set in cfg) colors out of these components + final int avgColor = GreyscaleFilter.grey888(blurryColor[0], blurryColor[1], blurryColor[2]); + final int avgOrig = GreyscaleFilter.grey888(fixedColor[0], fixedColor[1], fixedColor[2]); + // get grayscale color components + final int[] blurryAvg = getRGB888(avgColor, new int[3]); + final int[] fixedAvg = getRGB888(avgOrig, new int[3]); + + // now, calculate the ratios + final float ratioR = fixedAvg[0] > 0 ? blurryAvg[0] / (float) fixedAvg[0] : 0.0f, + ratioG = fixedAvg[1] > 0 ? blurryAvg[1] / (float) fixedAvg[1] : 0.0f, + ratioB = fixedAvg[2] > 0 ? blurryAvg[2] / (float) fixedAvg[2] : 0.0f; + + // weight ratio towards red and blue and multiply to make darker + final float bestRatio = GreyscaleFilter.component(ratioR, ratioG, ratioB);//ratioR * ratioR * ratioG * ratioB * ratioB; + + // associate normal color from colormaps avegrage with this ratio + sortedRatios.put(avgOrig, bestRatio); + } + + // now we have built our sorted maps, time to calculate color component mappings + for (int i = 0; i <= 0xFF; ++i) { + final int rgb = toRGB888(i, i, i); + // now the best part - approximation. we just pick the closest grayscale color ratio + final float ratio = sortedRatios.floorEntry(rgb).getValue(); + LUT_r8[i] = LUT_g8[i] = LUT_b8[i] = (byte) ((int) (i * ratio) & 0xFF); + // for alpha it is different: we use the same ratio as for greyscale color, but the base alpha is min 50% + LUT_a8[i] = (byte) (ratio * (Math.max(i, 0x7F) / (float) 0xFF) * 0xFF); + } + // all done + } + + /** + * For indexes + */ + public byte computePixel(byte pixel) { + return LUT_idx[pixel & 0xFF]; + } + + /** + * For HiColor pixels + */ + public short computePixel(short pixel) { + if (fuzzMix) { // if blurry feature enabled, everything else does not apply + return fuzzMixHi(pixel); + } + final int rgb[] = getRGB555(pixel, new int[4]); + return toRGB555(LUT_r5[rgb[0]], LUT_g5[rgb[1]], LUT_b5[rgb[2]]); + } + + /** + * In high detail mode in AlphaTrueColor color mode will compute special greyscale-to-ratio translucency + */ + public int computePixel(int pixel) { + if (fuzzMix) { // if blurry feature enabled, everything else does not apply + return fuzzMixTrue(pixel); + } + + if (!semiTranslucent) { + return computePixelFast(pixel); + } + final int argb[] = getARGB8888(pixel, new int[4]); + // the alpha from previous frame would stay until the pixel will not belong to FUZZ holder + argb[0] = Math.min(argb[0], GreyscaleFilter.component(argb[1], argb[2], argb[3])); + return toARGB8888(LUT_a8[argb[0]], LUT_r8[argb[1]], LUT_g8[argb[2]], LUT_b8[argb[3]]); + } + + /** + * For low detail mode, do not compute translucency + */ + public int computePixelFast(int pixel) { + if (fuzzMix) { // if blurry feature enabled, everything else does not apply + return fuzzMixTrueLow(pixel); + } + + final int rgb[] = getRGB888(pixel, new int[3]); + return 0xFF000000 + (toRGB888(LUT_r8[rgb[0]], LUT_g8[rgb[1]], LUT_b8[rgb[2]]) & 0xFFFFFF); + } +} \ No newline at end of file diff --git a/src/v/tables/ColorTint.java b/src/v/tables/ColorTint.java index 3744c0e..7ba29c8 100644 --- a/src/v/tables/ColorTint.java +++ b/src/v/tables/ColorTint.java @@ -25,7 +25,7 @@ * I think they may be invalid if the game uses custom COLORMAP, so we need an ability * to regenerate them when loading such lump. * Thus, it is an Enum... but only almost. - * + * * Added new LUT's for HiColor and TrueColor renderers * They are capable of tinting and gamma correcting full direct colors(not indexed) on the fly * - Good Sign @@ -146,4 +146,4 @@ public final int tintRed8(int red8) { public final int tintRed5(int red5) { return Math.min((int) (red5 * purepart + r5), 0x1F); } -} +} \ No newline at end of file diff --git a/src/v/tables/FuzzMix.java b/src/v/tables/FuzzMix.java index 5c6d5b4..d755a7c 100644 --- a/src/v/tables/FuzzMix.java +++ b/src/v/tables/FuzzMix.java @@ -1,81 +1,81 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.tables; - -/** - * FuzzMix: Unique feature by Maes for HiColor detailed mode - * This should be preserved, but I've moved it to appropriate place - * Option to enable the feature in cfg: fuzz_mix - * - * Note: the TrueColor alpha half-brite will only work - * properly with cfg: color_depth AlphaTrueColor also set - * - * Made it an interface, it is as easy to apply to anything as setting pixel - * - Good Sign 2017/04/16 - * - * @author velktron - */ -public interface FuzzMix { - - /** - * Was used by: - * R_DrawFuzzColumn.HiColor - * R_DrawFuzzColumnLow.HiColor - * - * Now used by BlurryTable::computePixel - * only if the option fuzz_mix enabled - */ - default short fuzzMixHi(short rgb) { - // super-fast half-brite trick - // 3DEF and >> 1: ok hue, but too dark - // 7BDE, no shift: good compromise - // 739C, no shift: results in too obvious tinting. - return (short) (rgb & 0x7BDE); - } - - /** - * Was used by: - * R_DrawFuzzColumn.TrueColor - * - * Now used by BlurryTable::computePixel - * only if the option fuzz_mix enabled - * - * AX: This is what makes it blurry - */ - default int fuzzMixTrue(int rgb) { - // Proper half-brite alpha! - return rgb & 0x10FFFFFF; - } - - /** - * Was used by: - * R_DrawFuzzColumnLow.TrueColor - * - * Now used by BlurryTable::computePixel - * only if the option fuzz_mix enabled - * - * AX: This is what made it dark and ugly - */ - default int fuzzMixTrueLow(int rgb) { - // super-fast half-brite trick - // 3DEF and >> 1: ok hue, but too dark - // FF7C7C7C, no shift: good compromise - // FF707070, no shift: results in too obvious tinting. - - return (rgb >> 1) & 0xFF7F7F7F; - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.tables; + +/** + * FuzzMix: Unique feature by Maes for HiColor detailed mode + * This should be preserved, but I've moved it to appropriate place + * Option to enable the feature in cfg: fuzz_mix + * + * Note: the TrueColor alpha half-brite will only work + * properly with cfg: color_depth AlphaTrueColor also set + * + * Made it an interface, it is as easy to apply to anything as setting pixel + * - Good Sign 2017/04/16 + * + * @author velktron + */ +public interface FuzzMix { + + /** + * Was used by: + * R_DrawFuzzColumn.HiColor + * R_DrawFuzzColumnLow.HiColor + * + * Now used by BlurryTable::computePixel + * only if the option fuzz_mix enabled + */ + default short fuzzMixHi(short rgb) { + // super-fast half-brite trick + // 3DEF and >> 1: ok hue, but too dark + // 7BDE, no shift: good compromise + // 739C, no shift: results in too obvious tinting. + return (short) (rgb & 0x7BDE); + } + + /** + * Was used by: + * R_DrawFuzzColumn.TrueColor + * + * Now used by BlurryTable::computePixel + * only if the option fuzz_mix enabled + * + * AX: This is what makes it blurry + */ + default int fuzzMixTrue(int rgb) { + // Proper half-brite alpha! + return rgb & 0x10FFFFFF; + } + + /** + * Was used by: + * R_DrawFuzzColumnLow.TrueColor + * + * Now used by BlurryTable::computePixel + * only if the option fuzz_mix enabled + * + * AX: This is what made it dark and ugly + */ + default int fuzzMixTrueLow(int rgb) { + // super-fast half-brite trick + // 3DEF and >> 1: ok hue, but too dark + // FF7C7C7C, no shift: good compromise + // FF707070, no shift: results in too obvious tinting. + + return (rgb >> 1) & 0xFF7F7F7F; + } +} \ No newline at end of file diff --git a/src/v/tables/GammaTables.java b/src/v/tables/GammaTables.java index 6f74df7..bc461ec 100644 --- a/src/v/tables/GammaTables.java +++ b/src/v/tables/GammaTables.java @@ -1,118 +1,118 @@ -// -// Copyright (C) 1993-1996 Id Software, Inc. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// DESCRIPTION: -// Gamma correction LUT stuff. -// Functions to draw patches (by post) directly to screen. -// Functions to blit a block to the screen. -// -// from v_video.c -// -package v.tables; - -import m.Settings; -import mochadoom.Engine; - -public class GammaTables { - - // Now where did these came from? - public final static int[][] LUT - = { - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, - 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, - 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, - 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, - 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, - 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, - 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, - 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, - 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, - 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255}, - {2, 4, 5, 7, 8, 10, 11, 12, 14, 15, 16, 18, 19, 20, 21, 23, 24, 25, 26, 27, 29, 30, 31, - 32, 33, 34, 36, 37, 38, 39, 40, 41, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 69, 70, 71, 72, 73, 74, 75, 76, 77, - 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, - 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, - 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 129, - 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, - 146, 147, 148, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, - 161, 162, 163, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, - 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 186, 187, 188, 189, - 190, 191, 192, 193, 194, 195, 196, 196, 197, 198, 199, 200, 201, 202, 203, 204, - 205, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 214, 215, 216, 217, 218, - 219, 220, 221, 222, 222, 223, 224, 225, 226, 227, 228, 229, 230, 230, 231, 232, - 233, 234, 235, 236, 237, 237, 238, 239, 240, 241, 242, 243, 244, 245, 245, 246, - 247, 248, 249, 250, 251, 252, 252, 253, 254, 255}, - {4, 7, 9, 11, 13, 15, 17, 19, 21, 22, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 40, 42, - 43, 45, 46, 47, 48, 50, 51, 52, 54, 55, 56, 57, 59, 60, 61, 62, 63, 65, 66, 67, 68, 69, - 70, 72, 73, 74, 75, 76, 77, 78, 79, 80, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, - 94, 95, 96, 97, 98, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - 113, 114, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, - 129, 130, 131, 132, 133, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, - 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 153, 154, 155, 156, 157, 158, 159, - 160, 160, 161, 162, 163, 164, 165, 166, 166, 167, 168, 169, 170, 171, 172, 172, 173, - 174, 175, 176, 177, 178, 178, 179, 180, 181, 182, 183, 183, 184, 185, 186, 187, 188, - 188, 189, 190, 191, 192, 193, 193, 194, 195, 196, 197, 197, 198, 199, 200, 201, 201, - 202, 203, 204, 205, 206, 206, 207, 208, 209, 210, 210, 211, 212, 213, 213, 214, 215, - 216, 217, 217, 218, 219, 220, 221, 221, 222, 223, 224, 224, 225, 226, 227, 228, 228, - 229, 230, 231, 231, 232, 233, 234, 235, 235, 236, 237, 238, 238, 239, 240, 241, 241, - 242, 243, 244, 244, 245, 246, 247, 247, 248, 249, 250, 251, 251, 252, 253, 254, 254, - 255}, - {8, 12, 16, 19, 22, 24, 27, 29, 31, 34, 36, 38, 40, 41, 43, 45, 47, 49, 50, 52, 53, 55, - 57, 58, 60, 61, 63, 64, 65, 67, 68, 70, 71, 72, 74, 75, 76, 77, 79, 80, 81, 82, 84, 85, - 86, 87, 88, 90, 91, 92, 93, 94, 95, 96, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, - 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, - 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 135, 136, 137, 138, 139, 140, - 141, 142, 143, 143, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, - 155, 156, 157, 158, 159, 160, 160, 161, 162, 163, 164, 165, 165, 166, 167, 168, 169, - 169, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 179, 180, 180, 181, 182, - 183, 183, 184, 185, 186, 186, 187, 188, 189, 189, 190, 191, 192, 192, 193, 194, 195, - 195, 196, 197, 197, 198, 199, 200, 200, 201, 202, 202, 203, 204, 205, 205, 206, 207, - 207, 208, 209, 210, 210, 211, 212, 212, 213, 214, 214, 215, 216, 216, 217, 218, 219, - 219, 220, 221, 221, 222, 223, 223, 224, 225, 225, 226, 227, 227, 228, 229, 229, 230, - 231, 231, 232, 233, 233, 234, 235, 235, 236, 237, 237, 238, 238, 239, 240, 240, 241, - 242, 242, 243, 244, 244, 245, 246, 246, 247, 247, 248, 249, 249, 250, 251, 251, 252, - 253, 253, 254, 254, 255}, - {16, 23, 28, 32, 36, 39, 42, 45, 48, 50, 53, 55, 57, 60, 62, 64, 66, 68, 69, 71, 73, 75, 76, - 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 93, 94, 96, 97, 98, 100, 101, 102, 103, 105, 106, - 107, 108, 109, 110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, - 125, 126, 128, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, - 142, 143, 143, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, - 156, 157, 158, 159, 159, 160, 161, 162, 163, 163, 164, 165, 166, 166, 167, 168, 169, - 169, 170, 171, 172, 172, 173, 174, 175, 175, 176, 177, 177, 178, 179, 180, 180, 181, - 182, 182, 183, 184, 184, 185, 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, - 193, 194, 195, 195, 196, 196, 197, 198, 198, 199, 200, 200, 201, 202, 202, 203, 203, - 204, 205, 205, 206, 207, 207, 208, 208, 209, 210, 210, 211, 211, 212, 213, 213, 214, - 214, 215, 216, 216, 217, 217, 218, 219, 219, 220, 220, 221, 221, 222, 223, 223, 224, - 224, 225, 225, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 232, 232, 233, 233, - 234, 234, 235, 235, 236, 236, 237, 237, 238, 239, 239, 240, 240, 241, 241, 242, 242, - 243, 243, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, - 251, 252, 252, 253, 254, 254, 255, 255} - }; - - static { - if (Engine.getConfig().equals(Settings.fix_gamma_ramp, Boolean.TRUE)) { - for (int i = 0; i < 128; --LUT[0][i++]) { - } - } - } - - private GammaTables() { - } -} +// +// Copyright (C) 1993-1996 Id Software, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// DESCRIPTION: +// Gamma correction LUT stuff. +// Functions to draw patches (by post) directly to screen. +// Functions to blit a block to the screen. +// +// from v_video.c +// +package v.tables; + +import m.Settings; +import mochadoom.Engine; + +public class GammaTables { + + // Now where did these came from? + public final static int[][] LUT + = { + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255}, + {2, 4, 5, 7, 8, 10, 11, 12, 14, 15, 16, 18, 19, 20, 21, 23, 24, 25, 26, 27, 29, 30, 31, + 32, 33, 34, 36, 37, 38, 39, 40, 41, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, + 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, + 161, 162, 163, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 214, 215, 216, 217, 218, + 219, 220, 221, 222, 222, 223, 224, 225, 226, 227, 228, 229, 230, 230, 231, 232, + 233, 234, 235, 236, 237, 237, 238, 239, 240, 241, 242, 243, 244, 245, 245, 246, + 247, 248, 249, 250, 251, 252, 252, 253, 254, 255}, + {4, 7, 9, 11, 13, 15, 17, 19, 21, 22, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 40, 42, + 43, 45, 46, 47, 48, 50, 51, 52, 54, 55, 56, 57, 59, 60, 61, 62, 63, 65, 66, 67, 68, 69, + 70, 72, 73, 74, 75, 76, 77, 78, 79, 80, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, + 129, 130, 131, 132, 133, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 153, 154, 155, 156, 157, 158, 159, + 160, 160, 161, 162, 163, 164, 165, 166, 166, 167, 168, 169, 170, 171, 172, 172, 173, + 174, 175, 176, 177, 178, 178, 179, 180, 181, 182, 183, 183, 184, 185, 186, 187, 188, + 188, 189, 190, 191, 192, 193, 193, 194, 195, 196, 197, 197, 198, 199, 200, 201, 201, + 202, 203, 204, 205, 206, 206, 207, 208, 209, 210, 210, 211, 212, 213, 213, 214, 215, + 216, 217, 217, 218, 219, 220, 221, 221, 222, 223, 224, 224, 225, 226, 227, 228, 228, + 229, 230, 231, 231, 232, 233, 234, 235, 235, 236, 237, 238, 238, 239, 240, 241, 241, + 242, 243, 244, 244, 245, 246, 247, 247, 248, 249, 250, 251, 251, 252, 253, 254, 254, + 255}, + {8, 12, 16, 19, 22, 24, 27, 29, 31, 34, 36, 38, 40, 41, 43, 45, 47, 49, 50, 52, 53, 55, + 57, 58, 60, 61, 63, 64, 65, 67, 68, 70, 71, 72, 74, 75, 76, 77, 79, 80, 81, 82, 84, 85, + 86, 87, 88, 90, 91, 92, 93, 94, 95, 96, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 135, 136, 137, 138, 139, 140, + 141, 142, 143, 143, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, + 155, 156, 157, 158, 159, 160, 160, 161, 162, 163, 164, 165, 165, 166, 167, 168, 169, + 169, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 179, 180, 180, 181, 182, + 183, 183, 184, 185, 186, 186, 187, 188, 189, 189, 190, 191, 192, 192, 193, 194, 195, + 195, 196, 197, 197, 198, 199, 200, 200, 201, 202, 202, 203, 204, 205, 205, 206, 207, + 207, 208, 209, 210, 210, 211, 212, 212, 213, 214, 214, 215, 216, 216, 217, 218, 219, + 219, 220, 221, 221, 222, 223, 223, 224, 225, 225, 226, 227, 227, 228, 229, 229, 230, + 231, 231, 232, 233, 233, 234, 235, 235, 236, 237, 237, 238, 238, 239, 240, 240, 241, + 242, 242, 243, 244, 244, 245, 246, 246, 247, 247, 248, 249, 249, 250, 251, 251, 252, + 253, 253, 254, 254, 255}, + {16, 23, 28, 32, 36, 39, 42, 45, 48, 50, 53, 55, 57, 60, 62, 64, 66, 68, 69, 71, 73, 75, 76, + 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 93, 94, 96, 97, 98, 100, 101, 102, 103, 105, 106, + 107, 108, 109, 110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 128, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 143, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, + 156, 157, 158, 159, 159, 160, 161, 162, 163, 163, 164, 165, 166, 166, 167, 168, 169, + 169, 170, 171, 172, 172, 173, 174, 175, 175, 176, 177, 177, 178, 179, 180, 180, 181, + 182, 182, 183, 184, 184, 185, 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, + 193, 194, 195, 195, 196, 196, 197, 198, 198, 199, 200, 200, 201, 202, 202, 203, 203, + 204, 205, 205, 206, 207, 207, 208, 208, 209, 210, 210, 211, 211, 212, 213, 213, 214, + 214, 215, 216, 216, 217, 217, 218, 219, 219, 220, 220, 221, 221, 222, 223, 223, 224, + 224, 225, 225, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 232, 232, 233, 233, + 234, 234, 235, 235, 236, 236, 237, 237, 238, 239, 239, 240, 240, 241, 241, 242, 242, + 243, 243, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, + 251, 252, 252, 253, 254, 254, 255, 255} + }; + + static { + if (Engine.getConfig().equals(Settings.fix_gamma_ramp, Boolean.TRUE)) { + for (int i = 0; i < 128; --LUT[0][i++]) { + } + } + } + + private GammaTables() { + } +} \ No newline at end of file diff --git a/src/v/tables/GreyscaleFilter.java b/src/v/tables/GreyscaleFilter.java index f0caf2c..7fe369c 100644 --- a/src/v/tables/GreyscaleFilter.java +++ b/src/v/tables/GreyscaleFilter.java @@ -1,129 +1,129 @@ -/* - * Copyright (C) 2017 Good Sign - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package v.tables; - -import m.Settings; -import mochadoom.Engine; - -/** - * - * @author Good Sign - */ -public enum GreyscaleFilter { - Lightness, - Average, - Luminance, // this one is the default for invulnerability map - Luminosity; - - private static GreyscaleFilter FILTER; - - public static int component(int r, int g, int b) { - if (FILTER == null) { - readSetting(); - } - return FILTER.getComponent(r, g, b); - } - - public static float component(float r, float g, float b) { - if (FILTER == null) { - readSetting(); - } - return FILTER.getComponent(r, g, b); - } - - public static int grey888(int rgb888) { - if (FILTER == null) { - readSetting(); - } - return FILTER.getGrey888(rgb888); - } - - public static int grey888(int r8, int g8, int b8) { - if (FILTER == null) { - readSetting(); - } - return FILTER.getGrey888(r8, g8, b8); - } - - public static short grey555(int r5, int g5, int b5) { - if (FILTER == null) { - readSetting(); - } - return FILTER.getGrey555(r5, g5, b5); - } - - public static short grey555(short rgb555) { - if (FILTER == null) { - readSetting(); - } - return FILTER.getGrey555(rgb555); - } - - private static void readSetting() { - FILTER = Engine.getConfig().getValue(Settings.greyscale_filter, GreyscaleFilter.class); - } - - public int getComponent(int r, int g, int b) { - switch (this) { - case Lightness: - return (Math.max(Math.max(r, g), b) + Math.min(Math.min(r, g), b)) / 2; - case Average: - return (r + g + b) / 3; - case Luminance: - return (int) (0.299f * r + 0.587f * g + 0.114f * b); - case Luminosity: - return (int) (0.2126f * r + 0.7152f * g + 0.0722f * b); - } - - // should not happen - return 0; - } - - public float getComponent(float r, float g, float b) { - switch (this) { - case Lightness: - return (Math.max(Math.max(r, g), b) + Math.min(Math.min(r, g), b)) / 2; - case Average: - return (r + g + b) / 3; - case Luminance: - return 0.299f * r + 0.587f * g + 0.114f * b; - case Luminosity: - return 0.2126f * r + 0.7152f * g + 0.0722f * b; - } - - // should not happen - return 0.0f; - } - - public int getGrey888(int r8, int g8, int b8) { - final int component = getComponent(r8, g8, b8) & 0xFF; - return 0xFF000000 + (component << 16) + (component << 8) + component; - } - - public short getGrey555(int r5, int g5, int b5) { - final int component = getComponent(r5, g5, b5) & 0x1F; - return (short) ((component << 10) + (component << 5) + component); - } - - public int getGrey888(int rgb888) { - return getGrey888((rgb888 >> 16) & 0xFF, (rgb888 >> 8) & 0xFF, rgb888 & 0xFF); - } - - public short getGrey555(short rgb555) { - return getGrey555((rgb555 >> 10) & 0x1F, (rgb555 >> 5) & 0x1F, rgb555 & 0x1F); - } -} +/* + * Copyright (C) 2017 Good Sign + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package v.tables; + +import m.Settings; +import mochadoom.Engine; + +/** + * + * @author Good Sign + */ +public enum GreyscaleFilter { + Lightness, + Average, + Luminance, // this one is the default for invulnerability map + Luminosity; + + private static GreyscaleFilter FILTER; + + public static int component(int r, int g, int b) { + if (FILTER == null) { + readSetting(); + } + return FILTER.getComponent(r, g, b); + } + + public static float component(float r, float g, float b) { + if (FILTER == null) { + readSetting(); + } + return FILTER.getComponent(r, g, b); + } + + public static int grey888(int rgb888) { + if (FILTER == null) { + readSetting(); + } + return FILTER.getGrey888(rgb888); + } + + public static int grey888(int r8, int g8, int b8) { + if (FILTER == null) { + readSetting(); + } + return FILTER.getGrey888(r8, g8, b8); + } + + public static short grey555(int r5, int g5, int b5) { + if (FILTER == null) { + readSetting(); + } + return FILTER.getGrey555(r5, g5, b5); + } + + public static short grey555(short rgb555) { + if (FILTER == null) { + readSetting(); + } + return FILTER.getGrey555(rgb555); + } + + private static void readSetting() { + FILTER = Engine.getConfig().getValue(Settings.greyscale_filter, GreyscaleFilter.class); + } + + public int getComponent(int r, int g, int b) { + switch (this) { + case Lightness: + return (Math.max(Math.max(r, g), b) + Math.min(Math.min(r, g), b)) / 2; + case Average: + return (r + g + b) / 3; + case Luminance: + return (int) (0.299f * r + 0.587f * g + 0.114f * b); + case Luminosity: + return (int) (0.2126f * r + 0.7152f * g + 0.0722f * b); + } + + // should not happen + return 0; + } + + public float getComponent(float r, float g, float b) { + switch (this) { + case Lightness: + return (Math.max(Math.max(r, g), b) + Math.min(Math.min(r, g), b)) / 2; + case Average: + return (r + g + b) / 3; + case Luminance: + return 0.299f * r + 0.587f * g + 0.114f * b; + case Luminosity: + return 0.2126f * r + 0.7152f * g + 0.0722f * b; + } + + // should not happen + return 0.0f; + } + + public int getGrey888(int r8, int g8, int b8) { + final int component = getComponent(r8, g8, b8) & 0xFF; + return 0xFF000000 + (component << 16) + (component << 8) + component; + } + + public short getGrey555(int r5, int g5, int b5) { + final int component = getComponent(r5, g5, b5) & 0x1F; + return (short) ((component << 10) + (component << 5) + component); + } + + public int getGrey888(int rgb888) { + return getGrey888((rgb888 >> 16) & 0xFF, (rgb888 >> 8) & 0xFF, rgb888 & 0xFF); + } + + public short getGrey555(short rgb555) { + return getGrey555((rgb555 >> 10) & 0x1F, (rgb555 >> 5) & 0x1F, rgb555 & 0x1F); + } +} \ No newline at end of file diff --git a/src/v/tables/LightsAndColors.java b/src/v/tables/LightsAndColors.java index fb71ed6..6018322 100644 --- a/src/v/tables/LightsAndColors.java +++ b/src/v/tables/LightsAndColors.java @@ -26,7 +26,7 @@ * Combined colormap and light LUTs. * Used for z-depth cuing per column/row, * and other lighting effects (sector ambient, flash). - * + * * @author velktron * * @param The data type of the SCREEN @@ -37,7 +37,7 @@ public class LightsAndColors { /** For HiColor, these are, effectively, a bunch of 555 RGB palettes, * for TrueColor they are a bunch of 32-bit ARGB palettes etc. - * Only for indexed they represent index remappings. + * Only for indexed they represent index remappings. */ /** "peg" this to the one from RendererData */ public V[] colormaps; @@ -106,7 +106,7 @@ public int numColorMaps() { * player_t.fixedcolormap have a range of 0..31 in vanilla. * We must respect it. However, we can have more lightlevels then vanilla. * So we must scale player_t.fixedcolormap by the difference with vanilla lightBits - * + * * @param player * @return index in rich bit liteColorMaps */ @@ -130,7 +130,7 @@ private static class LCData { * These two are tied by an inverse relationship. E.g. 256 levels, 0 shift * 128 levels, 1 shift ...etc... 16 levels, 4 shift (default). Or even less, * if you want. - * + * * By setting it to the max however you get smoother light and get rid of * lightsegshift globally, too. Of course, by increasing the number of light * levels, you also put more memory pressure, and due to their being only @@ -140,7 +140,7 @@ private static class LCData { final int LIGHTSEGSHIFT; /** Number of diminishing brightness levels. - There a 0-31, i.e. 32 LUT in the COLORMAP lump. + There a 0-31, i.e. 32 LUT in the COLORMAP lump. TODO: how can those be distinct from the light levels??? */ final int NUMCOLORMAPS; @@ -148,7 +148,7 @@ private static class LCData { // These are a bit more tricky to figure out though. /** Maximum index used for light levels of sprites. In practice, * it's capped by the number of light levels??? - * + * * Normally set to 48 (32 +16???) */ final int MAXLIGHTSCALE; @@ -156,7 +156,7 @@ private static class LCData { /** Used to scale brightness of walls and sprites. Their "scale" is shifted by * this amount, and this results in an index, which is capped by MAXLIGHTSCALE. * Normally it's 12 for 32 levels, so 11 for 64, 10 for 128, ans 9 for 256. - * + * */ final int LIGHTSCALESHIFT; @@ -167,7 +167,7 @@ private static class LCData { /** Normally 20 for 32 colormaps, applied to distance. * Formula: 25-LBITS - * + * */ final int LIGHTZSHIFT; @@ -183,4 +183,4 @@ private static class LCData { LIGHTZSHIFT = 25 - bpp.lightBits; } } -} +} \ No newline at end of file diff --git a/src/v/tables/Playpal.java b/src/v/tables/Playpal.java index 799bd5b..d9d5cc1 100644 --- a/src/v/tables/Playpal.java +++ b/src/v/tables/Playpal.java @@ -104,7 +104,7 @@ public class Playpal { /** * Why not restore idea of grayscale palette? * - Good Sign 2017/04/12 - * + * * for (int i = 0; i < 256; i++) { * greypal[3 * i + 2] = greypal[3 * i + 1] = greypal[3 * i] = (playpal[3 * i] + playpal[3 * i + 1] + playpal[3 * i + 2])/3; * } @@ -159,4 +159,4 @@ private static byte[] properPlaypal(int[] data, byte[] lumpData) { private Playpal() { } -} +} \ No newline at end of file diff --git a/src/w/AidedReadableDoomObject.java b/src/w/AidedReadableDoomObject.java index 3bce46d..178885e 100644 --- a/src/w/AidedReadableDoomObject.java +++ b/src/w/AidedReadableDoomObject.java @@ -5,11 +5,11 @@ /** This is for objects that can be read from disk, but cannot * self-determine their own length for some reason. - * + * * @author Maes * */ public interface AidedReadableDoomObject { public void read(DataInputStream f, int len) throws IOException; -} +} \ No newline at end of file diff --git a/src/w/CacheableDoomObject.java b/src/w/CacheableDoomObject.java index f86108d..71ec62d 100644 --- a/src/w/CacheableDoomObject.java +++ b/src/w/CacheableDoomObject.java @@ -4,44 +4,44 @@ import java.nio.ByteBuffer; /** All objects that can be deserialized from raw byte buffers such as those - * read from WAD lumps should implement this method, so that the WadLoader + * read from WAD lumps should implement this method, so that the WadLoader * can cache them, and recursive calls to sub-objects can be made. - * + * * E.g. an object of type A consists of a header and a list of objects of type B. - * Calling A.unpack(buf) will cause A to unmarshal its own header, set the list of + * Calling A.unpack(buf) will cause A to unmarshal its own header, set the list of * B objects, and then call B.unpack() for each of them, by passing the same buffer * along. - * + * * This system works cleanly, and allows to simulate Doom's "cached memory" while * returning proper objects of the correct type and keeping close to Java's - * "correct" way of doing things. - * * + * "correct" way of doing things. + * * * For example, if a patch_t is read from disk, the WadLoader uses its unpack() method * to read it from a lump read from disk, and creates a new patch_t object, which is placed * in the lump cache (which holds CacheableDoomObject, incidentally). The next time this * same patch_t is requested, the reference to the already cached patch_t will be returned, * if it hasn't been forcedly flushed from the cache. Voila', lump caching! - * + * * The principle can be applied to ARRAYS of similar objects too: using the same buffer, * iterative serial unpacking is possible, while still mantaining a "cached" reference * to their array (TODO: actually, this needs to be implemented more efficiently. Look in - * WadLoader) - * + * WadLoader) + * * The opposite would be a "PackableDoomObject", aka objects that can pack themselves into * a byte buffer for transmission purposes, although Doom doesn't really need to write as * much as it needs reading. - * - * For the purpose of saving/loading games, which need to read/write to variable disk + * + * For the purpose of saving/loading games, which need to read/write to variable disk * structures ALL the time, use the ReadableDoomObject/WritableDoomObject interfaces. - * Their difference is that they are highly mutable and supposed to be read from files - * or input/output streams, and that a continuous reference to them as deserialized + * Their difference is that they are highly mutable and supposed to be read from files + * or input/output streams, and that a continuous reference to them as deserialized * objects (e.g. in the caching mechanism) is not needed. - * - * + * + * * @author Velktron * */ public interface CacheableDoomObject { public void unpack(ByteBuffer buf) throws IOException; -} +} \ No newline at end of file diff --git a/src/w/CacheableDoomObjectContainer.java b/src/w/CacheableDoomObjectContainer.java index 49bf29d..f305310 100644 --- a/src/w/CacheableDoomObjectContainer.java +++ b/src/w/CacheableDoomObjectContainer.java @@ -3,15 +3,15 @@ import java.io.IOException; import java.nio.ByteBuffer; -/** A container allowing for caching of arrays of CacheableDoomObjects - * - * It's a massive improvement over the older system, allowing for proper - * caching and auto-unpacking of arrays of CacheableDoomObjects and much +/** A container allowing for caching of arrays of CacheableDoomObjects + * + * It's a massive improvement over the older system, allowing for proper + * caching and auto-unpacking of arrays of CacheableDoomObjects and much * cleaner code throughout. - * + * * The container itself is a CacheableDoomObject....can you feel the * abuse? ;-) - * + * */ public class CacheableDoomObjectContainer implements CacheableDoomObject { @@ -33,7 +33,7 @@ public void unpack(ByteBuffer buf) throws IOException { } /** Statically usable method - * + * * @param buf * @param stuff * @throws IOException @@ -44,4 +44,4 @@ public static void unpack(ByteBuffer buf, CacheableDoomObject[] stuff) throws IO } } -} +} \ No newline at end of file diff --git a/src/w/DoomBuffer.java b/src/w/DoomBuffer.java index 44dabae..4f77f1b 100644 --- a/src/w/DoomBuffer.java +++ b/src/w/DoomBuffer.java @@ -5,14 +5,14 @@ import java.nio.ByteOrder; /** Very similar to the concept of ReadableDoomObjects - * but made to work with byte buffers instead. - * - * This is normally NOT used to pass data around: I am + * but made to work with byte buffers instead. + * + * This is normally NOT used to pass data around: I am * using it as a workaround to store raw byte buffers * into a "CacheableDoomObject" array, as Java * doesn't seem to like storing both ByteBuffers and * CacheableDoomObjects in the same array. WTF... - * + * * @author admin * */ @@ -156,7 +156,7 @@ public static String readString(ByteBuffer buf) throws IOException { /** MAES: Reads a specified number of bytes from a buffer into a new String. * With many lengths being implicit, we need to actually take the loader by the hand. - * + * * @param buf * @param len * @return @@ -181,7 +181,7 @@ public static String getString(ByteBuffer buf, int len) throws IOException { /** MAES: Reads a maximum specified number of bytes from a buffer into a new String, * considering the bytes as representing a null-terminated, C-style string. - * + * * @param buf * @param len * @return @@ -213,7 +213,7 @@ public static String getNullTerminatedString(ByteBuffer buf, int len) throws IOE /** MAES: Reads a specified number of bytes from a buffer into a new String. * With many lengths being implicit, we need to actually take the loader by the hand. - * + * * @param buf * @param len * @return @@ -270,4 +270,4 @@ public static short getLEShort(byte[] buf) { return (short) (buf[0] << 8 | buf[1]); } -} +} \ No newline at end of file diff --git a/src/w/DoomIO.java b/src/w/DoomIO.java index 8c98efc..ea92837 100644 --- a/src/w/DoomIO.java +++ b/src/w/DoomIO.java @@ -10,7 +10,7 @@ of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @@ -34,20 +34,20 @@ of the License, or (at your option) any later version. import mochadoom.Loggers; /** - * An extension of RandomAccessFile, which handles readString/WriteString specially + * An extension of RandomAccessFile, which handles readString/WriteString specially * and offers several Doom related (and cross-OS) helper functions for reading/writing * arrays of multiple objects or fixed-length strings from/to disk. - * + * * TO DEVELOPERS: this is the preferrered method of I/O for anything implemented. - * In addition, Doomfiles can be passed to objects implementing the IReadableDoomObject + * In addition, Doomfiles can be passed to objects implementing the IReadableDoomObject * and IWritableDoomObject interfaces, which will "autoread" or "autowrite" themselves * to the implied stream. - * + * * TODO: in the light of greater future portabililty and compatibility in certain * environments, PERHAPS this should have been implemented using Streams. Perhaps * it's possible to change the underlying implementation without (?) changing too - * much of the exposed interface, but it's not a priority for me right now. - * + * much of the exposed interface, but it's not a priority for me right now. + * */ public class DoomIO { @@ -95,7 +95,7 @@ public static final String readString(DataInputStream dis) throws IOException { /** MAES: Reads a specified number of bytes from a file into a new String. * With many lengths being implicit, we need to actually take the loader by the hand. - * + * * @param len * @return * @throws IOException @@ -136,7 +136,7 @@ public static String readString(InputStream f, int len) throws IOException { /** MAES: Reads a specified number of bytes from a file into a new, NULL TERMINATED String. * With many lengths being implicit, we need to actually take the loader by the hand. - * + * * @param len * @return * @throws IOException @@ -170,7 +170,7 @@ public static final String readNullTerminatedString(InputStream dis, int len) th /** MAES: Reads multiple strings with a specified number of bytes from a file. * If the array is not large enough, only partial reads will occur. - * + * * @param len * @return * @throws IOException @@ -195,8 +195,8 @@ public static final String[] readMultipleFixedLengthStrings(DataInputStream dis, return dest; } - /** Writes a length specified string (Pascal style) to a file. - * + /** Writes a length specified string (Pascal style) to a file. + * * */ public static void writeString(DataOutputStream dos, String s) { try { @@ -215,9 +215,9 @@ public static void writeString(DataOutputStream dos, String s) { } /** Writes a String with a specified len to a file. - * This is useful for fixed-size String fields in - * files. Any leftover space will be filled with 0x00s. - * + * This is useful for fixed-size String fields in + * files. Any leftover space will be filled with 0x00s. + * * @param s * @param len * @throws IOException @@ -329,7 +329,7 @@ public static void readBooleanArray(DataInputStream dis, boolean[] s, int len) t /** Reads an array of "int booleans" into an array or * proper booleans. 4 bytes per boolean are used! - * + * * @param s * @param len * @throws IOException @@ -402,7 +402,7 @@ public static final void writeCharArray(DataOutputStream dos, char[] charr, int } /** Will read an array of proper Unicode chars. - * + * * @param charr * @param len * @throws IOException @@ -420,7 +420,7 @@ public static final void readCharArray(DataInputStream dis, char[] charr, int le /** Will read a bunch of non-unicode chars into a char array. * Useful when dealing with legacy text files. - * + * * @param charr * @param len * @throws IOException @@ -436,7 +436,7 @@ public static final void readNonUnicodeCharArray(DataInputStream dis, char[] cha } } - /** Writes an item reference. + /** Writes an item reference. public void writeItem(gitem_t item) throws IOException { if (item == null) writeInt(-1); @@ -444,7 +444,7 @@ public void writeItem(gitem_t item) throws IOException { writeInt(item.index); } */ - /** Reads the item index and returns the game item. + /** Reads the item index and returns the game item. public gitem_t readItem() throws IOException { int ndx = readInt(); if (ndx == -1) @@ -452,7 +452,7 @@ public gitem_t readItem() throws IOException { else return GameItemList.itemlist[ndx]; } - * @throws IOException + * @throws IOException */ public static final long readUnsignedLEInt(DataInputStream dis) throws IOException { int tmp = dis.readInt(); @@ -489,7 +489,7 @@ public static final short readLEShort(DataInputStream dis) throws IOException { } /** Reads a "big boolean" using 4 bytes. - * + * * @return * @throws IOException */ @@ -498,4 +498,4 @@ public static final boolean readIntBoolean(DataInputStream dis) throws IOExcepti } -} +} \ No newline at end of file diff --git a/src/w/IPackableDoomObject.java b/src/w/IPackableDoomObject.java index d700ecd..c0cb36b 100644 --- a/src/w/IPackableDoomObject.java +++ b/src/w/IPackableDoomObject.java @@ -6,4 +6,4 @@ public interface IPackableDoomObject { public void pack(ByteBuffer buf) throws IOException; -} +} \ No newline at end of file diff --git a/src/w/IReadWriteDoomObject.java b/src/w/IReadWriteDoomObject.java index cab97d9..0399aa4 100644 --- a/src/w/IReadWriteDoomObject.java +++ b/src/w/IReadWriteDoomObject.java @@ -2,4 +2,4 @@ public interface IReadWriteDoomObject extends IReadableDoomObject, IWritableDoomObject { -} +} \ No newline at end of file diff --git a/src/w/IReadableDoomObject.java b/src/w/IReadableDoomObject.java index 8ac679e..f2a7bed 100644 --- a/src/w/IReadableDoomObject.java +++ b/src/w/IReadableDoomObject.java @@ -7,11 +7,11 @@ * Every object is supposed to do its own umarshalling. This way, * structured and hierchical reads are possible. Another superior innovation * of Mocha Doom ;-) Err....ok :-p - * + * * @author Velktron * */ public interface IReadableDoomObject { public void read(DataInputStream f) throws IOException; -} +} \ No newline at end of file diff --git a/src/w/IWadLoader.java b/src/w/IWadLoader.java index 750504c..376e551 100644 --- a/src/w/IWadLoader.java +++ b/src/w/IWadLoader.java @@ -119,7 +119,7 @@ public interface IWadLoader { @W_Wad.C(W_CacheLumpNum) public abstract T CacheLumpNum(int lump, int tag, Class what); - // MAES 24/8/2011: superseded by auto-allocating version with proper + // MAES 24/8/2011: superseded by auto-allocating version with proper // container-based caching. @Deprecated public abstract void CacheLumpNumIntoArray(int lump, int tag, @@ -354,4 +354,4 @@ default byte[][] LoadColormap() { return colormap; } -} +} \ No newline at end of file diff --git a/src/w/IWritableDoomObject.java b/src/w/IWritableDoomObject.java index 85d421d..c25b36c 100644 --- a/src/w/IWritableDoomObject.java +++ b/src/w/IWritableDoomObject.java @@ -6,4 +6,4 @@ public interface IWritableDoomObject { public void write(DataOutputStream dos) throws IOException; -} +} \ No newline at end of file diff --git a/src/w/InputStreamSugar.java b/src/w/InputStreamSugar.java index 8a856d4..c1b7fda 100644 --- a/src/w/InputStreamSugar.java +++ b/src/w/InputStreamSugar.java @@ -19,7 +19,7 @@ * we can try and skip directly by using the file channel, otherwise we can try * (eww) closing the stream, reopening it (ASSUMING WE KNOW THE SOURCE'S URI AND * TYPE), and then skipping. - * + * * @author Maes */ public class InputStreamSugar { @@ -32,13 +32,13 @@ public class InputStreamSugar { public static final int ZIP_FILE = 0x4; // Zipped file - public static final int BAD_URI = -1; // Bad or unparseable + public static final int BAD_URI = -1; // Bad or unparseable /** * Creates an inputstream from a local file, network resource, or zipped * file (also over a network). If an entry name is specifid AND the type is * specified to be zip, then a zipentry with that name will be sought. - * + * * @param resource * @param contained * @param type @@ -55,7 +55,7 @@ public static final InputStream createInputStreamFromURI(String resource, is = getDirectInputStream(resource); } else { // Entry specified AND type specified to be zip - // We might want to open even a zip file without looking + // We might want to open even a zip file without looking // for any particular entry. if (entry != null && C2JUtils.flags(type, ZIP_FILE)) { @@ -67,7 +67,7 @@ public static final InputStream createInputStreamFromURI(String resource, } catch (Exception e) { // Local zip file? try { - // Open resource as local file-backed zip input stream, + // Open resource as local file-backed zip input stream, // and search proper entry. zis = new ZipInputStream(new FileInputStream(resource)); } catch (Exception e1) { @@ -91,11 +91,11 @@ public static final InputStream createInputStreamFromURI(String resource, return getDirectInputStream(resource); } - /** Match zip entries in a ZipInputStream based only on their name. + /** Match zip entries in a ZipInputStream based only on their name. * Luckily (?) ZipEntries do not keep references to their originating * streams, so opening/closing ZipInputStreams all the time won't result * in a garbage hell...I hope. - * + * * @param zis * @param entryname * @return @@ -150,7 +150,7 @@ private final static InputStream getDirectInputStream(String resource) { * position. With some types of stream, this is possible if you poke deep * enough. With others, it's not, and you can only close & reopen them * (provided you know how to do that) and then skip to a particular position - * + * * @param is * @param pos * The desired position @@ -191,7 +191,7 @@ public static final InputStream streamSeek(InputStream is, long pos, if (guesspos > 0 && guesspos <= pos) { long skipped = 0; long mustskip = pos - guesspos; - // Repeat skipping until proper amount reached + // Repeat skipping until proper amount reached while (skipped < mustskip) { skipped += is.skip(mustskip - skipped); } @@ -256,9 +256,9 @@ public static List getAllEntries(ZipInputStream zis) return zes; } - /** Attempts to return a stream size estimate. Only guaranteed to work 100% + /** Attempts to return a stream size estimate. Only guaranteed to work 100% * for streams representing local files, and zips (if you have the entry). - * + * * @param is * @param z * @return @@ -290,4 +290,4 @@ public static long getSizeEstimate(InputStream is, ZipEntry z) { } } -} +} \ No newline at end of file diff --git a/src/w/JadDecompress.java b/src/w/JadDecompress.java index e8b9e7b..4ef1c5c 100644 --- a/src/w/JadDecompress.java +++ b/src/w/JadDecompress.java @@ -61,4 +61,4 @@ public static void decode(byte[] input, byte[] output) { LOGGER.log(Level.INFO, String.format("Expanded %d to %d", input_ptr, output_ptr)); } -} +} \ No newline at end of file diff --git a/src/w/WadLoader.java b/src/w/WadLoader.java index 5ca3127..9ed2e27 100644 --- a/src/w/WadLoader.java +++ b/src/w/WadLoader.java @@ -81,11 +81,11 @@ public WadLoader() { /** * MAES: probably array of byte[]??? void** lumpcache; - * + * * Actually, loaded objects will be deserialized here as the general type * "CacheableDoomObject" (in the worst case they will be byte[] or * ByteBuffer). - * + * * Not to brag, but this system is FAR superior to the inline unmarshaling * used in other projects ;-) */ @@ -100,7 +100,7 @@ public WadLoader() { * #define strcmpi strcasecmp MAES: this is just capitalization. However we * can't manipulate String object in Java directly like this, so this must * be a return type. - * + * * TODO: maybe move this in utils? */ public String strupr(String s) { @@ -136,7 +136,7 @@ public void strupr(char[] s) { /** * This is where lumps are actually read + loaded from a file. - * + * * @param filename * @throws Exception */ @@ -186,7 +186,7 @@ private void AddFile(String uri, ZipEntry entry, int type) throws Exception { singleinfo.filepos = 0; singleinfo.size = InputStreamSugar.getSizeEstimate(handle, wadinfo.entry); - // Single lumps. Only use 8 characters + // Single lumps. Only use 8 characters singleinfo.actualname = singleinfo.name = C2JUtils.removeExtension(uri).toUpperCase(); // MAES: check out certain known types of extension @@ -202,7 +202,7 @@ private void AddFile(String uri, ZipEntry entry, int type) throws Exception { } else { // MAES: 14/06/10 this is historical, for this is the first time I - // implement reading something from RAF into Doom's structs. + // implement reading something from RAF into Doom's structs. // Kudos to the JAKE2 team who solved this problem before me. // MAES: 25/10/11: In retrospect, this solution, while functional, was // inelegant and limited. @@ -255,7 +255,7 @@ private void AddFile(String uri, ZipEntry entry, int type) throws Exception { } // end loading wad - // At this point, a WADFILE or LUMPFILE been successfully loaded, + // At this point, a WADFILE or LUMPFILE been successfully loaded, // and so is added to the list this.wadfiles.add(wadinfo); @@ -309,7 +309,7 @@ private void AddFile(String uri, ZipEntry entry, int type) throws Exception { /** Try to guess a realistic wad size limit based only on the number of lumps and their * STATED contents, in case it's not possible to get an accurate stream size otherwise. * Of course, they may be way off with deliberately malformed files etc. - * + * * @param header * @param lumpinfo2 * @return @@ -449,7 +449,7 @@ public void InitMultipleFiles(String[] filenames) throws Exception { */ protected void addZipFile(String s, int type) throws IOException, Exception { - // Get entries + // Get entries BufferedInputStream is = new BufferedInputStream( InputStreamSugar.createInputStreamFromURI(s, null, type) ); @@ -486,13 +486,13 @@ public final int NumLumps() { /** * W_CheckNumForName2 Returns -1 if name not found. - * + * * A slightly better implementation, uses string hashes * as direct comparators (though 64-bit long descriptors * could be used). It's faster than the old method, but - * still short from the HashMap's performance by - * an order of magnitude. - * + * still short from the HashMap's performance by + * an order of magnitude. + * * @param name * @return * @@ -520,12 +520,12 @@ public int CheckNumForName2(String name) { } */ /** * Old, shitty method for CheckNumForName. It's an overly literal - * translation of how the C original worked, which was none too good + * translation of how the C original worked, which was none too good * even without the overhead of converting a string to * its integer representation. It's so bad, that it's two orders * of magnitude slower than a HashMap implemetation, and one from * a direct hash/longname comparison with linear search. - * + * * @param name * @return * @@ -652,7 +652,7 @@ public final void ReadLump(int lump, byte[] buf) { /** * W_ReadLump Loads the lump into the given buffer, which must be >= * W_LumpLength(). SKIPS CACHING - * + * * @throws IOException */ @Override @@ -685,7 +685,7 @@ public final void ReadLump(int lump, byte[] buf, int offset) { handle = InputStreamSugar.streamSeek(handle, l.position, l.wadfile.maxsize, l.wadfile.name, l.wadfile.entry, l.wadfile.type); - // read buffered. Unfortunately that interferes badly with + // read buffered. Unfortunately that interferes badly with // guesstimating the actual stream position. BufferedInputStream bis = new BufferedInputStream(handle, 8192); @@ -717,10 +717,10 @@ public final void ReadLump(int lump, byte[] buf, int offset) { /** The most basic of the Wadloader functions. Will attempt to read a lump * off disk, based on the specific class type (it will call the unpack() - * method). If not possible to call the unpack method, it will leave a + * method). If not possible to call the unpack method, it will leave a * DoomBuffer object in its place, with the raw byte contents. It's - * - * + * + * */ @Override @SuppressWarnings("unchecked") @@ -781,16 +781,16 @@ public T CacheLumpNum(int lump, int tag, Class doomhash; @@ -1102,7 +1102,7 @@ protected void InitLumpHash() { /* * (non-Javadoc) - * + * * @see w.IWadLoader#CheckNumForName(java.lang.String) */ @Override @@ -1122,7 +1122,7 @@ public int CheckNumForName(String name/* , int namespace */) { /* * (non-Javadoc) - * + * * @see w.IWadLoader#CheckNumForName(java.lang.String) */ @Override @@ -1188,19 +1188,19 @@ public void CloseAllHandles() { public static final int ns_flats = 1; public static final int ns_sprites = 2; - /** + /** * Based on Boom's W_CoalesceMarkedResource * Sort of mashes similar namespaces together so that they form * a continuous space (single start and end, e.g. so that multiple * S_START and S_END as well as special DEUTEX lumps mash together * under a common S_START/S_END boundary). Also also sort of performs * a "bubbling down" of marked lumps at the end of the namespace. - * + * * It's convenient for sprites, but can be replaced by alternatives * for flats. - * + * * killough 4/17/98: add namespace tags - * + * * @param start_marker * @param end_marker * @param namespace @@ -1313,9 +1313,9 @@ public void UnlockLumpNum(CacheableDoomObject lump) { Integer lumpno = zone.remove(lump); // Force nulling. This should trigger garbage collection, - // and reclaim some memory, provided you also nulled any other - // reference to a certain lump. Therefore, make sure you null - // stuff right after calling this method, if you want to make sure + // and reclaim some memory, provided you also nulled any other + // reference to a certain lump. Therefore, make sure you null + // stuff right after calling this method, if you want to make sure // that they won't be referenced anywhere else. if (lumpno != null) { lumpcache[lumpno] = null; @@ -1583,4 +1583,4 @@ public int GetNumWadfiles() { //Eclpise and see what gives from there. // //A good place to start is the testers/ directory, where you can get an idea of -//how a few of the implemented stuff works. +//how a few of the implemented stuff works. \ No newline at end of file diff --git a/src/w/animenum_t.java b/src/w/animenum_t.java index c3d3ff4..4c66e8c 100644 --- a/src/w/animenum_t.java +++ b/src/w/animenum_t.java @@ -6,4 +6,4 @@ public enum animenum_t { ANIM_RANDOM, ANIM_LEVEL -} +} \ No newline at end of file diff --git a/src/w/filelump_t.java b/src/w/filelump_t.java index 9019637..b2f5f08 100644 --- a/src/w/filelump_t.java +++ b/src/w/filelump_t.java @@ -5,7 +5,7 @@ import java.io.IOException; /** filelumps are on-disk structures. lumpinfos are almost the same, but are memory only. - * + * * @author Maes * */ @@ -67,4 +67,4 @@ public void write(DataOutputStream dos) } -} +} \ No newline at end of file diff --git a/src/w/li_namespace.java b/src/w/li_namespace.java index 34272d8..f530fb8 100644 --- a/src/w/li_namespace.java +++ b/src/w/li_namespace.java @@ -9,5 +9,4 @@ public enum li_namespace { ns_prboom, ns_demos, ns_hires //e6y -} // haleyjd 05/21/02: renamed from "namespace" - +} // haleyjd 05/21/02: renamed from "namespace" \ No newline at end of file diff --git a/src/w/lumpinfo_t.java b/src/w/lumpinfo_t.java index c9c0b5f..2d359d9 100644 --- a/src/w/lumpinfo_t.java +++ b/src/w/lumpinfo_t.java @@ -69,4 +69,4 @@ public lumpinfo_t clone() { } -} +} \ No newline at end of file diff --git a/src/w/name8.java b/src/w/name8.java index 65016ff..99746d0 100644 --- a/src/w/name8.java +++ b/src/w/name8.java @@ -24,7 +24,7 @@ public name8(String name) { * 8-bit representation of a fixed-length 8 char string. * It's for all effects and purposes a unique 64-bit hash, and can be used to * speed up comparisons. - * + * * @param name * @return */ @@ -60,7 +60,7 @@ public static long byteArrayToLong(byte[] src, int ofs) { } /** Probably has horrible performance... - * + * * @param src * @param ofs * @return @@ -74,4 +74,4 @@ public static int stringToInt(String src, int ofs) { return (s[ofs] << 24) | (s[ofs + 1] << 16) | (s[ofs + 2] << 8) | s[ofs + 3]; } -} +} \ No newline at end of file diff --git a/src/w/statenum_t.java b/src/w/statenum_t.java index d8e8554..614fdd2 100644 --- a/src/w/statenum_t.java +++ b/src/w/statenum_t.java @@ -16,4 +16,4 @@ public int getValue() { return value; } -} +} \ No newline at end of file diff --git a/src/w/wad_source_t.java b/src/w/wad_source_t.java index b235d15..c536403 100644 --- a/src/w/wad_source_t.java +++ b/src/w/wad_source_t.java @@ -4,7 +4,7 @@ // Ty 08/29/98 - add source field to identify where this lump came from public enum wad_source_t { // CPhipps - define elements in order of 'how new/unusual' - source_iwad, // iwad file load + source_iwad, // iwad file load source_pre, // predefined lump source_auto_load, // lump auto-loaded by config file source_pwad, // pwad file load @@ -14,4 +14,4 @@ public enum wad_source_t { // ,source_deh_auto_load , source_deh, source_err -} +} \ No newline at end of file diff --git a/src/w/wadfile_info_t.java b/src/w/wadfile_info_t.java index 62b2a93..42dd22f 100644 --- a/src/w/wadfile_info_t.java +++ b/src/w/wadfile_info_t.java @@ -4,7 +4,7 @@ import java.util.zip.ZipEntry; // CPhipps - changed wad init -// We _must_ have the wadfiles[] the same as those actually loaded, so there +// We _must_ have the wadfiles[] the same as those actually loaded, so there // is no point having these separate entities. This belongs here. public class wadfile_info_t { @@ -15,4 +15,4 @@ public class wadfile_info_t { public InputStream handle; public boolean cached; // Whether we use local caching e.g. for URL or zips public long maxsize = -1; // Update when known for sure. Will speed up seeking. -} +} \ No newline at end of file diff --git a/src/w/wadheader_t.java b/src/w/wadheader_t.java index b033d0c..7a7f6fa 100644 --- a/src/w/wadheader_t.java +++ b/src/w/wadheader_t.java @@ -46,4 +46,4 @@ public void write(DataOutputStream dos) } -} +} \ No newline at end of file diff --git a/src/w/wadinfo_t.java b/src/w/wadinfo_t.java index 483fc40..99b1c41 100644 --- a/src/w/wadinfo_t.java +++ b/src/w/wadinfo_t.java @@ -17,4 +17,4 @@ public void read(DataInputStream f) throws IOException { infotableofs = DoomIO.readUnsignedLEInt(f); } -} +} \ No newline at end of file