diff --git a/docs/rh-log.txt b/docs/rh-log.txt index ac304a52d..21d2534d0 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,3 +1,645 @@ +January 3, 2010 (Changes by Graf Zahl) +- fixed: The player setup menu used the main menu's line spacing which + for all non-Doom games was too wide. +- fixed: Strife's dialogues cannot use the new options menu code to draw + themselves so now they use a stripped down version of the old code. +- Replaced I_MSTime with I_FPSTime in node builder timing because basetime + will not be initialized yet if a map is started directly from the commandline. + +January 2, 2010 (Changes by Graf Zahl) +- fixed: Polyobjects could contain segs that weren't flagged as such. +- fixed: Trying to show a popup crashed in the SBARINFO code because of a + missing NULL pointer check. +- fixed: The ACS thinker needs its own statnum above all actors. Otherwise + order of execution is not guaranteed. +- fixed: Only ActorMovers should go into STAT_ACTORMOVER, not all PathFollowers. +- fixed: SBARINFO's DrawGem command accepted a size value of 0 and divided + by it. Reinstated the old '+1' this command had in the old code. +- fixed: The floor waggle code used FloatBobOffsets as sine table but this + only has 64 entries and is not precise enough. It now uses finesine instead. +- fixed: When compositing a multipatch texture any patch that is a multpatch + texture itself and contains rotations may not be composited directly into + the destination buffer. This must be done with an intermediate buffer. +- Fixed: Drawing a slider in the options menu did not scale the x-coordinate. +- Fixed: If the alt HUD had to draw negative numbers the minus sign was misplaced + due to incorrect texture coordinate calculations. +- changed option menu scaling for widescreen modes so that it doesn't scale down + so quickly. +- made some error messages in DECORATE that don't affect the parsing non-fatal + so that the parser can continue to find more problems. + +January 1, 2010 (Changes by Graf Zahl) +- added initial support for a GAMEINFO lump in PWADs. When the game is started + all files loaded with '-file' are scanned for this lump. This lump is read + before any WAD initialization takes place, in particular the IWAD is not yet + loaded at this time. This allows PWADs the option to specify an IWAD they + want to run with and optionally autoload external resource WADs. +- Fixed a few places where FixPathSeperator was called with a locked FString buffer. + It's better to use the FString version of this function instead. +- replaced wadlist_t with an array of FStrings and added a list parameter to + everything that eventually calls D_AddFile. Also create the list of files + loaded on the command line separately to allow further checks on them. +- Added Blzut3's Solaris patch. +- Fixed: Heretic's Weredragon (Beast) should not have a melee state. + +January 1, 2010 (SBARINFO update) +- Reorganized the SBarInfo code. +- Added interpolate() flag to drawnumber, drawbar, and drawgem. The old + way of interpolating the health and armor is depreciated. +- Added: armortype to drawswitchableimage loosely based on Gez's submission. +- As an extension to the previous you can now use comparison operators on + inventory items and armortype in drawswitchableimage. + +January 1, 2010 (Changes by Graf Zahl) +- Added Blzut3's Solaris patch. +- Fixed: Heretic's Weredragon (Beast) should not have a melee state. + +December 31, 2009 (Changes by Graf Zahl) +- fixed: FastProjectile was missing all sky checks when the projectile's move + was blocked. + +December 30, 2009 +- Fixed: A_ThrowGrenade used the same code as the old fighter flechette, so + it was just as broken at aiming up and down. + +December 30, 2009 (Changes by Graf Zahl) +- fixed: Movement performed by actor movers was not interpolated because + it happened outside the moved actor's Tick function. This got particularly + obvious with moving skybox viewpoints (See Daedalus's MAP21 intro for a good + example.) + Fixing this exposed a design flaw in the thinker system: + Having every single actor default to the highest available statnum means that + nothing can be placed in a slot where it is guaranteed to be run after all actors + have ticked. But this is required for any thinker that moves an actor + (i.e. AActorMover and DSectorEffect.) With DSectorEffect it just went unnoticed + because they were added at the end of the list so almost nothing they moved was + behind them in a thinker list. However, when an actor was spawned on a moving + floor it did not move smoothly. The default statnum is now 100 so that there's + sufficient slots above where such thinkers can be placed. + +December 28, 2009 +- Fixed: Shooting up and down with AArtiPoisonBag3::Use() was completely + broken. I don't know what I thinking when I plugged in 2*finesine[pitch] + for Hexen's lookdir, because that's totally wrong. Not only is the + magnitude far too low, but it also aims in the opposite direction you + are looking. The new code only attempts to be close to Hexen's original + while looking straight ahead and extrapolates that to other angles using + proper 3D math. + +December 28, 2009 (Changes by Graf Zahl) +- Added full sound definitions for Heretic's ChickenPlayer and Hexen's + PigPlayer (submitted by NeuralStunner.) +- Added unmorph fix by Gez. +- merged all portals with the same displacement together. While this provides + a mild performance increase it's not what I hoped it would do... +- Moved portal initialization for the portal things to P_SpawnSpecials + instead of having the things self-initialize in PostBeginPlay. This was + done to ensure that the portals are fully set up when the game begins. + Otherwise there is no decent way to let the renderer post-process this + information during setup. +- Changed: For 800x600 the default scaling handling of the options menu + makes it become too small so for any resolution with a width between + 800 and 959 it has been reverted to the regular clean scaling factor. + +December 27, 2009 (Changes by Graf Zahl) +- added Hirogen2's Backpack fix for sv_unlimited_pickup. +- added a linedef based method to define portals. Portals defined this way + still have the same limitations as those defines with the portal things. + +December 25, 2009 (Changes by Graf Zahl) +- Fixed: Decals could spread to walls which had a decal-less texture or + were flagged not to have decals. +- Fixed: DBaseDecal/DImpactDecal::CloneSelf never checked the return value + from their StickToWall call and left unplaced decals behind if that happened. +- Reintroduced Doom.exe's player_t::usedown variable so that respawning a + player does not immediately activate switches. oldbuttons was not usable + for this. This also required that CopyPlayer preserves this info. +- Fixed: When restarting the music there was a NULL pointer check missing + so it crashed when the game was started wi +- Fixed: If the Use key is used to respawn the player it must be cleared + so that it doesn't trigger any subsequent actions after respawning. +- Fixed: Resurrecting a monster did not restore flags5 and flags6. +- Fixed: Projectiles which killed a non-monster were unable to determine + what precisely they hit because MF_CORPSE is only valid for monsters. + A new flag, MF6_KILLED that gets set for all objects that die, was added + for this case. +- Added a generic A_Weave function that exposes all possible options of + A_BishopMissileWeave and A_CStaffMissileSlither. These 2 functions are + no longer needed from DECORATE and therefore deprecated. + +December 24, 2009 +- The options menu no longer scales up so quickly, so it can fit wider text + onscreen. In addition, it now uses the whole height available to it. Also, + at lower resolutions, items on the compatibility options menu now cut off + the beginning of the option label rather than the option setting, making + this menu useable where previously it was not. +- Added a channel parameter to the sector overload of SN_StopSequence() so + it can be properly paired with calls to SN_StartSequence(). +- Fixed: P_CheckPlayerSprites() ignored the MF4_NOSKIN flag. It now also sets + the X scale, so switching skins while morphed does not produce weird + stretching upon unmorphing. +- Fixed: Calling S_ChangeMusic() with the same song but a different looping + flag now restarts the song so that the new looping setting can be applied. + (This was easier than modifying every music handler to support modifying + loop changes on the fly, which seems like overkill.) +- Fixed: savepatchsize was declared incorrectly in d_dehacked.cpp:DoInclude(). +- Changed AFastProjectile::Effect() so that it sets the spawned trail to face + same direction as the projectile. + +December 24, 2009 (Changes by Graf Zahl) +- fixed: The UDMF blockfloaters flag was misnamed. Changed to match the spec. + +December 23, 2009 (Changes by Graf Zahl) +- made the initial weave index for A_BishopMissileWeave and A_CStaffMissileSlither + a configurable actor property. +- added a menu item for snd_channels. + +December 20, 2009 (Changes by Graf Zahl) +- Fixed: The Dehacked parser could read past the end of the file if the last + element was improperly defined. + +December 19, 2009 +- Extended MF3_SKYEXPLODE to apply to horizon walls as well. + +December 19, 2009 (Changes by Graf Zahl) +- Fixed: It was not possible to set the ammo type of a weapon explicitly to + 'none'. + +December 18, 2009 +- A_FreezeDeath() now removes fuzz effects. +- In mus2midi.cpp, added range checking to MUS_SYSEVENT and MUS_CTRLCHANGE, + and masking for note-off keys, note-on velocities, and program changes. + +December 18, 2009 (Changes by Graf Zahl) +- added all known maps requiring inverted sprite sorting to compatibility.txt. +- added compatibility option to invert sprite sorting. Apparently Doom.exe + originally sorted them differently than most source port and on some maps + which depends on this it doesn't look right (e.g. Strain MAP13) + +December 17, 2009 +- Fixed: Using Transfer_Heights with the SECF_UNDERWATER and + SECF_FAKEFLOORONLY flags applied the water effect to the ceiling and not + just the floor. +- Replaced sprite sorting with a stable sort. Performance at the start of + nuts.wad seems the same. + +December 16, 2009 (Changes by Graf Zahl) +- Fixed: Morphed players tried endlessly to switch to a weapon they picked up. +- fixed: P_DamageMobj just set an ice corpse's velocity to 0 to make it shatter. + But that's insufficient because it doesn't factor in any subsequent velocity + change that happens between the damaging and the next call to A_FreezeDeathChunks. +- fixed: The TimeFreezer did not freeze other players' controls in a + multiplayer game. +- fixed: DECORATE's 'gravity' property incorrectly messed around with the + NOGRAVITY flag. +- fixed: Hitscan attacks didn't check the puff's replacement for damage types. + +December 13, 2009 (Changes by Graf Zahl) +- fixed: old-style DECORATE definitions with non-alphanumeric characters in + the name produced an error. + +December 12, 2009 +- Added a DMG_NO_FACTOR flag for P_DamageMobj(). A_KillChildren, A_KillMaster, + and A_KillSiblings now use it. +- Added a damage type parameter to A_KillChildren, A_KillMaster, and + A_KillSiblings. + +December 11, 2009 (Changes by Graf Zahl) +- fixed: Auto-COMPAT_SHORTTEX for IWADs must be set per IWAD, not in general + for Doom. +- added autodetection of Harmony's IWAD. +- Added SnailMan's updated language.ita file. + +December 11, 2009 +- Fixed: You should still be able to pick up ammo that has a max amount set + at 0. +- Added a few NULL screen checks. + +December 6, 2009 (Changes by Graf Zahl) +- added some code that prevents overlapping monsters from getting stuck in + each other. PIT_CheckThing will return true under the following contitions + now: + * It was called from P_Move + * The actor that is blocking the move already overlaps with the monster + that is being moved. + * the move will take the 2 actors further apart. + +December 1, 2009 +- Added another surface to receive a copy of the top back buffer immediately + before it is presented. This effectively produces a copy of the front + buffer without the performance penalty of GetFrontBufferData, so fullscreen + wipe preparation and screenshots are faster now. At lower resolutions, + always copying the backbuffer does incur a slight FPS hit, but it's + practically free at higher resolutions. + +November 30, 2009 +- The initial wipe screen is now kept in video memory. I had previously + assumed that since the wipes only run at 35 FPS, the time spent DMA'ing + it from system to video memory would be acceptable. Apparently I was wrong. + In particular, updating the same surface several times probably has to + synchronize between each one, making melt particularly slower than it + needs to be. + +November 29, 2009 (Changes by Graf Zahl) +- fixed: Line_SetBlocking and Line_SetTextureScale were not in the list + of action specials used by DECORATE or MAPINFO. +- fixed: Teleport_NoStop was not in the line special function table. + +November 28, 2009 +- Fixed: The FPS meter cannot use I_MSTime(), because if the game is started + with +vid_fps 1, it can need the time before the timer is ready to start. +- Initialize TempRenderTexture and the back buffer to black upon creation. +- Fixed: Windowed mode always needs to draw to the temporary surface, even + when not gamma correcting, so that D3DFB::GetCurrentScreen() can read from + it. +- Use the spawned class's scale as default for ScriptedMarine instead of + DoomPlayer. + +November 28, 2009 (Changes by Graf Zahl) +- fixed: Morph weapons weren't destroyed because the code checked for + them in the unmorphed player class. +- fixed: With padding the largest texture to fit into a page is 254x254. + +November 27, 2009 (Changes by Graf Zahl) +- fixed an uninitialized variable in p_xlat.cpp (Thanks, MSVC for not + warning about such an obvious problem! :( ) +- fixed: The charge attack of Heretic's imp is not precisely the same + as A_SkullAttack with a different speed so A_ImpMsAttack has been + reinstated. + +November 25, 2009 +- Make the palette indexes used by FRemapTable subject to the global remap + table, just as the images they translate are. + +November 24, 2009 (Changes by Graf Zahl) +- Added MF4_ALLOWPARTICLES checks to blood spawning code. +- Fixed: EV_DoDonut, EV_DoElevator and EV_StartWaggle did not to any 0-tag + checks. +- Fixed: Doom line type 44 (lower ceiling to 8 above floor) must halt + movement if blocked which essentially means it acts like a Hexen-style + crusher. +- Fixed: Not all places checking for player start spots did it correctly. + The editor number for player start spot 5 is now stored in the game info + so that there's only one place where this check needs to be done. +- Fixed: WIF_NOAUTOAIM only worked for projectiles. + +November 23, 2009 +- Added Windows 7 (aka Windows NT 6.1) and Server 2008 identification to + I_DetectOS(). +- Fixed: ArtiPork did not use all its sprite frames. +- Fixed: FWadCollection::AddFile() did not call FixPathSeperator(), so + savegames would hold the full file path for wads that had been specified + with backslash characters, because GetWadName() would not trim off the + path. + +November 22, 2009 (Changes by Graf Zahl) +- extended Doom map format linedef translator so that it also handles the flags. + +November 19, 2009 +- Replaced toint/quickertoint with the portable routines from xs_Float.h. The + former used fistp, which is not portable across platforms, so cannot be + used in the play simulation. They were only suitable for the renderer. + xs_Float.h also has a very fast float->fixed conversion, so FLOAT2FIXED + uses that now. + (And I also learned that the FPU's round to nearest is not the rounding I + learned in grade school but actually Banker's Rounding. I had no idea.) + (Also, also, the only thing that could have made quickertoint faster than + toint was that it stored a 32-bit int. I never timed them, and I doubt in + practice there was any real difference between the two.) + +November 18, 2009 +- Added padding around packed textures to compensate for apparent NVidia + texture coordinate imprecision. + +November 17, 2009 +- Fixed two bugs in FMODSoundRenderer::HandleChannelDelay(): + * Looping sounds that have been playing for a very long time, were evicted, + and then were restarted need to have their positions clamped to lie + within the bounds of the sounds. If we try to set a start position very + far beyond the end, it will overflow inside FMOD and not work. + * A start time of 0 is not actually valid and means the sound was never + assigned a start time. +- The latter bug also reveals a problem with starting looped sounds evicted: + They need to be assigned a start time so if they should have the opportunity + to start later, they will be properly synchronized. + +November 17, 2009 (Changes by Graf Zahl) +- fixed: P_NowayTraverse was called with a trace distance of 128 instead of + the 64 that should have been used. +- fixed: R_PointToAngle could overflow with very long vectors passed to + it. This caused rendering bugs on some maps. (Interestingly the only + other port having a safeguard for this in place was PrBoom.) +- reverted the change that makes 0-damage projectiles call P_DamageMobj. + Both Hexen and Heretic depend on such projectiles not doing it as do many + mods that create snow/rain effects plus any terrain splash mod. + +November 15, 2009 (Changes by Graf Zahl) +- fixed: fullscreen images with texture scaling used the unscaled size for + positioning. To avoid future problems with them I added a new DTA_Fullscreen + option for DrawTexture. +- fixed: The sky baseline position needs to take texture scaling into account. + +November 14, 2009 +- Do not squash skies taller than 200 into square pixels. + +November 14, 2009 (Changes by Graf Zahl) +- Added skillinfo fix by Gez. +- added a r_scaletallskies CVAR so that sky positioning can be checked + more easily. +- fixed: Skies with a height of exactly 200 pixels should not be stretched. + +November 13, 2009 +- More sky changes: Textures taller than 200 pixels but shorter than 241 + are scaled to the height of a 200 pixel tall sky. Skies taller than 240 + use the same scale as a 240 tall sky but are shifted down to make the + top of the texture align with the top of the screen when looking fully up. + Thus, by using a sky texture with a height of 240 or more pixels, the sky + will be drawn with square pixels instead of the vertically elongated ones + imposed by Doom's native 320x200 resolution. + +November 12, 2009 +- Improved sky stretching a bit: It now only stretches the sky as tall as it + needs to be: 228 pixels, not 256. It no longer stretches horizontally, + either. + + The reason it stretches to 228 and not 200 pixels is because Doom shifted + its sky texture down 28 pixels. By stretching to 228 pixels, we can keep + the sky tiled at the same height on the horizon. Skies 200 pixels tall + (or more) will continue to tile at the center of the screen when looking + directly ahead. +- Cleaned up win32/i_system.cpp. +- Put back the previous event-driven ticker, except now the timer isn't + started until the first time it is needed. + +November 11, 2009 +- Modified the event-driven ticks to use the same code for calculating the + time as the polled timer so that the timer does not start running until the + first time it is used. +- Removed the srand() call from D_DoomMain(), because it started the game + timer running prematurely, and we never call rand() anywhere. (Not to + mention, even if we did use rand(), always seeding it with 0 is rather + pointless.) +- Fixed: The framerate was not capped before starting a game. +- Removed the one embedded DeHackEd lump restriction. +- Fixed: nofreeaim in P_SpawnPlayerMissile() was broken. +- Fixed: MBF sky Y offsets were ignored. X offsets should also be applied to + the sky cylinder, not the screen like Hexen scrolling skies. + +November 9, 2009 +- Maps inside zips can now satisfy the map checks for IWAD detection. +- Fixed: F7ZFile did not delete its Archive when destroyed. + +November 7, 2009 +- Fixed: The x64 Release build was configured to use the 32-bit GME, and + neither release nor debug builds built the library. +- Fixed: R_GetOneSkyColumn() and R_GetTwoSkyColumns are mulscaling an + unsigned integer that can use all 32 bits. They must therefore use + the unsigned mul instruction rather than the signed imul instruction. +- Fixed several signed/unsigned comparison and possibly uninitialized + variable warnings flagged by GCC. + +November 5, 2009 (Changes by Graf Zahl) +- fixed: The 'new format only' flag for MAPINFO options was never checked. + (ZDoom itself doesn't use it yet so it's only relevant for child ports.) + +November 3, 2009 +- Fixed: S_RestartSound() cleared the evicted flag even if the sound + was not restarted because it was too close to too many other identical + sounds that were already playing. +- Added virtual status and audibility to the noise debug display. +- Added a command line option -warpwipe to perform the screen wipe if you + start with -warp or +map. + +October 31, 2009 +- Changed all coordinates for DrawTexture() to floating point so that the + player sprites will retain the same precision they had when they were + rendered as part of the 3D view. (needed for propery alignment of flashes + on top of weapon sprites) It worked just fine for D3D, but software + rendering was another matter. I consequently did battle with imprecisions + in the whole masked texture drawing routines that had previously been + partially masked by only drawing on whole pixel boundaries. Particularly, + the tops of posts are calculated by multiplying by spryscale, and the + texture mapping coordinates are calculated by multiplying by dc_iscale + (where dc_iscale = 1 / spryscale). Since these are both 16.16 fixed point + values, there is a significant variance. For best results, the drawing + routines should only use one of these values, but that would mean + introducing division into the inner loop. If the division removed the + necessity for the fudge code in R_DrawMaskedColumn(), would it be worth it? + Or would the divide be slower than the fudging? Or would I be better off + doing it like Build and using transparent pixel checks instead, not + bothering with skipping transparent areas? For now, I chop off the + fractional part of the top coordinate for software drawing, since it was + the easiest thing to do (even if it wasn't the most correct thing to do). + +October 29, 2009 +- Fixed: Sprites and decals that are drawn with addition must fade to black. +- Make TranslateToStartSpot() set the new sector references for a polyobj's + walls so that P_CheckSwitchRange() will work with them. +- Fixed: An unspecified save_dir will now save to the program directory on + Windows. (Other operating systems already use the user's home directory + instead.) +- Fixed: S_EvictAllChannels() must replace the channel's start time with its + position when evicting sounds, because restarting the sound system causes + the DSP clock to restart at 0, so start times that were recorded before + the reset are no longer applicable after the reset. +- Fixed: S_StopChannel() always set the channel's actor to NULL, eliminating + origin information when resetting the sound system. + +October 28, 2009 +- Added Gez's patch for IWAD detection of Blasphemer and Action Doom 2. +- Fixed: 0 damage projectiles did not call P_DamageMobj. +- Fixed: Do not exit P_DamageMobj early if damage is 0, so we can still get + the side effects from it. PainThreshold also needs to be inclusive, as + the docs already state. + +October 26, 2009 +- Changes to both A_MonsterRail() and A_CustomRailgun(): Save actor's pitch, + use a larger aiming range, ignore non-targets in P_AimLineAttack(), and + aim at the target anyway even if P_AimLineAttack() decides it has no + chance of hitting. +- Added another parameter to P_AimLineAttack(): A target to be aimed at. If + this is non-NULL, then all actors between the shooter and the target will + be ignored. +- Added new sound sequence ACS functions: + SoundSequenceOnActor(int tid, string seqname); + SoundSequenceOnSector(int tag, string seqname, int location); + SoundSequenceOnPolyobj(int polynum, string seqname); + SoundSequenceOnSector takes an extra parameter that specifies where in the + sector the sound comes from (floor, ceiling, interior, or all of it). See + the SECSEQ defines in zdefs.acs. +- Fixed: R_RenderDecal() must save various Wall globals, because the originals + may still be needed. In particular, when drawing a seg with a midtexture is + split by foreground geometry, the first drawseg generated from it will have + the correct WallSZ1,2 values, but subsequent ones will have whatever + R_RenderDecal() left behind. These values are used to calculate the upper + and lower bounds of the midtexture. (Ironically, my work to Build-ify things + had done away with these globals, but that's gone now.) + +October 24, 2009 (Changes by Graf Zahl) +- fixed: sector_t::GetHeightSec checked the wrong MoreFlags. +- made max. view pitch a property of the renderer so that it's overridable without + changing game code. +- made SpawningMapThing an argument of AActor::StaticSpawn instead of a global + variable. +- added a stub to the DECORATE parser for defining dynamic lights directly + in DECORATE. This is needed so that ZDoom remains compatible with any DECORATE + which uses this GZDoom feature in the future. + +October 24, 2009 +- Removed the Actor uservar array and replaced it with user-defined variables. + A_SetUserVar/SetUserVariable/GetUserVariable now take a variable name + instead of an array index. A_SetUserArray/SetUserArray/GetUserArray + have been added to access elements in user-defined arrays. +- Rewrote wide sky texture scaling again. This time, it should work for any + size textures at any scale. I also tried doing sky scrolling on the sky + cylinder, but that didn't look so good, so I left it in screen space. + +October 23, 2009 +- Fixed: When giving completely new ammo, the backpack did not clamp the + amount given to the ammo's max amount. +- Fixed drawing of wide high resolution skies. (At least for the samples I + received. I'm not convinced that it's yet fixed for the general case.) + +October 19, 2009 (Changes by Graf Zahl) +- fixed: Setting the first state's duration of a fast projectile to 0 caused + an underflow and blocked all further state changes. + +October 18, 2009 (Changes by Graf Zahl) +- fixed: FMultiPatchTexture::MakeTexture was missing a range check for the + special colormap index. + +October 17, 2009 (Changes by Graf Zahl) +- Fixed: 3DMidtex checks were treating the Null texture as a valid texture. +- Fixed: The rail sound's position was not clamped to the actual range between + the trail's start and end point. +- Fixed: Explosions no longer caused splashes. +- Fixed: Copying translations to lower decals had the shade color check wrong. +- Fixed: Waggling floors did not moved attached geometry. +- Cleaned up p_floor.cpp so that related parts of the code are grouped together. +- fixed: The translation addition broke parsing of palette index based translations. + +October 16, 2009 (Changes by Graf Zahl) +- added 'defaultterrain' option to terrain parser for mods that want to have + a different default terrain than a generic solid surface. +- added format char processing to A_Print(Bold) and all printable messages + that can be defined in DECORATE. +- Fixed: The railgun code ignored MF3_ALWAYSPUFF. +- added desaturated translations. +- added optional state parameters to A_ReFire and A_GunFlash and A_CountdownArg. + +October 15, 2009 (Changes by Graf Zahl) +- added ACS CheckActorClass function +- fixed: When a blasted actor collided with another one this other actor's + DONTBLAST flag was not checked. +- added a global DamageFactor actor property. All damage this actor takes is multiplied + by this factor in addition to damage type specific damage factors. +- added better earthquake functions for ACS and DECORATE. + +October 10, 2009 (Changes by Graf Zahl) +- Added MF6_NOTRIGGER flag that disables all line actions for an actor. + +October 9, 2009 (Changes by Graf Zahl) +- Added Gez's seeker missile submission. +- Added Gez's thing activation submission. +- added a NULL pointer check to fog spawning in unmorphing code. +- fixed: frozen corpses need to be treated as solid by z-movement code. +- fixed: AAmbientSound::Serialize was adjusting its timer value for savegames + even when it was set to a 'don't check' value. + +October 8, 2009 +- Reinstated the off-by-one check in D3DFB from r399. I thought I could get by + at just fixing it at a specific value, since the supply of SM14 cards isn't + all that diverse and all from ATI, but apparently Radeon 8500s and 9000s + have different precision levels in their pixel shaders. See bug report + + +October 8, 2009 (Changes by Graf Zahl) +- Added a PainThreshold actor property. +- fixed: Teleport_EndGame did not set the end sequence name properly. + +October 7, 2009 +- Since I am currently without a primary video card and stuck with this + Mobility Radeon 9000 (on a PCI card, no less!), I have decided to give the + PS14 support some loving: D3D windowed gamma now works on these cards using + a texture lookup for the gamma table. Sadly, this halves my framerate, so + setting gamma to 1 will skip the gamma correction, as it was before, for + full speed. (On my 8800 GT, the gamma correction was free.) + +October 4, 2009 (Changes by Graf Zahl) +- Deleted a_magewand.cpp because it only contained unused code. +- Fixed: The conversation code tried to get the player's tag instead of the + NPC's he is talking to when it had no given name. + +October 3, 2009 (Changes by Graf Zahl) +- Added Gez's MageWandMissile customization patch but moved the new functionality + into the FastProjectile base class and removed the native MageWandMissile + class, using the generic functionality instead. +- Fixed: GetReplacement and GetReplacee always checked the skill definitions, + even if they weren't supposed to be used. It was also missing a range check + for 'gameskill'. + +October 2, 2009 (Changes by Graf Zahl) +- fixed: Savegames stored the global fixed light levels when saving a player. + +October 1, 2009 (Changes by Graf Zahl) +- Fixed some GCC warnings. +- fixed: The BossCube could be blocked by floors and ceiling resulting + in incorrect movement. I changed it so that A_BrainSpit now sets the + MF5_NOINTERACTION flag for anything it spawns that has the MF_NOCLIP + flag. For travelling cubes active collision detection makes no sense + and only causes problems. This should also make the boss brain + work in the other games which previously were excluded by a game mode + check in the movement code. +- fixed: ACS's GetUserVariable did not work for the script activator. +- fixed: Moving floors could be blocked by 2 actors without MF2_PASSMOBJ + overlapping each other (common mapping bug, check Herian 2 MAP30.) + +September 30, 2009 (Changes by Graf Zahl) +- Fixed: Coordinate handling for multipatch texture compositing was not correct + for true color. Instead of using a clipping rectangle on the destination it + tried to alter the source offsets which produced incorrect results for + mirrored or rotated patches. + +September 29, 2009 +- Fixed: Alt+F4 no longer quit the program. + +September 28, 2009 (Changes by Graf Zahl) +- Added BHS's death special flags submission. +- Added Gez's A_Blast submission. + +September 27, 2009 (Changes by Graf Zahl) +- Fixed: Floor and ceiling huggers' velocity was wrong when used with + P_SpawnPlayerMissile. +- Fixed: When hitting a voodoo doll the real player needs to be checked for + invulnerability. +- Fixed: G_QueueBody was not notifying the translation that it was changed. +- Fixed: The multitexture composition code was missing a NULL pointer check. +- fixed: The changes for new colormap handling in FMultipatchtexture were incomplete. + Some code was still checking Blend.r instead of the full variable for colormap indices. + +September 26, 2009 +- Fixed: R_DrawPSprite() did not initialize the colormap for the targeter + vissprites. +- Added a check for SPAC_AnyCross to P_TestActivateLine() before the "monster" + activation checks. They are actually non-player checks and interfered with + SPAC_AnyCross. +- idclev and hxvisit are no longer considered cheats, however, they are still + invalid for net games. hxvisit was erroneously accepted for net games + before, which it shouldn't have, since it's basically idclev for Hexen. + +September 24, 2009 +- Fixed: "give health" without an amount would set your health to IDDQD health + instead of the player class's maximum. +- Fixed: A_CStaffCheck() assumed the player's max health was 100 instead + of getting checking for the true maximum. +- Changed P_XYMovement() to not call P_SlideMove() if the act of being + blocked changed the actor's velocity. I'm not entirely happy with this, + but it gets push-activated force fields to work. +- Fixed: FMultiPatchTexture::CopyTrueColorPixels() should clear the buffer + first before drawing into it if the copy op passed to it is OP_OVERWRITE. + FTexture::FillBuffer() sets this to erase whatever texture might have been + in the space it is going into. + September 22, 2009 - Added a technique to try and minimize input lag with vsync enabled: Two surfaces are alternately locked for read-only access each frame, forcing diff --git a/specs/udmf_zdoom.txt b/specs/udmf_zdoom.txt index fe64a8021..15512d9fb 100644 --- a/specs/udmf_zdoom.txt +++ b/specs/udmf_zdoom.txt @@ -192,6 +192,7 @@ Note: All fields default to false unless mentioned otherwise. 208: TranslucentLine, arg0 (arg0 must be preserved) 1: Polyobj_StartLine, arg3 5: Polyobj_ExplicitLine, arg4 + 181: Plane_Align, arg2 215: Teleport_Line, arg0 222: Scroll_Texture_Model, arg0 (arg0 must be preserved) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 50da166d9..67e83a96e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -696,8 +696,7 @@ add_executable( zdoom WIN32 g_shared/a_weaponpiece.cpp g_shared/a_weapons.cpp g_shared/hudmessages.cpp - g_shared/sbarinfo_display.cpp - g_shared/sbarinfo_parser.cpp + g_shared/sbarinfo.cpp g_shared/sbar_mugshot.cpp g_shared/shared_hud.cpp g_shared/shared_sbar.cpp @@ -779,6 +778,11 @@ add_executable( zdoom WIN32 set_source_files_properties( xlat/parse_xlat.cpp PROPERTIES OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/xlat_parser.c" ) set_source_files_properties( sc_man.cpp PROPERTIES OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h" ) +if(${CMAKE_SYSTEM_NAME} STREQUAL "SunOS") + # [BL] Solaris requires these to be explicitly linked. + set( ZDOOM_LIBS ${ZDOOM_LIBS} nsl socket) +endif(${CMAKE_SYSTEM_NAME} STREQUAL "SunOS") + target_link_libraries( zdoom ${ZDOOM_LIBS} gme gdtoa dumb lzma ) include_directories( . g_doom diff --git a/src/actionspecials.h b/src/actionspecials.h index aefdcc951..5d9471fec 100644 --- a/src/actionspecials.h +++ b/src/actionspecials.h @@ -53,6 +53,9 @@ DEFINE_SPECIAL(Sector_SetLink, 51, 4, 4, 4) DEFINE_SPECIAL(Scroll_Wall, 52, 5, 5, 5) DEFINE_SPECIAL(Line_SetTextureOffset, 53, 5, 5, 5) DEFINE_SPECIAL(Sector_ChangeFlags, 54, 3, 3, 3) +DEFINE_SPECIAL(Line_SetBlocking, 55, 3, 3, 3) +DEFINE_SPECIAL(Line_SetTextureScale, 56, 5, 5, 5) +DEFINE_SPECIAL(Sector_SetPortal, 57, -1, -1, 5) DEFINE_SPECIAL(Plat_PerpetualRaise, 60, 3, 3, 3) DEFINE_SPECIAL(Plat_Stop, 61, 1, 1, 1) @@ -103,19 +106,11 @@ DEFINE_SPECIAL(Light_Glow, 114, 4, 4, 4) DEFINE_SPECIAL(Light_Flicker, 115, 3, 3, 3) DEFINE_SPECIAL(Light_Strobe, 116, 5, 5, 5) DEFINE_SPECIAL(Light_Stop, 117, 1, 1, 1) - +DEFINE_SPECIAL(Plane_Copy, 118, -1, -1, 5) DEFINE_SPECIAL(Thing_Damage, 119, 2, 3, 3) DEFINE_SPECIAL(Radius_Quake, 120, 5, 5, 5) // Earthquake DEFINE_SPECIAL(Line_SetIdentification, 121, -1, -1, 5) -#if 0 // Skull Tag specials that might be added later -Thing_SetGravity, 122, -1, -1) -Thing_ReverseGravity, 123, -1, -1) -Thing_RevertGravity, 124, -1, -1) -#endif DEFINE_SPECIAL(Thing_Move, 125, 2, 3, 3) -#if 0 // Skull Tag special I doubt I will add -Thing_SetSprite, 126, -1, -1) -#endif DEFINE_SPECIAL(Thing_SetSpecial, 127, 5, 5, 5) DEFINE_SPECIAL(ThrustThingZ, 128, 4, 4, 4) DEFINE_SPECIAL(UsePuzzleItem, 129, 2, 5, 5) @@ -136,6 +131,7 @@ DEFINE_SPECIAL(Teleport_NoStop, 154, 2, 3, 3) // Although ZDoom doesn't support them it's better to have them defined so that // WADs using them somewhere can at least be started without aborting due // to an error message. +DEFINE_SPECIAL(SetGlobalFogParameter, 157, 2, 2, 2) DEFINE_SPECIAL(FS_Execute, 158, 1, 4, 4) DEFINE_SPECIAL(Sector_SetPlaneReflection, 159, 3, 3, 3) DEFINE_SPECIAL(Sector_Set3DFloor, 160, -1, -1, 5) @@ -154,7 +150,7 @@ DEFINE_SPECIAL(Thing_Hate, 177, 2, 3, 3) DEFINE_SPECIAL(Thing_ProjectileAimed, 178, 4, 5, 5) DEFINE_SPECIAL(ChangeSkill, 179, 1, 1, 1) DEFINE_SPECIAL(Thing_SetTranslation, 180, 2, 2, 2) -DEFINE_SPECIAL(Plane_Align, 181, -1, -1, 2) +DEFINE_SPECIAL(Plane_Align, 181, -1, -1, 3) DEFINE_SPECIAL(Line_Mirror, 182, -1, 0, 0) DEFINE_SPECIAL(Line_AlignCeiling, 183, 2, 2, 2) DEFINE_SPECIAL(Line_AlignFloor, 184, 2, 2, 2) @@ -163,7 +159,7 @@ DEFINE_SPECIAL(Sector_SetCeilingPanning, 186, 5, 5, 5) DEFINE_SPECIAL(Sector_SetFloorPanning, 187, 5, 5, 5) DEFINE_SPECIAL(Sector_SetCeilingScale, 188, 5, 5, 5) DEFINE_SPECIAL(Sector_SetFloorScale, 189, 5, 5, 5) -DEFINE_SPECIAL(Static_Init, 190, -1, -1, 3) +DEFINE_SPECIAL(Static_Init, 190, -1, -1, 4) DEFINE_SPECIAL(SetPlayerProperty, 191, 3, 3, 3) DEFINE_SPECIAL(Ceiling_LowerToHighestFloor, 192, 2, 2, 2) DEFINE_SPECIAL(Ceiling_LowerInstant, 193, 3, 3, 3) diff --git a/src/actor.h b/src/actor.h index b490aeade..6e8af2498 100644 --- a/src/actor.h +++ b/src/actor.h @@ -217,7 +217,7 @@ enum MF3_CRASHED = 0x00200000, // Actor entered its crash state MF3_FULLVOLDEATH = 0x00400000, // DeathSound is played full volume (for missiles) MF3_AVOIDMELEE = 0x00800000, // Avoids melee attacks (same as MBF's monster_backing but must be explicitly set) - /* = 0x01000000, */ + MF3_SCREENSEEKER = 0x01000000, // Fails the IsOkayToAttack test if potential target is outside player FOV MF3_FOILINVUL = 0x02000000, // Actor can hurt MF2_INVULNERABLE things MF3_NOTELEOTHER = 0x04000000, // Monster is unaffected by teleport other artifact MF3_BLOODLESSIMPACT = 0x08000000, // Projectile does not leave blood @@ -316,6 +316,9 @@ enum MF6_ARMED = 0x00002000, // From MBF: Object is armed (for MF6_TOUCHY objects) MF6_FALLING = 0x00004000, // From MBF: Object is falling (for pseudotorque simulation) MF6_LINEDONE = 0x00008000, // From MBF: Object has already run a line effect + MF6_NOTRIGGER = 0x00010000, // actor cannot trigger any line actions + MF6_SHATTERING = 0x00020000, // marks an ice corpse for forced shattering + MF6_KILLED = 0x00040000, // Something that was killed (but not necessarily a corpse) // --- mobj.renderflags --- @@ -431,17 +434,23 @@ enum EBounceFlags }; -// Used to affect the logic for MF5_USESPECIAL and MF6_BUMPSPECIAL +// Used to affect the logic for thing activation through death, USESPECIAL and BUMPSPECIAL // "thing" refers to what has the flag and the special, "trigger" refers to what used or bumped it enum EThingSpecialActivationType { - THINGSPEC_Default = 0, // Normal behavior: a player must be the trigger, and is the activator - THINGSPEC_ThingActs = 1, // The thing itself is the activator of the special - THINGSPEC_ThingTargets = 2, // The thing changes its target to the trigger - THINGSPEC_TriggerTargets = 4, // The trigger changes its target to the thing - THINGSPEC_MonsterTrigger = 8, // The thing can be triggered by a monster - THINGSPEC_MissileTrigger = 16, // The thing can be triggered by a projectile - THINGSPEC_ClearSpecial = 32, // Clears special after successful activation + THINGSPEC_Default = 0, // Normal behavior: a player must be the trigger, and is the activator + THINGSPEC_ThingActs = 1, // The thing itself is the activator of the special + THINGSPEC_ThingTargets = 1<<1, // The thing changes its target to the trigger + THINGSPEC_TriggerTargets = 1<<2, // The trigger changes its target to the thing + THINGSPEC_MonsterTrigger = 1<<3, // The thing can be triggered by a monster + THINGSPEC_MissileTrigger = 1<<4, // The thing can be triggered by a projectile + THINGSPEC_ClearSpecial = 1<<5, // Clears special after successful activation + THINGSPEC_NoDeathSpecial = 1<<6, // Don't activate special on death + THINGSPEC_TriggerActs = 1<<7, // The trigger is the activator of the special + // (overrides LEVEL_ACTOWNSPECIAL Hexen hack) + THINGSPEC_Activate = 1<<8, // The thing is activated when triggered + THINGSPEC_Deactivate = 1<<9, // The thing is deactivated when triggered + THINGSPEC_Switch = 1<<10, // The thing is alternatively activated and deactivated when triggered }; // [RH] Like msecnode_t, but for the blockmap @@ -540,7 +549,7 @@ public: void Serialize (FArchive &arc); - static AActor *StaticSpawn (const PClass *type, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement); + static AActor *StaticSpawn (const PClass *type, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement, bool SpawningMapThing = false); inline AActor *GetDefault () const { @@ -561,6 +570,7 @@ public: // BeginPlay: Called just after the actor is created virtual void BeginPlay (); + virtual void PostBeginPlay (); // LevelSpawned: Called after BeginPlay if this actor was spawned by the world virtual void LevelSpawned (); // Translates SpawnFlags into in-game flags. @@ -602,7 +612,7 @@ public: virtual bool SpecialBlastHandling (AActor *source, fixed_t strength); // Called by RoughBlockCheck - virtual bool IsOkayToAttack (AActor *target); + bool IsOkayToAttack (AActor *target); // Plays the actor's ActiveSound if its voice isn't already making noise. void PlayActiveSound (); @@ -773,6 +783,8 @@ public: TObjPtr LastLookActor; // Actor last looked for (if TIDtoHate != 0) fixed_t SpawnPoint[3]; // For nightmare respawn WORD SpawnAngle; + BYTE WeaveIndexXY; // Separated from special2 because it's used by globally accessible functions. + BYTE WeaveIndexZ; int skillrespawncount; int TIDtoHate; // TID of things to hate (0 if none) FNameNoInit Species; // For monster families @@ -782,7 +794,6 @@ public: int tid; // thing identifier int special; // special int args[5]; // special arguments - int uservar[10]; // user variables, accessible by DECORATE and ACS AActor *inext, **iprev;// Links to other mobjs in same bucket TObjPtr goal; // Monster's goal if not chasing anything @@ -805,6 +816,7 @@ public: fixed_t pushfactor; int lastpush; int activationtype; // How the thing behaves when activated with USESPECIAL or BUMPSPECIAL + int lastbump; // Last time the actor was bumped, used to control BUMPSPECIAL int Score; // manipulated by score items, ACS or DECORATE. The engine doesn't use this itself for anything. FNameNoInit Tag; // Strife's tag name. FIXME: should be case sensitive! @@ -840,7 +852,9 @@ public: fixed_t MaxDropOffHeight, MaxStepHeight; SDWORD Mass; SWORD PainChance; + int PainThreshold; FNameNoInit DamageType; + fixed_t DamageFactor; FState *SpawnState; FState *SeeState; @@ -855,6 +869,7 @@ public: // [RH] Used to interpolate the view to get >35 FPS fixed_t PrevX, PrevY, PrevZ; + angle_t PrevAngle; // ThingIDs static void ClearTIDHashes (); @@ -894,7 +909,6 @@ public: return GetClass()->ActorInfo->FindState(2, names, exact); } - bool HasSpecialDeathStates () const; }; @@ -967,6 +981,7 @@ inline AActor *Spawn (const PClass *type, fixed_t x, fixed_t y, fixed_t z, repla } AActor *Spawn (const char *type, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement); +AActor *Spawn (FName classname, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement); template inline T *Spawn (fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement) diff --git a/src/am_map.cpp b/src/am_map.cpp index e2b62e124..8f6871f99 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -167,6 +167,7 @@ CVAR (Color, am_ovtelecolor, 0xffff00, CVAR_ARCHIVE); CVAR (Color, am_intralevelcolor, 0x0000ff, CVAR_ARCHIVE); CVAR (Color, am_interlevelcolor, 0xff0000, CVAR_ARCHIVE); CVAR (Color, am_secretsectorcolor, 0xff00ff, CVAR_ARCHIVE); +CVAR (Color, am_ovsecretsectorcolor,0x00ffff, CVAR_ARCHIVE); CVAR (Int, am_map_secrets, 1, CVAR_ARCHIVE); CVAR (Bool, am_drawmapback, true, CVAR_ARCHIVE); CVAR (Color, am_thingcolor_friend, 0xfcfcfc, CVAR_ARCHIVE); @@ -413,14 +414,24 @@ void AM_getIslope (mline_t *ml, islope_t *is) } */ +//============================================================================= +// +// called by the coordinate drawer +// +//============================================================================= + void AM_GetPosition(fixed_t &x, fixed_t &y) { x = (m_x + m_w/2) << FRACTOMAPBITS; y = (m_y + m_h/2) << FRACTOMAPBITS; } + +//============================================================================= // // // +//============================================================================= + void AM_activateNewScale () { m_x += m_w/2; @@ -433,9 +444,12 @@ void AM_activateNewScale () m_y2 = m_y + m_h; } +//============================================================================= // // // +//============================================================================= + void AM_saveScaleAndLoc () { old_m_x = m_x; @@ -444,9 +458,12 @@ void AM_saveScaleAndLoc () old_m_h = m_h; } +//============================================================================= // // // +//============================================================================= + void AM_restoreScaleAndLoc () { m_w = old_m_w; @@ -469,9 +486,12 @@ void AM_restoreScaleAndLoc () scale_ftom = MapDiv(MAPUNIT, scale_mtof); } +//============================================================================= // // adds a marker at the current location // +//============================================================================= + bool AM_addMark () { if (marknums[0].isValid()) @@ -484,10 +504,13 @@ bool AM_addMark () return false; } +//============================================================================= // // Determines bounding box of all vertices, // sets global variables controlling zoom range. // +//============================================================================= + static void AM_findMinMaxBoundaries () { min_x = min_y = FIXED_MAX; @@ -515,6 +538,12 @@ static void AM_findMinMaxBoundaries () AM_calcMinMaxMtoF(); } +//============================================================================= +// +// +// +//============================================================================= + static void AM_calcMinMaxMtoF() { fixed_t a = MapDiv (SCREENWIDTH << MAPBITS, max_w); @@ -524,6 +553,12 @@ static void AM_calcMinMaxMtoF() max_scale_mtof = MapDiv (SCREENHEIGHT << MAPBITS, 2*PLAYERRADIUS); } +//============================================================================= +// +// +// +//============================================================================= + static void AM_ClipRotatedExtents (fixed_t pivotx, fixed_t pivoty) { if (am_rotate == 0 || (am_rotate == 2 && !viewactive)) @@ -588,6 +623,12 @@ static void AM_ClipRotatedExtents (fixed_t pivotx, fixed_t pivoty) m_y2 = m_y + m_h; } +//============================================================================= +// +// +// +//============================================================================= + static void AM_ScrollParchment (fixed_t dmapx, fixed_t dmapy) { mapxstart -= MulScale12 (dmapx, scale_mtof); @@ -614,9 +655,12 @@ static void AM_ScrollParchment (fixed_t dmapx, fixed_t dmapy) } } +//============================================================================= // // // +//============================================================================= + void AM_changeWindowLoc () { if (0 != (m_paninc.x | m_paninc.y)) @@ -646,9 +690,12 @@ void AM_changeWindowLoc () } +//============================================================================= // // // +//============================================================================= + void AM_initVariables () { int pnum; @@ -694,6 +741,11 @@ static void GetComponents (int color, DWORD *palette, float &r, float &g, float } */ +//============================================================================= +// +// +// +//============================================================================= static void AM_initColors (bool overlayed) { @@ -719,7 +771,8 @@ static void AM_initColors (bool overlayed) { YourColor.FromCVar (am_ovyourcolor); WallColor.FromCVar (am_ovwallcolor); - SecretSectorColor = SecretWallColor = WallColor; + SecretWallColor = WallColor; + SecretSectorColor.FromCVar (am_ovsecretsectorcolor); ThingColor_Item.FromCVar (am_ovthingcolor_item); ThingColor_Friend.FromCVar (am_ovthingcolor_friend); ThingColor_Monster.FromCVar (am_ovthingcolor_monster); @@ -840,9 +893,12 @@ static void AM_initColors (bool overlayed) lastpal = palette; } +//============================================================================= // // // +//============================================================================= + void AM_loadPics () { int i; @@ -859,6 +915,12 @@ void AM_loadPics () mapback = TexMan.CheckForTexture(autopage, FTexture::TEX_MiscPatch); } +//============================================================================= +// +// +// +//============================================================================= + bool AM_clearMarks () { for (int i = AM_NUMMARKPOINTS-1; i >= 0; i--) @@ -867,9 +929,12 @@ bool AM_clearMarks () return marknums[0].isValid(); } +//============================================================================= // // called right after the level has been loaded // +//============================================================================= + void AM_LevelInit () { leveljuststarted = 0; @@ -883,9 +948,12 @@ void AM_LevelInit () scale_ftom = MapDiv(MAPUNIT, scale_mtof); } +//============================================================================= // // // +//============================================================================= + void AM_Stop () { automapactive = false; @@ -894,9 +962,12 @@ void AM_Stop () viewactive = true; } +//============================================================================= // // // +//============================================================================= + void AM_Start () { if (!stopped) AM_Stop(); @@ -907,27 +978,36 @@ void AM_Start () +//============================================================================= // // set the window scale to the maximum size // +//============================================================================= + void AM_minOutWindowScale () { scale_mtof = min_scale_mtof; scale_ftom = MapDiv(MAPUNIT, scale_mtof); } +//============================================================================= // // set the window scale to the minimum size // +//============================================================================= + void AM_maxOutWindowScale () { scale_mtof = max_scale_mtof; scale_ftom = MapDiv(MAPUNIT, scale_mtof); } +//============================================================================= // // Called right after the resolution has changed // +//============================================================================= + void AM_NewResolution() { fixed_t oldmin = min_scale_mtof; @@ -949,11 +1029,23 @@ void AM_NewResolution() } +//============================================================================= +// +// +// +//============================================================================= + CCMD (togglemap) { gameaction = ga_togglemap; } +//============================================================================= +// +// +// +//============================================================================= + void AM_ToggleMap () { if (gamestate != GS_LEVEL) @@ -983,9 +1075,12 @@ void AM_ToggleMap () } } +//============================================================================= // // Handle events (user inputs) in automap mode // +//============================================================================= + bool AM_Responder (event_t *ev) { bool rc; @@ -1110,9 +1205,12 @@ bool AM_Responder (event_t *ev) } +//============================================================================= // // Zooming // +//============================================================================= + void AM_changeWindowScale () { // Change the scaling multipliers @@ -1126,9 +1224,12 @@ void AM_changeWindowScale () } +//============================================================================= // // // +//============================================================================= + void AM_doFollowPlayer () { fixed_t sx, sy; @@ -1156,6 +1257,12 @@ void AM_doFollowPlayer () } } +//============================================================================= +// +// +// +//============================================================================= + static void AM_ToggleFollowPlayer() { followplayer = !followplayer; @@ -1163,9 +1270,12 @@ static void AM_ToggleFollowPlayer() Printf ("%s\n", GStrings(followplayer ? "AMSTR_FOLLOWON" : "AMSTR_FOLLOWOFF")); } +//============================================================================= // // Updates on Game Tick // +//============================================================================= + void AM_Ticker () { if (!automapactive) @@ -1186,9 +1296,12 @@ void AM_Ticker () } +//============================================================================= // // Clear automap frame buffer. // +//============================================================================= + void AM_clearFB (const AMColor &color) { if (!mapback.isValid() || !am_drawmapback) @@ -1217,6 +1330,7 @@ void AM_clearFB (const AMColor &color) } +//============================================================================= // // Automap clipping of lines. // @@ -1224,6 +1338,8 @@ void AM_clearFB (const AMColor &color) // faster reject and precalculated slopes. If the speed is needed, // use a hash algorithm to handle the common cases. // +//============================================================================= + bool AM_clipMline (mline_t *ml, fline_t *fl) { enum { @@ -1345,9 +1461,12 @@ bool AM_clipMline (mline_t *ml, fline_t *fl) #undef DOOUTCODE +//============================================================================= // // Clip lines, draw visible parts of lines. // +//============================================================================= + void AM_drawMline (mline_t *ml, const AMColor &color) { fline_t fl; @@ -1358,11 +1477,12 @@ void AM_drawMline (mline_t *ml, const AMColor &color) } } - - +//============================================================================= // // Draws flat (floor/ceiling tile) aligned grid lines. // +//============================================================================= + void AM_drawGrid (const AMColor &color) { fixed_t x, y; @@ -1425,6 +1545,12 @@ void AM_drawGrid (const AMColor &color) } } +//============================================================================= +// +// +// +//============================================================================= + static bool AM_CheckSecret(line_t *line) { if (line->frontsector != NULL) @@ -1445,10 +1571,15 @@ static bool AM_CheckSecret(line_t *line) } return false; } + + +//============================================================================= // // Determines visible lines, draws them. // This is LineDef based, not LineSeg based. // +//============================================================================= + void AM_drawWalls (bool allmap) { int i; @@ -1556,10 +1687,13 @@ void AM_drawWalls (bool allmap) } +//============================================================================= // // Rotation in 2D. // Used to rotate player arrow line character. // +//============================================================================= + void AM_rotate (fixed_t *x, fixed_t *y, angle_t a) { fixed_t tmpx; @@ -1570,6 +1704,12 @@ void AM_rotate (fixed_t *x, fixed_t *y, angle_t a) *x = tmpx; } +//============================================================================= +// +// +// +//============================================================================= + void AM_rotatePoint (fixed_t *x, fixed_t *y) { fixed_t pivotx = m_x + m_w/2; @@ -1581,6 +1721,12 @@ void AM_rotatePoint (fixed_t *x, fixed_t *y) *y += pivoty; } +//============================================================================= +// +// +// +//============================================================================= + void AM_drawLineCharacter ( const mline_t *lineguy, @@ -1627,6 +1773,12 @@ AM_drawLineCharacter } } +//============================================================================= +// +// +// +//============================================================================= + void AM_drawPlayers () { mpoint_t pt; @@ -1723,6 +1875,12 @@ void AM_drawPlayers () } } +//============================================================================= +// +// +// +//============================================================================= + void AM_drawThings () { AMColor color; @@ -1777,6 +1935,12 @@ void AM_drawThings () } } +//============================================================================= +// +// +// +//============================================================================= + static void DrawMarker (FTexture *tex, fixed_t x, fixed_t y, int yadjust, INTBOOL flip, fixed_t xscale, fixed_t yscale, int translation, fixed_t alpha, DWORD fillcolor, FRenderStyle renderstyle) { @@ -1803,6 +1967,12 @@ static void DrawMarker (FTexture *tex, fixed_t x, fixed_t y, int yadjust, TAG_DONE); } +//============================================================================= +// +// +// +//============================================================================= + void AM_drawMarks () { for (int i = 0; i < AM_NUMMARKPOINTS; i++) @@ -1815,6 +1985,12 @@ void AM_drawMarks () } } +//============================================================================= +// +// +// +//============================================================================= + void AM_drawAuthorMarkers () { // [RH] Draw any actors derived from AMapMarker on the automap. @@ -1876,11 +2052,23 @@ void AM_drawAuthorMarkers () } } +//============================================================================= +// +// +// +//============================================================================= + void AM_drawCrosshair (const AMColor &color) { screen->DrawPixel(f_w/2, (f_h+1)/2, color.Index, color.RGB); } +//============================================================================= +// +// +// +//============================================================================= + void AM_Drawer () { if (!automapactive) @@ -1928,6 +2116,12 @@ void AM_Drawer () AM_drawMarks(); } +//============================================================================= +// +// +// +//============================================================================= + void AM_SerializeMarkers(FArchive &arc) { arc << markpointnum; diff --git a/src/asm_ia32/tmap.asm b/src/asm_ia32/tmap.asm index e6658f20f..e4c477598 100644 --- a/src/asm_ia32/tmap.asm +++ b/src/asm_ia32/tmap.asm @@ -623,10 +623,6 @@ rdcp1: sub edi,SPACEFILLER4 cmp BYTE [CPU+66],byte 5 jg rdcploop2 -; need 12 bytes of filler to make it aligned - db 0x8D,0x80,0,0,0,0 ; lea eax,[eax+00000000] - db 0x8D,0xBF,0,0,0,0 ; lea edi,[edi+00000000] - align 16 ; The registers should now look like this: diff --git a/src/basicinlines.h b/src/basicinlines.h index 3ec7ea8de..fa6ce822b 100644 --- a/src/basicinlines.h +++ b/src/basicinlines.h @@ -15,121 +15,123 @@ #define __forceinline inline #endif -__forceinline SDWORD Scale (SDWORD a, SDWORD b, SDWORD c) +static __forceinline SDWORD Scale (SDWORD a, SDWORD b, SDWORD c) { return (SDWORD)(((SQWORD)a*b)/c); } -__forceinline SDWORD MulScale (SDWORD a, SDWORD b, SDWORD c) +static __forceinline SDWORD MulScale (SDWORD a, SDWORD b, SDWORD c) { return (SDWORD)(((SQWORD)a*b)>>c); } -__forceinline SDWORD MulScale1 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 1); } -__forceinline SDWORD MulScale2 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 2); } -__forceinline SDWORD MulScale3 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 3); } -__forceinline SDWORD MulScale4 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 4); } -__forceinline SDWORD MulScale5 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 5); } -__forceinline SDWORD MulScale6 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 6); } -__forceinline SDWORD MulScale7 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 7); } -__forceinline SDWORD MulScale8 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 8); } -__forceinline SDWORD MulScale9 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 9); } -__forceinline SDWORD MulScale10 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 10); } -__forceinline SDWORD MulScale11 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 11); } -__forceinline SDWORD MulScale12 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 12); } -__forceinline SDWORD MulScale13 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 13); } -__forceinline SDWORD MulScale14 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 14); } -__forceinline SDWORD MulScale15 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 15); } -__forceinline SDWORD MulScale16 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 16); } -__forceinline SDWORD MulScale17 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 17); } -__forceinline SDWORD MulScale18 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 18); } -__forceinline SDWORD MulScale19 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 19); } -__forceinline SDWORD MulScale20 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 20); } -__forceinline SDWORD MulScale21 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 21); } -__forceinline SDWORD MulScale22 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 22); } -__forceinline SDWORD MulScale23 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 23); } -__forceinline SDWORD MulScale24 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 24); } -__forceinline SDWORD MulScale25 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 25); } -__forceinline SDWORD MulScale26 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 26); } -__forceinline SDWORD MulScale27 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 27); } -__forceinline SDWORD MulScale28 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 28); } -__forceinline SDWORD MulScale29 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 29); } -__forceinline SDWORD MulScale30 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 30); } -__forceinline SDWORD MulScale31 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 31); } -__forceinline SDWORD MulScale32 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 32); } +static __forceinline SDWORD MulScale1 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 1); } +static __forceinline SDWORD MulScale2 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 2); } +static __forceinline SDWORD MulScale3 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 3); } +static __forceinline SDWORD MulScale4 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 4); } +static __forceinline SDWORD MulScale5 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 5); } +static __forceinline SDWORD MulScale6 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 6); } +static __forceinline SDWORD MulScale7 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 7); } +static __forceinline SDWORD MulScale8 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 8); } +static __forceinline SDWORD MulScale9 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 9); } +static __forceinline SDWORD MulScale10 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 10); } +static __forceinline SDWORD MulScale11 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 11); } +static __forceinline SDWORD MulScale12 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 12); } +static __forceinline SDWORD MulScale13 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 13); } +static __forceinline SDWORD MulScale14 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 14); } +static __forceinline SDWORD MulScale15 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 15); } +static __forceinline SDWORD MulScale16 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 16); } +static __forceinline SDWORD MulScale17 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 17); } +static __forceinline SDWORD MulScale18 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 18); } +static __forceinline SDWORD MulScale19 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 19); } +static __forceinline SDWORD MulScale20 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 20); } +static __forceinline SDWORD MulScale21 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 21); } +static __forceinline SDWORD MulScale22 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 22); } +static __forceinline SDWORD MulScale23 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 23); } +static __forceinline SDWORD MulScale24 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 24); } +static __forceinline SDWORD MulScale25 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 25); } +static __forceinline SDWORD MulScale26 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 26); } +static __forceinline SDWORD MulScale27 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 27); } +static __forceinline SDWORD MulScale28 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 28); } +static __forceinline SDWORD MulScale29 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 29); } +static __forceinline SDWORD MulScale30 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 30); } +static __forceinline SDWORD MulScale31 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 31); } +static __forceinline SDWORD MulScale32 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a * b) >> 32); } -__forceinline SDWORD DMulScale (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD s) +static __forceinline DWORD UMulScale16 (DWORD a, DWORD b) { return (DWORD)(((QWORD)a * b) >> 16); } + +static __forceinline SDWORD DMulScale (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD s) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> s); } -__forceinline SDWORD DMulScale1 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 1); } -__forceinline SDWORD DMulScale2 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 2); } -__forceinline SDWORD DMulScale3 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 3); } -__forceinline SDWORD DMulScale4 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 4); } -__forceinline SDWORD DMulScale5 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 5); } -__forceinline SDWORD DMulScale6 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 6); } -__forceinline SDWORD DMulScale7 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 7); } -__forceinline SDWORD DMulScale8 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 8); } -__forceinline SDWORD DMulScale9 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 9); } -__forceinline SDWORD DMulScale10 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 10); } -__forceinline SDWORD DMulScale11 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 11); } -__forceinline SDWORD DMulScale12 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 12); } -__forceinline SDWORD DMulScale13 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 13); } -__forceinline SDWORD DMulScale14 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 14); } -__forceinline SDWORD DMulScale15 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 15); } -__forceinline SDWORD DMulScale16 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 16); } -__forceinline SDWORD DMulScale17 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 17); } -__forceinline SDWORD DMulScale18 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 18); } -__forceinline SDWORD DMulScale19 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 19); } -__forceinline SDWORD DMulScale20 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 20); } -__forceinline SDWORD DMulScale21 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 21); } -__forceinline SDWORD DMulScale22 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 22); } -__forceinline SDWORD DMulScale23 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 23); } -__forceinline SDWORD DMulScale24 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 24); } -__forceinline SDWORD DMulScale25 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 25); } -__forceinline SDWORD DMulScale26 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 26); } -__forceinline SDWORD DMulScale27 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 27); } -__forceinline SDWORD DMulScale28 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 28); } -__forceinline SDWORD DMulScale29 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 29); } -__forceinline SDWORD DMulScale30 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 30); } -__forceinline SDWORD DMulScale31 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 31); } -__forceinline SDWORD DMulScale32 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 32); } +static __forceinline SDWORD DMulScale1 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 1); } +static __forceinline SDWORD DMulScale2 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 2); } +static __forceinline SDWORD DMulScale3 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 3); } +static __forceinline SDWORD DMulScale4 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 4); } +static __forceinline SDWORD DMulScale5 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 5); } +static __forceinline SDWORD DMulScale6 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 6); } +static __forceinline SDWORD DMulScale7 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 7); } +static __forceinline SDWORD DMulScale8 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 8); } +static __forceinline SDWORD DMulScale9 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 9); } +static __forceinline SDWORD DMulScale10 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 10); } +static __forceinline SDWORD DMulScale11 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 11); } +static __forceinline SDWORD DMulScale12 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 12); } +static __forceinline SDWORD DMulScale13 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 13); } +static __forceinline SDWORD DMulScale14 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 14); } +static __forceinline SDWORD DMulScale15 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 15); } +static __forceinline SDWORD DMulScale16 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 16); } +static __forceinline SDWORD DMulScale17 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 17); } +static __forceinline SDWORD DMulScale18 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 18); } +static __forceinline SDWORD DMulScale19 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 19); } +static __forceinline SDWORD DMulScale20 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 20); } +static __forceinline SDWORD DMulScale21 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 21); } +static __forceinline SDWORD DMulScale22 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 22); } +static __forceinline SDWORD DMulScale23 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 23); } +static __forceinline SDWORD DMulScale24 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 24); } +static __forceinline SDWORD DMulScale25 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 25); } +static __forceinline SDWORD DMulScale26 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 26); } +static __forceinline SDWORD DMulScale27 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 27); } +static __forceinline SDWORD DMulScale28 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 28); } +static __forceinline SDWORD DMulScale29 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 29); } +static __forceinline SDWORD DMulScale30 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 30); } +static __forceinline SDWORD DMulScale31 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 31); } +static __forceinline SDWORD DMulScale32 (SDWORD a, SDWORD b, SDWORD c, SDWORD d) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d) >> 32); } -__forceinline SDWORD TMulScale1 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 1); } -__forceinline SDWORD TMulScale2 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 2); } -__forceinline SDWORD TMulScale3 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 3); } -__forceinline SDWORD TMulScale4 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 4); } -__forceinline SDWORD TMulScale5 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 5); } -__forceinline SDWORD TMulScale6 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 6); } -__forceinline SDWORD TMulScale7 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 7); } -__forceinline SDWORD TMulScale8 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 8); } -__forceinline SDWORD TMulScale9 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 9); } -__forceinline SDWORD TMulScale10 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 10); } -__forceinline SDWORD TMulScale11 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 11); } -__forceinline SDWORD TMulScale12 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 12); } -__forceinline SDWORD TMulScale13 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 13); } -__forceinline SDWORD TMulScale14 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 14); } -__forceinline SDWORD TMulScale15 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 15); } -__forceinline SDWORD TMulScale16 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 16); } -__forceinline SDWORD TMulScale17 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 17); } -__forceinline SDWORD TMulScale18 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 18); } -__forceinline SDWORD TMulScale19 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 19); } -__forceinline SDWORD TMulScale20 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 20); } -__forceinline SDWORD TMulScale21 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 21); } -__forceinline SDWORD TMulScale22 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 22); } -__forceinline SDWORD TMulScale23 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 23); } -__forceinline SDWORD TMulScale24 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 24); } -__forceinline SDWORD TMulScale25 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 25); } -__forceinline SDWORD TMulScale26 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 26); } -__forceinline SDWORD TMulScale27 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 27); } -__forceinline SDWORD TMulScale28 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 28); } -__forceinline SDWORD TMulScale29 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 29); } -__forceinline SDWORD TMulScale30 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 30); } -__forceinline SDWORD TMulScale31 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 31); } -__forceinline SDWORD TMulScale32 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 32); } +static __forceinline SDWORD TMulScale1 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 1); } +static __forceinline SDWORD TMulScale2 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 2); } +static __forceinline SDWORD TMulScale3 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 3); } +static __forceinline SDWORD TMulScale4 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 4); } +static __forceinline SDWORD TMulScale5 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 5); } +static __forceinline SDWORD TMulScale6 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 6); } +static __forceinline SDWORD TMulScale7 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 7); } +static __forceinline SDWORD TMulScale8 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 8); } +static __forceinline SDWORD TMulScale9 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 9); } +static __forceinline SDWORD TMulScale10 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 10); } +static __forceinline SDWORD TMulScale11 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 11); } +static __forceinline SDWORD TMulScale12 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 12); } +static __forceinline SDWORD TMulScale13 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 13); } +static __forceinline SDWORD TMulScale14 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 14); } +static __forceinline SDWORD TMulScale15 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 15); } +static __forceinline SDWORD TMulScale16 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 16); } +static __forceinline SDWORD TMulScale17 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 17); } +static __forceinline SDWORD TMulScale18 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 18); } +static __forceinline SDWORD TMulScale19 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 19); } +static __forceinline SDWORD TMulScale20 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 20); } +static __forceinline SDWORD TMulScale21 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 21); } +static __forceinline SDWORD TMulScale22 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 22); } +static __forceinline SDWORD TMulScale23 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 23); } +static __forceinline SDWORD TMulScale24 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 24); } +static __forceinline SDWORD TMulScale25 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 25); } +static __forceinline SDWORD TMulScale26 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 26); } +static __forceinline SDWORD TMulScale27 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 27); } +static __forceinline SDWORD TMulScale28 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 28); } +static __forceinline SDWORD TMulScale29 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 29); } +static __forceinline SDWORD TMulScale30 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 30); } +static __forceinline SDWORD TMulScale31 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 31); } +static __forceinline SDWORD TMulScale32 (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD f) { return (SDWORD)(((SQWORD)a*b + (SQWORD)c*d + (SQWORD)e*f) >> 32); } -__forceinline SDWORD BoundMulScale (SDWORD a, SDWORD b, SDWORD c) +static __forceinline SDWORD BoundMulScale (SDWORD a, SDWORD b, SDWORD c) { SQWORD x = ((SQWORD)a * b) >> c; return x > 0x7FFFFFFFll ? 0x7FFFFFFF : @@ -137,45 +139,45 @@ __forceinline SDWORD BoundMulScale (SDWORD a, SDWORD b, SDWORD c) (SDWORD)x; } -inline SDWORD DivScale (SDWORD a, SDWORD b, SDWORD c) +static inline SDWORD DivScale (SDWORD a, SDWORD b, SDWORD c) { return (SDWORD)(((SQWORD)a << c) / b); } -inline SDWORD DivScale1 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 1) / b); } -inline SDWORD DivScale2 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 2) / b); } -inline SDWORD DivScale3 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 3) / b); } -inline SDWORD DivScale4 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 4) / b); } -inline SDWORD DivScale5 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 5) / b); } -inline SDWORD DivScale6 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 6) / b); } -inline SDWORD DivScale7 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 7) / b); } -inline SDWORD DivScale8 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 8) / b); } -inline SDWORD DivScale9 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 9) / b); } -inline SDWORD DivScale10 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 10) / b); } -inline SDWORD DivScale11 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 11) / b); } -inline SDWORD DivScale12 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 12) / b); } -inline SDWORD DivScale13 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 13) / b); } -inline SDWORD DivScale14 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 14) / b); } -inline SDWORD DivScale15 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 15) / b); } -inline SDWORD DivScale16 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 16) / b); } -inline SDWORD DivScale17 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 17) / b); } -inline SDWORD DivScale18 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 18) / b); } -inline SDWORD DivScale19 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 19) / b); } -inline SDWORD DivScale20 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 20) / b); } -inline SDWORD DivScale21 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 21) / b); } -inline SDWORD DivScale22 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 22) / b); } -inline SDWORD DivScale23 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 23) / b); } -inline SDWORD DivScale24 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 24) / b); } -inline SDWORD DivScale25 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 25) / b); } -inline SDWORD DivScale26 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 26) / b); } -inline SDWORD DivScale27 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 27) / b); } -inline SDWORD DivScale28 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 28) / b); } -inline SDWORD DivScale29 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 29) / b); } -inline SDWORD DivScale30 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 30) / b); } -inline SDWORD DivScale31 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 31) / b); } -inline SDWORD DivScale32 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 32) / b); } +static inline SDWORD DivScale1 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 1) / b); } +static inline SDWORD DivScale2 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 2) / b); } +static inline SDWORD DivScale3 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 3) / b); } +static inline SDWORD DivScale4 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 4) / b); } +static inline SDWORD DivScale5 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 5) / b); } +static inline SDWORD DivScale6 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 6) / b); } +static inline SDWORD DivScale7 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 7) / b); } +static inline SDWORD DivScale8 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 8) / b); } +static inline SDWORD DivScale9 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 9) / b); } +static inline SDWORD DivScale10 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 10) / b); } +static inline SDWORD DivScale11 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 11) / b); } +static inline SDWORD DivScale12 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 12) / b); } +static inline SDWORD DivScale13 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 13) / b); } +static inline SDWORD DivScale14 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 14) / b); } +static inline SDWORD DivScale15 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 15) / b); } +static inline SDWORD DivScale16 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 16) / b); } +static inline SDWORD DivScale17 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 17) / b); } +static inline SDWORD DivScale18 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 18) / b); } +static inline SDWORD DivScale19 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 19) / b); } +static inline SDWORD DivScale20 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 20) / b); } +static inline SDWORD DivScale21 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 21) / b); } +static inline SDWORD DivScale22 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 22) / b); } +static inline SDWORD DivScale23 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 23) / b); } +static inline SDWORD DivScale24 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 24) / b); } +static inline SDWORD DivScale25 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 25) / b); } +static inline SDWORD DivScale26 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 26) / b); } +static inline SDWORD DivScale27 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 27) / b); } +static inline SDWORD DivScale28 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 28) / b); } +static inline SDWORD DivScale29 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 29) / b); } +static inline SDWORD DivScale30 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 30) / b); } +static inline SDWORD DivScale31 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 31) / b); } +static inline SDWORD DivScale32 (SDWORD a, SDWORD b) { return (SDWORD)(((SQWORD)a << 32) / b); } -__forceinline void clearbuf (void *buff, unsigned int count, SDWORD clear) +static __forceinline void clearbuf (void *buff, unsigned int count, SDWORD clear) { SDWORD *b2 = (SDWORD *)buff; for (unsigned int i = 0; i != count; ++i) @@ -184,7 +186,7 @@ __forceinline void clearbuf (void *buff, unsigned int count, SDWORD clear) } } -__forceinline void clearbufshort (void *buff, unsigned int count, WORD clear) +static __forceinline void clearbufshort (void *buff, unsigned int count, WORD clear) { SWORD *b2 = (SWORD *)buff; for (unsigned int i = 0; i != count; ++i) @@ -193,19 +195,9 @@ __forceinline void clearbufshort (void *buff, unsigned int count, WORD clear) } } -__forceinline SDWORD ksgn (SDWORD a) +static __forceinline SDWORD ksgn (SDWORD a) { if (a < 0) return -1; else if (a > 0) return 1; else return 0; } - -__forceinline int toint (float v) -{ - return int(v); -} - -__forceinline int quickertoint (float v) -{ - return int(v); -} diff --git a/src/basictypes.h b/src/basictypes.h index 37d7aec75..3aa4f4635 100644 --- a/src/basictypes.h +++ b/src/basictypes.h @@ -23,6 +23,10 @@ typedef int64_t SQWORD; typedef uint64_t QWORD; #endif +typedef SDWORD int32; +typedef float real32; +typedef double real64; + // windef.h, included by windows.h, has its own incompatible definition // of DWORD as a long. In files that mix Doom and Windows code, you // must define USE_WINDOWS_DWORD before including doomtype.h so that diff --git a/src/c_bind.cpp b/src/c_bind.cpp index f7450aaa5..d96b5a742 100644 --- a/src/c_bind.cpp +++ b/src/c_bind.cpp @@ -159,8 +159,8 @@ static const FBinding DefHexenBindings[] = { "9", "use ArtiBlastRadius" }, { "8", "use ArtiTeleport" }, { "7", "use ArtiTeleportOther" }, - { "6", "use ArtiEgg" }, - { "5", "use ArtiInvulnerability" }, + { "6", "use ArtiPork" }, + { "5", "use ArtiInvulnerability2" }, { "scroll", "+showscores" }, { NULL } }; diff --git a/src/c_cmds.cpp b/src/c_cmds.cpp index c61c98f8e..9ee99276f 100644 --- a/src/c_cmds.cpp +++ b/src/c_cmds.cpp @@ -253,7 +253,7 @@ CCMD (chase) CCMD (idclev) { - if (CheckCheatmode () || netgame) + if (netgame) return; if ((argv.argc() > 1) && (*(argv[1] + 2) == 0) && *(argv[1] + 1) && *argv[1]) @@ -291,7 +291,7 @@ CCMD (idclev) CCMD (hxvisit) { - if (CheckCheatmode ()) + if (netgame) return; if ((argv.argc() > 1) && (*(argv[1] + 2) == 0) && *(argv[1] + 1) && *argv[1]) diff --git a/src/c_console.cpp b/src/c_console.cpp index 0cf2d9bce..324fad660 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -1824,8 +1824,8 @@ CCMD (echo) int last = argv.argc()-1; for (int i = 1; i <= last; ++i) { - strbin (argv[i]); - Printf ("%s%s", argv[i], i!=last ? " " : "\n"); + FString formatted = strbin1 (argv[i]); + Printf ("%s%s", formatted.GetChars(), i!=last ? " " : "\n"); } } @@ -1843,7 +1843,7 @@ static const char logbar[] = "\n<------------------------------->\n"; void C_MidPrint (FFont *font, const char *msg) { - if (StatusBar == NULL) + if (StatusBar == NULL || screen == NULL) return; if (msg != NULL) diff --git a/src/c_dispatch.cpp b/src/c_dispatch.cpp index 6540fc4c9..5ebe450b6 100644 --- a/src/c_dispatch.cpp +++ b/src/c_dispatch.cpp @@ -1376,6 +1376,7 @@ void FConsoleAlias::SafeDelete () static BYTE PullinBad = 2; static const char *PullinFile; +extern TArray allwads; int C_ExecFile (const char *file, bool usePullin) { @@ -1492,7 +1493,7 @@ CCMD (pullin) FixPathSeperator (path); } } - D_AddFile (path); + D_AddFile (allwads, path); if (path != argv[i]) { delete[] path; diff --git a/src/compatibility.cpp b/src/compatibility.cpp index a6bb4a38a..f4fbaa529 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -105,6 +105,7 @@ static FCompatOption Options[] = { "mbfmonstermove", COMPATF_MBFMONSTERMOVE, 0 }, { "corpsegibs", COMPATF_CORPSEGIBS, 0 }, { "noblockfriends", COMPATF_NOBLOCKFRIENDS, 0 }, + { "spritesort", COMPATF_SPRITESORT, 0 }, { NULL, 0, 0 } }; @@ -196,7 +197,7 @@ void CheckCompatibility(MapData *map) FCompatValues *flags; // When playing Doom IWAD levels force COMPAT_SHORTTEX. - if (Wads.GetLumpFile(map->lumpnum) == 1 && gameinfo.gametype == GAME_Doom && !(level.flags & LEVEL_HEXENFORMAT)) + if (Wads.GetLumpFile(map->lumpnum) == 1 && (gameinfo.flags & GI_COMPATSHORTTEX) && !(level.flags & LEVEL_HEXENFORMAT)) { ii_compatflags = COMPATF_SHORTTEX; ib_compatflags = 0; diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 4889b2f75..4c2e7e5a0 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -252,6 +252,7 @@ DehSpriteMappings[] = #define CHECKKEY(a,b) if (!stricmp (Line1, (a))) (b) = atoi(Line2); static char *PatchFile, *PatchPt, *PatchName; +static int PatchSize; static char *Line1, *Line2; static int dversion, pversion; static bool including, includenotext; @@ -285,6 +286,7 @@ static int PatchPars (int); static int PatchCodePtrs (int); static int PatchMusic (int); static int DoInclude (int); +static bool DoDehPatch(); static const struct { const char *name; @@ -426,7 +428,7 @@ static bool ReadChars (char **stuff, int size) size++; PatchPt++; - } while (--size); + } while (--size && *PatchPt != 0); *str = 0; return true; @@ -523,7 +525,7 @@ static char *igets (void) { char *line; - if (*PatchPt == '\0') + if (*PatchPt == '\0' || PatchPt >= PatchFile + PatchSize ) return NULL; line = PatchPt; @@ -2203,7 +2205,7 @@ static int PatchStrings (int dummy) static int DoInclude (int dummy) { char *data; - int savedversion, savepversion; + int savedversion, savepversion, savepatchsize; char *savepatchfile, *savepatchpt, *savepatchname; if (including) @@ -2238,6 +2240,7 @@ static int DoInclude (int dummy) savepatchname = PatchName; savepatchfile = PatchFile; savepatchpt = PatchPt; + savepatchsize = PatchSize; savedversion = dversion; savepversion = pversion; including = true; @@ -2260,7 +2263,7 @@ static int DoInclude (int dummy) } } - DoDehPatch (path, false); + D_LoadDehFile(path); if (data != path) { @@ -2271,6 +2274,7 @@ static int DoInclude (int dummy) PatchName = savepatchname; PatchFile = savepatchfile; PatchPt = savepatchpt; + PatchSize = savepatchsize; dversion = savedversion; pversion = savepversion; } @@ -2280,105 +2284,70 @@ static int DoInclude (int dummy) return GetLine(); } -void DoDehPatch (const char *patchfile, bool autoloading, int lump) +int D_LoadDehLumps() { - char file[256]; - int cont; - int filelen = 0; // Be quiet, gcc + int lastlump = 0, lumpnum, count = 0; - PatchFile = NULL; - PatchName = NULL; - - if (lump < 0) + while ((lumpnum = Wads.FindLump("DEHACKED", &lastlump)) >= 0) { - lump = Wads.CheckNumForName ("DEHACKED"); + count += D_LoadDehLump(lumpnum); } + return count; +} - if (lump >= 0 && autoloading) +bool D_LoadDehLump(int lumpnum) +{ + PatchSize = Wads.LumpLength(lumpnum); + + PatchName = copystring(Wads.GetLumpFullPath(lumpnum)); + PatchFile = new char[PatchSize + 1]; + Wads.ReadLump(lumpnum, PatchFile); + PatchFile[PatchSize] = '\0'; // terminate with a '\0' character + return DoDehPatch(); +} + +bool D_LoadDehFile(const char *patchfile) +{ + FILE *deh; + + deh = fopen(patchfile, "rb"); + if (deh != NULL) { - // Execute the DEHACKED lump as a patch. - strcpy (file, "DEHACKED lump"); - filelen = Wads.LumpLength (lump); - if ( (PatchFile = new char[filelen + 1]) ) - { - Wads.ReadLump (lump, PatchFile); - } - else - { - Printf ("Not enough memory to apply patch\n"); - return; - } - } - else if (patchfile) - { - // Try to use patchfile as a patch. - FILE *deh; + PatchSize = Q_filelength(deh); - strcpy (file, patchfile); - FixPathSeperator (file); - DefaultExtension (file, ".deh"); - - if ( !(deh = fopen (file, "rb")) ) - { - strcpy (file, patchfile); - FixPathSeperator (file); - DefaultExtension (file, ".bex"); - deh = fopen (file, "rb"); - } - - if (deh) - { - filelen = Q_filelength (deh); - if ( (PatchFile = new char[filelen + 1]) ) - { - fread (PatchFile, 1, filelen, deh); - fclose (deh); - PatchName = copystring (patchfile); - FixPathSeperator (PatchName); - } - } - - if (!PatchFile) - { - // Couldn't find it on disk, try reading it from a lump - lump = Wads.CheckNumForFullName(patchfile, true); - if (lump == -1) - { - // Compatibility fallback. It's just here because - // some WAD may need it. Should be deleted it it can - // be confirmed that nothing uses this case. - FString filebase(ExtractFileBase (patchfile)); - lump = Wads.CheckNumForName (filebase); - } - if (lump >= 0) - { - filelen = Wads.LumpLength (lump); - if ( (PatchFile = new char[filelen + 1]) ) - { - Wads.ReadLump (lump, PatchFile); - } - else - { - Printf ("Not enough memory to apply patch\n"); - return; - } - } - } - - if (!PatchFile) - { - Printf ("Could not open DeHackEd patch \"%s\"\n", file); - return; - } + PatchName = copystring(patchfile); + PatchFile = new char[PatchSize + 1]; + fread(PatchFile, 1, PatchSize, deh); + fclose(deh); + PatchFile[PatchSize] = '\0'; // terminate with a '\0' character + return DoDehPatch(); } else { - // Nothing to do. - return; + // Couldn't find it in the filesystem; try from a lump instead. + int lumpnum = Wads.CheckNumForFullName(patchfile, true); + if (lumpnum < 0) + { + // Compatibility fallback. It's just here because + // some WAD may need it. Should be deleted if it can + // be confirmed that nothing uses this case. + FString filebase(ExtractFileBase(patchfile)); + lumpnum = Wads.CheckNumForName(filebase); + } + if (lumpnum >= 0) + { + return D_LoadDehLump(lumpnum); + } } + Printf ("Could not open DeHackEd patch \"%s\"\n", patchfile); + return false; +} - // End file with a NULL for our parser - PatchFile[filelen] = 0; +static bool DoDehPatch() +{ + Printf("Adding dehacked patch %s\n", PatchName); + + int cont; dversion = pversion = -1; cont = 0; @@ -2386,10 +2355,10 @@ void DoDehPatch (const char *patchfile, bool autoloading, int lump) { if (PatchFile[25] < '3') { - if (PatchName != NULL) delete[] PatchName; + delete[] PatchName; delete[] PatchFile; - Printf (PRINT_BOLD, "\"%s\" is an old and unsupported DeHackEd patch\n", file); - return; + Printf (PRINT_BOLD, "\"%s\" is an old and unsupported DeHackEd patch\n", PatchFile); + return false; } PatchPt = strchr (PatchFile, '\n'); while ((cont = GetLine()) == 1) @@ -2399,10 +2368,10 @@ void DoDehPatch (const char *patchfile, bool autoloading, int lump) } if (!cont || dversion == -1 || pversion == -1) { - if (PatchName != NULL) delete[] PatchName; + delete[] PatchName; delete[] PatchFile; - Printf (PRINT_BOLD, "\"%s\" is not a DeHackEd patch file\n", file); - return; + Printf (PRINT_BOLD, "\"%s\" is not a DeHackEd patch file\n", PatchFile); + return false; } } else @@ -2412,7 +2381,7 @@ void DoDehPatch (const char *patchfile, bool autoloading, int lump) pversion = 6; PatchPt = PatchFile; while ((cont = GetLine()) == 1) - ; + {} } if (pversion != 6) @@ -2439,10 +2408,10 @@ void DoDehPatch (const char *patchfile, bool autoloading, int lump) if (!LoadDehSupp ()) { Printf ("Could not load DEH support data\n"); - if (PatchName != NULL) delete[] PatchName; - delete[] PatchFile; UnloadDehSupp (); - return; + delete[] PatchName; + delete[] PatchFile; + return false; } do @@ -2459,10 +2428,10 @@ void DoDehPatch (const char *patchfile, bool autoloading, int lump) } while (cont); UnloadDehSupp (); - if (PatchName != NULL) delete[] PatchName; + delete[] PatchName; delete[] PatchFile; Printf ("Patch installed\n"); - + return true; } static inline bool CompareLabel (const char *want, const BYTE *have) diff --git a/src/d_dehacked.h b/src/d_dehacked.h index 6967c083d..67473f071 100644 --- a/src/d_dehacked.h +++ b/src/d_dehacked.h @@ -56,8 +56,9 @@ public: bool droppedbymonster; }; - -void DoDehPatch (const char *patchfile, bool autoloading, int lumpnum=-1); +int D_LoadDehLumps(); +bool D_LoadDehLump(int lumpnum); +bool D_LoadDehFile(const char *filename); void FinishDehPatch (); #endif //__D_DEHACK_H__ diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index b1270729a..3cf9773ce 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -58,18 +58,18 @@ EIWADType gameiwad; const IWADInfo IWADInfos[NUM_IWAD_TYPES] = { // banner text, autoname, fg color, bg color - { "Final Doom: TNT - Evilution", "TNT", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/tnt.txt", GI_MAPxx }, - { "Final Doom: Plutonia Experiment", "Plutonia", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/plutonia.txt", GI_MAPxx }, + { "Final Doom: TNT - Evilution", "TNT", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/tnt.txt", GI_MAPxx | GI_COMPATSHORTTEX }, + { "Final Doom: Plutonia Experiment", "Plutonia", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/plutonia.txt", GI_MAPxx | GI_COMPATSHORTTEX }, { "Hexen: Beyond Heretic", NULL, MAKERGB(240,240,240), MAKERGB(107,44,24), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx }, { "Hexen: Deathkings of the Dark Citadel", "HexenDK", MAKERGB(240,240,240), MAKERGB(139,68,9), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx }, { "Hexen: Demo Version", "HexenDemo",MAKERGB(240,240,240), MAKERGB(107,44,24), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx | GI_SHAREWARE }, - { "DOOM 2: Hell on Earth", "Doom2", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx }, + { "DOOM 2: Hell on Earth", "Doom2", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx | GI_COMPATSHORTTEX }, { "Heretic Shareware", NULL, MAKERGB(252,252,0), MAKERGB(168,0,0), GAME_Heretic, "mapinfo/hereticsw.txt",GI_SHAREWARE }, { "Heretic: Shadow of the Serpent Riders", NULL, MAKERGB(252,252,0), MAKERGB(168,0,0), GAME_Heretic, "mapinfo/heretic.txt", GI_MENUHACK_EXTENDED }, { "Heretic", NULL, MAKERGB(252,252,0), MAKERGB(168,0,0), GAME_Heretic, "mapinfo/heretic.txt" }, - { "DOOM Shareware", NULL, MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom1.txt", GI_SHAREWARE }, - { "The Ultimate DOOM", "Doom1", MAKERGB(84,84,84), MAKERGB(168,168,168), GAME_Doom, "mapinfo/ultdoom.txt" }, - { "DOOM Registered", "Doom1", MAKERGB(84,84,84), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom1.txt" }, + { "DOOM Shareware", NULL, MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom1.txt", GI_SHAREWARE | GI_COMPATSHORTTEX }, + { "The Ultimate DOOM", "Doom1", MAKERGB(84,84,84), MAKERGB(168,168,168), GAME_Doom, "mapinfo/ultdoom.txt", GI_COMPATSHORTTEX }, + { "DOOM Registered", "Doom1", MAKERGB(84,84,84), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom1.txt", GI_COMPATSHORTTEX }, { "Strife: Quest for the Sigil", NULL, MAKERGB(224,173,153), MAKERGB(0,107,101), GAME_Strife, "mapinfo/strife.txt", GI_MAPxx }, { "Strife: Teaser (Old Version)", NULL, MAKERGB(224,173,153), MAKERGB(0,107,101), GAME_Strife, "mapinfo/strife.txt", GI_MAPxx | GI_SHAREWARE }, { "Strife: Teaser (New Version)", NULL, MAKERGB(224,173,153), MAKERGB(0,107,101), GAME_Strife, "mapinfo/strife.txt", GI_MAPxx | GI_SHAREWARE | GI_TEASER2 }, @@ -77,8 +77,11 @@ const IWADInfo IWADInfos[NUM_IWAD_TYPES] = { "Ultimate Freedoom", "Freedoom1",MAKERGB(50,84,67), MAKERGB(198,220,209), GAME_Doom, "mapinfo/doom1.txt" }, { "Freedoom \"Demo\"", NULL, MAKERGB(50,84,67), MAKERGB(198,220,209), GAME_Doom, "mapinfo/doom1.txt" }, { "FreeDM", "FreeDM", MAKERGB(50,84,67), MAKERGB(198,220,209), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx }, + { "Blasphemer", "Blasphemer",MAKERGB(115,0,0), MAKERGB(0,0,0), GAME_Heretic, "mapinfo/heretic.txt" }, { "Chex(R) Quest", "Chex1", MAKERGB(255,255,0), MAKERGB(0,192,0), GAME_Chex, "mapinfo/chex.txt" }, { "Chex(R) Quest 3", "Chex3", MAKERGB(255,255,0), MAKERGB(0,192,0), GAME_Chex, "mapinfo/chex3.txt" }, + { "Action Doom 2: Urban Brawl", "UrbanBrawl",MAKERGB(168,168,0), MAKERGB(168,0,0), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx }, + { "Harmony", "Harmony", MAKERGB(110,180,230), MAKERGB(69,79,126), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx }, //{ "ZDoom Engine", NULL, MAKERGB(168,0,0), MAKERGB(168,168,168) }, }; @@ -104,8 +107,12 @@ static const char *IWADNames[] = "freedoom1.wad", "freedoomu.wad", "freedm.wad", + "blasphem.wad", + "blasphemer.wad", "chex.wad", "chex3.wad", + "action2.wad", + "harm1.wad", #ifdef unix "DOOM2.WAD", // Also look for all-uppercase names "PLUTONIA.WAD", @@ -124,8 +131,12 @@ static const char *IWADNames[] = "FREEDOOM1.WAD", "FREEDOOMU.WAD", "FREEDM.WAD", + "BLASPHEM.WAD", + "BLASPHEMER.WAD", "CHEX.WAD", "CHEX3.WAD", + "ACTION2.WAD", + "HARM1.WAD", #endif NULL }; @@ -141,6 +152,7 @@ static EIWADType ScanIWAD (const char *iwad) { static const char checklumps[][8] = { + "AD2LIB", "E1M1", "E4M2", "MAP01", @@ -154,11 +166,15 @@ static EIWADType ScanIWAD (const char *iwad) "MAP33", "INVCURS", { 'F','R','E','E','D','O','O','M' }, + { 'B','L','A','S','P','H','E','M' }, "W94_1", { 'P','O','S','S','H','0','M','0' }, "CYCLA1", "FLMBA1", "MAPINFO", + "0HAWK01", + "0CARA3", + "0NOSE1", { 'G','A','M','E','I','N','F','O' }, "E2M1","E2M2","E2M3","E2M4","E2M5","E2M6","E2M7","E2M8","E2M9", "E3M1","E3M2","E3M3","E3M4","E3M5","E3M6","E3M7","E3M8","E3M9", @@ -166,9 +182,10 @@ static EIWADType ScanIWAD (const char *iwad) { 'S','P','I','D','A','1','D','1' }, }; -#define NUM_CHECKLUMPS (sizeof(checklumps)/8) +#define NUM_CHECKLUMPS (countof(checklumps)) enum { + Check_ad2lib, Check_e1m1, Check_e4m1, Check_map01, @@ -182,15 +199,19 @@ static EIWADType ScanIWAD (const char *iwad) Check_map33, Check_invcurs, Check_FreeDoom, + Check_Blasphem, Check_W94_1, Check_POSSH0M0, Check_Cycla1, Check_Flmba1, Check_Mapinfo, + Check_Hawk, + Check_Car, + Check_Nose, Check_Gameinfo, Check_e2m1 }; - int lumpsfound[NUM_CHECKLUMPS]; + bool lumpsfound[NUM_CHECKLUMPS]; size_t i; memset (lumpsfound, 0, sizeof(lumpsfound)); @@ -201,10 +222,44 @@ static EIWADType ScanIWAD (const char *iwad) for(DWORD ii = 0; ii < iwadfile->LumpCount(); ii++) { FResourceLump *lump = iwadfile->GetLump(ii); + size_t j; - for (DWORD j = 0; j < NUM_CHECKLUMPS; j++) - if (strnicmp (lump->Name, checklumps[j], 8) == 0) - lumpsfound[j]++; + for (j = 0; j < NUM_CHECKLUMPS; j++) + { + if (!lumpsfound[j]) + { + if (strnicmp (lump->Name, checklumps[j], 8) == 0) + { + lumpsfound[j] = true; + break; + } + // Check for maps inside zips, too. + else if (lump->FullName != NULL) + { + if (checklumps[j][0] == 'E' && checklumps[j][2] == 'M' && checklumps[j][4] == '\0') + { + if (strnicmp(lump->FullName, "maps/", 5) == 0 && + strnicmp(lump->FullName + 5, checklumps[j], 4) == 0 && + stricmp(lump->FullName + 9, ".wad") == 0) + { + lumpsfound[j] = true; + break; + } + } + else if (checklumps[j][0] == 'M' && checklumps[j][1] == 'A' && checklumps[j][2] == 'P' && + checklumps[j][5] == '\0') + { + if (strnicmp(lump->FullName, "maps/", 5) == 0 && + strnicmp(lump->FullName + 5, checklumps[j], 5) == 0 && + stricmp(lump->FullName + 10, ".wad") == 0) + { + lumpsfound[j] = true; + break; + } + } + } + } + } } delete iwadfile; } @@ -228,16 +283,24 @@ static EIWADType ScanIWAD (const char *iwad) } else if (lumpsfound[Check_invcurs]) { - return IWAD_StrifeTeaser2; + return IWAD_StrifeTeaser2; // Strife0.wad from 14 Mar 1996 } else { - return IWAD_StrifeTeaser; + return IWAD_StrifeTeaser; // Strife0.wad from 22 Feb 1996 } } else if (lumpsfound[Check_map01]) { - if (lumpsfound[Check_FreeDoom]) + if (lumpsfound[Check_ad2lib]) + { + return IWAD_ActionDoom2; + } + else if (lumpsfound[Check_Hawk] && lumpsfound[Check_Car] && lumpsfound[Check_Nose]) + { + return IWAD_Harmony; + } + else if (lumpsfound[Check_FreeDoom]) { // Is there a 100% reliable way to tell FreeDoom and FreeDM // apart based solely on the lump names? @@ -287,7 +350,11 @@ static EIWADType ScanIWAD (const char *iwad) } else { - if (lumpsfound[Check_Extended]) + if (lumpsfound[Check_Blasphem]) + { + return IWAD_Blasphemer; + } + else if (lumpsfound[Check_Extended]) { return IWAD_HereticExtended; } @@ -379,8 +446,7 @@ static int CheckIWAD (const char *doomwaddir, WadStuff *wads) FString iwad; iwad.Format ("%s%s%s", doomwaddir, slash, IWADNames[i]); - FixPathSeperator (iwad.LockBuffer()); - iwad.UnlockBuffer(); + FixPathSeperator (iwad); if (FileExists (iwad)) { wads[i].Type = ScanIWAD (iwad); @@ -417,7 +483,7 @@ static int CheckIWAD (const char *doomwaddir, WadStuff *wads) // //========================================================================== -static EIWADType IdentifyVersion (const char *zdoom_wad) +static EIWADType IdentifyVersion (TArray &wadfiles, const char *iwad, const char *zdoom_wad) { WadStuff wads[countof(IWADNames)]; size_t foundwads[NUM_IWAD_TYPES] = { 0 }; @@ -428,10 +494,15 @@ static EIWADType IdentifyVersion (const char *zdoom_wad) bool iwadparmfound = false; FString custwad; + if (iwadparm == NULL && iwad != NULL && *iwad != 0) + { + iwadparm = iwad; + } + if (iwadparm) { custwad = iwadparm; - FixPathSeperator (custwad.LockBuffer()); + FixPathSeperator (custwad); if (CheckIWAD (custwad, wads)) { // -iwad parameter was a directory iwadparm = NULL; @@ -457,8 +528,7 @@ static EIWADType IdentifyVersion (const char *zdoom_wad) if (stricmp (key, "Path") == 0) { FString nice = NicePath(value); - FixPathSeperator(nice.LockBuffer()); - nice.UnlockBuffer(); + FixPathSeperator(nice); CheckIWAD(nice, wads); } } @@ -563,14 +633,14 @@ static EIWADType IdentifyVersion (const char *zdoom_wad) exit (0); // zdoom.pk3 must always be the first file loaded and the IWAD second. - D_AddFile (zdoom_wad); + D_AddFile (wadfiles, zdoom_wad); if (wads[pickwad].Type == IWAD_HexenDK) { // load hexen.wad before loading hexdd.wad - D_AddFile (wads[foundwads[IWAD_Hexen]-1].Path); + D_AddFile (wadfiles, wads[foundwads[IWAD_Hexen]-1].Path); } - D_AddFile (wads[pickwad].Path); + D_AddFile (wadfiles, wads[pickwad].Path); if (wads[pickwad].Type == IWAD_Strife) { // Try to load voices.wad along with strife1.wad @@ -586,16 +656,16 @@ static EIWADType IdentifyVersion (const char *zdoom_wad) path = FString (wads[pickwad].Path.GetChars(), lastslash + 1); } path += "voices.wad"; - D_AddFile (path); + D_AddFile (wadfiles, path); } return wads[pickwad].Type; } -const IWADInfo *D_FindIWAD(const char *basewad) +const IWADInfo *D_FindIWAD(TArray &wadfiles, const char *iwad, const char *basewad) { - EIWADType iwadType = IdentifyVersion(basewad); + EIWADType iwadType = IdentifyVersion(wadfiles, iwad, basewad); gameiwad = iwadType; const IWADInfo *iwad_info = &IWADInfos[iwadType]; I_SetIWADInfo(iwad_info); diff --git a/src/d_main.cpp b/src/d_main.cpp index d86a26465..4b3d4fcb0 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include "doomerrors.h" @@ -102,6 +103,8 @@ #include "m_cheat.h" #include "compatibility.h" #include "m_joy.h" +#include "sc_man.h" +#include "resourcefiles/resourcefile.h" EXTERN_CVAR(Bool, hud_althud) void DrawHUD(); @@ -118,7 +121,7 @@ extern void R_ExecuteSetViewSize (); extern void G_NewInit (); extern void SetupPlayerClasses (); extern bool CheckCheatmode (); -extern const IWADInfo *D_FindIWAD(const char *basewad); +const IWADInfo *D_FindIWAD(TArray &wadfiles, const char *iwad, const char *basewad); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -182,7 +185,7 @@ CVAR (Int, wipetype, 1, CVAR_ARCHIVE); CVAR (Int, snd_drawoutput, 0, 0); bool DrawFSHUD; // [RH] Draw fullscreen HUD? -wadlist_t *wadfiles; // [RH] remove limit on # of loaded wads +TArray allwads; bool devparm; // started game with -devparm const char *D_DrawIcon; // [RH] Patch name of icon to draw on next refresh int NoWipe; // [RH] Allow wipe? (Needs to be set each time) @@ -204,7 +207,6 @@ cycle_t FrameCycles; // PRIVATE DATA DEFINITIONS ------------------------------------------------ -static wadlist_t **wadtail = &wadfiles; static int demosequence; static int pagetic; @@ -492,12 +494,12 @@ CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL) break; case 1: // Doom2.exe compatible with a few relaxed settings - v = COMPATF_SHORTTEX|COMPATF_STAIRINDEX|COMPATF_USEBLOCKING|COMPATF_NODOORLIGHT| + v = COMPATF_SHORTTEX|COMPATF_STAIRINDEX|COMPATF_USEBLOCKING|COMPATF_NODOORLIGHT|COMPATF_SPRITESORT| COMPATF_TRACE|COMPATF_MISSILECLIP|COMPATF_SOUNDTARGET|COMPATF_DEHHEALTH|COMPATF_CROSSDROPOFF; break; case 2: // same as 1 but stricter (NO_PASSMOBJ and INVISIBILITY are also set) - v = COMPATF_SHORTTEX|COMPATF_STAIRINDEX|COMPATF_USEBLOCKING|COMPATF_NODOORLIGHT| + v = COMPATF_SHORTTEX|COMPATF_STAIRINDEX|COMPATF_USEBLOCKING|COMPATF_NODOORLIGHT|COMPATF_SPRITESORT| COMPATF_TRACE|COMPATF_MISSILECLIP|COMPATF_SOUNDTARGET|COMPATF_NO_PASSMOBJ|COMPATF_LIMITPAIN| COMPATF_DEHHEALTH|COMPATF_INVISIBILITY|COMPATF_CROSSDROPOFF|COMPATF_CORPSEGIBS; break; @@ -546,6 +548,7 @@ CVAR (Flag, compat_mushroom, compatflags, COMPATF_MUSHROOM); CVAR (Flag, compat_mbfmonstermove,compatflags, COMPATF_MBFMONSTERMOVE); CVAR (Flag, compat_corpsegibs, compatflags, COMPATF_CORPSEGIBS); CVAR (Flag, compat_noblockfriends,compatflags,COMPATF_NOBLOCKFRIENDS); +CVAR (Flag, compat_spritesort, compatflags,COMPATF_SPRITESORT); //========================================================================== // @@ -560,7 +563,7 @@ void D_Display () bool wipe; bool hw2d; - if (nodrawers) + if (nodrawers || screen == NULL) return; // for comparative timing / profiling cycle_t cycles; @@ -860,6 +863,9 @@ void D_DoomLoop () { int lasttic = 0; + // Clamp the timer to TICRATE until the playloop has been entered. + r_NoInterpolate = true; + for (;;) { try @@ -950,8 +956,7 @@ void D_PageDrawer (void) if (Page != NULL) { screen->DrawTexture (Page, 0, 0, - DTA_VirtualWidth, Page->GetWidth(), - DTA_VirtualHeight, Page->GetHeight(), + DTA_Fullscreen, true, DTA_Masked, false, DTA_BilinearFilter, true, TAG_DONE); @@ -1041,7 +1046,10 @@ void D_DoStrifeAdvanceDemo () pagetic = 7 * TICRATE; pagename = "PANEL1"; S_Sound (CHAN_VOICE | CHAN_UI, voices[0], 1, ATTN_NORM); - S_StartMusic ("d_intro"); + // The new Strife teaser has D_FMINTR. + // The full retail Strife has D_INTRO. + // And the old Strife teaser has both. (I do not know which one it actually uses, nor do I care.) + S_StartMusic (gameinfo.flags & GI_TEASER2 ? "d_fmintr" : "d_intro"); break; case 4: @@ -1239,11 +1247,11 @@ CCMD (endgame) // //========================================================================== -void D_AddFile (const char *file, bool check) +bool D_AddFile (TArray &wadfiles, const char *file, bool check, int position) { if (file == NULL) { - return; + return false; } if (check && !DirEntryExists (file)) @@ -1252,16 +1260,16 @@ void D_AddFile (const char *file, bool check) if (f == NULL) { Printf ("Can't find '%s'\n", file); - return; + return false; } file = f; } - wadlist_t *wad = (wadlist_t *)M_Malloc (sizeof(*wad) + strlen(file)); - *wadtail = wad; - wad->next = NULL; - strcpy (wad->name, file); - wadtail = &wad->next; + FString f = file; + FixPathSeperator(f); + if (position == -1) wadfiles.Push(f); + else wadfiles.Insert(position, f); + return true; } //========================================================================== @@ -1270,13 +1278,13 @@ void D_AddFile (const char *file, bool check) // //========================================================================== -void D_AddWildFile (const char *value) +void D_AddWildFile (TArray &wadfiles, const char *value) { const char *wadfile = BaseFileSearch (value, ".wad"); if (wadfile != NULL) { - D_AddFile (wadfile); + D_AddFile (wadfiles, wadfile); } else { // Try pattern matching @@ -1306,12 +1314,12 @@ void D_AddWildFile (const char *value) { if (sep == NULL) { - D_AddFile (I_FindName (&findstate)); + D_AddFile (wadfiles, I_FindName (&findstate)); } else { strcpy (sep+1, I_FindName (&findstate)); - D_AddFile (path); + D_AddFile (wadfiles, path); } } } while (I_FindNext (handle, &findstate) == 0); @@ -1328,7 +1336,7 @@ void D_AddWildFile (const char *value) // //========================================================================== -void D_AddConfigWads (const char *section) +void D_AddConfigWads (TArray &wadfiles, const char *section) { if (GameConfig->SetSection (section)) { @@ -1342,7 +1350,7 @@ void D_AddConfigWads (const char *section) { // D_AddWildFile resets GameConfig's position, so remember it GameConfig->GetPosition (pos); - D_AddWildFile (value); + D_AddWildFile (wadfiles, value); // Reset GameConfig's position to get next wad GameConfig->SetPosition (pos); } @@ -1358,7 +1366,7 @@ void D_AddConfigWads (const char *section) // //========================================================================== -static void D_AddDirectory (const char *dir) +static void D_AddDirectory (TArray &wadfiles, const char *dir) { char curdir[PATH_MAX]; @@ -1388,7 +1396,7 @@ static void D_AddDirectory (const char *dir) if (!(I_FindAttr (&findstate) & FA_DIREC)) { strcpy (skindir + stuffstart, I_FindName (&findstate)); - D_AddFile (skindir); + D_AddFile (wadfiles, skindir); } } while (I_FindNext (handle, &findstate) == 0); I_FindClose (handle); @@ -1483,9 +1491,9 @@ bool ConsiderPatches (const char *arg, const char *ext) for (i = 0; i < files->NumArgs(); ++i) { if ( (f = BaseFileSearch (files->GetArg (i), ".deh")) ) - DoDehPatch (f, false); + D_LoadDehFile(f); else if ( (f = BaseFileSearch (files->GetArg (i), ".bex")) ) - DoDehPatch (f, false); + D_LoadDehFile(f); } noDef = true; } @@ -1581,6 +1589,161 @@ void D_MultiExec (DArgs *list, bool usePullin) } } +static void GetCmdLineFiles(TArray &wadfiles) +{ + DArgs *files = Args->GatherFiles ("-file", ".wad", true); + DArgs *files1 = Args->GatherFiles (NULL, ".zip", false); + DArgs *files2 = Args->GatherFiles (NULL, ".pk3", false); + DArgs *files3 = Args->GatherFiles (NULL, ".txt", false); + if (files->NumArgs() > 0 || files1->NumArgs() > 0 || files2->NumArgs() > 0 || files3->NumArgs() > 0) + { + // Check for -file in shareware + if (gameinfo.flags & GI_SHAREWARE) + { + I_FatalError ("You cannot -file with the shareware version. Register!"); + } + + // the files gathered are wadfile/lump names + for (int i = 0; i < files->NumArgs(); i++) + { + D_AddWildFile (wadfiles, files->GetArg (i)); + } + for (int i = 0; i < files1->NumArgs(); i++) + { + D_AddWildFile (wadfiles, files1->GetArg (i)); + } + for (int i = 0; i < files2->NumArgs(); i++) + { + D_AddWildFile (wadfiles, files2->GetArg (i)); + } + for (int i = 0; i < files3->NumArgs(); i++) + { + D_AddWildFile (wadfiles, files3->GetArg (i)); + } + } + files->Destroy(); + files1->Destroy(); + files2->Destroy(); + files3->Destroy(); +} + +static void CopyFiles(TArray &to, TArray &from) +{ + unsigned int ndx = to.Reserve(from.Size()); + for(unsigned i=0;i &pwads, const char *fn, const char *data, int size) +{ + FScanner sc; + FString iwad; + int pos = 0; + + const char *lastSlash = strrchr (fn, '/'); + + sc.OpenMem("GAMEINFO", data, size); + while(sc.GetToken()) + { + sc.TokenMustBe(TK_Identifier); + FString nextKey = sc.String; + sc.MustGetToken('='); + if (!nextKey.CompareNoCase("IWAD")) + { + sc.MustGetString(); + iwad = sc.String; + } + else if (!nextKey.CompareNoCase("LOAD")) + { + do + { + sc.MustGetString(); + + // Try looking for the wad in the same directory as the .wad + // before looking for it in the current directory. + + if (lastSlash != NULL) + { + FString checkpath(fn, (lastSlash - fn) + 1); + checkpath += sc.String; + + if (!FileExists (checkpath)) + { + pos += D_AddFile(pwads, sc.String, true, pos); + } + else + { + pos += D_AddFile(pwads, checkpath, true, pos); + } + } + } + while (sc.CheckToken(',')); + } + } + return iwad; +} + +static FString CheckGameInfo(TArray & pwads) +{ + DWORD t = I_FPSTime(); + // scan the list of WADs backwards to find the last one that contains a GAMEINFO lump + for(int i=pwads.Size()-1; i>=0; i--) + { + bool isdir = false; + FileReader *wadinfo; + FResourceFile *resfile; + const char *filename = pwads[i]; + + // Does this exist? If so, is it a directory? + struct stat info; + if (stat(pwads[i], &info) != 0) + { + Printf(TEXTCOLOR_RED "Could not stat %s\n", filename); + continue; + } + isdir = (info.st_mode & S_IFDIR) != 0; + + if (!isdir) + { + try + { + wadinfo = new FileReader(filename); + } + catch (CRecoverableError &) + { + // Didn't find file + continue; + } + resfile = FResourceFile::OpenResourceFile(filename, wadinfo, true); + } + else + resfile = FResourceFile::OpenDirectory(filename, true); + + if (resfile != NULL) + { + DWORD cnt = resfile->LumpCount(); + for(int i=cnt-1; i>=0; i--) + { + FResourceLump *lmp = resfile->GetLump(i); + + if (lmp->Namespace == ns_global && !stricmp(lmp->Name, "GAMEINFO")) + { + // Found one! + FString iwad = ParseGameInfo(pwads, resfile->Filename, (const char*)lmp->CacheLump(), lmp->LumpSize); + delete resfile; + return iwad; + } + } + delete resfile; + } + } + t = I_FPSTime() - t; + Printf("Gameinfo scan took %d ms\n", t); + return ""; +} + //========================================================================== // // D_DoomMain @@ -1593,8 +1756,7 @@ void D_DoomMain (void) char *v; const char *wad; DArgs *execFiles; - - srand(I_MSTime()); + TArray pwads; // Set the FPU precision to 53 significant bits. This is the default // for Visual C++, but not for GCC, so some slight math variances @@ -1635,11 +1797,15 @@ void D_DoomMain (void) { I_FatalError ("Cannot find " BASEWAD); } + FString basewad = wad; // Load zdoom.pk3 alone so that we can get access to the internal gameinfos before // the IWAD is known. - const IWADInfo *iwad_info = D_FindIWAD(wad); + GetCmdLineFiles(pwads); + FString iwad = CheckGameInfo(pwads); + + const IWADInfo *iwad_info = D_FindIWAD(allwads, iwad, basewad); gameinfo.gametype = iwad_info->gametype; gameinfo.flags = iwad_info->flags; @@ -1656,7 +1822,7 @@ void D_DoomMain (void) // it for something else, so this gets to stay here. wad = BaseFileSearch ("zvox.wad", NULL); if (wad) - D_AddFile (wad); + D_AddFile (allwads, wad); // [RH] Add any .wad files in the skins directory #ifdef unix @@ -1665,27 +1831,27 @@ void D_DoomMain (void) file = progdir; #endif file += "skins"; - D_AddDirectory (file); + D_AddDirectory (allwads, file); #ifdef unix file = NicePath("~/" GAME_DIR "/skins"); - D_AddDirectory (file); + D_AddDirectory (allwads, file); #endif // Add common (global) wads - D_AddConfigWads ("Global.Autoload"); + D_AddConfigWads (allwads, "Global.Autoload"); // Add game-specific wads file = GameNames[gameinfo.gametype]; file += ".Autoload"; - D_AddConfigWads (file); + D_AddConfigWads (allwads, file); // Add IWAD-specific wads if (iwad_info->Autoname != NULL) { file = iwad_info->Autoname; file += ".Autoload"; - D_AddConfigWads(file); + D_AddConfigWads(allwads, file); } } @@ -1700,39 +1866,11 @@ void D_DoomMain (void) C_ExecCmdLineParams (); // [RH] do all +set commands on the command line - DArgs *files = Args->GatherFiles ("-file", ".wad", true); - DArgs *files1 = Args->GatherFiles (NULL, ".zip", false); - DArgs *files2 = Args->GatherFiles (NULL, ".pk3", false); - DArgs *files3 = Args->GatherFiles (NULL, ".txt", false); - if (files->NumArgs() > 0 || files1->NumArgs() > 0 || files2->NumArgs() > 0 || files3->NumArgs() > 0) - { - // Check for -file in shareware - if (gameinfo.flags & GI_SHAREWARE) - { - I_FatalError ("You cannot -file with the shareware version. Register!"); - } - - // the files gathered are wadfile/lump names - for (int i = 0; i < files->NumArgs(); i++) - { - D_AddWildFile (files->GetArg (i)); - } - for (int i = 0; i < files1->NumArgs(); i++) - { - D_AddWildFile (files1->GetArg (i)); - } - for (int i = 0; i < files2->NumArgs(); i++) - { - D_AddWildFile (files2->GetArg (i)); - } - for (int i = 0; i < files3->NumArgs(); i++) - { - D_AddWildFile (files3->GetArg (i)); - } - } + CopyFiles(allwads, pwads); Printf ("W_Init: Init WADfiles.\n"); - Wads.InitMultipleFiles (&wadfiles); + Wads.InitMultipleFiles (allwads); + allwads.Clear(); // [RH] Initialize localizable strings. GStrings.LoadStrings (false); @@ -1941,13 +2079,12 @@ void D_DoomMain (void) DecalLibrary.Clear (); DecalLibrary.ReadAllDecals (); - // [RH] Try adding .deh and .bex files on the command line. + // [RH] Add any .deh and .bex files on the command line. // If there are none, try adding any in the config file. + // Note that the command line overrides defaults from the config. - if (!ConsiderPatches ("-deh", ".deh") && - !ConsiderPatches ("-bex", ".bex") && - (gameinfo.gametype == GAME_Doom) && - GameConfig->SetSection ("Doom.DefaultDehacked")) + if ((ConsiderPatches("-deh", ".deh") | ConsiderPatches("-bex", ".bex")) == 0 && + gameinfo.gametype == GAME_Doom && GameConfig->SetSection ("Doom.DefaultDehacked")) { const char *key; const char *value; @@ -1957,17 +2094,19 @@ void D_DoomMain (void) if (stricmp (key, "Path") == 0 && FileExists (value)) { Printf ("Applying patch %s\n", value); - DoDehPatch (value, true); + D_LoadDehFile(value); } } } - DoDehPatch (NULL, true); // See if there's a patch in a PWAD - FinishDehPatch (); // Create replacements for dehacked pickups + // Load embedded Dehacked patches + D_LoadDehLumps(); + + // Create replacements for dehacked pickups + FinishDehPatch(); FActorInfo::StaticSetActorNums (); - // [RH] User-configurable startup strings. Because BOOM does. static const char *startupString[5] = { "STARTUP1", "STARTUP2", "STARTUP3", "STARTUP4", "STARTUP5" @@ -2035,7 +2174,7 @@ void D_DoomMain (void) V_Init2(); - files = Args->GatherFiles ("-playdemo", ".lmp", false); + DArgs *files = Args->GatherFiles ("-playdemo", ".lmp", false); if (files->NumArgs() > 0) { singledemo = true; // quit after one demo @@ -2065,7 +2204,10 @@ void D_DoomMain (void) if (autostart || netgame) { // Do not do any screenwipes when autostarting a game. - NoWipe = 35; + if (!Args->CheckParm("-warpwipe")) + { + NoWipe = TICRATE; + } CheckWarpTransMap (startmap, true); if (demorecording) G_BeginRecording (startmap); diff --git a/src/d_main.h b/src/d_main.h index dac990bd3..517653bec 100644 --- a/src/d_main.h +++ b/src/d_main.h @@ -49,7 +49,7 @@ void D_PageTicker (void); void D_PageDrawer (void); void D_AdvanceDemo (void); void D_StartTitle (void); -void D_AddFile (const char *file, bool check = true); +bool D_AddFile (TArray &wadfiles, const char *file, bool check = true, int position = -1); // [RH] Set this to something to draw an icon during the next screen refresh. @@ -77,8 +77,11 @@ enum EIWADType IWAD_FreeDoomU, IWAD_FreeDoom1, IWAD_FreeDM, + IWAD_Blasphemer, IWAD_ChexQuest, IWAD_ChexQuest3, + IWAD_ActionDoom2, + IWAD_Harmony, IWAD_Custom, NUM_IWAD_TYPES diff --git a/src/d_net.cpp b/src/d_net.cpp index f916721b4..264aed1df 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -2100,9 +2100,9 @@ void Net_DoCommand (int type, BYTE **stream, int player) case DEM_SUMMONFOE2: { const PClass *typeinfo; - int angle; - SWORD tid; - BYTE special; + int angle = 0; + SWORD tid = 0; + BYTE special = 0; int args[5]; s = ReadString (stream); @@ -2367,8 +2367,10 @@ void Net_DoCommand (int type, BYTE **stream, int player) } break; - case DEM_CONVERSATION: - P_ConversationCommand (player, stream); + case DEM_CONVREPLY: + case DEM_CONVCLOSE: + case DEM_CONVNULL: + P_ConversationCommand (type, player, stream); break; case DEM_SETSLOT: @@ -2500,29 +2502,8 @@ void Net_SkipCommand (int type, BYTE **stream) skip = 3 + *(*stream + 2) * 4; break; - case DEM_CONVERSATION: - { - t = **stream; - skip = 1; - - switch (t) - { - case CONV_ANIMATE: - skip += 1; - break; - - case CONV_GIVEINVENTORY: - skip += strlen ((char *)(*stream + skip)) + 1; - break; - - case CONV_TAKEINVENTORY: - skip += strlen ((char *)(*stream + skip)) + 3; - break; - - default: - break; - } - } + case DEM_CONVREPLY: + skip = 3; break; case DEM_SETSLOT: diff --git a/src/d_player.h b/src/d_player.h index fa936b552..3c9ad954c 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -276,6 +276,7 @@ public: bool centering; BYTE turnticks; bool attackdown; + bool usedown; DWORD oldbuttons; int health; // only used between levels, mo->health // is used during levels @@ -414,9 +415,6 @@ void P_CheckPlayerSprites(); #define CROUCHSPEED (FRACUNIT/12) -#define MAX_DN_ANGLE 56 // Max looking down angle -#define MAX_UP_ANGLE 32 // Max looking up angle - // [GRB] Custom player classes enum diff --git a/src/d_protocol.h b/src/d_protocol.h index eae1f4aa9..a78c39ae2 100644 --- a/src/d_protocol.h +++ b/src/d_protocol.h @@ -147,14 +147,17 @@ enum EDemoCommand DEM_ADDCONTROLLER, // 48 Player to add to the controller list. DEM_DELCONTROLLER, // 49 Player to remove from the controller list. DEM_KILLCLASSCHEAT, // 50 String: Class to kill. - DEM_CONVERSATION, // 51 Make conversations work. + DEM_UNDONE11, // 51 DEM_SUMMON2, // 52 String: Thing to fabricate, WORD: angle offset DEM_SUMMONFRIEND2, // 53 DEM_SUMMONFOE2, // 54 DEM_ADDSLOTDEFAULT, // 55 DEM_ADDSLOT, // 56 DEM_SETSLOT, // 57 - DEM_SUMMONMBF, + DEM_SUMMONMBF, // 58 + DEM_CONVREPLY, // 59 Word: Dialogue node, Byte: Reply number + DEM_CONVCLOSE, // 60 + DEM_CONVNULL, // 61 }; // The following are implemented by cht_DoCheat in m_cheat.cpp diff --git a/src/dobject.cpp b/src/dobject.cpp index 00ae9ee50..bb4c0d04b 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -508,6 +508,88 @@ size_t DObject::StaticPointerSubstitution (DObject *old, DObject *notOld) return changed; } +void DObject::SerializeUserVars(FArchive &arc) +{ + PSymbolTable *symt; + FName varname; + DWORD count, j; + int *varloc; + + if (SaveVersion < 1933) + { + return; + } + + symt = &GetClass()->Symbols; + + if (arc.IsStoring()) + { + // Write all user variables. + for (; symt != NULL; symt = symt->ParentSymbolTable) + { + for (unsigned i = 0; i < symt->Symbols.Size(); ++i) + { + PSymbol *sym = symt->Symbols[i]; + if (sym->SymbolType == SYM_Variable) + { + PSymbolVariable *var = static_cast(sym); + if (var->bUserVar) + { + count = var->ValueType.Type == VAL_Array ? var->ValueType.size : 1; + varloc = (int *)(reinterpret_cast(this) + var->offset); + + arc << var->SymbolName; + arc.WriteCount(count); + for (j = 0; j < count; ++j) + { + arc << varloc[j]; + } + } + } + } + } + // Write terminator. + varname = NAME_None; + arc << varname; + } + else + { + // Read user variables until 'None' is encountered. + arc << varname; + while (varname != NAME_None) + { + PSymbol *sym = symt->FindSymbol(varname, true); + DWORD wanted = 0; + + if (sym != NULL && sym->SymbolType == SYM_Variable) + { + PSymbolVariable *var = static_cast(sym); + + if (var->bUserVar) + { + wanted = var->ValueType.Type == VAL_Array ? var->ValueType.size : 1; + varloc = (int *)(reinterpret_cast(this) + var->offset); + } + } + count = arc.ReadCount(); + for (j = 0; j < MIN(wanted, count); ++j) + { + arc << varloc[j]; + } + if (wanted < count) + { + // Ignore remaining values from archive. + for (; j < count; ++j) + { + int foo; + arc << foo; + } + } + arc << varname; + } + } +} + void DObject::Serialize (FArchive &arc) { ObjectFlags |= OF_SerialSuccess; diff --git a/src/dobject.h b/src/dobject.h index 8ed1f2352..fb0d30f7c 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -455,6 +455,7 @@ public: inline bool IsKindOf (const PClass *base) const; inline bool IsA (const PClass *type) const; + void SerializeUserVars(FArchive &arc); virtual void Serialize (FArchive &arc); // For catching Serialize functions in derived classes diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 33faac64d..0e2e26aac 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -37,6 +37,7 @@ #include "actor.h" #include "templates.h" #include "autosegs.h" +#include "v_text.h" IMPLEMENT_POINTY_CLASS(PClass) DECLARE_POINTER(ParentClass) @@ -134,7 +135,7 @@ void PClass::StaticFreeData (PClass *type) { if (type->Defaults != NULL) { - delete[] type->Defaults; + M_Free(type->Defaults); type->Defaults = NULL; } type->FreeStateList (); @@ -211,7 +212,7 @@ void PClass::InsertIntoHash () else if (lexx == 0) { // This type has already been inserted // ... but there is no need whatsoever to make it a fatal error! - Printf ("Tried to register class '%s' more than once.\n", TypeName.GetChars()); + Printf (TEXTCOLOR_RED"Tried to register class '%s' more than once.\n", TypeName.GetChars()); break; } else @@ -307,7 +308,7 @@ PClass *PClass::CreateDerivedClass (FName name, unsigned int size) type->Meta = Meta; // Set up default instance of the new class. - type->Defaults = new BYTE[size]; + type->Defaults = (BYTE *)M_Malloc(size); memcpy (type->Defaults, Defaults, Size); if (size > Size) { @@ -341,6 +342,19 @@ PClass *PClass::CreateDerivedClass (FName name, unsigned int size) return type; } +// Add bytes to the end of this class. Returns the +// previous size of the class. +unsigned int PClass::Extend(unsigned int extension) +{ + assert(this->bRuntimeClass); + + unsigned int oldsize = Size; + Size += extension; + Defaults = (BYTE *)M_Realloc(Defaults, Size); + memset(Defaults + oldsize, 0, extension); + return oldsize; +} + // Like FindClass but creates a placeholder if no class // is found. CreateDerivedClass will automatcally fill in // the placeholder when the actual class is defined. diff --git a/src/dobjtype.h b/src/dobjtype.h index b6ff7bc43..264aaf11c 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -55,8 +55,9 @@ class PSymbolVariable : public PSymbol DECLARE_CLASS(PSymbolVariable, PSymbol); public: FExpressionType ValueType; - int size; + //int size; intptr_t offset; + bool bUserVar; PSymbolVariable(FName name) : PSymbol(name, SYM_Variable) {} PSymbolVariable() : PSymbol(NAME_None, SYM_Variable) {} @@ -142,6 +143,8 @@ struct PSymbolTable private: PSymbolTable *ParentSymbolTable; TArray Symbols; + + friend class DObject; }; // Meta-info for every class derived from DObject --------------------------- @@ -177,6 +180,7 @@ public: void InsertIntoHash (); DObject *CreateNew () const; PClass *CreateDerivedClass (FName name, unsigned int size); + unsigned int Extend(unsigned int extension); void InitializeActorInfo (); void BuildFlatPointers (); void FreeStateList(); diff --git a/src/doomdef.h b/src/doomdef.h index 868479faa..485f15a5e 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -324,6 +324,7 @@ enum COMPATF_MBFMONSTERMOVE = 1 << 24, // Monsters are affected by friction and pushers/pullers. COMPATF_CORPSEGIBS = 1 << 25, // Crushed monsters are turned into gibs, rather than replaced by gibs. COMPATF_NOBLOCKFRIENDS = 1 << 26, // Friendly monsters aren't blocked by monster-blocking lines. + COMPATF_SPRITESORT = 1 << 27, // Invert sprite sorting order for sprites of equal distance }; // Emulate old bugs for select maps. These are not exposed by a cvar diff --git a/src/dsectoreffect.cpp b/src/dsectoreffect.cpp index 48e58d0a9..6c717b998 100644 --- a/src/dsectoreffect.cpp +++ b/src/dsectoreffect.cpp @@ -27,10 +27,12 @@ #include "p_local.h" #include "p_3dmidtex.h" #include "r_interpolate.h" +#include "statnums.h" IMPLEMENT_CLASS (DSectorEffect) DSectorEffect::DSectorEffect () +: DThinker(STAT_SECTOREFFECT) { m_Sector = NULL; } diff --git a/src/dthinker.cpp b/src/dthinker.cpp index d7df8abc3..3074aca99 100644 --- a/src/dthinker.cpp +++ b/src/dthinker.cpp @@ -203,7 +203,7 @@ void DThinker::SerializeAll(FArchive &arc, bool hubLoad) // before the crash - which is not the case with all other options. //DestroyAllThinkers(); - I_FatalError(err.GetMessage()); + I_FatalError("%s", err.GetMessage()); throw; } bSerialOverride = false; diff --git a/src/dthinker.h b/src/dthinker.h index e50141475..32857d86b 100644 --- a/src/dthinker.h +++ b/src/dthinker.h @@ -36,6 +36,7 @@ #include #include "dobject.h" +#include "statnums.h" class AActor; class player_t; @@ -62,7 +63,7 @@ class DThinker : public DObject { DECLARE_CLASS (DThinker, DObject) public: - DThinker (int statnum = MAX_STATNUM) throw(); + DThinker (int statnum = STAT_DEFAULT) throw(); void Destroy (); virtual ~DThinker (); virtual void Tick (); diff --git a/src/f_finale.cpp b/src/f_finale.cpp index 82b8a9e02..e08f249eb 100644 --- a/src/f_finale.cpp +++ b/src/f_finale.cpp @@ -809,8 +809,8 @@ void F_DemonScroll () int yval; FTexture *final1 = TexMan(tex1); FTexture *final2 = TexMan(tex2); - int fwidth = final1->GetWidth(); - int fheight = final1->GetHeight(); + int fwidth = final1->GetScaledWidth(); + int fheight = final1->GetScaledHeight(); if (FinaleCount < 70) { @@ -884,10 +884,7 @@ void F_DrawUnderwater(void) // intentional fall-through case 2: pic = TexMan("E2END"); - screen->DrawTexture (pic, 0, 0, - DTA_VirtualWidth, pic->GetWidth(), - DTA_VirtualHeight, pic->GetHeight(), - TAG_DONE); + screen->DrawTexture (pic, 0, 0, DTA_Fullscreen, true, TAG_DONE); screen->FillBorder (NULL); paused = false; menuactive = MENU_Off; @@ -907,10 +904,7 @@ void F_DrawUnderwater(void) screen->UpdatePalette (); pic = TexMan("TITLE"); - screen->DrawTexture (pic, 0, 0, - DTA_VirtualWidth, pic->GetWidth(), - DTA_VirtualHeight, pic->GetHeight(), - TAG_DONE); + screen->DrawTexture (pic, 0, 0, DTA_Fullscreen, true, TAG_DONE); screen->FillBorder (NULL); NoWipe = 0; break; @@ -960,8 +954,8 @@ void F_BunnyScroll (void) V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT); tex = TexMan(tex1); - fwidth = tex->GetWidth(); - fheight = tex->GetHeight(); + fwidth = tex->GetScaledWidth(); + fheight = tex->GetScaledHeight(); scrolled = clamp (((signed)FinaleCount-230)*fwidth/640, 0, fwidth); @@ -1315,25 +1309,24 @@ void F_Drawer (void) if (picname != NULL) { FTexture *pic = TexMan[picname]; - screen->DrawTexture (pic, 0, 0, - DTA_VirtualWidth, pic->GetWidth(), - DTA_VirtualHeight, pic->GetHeight(), - TAG_DONE); + screen->DrawTexture (pic, 0, 0, DTA_Fullscreen, true, TAG_DONE); screen->FillBorder (NULL); if (FinaleStage >= 14) { // Chess pic, draw the correct character graphic + double w = pic->GetScaledWidthDouble(); + double h = pic->GetScaledHeightDouble(); if (multiplayer) { screen->DrawTexture (TexMan["CHESSALL"], 20, 0, - DTA_VirtualWidth, pic->GetWidth(), - DTA_VirtualHeight, pic->GetHeight(), TAG_DONE); + DTA_VirtualWidth, w, + DTA_VirtualHeight, h, TAG_DONE); } else if (players[consoleplayer].CurrentPlayerClass > 0) { picname = players[consoleplayer].CurrentPlayerClass == 1 ? "CHESSC" : "CHESSM"; screen->DrawTexture (TexMan[picname], 60, 0, - DTA_VirtualWidth, pic->GetWidth(), - DTA_VirtualHeight, pic->GetHeight(), TAG_DONE); + DTA_VirtualWidth, w, + DTA_VirtualHeight, h, TAG_DONE); } } } diff --git a/src/farchive.cpp b/src/farchive.cpp index f8992b81c..6b9ba2790 100644 --- a/src/farchive.cpp +++ b/src/farchive.cpp @@ -3,7 +3,7 @@ ** Implements an archiver for DObject serialization. ** **--------------------------------------------------------------------------- -** Copyright 1998-2006 Randy Heit +** Copyright 1998-2009 Randy Heit ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -1074,6 +1074,7 @@ FArchive &FArchive::WriteObject (DObject *obj) WriteClass (type); // Printf ("Make class %s (%u)\n", type->Name, m_File->Tell()); MapObject (obj); + obj->SerializeUserVars (*this); obj->Serialize (*this); obj->CheckIfSerialized (); } @@ -1105,6 +1106,7 @@ FArchive &FArchive::WriteObject (DObject *obj) WriteCount (m_TypeMap[type->ClassIndex].toArchive); // Printf ("Reuse class %s (%u)\n", type->Name, m_File->Tell()); MapObject (obj); + obj->SerializeUserVars (*this); obj->Serialize (*this); obj->CheckIfSerialized (); } @@ -1160,6 +1162,7 @@ FArchive &FArchive::ReadObject (DObject* &obj, PClass *wanttype) // stored in the archive. AActor *tempobj = static_cast(type->CreateNew ()); MapObject (obj != NULL ? obj : tempobj); + tempobj->SerializeUserVars (*this); tempobj->Serialize (*this); tempobj->CheckIfSerialized (); // If this player is not present anymore, keep the new body @@ -1187,6 +1190,7 @@ FArchive &FArchive::ReadObject (DObject* &obj, PClass *wanttype) // Printf ("New class: %s (%u)\n", type->Name, m_File->Tell()); obj = type->CreateNew (); MapObject (obj); + obj->SerializeUserVars (*this); obj->Serialize (*this); obj->CheckIfSerialized (); break; @@ -1201,6 +1205,7 @@ FArchive &FArchive::ReadObject (DObject* &obj, PClass *wanttype) AActor *tempobj = static_cast(type->CreateNew ()); MapObject (obj != NULL ? obj : tempobj); + tempobj->SerializeUserVars (*this); tempobj->Serialize (*this); tempobj->CheckIfSerialized (); if (obj != NULL) @@ -1225,6 +1230,7 @@ FArchive &FArchive::ReadObject (DObject* &obj, PClass *wanttype) // Printf ("Use class: %s (%u)\n", type->Name, m_File->Tell()); obj = type->CreateNew (); MapObject (obj); + obj->SerializeUserVars (*this); obj->Serialize (*this); obj->CheckIfSerialized (); break; @@ -1389,7 +1395,7 @@ const PClass *FArchive::ReadClass () FName zaname(typeName.val, true); if (zaname != NAME_None) { - for (unsigned int i = 0; i < PClass::m_Types.Size(); i++) + for (unsigned int i = PClass::m_Types.Size(); i-- > 0; ) { if (PClass::m_Types[i]->TypeName == zaname) { diff --git a/src/g_doom/a_bossbrain.cpp b/src/g_doom/a_bossbrain.cpp index 49517bb8e..38e90c19a 100644 --- a/src/g_doom/a_bossbrain.cpp +++ b/src/g_doom/a_bossbrain.cpp @@ -114,6 +114,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BrainSpit) // spawn brain missile spit = P_SpawnMissile (self, targ, spawntype); + // Boss cubes should move freely to their destination so it's + // probably best to disable all collision detection for them. + if (spit->flags & MF_NOCLIP) spit->flags5 |= MF5_NOINTERACTION; + if (spit != NULL) { spit->target = targ; diff --git a/src/g_doom/a_doomweaps.cpp b/src/g_doom/a_doomweaps.cpp index 024495ce6..7577b1515 100644 --- a/src/g_doom/a_doomweaps.cpp +++ b/src/g_doom/a_doomweaps.cpp @@ -141,7 +141,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Saw) // use meleerange + 1 so the puff doesn't skip the flash (i.e. plays all states) P_LineAttack (self, angle, MELEERANGE+1, P_AimLineAttack (self, angle, MELEERANGE+1, &linetarget), damage, - GetDefaultByType(pufftype)->DamageType, pufftype); + NAME_None, pufftype); if (!linetarget) { @@ -272,7 +272,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CloseShotgun2) { PARAM_ACTION_PROLOGUE; S_Sound (self, CHAN_WEAPON, "weapons/sshotc", 1, ATTN_NORM); - CALL_ACTION(A_ReFire, self); + A_ReFire (self); return 0; } diff --git a/src/g_doom/a_fatso.cpp b/src/g_doom/a_fatso.cpp index 741622b9a..a147101d3 100644 --- a/src/g_doom/a_fatso.cpp +++ b/src/g_doom/a_fatso.cpp @@ -124,7 +124,14 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FatAttack3) // Original idea: Linguica // -AActor * P_OldSpawnMissile(AActor * source, AActor * dest, const PClass *type); +AActor * P_OldSpawnMissile(AActor * source, AActor * owner, AActor * dest, const PClass *type); + +enum +{ + MSF_Standard = 0, + MSF_Classic = 1, + MSF_DontHurt = 2, +}; DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Mushroom) { @@ -140,14 +147,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Mushroom) if (n == 0) n = self->Damage; // GetMissileDamage (0, 1); if (spawntype == NULL) spawntype = PClass::FindClass("FatShot"); - P_RadiusAttack (self, self->target, 128, 128, self->DamageType, true); - if (self->z <= self->floorz + (128<target, 128, 128, self->DamageType, !(flags & MSF_DontHurt)); + P_CheckSplash(self, 128<target) : self; target->height = self->height; for (i = -n; i <= n; i += 8) { @@ -157,13 +162,14 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Mushroom) target->x = self->x + (i << FRACBITS); // Aim in many directions from source target->y = self->y + (j << FRACBITS); target->z = self->z + (P_AproxDistance(i,j) * vrange); // Aim up fairly high - if (flags == 0 && (!(self->state->DefineFlags & SDF_DEHACKED) || !(i_compatflags & COMPATF_MUSHROOM))) - { - mo = P_SpawnMissile (self, target, spawntype); // Launch fireball + if ((flags & MSF_Classic) || // Flag explicitely set, or no flags and compat options + (flags == 0 && (self->state->DefineFlags & SDF_DEHACKED) && (i_compatflags & COMPATF_MUSHROOM))) + { // Use old function for MBF compatibility + mo = P_OldSpawnMissile (self, master, target, spawntype); } - else + else // Use normal function { - mo = P_OldSpawnMissile (self, target, spawntype); // Launch fireball + mo = P_SpawnMissile(self, target, spawntype, master); } if (mo != NULL) { // Slow it down a bit diff --git a/src/g_doom/a_lostsoul.cpp b/src/g_doom/a_lostsoul.cpp index 5851b158b..887f2d9d7 100644 --- a/src/g_doom/a_lostsoul.cpp +++ b/src/g_doom/a_lostsoul.cpp @@ -19,7 +19,6 @@ // SkullAttack // Fly at the player like a missile. // -#define SKULLSPEED (20*FRACUNIT) void A_SkullAttack(AActor *self, fixed_t speed) { @@ -57,8 +56,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SkullAttack) return 0; } - - DEFINE_ACTION_FUNCTION(AActor, A_BetaSkullAttack) { PARAM_ACTION_PROLOGUE; diff --git a/src/g_doom/a_painelemental.cpp b/src/g_doom/a_painelemental.cpp index f0c77eaef..b8be64f59 100644 --- a/src/g_doom/a_painelemental.cpp +++ b/src/g_doom/a_painelemental.cpp @@ -31,9 +31,6 @@ static const PClass *GetSpawnType(VMValue *param) } -#define SKULLSPEED (20*FRACUNIT) -void A_SkullAttack(AActor *self, fixed_t speed); - // // A_PainShootSkull // Spawn a lost soul and launch it at the target diff --git a/src/g_doom/a_scriptedmarine.cpp b/src/g_doom/a_scriptedmarine.cpp index 6ca1c1995..34d7b5470 100644 --- a/src/g_doom/a_scriptedmarine.cpp +++ b/src/g_doom/a_scriptedmarine.cpp @@ -97,14 +97,6 @@ void AScriptedMarine::BeginPlay () } } } - - // Copy the standard player's scaling - AActor * playerdef = GetDefaultByName("DoomPlayer"); - if (playerdef != NULL) - { - scaleX = playerdef->scaleX; - scaleY = playerdef->scaleY; - } } void AScriptedMarine::Tick () @@ -112,7 +104,7 @@ void AScriptedMarine::Tick () Super::Tick (); // Override the standard sprite, if desired - if (SpriteOverride != 0 && sprite == GetClass()->ActorInfo->OwnedStates[0].sprite) + if (SpriteOverride != 0 && sprite == SpawnState->sprite) { sprite = SpriteOverride; } @@ -474,7 +466,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_M_FireShotgun2) P_LineAttack (self, angle, MISSILERANGE, pitch + (pr_m_fireshotgun2.Random2() * 332063), damage, - NAME_None, PClass::FindClass(NAME_BulletPuff)); + NAME_None, NAME_BulletPuff); } self->special1 = level.maptime; return 0; @@ -648,14 +640,12 @@ void AScriptedMarine::SetSprite (const PClass *source) if (source == NULL || source->ActorInfo == NULL) { // A valid actor class wasn't passed, so use the standard sprite SpriteOverride = sprite = GetClass()->ActorInfo->OwnedStates[0].sprite; - // Copy the standard player's scaling - AActor * playerdef = GetDefaultByName("DoomPlayer"); - if (playerdef == NULL) playerdef = GetDefaultByType(RUNTIME_CLASS(AScriptedMarine)); - scaleX = playerdef->scaleX; - scaleY = playerdef->scaleY; + // Copy the standard scaling + scaleX = GetDefault()->scaleX; + scaleY = GetDefault()->scaleY; } else - { // Use the same sprite the passed class spawns with + { // Use the same sprite and scaling the passed class spawns with SpriteOverride = sprite = GetDefaultByType (source)->SpawnState->sprite; scaleX = GetDefaultByType(source)->scaleX; scaleY = GetDefaultByType(source)->scaleY; diff --git a/src/g_game.cpp b/src/g_game.cpp index 43587bb08..dc576c2fc 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1316,7 +1316,7 @@ void G_PlayerReborn (int player) p->skill = b_skill; //Added by MC: - p->oldbuttons = ~0, p->attackdown = true; // don't do anything immediately + p->oldbuttons = ~0, p->attackdown = true; p->usedown = true; // don't do anything immediately p->original_oldbuttons = ~0; p->playerstate = PST_LIVE; @@ -1475,10 +1475,8 @@ void G_DeathMatchSpawnPlayer (int playernum) { if (playernum < 4) spot->type = playernum+1; - else if (gameinfo.gametype != GAME_Hexen) - spot->type = playernum+4001-4; // [RH] > 4 players - else - spot->type = playernum+9100-4; + else + spot->type = playernum + gameinfo.player5start - 4; } AActor *mo = P_SpawnPlayer (spot); @@ -1506,6 +1504,7 @@ static void G_QueueBody (AActor *body) { *translationtables[TRANSLATION_PlayerCorpses][modslot] = *TranslationToTable(body->Translation); body->Translation = TRANSLATION(TRANSLATION_PlayerCorpses,modslot); + translationtables[TRANSLATION_PlayerCorpses][modslot]->UpdateNative(); } bodyqueslot++; @@ -1568,17 +1567,13 @@ void G_DoReborn (int playernum, bool freshbot) // fake as other player // [RH] These numbers should be common across all games. Or better yet, not // used at all outside P_SpawnMapThing(). - if (playernum < 4 || gameinfo.gametype == GAME_Strife) + if (playernum < 4) { playerstarts[i].type = playernum + 1; } - else if (gameinfo.gametype == GAME_Hexen) - { - playerstarts[i].type = playernum + 9100 - 4; - } else { - playerstarts[i].type = playernum + 4001 - 4; + playerstarts[i].type = playernum + gameinfo.player5start - 4; } AActor *mo = P_SpawnPlayer (&playerstarts[i]); if (mo != NULL) P_PlayerStartStomp(mo); @@ -1850,14 +1845,11 @@ FString G_BuildSaveName (const char *prefix, int slot) { leader = save_dir; } + if (leader.IsEmpty()) + { #ifdef unix - if (leader.IsEmpty()) - { leader = "~/" GAME_DIR; - } #elif defined(__APPLE__) - if (leader.IsEmpty()) - { char cpath[PATH_MAX]; FSRef folder; @@ -1866,8 +1858,10 @@ FString G_BuildSaveName (const char *prefix, int slot) { leader << cpath << "/" GAME_DIR "/Savegames/"; } - } +#else + leader = progdir; #endif + } } size_t len = leader.Len(); if (leader[0] != '\0' && leader[len-1] != '\\' && leader[len-1] != '/') diff --git a/src/g_heretic/a_hereticartifacts.cpp b/src/g_heretic/a_hereticartifacts.cpp index accb8afa9..7e092861a 100644 --- a/src/g_heretic/a_hereticartifacts.cpp +++ b/src/g_heretic/a_hereticartifacts.cpp @@ -53,10 +53,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_TimeBomb) self->RenderStyle = STYLE_Add; self->alpha = FRACUNIT; P_RadiusAttack (self, self->target, 128, 128, self->DamageType, true); - if (self->z <= self->floorz + (128<target || pr_impmsatk() > 64) + { + self->SetState (self->SeeState); + return 0; + } + A_SkullAttack(self, 12 * FRACUNIT); + return 0; +} + //---------------------------------------------------------------------------- // // PROC A_ImpExplode diff --git a/src/g_hexen/a_bishop.cpp b/src/g_hexen/a_bishop.cpp index 22541a57a..23b4de431 100644 --- a/src/g_hexen/a_bishop.cpp +++ b/src/g_hexen/a_bishop.cpp @@ -65,7 +65,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_BishopAttack2) if (mo != NULL) { mo->tracer = self->target; - mo->special2 = 16; // High word == x/y, Low word == z } self->special1--; return 0; @@ -81,25 +80,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_BishopMissileWeave) { PARAM_ACTION_PROLOGUE; - fixed_t newX, newY; - int weaveXY, weaveZ; - int angle; - - if (self->special2 == 0) self->special2 = 16; - - weaveXY = self->special2 >> 16; - weaveZ = self->special2 & 0xFFFF; - angle = (self->angle + ANG90) >> ANGLETOFINESHIFT; - newX = self->x - FixedMul (finecosine[angle], FloatBobOffsets[weaveXY]<<1); - newY = self->y - FixedMul (finesine[angle], FloatBobOffsets[weaveXY]<<1); - weaveXY = (weaveXY + 2) & 63; - newX += FixedMul (finecosine[angle], FloatBobOffsets[weaveXY]<<1); - newY += FixedMul (finesine[angle], FloatBobOffsets[weaveXY]<<1); - P_TryMove (self, newX, newY, true); - self->z -= FloatBobOffsets[weaveZ]; - weaveZ = (weaveZ + 2) & 63; - self->z += FloatBobOffsets[weaveZ]; - self->special2 = weaveZ + (weaveXY<<16); + A_Weave(self, 2, 2, 2*FRACUNIT, FRACUNIT); return 0; } diff --git a/src/g_hexen/a_blastradius.cpp b/src/g_hexen/a_blastradius.cpp index bbc9af2d2..7a2a6028c 100644 --- a/src/g_hexen/a_blastradius.cpp +++ b/src/g_hexen/a_blastradius.cpp @@ -8,87 +8,21 @@ #include "s_sound.h" */ +/* For reference, the default values: #define BLAST_RADIUS_DIST 255*FRACUNIT #define BLAST_SPEED 20*FRACUNIT #define BLAST_FULLSTRENGTH 255 +*/ // Disc of Repulsion -------------------------------------------------------- -class AArtiBlastRadius : public AInventory -{ - DECLARE_CLASS (AArtiBlastRadius, AInventory) -public: - bool Use (bool pickup); -protected: - void BlastActor (AActor *victim, fixed_t strength); -}; - -IMPLEMENT_CLASS (AArtiBlastRadius) - -//========================================================================== -// -// AArtiBlastRadius :: Activate -// -// Blast all actors away -// -//========================================================================== - -bool AArtiBlastRadius::Use (bool pickup) -{ - AActor *mo; - TThinkerIterator iterator; - fixed_t dist; - - S_Sound (Owner, CHAN_AUTO, "BlastRadius", 1, ATTN_NORM); - P_NoiseAlert (Owner, Owner); - - while ( (mo = iterator.Next ()) ) - { - if ((mo == Owner) || (mo->flags2 & MF2_BOSS)) - { // Not a valid monster - continue; - } - if ((mo->flags & MF_ICECORPSE) || (mo->flags3 & MF3_CANBLAST)) - { - // Let these special cases go - } - else if ((mo->flags3 & MF3_ISMONSTER) && (mo->health <= 0)) - { - continue; - } - else if (!(mo->flags3 & MF3_ISMONSTER) && - !(mo->player) && - !(mo->flags & MF_MISSILE) && - !(mo->flags3 & MF3_CANBLAST) && - !(mo->flags6 & MF6_TOUCHY)) - { // Must be monster, player, missile, or touchy - continue; - } - if (mo->flags2 & MF2_DORMANT) - { - continue; // no dormant creatures - } - if (mo->flags3 & MF3_DONTBLAST) - { // A few things that would normally be blastable should not be blasted - continue; - } - dist = P_AproxDistance (Owner->x - mo->x, Owner->y - mo->y); - if (dist > BLAST_RADIUS_DIST) - { // Out of range - continue; - } - BlastActor (mo, BLAST_FULLSTRENGTH); - } - return true; -} - //========================================================================== // // AArtiBlastRadius :: BlastActor // //========================================================================== -void AArtiBlastRadius::BlastActor (AActor *victim, fixed_t strength) +void BlastActor (AActor *victim, fixed_t strength, fixed_t speed, AActor * Owner, const PClass * blasteffect) { angle_t angle,ang; AActor *mo; @@ -101,72 +35,121 @@ void AArtiBlastRadius::BlastActor (AActor *victim, fixed_t strength) angle = R_PointToAngle2 (Owner->x, Owner->y, victim->x, victim->y); angle >>= ANGLETOFINESHIFT; - if (strength < BLAST_FULLSTRENGTH) + victim->velx = FixedMul (speed, finecosine[angle]); + victim->vely = FixedMul (speed, finesine[angle]); + + // Spawn blast puff + ang = R_PointToAngle2 (victim->x, victim->y, Owner->x, Owner->y); + ang >>= ANGLETOFINESHIFT; + x = victim->x + FixedMul (victim->radius+FRACUNIT, finecosine[ang]); + y = victim->y + FixedMul (victim->radius+FRACUNIT, finesine[ang]); + z = victim->z - victim->floorclip + (victim->height>>1); + mo = Spawn (blasteffect, x, y, z, ALLOW_REPLACE); + if (mo) { - victim->velx = FixedMul (strength, finecosine[angle]); - victim->vely = FixedMul (strength, finesine[angle]); - if (victim->player) + mo->velx = victim->velx; + mo->vely = victim->vely; + } + if (victim->flags & MF_MISSILE) + { + // [RH] Floor and ceiling huggers should not be blasted vertically. + if (!(victim->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER))) { - // Players handled automatically - } - else - { - victim->flags2 |= MF2_BLASTED; + victim->velz = 8*FRACUNIT; + mo->velz = victim->velz; } } - else // full strength blast from artifact + else { - if (victim->flags & MF_MISSILE) - { -#if 0 - if (victim->IsKindOf (RUNTIME_CLASS(AMageStaffFX2))) - { // Reflect to originator - victim->special1 = (int)victim->target; - victim->target = Owner; - } -#endif - } - victim->velx = FixedMul (BLAST_SPEED, finecosine[angle]); - victim->vely = FixedMul (BLAST_SPEED, finesine[angle]); - - // Spawn blast puff - ang = R_PointToAngle2 (victim->x, victim->y, Owner->x, Owner->y); - ang >>= ANGLETOFINESHIFT; - x = victim->x + FixedMul (victim->radius+FRACUNIT, finecosine[ang]); - y = victim->y + FixedMul (victim->radius+FRACUNIT, finesine[ang]); - z = victim->z - victim->floorclip + (victim->height>>1); - mo = Spawn ("BlastEffect", x, y, z, ALLOW_REPLACE); - if (mo) - { - mo->velx = victim->velx; - mo->vely = victim->vely; - } - - if (victim->flags & MF_MISSILE) - { - // [RH] Floor and ceiling huggers should not be blasted vertically. - if (!(victim->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER))) - { - victim->velz = 8*FRACUNIT; - mo->velz = victim->velz; - } - } - else - { - victim->velz = (1000 / victim->Mass) << FRACBITS; - } - if (victim->player) - { - // Players handled automatically - } - else - { - victim->flags2 |= MF2_BLASTED; - } - if (victim->flags6 & MF6_TOUCHY) - { // Touchy objects die when blasted - victim->flags6 &= ~MF6_ARMED; // Disarm - P_DamageMobj(victim, Owner, Owner, victim->health, NAME_Melee, DMG_FORCED); - } + victim->velz = (1000 / victim->Mass) << FRACBITS; + } + if (victim->player) + { + // Players handled automatically + } + else + { + victim->flags2 |= MF2_BLASTED; + } + if (victim->flags6 & MF6_TOUCHY) + { // Touchy objects die when blasted + victim->flags6 &= ~MF6_ARMED; // Disarm + P_DamageMobj(victim, Owner, Owner, victim->health, NAME_Melee, DMG_FORCED); } } + +enum +{ + BF_USEAMMO = 1, + BF_DONTWARN = 2, + BF_AFFECTBOSSES = 4, +}; + +//========================================================================== +// +// AArtiBlastRadius :: Activate +// +// Blast all actors away +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS (AActor, A_Blast) +{ + PARAM_ACTION_PROLOGUE; + PARAM_INT_OPT (blastflags) { blastflags = 0; } + PARAM_FIXED_OPT (strength) { strength = 255; } + PARAM_FIXED_OPT (radius) { radius = 255; } + PARAM_FIXED_OPT (speed) { speed = 20; } + PARAM_CLASS_OPT (blasteffect, AActor) { blasteffect = PClass::FindClass("BlastEffect"); } + PARAM_SOUND_OPT (blastsound) { blastsound = "BlastRadius"; } + + AActor *mo; + TThinkerIterator iterator; + fixed_t dist; + + if (self->player && (blastflags & BF_USEAMMO)) + { + AWeapon *weapon = self->player->ReadyWeapon; + if (!weapon->DepleteAmmo(weapon->bAltFire)) + { + return 0; + } + } + + S_Sound (self, CHAN_AUTO, blastsound, 1, ATTN_NORM); + + if (!(blastflags & BF_DONTWARN)) + { + P_NoiseAlert (self, self); + } + while ( (mo = iterator.Next ()) ) + { + if ((mo == self) || ((mo->flags2 & MF2_BOSS) && !(blastflags & BF_AFFECTBOSSES)) + || (mo->flags2 & MF2_DORMANT) || (mo->flags3 & MF3_DONTBLAST)) + { // Not a valid monster: originator, boss, dormant, or otherwise protected + continue; + } + if ((mo->flags & MF_ICECORPSE) || (mo->flags3 & MF3_CANBLAST)) + { + // Let these special cases go + } + else if ((mo->flags3 & MF3_ISMONSTER) && (mo->health <= 0)) + { + continue; + } + else if (!(mo->player) && + !(mo->flags & MF_MISSILE) && + !(mo->flags3 & (MF3_ISMONSTER|MF3_CANBLAST)) && + !(mo->flags6 & (MF6_TOUCHY|MF6_VULNERABLE))) + { // Must be monster, player, missile, touchy or vulnerable + continue; + } + dist = P_AproxDistance (self->x - mo->x, self->y - mo->y); + if (dist > radius) + { // Out of range + continue; + } + BlastActor (mo, strength, speed, self, blasteffect); + } + return 0; +} diff --git a/src/g_hexen/a_clericholy.cpp b/src/g_hexen/a_clericholy.cpp index e45ed07b2..9cf1a79ad 100644 --- a/src/g_hexen/a_clericholy.cpp +++ b/src/g_hexen/a_clericholy.cpp @@ -145,36 +145,6 @@ bool AHolySpirit::SpecialBlastHandling (AActor *source, fixed_t strength) return true; } -bool AHolySpirit::IsOkayToAttack (AActor *link) -{ - if ((link->flags3&MF3_ISMONSTER || - (link->player && link != target)) - && !(link->flags2&MF2_DORMANT)) - { - if (target != NULL && link->IsFriend(target)) - { - return false; - } - if (!(link->flags & MF_SHOOTABLE)) - { - return false; - } - if (multiplayer && !deathmatch && link->player && target != NULL && target->player) - { - return false; - } - if (link == target) - { - return false; - } - else if (P_CheckSight (this, link)) - { - return true; - } - } - return false; -} - //============================================================================ // // A_CHolyAttack2 diff --git a/src/g_hexen/a_clericstaff.cpp b/src/g_hexen/a_clericstaff.cpp index 476560e8f..6c6481fb6 100644 --- a/src/g_hexen/a_clericstaff.cpp +++ b/src/g_hexen/a_clericstaff.cpp @@ -48,9 +48,9 @@ DEFINE_ACTION_FUNCTION(AActor, A_CStaffCheck) { PARAM_ACTION_PROLOGUE; - AActor *pmo; + APlayerPawn *pmo; int damage; - int newLife; + int newLife, max; angle_t angle; int slope; int i; @@ -65,6 +65,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CStaffCheck) pmo = player->mo; damage = 20+(pr_staffcheck()&15); + max = pmo->GetMaxHealth(); for (i = 0; i < 3; i++) { angle = pmo->angle+i*(ANG45/16); @@ -78,7 +79,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CStaffCheck) && (!(linetarget->flags2&(MF2_DORMANT+MF2_INVULNERABLE)))) { newLife = player->health+(damage>>3); - newLife = newLife > 100 ? 100 : newLife; + newLife = newLife > max ? max : newLife; if (newLife > player->health) { pmo->health = player->health = newLife; @@ -101,7 +102,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CStaffCheck) if ((linetarget->player && (!linetarget->IsTeammate (pmo) || level.teamdamage != 0)) || linetarget->flags3&MF3_ISMONSTER) { newLife = player->health+(damage>>4); - newLife = newLife > 100 ? 100 : newLife; + newLife = newLife > max ? max : newLife; pmo->health = player->health = newLife; P_SetPsprite (player, ps_weapon, weapon->FindState ("Drain")); } @@ -139,12 +140,12 @@ DEFINE_ACTION_FUNCTION(AActor, A_CStaffAttack) mo = P_SpawnPlayerMissile (self, RUNTIME_CLASS(ACStaffMissile), self->angle-(ANG45/15)); if (mo) { - mo->special2 = 32; + mo->WeaveIndexXY = 32; } mo = P_SpawnPlayerMissile (self, RUNTIME_CLASS(ACStaffMissile), self->angle+(ANG45/15)); if (mo) { - mo->special2 = 0; + mo->WeaveIndexXY = 0; } S_Sound (self, CHAN_WEAPON, "ClericCStaffFire", 1, ATTN_NORM); return 0; @@ -160,19 +161,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CStaffMissileSlither) { PARAM_ACTION_PROLOGUE; - fixed_t newX, newY; - int weaveXY; - int angle; - - weaveXY = self->special2; - angle = (self->angle+ANG90)>>ANGLETOFINESHIFT; - newX = self->x-FixedMul(finecosine[angle], FloatBobOffsets[weaveXY]); - newY = self->y-FixedMul(finesine[angle], FloatBobOffsets[weaveXY]); - weaveXY = (weaveXY+3)&63; - newX += FixedMul(finecosine[angle], FloatBobOffsets[weaveXY]); - newY += FixedMul(finesine[angle], FloatBobOffsets[weaveXY]); - P_TryMove (self, newX, newY, true); - self->special2 = weaveXY; + A_Weave(self, 3, 0, FRACUNIT, 0); return 0; } diff --git a/src/g_hexen/a_flechette.cpp b/src/g_hexen/a_flechette.cpp index 4049c2ee6..7e60ec0a3 100644 --- a/src/g_hexen/a_flechette.cpp +++ b/src/g_hexen/a_flechette.cpp @@ -104,21 +104,38 @@ bool AArtiPoisonBag3::Use (bool pickup) { AActor *mo; - mo = Spawn ("ThrowingBomb", Owner->x, Owner->y, + mo = Spawn("ThrowingBomb", Owner->x, Owner->y, Owner->z-Owner->floorclip+35*FRACUNIT + (Owner->player? Owner->player->crouchoffset : 0), ALLOW_REPLACE); if (mo) { - angle_t pitch = (angle_t)Owner->pitch >> ANGLETOFINESHIFT; + mo->angle = Owner->angle + (((pr_poisonbag()&7) - 4) << 24); + + /* Original flight code from Hexen + * mo->momz = 4*FRACUNIT+((player->lookdir)<<(FRACBITS-4)); + * mo->z += player->lookdir<<(FRACBITS-4); + * P_ThrustMobj(mo, mo->angle, mo->info->speed); + * mo->momx += player->mo->momx>>1; + * mo->momy += player->mo->momy>>1; + */ + + // When looking straight ahead, it uses a z velocity of 4 while the xy velocity + // is as set by the projectile. To accomodate this with a proper trajectory, we + // aim the projectile ~20 degrees higher than we're looking at and increase the + // speed we fire at accordingly. + angle_t orgpitch = angle_t(-Owner->pitch) >> ANGLETOFINESHIFT; + angle_t modpitch = angle_t(0xDC00000 - Owner->pitch) >> ANGLETOFINESHIFT; + angle_t angle = mo->angle >> ANGLETOFINESHIFT; + fixed_t speed = fixed_t(sqrt((double)mo->Speed*mo->Speed + (4.0*65536*4*65536))); + fixed_t xyscale = FixedMul(speed, finecosine[modpitch]); + + mo->velz = FixedMul(speed, finesine[modpitch]); + mo->velx = FixedMul(xyscale, finecosine[angle]) + (Owner->velx >> 1); + mo->vely = FixedMul(xyscale, finesine[angle]) + (Owner->vely >> 1); + mo->z += FixedMul(mo->Speed, finesine[orgpitch]); - mo->angle = Owner->angle+(((pr_poisonbag()&7)-4)<<24); - mo->velz = 4*FRACUNIT + 2*finesine[pitch]; - mo->z += 2*finesine[pitch]; - P_ThrustMobj (mo, mo->angle, mo->Speed); - mo->velx += Owner->velx >> 1; - mo->vely += Owner->vely >> 1; mo->target = Owner; mo->tics -= pr_poisonbag()&3; - P_CheckMissileSpawn (mo); + P_CheckMissileSpawn(mo); return true; } return false; diff --git a/src/g_hexen/a_hexenglobal.h b/src/g_hexen/a_hexenglobal.h index a942998fe..a20827774 100644 --- a/src/g_hexen/a_hexenglobal.h +++ b/src/g_hexen/a_hexenglobal.h @@ -9,7 +9,6 @@ class AHolySpirit : public AActor public: bool Slam (AActor *thing); bool SpecialBlastHandling (AActor *source, fixed_t strength); - bool IsOkayToAttack (AActor *link); }; class AFighterWeapon : public AWeapon diff --git a/src/g_hexen/a_hexenmisc.cpp b/src/g_hexen/a_hexenmisc.cpp index ff6b56202..387e9fcc6 100644 --- a/src/g_hexen/a_hexenmisc.cpp +++ b/src/g_hexen/a_hexenmisc.cpp @@ -44,7 +44,6 @@ #include "a_magecone.cpp" #include "a_magelightning.cpp" #include "a_magestaff.cpp" -#include "a_magewand.cpp" #include "a_pig.cpp" #include "a_serpent.cpp" #include "a_spike.cpp" diff --git a/src/g_hexen/a_hexenspecialdecs.cpp b/src/g_hexen/a_hexenspecialdecs.cpp index 0edb27e25..4f513cfe0 100644 --- a/src/g_hexen/a_hexenspecialdecs.cpp +++ b/src/g_hexen/a_hexenspecialdecs.cpp @@ -383,6 +383,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_BellReset2) self->flags |= MF_SHOOTABLE; self->flags &= ~MF_CORPSE; + self->flags6 &= ~MF6_KILLED; self->health = 5; return 0; } diff --git a/src/g_hexen/a_magelightning.cpp b/src/g_hexen/a_magelightning.cpp index 10c555e4c..e124eb537 100644 --- a/src/g_hexen/a_magelightning.cpp +++ b/src/g_hexen/a_magelightning.cpp @@ -215,8 +215,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_LightningZap) { PARAM_ACTION_PROLOGUE; - const PClass *lightning=PClass::FindClass((ENamedName) self->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_None)); - if (lightning == NULL) lightning = PClass::FindClass("LightningZap"); + const PClass *lightning=PClass::FindClass((ENamedName) self->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_LightningZap)); AActor *mo; fixed_t deltaZ; @@ -339,9 +338,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_ZapMimic) DEFINE_ACTION_FUNCTION(AActor, A_LastZap) { PARAM_ACTION_PROLOGUE; - - const PClass *lightning = PClass::FindClass(ENamedName(self->GetClass()->Meta.GetMetaInt(ACMETA_MissileName, NAME_None))); - if (lightning == NULL) lightning = PClass::FindClass("LightningZap"); + const PClass *lightning=PClass::FindClass((ENamedName) self->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_LightningZap)); AActor *mo; diff --git a/src/g_hexen/a_magestaff.cpp b/src/g_hexen/a_magestaff.cpp index 503d81a17..b672355ec 100644 --- a/src/g_hexen/a_magestaff.cpp +++ b/src/g_hexen/a_magestaff.cpp @@ -88,7 +88,6 @@ class AMageStaffFX2 : public AActor DECLARE_CLASS(AMageStaffFX2, AActor) public: int SpecialMissileHit (AActor *victim); - bool IsOkayToAttack (AActor *link); bool SpecialBlastHandling (AActor *source, fixed_t strength); }; @@ -106,41 +105,6 @@ int AMageStaffFX2::SpecialMissileHit (AActor *victim) return -1; } -bool AMageStaffFX2::IsOkayToAttack (AActor *link) -{ - if (((link->flags3 & MF3_ISMONSTER) || link->player) && !(link->flags2 & MF2_DORMANT)) - { - if (!(link->flags & MF_SHOOTABLE)) - { - return false; - } - if (multiplayer && !deathmatch && link->player && target->player) - { - return false; - } - if (link == target) - { - return false; - } - if (target != NULL && target->IsFriend(link)) - { - return false; - } - if (P_CheckSight (this, link)) - { - AActor *master = target; - angle_t angle = R_PointToAngle2 (master->x, master->y, - link->x, link->y) - master->angle; - angle >>= 24; - if (angle>226 || angle<30) - { - return true; - } - } - } - return false; -} - bool AMageStaffFX2::SpecialBlastHandling (AActor *source, fixed_t strength) { // Reflect to originator diff --git a/src/g_hexen/a_magewand.cpp b/src/g_hexen/a_magewand.cpp deleted file mode 100644 index de29bf7d8..000000000 --- a/src/g_hexen/a_magewand.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* -#include "actor.h" -#include "gi.h" -#include "m_random.h" -#include "s_sound.h" -#include "d_player.h" -#include "a_action.h" -#include "p_local.h" -#include "a_action.h" -#include "p_pspr.h" -#include "gstrings.h" -#include "a_hexenglobal.h" -#include "thingdef/thingdef.h" -*/ - -static FRandom pr_smoke ("MWandSmoke"); - -void A_MWandAttack (AActor *actor); - -// Wand Missile ------------------------------------------------------------- - -class AMageWandMissile : public AFastProjectile -{ - DECLARE_CLASS(AMageWandMissile, AFastProjectile) -public: - void Effect (); -}; - -IMPLEMENT_CLASS (AMageWandMissile) - -void AMageWandMissile::Effect () -{ - fixed_t hitz; - - //if (pr_smoke() < 128) // [RH] I think it looks better if it's consistent - { - hitz = z-8*FRACUNIT; - if (hitz < floorz) - { - hitz = floorz; - } - Spawn ("MageWandSmoke", x, y, hitz, ALLOW_REPLACE); - } -} - -//============================================================================ -// -// A_MWandAttack -// -//============================================================================ - -DEFINE_ACTION_FUNCTION(AActor, A_MWandAttack) -{ - PARAM_ACTION_PROLOGUE; - - AActor *mo; - - mo = P_SpawnPlayerMissile (self, RUNTIME_CLASS(AMageWandMissile)); - S_Sound (self, CHAN_WEAPON, "MageWandFire", 1, ATTN_NORM); - return 0; -} diff --git a/src/g_level.cpp b/src/g_level.cpp index bb0b9b48c..1b0bd1130 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -84,6 +84,18 @@ #include "g_hub.h" +#ifndef STAT +#define STAT_NEW(map) +#define STAT_END(newl) +#define STAT_READ(png) +#define STAT_WRITE(f) +#else +void STAT_NEW(const char *lev); +void STAT_END(const char *newl); +void STAT_READ(PNGHandle *png); +void STAT_WRITE(FILE *f); +#endif + EXTERN_CVAR (Float, sv_gravity) EXTERN_CVAR (Float, sv_aircontrol) EXTERN_CVAR (Int, disableautosave) @@ -168,7 +180,7 @@ static void SetEndSequence (char *nextmap, int type) newseq.EndType = type; seqnum = (int)EndSequences.Push (newseq); } - mysnprintf(nextmap, sizeof(nextmap), "enDSeQ%04x", (WORD)seqnum); + mysnprintf(nextmap, 11, "enDSeQ%04x", (WORD)seqnum); } //========================================================================== @@ -495,6 +507,8 @@ void G_InitNew (const char *mapname, bool bTitleLevel) // force players to be initialized upon first level load for (i = 0; i < MAXPLAYERS; i++) players[i].playerstate = PST_ENTER; // [BC] + + STAT_NEW(mapname); } usergame = !bTitleLevel; // will be set false if a demo @@ -556,18 +570,24 @@ void G_ChangeLevel(const char *levelname, int position, bool keepFacing, int nex return; } - nextlevel = levelname; - - if (strncmp(levelname, "enDSeQ", 6)) + if (strncmp(levelname, "enDSeQ", 6) != 0) { - nextinfo = FindLevelInfo (nextlevel)->CheckLevelRedirect (); - if (nextinfo) + nextinfo = FindLevelInfo (nextlevel); + if (nextinfo != NULL) { - nextlevel = nextinfo->mapname; + level_info_t *nextredir = nextinfo->CheckLevelRedirect(); + if (nextredir != NULL) + { + nextinfo = nextredir; + levelname = nextinfo->mapname; + } } } - if (nextSkill != -1) NextSkill = nextSkill; + nextlevel = levelname; + + if (nextSkill != -1) + NextSkill = nextSkill; g_nomonsters = nomonsters; @@ -596,6 +616,8 @@ void G_ChangeLevel(const char *levelname, int position, bool keepFacing, int nex FBehavior::StaticStartTypedScripts (SCRIPT_Unloading, NULL, false, 0, true); unloading = false; + STAT_END(nextlevel); + if (thiscluster && (thiscluster->flags & CLUSTER_HUB)) { if ((level.flags & LEVEL_NOINTERMISSION) || (nextcluster == thiscluster)) @@ -1389,6 +1411,8 @@ void G_AirControlChanged () void G_SerializeLevel (FArchive &arc, bool hubLoad) { int i = level.totaltime; + + screen->StartSerialize(arc); arc << level.flags << level.flags2 @@ -1507,6 +1531,7 @@ void G_SerializeLevel (FArchive &arc, bool hubLoad) } } } + screen->EndSerialize(arc); } //========================================================================== @@ -1627,6 +1652,7 @@ void G_WriteSnapshots (FILE *file) { unsigned int i; + STAT_WRITE(file); for (i = 0; i < wadlevelinfos.Size(); i++) { if (wadlevelinfos[i].snapshot) @@ -1777,6 +1803,7 @@ void G_ReadSnapshots (PNGHandle *png) arc << pnum; } } + STAT_READ(png); png->File->ResetFilePtr(); } diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index bb3449e0a..f85b08960 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -85,7 +85,9 @@ level_info_t *FindLevelInfo (const char *mapname) int i; if ((i = FindWadLevelInfo (mapname)) > -1) + { return &wadlevelinfos[i]; + } else { if (TheDefaultLevelInfo.LevelName.IsEmpty()) @@ -1359,6 +1361,7 @@ MapFlagHandlers[] = { "compat_mbfmonstermove", MITYPE_COMPATFLAG, COMPATF_MBFMONSTERMOVE}, { "compat_corpsegibs", MITYPE_COMPATFLAG, COMPATF_CORPSEGIBS}, { "compat_noblockfriends", MITYPE_COMPATFLAG, COMPATF_NOBLOCKFRIENDS}, + { "compat_spritesort", MITYPE_COMPATFLAG, COMPATF_SPRITESORT}, { "cd_start_track", MITYPE_EATNEXT, 0, 0 }, { "cd_end1_track", MITYPE_EATNEXT, 0, 0 }, { "cd_end2_track", MITYPE_EATNEXT, 0, 0 }, @@ -1461,6 +1464,10 @@ void FMapInfoParser::ParseMapDefinition(level_info_t &info) { if (sc.Compare(((FMapOptInfo *)(*probe))->name)) { + if (!((FMapOptInfo *)(*probe))->old && format_type != FMT_New) + { + sc.ScriptError("MAPINFO option '%s' requires the new MAPINFO format", sc.String); + } ((FMapOptInfo *)(*probe))->handler(*this, &info); success = true; break; diff --git a/src/g_raven/a_minotaur.cpp b/src/g_raven/a_minotaur.cpp index 1057a7c9b..078a065f4 100644 --- a/src/g_raven/a_minotaur.cpp +++ b/src/g_raven/a_minotaur.cpp @@ -82,49 +82,6 @@ void AMinotaurFriend::Serialize (FArchive &arc) arc << StartTime; } -bool AMinotaurFriend::IsOkayToAttack (AActor *link) -{ - if ((link->flags3 & MF3_ISMONSTER) && (link != tracer)) - { - if (!((link->flags ^ flags) & MF_FRIENDLY)) - { // Don't attack friends - if (link->flags & MF_FRIENDLY) - { - if (!deathmatch || link->FriendPlayer == 0 || FriendPlayer == 0 || - link->FriendPlayer != FriendPlayer) - { - return false; - } - } - else - { - return false; - } - } - if (!(link->flags&MF_SHOOTABLE)) - { - return false; - } - if (link->flags2&MF2_DORMANT) - { - return false; - } - if ((link->flags5 & MF5_SUMMONEDMONSTER) && (link->tracer == tracer)) - { - return false; - } - if (multiplayer && !deathmatch && link->player) - { - return false; - } - if (P_CheckSight (this, link)) - { - return true; - } - } - return false; -} - void AMinotaurFriend::Die (AActor *source, AActor *inflictor) { Super::Die (source, inflictor); diff --git a/src/g_raven/ravenshared.h b/src/g_raven/ravenshared.h index 7afd75947..fb9edf279 100644 --- a/src/g_raven/ravenshared.h +++ b/src/g_raven/ravenshared.h @@ -20,7 +20,6 @@ class AMinotaurFriend : public AMinotaur public: int StartTime; - bool IsOkayToAttack (AActor *target); void Die (AActor *source, AActor *inflictor); bool OkayToSwitchTarget (AActor *other); void BeginPlay (); diff --git a/src/g_shared/a_action.cpp b/src/g_shared/a_action.cpp index 494ea11f0..4c007ad17 100644 --- a/src/g_shared/a_action.cpp +++ b/src/g_shared/a_action.cpp @@ -183,6 +183,12 @@ DEFINE_ACTION_FUNCTION(AActor, A_FreezeDeath) self->flags2 |= MF2_PUSHABLE|MF2_TELESTOMP|MF2_PASSMOBJ|MF2_SLIDE; self->flags3 |= MF3_CRASHED; self->height = self->GetDefault()->height; + // Remove fuzz effects from frozen actors. + if (self->RenderStyle.BlendOp >= STYLEOP_Fuzz && self->RenderStyle.BlendOp <= STYLEOP_FuzzOrRevSub) + { + self->RenderStyle = STYLE_Normal; + } + S_Sound (self, CHAN_BODY, "misc/freeze", 1, ATTN_NORM); // [RH] Andy Baker's stealth monsters @@ -198,7 +204,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FreezeDeath) self->player->poisoncount = 0; self->player->bonuscount = 0; } - else if (self->flags3&MF3_ISMONSTER && self->special) + else if (self->flags3 & MF3_ISMONSTER && self->special) { // Initiate monster death actions LineSpecials [self->special] (NULL, self, false, self->args[0], self->args[1], self->args[2], self->args[3], self->args[4]); @@ -261,18 +267,19 @@ DEFINE_ACTION_FUNCTION(AActor, A_FreezeDeathChunks) int numChunks; AActor *mo; - if (self->velx || self->vely || self->velz) + if ((self->velx || self->vely || self->velz) && !(self->flags6 & MF6_SHATTERING)) { self->tics = 3*TICRATE; return 0; } + self->velx = self->vely = self->velz = 0; S_Sound (self, CHAN_BODY, "misc/icebreak", 1, ATTN_NORM); // [RH] In Hexen, this creates a random number of shards (range [24,56]) // with no relation to the size of the self shattering. I think it should // base the number of shards on the size of the dead thing, so bigger // things break up into more shards than smaller things. - // An self with radius 20 and height 64 creates ~40 chunks. + // An actor with radius 20 and height 64 creates ~40 chunks. numChunks = MAX (4, (self->radius>>FRACBITS)*(self->height>>FRACBITS)/32); i = (pr_freeze.Random2()) % (numChunks/4); for (i = MAX (24, numChunks + i); i >= 0; i--) diff --git a/src/g_shared/a_decals.cpp b/src/g_shared/a_decals.cpp index e1184c40c..d992bec9e 100644 --- a/src/g_shared/a_decals.cpp +++ b/src/g_shared/a_decals.cpp @@ -298,6 +298,13 @@ FTextureID DBaseDecal::StickToWall (side_t *wall, fixed_t x, fixed_t y, F3DFloor else return FNullTextureID(); CalcFracPos (wall, x, y); + FTexture *texture = TexMan[tex]; + + if (texture == NULL || texture->bNoDecals) + { + return FNullTextureID(); + } + return tex; } @@ -412,7 +419,7 @@ static fixed_t Length (fixed_t dx, fixed_t dy) static side_t *NextWall (const side_t *wall) { - line_t *line = wall->linedef;; + line_t *line = wall->linedef; if (line->sidedef[0] == wall) { @@ -546,11 +553,18 @@ DBaseDecal *DBaseDecal::CloneSelf (const FDecalTemplate *tpl, fixed_t ix, fixed_ DBaseDecal *decal = new DBaseDecal(iz); if (decal != NULL) { - decal->StickToWall (wall, ix, iy, ffloor); - tpl->ApplyToDecal (decal, wall); - decal->AlphaColor = AlphaColor; - decal->RenderFlags = (decal->RenderFlags & RF_DECALMASK) | - (this->RenderFlags & ~RF_DECALMASK); + if (decal->StickToWall (wall, ix, iy, ffloor).isValid()) + { + tpl->ApplyToDecal (decal, wall); + decal->AlphaColor = AlphaColor; + decal->RenderFlags = (decal->RenderFlags & RF_DECALMASK) | + (this->RenderFlags & ~RF_DECALMASK); + } + else + { + decal->Destroy(); + return NULL; + } } return decal; } @@ -641,26 +655,23 @@ DImpactDecal *DImpactDecal::StaticCreate (const FDecalTemplate *tpl, fixed_t x, { if (tpl->LowerDecal) { - int lowercolor = color; + int lowercolor; const FDecalTemplate * tpl_low = tpl->LowerDecal->GetDecal(); // If the default color of the lower decal is the same as the main decal's // apply the custom color as well. - if (tpl->ShadeColor == tpl_low->ShadeColor) lowercolor=0; + if (tpl->ShadeColor != tpl_low->ShadeColor) lowercolor=0; + else lowercolor = color; StaticCreate (tpl_low, x, y, z, wall, ffloor, lowercolor); } DImpactDecal::CheckMax(); decal = new DImpactDecal (z); - - FTextureID stickypic = decal->StickToWall (wall, x, y, ffloor); - FTexture *tex = TexMan[stickypic]; - - if (tex != NULL && tex->bNoDecals) + if (decal == NULL) { return NULL; } - if (decal == NULL) + if (!decal->StickToWall (wall, x, y, ffloor).isValid()) { return NULL; } @@ -684,15 +695,27 @@ DImpactDecal *DImpactDecal::StaticCreate (const FDecalTemplate *tpl, fixed_t x, DBaseDecal *DImpactDecal::CloneSelf (const FDecalTemplate *tpl, fixed_t ix, fixed_t iy, fixed_t iz, side_t *wall, F3DFloor * ffloor) const { + if (wall->Flags & WALLF_NOAUTODECALS) + { + return NULL; + } + DImpactDecal::CheckMax(); DImpactDecal *decal = new DImpactDecal(iz); if (decal != NULL) { - decal->StickToWall (wall, ix, iy, ffloor); - tpl->ApplyToDecal (decal, wall); - decal->AlphaColor = AlphaColor; - decal->RenderFlags = (decal->RenderFlags & RF_DECALMASK) | - (this->RenderFlags & ~RF_DECALMASK); + if (decal->StickToWall (wall, ix, iy, ffloor).isValid()) + { + tpl->ApplyToDecal (decal, wall); + decal->AlphaColor = AlphaColor; + decal->RenderFlags = (decal->RenderFlags & RF_DECALMASK) | + (this->RenderFlags & ~RF_DECALMASK); + } + else + { + decal->Destroy(); + return NULL; + } } return decal; } diff --git a/src/g_shared/a_fastprojectile.cpp b/src/g_shared/a_fastprojectile.cpp index 0127ed0d7..3bd2903ee 100644 --- a/src/g_shared/a_fastprojectile.cpp +++ b/src/g_shared/a_fastprojectile.cpp @@ -2,6 +2,8 @@ #include "a_sharedglobal.h" #include "p_local.h" #include "g_level.h" +#include "r_sky.h" +#include "p_lnspec.h" IMPLEMENT_CLASS(AFastProjectile) @@ -26,6 +28,7 @@ void AFastProjectile::Tick () PrevX = x; PrevY = y; PrevZ = z; + PrevAngle = angle; if (!(flags5 & MF5_NOTIMEFREEZE)) { @@ -71,6 +74,26 @@ void AFastProjectile::Tick () if (!P_TryMove (this, x + xfrac,y + yfrac, true, false, tm)) { // Blocked move + if (!(flags3 & MF3_SKYEXPLODE)) + { + if (tm.ceilingline && + tm.ceilingline->backsector && + tm.ceilingline->backsector->GetTexture(sector_t::ceiling) == skyflatnum && + z >= tm.ceilingline->backsector->ceilingplane.ZatPoint (x, y)) + { + // Hack to prevent missiles exploding against the sky. + // Does not handle sky floors. + Destroy (); + return; + } + // [RH] Don't explode on horizon lines. + if (BlockingLine != NULL && BlockingLine->special == Line_Horizon) + { + Destroy (); + return; + } + } + P_ExplodeMissile (this, BlockingLine, BlockingMobj); return; } @@ -78,6 +101,15 @@ void AFastProjectile::Tick () z += zfrac; if (z <= floorz) { // Hit the floor + + if (floorpic == skyflatnum && !(flags3 & MF3_SKYEXPLODE)) + { + // [RH] Just remove the missile without exploding it + // if this is a sky floor. + Destroy (); + return; + } + z = floorz; P_HitFloor (this); P_ExplodeMissile (this, NULL, NULL); @@ -85,6 +117,13 @@ void AFastProjectile::Tick () } if (z + height > ceilingz) { // Hit the ceiling + + if (ceilingpic == skyflatnum && !(flags3 & MF3_SKYEXPLODE)) + { + Destroy (); + return; + } + z = ceilingz - height; P_ExplodeMissile (this, NULL, NULL); return; @@ -99,7 +138,7 @@ void AFastProjectile::Tick () // Advance the state if (tics != -1) { - tics--; + if (tics > 0) tics--; while (!tics) { if (!SetState (state->GetNextState ())) @@ -113,5 +152,27 @@ void AFastProjectile::Tick () void AFastProjectile::Effect() { + //if (pr_smoke() < 128) // [RH] I think it looks better if it's consistent + { + FName name = (ENamedName) this->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_None); + if (name != NAME_None) + { + fixed_t hitz = z-8*FRACUNIT; + if (hitz < floorz) + { + hitz = floorz; + } + + const PClass *trail = PClass::FindClass(name); + if (trail != NULL) + { + AActor *act = Spawn (trail, x, y, hitz, ALLOW_REPLACE); + if (act != NULL) + { + act->angle = this->angle; + } + } + } + } } diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index f12c36aef..a807ad70b 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -177,7 +177,7 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, { AWeapon *beastweap; APlayerPawn *mo; - AActor *pmo; + APlayerPawn *pmo; angle_t angle; pmo = player->mo; @@ -192,7 +192,7 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, return false; } - bool DeliberateUnmorphIsOkay = !!(player->MorphStyle & unmorphflag); + bool DeliberateUnmorphIsOkay = !!(MORPH_STANDARDUNDOING & unmorphflag); if ((pmo->flags2 & MF2_INVULNERABLE) // If the player is invulnerable && ((player != activator) // and either did not decide to unmorph, @@ -314,7 +314,10 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, } angle = mo->angle >> ANGLETOFINESHIFT; - Spawn(exit_flash, pmo->x + 20*finecosine[angle], pmo->y + 20*finesine[angle], pmo->z + TELEFOGHEIGHT, ALLOW_REPLACE); + if (exit_flash != NULL) + { + Spawn(exit_flash, pmo->x + 20*finecosine[angle], pmo->y + 20*finesine[angle], pmo->z + TELEFOGHEIGHT, ALLOW_REPLACE); + } mo->SetupWeaponSlots(); // Use original class's weapon slots. beastweap = player->ReadyWeapon; if (player->PremorphWeapon != NULL) @@ -327,7 +330,7 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, } if (correctweapon) { // Better "lose morphed weapon" semantics - const PClass *morphweapon = PClass::FindClass (mo->MorphWeapon); + const PClass *morphweapon = PClass::FindClass (pmo->MorphWeapon); if (morphweapon != NULL && morphweapon->IsDescendantOf (RUNTIME_CLASS(AWeapon))) { AWeapon *OriginalMorphWeapon = static_cast(mo->FindInventory (morphweapon)); diff --git a/src/g_shared/a_morph.h b/src/g_shared/a_morph.h index cfaec9ae3..891fe619b 100644 --- a/src/g_shared/a_morph.h +++ b/src/g_shared/a_morph.h @@ -23,6 +23,9 @@ enum MORPH_UNDOBYDEATH = 0x00000200, // Actor unmorphs when killed and (unless MORPH_UNDOBYDEATHSAVES) stays dead MORPH_UNDOBYDEATHFORCED = 0x00000400, // Actor (if unmorphed when killed) forces unmorph (not very useful with UNDOBYDEATHSAVES) MORPH_UNDOBYDEATHSAVES = 0x00000800, // Actor (if unmorphed when killed) regains their health and doesn't die + MORPH_UNDOBYTIMEOUT = 0x00001000, // Player unmorphs once countdown expires + + MORPH_STANDARDUNDOING = MORPH_UNDOBYTOMEOFPOWER | MORPH_UNDOBYCHAOSDEVICE | MORPH_UNDOBYTIMEOUT, }; class PClass; diff --git a/src/g_shared/a_movingcamera.cpp b/src/g_shared/a_movingcamera.cpp index 0e704c95a..de67c51d9 100644 --- a/src/g_shared/a_movingcamera.cpp +++ b/src/g_shared/a_movingcamera.cpp @@ -507,6 +507,7 @@ class AActorMover : public APathFollower { DECLARE_CLASS (AActorMover, APathFollower) public: + void BeginPlay(); void PostBeginPlay (); void Activate (AActor *activator); void Deactivate (AActor *activator); @@ -516,6 +517,11 @@ protected: IMPLEMENT_CLASS (AActorMover) +void AActorMover::BeginPlay() +{ + ChangeStatNum(STAT_ACTORMOVER); +} + void AActorMover::PostBeginPlay () { Super::PostBeginPlay (); @@ -585,6 +591,7 @@ void AActorMover::Activate (AActor *activator) tracer->PrevX = tracer->x; tracer->PrevY = tracer->y; tracer->PrevZ = tracer->z; + tracer->PrevAngle = tracer->angle; } void AActorMover::Deactivate (AActor *activator) diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index ad5505be2..640570016 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -1202,7 +1202,7 @@ bool AInventory::TryPickup (AActor *&toucher) ItemFlags &= ~IF_PICKUPGOOD; GoAwayAndDie (); } - else if (MaxAmount == 0) + else if (MaxAmount == 0 && !IsKindOf(RUNTIME_CLASS(AAmmo))) { // Special case: If an item's MaxAmount is 0, you can still pick it // up if it is autoactivate-able. @@ -1581,7 +1581,7 @@ void ABackpackItem::Serialize (FArchive &arc) AInventory *ABackpackItem::CreateCopy (AActor *other) { // Find every unique type of ammo. Give it to the player if - // he doesn't have it already, and double it's maximum capacity. + // he doesn't have it already, and double its maximum capacity. for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i) { const PClass *type = PClass::m_Types[i]; @@ -1600,7 +1600,14 @@ AInventory *ABackpackItem::CreateCopy (AActor *other) { // The player did not have the ammo. Add it. ammo = static_cast(Spawn (type, 0, 0, 0, NO_REPLACE)); ammo->Amount = bDepleted ? 0 : amount; - if (ammo->BackpackMaxAmount > ammo->MaxAmount) ammo->MaxAmount = ammo->BackpackMaxAmount; + if (ammo->BackpackMaxAmount > ammo->MaxAmount) + { + ammo->MaxAmount = ammo->BackpackMaxAmount; + } + if (ammo->Amount > ammo->MaxAmount) + { + ammo->Amount = ammo->MaxAmount; + } ammo->AttachToOwner (other); } else @@ -1643,7 +1650,7 @@ bool ABackpackItem::HandlePickup (AInventory *item) { if (probe->GetClass()->ParentClass == RUNTIME_CLASS(AAmmo)) { - if (probe->Amount < probe->MaxAmount) + if (probe->Amount < probe->MaxAmount || sv_unlimited_pickup) { int amount = static_cast(probe->GetDefault())->BackpackAmount; // extra ammo in baby mode and nightmare mode @@ -1652,7 +1659,7 @@ bool ABackpackItem::HandlePickup (AInventory *item) amount = FixedMul(amount, G_SkillProperty(SKILLP_AmmoFactor)); } probe->Amount += amount; - if (probe->Amount > probe->MaxAmount) + if (probe->Amount > probe->MaxAmount && !sv_unlimited_pickup) { probe->Amount = probe->MaxAmount; } diff --git a/src/g_shared/a_quake.cpp b/src/g_shared/a_quake.cpp index 58c9b83d6..c53605333 100644 --- a/src/g_shared/a_quake.cpp +++ b/src/g_shared/a_quake.cpp @@ -33,14 +33,14 @@ DEarthquake::DEarthquake() //========================================================================== DEarthquake::DEarthquake (AActor *center, int intensity, int duration, - int damrad, int tremrad) + int damrad, int tremrad, FSoundID quakesound) : DThinker(STAT_EARTHQUAKE) { - m_QuakeSFX = "world/quake"; + m_QuakeSFX = quakesound; m_Spot = center; // Radii are specified in tile units (64 pixels) - m_DamageRadius = damrad << (FRACBITS+6); - m_TremorRadius = tremrad << (FRACBITS+6); + m_DamageRadius = damrad << (FRACBITS); + m_TremorRadius = tremrad << (FRACBITS); m_Intensity = intensity; m_Countdown = duration; } @@ -56,7 +56,15 @@ void DEarthquake::Serialize (FArchive &arc) Super::Serialize (arc); arc << m_Spot << m_Intensity << m_Countdown << m_TremorRadius << m_DamageRadius; - m_QuakeSFX = "world/quake"; + + if (SaveVersion >= 1912) + { + arc << m_QuakeSFX; + } + else + { + m_QuakeSFX = "world/quake"; + } } //========================================================================== @@ -158,7 +166,7 @@ int DEarthquake::StaticGetQuakeIntensity (AActor *victim) // //========================================================================== -bool P_StartQuake (AActor *activator, int tid, int intensity, int duration, int damrad, int tremrad) +bool P_StartQuake (AActor *activator, int tid, int intensity, int duration, int damrad, int tremrad, FSoundID quakesfx) { AActor *center; bool res = false; @@ -169,7 +177,7 @@ bool P_StartQuake (AActor *activator, int tid, int intensity, int duration, int { if (activator != NULL) { - new DEarthquake(activator, intensity, duration, damrad, tremrad); + new DEarthquake(activator, intensity, duration, damrad, tremrad, quakesfx); return true; } } @@ -179,7 +187,7 @@ bool P_StartQuake (AActor *activator, int tid, int intensity, int duration, int while ( (center = iterator.Next ()) ) { res = true; - new DEarthquake (center, intensity, duration, damrad, tremrad); + new DEarthquake (center, intensity, duration, damrad, tremrad, quakesfx); } } diff --git a/src/g_shared/a_randomspawner.cpp b/src/g_shared/a_randomspawner.cpp index 1d8ceaa54..0df69228a 100644 --- a/src/g_shared/a_randomspawner.cpp +++ b/src/g_shared/a_randomspawner.cpp @@ -67,20 +67,29 @@ class ARandomSpawner : public AActor else if (pr_randomspawn() <= di->probability) // prob 255 = always spawn, prob 0 = never spawn. { // Handle replacement here so as to get the proper speed and flags for missiles - const PClass * cls; PClass * rep; + const PClass *cls; cls = PClass::FindClass(di->Name); - if (cls) rep = cls->ActorInfo->GetReplacement()->Class; - if (rep) cls = rep; - if (cls) + if (cls != NULL) + { + const PClass *rep = cls->ActorInfo->GetReplacement()->Class; + if (rep != NULL) + { + cls = rep; + } + } + if (cls != NULL) { Species = cls->TypeName; - AActor * defmobj = GetDefaultByType(cls); + AActor *defmobj = GetDefaultByType(cls); this->Speed = defmobj->Speed; this->flags |= (defmobj->flags & MF_MISSILE); this->flags2 |= (defmobj->flags2 & MF2_SEEKERMISSILE); this->flags4 |= (defmobj->flags4 & MF4_SPECTRAL); } - else Species = NAME_None; + else + { + Species = NAME_None; + } } } } @@ -152,7 +161,7 @@ class ARandomSpawner : public AActor boss = true; // If a replaced actor has either of those same flags, it's also a boss. AActor * rep = GetDefaultByType(GetClass()->ActorInfo->GetReplacee()->Class); - if (rep && (rep->flags4 & MF4_BOSSDEATH) || (rep->flags2 & MF2_BOSS)) + if (rep && ((rep->flags4 & MF4_BOSSDEATH) || (rep->flags2 & MF2_BOSS))) boss = true; } if (boss) this->tracer = newmobj; diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index 11752234e..c152bf44d 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -89,16 +89,24 @@ public: class ASkyViewpoint : public AActor { DECLARE_CLASS (ASkyViewpoint, AActor) + HAS_OBJECT_POINTERS public: void Serialize (FArchive &arc); void BeginPlay (); void Destroy (); bool bInSkybox; bool bAlways; - ASkyViewpoint *Mate; + TObjPtr Mate; fixed_t PlaneAlpha; }; +class AStackPoint : public ASkyViewpoint +{ + DECLARE_CLASS (AStackPoint, ASkyViewpoint) +public: + void BeginPlay (); +}; + class DFlashFader : public DThinker { DECLARE_CLASS (DFlashFader, DThinker) @@ -128,7 +136,7 @@ class DEarthquake : public DThinker DECLARE_CLASS (DEarthquake, DThinker) HAS_OBJECT_POINTERS public: - DEarthquake (AActor *center, int intensity, int duration, int damrad, int tremrad); + DEarthquake (AActor *center, int intensity, int duration, int damrad, int tremrad, FSoundID quakesfx); void Serialize (FArchive &arc); void Tick (); diff --git a/src/g_shared/a_skies.cpp b/src/g_shared/a_skies.cpp index 77704dd50..0454498e2 100644 --- a/src/g_shared/a_skies.cpp +++ b/src/g_shared/a_skies.cpp @@ -35,10 +35,13 @@ #include "actor.h" #include "a_sharedglobal.h" #include "p_local.h" +#include "p_lnspec.h" // arg0 = Visibility*4 for this skybox -IMPLEMENT_CLASS (ASkyViewpoint) +IMPLEMENT_POINTY_CLASS (ASkyViewpoint) + DECLARE_POINTER(Mate) +END_POINTERS // If this actor has no TID, make it the default sky box void ASkyViewpoint::BeginPlay () @@ -86,6 +89,64 @@ void ASkyViewpoint::Destroy () Super::Destroy(); } +// For an RR compatible linedef based definition. This searches the viewpoint's sector +// for a skybox line special, gets its tag and transfers the skybox to all tagged sectors. +class ASkyCamCompat : public ASkyViewpoint +{ + DECLARE_CLASS (ASkyCamCompat, ASkyViewpoint) +public: + void BeginPlay (); +}; + +IMPLEMENT_CLASS (ASkyCamCompat) + +extern FTextureID skyflatnum; + +void ASkyCamCompat::BeginPlay () +{ + if (Sector == NULL) + { + Printf("Sector not initialized for SkyCamCompat\n"); + Sector = P_PointInSector(x, y); + } + if (Sector) + { + line_t * refline = NULL; + for (short i = 0; i < Sector->linecount; i++) + { + refline = Sector->lines[i]; + if (refline->special == Sector_SetPortal && refline->args[1] == 2) + { + // We found the setup linedef for this skybox, so let's use it for our init. + int skybox_id = refline->args[0]; + + // Then, change the alpha + alpha = refline->args[4]; + + // Finally, skyboxify all tagged sectors + // This involves changing their texture to the sky flat, because while + // EE works with any texture for its skybox portals, ZDoom doesn't. + for (int secnum =-1; (secnum = P_FindSectorFromTag (skybox_id, secnum)) != -1; ) + { + // plane: 0=floor, 1=ceiling, 2=both + if (refline->args[2] == 1 || refline->args[2] == 2) + { + sectors[secnum].CeilingSkyBox = this; + sectors[secnum].SetTexture(sector_t::ceiling, skyflatnum, false); + } + if (refline->args[2] == 0 || refline->args[2] == 2) + { + sectors[secnum].FloorSkyBox = this; + sectors[secnum].SetTexture(sector_t::floor, skyflatnum, false); + } + } + } + } + } + // Do not call the SkyViewpoint's super method because it would trash our setup + AActor::BeginPlay(); +} + //--------------------------------------------------------------------------- // arg0 = tid of matching SkyViewpoint @@ -144,13 +205,6 @@ void ASkyPicker::PostBeginPlay () // arg0 = opacity of plane; 0 = invisible, 255 = fully opaque -class AStackPoint : public ASkyViewpoint -{ - DECLARE_CLASS (AStackPoint, ASkyViewpoint) -public: - void BeginPlay (); -}; - IMPLEMENT_CLASS (AStackPoint) void AStackPoint::BeginPlay () @@ -161,50 +215,6 @@ void AStackPoint::BeginPlay () bAlways = true; } -//--------------------------------------------------------------------------- -// Upper stacks go in the top sector. Lower stacks go in the bottom sector. - -class AUpperStackLookOnly : public AStackPoint -{ - DECLARE_CLASS (AUpperStackLookOnly, AStackPoint) -public: - void PostBeginPlay (); -}; - -class ALowerStackLookOnly : public AStackPoint -{ - DECLARE_CLASS (ALowerStackLookOnly, AStackPoint) -public: - void PostBeginPlay (); -}; - -IMPLEMENT_CLASS (AUpperStackLookOnly) -IMPLEMENT_CLASS (ALowerStackLookOnly) - -void AUpperStackLookOnly::PostBeginPlay () -{ - Super::PostBeginPlay (); - TActorIterator it (tid); - Sector->FloorSkyBox = it.Next(); - if (Sector->FloorSkyBox != NULL) - { - Sector->FloorSkyBox->Mate = this; - Sector->FloorSkyBox->PlaneAlpha = Scale (args[0], OPAQUE, 255); - } -} - -void ALowerStackLookOnly::PostBeginPlay () -{ - Super::PostBeginPlay (); - TActorIterator it (tid); - Sector->CeilingSkyBox = it.Next(); - if (Sector->CeilingSkyBox != NULL) - { - Sector->CeilingSkyBox->Mate = this; - Sector->CeilingSkyBox->PlaneAlpha = Scale (args[0], OPAQUE, 255); - } -} - //--------------------------------------------------------------------------- class ASectorSilencer : public AActor diff --git a/src/g_shared/sbar.h b/src/g_shared/sbar.h index 162947bf4..d3e72c79e 100644 --- a/src/g_shared/sbar.h +++ b/src/g_shared/sbar.h @@ -32,6 +32,9 @@ ** */ +#ifndef __SBAR_H__ +#define __SBAR_H__ + #include "dobject.h" #include "v_collection.h" #include "v_text.h" @@ -195,22 +198,37 @@ private: }; class player_t; -struct FMugShot +class FMugShot { - FMugShot(); - void Tick(player_t *player); - bool SetState(const char *state_name, bool wait_till_done=false, bool reset=false); - int UpdateState(player_t *player, int stateflags=0); - FTexture *GetFace(player_t *player, const char *default_face, int accuracy, int stateflags=0); + public: + enum StateFlags + { + STANDARD = 0x0, - FMugShotState *CurrentState; - int RampageTimer; - int LastDamageAngle; - int FaceHealth; - bool bEvilGrin; - bool bDamageFaceActive; - bool bNormal; - bool bOuchActive; + XDEATHFACE = 0x1, + ANIMATEDGODMODE = 0x2, + DISABLEGRIN = 0x4, + DISABLEOUCH = 0x8, + DISABLEPAIN = 0x10, + DISABLERAMPAGE = 0x20, + }; + + FMugShot(); + void Grin(bool grin=true) { bEvilGrin = grin; } + void Tick(player_t *player); + bool SetState(const char *state_name, bool wait_till_done=false, bool reset=false); + int UpdateState(player_t *player, StateFlags stateflags=STANDARD); + FTexture *GetFace(player_t *player, const char *default_face, int accuracy, StateFlags stateflags=STANDARD); + + private: + FMugShotState *CurrentState; + int RampageTimer; + int LastDamageAngle; + int FaceHealth; + bool bEvilGrin; + bool bDamageFaceActive; + bool bNormal; + bool bOuchActive; }; extern TArray MugShotStates; @@ -369,3 +387,5 @@ DBaseStatusBar *CreateCustomStatusBar(int script=0); void ST_LoadCrosshair(bool alwaysload=false); extern FTexture *CrosshairImage; + +#endif /* __SBAR_H__ */ diff --git a/src/g_shared/sbar_mugshot.cpp b/src/g_shared/sbar_mugshot.cpp index c62e7c4d6..a41476576 100644 --- a/src/g_shared/sbar_mugshot.cpp +++ b/src/g_shared/sbar_mugshot.cpp @@ -324,7 +324,7 @@ bool FMugShot::SetState(const char *state_name, bool wait_till_done, bool reset) // //=========================================================================== -int FMugShot::UpdateState(player_t *player, int stateflags) +int FMugShot::UpdateState(player_t *player, StateFlags stateflags) { int i; angle_t badguyangle; @@ -333,7 +333,7 @@ int FMugShot::UpdateState(player_t *player, int stateflags) if (player->health > 0) { - if (bEvilGrin && !(stateflags & DRAWMUGSHOT_DISABLEGRIN)) + if (bEvilGrin && !(stateflags & DISABLEGRIN)) { if (player->bonuscount) { @@ -348,7 +348,7 @@ int FMugShot::UpdateState(player_t *player, int stateflags) if (player->damagecount && // Now go in if pain is disabled but we think ouch will be shown (and ouch is not disabled!) - (!(stateflags & DRAWMUGSHOT_DISABLEPAIN) || (((FaceHealth != -1 && FaceHealth - player->health > ST_MUCHPAIN) || bOuchActive) && !(stateflags & DRAWMUGSHOT_DISABLEOUCH)))) + (!(stateflags & DISABLEPAIN) || (((FaceHealth != -1 && FaceHealth - player->health > ST_MUCHPAIN) || bOuchActive) && !(stateflags & DISABLEOUCH)))) { int damage_angle = 1; if (player->attacker && player->attacker != player->mo) @@ -380,7 +380,7 @@ int FMugShot::UpdateState(player_t *player, int stateflags) } } bool use_ouch = false; - if (((FaceHealth != -1 && FaceHealth - player->health > ST_MUCHPAIN) || bOuchActive) && !(stateflags & DRAWMUGSHOT_DISABLEOUCH)) + if (((FaceHealth != -1 && FaceHealth - player->health > ST_MUCHPAIN) || bOuchActive) && !(stateflags & DISABLEOUCH)) { use_ouch = true; full_state_name = "ouch."; @@ -407,7 +407,7 @@ int FMugShot::UpdateState(player_t *player, int stateflags) else { bool use_ouch = false; - if (((FaceHealth != -1 && player->health - FaceHealth > ST_MUCHPAIN) || bOuchActive) && !(stateflags & DRAWMUGSHOT_DISABLEOUCH)) + if (((FaceHealth != -1 && player->health - FaceHealth > ST_MUCHPAIN) || bOuchActive) && !(stateflags & DISABLEOUCH)) { use_ouch = true; full_state_name = "ouch."; @@ -425,7 +425,7 @@ int FMugShot::UpdateState(player_t *player, int stateflags) } } - if (RampageTimer == ST_RAMPAGEDELAY && !(stateflags & DRAWMUGSHOT_DISABLERAMPAGE)) + if (RampageTimer == ST_RAMPAGEDELAY && !(stateflags & DISABLERAMPAGE)) { SetState("rampage", !bNormal); //If we have nothing better to show, use the rampage face. return 0; @@ -436,7 +436,7 @@ int FMugShot::UpdateState(player_t *player, int stateflags) bool good; if ((player->cheats & CF_GODMODE) || (player->mo != NULL && player->mo->flags2 & MF2_INVULNERABLE)) { - good = SetState((stateflags & DRAWMUGSHOT_ANIMATEDGODMODE) ? "godanimated" : "god"); + good = SetState((stateflags & ANIMATEDGODMODE) ? "godanimated" : "god"); } else { @@ -450,7 +450,7 @@ int FMugShot::UpdateState(player_t *player, int stateflags) } else { - if (!(stateflags & DRAWMUGSHOT_XDEATHFACE) || !(player->cheats & CF_EXTREMELYDEAD)) + if (!(stateflags & XDEATHFACE) || !(player->cheats & CF_EXTREMELYDEAD)) { full_state_name = "death."; } @@ -473,7 +473,7 @@ int FMugShot::UpdateState(player_t *player, int stateflags) // //=========================================================================== -FTexture *FMugShot::GetFace(player_t *player, const char *default_face, int accuracy, int stateflags) +FTexture *FMugShot::GetFace(player_t *player, const char *default_face, int accuracy, StateFlags stateflags) { int angle = UpdateState(player, stateflags); int level = 0; diff --git a/src/g_shared/sbarinfo.cpp b/src/g_shared/sbarinfo.cpp new file mode 100644 index 000000000..1344ad1c0 --- /dev/null +++ b/src/g_shared/sbarinfo.cpp @@ -0,0 +1,1320 @@ +/* +** sbarinfo_display.cpp +** +** Contains all functions required for the display of custom statusbars. +** +**--------------------------------------------------------------------------- +** Copyright 2008 Braden Obrzut +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "doomtype.h" +#include "doomstat.h" +#include "v_font.h" +#include "v_video.h" +#include "sbar.h" +#include "r_defs.h" +#include "w_wad.h" +#include "m_random.h" +#include "d_player.h" +#include "st_stuff.h" +#include "r_local.h" +#include "m_swap.h" +#include "a_keys.h" +#include "templates.h" +#include "i_system.h" +#include "sbarinfo.h" +#include "gi.h" +#include "r_translate.h" +#include "r_main.h" +#include "a_weaponpiece.h" +#include "a_strifeglobal.h" +#include "g_level.h" +#include "v_palette.h" +#include "p_acs.h" + +#define ADJUST_RELCENTER(x, y, outX, outY) \ + if(x.RelCenter()) \ + outX = *x + SCREENWIDTH/(hud_scale ? CleanXfac*2 : 2); \ + else \ + outX = *x; \ + if(y.RelCenter()) \ + outY = *y + SCREENHEIGHT/(hud_scale ? CleanYfac*2 : 2); \ + else \ + outY = *y; + +#define ARTIFLASH_OFFSET (statusBar->invBarOffset+6) +enum +{ + imgARTIBOX, + imgSELECTBOX, + imgCURSOR, + imgINVLFGEM1, + imgINVLFGEM2, + imgINVRTGEM1, + imgINVRTGEM2, +}; + +EXTERN_CVAR(Int, fraglimit) +EXTERN_CVAR(Int, screenblocks) +EXTERN_CVAR(Bool, vid_fps) +EXTERN_CVAR(Bool, hud_scale) + +class DSBarInfo; + +//////////////////////////////////////////////////////////////////////////////// + +/** + * This class is used to help prevent errors that may occur from adding or + * subtracting from coordinates. + * + * In order to provide the maximum flexibility, coordinates are packed into + * an int with one bit reserved for relCenter. + */ +class SBarInfoCoordinate +{ + public: + SBarInfoCoordinate &Add(int add) + { + value += add; + return *this; + } + int Coordinate() const { return value; } + bool RelCenter() const { return relCenter; } + void Set(int coord, bool center) { value = coord; relCenter = center; } + void SetCoord(int coord) { value = coord; } + void SetRelCenter(bool center) { relCenter = center; } + + int operator* () const { return Coordinate(); } + SBarInfoCoordinate operator+ (int add) const { return SBarInfoCoordinate(*this).Add(add); } + SBarInfoCoordinate operator+ (const SBarInfoCoordinate &other) const { return SBarInfoCoordinate(*this).Add(other.Coordinate()); } + SBarInfoCoordinate operator- (int sub) const { return SBarInfoCoordinate(*this).Add(-sub); } + SBarInfoCoordinate operator- (const SBarInfoCoordinate &other) const { return SBarInfoCoordinate(*this).Add(-other.Coordinate()); } + void operator+= (int add) { Add(add); } + void operator-= (int sub) { Add(-sub); } + + protected: + unsigned relCenter:1; + int value:31; +}; + +class SBarInfoMainBlock; + +//////////////////////////////////////////////////////////////////////////////// +/* There are three major classes here. The SBarInfoCommand is our root class. + * SBarInfoCommandFlowControl would be the base class for command which + * implements a sub-block. And SBarInfoMainBlock which is the root for a + * single hud (so all commands are held inside a MainBlock at some point). + * + * A MainBlock can be passed NULL for the first argument of the Draw function. + */ + +class SBarInfoCommand +{ + public: + enum Offset + { + TOP = 0x1, + VMIDDLE = 0x2, + BOTTOM = 0x4, + + LEFT = 0x10, + RIGHT = 0x20, + HMIDDLE = 0x40, + + CENTER = VMIDDLE|HMIDDLE, + CENTER_BOTTOM = BOTTOM|HMIDDLE + }; + + SBarInfoCommand(SBarInfo *script) : script(script) {} + virtual ~SBarInfoCommand() {} + + virtual void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar)=0; + virtual void Parse(FScanner &sc, bool fullScreenOffsets)=0; + virtual void Reset() {} + virtual void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) {} + + protected: + void GetCoordinates(FScanner &sc, bool fullScreenOffsets, SBarInfoCoordinate &x, SBarInfoCoordinate &y) + { + bool negative = false; + bool relCenter = false; + SBarInfoCoordinate *coords[2] = {&x, &y}; + for(int i = 0;i < 2;i++) + { + negative = false; + relCenter = false; + if(i > 0) + sc.MustGetToken(','); + + // [-]INT center + negative = sc.CheckToken('-'); + sc.MustGetToken(TK_IntConst); + coords[i]->Set(negative ? -sc.Number : sc.Number, false); + if(sc.CheckToken('+')) + { + sc.MustGetToken(TK_Identifier); + if(!sc.Compare("center")) + sc.ScriptError("Expected 'center' but got '%s' instead.", sc.String); + relCenter = true; + } + if(fullScreenOffsets) + { + coords[i]->SetRelCenter(relCenter); + } + } + + if(!fullScreenOffsets) + y.SetCoord((negative ? -sc.Number : sc.Number) - (200 - script->height)); + } + EColorRange GetTranslation(FScanner &sc) + { + sc.MustGetToken(TK_Identifier); + EColorRange returnVal = CR_UNTRANSLATED; + FString namedTranslation; //we must send in "[translation]" + const BYTE *trans_ptr; + namedTranslation.Format("[%s]", sc.String); + trans_ptr = (const BYTE *)(&namedTranslation[0]); + if((returnVal = V_ParseFontColor(trans_ptr, CR_UNTRANSLATED, CR_UNTRANSLATED)) == CR_UNDEFINED) + { + sc.ScriptError("Missing definition for color %s.", sc.String); + } + return returnVal; + } + + SBarInfo *script; +}; + +class SBarInfoCommandFlowControl : public SBarInfoCommand +{ + public: + SBarInfoCommandFlowControl(SBarInfo *script) : SBarInfoCommand(script) {} + ~SBarInfoCommandFlowControl() + { + for(unsigned int i = 0;i < commands.Size();i++) + delete commands[i]; + } + + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) + { + for(unsigned int i = 0;i < commands.Size();i++) + commands[i]->Draw(block, statusBar); + } + int NumCommands() const { return commands.Size(); } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + sc.MustGetToken('{'); + SBarInfoCommand *cmd = NULL; + while((cmd = NextCommand(sc)) != NULL) + { + cmd->Parse(sc, fullScreenOffsets); + commands.Push(cmd); + } + } + void Reset() + { + for(unsigned int i = 0;i < commands.Size();i++) + commands[i]->Reset(); + } + void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) + { + for(unsigned int i = 0;i < commands.Size();i++) + commands[i]->Tick(block, statusBar, hudChanged); + } + + private: + SBarInfoCommand *NextCommand(FScanner &sc); + + TArray commands; +}; + +class SBarInfoMainBlock : public SBarInfoCommandFlowControl +{ + public: + SBarInfoMainBlock(SBarInfo *script) : SBarInfoCommandFlowControl(script), + alpha(FRACUNIT), forceScaled(false), fullScreenOffsets(false) + { + } + + int Alpha() const { return alpha; } + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, int xOffset, int yOffset, int alpha) + { + this->xOffset = xOffset; + this->yOffset = yOffset; + this->alpha = alpha; + SBarInfoCommandFlowControl::Draw(this, statusBar); + } + bool ForceScaled() const { return forceScaled; } + bool FullScreenOffsets() const { return fullScreenOffsets; } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + this->fullScreenOffsets = fullScreenOffsets; + if(sc.CheckToken(',')) + { + while(sc.CheckToken(TK_Identifier)) + { + if(sc.Compare("forcescaled")) + forceScaled = true; + else if(sc.Compare("fullscreenoffsets")) + this->fullScreenOffsets = true; + else + sc.ScriptError("Unkown flag '%s'.", sc.String); + if(!sc.CheckToken('|') && !sc.CheckToken(',')) + { + SBarInfoCommandFlowControl::Parse(sc, this->fullScreenOffsets); + return; + } + } + sc.MustGetToken(TK_FloatConst); + alpha = fixed_t(FRACUNIT * sc.Float); + } + SBarInfoCommandFlowControl::Parse(sc, this->fullScreenOffsets); + } + void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) { SBarInfoCommandFlowControl::Tick(this, statusBar, hudChanged); } + int XOffset() const { return xOffset; } + int YOffset() const { return yOffset; } + + private: + int alpha; + bool forceScaled; + bool fullScreenOffsets; + int xOffset; + int yOffset; +}; + +//////////////////////////////////////////////////////////////////////////////// + +SBarInfo *SBarInfoScript[2] = {NULL,NULL}; + +enum //Key words +{ + SBARINFO_BASE, + SBARINFO_HEIGHT, + SBARINFO_INTERPOLATEHEALTH, + SBARINFO_INTERPOLATEARMOR, + SBARINFO_COMPLETEBORDER, + SBARINFO_MONOSPACEFONTS, + SBARINFO_LOWERHEALTHCAP, + SBARINFO_STATUSBAR, + SBARINFO_MUGSHOT, + SBARINFO_CREATEPOPUP, +}; + +enum //Bar types +{ + STBAR_NONE, + STBAR_FULLSCREEN, + STBAR_NORMAL, + STBAR_AUTOMAP, + STBAR_INVENTORY, + STBAR_INVENTORYFULLSCREEN, + STBAR_POPUPLOG, + STBAR_POPUPKEYS, + STBAR_POPUPSTATUS, +}; + +static const char *SBarInfoTopLevel[] = +{ + "base", + "height", + "interpolatehealth", + "interpolatearmor", + "completeborder", + "monospacefonts", + "lowerhealthcap", + "statusbar", + "mugshot", + "createpopup", + NULL +}; + +static const char *StatusBars[] = +{ + "none", + "fullscreen", + "normal", + "automap", + "inventory", + "inventoryfullscreen", + "popuplog", + "popupkeys", + "popupstatus", + NULL +}; + +static void FreeSBarInfoScript() +{ + for(int i = 0;i < 2;i++) + { + if (SBarInfoScript[i] != NULL) + { + delete SBarInfoScript[i]; + SBarInfoScript[i] = NULL; + } + } +} + +void SBarInfo::Load() +{ + if(gameinfo.statusbar.IsNotEmpty()) + { + int lump = Wads.CheckNumForFullName(gameinfo.statusbar, true); + if(lump != -1) + { + Printf ("ParseSBarInfo: Loading default status bar definition.\n"); + if(SBarInfoScript[SCRIPT_DEFAULT] == NULL) + SBarInfoScript[SCRIPT_DEFAULT] = new SBarInfo(lump); + else + SBarInfoScript[SCRIPT_DEFAULT]->ParseSBarInfo(lump); + } + } + + if(Wads.CheckNumForName("SBARINFO") != -1) + { + Printf ("ParseSBarInfo: Loading custom status bar definition.\n"); + int lastlump, lump; + lastlump = 0; + while((lump = Wads.FindLump("SBARINFO", &lastlump)) != -1) + { + if(SBarInfoScript[SCRIPT_CUSTOM] == NULL) + SBarInfoScript[SCRIPT_CUSTOM] = new SBarInfo(lump); + else //We now have to load multiple SBarInfo Lumps so the 2nd time we need to use this method instead. + SBarInfoScript[SCRIPT_CUSTOM]->ParseSBarInfo(lump); + } + } + atterm(FreeSBarInfoScript); +} + +//SBarInfo Script Reader +void SBarInfo::ParseSBarInfo(int lump) +{ + gameType = gameinfo.gametype; + bool baseSet = false; + FScanner sc(lump); + sc.SetCMode(true); + while(sc.CheckToken(TK_Identifier) || sc.CheckToken(TK_Include)) + { + if(sc.TokenType == TK_Include) + { + sc.MustGetToken(TK_StringConst); + int lump = Wads.CheckNumForFullName(sc.String, true); + if (lump == -1) + sc.ScriptError("Lump '%s' not found", sc.String); + ParseSBarInfo(lump); + continue; + } + switch(sc.MustMatchString(SBarInfoTopLevel)) + { + case SBARINFO_BASE: + baseSet = true; + if(!sc.CheckToken(TK_None)) + sc.MustGetToken(TK_Identifier); + if(sc.Compare("Doom")) + { + int lump = Wads.CheckNumForFullName("sbarinfo/doom.txt", true); + if(lump == -1) + sc.ScriptError("Standard Doom Status Bar not found."); + ParseSBarInfo(lump); + } + else if(sc.Compare("Heretic")) + gameType = GAME_Heretic; + else if(sc.Compare("Hexen")) + gameType = GAME_Hexen; + else if(sc.Compare("Strife")) + gameType = GAME_Strife; + else if(sc.Compare("None")) + gameType = GAME_Any; + else + sc.ScriptError("Bad game name: %s", sc.String); + sc.MustGetToken(';'); + break; + case SBARINFO_HEIGHT: + sc.MustGetToken(TK_IntConst); + this->height = sc.Number; + sc.MustGetToken(';'); + break; + case SBARINFO_INTERPOLATEHEALTH: //mimics heretics interpolated health values. + if(sc.CheckToken(TK_True)) + { + interpolateHealth = true; + } + else + { + sc.MustGetToken(TK_False); + interpolateHealth = false; + } + if(sc.CheckToken(',')) //speed param + { + sc.MustGetToken(TK_IntConst); + this->interpolationSpeed = sc.Number; + } + sc.MustGetToken(';'); + break; + case SBARINFO_INTERPOLATEARMOR: //Since interpolatehealth is such a popular command + if(sc.CheckToken(TK_True)) + { + interpolateArmor = true; + } + else + { + sc.MustGetToken(TK_False); + interpolateArmor = false; + } + if(sc.CheckToken(',')) //speed + { + sc.MustGetToken(TK_IntConst); + this->armorInterpolationSpeed = sc.Number; + } + sc.MustGetToken(';'); + break; + case SBARINFO_COMPLETEBORDER: //draws the border instead of an HOM + if(sc.CheckToken(TK_True)) + { + completeBorder = true; + } + else + { + sc.MustGetToken(TK_False); + completeBorder = false; + } + sc.MustGetToken(';'); + break; + case SBARINFO_MONOSPACEFONTS: + if(sc.CheckToken(TK_True)) + { + sc.MustGetToken(','); + sc.MustGetToken(TK_StringConst); + spacingCharacter = sc.String[0]; + } + else + { + sc.MustGetToken(TK_False); + spacingCharacter = '\0'; + sc.MustGetToken(','); + sc.MustGetToken(TK_StringConst); //Don't tell anyone we're just ignoring this ;) + } + sc.MustGetToken(';'); + break; + case SBARINFO_LOWERHEALTHCAP: + if(sc.CheckToken(TK_False)) + { + lowerHealthCap = false; + } + else + { + sc.MustGetToken(TK_True); + lowerHealthCap = true; + } + sc.MustGetToken(';'); + break; + case SBARINFO_STATUSBAR: + { + if(!baseSet) //If the user didn't explicitly define a base, do so now. + gameType = GAME_Any; + int barNum = 0; + if(!sc.CheckToken(TK_None)) + { + sc.MustGetToken(TK_Identifier); + barNum = sc.MustMatchString(StatusBars); + } + this->huds[barNum] = new SBarInfoMainBlock(this); + if(barNum == STBAR_AUTOMAP) + { + automapbar = true; + } + this->huds[barNum]->Parse(sc, false); + break; + } + case SBARINFO_MUGSHOT: + { + sc.MustGetToken(TK_StringConst); + FMugShotState state(sc.String); + if(sc.CheckToken(',')) //first loop must be a comma + { + do + { + sc.MustGetToken(TK_Identifier); + if(sc.Compare("health")) + state.bUsesLevels = true; + else if(sc.Compare("health2")) + state.bUsesLevels = state.bHealth2 = true; + else if(sc.Compare("healthspecial")) + state.bUsesLevels = state.bHealthSpecial = true; + else if(sc.Compare("directional")) + state.bDirectional = true; + else + sc.ScriptError("Unknown MugShot state flag '%s'.", sc.String); + } + while(sc.CheckToken(',') || sc.CheckToken('|')); + } + ParseMugShotBlock(sc, state); + int index; + if((index = FindMugShotStateIndex(state.State)) != -1) //We already had this state, remove the old one. + { + MugShotStates.Delete(index); + } + MugShotStates.Push(state); + break; + } + case SBARINFO_CREATEPOPUP: + { + int pop = 0; + sc.MustGetToken(TK_Identifier); + if(sc.Compare("log")) + pop = 0; + else if(sc.Compare("keys")) + pop = 1; + else if(sc.Compare("status")) + pop = 2; + else + sc.ScriptError("Unkown popup: '%s'", sc.String); + Popup &popup = popups[pop]; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + popup.width = sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + popup.height = sc.Number; + sc.MustGetToken(','); + if(!sc.CheckToken(TK_None)) + { + sc.MustGetToken(TK_Identifier); + if(sc.Compare("slideinbottom")) + { + popup.transition = Popup::TRANSITION_SLIDEINBOTTOM; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + popup.speed = sc.Number; + } + else if(sc.Compare("fade")) + { + popup.transition = Popup::TRANSITION_FADE; + sc.MustGetToken(','); + sc.MustGetToken(TK_FloatConst); + popup.speed = fixed_t(FRACUNIT * (1.0 / (35.0 * sc.Float))); + sc.MustGetToken(','); + sc.MustGetToken(TK_FloatConst); + popup.speed2 = fixed_t(FRACUNIT * (1.0 / (35.0 * sc.Float))); + } + else + sc.ScriptError("Unkown transition type: '%s'", sc.String); + } + popup.init(); + sc.MustGetToken(';'); + break; + } + } + } +} + +void SBarInfo::ParseMugShotBlock(FScanner &sc, FMugShotState &state) +{ + sc.MustGetToken('{'); + while(!sc.CheckToken('}')) + { + FMugShotFrame frame; + bool multiframe = false; + if(sc.CheckToken('{')) + multiframe = true; + do + { + sc.MustGetToken(TK_Identifier); + if(strlen(sc.String) > 5) + sc.ScriptError("MugShot frames cannot exceed 5 characters."); + frame.Graphic.Push(sc.String); + } + while(multiframe && sc.CheckToken(',')); + if(multiframe) + sc.MustGetToken('}'); + bool negative = sc.CheckToken('-'); + sc.MustGetToken(TK_IntConst); + frame.Delay = (negative ? -1 : 1)*sc.Number; + sc.MustGetToken(';'); + state.Frames.Push(frame); + } +} + +void SBarInfo::ResetHuds() +{ + for(int i = 0;i < NUMHUDS;i++) + huds[i]->Reset(); +} + +int SBarInfo::newImage(const char *patchname) +{ + if(patchname[0] == '\0' || stricmp(patchname, "nullimage") == 0) + { + return -1; + } + for(unsigned int i = 0;i < this->Images.Size();i++) //did we already load it? + { + if(stricmp(this->Images[i], patchname) == 0) + { + return i; + } + } + return this->Images.Push(patchname); +} + +SBarInfo::SBarInfo() //make results more predicable +{ + Init(); +} + +SBarInfo::SBarInfo(int lumpnum) +{ + Init(); + ParseSBarInfo(lumpnum); +} + +void SBarInfo::Init() +{ + automapbar = false; + interpolateHealth = false; + interpolateArmor = false; + completeBorder = false; + lowerHealthCap = true; + interpolationSpeed = 8; + armorInterpolationSpeed = 8; + height = 0; + spacingCharacter = '\0'; + + for(unsigned int i = 0;i < NUMHUDS;i++) + huds[i] = new SBarInfoMainBlock(this); +} + +SBarInfo::~SBarInfo() +{ + for (size_t i = 0; i < NUMHUDS; ++i) + { + delete huds[i]; +// huds[i].commands.Clear(); + } +} + +//Popup +Popup::Popup() +{ + transition = TRANSITION_NONE; + height = 320; + width = 200; + speed = 0; + x = 320; + y = 200; + alpha = FRACUNIT; + opened = false; + moving = false; +} + +void Popup::init() +{ + x = width; + y = height; + if(transition == TRANSITION_SLIDEINBOTTOM) + { + x = 0; + } + else if(transition == TRANSITION_FADE) + { + alpha = 0; + x = 0; + y = 0; + } +} + +void Popup::tick() +{ + if(transition == TRANSITION_SLIDEINBOTTOM) + { + if(moving) + { + if(opened) + y -= clamp(height + (y - height), 1, speed); + else + y += clamp(height - y, 1, speed); + } + if(y != 0 && y != height) + moving = true; + else + moving = false; + } + else if(transition == TRANSITION_FADE) + { + if(moving) + { + if(opened) + alpha = clamp(alpha + speed, 0, FRACUNIT); + else + alpha = clamp(alpha - speed2, 0, FRACUNIT); + } + if(alpha == 0 || alpha == FRACUNIT) + moving = false; + else + moving = true; + } + else + { + if(opened) + { + y = 0; + x = 0; + } + else + { + y = height; + x = width; + } + moving = false; + } +} + +bool Popup::isDoneMoving() +{ + return !moving; +} + +int Popup::getXOffset() +{ + return x; +} + +int Popup::getYOffset() +{ + return y; +} + +int Popup::getAlpha(int maxAlpha) +{ + double a = (double) alpha / (double) FRACUNIT; + double b = (double) maxAlpha / (double) FRACUNIT; + return fixed_t((a * b) * FRACUNIT); +} + +void Popup::open() +{ + opened = true; + moving = true; +} + +void Popup::close() +{ + opened = false; + moving = true; +} + +//////////////////////////////////////////////////////////////////////////////// + +class DSBarInfo : public DBaseStatusBar +{ + DECLARE_CLASS(DSBarInfo, DBaseStatusBar) +public: + DSBarInfo (SBarInfo *script=NULL) : DBaseStatusBar(script->height), + ammo1(NULL), ammo2(NULL), ammocount1(0), ammocount2(0), armor(NULL), + pendingPopup(POP_None), currentPopup(POP_None), lastHud(-1), + lastInventoryBar(NULL), lastPopup(NULL) + { + this->script = script; + + static const char *InventoryBarLumps[] = + { + "ARTIBOX", "SELECTBO", "INVCURS", "INVGEML1", + "INVGEML2", "INVGEMR1", "INVGEMR2", + "USEARTIA", "USEARTIB", "USEARTIC", "USEARTID", + }; + TArray patchnames; + patchnames.Resize(script->Images.Size()+10); + unsigned int i = 0; + for(i = 0;i < script->Images.Size();i++) + { + patchnames[i] = script->Images[i]; + } + for(i = 0;i < 10;i++) + { + patchnames[i+script->Images.Size()] = InventoryBarLumps[i]; + } + for (i = 0;i < numskins;i++) + { + AddFaceToImageCollection (&skins[i], &Images); + } + invBarOffset = script->Images.Size(); + Images.Init(&patchnames[0], patchnames.Size()); + } + + ~DSBarInfo () + { + Images.Uninit(); + } + + void Draw (EHudState state) + { + DBaseStatusBar::Draw(state); + int hud = STBAR_NORMAL; + if(state == HUD_StatusBar) + { + if(script->completeBorder) //Fill the statusbar with the border before we draw. + { + FTexture *b = TexMan[gameinfo.border->b]; + R_DrawBorder(viewwindowx, viewwindowy + viewheight + b->GetHeight(), viewwindowx + viewwidth, SCREENHEIGHT); + if(screenblocks == 10) + screen->FlatFill(viewwindowx, viewwindowy + viewheight, viewwindowx + viewwidth, viewwindowy + viewheight + b->GetHeight(), b, true); + } + if(script->automapbar && automapactive) + { + hud = STBAR_AUTOMAP; + } + else + { + hud = STBAR_NORMAL; + } + } + else if(state == HUD_Fullscreen) + { + hud = STBAR_FULLSCREEN; + } + else + { + hud = STBAR_NONE; + } + bool oldhud_scale = hud_scale; + if(script->huds[hud]->ForceScaled()) //scale the statusbar + { + SetScaled(true, true); + setsizeneeded = true; + if(script->huds[hud]->FullScreenOffsets()) + hud_scale = true; + } + + //prepare ammo counts + GetCurrentAmmo(ammo1, ammo2, ammocount1, ammocount2); + armor = CPlayer->mo->FindInventory(); + if(hud != lastHud) + script->huds[hud]->Tick(NULL, this, true); + script->huds[hud]->Draw(NULL, this, 0, 0, FRACUNIT); + lastHud = hud; + if(CPlayer->inventorytics > 0 && !(level.flags & LEVEL_NOINVENTORYBAR) && (state == HUD_StatusBar || state == HUD_Fullscreen)) + { + SBarInfoMainBlock *inventoryBar = state == HUD_StatusBar ? script->huds[STBAR_INVENTORY] : script->huds[STBAR_INVENTORYFULLSCREEN]; + if(inventoryBar != lastInventoryBar) + inventoryBar->Tick(NULL, this, true); + + // No overlay? Lets cancel it. + if(inventoryBar->NumCommands() == 0) + CPlayer->inventorytics = 0; + else + inventoryBar->Draw(NULL, this, 0, 0, FRACUNIT); + } + if(currentPopup != POP_None) + { + int popbar = 0; + if(currentPopup == POP_Log) + popbar = STBAR_POPUPLOG; + else if(currentPopup == POP_Keys) + popbar = STBAR_POPUPKEYS; + else if(currentPopup == POP_Status) + popbar = STBAR_POPUPSTATUS; + if(script->huds[popbar] != lastPopup) + { + lastPopup = script->huds[popbar]; + lastPopup->Tick(NULL, this, true); + } + + script->huds[popbar]->Draw(NULL, this, script->popups[currentPopup-1].getXOffset(), script->popups[currentPopup-1].getYOffset(), script->popups[currentPopup-1].getAlpha()); + } + else + lastPopup = NULL; + if(script->huds[hud]->ForceScaled() && script->huds[hud]->FullScreenOffsets()) + hud_scale = oldhud_scale; + } + + void NewGame () + { + if (CPlayer != NULL) + { + AttachToPlayer (CPlayer); + + // Reset the huds + script->ResetHuds(); + lastHud = -1; // Reset + } + } + + void SetMugShotState (const char *state_name, bool wait_till_done, bool reset) + { + script->MugShot.SetState(state_name, wait_till_done, reset); + } + + void Tick () + { + DBaseStatusBar::Tick(); + + script->MugShot.Tick(CPlayer); + if(currentPopup != POP_None) + { + script->popups[currentPopup-1].tick(); + if(script->popups[currentPopup-1].opened == false && script->popups[currentPopup-1].isDoneMoving()) + { + currentPopup = pendingPopup; + if(currentPopup != POP_None) + script->popups[currentPopup-1].open(); + } + + if (lastPopup != NULL) lastPopup->Tick(NULL, this, false); + } + + if(lastHud != -1) + script->huds[lastHud]->Tick(NULL, this, false); + if(lastInventoryBar != NULL && CPlayer->inventorytics > 0) + lastInventoryBar->Tick(NULL, this, false); + } + + void ReceivedWeapon(AWeapon *weapon) + { + script->MugShot.Grin(); + } + + // void DSBarInfo::FlashItem(const PClass *itemtype) - Is defined with CommandDrawSelectedInventory + void FlashItem(const PClass *itemtype); + + void ShowPop(int popnum) + { + DBaseStatusBar::ShowPop(popnum); + if(popnum != currentPopup) + { + pendingPopup = popnum; + } + else + pendingPopup = POP_None; + if(currentPopup != POP_None) + script->popups[currentPopup-1].close(); + else + { + currentPopup = pendingPopup; + pendingPopup = POP_None; + if(currentPopup != POP_None) + script->popups[currentPopup-1].open(); + } + } + + //draws an image with the specified flags + void DrawGraphic(FTexture* texture, SBarInfoCoordinate x, SBarInfoCoordinate y, int xOffset, int yOffset, int alpha, bool fullScreenOffsets, bool translate=false, bool dim=false, int offsetflags=0, bool alphaMap=false, int forceWidth=-1, int forceHeight=-1, fixed_t cx=0, fixed_t cy=0, fixed_t cr=0, fixed_t cb=0, bool clearDontDraw=false) const + { + if (texture == NULL) + return; + + if((offsetflags & SBarInfoCommand::CENTER) == SBarInfoCommand::CENTER) + { + x -= (texture->GetScaledWidth()/2)-texture->LeftOffset; + y -= (texture->GetScaledHeight()/2)-texture->TopOffset; + } + + x += xOffset; + y += yOffset; + int w, h; + if(!fullScreenOffsets) + { + fixed_t tmp = 0; + // I'll handle the conversion from fixed to int myself for more control + fixed_t fx = (x + ST_X).Coordinate() << FRACBITS; + fixed_t fy = (y + ST_Y).Coordinate() << FRACBITS; + fixed_t fw = (forceWidth <= -1 ? texture->GetScaledWidth() : forceWidth) << FRACBITS; + fixed_t fh = (forceHeight <= -1 ? texture->GetScaledHeight() : forceHeight) << FRACBITS; + fixed_t fcx = cx == 0 ? 0 : fx + cx - (texture->GetScaledLeftOffset() << FRACBITS); + fixed_t fcy = cy == 0 ? 0 : fy + cy - (texture->GetScaledTopOffset() << FRACBITS); + fixed_t fcr = fx + fw - cr; + fixed_t fcb = fy + fh - cb; + if(Scaled) + { + if(cx != 0 || cy != 0) + screen->VirtualToRealCoordsFixed(fcx, fcy, tmp, tmp, 320, 200, true); + if(cr != 0 || cb != 0 || clearDontDraw) + screen->VirtualToRealCoordsFixed(fcr, fcb, tmp, tmp, 320, 200, true); + screen->VirtualToRealCoordsFixed(fx, fy, fw, fh, 320, 200, true); + } + // Round to nearest + w = (fw + (FRACUNIT>>1)) >> FRACBITS; + h = (fh + (FRACUNIT>>1)) >> FRACBITS; + cr = cr != 0 ? fcr >> FRACBITS : INT_MAX; + cb = cb != 0 ? fcb >> FRACBITS : INT_MAX; + if(clearDontDraw) + screen->Clear(MAX(fx, fcx)>>FRACBITS, MAX(fy, fcy)>>FRACBITS, fcr>>FRACBITS, fcb>>FRACBITS, GPalette.BlackIndex, 0); + else + { + if(alphaMap) + { + screen->DrawTexture(texture, (fx >> FRACBITS), (fy >> FRACBITS), + DTA_DestWidth, w, + DTA_DestHeight, h, + DTA_ClipLeft, fcx>>FRACBITS, + DTA_ClipTop, fcy>>FRACBITS, + DTA_ClipRight, cr, + DTA_ClipBottom, cb, + DTA_Translation, translate ? GetTranslation() : 0, + DTA_ColorOverlay, dim ? DIM_OVERLAY : 0, + DTA_CenterBottomOffset, (offsetflags & SBarInfoCommand::CENTER_BOTTOM) == SBarInfoCommand::CENTER_BOTTOM, + DTA_Alpha, alpha, + DTA_AlphaChannel, alphaMap, + DTA_FillColor, 0, + TAG_DONE); + } + else + { + screen->DrawTexture(texture, (fx >> FRACBITS), (fy >> FRACBITS), + DTA_DestWidth, w, + DTA_DestHeight, h, + DTA_ClipLeft, fcx>>FRACBITS, + DTA_ClipTop, fcy>>FRACBITS, + DTA_ClipRight, cr, + DTA_ClipBottom, cb, + DTA_Translation, translate ? GetTranslation() : 0, + DTA_ColorOverlay, dim ? DIM_OVERLAY : 0, + DTA_CenterBottomOffset, (offsetflags & SBarInfoCommand::CENTER_BOTTOM) == SBarInfoCommand::CENTER_BOTTOM, + DTA_Alpha, alpha, + TAG_DONE); + } + } + } + else + { + int rx, ry, rcx=0, rcy=0, rcr=INT_MAX, rcb=INT_MAX; + ADJUST_RELCENTER(x,y,rx,ry) + + w = (forceWidth <= -1 ? texture->GetScaledWidth() : forceWidth); + h = (forceHeight <= -1 ? texture->GetScaledHeight() : forceHeight); + if(vid_fps && rx < 0 && ry >= 0) + ry += 10; + + // Check for clipping + if(cx != 0 || cy != 0 || cr != 0 || cb != 0) + { + rcx = cx == 0 ? 0 : rx+(cx>>FRACBITS); + rcy = cy == 0 ? 0 : ry+(cy>>FRACBITS); + rcr = cr == 0 ? INT_MAX : rx+w-(cr>>FRACBITS); + rcb = cb == 0 ? INT_MAX : ry+h-(cb>>FRACBITS); + // Fix the clipping for fullscreenoffsets. + if(ry < 0) + { + if(rcy != 0) + rcy = hud_scale ? SCREENHEIGHT + (rcy*CleanYfac) : SCREENHEIGHT + rcy; + if(rcb != INT_MAX) + rcb = hud_scale ? SCREENHEIGHT + (rcb*CleanYfac) : SCREENHEIGHT + rcb; + } + else if(hud_scale) + { + rcy *= CleanYfac; + if(rcb != INT_MAX) + rcb *= CleanYfac; + } + if(rx < 0) + { + if(rcx != 0) + rcx = hud_scale ? SCREENWIDTH + (rcx*CleanXfac) : SCREENWIDTH + rcx; + if(rcr != INT_MAX) + rcr = hud_scale ? SCREENWIDTH + (rcr*CleanXfac) : SCREENWIDTH + rcr; + } + else if(hud_scale) + { + rcx *= CleanXfac; + if(rcr != INT_MAX) + rcr *= CleanXfac; + } + } + + if(clearDontDraw) + { + screen->Clear(rcx, rcy, MIN(rcr, w*(hud_scale ? CleanXfac : 1)), MIN(rcb, h*(hud_scale ? CleanYfac : 1)), GPalette.BlackIndex, 0); + } + else + { + if(alphaMap) + { + screen->DrawTexture(texture, rx, ry, + DTA_DestWidth, w, + DTA_DestHeight, h, + DTA_ClipLeft, rcx, + DTA_ClipTop, rcy, + DTA_ClipRight, rcr, + DTA_ClipBottom, rcb, + DTA_Translation, translate ? GetTranslation() : 0, + DTA_ColorOverlay, dim ? DIM_OVERLAY : 0, + DTA_CenterBottomOffset, (offsetflags & SBarInfoCommand::CENTER_BOTTOM) == SBarInfoCommand::CENTER_BOTTOM, + DTA_HUDRules, HUD_Normal, + DTA_Alpha, alpha, + DTA_AlphaChannel, alphaMap, + DTA_FillColor, 0, + TAG_DONE); + } + else + { + screen->DrawTexture(texture, rx, ry, + DTA_DestWidth, w, + DTA_DestHeight, h, + DTA_ClipLeft, rcx, + DTA_ClipTop, rcy, + DTA_ClipRight, rcr, + DTA_ClipBottom, rcb, + DTA_Translation, translate ? GetTranslation() : 0, + DTA_ColorOverlay, dim ? DIM_OVERLAY : 0, + DTA_CenterBottomOffset, (offsetflags & SBarInfoCommand::CENTER_BOTTOM) == SBarInfoCommand::CENTER_BOTTOM, + DTA_HUDRules, HUD_Normal, + DTA_Alpha, alpha, + TAG_DONE); + } + } + } + } + + void DrawString(FFont *font, const char* str, SBarInfoCoordinate x, SBarInfoCoordinate y, int xOffset, int yOffset, int alpha, bool fullScreenOffsets, EColorRange translation, int spacing=0, bool drawshadow=false) const + { + x += spacing; + int ax = *x; + int ay = *y; + if(fullScreenOffsets) + { + ADJUST_RELCENTER(x,y,ax,ay) + } + while(*str != '\0') + { + if(*str == ' ') + { + ax += font->GetSpaceWidth(); + str++; + continue; + } + int width; + if(script->spacingCharacter == '\0') //No monospace? + width = font->GetCharWidth((int) *str); + else + width = font->GetCharWidth((int) script->spacingCharacter); + FTexture* character = font->GetChar((int) *str, &width); + if(character == NULL) //missing character. + { + str++; + continue; + } + if(script->spacingCharacter == '\0') //If we are monospaced lets use the offset + ax += (character->LeftOffset+1); //ignore x offsets since we adapt to character size + + int rx, ry, rw, rh; + rx = ax + xOffset; + ry = ay + yOffset; + rw = character->GetScaledWidth(); + rh = character->GetScaledHeight(); + if(!fullScreenOffsets) + { + rx += ST_X; + ry += ST_Y; + if(Scaled) + screen->VirtualToRealCoordsInt(rx, ry, rw, rh, 320, 200, true); + } + else + { + if(vid_fps && ax < 0 && ay >= 0) + ry += 10; + } + if(drawshadow) + { + int salpha = fixed_t(((double) alpha / (double) FRACUNIT) * ((double) HR_SHADOW / (double) FRACUNIT) * FRACUNIT); + if(!fullScreenOffsets) + { + screen->DrawTexture(character, rx+2, ry+2, + DTA_DestWidth, rw, + DTA_DestHeight, rh, + DTA_Alpha, salpha, + DTA_FillColor, 0, + TAG_DONE); + } + else + { + screen->DrawTexture(character, rx+2, ry+2, + DTA_DestWidth, rw, + DTA_DestHeight, rh, + DTA_Alpha, salpha, + DTA_HUDRules, HUD_Normal, + DTA_FillColor, 0, + TAG_DONE); + } + } + if(!fullScreenOffsets) + { + screen->DrawTexture(character, rx, ry, + DTA_DestWidth, rw, + DTA_DestHeight, rh, + DTA_Translation, font->GetColorTranslation(translation), + DTA_Alpha, alpha, + TAG_DONE); + } + else + { + screen->DrawTexture(character, rx, ry, + DTA_DestWidth, rw, + DTA_DestHeight, rh, + DTA_Translation, font->GetColorTranslation(translation), + DTA_Alpha, alpha, + DTA_HUDRules, HUD_Normal, + TAG_DONE); + } + if(script->spacingCharacter == '\0') + ax += width + spacing - (character->LeftOffset+1); + else //width gets changed at the call to GetChar() + ax += font->GetCharWidth((int) script->spacingCharacter) + spacing; + str++; + } + } + + FRemapTable* GetTranslation() const + { + if(gameinfo.gametype & GAME_Raven) + return translationtables[TRANSLATION_PlayersExtra][int(CPlayer - players)]; + return translationtables[TRANSLATION_Players][int(CPlayer - players)]; + } + + AAmmo *ammo1, *ammo2; + int ammocount1, ammocount2; + ABasicArmor *armor; + FImageCollection Images; + unsigned int invBarOffset; + +private: + SBarInfo *script; + int pendingPopup; + int currentPopup; + int lastHud; + SBarInfoMainBlock *lastInventoryBar; + SBarInfoMainBlock *lastPopup; +}; + +IMPLEMENT_CLASS(DSBarInfo); + +DBaseStatusBar *CreateCustomStatusBar (int script) +{ + if(SBarInfoScript[script] == NULL) + I_FatalError("Tried to create a status bar with no script!"); + return new DSBarInfo(SBarInfoScript[script]); +} + +#include "sbarinfo_commands.cpp" diff --git a/src/g_shared/sbarinfo.h b/src/g_shared/sbarinfo.h index ef36c50c5..6fd7b1f0c 100644 --- a/src/g_shared/sbarinfo.h +++ b/src/g_shared/sbarinfo.h @@ -37,57 +37,24 @@ #define __SBarInfo_SBAR_H__ #include "tarray.h" -#include "v_collection.h" #define NUMHUDS 9 #define NUMPOPUPS 3 -class FBarTexture; class FScanner; -/** - * This class is used to help prevent errors that may occur from adding or - * subtracting from coordinates. - * - * In order to provide the maximum flexibility, coordinates are packed into - * an int with one bit reserved for relCenter. - */ -class SBarInfoCoordinate -{ - public: - SBarInfoCoordinate &Add(int add); - int Coordinate() const { return value; } - bool RelCenter() const { return relCenter; } - void Set(int coord, bool center) { value = coord; relCenter = center; } - void SetCoord(int coord) { value = coord; } - void SetRelCenter(bool center) { relCenter = center; } - - int operator* () const { return Coordinate(); } - SBarInfoCoordinate operator+ (int add) const { return SBarInfoCoordinate(*this).Add(add); } - SBarInfoCoordinate operator+ (const SBarInfoCoordinate &other) const { return SBarInfoCoordinate(*this).Add(other.Coordinate()); } - SBarInfoCoordinate operator- (int sub) const { return SBarInfoCoordinate(*this).Add(-sub); } - SBarInfoCoordinate operator- (const SBarInfoCoordinate &other) const { return SBarInfoCoordinate(*this).Add(-other.Coordinate()); } - void operator+= (int add) { Add(add); } - void operator-= (int sub) { Add(-sub); } - - protected: - unsigned relCenter:1; - int value:31; -}; - -struct SBarInfoCommand; //we need to be able to use this before it is defined. -struct MugShotState; +class SBarInfoMainBlock; //Popups! -enum PopupTransition -{ - TRANSITION_NONE, - TRANSITION_SLIDEINBOTTOM, - TRANSITION_FADE, -}; - struct Popup { + enum PopupTransition + { + TRANSITION_NONE, + TRANSITION_SLIDEINBOTTOM, + TRANSITION_FADE, + }; + PopupTransition transition; bool opened; bool moving; @@ -110,54 +77,10 @@ struct Popup int getAlpha(int maxAlpha=FRACUNIT); }; -//SBarInfo -struct SBarInfoBlock -{ - TArray commands; - bool forceScaled; - bool fullScreenOffsets; - int alpha; - - SBarInfoBlock(); -}; - -struct SBarInfoCommand -{ - SBarInfoCommand(); - ~SBarInfoCommand(); - void setString(FScanner &sc, const char* source, int strnum, int maxlength=-1, bool exact=false); - - int type; - int special; - union - { - int special2; - SBarInfoCoordinate sbcoord2; - }; - union - { - int special3; - SBarInfoCoordinate sbcoord3; - }; - int special4; - int flags; - SBarInfoCoordinate x; - SBarInfoCoordinate y; - int value; - int image_index; - FTextureID sprite_index; - FString string[2]; - FFont *font; - EColorRange translation; - EColorRange translation2; - EColorRange translation3; - SBarInfoBlock subBlock; //for type SBarInfo_CMD_GAMEMODE -}; - struct SBarInfo { TArray Images; - SBarInfoBlock huds[NUMHUDS]; + SBarInfoMainBlock *huds[NUMHUDS]; Popup popups[NUMPOPUPS]; bool automapbar; bool interpolateHealth; @@ -169,16 +92,14 @@ struct SBarInfo int armorInterpolationSpeed; int height; int gameType; + FMugShot MugShot; int GetGameType() { return gameType; } void ParseSBarInfo(int lump); - void ParseSBarInfoBlock(FScanner &sc, SBarInfoBlock &block); void ParseMugShotBlock(FScanner &sc, FMugShotState &state); - void getCoordinates(FScanner &sc, bool fullScreenOffsets, SBarInfoCoordinate &x, SBarInfoCoordinate &y); //retrieves the next two arguments as x and y. - int getSignedInteger(FScanner &sc); //returns a signed integer. + void ResetHuds(); int newImage(const char* patchname); void Init(); - EColorRange GetTranslation(FScanner &sc, const char* translation); SBarInfo(); SBarInfo(int lumpnum); ~SBarInfo(); @@ -190,243 +111,4 @@ struct SBarInfo #define SCRIPT_DEFAULT 1 extern SBarInfo *SBarInfoScript[2]; -// Enums used between the parser and the display -enum //gametype flags -{ - GAMETYPE_SINGLEPLAYER = 1, - GAMETYPE_COOPERATIVE = 2, - GAMETYPE_DEATHMATCH = 4, - GAMETYPE_TEAMGAME = 8, -}; - -enum //drawimage flags -{ - DRAWIMAGE_PLAYERICON = 0x1, - DRAWIMAGE_AMMO1 = 0x2, - DRAWIMAGE_AMMO2 = 0x4, - DRAWIMAGE_INVENTORYICON = 0x8, - DRAWIMAGE_TRANSLATABLE = 0x10, - DRAWIMAGE_WEAPONSLOT = 0x20, - DRAWIMAGE_SWITCHABLE_AND = 0x40, - DRAWIMAGE_INVULNERABILITY = 0x80, - DRAWIMAGE_OFFSET_CENTER = 0x100, - DRAWIMAGE_OFFSET_CENTERBOTTOM = 0x200, - DRAWIMAGE_ARMOR = 0x800, - DRAWIMAGE_WEAPONICON = 0x1000, - DRAWIMAGE_SIGIL = 0x2000, - DRAWIMAGE_KEYSLOT = 0x4000, - DRAWIMAGE_HEXENARMOR = 0x8000, - - DRAWIMAGE_OFFSET = DRAWIMAGE_OFFSET_CENTER|DRAWIMAGE_OFFSET_CENTERBOTTOM, -}; - -enum //drawnumber flags -{ - DRAWNUMBER_HEALTH = 0x1, - DRAWNUMBER_ARMOR = 0x2, - DRAWNUMBER_AMMO1 = 0x4, - DRAWNUMBER_AMMO2 = 0x8, - DRAWNUMBER_AMMO = 0x10, - DRAWNUMBER_AMMOCAPACITY = 0x20, - DRAWNUMBER_FRAGS = 0x40, - DRAWNUMBER_INVENTORY = 0x80, - DRAWNUMBER_KILLS = 0x100, - DRAWNUMBER_MONSTERS = 0x200, - DRAWNUMBER_ITEMS = 0x400, - DRAWNUMBER_TOTALITEMS = 0x800, - DRAWNUMBER_SECRETS = 0x1000, - DRAWNUMBER_TOTALSECRETS = 0x2000, - DRAWNUMBER_ARMORCLASS = 0x4000, - DRAWNUMBER_GLOBALVAR = 0x8000, - DRAWNUMBER_GLOBALARRAY = 0x10000, - DRAWNUMBER_FILLZEROS = 0x20000, - DRAWNUMBER_WHENNOTZERO = 0x40000, - DRAWNUMBER_POWERUPTIME = 0x80000, - DRAWNUMBER_DRAWSHADOW = 0x100000, - DRAWNUMBER_AIRTIME = 0x200000, -}; - -enum //drawbar flags (will go into special2) -{ - DRAWBAR_HORIZONTAL = 1, - DRAWBAR_REVERSE = 2, - DRAWBAR_COMPAREDEFAULTS = 4, -}; - -enum //drawselectedinventory flags -{ - DRAWSELECTEDINVENTORY_ALTERNATEONEMPTY = 0x1, - DRAWSELECTEDINVENTORY_ARTIFLASH = 0x2, - DRAWSELECTEDINVENTORY_ALWAYSSHOWCOUNTER = 0x4, - DRAWSELECTEDINVENTORY_CENTER = 0x8, - DRAWSELECTEDINVENTORY_CENTERBOTTOM = 0x10, - DRAWSELECTEDINVENTORY_DRAWSHADOW = 0x20, -}; - -enum //drawinventorybar flags -{ - DRAWINVENTORYBAR_ALWAYSSHOW = 0x1, - DRAWINVENTORYBAR_NOARTIBOX = 0x2, - DRAWINVENTORYBAR_NOARROWS = 0x4, - DRAWINVENTORYBAR_ALWAYSSHOWCOUNTER = 0x8, - DRAWINVENTORYBAR_TRANSLUCENT = 0x10, - DRAWINVENTORYBAR_VERTICAL = 0x20, -}; - -enum //drawgem flags -{ - DRAWGEM_WIGGLE = 1, - DRAWGEM_TRANSLATABLE = 2, - DRAWGEM_ARMOR = 4, - DRAWGEM_REVERSE = 8, -}; - -enum //drawshader flags -{ - DRAWSHADER_VERTICAL = 1, - DRAWSHADER_REVERSE = 2, -}; - -enum //drawmugshot flags -{ - DRAWMUGSHOT_XDEATHFACE = 0x1, - DRAWMUGSHOT_ANIMATEDGODMODE = 0x2, - DRAWMUGSHOT_DISABLEGRIN = 0x4, - DRAWMUGSHOT_DISABLEOUCH = 0x8, - DRAWMUGSHOT_DISABLEPAIN = 0x10, - DRAWMUGSHOT_DISABLERAMPAGE = 0x20, -}; - -enum //drawkeybar flags -{ - DRAWKEYBAR_VERTICAL = 0x1, - DRAWKEYBAR_REVERSEROWS = 0x2, -}; - -enum //event flags -{ - SBARINFOEVENT_NOT = 1, - SBARINFOEVENT_OR = 2, - SBARINFOEVENT_AND = 4, -}; - -enum //aspect ratios -{ - ASPECTRATIO_4_3 = 0, - ASPECTRATIO_16_9 = 1, - ASPECTRATIO_16_10 = 2, - ASPECTRATIO_5_4 = 4, -}; - -enum //Key words -{ - SBARINFO_BASE, - SBARINFO_HEIGHT, - SBARINFO_INTERPOLATEHEALTH, - SBARINFO_INTERPOLATEARMOR, - SBARINFO_COMPLETEBORDER, - SBARINFO_MONOSPACEFONTS, - SBARINFO_LOWERHEALTHCAP, - SBARINFO_STATUSBAR, - SBARINFO_MUGSHOT, - SBARINFO_CREATEPOPUP, -}; - -enum //Bar types -{ - STBAR_NONE, - STBAR_FULLSCREEN, - STBAR_NORMAL, - STBAR_AUTOMAP, - STBAR_INVENTORY, - STBAR_INVENTORYFULLSCREEN, - STBAR_POPUPLOG, - STBAR_POPUPKEYS, - STBAR_POPUPSTATUS, -}; - -enum //Bar key words -{ - SBARINFO_DRAWIMAGE, - SBARINFO_DRAWNUMBER, - SBARINFO_DRAWSWITCHABLEIMAGE, - SBARINFO_DRAWMUGSHOT, - SBARINFO_DRAWSELECTEDINVENTORY, - SBARINFO_DRAWINVENTORYBAR, - SBARINFO_DRAWBAR, - SBARINFO_DRAWGEM, - SBARINFO_DRAWSHADER, - SBARINFO_DRAWSTRING, - SBARINFO_DRAWKEYBAR, - SBARINFO_GAMEMODE, - SBARINFO_PLAYERCLASS, - SBARINFO_ASPECTRATIO, - SBARINFO_ISSELECTED, - SBARINFO_USESAMMO, - SBARINFO_USESSECONDARYAMMO, - SBARINFO_HASWEAPONPIECE, - SBARINFO_INVENTORYBARNOTVISIBLE, - SBARINFO_WEAPONAMMO, - SBARINFO_ININVENTORY, -}; - -//All this so I can change the mugshot state in ACS... -class FBarShader : public FTexture -{ -public: - FBarShader(bool vertical, bool reverse); - const BYTE *GetColumn(unsigned int column, const Span **spans_out); - const BYTE *GetPixels(); - void Unload(); -private: - BYTE Pixels[512]; - Span DummySpan[2]; -}; - -class DSBarInfo : public DBaseStatusBar -{ - DECLARE_CLASS(DSBarInfo, DBaseStatusBar) -public: - DSBarInfo(SBarInfo *script=NULL); - ~DSBarInfo(); - void Draw(EHudState state); - void NewGame(); - void AttachToPlayer(player_t *player); - void Tick(); - void ReceivedWeapon (AWeapon *weapon); - void FlashItem(const PClass *itemtype); - void ShowPop(int popnum); - void SetMugShotState(const char* stateName, bool waitTillDone=false, bool reset=false); -private: - void doCommands(SBarInfoBlock &block, int xOffset=0, int yOffset=0, int alpha=FRACUNIT); - void DrawGraphic(FTexture* texture, SBarInfoCoordinate x, SBarInfoCoordinate y, int xOffset, int yOffset, int alpha, bool fullScreenOffsets, bool translate=false, bool dim=false, int offsetflags=0); - void DrawString(const char* str, SBarInfoCoordinate x, SBarInfoCoordinate y, int xOffset, int yOffset, int alpha, bool fullScreenOffsets, EColorRange translation, int spacing=0, bool drawshadow=false); - void DrawNumber(int num, int len, SBarInfoCoordinate x, SBarInfoCoordinate y, int xOffset, int yOffset, int alpha, bool fullScreenOffsets, EColorRange translation, int spacing=0, bool fillzeros=false, bool drawshadow=false); - void DrawFace(const char *defaultFace, int accuracy, int stateflags, SBarInfoCoordinate x, SBarInfoCoordinate y, int xOffset, int yOffset, int alpha, bool fullScreenOffsets); - int updateState(bool xdth, bool animatedgodmode); - void DrawInventoryBar(int type, int num, SBarInfoCoordinate x, SBarInfoCoordinate y, int xOffset, int yOffset, int alpha, bool fullScreenOffsets, bool alwaysshow, - SBarInfoCoordinate counterx, SBarInfoCoordinate countery, EColorRange translation, bool drawArtiboxes, bool noArrows, bool alwaysshowcounter, int bgalpha, bool vertical); - void DrawGem(FTexture* chain, FTexture* gem, int value, SBarInfoCoordinate x, SBarInfoCoordinate y, int xOffset, int yOffset, int alpha, bool fullScreenOffsets, int padleft, int padright, int chainsize, - bool wiggle, bool translate); - FRemapTable* getTranslation(); - - SBarInfo *script; - FImageCollection Images; - FPlayerSkin *oldSkin; - FFont *drawingFont; - int oldHealth; - int oldArmor; - int mugshotHealth; - int chainWiggle; - int artiflash; - int pendingPopup; - int currentPopup; - unsigned int invBarOffset; - FBarShader shader_horz_normal; - FBarShader shader_horz_reverse; - FBarShader shader_vert_normal; - FBarShader shader_vert_reverse; - FMugShot MugShot; -}; - #endif //__SBarInfo_SBAR_H__ diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp new file mode 100644 index 000000000..ebc6dad46 --- /dev/null +++ b/src/g_shared/sbarinfo_commands.cpp @@ -0,0 +1,2605 @@ +/* +** sbarinfo_commands.cpp +** +** This is an extension to the sbarinfo.cpp file. This contains all of the +** commands. +**--------------------------------------------------------------------------- +** Copyright 2008 Braden Obrzut +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +// [BL] Do note that this file is included by sbarinfo.cpp. This is done so +// that all the code can be more or less in one spot. +// +// At the bottom of this file is a function that belongs to +// SBarInfoCommandFlowControl which creates the instances of the following +// classes. +//////////////////////////////////////////////////////////////////////////////// + +class CommandDrawImage : public SBarInfoCommand +{ + public: + CommandDrawImage(SBarInfo *script) : SBarInfoCommand(script), + translatable(false), type(NORMAL_IMAGE), image(-1), offset(static_cast (TOP|LEFT)), + texture(NULL), alpha(FRACUNIT) + { + } + + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) + { + if(texture == NULL) + return; + + // We must calculate this per frame in order to prevent glitches with cl_capfps true. + fixed_t frameAlpha = block->Alpha(); + if(alpha != FRACUNIT) + frameAlpha = fixed_t(((double) block->Alpha() / (double) FRACUNIT) * ((double) alpha / (double) OPAQUE) * FRACUNIT); + + statusBar->DrawGraphic(texture, imgx, imgy, block->XOffset(), block->YOffset(), frameAlpha, block->FullScreenOffsets(), + translatable, false, offset); + } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + bool getImage = true; + if(sc.CheckToken(TK_Identifier)) + { + getImage = false; + if(sc.Compare("playericon")) + type = PLAYERICON; + else if(sc.Compare("ammoicon1")) + type = AMMO1; + else if(sc.Compare("ammoicon2")) + type = AMMO2; + else if(sc.Compare("armoricon")) + type = ARMOR; + else if(sc.Compare("weaponicon")) + type = WEAPONICON; + else if(sc.Compare("sigil")) + type = SIGIL; + else if(sc.Compare("hexenarmor")) + { + sc.MustGetToken(TK_Identifier); + if(sc.Compare("armor")) + type = HEXENARMOR_ARMOR; + else if(sc.Compare("shield")) + type = HEXENARMOR_SHIELD; + else if(sc.Compare("helm")) + type = HEXENARMOR_HELM; + else if(sc.Compare("amulet")) + type = HEXENARMOR_AMULET; + else + sc.ScriptError("Unkown armor type: '%s'", sc.String); + sc.MustGetToken(','); + getImage = true; + } + else if(sc.Compare("translatable")) + { + translatable = true; + getImage = true; + } + else + { + type = INVENTORYICON; + const PClass* item = PClass::FindClass(sc.String); + if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of Inventory + { + sc.ScriptError("'%s' is not a type of inventory item.", sc.String); + } + sprite = ((AInventory *)GetDefaultByType(item))->Icon; + image = -1; + } + } + if(getImage) + { + sc.MustGetToken(TK_StringConst); + image = script->newImage(sc.String); + sprite.SetInvalid(); + } + sc.MustGetToken(','); + GetCoordinates(sc, fullScreenOffsets, imgx, imgy); + if(sc.CheckToken(',')) + { + sc.MustGetToken(TK_Identifier); + if(sc.Compare("center")) + offset = CENTER; + else if(sc.Compare("centerbottom")) + offset = static_cast (HMIDDLE|BOTTOM); + else + sc.ScriptError("'%s' is not a valid alignment.", sc.String); + } + sc.MustGetToken(';'); + } + void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) + { + texture = NULL; + alpha = FRACUNIT; + if(type == PLAYERICON) + texture = TexMan[statusBar->CPlayer->mo->ScoreIcon]; + else if(type == AMMO1) + { + if(statusBar->ammo1 != NULL) + texture = TexMan[statusBar->ammo1->Icon]; + } + else if(type == AMMO2) + { + if(statusBar->ammo2 != NULL) + texture = TexMan[statusBar->ammo2->Icon]; + } + else if(type == ARMOR) + { + if(statusBar->armor != NULL && statusBar->armor->Amount != 0) + texture = TexMan(statusBar->armor->Icon); + } + else if(type == WEAPONICON) + { + AWeapon *weapon = statusBar->CPlayer->ReadyWeapon; + if(weapon != NULL && weapon->Icon.isValid()) + { + texture = TexMan[weapon->Icon]; + } + } + else if(type == SIGIL) + { + AInventory *item = statusBar->CPlayer->mo->FindInventory(); + if (item != NULL) + texture = TexMan[item->Icon]; + } + else if(type == HEXENARMOR_ARMOR || type == HEXENARMOR_SHIELD || type == HEXENARMOR_HELM || type == HEXENARMOR_AMULET) + { + int armorType = type - HEXENARMOR_ARMOR; + + AHexenArmor *harmor = statusBar->CPlayer->mo->FindInventory(); + if (harmor != NULL) + { + if (harmor->Slots[armorType] > 0 && harmor->SlotsIncrement[armorType] > 0) + { + //combine the alpha values + alpha = fixed_t(((double) alpha / (double) FRACUNIT) * ((double) MIN (OPAQUE, Scale(harmor->Slots[armorType], OPAQUE, harmor->SlotsIncrement[armorType])) / (double) OPAQUE) * FRACUNIT); + texture = statusBar->Images[image]; + } + else + return; + } + } + else if(type == INVENTORYICON) + texture = TexMan[sprite]; + else if(type == SELECTEDINVENTORYICON && statusBar->CPlayer->mo->InvSel != NULL) + texture = TexMan(statusBar->CPlayer->mo->InvSel->Icon); + else if(image >= 0) + texture = statusBar->Images[image]; + } + protected: + enum ImageType + { + PLAYERICON, + AMMO1, + AMMO2, + ARMOR, + WEAPONICON, + SIGIL, + HEXENARMOR_ARMOR, + HEXENARMOR_SHIELD, + HEXENARMOR_HELM, + HEXENARMOR_AMULET, + INVENTORYICON, + WEAPONSLOT, + SELECTEDINVENTORYICON, + + NORMAL_IMAGE + }; + + bool translatable; + ImageType type; + int image; + FTextureID sprite; + // I'm using imgx/imgy here so that I can inherit drawimage with drawnumber for some commands. + SBarInfoCoordinate imgx; + SBarInfoCoordinate imgy; + Offset offset; + + FTexture *texture; + int alpha; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CommandDrawSwitchableImage : public CommandDrawImage +{ + private: + enum Operator + { + EQUAL, + LESS, + GREATER, + LESSOREQUAL, + GREATEROREQUAL, + NOTEQUAL + }; + + static void GetOperation(FScanner &sc, Operator &op, int &value) + { + if(sc.CheckToken(TK_Eq)) + op = EQUAL; + else if(sc.CheckToken('<')) + op = LESS; + else if(sc.CheckToken('>')) + op = GREATER; + else if(sc.CheckToken(TK_Leq)) + op = LESSOREQUAL; + else if(sc.CheckToken(TK_Geq)) + op = GREATEROREQUAL; + else if(sc.CheckToken(TK_Neq)) + op = NOTEQUAL; + else + { // Default + op = GREATER; + value = 0; + return; + } + sc.MustGetToken(TK_IntConst); + value = sc.Number; + } + static bool EvaluateOperation(const Operator &op, const int &value, const int &compare) + { + switch(op) + { + case EQUAL: + return compare == value; + case LESS: + return compare < value; + case GREATER: + default: + return compare > value; + case LESSOREQUAL: + return compare <= value; + case GREATEROREQUAL: + return compare >= value; + case NOTEQUAL: + return compare != value; + } + } + + public: + CommandDrawSwitchableImage(SBarInfo *script) : CommandDrawImage(script), + condition(INVENTORY), conditionAnd(false) + { + conditionalImage[0] = conditionalImage[1] = conditionalImage[2] = -1; + conditionalValue[0] = conditionalValue[1] = 0; + armorType[0] = armorType[1] = 0; + } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + sc.MustGetToken(TK_Identifier); + if(sc.Compare("weaponslot")) + { + condition = WEAPONSLOT; + sc.MustGetToken(TK_IntConst); + conditionalValue[0] = sc.Number; + } + else if(sc.Compare("invulnerable")) + { + condition = INVULNERABILITY; + } + else if(sc.Compare("keyslot")) + { + condition = KEYSLOT; + sc.MustGetToken(TK_IntConst); + conditionalValue[0] = sc.Number; + } + else if(sc.Compare("armortype")) + { + condition = ARMORTYPE; + sc.MustGetToken(TK_Identifier); + armorType[0] = FName(sc.String).GetIndex(); + GetOperation(sc, conditionalOperator[0], conditionalValue[0]); + } + else + { + inventoryItem[0] = sc.String; + const PClass* item = PClass::FindClass(sc.String); + if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of Inventory + { + sc.ScriptError("'%s' is not a type of inventory item.", sc.String); + } + GetOperation(sc, conditionalOperator[0], conditionalValue[0]); + } + if(sc.CheckToken(TK_AndAnd) && condition != INVULNERABILITY) + { + conditionAnd = true; + if(condition == KEYSLOT || condition == WEAPONSLOT) + { + sc.MustGetToken(TK_IntConst); + conditionalValue[1] = sc.Number; + } + else if(condition == ARMORTYPE) + { + sc.MustGetToken(TK_Identifier); + armorType[1] = FName(sc.String).GetIndex(); + GetOperation(sc, conditionalOperator[1], conditionalValue[1]); + } + else + { + sc.MustGetToken(TK_Identifier); + inventoryItem[1] = sc.String; + const PClass* item = PClass::FindClass(sc.String); + if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of Inventory + { + sc.ScriptError("'%s' is not a type of inventory item.", sc.String); + } + GetOperation(sc, conditionalOperator[1], conditionalValue[1]); + } + } + // [BL] I have word that MSVC++ wants this static_cast ;) Shut up MSVC! + for(unsigned int i = 0;i < static_cast (conditionAnd ? 3 : 1);i++) + { + sc.MustGetToken(','); + sc.MustGetToken(TK_StringConst); + conditionalImage[i] = script->newImage(sc.String); + } + sc.MustGetToken(','); + CommandDrawImage::Parse(sc, fullScreenOffsets); + } + void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) + { + // DrawSwitchable image allows 2 or 4 images to be supplied. + // drawAlt toggles these: + // 1 = first image + // 2 = second image + // 3 = thrid image + // 0 = last image + int drawAlt = 0; + if(condition == WEAPONSLOT) //weaponslots + { + drawAlt = 1; //draw off state until we know we have something. + for (int i = 0; i < statusBar->CPlayer->weapons.Slots[conditionalValue[0]].Size(); i++) + { + const PClass *weap = statusBar->CPlayer->weapons.Slots[conditionalValue[0]].GetWeapon(i); + if(weap == NULL) + { + continue; + } + else if(statusBar->CPlayer->mo->FindInventory(weap) != NULL) + { + drawAlt = 0; + break; + } + } + } + else if(condition == INVULNERABILITY) + { + if(statusBar->CPlayer->cheats&CF_GODMODE) + { + drawAlt = 1; + } + } + else if(condition == KEYSLOT) + { + bool found1 = false; + bool found2 = false; + drawAlt = 1; + + for(AInventory *item = statusBar->CPlayer->mo->Inventory;item != NULL;item = item->Inventory) + { + if(item->IsKindOf(RUNTIME_CLASS(AKey))) + { + int keynum = static_cast(item)->KeyNumber; + + if(keynum == conditionalValue[0]) + found1 = true; + if(conditionAnd && keynum == conditionalValue[1]) // two keys + found2 = true; + } + } + + if(conditionAnd) + { + if(found1 && found2) + drawAlt = 0; + else if(found1) + drawAlt = 2; + else if(found2) + drawAlt = 3; + } + else + { + if(found1) + drawAlt = 0; + } + } + else if(condition == ARMORTYPE) + { + ABasicArmor *armor = (ABasicArmor *) statusBar->CPlayer->mo->FindInventory(NAME_BasicArmor); + if(armor != NULL) + { + bool matches1 = armor->ArmorType.GetIndex() == armorType[0] && EvaluateOperation(conditionalOperator[0], conditionalValue[0], armor->Amount); + bool matches2 = armor->ArmorType.GetIndex() == armorType[1] && EvaluateOperation(conditionalOperator[1], conditionalValue[1], armor->Amount); + + drawAlt = 1; + if(conditionAnd) + { + if(matches1 && matches2) + drawAlt = 0; + else if(matches2) + drawAlt = 3; + else if(matches1) + drawAlt = 2; + } + else if(matches1) + drawAlt = 0; + } + } + else //check the inventory items and draw selected sprite + { + AInventory* item = statusBar->CPlayer->mo->FindInventory(PClass::FindClass(inventoryItem[0])); + if(item == NULL || !EvaluateOperation(conditionalOperator[0], conditionalValue[0], item->Amount)) + drawAlt = 1; + if(conditionAnd) + { + item = statusBar->CPlayer->mo->FindInventory(PClass::FindClass(inventoryItem[1])); + bool secondCondition = item != NULL && EvaluateOperation(conditionalOperator[1], conditionalValue[1], item->Amount); + if((item != NULL && secondCondition) && drawAlt == 0) //both + { + drawAlt = 0; + } + else if((item != NULL && secondCondition) && drawAlt == 1) //2nd + { + drawAlt = 3; + } + else if((item == NULL || !secondCondition) && drawAlt == 0) //1st + { + drawAlt = 2; + } + } + } + if(drawAlt != 0) //draw 'off' image + { + texture = statusBar->Images[conditionalImage[drawAlt-1]]; + + // Since we're not going to call our parent's tick() method, + // be sure to set the alpha value properly. + alpha = FRACUNIT; + return; + } + CommandDrawImage::Tick(block, statusBar, hudChanged); + } + + private: + enum Condition + { + WEAPONSLOT, + INVULNERABILITY, + KEYSLOT, + ARMORTYPE, + + INVENTORY + }; + + Condition condition; + bool conditionAnd; + int conditionalImage[3]; + int conditionalValue[2]; + Operator conditionalOperator[2]; + FString inventoryItem[2]; + int armorType[2]; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CommandDrawString : public SBarInfoCommand +{ + public: + CommandDrawString(SBarInfo *script) : SBarInfoCommand(script), + shadow(false), spacing(0), font(NULL), translation(CR_UNTRANSLATED) + { + } + + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) + { + statusBar->DrawString(font, str.GetChars(), x, y, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets(), translation, spacing, shadow); + } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + sc.MustGetToken(TK_Identifier); + font = V_GetFont(sc.String); + if(font == NULL) + sc.ScriptError("Unknown font '%s'.", sc.String); + sc.MustGetToken(','); + translation = GetTranslation(sc); + sc.MustGetToken(','); + sc.MustGetToken(TK_StringConst); + str = sc.String; + sc.MustGetToken(','); + GetCoordinates(sc, fullScreenOffsets, x, y); + if(sc.CheckToken(',')) //spacing + { + sc.MustGetToken(TK_IntConst); + spacing = sc.Number; + } + sc.MustGetToken(';'); + + if(script->spacingCharacter == '\0') + x -= static_cast (font->StringWidth(str)+(spacing * str.Len())); + else //monospaced, so just multiplay the character size + x -= static_cast ((font->GetCharWidth((int) script->spacingCharacter) + spacing) * str.Len()); + } + protected: + bool shadow; + int spacing; + FFont *font; + EColorRange translation; + SBarInfoCoordinate x; + SBarInfoCoordinate y; + FString str; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CommandDrawNumber : public CommandDrawString +{ + public: + CommandDrawNumber(SBarInfo *script) : CommandDrawString(script), + fillZeros(false), whenNotZero(false), interpolationSpeed(0), drawValue(0), + length(3), lowValue(-1), lowTranslation(CR_UNTRANSLATED), highValue(-1), + highTranslation(CR_UNTRANSLATED), value(CONSTANT), valueArgument(0), + inventoryItem(NULL) + { + } + + void Parse(FScanner &sc, bool fullScreenOffsets) + { + sc.MustGetToken(TK_IntConst); + length = sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_Identifier); + font = V_GetFont(sc.String); + if(font == NULL) + sc.ScriptError("Unknown font '%s'.", sc.String); + sc.MustGetToken(','); + normalTranslation = GetTranslation(sc); + sc.MustGetToken(','); + if(sc.CheckToken(TK_IntConst)) + { + value = CONSTANT; + valueArgument = sc.Number; + sc.MustGetToken(','); + } + else + { + sc.MustGetToken(TK_Identifier); + if(sc.Compare("health")) + value = HEALTH; + else if(sc.Compare("armor")) + value = ARMOR; + else if(sc.Compare("ammo1")) + value = AMMO1; + else if(sc.Compare("ammo2")) + value = AMMO2; + else if(sc.Compare("score")) + value = SCORE; + else if(sc.Compare("ammo")) //request the next string to be an ammo type + { + value = AMMO; + sc.MustGetToken(TK_Identifier); + inventoryItem = PClass::FindClass(sc.String); + if(inventoryItem == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(inventoryItem)) //must be a kind of ammo + { + sc.ScriptError("'%s' is not a type of ammo.", sc.String); + } + } + else if(sc.Compare("ammocapacity")) + { + value = AMMOCAPACITY; + sc.MustGetToken(TK_Identifier); + inventoryItem = PClass::FindClass(sc.String); + if(inventoryItem == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(inventoryItem)) //must be a kind of ammo + { + sc.ScriptError("'%s' is not a type of ammo.", sc.String); + } + } + else if(sc.Compare("frags")) + value = FRAGS; + else if(sc.Compare("kills")) + value = KILLS; + else if(sc.Compare("monsters")) + value = MONSTERS; + else if(sc.Compare("items")) + value = ITEMS; + else if(sc.Compare("totalitems")) + value = TOTALITEMS; + else if(sc.Compare("secrets")) + value = SECRETS; + else if(sc.Compare("totalsecrets")) + value = TOTALSECRETS; + else if(sc.Compare("armorclass")) + value = ARMORCLASS; + else if(sc.Compare("airtime")) + value = AIRTIME; + else if(sc.Compare("globalvar")) + { + value = GLOBALVAR; + sc.MustGetToken(TK_IntConst); + if(sc.Number < 0 || sc.Number >= NUM_GLOBALVARS) + sc.ScriptError("Global variable number out of range: %d", sc.Number); + valueArgument = sc.Number; + } + else if(sc.Compare("globalarray")) //acts like variable[playernumber()] + { + value = GLOBALARRAY; + sc.MustGetToken(TK_IntConst); + if(sc.Number < 0 || sc.Number >= NUM_GLOBALVARS) + sc.ScriptError("Global variable number out of range: %d", sc.Number); + valueArgument = sc.Number; + } + else if(sc.Compare("poweruptime")) + { + value = POWERUPTIME; + sc.MustGetToken(TK_Identifier); + inventoryItem = PClass::FindClass(sc.String); + if(inventoryItem == NULL || !PClass::FindClass("PowerupGiver")->IsAncestorOf(inventoryItem)) + { + sc.ScriptError("'%s' is not a type of PowerupGiver.", sc.String); + } + } + else + { + value = INVENTORY; + inventoryItem = PClass::FindClass(sc.String); + if(inventoryItem == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(inventoryItem)) //must be a kind of ammo + { + sc.ScriptError("'%s' is not a type of inventory item.", sc.String); + } + } + sc.MustGetToken(','); + } + while(sc.CheckToken(TK_Identifier)) + { + if(sc.Compare("fillzeros")) + fillZeros = true; + else if(sc.Compare("whennotzero")) + whenNotZero = true; + else if(sc.Compare("drawshadow")) + shadow = true; + else if(sc.Compare("interpolate")) + { + sc.MustGetToken('('); + sc.MustGetToken(TK_IntConst); + interpolationSpeed = sc.Number; + sc.MustGetToken(')'); + } + else + sc.ScriptError("Unknown flag '%s'.", sc.String); + if(!sc.CheckToken('|')) + sc.MustGetToken(','); + } + GetCoordinates(sc, fullScreenOffsets, startX, y); + if(sc.CheckToken(',')) + { + bool needsComma = false; + if(sc.CheckToken(TK_IntConst)) //font spacing + { + spacing = sc.Number; + needsComma = true; + } + if(!needsComma || sc.CheckToken(',')) //2nd coloring for "low-on" value + { + lowTranslation = GetTranslation(sc); + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + lowValue = sc.Number; + if(sc.CheckToken(',')) //3rd coloring for "high-on" value + { + highTranslation = GetTranslation(sc); + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + highValue = sc.Number; + } + } + } + sc.MustGetToken(';'); + + if(value == HEALTH) + interpolationSpeed = script->interpolateHealth ? script->interpolationSpeed : interpolationSpeed; + else if(value == ARMOR) + interpolationSpeed = script->interpolateArmor ? script->armorInterpolationSpeed : interpolationSpeed; + } + void Reset() + { + drawValue = 0; + } + void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) + { + int num = valueArgument; + switch(value) + { + case HEALTH: + num = statusBar->CPlayer->mo->health; + if(script->lowerHealthCap && num < 0) //health shouldn't display negatives + num = 0; + if(script->interpolateHealth) + interpolationSpeed = script->interpolationSpeed; + break; + case ARMOR: + num = statusBar->armor != NULL ? statusBar->armor->Amount : 0; + if(script->interpolateArmor) + interpolationSpeed = script->armorInterpolationSpeed; + break; + case AMMO1: + if(statusBar->ammo1 == NULL) //no ammo, do not draw + { + str = ""; + return; + } + num = statusBar->ammocount1; + break; + case AMMO2: + if(statusBar->ammo2 == NULL) //no ammo, do not draw + { + str = ""; + return; + } + num = statusBar->ammocount2; + break; + case AMMO: + { + AInventory* item = statusBar->CPlayer->mo->FindInventory(inventoryItem); + if(item != NULL) + num = item->Amount; + else + num = 0; + break; + } + case AMMOCAPACITY: + { + AInventory* item = statusBar->CPlayer->mo->FindInventory(inventoryItem); + if(item != NULL) + num = item->MaxAmount; + else + num = ((AInventory *)GetDefaultByType(inventoryItem))->MaxAmount; + break; + } + case FRAGS: + num = statusBar->CPlayer->fragcount; + break; + case KILLS: + num = level.killed_monsters; + break; + case MONSTERS: + num = level.total_monsters; + break; + case ITEMS: + num = level.found_items; + break; + case TOTALITEMS: + num = level.total_items; + break; + case SECRETS: + num = level.found_secrets; + break; + case SCORE: + num = statusBar->CPlayer->mo->Score; + break; + case TOTALSECRETS: + num = level.total_secrets; + break; + case ARMORCLASS: + { + AHexenArmor *harmor = statusBar->CPlayer->mo->FindInventory(); + if(harmor != NULL) + { + num = harmor->Slots[0] + harmor->Slots[1] + + harmor->Slots[2] + harmor->Slots[3] + harmor->Slots[4]; + } + //Hexen counts basic armor also so we should too. + if(statusBar->armor != NULL) + { + num += statusBar->armor->SavePercent; + } + num /= (5*FRACUNIT); + break; + } + case GLOBALVAR: + num = ACS_GlobalVars[valueArgument]; + break; + case GLOBALARRAY: + num = ACS_GlobalArrays[valueArgument][consoleplayer]; + break; + case POWERUPTIME: + { + //Get the PowerupType and check to see if the player has any in inventory. + const PClass* powerupType = ((APowerupGiver*) GetDefaultByType(inventoryItem))->PowerupType; + APowerup* powerup = (APowerup*) statusBar->CPlayer->mo->FindInventory(powerupType); + if(powerup != NULL) + num = powerup->EffectTics / TICRATE + 1; + break; + } + case INVENTORY: + { + AInventory* item = statusBar->CPlayer->mo->FindInventory(inventoryItem); + if(item != NULL) + num = item->Amount; + else + num = 0; + break; + } + case AIRTIME: + { + if(statusBar->CPlayer->mo->waterlevel < 3) + num = level.airsupply/TICRATE; + else + num = clamp((statusBar->CPlayer->air_finished - level.time + (TICRATE-1))/TICRATE, 0, INT_MAX); + break; + } + case SELECTEDINVENTORY: + if(statusBar->CPlayer->mo->InvSel != NULL) + num = statusBar->CPlayer->mo->InvSel->Amount; + break; + default: break; + } + if(interpolationSpeed != 0 && (!hudChanged || level.time == 1)) + { + if(num < drawValue) + drawValue -= clamp((drawValue - num) >> 2, 1, interpolationSpeed); + else if(drawValue < num) + drawValue += clamp((num - drawValue) >> 2, 1, interpolationSpeed); + } + else + drawValue = num; + if(whenNotZero && drawValue == 0) + { + str = ""; + return; + } + + translation = normalTranslation; + if(lowValue != -1 && drawValue <= lowValue) //low + translation = lowTranslation; + else if(highValue != -1 && drawValue >= highValue) //high + translation = highTranslation; + + x = startX; + + // 10^9 is a largest we can hold in a 32-bit int. So if we go any larger we have to toss out the positions limit. + int maxval = length <= 9 ? (int) ceil(pow(10., length))-1 : INT_MAX; + if(!fillZeros || length == 1) + drawValue = clamp(drawValue, -maxval, maxval); + else //The community wanted negatives to take the last digit, but we can only do this if there is room + drawValue = clamp(drawValue, length <= 9 ? (int) -(ceil(pow(10., length-1))-1) : INT_MIN, maxval); + str.Format("%d", drawValue); + if(fillZeros) + { + if(drawValue < 0) //We don't want the negative just yet + str.Format("%d", -drawValue); + while(str.Len() < (unsigned int) length) + { + if(drawValue < 0 && str.Len() == (unsigned int) (length-1)) + str.Insert(0, "-"); + else + str.Insert(0, "0"); + } + } + if(script->spacingCharacter == '\0') + x -= static_cast (font->StringWidth(str)+(spacing * str.Len())); + else //monospaced, so just multiplay the character size + x -= static_cast ((font->GetCharWidth((int) script->spacingCharacter) + spacing) * str.Len()); + } + protected: + enum ValueType + { + HEALTH, + ARMOR, + AMMO1, + AMMO2, + AMMO, + AMMOCAPACITY, + FRAGS, + INVENTORY, + KILLS, + MONSTERS, + ITEMS, + TOTALITEMS, + SECRETS, + TOTALSECRETS, + ARMORCLASS, + GLOBALVAR, + GLOBALARRAY, + POWERUPTIME, + AIRTIME, + SELECTEDINVENTORY, + SCORE, + + CONSTANT + }; + + bool fillZeros; + bool whenNotZero; + + int interpolationSpeed; + int drawValue; + + int length; + int lowValue; + EColorRange lowTranslation; + int highValue; + EColorRange highTranslation; + EColorRange normalTranslation; + ValueType value; + int valueArgument; + const PClass *inventoryItem; + + SBarInfoCoordinate startX; + + friend class CommandDrawInventoryBar; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CommandDrawMugShot : public SBarInfoCommand +{ + public: + CommandDrawMugShot(SBarInfo *script) : SBarInfoCommand(script), + accuracy(5), stateFlags(FMugShot::STANDARD) + { + } + + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) + { + FTexture *face = script->MugShot.GetFace(statusBar->CPlayer, defaultFace, accuracy, stateFlags); + if (face != NULL) + statusBar->DrawGraphic(face, x, y, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets()); + } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + if(sc.CheckToken(TK_StringConst)) + { + defaultFace = sc.String; + if(defaultFace.Len() > 3) + sc.ScriptError("Default can not be longer than 3 characters."); + sc.MustGetToken(','); + } + sc.MustGetToken(TK_IntConst); //accuracy + if(sc.Number < 1 || sc.Number > 9) + sc.ScriptError("Expected a number between 1 and 9, got %d instead.", sc.Number); + accuracy = sc.Number; + sc.MustGetToken(','); + while(sc.CheckToken(TK_Identifier)) + { + if(sc.Compare("xdeathface")) + stateFlags = static_cast (stateFlags|FMugShot::XDEATHFACE); + else if(sc.Compare("animatedgodmode")) + stateFlags = static_cast (stateFlags|FMugShot::ANIMATEDGODMODE); + else if(sc.Compare("disablegrin")) + stateFlags = static_cast (stateFlags|FMugShot::DISABLEGRIN); + else if(sc.Compare("disableouch")) + stateFlags = static_cast (stateFlags|FMugShot::DISABLEOUCH); + else if(sc.Compare("disablepain")) + stateFlags = static_cast (stateFlags|FMugShot::DISABLEPAIN); + else if(sc.Compare("disablerampage")) + stateFlags = static_cast (stateFlags|FMugShot::DISABLERAMPAGE); + else + sc.ScriptError("Unknown flag '%s'.", sc.String); + if(!sc.CheckToken('|')) + sc.MustGetToken(','); + } + + GetCoordinates(sc, fullScreenOffsets, x, y); + sc.MustGetToken(';'); + } + + protected: + FString defaultFace; //Deprecated + + int accuracy; + FMugShot::StateFlags stateFlags; + SBarInfoCoordinate x; + SBarInfoCoordinate y; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CommandDrawSelectedInventory : public SBarInfoCommandFlowControl, private CommandDrawImage, private CommandDrawNumber +{ + public: + CommandDrawSelectedInventory(SBarInfo *script) : SBarInfoCommandFlowControl(script), + CommandDrawImage(script), CommandDrawNumber(script), alternateOnEmpty(false), + artiflash(false), alwaysShowCounter(false) + { + } + + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) + { + if(statusBar->CPlayer->mo->InvSel != NULL && !(level.flags & LEVEL_NOINVENTORYBAR)) + { + if(artiflash && artiflashTick) + { + statusBar->DrawGraphic(statusBar->Images[ARTIFLASH_OFFSET+(4-artiflashTick)], imgx, imgy, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets(), + translatable, false, offset); + } + else + CommandDrawImage::Draw(block, statusBar); + if(alwaysShowCounter || statusBar->CPlayer->mo->InvSel->Amount != 1) + CommandDrawNumber::Draw(block, statusBar); + } + else if(alternateOnEmpty) + SBarInfoCommandFlowControl::Draw(block, statusBar); + } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + type = SELECTEDINVENTORYICON; + value = SELECTEDINVENTORY; + while(true) //go until we get a font (non-flag) + { + sc.MustGetToken(TK_Identifier); + if(sc.Compare("alternateonempty")) + alternateOnEmpty = true; + else if(sc.Compare("artiflash")) + artiflash = true; + else if(sc.Compare("alwaysshowcounter")) + alwaysShowCounter = true; + else if(sc.Compare("center")) + offset = CENTER; + else if(sc.Compare("centerbottom")) + offset = static_cast (HMIDDLE|BOTTOM); + else if(sc.Compare("drawshadow")) + shadow = true; + else + { + font = V_GetFont(sc.String); + if(font == NULL) + sc.ScriptError("Unknown font '%s'.", sc.String); + sc.MustGetToken(','); + break; + } + if(!sc.CheckToken('|')) + sc.MustGetToken(','); + } + CommandDrawImage::GetCoordinates(sc, fullScreenOffsets, imgx, imgy); + startX = imgx + 30; + y = imgy + 24; + normalTranslation = CR_GOLD; + if(sc.CheckToken(',')) //more font information + { + CommandDrawNumber::GetCoordinates(sc, fullScreenOffsets, startX, y); + if(sc.CheckToken(',')) + { + normalTranslation = CommandDrawNumber::GetTranslation(sc); + if(sc.CheckToken(',')) + { + sc.MustGetToken(TK_IntConst); + spacing = sc.Number; + } + } + } + if(alternateOnEmpty) + SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets); + else + sc.MustGetToken(';'); + } + void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) + { + if(artiflashTick > 0) + artiflashTick--; + + CommandDrawImage::Tick(block, statusBar, hudChanged); + CommandDrawNumber::Tick(block, statusBar, hudChanged); + } + + static void Flash() { artiflashTick = 4; } + protected: + bool alternateOnEmpty; + bool artiflash; + bool alwaysShowCounter; + + static int artiflashTick; +}; +int CommandDrawSelectedInventory::artiflashTick = 4; + +void DSBarInfo::FlashItem(const PClass *itemtype) +{ + CommandDrawSelectedInventory::Flash(); +} + +//////////////////////////////////////////////////////////////////////////////// + +class CommandGameMode : public SBarInfoCommandFlowControl +{ + public: + CommandGameMode(SBarInfo *script) : SBarInfoCommandFlowControl(script), + modes(static_cast (0)) + { + } + + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) + { + if((!multiplayer && (modes & SINGLEPLAYER)) || + (deathmatch && (modes & DEATHMATCH)) || + (multiplayer && !deathmatch && (modes & COOPERATIVE)) || + (teamplay && (modes & TEAMGAME))) + { + SBarInfoCommandFlowControl::Draw(block, statusBar); + } + } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + do + { + sc.MustGetToken(TK_Identifier); + modes |= static_cast (1<CPlayer->ReadyWeapon != NULL && (statusBar->CPlayer->ReadyWeapon->AmmoType1 != NULL || statusBar->CPlayer->ReadyWeapon->AmmoType2 != NULL)) ^ negate) + SBarInfoCommandFlowControl::Draw(block, statusBar); + } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + if(sc.CheckToken(TK_Identifier)) + { + if(sc.Compare("not")) + negate = true; + else + sc.ScriptError("Expected 'not', but got '%s' instead.", sc.String); + } + SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets); + } + protected: + bool negate; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CommandUsesSecondaryAmmo : public CommandUsesAmmo +{ + public: + CommandUsesSecondaryAmmo(SBarInfo *script) : CommandUsesAmmo(script) + { + } + + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) + { + if((statusBar->CPlayer->ReadyWeapon != NULL && statusBar->CPlayer->ReadyWeapon->AmmoType2 != NULL && statusBar->CPlayer->ReadyWeapon->AmmoType1 != statusBar->CPlayer->ReadyWeapon->AmmoType2) ^ negate) + SBarInfoCommandFlowControl::Draw(block, statusBar); + } +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CommandInventoryBarNotVisible : public SBarInfoCommandFlowControl +{ + public: + CommandInventoryBarNotVisible(SBarInfo *script) : SBarInfoCommandFlowControl(script) + { + } + + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) + { + if(statusBar->CPlayer->inventorytics <= 0 || (level.flags & LEVEL_NOINVENTORYBAR)) + SBarInfoCommandFlowControl::Draw(block, statusBar); + } +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CommandAspectRatio : public SBarInfoCommandFlowControl +{ + public: + CommandAspectRatio(SBarInfo *script) : SBarInfoCommandFlowControl(script), + ratio(ASPECTRATIO_4_3) + { + } + + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) + { + if(CheckRatio(screen->GetWidth(), screen->GetHeight()) == ratio) + SBarInfoCommandFlowControl::Draw(block, statusBar); + } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + sc.MustGetToken(TK_StringConst); + if(sc.Compare("4:3")) + ratio = ASPECTRATIO_4_3; + else if(sc.Compare("16:9")) + ratio = ASPECTRATIO_16_9; + else if(sc.Compare("16:10")) + ratio = ASPECTRATIO_16_10; + else if(sc.Compare("5:4")) + ratio = ASPECTRATIO_5_4; + else + sc.ScriptError("Unkown aspect ratio: %s", sc.String); + SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets); + } + protected: + enum Ratio + { + ASPECTRATIO_4_3 = 0, + ASPECTRATIO_16_9 = 1, + ASPECTRATIO_16_10 = 2, + ASPECTRATIO_5_4 = 4 + }; + + Ratio ratio; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CommandDrawShader : public SBarInfoCommand +{ + public: + CommandDrawShader(SBarInfo *script) : SBarInfoCommand(script), + vertical(false), reverse(false), width(1), height(1) + { + } + + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) + { + statusBar->DrawGraphic(&shaders[(vertical<<1) + reverse], x, y, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets(), false, false, 0, true, width, height); + } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + sc.MustGetToken(TK_IntConst); + width = sc.Number; + if(sc.Number < 1) + sc.ScriptError("Width must be greater than 1."); + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + height = sc.Number; + if(sc.Number < 1) + sc.ScriptError("Height must be greater than 1."); + sc.MustGetToken(','); + sc.MustGetToken(TK_Identifier); + if(sc.Compare("vertical")) + vertical = true; + else if(!sc.Compare("horizontal")) + sc.ScriptError("Unknown direction '%s'.", sc.String); + sc.MustGetToken(','); + if(sc.CheckToken(TK_Identifier)) + { + if(!sc.Compare("reverse")) + { + sc.ScriptError("Exspected 'reverse', got '%s' instead.", sc.String); + } + reverse = true; + sc.MustGetToken(','); + } + GetCoordinates(sc, fullScreenOffsets, x, y); + sc.MustGetToken(';'); + } + protected: + bool vertical; + bool reverse; + unsigned int width; + unsigned int height; + SBarInfoCoordinate x; + SBarInfoCoordinate y; + private: + class FBarShader : public FTexture + { + public: + FBarShader(bool vertical, bool reverse) + { + int i; + + Width = vertical ? 2 : 256; + Height = vertical ? 256 : 2; + CalcBitSize(); + + // Fill the column/row with shading values. + // Vertical shaders have have minimum alpha at the top + // and maximum alpha at the bottom, unless flipped by + // setting reverse to true. Horizontal shaders are just + // the opposite. + if (vertical) + { + if (!reverse) + { + for (i = 0; i < 256; ++i) + { + Pixels[i] = i; + Pixels[256+i] = i; + } + } + else + { + for (i = 0; i < 256; ++i) + { + Pixels[i] = 255 - i; + Pixels[256+i] = 255 -i; + } + } + } + else + { + if (!reverse) + { + for (i = 0; i < 256; ++i) + { + Pixels[i*2] = 255 - i; + Pixels[i*2+1] = 255 - i; + } + } + else + { + for (i = 0; i < 256; ++i) + { + Pixels[i*2] = i; + Pixels[i*2+1] = i; + } + } + } + DummySpan[0].TopOffset = 0; + DummySpan[0].Length = vertical ? 256 : 2; + DummySpan[1].TopOffset = 0; + DummySpan[1].Length = 0; + } + const BYTE *GetColumn(unsigned int column, const Span **spans_out) + { + if (spans_out != NULL) + { + *spans_out = DummySpan; + } + return Pixels + ((column & WidthMask) << HeightBits); + } + const BYTE *GetPixels() { return Pixels; } + void Unload() {} + private: + BYTE Pixels[512]; + Span DummySpan[2]; + }; + + static FBarShader shaders[4]; +}; + +CommandDrawShader::FBarShader CommandDrawShader::shaders[4] = +{ + FBarShader(false, false), FBarShader(false, true), + FBarShader(true, false), FBarShader(true, true) +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CommandDrawInventoryBar : public SBarInfoCommand +{ + public: + CommandDrawInventoryBar(SBarInfo *script) : SBarInfoCommand(script), + style(GAME_Doom), size(7), alwaysShow(false), noArtibox(false), + noArrows(false), alwaysShowCounter(false), translucent(false), + vertical(false), counters(NULL), font(NULL), translation(CR_GOLD), + fontSpacing(0) + { + } + ~CommandDrawInventoryBar() + { + if(counters != NULL) + { + for(unsigned int i = 0;i < size;i++) + delete counters[i]; + delete[] counters; + } + } + + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) + { + int spacing = 0; + if(!vertical) + spacing = (style != GAME_Strife) ? statusBar->Images[statusBar->invBarOffset + imgARTIBOX]->GetScaledWidth() + 1 : statusBar->Images[statusBar->invBarOffset + imgCURSOR]->GetScaledWidth() - 1; + else + spacing = (style != GAME_Strife) ? statusBar->Images[statusBar->invBarOffset + imgARTIBOX]->GetScaledHeight() + 1 : statusBar->Images[statusBar->invBarOffset + imgCURSOR]->GetScaledHeight() - 1; + + int bgalpha = block->Alpha(); + if(translucent) + bgalpha = fixed_t((((double) block->Alpha() / (double) FRACUNIT) * ((double) HX_SHADOW / (double) FRACUNIT)) * FRACUNIT); + + AInventory *item; + unsigned int i = 0; + // If the player has no artifacts, don't draw the bar + statusBar->CPlayer->mo->InvFirst = statusBar->ValidateInvFirst(size); + if(statusBar->CPlayer->mo->InvFirst != NULL || alwaysShow) + { + for(item = statusBar->CPlayer->mo->InvFirst, i = 0; item != NULL && i < size; item = item->NextInv(), ++i) + { + SBarInfoCoordinate rx = x + (!vertical ? i*spacing : 0); + SBarInfoCoordinate ry = y + (vertical ? i*spacing : 0); + if(!noArtibox) + statusBar->DrawGraphic(statusBar->Images[statusBar->invBarOffset + imgARTIBOX], rx, ry, block->XOffset(), block->YOffset(), bgalpha, block->FullScreenOffsets()); + + if(style != GAME_Strife) //Strife draws the cursor before the icons + statusBar->DrawGraphic(TexMan(item->Icon), rx, ry, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets(), false, item->Amount <= 0); + if(item == statusBar->CPlayer->mo->InvSel) + { + if(style == GAME_Heretic) + statusBar->DrawGraphic(statusBar->Images[statusBar->invBarOffset + imgSELECTBOX], rx, ry+29, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets()); + else if(style == GAME_Hexen) + statusBar->DrawGraphic(statusBar->Images[statusBar->invBarOffset + imgSELECTBOX], rx, ry-1, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets()); + else if(style == GAME_Strife) + statusBar->DrawGraphic(statusBar->Images[statusBar->invBarOffset + imgCURSOR], rx-6, ry-2, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets()); + else + statusBar->DrawGraphic(statusBar->Images[statusBar->invBarOffset + imgSELECTBOX], rx, ry, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets()); + } + if(style == GAME_Strife) + statusBar->DrawGraphic(TexMan(item->Icon), rx, ry, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets(), false, item->Amount <= 0); + if(counters != NULL && (alwaysShowCounter || item->Amount != 1)) + { + counters[i]->valueArgument = item->Amount; + counters[i]->Draw(block, statusBar); + } + } + for (; i < size && !noArtibox; ++i) + statusBar->DrawGraphic(statusBar->Images[statusBar->invBarOffset + imgARTIBOX], x + (!vertical ? (i*spacing) : 0), y + (vertical ? (i*spacing) : 0), block->XOffset(), block->YOffset(), bgalpha, block->FullScreenOffsets()); + + // Is there something to the left? + if (!noArrows && statusBar->CPlayer->mo->FirstInv() != statusBar->CPlayer->mo->InvFirst) + { + int offset = style != GAME_Strife ? -12 : 14; + statusBar->DrawGraphic(statusBar->Images[!(gametic & 4) ? + statusBar->invBarOffset + imgINVLFGEM1 : statusBar->invBarOffset + imgINVLFGEM2], x + (!vertical ? offset : 0), y + (vertical ? offset : 0), block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets()); + } + // Is there something to the right? + if (!noArrows && item != NULL) + { + int offset = style != GAME_Strife ? size*31+2 : size*35-4; + statusBar->DrawGraphic(statusBar->Images[!(gametic & 4) ? + statusBar->invBarOffset + imgINVRTGEM1 : statusBar->invBarOffset + imgINVRTGEM2], x + (!vertical ? offset : 0), y + (vertical ? offset : 0), block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets()); + } + } + } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + sc.MustGetToken(TK_Identifier); + if(sc.Compare("Doom")) + style = GAME_Doom; + else if(sc.Compare("Heretic")) + style = GAME_Heretic; + else if(sc.Compare("Hexen")) + style = GAME_Hexen; + else if(sc.Compare("Strife")) + style = GAME_Strife; + else + sc.ScriptError("Unknown style '%s'.", sc.String); + + sc.MustGetToken(','); + while(sc.CheckToken(TK_Identifier)) + { + if(sc.Compare("alwaysshow")) + alwaysShow = true; + else if(sc.Compare("noartibox")) + noArtibox = true; + else if(sc.Compare("noarrows")) + noArrows = true; + else if(sc.Compare("alwaysshowcounter")) + alwaysShowCounter = true; + else if(sc.Compare("translucent")) + translucent = true; + else if(sc.Compare("vertical")) + vertical = true; + else + sc.ScriptError("Unknown flag '%s'.", sc.String); + if(!sc.CheckToken('|')) + sc.MustGetToken(','); + } + sc.MustGetToken(TK_IntConst); + size = sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_Identifier); + font = V_GetFont(sc.String); + if(font == NULL) + sc.ScriptError("Unknown font '%s'.", sc.String); + + sc.MustGetToken(','); + GetCoordinates(sc, fullScreenOffsets, x, y); + counterX = x + 26; + counterY = y + 22; + if(sc.CheckToken(',')) //more font information + { + GetCoordinates(sc, fullScreenOffsets, counterX, counterY); + if(sc.CheckToken(',')) + { + translation = GetTranslation(sc); + if(sc.CheckToken(',')) + { + sc.MustGetToken(TK_IntConst); + fontSpacing = sc.Number; + } + } + } + sc.MustGetToken(';'); + } + void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) + { + // Make the counters if need be. + if(counters == NULL) + { + int spacing = 0; + if(!vertical) + spacing = (style != GAME_Strife) ? statusBar->Images[statusBar->invBarOffset + imgARTIBOX]->GetScaledWidth() + 1 : statusBar->Images[statusBar->invBarOffset + imgCURSOR]->GetScaledWidth() - 1; + else + spacing = (style != GAME_Strife) ? statusBar->Images[statusBar->invBarOffset + imgARTIBOX]->GetScaledHeight() + 1 : statusBar->Images[statusBar->invBarOffset + imgCURSOR]->GetScaledHeight() - 1; + counters = new CommandDrawNumber*[size]; + + for(unsigned int i = 0;i < size;i++) + { + counters[i] = new CommandDrawNumber(script); + + counters[i]->startX = counterX + (!vertical ? spacing*i : 0); + counters[i]->y = counterY + (vertical ? spacing*i : 0); + counters[i]->normalTranslation = translation; + counters[i]->font = font; + counters[i]->spacing = fontSpacing; + counters[i]->whenNotZero = !alwaysShowCounter; + counters[i]->drawValue = counters[i]->value = CommandDrawNumber::CONSTANT; + counters[i]->length = 3; + } + } + + for(unsigned int i = 0;i < size;i++) + counters[i]->Tick(block, statusBar, hudChanged); + } + protected: + int style; + unsigned int size; + bool alwaysShow; + bool noArtibox; + bool noArrows; + bool alwaysShowCounter; + bool translucent; + bool vertical; + SBarInfoCoordinate x; + SBarInfoCoordinate y; + CommandDrawNumber* *counters; + private: + // This information will be transferred to counters as soon as possible. + FFont *font; + SBarInfoCoordinate counterX; + SBarInfoCoordinate counterY; + EColorRange translation; + int fontSpacing; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CommandDrawKeyBar : public SBarInfoCommand +{ + public: + CommandDrawKeyBar(SBarInfo *script) : SBarInfoCommand(script), + number(3), vertical(false), reverseRows(false), iconSize(-1), + rowIconSize(-1), keyOffset(0), rowSize(0) + { + } + + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) + { + AInventory *item = statusBar->CPlayer->mo->Inventory; + if(item == NULL) + return; + int slotOffset = 0; + int rowOffset = 0; + int rowWidth = 0; + for(unsigned int i = 0;i < number+keyOffset;i++) + { + while(!item->Icon.isValid() || !item->IsKindOf(RUNTIME_CLASS(AKey))) + { + item = item->Inventory; + if(item == NULL) + return; + } + if(i >= keyOffset) //Should we start drawing? + { + if(!vertical) + { + statusBar->DrawGraphic(TexMan[item->Icon], x+slotOffset, y+rowOffset, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets()); + rowWidth = rowIconSize == -1 ? TexMan[item->Icon]->GetScaledHeight()+2 : rowIconSize; + } + else + { + statusBar->DrawGraphic(TexMan[item->Icon], x+rowOffset, y+slotOffset, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets()); + rowWidth = rowIconSize == -1 ? TexMan[item->Icon]->GetScaledWidth()+2 : rowIconSize; + } + + // If cmd.special is -1 then the slot size is auto detected + if(iconSize == -1) + { + if(!vertical) + slotOffset += TexMan[item->Icon]->GetScaledWidth() + 2; + else + slotOffset += TexMan[item->Icon]->GetScaledHeight() + 2; + } + else + slotOffset += iconSize; + + if(rowSize > 0 && (i % rowSize == rowSize-1)) + { + if(reverseRows) + rowOffset -= rowWidth; + else + rowOffset += rowWidth; + rowWidth = 0; + slotOffset = 0; + } + } + + item = item->Inventory; + if(item == NULL) + return; + } + } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + sc.MustGetToken(TK_IntConst); + number = sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_Identifier); + if(sc.Compare("vertical")) + vertical = true; + else if(!sc.Compare("horizontal")) + sc.ScriptError("Unknown direction '%s'.", sc.String); + sc.MustGetToken(','); + while(sc.CheckToken(TK_Identifier)) + { + if(sc.Compare("reverserows")) + reverseRows = true; + else + sc.ScriptError("Unknown flag '%s'.", sc.String); + if(!sc.CheckToken('|')) + sc.MustGetToken(','); + } + if(sc.CheckToken(TK_Auto)) + iconSize = -1; + else + { + sc.MustGetToken(TK_IntConst); + iconSize = sc.Number; + } + sc.MustGetToken(','); + GetCoordinates(sc, fullScreenOffsets, x, y); + if(sc.CheckToken(',')) + { + //key offset + sc.MustGetToken(TK_IntConst); + keyOffset = sc.Number; + if(sc.CheckToken(',')) + { + //max per row/column + sc.MustGetToken(TK_IntConst); + rowSize = sc.Number; + sc.MustGetToken(','); + //row/column spacing (opposite of previous) + if(sc.CheckToken(TK_Auto)) + rowIconSize = -1; + else + { + sc.MustGetToken(TK_IntConst); + rowIconSize = sc.Number; + } + } + } + sc.MustGetToken(';'); + } + protected: + unsigned int number; + bool vertical; + bool reverseRows; + int iconSize; + int rowIconSize; + unsigned int keyOffset; + unsigned int rowSize; + SBarInfoCoordinate x; + SBarInfoCoordinate y; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CommandDrawBar : public SBarInfoCommand +{ + public: + CommandDrawBar(SBarInfo *script) : SBarInfoCommand(script), + border(0), horizontal(false), reverse(false), foreground(-1), background(-1), + type(HEALTH), inventoryItem(NULL), interpolationSpeed(0), drawValue(0) + { + } + + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) + { + if(foreground == -1 || statusBar->Images[foreground] == NULL) + return; //don't draw anything. + assert(statusBar->Images[foreground] != NULL); + + FTexture *fg = statusBar->Images[foreground]; + FTexture *bg = (background != -1) ? statusBar->Images[background] : NULL; + + if(border != 0) + { + //Draw the whole foreground + statusBar->DrawGraphic(fg, this->x, this->y, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets()); + } + else + { + // Draw background + if (bg != NULL && bg->GetScaledWidth() == fg->GetScaledWidth() && bg->GetScaledHeight() == fg->GetScaledHeight()) + statusBar->DrawGraphic(bg, this->x, this->y, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets()); + else + statusBar->DrawGraphic(fg, this->x, this->y, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets(), false, false, 0, false, -1, -1, 0, 0, 0, 0, true); + } + + // {cx, cy, cr, cb} + fixed_t clip[4] = {0, 0, 0, 0}; + + fixed_t sizeOfImage = (horizontal ? fg->GetScaledWidth()-border*2 : fg->GetScaledHeight()-border*2)<GetScaledWidth() == fg->GetScaledWidth() && bg->GetScaledHeight() == fg->GetScaledHeight()) + statusBar->DrawGraphic(bg, this->x, this->y, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets(), false, false, 0, false, -1, -1, clip[0], clip[1], clip[2], clip[3]); + else + statusBar->DrawGraphic(fg, this->x, this->y, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets(), false, false, 0, false, -1, -1, clip[0], clip[1], clip[2], clip[3], true); + } + else + statusBar->DrawGraphic(fg, this->x, this->y, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets(), false, false, 0, false, -1, -1, clip[0], clip[1], clip[2], clip[3]); + } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + sc.MustGetToken(TK_StringConst); + foreground = script->newImage(sc.String); + sc.MustGetToken(','); + sc.MustGetToken(TK_StringConst); + background = script->newImage(sc.String); + sc.MustGetToken(','); + sc.MustGetToken(TK_Identifier); + if(sc.Compare("health")) + { + type = HEALTH; + if(sc.CheckToken(TK_Identifier)) //comparing reference + { + inventoryItem = PClass::FindClass(sc.String); + if(inventoryItem == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(inventoryItem)) //must be a kind of inventory + sc.ScriptError("'%s' is not a type of inventory item.", sc.String); + } + } + else if(sc.Compare("armor")) + { + type = ARMOR; + if(sc.CheckToken(TK_Identifier)) + { + inventoryItem = PClass::FindClass(sc.String); + if(inventoryItem == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(inventoryItem)) //must be a kind of inventory + sc.ScriptError("'%s' is not a type of inventory item.", sc.String); + } + } + else if(sc.Compare("ammo1")) + type = AMMO1; + else if(sc.Compare("ammo2")) + type = AMMO2; + else if(sc.Compare("ammo")) //request the next string to be an ammo type + { + sc.MustGetToken(TK_Identifier); + type = AMMO; + inventoryItem = PClass::FindClass(sc.String); + if(inventoryItem == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(inventoryItem)) //must be a kind of ammo + { + sc.ScriptError("'%s' is not a type of ammo.", sc.String); + } + } + else if(sc.Compare("frags")) + type = FRAGS; + else if(sc.Compare("kills")) + type = KILLS; + else if(sc.Compare("items")) + type = ITEMS; + else if(sc.Compare("secrets")) + type = SECRETS; + else if(sc.Compare("airtime")) + type = AIRTIME; + else if(sc.Compare("poweruptime")) + { + type = POWERUPTIME; + sc.MustGetToken(TK_Identifier); + inventoryItem = PClass::FindClass(sc.String); + if(inventoryItem == NULL || !PClass::FindClass("PowerupGiver")->IsAncestorOf(inventoryItem)) + { + sc.ScriptError("'%s' is not a type of PowerupGiver.", sc.String); + } + } + else + { + type = INVENTORY; + inventoryItem = PClass::FindClass(sc.String); + if(inventoryItem == NULL || !RUNTIME_CLASS(AInventory)->IsAncestorOf(inventoryItem)) + { + sc.ScriptError("'%s' is not a type of inventory item.", sc.String); + } + } + sc.MustGetToken(','); + sc.MustGetToken(TK_Identifier); + if(sc.Compare("horizontal")) + horizontal = true; + else if(!sc.Compare("vertical")) + sc.ScriptError("Unknown direction '%s'.", sc.String); + sc.MustGetToken(','); + while(sc.CheckToken(TK_Identifier)) + { + if(sc.Compare("reverse")) + reverse = true; + else if(sc.Compare("interpolate")) + { + sc.MustGetToken('('); + sc.MustGetToken(TK_IntConst); + interpolationSpeed = sc.Number; + sc.MustGetToken(')'); + } + else + sc.ScriptError("Unkown flag '%s'.", sc.String); + if(!sc.CheckToken('|')) + sc.MustGetToken(','); + } + GetCoordinates(sc, fullScreenOffsets, x, y); + if(sc.CheckToken(',')) //border + { + sc.MustGetToken(TK_IntConst); + border = sc.Number; + + // Flip the direction since it represents the area to clip + if(border != 0) + reverse = !reverse; + } + sc.MustGetToken(';'); + + if(type == HEALTH) + interpolationSpeed = script->interpolateHealth ? script->interpolationSpeed : interpolationSpeed; + else if(type == ARMOR) + interpolationSpeed = script->interpolateArmor ? script->armorInterpolationSpeed : interpolationSpeed; + } + void Reset() + { + drawValue = 0; + } + void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) + { + fixed_t value = 0; + int max = 0; + switch(type) + { + case HEALTH: + value = statusBar->CPlayer->mo->health; + if(value < 0) //health shouldn't display negatives + value = 0; + + if(inventoryItem != NULL) + { + AInventory *item = statusBar->CPlayer->mo->FindInventory(inventoryItem); //max comparer + if(item != NULL) + max = item->Amount; + else + max = 0; + } + else //default to the class's health + max = statusBar->CPlayer->mo->GetMaxHealth() + statusBar->CPlayer->stamina; + break; + case ARMOR: + value = statusBar->armor != NULL ? statusBar->armor->Amount : 0; + if(inventoryItem != NULL) + { + AInventory *item = statusBar->CPlayer->mo->FindInventory(inventoryItem); + if(item != NULL) + max = item->Amount; + else + max = 0; + } + else + max = 100; + break; + case AMMO1: + value = statusBar->ammocount1; + if(statusBar->ammo1 == NULL) //no ammo, draw as empty + { + value = 0; + max = 1; + } + else + max = statusBar->ammo1->MaxAmount; + break; + case AMMO2: + value = statusBar->ammocount2; + if(statusBar->ammo2 == NULL) //no ammo, draw as empty + { + value = 0; + max = 1; + } + else + max = statusBar->ammo2->MaxAmount; + break; + case AMMO: + { + AInventory *item = statusBar->CPlayer->mo->FindInventory(inventoryItem); + if(item != NULL) + { + value = item->Amount; + max = item->MaxAmount; + } + else + value = 0; + break; + } + case FRAGS: + value = statusBar->CPlayer->fragcount; + max = fraglimit; + break; + case KILLS: + value = level.killed_monsters; + max = level.total_monsters; + break; + case ITEMS: + value = level.found_items; + max = level.total_items; + break; + case SECRETS: + value = level.found_secrets; + max = level.total_secrets; + break; + case INVENTORY: + { + AInventory *item = statusBar->CPlayer->mo->FindInventory(inventoryItem); + if(item != NULL) + { + value = item->Amount; + max = item->MaxAmount; + } + else + value = 0; + break; + } + case AIRTIME: + value = clamp(statusBar->CPlayer->air_finished - level.time, 0, INT_MAX); + max = level.airsupply; + break; + case POWERUPTIME: + { + //Get the PowerupType and check to see if the player has any in inventory. + APowerupGiver *powerupGiver = (APowerupGiver*) GetDefaultByType(inventoryItem); + const PClass *powerupType = powerupGiver->PowerupType; + APowerup *powerup = (APowerup*) statusBar->CPlayer->mo->FindInventory(powerupType); + if(powerup != NULL && powerupType != NULL && powerupGiver != NULL) + { + value = powerup->EffectTics + 1; + if(powerupGiver->EffectTics == 0) //if 0 we need to get the default from the powerup + max = ((APowerup*) GetDefaultByType(powerupType))->EffectTics + 1; + else + max = powerupGiver->EffectTics + 1; + } + break; + } + default: return; + } + + if(border != 0) + value = max - value; //invert since the new drawing method requires drawing the bg on the fg. + if(max != 0 && value > 0) + { + value = (value << FRACBITS) / max; + if(value > FRACUNIT) + value = FRACUNIT; + } + else if(border != 0 && max == 0 && value <= 0) + value = FRACUNIT; + else + value = 0; + if(interpolationSpeed != 0 && (!hudChanged || level.time == 1)) + { + if(value < drawValue) + drawValue -= clamp((drawValue - value) >> 2, 1, FixedDiv(interpolationSpeed<((value - drawValue) >> 2, 1, FixedDiv(interpolationSpeed<CPlayer->ReadyWeapon != NULL) + { + const PClass *readyWeapon = statusBar->CPlayer->ReadyWeapon->GetClass(); + if(((weapon[1] != NULL) && + ((negate && (weapon[0] != readyWeapon && weapon[1] != readyWeapon)) || + (!negate && (weapon[0] == readyWeapon || weapon[1] == readyWeapon)))) || + ((weapon[1] == NULL) && + ((!negate && weapon[0] == readyWeapon) || (negate && weapon[0] != readyWeapon)))) + { + SBarInfoCommandFlowControl::Draw(block, statusBar); + } + } + } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + //Using StringConst instead of Identifieres is deperecated! + if(sc.CheckToken(TK_Identifier)) + { + if(sc.Compare("not")) + { + negate = true; + if(!sc.CheckToken(TK_StringConst)) + sc.MustGetToken(TK_Identifier); + } + } + else + sc.MustGetToken(TK_StringConst); + for(int i = 0;i < 2;i++) + { + weapon[i] = PClass::FindClass(sc.String); + if(weapon[i] == NULL || !RUNTIME_CLASS(AWeapon)->IsAncestorOf(weapon[i])) + sc.ScriptError("'%s' is not a type of weapon.", sc.String); + + if(sc.CheckToken(',')) + { + if(!sc.CheckToken(TK_StringConst)) + sc.MustGetToken(TK_Identifier); + } + else + break; + } + SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets); + } + protected: + bool negate; + const PClass *weapon[2]; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CommandPlayerClass : public SBarInfoCommandFlowControl +{ + public: + CommandPlayerClass(SBarInfo *script) : SBarInfoCommandFlowControl(script) + { + } + + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) + { + if(statusBar->CPlayer->cls == NULL) + return; //No class so we can not continue + + int spawnClass = statusBar->CPlayer->cls->ClassIndex; + for(unsigned int i = 0;i < classes.Size();i++) + { + if(classes[i] == spawnClass) + SBarInfoCommandFlowControl::Draw(block, statusBar); + } + } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + sc.MustGetToken(TK_Identifier); + do + { + bool foundClass = false; + for(unsigned int c = 0;c < PlayerClasses.Size();c++) + { + if(stricmp(sc.String, PlayerClasses[c].Type->Meta.GetMetaString(APMETA_DisplayName)) == 0) + { + foundClass = true; + classes.Push(PlayerClasses[c].Type->ClassIndex); + break; + } + } + if(!foundClass) + sc.ScriptError("Unkown PlayerClass '%s'.", sc.String); + if(!sc.CheckToken(',')) + break; + } + while(sc.CheckToken(TK_Identifier)); + SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets); + } + protected: + TArray classes; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CommandHasWeaponPiece : public SBarInfoCommandFlowControl +{ + public: + CommandHasWeaponPiece(SBarInfo *script) : SBarInfoCommandFlowControl(script), + weapon(NULL), piece(1) + { + } + + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) + { + for(AInventory *inv = statusBar->CPlayer->mo->Inventory;inv != NULL;inv=inv->Inventory) + { + if(inv->IsKindOf(RUNTIME_CLASS(AWeaponHolder))) + { + AWeaponHolder *hold = static_cast(inv); + if(hold->PieceWeapon == weapon) + { + if(hold->PieceMask & (1 << (piece-1))) + SBarInfoCommandFlowControl::Draw(block, statusBar); + break; + } + } + } + } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + sc.MustGetToken(TK_Identifier); + weapon = PClass::FindClass(sc.String); + if(weapon == NULL || !RUNTIME_CLASS(AWeapon)->IsAncestorOf(weapon)) //must be a weapon + sc.ScriptError("%s is not a kind of weapon.", sc.String); + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + if(sc.Number < 1) + sc.ScriptError("Weapon piece number can not be less than 1."); + piece = sc.Number; + SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets); + } + protected: + const PClass *weapon; + unsigned int piece; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CommandDrawGem : public SBarInfoCommand +{ + public: + CommandDrawGem(SBarInfo *script) : SBarInfoCommand(script), + wiggle(false), translatable(false), armor(false), reverse(false), + chain(-1), gem(-1), leftPadding(0), rightPadding(0), chainSize(1), + interpolationSpeed(0), drawValue(0), goalValue(0), chainWiggle(0) + { + } + + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) + { + FTexture *chainImg = statusBar->Images[chain]; + FTexture *gemImg = statusBar->Images[gem]; + if(chainImg == NULL) + return; + + SBarInfoCoordinate drawY = y; + if(wiggle && drawValue != goalValue) // Should only wiggle when the value doesn't equal what is being drawn. + drawY += chainWiggle; + int chainWidth = chainImg->GetWidth(); + int offset = (int) (((double) (chainWidth-leftPadding-rightPadding)/100)*drawValue); + statusBar->DrawGraphic(chainImg, x+(offset%chainSize), drawY, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets()); + if(gemImg != NULL) + statusBar->DrawGraphic(gemImg, x+leftPadding+offset, drawY, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets(), translatable); + } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + while(sc.CheckToken(TK_Identifier)) + { + if(sc.Compare("wiggle")) + wiggle = true; + else if(sc.Compare("translatable")) + translatable = true; + else if(sc.Compare("armor")) + armor = true; + else if(sc.Compare("interpolate")) + { + sc.MustGetToken('('); + sc.MustGetToken(TK_IntConst); + interpolationSpeed = sc.Number; + sc.MustGetToken(')'); + } + else if(sc.Compare("reverse")) + reverse = true; + else + sc.ScriptError("Unknown drawgem flag '%s'.", sc.String); + if(!sc.CheckToken('|')) + sc.MustGetToken(','); + } + sc.MustGetToken(TK_StringConst); //chain + chain = script->newImage(sc.String); + sc.MustGetToken(','); + sc.MustGetToken(TK_StringConst); //gem + gem = script->newImage(sc.String); + sc.MustGetToken(','); + bool negative = sc.CheckToken('-'); + sc.MustGetToken(TK_IntConst); + leftPadding = (negative ? -1 : 1) * sc.Number; + sc.MustGetToken(','); + negative = sc.CheckToken('-'); + sc.MustGetToken(TK_IntConst); + rightPadding = (negative ? -1 : 1) * sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + if(sc.Number < 0) + sc.ScriptError("Chain size must be a positive number."); + chainSize = sc.Number+1; + sc.MustGetToken(','); + GetCoordinates(sc, fullScreenOffsets, x, y); + sc.MustGetToken(';'); + + if(!armor) + interpolationSpeed = script->interpolateHealth ? script->interpolationSpeed : interpolationSpeed; + else + interpolationSpeed = script->interpolateArmor ? script->armorInterpolationSpeed : interpolationSpeed; + } + void Reset() + { + drawValue = 0; + } + void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) + { + goalValue = armor ? statusBar->armor->Amount : statusBar->CPlayer->mo->health; + int max = armor ? 100 : statusBar->CPlayer->mo->GetMaxHealth() + statusBar->CPlayer->stamina; + if(max != 0 && goalValue > 0) + { + goalValue = (goalValue*100)/max; + if(goalValue > 100) + goalValue = 100; + } + else + goalValue = 0; + + goalValue = reverse ? 100 - goalValue : goalValue; + + if(interpolationSpeed != 0 && (!hudChanged || level.time == 1)) // At the start force an animation + { + if(goalValue < drawValue) + drawValue -= clamp((drawValue - goalValue) >> 2, 1, interpolationSpeed); + else if(drawValue < goalValue) + drawValue += clamp((goalValue - drawValue) >> 2, 1, interpolationSpeed); + } + else + drawValue = goalValue; + + if(wiggle && level.time & 1) + chainWiggle = pr_chainwiggle() & 1; + } + protected: + bool wiggle; + bool translatable; + bool armor; + bool reverse; + int chain; + int gem; + int leftPadding; + int rightPadding; + unsigned int chainSize; + SBarInfoCoordinate x; + SBarInfoCoordinate y; + + int interpolationSpeed; + int drawValue; + int goalValue; + private: + int chainWiggle; + static FRandom pr_chainwiggle; +}; +FRandom CommandDrawGem::pr_chainwiggle; //use the same method of chain wiggling as heretic. + +//////////////////////////////////////////////////////////////////////////////// + +class CommandWeaponAmmo : public SBarInfoCommandFlowControl +{ + public: + CommandWeaponAmmo(SBarInfo *script) : SBarInfoCommandFlowControl(script), + conditionAnd(false), negate(false) + { + ammo[0] = NULL; + ammo[1] = NULL; + } + + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) + { + if(statusBar->CPlayer->ReadyWeapon != NULL) + { + const PClass *AmmoType1 = statusBar->CPlayer->ReadyWeapon->AmmoType1; + const PClass *AmmoType2 = statusBar->CPlayer->ReadyWeapon->AmmoType2; + bool usesammo1 = (AmmoType1 != NULL); + bool usesammo2 = (AmmoType2 != NULL); + if(!negate && !usesammo1 && !usesammo2) //if the weapon doesn't use ammo don't go though the trouble. + { + SBarInfoCommandFlowControl::Draw(block, statusBar); + return; + } + //Or means only 1 ammo type needs to match and means both need to match. + if(ammo[1] != NULL) + { + bool match1 = ((usesammo1 && (AmmoType1 == ammo[0] || AmmoType1 == ammo[1])) || !usesammo1); + bool match2 = ((usesammo2 && (AmmoType2 == ammo[0] || AmmoType2 == ammo[1])) || !usesammo2); + if((!conditionAnd && (match1 || match2)) || (conditionAnd && (match1 && match2))) + { + if(!negate) + SBarInfoCommandFlowControl::Draw(block, statusBar); + } + else if(negate) + { + SBarInfoCommandFlowControl::Draw(block, statusBar); + } + } + else //Every thing here could probably be one long if statement but then it would be more confusing. + { + if((usesammo1 && (AmmoType1 == ammo[0])) || (usesammo2 && (AmmoType2 == ammo[0]))) + { + if(!negate) + SBarInfoCommandFlowControl::Draw(block, statusBar); + } + else if(negate) + { + SBarInfoCommandFlowControl::Draw(block, statusBar); + } + } + } + } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + sc.MustGetToken(TK_Identifier); + if(sc.Compare("not")) + { + negate = true; + sc.MustGetToken(TK_Identifier); + } + for(int i = 0;i < 2;i++) + { + ammo[i] = PClass::FindClass(sc.String); + if(ammo[i] == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(ammo[i])) //must be a kind of ammo + sc.ScriptError("'%s' is not a type of ammo.", sc.String); + + if(sc.CheckToken(TK_OrOr)) + { + conditionAnd = false; + sc.MustGetToken(TK_Identifier); + } + else if(sc.CheckToken(TK_AndAnd)) + { + conditionAnd = true; + sc.MustGetToken(TK_Identifier); + } + else + break; + } + SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets); + } + protected: + bool conditionAnd; + bool negate; + const PClass *ammo[2]; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CommandInInventory : public SBarInfoCommandFlowControl +{ + public: + CommandInInventory(SBarInfo *script) : SBarInfoCommandFlowControl(script), + conditionAnd(false), negate(false) + { + item[0] = item[1] = NULL; + amount[0] = amount[1] = 0; + } + + void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) + { + AInventory *invItem[2] = { statusBar->CPlayer->mo->FindInventory(item[0]), statusBar->CPlayer->mo->FindInventory(item[1]) }; + if (invItem[0] != NULL && amount[0] > 0 && invItem[0]->Amount < amount[0]) invItem[0] = NULL; + if (invItem[1] != NULL && amount[1] > 0 && invItem[1]->Amount < amount[1]) invItem[1] = NULL; + if(invItem[1] != NULL && conditionAnd) + { + if((invItem[0] != NULL && invItem[1] != NULL) && !negate) + SBarInfoCommandFlowControl::Draw(block, statusBar); + else if((invItem[0] == NULL || invItem[1] == NULL) && negate) + SBarInfoCommandFlowControl::Draw(block, statusBar); + } + else if(invItem[1] != NULL && !conditionAnd) + { + if((invItem[0] != NULL || invItem[1] != NULL) && !negate) + SBarInfoCommandFlowControl::Draw(block, statusBar); + else if((invItem[0] == NULL && invItem[1] == NULL) && negate) + SBarInfoCommandFlowControl::Draw(block, statusBar); + } + else if((invItem[0] != NULL) && !negate) + SBarInfoCommandFlowControl::Draw(block, statusBar); + else if((invItem[0] == NULL) && negate) + SBarInfoCommandFlowControl::Draw(block, statusBar); + } + void Parse(FScanner &sc, bool fullScreenOffsets) + { + sc.MustGetToken(TK_Identifier); + if(sc.Compare("not")) + { + negate = true; + sc.MustGetToken(TK_Identifier); + } + for(int i = 0;i < 2;i++) + { + item[i] = PClass::FindClass(sc.String); + if(item[i] == NULL || !RUNTIME_CLASS(AInventory)->IsAncestorOf(item[i])) + sc.ScriptError("'%s' is not a type of inventory item.", sc.String); + + if (sc.CheckToken(',')) + { + sc.MustGetNumber(); + amount[i] = sc.Number; + } + + if(sc.CheckToken(TK_OrOr)) + { + conditionAnd = false; + sc.MustGetToken(TK_Identifier); + } + else if(sc.CheckToken(TK_AndAnd)) + { + conditionAnd = true; + sc.MustGetToken(TK_Identifier); + } + else + break; + } + SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets); + } + protected: + bool conditionAnd; + bool negate; + const PClass *item[2]; + int amount[2]; +}; + +//////////////////////////////////////////////////////////////////////////////// + +static const char *SBarInfoCommandNames[] = +{ + "drawimage", "drawnumber", "drawswitchableimage", + "drawmugshot", "drawselectedinventory", + "drawinventorybar", "drawbar", "drawgem", + "drawshader", "drawstring", "drawkeybar", + "gamemode", "playerclass", "aspectratio", + "isselected", "usesammo", "usessecondaryammo", + "hasweaponpiece", "inventorybarnotvisible", + "weaponammo", "ininventory", + NULL +}; + +enum SBarInfoCommands +{ + SBARINFO_DRAWIMAGE, SBARINFO_DRAWNUMBER, SBARINFO_DRAWSWITCHABLEIMAGE, + SBARINFO_DRAWMUGSHOT, SBARINFO_DRAWSELECTEDINVENTORY, + SBARINFO_DRAWINVENTORYBAR, SBARINFO_DRAWBAR, SBARINFO_DRAWGEM, + SBARINFO_DRAWSHADER, SBARINFO_DRAWSTRING, SBARINFO_DRAWKEYBAR, + SBARINFO_GAMEMODE, SBARINFO_PLAYERCLASS, SBARINFO_ASPECTRATIO, + SBARINFO_ISSELECTED, SBARINFO_USESAMMO, SBARINFO_USESSECONDARYAMMO, + SBARINFO_HASWEAPONPIECE, SBARINFO_INVENTORYBARNOTVISIBLE, + SBARINFO_WEAPONAMMO, SBARINFO_ININVENTORY, +}; + +SBarInfoCommand *SBarInfoCommandFlowControl::NextCommand(FScanner &sc) +{ + if(sc.CheckToken(TK_Identifier)) + { + switch(sc.MatchString(SBarInfoCommandNames)) + { + default: break; + case SBARINFO_DRAWIMAGE: return new CommandDrawImage(script); + case SBARINFO_DRAWSWITCHABLEIMAGE: return new CommandDrawSwitchableImage(script); + case SBARINFO_DRAWSTRING: return new CommandDrawString(script); + case SBARINFO_DRAWNUMBER: return new CommandDrawNumber(script); + case SBARINFO_DRAWMUGSHOT: return new CommandDrawMugShot(script); + case SBARINFO_DRAWSELECTEDINVENTORY: return reinterpret_cast (new CommandDrawSelectedInventory(script)); + case SBARINFO_DRAWSHADER: return new CommandDrawShader(script); + case SBARINFO_DRAWINVENTORYBAR: return new CommandDrawInventoryBar(script); + case SBARINFO_DRAWKEYBAR: return new CommandDrawKeyBar(script); + case SBARINFO_DRAWBAR: return new CommandDrawBar(script); + case SBARINFO_DRAWGEM: return new CommandDrawGem(script); + case SBARINFO_GAMEMODE: return new CommandGameMode(script); + case SBARINFO_USESAMMO: return new CommandUsesAmmo(script); + case SBARINFO_USESSECONDARYAMMO: return new CommandUsesSecondaryAmmo(script); + case SBARINFO_INVENTORYBARNOTVISIBLE: return new CommandInventoryBarNotVisible(script); + case SBARINFO_ASPECTRATIO: return new CommandAspectRatio(script); + case SBARINFO_ISSELECTED: return new CommandIsSelected(script); + case SBARINFO_PLAYERCLASS: return new CommandPlayerClass(script); + case SBARINFO_HASWEAPONPIECE: return new CommandHasWeaponPiece(script); + case SBARINFO_WEAPONAMMO: return new CommandWeaponAmmo(script); + case SBARINFO_ININVENTORY: return new CommandInInventory(script); + } + + sc.ScriptError("Unknown command '%s'.\n", sc.String); + return NULL; + } + + sc.MustGetToken('}'); + return NULL; +} diff --git a/src/g_shared/sbarinfo_display.cpp b/src/g_shared/sbarinfo_display.cpp deleted file mode 100644 index 77c52c60f..000000000 --- a/src/g_shared/sbarinfo_display.cpp +++ /dev/null @@ -1,1765 +0,0 @@ -/* -** sbarinfo_display.cpp -** -** Contains all functions required for the display of custom statusbars. -** -**--------------------------------------------------------------------------- -** Copyright 2008 Braden Obrzut -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ - -#include "doomtype.h" -#include "doomstat.h" -#include "v_font.h" -#include "v_video.h" -#include "sbar.h" -#include "r_defs.h" -#include "w_wad.h" -#include "m_random.h" -#include "d_player.h" -#include "st_stuff.h" -#include "r_local.h" -#include "m_swap.h" -#include "a_keys.h" -#include "templates.h" -#include "i_system.h" -#include "sbarinfo.h" -#include "gi.h" -#include "r_translate.h" -#include "r_main.h" -#include "a_weaponpiece.h" -#include "a_strifeglobal.h" -#include "g_level.h" -#include "v_palette.h" -#include "p_acs.h" - -static FRandom pr_chainwiggle; //use the same method of chain wiggling as heretic. - -#define ST_RAMPAGETIME (TICRATE*2) -#define ARTIFLASH_OFFSET (invBarOffset+6) - -EXTERN_CVAR(Int, fraglimit) -EXTERN_CVAR(Int, screenblocks) -EXTERN_CVAR(Bool, vid_fps) -EXTERN_CVAR(Bool, hud_scale) - -enum -{ - imgARTIBOX, - imgSELECTBOX, - imgCURSOR, - imgINVLFGEM1, - imgINVLFGEM2, - imgINVRTGEM1, - imgINVRTGEM2, -}; - -#define ADJUST_RELCENTER(x, y, outX, outY) \ - if(x.RelCenter()) \ - outX = *x + SCREENWIDTH/(hud_scale ? CleanXfac*2 : 2); \ - else \ - outX = *x; \ - if(y.RelCenter()) \ - outY = *y + SCREENHEIGHT/(hud_scale ? CleanYfac*2 : 2); \ - else \ - outY = *y; - -//////////////////////////////////////////////////////////////////////////////// - -SBarInfoCoordinate &SBarInfoCoordinate::Add(int add) -{ - value += add; - return *this; -} - -//////////////////////////////////////////////////////////////////////////////// - -//Used for shading -FBarShader::FBarShader(bool vertical, bool reverse) //make an alpha map -{ - int i; - - Width = vertical ? 2 : 256; - Height = vertical ? 256 : 2; - CalcBitSize(); - - // Fill the column/row with shading values. - // Vertical shaders have have minimum alpha at the top - // and maximum alpha at the bottom, unless flipped by - // setting reverse to true. Horizontal shaders are just - // the opposite. - if (vertical) - { - if (!reverse) - { - for (i = 0; i < 256; ++i) - { - Pixels[i] = i; - Pixels[256+i] = i; - } - } - else - { - for (i = 0; i < 256; ++i) - { - Pixels[i] = 255 - i; - Pixels[256+i] = 255 -i; - } - } - } - else - { - if (!reverse) - { - for (i = 0; i < 256; ++i) - { - Pixels[i*2] = 255 - i; - Pixels[i*2+1] = 255 - i; - } - } - else - { - for (i = 0; i < 256; ++i) - { - Pixels[i*2] = i; - Pixels[i*2+1] = i; - } - } - } - DummySpan[0].TopOffset = 0; - DummySpan[0].Length = vertical ? 256 : 2; - DummySpan[1].TopOffset = 0; - DummySpan[1].Length = 0; -} - -const BYTE *FBarShader::GetColumn(unsigned int column, const Span **spans_out) -{ - if (spans_out != NULL) - { - *spans_out = DummySpan; - } - return Pixels + ((column & WidthMask) << HeightBits); -} - -const BYTE *FBarShader::GetPixels() -{ - return Pixels; -} - -void FBarShader::Unload() -{ -} - -//SBarInfo Display -DSBarInfo::DSBarInfo (SBarInfo *script) : DBaseStatusBar(script->height), - shader_horz_normal(false, false), - shader_horz_reverse(false, true), - shader_vert_normal(true, false), - shader_vert_reverse(true, true) -{ - this->script = script; - - static const char *InventoryBarLumps[] = - { - "ARTIBOX", "SELECTBO", "INVCURS", "INVGEML1", - "INVGEML2", "INVGEMR1", "INVGEMR2", - "USEARTIA", "USEARTIB", "USEARTIC", "USEARTID", - }; - TArray patchnames; - patchnames.Resize(script->Images.Size()+10); - unsigned int i = 0; - for(i = 0;i < script->Images.Size();i++) - { - patchnames[i] = script->Images[i]; - } - for(i = 0;i < 10;i++) - { - patchnames[i+script->Images.Size()] = InventoryBarLumps[i]; - } - for (i = 0;i < numskins;i++) - { - AddFaceToImageCollection (&skins[i], &Images); - } - invBarOffset = script->Images.Size(); - Images.Init(&patchnames[0], patchnames.Size()); - drawingFont = V_GetFont("ConFont"); - oldHealth = 0; - oldArmor = 0; - chainWiggle = 0; - artiflash = 4; - currentPopup = POP_None; - pendingPopup = POP_None; -} - -DSBarInfo::~DSBarInfo () -{ - Images.Uninit(); -} - -void DSBarInfo::Draw (EHudState state) -{ - DBaseStatusBar::Draw(state); - int hud = STBAR_NORMAL; - if(state == HUD_StatusBar) - { - if(script->completeBorder) //Fill the statusbar with the border before we draw. - { - FTexture *b = TexMan[gameinfo.border->b]; - R_DrawBorder(viewwindowx, viewwindowy + viewheight + b->GetHeight(), viewwindowx + viewwidth, SCREENHEIGHT); - if(screenblocks == 10) - screen->FlatFill(viewwindowx, viewwindowy + viewheight, viewwindowx + viewwidth, viewwindowy + viewheight + b->GetHeight(), b, true); - } - if(script->automapbar && automapactive) - { - hud = STBAR_AUTOMAP; - } - else - { - hud = STBAR_NORMAL; - } - } - else if(state == HUD_Fullscreen) - { - hud = STBAR_FULLSCREEN; - } - else - { - hud = STBAR_NONE; - } - bool oldhud_scale = hud_scale; - if(script->huds[hud].forceScaled) //scale the statusbar - { - SetScaled(true, true); - setsizeneeded = true; - if(script->huds[hud].fullScreenOffsets) - hud_scale = true; - } - doCommands(script->huds[hud], 0, 0, script->huds[hud].alpha); - if(CPlayer->inventorytics > 0 && !(level.flags & LEVEL_NOINVENTORYBAR)) - { - if(state == HUD_StatusBar) - { - // No overlay? Lets cancel it. - if(script->huds[STBAR_INVENTORY].commands.Size() == 0) - CPlayer->inventorytics = 0; - else - doCommands(script->huds[STBAR_INVENTORY], 0, 0, script->huds[STBAR_INVENTORY].alpha); - } - else if(state == HUD_Fullscreen) - { - if(script->huds[STBAR_INVENTORYFULLSCREEN].commands.Size() == 0) - CPlayer->inventorytics = 0; - else - doCommands(script->huds[STBAR_INVENTORYFULLSCREEN], 0, 0, script->huds[STBAR_INVENTORYFULLSCREEN].alpha); - } - } - if(currentPopup != POP_None) - { - int popbar = 0; - if(currentPopup == POP_Log) - popbar = STBAR_POPUPLOG; - else if(currentPopup == POP_Keys) - popbar = STBAR_POPUPKEYS; - else if(currentPopup == POP_Status) - popbar = STBAR_POPUPSTATUS; - doCommands(script->huds[popbar], script->popups[currentPopup-1].getXOffset(), script->popups[currentPopup-1].getYOffset(), - script->popups[currentPopup-1].getAlpha(script->huds[popbar].alpha)); - } - if(script->huds[hud].forceScaled && script->huds[hud].fullScreenOffsets) - hud_scale = oldhud_scale; -} - -void DSBarInfo::NewGame () -{ - if (CPlayer != NULL) - { - AttachToPlayer (CPlayer); - } -} - -void DSBarInfo::AttachToPlayer (player_t *player) -{ - DBaseStatusBar::AttachToPlayer(player); -// MugShot.CurrentState = NULL; -} - -void DSBarInfo::SetMugShotState (const char *state_name, bool wait_till_done, bool reset) -{ - MugShot.SetState(state_name, wait_till_done, reset); -} - -void DSBarInfo::Tick () -{ - DBaseStatusBar::Tick(); - if(level.time & 1) - chainWiggle = pr_chainwiggle() & 1; - if(!script->interpolateHealth) - { - oldHealth = CPlayer->health; - } - else - { - int health = script->lowerHealthCap ? CPlayer->health : CPlayer->mo->health; - if(oldHealth > health) - { - oldHealth -= clamp((oldHealth - health), 1, script->interpolationSpeed); - } - else if(oldHealth < CPlayer->health) - { - oldHealth += clamp((health - oldHealth), 1, script->interpolationSpeed); - } - } - AInventory *armor = CPlayer->mo != NULL? CPlayer->mo->FindInventory() : NULL; - if(armor == NULL) - { - oldArmor = 0; - } - else - { - if(!script->interpolateArmor) - { - oldArmor = armor->Amount; - } - else - { - if(oldArmor > armor->Amount) - { - oldArmor -= clamp((oldArmor - armor->Amount) >> 2, 1, script->armorInterpolationSpeed); - } - else if(oldArmor < armor->Amount) - { - oldArmor += clamp((armor->Amount - oldArmor) >> 2, 1, script->armorInterpolationSpeed); - } - } - } - if(artiflash) - { - artiflash--; - } - - MugShot.Tick(CPlayer); - if(currentPopup != POP_None) - { - script->popups[currentPopup-1].tick(); - if(script->popups[currentPopup-1].opened == false && script->popups[currentPopup-1].isDoneMoving()) - { - currentPopup = pendingPopup; - if(currentPopup != POP_None) - script->popups[currentPopup-1].open(); - } - } -} - -void DSBarInfo::ReceivedWeapon(AWeapon *weapon) -{ - MugShot.bEvilGrin = true; -} - -void DSBarInfo::FlashItem(const PClass *itemtype) -{ - artiflash = 4; -} - -void DSBarInfo::ShowPop(int popnum) -{ - DBaseStatusBar::ShowPop(popnum); - if(popnum != currentPopup) - { - pendingPopup = popnum; - } - else - pendingPopup = POP_None; - if(currentPopup != POP_None) - script->popups[currentPopup-1].close(); - else - { - currentPopup = pendingPopup; - pendingPopup = POP_None; - if(currentPopup != POP_None) - script->popups[currentPopup-1].open(); - } -} - -void DSBarInfo::doCommands(SBarInfoBlock &block, int xOffset, int yOffset, int alpha) -{ - //prepare ammo counts - AAmmo *ammo1, *ammo2; - int ammocount1, ammocount2; - GetCurrentAmmo(ammo1, ammo2, ammocount1, ammocount2); - ABasicArmor *armor = CPlayer->mo->FindInventory(); - int health = CPlayer->mo->health; - int armorAmount = armor != NULL ? armor->Amount : 0; - if(script->interpolateHealth) - { - health = oldHealth; - } - if(script->interpolateArmor) - { - armorAmount = oldArmor; - } - for(unsigned int i = 0;i < block.commands.Size();i++) - { - SBarInfoCommand& cmd = block.commands[i]; - switch(cmd.type) //read and execute all the commands - { - case SBARINFO_DRAWSWITCHABLEIMAGE: //draw the alt image if we don't have the item else this is like a normal drawimage - { - // DrawSwitchable image allows 2 or 4 images to be supplied. - // drawAlt toggles these: - // 1 = first image - // 2 = second image - // 3 = thrid image - // 0 = last image - int drawAlt = 0; - if((cmd.flags & DRAWIMAGE_WEAPONSLOT)) //weaponslots - { - drawAlt = 1; //draw off state until we know we have something. - for (int i = 0; i < CPlayer->weapons.Slots[cmd.value].Size(); i++) - { - const PClass *weap = CPlayer->weapons.Slots[cmd.value].GetWeapon(i); - if(weap == NULL) - { - continue; - } - else if(CPlayer->mo->FindInventory(weap) != NULL) - { - drawAlt = 0; - break; - } - } - } - else if((cmd.flags & DRAWIMAGE_INVULNERABILITY)) - { - if(CPlayer->cheats&CF_GODMODE) - { - drawAlt = 1; - } - } - else if(cmd.flags & DRAWIMAGE_KEYSLOT) - { - bool found1 = false; - bool found2 = false; - drawAlt = 1; - - for(AInventory *item = CPlayer->mo->Inventory;item != NULL;item = item->Inventory) - { - if(item->IsKindOf(RUNTIME_CLASS(AKey))) - { - int keynum = static_cast(item)->KeyNumber; - - if(keynum == cmd.value) - found1 = true; - if(cmd.flags & DRAWIMAGE_SWITCHABLE_AND && keynum == cmd.special4) // two keys - found2 = true; - } - } - - if(cmd.flags & DRAWIMAGE_SWITCHABLE_AND) - { - if(found1 && found2) - drawAlt = 0; - else if(found1) - drawAlt = 2; - else if(found2) - drawAlt = 3; - } - else - { - if(found1) - drawAlt = 0; - } - } - else //check the inventory items and draw selected sprite - { - AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0])); - if(item == NULL || item->Amount == 0) - drawAlt = 1; - if((cmd.flags & DRAWIMAGE_SWITCHABLE_AND)) - { - item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[1])); - if((item != NULL && item->Amount != 0) && drawAlt == 0) //both - { - drawAlt = 0; - } - else if((item != NULL && item->Amount != 0) && drawAlt == 1) //2nd - { - drawAlt = 3; - } - else if((item == NULL || item->Amount == 0) && drawAlt == 0) //1st - { - drawAlt = 2; - } - } - } - if(drawAlt != 0) //draw 'off' image - { - if(cmd.special != -1 && drawAlt == 1) - DrawGraphic(Images[cmd.special], cmd.x, cmd.y, xOffset, yOffset, alpha, block.fullScreenOffsets, !!(cmd.flags & DRAWIMAGE_TRANSLATABLE), false, !!(cmd.flags & DRAWIMAGE_OFFSET)); - else if(cmd.special2 != -1 && drawAlt == 2) - DrawGraphic(Images[cmd.special2], cmd.x, cmd.y, xOffset, yOffset, alpha, block.fullScreenOffsets, !!(cmd.flags & DRAWIMAGE_TRANSLATABLE), false, !!(cmd.flags & DRAWIMAGE_OFFSET)); - else if(cmd.special3 != -1 && drawAlt == 3) - DrawGraphic(Images[cmd.special3], cmd.x, cmd.y, xOffset, yOffset, alpha, block.fullScreenOffsets, !!(cmd.flags & DRAWIMAGE_TRANSLATABLE), false, (cmd.flags & DRAWIMAGE_OFFSET)); - break; - } - } - case SBARINFO_DRAWIMAGE: - { - FTexture *texture = NULL; - if((cmd.flags & DRAWIMAGE_PLAYERICON)) - texture = TexMan[CPlayer->mo->ScoreIcon]; - else if((cmd.flags & DRAWIMAGE_AMMO1)) - { - if(ammo1 != NULL) - texture = TexMan[ammo1->Icon]; - } - else if((cmd.flags & DRAWIMAGE_AMMO2)) - { - if(ammo2 != NULL) - texture = TexMan[ammo2->Icon]; - } - else if((cmd.flags & DRAWIMAGE_ARMOR)) - { - if(armor != NULL && armor->Amount != 0) - texture = TexMan(armor->Icon); - } - else if((cmd.flags & DRAWIMAGE_WEAPONICON)) - { - AWeapon *weapon = CPlayer->ReadyWeapon; - if(weapon != NULL && weapon->Icon.isValid()) - { - texture = TexMan[weapon->Icon]; - } - } - else if(cmd.flags & DRAWIMAGE_SIGIL) - { - AInventory *item = CPlayer->mo->FindInventory(); - if (item != NULL) - texture = TexMan[item->Icon]; - } - else if(cmd.flags & DRAWIMAGE_HEXENARMOR) - { - AHexenArmor *harmor = CPlayer->mo->FindInventory(); - if (harmor != NULL) - { - if (harmor->Slots[cmd.value] > 0 && harmor->SlotsIncrement[cmd.value] > 0) - { - //combine the alpha values - alpha = fixed_t(((double) alpha / (double) FRACUNIT) * ((double) MIN (OPAQUE, Scale(harmor->Slots[cmd.value], OPAQUE, harmor->SlotsIncrement[cmd.value])) / (double) OPAQUE) * FRACUNIT); - texture = Images[cmd.image_index]; - } - else - break; - } - } - else if((cmd.flags & DRAWIMAGE_INVENTORYICON)) - texture = TexMan[cmd.sprite_index]; - else if(cmd.image_index >= 0) - texture = Images[cmd.image_index]; - - // if we reach here with DRAWIMAGE_HEXENARMOR set we know we want it to be dim. - DrawGraphic(texture, cmd.x, cmd.y, xOffset, yOffset, alpha, block.fullScreenOffsets, !!(cmd.flags & DRAWIMAGE_TRANSLATABLE), false, (cmd.flags & (DRAWIMAGE_OFFSET_CENTER|DRAWIMAGE_OFFSET_CENTERBOTTOM))); - break; - } - case SBARINFO_DRAWNUMBER: - { - int value = cmd.value; - if(drawingFont != cmd.font) - { - drawingFont = cmd.font; - } - if(cmd.flags & DRAWNUMBER_HEALTH) - { - value = health; - if(script->lowerHealthCap && value < 0) //health shouldn't display negatives - { - value = 0; - } - } - else if(cmd.flags & DRAWNUMBER_ARMOR) - { - value = armorAmount; - } - else if(cmd.flags & DRAWNUMBER_AMMO1) - { - value = ammocount1; - if(ammo1 == NULL) //no ammo, do not draw - { - continue; - } - } - else if(cmd.flags & DRAWNUMBER_AMMO2) - { - value = ammocount2; - if(ammo2 == NULL) //no ammo, do not draw - { - continue; - } - } - else if(cmd.flags & DRAWNUMBER_AMMO) - { - const PClass* ammo = PClass::FindClass(cmd.string[0]); - AInventory* item = CPlayer->mo->FindInventory(ammo); - if(item != NULL) - { - value = item->Amount; - } - else - { - value = 0; - } - } - else if(cmd.flags & DRAWNUMBER_AMMOCAPACITY) - { - const PClass* ammo = PClass::FindClass(cmd.string[0]); - AInventory* item = CPlayer->mo->FindInventory(ammo); - if(item != NULL) - { - value = item->MaxAmount; - } - else - { - value = ((AInventory *)GetDefaultByType(ammo))->MaxAmount; - } - } - else if(cmd.flags & DRAWNUMBER_FRAGS) - value = CPlayer->fragcount; - else if(cmd.flags & DRAWNUMBER_KILLS) - value = level.killed_monsters; - else if(cmd.flags & DRAWNUMBER_MONSTERS) - value = level.total_monsters; - else if(cmd.flags & DRAWNUMBER_ITEMS) - value = level.found_items; - else if(cmd.flags & DRAWNUMBER_TOTALITEMS) - value = level.total_items; - else if(cmd.flags & DRAWNUMBER_SECRETS) - value = level.found_secrets; - else if(cmd.flags & DRAWNUMBER_TOTALSECRETS) - value = level.total_secrets; - else if(cmd.flags & DRAWNUMBER_ARMORCLASS) - { - AHexenArmor *harmor = CPlayer->mo->FindInventory(); - if(harmor != NULL) - { - value = harmor->Slots[0] + harmor->Slots[1] + - harmor->Slots[2] + harmor->Slots[3] + harmor->Slots[4]; - } - //Hexen counts basic armor also so we should too. - if(armor != NULL) - { - value += armor->SavePercent; - } - value /= (5*FRACUNIT); - } - else if(cmd.flags & DRAWNUMBER_GLOBALVAR) - value = ACS_GlobalVars[cmd.value]; - else if(cmd.flags & DRAWNUMBER_GLOBALARRAY) - value = ACS_GlobalArrays[cmd.value][consoleplayer]; - else if(cmd.flags & DRAWNUMBER_POWERUPTIME) - { - //Get the PowerupType and check to see if the player has any in inventory. - const PClass* powerupType = ((APowerupGiver*) GetDefaultByType(PClass::FindClass(cmd.string[0])))->PowerupType; - APowerup* powerup = (APowerup*) CPlayer->mo->FindInventory(powerupType); - if(powerup != NULL) - { - value = powerup->EffectTics / TICRATE + 1; - } - } - else if(cmd.flags & DRAWNUMBER_INVENTORY) - { - AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0])); - if(item != NULL) - { - value = item->Amount; - } - else - { - value = 0; - } - } - else if(cmd.flags & DRAWNUMBER_AIRTIME) - { - if(CPlayer->mo->waterlevel < 3) - value = level.airsupply/TICRATE; - else - value = clamp((CPlayer->air_finished - level.time + (TICRATE-1))/TICRATE, 0, INT_MAX); - } - bool fillzeros = !!(cmd.flags & DRAWNUMBER_FILLZEROS); - bool drawshadow = !!(cmd.flags & DRAWNUMBER_DRAWSHADOW); - EColorRange translation = cmd.translation; - if(cmd.special3 != -1 && value <= cmd.special3) //low - translation = cmd.translation2; - else if(cmd.special4 != -1 && value >= cmd.special4) //high - translation = cmd.translation3; - if((cmd.flags & DRAWNUMBER_WHENNOTZERO) && value == 0) - break; - DrawNumber(value, cmd.special, cmd.x, cmd.y, xOffset, yOffset, alpha, block.fullScreenOffsets, translation, cmd.special2, fillzeros, drawshadow); - break; - } - case SBARINFO_DRAWMUGSHOT: - { - bool xdth = false; - bool animatedgodmode = false; - if(cmd.flags & DRAWMUGSHOT_XDEATHFACE) - xdth = true; - if(cmd.flags & DRAWMUGSHOT_ANIMATEDGODMODE) - animatedgodmode = true; - int stateflags = cmd.flags & (DRAWMUGSHOT_XDEATHFACE | DRAWMUGSHOT_ANIMATEDGODMODE | DRAWMUGSHOT_DISABLEGRIN | DRAWMUGSHOT_DISABLEOUCH | DRAWMUGSHOT_DISABLEPAIN | DRAWMUGSHOT_DISABLERAMPAGE); - DrawFace(cmd.string[0], cmd.special, stateflags, cmd.x, cmd.y, xOffset, yOffset, alpha, block.fullScreenOffsets); - break; - } - case SBARINFO_DRAWSELECTEDINVENTORY: - if(CPlayer->mo->InvSel != NULL && !(level.flags & LEVEL_NOINVENTORYBAR)) - { - int offsetflags = (cmd.flags & DRAWSELECTEDINVENTORY_CENTER) ? DRAWIMAGE_OFFSET_CENTER : 0; - offsetflags |= (cmd.flags & DRAWSELECTEDINVENTORY_CENTERBOTTOM) ? DRAWIMAGE_OFFSET_CENTERBOTTOM : 0; - if((cmd.flags & DRAWSELECTEDINVENTORY_ARTIFLASH) && artiflash) - { - DrawGraphic(Images[ARTIFLASH_OFFSET+(4-artiflash)], cmd.x, cmd.y, xOffset, yOffset, alpha, block.fullScreenOffsets, false, CPlayer->mo->InvSel->Amount <= 0, offsetflags); - } - else - { - DrawGraphic(TexMan(CPlayer->mo->InvSel->Icon), cmd.x, cmd.y, xOffset, yOffset, alpha, block.fullScreenOffsets, false, CPlayer->mo->InvSel->Amount <= 0, offsetflags); - } - if((cmd.flags & DRAWSELECTEDINVENTORY_ALWAYSSHOWCOUNTER) || CPlayer->mo->InvSel->Amount != 1) - { - if(drawingFont != cmd.font) - { - drawingFont = cmd.font; - } - DrawNumber(CPlayer->mo->InvSel->Amount, 3, cmd.sbcoord2, cmd.sbcoord3, xOffset, yOffset, alpha, block.fullScreenOffsets, cmd.translation, cmd.special4, false, !!(cmd.flags & DRAWSELECTEDINVENTORY_DRAWSHADOW)); - } - } - else if((cmd.flags & DRAWSELECTEDINVENTORY_ALTERNATEONEMPTY)) - { - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - } - break; - case SBARINFO_DRAWINVENTORYBAR: - { - bool alwaysshow = false; - bool artibox = true; - bool noarrows = false; - bool alwaysshowcounter = false; - bool vertical = false; - int bgalpha = alpha; - if((cmd.flags & DRAWINVENTORYBAR_ALWAYSSHOW)) - alwaysshow = true; - if((cmd.flags & DRAWINVENTORYBAR_NOARTIBOX)) - artibox = false; - if((cmd.flags & DRAWINVENTORYBAR_NOARROWS)) - noarrows = true; - if((cmd.flags & DRAWINVENTORYBAR_ALWAYSSHOWCOUNTER)) - alwaysshowcounter = true; - if(cmd.flags & DRAWINVENTORYBAR_TRANSLUCENT) - bgalpha = fixed_t((((double) alpha / (double) FRACUNIT) * ((double) HX_SHADOW / (double) FRACUNIT)) * FRACUNIT); - if((cmd.flags & DRAWINVENTORYBAR_VERTICAL)) - vertical = true; - if(drawingFont != cmd.font) - { - drawingFont = cmd.font; - } - DrawInventoryBar(cmd.special, cmd.value, cmd.x, cmd.y, xOffset, yOffset, alpha, block.fullScreenOffsets, alwaysshow, cmd.sbcoord2, cmd.sbcoord3, cmd.translation, artibox, noarrows, alwaysshowcounter, bgalpha, vertical); - break; - } - case SBARINFO_DRAWBAR: - { - if(cmd.image_index == -1 || Images[cmd.image_index] == NULL) - break; //don't draw anything. - bool horizontal = !!((cmd.special2 & DRAWBAR_HORIZONTAL)); - bool reverse = !!((cmd.special2 & DRAWBAR_REVERSE)); - fixed_t value = 0; - int max = 0; - if(cmd.flags == DRAWNUMBER_HEALTH) - { - value = health; - if(value < 0) //health shouldn't display negatives - { - value = 0; - } - if(!(cmd.special2 & DRAWBAR_COMPAREDEFAULTS)) - { - AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0])); //max comparer - if(item != NULL) - { - max = item->Amount; - } - else - { - max = 0; - } - } - else //default to the class's health - { - max = CPlayer->mo->GetMaxHealth() + CPlayer->stamina; - } - } - else if(cmd.flags == DRAWNUMBER_ARMOR) - { - value = armorAmount; - if(!((cmd.special2 & DRAWBAR_COMPAREDEFAULTS) == DRAWBAR_COMPAREDEFAULTS)) - { - AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0])); //max comparer - if(item != NULL) - { - max = item->Amount; - } - else - { - max = 0; - } - } - else //default to 100 - { - max = 100; - } - } - else if(cmd.flags == DRAWNUMBER_AMMO1) - { - value = ammocount1; - if(ammo1 == NULL) //no ammo, draw as empty - { - value = 0; - max = 1; - } - else - max = ammo1->MaxAmount; - } - else if(cmd.flags == DRAWNUMBER_AMMO2) - { - value = ammocount2; - if(ammo2 == NULL) //no ammo, draw as empty - { - value = 0; - max = 1; - } - else - max = ammo2->MaxAmount; - } - else if(cmd.flags == DRAWNUMBER_AMMO) - { - const PClass* ammo = PClass::FindClass(cmd.string[0]); - AInventory* item = CPlayer->mo->FindInventory(ammo); - if(item != NULL) - { - value = item->Amount; - max = item->MaxAmount; - } - else - { - value = 0; - } - } - else if(cmd.flags == DRAWNUMBER_FRAGS) - { - value = CPlayer->fragcount; - max = fraglimit; - } - else if(cmd.flags == DRAWNUMBER_KILLS) - { - value = level.killed_monsters; - max = level.total_monsters; - } - else if(cmd.flags == DRAWNUMBER_ITEMS) - { - value = level.found_items; - max = level.total_items; - } - else if(cmd.flags == DRAWNUMBER_SECRETS) - { - value = level.found_secrets; - max = level.total_secrets; - } - else if(cmd.flags == DRAWNUMBER_INVENTORY) - { - AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0])); - if(item != NULL) - { - value = item->Amount; - max = item->MaxAmount; - } - else - { - value = 0; - } - } - else if(cmd.flags & DRAWNUMBER_POWERUPTIME) - { - //Get the PowerupType and check to see if the player has any in inventory. - APowerupGiver* powerupGiver = (APowerupGiver*) GetDefaultByType(PClass::FindClass(cmd.string[0])); - const PClass* powerupType = powerupGiver->PowerupType; - APowerup* powerup = (APowerup*) CPlayer->mo->FindInventory(powerupType); - if(powerup != NULL && powerupType != NULL && powerupGiver != NULL) - { - value = powerup->EffectTics + 1; - if(powerupGiver->EffectTics == 0) //if 0 we need to get the default from the powerup - max = ((APowerup*) GetDefaultByType(powerupType))->EffectTics + 1; - else - max = powerupGiver->EffectTics + 1; - } - } - else if(cmd.flags & DRAWNUMBER_AIRTIME) - { - value = clamp(CPlayer->air_finished - level.time, 0, INT_MAX); - max = level.airsupply; - } - if(cmd.special3 != 0) - value = max - value; //invert since the new drawing method requires drawing the bg on the fg. - if(max != 0 && value > 0) - { - value = (value << FRACBITS) / max; - if(value > FRACUNIT) - value = FRACUNIT; - } - else if(cmd.special3 != 0 && max == 0 && value <= 0) - { - value = FRACUNIT; - } - else - { - value = 0; - } - assert(Images[cmd.image_index] != NULL); - - FTexture *fg = Images[cmd.image_index]; - FTexture *bg = (cmd.special != -1) ? Images[cmd.special] : NULL; - int x, y, w, h; - int cx, cy, cw, ch, cr, cb; - - // These still need to be caclulated for the clear call. - if(!block.fullScreenOffsets) - { - // Calc real screen coordinates for bar - x = *(cmd.x + ST_X + xOffset) << FRACBITS; - y = *(cmd.y + ST_Y + yOffset) << FRACBITS; - w = fg->GetScaledWidth() << FRACBITS; - h = fg->GetScaledHeight() << FRACBITS; - if (Scaled) - { - screen->VirtualToRealCoords(x, y, w, h, 320, 200, true); - } - x >>= FRACBITS; - y >>= FRACBITS; - w = (w + FRACUNIT/2) >> FRACBITS; - h = (h + FRACUNIT/2) >> FRACBITS; - } - else - { - ADJUST_RELCENTER(cmd.x,cmd.y,x,y) - - x += xOffset; - y += yOffset; - w = fg->GetScaledWidth(); - h = fg->GetScaledHeight(); - - if(vid_fps && x < 0 && y >= 0) - y += 10; - } - - if(cmd.special3 != 0) - { - //Draw the whole foreground - DrawGraphic(fg, cmd.x, cmd.y, xOffset, yOffset, alpha, block.fullScreenOffsets); - } - else - { - // Draw background - if (bg != NULL && bg->GetScaledWidth() == fg->GetScaledWidth() && bg->GetScaledHeight() == fg->GetScaledHeight()) - { - DrawGraphic(bg, cmd.x, cmd.y, xOffset, yOffset, alpha, block.fullScreenOffsets); - } - else - { - int clx = x; - int cly = y; - int clw = x + w; - int clh = y + h; - if(block.fullScreenOffsets) - { - clx = x < 0 ? SCREENWIDTH + x : x; - cly = y < 0 ? SCREENHEIGHT + y : y; - clw = clx+w > SCREENWIDTH ? SCREENWIDTH-clx : clx+w; - clh = cly+h > SCREENHEIGHT ? SCREENHEIGHT-cly : cly+h; - } - screen->Clear(clx, cly, clw, clh, GPalette.BlackIndex, 0); - } - } - - if(!block.fullScreenOffsets) - { - // Calc clipping rect for background - cx = *(cmd.x + ST_X + cmd.special3 + xOffset) << FRACBITS; - cy = *(cmd.y + ST_Y + cmd.special3 + yOffset) << FRACBITS; - cw = (fg->GetScaledWidth() - fg->GetScaledLeftOffset() - cmd.special3 * 2) << FRACBITS; - ch = (fg->GetScaledHeight() - fg->GetScaledTopOffset() - cmd.special3 * 2) << FRACBITS; - if (Scaled) - { - screen->VirtualToRealCoords(cx, cy, cw, ch, 320, 200, true); - } - cx >>= FRACBITS; - cy >>= FRACBITS; - cw = (cw + FRACUNIT/2) >> FRACBITS; - ch = (ch + FRACUNIT/2) >> FRACBITS; - } - else - { - ADJUST_RELCENTER(cmd.x,cmd.y,cx,cy) - - cx += cmd.special3 + xOffset; - cy += cmd.special3 + yOffset; - cw = fg->GetScaledWidth() - fg->GetScaledLeftOffset() - cmd.special3 * 2; - ch = fg->GetScaledHeight() - fg->GetScaledTopOffset() - cmd.special3 * 2; - if(vid_fps && x < 0 && y >= 0) - y += 10; - } - - if (horizontal) - { - if ((cmd.special3 != 0 && reverse) || (cmd.special3 == 0 && !reverse)) - { // left to right - cr = cx + FixedMul(cw, value); - } - else - { // right to left - cr = cx + cw; - cx += FixedMul(cw, FRACUNIT - value); - } - cb = cy + ch; - } - else - { - if ((cmd.special3 != 0 && reverse) || (cmd.special3 == 0 && !reverse)) - { // bottom to top - cb = cy + ch; - cy += FixedMul(ch, FRACUNIT - value); - } - else - { // top to bottom - cb = cy + FixedMul(ch, value); - } - cr = cx + cw; - } - // Fix the clipping for fullscreenoffsets. - if(block.fullScreenOffsets && y < 0) - { - cy = hud_scale ? SCREENHEIGHT + (cy*CleanYfac) : SCREENHEIGHT + cy; - cb = hud_scale ? SCREENHEIGHT + (cb*CleanYfac) : SCREENHEIGHT + cb; - } - else if(block.fullScreenOffsets && hud_scale) - { - cy *= CleanYfac; - cb *= CleanYfac; - } - if(block.fullScreenOffsets && x < 0) - { - cx = hud_scale ? SCREENWIDTH + (cx*CleanXfac) : SCREENWIDTH + cx; - cr = hud_scale ? SCREENWIDTH + (cr*CleanXfac) : SCREENWIDTH + cr; - } - else if(block.fullScreenOffsets && hud_scale) - { - cx *= CleanXfac; - cr *= CleanXfac; - } - - // Draw background - if(cmd.special3 != 0) - { - if (bg != NULL && bg->GetScaledWidth() == fg->GetScaledWidth() && bg->GetScaledHeight() == fg->GetScaledHeight()) - { - if(!block.fullScreenOffsets) - { - screen->DrawTexture(bg, x, y, - DTA_DestWidth, w, - DTA_DestHeight, h, - DTA_ClipLeft, cx, - DTA_ClipTop, cy, - DTA_ClipRight, cr, - DTA_ClipBottom, cb, - DTA_Alpha, alpha, - TAG_DONE); - } - else - { - screen->DrawTexture(bg, x, y, - DTA_DestWidth, w, - DTA_DestHeight, h, - DTA_ClipLeft, cx, - DTA_ClipTop, cy, - DTA_ClipRight, cr, - DTA_ClipBottom, cb, - DTA_Alpha, alpha, - DTA_HUDRules, HUD_Normal, - TAG_DONE); - } - } - else - { - screen->Clear(cx, cy, cr, cb, GPalette.BlackIndex, 0); - } - } - else - { - if(!block.fullScreenOffsets) - { - screen->DrawTexture(fg, x, y, - DTA_DestWidth, w, - DTA_DestHeight, h, - DTA_ClipLeft, cx, - DTA_ClipTop, cy, - DTA_ClipRight, cr, - DTA_ClipBottom, cb, - DTA_Alpha, alpha, - TAG_DONE); - } - else - { - screen->DrawTexture(fg, x, y, - DTA_DestWidth, w, - DTA_DestHeight, h, - DTA_ClipLeft, cx, - DTA_ClipTop, cy, - DTA_ClipRight, cr, - DTA_ClipBottom, cb, - DTA_Alpha, alpha, - DTA_HUDRules, HUD_Normal, - TAG_DONE); - } - } - break; - } - case SBARINFO_DRAWGEM: - { - int value = (cmd.flags & DRAWGEM_ARMOR) ? armorAmount : health; - int max = (cmd.flags & DRAWGEM_ARMOR) ? 100 : CPlayer->mo->GetMaxHealth() + CPlayer->stamina; - bool wiggle = false; - bool translate = !!(cmd.flags & DRAWGEM_TRANSLATABLE); - if(max != 0 || value < 0) - { - value = (value*100)/max; - if(value > 100) - value = 100; - } - else - { - value = 0; - } - value = (cmd.flags & DRAWGEM_REVERSE) ? 100 - value : value; - if(health != CPlayer->health) - { - wiggle = !!(cmd.flags & DRAWGEM_WIGGLE); - } - DrawGem(Images[cmd.special], Images[cmd.image_index], value, cmd.x, cmd.y, xOffset, yOffset, alpha, block.fullScreenOffsets, cmd.special2, cmd.special3, cmd.special4+1, wiggle, translate); - break; - } - case SBARINFO_DRAWSHADER: - { - FBarShader *const shaders[4] = - { - &shader_horz_normal, &shader_horz_reverse, - &shader_vert_normal, &shader_vert_reverse - }; - bool vertical = !!(cmd.flags & DRAWSHADER_VERTICAL); - bool reverse = !!(cmd.flags & DRAWSHADER_REVERSE); - SBarInfoCoordinate x = cmd.x + xOffset; - SBarInfoCoordinate y = cmd.y + yOffset; - int w = cmd.special; - int h = cmd.special2; - if(!block.fullScreenOffsets) - { - x += ST_X; - y += ST_Y; - if(Scaled) - { - int tmpX = *x; - int tmpY = *y; - screen->VirtualToRealCoordsInt(tmpX, tmpY, w, h, 320, 200, true); - x.SetCoord(tmpX); - y.SetCoord(tmpY); - } - } - else - { - if(vid_fps && *x < 0 && *y >= 0) - y += 10; - } - if(!block.fullScreenOffsets) - { - screen->DrawTexture (shaders[(vertical << 1) + reverse], *x, *y, - DTA_DestWidth, w, - DTA_DestHeight, h, - DTA_Alpha, alpha, - DTA_AlphaChannel, true, - DTA_FillColor, 0, - TAG_DONE); - } - else - { - int rx, ry; - ADJUST_RELCENTER(x,y,rx,ry) - - screen->DrawTexture (shaders[(vertical << 1) + reverse], rx, ry, - DTA_DestWidth, w, - DTA_DestHeight, h, - DTA_Alpha, alpha, - DTA_AlphaChannel, true, - DTA_FillColor, 0, - DTA_HUDRules, HUD_Normal, - TAG_DONE); - } - break; - } - case SBARINFO_DRAWSTRING: - if(drawingFont != cmd.font) - { - drawingFont = cmd.font; - } - DrawString(cmd.string[0], cmd.x - drawingFont->StringWidth(cmd.string[0]), cmd.y, xOffset, yOffset, alpha, block.fullScreenOffsets, cmd.translation, cmd.special); - break; - case SBARINFO_DRAWKEYBAR: - { - bool vertical = !!(cmd.flags & DRAWKEYBAR_VERTICAL); - AInventory *item = CPlayer->mo->Inventory; - if(item == NULL) - break; - int slotOffset = 0; - int rowOffset = 0; - int rowWidth = 0; - for(int i = 0;i < cmd.value+cmd.special2;i++) - { - while(!item->Icon.isValid() || !item->IsKindOf(RUNTIME_CLASS(AKey))) - { - item = item->Inventory; - if(item == NULL) - goto FinishDrawKeyBar; - } - if(i >= cmd.special2) //Should we start drawing? - { - if(!vertical) - { - DrawGraphic(TexMan[item->Icon], cmd.x+slotOffset, cmd.y+rowOffset, xOffset, yOffset, alpha, block.fullScreenOffsets); - rowWidth = cmd.special4 == -1 ? TexMan[item->Icon]->GetScaledHeight()+2 : cmd.special4; - } - else - { - DrawGraphic(TexMan[item->Icon], cmd.x+rowOffset, cmd.y+slotOffset, xOffset, yOffset, alpha, block.fullScreenOffsets); - rowWidth = cmd.special4 == -1 ? TexMan[item->Icon]->GetScaledWidth()+2 : cmd.special4; - } - - // If cmd.special is -1 then the slot size is auto detected - if(cmd.special == -1) - { - if(!vertical) - slotOffset += TexMan[item->Icon]->GetScaledWidth() + 2; - else - slotOffset += TexMan[item->Icon]->GetScaledHeight() + 2; - } - else - slotOffset += cmd.special; - - if(cmd.special3 > 0 && (i % cmd.special3 == cmd.special3-1)) - { - if(cmd.flags & DRAWKEYBAR_REVERSEROWS) - rowOffset -= rowWidth; - else - rowOffset += rowWidth; - rowWidth = 0; - slotOffset = 0; - } - } - - item = item->Inventory; - if(item == NULL) - break; - } - FinishDrawKeyBar: - break; - } - case SBARINFO_GAMEMODE: - if(((cmd.flags & GAMETYPE_SINGLEPLAYER) && !multiplayer) || - ((cmd.flags & GAMETYPE_DEATHMATCH) && deathmatch) || - ((cmd.flags & GAMETYPE_COOPERATIVE) && multiplayer && !deathmatch) || - ((cmd.flags & GAMETYPE_TEAMGAME) && teamplay)) - { - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - } - break; - case SBARINFO_PLAYERCLASS: - { - if(CPlayer->cls == NULL) break; //No class so we can not continue - int spawnClass = CPlayer->cls->ClassIndex; - if(cmd.special == spawnClass || cmd.special2 == spawnClass || cmd.special3 == spawnClass) - { - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - } - break; - } - case SBARINFO_ASPECTRATIO: - if(CheckRatio(screen->GetWidth(), screen->GetHeight()) == cmd.value) - { - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - } - break; - case SBARINFO_ISSELECTED: - if(CPlayer->ReadyWeapon != NULL) - { - const PClass *weapon1 = PClass::FindClass(cmd.string[0]); - const PClass *weapon2 = PClass::FindClass(cmd.string[1]); - if(weapon2 != NULL) - { - if((cmd.flags & SBARINFOEVENT_NOT) && (weapon1 != CPlayer->ReadyWeapon->GetClass() && weapon2 != CPlayer->ReadyWeapon->GetClass())) - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - else if(!(cmd.flags & SBARINFOEVENT_NOT) && (weapon1 == CPlayer->ReadyWeapon->GetClass() || weapon2 == CPlayer->ReadyWeapon->GetClass())) - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - } - else - { - if(!(cmd.flags & SBARINFOEVENT_NOT) && weapon1 == CPlayer->ReadyWeapon->GetClass()) - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - else if((cmd.flags & SBARINFOEVENT_NOT) && weapon1 != CPlayer->ReadyWeapon->GetClass()) - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - } - } - break; - case SBARINFO_USESAMMO: - if ((CPlayer->ReadyWeapon != NULL && (CPlayer->ReadyWeapon->AmmoType1 != NULL || CPlayer->ReadyWeapon->AmmoType2 != NULL)) ^ - !!(cmd.flags & SBARINFOEVENT_NOT)) - { - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - } - break; - case SBARINFO_USESSECONDARYAMMO: - if((CPlayer->ReadyWeapon != NULL && CPlayer->ReadyWeapon->AmmoType2 != NULL && CPlayer->ReadyWeapon->AmmoType2 != CPlayer->ReadyWeapon->AmmoType1 && !(cmd.flags & SBARINFOEVENT_NOT)) || - ((CPlayer->ReadyWeapon == NULL || CPlayer->ReadyWeapon->AmmoType2 == NULL || CPlayer->ReadyWeapon->AmmoType2 == CPlayer->ReadyWeapon->AmmoType1) && cmd.flags & SBARINFOEVENT_NOT)) - { - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - } - break; - case SBARINFO_HASWEAPONPIECE: - { - AInventory *inv; - AWeaponHolder *hold; - const PClass *weapon = PClass::FindClass(cmd.string[0]); - for(inv = CPlayer->mo->Inventory;inv != NULL;inv=inv->Inventory) - { - if(inv->IsKindOf(RUNTIME_CLASS(AWeaponHolder))) - { - hold = static_cast(inv); - if(hold->PieceWeapon == weapon) - { - if(hold->PieceMask & (1 << (cmd.value-1))) - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - break; - } - } - } - break; - } - case SBARINFO_INVENTORYBARNOTVISIBLE: - if(CPlayer->inventorytics <= 0 || (level.flags & LEVEL_NOINVENTORYBAR)) - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - break; - case SBARINFO_WEAPONAMMO: - if(CPlayer->ReadyWeapon != NULL) - { - const PClass *AmmoType1 = CPlayer->ReadyWeapon->AmmoType1; - const PClass *AmmoType2 = CPlayer->ReadyWeapon->AmmoType2; - const PClass *IfAmmo1 = PClass::FindClass(cmd.string[0]); - const PClass *IfAmmo2 = (cmd.flags & SBARINFOEVENT_OR) || (cmd.flags & SBARINFOEVENT_AND) ? - PClass::FindClass(cmd.string[1]) : NULL; - bool usesammo1 = (AmmoType1 != NULL); - bool usesammo2 = (AmmoType2 != NULL); - if(!(cmd.flags & SBARINFOEVENT_NOT) && !usesammo1 && !usesammo2) //if the weapon doesn't use ammo don't go though the trouble. - { - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - break; - } - //Or means only 1 ammo type needs to match and means both need to match. - if((cmd.flags & SBARINFOEVENT_OR) || (cmd.flags & SBARINFOEVENT_AND)) - { - bool match1 = ((usesammo1 && (AmmoType1 == IfAmmo1 || AmmoType1 == IfAmmo2)) || !usesammo1); - bool match2 = ((usesammo2 && (AmmoType2 == IfAmmo1 || AmmoType2 == IfAmmo2)) || !usesammo2); - if(((cmd.flags & SBARINFOEVENT_OR) && (match1 || match2)) || ((cmd.flags & SBARINFOEVENT_AND) && (match1 && match2))) - { - if(!(cmd.flags & SBARINFOEVENT_NOT)) - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - } - else if(cmd.flags & SBARINFOEVENT_NOT) - { - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - } - } - else //Every thing here could probably be one long if statement but then it would be more confusing. - { - if((usesammo1 && (AmmoType1 == IfAmmo1)) || (usesammo2 && (AmmoType2 == IfAmmo1))) - { - if(!(cmd.flags & SBARINFOEVENT_NOT)) - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - } - else if(cmd.flags & SBARINFOEVENT_NOT) - { - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - } - } - } - break; - case SBARINFO_ININVENTORY: - { - AInventory *item1 = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0])); - AInventory *item2 = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[1])); - if (item1 != NULL && cmd.special2 > 0 && item1->Amount < cmd.special2) item1 = NULL; - if (item2 != NULL && cmd.special3 > 0 && item2->Amount < cmd.special3) item2 = NULL; - if(cmd.flags & SBARINFOEVENT_AND) - { - if((item1 != NULL && item2 != NULL) && !(cmd.flags & SBARINFOEVENT_NOT)) - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - else if((item1 == NULL || item2 == NULL) && (cmd.flags & SBARINFOEVENT_NOT)) - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - } - else if(cmd.flags & SBARINFOEVENT_OR) - { - if((item1 != NULL || item2 != NULL) && !(cmd.flags & SBARINFOEVENT_NOT)) - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - else if((item1 == NULL && item2 == NULL) && (cmd.flags & SBARINFOEVENT_NOT)) - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - } - else if((item1 != NULL) && !(cmd.flags & SBARINFOEVENT_NOT)) - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - else if((item1 == NULL) && (cmd.flags & SBARINFOEVENT_NOT)) - doCommands(cmd.subBlock, xOffset, yOffset, alpha); - break; - } - } - } -} - -//draws an image with the specified flags -void DSBarInfo::DrawGraphic(FTexture* texture, SBarInfoCoordinate x, SBarInfoCoordinate y, int xOffset, int yOffset, int alpha, bool fullScreenOffsets, - bool translate, bool dim, int offsetflags) //flags -{ - if (texture == NULL) - return; - - if(offsetflags & DRAWIMAGE_OFFSET_CENTER) - { - x -= (texture->GetScaledWidth()/2)-texture->LeftOffset; - y -= (texture->GetScaledHeight()/2)-texture->TopOffset; - } - - x += xOffset; - y += yOffset; - int w, h; - if(!fullScreenOffsets) - { - // I'll handle the conversion from fixed to int myself for more control - fixed_t fx = (x + ST_X).Coordinate() << FRACBITS; - fixed_t fy = (y + ST_Y).Coordinate() << FRACBITS; - fixed_t fw = texture->GetScaledWidth() << FRACBITS; - fixed_t fh = texture->GetScaledHeight() << FRACBITS; - if(Scaled) - screen->VirtualToRealCoords(fx, fy, fw, fh, 320, 200, true); - // Round to nearest - w = (fw + (FRACUNIT>>1)) >> FRACBITS; - h = (fh + (FRACUNIT>>1)) >> FRACBITS; - screen->DrawTexture(texture, (fx >> FRACBITS), (fy >> FRACBITS), - DTA_DestWidth, w, - DTA_DestHeight, h, - DTA_Translation, translate ? getTranslation() : 0, - DTA_ColorOverlay, dim ? DIM_OVERLAY : 0, - DTA_CenterBottomOffset, (offsetflags & DRAWIMAGE_OFFSET_CENTERBOTTOM), - DTA_Alpha, alpha, - TAG_DONE); - } - else - { - int rx, ry; - ADJUST_RELCENTER(x,y,rx,ry) - - w = texture->GetScaledWidth(); - h = texture->GetScaledHeight(); - if(vid_fps && rx < 0 && ry >= 0) - ry += 10; - screen->DrawTexture(texture, rx, ry, - DTA_DestWidth, w, - DTA_DestHeight, h, - DTA_Translation, translate ? getTranslation() : 0, - DTA_ColorOverlay, dim ? DIM_OVERLAY : 0, - DTA_CenterBottomOffset, (offsetflags & DRAWIMAGE_OFFSET_CENTERBOTTOM), - DTA_HUDRules, HUD_Normal, - DTA_Alpha, alpha, - TAG_DONE); - } -} - -void DSBarInfo::DrawString(const char* str, SBarInfoCoordinate x, SBarInfoCoordinate y, int xOffset, int yOffset, int alpha, bool fullScreenOffsets, EColorRange translation, int spacing, bool drawshadow) -{ - x += spacing; - int ax = *x; - int ay = *y; - if(fullScreenOffsets) - { - ADJUST_RELCENTER(x,y,ax,ay) - } - while(*str != '\0') - { - if(*str == ' ') - { - ax += drawingFont->GetSpaceWidth(); - str++; - continue; - } - int width; - if(script->spacingCharacter == '\0') //No monospace? - width = drawingFont->GetCharWidth((int) *str); - else - width = drawingFont->GetCharWidth((int) script->spacingCharacter); - FTexture* character = drawingFont->GetChar((int) *str, &width); - if(character == NULL) //missing character. - { - str++; - continue; - } - if(script->spacingCharacter == '\0') //If we are monospaced lets use the offset - ax += (character->LeftOffset+1); //ignore x offsets since we adapt to character size - - int rx, ry, rw, rh; - rx = ax + xOffset; - ry = ay + yOffset; - rw = character->GetScaledWidth(); - rh = character->GetScaledHeight(); - if(!fullScreenOffsets) - { - rx += ST_X; - ry += ST_Y; - if(Scaled) - screen->VirtualToRealCoordsInt(rx, ry, rw, rh, 320, 200, true); - } - else - { - if(vid_fps && ax < 0 && ay >= 0) - ry += 10; - } - if(drawshadow) - { - int salpha = fixed_t(((double) alpha / (double) FRACUNIT) * ((double) HR_SHADOW / (double) FRACUNIT) * FRACUNIT); - if(!fullScreenOffsets) - { - screen->DrawTexture(character, rx+2, ry+2, - DTA_DestWidth, rw, - DTA_DestHeight, rh, - DTA_Alpha, salpha, - DTA_FillColor, 0, - TAG_DONE); - } - else - { - screen->DrawTexture(character, rx+2, ry+2, - DTA_DestWidth, rw, - DTA_DestHeight, rh, - DTA_Alpha, salpha, - DTA_HUDRules, HUD_Normal, - DTA_FillColor, 0, - TAG_DONE); - } - } - if(!fullScreenOffsets) - { - screen->DrawTexture(character, rx, ry, - DTA_DestWidth, rw, - DTA_DestHeight, rh, - DTA_Translation, drawingFont->GetColorTranslation(translation), - DTA_Alpha, alpha, - TAG_DONE); - } - else - { - screen->DrawTexture(character, rx, ry, - DTA_DestWidth, rw, - DTA_DestHeight, rh, - DTA_Translation, drawingFont->GetColorTranslation(translation), - DTA_Alpha, alpha, - DTA_HUDRules, HUD_Normal, - TAG_DONE); - } - if(script->spacingCharacter == '\0') - ax += width + spacing - (character->LeftOffset+1); - else //width gets changed at the call to GetChar() - ax += drawingFont->GetCharWidth((int) script->spacingCharacter) + spacing; - str++; - } -} - -//draws the specified number up to len digits -void DSBarInfo::DrawNumber(int num, int len, SBarInfoCoordinate x, SBarInfoCoordinate y, int xOffset, int yOffset, int alpha, bool fullScreenOffsets, EColorRange translation, int spacing, bool fillzeros, bool drawshadow) -{ - FString value; - // 10^9 is a largest we can hold in a 32-bit int. So if we go any larger we have to toss out the positions limit. - int maxval = len <= 9 ? (int) ceil(pow(10., len))-1 : INT_MAX; - if(!fillzeros || len == 1) - num = clamp(num, -maxval, maxval); - else //The community wanted negatives to take the last digit, but we can only do this if there is room - num = clamp(num, len <= 9 ? (int) -(ceil(pow(10., len-1))-1) : INT_MIN, maxval); - value.Format("%d", num); - if(fillzeros) - { - if(num < 0) //We don't want the negative just yet - value.Format("%d", -num); - while(fillzeros && value.Len() < (unsigned int) len) - { - if(num < 0 && value.Len() == (unsigned int) (len-1)) - value.Insert(0, "-"); - else - value.Insert(0, "0"); - } - } - if(script->spacingCharacter == '\0') - x -= int(drawingFont->StringWidth(value)+(spacing * value.Len())); - else //monospaced, so just multiplay the character size - x -= int((drawingFont->GetCharWidth((int) script->spacingCharacter) + spacing) * value.Len()); - DrawString(value, x, y, xOffset, yOffset, alpha, fullScreenOffsets, translation, spacing, drawshadow); -} - -//draws the mug shot -void DSBarInfo::DrawFace(const char *defaultFace, int accuracy, int stateflags, SBarInfoCoordinate x, SBarInfoCoordinate y, int xOffset, int yOffset, int alpha, bool fullScreenOffsets) -{ - FTexture *face = MugShot.GetFace(CPlayer, defaultFace, accuracy, stateflags); - if (face != NULL) - { - DrawGraphic(face, x, y, xOffset, yOffset, alpha, fullScreenOffsets); - } -} - -void DSBarInfo::DrawInventoryBar(int type, int num, SBarInfoCoordinate x, SBarInfoCoordinate y, int xOffset, int yOffset, int alpha, bool fullScreenOffsets, bool alwaysshow, - SBarInfoCoordinate counterx, SBarInfoCoordinate countery, EColorRange translation, bool drawArtiboxes, bool noArrows, bool alwaysshowcounter, int bgalpha, bool vertical) -{ //yes, there is some Copy & Paste here too - AInventory *item; - int i; - int spacing = 0; - if(!vertical) - spacing = (type != GAME_Strife) ? Images[invBarOffset + imgARTIBOX]->GetScaledWidth() + 1 : Images[invBarOffset + imgCURSOR]->GetScaledWidth() - 1; - else - spacing = (type != GAME_Strife) ? Images[invBarOffset + imgARTIBOX]->GetScaledHeight() + 1 : Images[invBarOffset + imgCURSOR]->GetScaledHeight() - 1; - - // If the player has no artifacts, don't draw the bar - CPlayer->mo->InvFirst = ValidateInvFirst(num); - if(CPlayer->mo->InvFirst != NULL || alwaysshow) - { - for(item = CPlayer->mo->InvFirst, i = 0; item != NULL && i < num; item = item->NextInv(), ++i) - { - SBarInfoCoordinate rx = x + (!vertical ? i*spacing : 0); - SBarInfoCoordinate ry = y + (vertical ? i*spacing : 0); - if(drawArtiboxes) - { - DrawGraphic(Images[invBarOffset + imgARTIBOX], rx, ry, xOffset, yOffset, bgalpha, fullScreenOffsets); - } - if(type != GAME_Strife) //Strife draws the cursor before the icons - DrawGraphic(TexMan(item->Icon), rx, ry, xOffset, yOffset, alpha, fullScreenOffsets, false, item->Amount <= 0); - if(item == CPlayer->mo->InvSel) - { - if(type == GAME_Heretic) - { - DrawGraphic(Images[invBarOffset + imgSELECTBOX], rx, ry+29, xOffset, yOffset, alpha, fullScreenOffsets); - } - else if(type == GAME_Hexen) - { - DrawGraphic(Images[invBarOffset + imgSELECTBOX], rx, ry-1, xOffset, yOffset, alpha, fullScreenOffsets); - } - else if(type == GAME_Strife) - { - DrawGraphic(Images[invBarOffset + imgCURSOR], rx-6, ry-2, xOffset, yOffset, alpha, fullScreenOffsets); - } - else - { - DrawGraphic(Images[invBarOffset + imgSELECTBOX], rx, ry, xOffset, yOffset, alpha, fullScreenOffsets); - } - } - if(type == GAME_Strife) - DrawGraphic(TexMan(item->Icon), rx, ry, xOffset, yOffset, alpha, fullScreenOffsets, false, item->Amount <= 0); - if(alwaysshowcounter || item->Amount != 1) - { - DrawNumber(item->Amount, 3, counterx+(!vertical ? i*spacing : 0), countery+(vertical ? i*spacing : 0), xOffset, yOffset, alpha, fullScreenOffsets, translation); - } - } - for (; i < num && drawArtiboxes; ++i) - { - DrawGraphic(Images[invBarOffset + imgARTIBOX], x + (!vertical ? (i*spacing) : 0), y + (vertical ? (i*spacing) : 0), xOffset, yOffset, bgalpha, fullScreenOffsets); - } - // Is there something to the left? - if (!noArrows && CPlayer->mo->FirstInv() != CPlayer->mo->InvFirst) - { - int offset = type != GAME_Strife ? -12 : 14; - DrawGraphic(Images[!(gametic & 4) ? - invBarOffset + imgINVLFGEM1 : invBarOffset + imgINVLFGEM2], x + (!vertical ? offset : 0), y + (vertical ? offset : 0), xOffset, yOffset, alpha, fullScreenOffsets); - } - // Is there something to the right? - if (!noArrows && item != NULL) - { - int offset = type != GAME_Strife ? num*31+2 : num*35-4; - DrawGraphic(Images[!(gametic & 4) ? - invBarOffset + imgINVRTGEM1 : invBarOffset + imgINVRTGEM2], x + (!vertical ? offset : 0), y + (vertical ? offset : 0), xOffset, yOffset, alpha, fullScreenOffsets); - } - } -} - -//draws heretic/hexen style life gems -void DSBarInfo::DrawGem(FTexture* chain, FTexture* gem, int value, SBarInfoCoordinate x, SBarInfoCoordinate y, int xOffset, int yOffset, int alpha, bool fullScreenOffsets, int padleft, int padright, int chainsize, - bool wiggle, bool translate) -{ - if(chain == NULL) - return; - if(value > 100) - value = 100; - else if(value < 0) - value = 0; - if(wiggle) - y += chainWiggle; - int chainWidth = chain->GetWidth(); - int offset = (int) (((double) (chainWidth-padleft-padright)/100)*value); - DrawGraphic(chain, x+(offset%chainsize), y, xOffset, yOffset, alpha, fullScreenOffsets); - if(gem != NULL) - DrawGraphic(gem, x+padleft+offset, y, xOffset, yOffset, alpha, fullScreenOffsets, translate); -} - -FRemapTable* DSBarInfo::getTranslation() -{ - if(gameinfo.gametype & GAME_Raven) - return translationtables[TRANSLATION_PlayersExtra][int(CPlayer - players)]; - return translationtables[TRANSLATION_Players][int(CPlayer - players)]; -} - -IMPLEMENT_CLASS(DSBarInfo); - -DBaseStatusBar *CreateCustomStatusBar (int script) -{ - if(SBarInfoScript[script] == NULL) - I_FatalError("Tried to create a status bar with no script!"); - return new DSBarInfo(SBarInfoScript[script]); -} - -DBaseStatusBar *CreateDoomStatusBar () -{ - return new DSBarInfo(SBarInfoScript[1]); -} diff --git a/src/g_shared/sbarinfo_parser.cpp b/src/g_shared/sbarinfo_parser.cpp deleted file mode 100644 index b6130826b..000000000 --- a/src/g_shared/sbarinfo_parser.cpp +++ /dev/null @@ -1,1622 +0,0 @@ -/* -** sbarinfo_parser.cpp -** -** Reads custom status bar definitions. -** -**--------------------------------------------------------------------------- -** Copyright 2008 Braden Obrzut -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ - -#include "doomtype.h" -#include "doomstat.h" -#include "sc_man.h" -#include "v_font.h" -#include "w_wad.h" -#include "d_player.h" -#include "sbar.h" -#include "sbarinfo.h" -#include "templates.h" -#include "m_random.h" -#include "gi.h" -#include "i_system.h" -#include "g_level.h" -#include "p_acs.h" - -SBarInfo *SBarInfoScript[2] = {NULL,NULL}; - -static const char *SBarInfoTopLevel[] = -{ - "base", - "height", - "interpolatehealth", - "interpolatearmor", - "completeborder", - "monospacefonts", - "lowerhealthcap", - "statusbar", - "mugshot", - "createpopup", - NULL -}; - -static const char *StatusBars[] = -{ - "none", - "fullscreen", - "normal", - "automap", - "inventory", - "inventoryfullscreen", - "popuplog", - "popupkeys", - "popupstatus", - NULL -}; - -static const char *SBarInfoRoutineLevel[] = -{ - "drawimage", - "drawnumber", - "drawswitchableimage", - "drawmugshot", - "drawselectedinventory", - "drawinventorybar", - "drawbar", - "drawgem", - "drawshader", - "drawstring", - "drawkeybar", - "gamemode", - "playerclass", - "aspectratio", - "isselected", - "usesammo", - "usessecondaryammo", - "hasweaponpiece", - "inventorybarnotvisible", - "weaponammo", //event - "ininventory", - NULL -}; - -static void FreeSBarInfoScript() -{ - for(int i = 0;i < 2;i++) - { - if (SBarInfoScript[i] != NULL) - { - delete SBarInfoScript[i]; - SBarInfoScript[i] = NULL; - } - } -} - -void SBarInfo::Load() -{ - if(gameinfo.statusbar.IsNotEmpty()) - { - int lump = Wads.CheckNumForFullName(gameinfo.statusbar, true); - if(lump != -1) - { - Printf ("ParseSBarInfo: Loading default status bar definition.\n"); - if(SBarInfoScript[SCRIPT_DEFAULT] == NULL) - SBarInfoScript[SCRIPT_DEFAULT] = new SBarInfo(lump); - else - SBarInfoScript[SCRIPT_DEFAULT]->ParseSBarInfo(lump); - } - } - - if(Wads.CheckNumForName("SBARINFO") != -1) - { - Printf ("ParseSBarInfo: Loading custom status bar definition.\n"); - int lastlump, lump; - lastlump = 0; - while((lump = Wads.FindLump("SBARINFO", &lastlump)) != -1) - { - if(SBarInfoScript[SCRIPT_CUSTOM] == NULL) - SBarInfoScript[SCRIPT_CUSTOM] = new SBarInfo(lump); - else //We now have to load multiple SBarInfo Lumps so the 2nd time we need to use this method instead. - SBarInfoScript[SCRIPT_CUSTOM]->ParseSBarInfo(lump); - } - } - atterm(FreeSBarInfoScript); -} - -//SBarInfo Script Reader -void SBarInfo::ParseSBarInfo(int lump) -{ - gameType = gameinfo.gametype; - bool baseSet = false; - FScanner sc(lump); - sc.SetCMode(true); - while(sc.CheckToken(TK_Identifier) || sc.CheckToken(TK_Include)) - { - if(sc.TokenType == TK_Include) - { - sc.MustGetToken(TK_StringConst); - int lump = Wads.CheckNumForFullName(sc.String, true); - if (lump == -1) - sc.ScriptError("Lump '%s' not found", sc.String); - ParseSBarInfo(lump); - continue; - } - switch(sc.MustMatchString(SBarInfoTopLevel)) - { - case SBARINFO_BASE: - baseSet = true; - if(!sc.CheckToken(TK_None)) - sc.MustGetToken(TK_Identifier); - if(sc.Compare("Doom")) - { - int lump = Wads.CheckNumForFullName("sbarinfo/doom.txt", true); - if(lump == -1) - sc.ScriptError("Standard Doom Status Bar not found."); - ParseSBarInfo(lump); - } - else if(sc.Compare("Heretic")) - gameType = GAME_Heretic; - else if(sc.Compare("Hexen")) - gameType = GAME_Hexen; - else if(sc.Compare("Strife")) - gameType = GAME_Strife; - else if(sc.Compare("None")) - gameType = GAME_Any; - else - sc.ScriptError("Bad game name: %s", sc.String); - sc.MustGetToken(';'); - break; - case SBARINFO_HEIGHT: - sc.MustGetToken(TK_IntConst); - this->height = sc.Number; - sc.MustGetToken(';'); - break; - case SBARINFO_INTERPOLATEHEALTH: //mimics heretics interpolated health values. - if(sc.CheckToken(TK_True)) - { - interpolateHealth = true; - } - else - { - sc.MustGetToken(TK_False); - interpolateHealth = false; - } - if(sc.CheckToken(',')) //speed param - { - sc.MustGetToken(TK_IntConst); - this->interpolationSpeed = sc.Number; - } - sc.MustGetToken(';'); - break; - case SBARINFO_INTERPOLATEARMOR: //Since interpolatehealth is such a popular command - if(sc.CheckToken(TK_True)) - { - interpolateArmor = true; - } - else - { - sc.MustGetToken(TK_False); - interpolateArmor = false; - } - if(sc.CheckToken(',')) //speed - { - sc.MustGetToken(TK_IntConst); - this->armorInterpolationSpeed = sc.Number; - } - sc.MustGetToken(';'); - break; - case SBARINFO_COMPLETEBORDER: //draws the border instead of an HOM - if(sc.CheckToken(TK_True)) - { - completeBorder = true; - } - else - { - sc.MustGetToken(TK_False); - completeBorder = false; - } - sc.MustGetToken(';'); - break; - case SBARINFO_MONOSPACEFONTS: - if(sc.CheckToken(TK_True)) - { - sc.MustGetToken(','); - sc.MustGetToken(TK_StringConst); - spacingCharacter = sc.String[0]; - } - else - { - sc.MustGetToken(TK_False); - spacingCharacter = '\0'; - sc.MustGetToken(','); - sc.MustGetToken(TK_StringConst); //Don't tell anyone we're just ignoring this ;) - } - sc.MustGetToken(';'); - break; - case SBARINFO_LOWERHEALTHCAP: - if(sc.CheckToken(TK_False)) - { - lowerHealthCap = false; - } - else - { - sc.MustGetToken(TK_True); - lowerHealthCap = true; - } - sc.MustGetToken(';'); - break; - case SBARINFO_STATUSBAR: - { - if(!baseSet) //If the user didn't explicitly define a base, do so now. - gameType = GAME_Any; - int barNum = 0; - if(!sc.CheckToken(TK_None)) - { - sc.MustGetToken(TK_Identifier); - barNum = sc.MustMatchString(StatusBars); - } - this->huds[barNum] = SBarInfoBlock(); - if(sc.CheckToken(',')) - { - while(sc.CheckToken(TK_Identifier)) - { - if(sc.Compare("forcescaled")) - { - this->huds[barNum].forceScaled = true; - } - else if(sc.Compare("fullscreenoffsets")) - { - this->huds[barNum].fullScreenOffsets = true; - } - else - { - sc.ScriptError("Unkown flag '%s'.", sc.String); - } - if(!sc.CheckToken('|') && !sc.CheckToken(',')) - goto FinishStatusBar; //No more args so we must skip over anything else and go to the end. - } - sc.MustGetToken(TK_FloatConst); - this->huds[barNum].alpha = fixed_t(FRACUNIT * sc.Float); - } - FinishStatusBar: - sc.MustGetToken('{'); - if(barNum == STBAR_AUTOMAP) - { - automapbar = true; - } - ParseSBarInfoBlock(sc, this->huds[barNum]); - break; - } - case SBARINFO_MUGSHOT: - { - sc.MustGetToken(TK_StringConst); - FMugShotState state(sc.String); - if(sc.CheckToken(',')) //first loop must be a comma - { - do - { - sc.MustGetToken(TK_Identifier); - if(sc.Compare("health")) - state.bUsesLevels = true; - else if(sc.Compare("health2")) - state.bUsesLevels = state.bHealth2 = true; - else if(sc.Compare("healthspecial")) - state.bUsesLevels = state.bHealthSpecial = true; - else if(sc.Compare("directional")) - state.bDirectional = true; - else - sc.ScriptError("Unknown MugShot state flag '%s'.", sc.String); - } - while(sc.CheckToken(',') || sc.CheckToken('|')); - } - ParseMugShotBlock(sc, state); - int index; - if((index = FindMugShotStateIndex(state.State)) != -1) //We already had this state, remove the old one. - { - MugShotStates.Delete(index); - } - MugShotStates.Push(state); - break; - } - case SBARINFO_CREATEPOPUP: - { - int pop = 0; - sc.MustGetToken(TK_Identifier); - if(sc.Compare("log")) - pop = 0; - else if(sc.Compare("keys")) - pop = 1; - else if(sc.Compare("status")) - pop = 2; - else - sc.ScriptError("Unkown popup: '%s'", sc.String); - Popup &popup = popups[pop]; - sc.MustGetToken(','); - sc.MustGetToken(TK_IntConst); - popup.width = sc.Number; - sc.MustGetToken(','); - sc.MustGetToken(TK_IntConst); - popup.height = sc.Number; - sc.MustGetToken(','); - if(!sc.CheckToken(TK_None)) - { - sc.MustGetToken(TK_Identifier); - if(sc.Compare("slideinbottom")) - { - popup.transition = TRANSITION_SLIDEINBOTTOM; - sc.MustGetToken(','); - sc.MustGetToken(TK_IntConst); - popup.speed = sc.Number; - } - else if(sc.Compare("fade")) - { - popup.transition = TRANSITION_FADE; - sc.MustGetToken(','); - sc.MustGetToken(TK_FloatConst); - popup.speed = fixed_t(FRACUNIT * (1.0 / (35.0 * sc.Float))); - sc.MustGetToken(','); - sc.MustGetToken(TK_FloatConst); - popup.speed2 = fixed_t(FRACUNIT * (1.0 / (35.0 * sc.Float))); - } - else - sc.ScriptError("Unkown transition type: '%s'", sc.String); - } - popup.init(); - sc.MustGetToken(';'); - break; - } - } - } -} - -void SBarInfo::ParseSBarInfoBlock(FScanner &sc, SBarInfoBlock &block) -{ - while(sc.CheckToken(TK_Identifier)) - { - SBarInfoCommand cmd; - - switch(cmd.type = sc.MustMatchString(SBarInfoRoutineLevel)) - { - case SBARINFO_DRAWSWITCHABLEIMAGE: - sc.MustGetToken(TK_Identifier); - if(sc.Compare("weaponslot")) - { - cmd.flags = DRAWIMAGE_WEAPONSLOT; - sc.MustGetToken(TK_IntConst); - cmd.value = sc.Number; - } - else if(sc.Compare("invulnerable")) - { - cmd.flags = DRAWIMAGE_INVULNERABILITY; - } - else if(sc.Compare("keyslot")) - { - cmd.flags = DRAWIMAGE_KEYSLOT; - sc.MustGetToken(TK_IntConst); - cmd.value = sc.Number; - } - else - { - cmd.setString(sc, sc.String, 0); - const PClass* item = PClass::FindClass(sc.String); - if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of Inventory - { - sc.ScriptError("'%s' is not a type of inventory item.", sc.String); - } - } - if(sc.CheckToken(TK_AndAnd)) - { - cmd.flags |= DRAWIMAGE_SWITCHABLE_AND; - if(cmd.flags & DRAWIMAGE_KEYSLOT) - { - sc.MustGetToken(TK_IntConst); - cmd.special4 = sc.Number; - } - else - { - sc.MustGetToken(TK_Identifier); - cmd.setString(sc, sc.String, 1); - const PClass* item = PClass::FindClass(sc.String); - if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of Inventory - { - sc.ScriptError("'%s' is not a type of inventory item.", sc.String); - } - } - sc.MustGetToken(','); - sc.MustGetToken(TK_StringConst); - cmd.special = newImage(sc.String); - sc.MustGetToken(','); - sc.MustGetToken(TK_StringConst); - cmd.special2 = newImage(sc.String); - sc.MustGetToken(','); - sc.MustGetToken(TK_StringConst); - cmd.special3 = newImage(sc.String); - sc.MustGetToken(','); - } - else - { - sc.MustGetToken(','); - sc.MustGetToken(TK_StringConst); - cmd.special = newImage(sc.String); - sc.MustGetToken(','); - } - case SBARINFO_DRAWIMAGE: - { - bool getImage = true; - if(sc.CheckToken(TK_Identifier)) - { - getImage = false; - if(sc.Compare("playericon")) - cmd.flags |= DRAWIMAGE_PLAYERICON; - else if(sc.Compare("ammoicon1")) - cmd.flags |= DRAWIMAGE_AMMO1; - else if(sc.Compare("ammoicon2")) - cmd.flags |= DRAWIMAGE_AMMO2; - else if(sc.Compare("armoricon")) - cmd.flags |= DRAWIMAGE_ARMOR; - else if(sc.Compare("weaponicon")) - cmd.flags |= DRAWIMAGE_WEAPONICON; - else if(sc.Compare("sigil")) - cmd.flags |= DRAWIMAGE_SIGIL; - else if(sc.Compare("hexenarmor")) - { - cmd.flags = DRAWIMAGE_HEXENARMOR; - sc.MustGetToken(TK_Identifier); - if(sc.Compare("armor")) - cmd.value = 0; - else if(sc.Compare("shield")) - cmd.value = 1; - else if(sc.Compare("helm")) - cmd.value = 2; - else if(sc.Compare("amulet")) - cmd.value = 3; - else - sc.ScriptError("Unkown armor type: '%s'", sc.String); - sc.MustGetToken(','); - getImage = true; - } - else if(sc.Compare("translatable")) - { - cmd.flags |= DRAWIMAGE_TRANSLATABLE; - getImage = true; - } - else - { - //sc.CheckToken(TK_Identifier); - cmd.flags |= DRAWIMAGE_INVENTORYICON; - const PClass* item = PClass::FindClass(sc.String); - if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of Inventory - { - sc.ScriptError("'%s' is not a type of inventory item.", sc.String); - } - cmd.sprite_index = ((AInventory *)GetDefaultByType(item))->Icon; - cmd.image_index = -1; - } - } - if(getImage) - { - sc.MustGetToken(TK_StringConst); - cmd.image_index = newImage(sc.String); - cmd.sprite_index.SetInvalid(); - } - sc.MustGetToken(','); - this->getCoordinates(sc, block.fullScreenOffsets, cmd.x, cmd.y); - if(sc.CheckToken(',')) - { - sc.MustGetToken(TK_Identifier); - if(sc.Compare("center")) - cmd.flags |= DRAWIMAGE_OFFSET_CENTER; - else if(sc.Compare("centerbottom")) - cmd.flags |= DRAWIMAGE_OFFSET_CENTERBOTTOM; - else - sc.ScriptError("'%s' is not a valid alignment.", sc.String); - } - sc.MustGetToken(';'); - break; - } - case SBARINFO_DRAWNUMBER: - cmd.special4 = cmd.special3 = -1; - sc.MustGetToken(TK_IntConst); - cmd.special = sc.Number; - sc.MustGetToken(','); - sc.MustGetToken(TK_Identifier); - cmd.font = V_GetFont(sc.String); - if(cmd.font == NULL) - sc.ScriptError("Unknown font '%s'.", sc.String); - sc.MustGetToken(','); - sc.MustGetToken(TK_Identifier); - cmd.translation = this->GetTranslation(sc, sc.String); - sc.MustGetToken(','); - if(sc.CheckToken(TK_IntConst)) - { - cmd.value = sc.Number; - sc.MustGetToken(','); - } - else - { - sc.MustGetToken(TK_Identifier); - if(sc.Compare("health")) - cmd.flags = DRAWNUMBER_HEALTH; - else if(sc.Compare("armor")) - cmd.flags = DRAWNUMBER_ARMOR; - else if(sc.Compare("ammo1")) - cmd.flags = DRAWNUMBER_AMMO1; - else if(sc.Compare("ammo2")) - cmd.flags = DRAWNUMBER_AMMO2; - else if(sc.Compare("ammo")) //request the next string to be an ammo type - { - sc.MustGetToken(TK_Identifier); - cmd.setString(sc, sc.String, 0); - cmd.flags = DRAWNUMBER_AMMO; - const PClass* ammo = PClass::FindClass(sc.String); - if(ammo == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(ammo)) //must be a kind of ammo - { - sc.ScriptError("'%s' is not a type of ammo.", sc.String); - } - } - else if(sc.Compare("ammocapacity")) - { - sc.MustGetToken(TK_Identifier); - cmd.setString(sc, sc.String, 0); - cmd.flags = DRAWNUMBER_AMMOCAPACITY; - const PClass* ammo = PClass::FindClass(sc.String); - if(ammo == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(ammo)) //must be a kind of ammo - { - sc.ScriptError("'%s' is not a type of ammo.", sc.String); - } - } - else if(sc.Compare("frags")) - cmd.flags = DRAWNUMBER_FRAGS; - else if(sc.Compare("kills")) - cmd.flags |= DRAWNUMBER_KILLS; - else if(sc.Compare("monsters")) - cmd.flags |= DRAWNUMBER_MONSTERS; - else if(sc.Compare("items")) - cmd.flags |= DRAWNUMBER_ITEMS; - else if(sc.Compare("totalitems")) - cmd.flags |= DRAWNUMBER_TOTALITEMS; - else if(sc.Compare("secrets")) - cmd.flags |= DRAWNUMBER_SECRETS; - else if(sc.Compare("totalsecrets")) - cmd.flags |= DRAWNUMBER_TOTALSECRETS; - else if(sc.Compare("armorclass")) - cmd.flags |= DRAWNUMBER_ARMORCLASS; - else if(sc.Compare("airtime")) - cmd.flags |= DRAWNUMBER_AIRTIME; - else if(sc.Compare("globalvar")) - { - cmd.flags |= DRAWNUMBER_GLOBALVAR; - sc.MustGetToken(TK_IntConst); - if(sc.Number < 0 || sc.Number >= NUM_GLOBALVARS) - sc.ScriptError("Global variable number out of range: %d", sc.Number); - cmd.value = sc.Number; - } - else if(sc.Compare("globalarray")) //acts like variable[playernumber()] - { - cmd.flags |= DRAWNUMBER_GLOBALARRAY; - sc.MustGetToken(TK_IntConst); - if(sc.Number < 0 || sc.Number >= NUM_GLOBALVARS) - sc.ScriptError("Global variable number out of range: %d", sc.Number); - cmd.value = sc.Number; - } - else if(sc.Compare("poweruptime")) - { - cmd.flags |= DRAWNUMBER_POWERUPTIME; - sc.MustGetToken(TK_Identifier); - cmd.setString(sc, sc.String, 0); - const PClass* item = PClass::FindClass(sc.String); - if(item == NULL || !PClass::FindClass("PowerupGiver")->IsAncestorOf(item)) - { - sc.ScriptError("'%s' is not a type of PowerupGiver.", sc.String); - } - } - else - { - cmd.flags = DRAWNUMBER_INVENTORY; - cmd.setString(sc, sc.String, 0); - const PClass* item = PClass::FindClass(sc.String); - if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of ammo - { - sc.ScriptError("'%s' is not a type of inventory item.", sc.String); - } - } - sc.MustGetToken(','); - } - while(sc.CheckToken(TK_Identifier)) - { - if(sc.Compare("fillzeros")) - cmd.flags |= DRAWNUMBER_FILLZEROS; - else if(sc.Compare("whennotzero")) - cmd.flags |= DRAWNUMBER_WHENNOTZERO; - else if(sc.Compare("drawshadow")) - cmd.flags |= DRAWNUMBER_DRAWSHADOW; - else - sc.ScriptError("Unknown flag '%s'.", sc.String); - if(!sc.CheckToken('|')) - sc.MustGetToken(','); - } - this->getCoordinates(sc, block.fullScreenOffsets, cmd.x, cmd.y); - if(sc.CheckToken(',')) - { - bool needsComma = false; - if(sc.CheckToken(TK_IntConst)) //font spacing - { - cmd.special2 = sc.Number; - needsComma = true; - } - if(!needsComma || sc.CheckToken(',')) //2nd coloring for "low-on" value - { - sc.MustGetToken(TK_Identifier); - cmd.translation2 = this->GetTranslation(sc, sc.String); - sc.MustGetToken(','); - sc.MustGetToken(TK_IntConst); - cmd.special3 = sc.Number; - if(sc.CheckToken(',')) //3rd coloring for "high-on" value - { - sc.MustGetToken(TK_Identifier); - cmd.translation3 = this->GetTranslation(sc, sc.String); - sc.MustGetToken(','); - sc.MustGetToken(TK_IntConst); - cmd.special4 = sc.Number; - } - } - } - sc.MustGetToken(';'); - break; - case SBARINFO_DRAWMUGSHOT: - if(sc.CheckToken(TK_StringConst)) - { - cmd.setString(sc, sc.String, 0, 3, true); - sc.MustGetToken(','); - } - sc.MustGetToken(TK_IntConst); //accuracy - if(sc.Number < 1 || sc.Number > 9) - sc.ScriptError("Expected a number between 1 and 9, got %d instead.", sc.Number); - cmd.special = sc.Number; - sc.MustGetToken(','); - while(sc.CheckToken(TK_Identifier)) - { - if(sc.Compare("xdeathface")) - cmd.flags |= DRAWMUGSHOT_XDEATHFACE; - else if(sc.Compare("animatedgodmode")) - cmd.flags |= DRAWMUGSHOT_ANIMATEDGODMODE; - else if(sc.Compare("disablegrin")) - cmd.flags |= DRAWMUGSHOT_DISABLEGRIN; - else if(sc.Compare("disableouch")) - cmd.flags |= DRAWMUGSHOT_DISABLEOUCH; - else if(sc.Compare("disablepain")) - cmd.flags |= DRAWMUGSHOT_DISABLEPAIN; - else if(sc.Compare("disablerampage")) - cmd.flags |= DRAWMUGSHOT_DISABLERAMPAGE; - else - sc.ScriptError("Unknown flag '%s'.", sc.String); - if(!sc.CheckToken('|')) - sc.MustGetToken(','); - } - - this->getCoordinates(sc, block.fullScreenOffsets, cmd.x, cmd.y); - sc.MustGetToken(';'); - break; - case SBARINFO_DRAWSELECTEDINVENTORY: - { - bool alternateonempty = false; - while(true) //go until we get a font (non-flag) - { - sc.MustGetToken(TK_Identifier); - if(sc.Compare("alternateonempty")) - { - alternateonempty = true; - cmd.flags |= DRAWSELECTEDINVENTORY_ALTERNATEONEMPTY; - } - else if(sc.Compare("artiflash")) - { - cmd.flags |= DRAWSELECTEDINVENTORY_ARTIFLASH; - } - else if(sc.Compare("alwaysshowcounter")) - { - cmd.flags |= DRAWSELECTEDINVENTORY_ALWAYSSHOWCOUNTER; - } - else if(sc.Compare("center")) - { - cmd.flags |= DRAWSELECTEDINVENTORY_CENTER; - } - else if(sc.Compare("centerbottom")) - { - cmd.flags |= DRAWSELECTEDINVENTORY_CENTERBOTTOM; - } - else if(sc.Compare("drawshadow")) - { - cmd.flags |= DRAWSELECTEDINVENTORY_DRAWSHADOW; - } - else - { - cmd.font = V_GetFont(sc.String); - if(cmd.font == NULL) - sc.ScriptError("Unknown font '%s'.", sc.String); - sc.MustGetToken(','); - break; - } - if(!sc.CheckToken('|')) - sc.MustGetToken(','); - } - this->getCoordinates(sc, block.fullScreenOffsets, cmd.x, cmd.y); - cmd.sbcoord2 = cmd.x + 30; - cmd.sbcoord3 = cmd.y + 24; - cmd.translation = CR_GOLD; - if(sc.CheckToken(',')) //more font information - { - this->getCoordinates(sc, block.fullScreenOffsets, cmd.sbcoord2, cmd.sbcoord3); - if(sc.CheckToken(',')) - { - sc.MustGetToken(TK_Identifier); - cmd.translation = this->GetTranslation(sc, sc.String); - if(sc.CheckToken(',')) - { - sc.MustGetToken(TK_IntConst); - cmd.special4 = sc.Number; - } - } - } - if(alternateonempty) - { - sc.MustGetToken('{'); - cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets; - this->ParseSBarInfoBlock(sc, cmd.subBlock); - } - else - { - sc.MustGetToken(';'); - } - break; - } - case SBARINFO_DRAWINVENTORYBAR: - sc.MustGetToken(TK_Identifier); - if(sc.Compare("Doom")) - cmd.special = GAME_Doom; - else if(sc.Compare("Heretic")) - cmd.special = GAME_Heretic; - else if(sc.Compare("Hexen")) - cmd.special = GAME_Hexen; - else if(sc.Compare("Strife")) - cmd.special = GAME_Strife; - else - sc.ScriptError("Unknown style '%s'.", sc.String); - - sc.MustGetToken(','); - while(sc.CheckToken(TK_Identifier)) - { - if(sc.Compare("alwaysshow")) - { - cmd.flags |= DRAWINVENTORYBAR_ALWAYSSHOW; - } - else if(sc.Compare("noartibox")) - { - cmd.flags |= DRAWINVENTORYBAR_NOARTIBOX; - } - else if(sc.Compare("noarrows")) - { - cmd.flags |= DRAWINVENTORYBAR_NOARROWS; - } - else if(sc.Compare("alwaysshowcounter")) - { - cmd.flags |= DRAWINVENTORYBAR_ALWAYSSHOWCOUNTER; - } - else if(sc.Compare("translucent")) - { - cmd.flags |= DRAWINVENTORYBAR_TRANSLUCENT; - } - else if(sc.Compare("vertical")) - { - cmd.flags |= DRAWINVENTORYBAR_VERTICAL; - } - else - { - sc.ScriptError("Unknown flag '%s'.", sc.String); - } - if(!sc.CheckToken('|')) - sc.MustGetToken(','); - } - sc.MustGetToken(TK_IntConst); - cmd.value = sc.Number; - sc.MustGetToken(','); - sc.MustGetToken(TK_Identifier); - cmd.font = V_GetFont(sc.String); - if(cmd.font == NULL) - sc.ScriptError("Unknown font '%s'.", sc.String); - - sc.MustGetToken(','); - this->getCoordinates(sc, block.fullScreenOffsets, cmd.x, cmd.y); - cmd.sbcoord2 = cmd.x + 26; - cmd.sbcoord3 = cmd.y + 22; - cmd.translation = CR_GOLD; - if(sc.CheckToken(',')) //more font information - { - this->getCoordinates(sc, block.fullScreenOffsets, cmd.sbcoord2, cmd.sbcoord3); - if(sc.CheckToken(',')) - { - sc.MustGetToken(TK_Identifier); - cmd.translation = this->GetTranslation(sc, sc.String); - if(sc.CheckToken(',')) - { - sc.MustGetToken(TK_IntConst); - cmd.special4 = sc.Number; - } - } - } - sc.MustGetToken(';'); - break; - case SBARINFO_DRAWBAR: - sc.MustGetToken(TK_StringConst); - cmd.image_index = newImage(sc.String); - cmd.sprite_index.SetInvalid(); - sc.MustGetToken(','); - sc.MustGetToken(TK_StringConst); - cmd.special = newImage(sc.String); - sc.MustGetToken(','); - sc.MustGetToken(TK_Identifier); //yeah, this is the same as drawnumber, there might be a better way to copy it... - if(sc.Compare("health")) - { - cmd.flags = DRAWNUMBER_HEALTH; - if(sc.CheckToken(TK_Identifier)) //comparing reference - { - cmd.setString(sc, sc.String, 0); - const PClass* item = PClass::FindClass(sc.String); - if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of inventory - { - sc.ScriptError("'%s' is not a type of inventory item.", sc.String); - } - } - else - cmd.special2 = DRAWBAR_COMPAREDEFAULTS; - } - else if(sc.Compare("armor")) - { - cmd.flags = DRAWNUMBER_ARMOR; - if(sc.CheckToken(TK_Identifier)) - { - cmd.setString(sc, sc.String, 0); - const PClass* item = PClass::FindClass(sc.String); - if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of inventory - { - sc.ScriptError("'%s' is not a type of inventory item.", sc.String); - } - } - else - cmd.special2 = DRAWBAR_COMPAREDEFAULTS; - } - else if(sc.Compare("ammo1")) - cmd.flags = DRAWNUMBER_AMMO1; - else if(sc.Compare("ammo2")) - cmd.flags = DRAWNUMBER_AMMO2; - else if(sc.Compare("ammo")) //request the next string to be an ammo type - { - sc.MustGetToken(TK_Identifier); - cmd.setString(sc, sc.String, 0); - cmd.flags = DRAWNUMBER_AMMO; - const PClass* ammo = PClass::FindClass(sc.String); - if(ammo == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(ammo)) //must be a kind of ammo - { - sc.ScriptError("'%s' is not a type of ammo.", sc.String); - } - } - else if(sc.Compare("frags")) - cmd.flags = DRAWNUMBER_FRAGS; - else if(sc.Compare("kills")) - cmd.flags = DRAWNUMBER_KILLS; - else if(sc.Compare("items")) - cmd.flags = DRAWNUMBER_ITEMS; - else if(sc.Compare("secrets")) - cmd.flags = DRAWNUMBER_SECRETS; - else if(sc.Compare("airtime")) - cmd.flags = DRAWNUMBER_AIRTIME; - else if(sc.Compare("poweruptime")) - { - cmd.flags |= DRAWNUMBER_POWERUPTIME; - sc.MustGetToken(TK_Identifier); - cmd.setString(sc, sc.String, 0); - const PClass* item = PClass::FindClass(sc.String); - if(item == NULL || !PClass::FindClass("PowerupGiver")->IsAncestorOf(item)) - { - sc.ScriptError("'%s' is not a type of PowerupGiver.", sc.String); - } - } - else - { - cmd.flags = DRAWNUMBER_INVENTORY; - cmd.setString(sc, sc.String, 0); - const PClass* item = PClass::FindClass(sc.String); - if(item == NULL || !RUNTIME_CLASS(AInventory)->IsAncestorOf(item)) - { - sc.ScriptError("'%s' is not a type of inventory item.", sc.String); - } - } - sc.MustGetToken(','); - sc.MustGetToken(TK_Identifier); - if(sc.Compare("horizontal")) - cmd.special2 += DRAWBAR_HORIZONTAL; - else if(!sc.Compare("vertical")) - sc.ScriptError("Unknown direction '%s'.", sc.String); - sc.MustGetToken(','); - while(sc.CheckToken(TK_Identifier)) - { - if(sc.Compare("reverse")) - cmd.special2 += DRAWBAR_REVERSE; - else - sc.ScriptError("Unkown flag '%s'.", sc.String); - if(!sc.CheckToken('|')) - sc.MustGetToken(','); - } - this->getCoordinates(sc, block.fullScreenOffsets, cmd.x, cmd.y); - if(sc.CheckToken(',')) //border - { - sc.MustGetToken(TK_IntConst); - cmd.special3 = sc.Number; - } - sc.MustGetToken(';'); - break; - case SBARINFO_DRAWGEM: - while(sc.CheckToken(TK_Identifier)) - { - if(sc.Compare("wiggle")) - cmd.flags |= DRAWGEM_WIGGLE; - else if(sc.Compare("translatable")) - cmd.flags |= DRAWGEM_TRANSLATABLE; - else if(sc.Compare("armor")) - cmd.flags |= DRAWGEM_ARMOR; - else if(sc.Compare("reverse")) - cmd.flags |= DRAWGEM_REVERSE; - else - sc.ScriptError("Unknown drawgem flag '%s'.", sc.String); - if(!sc.CheckToken('|')) - sc.MustGetToken(','); - } - sc.MustGetToken(TK_StringConst); //chain - cmd.special = newImage(sc.String); - sc.MustGetToken(','); - sc.MustGetToken(TK_StringConst); //gem - cmd.image_index = newImage(sc.String); - cmd.sprite_index.SetInvalid(); - sc.MustGetToken(','); - cmd.special2 = this->getSignedInteger(sc); - sc.MustGetToken(','); - cmd.special3 = this->getSignedInteger(sc); - sc.MustGetToken(','); - sc.MustGetToken(TK_IntConst); - if(sc.Number < 0) - sc.ScriptError("Chain size must be a positive number."); - cmd.special4 = sc.Number; - sc.MustGetToken(','); - this->getCoordinates(sc, block.fullScreenOffsets, cmd.x, cmd.y); - sc.MustGetToken(';'); - break; - case SBARINFO_DRAWSHADER: - sc.MustGetToken(TK_IntConst); - cmd.special = sc.Number; - if(sc.Number < 1) - sc.ScriptError("Width must be greater than 1."); - sc.MustGetToken(','); - sc.MustGetToken(TK_IntConst); - cmd.special2 = sc.Number; - if(sc.Number < 1) - sc.ScriptError("Height must be greater than 1."); - sc.MustGetToken(','); - sc.MustGetToken(TK_Identifier); - if(sc.Compare("vertical")) - cmd.flags |= DRAWSHADER_VERTICAL; - else if(!sc.Compare("horizontal")) - sc.ScriptError("Unknown direction '%s'.", sc.String); - sc.MustGetToken(','); - if(sc.CheckToken(TK_Identifier)) - { - if(!sc.Compare("reverse")) - { - sc.ScriptError("Exspected 'reverse', got '%s' instead.", sc.String); - } - cmd.flags |= DRAWSHADER_REVERSE; - sc.MustGetToken(','); - } - this->getCoordinates(sc, block.fullScreenOffsets, cmd.x, cmd.y); - sc.MustGetToken(';'); - break; - case SBARINFO_DRAWSTRING: - sc.MustGetToken(TK_Identifier); - cmd.font = V_GetFont(sc.String); - if(cmd.font == NULL) - sc.ScriptError("Unknown font '%s'.", sc.String); - sc.MustGetToken(','); - sc.MustGetToken(TK_Identifier); - cmd.translation = this->GetTranslation(sc, sc.String); - sc.MustGetToken(','); - sc.MustGetToken(TK_StringConst); - cmd.setString(sc, sc.String, 0, -1, false); - sc.MustGetToken(','); - this->getCoordinates(sc, block.fullScreenOffsets, cmd.x, cmd.y); - if(sc.CheckToken(',')) //spacing - { - sc.MustGetToken(TK_IntConst); - cmd.special = sc.Number; - } - sc.MustGetToken(';'); - break; - case SBARINFO_DRAWKEYBAR: - sc.MustGetToken(TK_IntConst); - cmd.value = sc.Number; - sc.MustGetToken(','); - sc.MustGetToken(TK_Identifier); - if(sc.Compare("vertical")) - cmd.flags |= DRAWKEYBAR_VERTICAL; - else if(!sc.Compare("horizontal")) - sc.ScriptError("Unknown direction '%s'.", sc.String); - sc.MustGetToken(','); - while(sc.CheckToken(TK_Identifier)) - { - if(sc.Compare("reverserows")) - cmd.flags |= DRAWKEYBAR_REVERSEROWS; - else - sc.ScriptError("Unknown flag '%s'.", sc.String); - if(!sc.CheckToken('|')) - sc.MustGetToken(','); - } - if(sc.CheckToken(TK_Auto)) - cmd.special = -1; - else - { - sc.MustGetToken(TK_IntConst); - cmd.special = sc.Number; - } - sc.MustGetToken(','); - this->getCoordinates(sc, block.fullScreenOffsets, cmd.x, cmd.y); - if(sc.CheckToken(',')) - { - //key offset - sc.MustGetToken(TK_IntConst); - cmd.special2 = sc.Number; - if(sc.CheckToken(',')) - { - //max per row/column - sc.MustGetToken(TK_IntConst); - cmd.special3 = sc.Number; - sc.MustGetToken(','); - //row/column spacing (opposite of previous) - if(sc.CheckToken(TK_Auto)) - cmd.special4 = -1; - else - { - sc.MustGetToken(TK_IntConst); - cmd.special4 = sc.Number; - } - } - } - sc.MustGetToken(';'); - break; - case SBARINFO_GAMEMODE: - while(sc.CheckToken(TK_Identifier)) - { - if(sc.Compare("singleplayer")) - cmd.flags |= GAMETYPE_SINGLEPLAYER; - else if(sc.Compare("cooperative")) - cmd.flags |= GAMETYPE_COOPERATIVE; - else if(sc.Compare("deathmatch")) - cmd.flags |= GAMETYPE_DEATHMATCH; - else if(sc.Compare("teamgame")) - cmd.flags |= GAMETYPE_TEAMGAME; - //else I'm removing this error to allow cross port compatiblity. If it doesn't know what a gamemode is lets just ignore it. - // sc.ScriptError("Unknown gamemode: %s", sc.String); - if(sc.CheckToken('{')) - break; - sc.MustGetToken(','); - } - cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets; - this->ParseSBarInfoBlock(sc, cmd.subBlock); - break; - case SBARINFO_PLAYERCLASS: - cmd.special = cmd.special2 = cmd.special3 = -1; - for(int i = 0;i < 3 && sc.CheckToken(TK_Identifier);i++) //up to 3 classes - { - bool foundClass = false; - for(unsigned int c = 0;c < PlayerClasses.Size();c++) - { - if(stricmp(sc.String, PlayerClasses[c].Type->Meta.GetMetaString(APMETA_DisplayName)) == 0) - { - foundClass = true; - if(i == 0) - cmd.special = PlayerClasses[c].Type->ClassIndex; - else if(i == 1) - cmd.special2 = PlayerClasses[c].Type->ClassIndex; - else //should be 2 - cmd.special3 = PlayerClasses[c].Type->ClassIndex; - break; - } - } - if(!foundClass) - sc.ScriptError("Unkown PlayerClass '%s'.", sc.String); - if(sc.CheckToken('{') || i == 2) - goto FinishPlayerClass; - sc.MustGetToken(','); - } - FinishPlayerClass: - cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets; - this->ParseSBarInfoBlock(sc, cmd.subBlock); - break; - case SBARINFO_ASPECTRATIO: - sc.MustGetToken(TK_StringConst); - if(sc.Compare("4:3")) - cmd.value = ASPECTRATIO_4_3; - else if(sc.Compare("16:9")) - cmd.value = ASPECTRATIO_16_9; - else if(sc.Compare("16:10")) - cmd.value = ASPECTRATIO_16_10; - else if(sc.Compare("5:4")) - cmd.value = ASPECTRATIO_5_4; - else - sc.ScriptError("Unkown aspect ratio: %s", sc.String); - sc.MustGetToken('{'); - cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets; - this->ParseSBarInfoBlock(sc, cmd.subBlock); - break; - case SBARINFO_ISSELECTED: - //Using StringConst instead of Identifieres is deperecated! - if(sc.CheckToken(TK_Identifier)) - { - if(sc.Compare("not")) - { - cmd.flags |= SBARINFOEVENT_NOT; - if(!sc.CheckToken(TK_StringConst)) - sc.MustGetToken(TK_Identifier); - } - } - else - sc.MustGetToken(TK_StringConst); - for(int i = 0;i < 2;i++) - { - cmd.setString(sc, sc.String, i); - const PClass* item = PClass::FindClass(sc.String); - if(item == NULL || !RUNTIME_CLASS(AWeapon)->IsAncestorOf(item)) - { - sc.ScriptError("'%s' is not a type of weapon.", sc.String); - } - if(sc.CheckToken(',')) - { - if(!sc.CheckToken(TK_StringConst)) - sc.MustGetToken(TK_Identifier); - } - else - break; - } - sc.MustGetToken('{'); - cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets; - this->ParseSBarInfoBlock(sc, cmd.subBlock); - break; - case SBARINFO_USESSECONDARYAMMO: - case SBARINFO_USESAMMO: - if(sc.CheckToken(TK_Identifier)) - { - if(sc.Compare("not")) - cmd.flags |= SBARINFOEVENT_NOT; - else - sc.ScriptError("Expected 'not' got '%s' instead.", sc.String); - } - sc.MustGetToken('{'); - cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets; - this->ParseSBarInfoBlock(sc, cmd.subBlock); - break; - case SBARINFO_HASWEAPONPIECE: - { - sc.MustGetToken(TK_Identifier); - const PClass* weapon = PClass::FindClass(sc.String); - if(weapon == NULL || !RUNTIME_CLASS(AWeapon)->IsAncestorOf(weapon)) //must be a weapon - sc.ScriptError("%s is not a kind of weapon.", sc.String); - cmd.setString(sc, sc.String, 0); - sc.MustGetToken(','); - sc.MustGetToken(TK_IntConst); - if(sc.Number < 1) - sc.ScriptError("Weapon piece number can not be less than 1."); - cmd.value = sc.Number; - sc.MustGetToken('{'); - cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets; - this->ParseSBarInfoBlock(sc, cmd.subBlock); - break; - } - case SBARINFO_INVENTORYBARNOTVISIBLE: - sc.MustGetToken('{'); - cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets; - this->ParseSBarInfoBlock(sc, cmd.subBlock); - break; - case SBARINFO_WEAPONAMMO: - sc.MustGetToken(TK_Identifier); - if(sc.Compare("not")) - { - cmd.flags |= SBARINFOEVENT_NOT; - sc.MustGetToken(TK_Identifier); - } - for(int i = 0;i < 2;i++) - { - cmd.setString(sc, sc.String, i); - const PClass* ammo = PClass::FindClass(sc.String); - if(ammo == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(ammo)) //must be a kind of ammo - { - sc.ScriptError("'%s' is not a type of ammo.", sc.String); - } - if(sc.CheckToken(TK_OrOr)) - { - cmd.flags |= SBARINFOEVENT_OR; - sc.MustGetToken(TK_Identifier); - } - else if(sc.CheckToken(TK_AndAnd)) - { - cmd.flags |= SBARINFOEVENT_AND; - sc.MustGetToken(TK_Identifier); - } - else - break; - } - sc.MustGetToken('{'); - cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets; - this->ParseSBarInfoBlock(sc, cmd.subBlock); - break; - case SBARINFO_ININVENTORY: - cmd.special2 = cmd.special3 = 0; - sc.MustGetToken(TK_Identifier); - if(sc.Compare("not")) - { - cmd.flags |= SBARINFOEVENT_NOT; - sc.MustGetToken(TK_Identifier); - } - for(int i = 0;i < 2;i++) - { - cmd.setString(sc, sc.String, i); - const PClass* item = PClass::FindClass(sc.String); - if(item == NULL || !RUNTIME_CLASS(AInventory)->IsAncestorOf(item)) - { - sc.ScriptError("'%s' is not a type of inventory item.", sc.String); - } - if (sc.CheckToken(',')) - { - sc.MustGetNumber(); - if (i == 0) cmd.special2 = sc.Number; - else cmd.special3 = sc.Number; - } - - if(sc.CheckToken(TK_OrOr)) - { - cmd.flags |= SBARINFOEVENT_OR; - sc.MustGetToken(TK_Identifier); - } - else if(sc.CheckToken(TK_AndAnd)) - { - cmd.flags |= SBARINFOEVENT_AND; - sc.MustGetToken(TK_Identifier); - } - else - break; - } - sc.MustGetToken('{'); - cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets; - this->ParseSBarInfoBlock(sc, cmd.subBlock); - break; - } - block.commands.Push(cmd); - } - sc.MustGetToken('}'); -} - -void SBarInfo::ParseMugShotBlock(FScanner &sc, FMugShotState &state) -{ - sc.MustGetToken('{'); - while(!sc.CheckToken('}')) - { - FMugShotFrame frame; - bool multiframe = false; - if(sc.CheckToken('{')) - multiframe = true; - do - { - sc.MustGetToken(TK_Identifier); - if(strlen(sc.String) > 5) - sc.ScriptError("MugShot frames cannot exceed 5 characters."); - frame.Graphic.Push(sc.String); - } - while(multiframe && sc.CheckToken(',')); - if(multiframe) - sc.MustGetToken('}'); - frame.Delay = getSignedInteger(sc); - sc.MustGetToken(';'); - state.Frames.Push(frame); - } -} - -void SBarInfo::getCoordinates(FScanner &sc, bool fullScreenOffsets, SBarInfoCoordinate &x, SBarInfoCoordinate &y) -{ - bool negative = false; - bool relCenter = false; - SBarInfoCoordinate *coords[2] = {&x, &y}; - for(int i = 0;i < 2;i++) - { - negative = false; - relCenter = false; - if(i > 0) - sc.MustGetToken(','); - - // [-]INT center - negative = sc.CheckToken('-'); - sc.MustGetToken(TK_IntConst); - coords[i]->Set(negative ? -sc.Number : sc.Number, false); - if(sc.CheckToken('+')) - { - sc.MustGetToken(TK_Identifier); - if(!sc.Compare("center")) - sc.ScriptError("Expected 'center' but got '%s' instead.", sc.String); - relCenter = true; - } - if(fullScreenOffsets) - { - coords[i]->SetRelCenter(relCenter); - } - } - - if(!fullScreenOffsets) - y.SetCoord((negative ? -sc.Number : sc.Number) - (200 - this->height)); -} - -int SBarInfo::getSignedInteger(FScanner &sc) -{ - if(sc.CheckToken('-')) - { - sc.MustGetToken(TK_IntConst); - return -sc.Number; - } - else - { - sc.MustGetToken(TK_IntConst); - return sc.Number; - } -} - -int SBarInfo::newImage(const char *patchname) -{ - if(patchname[0] == '\0' || stricmp(patchname, "nullimage") == 0) - { - return -1; - } - for(unsigned int i = 0;i < this->Images.Size();i++) //did we already load it? - { - if(stricmp(this->Images[i], patchname) == 0) - { - return i; - } - } - return this->Images.Push(patchname); -} - -//converts a string into a tranlation. -EColorRange SBarInfo::GetTranslation(FScanner &sc, const char* translation) -{ - EColorRange returnVal = CR_UNTRANSLATED; - FString namedTranslation; //we must send in "[translation]" - const BYTE *trans_ptr; - namedTranslation.Format("[%s]", translation); - trans_ptr = (const BYTE *)(&namedTranslation[0]); - if((returnVal = V_ParseFontColor(trans_ptr, CR_UNTRANSLATED, CR_UNTRANSLATED)) == CR_UNDEFINED) - { - sc.ScriptError("Missing definition for color %s.", translation); - } - return returnVal; -} - -SBarInfo::SBarInfo() //make results more predicable -{ - Init(); -} - -SBarInfo::SBarInfo(int lumpnum) -{ - Init(); - ParseSBarInfo(lumpnum); -} - -void SBarInfo::Init() -{ - automapbar = false; - interpolateHealth = false; - interpolateArmor = false; - completeBorder = false; - lowerHealthCap = true; - interpolationSpeed = 8; - armorInterpolationSpeed = 8; - height = 0; - spacingCharacter = '\0'; -} - -SBarInfo::~SBarInfo() -{ - for (size_t i = 0; i < NUMHUDS; ++i) - { - huds[i].commands.Clear(); - } -} - -void SBarInfoCommand::setString(FScanner &sc, const char* source, int strnum, int maxlength, bool exact) -{ - if(!exact) - { - if(maxlength != -1 && strlen(source) > (unsigned int) maxlength) - { - sc.ScriptError("%s is greater than %d characters.", source, maxlength); - return; - } - } - else - { - if(maxlength != -1 && strlen(source) != (unsigned int) maxlength) - { - sc.ScriptError("%s must be %d characters.", source, maxlength); - return; - } - } - string[strnum] = source; -} - -SBarInfoCommand::SBarInfoCommand() //sets the default values for more predicable behavior -{ - type = 0; - special = 0; - special2 = 0; - special3 = 0; - special4 = 0; - flags = 0; - x.Set(0, 0); - y.Set(0, 0); - value = 0; - image_index = 0; - sprite_index.SetInvalid(); - translation = CR_UNTRANSLATED; - translation2 = CR_UNTRANSLATED; - translation3 = CR_UNTRANSLATED; - font = V_GetFont("CONFONT"); -} - -SBarInfoCommand::~SBarInfoCommand() -{ -} - -SBarInfoBlock::SBarInfoBlock() -{ - forceScaled = false; - fullScreenOffsets = false; - alpha = FRACUNIT; -} - -//Popup -Popup::Popup() -{ - transition = TRANSITION_NONE; - height = 320; - width = 200; - speed = 0; - x = 320; - y = 200; - alpha = FRACUNIT; - opened = false; - moving = false; -} - -void Popup::init() -{ - x = width; - y = height; - if(transition == TRANSITION_SLIDEINBOTTOM) - { - x = 0; - } - else if(transition == TRANSITION_FADE) - { - alpha = 0; - x = 0; - y = 0; - } -} - -void Popup::tick() -{ - if(transition == TRANSITION_SLIDEINBOTTOM) - { - if(moving) - { - if(opened) - y -= clamp(height + (y - height), 1, speed); - else - y += clamp(height - y, 1, speed); - } - if(y != 0 && y != height) - moving = true; - else - moving = false; - } - else if(transition == TRANSITION_FADE) - { - if(moving) - { - if(opened) - alpha = clamp(alpha + speed, 0, FRACUNIT); - else - alpha = clamp(alpha - speed2, 0, FRACUNIT); - } - if(alpha == 0 || alpha == FRACUNIT) - moving = false; - else - moving = true; - } - else - { - if(opened) - { - y = 0; - x = 0; - } - else - { - y = height; - x = width; - } - moving = false; - } -} - -bool Popup::isDoneMoving() -{ - return !moving; -} - -int Popup::getXOffset() -{ - return x; -} - -int Popup::getYOffset() -{ - return y; -} - -int Popup::getAlpha(int maxAlpha) -{ - double a = (double) alpha / (double) FRACUNIT; - double b = (double) maxAlpha / (double) FRACUNIT; - return fixed_t((a * b) * FRACUNIT); -} - -void Popup::open() -{ - opened = true; - moving = true; -} - -void Popup::close() -{ - opened = false; - moving = true; -} diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index a1905aff9..87664c204 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -67,6 +67,7 @@ CVAR (Bool, hud_showsecrets, true,CVAR_ARCHIVE); // Show secrets on HUD CVAR (Bool, hud_showmonsters, true,CVAR_ARCHIVE); // Show monster stats on HUD CVAR (Bool, hud_showitems, false,CVAR_ARCHIVE); // Show item stats on HUD CVAR (Bool, hud_showstats, false, CVAR_ARCHIVE); // for stamina and accuracy. +CVAR (Bool, hud_showscore, false, CVAR_ARCHIVE); // for user maintained score CVAR (Int, hud_ammo_red, 25, CVAR_ARCHIVE) // ammo percent less than which status is red CVAR (Int, hud_ammo_yellow, 50, CVAR_ARCHIVE) // ammo percent less is yellow more green @@ -98,6 +99,7 @@ static FTexture * fragpic; // Frags icon static FTexture * invgems[4]; // Inventory arrows static int hudwidth, hudheight; // current width/height for HUD display +static int statspace; void AM_GetPosition(fixed_t & x, fixed_t & y); @@ -158,15 +160,20 @@ static void DrawImageToBox(FTexture * tex, int x, int y, int w, int h, int trans static void DrawHudText(FFont *font, int color, char * text, int x, int y, int trans=0xc000) { - int zerowidth = font->GetCharWidth('0'); + int zerowidth; + FTexture *tex_zero = font->GetChar('0', &zerowidth); x+=zerowidth/2; for(int i=0;text[i];i++) { + int width; + FTexture *texc = font->GetChar(text[i], &width); + int offset = texc->TopOffset - tex_zero->TopOffset + tex_zero->GetHeight(); screen->DrawChar(font, color, x, y, text[i], DTA_KeepRatio, true, DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, trans, - DTA_CenterBottomOffset, 1, TAG_DONE); + DTA_LeftOffset, width/2, DTA_TopOffset, offset, + /*DTA_CenterBottomOffset, 1,*/ TAG_DONE); x+=zerowidth; } } @@ -193,37 +200,35 @@ static void DrawHudNumber(FFont *font, int color, int num, int x, int y, int tra // //=========================================================================== +static void DrawStatLine(int x, int &y, const char *prefix, const char *string) +{ + y -= SmallFont->GetHeight()-1; + screen->DrawText(SmallFont, hudcolor_statnames, x, y, prefix, + DTA_KeepRatio, true, + DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0xc000, TAG_DONE); + + screen->DrawText(SmallFont, hudcolor_stats, x+statspace, y, string, + DTA_KeepRatio, true, + DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0xc000, TAG_DONE); +} + static void DrawStatus(player_t * CPlayer, int x, int y) { char tempstr[50]; - int space; + + if (hud_showscore) + { + mysnprintf(tempstr, countof(tempstr), "%i ", CPlayer->mo->Score); + DrawStatLine(x, y, "Sc:", tempstr); + } if (hud_showstats) { - space = SmallFont->StringWidth("Ac: "); - - y -= SmallFont->GetHeight()-1; - screen->DrawText(SmallFont, hudcolor_statnames, x, y, "Ac:", - DTA_KeepRatio, true, - DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0xc000, TAG_DONE); - mysnprintf(tempstr, countof(tempstr), "%i ", CPlayer->accuracy); - screen->DrawText(SmallFont, hudcolor_stats, x+space, y, tempstr, - DTA_KeepRatio, true, - DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0xc000, TAG_DONE); - - y-=SmallFont->GetHeight()-1; - screen->DrawText(SmallFont, hudcolor_statnames, x, y, "St:", - DTA_KeepRatio, true, - DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0xc000, TAG_DONE); - + DrawStatLine(x, y, "Ac:", tempstr); mysnprintf(tempstr, countof(tempstr), "%i ", CPlayer->stamina); - screen->DrawText(SmallFont, hudcolor_stats, x+space, y, tempstr, - DTA_KeepRatio, true, - DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0xc000, TAG_DONE); + DrawStatLine(x, y, "St:", tempstr); } - else - space=SmallFont->StringWidth("K: "); if (!deathmatch) { @@ -231,41 +236,20 @@ static void DrawStatus(player_t * CPlayer, int x, int y) // work in cooperative hub games if (hud_showsecrets) { - y -= SmallFont->GetHeight()-1; - screen->DrawText(SmallFont, hudcolor_statnames, x, y, "S:", - DTA_KeepRatio, true, - DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0xc000, TAG_DONE); - mysnprintf(tempstr, countof(tempstr), "%i/%i ", multiplayer? CPlayer->secretcount : level.found_secrets, level.total_secrets); - screen->DrawText(SmallFont, hudcolor_stats, x+space, y, tempstr, - DTA_KeepRatio, true, - DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0xc000, TAG_DONE); + DrawStatLine(x, y, "S:", tempstr); } if (hud_showitems) { - y -= SmallFont->GetHeight()-1; - screen->DrawText(SmallFont, hudcolor_statnames, x, y, "I:", - DTA_KeepRatio, true, - DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0xc000, TAG_DONE); - mysnprintf(tempstr, countof(tempstr), "%i/%i ", multiplayer? CPlayer->itemcount : level.found_items, level.total_items); - screen->DrawText(SmallFont, hudcolor_stats, x+space, y, tempstr, - DTA_KeepRatio, true, - DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0xc000, TAG_DONE); + DrawStatLine(x, y, "I:", tempstr); } if (hud_showmonsters) { - y -= SmallFont->GetHeight()-1; - screen->DrawText(SmallFont, hudcolor_statnames, x, y, "K:", - DTA_KeepRatio, true, - DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0xc000, TAG_DONE); - mysnprintf(tempstr, countof(tempstr), "%i/%i ", multiplayer? CPlayer->killcount : level.killed_monsters, level.total_monsters); - screen->DrawText(SmallFont, hudcolor_stats, x+space, y, tempstr, - DTA_KeepRatio, true, - DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0xc000, TAG_DONE); + DrawStatLine(x, y, "K:", tempstr); } } } @@ -926,6 +910,10 @@ void HUD_InitHud() KeyTypes.Clear(); UnassignedKeyTypes.Clear(); + statspace = SmallFont->StringWidth("Ac:"); + + + // Now read custom icon overrides int lump, lastlump = 0; diff --git a/src/g_skill.cpp b/src/g_skill.cpp index 3d2ef9bf2..a91e38264 100644 --- a/src/g_skill.cpp +++ b/src/g_skill.cpp @@ -40,6 +40,7 @@ #include "gi.h" #include "templates.h" #include "v_font.h" +#include "m_fixed.h" TArray AllSkills; int DefaultSkill = -1; diff --git a/src/g_strife/a_macil.cpp b/src/g_strife/a_macil.cpp deleted file mode 100644 index 6557ef7cf..000000000 --- a/src/g_strife/a_macil.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* -#include "actor.h" -#include "m_random.h" -#include "a_action.h" -#include "p_local.h" -#include "s_sound.h" -#include "a_strifeglobal.h" -*/ - -// Macil (version 2) --------------------------------------------------------- - -class AMacil1 : public AActor -{ - DECLARE_CLASS (AMacil1, AActor) -public: - int TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype); -}; - -IMPLEMENT_CLASS (AMacil1) - -//============================================================================ -// -// AMacil2 :: TakeSpecialDamage -// -// Macil is invulnerable to the first stage Sigil. -// -//============================================================================ - -int AMacil1::TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype) -{ - if (inflictor != NULL && inflictor->GetClass()->TypeName == NAME_SpectralLightningV1) - return -1; - - return Super::TakeSpecialDamage(inflictor, source, damage, damagetype); -} diff --git a/src/g_strife/a_oracle.cpp b/src/g_strife/a_oracle.cpp index 519b68c10..1582657f9 100644 --- a/src/g_strife/a_oracle.cpp +++ b/src/g_strife/a_oracle.cpp @@ -7,16 +7,6 @@ #include "thingdef/thingdef.h" */ -class AOracle : public AActor -{ - DECLARE_CLASS (AOracle, AActor) -public: - int TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype); -}; - - -IMPLEMENT_CLASS (AOracle) - DEFINE_ACTION_FUNCTION(AActor, A_WakeOracleSpectre) { @@ -34,18 +24,3 @@ DEFINE_ACTION_FUNCTION(AActor, A_WakeOracleSpectre) return 0; } - -//============================================================================ -// -// AOracle :: TakeSpecialDamage -// -// The Oracle is invulnerable to the first stage Sigil. -// -//============================================================================ - -int AOracle::TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype) -{ - if (inflictor != NULL && inflictor->GetClass()->TypeName == NAME_SpectralLightningV1) - return -1; - return Super::TakeSpecialDamage(inflictor, source, damage, damagetype); -} diff --git a/src/g_strife/a_rebels.cpp b/src/g_strife/a_rebels.cpp index 3944dd960..c38938c9b 100644 --- a/src/g_strife/a_rebels.cpp +++ b/src/g_strife/a_rebels.cpp @@ -100,8 +100,11 @@ DEFINE_ACTION_FUNCTION(AActor, A_Beacon) } if (owner != NULL) { - // Rebels are the same color as their owner - rebel->Translation = owner->Translation; + // Rebels are the same color as their owner (but only in multiplayer) + if (multiplayer) + { + rebel->Translation = owner->Translation; + } rebel->FriendPlayer = owner->player != NULL ? BYTE(owner->player - players + 1) : 0; // Set the rebel's target to whatever last hurt the player, so long as it's not // one of the player's other rebels. diff --git a/src/g_strife/a_spectral.cpp b/src/g_strife/a_spectral.cpp index 4ab555660..75757500a 100644 --- a/src/g_strife/a_spectral.cpp +++ b/src/g_strife/a_spectral.cpp @@ -72,14 +72,14 @@ DEFINE_ACTION_FUNCTION(AActor, A_SpectralLightning) x = self->x + pr_zap5.Random2(3) * FRACUNIT * 50; y = self->y + pr_zap5.Random2(3) * FRACUNIT * 50; - flash = Spawn (self->threshold > 25 ? PClass::FindClass("SpectralLightningV2") : - PClass::FindClass("SpectralLightningV1"), x, y, ONCEILINGZ, ALLOW_REPLACE); + flash = Spawn (self->threshold > 25 ? PClass::FindClass(NAME_SpectralLightningV2) : + PClass::FindClass(NAME_SpectralLightningV1), x, y, ONCEILINGZ, ALLOW_REPLACE); flash->target = self->target; flash->velz = -18*FRACUNIT; flash->health = self->health; - flash = Spawn("SpectralLightningV2", self->x, self->y, ONCEILINGZ, ALLOW_REPLACE); + flash = Spawn(NAME_SpectralLightningV2, self->x, self->y, ONCEILINGZ, ALLOW_REPLACE); flash->target = self->target; flash->velz = -18*FRACUNIT; diff --git a/src/g_strife/a_strifestuff.cpp b/src/g_strife/a_strifestuff.cpp index 2865725ce..d747872a8 100644 --- a/src/g_strife/a_strifestuff.cpp +++ b/src/g_strife/a_strifestuff.cpp @@ -21,19 +21,19 @@ // Include all the other Strife stuff here to reduce compile time #include "a_acolyte.cpp" +#include "a_spectral.cpp" #include "a_alienspectres.cpp" #include "a_coin.cpp" #include "a_crusader.cpp" #include "a_entityboss.cpp" #include "a_inquisitor.cpp" #include "a_loremaster.cpp" -#include "a_macil.cpp" +//#include "a_macil.cpp" #include "a_oracle.cpp" #include "a_programmer.cpp" #include "a_reaver.cpp" #include "a_rebels.cpp" #include "a_sentinel.cpp" -#include "a_spectral.cpp" #include "a_stalker.cpp" #include "a_strifeitems.cpp" #include "a_strifeweapons.cpp" diff --git a/src/g_strife/a_strifeweapons.cpp b/src/g_strife/a_strifeweapons.cpp index b4d35c94c..3cd8a15b2 100644 --- a/src/g_strife/a_strifeweapons.cpp +++ b/src/g_strife/a_strifeweapons.cpp @@ -459,7 +459,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireMauler1) // it should use a different puff. ZDoom's default range is longer // than this, so let's not handicap it by being too faithful to the // original. - P_LineAttack (self, angle, PLAYERMISSILERANGE, pitch, damage, NAME_Disintegrate, NAME_MaulerPuff); + P_LineAttack (self, angle, PLAYERMISSILERANGE, pitch, damage, NAME_None, NAME_MaulerPuff); } return 0; } diff --git a/src/g_strife/a_templar.cpp b/src/g_strife/a_templar.cpp index 0dd95090b..f12bbdf0a 100644 --- a/src/g_strife/a_templar.cpp +++ b/src/g_strife/a_templar.cpp @@ -32,7 +32,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_TemplarAttack) damage = (pr_templar() & 4) * 2; angle = self->angle + (pr_templar.Random2() << 19); pitchdiff = pr_templar.Random2() * 332063; - P_LineAttack (self, angle, MISSILERANGE+64*FRACUNIT, pitch+pitchdiff, damage, NAME_Disintegrate, NAME_MaulerPuff); + P_LineAttack (self, angle, MISSILERANGE+64*FRACUNIT, pitch+pitchdiff, damage, NAME_None, NAME_MaulerPuff); } return 0; } diff --git a/src/g_strife/a_thingstoblowup.cpp b/src/g_strife/a_thingstoblowup.cpp index 7350ab1f5..797b9af97 100644 --- a/src/g_strife/a_thingstoblowup.cpp +++ b/src/g_strife/a_thingstoblowup.cpp @@ -86,10 +86,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Explode512) { self->target->player->extralight = 5; } - if (self->z <= self->floorz + (512<RenderStyle = STYLE_Add; diff --git a/src/gameconfigfile.cpp b/src/gameconfigfile.cpp index 09faee2c0..e9f90a23d 100644 --- a/src/gameconfigfile.cpp +++ b/src/gameconfigfile.cpp @@ -161,6 +161,8 @@ FGameConfigFile::FGameConfigFile () // Create auto-load sections, so users know what's available. // Note that this totem pole is the reverse of the order that // they will appear in the file. + CreateSectionAtStart("Harmony.Autoload"); + CreateSectionAtStart("UrbanBrawl.Autoload"); CreateSectionAtStart("Chex3.Autoload"); CreateSectionAtStart("Chex.Autoload"); CreateSectionAtStart("Strife.Autoload"); @@ -269,8 +271,8 @@ void FGameConfigFile::DoGlobalSetup () SetValueForKey ("9", "use ArtiBlastRadius"); SetValueForKey ("8", "use ArtiTeleport"); SetValueForKey ("7", "use ArtiTeleportOther"); - SetValueForKey ("6", "use ArtiEgg"); - SetValueForKey ("5", "use ArtiInvulnerability"); + SetValueForKey ("6", "use ArtiPork"); + SetValueForKey ("5", "use ArtiInvulnerability2"); } } if (last < 204) @@ -328,6 +330,15 @@ void FGameConfigFile::DoGlobalSetup () dim->ResetToDefault (); } } + if (last < 210) + { + if (SetSection ("Hexen.Bindings")) + { + // These 2 were misnamed in earlier versions + SetValueForKey ("6", "use ArtiPork"); + SetValueForKey ("5", "use ArtiInvulnerability2"); + } + } } } } diff --git a/src/gccinlines.h b/src/gccinlines.h index ebadcd4cc..4f1ec7e5e 100644 --- a/src/gccinlines.h +++ b/src/gccinlines.h @@ -29,7 +29,7 @@ #define alloca __builtin_alloca #endif -inline SDWORD Scale (SDWORD a, SDWORD b, SDWORD c) +static inline SDWORD Scale (SDWORD a, SDWORD b, SDWORD c) { SDWORD result, dummy; @@ -47,7 +47,7 @@ inline SDWORD Scale (SDWORD a, SDWORD b, SDWORD c) return result; } -inline SDWORD MulScale (SDWORD a, SDWORD b, SDWORD c) +static inline SDWORD MulScale (SDWORD a, SDWORD b, SDWORD c) { SDWORD result, dummy; @@ -65,7 +65,7 @@ inline SDWORD MulScale (SDWORD a, SDWORD b, SDWORD c) } #define MAKECONSTMulScale(s) \ -inline SDWORD MulScale##s (SDWORD a, SDWORD b) { return ((SQWORD)a * b) >> s; } +static inline SDWORD MulScale##s (SDWORD a, SDWORD b) { return ((SQWORD)a * b) >> s; } MAKECONSTMulScale(1) MAKECONSTMulScale(2) @@ -101,8 +101,10 @@ MAKECONSTMulScale(31) MAKECONSTMulScale(32) #undef MAKECONSTMulScale +static inline DWORD UMulScale16(DWORD a, DWORD b) { return ((QWORD)a * b) >> 16; } + #define MAKECONSTDMulScale(s) \ - inline SDWORD DMulScale##s (SDWORD a, SDWORD b, SDWORD c, SDWORD d) \ + static inline SDWORD DMulScale##s (SDWORD a, SDWORD b, SDWORD c, SDWORD d) \ { \ return (((SQWORD)a * b) + ((SQWORD)c * d)) >> s; \ } @@ -142,7 +144,7 @@ MAKECONSTDMulScale(32) #undef MAKECONSTDMulScale #define MAKECONSTTMulScale(s) \ - inline SDWORD TMulScale##s (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD ee) \ + static inline SDWORD TMulScale##s (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD e, SDWORD ee) \ { \ return (((SQWORD)a * b) + ((SQWORD)c * d) + ((SQWORD)e * ee)) >> s; \ } @@ -181,7 +183,7 @@ MAKECONSTTMulScale(31) MAKECONSTTMulScale(32) #undef MAKECONSTTMulScale -inline SDWORD BoundMulScale (SDWORD a, SDWORD b, SDWORD c) +static inline SDWORD BoundMulScale (SDWORD a, SDWORD b, SDWORD c) { union { long long big; @@ -195,7 +197,7 @@ inline SDWORD BoundMulScale (SDWORD a, SDWORD b, SDWORD c) return u.l; } -inline SDWORD DivScale (SDWORD a, SDWORD b, SDWORD c) +static inline SDWORD DivScale (SDWORD a, SDWORD b, SDWORD c) { SDWORD result, dummy; SDWORD lo = a << c; @@ -212,7 +214,7 @@ inline SDWORD DivScale (SDWORD a, SDWORD b, SDWORD c) return result; } -inline SDWORD DivScale1 (SDWORD a, SDWORD b) +static inline SDWORD DivScale1 (SDWORD a, SDWORD b) { SDWORD result, dummy; @@ -229,7 +231,7 @@ inline SDWORD DivScale1 (SDWORD a, SDWORD b) } #define MAKECONSTDivScale(s) \ - inline SDWORD DivScale##s (SDWORD a, SDWORD b) \ + static inline SDWORD DivScale##s (SDWORD a, SDWORD b) \ { \ SDWORD result, dummy; \ asm volatile \ @@ -275,7 +277,7 @@ MAKECONSTDivScale(30) MAKECONSTDivScale(31) #undef MAKECONSTDivScale -inline SDWORD DivScale32 (SDWORD a, SDWORD b) +static inline SDWORD DivScale32 (SDWORD a, SDWORD b) { SDWORD result, dummy; @@ -290,7 +292,7 @@ inline SDWORD DivScale32 (SDWORD a, SDWORD b) return result; } -inline void clearbuf (void *buff, int count, SDWORD clear) +static inline void clearbuf (void *buff, int count, SDWORD clear) { int dummy1, dummy2; asm volatile @@ -303,7 +305,7 @@ inline void clearbuf (void *buff, int count, SDWORD clear) ); } -inline void clearbufshort (void *buff, unsigned int count, WORD clear) +static inline void clearbufshort (void *buff, unsigned int count, WORD clear) { asm volatile ("shr $1,%%ecx\n\t" @@ -315,7 +317,7 @@ inline void clearbufshort (void *buff, unsigned int count, WORD clear) :"%cc"); } -inline SDWORD ksgn (SDWORD a) +static inline SDWORD ksgn (SDWORD a) { SDWORD result, dummy; @@ -329,27 +331,3 @@ inline SDWORD ksgn (SDWORD a) :"%cc"); return result; } - -inline int toint (float v) -{ - volatile QWORD result; - - asm volatile - ("fistpq %0" - :"=m" (result) - :"t" (v) - :"%st"); - return result; -} - -inline int quickertoint (float v) -{ - volatile int result; - - asm volatile - ("fistpl %0" - :"=m" (result) - :"t" (v) - :"%st"); - return result; -} diff --git a/src/gi.cpp b/src/gi.cpp index ae473ef66..e0813479a 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -281,6 +281,7 @@ void FMapInfoParser::ParseGameInfo() GAMEINFOKEY_INT(defaultrespawntime, "defaultrespawntime") GAMEINFOKEY_INT(defaultdropstyle, "defaultdropstyle") GAMEINFOKEY_CSTRING(Endoom, "endoom", 8) + GAMEINFOKEY_INT(player5start, "player5start") else { // ignore unkown keys. diff --git a/src/gi.h b/src/gi.h index c5dfadfc4..0ada094c2 100644 --- a/src/gi.h +++ b/src/gi.h @@ -42,6 +42,7 @@ #define GI_SHAREWARE 0x00000002 #define GI_MENUHACK_EXTENDED 0x00000004 // (Heretic) #define GI_TEASER2 0x00000008 // Alternate version of the Strife Teaser +#define GI_COMPATSHORTTEX 0x00000010 // always force COMPAT_SHORTTEX for IWAD maps. #include "gametype.h" @@ -103,6 +104,7 @@ struct gameinfo_t int definventorymaxamount; int defaultrespawntime; int defaultdropstyle; + int player5start; const char *GetFinalePage(unsigned int num) const; }; diff --git a/src/i_net.cpp b/src/i_net.cpp index 739da291e..3ff86f7ec 100644 --- a/src/i_net.cpp +++ b/src/i_net.cpp @@ -46,6 +46,9 @@ # include # include # include +# ifdef __sun +# include +# endif #endif #include "doomtype.h" @@ -422,7 +425,11 @@ void StartNetwork (bool autoPort) // create communication socket mysocket = UDPsocket (); BindToLocalPort (mysocket, autoPort ? 0 : DOOMPORT); +#ifndef __sun ioctlsocket (mysocket, FIONBIO, &trueval); +#else + fcntl(mysocket, F_SETFL, trueval | O_NONBLOCK); +#endif } void SendAbort (void) diff --git a/src/info.cpp b/src/info.cpp index 3bb0ab482..99110737c 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -39,6 +39,8 @@ #include "m_fixed.h" #include "c_dispatch.h" #include "d_net.h" +#include "v_text.h" + #include "gi.h" #include "vm.h" #include "actor.h" @@ -156,15 +158,26 @@ void FActorInfo::StaticSetActorNums () void FActorInfo::RegisterIDs () { + const PClass *cls = PClass::FindClass(Class->TypeName); + bool set = false; + if (GameFilter == GAME_Any || (GameFilter & gameinfo.gametype)) { if (SpawnID != 0) { - SpawnableThings[SpawnID] = Class; + SpawnableThings[SpawnID] = cls; + if (cls != Class) + { + Printf(TEXTCOLOR_RED"Spawn ID %d refers to hidden class type '%s'\n", SpawnID, cls->TypeName.GetChars()); + } } if (DoomEdNum != -1) { - DoomEdMap.AddType (DoomEdNum, Class); + DoomEdMap.AddType (DoomEdNum, cls); + if (cls != Class) + { + Printf(TEXTCOLOR_RED"Editor number %d refers to hidden class type '%s'\n", DoomEdNum, cls->TypeName.GetChars()); + } } } // Fill out the list for Chex Quest with Doom's actors @@ -172,6 +185,10 @@ void FActorInfo::RegisterIDs () (GameFilter & GAME_Doom)) { DoomEdMap.AddType (DoomEdNum, Class, true); + if (cls != Class) + { + Printf(TEXTCOLOR_RED"Editor number %d refers to hidden class type '%s'\n", DoomEdNum, cls->TypeName.GetChars()); + } } } @@ -182,17 +199,22 @@ void FActorInfo::RegisterIDs () FActorInfo *FActorInfo::GetReplacement (bool lookskill) { - FName skillrepname = AllSkills[gameskill].GetReplacement(this->Class->TypeName); - if (skillrepname != NAME_None && PClass::FindClass(skillrepname) == NULL) + FName skillrepname; + + if (lookskill && AllSkills.Size() > (unsigned)gameskill) { - Printf("Warning: incorrect actor name in definition of skill %s: \n\ - class %s is replaced by inexistent class %s\n\ - Skill replacement will be ignored for this actor.\n", - AllSkills[gameskill].Name.GetChars(), - this->Class->TypeName.GetChars(), skillrepname.GetChars()); - AllSkills[gameskill].SetReplacement(this->Class->TypeName, NAME_None); - AllSkills[gameskill].SetReplacedBy(skillrepname, NAME_None); - lookskill = false; skillrepname = NAME_None; + skillrepname = AllSkills[gameskill].GetReplacement(this->Class->TypeName); + if (skillrepname != NAME_None && PClass::FindClass(skillrepname) == NULL) + { + Printf("Warning: incorrect actor name in definition of skill %s: \n" + "class %s is replaced by non-existent class %s\n" + "Skill replacement will be ignored for this actor.\n", + AllSkills[gameskill].Name.GetChars(), + this->Class->TypeName.GetChars(), skillrepname.GetChars()); + AllSkills[gameskill].SetReplacement(this->Class->TypeName, NAME_None); + AllSkills[gameskill].SetReplacedBy(skillrepname, NAME_None); + lookskill = false; skillrepname = NAME_None; + } } if (Replacement == NULL && (!lookskill || skillrepname == NAME_None)) { @@ -225,17 +247,22 @@ FActorInfo *FActorInfo::GetReplacement (bool lookskill) FActorInfo *FActorInfo::GetReplacee (bool lookskill) { - FName skillrepname = AllSkills[gameskill].GetReplacedBy(this->Class->TypeName); - if (skillrepname != NAME_None && PClass::FindClass(skillrepname) == NULL) + FName skillrepname; + + if (lookskill && AllSkills.Size() > (unsigned)gameskill) { - Printf("Warning: incorrect actor name in definition of skill %s: \ - inexistent class %s is replaced by class %s\n\ - Skill replacement will be ignored for this actor.\n", - AllSkills[gameskill].Name.GetChars(), - skillrepname.GetChars(), this->Class->TypeName.GetChars()); - AllSkills[gameskill].SetReplacedBy(this->Class->TypeName, NAME_None); - AllSkills[gameskill].SetReplacement(skillrepname, NAME_None); - lookskill = false; + skillrepname = AllSkills[gameskill].GetReplacedBy(this->Class->TypeName); + if (skillrepname != NAME_None && PClass::FindClass(skillrepname) == NULL) + { + Printf("Warning: incorrect actor name in definition of skill %s: \n" + "non-existent class %s is replaced by class %s\n" + "Skill replacement will be ignored for this actor.\n", + AllSkills[gameskill].Name.GetChars(), + skillrepname.GetChars(), this->Class->TypeName.GetChars()); + AllSkills[gameskill].SetReplacedBy(this->Class->TypeName, NAME_None); + AllSkills[gameskill].SetReplacement(skillrepname, NAME_None); + lookskill = false; + } } if (Replacee == NULL && (!lookskill || skillrepname == NAME_None)) { diff --git a/src/info.h b/src/info.h index 66e00b7af..8cc381601 100644 --- a/src/info.h +++ b/src/info.h @@ -60,6 +60,7 @@ struct FState long Misc2; // Was changed to BYTE, reverted to long for MBF compat BYTE Frame; BYTE DefineFlags; // Unused byte so let's use it during state creation. + short Light; FState *NextState; VMFunction *ActionFunc; @@ -188,6 +189,7 @@ extern FDoomEdMap DoomEdMap; int GetSpriteIndex(const char * spritename); TArray &MakeStateNameList(const char * fname); +void AddStateLight(FState *state, const char *lname); // Standard parameters for all action functons // self - Actor this action is to operate on (player if a weapon) diff --git a/src/m_alloc.cpp b/src/m_alloc.cpp index 569b5c94d..e81a5ce31 100644 --- a/src/m_alloc.cpp +++ b/src/m_alloc.cpp @@ -52,11 +52,14 @@ #endif #if defined(__APPLE__) #define _msize(p) malloc_size(p) +#elif defined(__sun) +#define _msize(p) (*((size_t*)(p)-1)) #elif !defined(_WIN32) #define _msize(p) malloc_usable_size(p) // from glibc/FreeBSD #endif #ifndef _DEBUG +#if !defined(__sun) void *M_Malloc(size_t size) { void *block = malloc(size); @@ -83,10 +86,50 @@ void *M_Realloc(void *memblock, size_t size) return block; } #else +void *M_Malloc(size_t size) +{ + void *block = malloc(size+sizeof(size_t)); + + if (block == NULL) + I_FatalError("Could not malloc %zu bytes", size); + + size_t *sizeStore = (size_t *) block; + *sizeStore = size; + block = sizeStore+1; + + GC::AllocBytes += _msize(block); + return block; +} + +void *M_Realloc(void *memblock, size_t size) +{ + if(memblock == NULL) + return M_Malloc(size); + + if (memblock != NULL) + { + GC::AllocBytes -= _msize(memblock); + } + void *block = realloc(((size_t*) memblock)-1, size+sizeof(size_t)); + if (block == NULL) + { + I_FatalError("Could not realloc %zu bytes", size); + } + + size_t *sizeStore = (size_t *) block; + *sizeStore = size; + block = sizeStore+1; + + GC::AllocBytes += _msize(block); + return block; +} +#endif +#else #ifdef _MSC_VER #include #endif +#if !defined(__sun) void *M_Malloc_Dbg(size_t size, const char *file, int lineno) { void *block = _malloc_dbg(size, _NORMAL_BLOCK, file, lineno); @@ -112,8 +155,49 @@ void *M_Realloc_Dbg(void *memblock, size_t size, const char *file, int lineno) GC::AllocBytes += _msize(block); return block; } +#else +void *M_Malloc_Dbg(size_t size, const char *file, int lineno) +{ + void *block = _malloc_dbg(size+sizeof(size_t), _NORMAL_BLOCK, file, lineno); + + if (block == NULL) + I_FatalError("Could not malloc %zu bytes", size); + + size_t *sizeStore = (size_t *) block; + *sizeStore = size; + block = sizeStore+1; + + GC::AllocBytes += _msize(block); + return block; +} + +void *M_Realloc_Dbg(void *memblock, size_t size, const char *file, int lineno) +{ + if(memblock == NULL) + return M_Malloc_Dbg(size, file, lineno); + + if (memblock != NULL) + { + GC::AllocBytes -= _msize(memblock); + } + void *block = _realloc_dbg(((size_t*) memblock)-1, size+sizeof(size_t), _NORMAL_BLOCK, file, lineno); + + if (block == NULL) + { + I_FatalError("Could not realloc %zu bytes", size); + } + + size_t *sizeStore = (size_t *) block; + *sizeStore = size; + block = sizeStore+1; + + GC::AllocBytes += _msize(block); + return block; +} +#endif #endif +#if !defined(__sun) void M_Free (void *block) { if (block != NULL) @@ -122,3 +206,14 @@ void M_Free (void *block) free(block); } } +#else +void M_Free (void *block) +{ + if(block != NULL) + { + GC::AllocBytes -= _msize(block); + free(((size_t*) block)-1); + } +} +#endif + diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index a4ffd5950..b82b2fca8 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -631,10 +631,14 @@ void cht_Give (player_t *player, const char *name, int amount) } else { - if (player->mo) - player->mo->health = deh.GodHealth; - - player->health = deh.GodHealth; + if (player->mo != NULL) + { + player->health = player->mo->health = player->mo->GetMaxHealth(); + } + else + { + player->health = deh.GodHealth; + } } if (!giveall) diff --git a/src/m_fixed.h b/src/m_fixed.h index 3d7f577b7..14db59d63 100644 --- a/src/m_fixed.h +++ b/src/m_fixed.h @@ -20,6 +20,8 @@ #include "basicinlines.h" #endif +#include "xs_Float.h" + #define MAKESAFEDIVSCALE(x) \ inline SDWORD SafeDivScale##x (SDWORD a, SDWORD b) \ { \ @@ -134,4 +136,8 @@ inline SDWORD ModDiv (SDWORD num, SDWORD den, SDWORD *dmval) return num % den; } + +#define FLOAT2FIXED(f) xs_Fix<16>::ToFix(f) +#define FIXED2FLOAT(f) ((f) / float(65536)) + #endif diff --git a/src/m_menu.cpp b/src/m_menu.cpp index d16a964dd..88eac1edf 100644 --- a/src/m_menu.cpp +++ b/src/m_menu.cpp @@ -82,6 +82,9 @@ #define KEY_REPEAT_DELAY (TICRATE*5/12) #define KEY_REPEAT_RATE (3) +#define INPUTGRID_WIDTH 13 +#define INPUTGRID_HEIGHT 5 + // TYPES ------------------------------------------------------------------- struct FSaveGameNode : public Node @@ -171,6 +174,8 @@ static void M_DrawFiles (); void M_DrawFrame (int x, int y, int width, int height); static void M_DrawSaveLoadBorder (int x,int y, int len); static void M_DrawSaveLoadCommon (); +static void M_DrawInputGrid(); + static void M_SetupNextMenu (oldmenu_t *menudef); static void M_StartMessage (const char *string, void(*routine)(int)); static void M_EndMessage (int key); @@ -235,6 +240,7 @@ static int saveSlot; // which slot to save in static size_t saveCharIndex; // which char we're editing static int LINEHEIGHT; +static const int PLAYERSETUP_LINEHEIGHT = 16; static char savegamestring[SAVESTRINGSIZE]; static FString EndString; @@ -275,6 +281,18 @@ static int epi; // Selected episode static const char *saved_playerclass = NULL; +// Heretic and Hexen do not, by default, come with glyphs for all of these +// characters. Oh well. Doom and Strife do. +static const char InputGridChars[INPUTGRID_WIDTH * INPUTGRID_HEIGHT] = + "ABCDEFGHIJKLM" + "NOPQRSTUVWXYZ" + "0123456789+-=" + ".,!?@'\":;[]()" + "<>^#$%&*/_ \b"; +static int InputGridX = INPUTGRID_WIDTH - 1; +static int InputGridY = INPUTGRID_HEIGHT - 1; +static bool InputGridOkay; // Last input was with a controller. + // PRIVATE MENU DEFINITIONS ------------------------------------------------ // @@ -2120,8 +2138,29 @@ static void M_PlayerSetupTicker (void) } } + +static void M_DrawPlayerSlider (int x, int y, int cur) +{ + const int range = 255; + + x = (x - 160) * CleanXfac + screen->GetWidth() / 2; + y = (y - 100) * CleanYfac + screen->GetHeight() / 2; + + screen->DrawText (ConFont, CR_WHITE, x, y, + "\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12", + DTA_CellX, 8 * CleanXfac, + DTA_CellY, 8 * CleanYfac, + TAG_DONE); + screen->DrawText (ConFont, CR_ORANGE, x + (5 + (int)((cur * 78) / range)) * CleanXfac, y, + "\x13", + DTA_CellX, 8 * CleanXfac, + DTA_CellY, 8 * CleanYfac, + TAG_DONE); +} + static void M_PlayerSetupDrawer () { + const int LINEHEIGHT = PLAYERSETUP_LINEHEIGHT; int x, xo, yo; EColorRange label, value; DWORD color; @@ -2251,9 +2290,9 @@ static void M_PlayerSetupDrawer () x = SmallFont->StringWidth ("Green") + 8 + PSetupDef.x; color = players[consoleplayer].userinfo.color; - M_DrawSlider (x, PSetupDef.y + LINEHEIGHT*2+yo, 0.0f, 255.0f, float(RPART(color)), -1); - M_DrawSlider (x, PSetupDef.y + LINEHEIGHT*3+yo, 0.0f, 255.0f, float(GPART(color)), -1); - M_DrawSlider (x, PSetupDef.y + LINEHEIGHT*4+yo, 0.0f, 255.0f, float(BPART(color)), -1); + M_DrawPlayerSlider (x, PSetupDef.y + LINEHEIGHT*2+yo, RPART(color)); + M_DrawPlayerSlider (x, PSetupDef.y + LINEHEIGHT*3+yo, GPART(color)); + M_DrawPlayerSlider (x, PSetupDef.y + LINEHEIGHT*4+yo, BPART(color)); // [GRB] Draw class setting int pclass = players[consoleplayer].userinfo.PlayerClass; @@ -2851,14 +2890,6 @@ bool M_Responder (event_t *ev) // This code previously listened for EV_GUI_KeyRepeat to handle repeating // in the menus, but that doesn't work with gamepads, so now we combine // the multiple inputs into buttons and handle the repetition manually. - // - // FIXME: genStringEnter and messageToPrint do not play well with game - // controllers. In fact, you can still interact with the rest of - // the menu using a controller while the menu waits for input. Especially - // bad if you, say, select quit from the main menu. (Ideally, - // genStringEnter will pop up a keypad if it detects a controller is - // being used to navigate the menu. At the very least, it should - // disable the controller.) if (ev->type == EV_GUI_Event) { // Save game and player name string input @@ -2866,8 +2897,9 @@ bool M_Responder (event_t *ev) { if (ev->subtype == EV_GUI_Char) { + InputGridOkay = false; if (saveCharIndex < genStringLen && - (genStringEnter == 2 || (size_t)SmallFont->StringWidth(savegamestring) < (genStringLen-1)*8)) + (genStringEnter == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(savegamestring) < (genStringLen-1)*8)) { savegamestring[saveCharIndex] = (char)ev->data1; savegamestring[++saveCharIndex] = 0; @@ -2875,8 +2907,7 @@ bool M_Responder (event_t *ev) return true; } ch = ev->data1; - if ((ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat) && - ch == '\b') + if ((ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat) && ch == '\b') { if (saveCharIndex > 0) { @@ -2944,48 +2975,68 @@ bool M_Responder (event_t *ev) { mkey = MKEY_Clear; } - else if (!keyup && !OptionsActive) + else if (!keyup) { - ch = tolower (ch); - if (messageToPrint) + if (OptionsActive) { - // Take care of any messages that need input - ch = tolower (ch); - assert(messageRoutine != NULL); - if (ch != ' ' && ch != 'n' && ch != 'y') - { - return false; - } - D_RemoveNextCharEvent(); - M_EndMessage(ch); - return true; + M_OptResponder(ev); } else { - // Search for a menu item associated with the pressed key. - for (i = (itemOn + 1) % currentMenu->numitems; - i != itemOn; - i = (i + 1) % currentMenu->numitems) + ch = tolower (ch); + if (messageToPrint) { + // Take care of any messages that need input + ch = tolower (ch); + assert(messageRoutine != NULL); + if (ch != ' ' && ch != 'n' && ch != 'y') + { + return false; + } + D_RemoveNextCharEvent(); + M_EndMessage(ch); + return true; + } + else + { + // Search for a menu item associated with the pressed key. + for (i = (itemOn + 1) % currentMenu->numitems; + i != itemOn; + i = (i + 1) % currentMenu->numitems) + { + if (currentMenu->menuitems[i].alphaKey == ch) + { + break; + } + } if (currentMenu->menuitems[i].alphaKey == ch) { - break; + itemOn = i; + S_Sound(CHAN_VOICE | CHAN_UI, "menu/cursor", 1, ATTN_NONE); + return true; } } - if (currentMenu->menuitems[i].alphaKey == ch) - { - itemOn = i; - S_Sound(CHAN_VOICE | CHAN_UI, "menu/cursor", 1, ATTN_NONE); - return true; - } } } break; } + if (!keyup) + { + InputGridOkay = false; + } } else if (ev->type == EV_KeyDown || ev->type == EV_KeyUp) { keyup = ev->type == EV_KeyUp; + // If this is a button down, it's okay to show the input grid if the + // next action causes us to enter genStringEnter mode. If we are + // already in that mode, then we let M_ButtonHandler() turn it on so + // that it will know if a button press happened while the input grid + // was turned off. + if (!keyup && !genStringEnter) + { + InputGridOkay = true; + } ch = ev->data1; switch (ch) { @@ -3116,6 +3167,74 @@ void M_ButtonHandler(EMenuKey key, bool repeat) } return; } + if (genStringEnter) + { + int ch; + + switch (key) + { + case MKEY_Down: + InputGridY = (InputGridY + 1) % INPUTGRID_HEIGHT; + break; + + case MKEY_Up: + InputGridY = (InputGridY + INPUTGRID_HEIGHT - 1) % INPUTGRID_HEIGHT; + break; + + case MKEY_Right: + InputGridX = (InputGridX + 1) % INPUTGRID_WIDTH; + break; + + case MKEY_Left: + InputGridX = (InputGridX + INPUTGRID_WIDTH - 1) % INPUTGRID_WIDTH; + break; + + case MKEY_Clear: + if (saveCharIndex > 0) + { + savegamestring[--saveCharIndex] = 0; + } + break; + + case MKEY_Enter: + assert(unsigned(InputGridX) < INPUTGRID_WIDTH && unsigned(InputGridY) < INPUTGRID_HEIGHT); + if (InputGridOkay) + { + ch = InputGridChars[InputGridX + InputGridY * INPUTGRID_WIDTH]; + if (ch == 0) // end + { + if (savegamestring[0] != '\0') + { + genStringEnter = 0; + if (messageToPrint) + { + M_ClearMenus(); + } + genStringEnd(SelSaveGame); + } + } + else if (ch == '\b') // bs + { + if (saveCharIndex > 0) + { + savegamestring[--saveCharIndex] = 0; + } + } + else if (saveCharIndex < genStringLen && + (genStringEnter == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(savegamestring) < (genStringLen-1)*8)) + { + savegamestring[saveCharIndex] = ch; + savegamestring[++saveCharIndex] = 0; + } + } + break; + + default: + break; // Keep GCC quiet + } + InputGridOkay = true; + return; + } if (currentMenu == &SaveDef || currentMenu == &LoadDef) { M_SaveLoadButtonHandler(key); @@ -3330,6 +3449,12 @@ static void M_SaveSelect (const FSaveGameNode *file) } else { + // If we are naming a new save, don't start the cursor on "end". + if (InputGridX == INPUTGRID_WIDTH - 1 && InputGridY == INPUTGRID_HEIGHT - 1) + { + InputGridX = 0; + InputGridY = 0; + } savegamestring[0] = 0; } saveCharIndex = strlen (savegamestring); @@ -3443,7 +3568,13 @@ void M_Drawer () screen->DrawText(SmallFont, CR_UNTRANSLATED, 160, y + fontheight + 1, GStrings["TXT_NO"], DTA_Clean, true, TAG_DONE); if (skullAnimCounter < 6) { - M_DrawConText(CR_RED, 150, y + (fontheight + 1) * messageSelection, "\xd"); + screen->DrawText(ConFont, CR_RED, + (150 - 160) * CleanXfac + screen->GetWidth() / 2, + (y + (fontheight + 1) * messageSelection - 100) * CleanYfac + screen->GetHeight() / 2, + "\xd", + DTA_CellX, 8 * CleanXfac, + DTA_CellY, 8 * CleanYfac, + TAG_DONE); } } } @@ -3516,7 +3647,7 @@ void M_Drawer () if (skullAnimCounter < 6) { screen->DrawText (ConFont, CR_RED, x - 16, - currentMenu->y + itemOn*LINEHEIGHT + + currentMenu->y + itemOn*PLAYERSETUP_LINEHEIGHT + (!(gameinfo.gametype & (GAME_DoomStrifeChex)) ? 6 : -1), "\xd", DTA_Clean, true, TAG_DONE); } @@ -3542,6 +3673,10 @@ void M_Drawer () } } } + if (genStringEnter && InputGridOkay) + { + M_DrawInputGrid(); + } } } @@ -3567,6 +3702,77 @@ static void M_ClearSaveStuff () } } +static void M_DrawInputGrid() +{ + const int cell_width = 18 * CleanXfac; + const int cell_height = 12 * CleanYfac; + const int top_padding = cell_height / 2 - SmallFont->GetHeight() * CleanYfac / 2; + + // Darken the background behind the character grid. + // Unless we frame it with a border, I think it looks better to extend the + // background across the full width of the screen. + screen->Dim(0, 0.8f, + 0 /*screen->GetWidth()/2 - 13 * cell_width / 2*/, + screen->GetHeight() - 5 * cell_height, + screen->GetWidth() /*13 * cell_width*/, + 5 * cell_height); + + // Highlight the background behind the selected character. + screen->Dim(MAKERGB(255,248,220), 0.6f, + InputGridX * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen->GetWidth() / 2, + InputGridY * cell_height - INPUTGRID_HEIGHT * cell_height + screen->GetHeight(), + cell_width, cell_height); + + for (int y = 0; y < INPUTGRID_HEIGHT; ++y) + { + const int yy = y * cell_height - INPUTGRID_HEIGHT * cell_height + screen->GetHeight(); + for (int x = 0; x < INPUTGRID_WIDTH; ++x) + { + int width; + const int xx = x * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen->GetWidth() / 2; + const int ch = InputGridChars[y * INPUTGRID_WIDTH + x]; + FTexture *pic = SmallFont->GetChar(ch, &width); + EColorRange color; + FRemapTable *remap; + + // The highlighted character is yellow; the rest are dark gray. + color = (x == InputGridX && y == InputGridY) ? CR_YELLOW : CR_DARKGRAY; + remap = SmallFont->GetColorTranslation(color); + + if (pic != NULL) + { + // Draw a normal character. + screen->DrawTexture(pic, xx + cell_width/2 - width*CleanXfac/2, yy + top_padding, + DTA_Translation, remap, + DTA_CleanNoMove, true, + TAG_DONE); + } + else if (ch == ' ') + { + // Draw the space as a box outline. We also draw it 50% wider than it really is. + const int x1 = xx + cell_width/2 - width * CleanXfac * 3 / 4; + const int x2 = x1 + width * 3 * CleanXfac / 2; + const int y1 = yy + top_padding; + const int y2 = y1 + SmallFont->GetHeight() * CleanYfac; + const int palentry = remap->Remap[remap->NumEntries*2/3]; + const uint32 palcolor = remap->Palette[remap->NumEntries*2/3]; + screen->Clear(x1, y1, x2, y1+CleanYfac, palentry, palcolor); // top + screen->Clear(x1, y2, x2, y2+CleanYfac, palentry, palcolor); // bottom + screen->Clear(x1, y1+CleanYfac, x1+CleanXfac, y2, palentry, palcolor); // left + screen->Clear(x2-CleanXfac, y1+CleanYfac, x2, y2, palentry, palcolor); // right + } + else if (ch == '\b' || ch == 0) + { + // Draw the backspace and end "characters". + const char *const str = ch == '\b' ? "BS" : "ED"; + screen->DrawText(SmallFont, color, + xx + cell_width/2 - SmallFont->StringWidth(str)*CleanXfac/2, + yy + top_padding, str, DTA_CleanNoMove, true, TAG_DONE); + } + } + } +} + // // M_ClearMenus // diff --git a/src/m_menu.h b/src/m_menu.h index ce7ba99e2..f46b6a9e9 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -84,9 +84,6 @@ void M_DeactivateMenuInput (); void M_NotifyNewSave (const char *file, const char *title, bool okForQuicksave); -// Draw a slider. Set fracdigits negative to not display the current value numerically. -void M_DrawSlider (int x, int y, double min, double max, double cur, int fracdigits=1); - // // MENU TYPEDEFS // @@ -181,7 +178,7 @@ struct menu_t { int scrolltop; int scrollpos; int y; - void (*PreDraw)(void); + bool (*PreDraw)(void); bool DontDim; void (*EscapeHandler)(void); }; @@ -267,6 +264,7 @@ extern menustack_t MenuStack[16]; extern int MenuStackDepth; extern bool OptionsActive; +extern int skullAnimCounter; extern menu_t *CurrentMenu; extern int CurrentItem; diff --git a/src/m_options.cpp b/src/m_options.cpp index 8048b52ea..9c8199757 100644 --- a/src/m_options.cpp +++ b/src/m_options.cpp @@ -94,6 +94,7 @@ EXTERN_CVAR(Bool, hud_althud) EXTERN_CVAR(Int, compatmode) EXTERN_CVAR (Bool, vid_vsync) EXTERN_CVAR(Bool, displaynametags) +EXTERN_CVAR (Int, snd_channels) // // defaulted values @@ -106,8 +107,6 @@ CVAR (Bool, show_obituaries, true, CVAR_ARCHIVE) EXTERN_CVAR (Bool, longsavemessages) EXTERN_CVAR (Bool, screenshot_quiet) -extern int skullAnimCounter; - EXTERN_CVAR (Bool, cl_run) EXTERN_CVAR (Int, crosshair) EXTERN_CVAR (Bool, freelook) @@ -346,7 +345,7 @@ menu_t JoystickConfigMenu = * *=======================================*/ -static menuitem_t ControlsItems[] = +menuitem_t ControlsItems[] = { { redtext,"ENTER to change, BACKSPACE to clear", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, @@ -610,6 +609,7 @@ EXTERN_CVAR (Color, am_ovtelecolor) EXTERN_CVAR (Color, am_intralevelcolor) EXTERN_CVAR (Color, am_interlevelcolor) EXTERN_CVAR (Color, am_secretsectorcolor) +EXTERN_CVAR (Color, am_ovsecretsectorcolor) EXTERN_CVAR (Color, am_thingcolor_friend) EXTERN_CVAR (Color, am_thingcolor_monster) EXTERN_CVAR (Color, am_thingcolor_item) @@ -645,6 +645,7 @@ static menuitem_t MapColorsItems[] = { { colorpicker, "2-sided walls (overlay)", {&am_ovotherwallscolor},{0}, {0}, {0}, {0} }, { colorpicker, "Not-yet-seen walls (overlay)", {&am_ovunseencolor}, {0}, {0}, {0}, {0} }, { colorpicker, "Teleporter (overlay)", {&am_ovtelecolor}, {0}, {0}, {0}, {0} }, + { colorpicker, "Secret sector (overlay)", {&am_ovsecretsectorcolor}, {0}, {0}, {0}, {0} }, { colorpicker, "Actors (overlay) (for cheat)", {&am_ovthingcolor}, {0}, {0}, {0}, {0} }, { colorpicker, "Monsters (overlay) (for cheat)", {&am_ovthingcolor_monster}, {0}, {0}, {0}, {0} }, { colorpicker, "Friends (overlay) (for cheat)", {&am_ovthingcolor_friend}, {0}, {0}, {0}, {0} }, @@ -1112,6 +1113,7 @@ static menuitem_t CompatibilityItems[] = { { bitflag, "Monster movement is affected by effects", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_MBFMONSTERMOVE} }, { bitflag, "Crushed monsters can be resurrected", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_CORPSEGIBS} }, { bitflag, "Friendly monsters aren't blocked", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_NOBLOCKFRIENDS} }, + { bitflag, "Invert sprite sorting", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_SPRITESORT} }, { discrete, "Interpolate monster movement", {&nomonsterinterpolation}, {2.0}, {0.0}, {0.0}, {NoYes} }, }; @@ -1251,6 +1253,7 @@ static menuitem_t SoundItems[] = { discrete, "Underwater reverb", {&snd_waterreverb}, {2.0}, {0.0}, {0.0}, {OnOff} }, { slider, "Underwater cutoff", {&snd_waterlp}, {0.0}, {2000.0},{50.0}, {NULL} }, { discrete, "Randomize pitches", {&snd_pitched}, {2.0}, {0.0}, {0.0}, {OnOff} }, + { slider, "Sound channels", {&snd_channels}, {8.0}, {256.0}, {8.0}, {NULL} }, { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, { more, "Restart sound", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)MakeSoundChanges} }, { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, @@ -1491,11 +1494,9 @@ void M_DrawConText (int color, int x, int y, const char *str) { int len = (int)strlen(str); - x = (x - 160) * CleanXfac + screen->GetWidth() / 2; - y = (y - 100) * CleanYfac + screen->GetHeight() / 2; screen->DrawText (ConFont, color, x, y, str, - DTA_CellX, 8 * CleanXfac, - DTA_CellY, 8 * CleanYfac, + DTA_CellX, 8 * CleanXfac_1, + DTA_CellY, 8 * CleanYfac_1, TAG_DONE); } @@ -1555,21 +1556,22 @@ bool M_StartOptionsMenu (void) return true; } -void M_DrawSlider (int x, int y, double min, double max, double cur,int fracdigits) +// Draw a slider. Set fracdigits negative to not display the current value numerically. +static void M_DrawSlider (int x, int y, double min, double max, double cur,int fracdigits) { double range; range = max - min; - cur = clamp(cur, min, max) - min; + double ccur = clamp(cur, min, max) - min; M_DrawConText(CR_WHITE, x, y, "\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12"); - M_DrawConText(CR_ORANGE, x + 5 + (int)((cur * 78) / range), y, "\x13"); + M_DrawConText(CR_ORANGE, x + int((5 + ((ccur * 78) / range)) * CleanXfac_1), y, "\x13"); if (fracdigits >= 0) { char textbuf[16]; mysnprintf(textbuf, countof(textbuf), "%.*f", fracdigits, cur); - screen->DrawText(SmallFont, CR_DARKGRAY, x + 12*8 + 4, y, textbuf, DTA_Clean, true, TAG_DONE); + screen->DrawText(SmallFont, CR_DARKGRAY, x + (12*8 + 4) * CleanXfac_1, y, textbuf, DTA_CleanNoMove_1, true, TAG_DONE); } } @@ -1640,6 +1642,7 @@ void M_OptDrawer () DWORD overlay; int labelofs; int indent; + int cursorspace; if (!CurrentMenu->DontDim) { @@ -1648,7 +1651,7 @@ void M_OptDrawer () if (CurrentMenu->PreDraw != NULL) { - CurrentMenu->PreDraw (); + if (CurrentMenu->PreDraw ()) return; } if (CurrentMenu->y != 0) @@ -1660,9 +1663,9 @@ void M_OptDrawer () if (BigFont && CurrentMenu->texttitle) { screen->DrawText (BigFont, gameinfo.gametype & GAME_DoomChex ? CR_RED : CR_UNTRANSLATED, - 160-BigFont->StringWidth (CurrentMenu->texttitle)/2, 10, - CurrentMenu->texttitle, DTA_Clean, true, TAG_DONE); - y = 15 + BigFont->GetHeight (); + (screen->GetWidth() - BigFont->StringWidth(CurrentMenu->texttitle) * CleanXfac_1) / 2, 10*CleanYfac_1, + CurrentMenu->texttitle, DTA_CleanNoMove_1, true, TAG_DONE); + y = 15 + BigFont->GetHeight(); } else { @@ -1671,7 +1674,7 @@ void M_OptDrawer () } if (gameinfo.gametype & GAME_Raven) { - labelofs = 2; + labelofs = 2 * CleanXfac_1; y -= 2; fontheight = 9; } @@ -1680,9 +1683,13 @@ void M_OptDrawer () labelofs = 0; fontheight = 8; } - ytop = y + CurrentMenu->scrolltop * 8; + cursorspace = 14 * CleanXfac_1; + y *= CleanYfac_1; + fontheight *= CleanYfac_1; + ytop = y + CurrentMenu->scrolltop * 8 * CleanYfac_1; + int lastrow = screen->GetHeight() - SmallFont->GetHeight() * CleanYfac_1; - for (i = 0; i < CurrentMenu->numitems && y <= 200 - SmallFont->GetHeight(); i++, y += fontheight) + for (i = 0; i < CurrentMenu->numitems && y <= lastrow; i++, y += fontheight) { if (i == CurrentMenu->scrolltop) { @@ -1693,15 +1700,30 @@ void M_OptDrawer () overlay = 0; if (item->type == discrete && item->c.discretecenter == 1) { - indent = 160; + indent = screen->GetWidth() / 2; } else if (item->type == joymore) { - indent = 4; + indent = 4 * CleanXfac_1; } else { indent = CurrentMenu->indent; + if (indent > 280) + { // kludge for the compatibility options with their extremely long labels + if (indent + 40 <= CleanWidth_1) + { + indent = (screen->GetWidth() - ((indent + 40) * CleanXfac_1)) / 2 + indent * CleanXfac_1; + } + else + { + indent = screen->GetWidth() - 40 * CleanXfac_1; + } + } + else + { + indent = (indent - 160) * CleanXfac_1 + screen->GetWidth() / 2; + } } if (item->type != screenres) @@ -1724,7 +1746,7 @@ void M_OptDrawer () label = somestring; } } - width = SmallFont->StringWidth(label); + width = SmallFont->StringWidth(label) * CleanXfac_1; switch (item->type) { case more: @@ -1734,34 +1756,34 @@ void M_OptDrawer () break; case joymore: - x = 20; + x = 20 * CleanXfac_1; color = MoreColor; break; case numberedmore: case rsafemore: case rightmore: - x = indent + 14; + x = indent + cursorspace; color = item->type != rightmore ? CR_GREEN : MoreColor; break; case redtext: - x = 160 - width / 2; + x = screen->GetWidth() / 2 - width / 2; color = LabelColor; break; case whitetext: - x = 160 - width / 2; + x = screen->GetWidth() / 2 - width / 2; color = CR_GOLD;//ValueColor; break; case listelement: - x = indent + 14; + x = indent + cursorspace; color = LabelColor; break; case colorpicker: - x = indent + 14; + x = indent + cursorspace; color = MoreColor; break; @@ -1778,7 +1800,7 @@ void M_OptDrawer () ? CR_YELLOW : LabelColor; break; } - screen->DrawText (SmallFont, color, x, y, label, DTA_Clean, true, DTA_ColorOverlay, overlay, TAG_DONE); + screen->DrawText (SmallFont, color, x, y, label, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, TAG_DONE); switch (item->type) { @@ -1788,8 +1810,8 @@ void M_OptDrawer () char tbuf[16]; mysnprintf (tbuf, countof(tbuf), "%d.", item->b.position); - x = indent - SmallFont->StringWidth (tbuf); - screen->DrawText (SmallFont, CR_GREY, x, y, tbuf, DTA_Clean, true, TAG_DONE); + x = indent - SmallFont->StringWidth (tbuf) * CleanXfac_1; + screen->DrawText (SmallFont, CR_GREY, x, y, tbuf, DTA_CleanNoMove_1, true, TAG_DONE); } break; @@ -1805,14 +1827,14 @@ void M_OptDrawer () if (v == vals) { - screen->DrawText (SmallFont, ValueColor, indent + 14, y, "Unknown", - DTA_Clean, true, TAG_DONE); + screen->DrawText (SmallFont, ValueColor, indent + cursorspace, y, "Unknown", + DTA_CleanNoMove_1, true, TAG_DONE); } else { screen->DrawText (SmallFont, item->type == cdiscrete ? v : ValueColor, - indent + 14, y, item->e.values[v].name, - DTA_Clean, true, TAG_DONE); + indent + cursorspace, y, item->e.values[v].name, + DTA_CleanNoMove_1, true, TAG_DONE); } } @@ -1862,15 +1884,15 @@ void M_OptDrawer () if (v == vals) { - screen->DrawText (SmallFont, ValueColor, indent + 14, y, "Unknown", - DTA_Clean, true, DTA_ColorOverlay, overlay, TAG_DONE); + screen->DrawText (SmallFont, ValueColor, indent + cursorspace, y, "Unknown", + DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, TAG_DONE); } else { screen->DrawText (SmallFont, item->type == cdiscrete ? v : ValueColor, - indent + 14, y, + indent + cursorspace, y, item->type != discretes ? item->e.values[v].name : item->e.valuestrings[v].name.GetChars(), - DTA_Clean, true, DTA_ColorOverlay, overlay, TAG_DONE); + DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, TAG_DONE); } } @@ -1882,18 +1904,18 @@ void M_OptDrawer () value = item->a.cvar->GetGenericRep (CVAR_String); v = M_FindCurVal(value.String, item->e.enumvalues, (int)item->b.numvalues); - screen->DrawText(SmallFont, ValueColor, indent + 14, y, v, DTA_Clean, true, TAG_DONE); + screen->DrawText(SmallFont, ValueColor, indent + cursorspace, y, v, DTA_CleanNoMove_1, true, TAG_DONE); } break; case nochoice: - screen->DrawText (SmallFont, CR_GOLD, indent + 14, y, - (item->e.values[(int)item->b.min]).name, DTA_Clean, true, TAG_DONE); + screen->DrawText (SmallFont, CR_GOLD, indent + cursorspace, y, + (item->e.values[(int)item->b.min]).name, DTA_CleanNoMove_1, true, TAG_DONE); break; case joy_sens: value.Float = SELECTED_JOYSTICK->GetSensitivity(); - M_DrawSlider (indent + 14, y + labelofs, item->b.min, item->c.max, value.Float, 1); + M_DrawSlider (indent + cursorspace, y + labelofs, item->b.min, item->c.max, value.Float, 1); break; case joy_slider: @@ -1906,29 +1928,29 @@ void M_OptDrawer () assert(item->e.joyslidernum == 1); value.Float = SELECTED_JOYSTICK->GetAxisDeadZone(item->a.joyselection); } - M_DrawSlider (indent + 14, y + labelofs, item->b.min, item->c.max, fabs(value.Float), 3); + M_DrawSlider (indent + cursorspace, y + labelofs, item->b.min, item->c.max, fabs(value.Float), 3); break; case joy_inverter: assert(item->e.joyslidernum == 0); value.Float = SELECTED_JOYSTICK->GetAxisScale(item->a.joyselection); - screen->DrawText(SmallFont, ValueColor, indent + 14, y, + screen->DrawText(SmallFont, ValueColor, indent + cursorspace, y, (value.Float < 0) ? "Yes" : "No", - DTA_Clean, true, TAG_DONE); + DTA_CleanNoMove_1, true, TAG_DONE); break; case slider: value = item->a.cvar->GetGenericRep (CVAR_Float); - M_DrawSlider (indent + 14, y + labelofs, item->b.min, item->c.max, value.Float, 1); + M_DrawSlider (indent + cursorspace, y + labelofs, item->b.min, item->c.max, value.Float, 1); break; case absslider: value = item->a.cvar->GetGenericRep (CVAR_Float); - M_DrawSlider (indent + 14, y + labelofs, item->b.min, item->c.max, fabs(value.Float), 1); + M_DrawSlider (indent + cursorspace, y + labelofs, item->b.min, item->c.max, fabs(value.Float), 1); break; case intslider: - M_DrawSlider (indent + 14, y + labelofs, item->b.min, item->c.max, item->a.fval, 0); + M_DrawSlider (indent + cursorspace, y + labelofs, item->b.min, item->c.max, item->a.fval, 0); break; case control: @@ -1938,12 +1960,12 @@ void M_OptDrawer () C_NameKeys (description, item->b.key1, item->c.key2); if (description[0]) { - M_DrawConText(CR_WHITE, indent + 14, y-1+labelofs, description); + M_DrawConText(CR_WHITE, indent + cursorspace, y-1+labelofs, description); } else { - screen->DrawText(SmallFont, CR_BLACK, indent + 14, y + labelofs, "---", - DTA_Clean, true, TAG_DONE); + screen->DrawText(SmallFont, CR_BLACK, indent + cursorspace, y + labelofs, "---", + DTA_CleanNoMove_1, true, TAG_DONE); } } break; @@ -1951,9 +1973,9 @@ void M_OptDrawer () case colorpicker: { int box_x, box_y; - box_x = (indent - 35 - 160) * CleanXfac + screen->GetWidth()/2; - box_y = (y - ((gameinfo.gametype & GAME_Raven) ? 99 : 100)) * CleanYfac + screen->GetHeight()/2; - screen->Clear (box_x, box_y, box_x + 32*CleanXfac, box_y + (fontheight-1)*CleanYfac, + box_x = indent - 35 * CleanXfac_1; + box_y = (gameinfo.gametype & GAME_Raven) ? y - CleanYfac_1 : y; + screen->Clear (box_x, box_y, box_x + 32*CleanXfac_1, box_y + fontheight-CleanYfac_1, item->a.colorcvar->GetIndex(), 0); } break; @@ -1962,12 +1984,12 @@ void M_OptDrawer () { int box_x, box_y; int x1, p; - const int w = fontheight*CleanXfac; - const int h = fontheight*CleanYfac; + const int w = fontheight; + const int h = fontheight; - box_y = (y - 98) * CleanYfac + screen->GetHeight()/2; + box_y = y - 2 * CleanYfac_1; p = 0; - box_x = (indent - 32 - 160) * CleanXfac + screen->GetWidth()/2; + box_x = indent - 32 * CleanXfac_1; for (x1 = 0, p = int(item->b.min * 16); x1 < 16; ++p, ++x1) { screen->Clear (box_x, box_y, box_x + w, box_y + h, p, 0); @@ -2022,7 +2044,7 @@ void M_OptDrawer () } screen->DrawText (SmallFont, ValueColor, - indent + 14, y, str, DTA_Clean, true, TAG_DONE); + indent + cursorspace, y, str, DTA_CleanNoMove_1, true, TAG_DONE); } break; @@ -2034,12 +2056,13 @@ void M_OptDrawer () i == CurrentItem && (skullAnimCounter < 6 || menuactive == MENU_WaitKey)) { - M_DrawConText(CR_RED, indent + 3, y-1+labelofs, "\xd"); + M_DrawConText(CR_RED, indent + 3 * CleanXfac_1, y-CleanYfac_1+labelofs, "\xd"); } } else { char *str = NULL; + int colwidth = screen->GetWidth() / 3; for (x = 0; x < 3; x++) { @@ -2056,13 +2079,13 @@ void M_OptDrawer () else color = CR_BRICK; //LabelColor; - screen->DrawText (SmallFont, color, 104 * x + 20, y, str, DTA_Clean, true, TAG_DONE); + screen->DrawText (SmallFont, color, colwidth * x + 20 * CleanXfac_1, y, str, DTA_CleanNoMove_1, true, TAG_DONE); } } if (i == CurrentItem && ((item->a.selmode != -1 && (skullAnimCounter < 6 || menuactive == MENU_WaitKey)) || testingmode)) { - M_DrawConText(CR_RED, item->a.selmode * 104 + 8, y-1 + labelofs, "\xd"); + M_DrawConText(CR_RED, item->a.selmode * colwidth + 8 * CleanXfac_1, y - CleanYfac_1 + labelofs, "\xd"); } } } @@ -2073,11 +2096,11 @@ void M_OptDrawer () if (CanScrollUp) { - M_DrawConText(CR_ORANGE, 3, ytop + labelofs, "\x1a"); + M_DrawConText(CR_ORANGE, 3 * CleanXfac_1, ytop + labelofs, "\x1a"); } if (CanScrollDown) { - M_DrawConText(CR_ORANGE, 3, y - 8 + labelofs, "\x1b"); + M_DrawConText(CR_ORANGE, 3 * CleanXfac_1, y - 8*CleanYfac_1 + labelofs, "\x1b"); } if (flagsvar) @@ -2100,8 +2123,8 @@ void M_OptDrawer () } } screen->DrawText (SmallFont, ValueColor, - 160 - (SmallFont->StringWidth (flagsblah) >> 1), 0, flagsblah, - DTA_Clean, true, TAG_DONE); + (screen->GetWidth() - SmallFont->StringWidth (flagsblah) * CleanXfac_1) / 2, 0, flagsblah, + DTA_CleanNoMove_1, true, TAG_DONE); } } @@ -2120,11 +2143,10 @@ void M_OptResponder(event_t *ev) CurrentMenu->items[0].label = OldMessage; CurrentMenu->items[0].type = OldType; } - else if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_KeyDown && tolower(ev->data1) == 't') + else if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_KeyDown) { - // Test selected resolution - if (CurrentMenu == &ModesMenu) - { + if (CurrentMenu == &ModesMenu && (ev->data1 == 't' || ev->data1 == 'T')) + { // Test selected resolution if (!(item->type == screenres && GetSelectedSize (CurrentItem, &NewWidth, &NewHeight))) { @@ -2140,6 +2162,23 @@ void M_OptResponder(event_t *ev) S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", 1, ATTN_NONE); SetModesMenu (NewWidth, NewHeight, NewBits); } + else if (ev->data1 >= '0' && ev->data1 <= '9') + { // Activate an item of type numberedmore + int i; + int num = ev->data1 == '0' ? 10 : ev->data1 - '0'; + + for (i = 0; i < CurrentMenu->numitems; ++i) + { + menuitem_t *item = CurrentMenu->items + i; + + if (item->type == numberedmore && item->b.position == num) + { + CurrentItem = i; + M_OptButtonHandler(MKEY_Enter, false); + break; + } + } + } } } @@ -2254,31 +2293,33 @@ void M_OptButtonHandler(EMenuKey key, bool repeat) } if (CurrentItem < 0) { - int maxitems, rowheight; + int ytop, maxitems, rowheight; // Figure out how many lines of text fit on the menu if (CurrentMenu->y != 0) { - maxitems = CurrentMenu->y; + ytop = CurrentMenu->y; } else if (BigFont && CurrentMenu->texttitle) { - maxitems = 15 + BigFont->GetHeight (); + ytop = 15 + BigFont->GetHeight (); } else { - maxitems = 15; + ytop = 15; } if (!(gameinfo.gametype & GAME_DoomChex)) { - maxitems -= 2; + ytop -= 2; rowheight = 9; } else { rowheight = 8; } - maxitems = (200 - SmallFont->GetHeight () - maxitems) / rowheight + 1; + ytop *= CleanYfac_1; + rowheight *= CleanYfac_1; + maxitems = (screen->GetHeight() - SmallFont->GetHeight() - ytop) / rowheight + 1; CurrentMenu->scrollpos = MAX (0,CurrentMenu->numitems - maxitems + CurrentMenu->scrolltop); CurrentItem = CurrentMenu->numitems - 1; @@ -2746,35 +2787,7 @@ void M_OptButtonHandler(EMenuKey key, bool repeat) item->b.key1 = item->c.key2 = 0; } break; -/* - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - int lookfor = ch == '0' ? 10 : ch - '0', i; - for (i = 0; i < CurrentMenu->numitems; ++i) - { - if (CurrentMenu->items[i].b.position == lookfor) - { - CurrentItem = i; - item = &CurrentMenu->items[i]; - break; - } - } - if (i == CurrentMenu->numitems) - { - break; - } - // Otherwise, fall through to '\r' below - } -*/ + case MKEY_Enter: if (CurrentMenu == &ModesMenu && item->type == screenres) { @@ -2977,7 +2990,7 @@ static void DefaultCustomColors () } } -static void ColorPickerDrawer () +static bool ColorPickerDrawer () { DWORD newColor = MAKEARGB(255, int(ColorPickerItems[2].a.fval), @@ -2986,16 +2999,17 @@ static void ColorPickerDrawer () DWORD oldColor = DWORD(*ColorPickerItems[0].a.colorcvar) | 0xFF000000; int x = screen->GetWidth()*2/3; - int y = (15 + BigFont->GetHeight() + SmallFont->GetHeight()*5 - 90) * CleanYfac + screen->GetHeight()/2; + int y = (15 + BigFont->GetHeight() + SmallFont->GetHeight()*5 - 10) * CleanYfac_1; - screen->Clear (x, y, x + 48*CleanXfac, y + 48*CleanYfac, -1, oldColor); - screen->Clear (x + 48*CleanXfac, y, x + 48*2*CleanXfac, y + 48*CleanYfac, -1, newColor); + screen->Clear (x, y, x + 48*CleanXfac_1, y + 48*CleanYfac_1, -1, oldColor); + screen->Clear (x + 48*CleanXfac_1, y, x + 48*2*CleanXfac_1, y + 48*CleanYfac_1, -1, newColor); - y += 49*CleanYfac; - screen->DrawText (SmallFont, CR_GRAY, x+(24-SmallFont->StringWidth("Old")/2)*CleanXfac, y, - "Old", DTA_CleanNoMove, true, TAG_DONE); - screen->DrawText (SmallFont, CR_WHITE, x+(48+24-SmallFont->StringWidth("New")/2)*CleanXfac, y, - "New", DTA_CleanNoMove, true, TAG_DONE); + y += 49*CleanYfac_1; + screen->DrawText (SmallFont, CR_GRAY, x+(24-SmallFont->StringWidth("Old")/2)*CleanXfac_1, y, + "Old", DTA_CleanNoMove_1, true, TAG_DONE); + screen->DrawText (SmallFont, CR_WHITE, x+(48+24-SmallFont->StringWidth("New")/2)*CleanXfac_1, y, + "New", DTA_CleanNoMove_1, true, TAG_DONE); + return false; } static void SetColorPickerSliders () @@ -3092,15 +3106,17 @@ CCMD (menu_mouse) MouseOptions (); } -static void DrawJoystickConfigMenuHeader() +static bool DrawJoystickConfigMenuHeader() { FString joyname = SELECTED_JOYSTICK->GetName(); screen->DrawText(BigFont, gameinfo.gametype & GAME_DoomChex ? CR_RED : CR_UNTRANSLATED, - 160-BigFont->StringWidth(CurrentMenu->texttitle)/2, 5, - CurrentMenu->texttitle, DTA_Clean, true, TAG_DONE); + (screen->GetWidth() - BigFont->StringWidth(CurrentMenu->texttitle) * CleanXfac_1) / 2, + 5 * CleanYfac_1, + CurrentMenu->texttitle, DTA_CleanNoMove_1, true, TAG_DONE); screen->DrawText(SmallFont, gameinfo.gametype & GAME_DoomChex ? CR_RED : CR_UNTRANSLATED, - 160-SmallFont->StringWidth(joyname)/2, 8 + BigFont->GetHeight(), - joyname, DTA_Clean, true, TAG_DONE); + (screen->GetWidth() - SmallFont->StringWidth(joyname) * CleanXfac_1) / 2, (8 + BigFont->GetHeight()) * CleanYfac_1, + joyname, DTA_CleanNoMove_1, true, TAG_DONE); + return false; } static void UpdateJoystickConfigMenu(IJoystickConfig *joy) diff --git a/src/mscinlines.h b/src/mscinlines.h index e432e4259..c7f2527f4 100644 --- a/src/mscinlines.h +++ b/src/mscinlines.h @@ -82,6 +82,13 @@ __forceinline SDWORD MulScale32 (SDWORD a, SDWORD b) __asm mov eax,edx } +__forceinline DWORD UMulScale16(DWORD a, DWORD b) +{ + __asm mov eax,a + __asm mul b + __asm shrd eax,edx,16 +} + __forceinline SDWORD DMulScale (SDWORD a, SDWORD b, SDWORD c, SDWORD d, SDWORD s) { __asm mov eax,a @@ -341,20 +348,4 @@ __forceinline SDWORD ksgn (SDWORD a) __asm adc eax,0 } -__forceinline int toint (float v) -{ - SQWORD res; - __asm fld v; - __asm fistp res; - return (int)res; -} - -__forceinline int quickertoint (float v) -{ - SDWORD res; - __asm fld v; - __asm fistp res; - return (int)res; -} - #pragma warning (default: 4035) diff --git a/src/mus2midi.cpp b/src/mus2midi.cpp index 9dafcaabf..beef4ffa4 100644 --- a/src/mus2midi.cpp +++ b/src/mus2midi.cpp @@ -121,7 +121,8 @@ bool ProduceMIDI (const BYTE *musBuf, TArray &outFile) BYTE chanUsed[16]; BYTE lastVel[16]; long trackLen; - + bool no_op; + // Do some validation of the MUS file if (MUSMagic != musHead->Magic) return false; @@ -178,12 +179,13 @@ bool ProduceMIDI (const BYTE *musBuf, TArray &outFile) midStatus = channel; midArgs = 0; // Most events have two args (0 means 2, 1 means 1) - + no_op = false; + switch (event & 0x70) { case MUS_NOTEOFF: midStatus |= MIDI_NOTEOFF; - mid1 = t; + mid1 = t & 127; mid2 = 64; break; @@ -192,7 +194,7 @@ bool ProduceMIDI (const BYTE *musBuf, TArray &outFile) mid1 = t & 127; if (t & 128) { - lastVel[channel] = musBuf[mus_p++];; + lastVel[channel] = musBuf[mus_p++] & 127; } mid2 = lastVel[channel]; break; @@ -204,9 +206,16 @@ bool ProduceMIDI (const BYTE *musBuf, TArray &outFile) break; case MUS_SYSEVENT: - midStatus |= MIDI_CTRLCHANGE; - mid1 = CtrlTranslate[t]; - mid2 = t == 12 ? LittleShort(musHead->NumChans) : 0; + if (t < 10 || t > 14) + { + no_op = true; + } + else + { + midStatus |= MIDI_CTRLCHANGE; + mid1 = CtrlTranslate[t]; + mid2 = t == 12 /* Mono */ ? LittleShort(musHead->NumChans) : 0; + } break; case MUS_CTRLCHANGE: @@ -214,27 +223,39 @@ bool ProduceMIDI (const BYTE *musBuf, TArray &outFile) { // program change midArgs = 1; midStatus |= MIDI_PRGMCHANGE; - mid1 = musBuf[mus_p++]; + mid1 = musBuf[mus_p++] & 127; mid2 = 0; // Assign mid2 just to make GCC happy } - else + else if (t > 0 && t < 10) { midStatus |= MIDI_CTRLCHANGE; mid1 = CtrlTranslate[t]; mid2 = musBuf[mus_p++]; } + else + { + no_op = true; + } break; case MUS_SCOREEND: - midStatus = 0xff; - mid1 = 0x2f; - mid2 = 0x00; + midStatus = MIDI_META; + mid1 = MIDI_META_EOT; + mid2 = 0; break; default: return false; } + if (no_op) + { + // A system-specific event with no data is a no-op. + midStatus = MIDI_META; + mid1 = MIDI_META_SSPEC; + mid2 = 0; + } + WriteVarLen (outFile, deltaTime); if (midStatus != status) diff --git a/src/mus2midi.h b/src/mus2midi.h index 79b410078..6780bdf47 100644 --- a/src/mus2midi.h +++ b/src/mus2midi.h @@ -46,6 +46,7 @@ #define MIDI_META ((BYTE)0xFF) // Meta event begin #define MIDI_META_TEMPO ((BYTE)0x51) #define MIDI_META_EOT ((BYTE)0x2F) // End-of-track +#define MIDI_META_SSPEC ((BYTE)0x7F) // System-specific event #define MIDI_NOTEOFF ((BYTE)0x80) // + note + velocity #define MIDI_NOTEON ((BYTE)0x90) // + note + velocity diff --git a/src/namedef.h b/src/namedef.h index 8510cd95b..a95b4b5a0 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -64,6 +64,9 @@ xx(Wind) xx(PointPusher) xx(PointPuller) +xx(UpperStackLookOnly) +xx(LowerStackLookOnly) + xx(BulletPuff) xx(StrifePuff) xx(MaulerPuff) @@ -129,6 +132,9 @@ xx(FWeapQuietus) xx(CWeapWraithverge) xx(MWeapBloodscourge) +// Misc Hexen classes +xx(LightningZap) + // Ammo and weapon names for the Strife status bar xx(ClipOfBullets) xx(PoisonBolts) @@ -147,6 +153,7 @@ xx(Mauler) xx(AcolyteBlue) xx(SpectralLightningV1) +xx(SpectralLightningV2) xx(TeleportDest) xx(TeleportDest2) @@ -367,7 +374,7 @@ xx(Blockplayers) xx(Blockeverything) xx(Zoneboundary) xx(Jumpover) -xx(Blockfloating) +xx(Blockfloaters) xx(Clipmidtex) xx(Wrapmidtex) xx(Midtex3d) diff --git a/src/nodebuild.cpp b/src/nodebuild.cpp index cd68c49e3..90aaff492 100644 --- a/src/nodebuild.cpp +++ b/src/nodebuild.cpp @@ -115,7 +115,7 @@ int FNodeBuilder::CreateNode (DWORD set, fixed_t bbox[4]) SplitSegs (set, node, splitseg, set1, set2); D(PrintSet (1, set1)); - D(Printf ("(%d,%d) delta (%d,%d) from seg %d\n", node.x>>16, node.y>>16, node.dx>>16, node.dy>>16, splitseg)); + D(Printf (PRINT_LOG, "(%d,%d) delta (%d,%d) from seg %d\n", node.x>>16, node.y>>16, node.dx>>16, node.dy>>16, splitseg)); D(PrintSet (2, set2)); node.intchildren[0] = CreateNode (set1, node.bbox[0]); node.intchildren[1] = CreateNode (set2, node.bbox[1]); @@ -138,7 +138,7 @@ int FNodeBuilder::CreateSubsector (DWORD set, fixed_t bbox[4]) bbox[BOXTOP] = bbox[BOXRIGHT] = INT_MIN; bbox[BOXBOTTOM] = bbox[BOXLEFT] = INT_MAX; - D(Printf ("Subsector from set %d\n", set)); + D(Printf (PRINT_LOG, "Subsector from set %d\n", set)); assert (set != DWORD_MAX); @@ -163,7 +163,7 @@ int FNodeBuilder::CreateSubsector (DWORD set, fixed_t bbox[4]) C_SetTicker (MulScale16 (SegsStuffed, (SDWORD)Segs.Size())); } - D(Printf ("bbox (%d,%d)-(%d,%d)\n", bbox[BOXLEFT]>>16, bbox[BOXBOTTOM]>>16, bbox[BOXRIGHT]>>16, bbox[BOXTOP]>>16)); + D(Printf (PRINT_LOG, "bbox (%d,%d)-(%d,%d)\n", bbox[BOXLEFT]>>16, bbox[BOXBOTTOM]>>16, bbox[BOXRIGHT]>>16, bbox[BOXTOP]>>16)); return ssnum; } @@ -293,7 +293,7 @@ bool FNodeBuilder::CheckSubsector (DWORD set, node_t &node, DWORD &splitseg, int do { - D(Printf (" - seg %d(%d,%d)-(%d,%d) line %d front %d back %d\n", seg, + D(Printf (PRINT_LOG, " - seg %d(%d,%d)-(%d,%d) line %d front %d back %d\n", seg, Vertices[Segs[seg].v1].x>>16, Vertices[Segs[seg].v1].y>>16, Vertices[Segs[seg].v2].x>>16, Vertices[Segs[seg].v2].y>>16, Segs[seg].linedef, Segs[seg].frontsector, Segs[seg].backsector)); @@ -330,7 +330,7 @@ bool FNodeBuilder::CheckSubsector (DWORD set, node_t &node, DWORD &splitseg, int return false; } - D(Printf("Need to synthesize a splitter for set %d on seg %d\n", set, seg)); + D(Printf(PRINT_LOG, "Need to synthesize a splitter for set %d on seg %d\n", set, seg)); splitseg = DWORD_MAX; // This is a very simple and cheap "fix" for subsectors with segs @@ -364,7 +364,7 @@ bool FNodeBuilder::CheckSubsectorOverlappingSegs (DWORD set, node_t &node, DWORD { // Do not put minisegs into a new subsector. swap (seg1, seg2); } - D(Printf("Need to synthesize a splitter for set %d on seg %d (ov)\n", set, seg2)); + D(Printf(PRINT_LOG, "Need to synthesize a splitter for set %d on seg %d (ov)\n", set, seg2)); splitseg = DWORD_MAX; return ShoveSegBehind (set, node, seg2, seg1); @@ -424,6 +424,8 @@ int FNodeBuilder::SelectSplitter (DWORD set, node_t &node, DWORD &splitseg, int memset (&PlaneChecked[0], 0, PlaneChecked.Size()); + D(Printf (PRINT_LOG, "Processing set %d\n", set)); + while (seg != DWORD_MAX) { FPrivSeg *pseg = &Segs[seg]; @@ -445,7 +447,7 @@ int FNodeBuilder::SelectSplitter (DWORD set, node_t &node, DWORD &splitseg, int int value = Heuristic (node, set, nosplit); - D(Printf ("Seg %5d (%5d,%5d)-(%5d,%5d) scores %d\n", seg, node.x>>16, node.y>>16, + D(Printf (PRINT_LOG, "Seg %5d, ld %d (%5d,%5d)-(%5d,%5d) scores %d\n", seg, Segs[seg].linedef, node.x>>16, node.y>>16, (node.x+node.dx)>>16, (node.y+node.dy)>>16, value)); if (value > bestvalue) @@ -469,11 +471,11 @@ int FNodeBuilder::SelectSplitter (DWORD set, node_t &node, DWORD &splitseg, int if (bestseg == DWORD_MAX) { // No lines split any others into two sets, so this is a convex region. - D(Printf ("set %d, step %d, nosplit %d has no good splitter (%d)\n", set, step, nosplit, nosplitters)); + D(Printf (PRINT_LOG, "set %d, step %d, nosplit %d has no good splitter (%d)\n", set, step, nosplit, nosplitters)); return nosplitters ? -1 : 0; } - D(Printf ("split seg %u in set %d, score %d, step %d, nosplit %d\n", bestseg, set, bestvalue, step, nosplit)); + D(Printf (PRINT_LOG, "split seg %u in set %d, score %d, step %d, nosplit %d\n", bestseg, set, bestvalue, step, nosplit)); splitseg = bestseg; SetNodeFromSeg (node, &Segs[bestseg]); @@ -488,7 +490,8 @@ int FNodeBuilder::SelectSplitter (DWORD set, node_t &node, DWORD &splitseg, int int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit) { - int score = 0; + // Set the initial score above 0 so that near vertex anti-weighting is less likely to produce a negative score. + int score = 1000000; int segsInSet = 0; int counts[2] = { 0, 0 }; int realSegs[2] = { 0, 0 }; @@ -581,7 +584,7 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit) { if (honorNoSplit) { - D(Printf ("Splits seg %d\n", i)); + D(Printf (PRINT_LOG, "Splits seg %d\n", i)); return -1; } else @@ -594,7 +597,28 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit) frac = InterceptVector (node, *test); if (frac < 0.001 || frac > 0.999) { - score -= int(1 / frac); + FPrivVert *v1 = &Vertices[test->v1]; + FPrivVert *v2 = &Vertices[test->v2]; + double x = v1->x, y = v1->y; + x += frac * (v2->x - x); + y += frac * (v2->y - y); + if (fabs(x - v1->x) < VERTEX_EPSILON+1 && fabs(y - v1->y) < VERTEX_EPSILON+1) + { + D(Printf("Splitter will produce same start vertex as seg %d\n", i)); + return -1; + } + if (fabs(x - v2->x) < VERTEX_EPSILON+1 && fabs(y - v2->y) < VERTEX_EPSILON+1) + { + D(Printf("Splitter will produce same end vertex as seg %d\n", i)); + return -1; + } + if (frac > 0.999) + { + frac = 1 - frac; + } + int penalty = int(1 / frac); + score = MAX(score - penalty, 1); + D(Printf ("Penalized splitter by %d for being near endpt of seg %d (%f).\n", penalty, i, frac)); } counts[0]++; @@ -627,7 +651,7 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit) // determine which sector it lies inside. if (realSegs[0] == 0 || realSegs[1] == 0) { - D(Printf ("Leaves a side with only mini segs\n")); + D(Printf (PRINT_LOG, "Leaves a side with only mini segs\n")); return -1; } @@ -636,7 +660,7 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit) // is not neccesarily bad, just undesirable. if (honorNoSplit && (specialSegs[0] == realSegs[0] || specialSegs[1] == realSegs[1])) { - D(Printf ("Leaves a side with only special segs\n")); + D(Printf (PRINT_LOG, "Leaves a side with only special segs\n")); return -1; } @@ -765,6 +789,11 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou newvert.y += fixed_t(frac * double(Vertices[seg->v2].y - newvert.y)); vertnum = VertexMap->SelectVertexClose (newvert); + if (vertnum == seg->v1 || vertnum == seg->v2) + { + Printf("SelectVertexClose selected endpoint of seg %u\n", set); + } + seg2 = SplitSeg (set, vertnum, sidev1); Segs[seg2].next = outset0; @@ -908,7 +937,7 @@ DWORD FNodeBuilder::SplitSeg (DWORD segnum, int splitvert, int v1InFront) Segs.Push (newseg); - D(Printf ("Split seg %d to get seg %d\n", segnum, newnum)); + D(Printf (PRINT_LOG, "Split seg %d to get seg %d\n", segnum, newnum)); return newnum; } @@ -988,14 +1017,14 @@ double FNodeBuilder::InterceptVector (const node_t &splitter, const FPrivSeg &se void FNodeBuilder::PrintSet (int l, DWORD set) { - Printf ("set %d:\n", l); + Printf (PRINT_LOG, "set %d:\n", l); for (; set != DWORD_MAX; set = Segs[set].next) { - Printf ("\t%u(%td):%d(%d,%d)-%d(%d,%d) ", set, Segs[set].frontsector-sectors, + Printf (PRINT_LOG, "\t%u(%td):%d(%d,%d)-%d(%d,%d) ", set, Segs[set].frontsector-sectors, Segs[set].v1, Vertices[Segs[set].v1].x>>16, Vertices[Segs[set].v1].y>>16, Segs[set].v2, Vertices[Segs[set].v2].x>>16, Vertices[Segs[set].v2].y>>16); } - Printf ("*\n"); + Printf (PRINT_LOG, "*\n"); } diff --git a/src/nodebuild.h b/src/nodebuild.h index d142a289b..d0d59e706 100644 --- a/src/nodebuild.h +++ b/src/nodebuild.h @@ -243,6 +243,9 @@ private: // Units are in fixed_ts. const double SIDE_EPSILON = 6.5536; +// Vertices within this distance of each other will be considered as the same vertex. +#define VERTEX_EPSILON 6 // This is a fixed_t value + inline int FNodeBuilder::PointOnSide (int x, int y, int x1, int y1, int dx, int dy) { // For most cases, a simple dot product is enough. diff --git a/src/nodebuild_gl.cpp b/src/nodebuild_gl.cpp index e3453cd33..d990d17c3 100644 --- a/src/nodebuild_gl.cpp +++ b/src/nodebuild_gl.cpp @@ -117,7 +117,7 @@ void FNodeBuilder::FixSplitSharers (const node_t &node) while (event != NULL && next != NULL && event->Info.Vertex != v2) { - D(Printf("Forced split of seg %d(%d->%d) at %d(%d,%d)\n", seg, + D(Printf(PRINT_LOG, "Forced split of seg %d(%d->%d) at %d(%d,%d)\n", seg, Segs[seg].v1, Segs[seg].v2, event->Info.Vertex, Vertices[event->Info.Vertex].x>>16, @@ -209,7 +209,7 @@ void FNodeBuilder::AddMinisegs (const node_t &node, DWORD splitseg, DWORD &fset, ); } - D(Printf ("**Minisegs** %d/%d added %d(%d,%d)->%d(%d,%d)\n", fnseg, bnseg, + D(Printf (PRINT_LOG, "**Minisegs** %d/%d added %d(%d,%d)->%d(%d,%d)\n", fnseg, bnseg, prev->Info.Vertex, Vertices[prev->Info.Vertex].x>>16, Vertices[prev->Info.Vertex].y>>16, event->Info.Vertex, diff --git a/src/nodebuild_utility.cpp b/src/nodebuild_utility.cpp index 3e50368cf..a8a618b9f 100644 --- a/src/nodebuild_utility.cpp +++ b/src/nodebuild_utility.cpp @@ -53,10 +53,6 @@ static const int PO_LINE_START = 1; static const int PO_LINE_EXPLICIT = 5; -// Vertices within this distance of each other vertically and horizontally -// will be considered as the same vertex. -const fixed_t VERTEX_EPSILON = 6; - #if 0 #define D(x) x #else @@ -555,8 +551,12 @@ int FNodeBuilder::FVertexMap::SelectVertexClose (FNodeBuilder::FPrivVert &vert) for (i = 0; i < block.Size(); ++i) { +#if VERTEX_EPSILON <= 1 + if (vertices[block[i]].x == vert.x && vertices[block[i]].y == vert.y) +#else if (abs(vertices[block[i]].x - vert.x) < VERTEX_EPSILON && abs(vertices[block[i]].y - vert.y) < VERTEX_EPSILON) +#endif { return block[i]; } diff --git a/src/p_3dfloors.cpp b/src/p_3dfloors.cpp index d1509c649..b9b82a566 100644 --- a/src/p_3dfloors.cpp +++ b/src/p_3dfloors.cpp @@ -130,6 +130,7 @@ static void P_Add3DFloor(sector_t* sec, sector_t* sec2, line_t* master, int flag if (ffloor->top.plane->a || ffloor->top.plane->b || ffloor->bottom.plane->a || ffloor->bottom.plane->b) { ffloor->alpha = FRACUNIT; + ffloor->flags &= ~FF_ADDITIVETRANS; } sec->e->XFloor.ffloors.Push(ffloor); diff --git a/src/p_3dmidtex.cpp b/src/p_3dmidtex.cpp index 94df21d8c..39186ae9f 100644 --- a/src/p_3dmidtex.cpp +++ b/src/p_3dmidtex.cpp @@ -219,6 +219,7 @@ bool P_GetMidTexturePosition(const line_t *line, int sideno, fixed_t *ptextop, f side_t *side = line->sidedef[sideno]; FTextureID texnum = side->GetTexture(side_t::mid); + if (!texnum.isValid()) return false; FTexture * tex= TexMan(texnum); if (!tex) return false; diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 3f24ad0ed..c899c5844 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -67,7 +67,6 @@ #include "c_bind.h" #include "info.h" #include "r_translate.h" -#include "sbarinfo.h" #include "cmdlib.h" #include "m_png.h" #include "p_setup.h" @@ -1397,7 +1396,7 @@ void FBehavior::LoadScriptsDirectory () switch (Format) { case ACS_Old: - scripts.dw = (DWORD *)(Data + ((DWORD *)Data)[1]); + scripts.dw = (DWORD *)(Data + ((DWORD *)Data)[1]); // FIXME: Has this been byte-swapped before-hand? NumScripts = scripts.dw[0]; if (NumScripts != 0) { @@ -1474,6 +1473,25 @@ void FBehavior::LoadScriptsDirectory () if (NumScripts > 1) { qsort (Scripts, NumScripts, sizeof(ScriptPtr), SortScripts); + // Check for duplicates because ACC originally did not enforce + // script number uniqueness across different script types. We + // only need to do this for old format lumps, because the ACCs + // that produce new format lumps won't let you do this. + if (Format == ACS_Old) + { + for (i = 0; i < NumScripts - 1; ++i) + { + if (Scripts[i].Number == Scripts[i+1].Number) + { + Printf("Script %d appears more than once.\n", Scripts[i].Number); + // Make the closed version the first one. + if (Scripts[i+1].Type == SCRIPT_Closed) + { + swap(Scripts[i], Scripts[i+1]); + } + } + } + } } if (Format == ACS_Old) @@ -1590,6 +1608,15 @@ const ScriptPtr *FBehavior::FindScript (int script) const const ScriptPtr *ptr = BinarySearch ((ScriptPtr *)Scripts, NumScripts, &ScriptPtr::Number, (WORD)script); + // If the preceding script has the same number, return it instead. + // See the note by the script sorting above for why. + if (ptr > Scripts) + { + if (ptr[-1].Number == script) + { + ptr--; + } + } return ptr; } @@ -1799,6 +1826,7 @@ END_POINTERS TObjPtr DACSThinker::ActiveThinker; DACSThinker::DACSThinker () +: DThinker(STAT_SCRIPTS) { if (ActiveThinker) { @@ -2428,6 +2456,8 @@ enum APROP_Species = 20, APROP_NameTag = 21, APROP_Score = 22, + APROP_Notrigger = 23, + APROP_DamageFactor = 24, }; // These are needed for ACS's APROP_RenderStyle @@ -2519,6 +2549,10 @@ void DLevelScript::DoSetActorProperty (AActor *actor, int property, int value) if (value) actor->flags3 |= MF3_NOTARGET; else actor->flags3 &= ~MF3_NOTARGET; break; + case APROP_Notrigger: + if (value) actor->flags6 |= MF6_NOTRIGGER; else actor->flags6 &= ~MF6_NOTRIGGER; + break; + case APROP_JumpZ: if (actor->IsKindOf (RUNTIME_CLASS (APlayerPawn))) static_cast(actor)->JumpZ = value; @@ -2588,6 +2622,10 @@ void DLevelScript::DoSetActorProperty (AActor *actor, int property, int value) actor->Tag = FBehavior::StaticLookupString(value); break; + case APROP_DamageFactor: + actor->DamageFactor = value; + break; + default: // do nothing. break; @@ -2620,6 +2658,7 @@ int DLevelScript::GetActorProperty (int tid, int property) case APROP_Health: return actor->health; case APROP_Speed: return actor->Speed; case APROP_Damage: return actor->Damage; // Should this call GetMissileDamage() instead? + case APROP_DamageFactor:return actor->DamageFactor; case APROP_Alpha: return actor->alpha; case APROP_RenderStyle: for (int style = STYLE_None; style < STYLE_Count; ++style) { // Check for a legacy render style that matches. @@ -2638,6 +2677,7 @@ int DLevelScript::GetActorProperty (int tid, int property) case APROP_Frightened: return !!(actor->flags4 & MF4_FRIGHTENED); case APROP_Friendly: return !!(actor->flags & MF_FRIENDLY); case APROP_Notarget: return !!(actor->flags3 & MF3_NOTARGET); + case APROP_Notrigger: return !!(actor->flags6 & MF6_NOTRIGGER); case APROP_SpawnHealth: if (actor->IsKindOf (RUNTIME_CLASS (APlayerPawn))) { return static_cast(actor)->MaxHealth; @@ -2679,6 +2719,7 @@ int DLevelScript::CheckActorProperty (int tid, int property, int value) case APROP_Health: case APROP_Speed: case APROP_Damage: + case APROP_DamageFactor: case APROP_Alpha: case APROP_RenderStyle: case APROP_Gravity: @@ -2694,6 +2735,7 @@ int DLevelScript::CheckActorProperty (int tid, int property, int value) case APROP_Frightened: case APROP_Friendly: case APROP_Notarget: + case APROP_Notrigger: return (GetActorProperty(tid, property) == (!!value)); // Strings are not covered by GetActorProperty, so make the check here @@ -2892,6 +2934,13 @@ enum EACSFunctions ACSF_SetActorVelocity, ACSF_SetUserVariable, ACSF_GetUserVariable, + ACSF_Radius_Quake2, + ACSF_CheckActorClass, + ACSF_SetUserArray, + ACSF_GetUserArray, + ACSF_SoundSequenceOnActor, + ACSF_SoundSequenceOnSector, + ACSF_SoundSequenceOnPolyobj, }; int DLevelScript::SideFromID(int id, int side) @@ -2926,6 +2975,67 @@ int DLevelScript::LineFromID(int id) } } +static void SetUserVariable(AActor *self, FName varname, int index, int value) +{ + PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true); + int max; + PSymbolVariable *var; + + if (sym == NULL || sym->SymbolType != SYM_Variable || + !(var = static_cast(sym))->bUserVar) + { + return; + } + if (var->ValueType.Type == VAL_Int) + { + max = 1; + } + else if (var->ValueType.Type == VAL_Array && var->ValueType.BaseType == VAL_Int) + { + max = var->ValueType.size; + } + else + { + return; + } + // Set the value of the specified user variable. + if (index >= 0 && index < max) + { + ((int *)(reinterpret_cast(self) + var->offset))[index] = value; + } +} + +static int GetUserVariable(AActor *self, FName varname, int index) +{ + PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true); + int max; + PSymbolVariable *var; + + if (sym == NULL || sym->SymbolType != SYM_Variable || + !(var = static_cast(sym))->bUserVar) + { + return 0; + } + if (var->ValueType.Type == VAL_Int) + { + max = 1; + } + else if (var->ValueType.Type == VAL_Array && var->ValueType.BaseType == VAL_Int) + { + max = var->ValueType.size; + } + else + { + return 0; + } + // Get the value of the specified user variable. + if (index >= 0 && index < max) + { + return ((int *)(reinterpret_cast(self) + var->offset))[index]; + } + return 0; +} + int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args) { AActor *actor; @@ -3093,23 +3203,24 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args) case ACSF_SetUserVariable: { int cnt = 0; - if (args[1] >= 0 && args[1] < 10) + FName varname(FBehavior::StaticLookupString(args[1]), true); + if (varname != NAME_None) { if (args[0] == 0) { if (activator != NULL) { - activator->uservar[args[1]] = args[2]; + SetUserVariable(activator, varname, 0, args[2]); } cnt++; } else { - TActorIterator iterator (args[0]); + TActorIterator iterator(args[0]); - while ( (actor = iterator.Next ()) ) + while ( (actor = iterator.Next()) ) { - actor->uservar[args[1]] = args[2]; + SetUserVariable(actor, varname, 0, args[2]); cnt++; } } @@ -3118,13 +3229,120 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args) } case ACSF_GetUserVariable: - if (args[1] >= 0 && args[1] < 10) + { + FName varname(FBehavior::StaticLookupString(args[1]), true); + if (varname != NAME_None) { - activator = SingleActorFromTID(args[0], NULL); - return activator != NULL? activator->uservar[args[1]] : 0; + AActor *a = args[0] == 0 ? (AActor *)activator : SingleActorFromTID(args[0], NULL); + return a != NULL ? GetUserVariable(a, varname, 0) : 0; } - else return 0; + return 0; + } + + case ACSF_SetUserArray: + { + int cnt = 0; + FName varname(FBehavior::StaticLookupString(args[1]), true); + if (varname != NAME_None) + { + if (args[0] == 0) + { + if (activator != NULL) + { + SetUserVariable(activator, varname, args[2], args[3]); + } + cnt++; + } + else + { + TActorIterator iterator(args[0]); + + while ( (actor = iterator.Next()) ) + { + SetUserVariable(actor, varname, args[2], args[3]); + cnt++; + } + } + } + return cnt; + } + case ACSF_GetUserArray: + { + FName varname(FBehavior::StaticLookupString(args[1]), true); + if (varname != NAME_None) + { + AActor *a = args[0] == 0 ? (AActor *)activator : SingleActorFromTID(args[0], NULL); + return a != NULL ? GetUserVariable(a, varname, args[2]) : 0; + } + return 0; + } + + case ACSF_Radius_Quake2: + P_StartQuake(activator, args[0], args[1], args[2], args[3], args[4], FBehavior::StaticLookupString(args[5])); + break; + + case ACSF_CheckActorClass: + { + AActor *a = args[0] == 0 ? (AActor *)activator : SingleActorFromTID(args[0], NULL); + return a->GetClass()->TypeName == FName(FBehavior::StaticLookupString(args[1])); + } + + case ACSF_SoundSequenceOnActor: + { + const char *seqname = FBehavior::StaticLookupString(args[1]); + if (seqname != NULL) + { + if (args[0] == 0) + { + if (activator != NULL) + { + SN_StartSequence(activator, seqname, 0); + } + } + else + { + FActorIterator it(args[0]); + AActor *actor; + + while ( (actor = it.Next()) ) + { + SN_StartSequence(actor, seqname, 0); + } + } + } + } + break; + + case ACSF_SoundSequenceOnSector: + { + const char *seqname = FBehavior::StaticLookupString(args[1]); + int space = args[2] < CHAN_FLOOR || args[2] > CHAN_INTERIOR ? CHAN_FULLHEIGHT : args[2]; + if (seqname != NULL) + { + int secnum = -1; + + while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) + { + SN_StartSequence(§ors[secnum], args[2], seqname, 0); + } + } + } + break; + + case ACSF_SoundSequenceOnPolyobj: + { + const char *seqname = FBehavior::StaticLookupString(args[1]); + if (seqname != NULL) + { + FPolyObj *poly = PO_GetPolyobj(args[0]); + if (poly != NULL) + { + SN_StartSequence(poly, seqname, 0); + } + } + } + break; default: break; @@ -4951,7 +5169,7 @@ int DLevelScript::RunScript () lookup = FBehavior::StaticLookupString (STACK(1)); if (lookup != NULL) { - if (activationline) + if (activationline != NULL) { SN_StartSequence (activationline->frontsector, CHAN_FULLHEIGHT, lookup, 0); } diff --git a/src/p_buildmap.cpp b/src/p_buildmap.cpp index b9e9b9517..094fa8f41 100644 --- a/src/p_buildmap.cpp +++ b/src/p_buildmap.cpp @@ -624,11 +624,11 @@ static void LoadWalls (walltype *walls, int numwalls, sectortype *bsec) int sidenum = int(intptr_t(lines[linenum].sidedef[1])); if (bsec->floorstat & 64) { // floor is aligned to first wall - R_AlignFlat (linenum, (DWORD)sidenum == (DWORD)bsec->wallptr, 0); + R_AlignFlat (linenum, sidenum == bsec->wallptr, 0); } if (bsec->ceilingstat & 64) { // ceiling is aligned to first wall - R_AlignFlat (linenum, (DWORD)sidenum == (DWORD)bsec->wallptr, 0); + R_AlignFlat (linenum, sidenum == bsec->wallptr, 0); } } for(i = 0; i < numsides; i++) @@ -827,4 +827,4 @@ void ACustomSprite::BeginPlay () renderflags |= RF_XFLIP; if (args[4] & 8) renderflags |= RF_YFLIP; -} +} \ No newline at end of file diff --git a/src/p_ceiling.cpp b/src/p_ceiling.cpp index 12f957b47..4dd072a06 100644 --- a/src/p_ceiling.cpp +++ b/src/p_ceiling.cpp @@ -112,7 +112,7 @@ void DCeiling::Tick () m_Sector->SetTexture(sector_t::ceiling, m_Texture); // fall through default: - SN_StopSequence (m_Sector); + SN_StopSequence (m_Sector, CHAN_CEILING); Destroy (); break; } @@ -145,7 +145,7 @@ void DCeiling::Tick () m_Sector->SetTexture(sector_t::ceiling, m_Texture); // fall through default: - SN_StopSequence (m_Sector); + SN_StopSequence (m_Sector, CHAN_CEILING); Destroy (); break; } @@ -500,7 +500,7 @@ bool EV_CeilingCrushStop (int tag) { if (scan->m_Tag == tag && scan->m_Direction != 0) { - SN_StopSequence (scan->m_Sector); + SN_StopSequence (scan->m_Sector, CHAN_CEILING); scan->m_OldDirection = scan->m_Direction; scan->m_Direction = 0; // in-stasis; rtn = true; diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 94feffe95..8e32ee8ab 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -57,6 +57,8 @@ #include "g_level.h" #include "d_event.h" #include "doomstat.h" +#include "c_console.h" +#include "sbar.h" // The conversations as they exist inside a SCRIPTxx lump. struct Response @@ -117,16 +119,15 @@ static void LoadScriptFile(FileReader *lump, int numnodes); static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeakerType); static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeakerType); static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses); -static void DrawConversationMenu (); +static bool DrawConversationMenu (); static void PickConversationReply (); static void CleanupConversationMenu (); static void ConversationMenuEscaped (); +static void TerminalResponse (const char *str); static FStrifeDialogueNode *CurNode, *PrevNode; static FBrokenLines *DialogueLines; -static bool Conversation_TakeStuff; - #define NUM_RANDOM_LINES 10 #define NUM_RANDOM_GOODBYES 3 @@ -282,7 +283,7 @@ static void LoadScriptFile(FileReader *lump, int numnodes) { node = ReadTeaserNode (lump, prevSpeakerType); } - StrifeDialogues.Push (node); + node->ThisNodeNum = StrifeDialogues.Push(node); } } @@ -623,18 +624,23 @@ static void TakeStrifeItem (player_t *player, const PClass *itemtype, int amount if (itemtype->IsDescendantOf (PClass::FindClass(NAME_QuestItem))) return; - // Don't take keys + // Don't take keys. if (itemtype->IsDescendantOf (RUNTIME_CLASS(AKey))) return; - // Don't take the sigil + // Don't take the sigil. if (itemtype == RUNTIME_CLASS(ASigil)) return; - Net_WriteByte (DEM_CONVERSATION); - Net_WriteByte (CONV_TAKEINVENTORY); - Net_WriteString (itemtype->TypeName.GetChars ()); - Net_WriteWord (amount); + AInventory *item = player->mo->FindInventory (itemtype); + if (item != NULL) + { + item->Amount -= amount; + if (item->Amount <= 0) + { + item->Destroy (); + } + } } CUSTOM_CVAR(Float, dlg_musicvolume, 1.0f, CVAR_ARCHIVE) @@ -732,7 +738,7 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang } // Set up the menu - ::CurNode = CurNode; // only set the global variaböle for the consoleplayer + ::CurNode = CurNode; // only set the global variable for the consoleplayer ConversationMenu.PreDraw = DrawConversationMenu; ConversationMenu.EscapeHandler = ConversationMenuEscaped; @@ -743,10 +749,13 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang FString dlgtext; dlgtext.Format("TXT_%s_%02d", toSay, 1+(pr_randomspeech() % NUM_RANDOM_LINES)); - toSay = GStrings[dlgtext.GetChars()]; - if (toSay==NULL) toSay = "Go away!"; // Ok, it's lame - but it doesn't look like an error to the player. ;) + toSay = GStrings[dlgtext]; + if (toSay == NULL) + { + toSay = "Go away!"; // Ok, it's lame - but it doesn't look like an error to the player. ;) + } } - DialogueLines = V_BreakLines (SmallFont, screen->GetWidth()/CleanXfac-24*2, toSay); + DialogueLines = V_BreakLines (SmallFont, screen->GetWidth()/CleanXfac - 24*2, toSay); // Fill out the possible choices ShowGold = false; @@ -804,6 +813,7 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang OptionsActive = true; menuactive = MENU_OnNoPause; ConversationPauseTic = gametic + 20; + M_SwitchMenu (&ConversationMenu); } } @@ -838,10 +848,13 @@ void P_ResumeConversation () // //============================================================================ -static void DrawConversationMenu () +static bool DrawConversationMenu () { const char *speakerName; int i, x, y, linesize; + int width, fontheight; + menuitem_t *item; + int labelofs; player_t *cp = &players[consoleplayer]; @@ -851,7 +864,7 @@ static void DrawConversationMenu () if (CurNode == NULL) { M_ClearMenus (); - return; + return true; } // [CW] Freeze the game depending on MAPINFO options. @@ -875,7 +888,7 @@ static void DrawConversationMenu () } else { - speakerName = cp->mo->GetTag("Person"); + speakerName = cp->ConversationNPC->GetTag("Person"); } // Dim the screen behind the dialogue (but only if there is no backdrop). @@ -925,99 +938,206 @@ static void DrawConversationMenu () screen->DrawTexture (TexMan(((AInventory *)GetDefaultByType (RUNTIME_CLASS(ACoin)))->Icon), 2, 189, DTA_320x200, true, TAG_DONE); } + + y = CurrentMenu->y; + + if (gameinfo.gametype & GAME_Raven) + { + labelofs = 2; + y -= 2; + fontheight = 9; + } + else + { + labelofs = 0; + fontheight = 8; + } + for (i = 0; i < CurrentMenu->numitems; i++, y += fontheight) + { + item = CurrentMenu->items + i; + + width = SmallFont->StringWidth(item->label); + x = CurrentMenu->indent + 14; + + screen->DrawText (SmallFont, CR_GREEN, x, y, item->label, DTA_Clean, true, TAG_DONE); + + if (item->b.position != 0) + { + char tbuf[16]; + + mysnprintf (tbuf, countof(tbuf), "%d.", item->b.position); + x = CurrentMenu->indent - SmallFont->StringWidth (tbuf); + screen->DrawText (SmallFont, CR_GREY, x, y, tbuf, DTA_Clean, true, TAG_DONE); + } + + if (i == CurrentItem && + (skullAnimCounter < 6 || menuactive == MENU_WaitKey)) + { + int x = (CurrentMenu->indent + 3 - 160) * CleanXfac + screen->GetWidth() / 2; + int yy = (y-1+labelofs - 100) * CleanYfac + screen->GetHeight() / 2; + screen->DrawText (ConFont, CR_RED, x, yy, "\xd", + DTA_CellX, 8 * CleanXfac, + DTA_CellY, 8 * CleanYfac, + TAG_DONE); + } + } + return true; } + //============================================================================ // // PickConversationReply // +// Run only on the local machine with the conversation menu up. +// //============================================================================ static void PickConversationReply () { - const char *replyText = NULL; FStrifeDialogueReply *reply = (FStrifeDialogueReply *)ConversationItems[ConversationMenu.lastOn].c.extra; - int i; - player_t *cp = &players[consoleplayer]; + FStrifeDialogueReply *replyscan; + int replynum = 0; - Conversation_TakeStuff = false; + assert(CurNode->ThisNodeNum >= 0 && CurNode->ThisNodeNum < 65536); + assert(StrifeDialogues[CurNode->ThisNodeNum] == CurNode); - M_ClearMenus (); - CleanupConversationMenu (); + // Determine reply number for netcode. if (reply == NULL) { - Net_WriteByte (DEM_CONVERSATION); - Net_WriteByte (CONV_NPCANGLE); - Net_WriteByte (DEM_CONVERSATION); - Net_WriteByte (CONV_CLOSE); + replyscan = NULL; + } + else + { + for (replyscan = CurNode->Children; replyscan != NULL && replyscan != reply; ++replynum, replyscan = replyscan->Next) + { } + } + + M_ClearMenus (); + if (replyscan == NULL) + { + Net_WriteByte(DEM_CONVCLOSE); + } + else + { + // Send dialogue and reply numbers across the wire. + assert(replynum < 256); + Net_WriteByte(DEM_CONVREPLY); + Net_WriteWord(CurNode->ThisNodeNum); + Net_WriteByte(replynum); + } + CleanupConversationMenu (); +} + +//============================================================================ +// +// HandleReply +// +// Run by the netcode on all machines. +// +//============================================================================ + +static void HandleReply(player_t *player, bool isconsole, int nodenum, int replynum) +{ + const char *replyText = NULL; + FStrifeDialogueReply *reply; + FStrifeDialogueNode *node; + AActor *npc; + bool takestuff; + int i; + + if (player->ConversationNPC == NULL || (unsigned)nodenum >= StrifeDialogues.Size()) + { return; } + // Find the reply. + node = StrifeDialogues[nodenum]; + for (i = 0, reply = node->Children; reply != NULL && i != replynum; ++i, reply = reply->Next) + { } + if (reply == NULL) + { + return; + } + + npc = player->ConversationNPC; + // Check if you have the requisite items for this choice for (i = 0; i < 3; ++i) { - if (!CheckStrifeItem (cp, reply->ItemCheck[i], reply->ItemCheckAmount[i])) + if (!CheckStrifeItem(player, reply->ItemCheck[i], reply->ItemCheckAmount[i])) { // No, you don't. Say so and let the NPC animate negatively. - if (reply->QuickNo) + if (reply->QuickNo && isconsole) { - Printf ("%s\n", reply->QuickNo); + TerminalResponse(reply->QuickNo); } - Net_WriteByte (DEM_CONVERSATION); - Net_WriteByte (CONV_ANIMATE); - Net_WriteByte (2); - - Net_WriteByte (DEM_CONVERSATION); - Net_WriteByte (CONV_NPCANGLE); - Net_WriteByte (DEM_CONVERSATION); - Net_WriteByte (CONV_CLOSE); + npc->ConversationAnimation(2); + npc->angle = player->ConversationNPCAngle; + npc->flags5 &= ~MF5_INCONVERSATION; return; } } // Yay, you do! Let the NPC animate affirmatively. - Net_WriteByte (DEM_CONVERSATION); - Net_WriteByte (CONV_ANIMATE); - Net_WriteByte (1); + npc->ConversationAnimation(1); // If this reply gives you something, then try to receive it. - Conversation_TakeStuff = true; + takestuff = true; if (reply->GiveType != NULL) { if (reply->GiveType->IsDescendantOf(RUNTIME_CLASS(AInventory))) { if (reply->GiveType->IsDescendantOf(RUNTIME_CLASS(AWeapon))) { - if (cp->mo->FindInventory(reply->GiveType) != NULL) + if (player->mo->FindInventory(reply->GiveType) != NULL) { - Conversation_TakeStuff = false; + takestuff = false; } } - if (Conversation_TakeStuff) + if (takestuff) { - Net_WriteByte (DEM_CONVERSATION); - Net_WriteByte (CONV_GIVEINVENTORY); - Net_WriteString (reply->GiveType->TypeName.GetChars ()); + AInventory *item = static_cast(Spawn(reply->GiveType, 0, 0, 0, NO_REPLACE)); + // Items given here should not count as items! + if (item->flags & MF_COUNTITEM) + { + level.total_items--; + item->flags &= ~MF_COUNTITEM; + } + if (item->GetClass()->TypeName == NAME_FlameThrower) + { + // The flame thrower gives less ammo when given in a dialog + static_cast(item)->AmmoGive1 = 40; + } + item->flags |= MF_DROPPED; + if (!item->CallTryPickup(player->mo)) + { + item->Destroy(); + takestuff = false; + } } - if (reply->GiveType->IsDescendantOf (RUNTIME_CLASS (ASlideshowStarter))) + if (reply->GiveType->IsDescendantOf(RUNTIME_CLASS(ASlideshowStarter))) gameaction = ga_slideshow; } else { // Trying to give a non-inventory item. - Conversation_TakeStuff = false; - Printf("Attempting to give non-inventory item %s\n", reply->GiveType->TypeName.GetChars()); + takestuff = false; + if (isconsole) + { + Printf("Attempting to give non-inventory item %s\n", reply->GiveType->TypeName.GetChars()); + } } } // Take away required items if the give was successful or none was needed. - if (Conversation_TakeStuff) + if (takestuff) { for (i = 0; i < 3; ++i) { - TakeStrifeItem (&players[consoleplayer], reply->ItemCheck[i], reply->ItemCheckAmount[i]); + TakeStrifeItem (player, reply->ItemCheck[i], reply->ItemCheckAmount[i]); } replyText = reply->QuickYes; } @@ -1029,12 +1149,12 @@ static void PickConversationReply () // Update the quest log, if needed. if (reply->LogNumber != 0) { - cp->SetLogNumber (reply->LogNumber); + player->SetLogNumber(reply->LogNumber); } - if (replyText != NULL) + if (replyText != NULL && isconsole) { - Printf ("%s\n", replyText); + TerminalResponse(replyText); } // Does this reply alter the speaker's conversation node? If NextNode is positive, @@ -1042,39 +1162,44 @@ static void PickConversationReply () // will show the new node right away without terminating the dialogue. if (reply->NextNode != 0) { - int rootnode = FindNode (cp->ConversationNPC->GetDefault()->Conversation); + int rootnode = FindNode (npc->GetDefault()->Conversation); if (reply->NextNode < 0) { - cp->ConversationNPC->Conversation = StrifeDialogues[rootnode - reply->NextNode - 1]; + npc->Conversation = StrifeDialogues[rootnode - reply->NextNode - 1]; if (gameaction != ga_slideshow) { - P_StartConversation (cp->ConversationNPC, cp->mo, cp->ConversationFaceTalker, false); + P_StartConversation (npc, player->mo, player->ConversationFaceTalker, false); return; } else { - S_StopSound (cp->ConversationNPC, CHAN_VOICE); + S_StopSound (npc, CHAN_VOICE); } } else { - cp->ConversationNPC->Conversation = StrifeDialogues[rootnode + reply->NextNode - 1]; + npc->Conversation = StrifeDialogues[rootnode + reply->NextNode - 1]; } } - Net_WriteByte (DEM_CONVERSATION); - Net_WriteByte (CONV_NPCANGLE); + npc->angle = player->ConversationNPCAngle; // [CW] Set these to NULL because we're not using to them // anymore. However, this can interfere with slideshows // so we don't set them to NULL in that case. if (gameaction != ga_slideshow) { - Net_WriteByte (DEM_CONVERSATION); - Net_WriteByte (CONV_SETNULL); + npc->flags5 &= ~MF5_INCONVERSATION; + player->ConversationFaceTalker = false; + player->ConversationNPC = NULL; + player->ConversationPC = NULL; + player->ConversationNPCAngle = 0; } - I_SetMusicVolume (1.f); + if (isconsole) + { + I_SetMusicVolume (1.f); + } } //============================================================================ @@ -1121,12 +1246,7 @@ void CleanupConversationMenu () void ConversationMenuEscaped () { CleanupConversationMenu (); - - Net_WriteByte (DEM_CONVERSATION); - Net_WriteByte (CONV_NPCANGLE); - - Net_WriteByte (DEM_CONVERSATION); - Net_WriteByte (CONV_SETNULL); + Net_WriteByte (DEM_CONVNULL); } //============================================================================ @@ -1137,77 +1257,62 @@ void ConversationMenuEscaped () // //============================================================================ -void P_ConversationCommand (int player, BYTE **stream) +void P_ConversationCommand (int netcode, int pnum, BYTE **stream) { - int type = ReadByte (stream); + player_t *player = &players[pnum]; - switch (type) + if (netcode == DEM_CONVREPLY) { - case CONV_NPCANGLE: - players[player].ConversationNPC->angle = players[player].ConversationNPCAngle; - break; - - case CONV_ANIMATE: - players[player].ConversationNPC->ConversationAnimation (ReadByte (stream)); - break; - - case CONV_GIVEINVENTORY: + int nodenum = ReadWord(stream); + int replynum = ReadByte(stream); + HandleReply(player, pnum == consoleplayer, nodenum, replynum); + } + else + { + assert(netcode == DEM_CONVNULL || netcode == DEM_CONVCLOSE); + if (player->ConversationNPC != NULL) { - AInventory *item = static_cast (Spawn (ReadString (stream), 0, 0, 0, NO_REPLACE)); - // Items given here should not count as items! - if (item->flags & MF_COUNTITEM) - { - level.total_items--; - item->flags &= ~MF_COUNTITEM; - } - if (item->GetClass()->TypeName == NAME_FlameThrower) - { - // The flame thrower gives less ammo when given in a dialog - static_cast(item)->AmmoGive1 = 40; - } - item->flags |= MF_DROPPED; - if (!item->CallTryPickup (players[player].mo)) - { - item->Destroy (); - Conversation_TakeStuff = false; - } + player->ConversationNPC->angle = player->ConversationNPCAngle; + player->ConversationNPC->flags5 &= ~MF5_INCONVERSATION; } - break; - - case CONV_TAKEINVENTORY: + if (netcode == DEM_CONVNULL) { - AInventory *item = players[player].ConversationPC->FindInventory (PClass::FindClass (ReadString (stream))); - - if (item != NULL) - { - item->Amount -= ReadWord (stream); - if (item->Amount <= 0) - { - item->Destroy (); - } - } + player->ConversationFaceTalker = false; + player->ConversationNPC = NULL; + player->ConversationPC = NULL; + player->ConversationNPCAngle = 0; + } + } +} + +//============================================================================ +// +// TerminalResponse +// +// Similar to C_MidPrint, but lower and colored and sized to match the +// rest of the dialogue text. +// +//============================================================================ + +static void TerminalResponse (const char *str) +{ + if (str != NULL) + { + if (StatusBar != NULL) + { + AddToConsole(-1, str); + AddToConsole(-1, "\n"); + // The message is positioned a bit above the menu choices, because + // merchants can tell you something like this but continue to show + // their dialogue screen. I think most other conversations use this + // only as a response for terminating the dialogue. + StatusBar->AttachMessage(new DHUDMessageFadeOut(SmallFont, str, + float(CleanWidth/2) + 0.4f, float(ConversationMenu.y - 110 + CleanHeight/2), CleanWidth, -CleanHeight, + CR_UNTRANSLATED, 3, 1), MAKE_ID('T','A','L','K')); + } + else + { + Printf("%s\n", str); } - break; - - case CONV_SETNULL: - if (players[player].ConversationNPC != NULL) - { - players[player].ConversationNPC->flags5 &= ~MF5_INCONVERSATION; - } - players[player].ConversationFaceTalker = false; - players[player].ConversationNPC = NULL; - players[player].ConversationPC = NULL; - players[player].ConversationNPCAngle = 0; - break; - - case CONV_CLOSE: - if (players[player].ConversationNPC != NULL) - { - players[player].ConversationNPC->flags5 &= ~MF5_INCONVERSATION; - } - break; - - default: - break; } } diff --git a/src/p_conversation.h b/src/p_conversation.h index a6da8a8b6..13658e0f0 100644 --- a/src/p_conversation.h +++ b/src/p_conversation.h @@ -17,6 +17,7 @@ struct FStrifeDialogueNode ~FStrifeDialogueNode (); const PClass *DropType; const PClass *ItemCheck[3]; + int ThisNodeNum; // location of this node in StrifeDialogues int ItemCheckNode; // index into StrifeDialogues const PClass *SpeakerType; @@ -47,17 +48,6 @@ struct FStrifeDialogueReply FBrokenLines *ReplyLines; }; -// [CW] These are used to make conversations work. -enum -{ - CONV_NPCANGLE, - CONV_ANIMATE, - CONV_GIVEINVENTORY, - CONV_TAKEINVENTORY, - CONV_SETNULL, - CONV_CLOSE, -}; - extern TArray StrifeDialogues; // There were 344 types in Strife, and Strife conversations refer @@ -74,6 +64,6 @@ void P_FreeStrifeConversations (); void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveangle); void P_ResumeConversation (); -void P_ConversationCommand (int player, BYTE **stream); +void P_ConversationCommand (int netcode, int player, BYTE **stream); #endif diff --git a/src/p_doors.cpp b/src/p_doors.cpp index c77ded90d..29ec1fc6b 100644 --- a/src/p_doors.cpp +++ b/src/p_doors.cpp @@ -133,7 +133,7 @@ void DDoor::Tick () if (res == pastdest) { - SN_StopSequence (m_Sector); + SN_StopSequence (m_Sector, CHAN_CEILING); switch (m_Type) { case doorRaise: @@ -179,7 +179,7 @@ void DDoor::Tick () if (res == pastdest) { - SN_StopSequence (m_Sector); + SN_StopSequence (m_Sector, CHAN_CEILING); switch (m_Type) { case doorRaise: @@ -422,7 +422,9 @@ bool EV_DoDoor (DDoor::EVlDoor type, line_t *line, AActor *thing, // [RH] If this sector doesn't have a specific sound // attached to it, start the door close sequence. // Otherwise, just let the current one continue. - if (sec->seqType == -1) + // FIXME: This should be check if the sound sequence has separate up/down + // paths, not if it was manually set. + if (sec->seqType == -1 || SN_CheckSequence(sec, CHAN_CEILING) == NULL) { door->DoorSound (false); } diff --git a/src/p_effect.cpp b/src/p_effect.cpp index 19b623b38..0ecb53798 100644 --- a/src/p_effect.cpp +++ b/src/p_effect.cpp @@ -235,7 +235,18 @@ static void MakeFountain (AActor *actor, int color1, int color2) void P_RunEffect (AActor *actor, int effects) { - angle_t moveangle = R_PointToAngle2(0,0,actor->velx,actor->vely); + angle_t moveangle; + + // 512 is the limit below which R_PointToAngle2 does no longer returns usable values. + if (abs(actor->velx) > 512 || abs(actor->vely) > 512) + { + moveangle = R_PointToAngle2(0,0,actor->velx,actor->vely); + } + else + { + moveangle = actor->angle; + } + particle_t *particle; int i; @@ -472,17 +483,19 @@ void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end } else { + // Only consider sound in 2D (for now, anyway) // [BB] You have to devide by lengthsquared here, not multiply with it. - r = ((start.Y - FIXED2FLOAT(mo->y)) * (-dir.Y) - - (start.X - FIXED2FLOAT(mo->x)) * (dir.X)) / lengthsquared; + + r = ((start.Y - FIXED2FLOAT(mo->y)) * (-dir.Y) - (start.X - FIXED2FLOAT(mo->x)) * (dir.X)) / lengthsquared; + r = clamp(r, 0., 1.); dirz = dir.Z; dir.Z = 0; point = start + r * dir; dir.Z = dirz; - S_Sound (FLOAT2FIXED(point.X), FLOAT2FIXED(point.Y), mo->z, + S_Sound (FLOAT2FIXED(point.X), FLOAT2FIXED(point.Y), viewz, CHAN_WEAPON, sound, 1, ATTN_NORM); } } diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 5e24f1fbc..f73a17eda 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -507,6 +507,8 @@ bool P_Move (AActor *actor) } FCheckPosition tm; + tm.FromPMove = true; + try_ok = true; for(int i=1; i < steps; i++) { @@ -518,7 +520,7 @@ bool P_Move (AActor *actor) if (try_ok) try_ok = P_TryMove (actor, tryx, tryy, dropoff, false, tm); // [GrafZahl] Interpolating monster movement as it is done here just looks bad - // so make it switchable! + // so make it switchable if (nomonsterinterpolation) { actor->PrevX = actor->x; @@ -583,15 +585,19 @@ bool P_Move (AActor *actor) line_t *ld; int good = 0; - while (spechit.Pop (ld)) + if (!(actor->flags6 & MF6_NOTRIGGER)) { - // [RH] let monsters push lines, as well as use them - if (((actor->flags4 & MF4_CANUSEWALLS) && P_ActivateLine (ld, actor, 0, SPAC_Use)) || - ((actor->flags2 & MF2_PUSHWALL) && P_ActivateLine (ld, actor, 0, SPAC_Push))) + while (spechit.Pop (ld)) { - good |= ld == actor->BlockingLine ? 1 : 2; + // [RH] let monsters push lines, as well as use them + if (((actor->flags4 & MF4_CANUSEWALLS) && P_ActivateLine (ld, actor, 0, SPAC_Use)) || + ((actor->flags2 & MF2_PUSHWALL) && P_ActivateLine (ld, actor, 0, SPAC_Push))) + { + good |= ld == actor->BlockingLine ? 1 : 2; + } } } + else spechit.Clear(); return good && ((pr_opendoor() >= 203) ^ (good & 1)); } else @@ -2551,6 +2557,10 @@ static bool P_CheckForResurrection(AActor *self, bool usevilestates) S_Sound (corpsehit, CHAN_BODY, "vile/raise", 1, ATTN_IDLE); info = corpsehit->GetDefault (); + if (corpsehit->state == corpsehit->FindState(NAME_GenericCrush)) + { + corpsehit->Translation = info->Translation; // Clean up bloodcolor translation from crushed corpses + } if (ib_compatflags & BCOMPATF_VILEGHOSTS) { corpsehit->height <<= 2; @@ -2569,7 +2579,6 @@ static bool P_CheckForResurrection(AActor *self, bool usevilestates) corpsehit->RenderStyle = STYLE_Translucent; } } - corpsehit->Translation = info->Translation; // Clean up bloodcolor translation from crushed corpses } else { @@ -2580,6 +2589,8 @@ static bool P_CheckForResurrection(AActor *self, bool usevilestates) corpsehit->flags2 = info->flags2; corpsehit->flags3 = info->flags3; corpsehit->flags4 = info->flags4; + corpsehit->flags5 = info->flags5; + corpsehit->flags6 = info->flags6; corpsehit->health = info->health; corpsehit->target = NULL; corpsehit->lastenemy = NULL; @@ -2719,6 +2730,9 @@ DEFINE_ACTION_FUNCTION(AActor, A_MonsterRail) if (!self->target) return 0; + fixed_t saved_pitch = self->pitch; + AActor *linetarget; + // [RH] Andy Baker's stealth monsters if (self->flags & MF_STEALTH) { @@ -2732,7 +2746,15 @@ DEFINE_ACTION_FUNCTION(AActor, A_MonsterRail) self->target->x, self->target->y); - self->pitch = P_AimLineAttack (self, self->angle, MISSILERANGE); + self->pitch = P_AimLineAttack (self, self->angle, MISSILERANGE, &linetarget, ANGLE_1*60, false, false, false, self->target); + if (linetarget == NULL) + { + // We probably won't hit the target, but aim at it anyway so we don't look stupid. + FVector2 xydiff(self->target->x - self->x, self->target->y - self->y); + double zdiff = (self->target->z + (self->target->height>>1)) - + (self->z + (self->height>>1) - self->floorclip); + self->pitch = int(atan2(zdiff, xydiff.Length()) * ANGLE_180 / -M_PI); + } // Let the aim trail behind the player self->angle = R_PointToAngle2 (self->x, @@ -2746,6 +2768,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_MonsterRail) } P_RailAttack (self, self->GetMissileDamage (0, 1), 0); + self->pitch = saved_pitch; return 0; } @@ -3018,10 +3041,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Detonate) PARAM_ACTION_PROLOGUE; int damage = self->GetMissileDamage(0, 1); P_RadiusAttack (self, self->target, damage, damage, self->DamageType, true); - if (self->z <= self->floorz + (damage << FRACBITS)) - { - P_HitFloor (self); - } + P_CheckSplash(self, damage<seqType >= 0) + { + SN_StartSequence (sec, CHAN_FLOOR, sec->seqType, SEQ_PLATFORM, 0); + } + else + { + SN_StartSequence (sec, CHAN_FLOOR, "Floor", 0); + } +} + + +//========================================================================== // // FLOORS // +//========================================================================== IMPLEMENT_CLASS (DFloor) @@ -60,71 +82,12 @@ void DFloor::Serialize (FArchive &arc) << m_Hexencrush; } -IMPLEMENT_POINTY_CLASS (DElevator) - DECLARE_POINTER(m_Interp_Floor) - DECLARE_POINTER(m_Interp_Ceiling) -END_POINTERS - -DElevator::DElevator () -{ -} - -void DElevator::Serialize (FArchive &arc) -{ - Super::Serialize (arc); - arc << m_Type - << m_Direction - << m_FloorDestDist - << m_CeilingDestDist - << m_Speed - << m_Interp_Floor - << m_Interp_Ceiling; -} - -void DElevator::Destroy() -{ - if (m_Interp_Ceiling != NULL) - { - m_Interp_Ceiling->DelRef(); - m_Interp_Ceiling = NULL; - } - if (m_Interp_Floor != NULL) - { - m_Interp_Floor->DelRef(); - m_Interp_Floor = NULL; - } - Super::Destroy(); -} - -IMPLEMENT_POINTY_CLASS (DWaggleBase) - DECLARE_POINTER(m_Interpolation) -END_POINTERS - -IMPLEMENT_CLASS (DFloorWaggle) -IMPLEMENT_CLASS (DCeilingWaggle) - -DWaggleBase::DWaggleBase () -{ -} - -void DWaggleBase::Serialize (FArchive &arc) -{ - Super::Serialize (arc); - arc << m_OriginalDist - << m_Accumulator - << m_AccDelta - << m_TargetScale - << m_Scale - << m_ScaleDelta - << m_Ticker - << m_State - << m_Interpolation; -} - - +//========================================================================== // -// MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN) +// MOVE A FLOOR TO ITS DESTINATION (UP OR DOWN) // +//========================================================================== + void DFloor::Tick () { EResult res; @@ -163,7 +126,7 @@ void DFloor::Tick () if (res == pastdest) { - SN_StopSequence (m_Sector); + SN_StopSequence (m_Sector, CHAN_FLOOR); if (m_Type == buildStair) m_Type = waitStair; @@ -238,62 +201,11 @@ void DFloor::Tick () } } +//========================================================================== // -// T_MoveElevator() // -// Move an elevator to it's destination (up or down) -// Called once per tick for each moving floor. // -// Passed an elevator_t structure that contains all pertinent info about the -// move. See P_SPEC.H for fields. -// No return. -// -// jff 02/22/98 added to support parallel floor/ceiling motion -// -void DElevator::Tick () -{ - EResult res; - - fixed_t oldfloor, oldceiling; - - oldfloor = m_Sector->floorplane.d; - oldceiling = m_Sector->ceilingplane.d; - - if (m_Direction < 0) // moving down - { - res = MoveFloor (m_Speed, m_FloorDestDist, m_Direction); - if (res == ok || res == pastdest) - { - res = MoveCeiling (m_Speed, m_CeilingDestDist, m_Direction); - if (res == crushed) - { - MoveFloor (m_Speed, oldfloor, -m_Direction); - } - } - } - else // up - { - res = MoveCeiling (m_Speed, m_CeilingDestDist, m_Direction); - if (res == ok || res == pastdest) - { - res = MoveFloor (m_Speed, m_FloorDestDist, m_Direction); - if (res == crushed) - { - MoveCeiling (m_Speed, oldceiling, -m_Direction); - } - } - } - - if (res == pastdest) // if destination height acheived - { - // make floor stop sound - SN_StopSequence (m_Sector); - - m_Sector->floordata = NULL; //jff 2/22/98 - m_Sector->ceilingdata = NULL; //jff 2/22/98 - Destroy (); // remove elevator from actives - } -} +//========================================================================== void DFloor::SetFloorChangeType (sector_t *sec, int change) { @@ -315,37 +227,35 @@ void DFloor::SetFloorChangeType (sector_t *sec, int change) } } -static void StartFloorSound (sector_t *sec) -{ - if (sec->seqType >= 0) - { - SN_StartSequence (sec, CHAN_FLOOR, sec->seqType, SEQ_PLATFORM, 0); - } - else - { - SN_StartSequence (sec, CHAN_FLOOR, "Floor", 0); - } -} +//========================================================================== +// +// +// +//========================================================================== void DFloor::StartFloorSound () { ::StartFloorSound (m_Sector); } -void DElevator::StartFloorSound () -{ - ::StartFloorSound (m_Sector); -} +//========================================================================== +// +// +// +//========================================================================== DFloor::DFloor (sector_t *sec) : DMovingFloor (sec) { } +//========================================================================== // // HANDLE FLOOR TYPES // [RH] Added tag, speed, height, crush, change params. // +//========================================================================== + bool EV_DoFloor (DFloor::EFloor floortype, line_t *line, int tag, fixed_t speed, fixed_t height, int crush, int change, bool hexencrush) { @@ -601,10 +511,14 @@ manual_floor: return rtn; } +//========================================================================== +// // [RH] // EV_FloorCrushStop // Stop a floor from crushing! // +//========================================================================== + bool EV_FloorCrushStop (int tag) { int secnum = -1; @@ -616,7 +530,7 @@ bool EV_FloorCrushStop (int tag) if (sec->floordata && sec->floordata->IsKindOf (RUNTIME_CLASS(DFloor)) && barrier_cast(sec->floordata)->m_Type == DFloor::floorRaiseAndCrush) { - SN_StopSequence (sec); + SN_StopSequence (sec, CHAN_FLOOR); sec->floordata->Destroy (); sec->floordata = NULL; } @@ -624,61 +538,11 @@ bool EV_FloorCrushStop (int tag) return true; } +//========================================================================== // -// EV_DoChange() +// Linear tag search to emulate stair building from Doom.exe // -// Handle pure change types. These change floor texture and sector type -// by trigger or numeric model without moving the floor. -// -// The linedef causing the change and the type of change is passed -// Returns true if any sector changes -// -// jff 3/15/98 added to better support generalized sector types -// [RH] Added tag parameter. -// -bool EV_DoChange (line_t *line, EChange changetype, int tag) -{ - int secnum; - bool rtn; - sector_t *sec; - sector_t *secm; - - secnum = -1; - rtn = false; - // change all sectors with the same tag as the linedef - while ((secnum = P_FindSectorFromTag (tag, secnum)) >= 0) - { - sec = §ors[secnum]; - - rtn = true; - - // handle trigger or numeric change type - FTextureID oldpic = sec->GetTexture(sector_t::floor); - - switch(changetype) - { - case trigChangeOnly: - if (line) - { // [RH] if no line, no change - sec->SetTexture(sector_t::floor, line->frontsector->GetTexture(sector_t::floor)); - sec->special = (sec->special & SECRET_MASK) | (line->frontsector->special & ~SECRET_MASK); - } - break; - case numChangeOnly: - secm = sec->FindModelFloorSector (sec->CenterFloor()); - if (secm) - { // if no model, no change - sec->SetTexture(sector_t::floor, secm->GetTexture(sector_t::floor)); - sec->special = secm->special; - } - break; - default: - break; - } - } - return rtn; -} - +//========================================================================== static int P_FindSectorFromTagLinear (int tag, int start) { @@ -689,6 +553,7 @@ static int P_FindSectorFromTagLinear (int tag, int start) return -1; } +//========================================================================== // // BUILD A STAIRCASE! // [RH] Added stairsize, srcspeed, delay, reset, igntxt, usespecials parameters @@ -696,6 +561,8 @@ static int P_FindSectorFromTagLinear (int tag, int start) // by its special. If usespecials is 2, each sector stays in "sync" with // the others. // +//========================================================================== + bool EV_BuildStairs (int tag, DFloor::EStair type, line_t *line, fixed_t stairsize, fixed_t speed, int delay, int reset, int igntxt, int usespecials) @@ -806,6 +673,7 @@ manual_stair: sec = tsec; continue; } + } newsecnum = (int)(tsec - sectors); } @@ -829,13 +697,17 @@ manual_stair: if (!igntxt && tsec->GetTexture(sector_t::floor) != texture) continue; - height += stairstep; + // Doom bug: Height was changed before discarding the sector as part of the stairs. + // Needs to be compatibility optioned because some maps (Eternall MAP25) depend on it. + if (i_compatflags & COMPATF_STAIRINDEX) height += stairstep; // if sector's floor already moving, look for another //jff 2/26/98 special lockout condition for retriggering if (tsec->PlaneMoving(sector_t::floor) || tsec->stairlock) continue; + if (!(i_compatflags & COMPATF_STAIRINDEX)) height += stairstep; + ok = true; break; } @@ -897,8 +769,13 @@ manual_stair: return rtn; } +//========================================================================== +// // [RH] Added pillarspeed and slimespeed parameters -bool EV_DoDonut (int tag, fixed_t pillarspeed, fixed_t slimespeed) +// +//========================================================================== + +bool EV_DoDonut (int tag, line_t *line, fixed_t pillarspeed, fixed_t slimespeed) { sector_t* s1; sector_t* s2; @@ -909,13 +786,24 @@ bool EV_DoDonut (int tag, fixed_t pillarspeed, fixed_t slimespeed) DFloor* floor; vertex_t* spot; fixed_t height; + bool manual = false; secnum = -1; rtn = false; + + if (tag == 0) + { + if (!line || !(s1 = line->backsector)) + return rtn; + manual = true; + goto manual_donut; + } + while ((secnum = P_FindSectorFromTag(tag,secnum)) >= 0) { s1 = §ors[secnum]; // s1 is pillar's sector - + +manual_donut: // ALREADY MOVING? IF SO, KEEP GOING... if (s1->PlaneMoving(sector_t::floor)) continue; @@ -962,10 +850,26 @@ bool EV_DoDonut (int tag, fixed_t pillarspeed, fixed_t slimespeed) floor->StartFloorSound (); break; } + if (manual) break; } return rtn; } +//========================================================================== +// +// Elevators +// +//========================================================================== + +IMPLEMENT_POINTY_CLASS (DElevator) + DECLARE_POINTER(m_Interp_Floor) + DECLARE_POINTER(m_Interp_Ceiling) +END_POINTERS + +DElevator::DElevator () +{ +} + DElevator::DElevator (sector_t *sec) : Super (sec) { @@ -975,6 +879,111 @@ DElevator::DElevator (sector_t *sec) m_Interp_Ceiling = sec->SetInterpolation(sector_t::CeilingMove, true); } +void DElevator::Serialize (FArchive &arc) +{ + Super::Serialize (arc); + arc << m_Type + << m_Direction + << m_FloorDestDist + << m_CeilingDestDist + << m_Speed + << m_Interp_Floor + << m_Interp_Ceiling; +} + +//========================================================================== +// +// +// +//========================================================================== + +void DElevator::Destroy() +{ + if (m_Interp_Ceiling != NULL) + { + m_Interp_Ceiling->DelRef(); + m_Interp_Ceiling = NULL; + } + if (m_Interp_Floor != NULL) + { + m_Interp_Floor->DelRef(); + m_Interp_Floor = NULL; + } + Super::Destroy(); +} + +//========================================================================== +// +// T_MoveElevator() +// +// Move an elevator to it's destination (up or down) +// Called once per tick for each moving floor. +// +// Passed an elevator_t structure that contains all pertinent info about the +// move. See P_SPEC.H for fields. +// No return. +// +// jff 02/22/98 added to support parallel floor/ceiling motion +// +//========================================================================== + +void DElevator::Tick () +{ + EResult res; + + fixed_t oldfloor, oldceiling; + + oldfloor = m_Sector->floorplane.d; + oldceiling = m_Sector->ceilingplane.d; + + if (m_Direction < 0) // moving down + { + res = MoveFloor (m_Speed, m_FloorDestDist, m_Direction); + if (res == ok || res == pastdest) + { + res = MoveCeiling (m_Speed, m_CeilingDestDist, m_Direction); + if (res == crushed) + { + MoveFloor (m_Speed, oldfloor, -m_Direction); + } + } + } + else // up + { + res = MoveCeiling (m_Speed, m_CeilingDestDist, m_Direction); + if (res == ok || res == pastdest) + { + res = MoveFloor (m_Speed, m_FloorDestDist, m_Direction); + if (res == crushed) + { + MoveCeiling (m_Speed, oldceiling, -m_Direction); + } + } + } + + if (res == pastdest) // if destination height acheived + { + // make floor stop sound + SN_StopSequence (m_Sector, CHAN_FLOOR); + + m_Sector->floordata = NULL; //jff 2/22/98 + m_Sector->ceilingdata = NULL; //jff 2/22/98 + Destroy (); // remove elevator from actives + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void DElevator::StartFloorSound () +{ + ::StartFloorSound (m_Sector); +} + +//========================================================================== // // EV_DoElevator // @@ -985,6 +994,8 @@ DElevator::DElevator (sector_t *sec) // jff 2/22/98 new type to move floor and ceiling in parallel // [RH] Added speed, tag, and height parameters and new types. // +//========================================================================== + bool EV_DoElevator (line_t *line, DElevator::EElevator elevtype, fixed_t speed, fixed_t height, int tag) { @@ -995,17 +1006,28 @@ bool EV_DoElevator (line_t *line, DElevator::EElevator elevtype, fixed_t floorheight, ceilingheight; fixed_t newheight; vertex_t* spot; + bool manual = false; if (!line && (elevtype == DElevator::elevateCurrent)) return false; secnum = -1; rtn = false; + + if (tag == 0) + { + if (!line || !(sec = line->backsector)) + return rtn; + manual = true; + goto manual_elevator; + } + + // act on all sectors with the same tag as the triggering linedef while ((secnum = P_FindSectorFromTag (tag, secnum)) >= 0) { sec = §ors[secnum]; - +manual_elevator: // If either floor or ceiling is already activated, skip it if (sec->PlaneMoving(sector_t::floor) || sec->ceilingdata) //jff 2/22/98 continue; @@ -1066,11 +1088,102 @@ bool EV_DoElevator (line_t *line, DElevator::EElevator elevtype, elevator->m_CeilingDestDist = sec->ceilingplane.PointToDist (sec->soundorg[0], sec->soundorg[1], ceilingheight - height); break; } + if (manual) break; } return rtn; } +//========================================================================== +// +// EV_DoChange() +// +// Handle pure change types. These change floor texture and sector type +// by trigger or numeric model without moving the floor. +// +// The linedef causing the change and the type of change is passed +// Returns true if any sector changes +// +// jff 3/15/98 added to better support generalized sector types +// [RH] Added tag parameter. +// +//========================================================================== + +bool EV_DoChange (line_t *line, EChange changetype, int tag) +{ + int secnum; + bool rtn; + sector_t *sec; + sector_t *secm; + + secnum = -1; + rtn = false; + // change all sectors with the same tag as the linedef + while ((secnum = P_FindSectorFromTag (tag, secnum)) >= 0) + { + sec = §ors[secnum]; + + rtn = true; + + // handle trigger or numeric change type + FTextureID oldpic = sec->GetTexture(sector_t::floor); + + switch(changetype) + { + case trigChangeOnly: + if (line) + { // [RH] if no line, no change + sec->SetTexture(sector_t::floor, line->frontsector->GetTexture(sector_t::floor)); + sec->special = (sec->special & SECRET_MASK) | (line->frontsector->special & ~SECRET_MASK); + } + break; + case numChangeOnly: + secm = sec->FindModelFloorSector (sec->CenterFloor()); + if (secm) + { // if no model, no change + sec->SetTexture(sector_t::floor, secm->GetTexture(sector_t::floor)); + sec->special = secm->special; + } + break; + default: + break; + } + } + return rtn; +} + + +//========================================================================== +// +// +// +//========================================================================== + +IMPLEMENT_POINTY_CLASS (DWaggleBase) + DECLARE_POINTER(m_Interpolation) +END_POINTERS + +IMPLEMENT_CLASS (DFloorWaggle) +IMPLEMENT_CLASS (DCeilingWaggle) + +DWaggleBase::DWaggleBase () +{ +} + +void DWaggleBase::Serialize (FArchive &arc) +{ + Super::Serialize (arc); + arc << m_OriginalDist + << m_Accumulator + << m_AccDelta + << m_TargetScale + << m_Scale + << m_ScaleDelta + << m_Ticker + << m_State + << m_Interpolation; +} + //========================================================================== // // WaggleBase @@ -1096,6 +1209,12 @@ void DWaggleBase::Destroy() Super::Destroy(); } +//========================================================================== +// +// +// +//========================================================================== + void DWaggleBase::DoWaggle (bool ceiling) { secplane_t *plane; @@ -1154,12 +1273,25 @@ void DWaggleBase::DoWaggle (bool ceiling) break; } m_Accumulator += m_AccDelta; + + +#if 1 + fixed_t mag = finesine[(m_Accumulator>>9)&8191]*8; +#else + // Hexen used a 64 entry(!) sine table here which is not nearly precise enough for smooth movement + fixed_t mag = FloatBobOffsets[(m_Accumulator>>FRACBITS)&63]; +#endif + dist = plane->d; - plane->d = m_OriginalDist + plane->PointToDist (0, 0, - FixedMul (FloatBobOffsets[(m_Accumulator>>FRACBITS)&63], m_Scale)); + plane->d = m_OriginalDist + plane->PointToDist (0, 0, FixedMul (mag, m_Scale)); m_Sector->ChangePlaneTexZ(pos, plane->HeightDiff (dist)); dist = plane->HeightDiff (dist); - P_ChangeSector (m_Sector, true, dist, ceiling, false); + + // Interesting: Hexen passes 'true' for the crunch parameter which really is crushing damage here... + // Also, this does not reset the move if it blocks. + P_Scroll3dMidtex(m_Sector, 1, dist, ceiling); + P_MoveLinkedSectors(m_Sector, 1, dist, ceiling); + P_ChangeSector (m_Sector, 1, dist, ceiling, false); } //========================================================================== @@ -1176,7 +1308,7 @@ DFloorWaggle::DFloorWaggle (sector_t *sec) : Super (sec) { sec->floordata = this; - m_Interpolation = sec->SetInterpolation(sector_t::FloorMove, false); + m_Interpolation = sec->SetInterpolation(sector_t::FloorMove, true); } void DFloorWaggle::Tick () @@ -1198,7 +1330,7 @@ DCeilingWaggle::DCeilingWaggle (sector_t *sec) : Super (sec) { sec->ceilingdata = this; - m_Interpolation = sec->SetInterpolation(sector_t::CeilingMove, false); + m_Interpolation = sec->SetInterpolation(sector_t::CeilingMove, true); } void DCeilingWaggle::Tick () @@ -1212,19 +1344,31 @@ void DCeilingWaggle::Tick () // //========================================================================== -bool EV_StartWaggle (int tag, int height, int speed, int offset, +bool EV_StartWaggle (int tag, line_t *line, int height, int speed, int offset, int timer, bool ceiling) { int sectorIndex; sector_t *sector; DWaggleBase *waggle; bool retCode; + bool manual = false; retCode = false; sectorIndex = -1; + + if (tag == 0) + { + if (!line || !(sector = line->backsector)) + return retCode; + manual = true; + goto manual_waggle; + } + + while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0) { sector = §ors[sectorIndex]; +manual_waggle: if ((!ceiling && sector->PlaneMoving(sector_t::floor)) || (ceiling && sector->PlaneMoving(sector_t::ceiling))) { // Already busy with another thinker @@ -1249,6 +1393,7 @@ bool EV_StartWaggle (int tag, int height, int speed, int offset, /(TICRATE+((3*TICRATE)*height)/255); waggle->m_Ticker = timer ? timer*TICRATE : -1; waggle->m_State = WGLSTATE_EXPAND; + if (manual) break; } return retCode; } diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index b508d8f55..24d6cfd83 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -396,6 +396,8 @@ void AActor::Die (AActor *source, AActor *inflictor) // be revived by an Arch-Vile. Batman Doom needs this. flags |= MF_CORPSE; } + flags6 |= MF6_KILLED; + // [RH] Allow the death height to be overridden using metadata. fixed_t metaheight = 0; if (DamageType == NAME_Fire) @@ -420,11 +422,11 @@ void AActor::Die (AActor *source, AActor *inflictor) // the activator of the script. // New: In Hexen, the thing that died is the activator, // so now a level flag selects who the activator gets to be. - if (special && (!(flags & MF_SPECIAL) || (flags3 & MF3_ISMONSTER))) + // Everything is now moved to P_ActivateThingSpecial(). + if (special && (!(flags & MF_SPECIAL) || (flags3 & MF3_ISMONSTER)) + && !(activationtype & THINGSPEC_NoDeathSpecial)) { - LineSpecials[special] (NULL, level.flags & LEVEL_ACTOWNSPECIAL - ? this : source, false, args[0], args[1], args[2], args[3], args[4]); - special = 0; + P_ActivateThingSpecial(this, source, true); } if (CountsAsKill()) @@ -906,6 +908,7 @@ void P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage else if (target->flags & MF_ICECORPSE) // frozen { target->tics = 1; + target->flags6 |= MF6_SHATTERING; target->velx = target->vely = target->velz = 0; } return; @@ -931,7 +934,8 @@ void P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage } if (inflictor != NULL) { - if (inflictor->flags5 & MF5_PIERCEARMOR) flags |= DMG_NO_ARMOR; + if (inflictor->flags5 & MF5_PIERCEARMOR) + flags |= DMG_NO_ARMOR; } MeansOfDeath = mod; @@ -981,33 +985,41 @@ void P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage { return; } - } // Handle active damage modifiers (e.g. PowerDamage) if (source != NULL && source->Inventory != NULL) { int olddam = damage; source->Inventory->ModifyDamage(olddam, mod, damage, false); - if (olddam != damage && damage <= 0) return; + if (olddam != damage && damage <= 0) + return; } // Handle passive damage modifiers (e.g. PowerProtection) if (target->Inventory != NULL) { int olddam = damage; target->Inventory->ModifyDamage(olddam, mod, damage, true); - if (olddam != damage && damage <= 0) return; + if (olddam != damage && damage <= 0) + return; } - DmgFactors * df = target->GetClass()->ActorInfo->DamageFactors; - if (df != NULL) + if (!(flags & DMG_NO_FACTOR)) { - fixed_t * pdf = df->CheckKey(mod); - if (pdf== NULL && mod != NAME_None) pdf = df->CheckKey(NAME_None); - if (pdf != NULL) + DmgFactors *df = target->GetClass()->ActorInfo->DamageFactors; + if (df != NULL) { - damage = FixedMul(damage, *pdf); - if (damage <= 0) return; + fixed_t *pdf = df->CheckKey(mod); + if (pdf== NULL && mod != NAME_None) pdf = df->CheckKey(NAME_None); + if (pdf != NULL) + { + damage = FixedMul(damage, *pdf); + if (damage <= 0) + return; + } } + damage = FixedMul(damage, target->DamageFactor); + if (damage < 0) + return; } damage = target->TakeSpecialDamage (inflictor, source, damage, mod); @@ -1038,7 +1050,6 @@ void P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage ang = R_PointToAngle2 (origin->x, origin->y, target->x, target->y); - // Calculate this as float to avoid overflows so that the // clamping that had to be done here can be removed. double fltthrust; @@ -1051,6 +1062,9 @@ void P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage thrust = FLOAT2FIXED(fltthrust); + // Don't apply ultra-small damage thrust + if (thrust < FRACUNIT/100) thrust = 0; + // make fall forwards sometimes if ((damage < 40) && (damage > target->health) && (target->z - origin->z > 64*FRACUNIT) @@ -1088,6 +1102,7 @@ void P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage // if (player) { + //Added by MC: Lets bots look allround for enemies if they survive an ambush. if (player->isbot) { @@ -1103,8 +1118,9 @@ void P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage if (!(flags & DMG_FORCED)) { - if (damage < TELEFRAG_DAMAGE && ((target->flags2 & MF2_INVULNERABLE) || - (target->player->cheats & CF_GODMODE))) + // check the real player, not a voodoo doll here for invulnerability effects + if (damage < TELEFRAG_DAMAGE && ((player->mo->flags2 & MF2_INVULNERABLE) || + (player->cheats & CF_GODMODE))) { // player is invulnerable, so don't hurt him return; } @@ -1127,8 +1143,9 @@ void P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage damage = newdam; if (damage <= 0) { - // If MF&_FORCEPAIN is set make the player enter the pain state. - if (inflictor != NULL && (inflictor->flags6 & MF6_FORCEPAIN)) goto dopain; + // If MF6_FORCEPAIN is set, make the player enter the pain state. + if (inflictor != NULL && (inflictor->flags6 & MF6_FORCEPAIN)) + goto dopain; return; } } @@ -1260,49 +1277,55 @@ void P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage } } - pc = target->GetClass()->ActorInfo->PainChances; - painchance = target->PainChance; - if (pc != NULL) - { - BYTE * ppc = pc->CheckKey(mod); - if (ppc != NULL) - { - painchance = *ppc; - } - } -dopain: if (!(target->flags5 & MF5_NOPAIN) && (inflictor == NULL || !(inflictor->flags5 & MF5_PAINLESS)) && - !G_SkillProperty(SKILLP_NoPain) && (pr_damagemobj() < painchance || - (inflictor != NULL && (inflictor->flags6 & MF6_FORCEPAIN))) && !(target->flags & MF_SKULLFLY)) + !G_SkillProperty(SKILLP_NoPain) && !(target->flags & MF_SKULLFLY)) { - if (mod == NAME_Electric) + pc = target->GetClass()->ActorInfo->PainChances; + painchance = target->PainChance; + if (pc != NULL) { - if (pr_lightning() < 96) + BYTE * ppc = pc->CheckKey(mod); + if (ppc != NULL) { - justhit = true; - FState * painstate = target->FindState(NAME_Pain, mod); - if (painstate != NULL) target->SetState (painstate); + painchance = *ppc; } - else - { // "electrocute" the target - target->renderflags |= RF_FULLBRIGHT; - if ((target->flags3 & MF3_ISMONSTER) && pr_lightning() < 128) + } + + if ((damage >= target->PainThreshold && pr_damagemobj() < painchance) || + (inflictor != NULL && (inflictor->flags6 & MF6_FORCEPAIN))) + { +dopain: + if (mod == NAME_Electric) + { + if (pr_lightning() < 96) { - target->Howl (); + justhit = true; + FState *painstate = target->FindState(NAME_Pain, mod); + if (painstate != NULL) + target->SetState(painstate); + } + else + { // "electrocute" the target + target->renderflags |= RF_FULLBRIGHT; + if ((target->flags3 & MF3_ISMONSTER) && pr_lightning() < 128) + { + target->Howl (); + } } } - } - else - { - justhit = true; - FState * painstate = target->FindState(NAME_Pain, mod); - if (painstate != NULL) target->SetState (painstate); - if (mod == NAME_PoisonCloud) + else { - if ((target->flags3 & MF3_ISMONSTER) && pr_poison() < 128) + justhit = true; + FState *painstate = target->FindState(NAME_Pain, mod); + if (painstate != NULL) + target->SetState(painstate); + if (mod == NAME_PoisonCloud) { - target->Howl (); + if ((target->flags3 & MF3_ISMONSTER) && pr_poison() < 128) + { + target->Howl (); + } } } } @@ -1344,7 +1367,6 @@ dopain: // killough 11/98: Don't attack a friend, unless hit by that friend. if (justhit && (target->target == source || !target->target || !target->IsFriend(target->target))) target->flags |= MF_JUSTHIT; // fight back! - } bool AActor::OkayToSwitchTarget (AActor *other) diff --git a/src/p_linkedsectors.cpp b/src/p_linkedsectors.cpp index b932eb0aa..2d06bc043 100644 --- a/src/p_linkedsectors.cpp +++ b/src/p_linkedsectors.cpp @@ -354,12 +354,22 @@ void P_AddSectorLinksByID(sector_t *control, int id, INTBOOL ceiling) { int movetype = ld->args[3]; + // [GZ] Eternity does allow the attached sector to be the control sector, + // this permits elevator effects (ceiling attached to floors), so instead + // of checking whether the two sectors are the same, we prevent a plane + // from being attached to itself. This should be enough to do the trick. + if (ld->frontsector == control) + { + if (ceiling) movetype &= ~LINK_CEILING; + else movetype &= ~LINK_FLOOR; + } + // Make sure we have only valid combinations movetype &= LINK_FLAGMASK; if ((movetype & LINK_FLOORMIRROR) == LINK_FLOORMIRRORFLAG) movetype &= ~LINK_FLOORMIRRORFLAG; if ((movetype & LINK_CEILINGMIRROR) == LINK_CEILINGMIRRORFLAG) movetype &= ~LINK_CEILINGMIRRORFLAG; - if (movetype != 0 && ld->frontsector != NULL && ld->frontsector != control) + if (movetype != 0 && ld->frontsector != NULL)//&& ld->frontsector != control) Needs to be allowed! { AddSingleSector(scrollplane, ld->frontsector, movetype); } diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index d5caabeb9..947d1069b 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -341,13 +341,13 @@ FUNC(LS_Floor_LowerToLowestTxTy) FUNC(LS_Floor_Waggle) // Floor_Waggle (tag, amplitude, frequency, delay, time) { - return EV_StartWaggle (arg0, arg1, arg2, arg3, arg4, false); + return EV_StartWaggle (arg0, ln, arg1, arg2, arg3, arg4, false); } FUNC(LS_Ceiling_Waggle) // Ceiling_Waggle (tag, amplitude, frequency, delay, time) { - return EV_StartWaggle (arg0, arg1, arg2, arg3, arg4, true); + return EV_StartWaggle (arg0, ln, arg1, arg2, arg3, arg4, true); } FUNC(LS_Floor_TransferTrigger) @@ -365,7 +365,7 @@ FUNC(LS_Floor_TransferNumeric) FUNC(LS_Floor_Donut) // Floor_Donut (pillartag, pillarspeed, slimespeed) { - return EV_DoDonut (arg0, SPEED(arg1), SPEED(arg2)); + return EV_DoDonut (arg0, ln, SPEED(arg1), SPEED(arg2)); } FUNC(LS_Generic_Floor) @@ -1061,6 +1061,30 @@ FUNC(LS_HealThing) return it ? true : false; } +// So that things activated/deactivated by ACS or DECORATE *and* by +// the BUMPSPECIAL or USESPECIAL flags work correctly both ways. +void DoActivateThing(AActor * thing, AActor * activator) +{ + if (thing->activationtype & THINGSPEC_Activate) + { + thing->activationtype &= ~THINGSPEC_Activate; // Clear flag + if (thing->activationtype & THINGSPEC_Switch) // Set other flag if switching + thing->activationtype |= THINGSPEC_Deactivate; + } + thing->Activate (activator); +} + +void DoDeactivateThing(AActor * thing, AActor * activator) +{ + if (thing->activationtype & THINGSPEC_Deactivate) + { + thing->activationtype &= ~THINGSPEC_Deactivate; // Clear flag + if (thing->activationtype & THINGSPEC_Switch) // Set other flag if switching + thing->activationtype |= THINGSPEC_Activate; + } + thing->Deactivate (activator); +} + FUNC(LS_Thing_Activate) // Thing_Activate (tid) { @@ -1076,7 +1100,7 @@ FUNC(LS_Thing_Activate) // Actor might remove itself as part of activation, so get next // one before activating it. AActor *temp = iterator.Next (); - actor->Activate (it); + DoActivateThing(actor, it); actor = temp; count++; } @@ -1085,7 +1109,7 @@ FUNC(LS_Thing_Activate) } else if (it != NULL) { - it->Activate(it); + DoActivateThing(it, it); return true; } return false; @@ -1106,7 +1130,7 @@ FUNC(LS_Thing_Deactivate) // Actor might removes itself as part of deactivation, so get next // one before we activate it. AActor *temp = iterator.Next (); - actor->Deactivate (it); + DoDeactivateThing(actor, it); actor = temp; count++; } @@ -1115,7 +1139,7 @@ FUNC(LS_Thing_Deactivate) } else if (it != NULL) { - it->Deactivate(it); + DoDeactivateThing(it, it); return true; } return false; @@ -1712,7 +1736,7 @@ FUNC(LS_Light_Stop) FUNC(LS_Radius_Quake) // Radius_Quake (intensity, duration, damrad, tremrad, tid) { - return P_StartQuake (it, arg4, arg0, arg1, arg2, arg3); + return P_StartQuake (it, arg4, arg0, arg1, arg2*64, arg3*64, "world/quake"); } FUNC(LS_UsePuzzleItem) @@ -1829,7 +1853,7 @@ void AdjustPusher (int tag, int magnitude, int angle, DPusher::EPusher type) FUNC(LS_Sector_SetWind) // Sector_SetWind (tag, amount, angle) { - if (ln || arg3) + if (arg3) return false; AdjustPusher (arg0, arg1, arg2, DPusher::p_wind); @@ -1839,7 +1863,7 @@ FUNC(LS_Sector_SetWind) FUNC(LS_Sector_SetCurrent) // Sector_SetCurrent (tag, amount, angle) { - if (ln || arg3) + if (arg3) return false; AdjustPusher (arg0, arg1, arg2, DPusher::p_current); @@ -2489,6 +2513,7 @@ enum PROP_UNUSED1, PROP_UNUSED2, PROP_SPEED, + PROP_BUDDHA, }; FUNC(LS_SetPlayerProperty) @@ -2605,6 +2630,9 @@ FUNC(LS_SetPlayerProperty) // Set or clear a flag switch (arg2) { + case PROP_BUDDHA: + mask = CF_BUDDHA; + break; case PROP_FROZEN: mask = CF_FROZEN; break; @@ -2942,262 +2970,262 @@ FUNC(LS_StartConversation) lnSpecFunc LineSpecials[256] = { - LS_NOP, - LS_NOP, // Polyobj_StartLine, - LS_Polyobj_RotateLeft, - LS_Polyobj_RotateRight, - LS_Polyobj_Move, - LS_NOP, // Polyobj_ExplicitLine - LS_Polyobj_MoveTimes8, - LS_Polyobj_DoorSwing, - LS_Polyobj_DoorSlide, - LS_NOP, // Line_Horizon - LS_Door_Close, - LS_Door_Open, - LS_Door_Raise, - LS_Door_LockedRaise, - LS_Door_Animated, - LS_Autosave, - LS_NOP, // Transfer_WallLight - LS_Thing_Raise, - LS_StartConversation, - LS_Thing_Stop, - LS_Floor_LowerByValue, - LS_Floor_LowerToLowest, - LS_Floor_LowerToNearest, - LS_Floor_RaiseByValue, - LS_Floor_RaiseToHighest, - LS_Floor_RaiseToNearest, - LS_Stairs_BuildDown, - LS_Stairs_BuildUp, - LS_Floor_RaiseAndCrush, - LS_Pillar_Build, - LS_Pillar_Open, - LS_Stairs_BuildDownSync, - LS_Stairs_BuildUpSync, - LS_ForceField, - LS_ClearForceField, - LS_Floor_RaiseByValueTimes8, - LS_Floor_LowerByValueTimes8, - LS_Floor_MoveToValue, - LS_Ceiling_Waggle, - LS_Teleport_ZombieChanger, - LS_Ceiling_LowerByValue, - LS_Ceiling_RaiseByValue, - LS_Ceiling_CrushAndRaise, - LS_Ceiling_LowerAndCrush, - LS_Ceiling_CrushStop, - LS_Ceiling_CrushRaiseAndStay, - LS_Floor_CrushStop, - LS_Ceiling_MoveToValue, - LS_NOP, // Sector_Attach3dMidtex - LS_GlassBreak, - LS_NOP, // 50: ExtraFloor_LightOnly - LS_Sector_SetLink, - LS_Scroll_Wall, - LS_Line_SetTextureOffset, - LS_Sector_ChangeFlags, - LS_Line_SetBlocking, - LS_Line_SetTextureScale, - LS_NOP, // 57 - LS_NOP, // 58 - LS_NOP, // 59 - LS_Plat_PerpetualRaise, - LS_Plat_Stop, - LS_Plat_DownWaitUpStay, - LS_Plat_DownByValue, - LS_Plat_UpWaitDownStay, - LS_Plat_UpByValue, - LS_Floor_LowerInstant, - LS_Floor_RaiseInstant, - LS_Floor_MoveToValueTimes8, - LS_Ceiling_MoveToValueTimes8, - LS_Teleport, - LS_Teleport_NoFog, - LS_ThrustThing, - LS_DamageThing, - LS_Teleport_NewMap, - LS_Teleport_EndGame, - LS_TeleportOther, - LS_TeleportGroup, - LS_TeleportInSector, - LS_NOP, // 79 - LS_ACS_Execute, - LS_ACS_Suspend, - LS_ACS_Terminate, - LS_ACS_LockedExecute, - LS_ACS_ExecuteWithResult, - LS_ACS_LockedExecuteDoor, - LS_NOP, // 86 - LS_NOP, // 87 - LS_NOP, // 88 - LS_NOP, // 89 - LS_Polyobj_OR_RotateLeft, - LS_Polyobj_OR_RotateRight, - LS_Polyobj_OR_Move, - LS_Polyobj_OR_MoveTimes8, - LS_Pillar_BuildAndCrush, - LS_FloorAndCeiling_LowerByValue, - LS_FloorAndCeiling_RaiseByValue, - LS_NOP, // 97 - LS_NOP, // 98 - LS_NOP, // 99 - LS_NOP, // Scroll_Texture_Left - LS_NOP, // Scroll_Texture_Right - LS_NOP, // Scroll_Texture_Up - LS_NOP, // Scroll_Texture_Down - LS_NOP, // 104 - LS_NOP, // 105 - LS_NOP, // 106 - LS_NOP, // 107 - LS_NOP, // 108 - LS_Light_ForceLightning, - LS_Light_RaiseByValue, - LS_Light_LowerByValue, - LS_Light_ChangeToValue, - LS_Light_Fade, - LS_Light_Glow, - LS_Light_Flicker, - LS_Light_Strobe, - LS_Light_Stop, - LS_NOP, // 118 - LS_Thing_Damage, - LS_Radius_Quake, - LS_NOP, // Line_SetIdentification - LS_NOP, // Thing_SetGravity // [BC] Start - LS_NOP, // Thing_ReverseGravity - LS_NOP, // Thing_RevertGravity - LS_Thing_Move, - LS_NOP, // Thing_SetSprite - LS_Thing_SetSpecial, - LS_ThrustThingZ, // [BC] End - LS_UsePuzzleItem, - LS_Thing_Activate, - LS_Thing_Deactivate, - LS_Thing_Remove, - LS_Thing_Destroy, - LS_Thing_Projectile, - LS_Thing_Spawn, - LS_Thing_ProjectileGravity, - LS_Thing_SpawnNoFog, - LS_Floor_Waggle, - LS_Thing_SpawnFacing, - LS_Sector_ChangeSound, - LS_NOP, // 141 Music_Pause // [BC] Start - LS_NOP, // 142 Music_Change - LS_NOP, // 143 Player_RemoveItem - LS_NOP, // 144 Player_GiveItem - LS_NOP, // 145 Player_SetTeam - LS_NOP, // 146 Player_SetLeader - LS_NOP, // 147 Team_InitFP - LS_NOP, // 148 TeleportAll - LS_NOP, // 149 TeleportAll_NoFog - LS_NOP, // 150 Team_GiveFP - LS_NOP, // 151 Team_UseFP - LS_NOP, // 152 Team_Score - LS_NOP, // 153 Team_Init - LS_NOP, // 154 Var_Lock - LS_NOP, // 155 Team_RemoveItem - LS_NOP, // 156 Team_GiveItem // [BC] End - LS_NOP, // 157 - LS_NOP, // 158 - LS_NOP, // 159 - LS_NOP, // 160 - LS_NOP, // 161 - LS_NOP, // 162 - LS_NOP, // 163 - LS_NOP, // 164 - LS_NOP, // 165 - LS_NOP, // 166 - LS_NOP, // 167 - LS_NOP, // 168 - LS_Generic_Crusher2, - LS_Sector_SetCeilingScale2, - LS_Sector_SetFloorScale2, - LS_Plat_UpNearestWaitDownStay, - LS_NoiseAlert, - LS_SendToCommunicator, - LS_Thing_ProjectileIntercept, - LS_Thing_ChangeTID, - LS_Thing_Hate, - LS_Thing_ProjectileAimed, - LS_ChangeSkill, - LS_Thing_SetTranslation, - LS_NOP, // Plane_Align - LS_NOP, // Line_Mirror - LS_Line_AlignCeiling, - LS_Line_AlignFloor, - LS_Sector_SetRotation, - LS_Sector_SetCeilingPanning, - LS_Sector_SetFloorPanning, - LS_Sector_SetCeilingScale, - LS_Sector_SetFloorScale, - LS_NOP, // Static_Init - LS_SetPlayerProperty, - LS_Ceiling_LowerToHighestFloor, - LS_Ceiling_LowerInstant, - LS_Ceiling_RaiseInstant, - LS_Ceiling_CrushRaiseAndStayA, - LS_Ceiling_CrushAndRaiseA, - LS_Ceiling_CrushAndRaiseSilentA, - LS_Ceiling_RaiseByValueTimes8, - LS_Ceiling_LowerByValueTimes8, - LS_Generic_Floor, - LS_Generic_Ceiling, - LS_Generic_Door, - LS_Generic_Lift, - LS_Generic_Stairs, - LS_Generic_Crusher, - LS_Plat_DownWaitUpStayLip, - LS_Plat_PerpetualRaiseLip, - LS_TranslucentLine, - LS_NOP, // Transfer_Heights - LS_NOP, // Transfer_FloorLight - LS_NOP, // Transfer_CeilingLight - LS_Sector_SetColor, - LS_Sector_SetFade, - LS_Sector_SetDamage, - LS_Teleport_Line, - LS_Sector_SetGravity, - LS_Stairs_BuildUpDoom, - LS_Sector_SetWind, - LS_Sector_SetFriction, - LS_Sector_SetCurrent, - LS_Scroll_Texture_Both, - LS_NOP, // Scroll_Texture_Model - LS_Scroll_Floor, - LS_Scroll_Ceiling, - LS_NOP, // Scroll_Texture_Offsets - LS_ACS_ExecuteAlways, - LS_PointPush_SetForce, - LS_Plat_RaiseAndStayTx0, - LS_Thing_SetGoal, - LS_Plat_UpByValueStayTx, - LS_Plat_ToggleCeiling, - LS_Light_StrobeDoom, - LS_Light_MinNeighbor, - LS_Light_MaxNeighbor, - LS_Floor_TransferTrigger, - LS_Floor_TransferNumeric, - LS_ChangeCamera, - LS_Floor_RaiseToLowestCeiling, - LS_Floor_RaiseByValueTxTy, - LS_Floor_RaiseByTexture, - LS_Floor_LowerToLowestTxTy, - LS_Floor_LowerToHighest, - LS_Exit_Normal, - LS_Exit_Secret, - LS_Elevator_RaiseToNearest, - LS_Elevator_MoveToFloor, - LS_Elevator_LowerToNearest, - LS_HealThing, - LS_Door_CloseWaitOpen, - LS_Floor_Donut, - LS_FloorAndCeiling_LowerRaise, - LS_Ceiling_RaiseToNearest, - LS_Ceiling_LowerToLowest, - LS_Ceiling_LowerToFloor, - LS_Ceiling_CrushRaiseAndStaySilA + /* 0 */ LS_NOP, + /* 1 */ LS_NOP, // Polyobj_StartLine, + /* 2 */ LS_Polyobj_RotateLeft, + /* 3 */ LS_Polyobj_RotateRight, + /* 4 */ LS_Polyobj_Move, + /* 5 */ LS_NOP, // Polyobj_ExplicitLine + /* 6 */ LS_Polyobj_MoveTimes8, + /* 7 */ LS_Polyobj_DoorSwing, + /* 8 */ LS_Polyobj_DoorSlide, + /* 9 */ LS_NOP, // Line_Horizon + /* 10 */ LS_Door_Close, + /* 11 */ LS_Door_Open, + /* 12 */ LS_Door_Raise, + /* 13 */ LS_Door_LockedRaise, + /* 14 */ LS_Door_Animated, + /* 15 */ LS_Autosave, + /* 16 */ LS_NOP, // Transfer_WallLight + /* 17 */ LS_Thing_Raise, + /* 18 */ LS_StartConversation, + /* 19 */ LS_Thing_Stop, + /* 20 */ LS_Floor_LowerByValue, + /* 21 */ LS_Floor_LowerToLowest, + /* 22 */ LS_Floor_LowerToNearest, + /* 23 */ LS_Floor_RaiseByValue, + /* 24 */ LS_Floor_RaiseToHighest, + /* 25 */ LS_Floor_RaiseToNearest, + /* 26 */ LS_Stairs_BuildDown, + /* 27 */ LS_Stairs_BuildUp, + /* 28 */ LS_Floor_RaiseAndCrush, + /* 29 */ LS_Pillar_Build, + /* 30 */ LS_Pillar_Open, + /* 31 */ LS_Stairs_BuildDownSync, + /* 32 */ LS_Stairs_BuildUpSync, + /* 33 */ LS_ForceField, + /* 34 */ LS_ClearForceField, + /* 35 */ LS_Floor_RaiseByValueTimes8, + /* 36 */ LS_Floor_LowerByValueTimes8, + /* 37 */ LS_Floor_MoveToValue, + /* 38 */ LS_Ceiling_Waggle, + /* 39 */ LS_Teleport_ZombieChanger, + /* 40 */ LS_Ceiling_LowerByValue, + /* 41 */ LS_Ceiling_RaiseByValue, + /* 42 */ LS_Ceiling_CrushAndRaise, + /* 43 */ LS_Ceiling_LowerAndCrush, + /* 44 */ LS_Ceiling_CrushStop, + /* 45 */ LS_Ceiling_CrushRaiseAndStay, + /* 46 */ LS_Floor_CrushStop, + /* 47 */ LS_Ceiling_MoveToValue, + /* 48 */ LS_NOP, // Sector_Attach3dMidtex + /* 49 */ LS_GlassBreak, + /* 50 */ LS_NOP, // ExtraFloor_LightOnly + /* 51 */ LS_Sector_SetLink, + /* 52 */ LS_Scroll_Wall, + /* 53 */ LS_Line_SetTextureOffset, + /* 54 */ LS_Sector_ChangeFlags, + /* 55 */ LS_Line_SetBlocking, + /* 56 */ LS_Line_SetTextureScale, + /* 57 */ LS_NOP, // Sector_SetPortal + /* 58 */ LS_NOP, + /* 59 */ LS_NOP, + /* 60 */ LS_Plat_PerpetualRaise, + /* 61 */ LS_Plat_Stop, + /* 62 */ LS_Plat_DownWaitUpStay, + /* 63 */ LS_Plat_DownByValue, + /* 64 */ LS_Plat_UpWaitDownStay, + /* 65 */ LS_Plat_UpByValue, + /* 66 */ LS_Floor_LowerInstant, + /* 67 */ LS_Floor_RaiseInstant, + /* 68 */ LS_Floor_MoveToValueTimes8, + /* 69 */ LS_Ceiling_MoveToValueTimes8, + /* 70 */ LS_Teleport, + /* 71 */ LS_Teleport_NoFog, + /* 72 */ LS_ThrustThing, + /* 73 */ LS_DamageThing, + /* 74 */ LS_Teleport_NewMap, + /* 75 */ LS_Teleport_EndGame, + /* 76 */ LS_TeleportOther, + /* 77 */ LS_TeleportGroup, + /* 78 */ LS_TeleportInSector, + /* 79 */ LS_NOP, + /* 80 */ LS_ACS_Execute, + /* 81 */ LS_ACS_Suspend, + /* 82 */ LS_ACS_Terminate, + /* 83 */ LS_ACS_LockedExecute, + /* 84 */ LS_ACS_ExecuteWithResult, + /* 85 */ LS_ACS_LockedExecuteDoor, + /* 86 */ LS_NOP, + /* 87 */ LS_NOP, + /* 88 */ LS_NOP, + /* 89 */ LS_NOP, + /* 90 */ LS_Polyobj_OR_RotateLeft, + /* 91 */ LS_Polyobj_OR_RotateRight, + /* 92 */ LS_Polyobj_OR_Move, + /* 93 */ LS_Polyobj_OR_MoveTimes8, + /* 94 */ LS_Pillar_BuildAndCrush, + /* 95 */ LS_FloorAndCeiling_LowerByValue, + /* 96 */ LS_FloorAndCeiling_RaiseByValue, + /* 97 */ LS_NOP, + /* 98 */ LS_NOP, + /* 99 */ LS_NOP, + /* 100 */ LS_NOP, // Scroll_Texture_Left + /* 101 */ LS_NOP, // Scroll_Texture_Right + /* 102 */ LS_NOP, // Scroll_Texture_Up + /* 103 */ LS_NOP, // Scroll_Texture_Down + /* 104 */ LS_NOP, + /* 105 */ LS_NOP, + /* 106 */ LS_NOP, + /* 107 */ LS_NOP, + /* 108 */ LS_NOP, + /* 109 */ LS_Light_ForceLightning, + /* 110 */ LS_Light_RaiseByValue, + /* 111 */ LS_Light_LowerByValue, + /* 112 */ LS_Light_ChangeToValue, + /* 113 */ LS_Light_Fade, + /* 114 */ LS_Light_Glow, + /* 115 */ LS_Light_Flicker, + /* 116 */ LS_Light_Strobe, + /* 117 */ LS_Light_Stop, + /* 118 */ LS_NOP, // Plane_Copy + /* 119 */ LS_Thing_Damage, + /* 120 */ LS_Radius_Quake, + /* 121 */ LS_NOP, // Line_SetIdentification + /* 122 */ LS_NOP, + /* 123 */ LS_NOP, + /* 124 */ LS_NOP, + /* 125 */ LS_Thing_Move, + /* 126 */ LS_NOP, + /* 127 */ LS_Thing_SetSpecial, + /* 128 */ LS_ThrustThingZ, + /* 129 */ LS_UsePuzzleItem, + /* 130 */ LS_Thing_Activate, + /* 131 */ LS_Thing_Deactivate, + /* 132 */ LS_Thing_Remove, + /* 133 */ LS_Thing_Destroy, + /* 134 */ LS_Thing_Projectile, + /* 135 */ LS_Thing_Spawn, + /* 136 */ LS_Thing_ProjectileGravity, + /* 137 */ LS_Thing_SpawnNoFog, + /* 138 */ LS_Floor_Waggle, + /* 139 */ LS_Thing_SpawnFacing, + /* 140 */ LS_Sector_ChangeSound, + /* 141 */ LS_NOP, + /* 142 */ LS_NOP, + /* 143 */ LS_NOP, + /* 144 */ LS_NOP, + /* 145 */ LS_NOP, // 145 Player_SetTeam + /* 146 */ LS_NOP, + /* 147 */ LS_NOP, + /* 148 */ LS_NOP, + /* 149 */ LS_NOP, + /* 150 */ LS_NOP, + /* 151 */ LS_NOP, + /* 152 */ LS_NOP, // 152 Team_Score + /* 153 */ LS_NOP, // 153 Team_GivePoints + /* 154 */ LS_Teleport_NoStop, + /* 155 */ LS_NOP, + /* 156 */ LS_NOP, + /* 157 */ LS_NOP, // SetGlobalFogParameter // in GZDoom + /* 158 */ LS_NOP, // FS_Execute in GZDoom + /* 159 */ LS_NOP, // Sector_SetPlaneReflection in GZDoom + /* 160 */ LS_NOP, // Sector_Set3DFloor in GZDoom and Vavoom + /* 161 */ LS_NOP, // Sector_SetContents in GZDoom and Vavoom + /* 162 */ LS_NOP, + /* 163 */ LS_NOP, + /* 164 */ LS_NOP, + /* 165 */ LS_NOP, + /* 166 */ LS_NOP, + /* 167 */ LS_NOP, + /* 168 */ LS_NOP, + /* 169 */ LS_Generic_Crusher2, + /* 170 */ LS_Sector_SetCeilingScale2, + /* 171 */ LS_Sector_SetFloorScale2, + /* 172 */ LS_Plat_UpNearestWaitDownStay, + /* 173 */ LS_NoiseAlert, + /* 174 */ LS_SendToCommunicator, + /* 175 */ LS_Thing_ProjectileIntercept, + /* 176 */ LS_Thing_ChangeTID, + /* 177 */ LS_Thing_Hate, + /* 178 */ LS_Thing_ProjectileAimed, + /* 179 */ LS_ChangeSkill, + /* 180 */ LS_Thing_SetTranslation, + /* 181 */ LS_NOP, // Plane_Align + /* 182 */ LS_NOP, // Line_Mirror + /* 183 */ LS_Line_AlignCeiling, + /* 184 */ LS_Line_AlignFloor, + /* 185 */ LS_Sector_SetRotation, + /* 186 */ LS_Sector_SetCeilingPanning, + /* 187 */ LS_Sector_SetFloorPanning, + /* 188 */ LS_Sector_SetCeilingScale, + /* 189 */ LS_Sector_SetFloorScale, + /* 190 */ LS_NOP, // Static_Init + /* 191 */ LS_SetPlayerProperty, + /* 192 */ LS_Ceiling_LowerToHighestFloor, + /* 193 */ LS_Ceiling_LowerInstant, + /* 194 */ LS_Ceiling_RaiseInstant, + /* 195 */ LS_Ceiling_CrushRaiseAndStayA, + /* 196 */ LS_Ceiling_CrushAndRaiseA, + /* 197 */ LS_Ceiling_CrushAndRaiseSilentA, + /* 198 */ LS_Ceiling_RaiseByValueTimes8, + /* 199 */ LS_Ceiling_LowerByValueTimes8, + /* 200 */ LS_Generic_Floor, + /* 201 */ LS_Generic_Ceiling, + /* 202 */ LS_Generic_Door, + /* 203 */ LS_Generic_Lift, + /* 204 */ LS_Generic_Stairs, + /* 205 */ LS_Generic_Crusher, + /* 206 */ LS_Plat_DownWaitUpStayLip, + /* 207 */ LS_Plat_PerpetualRaiseLip, + /* 208 */ LS_TranslucentLine, + /* 209 */ LS_NOP, // Transfer_Heights + /* 210 */ LS_NOP, // Transfer_FloorLight + /* 211 */ LS_NOP, // Transfer_CeilingLight + /* 212 */ LS_Sector_SetColor, + /* 213 */ LS_Sector_SetFade, + /* 214 */ LS_Sector_SetDamage, + /* 215 */ LS_Teleport_Line, + /* 216 */ LS_Sector_SetGravity, + /* 217 */ LS_Stairs_BuildUpDoom, + /* 218 */ LS_Sector_SetWind, + /* 219 */ LS_Sector_SetFriction, + /* 220 */ LS_Sector_SetCurrent, + /* 221 */ LS_Scroll_Texture_Both, + /* 222 */ LS_NOP, // Scroll_Texture_Model + /* 223 */ LS_Scroll_Floor, + /* 224 */ LS_Scroll_Ceiling, + /* 225 */ LS_NOP, // Scroll_Texture_Offsets + /* 226 */ LS_ACS_ExecuteAlways, + /* 227 */ LS_PointPush_SetForce, + /* 228 */ LS_Plat_RaiseAndStayTx0, + /* 229 */ LS_Thing_SetGoal, + /* 230 */ LS_Plat_UpByValueStayTx, + /* 231 */ LS_Plat_ToggleCeiling, + /* 232 */ LS_Light_StrobeDoom, + /* 233 */ LS_Light_MinNeighbor, + /* 234 */ LS_Light_MaxNeighbor, + /* 235 */ LS_Floor_TransferTrigger, + /* 236 */ LS_Floor_TransferNumeric, + /* 237 */ LS_ChangeCamera, + /* 238 */ LS_Floor_RaiseToLowestCeiling, + /* 239 */ LS_Floor_RaiseByValueTxTy, + /* 240 */ LS_Floor_RaiseByTexture, + /* 241 */ LS_Floor_LowerToLowestTxTy, + /* 242 */ LS_Floor_LowerToHighest, + /* 243 */ LS_Exit_Normal, + /* 244 */ LS_Exit_Secret, + /* 245 */ LS_Elevator_RaiseToNearest, + /* 246 */ LS_Elevator_MoveToFloor, + /* 247 */ LS_Elevator_LowerToNearest, + /* 248 */ LS_HealThing, + /* 249 */ LS_Door_CloseWaitOpen, + /* 250 */ LS_Floor_Donut, + /* 251 */ LS_FloorAndCeiling_LowerRaise, + /* 252 */ LS_Ceiling_RaiseToNearest, + /* 253 */ LS_Ceiling_LowerToLowest, + /* 254 */ LS_Ceiling_LowerToFloor, + /* 255 */ LS_Ceiling_CrushRaiseAndStaySilA }; #define DEFINE_SPECIAL(name, num, min, max, mmax) {#name, num, min, max, mmax}, diff --git a/src/p_lnspec.h b/src/p_lnspec.h index 495947243..2f5a67810 100644 --- a/src/p_lnspec.h +++ b/src/p_lnspec.h @@ -196,5 +196,6 @@ typedef int (*lnSpecFunc)(struct line_t *line, extern lnSpecFunc LineSpecials[256]; int P_FindLineSpecial (const char *string, int *min_args=NULL, int *max_args=NULL); +bool P_ActivateThingSpecial(AActor * thing, AActor * trigger, bool death=false); #endif //__P_LNSPEC_H__ diff --git a/src/p_local.h b/src/p_local.h index 71a662fb7..1707db26e 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -31,7 +31,7 @@ #include -#define STEEPSLOPE 46341 // [RH] Minimum floorplane.c value for walking +#define STEEPSLOPE 46342 // [RH] Minimum floorplane.c value for walking #define BONUSADD 6 @@ -52,6 +52,7 @@ //#define GRAVITY FRACUNIT #define MAXMOVE (30*FRACUNIT) +#define TALKRANGE (128*FRACUNIT) #define USERANGE (64*FRACUNIT) #define MELEERANGE (64*FRACUNIT) #define MISSILERANGE (32*64*FRACUNIT) @@ -110,9 +111,9 @@ void P_RipperBlood (AActor *mo, AActor *bleeder); int P_GetThingFloorType (AActor *thing); void P_ExplodeMissile (AActor *missile, line_t *explodeline, AActor *target); -AActor *P_SpawnMissile (AActor* source, AActor* dest, const PClass *type); +AActor *P_SpawnMissile (AActor* source, AActor* dest, const PClass *type, AActor* owner = NULL); AActor *P_SpawnMissileZ (AActor* source, fixed_t z, AActor* dest, const PClass *type); -AActor *P_SpawnMissileXYZ (fixed_t x, fixed_t y, fixed_t z, AActor *source, AActor *dest, const PClass *type, bool checkspawn = true); +AActor *P_SpawnMissileXYZ (fixed_t x, fixed_t y, fixed_t z, AActor *source, AActor *dest, const PClass *type, bool checkspawn = true, AActor *owner = NULL); AActor *P_SpawnMissileAngle (AActor *source, const PClass *type, angle_t angle, fixed_t velz); AActor *P_SpawnMissileAngleSpeed (AActor *source, const PClass *type, angle_t angle, fixed_t velz, fixed_t speed); AActor *P_SpawnMissileAngleZ (AActor *source, fixed_t z, const PClass *type, angle_t angle, fixed_t velz); @@ -346,6 +347,7 @@ struct FCheckPosition sector_t *ceilingsector; bool touchmidtex; bool floatok; + bool FromPMove; line_t *ceilingline; AActor *stepthing; // [RH] These are used by PIT_CheckThing and P_XYMovement to apply @@ -359,6 +361,7 @@ struct FCheckPosition DoRipping = rip; LastRipped = NULL; PushTime = 0; + FromPMove = false; } }; @@ -388,13 +391,14 @@ bool P_BounceWall (AActor *mo); bool P_BounceActor (AActor *mo, AActor * BlockingMobj); bool P_CheckSight (const AActor* t1, const AActor* t2, int flags=0); void P_ResetSightCounters (bool full); +bool P_TalkFacing (AActor *player); void P_UseLines (player_t* player); bool P_UsePuzzleItem (AActor *actor, int itemType); void P_FindFloorCeiling (AActor *actor, bool onlymidtex = false); bool P_ChangeSector (sector_t* sector, int crunch, int amt, int floorOrCeil, bool isreset); -fixed_t P_AimLineAttack (AActor *t1, angle_t angle, fixed_t distance, AActor **pLineTarget = NULL, fixed_t vrange=0, bool forcenosmart=false, bool check3d = false, bool checknonshootable = false); +fixed_t P_AimLineAttack (AActor *t1, angle_t angle, fixed_t distance, AActor **pLineTarget = NULL, fixed_t vrange=0, bool forcenosmart=false, bool check3d = false, bool checknonshootable = false, AActor *target=NULL); AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, int pitch, int damage, FName damageType, const PClass *pufftype, bool ismelee = false); AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, int pitch, int damage, FName damageType, FName pufftype, bool ismelee = false); void P_TraceBleed (int damage, fixed_t x, fixed_t y, fixed_t z, AActor *target, angle_t angle, int pitch); @@ -403,7 +407,8 @@ void P_TraceBleed (int damage, AActor *target, AActor *missile); // missile ver void P_TraceBleed (int damage, AActor *target); // random direction version void P_RailAttack (AActor *source, int damage, int offset, int color1 = 0, int color2 = 0, float maxdiff = 0, bool silent = false, const PClass *puff = NULL, bool pierce = true); // [RH] Shoot a railgun bool P_HitFloor (AActor *thing); -bool P_HitWater (AActor *thing, sector_t *sec, fixed_t splashx = FIXED_MIN, fixed_t splashy = FIXED_MIN, fixed_t splashz=FIXED_MIN, bool checkabove = false); +bool P_HitWater (AActor *thing, sector_t *sec, fixed_t splashx = FIXED_MIN, fixed_t splashy = FIXED_MIN, fixed_t splashz=FIXED_MIN, bool checkabove = false, bool alert = true); +void P_CheckSplash(AActor *self, fixed_t distance); bool P_CheckMissileSpawn (AActor *missile); void P_PlaySpawnSound(AActor *missile, AActor *spawner); @@ -465,6 +470,7 @@ enum EDmgFlags DMG_INFLICTOR_IS_PUFF = 2, DMG_THRUSTLESS = 4, DMG_FORCED = 8, + DMG_NO_FACTOR = 16, }; @@ -516,6 +522,7 @@ bool PO_RotatePolyobj (int num, angle_t angle); void PO_Init (); bool PO_Busy (int polyobj); void PO_ClosestPoint(const FPolyObj *poly, fixed_t ox, fixed_t oy, fixed_t &x, fixed_t &y, seg_t **seg); +struct FPolyObj *PO_GetPolyobj(int polyNum); // // P_SPEC diff --git a/src/p_map.cpp b/src/p_map.cpp index b32c9e3ad..e3c7de323 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -161,6 +161,12 @@ static bool PIT_FindFloorCeiling (line_t *ld, const FBoundingBox &box, FCheckPos } +//========================================================================== +// +// +// +//========================================================================== + void P_GetFloorCeilingZ(FCheckPosition &tmf, bool get) { sector_t *sec; @@ -278,6 +284,7 @@ void P_FindFloorCeiling (AActor *actor, bool onlyspawnpos) } } +//========================================================================== // // TELEPORT MOVE // @@ -291,6 +298,9 @@ void P_FindFloorCeiling (AActor *actor, bool onlyspawnpos) // move was made, so the height checking I added for 1.13 could // potentially erroneously indicate the move was okay if the thing // was being teleported between two non-overlapping height ranges. +// +//========================================================================== + bool P_TeleportMove (AActor *thing, fixed_t x, fixed_t y, fixed_t z, bool telefrag) { FCheckPosition tmf; @@ -389,12 +399,15 @@ bool P_TeleportMove (AActor *thing, fixed_t x, fixed_t y, fixed_t z, bool telefr return true; } +//========================================================================== // // [RH] P_PlayerStartStomp // // Like P_TeleportMove, but it doesn't move anything, and only monsters and other // players get telefragged. // +//========================================================================== + void P_PlayerStartStomp (AActor *actor) { AActor *th; @@ -425,6 +438,12 @@ void P_PlayerStartStomp (AActor *actor) } } +//========================================================================== +// +// +// +//========================================================================== + inline fixed_t secfriction (const sector_t *sec) { fixed_t friction = Terrains[TerrainTypes[sec->GetTexture(sector_t::floor)]].Friction; @@ -437,12 +456,15 @@ inline fixed_t secmovefac (const sector_t *sec) return movefactor != 0 ? movefactor : sec->movefactor; } +//========================================================================== // // killough 8/28/98: // // P_GetFriction() // // Returns the friction associated with a particular mobj. +// +//========================================================================== int P_GetFriction (const AActor *mo, int *frictionfactor) { @@ -511,11 +533,15 @@ int P_GetFriction (const AActor *mo, int *frictionfactor) return friction; } +//========================================================================== +// // phares 3/19/98 // P_GetMoveFactor() returns the value by which the x,y // movements are multiplied to add to player movement. // // killough 8/28/98: rewritten +// +//========================================================================== int P_GetMoveFactor (const AActor *mo, int *frictionp) { @@ -550,10 +576,14 @@ int P_GetMoveFactor (const AActor *mo, int *frictionp) // MOVEMENT ITERATOR FUNCTIONS // +//========================================================================== +// // // PIT_CheckLine // Adjusts tmfloorz and tmceilingz as lines are contacted // +// +//========================================================================== static // killough 3/26/98: make static bool PIT_CheckLine (line_t *ld, const FBoundingBox &box, FCheckPosition &tm) @@ -801,13 +831,32 @@ bool PIT_CheckThing (AActor *thing, FCheckPosition &tm) } } } + + // Both things overlap in x or y direction + bool unblocking = false; + + if (tm.FromPMove) + { + fixed_t newdist = P_AproxDistance(thing->x - tm.x, thing->y - tm.y); + fixed_t olddist = P_AproxDistance(thing->x - tm.thing->x, thing->y - tm.thing->y); + + // Both actors already overlap. To prevent them from remaining stuck allow the move if it + // takes them further apart. + if (newdist > olddist) + { + // ... but not if they did not overlap in z-direction before but would after the move. + unblocking = !((tm.thing->x >= thing->x + thing->height && tm.x < thing->x + thing->height) || + (tm.thing->x + tm.thing->height <= thing->x && tm.x + tm.thing->height > thing->x)); + } + } + // [RH] If the other thing is a bridge, then treat the moving thing as if it had MF2_PASSMOBJ, so // you can use a scrolling floor to move scenery items underneath a bridge. if ((tm.thing->flags2 & MF2_PASSMOBJ || thing->flags4 & MF4_ACTLIKEBRIDGE) && !(i_compatflags & COMPATF_NO_PASSMOBJ)) { // check if a mobj passed over/under another object if (tm.thing->flags3 & thing->flags3 & MF3_DONTOVERLAP) { // Some things prefer not to overlap each other, if possible - return false; + return unblocking; } if ((tm.thing->z >= topz) || (tm.thing->z + tm.thing->height <= thing->z)) { @@ -837,24 +886,13 @@ bool PIT_CheckThing (AActor *thing, FCheckPosition &tm) // Check for MF6_BUMPSPECIAL // By default, only players can activate things by bumping into them - if ((thing->flags6 & MF6_BUMPSPECIAL)) + if ((thing->flags6 & MF6_BUMPSPECIAL) && ((tm.thing->player != NULL) + || ((thing->activationtype & THINGSPEC_MonsterTrigger) && (tm.thing->flags3 & MF3_ISMONSTER)) + || ((thing->activationtype & THINGSPEC_MissileTrigger) && (tm.thing->flags & MF_MISSILE)) + ) && (level.maptime > thing->lastbump)) // Leave the bumper enough time to go away { - if (((tm.thing->player != NULL) - || ((thing->activationtype & THINGSPEC_MonsterTrigger) && (thing->flags3 & MF3_ISMONSTER)) - || ((thing->activationtype & THINGSPEC_MissileTrigger) && (thing->flags & MF_MISSILE)) - )) - { // Target switching mechanism - if (thing->activationtype & THINGSPEC_ThingTargets) thing->target = tm.thing; - if (thing->activationtype & THINGSPEC_TriggerTargets) tm.thing->target = thing; - // Run the special - - int res = LineSpecials[thing->special] (NULL, - ((thing->activationtype & THINGSPEC_ThingActs) ? thing : tm.thing), // Who triggers? - false, thing->args[0], thing->args[1], thing->args[2], thing->args[3], thing->args[4]); - - if (thing->activationtype & THINGSPEC_ClearSpecial && res) thing->special = 0; - - } + if (P_ActivateThingSpecial(thing, tm.thing)) + thing->lastbump = level.maptime + TICRATE; } // Check for skulls slamming into things @@ -867,8 +905,9 @@ bool PIT_CheckThing (AActor *thing, FCheckPosition &tm) // Check for blasted thing running into another if ((tm.thing->flags2 & MF2_BLASTED) && (thing->flags & MF_SHOOTABLE)) { - if (!(thing->flags2 & MF2_BOSS) && (thing->flags3 & MF3_ISMONSTER)) + if (!(thing->flags2 & MF2_BOSS) && (thing->flags3 & MF3_ISMONSTER) && !(thing->flags3 & MF3_DONTBLAST)) { + // ideally this should take the mass factor into account thing->velx += tm.thing->velx; thing->vely += tm.thing->vely; if ((thing->velx + thing->vely) > 3*FRACUNIT) @@ -1071,7 +1110,6 @@ bool PIT_CheckThing (AActor *thing, FCheckPosition &tm) } // Do damage damage = tm.thing->GetMissileDamage ((tm.thing->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1); - // [GZ] If MF6_FORCEPAIN is set, we need to call P_DamageMobj even if damage is 0! if ((damage > 0) || (tm.thing->flags6 & MF6_FORCEPAIN)) { P_DamageMobj (thing, tm.thing, tm.thing->target, damage, tm.thing->DamageType); @@ -1092,7 +1130,7 @@ bool PIT_CheckThing (AActor *thing, FCheckPosition &tm) } } } - else if (damage < 0) + else { P_GiveBody (thing, -damage); } @@ -1126,7 +1164,7 @@ bool PIT_CheckThing (AActor *thing, FCheckPosition &tm) // despite another solid thing being in the way. // killough 4/11/98: Treat no-clipping things as not blocking - return !solid; + return !solid || unblocking; // return !(thing->flags & MF_SOLID); // old code -- killough } @@ -1141,40 +1179,7 @@ bool PIT_CheckThing (AActor *thing, FCheckPosition &tm) =============================================================================== */ -//---------------------------------------------------------------------------- -// -// FUNC P_TestMobjLocation -// -// Returns true if the mobj is not blocked by anything at its current -// location, otherwise returns false. -// -//---------------------------------------------------------------------------- - -bool P_TestMobjLocation (AActor *mobj) -{ - int flags; - - flags = mobj->flags; - mobj->flags &= ~MF_PICKUP; - if (P_CheckPosition(mobj, mobj->x, mobj->y)) - { // XY is ok, now check Z - mobj->flags = flags; - fixed_t z = mobj->z; - if (mobj->flags2 & MF2_FLOATBOB) - { - z -= FloatBobOffsets[(mobj->FloatBobPhase + level.maptime - 1) & 63]; - } - if ((z < mobj->floorz) || (z + mobj->height > mobj->ceilingz)) - { // Bad Z - return false; - } - return true; - } - mobj->flags = flags; - return false; -} - - +//========================================================================== // // P_CheckPosition // This is purely informative, nothing is modified @@ -1198,6 +1203,9 @@ bool P_TestMobjLocation (AActor *mobj) // numspeciallines // AActor *BlockingMobj = pointer to thing that blocked position (NULL if not // blocked, or blocked by a line). +// +//========================================================================== + bool P_CheckPosition (AActor *thing, fixed_t x, fixed_t y, FCheckPosition &tm) { sector_t *newsec; @@ -1384,6 +1392,40 @@ bool P_CheckPosition (AActor *thing, fixed_t x, fixed_t y) return P_CheckPosition(thing, x, y, tm); } +//---------------------------------------------------------------------------- +// +// FUNC P_TestMobjLocation +// +// Returns true if the mobj is not blocked by anything at its current +// location, otherwise returns false. +// +//---------------------------------------------------------------------------- + +bool P_TestMobjLocation (AActor *mobj) +{ + int flags; + + flags = mobj->flags; + mobj->flags &= ~MF_PICKUP; + if (P_CheckPosition(mobj, mobj->x, mobj->y)) + { // XY is ok, now check Z + mobj->flags = flags; + fixed_t z = mobj->z; + if (mobj->flags2 & MF2_FLOATBOB) + { + z -= FloatBobOffsets[(mobj->FloatBobPhase + level.maptime - 1) & 63]; + } + if ((z < mobj->floorz) || (z + mobj->height > mobj->ceilingz)) + { // Bad Z + return false; + } + return true; + } + mobj->flags = flags; + return false; +} + + //============================================================================= // // P_CheckOnmobj(AActor *thing) @@ -1441,10 +1483,15 @@ bool P_TestMobjZ (AActor *actor, bool quick, AActor **pOnmobj) { // Can't hit thing continue; } - if (thing->flags & (MF_SPECIAL|MF_NOCLIP|MF_CORPSE)) - { // [RH] Corpses and specials and noclippers don't block moves + if (thing->flags & (MF_SPECIAL|MF_NOCLIP)) + { // [RH] Specials and noclippers don't block moves continue; } + if (thing->flags & (MF_CORPSE)) + { // Corpses need a few more checks + if (!(actor->flags & MF_ICECORPSE)) + continue; + } if (!(thing->flags4 & MF4_ACTLIKEBRIDGE) && (actor->flags & MF_SPECIAL)) { // [RH] Only bridges block pickup items continue; @@ -1525,7 +1572,7 @@ void P_FakeZMovement (AActor *mo) static void CheckForPushSpecial (line_t *line, int side, AActor *mobj) { - if (line->special) + if (line->special && !(mobj->flags6 & MF6_NOTRIGGER)) { if (mobj->flags2 & MF2_PUSHWALL) { @@ -1547,11 +1594,14 @@ static void CheckForPushSpecial (line_t *line, int side, AActor *mobj) } } +//========================================================================== // // P_TryMove // Attempt to move to a new position, // crossing special lines unless MF_TELEPORT is set. // +//========================================================================== + bool P_TryMove (AActor *thing, fixed_t x, fixed_t y, int dropoff, // killough 3/15/98: allow dropoff as option const secplane_t *onfloor, // [RH] Let P_TryMove keep the thing on the floor @@ -1801,7 +1851,7 @@ bool P_TryMove (AActor *thing, fixed_t x, fixed_t y, // see if the line was crossed side = P_PointOnLineSide (thing->x, thing->y, ld); oldside = P_PointOnLineSide (oldx, oldy, ld); - if (side != oldside && ld->special) + if (side != oldside && ld->special && !(thing->flags6 & MF6_NOTRIGGER)) { if (thing->player) { @@ -1926,10 +1976,12 @@ bool P_TryMove (AActor *thing, fixed_t x, fixed_t y, +//========================================================================== // // P_CheckMove // Similar to P_TryMove but doesn't actually move the actor. Used for polyobject crushing // +//========================================================================== bool P_CheckMove(AActor *thing, fixed_t x, fixed_t y) { @@ -2005,10 +2057,13 @@ bool P_CheckMove(AActor *thing, fixed_t x, fixed_t y) +//========================================================================== // // SLIDE MOVE // Allows the player to slide along any angled walls. // +//========================================================================== + struct FSlide { fixed_t bestslidefrac; @@ -2031,12 +2086,15 @@ struct FSlide bool BounceWall (AActor *mo); }; +//========================================================================== // // P_HitSlideLine // Adjusts the xmove / ymove // so that the next move will slide along the wall. // If the floor is icy, then you can bounce off a wall. // phares // +//========================================================================== + void FSlide::HitSlideLine (line_t* ld) { int side; @@ -2170,9 +2228,12 @@ void FSlide::HitSlideLine (line_t* ld) } +//========================================================================== // // PTR_SlideTraverse // +//========================================================================== + void FSlide::SlideTraverse (fixed_t startx, fixed_t starty, fixed_t endx, fixed_t endy) { FLineOpening open; @@ -2261,6 +2322,7 @@ void FSlide::SlideTraverse (fixed_t startx, fixed_t starty, fixed_t endx, fixed_ +//========================================================================== // // P_SlideMove // @@ -2270,6 +2332,8 @@ void FSlide::SlideTraverse (fixed_t startx, fixed_t starty, fixed_t endx, fixed_ // // This is a kludgy mess. // +//========================================================================== + void FSlide::SlideMove (AActor *mo, fixed_t tryx, fixed_t tryy, int numsteps) { fixed_t leadx, leady; @@ -2282,7 +2346,7 @@ void FSlide::SlideMove (AActor *mo, fixed_t tryx, fixed_t tryy, int numsteps) hitcount = 3; slidemo = mo; - if (mo->player && mo->reactiontime > 0) + if (mo->player && mo->player->mo == mo && mo->reactiontime > 0) return; // player coming right out of a teleporter. retry: @@ -2697,12 +2761,18 @@ bool P_BounceWall (AActor *mo) return slide.BounceWall(mo); } +//========================================================================== +// +// +// +//========================================================================== + extern FRandom pr_bounce; bool P_BounceActor (AActor *mo, AActor * BlockingMobj) { if (mo && BlockingMobj && ((mo->BounceFlags & BOUNCE_AllActors) - || ((mo->flags & MF_MISSILE) && (BlockingMobj->flags2 & MF2_REFLECTIVE) - || ((!BlockingMobj->player) && (!(BlockingMobj->flags3 & MF3_ISMONSTER)))) + || ((mo->flags & MF_MISSILE) && (BlockingMobj->flags2 & MF2_REFLECTIVE)) + || ((BlockingMobj->player == NULL) && (!(BlockingMobj->flags3 & MF3_ISMONSTER))) )) { fixed_t speed; @@ -2749,7 +2819,7 @@ struct aim_t bool AimTraverse3DFloors(const divline_t &trace, intercept_t * in); #endif - void AimTraverse (fixed_t startx, fixed_t starty, fixed_t endx, fixed_t endy, bool checknonshootable = false); + void AimTraverse (fixed_t startx, fixed_t starty, fixed_t endx, fixed_t endy, bool checknonshootable = false, AActor *target=NULL); }; @@ -2866,7 +2936,7 @@ bool aim_t::AimTraverse3DFloors(const divline_t &trace, intercept_t * in) // //============================================================================ -void aim_t::AimTraverse (fixed_t startx, fixed_t starty, fixed_t endx, fixed_t endy, bool checknonshootable) +void aim_t::AimTraverse (fixed_t startx, fixed_t starty, fixed_t endx, fixed_t endy, bool checknonshootable, AActor *target) { FPathTraverse it(startx, starty, endx, endy, PT_ADDLINES|PT_ADDTHINGS); intercept_t *in; @@ -2921,6 +2991,9 @@ void aim_t::AimTraverse (fixed_t startx, fixed_t starty, fixed_t endx, fixed_t e if (th == shootthing) continue; // can't shoot self + if (target != NULL && th != target) + continue; // only care about target, and you're not it + if (!checknonshootable) // For info CCMD, ignore stuff about GHOST and SHOOTABLE flags { if (!(th->flags&MF_SHOOTABLE)) @@ -3032,8 +3105,8 @@ void aim_t::AimTraverse (fixed_t startx, fixed_t starty, fixed_t endx, fixed_t e if (sv_smartaim < 2) { // friends don't aim at friends (except players), at least not first - thing_friend=th; - pitch_friend=thingpitch; + thing_friend = th; + pitch_friend = thingpitch; } } else if (!(th->flags3&MF3_ISMONSTER) && th->player == NULL) @@ -3041,27 +3114,27 @@ void aim_t::AimTraverse (fixed_t startx, fixed_t starty, fixed_t endx, fixed_t e if (sv_smartaim < 3) { // don't autoaim at barrels and other shootable stuff unless no monsters have been found - thing_other=th; - pitch_other=thingpitch; + thing_other = th; + pitch_other = thingpitch; } } else { - linetarget=th; - aimpitch=thingpitch; + linetarget = th; + aimpitch = thingpitch; return; } } else { - linetarget=th; - aimpitch=thingpitch; + linetarget = th; + aimpitch = thingpitch; return; } if (checknonshootable) { - linetarget=th; - aimpitch=thingpitch; + linetarget = th; + aimpitch = thingpitch; } } } @@ -3071,7 +3144,8 @@ void aim_t::AimTraverse (fixed_t startx, fixed_t starty, fixed_t endx, fixed_t e // P_AimLineAttack // //============================================================================ -fixed_t P_AimLineAttack (AActor *t1, angle_t angle, fixed_t distance, AActor **pLineTarget, fixed_t vrange, bool forcenosmart, bool check3d, bool checknonshootable) + +fixed_t P_AimLineAttack (AActor *t1, angle_t angle, fixed_t distance, AActor **pLineTarget, fixed_t vrange, bool forcenosmart, bool check3d, bool checknonshootable, AActor *target) { fixed_t x2; fixed_t y2; @@ -3102,11 +3176,20 @@ fixed_t P_AimLineAttack (AActor *t1, angle_t angle, fixed_t distance, AActor **p } else { - // 35 degrees is approximately what Doom used. You cannot have a - // vrange of 0 degrees, because then toppitch and bottompitch will - // be equal, and PTR_AimTraverse will never find anything to shoot at - // if it crosses a line. - vrange = clamp (t1->player->userinfo.GetAimDist(), ANGLE_1/2, ANGLE_1*35); + // [BB] Disable autoaim on weapons with WIF_NOAUTOAIM. + AWeapon *weapon = t1->player->ReadyWeapon; + if ( weapon && (weapon->WeaponFlags & WIF_NOAUTOAIM) ) + { + vrange = ANGLE_1/2; + } + else + { + // 35 degrees is approximately what Doom used. You cannot have a + // vrange of 0 degrees, because then toppitch and bottompitch will + // be equal, and PTR_AimTraverse will never find anything to shoot at + // if it crosses a line. + vrange = clamp (t1->player->userinfo.aimdist, ANGLE_1/2, ANGLE_1*35); + } } } aim.toppitch = t1->pitch - vrange; @@ -3140,35 +3223,32 @@ fixed_t P_AimLineAttack (AActor *t1, angle_t angle, fixed_t distance, AActor **p } #endif - aim.AimTraverse (t1->x, t1->y, x2, y2, checknonshootable); + aim.AimTraverse (t1->x, t1->y, x2, y2, checknonshootable, target); if (!aim.linetarget) { if (aim.thing_other) { - aim.linetarget=aim.thing_other; - aim.aimpitch=aim.pitch_other; + aim.linetarget = aim.thing_other; + aim.aimpitch = aim.pitch_other; } else if (aim.thing_friend) { - aim.linetarget=aim.thing_friend; - aim.aimpitch=aim.pitch_friend; + aim.linetarget = aim.thing_friend; + aim.aimpitch = aim.pitch_friend; } } - if (pLineTarget) *pLineTarget = aim.linetarget; + if (pLineTarget) + *pLineTarget = aim.linetarget; return aim.linetarget ? aim.aimpitch : t1->pitch; } -/* -================= -= -= P_LineAttack -= -= if damage == 0, it is just a test trace that will leave linetarget set -= -================= -*/ +//========================================================================== +// +// +// +//========================================================================== static bool CheckForGhost (FTraceResults &res) { @@ -3204,6 +3284,14 @@ static bool CheckForSpectral (FTraceResults &res) return false; } +//========================================================================== +// +// P_LineAttack +// +// if damage == 0, it is just a test trace that will leave linetarget set +// +//========================================================================== + AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, int pitch, int damage, FName damageType, const PClass *pufftype, bool ismeleeattack) { @@ -3237,12 +3325,28 @@ AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, t1->player->ReadyWeapon != NULL && (t1->player->ReadyWeapon->flags2 & MF2_THRUGHOST)); + // We need to check the defaults of the replacement here + AActor *puffDefaults = GetDefaultByType(pufftype->ActorInfo->GetReplacement()->Class); + + // if the puff uses a non-standard damage type this will override default and melee damage type. + // All other explicitly passed damage types (currenty only MDK) will be preserved. + if ((damageType == NAME_None || damageType == NAME_Melee) && puffDefaults->DamageType != NAME_None) + { + damageType = puffDefaults->DamageType; + } + + int tflags; + if (puffDefaults != NULL && puffDefaults->flags6 & MF6_NOTRIGGER) tflags = TRACE_NoSky; + else tflags = TRACE_NoSky|TRACE_Impact; + if (!Trace (t1->x, t1->y, shootz, t1->Sector, vx, vy, vz, distance, MF_SHOOTABLE, ML_BLOCKEVERYTHING, t1, trace, - TRACE_NoSky|TRACE_Impact, hitGhosts ? CheckForGhost : CheckForSpectral)) + tflags, hitGhosts ? CheckForGhost : CheckForSpectral)) { // hit nothing - AActor *puffDefaults = GetDefaultByType (pufftype); - if (puffDefaults->ActiveSound) + if (puffDefaults == NULL) + { + } + else if (puffDefaults->ActiveSound) { // Play miss sound S_Sound (t1, CHAN_WEAPON, puffDefaults->ActiveSound, 1, ATTN_NORM); } @@ -3320,14 +3424,14 @@ AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, hitz = shootz + FixedMul (vz, trace.Distance); // Spawn bullet puffs or blood spots, depending on target type. - AActor *puffDefaults = GetDefaultByType (pufftype); if ((puffDefaults->flags3 & MF3_PUFFONACTORS) || (trace.Actor->flags & MF_NOBLOOD) || (trace.Actor->flags2 & (MF2_INVULNERABLE|MF2_DORMANT))) { + // We must pass the unreplaced puff type here puff = P_SpawnPuff (t1, pufftype, hitx, hity, hitz, angle - ANG180, 2, flags|PF_HITTHING); } - if (!(GetDefaultByType(pufftype)->flags3&MF3_BLOODLESSIMPACT)) + if (!(puffDefaults->flags3&MF3_BLOODLESSIMPACT)) { if (!bloodsplatter && !axeBlood && !(trace.Actor->flags & MF_NOBLOOD) && @@ -3414,6 +3518,12 @@ AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, return NULL; } +//========================================================================== +// +// +// +//========================================================================== + void P_TraceBleed (int damage, fixed_t x, fixed_t y, fixed_t z, AActor *actor, angle_t angle, int pitch) { if (!cl_bloodsplats) @@ -3503,6 +3613,12 @@ void P_TraceBleed (int damage, AActor *target, angle_t angle, int pitch) target, angle, pitch); } +//========================================================================== +// +// +// +//========================================================================== + void P_TraceBleed (int damage, AActor *target, AActor *missile) { int pitch; @@ -3528,6 +3644,12 @@ void P_TraceBleed (int damage, AActor *target, AActor *missile) pitch); } +//========================================================================== +// +// +// +//========================================================================== + void P_TraceBleed (int damage, AActor *target) { if (target != NULL) @@ -3540,9 +3662,12 @@ void P_TraceBleed (int damage, AActor *target) } } +//========================================================================== // // [RH] Rail gun stuffage // +//========================================================================== + struct SRailHit { AActor *HitActor; @@ -3572,6 +3697,12 @@ static bool ProcessRailHit (FTraceResults &res) return true; } +//========================================================================== +// +// +// +//========================================================================== + static bool ProcessNoPierceRailHit (FTraceResults &res) { if (res.HitType != TRACE_HitActor) @@ -3594,6 +3725,12 @@ static bool ProcessNoPierceRailHit (FTraceResults &res) return false; } +//========================================================================== +// +// +// +//========================================================================== + void P_RailAttack (AActor *source, int damage, int offset, int color1, int color2, float maxdiff, bool silent, const PClass *puffclass, bool pierce) { fixed_t vx, vy, vz; @@ -3635,22 +3772,29 @@ void P_RailAttack (AActor *source, int damage, int offset, int color1, int color start.Y = FIXED2FLOAT(y1); start.Z = FIXED2FLOAT(shootz); + int flags; + + AActor *puffDefaults = puffclass == NULL? + NULL : GetDefaultByType (puffclass->ActorInfo->GetReplacement()->Class); + + if (puffDefaults != NULL && puffDefaults->flags6 & MF6_NOTRIGGER) flags = 0; + else flags = TRACE_PCross|TRACE_Impact; + if (pierce) { Trace (x1, y1, shootz, source->Sector, vx, vy, vz, 8192*FRACUNIT, MF_SHOOTABLE, ML_BLOCKEVERYTHING, source, trace, - TRACE_PCross|TRACE_Impact, ProcessRailHit); + flags, ProcessRailHit); } else { Trace (x1, y1, shootz, source->Sector, vx, vy, vz, 8192*FRACUNIT, MF_SHOOTABLE, ML_BLOCKEVERYTHING, source, trace, - TRACE_PCross|TRACE_Impact, ProcessNoPierceRailHit); + flags, ProcessNoPierceRailHit); } // Hurt anything the trace hit unsigned int i; - AActor *puffDefaults = puffclass == NULL? NULL : GetDefaultByType (puffclass); FName damagetype = (puffDefaults == NULL || puffDefaults->DamageType == NAME_None) ? FName(NAME_Railgun) : puffDefaults->DamageType; // used as damage inflictor @@ -3661,6 +3805,7 @@ void P_RailAttack (AActor *source, int damage, int offset, int color1, int color for (i = 0; i < RailHits.Size (); i++) { fixed_t x, y, z; + bool spawnpuff; x = x1 + FixedMul (RailHits[i].Distance, vx); y = y1 + FixedMul (RailHits[i].Distance, vy); @@ -3669,13 +3814,15 @@ void P_RailAttack (AActor *source, int damage, int offset, int color1, int color if ((RailHits[i].HitActor->flags & MF_NOBLOOD) || (RailHits[i].HitActor->flags2 & (MF2_DORMANT|MF2_INVULNERABLE))) { - if (puffclass != NULL) P_SpawnPuff (source, puffclass, x, y, z, source->angle - ANG90, 1, PF_HITTHING); + spawnpuff = puffclass != NULL; } else { + spawnpuff = (puffclass != NULL && puffDefaults->flags3 & MF3_ALWAYSPUFF); P_SpawnBlood (x, y, z, source->angle - ANG180, damage, RailHits[i].HitActor); P_TraceBleed (damage, x, y, z, RailHits[i].HitActor, source->angle, pitch); } + if (spawnpuff) P_SpawnPuff (source, puffclass, x, y, z, source->angle - ANG90, 1, PF_HITTHING); P_DamageMobj (RailHits[i].HitActor, thepuff? thepuff:source, source, damage, damagetype, DMG_INFLICTOR_IS_PUFF); } @@ -3683,6 +3830,11 @@ void P_RailAttack (AActor *source, int damage, int offset, int color1, int color if (trace.HitType == TRACE_HitWall) { SpawnShootDecal (source, trace); + if (puffclass != NULL && puffDefaults->flags3 & MF3_ALWAYSPUFF) + { + P_SpawnPuff (source, puffclass, trace.X, trace.Y, trace.Z, source->angle - ANG90, 1, 0); + } + } if (trace.HitType == TRACE_HitFloor && trace.CrossedWater == NULL && @@ -3710,9 +3862,12 @@ void P_RailAttack (AActor *source, int damage, int offset, int color1, int color P_DrawRailTrail (source, start, end, color1, color2, maxdiff, silent); } +//========================================================================== // // [RH] P_AimCamera // +//========================================================================== + CVAR (Float, chase_height, -8.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Float, chase_dist, 90.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) @@ -3750,9 +3905,57 @@ void P_AimCamera (AActor *t1, fixed_t &CameraX, fixed_t &CameraY, fixed_t &Camer } +//========================================================================== +// +// P_TalkFacing +// +// Looks for something within 5.625 degrees left or right of the player +// to talk to. +// +//========================================================================== + +bool P_TalkFacing(AActor *player) +{ + AActor *linetarget; + + P_AimLineAttack(player, player->angle, TALKRANGE, &linetarget, ANGLE_1*35, true); + if (linetarget == NULL) + { + P_AimLineAttack(player, player->angle + (ANGLE_90 >> 4), TALKRANGE, &linetarget, ANGLE_1*35, true); + if (linetarget == NULL) + { + P_AimLineAttack(player, player->angle - (ANGLE_90 >> 4), TALKRANGE, &linetarget, ANGLE_1*35, true); + if (linetarget == NULL) + { + return false; + } + } + } + // Dead things can't talk. + if (linetarget->health <= 0) + { + return false; + } + // Fighting things don't talk either. + if (linetarget->flags4 & MF4_INCOMBAT) + { + return false; + } + if (linetarget->Conversation != NULL) + { + // Give the NPC a chance to play a brief animation + linetarget->ConversationAnimation (0); + P_StartConversation (linetarget, player, true, true); + return true; + } + return false; +} + +//========================================================================== // // USE LINES // +//========================================================================== bool P_UseTraverse(AActor *usething, fixed_t endx, fixed_t endy, bool &foundline) { @@ -3764,57 +3967,21 @@ bool P_UseTraverse(AActor *usething, fixed_t endx, fixed_t endy, bool &foundline // [RH] Check for things to talk with or use a puzzle item on if (!in->isaline) { - if (usething==in->d.thing) continue; + if (usething == in->d.thing) + continue; // Check thing // Check for puzzle item use or USESPECIAL flag // Extended to use the same activationtype mechanism as BUMPSPECIAL does if (in->d.thing->flags5 & MF5_USESPECIAL || in->d.thing->special == UsePuzzleItem) - { // Target switching mechanism - if (in->d.thing->activationtype & THINGSPEC_ThingTargets) in->d.thing->target = usething; - if (in->d.thing->activationtype & THINGSPEC_TriggerTargets) usething->target = in->d.thing; - // Run the special - if (LineSpecials[in->d.thing->special] (NULL, // Who triggers? - ((in->d.thing->activationtype & THINGSPEC_ThingActs) ? in->d.thing : usething), false, - in->d.thing->args[0], in->d.thing->args[1], in->d.thing->args[2], - in->d.thing->args[3], in->d.thing->args[4])) - { - if (in->d.thing->activationtype & THINGSPEC_ClearSpecial) in->d.thing->special = 0; + { + if (P_ActivateThingSpecial(in->d.thing, usething)) return true; - } - } - // Dead things can't talk. - if (in->d.thing->health <= 0) - { - continue; - } - // Fighting things don't talk either. - if (in->d.thing->flags4 & MF4_INCOMBAT) - { - continue; - } - if (in->d.thing->Conversation != NULL) - { - // Give the NPC a chance to play a brief animation - in->d.thing->ConversationAnimation (0); - P_StartConversation (in->d.thing, usething, true, true); - return true; } continue; } FLineOpening open; - // [RH] The range passed to P_PathTraverse was doubled so that it could - // find things up to 128 units away (for Strife), but it should still reject - // lines further than 64 units away. - if (in->frac > FRACUNIT/2) - { - // don't pass usething here. It will not do what might be expected! - P_LineOpening (open, NULL, in->d.line, it.Trace().x + FixedMul (it.Trace().dx, in->frac), - it.Trace().y + FixedMul (it.Trace().dy, in->frac)); - if (open.range <= 0) return false; - else continue; - } if (in->d.line->special == 0 || !(in->d.line->activation & (SPAC_Use|SPAC_UseThrough))) { blocked: @@ -3886,6 +4053,8 @@ bool P_UseTraverse(AActor *usething, fixed_t endx, fixed_t endy, bool &foundline return false; } +//========================================================================== +// // Returns false if a "oof" sound should be made because of a blocking // linedef. Makes 2s middles which are impassable, as well as 2s uppers // and lowers which block the player, cause the sound effect when the @@ -3895,6 +4064,7 @@ bool P_UseTraverse(AActor *usething, fixed_t endx, fixed_t endy, bool &foundline // // by Lee Killough // +//========================================================================== bool P_NoWayTraverse (AActor *usething, fixed_t endx, fixed_t endy) { @@ -3906,7 +4076,7 @@ bool P_NoWayTraverse (AActor *usething, fixed_t endx, fixed_t endy) line_t *ld = in->d.line; FLineOpening open; - // [GrafZahl] de-obfuscated. Was I the only one who was unable to makes sense out of + // [GrafZahl] de-obfuscated. Was I the only one who was unable to make sense out of // this convoluted mess? if (ld->special) continue; if (ld->flags&(ML_BLOCKING|ML_BLOCKEVERYTHING|ML_BLOCK_PLAYERS)) return true; @@ -3919,26 +4089,25 @@ bool P_NoWayTraverse (AActor *usething, fixed_t endx, fixed_t endy) return false; } -/* -================ -= -= P_UseLines -= -= Looks for special lines in front of the player to activate -================ -*/ +//========================================================================== +// +// P_UseLines +// +// Looks for special lines in front of the player to activate +// +//========================================================================== void P_UseLines (player_t *player) { angle_t angle; - fixed_t x2, y2; + fixed_t x1, y1; bool foundline; foundline = false; angle = player->mo->angle >> ANGLETOFINESHIFT; - x2 = player->mo->x + (USERANGE>>FRACBITS)*finecosine[angle]*2; - y2 = player->mo->y + (USERANGE>>FRACBITS)*finesine[angle]*2; + x1 = player->mo->x + (USERANGE>>FRACBITS)*finecosine[angle]; + y1 = player->mo->y + (USERANGE>>FRACBITS)*finesine[angle]; // old code: // @@ -3946,13 +4115,13 @@ void P_UseLines (player_t *player) // // This added test makes the "oof" sound work on 2s lines -- killough: - if (!P_UseTraverse (player->mo, x2, y2, foundline)) + if (!P_UseTraverse (player->mo, x1, y1, foundline)) { // [RH] Give sector a chance to eat the use sector_t *sec = player->mo->Sector; int spac = SECSPAC_Use; if (foundline) spac |= SECSPAC_UseWall; if ((!sec->SecActTarget || !sec->SecActTarget->TriggerAction (player->mo, spac)) && - P_NoWayTraverse (player->mo, x2, y2)) + P_NoWayTraverse (player->mo, x1, y1)) { S_Sound (player->mo, CHAN_VOICE, "*usefail", 1, ATTN_IDLE); } @@ -4029,9 +4198,13 @@ bool P_UsePuzzleItem (AActor *PuzzleItemUser, int PuzzleItemType) return false; } +//========================================================================== // // RADIUS ATTACK // +// +//========================================================================== + // [RH] Damage scale to apply to thing that shot the missile. static float selfthrustscale; @@ -4044,10 +4217,13 @@ CUSTOM_CVAR (Float, splashfactor, 1.f, CVAR_SERVERINFO) selfthrustscale = 1.f / self; } +//========================================================================== // // P_RadiusAttack // Source is the creature that caused the explosion at spot. // +//========================================================================== + void P_RadiusAttack (AActor *bombspot, AActor *bombsource, int bombdamage, int bombdistance, FName bombmod, bool DamageSource, bool bombdodamage, int fulldamagedistance) { @@ -4209,7 +4385,7 @@ void P_RadiusAttack (AActor *bombspot, AActor *bombsource, int bombdamage, int b if (P_CheckSight (thing, bombspot, 1)) { // OK to damage; target is in direct path dist = clamp(dist - fulldamagedistance, 0, dist); - int damage = Scale (bombdamage, bombdistance-dist, bombdistance-fulldamagedistance); + int damage = Scale (bombdamage, bombdistance-dist, bombdistance); damage = (int)((float)damage * splashfactor); damage = Scale(damage, thing->GetClass()->Meta.GetMetaFixed(AMETA_RDFactor, FRACUNIT), FRACUNIT); @@ -4223,6 +4399,7 @@ void P_RadiusAttack (AActor *bombspot, AActor *bombsource, int bombdamage, int b } } +//========================================================================== // // SECTOR HEIGHT CHANGING // After modifying a sector's floor or ceiling height, @@ -4241,6 +4418,8 @@ void P_RadiusAttack (AActor *bombspot, AActor *bombsource, int bombdamage, int b // DOOM crushing behavior set crushchange to 10 or -1 // if no crushing is desired. // +//========================================================================== + struct FChangePosition { @@ -4315,14 +4494,26 @@ void P_FindAboveIntersectors (AActor *actor) { // Can't hit thing continue; } - if (thing->flags & (MF_CORPSE|MF_SPECIAL)) + if (thing->flags & (MF_SPECIAL)) { // [RH] Corpses and specials don't block moves continue; } + if (thing->flags & (MF_CORPSE)) + { // Corpses need a few more checks + if (!(actor->flags & MF_ICECORPSE)) + continue; + } if (thing == actor) { // Don't clip against self continue; } + if (!((thing->flags2 | actor->flags2) & MF2_PASSMOBJ) && !((thing->flags3 | actor->flags3) & MF3_ISMONSTER)) + { + // Don't bother if both things don't have MF2_PASSMOBJ set and aren't monsters. + // These things would always block each other which in nearly every situation is + // not what is wanted here. + continue; + } if (thing->z >= actor->z && thing->z <= actor->z + actor->height) { // Thing intersects above the base @@ -4357,14 +4548,26 @@ void P_FindBelowIntersectors (AActor *actor) { // Can't hit thing continue; } - if (thing->flags & (MF_CORPSE|MF_SPECIAL)) + if (thing->flags & (MF_SPECIAL)) { // [RH] Corpses and specials don't block moves continue; } + if (thing->flags & (MF_CORPSE)) + { // Corpses need a few more checks + if (!(actor->flags & MF_ICECORPSE)) + continue; + } if (thing == actor) { // Don't clip against self continue; } + if (!((thing->flags2 | actor->flags2) & MF2_PASSMOBJ) && !((thing->flags3 | actor->flags3) & MF3_ISMONSTER)) + { + // Don't bother if both things don't have MF2_PASSMOBJ set and aren't monsters. + // These things would always block each other which in nearly every situation is + // not what is wanted here. + continue; + } if (thing->z + thing->height <= actor->z + actor->height && thing->z + thing->height > actor->z) { // Thing intersects below the base @@ -5108,6 +5311,12 @@ void P_CreateSecNodeList (AActor *thing, fixed_t x, fixed_t y) } } +//========================================================================== +// +// +// +//========================================================================== + void SpawnShootDecal (AActor *t1, const FTraceResults &trace) { FDecalBase *decalbase = NULL; @@ -5127,6 +5336,12 @@ void SpawnShootDecal (AActor *t1, const FTraceResults &trace) } } +//========================================================================== +// +// +// +//========================================================================== + static void SpawnDeepSplash (AActor *t1, const FTraceResults &trace, AActor *puff, fixed_t vx, fixed_t vy, fixed_t vz, fixed_t shootz) { @@ -5151,3 +5366,67 @@ static void SpawnDeepSplash (AActor *t1, const FTraceResults &trace, AActor *puf } } } + +//============================================================================= +// +// P_ActivateThingSpecial +// +// Handles the code for things activated by death, USESPECIAL or BUMPSPECIAL +// +//============================================================================= + +bool P_ActivateThingSpecial(AActor * thing, AActor * trigger, bool death) +{ + bool res = false; + + // Target switching mechanism + if (thing->activationtype & THINGSPEC_ThingTargets) thing->target = trigger; + if (thing->activationtype & THINGSPEC_TriggerTargets) trigger->target = thing; + + // State change mechanism. The thing needs to be not dead and to have at least one of the relevant flags + if (!death && (thing->activationtype & (THINGSPEC_Activate|THINGSPEC_Deactivate|THINGSPEC_Switch))) + { + // If a switchable thing does not know whether it should be activated + // or deactivated, the default is to activate it. + if ((thing->activationtype & THINGSPEC_Switch) + && !(thing->activationtype & (THINGSPEC_Activate|THINGSPEC_Deactivate))) + { + thing->activationtype |= THINGSPEC_Activate; + } + // Can it be activated? + if (thing->activationtype & THINGSPEC_Activate) + { + thing->activationtype &= ~THINGSPEC_Activate; // Clear flag + if (thing->activationtype & THINGSPEC_Switch) // Set other flag if switching + thing->activationtype |= THINGSPEC_Deactivate; + thing->Activate(trigger); + res = true; + } + // If not, can it be deactivated? + else if (thing->activationtype & THINGSPEC_Deactivate) + { + thing->activationtype &= ~THINGSPEC_Deactivate; // Clear flag + if (thing->activationtype & THINGSPEC_Switch) // Set other flag if switching + thing->activationtype |= THINGSPEC_Activate; + thing->Deactivate(trigger); + res = true; + } + } + + // Run the special, if any + if (thing->special) + { + res = !! LineSpecials[thing->special] (NULL, + // TriggerActs overrides the level flag, which only concerns thing activated by death + (((death && level.flags & LEVEL_ACTOWNSPECIAL && !(thing->activationtype & THINGSPEC_TriggerActs)) + || (thing->activationtype & THINGSPEC_ThingActs)) // Who triggers? + ? thing : trigger), + false, thing->args[0], thing->args[1], thing->args[2], thing->args[3], thing->args[4]); + + // Clears the special if it was run on thing's death or if flag is set. + if (death || (thing->activationtype & THINGSPEC_ClearSpecial && res)) thing->special = 0; + } + + // Returns the result + return res; +} diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 6e9272ca2..4db035941 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -85,7 +85,6 @@ EXTERN_CVAR (Int, cl_rockettrails) // PRIVATE DATA DEFINITIONS ------------------------------------------------ -static bool SpawningMapThing; static FRandom pr_explodemissile ("ExplodeMissile"); FRandom pr_bounce ("Bounce"); static FRandom pr_reflect ("Reflect"); @@ -300,8 +299,56 @@ void AActor::Serialize (FArchive &arc) << Species << Score << Tag; + if (SaveVersion >= 1904) + { + arc << lastpush << lastbump; + } - for(int i=0; i<10; i++) arc << uservar[i]; + if (SaveVersion >= 1900) + { + arc << PainThreshold; + } + if (SaveVersion >= 1914) + { + arc << DamageFactor; + } + if (SaveVersion > 2036) + { + arc << WeaveIndexXY << WeaveIndexZ; + } + else + { + int index; + + if (SaveVersion < 2036) + { + index = special2; + } + else + { + arc << index; + } + // A_BishopMissileWeave and A_CStaffMissileSlither stored the weaveXY + // value in different parts of the index. + if (this->IsKindOf(PClass::FindClass("BishopFX"))) + { + WeaveIndexXY = index >> 16; + WeaveIndexZ = index; + } + else + { + WeaveIndexXY = index; + WeaveIndexZ = 0; + } + } + + // Skip past uservar array in old savegames + if (SaveVersion < 1933) + { + int foo; + for (int i = 0; i < 10; ++i) + arc << foo; + } if (arc.IsStoring ()) { @@ -390,6 +437,7 @@ void AActor::Serialize (FArchive &arc) PrevX = x; PrevY = y; PrevZ = z; + PrevAngle = angle; UpdateWaterLevel(z, false); } } @@ -541,6 +589,7 @@ bool AActor::SetState (FState *newstate) newstate = newstate->GetNextState(); } while (tics == 0); + screen->StateChanged(this); return true; } @@ -600,6 +649,7 @@ bool AActor::SetStateNF (FState *newstate) newstate = newstate->GetNextState(); } while (tics == 0); + screen->StateChanged(this); return true; } @@ -998,7 +1048,7 @@ bool AActor::Grind(bool items) if (state == NULL // Only use the default crushed state if: && !(flags & MF_NOBLOOD) // 1. the monster bleeeds, && (i_compatflags & COMPATF_CORPSEGIBS) // 2. the compat setting is on, - && player != NULL) // 3. and the thing isn't a player. + && player == NULL) // 3. and the thing isn't a player. { isgeneric = true; state = FindState(NAME_GenericCrush); @@ -1157,7 +1207,7 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) FState *nextstate=NULL; - if (target != NULL && target->flags & (MF_SHOOTABLE|MF_CORPSE)) + if (target != NULL && ((target->flags & (MF_SHOOTABLE|MF_CORPSE)) || (target->flags6 & MF6_KILLED)) ) { if (target->flags & MF_NOBLOOD) nextstate = mo->FindState(NAME_Crash); if (nextstate == NULL) nextstate = mo->FindState(NAME_Death, NAME_Extreme); @@ -1170,7 +1220,7 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) return; } - if (line != NULL && line->special == Line_Horizon) + if (line != NULL && line->special == Line_Horizon && !(mo->flags3 & MF3_SKYEXPLODE)) { // [RH] Don't explode missiles on horizon lines. mo->Destroy (); @@ -1736,6 +1786,8 @@ fixed_t P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly) */ // [RH] If walking on a slope, stay on the slope // killough 3/15/98: Allow objects to drop off + fixed_t startvelx = mo->velx, startvely = mo->vely; + if (!P_TryMove (mo, ptryx, ptryy, true, walkplane, tm)) { // blocked move @@ -1759,35 +1811,44 @@ fixed_t P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly) { mo->velz = WATER_JUMP_SPEED; } - if (player && (i_compatflags & COMPATF_WALLRUN)) + // If the blocked move executed any push specials that changed the + // actor's velocity, do not attempt to slide. + if (mo->velx == startvelx && mo->vely == startvely) { - // [RH] Here is the key to wall running: The move is clipped using its full speed. - // If the move is done a second time (because it was too fast for one move), it - // is still clipped against the wall at its full speed, so you effectively - // execute two moves in one tic. - P_SlideMove (mo, mo->velx, mo->vely, 1); + if (player && (i_compatflags & COMPATF_WALLRUN)) + { + // [RH] Here is the key to wall running: The move is clipped using its full speed. + // If the move is done a second time (because it was too fast for one move), it + // is still clipped against the wall at its full speed, so you effectively + // execute two moves in one tic. + P_SlideMove (mo, mo->velx, mo->vely, 1); + } + else + { + P_SlideMove (mo, onestepx, onestepy, totalsteps); + } + if ((mo->velx | mo->vely) == 0) + { + steps = 0; + } + else + { + if (!player || !(i_compatflags & COMPATF_WALLRUN)) + { + xmove = mo->velx; + ymove = mo->vely; + onestepx = xmove / steps; + onestepy = ymove / steps; + P_CheckSlopeWalk (mo, xmove, ymove); + } + startx = mo->x - Scale (xmove, step, steps); + starty = mo->y - Scale (ymove, step, steps); + } } else - { - P_SlideMove (mo, onestepx, onestepy, totalsteps); - } - if ((mo->velx | mo->vely) == 0) { steps = 0; } - else - { - if (!player || !(i_compatflags & COMPATF_WALLRUN)) - { - xmove = mo->velx; - ymove = mo->vely; - onestepx = xmove / steps; - onestepy = ymove / steps; - P_CheckSlopeWalk (mo, xmove, ymove); - } - startx = mo->x - Scale (xmove, step, steps); - starty = mo->y - Scale (ymove, step, steps); - } } else { // slide against another actor @@ -1870,22 +1931,24 @@ fixed_t P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly) } explode: // explode a missile - if (tm.ceilingline && - tm.ceilingline->backsector && - tm.ceilingline->backsector->GetTexture(sector_t::ceiling) == skyflatnum && - mo->z >= tm.ceilingline->backsector->ceilingplane.ZatPoint (mo->x, mo->y) && //killough - !(mo->flags3 & MF3_SKYEXPLODE)) + if (!(mo->flags3 & MF3_SKYEXPLODE)) { - // Hack to prevent missiles exploding against the sky. - // Does not handle sky floors. - mo->Destroy (); - return oldfloorz; - } - // [RH] Don't explode on horizon lines. - if (mo->BlockingLine != NULL && mo->BlockingLine->special == Line_Horizon) - { - mo->Destroy (); - return oldfloorz; + if (tm.ceilingline && + tm.ceilingline->backsector && + tm.ceilingline->backsector->GetTexture(sector_t::ceiling) == skyflatnum && + mo->z >= tm.ceilingline->backsector->ceilingplane.ZatPoint (mo->x, mo->y)) + { + // Hack to prevent missiles exploding against the sky. + // Does not handle sky floors. + mo->Destroy (); + return oldfloorz; + } + // [RH] Don't explode on horizon lines. + if (mo->BlockingLine != NULL && mo->BlockingLine->special == Line_Horizon) + { + mo->Destroy (); + return oldfloorz; + } } P_ExplodeMissile (mo, mo->BlockingLine, BlockingMobj); return oldfloorz; @@ -2166,8 +2229,12 @@ void P_ZMovement (AActor *mo, fixed_t oldfloorz) // teleported the actor so it is no longer below the floor. if (mo->z <= mo->floorz) { - if ((mo->flags & MF_MISSILE) && - (!(gameinfo.gametype & GAME_DoomChex) || !(mo->flags & MF_NOCLIP))) + // old code for boss cube disabled + //if ((mo->flags & MF_MISSILE) && (!(gameinfo.gametype & GAME_DoomChex) || !(mo->flags & MF_NOCLIP))) + + // We can't remove this completely because it was abused by some DECORATE definitions + // (e.g. the monster pack's Afrit) + if ((mo->flags & MF_MISSILE) && ((mo->flags & MF_NOGRAVITY) || !(mo->flags & MF_NOCLIP))) { mo->z = mo->floorz; if (mo->BounceFlags & BOUNCE_Floors) @@ -2278,8 +2345,8 @@ void P_ZMovement (AActor *mo, fixed_t oldfloorz) } if (mo->velz > 0) mo->velz = 0; - if (mo->flags & MF_MISSILE && - (!(gameinfo.gametype & GAME_DoomChex) || !(mo->flags & MF_NOCLIP))) + if (mo->flags & MF_MISSILE) + //&& (!(gameinfo.gametype & GAME_DoomChex) || !(mo->flags & MF_NOCLIP))) { if (mo->flags3 & MF3_CEILINGHUGGER) { @@ -2671,36 +2738,59 @@ void AActor::PlayActiveSound () bool AActor::IsOkayToAttack (AActor *link) { - if (player) // Minotaur looking around player + if (!(player // Original AActor::IsOkayToAttack was only for players + // || (flags & MF_FRIENDLY) // Maybe let friendly monsters use the function as well? + || (flags5 & MF5_SUMMONEDMONSTER) // AMinotaurFriend has its own version, generalized to other summoned monsters + || (flags2 & MF2_SEEKERMISSILE))) // AHolySpirit and AMageStaffFX2 as well, generalized to other seeker missiles + { // Normal monsters and other actors always return false. + return false; + } + // Standard things to eliminate: an actor shouldn't attack itself, + // or a non-shootable, dormant, non-player-and-non-monster actor. + if (link == this) return false; + if (!(link->player||(link->flags3 & MF3_ISMONSTER)))return false; + if (!(link->flags & MF_SHOOTABLE)) return false; + if (link->flags2 & MF2_DORMANT) return false; + + // An actor shouldn't attack friendly actors. The reference depends + // on the type of actor: for a player's actor, itself; for a projectile, + // its target; and for a summoned minion, its tracer. + AActor * Friend = NULL; + if (player) Friend = this; + else if (flags5 & MF5_SUMMONEDMONSTER) Friend = tracer; + else if (flags2 & MF2_SEEKERMISSILE) Friend = target; + else if ((flags & MF_FRIENDLY) && FriendPlayer) Friend = players[FriendPlayer-1].mo; + + // Friend checks + if (link == Friend) return false; + if (Friend == NULL) return false; + if (Friend->IsFriend(link)) return false; + if ((link->flags5 & MF5_SUMMONEDMONSTER) // No attack against minions on the same side + && (link->tracer == Friend)) return false; + if (multiplayer && !deathmatch // No attack against fellow players in coop + && link->player && Friend->player) return false; + if (((flags & link->flags) & MF_FRIENDLY) // No friendly infighting amongst minions + && IsFriend(link)) return false; + + // Now that all the actor checks are made, the line of sight can be checked + if (P_CheckSight (this, link)) { - if ((link->flags3 & MF3_ISMONSTER) || (link->player && (link != this))) + // AMageStaffFX2::IsOkayToAttack had an extra check here, generalized with a flag, + // to only allow the check to succeed if the enemy was in a ~84° FOV of the player + if (flags3 & MF3_SCREENSEEKER) { - if (IsFriend(link)) - { - return false; - } - if (!(link->flags & MF_SHOOTABLE)) - { - return false; - } - if (link->flags2 & MF2_DORMANT) - { - return false; - } - if ((link->flags5 & MF5_SUMMONEDMONSTER) && (link->tracer == this)) - { - return false; - } - if (multiplayer && !deathmatch && link->player) - { - return false; - } - if (P_CheckSight (this, link)) + angle_t angle = R_PointToAngle2(Friend->x, + Friend->y, link->x, link->y) - Friend->angle; + angle >>= 24; + if (angle>226 || angle<30) { return true; } } + // Other actors are not concerned by this check + else return true; } + // The sight check was failed, or the angle wasn't right for a screenseeker return false; } @@ -2741,16 +2831,21 @@ void AActor::Tick () AActor *onmo; int i; - assert (state != NULL); + //assert (state != NULL); if (state == NULL) { + Printf("Actor of type %s at (%f,%f) left without a state\n", GetClass()->TypeName.GetChars(), + x/65536., y/65536.); Destroy(); return; } + // This is necessary to properly interpolate movement outside this function + // like from an ActorMover PrevX = x; PrevY = y; PrevZ = z; + PrevAngle = angle; if (flags5 & MF5_NOINTERACTION) { @@ -3302,7 +3397,7 @@ void AActor::Tick () bool AActor::UpdateWaterLevel (fixed_t oldz, bool dosplash) { BYTE lastwaterlevel = waterlevel; - fixed_t fh=FIXED_MIN; + fixed_t fh = FIXED_MIN; bool reset=false; waterlevel = 0; @@ -3337,18 +3432,21 @@ bool AActor::UpdateWaterLevel (fixed_t oldz, bool dosplash) } } } - else if (z + height > hsec->ceilingplane.ZatPoint (x, y)) + else if (!(hsec->MoreFlags & SECF_FAKEFLOORONLY) && (z + height > hsec->ceilingplane.ZatPoint (x, y))) { waterlevel = 3; } else { - waterlevel=0; + waterlevel = 0; } } // even non-swimmable deep water must be checked here to do the splashes correctly // But the water level must be reset when this function returns - if (!(hsec->MoreFlags&SECF_UNDERWATERMASK)) reset=true; + if (!(hsec->MoreFlags&SECF_UNDERWATERMASK)) + { + reset = true; + } } #ifdef _3DFLOORS else @@ -3393,8 +3491,11 @@ bool AActor::UpdateWaterLevel (fixed_t oldz, bool dosplash) { P_HitWater(this, Sector, FIXED_MIN, FIXED_MIN, fh, true); } - boomwaterlevel=waterlevel; - if (reset) waterlevel=lastwaterlevel; + boomwaterlevel = waterlevel; + if (reset) + { + waterlevel = lastwaterlevel; + } return false; // we did the splash ourselves } @@ -3404,7 +3505,7 @@ bool AActor::UpdateWaterLevel (fixed_t oldz, bool dosplash) // //========================================================================== -AActor *AActor::StaticSpawn (const PClass *type, fixed_t ix, fixed_t iy, fixed_t iz, replace_t allowreplacement) +AActor *AActor::StaticSpawn (const PClass *type, fixed_t ix, fixed_t iy, fixed_t iz, replace_t allowreplacement, bool SpawningMapThing) { if (type == NULL) { @@ -3430,6 +3531,9 @@ AActor *AActor::StaticSpawn (const PClass *type, fixed_t ix, fixed_t iy, fixed_t actor->picnum.SetInvalid(); actor->health = actor->SpawnHealth(); + // Actors with zero gravity need the NOGRAVITY flag set. + if (actor->gravity == 0) actor->flags |= MF_NOGRAVITY; + FRandom &rng = bglobal.m_Thinking ? pr_botspawnmobj : pr_spawnmobj; if (actor->isFast() && actor->flags3 & MF3_ISMONSTER) @@ -3568,16 +3672,27 @@ AActor *AActor::StaticSpawn (const PClass *type, fixed_t ix, fixed_t iy, fixed_t { level.total_items++; } + screen->StateChanged(actor); return actor; } AActor *Spawn (const char *type, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement) { - const PClass *cls = PClass::FindClass(type); - if (cls == NULL) + FName classname(type, true); + if (classname == NAME_None) { I_Error("Attempt to spawn actor of unknown type '%s'\n", type); } + return Spawn(classname, x, y, z, allowreplacement); +} + +AActor *Spawn (FName classname, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement) +{ + const PClass *cls = PClass::FindClass(classname); + if (cls == NULL) + { + I_Error("Attempt to spawn actor of unknown type '%s'\n", classname.GetChars()); + } return AActor::StaticSpawn (cls, x, y, z, allowreplacement); } @@ -3638,6 +3753,11 @@ void AActor::BeginPlay () } } +void AActor::PostBeginPlay () +{ + PrevAngle = angle; +} + bool AActor::isFast() { if (flags5&MF5_ALWAYSFAST) return true; @@ -3652,7 +3772,7 @@ void AActor::Activate (AActor *activator) if (flags2 & MF2_DORMANT) { flags2 &= ~MF2_DORMANT; - FState *state = FindState("Active"); + FState *state = FindState(NAME_Active); if (state != NULL) { SetState(state); @@ -3672,7 +3792,7 @@ void AActor::Deactivate (AActor *activator) if (!(flags2 & MF2_DORMANT)) { flags2 |= MF2_DORMANT; - FState *state = FindState("Inactive"); + FState *state = FindState(NAME_Inactive); if (state != NULL) { SetState(state); @@ -3781,17 +3901,13 @@ APlayerPawn *P_SpawnPlayer (FMapThing *mthing, bool tempplayer) // [RH] Things 4001-? are also multiplayer starts. Just like 1-4. // To make things simpler, figure out which player is being // spawned here. - if (mthing->type <= 4 || gameinfo.gametype == GAME_Strife) // don't forget Strife's starts 5-8 here! + if (mthing->type <= 4) { playernum = mthing->type - 1; } - else if (gameinfo.gametype != GAME_Hexen) - { - playernum = mthing->type - 4001 + 4; - } else { - playernum = mthing->type - 9100 + 4; + playernum = mthing->type - gameinfo.player5start + 4; } // not playing? @@ -4083,12 +4199,9 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) } else { - const int base = (gameinfo.gametype == GAME_Strife) ? 5 : - (gameinfo.gametype == GAME_Hexen) ? 9100 : 4001; - - if (mthing->type >= base && mthing->type < base + MAXPLAYERS - 4) + if (mthing->type >= gameinfo.player5start && mthing->type < gameinfo.player5start + MAXPLAYERS - 4) { - pnum = mthing->type - base + 4; + pnum = mthing->type - gameinfo.player5start + 4; } } @@ -4284,9 +4397,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) else z = ONFLOORZ; - SpawningMapThing = true; - mobj = Spawn (i, x, y, z, NO_REPLACE); - SpawningMapThing = false; + mobj = AActor::StaticSpawn (i, x, y, z, NO_REPLACE, true); if (z == ONFLOORZ) mobj->z += mthing->z; @@ -4311,7 +4422,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) mobj->tid = mthing->thingid; mobj->AddToHash (); - mobj->angle = (DWORD)((mthing->angle * UCONST64(0x100000000)) / 360); + mobj->PrevAngle = mobj->angle = (DWORD)((mthing->angle * UCONST64(0x100000000)) / 360); mobj->BeginPlay (); if (!(mobj->ObjectFlags & OF_EuthanizeMe)) { @@ -4339,6 +4450,11 @@ AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t puff = Spawn (pufftype, x, y, z, ALLOW_REPLACE); if (puff == NULL) return NULL; + + // [BB] If the puff came from a player, set the target of the puff to this player. + if ( puff && (puff->flags5 & MF5_PUFFGETSOWNER)) + puff->target = source; + if (source != NULL) puff->angle = R_PointToAngle2(x, y, source->x, source->y); // If a puff has a crash state and an actor was not hit, @@ -4374,10 +4490,6 @@ AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t } } - // [BB] If the puff came from a player, set the target of the puff to this player. - if ( puff && (puff->flags5 & MF5_PUFFGETSOWNER)) - puff->target = source; - return puff; } @@ -4394,8 +4506,13 @@ void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage, AAc AActor *th; PalEntry bloodcolor = (PalEntry)originator->GetClass()->Meta.GetMetaInt(AMETA_BloodColor); const PClass *bloodcls = PClass::FindClass((ENamedName)originator->GetClass()->Meta.GetMetaInt(AMETA_BloodType, NAME_Blood)); + + int bloodtype = cl_bloodtype; + + if (bloodcls != NULL && !(GetDefaultByType(bloodcls)->flags4 & MF4_ALLOWPARTICLES)) + bloodtype = 0; - if (bloodcls!=NULL && cl_bloodtype <= 1) + if (bloodcls!=NULL && bloodtype <= 1) { z += pr_spawnblood.Random2 () << 10; th = Spawn (bloodcls, x, y, z, ALLOW_REPLACE); @@ -4437,7 +4554,7 @@ void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage, AAc } } - if (cl_bloodtype >= 1) + if (bloodtype >= 1) P_DrawSplash2 (40, x, y, z, dir, 2, bloodcolor); } @@ -4452,7 +4569,12 @@ void P_BloodSplatter (fixed_t x, fixed_t y, fixed_t z, AActor *originator) PalEntry bloodcolor = (PalEntry)originator->GetClass()->Meta.GetMetaInt(AMETA_BloodColor); const PClass *bloodcls = PClass::FindClass((ENamedName)originator->GetClass()->Meta.GetMetaInt(AMETA_BloodType2, NAME_BloodSplatter)); - if (bloodcls!=NULL && cl_bloodtype <= 1) + int bloodtype = cl_bloodtype; + + if (bloodcls != NULL && !(GetDefaultByType(bloodcls)->flags4 & MF4_ALLOWPARTICLES)) + bloodtype = 0; + + if (bloodcls!=NULL && bloodtype <= 1) { AActor *mo; @@ -4468,7 +4590,7 @@ void P_BloodSplatter (fixed_t x, fixed_t y, fixed_t z, AActor *originator) mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a); } } - if (cl_bloodtype >= 1) + if (bloodtype >= 1) { P_DrawSplash2 (40, x, y, z, R_PointToAngle2 (x, y, originator->x, originator->y), 2, bloodcolor); } @@ -4485,7 +4607,12 @@ void P_BloodSplatter2 (fixed_t x, fixed_t y, fixed_t z, AActor *originator) PalEntry bloodcolor = (PalEntry)originator->GetClass()->Meta.GetMetaInt(AMETA_BloodColor); const PClass *bloodcls = PClass::FindClass((ENamedName)originator->GetClass()->Meta.GetMetaInt(AMETA_BloodType3, NAME_AxeBlood)); - if (bloodcls!=NULL && cl_bloodtype <= 1) + int bloodtype = cl_bloodtype; + + if (bloodcls != NULL && !(GetDefaultByType(bloodcls)->flags4 & MF4_ALLOWPARTICLES)) + bloodtype = 0; + + if (bloodcls!=NULL && bloodtype <= 1) { AActor *mo; @@ -4501,7 +4628,7 @@ void P_BloodSplatter2 (fixed_t x, fixed_t y, fixed_t z, AActor *originator) mo->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a); } } - if (cl_bloodtype >= 1) + if (bloodtype >= 1) { P_DrawSplash2 (100, x, y, z, R_PointToAngle2 (0, 0, originator->x - x, originator->y - y), 2, bloodcolor); } @@ -4522,7 +4649,13 @@ void P_RipperBlood (AActor *mo, AActor *bleeder) x = mo->x + (pr_ripperblood.Random2 () << 12); y = mo->y + (pr_ripperblood.Random2 () << 12); z = mo->z + (pr_ripperblood.Random2 () << 12); - if (bloodcls!=NULL && cl_bloodtype <= 1) + + int bloodtype = cl_bloodtype; + + if (bloodcls != NULL && !(GetDefaultByType(bloodcls)->flags4 & MF4_ALLOWPARTICLES)) + bloodtype = 0; + + if (bloodcls!=NULL && bloodtype <= 1) { AActor *th; th = Spawn (bloodcls, x, y, z, ALLOW_REPLACE); @@ -4538,7 +4671,7 @@ void P_RipperBlood (AActor *mo, AActor *bleeder) th->Translation = TRANSLATION(TRANSLATION_Blood, bloodcolor.a); } } - if (cl_bloodtype >= 1) + if (bloodtype >= 1) { P_DrawSplash2 (28, x, y, z, 0, 0, bloodcolor); } @@ -4569,7 +4702,7 @@ int P_GetThingFloorType (AActor *thing) // Returns true if hit liquid and splashed, false if not. //--------------------------------------------------------------------------- -bool P_HitWater (AActor * thing, sector_t * sec, fixed_t x, fixed_t y, fixed_t z, bool checkabove) +bool P_HitWater (AActor * thing, sector_t * sec, fixed_t x, fixed_t y, fixed_t z, bool checkabove, bool alert) { if (thing->flags2 & MF2_FLOATBOB || thing->flags3 & MF3_DONTSPLASH) return false; @@ -4667,7 +4800,7 @@ foundone: { mo = Spawn (splash->SplashBase, x, y, z, ALLOW_REPLACE); } - if (thing->player && !splash->NoAlert) + if (thing->player && !splash->NoAlert && alert) { P_NoiseAlert (thing, thing, true); } @@ -4744,6 +4877,25 @@ bool P_HitFloor (AActor *thing) return P_HitWater (thing, m->m_sector); } +//--------------------------------------------------------------------------- +// +// P_CheckSplash +// +// Checks for splashes caused by explosions +// +//--------------------------------------------------------------------------- + +void P_CheckSplash(AActor *self, fixed_t distance) +{ + if (self->z <= self->floorz + (distance<floorsector == self->Sector) + { + // Explosion splashes never alert monsters. This is because A_Explode has + // a separate parameter for that so this would get in the way of proper + // behavior. + P_HitWater (self, self->Sector, self->x, self->y, self->floorz, false, false); + } +} + //--------------------------------------------------------------------------- // // FUNC P_CheckMissileSpawn @@ -4894,10 +5046,10 @@ static fixed_t GetDefaultSpeed(const PClass *type) // //--------------------------------------------------------------------------- -AActor *P_SpawnMissile (AActor *source, AActor *dest, const PClass *type) +AActor *P_SpawnMissile (AActor *source, AActor *dest, const PClass *type, AActor *owner) { return P_SpawnMissileXYZ (source->x, source->y, source->z + 32*FRACUNIT, - source, dest, type); + source, dest, type, true, owner); } AActor *P_SpawnMissileZ (AActor *source, fixed_t z, AActor *dest, const PClass *type) @@ -4906,7 +5058,7 @@ AActor *P_SpawnMissileZ (AActor *source, fixed_t z, AActor *dest, const PClass * } AActor *P_SpawnMissileXYZ (fixed_t x, fixed_t y, fixed_t z, - AActor *source, AActor *dest, const PClass *type, bool checkspawn) + AActor *source, AActor *dest, const PClass *type, bool checkspawn, AActor *owner) { if (dest == NULL) { @@ -4923,7 +5075,10 @@ AActor *P_SpawnMissileXYZ (fixed_t x, fixed_t y, fixed_t z, AActor *th = Spawn (type, x, y, z, ALLOW_REPLACE); P_PlaySpawnSound(th, source); - th->target = source; // record missile's originator + + // record missile's originator + if (owner) th->target = owner; + else th->target = source; float speed = (float)(th->Speed); @@ -4966,14 +5121,14 @@ AActor *P_SpawnMissileXYZ (fixed_t x, fixed_t y, fixed_t z, return (!checkspawn || P_CheckMissileSpawn (th)) ? th : NULL; } -AActor * P_OldSpawnMissile(AActor * source, AActor * dest, const PClass *type) +AActor * P_OldSpawnMissile(AActor * source, AActor * owner, AActor * dest, const PClass *type) { angle_t an; fixed_t dist; AActor *th = Spawn (type, source->x, source->y, source->z + 4*8*FRACUNIT, ALLOW_REPLACE); P_PlaySpawnSound(th, source); - th->target = source; // record missile's originator + th->target = owner; // record missile's originator th->angle = an = R_PointToAngle2 (source->x, source->y, dest->x, dest->y); an >>= ANGLETOFINESHIFT; @@ -5102,8 +5257,6 @@ AActor *P_SpawnPlayerMissile (AActor *source, fixed_t x, fixed_t y, fixed_t z, AActor *linetarget; int vrange = nofreeaim? ANGLE_1*35 : 0; - // Note: NOAUTOAIM is implemented only here, and not in the hitscan or rail attack functions. - // That is because it is only justified for projectiles affected by gravity, not for other attacks. if (source && source->player && source->player->ReadyWeapon && (source->player->ReadyWeapon->WeaponFlags & WIF_NOAUTOAIM)) { // Keep exactly the same angle and pitch as the player's own aim @@ -5130,6 +5283,10 @@ AActor *P_SpawnPlayerMissile (AActor *source, fixed_t x, fixed_t y, fixed_t z, if (linetarget == NULL) { an = angle; + if (nofreeaim || !level.IsFreelookAllowed()) + { + pitch = 0; + } } if (pLineTarget) *pLineTarget = linetarget; @@ -5166,16 +5323,16 @@ AActor *P_SpawnPlayerMissile (AActor *source, fixed_t x, fixed_t y, fixed_t z, vz = -finesine[pitch>>ANGLETOFINESHIFT]; speed = MissileActor->Speed; - MissileActor->velx = FixedMul (vx, speed); - MissileActor->vely = FixedMul (vy, speed); + FVector3 vec(vx, vy, vz); + if (MissileActor->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER)) { - MissileActor->velz = 0; - } - else - { - MissileActor->velz = FixedMul (vz, speed); + vec.Z = 0; } + vec.Resize(speed); + MissileActor->velx = (fixed_t)vec.X; + MissileActor->vely = (fixed_t)vec.Y; + MissileActor->velz = (fixed_t)vec.Z; if (MissileActor->flags4 & MF4_SPECTRAL) MissileActor->health = -1; @@ -5340,7 +5497,7 @@ int AActor::TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FN void AActor::Crash() { - if ((flags & MF_CORPSE) && + if (((flags & MF_CORPSE) || (flags6 & MF6_KILLED)) && !(flags3 & MF3_CRASHED) && !(flags & MF_ICECORPSE)) { diff --git a/src/p_pillar.cpp b/src/p_pillar.cpp index f20dc1ef4..1d2d5ba81 100644 --- a/src/p_pillar.cpp +++ b/src/p_pillar.cpp @@ -98,7 +98,7 @@ void DPillar::Tick () if (r == pastdest && s == pastdest) { - SN_StopSequence (m_Sector); + SN_StopSequence (m_Sector, CHAN_FLOOR); Destroy (); } else diff --git a/src/p_plats.cpp b/src/p_plats.cpp index 962447e49..87d9e6737 100644 --- a/src/p_plats.cpp +++ b/src/p_plats.cpp @@ -82,7 +82,7 @@ void DPlat::Tick () } else if (res == pastdest) { - SN_StopSequence (m_Sector); + SN_StopSequence (m_Sector, CHAN_FLOOR); if (m_Type != platToggle) { m_Count = m_Wait; @@ -121,7 +121,7 @@ void DPlat::Tick () if (res == pastdest) { - SN_StopSequence (m_Sector); + SN_StopSequence (m_Sector, CHAN_FLOOR); // if not an instant toggle, start waiting if (m_Type != platToggle) //jff 3/14/98 toggle up down { // is silent, instant, no waiting diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 7faf9b1b4..37fb2f651 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -206,7 +206,7 @@ void P_BringUpWeapon (player_t *player) // //--------------------------------------------------------------------------- -void P_FireWeapon (player_t *player) +void P_FireWeapon (player_t *player, FState *state) { AWeapon *weapon; @@ -225,7 +225,11 @@ void P_FireWeapon (player_t *player) player->mo->PlayAttacking (); weapon->bAltFire = false; - P_SetPsprite (player, ps_weapon, weapon->GetAtkState(!!player->refire)); + if (state == NULL) + { + state = weapon->GetAtkState(!!player->refire); + } + P_SetPsprite (player, ps_weapon, state); if (!(weapon->WeaponFlags & WIF_NOALERT)) { P_NoiseAlert (player->mo, player->mo, false); @@ -238,7 +242,7 @@ void P_FireWeapon (player_t *player) // //--------------------------------------------------------------------------- -void P_FireWeaponAlt (player_t *player) +void P_FireWeaponAlt (player_t *player, FState *state) { AWeapon *weapon; @@ -258,8 +262,12 @@ void P_FireWeaponAlt (player_t *player) player->mo->PlayAttacking (); weapon->bAltFire = true; + if (state == NULL) + { + state = weapon->GetAltAtkState(!!player->refire); + } - P_SetPsprite (player, ps_weapon, weapon->GetAltAtkState(!!player->refire)); + P_SetPsprite (player, ps_weapon, state); if (!(weapon->WeaponFlags & WIF_NOALERT)) { P_NoiseAlert (player->mo, player->mo, false); @@ -459,7 +467,7 @@ void P_CheckWeaponFire (player_t *player) if (!player->attackdown || !(weapon->WeaponFlags & WIF_NOAUTOFIRE)) { player->attackdown = true; - P_FireWeapon (player); + P_FireWeapon (player, NULL); return; } } @@ -468,7 +476,7 @@ void P_CheckWeaponFire (player_t *player) if (!player->attackdown || !(weapon->WeaponFlags & WIF_NOAUTOFIRE)) { player->attackdown = true; - P_FireWeaponAlt (player); + P_FireWeaponAlt (player, NULL); return; } } @@ -500,6 +508,11 @@ void P_CheckWeaponSwitch (player_t *player) P_SetPsprite (player, ps_weapon, weapon->GetDownState()); return; } + else if (player->morphTics != 0) + { + // morphed classes cannot change weapons so don't even try again. + player->PendingWeapon = WP_NOCHANGE; + } } //--------------------------------------------------------------------------- @@ -510,29 +523,35 @@ void P_CheckWeaponSwitch (player_t *player) // //--------------------------------------------------------------------------- -DEFINE_ACTION_FUNCTION(AInventory, A_ReFire) +DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_ReFire) { PARAM_ACTION_PROLOGUE; + PARAM_STATE_OPT(state) { state = NULL; } + A_ReFire(self, state); + return 0; +} +void A_ReFire(AActor *self, FState *state) +{ player_t *player = self->player; if (NULL == player) { - return 0; + return; } if ((player->cmd.ucmd.buttons&BT_ATTACK) && !player->ReadyWeapon->bAltFire && player->PendingWeapon == WP_NOCHANGE && player->health) { player->refire++; - P_FireWeapon (player); + P_FireWeapon (player, state); } else if ((player->cmd.ucmd.buttons&BT_ALTATTACK) && player->ReadyWeapon->bAltFire && player->PendingWeapon == WP_NOCHANGE && player->health) { player->refire++; - P_FireWeaponAlt (player); + P_FireWeaponAlt (player, state); } else { @@ -540,7 +559,6 @@ DEFINE_ACTION_FUNCTION(AInventory, A_ReFire) player->ReadyWeapon->CheckAmmo (player->ReadyWeapon->bAltFire ? AWeapon::AltFire : AWeapon::PrimaryFire, true); } - return 0; } DEFINE_ACTION_FUNCTION(AInventory, A_ClearReFire) @@ -678,9 +696,11 @@ DEFINE_ACTION_FUNCTION(AInventory, A_Raise) // // A_GunFlash // -DEFINE_ACTION_FUNCTION(AInventory, A_GunFlash) +DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_GunFlash) { PARAM_ACTION_PROLOGUE; + PARAM_STATE_OPT(flash) { flash = NULL; } + player_t *player = self->player; if (NULL == player) @@ -689,9 +709,17 @@ DEFINE_ACTION_FUNCTION(AInventory, A_GunFlash) } player->mo->PlayAttacking2 (); - FState * flash=NULL; - if (player->ReadyWeapon->bAltFire) flash = player->ReadyWeapon->FindState(NAME_AltFlash); - if (flash == NULL) flash = player->ReadyWeapon->FindState(NAME_Flash); + if (flash == NULL) + { + if (player->ReadyWeapon->bAltFire) + { + flash = player->ReadyWeapon->FindState(NAME_AltFlash); + } + if (flash == NULL) + { + flash = player->ReadyWeapon->FindState(NAME_Flash); + } + } P_SetPsprite (player, ps_flash, flash); return 0; } diff --git a/src/p_pspr.h b/src/p_pspr.h index 849816537..90a57b555 100644 --- a/src/p_pspr.h +++ b/src/p_pspr.h @@ -95,6 +95,6 @@ void DoReadyWeaponToFire(AActor * self, bool primary = true, bool secondary = tr void DoReadyWeaponToSwitch(AActor * self); DECLARE_ACTION(A_Raise) -DECLARE_ACTION(A_ReFire) +void A_ReFire(AActor *self, FState *state = NULL); #endif // __P_PSPR_H__ diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index d9ee189e5..5e9a18f9c 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -244,6 +244,8 @@ static void CopyPlayer (player_t *dst, player_t *src, const char *name) // needs to come from the save for bots. userinfo_t uibackup = dst->userinfo; int chasecam = dst->cheats & CF_CHASECAM; // Remember the chasecam setting + bool attackdown = dst->attackdown; + bool usedown = dst->usedown; *dst = *src; dst->cheats |= chasecam; @@ -270,6 +272,9 @@ static void CopyPlayer (player_t *dst, player_t *src, const char *name) { dst->mo->player = dst; } + // These 2 variables may not be overwritten. + dst->attackdown = attackdown; + dst->usedown = usedown; } static void SpawnExtraPlayers () diff --git a/src/p_setup.cpp b/src/p_setup.cpp index f844b01ea..0afb49be4 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -68,6 +68,7 @@ void P_SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt); void P_SetSlopes (); +void P_CopySlopes(); void BloodCrypt (void *data, int key, int len); void P_ClearUDMFKeys(); @@ -1601,9 +1602,6 @@ void P_SpawnThings (int position) { int numthings = MapThingsConverted.Size(); - // [RH] Spawn slope creating things first. - P_SpawnSlopeMakers (&MapThingsConverted[0], &MapThingsConverted[numthings]); - for (int i=0; i < numthings; i++) { SpawnMapThing (i, &MapThingsConverted[i], position); @@ -1705,7 +1703,11 @@ void P_SetLineID (line_t *ld) case Polyobj_ExplicitLine: ld->id = ld->args[4]; break; - + + case Plane_Align: + ld->id = ld->args[2]; + break; + case Static_Init: if (ld->args[1] == Init_SectorLink) ld->id = ld->args[0]; break; @@ -3416,8 +3418,24 @@ void P_SetupLevel (char *lumpname, int position) else { // We need translators only for Doom format maps. - // If none has been defined in a map use the game's default. - P_LoadTranslator(!level.info->Translator.IsEmpty()? level.info->Translator.GetChars() : gameinfo.translator.GetChars()); + const char *translator; + + if (!level.info->Translator.IsEmpty()) + { + // The map defines its own translator. + translator = level.info->Translator.GetChars(); + } + else + { + // Has the user overridden the game's default translator with a commandline parameter? + translator = Args->CheckValue("-xlat"); + if (translator == NULL) + { + // Use the game's default. + translator = gameinfo.translator.GetChars(); + } + } + P_LoadTranslator(translator); } CheckCompatibility(map); @@ -3559,17 +3577,23 @@ void P_SetupLevel (char *lumpname, int position) } else if (!map->isText) // regular nodes are not supported for text maps { - times[7].Clock(); - P_LoadSubsectors (map); - times[7].Unclock(); + // If all 3 node related lumps are empty there's no need to output a message. + // This just means that the map has no nodes and the engine is supposed to build them. + if (map->Size(ML_SEGS) != 0 || map->Size(ML_SSECTORS) != 0 || map->Size(ML_NODES) != 0) + { + times[7].Clock(); + P_LoadSubsectors (map); + times[7].Unclock(); - times[8].Clock(); - if (!ForceNodeBuild) P_LoadNodes (map); - times[8].Unclock(); + times[8].Clock(); + if (!ForceNodeBuild) P_LoadNodes (map); + times[8].Unclock(); - times[9].Clock(); - if (!ForceNodeBuild) P_LoadSegs (map); - times[9].Unclock(); + times[9].Clock(); + if (!ForceNodeBuild) P_LoadSegs (map); + times[9].Unclock(); + } + else ForceNodeBuild = true; } else ForceNodeBuild = true; } @@ -3577,7 +3601,7 @@ void P_SetupLevel (char *lumpname, int position) { unsigned int startTime, endTime; - startTime = I_MSTime (); + startTime = I_FPSTime (); TArray polyspots, anchors; P_GetPolySpots (map, polyspots, anchors); FNodeBuilder::FLevel leveldata = @@ -3594,7 +3618,7 @@ void P_SetupLevel (char *lumpname, int position) segs, numsegs, subsectors, numsubsectors, vertexes, numvertexes); - endTime = I_MSTime (); + endTime = I_FPSTime (); DPrintf ("BSP generation took %.3f sec (%d segs)\n", (endTime - startTime) * 0.001, numsegs); } @@ -3623,11 +3647,15 @@ void P_SetupLevel (char *lumpname, int position) deathmatchstarts.Clear (); - // Spawn 3d floors - must be done before spawning things so it can't be done in P_SpawnSpecials - P_Spawn3DFloors(); - if (!buildmap) { + // [RH] Spawn slope creating things first. + P_SpawnSlopeMakers (&MapThingsConverted[0], &MapThingsConverted[MapThingsConverted.Size()]); + P_CopySlopes(); + + // Spawn 3d floors - must be done before spawning things so it can't be done in P_SpawnSpecials + P_Spawn3DFloors(); + times[14].Clock(); P_SpawnThings(position); diff --git a/src/p_slopes.cpp b/src/p_slopes.cpp index 0f9098733..3ebaef144 100644 --- a/src/p_slopes.cpp +++ b/src/p_slopes.cpp @@ -118,9 +118,8 @@ static void P_SlopeLineToPoint (int lineid, fixed_t x, fixed_t y, fixed_t z, boo // //=========================================================================== -static void P_CopyPlane (int tag, fixed_t x, fixed_t y, bool copyCeil) +static void P_CopyPlane (int tag, sector_t *dest, bool copyCeil) { - sector_t *dest = P_PointInSector (x, y); sector_t *source; int secnum; size_t planeofs; @@ -144,6 +143,12 @@ static void P_CopyPlane (int tag, fixed_t x, fixed_t y, bool copyCeil) *(secplane_t *)((BYTE *)dest + planeofs) = *(secplane_t *)((BYTE *)source + planeofs); } +static void P_CopyPlane (int tag, fixed_t x, fixed_t y, bool copyCeil) +{ + sector_t *dest = P_PointInSector (x, y); + P_CopyPlane(tag, dest, copyCeil); +} + //=========================================================================== // // P_SetSlope @@ -548,7 +553,6 @@ void P_SetSlopes () if (lines[i].special == Plane_Align) { lines[i].special = 0; - lines[i].id = lines[i].args[2]; if (lines[i].backsector != NULL) { // args[0] is for floor, args[1] is for ceiling @@ -572,3 +576,52 @@ void P_SetSlopes () } } +//=========================================================================== +// +// P_CopySlopes +// +//=========================================================================== + +void P_CopySlopes() +{ + for (int i = 0; i < numlines; i++) + { + if (lines[i].special == Plane_Copy) + { + // The args are used for the tags of sectors to copy: + // args[0]: front floor + // args[1]: front ceiling + // args[2]: back floor + // args[3]: back ceiling + // args[4]: copy slopes from one side of the line to the other. + lines[i].special = 0; + for (int s = 0; s < (lines[i].backsector ? 4 : 2); s++) + { + if (lines[i].args[s]) + P_CopyPlane(lines[i].args[s], + (s & 2 ? lines[i].backsector : lines[i].frontsector), s & 1); + } + + if (lines[i].backsector != NULL) + { + if ((lines[i].args[4] & 3) == 1) + { + lines[i].backsector->floorplane = lines[i].frontsector->floorplane; + } + else if ((lines[i].args[4] & 3) == 2) + { + lines[i].frontsector->floorplane = lines[i].backsector->floorplane; + } + if ((lines[i].args[4] & 12) == 4) + { + lines[i].backsector->ceilingplane = lines[i].frontsector->ceilingplane; + } + else if ((lines[i].args[4] & 12) == 8) + { + lines[i].frontsector->ceilingplane = lines[i].backsector->ceilingplane; + } + } + } + } +} + diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 88dec732e..95bee9eb3 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -59,6 +59,7 @@ #include "statnums.h" #include "g_level.h" #include "v_font.h" +#include "a_sharedglobal.h" // State. #include "r_state.h" @@ -68,6 +69,8 @@ #include "r_interpolate.h" static FRandom pr_playerinspecialsector ("PlayerInSpecialSector"); +void P_SetupPortals(); + // [GrafZahl] Make this message changable by the user! ;) CVAR(String, secretmessage, "A Secret is revealed!", CVAR_ARCHIVE) @@ -293,7 +296,10 @@ bool P_TestActivateLine (line_t *line, AActor *mo, int side, int activationType) } if (activationType == SPAC_Use) { - if (!P_CheckSwitchRange(mo, line, side)) return false; + if (!P_CheckSwitchRange(mo, line, side)) + { + return false; + } } if ((lineActivation & activationType) == 0) @@ -311,7 +317,10 @@ bool P_TestActivateLine (line_t *line, AActor *mo, int side, int activationType) return false; } } - + if (activationType == SPAC_AnyCross && (lineActivation & activationType)) + { + return true; + } if (mo && !mo->player && !(mo->flags & MF_MISSILE) && !(line->flags & ML_MONSTERSCANACTIVATE) && @@ -389,7 +398,6 @@ bool P_TestActivateLine (line_t *line, AActor *mo, int side, int activationType) // void P_PlayerInSpecialSector (player_t *player, sector_t * sector) { - if (sector == NULL) { // Falling, not all the way down yet? @@ -822,6 +830,162 @@ void DWallLightTransfer::DoTransfer (BYTE lightlevel, int target, BYTE flags) } } +//----------------------------------------------------------------------------- +// +// Portals +// +//----------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// Upper stacks go in the top sector. Lower stacks go in the bottom sector. + +static void SetupFloorPortal (AStackPoint *point) +{ + NActorIterator it (NAME_LowerStackLookOnly, point->tid); + sector_t *Sector = point->Sector; + Sector->FloorSkyBox = static_cast(it.Next()); + if (Sector->FloorSkyBox != NULL) + { + Sector->FloorSkyBox->Mate = point; + Sector->FloorSkyBox->PlaneAlpha = Scale (point->args[0], OPAQUE, 255); + } +} + +static void SetupCeilingPortal (AStackPoint *point) +{ + NActorIterator it (NAME_UpperStackLookOnly, point->tid); + sector_t *Sector = point->Sector; + Sector->CeilingSkyBox = static_cast(it.Next()); + if (Sector->CeilingSkyBox != NULL) + { + Sector->CeilingSkyBox->Mate = point; + Sector->CeilingSkyBox->PlaneAlpha = Scale (point->args[0], OPAQUE, 255); + } +} + +void P_SetupPortals() +{ + TThinkerIterator it; + AStackPoint *pt; + TArray points; + + while ((pt = it.Next())) + { + FName nm = pt->GetClass()->TypeName; + if (nm == NAME_UpperStackLookOnly) + { + SetupFloorPortal(pt); + } + else if (nm == NAME_LowerStackLookOnly) + { + SetupCeilingPortal(pt); + } + pt->special1 = 0; + points.Push(pt); + } + + for(unsigned i=0;ispecial1 == 0 && points[i]->Mate != NULL) + { + for(unsigned j=1;jspecial1 == 0 && points[j]->Mate != NULL && points[i]->GetClass() == points[j]->GetClass()) + { + fixed_t deltax1 = points[i]->Mate->x - points[i]->x; + fixed_t deltay1 = points[i]->Mate->y - points[i]->y; + fixed_t deltax2 = points[j]->Mate->x - points[j]->x; + fixed_t deltay2 = points[j]->Mate->y - points[j]->y; + if (deltax1 == deltax2 && deltay1 == deltay2) + { + if (points[j]->Sector->FloorSkyBox == points[j]->Mate) + points[j]->Sector->FloorSkyBox = points[i]->Mate; + + if (points[j]->Sector->CeilingSkyBox == points[j]->Mate) + points[j]->Sector->CeilingSkyBox = points[i]->Mate; + + points[j]->special1 = 1; + } + } + } + } + } +} + +inline void SetPortal(sector_t *sector, int plane, AStackPoint *portal) +{ + // plane: 0=floor, 1=ceiling, 2=both + if (plane > 0) + { + if (sector->CeilingSkyBox == NULL) sector->CeilingSkyBox = portal; + } + if (plane == 2 || plane == 0) + { + if (sector->FloorSkyBox == NULL) sector->FloorSkyBox = portal; + } +} + +void P_SpawnPortal(line_t *line, int sectortag, int plane, int alpha) +{ + for (int i=0;iv1->x + line->v2->x) >> 1; + fixed_t y1 = (line->v1->y + line->v2->y) >> 1; + fixed_t x2 = (lines[i].v1->x + lines[i].v2->x) >> 1; + fixed_t y2 = (lines[i].v1->y + lines[i].v2->y) >> 1; + + AStackPoint *anchor = Spawn(x1, y1, 0, NO_REPLACE); + AStackPoint *reference = Spawn(x2, y2, 0, NO_REPLACE); + + reference->Mate = anchor; + anchor->Mate = reference; + + // This is so that the renderer can distinguish these portals from + // the ones spawned with the '*StackLookOnly' things. + reference->flags |= MF_JUSTATTACKED; + anchor->flags |= MF_JUSTATTACKED; + + for (int s=-1; (s = P_FindSectorFromTag(sectortag,s)) >= 0;) + { + SetPortal(§ors[s], plane, reference); + } + + for (int j=0;j= 0;) + { + SetPortal(§ors[s], plane, reference); + } + } + } + } + + return; + } + } +} + // // P_SpawnSpecials @@ -834,6 +998,8 @@ void P_SpawnSpecials (void) sector_t *sector; int i; + P_SetupPortals(); + // Init special SECTORs. sector = sectors; for (i = 0; i < numsectors; i++, sector++) @@ -1044,6 +1210,22 @@ void P_SpawnSpecials (void) } break; + case Sector_SetPortal: + // arg 0 = sector tag + // arg 1 = type + // - 0: normal (handled here) + // - 1: copy (handled by the portal they copy) + // - 2: EE-style skybox (handled by the camera object) + // other values reserved for later use + // arg 2 = 0:floor, 1:ceiling, 2:both + // arg 3 = 0: anchor, 1: reference line + // arg 4 = for the anchor only: alpha + if (lines[i].args[1] == 0 && lines[i].args[3] == 0) + { + P_SpawnPortal(&lines[i], lines[i].args[0], lines[i].args[2], lines[i].args[4]); + } + break; + // [RH] ZDoom Static_Init settings case Static_Init: switch (lines[i].args[1]) @@ -1332,11 +1514,11 @@ static void P_SpawnScrollers(void) if (special != 0) { int max = LineSpecialsInfo[special] != NULL ? LineSpecialsInfo[special]->map_args : countof(l->args); - for (int arg = max; arg < (int)countof(l->args); ++arg) + for (unsigned arg = max; arg < countof(l->args); ++arg) { if (l->args[arg] != 0) { - Printf("Line %d (type %d:%s), arg %d is %d (should be 0)\n", + Printf("Line %d (type %d:%s), arg %u is %d (should be 0)\n", i, special, LineSpecialsInfo[special]->name, arg+1, l->args[arg]); } } @@ -1882,11 +2064,13 @@ static void P_SpawnPushers () case Sector_SetWind: // wind for (s = -1; (s = P_FindSectorFromTag (l->args[0],s)) >= 0 ; ) new DPusher (DPusher::p_wind, l->args[3] ? l : NULL, l->args[1], l->args[2], NULL, s); + l->special = 0; break; case Sector_SetCurrent: // current for (s = -1; (s = P_FindSectorFromTag (l->args[0],s)) >= 0 ; ) new DPusher (DPusher::p_current, l->args[3] ? l : NULL, l->args[1], l->args[2], NULL, s); + l->special = 0; break; case PointPush_SetForce: // push/pull @@ -1915,6 +2099,7 @@ static void P_SpawnPushers () } } } + l->special = 0; break; } } diff --git a/src/p_spec.h b/src/p_spec.h index 7f2dc4b38..ca0d9492c 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -825,7 +825,7 @@ protected: friend bool EV_DoFloor (DFloor::EFloor floortype, line_t *line, int tag, fixed_t speed, fixed_t height, int crush, int change, bool hexencrush); friend bool EV_FloorCrushStop (int tag); - friend bool EV_DoDonut (int tag, fixed_t pillarspeed, fixed_t slimespeed); + friend bool EV_DoDonut (int tag, line_t *line, fixed_t pillarspeed, fixed_t slimespeed); private: DFloor (); }; @@ -836,7 +836,7 @@ bool EV_BuildStairs (int tag, DFloor::EStair type, line_t *line, bool EV_DoFloor (DFloor::EFloor floortype, line_t *line, int tag, fixed_t speed, fixed_t height, int crush, int change, bool hexencrush); bool EV_FloorCrushStop (int tag); -bool EV_DoDonut (int tag, fixed_t pillarspeed, fixed_t slimespeed); +bool EV_DoDonut (int tag, line_t *line, fixed_t pillarspeed, fixed_t slimespeed); inline FArchive &operator<< (FArchive &arc, DFloor::EFloor &type) { @@ -915,7 +915,7 @@ protected: int m_State; TObjPtr m_Interpolation; - friend bool EV_StartWaggle (int tag, int height, int speed, + friend bool EV_StartWaggle (int tag, line_t *line, int height, int speed, int offset, int timer, bool ceiling); void DoWaggle (bool ceiling); @@ -923,7 +923,7 @@ protected: DWaggleBase (); }; -bool EV_StartWaggle (int tag, int height, int speed, +bool EV_StartWaggle (int tag, line_t *line, int height, int speed, int offset, int timer, bool ceiling); class DFloorWaggle : public DWaggleBase @@ -981,6 +981,6 @@ void P_DoDeferedScripts (void); // // [RH] p_quake.c // -bool P_StartQuake (AActor *activator, int tid, int intensity, int duration, int damrad, int tremrad); +bool P_StartQuake (AActor *activator, int tid, int intensity, int duration, int damrad, int tremrad, FSoundID quakesfx); #endif diff --git a/src/p_switch.cpp b/src/p_switch.cpp index 4e312a166..b7eeb784d 100644 --- a/src/p_switch.cpp +++ b/src/p_switch.cpp @@ -274,11 +274,11 @@ void P_ProcessSwitchDef (FScanner &sc) { if (def2 != NULL) { - free (def2); + M_Free (def2); } if (def1 != NULL) { - free (def1); + M_Free (def1); } return; } @@ -466,7 +466,8 @@ bool P_CheckSwitchRange(AActor *user, line_t *line, int sideno) { // Activated from an empty side -> always succeed side_t *side = line->sidedef[sideno]; - if (side == NULL) return true; + if (side == NULL) + return true; fixed_t checktop; fixed_t checkbot; @@ -474,7 +475,8 @@ bool P_CheckSwitchRange(AActor *user, line_t *line, int sideno) FLineOpening open; // 3DMIDTEX forces CHECKSWITCHRANGE because otherwise it might cause problems. - if (!(line->flags & (ML_3DMIDTEX|ML_CHECKSWITCHRANGE))) return true; + if (!(line->flags & (ML_3DMIDTEX|ML_CHECKSWITCHRANGE))) + return true; // calculate the point where the user would touch the wall. divline_t dll, dlu; @@ -488,11 +490,25 @@ bool P_CheckSwitchRange(AActor *user, line_t *line, int sideno) dlu.dy = finesine[user->angle >> ANGLETOFINESHIFT]; inter = P_InterceptVector(&dll, &dlu); - checkx = dll.x + FixedMul(dll.dx, inter); - checky = dll.y + FixedMul(dll.dy, inter); - // one sided line - if (line->sidedef[1] == NULL) + // Polyobjects must test the containing sector, not the one they originate from. + if (line->sidedef[0]->Flags & WALLF_POLYOBJ) + { + // Get a check point slightly inside the polyobject so that this still works + // if the polyobject lies directly on a sector boundary + checkx = dll.x + FixedMul(dll.dx, inter + (FRACUNIT/100)); + checky = dll.y + FixedMul(dll.dy, inter + (FRACUNIT/100)); + front = P_PointInSector(checkx, checky); + } + else + { + checkx = dll.x + FixedMul(dll.dx, inter); + checky = dll.y + FixedMul(dll.dy, inter); + } + + + // one sided line or polyobject + if (line->sidedef[1] == NULL || (line->sidedef[0]->Flags & WALLF_POLYOBJ)) { onesided: fixed_t sectorc = front->ceilingplane.ZatPoint(checkx, checky); @@ -502,7 +518,8 @@ bool P_CheckSwitchRange(AActor *user, line_t *line, int sideno) // Now get the information from the line. P_LineOpening(open, NULL, line, checkx, checky, user->x, user->y); - if (open.range <= 0) goto onesided; + if (open.range <= 0) + goto onesided; if ((TryFindSwitch (side, side_t::top)) != -1) { @@ -516,8 +533,9 @@ bool P_CheckSwitchRange(AActor *user, line_t *line, int sideno) { // 3DMIDTEX lines will force a mid texture check if no switch is found on this line // to keep compatibility with Eternity's implementation. - if (!P_GetMidTexturePosition(line, sideno, &checktop, &checkbot)) return false; - return user->z < checktop || user->z + user->height > checkbot; + if (!P_GetMidTexturePosition(line, sideno, &checktop, &checkbot)) + return false; + return user->z < checktop && user->z + user->height > checkbot; } else { diff --git a/src/p_terrain.cpp b/src/p_terrain.cpp index dbb187530..a2750fbea 100644 --- a/src/p_terrain.cpp +++ b/src/p_terrain.cpp @@ -63,7 +63,8 @@ enum EOuterKeywords OUT_IFHERETIC, OUT_IFHEXEN, OUT_IFSTRIFE, - OUT_ENDIF + OUT_ENDIF, + OUT_DEFAULTTERRAIN }; enum ETerrainKeywords @@ -125,6 +126,7 @@ static void GenericParse (FScanner &sc, FGenericParse *parser, const char **keyw void *fields, const char *type, FName name); static void ParseDamage (FScanner &sc, int keyword, void *fields); static void ParseFriction (FScanner &sc, int keyword, void *fields); +static void ParseDefault (FScanner &sc); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- @@ -133,6 +135,7 @@ static void ParseFriction (FScanner &sc, int keyword, void *fields); FTerrainTypeArray TerrainTypes; TArray Splashes; TArray Terrains; +WORD DefaultTerrainType; // PRIVATE DATA DEFINITIONS ------------------------------------------------ @@ -146,6 +149,7 @@ static const char *OuterKeywords[] = "ifhexen", "ifstrife", "endif", + "defaultterrain", NULL }; @@ -222,27 +226,7 @@ static FGenericParse TerrainParser[] = { GEN_Bool, {theoffsetof(FTerrainDef, AllowProtection)} }, }; -/* -struct -{ - char *name; - int type; - bool Heretic; -} - TerrainTypeDefs[] = -{ - { "FLTWAWA1", FLOOR_WATER, true }, - { "FLTFLWW1", FLOOR_WATER, true }, - { "FLTLAVA1", FLOOR_LAVA, true }, - { "FLATHUH1", FLOOR_LAVA, true }, - { "FLTSLUD1", FLOOR_SLUDGE, true }, - { "X_005", FLOOR_WATER, false }, - { "X_001", FLOOR_LAVA, false }, - { "X_009", FLOOR_SLUDGE, false }, - { "F_033", FLOOR_ICE, false }, - { "END", -1 } -}; -*/ + // CODE -------------------------------------------------------------------- @@ -258,9 +242,9 @@ void P_InitTerrainTypes () int lump; int size; - size = (TexMan.NumTextures()+1)*sizeof(BYTE); + size = (TexMan.NumTextures()+1); TerrainTypes.Resize(size); - memset (&TerrainTypes[0], 0, size); + TerrainTypes.Clear(); MakeDefaultTerrain (); @@ -343,6 +327,10 @@ static void ParseOuter (FScanner &sc) ParseFloor (sc); break; + case OUT_DEFAULTTERRAIN: + ParseDefault (sc); + break; + case OUT_IFDOOM: if (!(gameinfo.gametype & GAME_DoomChex)) { @@ -676,7 +664,27 @@ static void ParseFloor (FScanner &sc) Printf ("Unknown terrain %s\n", sc.String); terrain = 0; } - TerrainTypes[picnum] = terrain; + TerrainTypes.Set(picnum.GetIndex(), terrain); +} + +//========================================================================== +// +// ParseFloor +// +//========================================================================== + +static void ParseDefault (FScanner &sc) +{ + int terrain; + + sc.MustGetString (); + terrain = FindTerrain (sc.String); + if (terrain == -1) + { + Printf ("Unknown terrain %s\n", sc.String); + terrain = 0; + } + DefaultTerrainType = terrain; } //========================================================================== diff --git a/src/p_terrain.h b/src/p_terrain.h index e5a98ccf8..9c28b3e90 100644 --- a/src/p_terrain.h +++ b/src/p_terrain.h @@ -39,25 +39,36 @@ class PClass; -// This is just a wrapper class so that I don't have to expose FTextureID's implementation -// to anything that doesn't really need it. +extern WORD DefaultTerrainType; + + class FTerrainTypeArray { public: - TArray Types; + TArray Types; - BYTE &operator [](FTextureID tex) + WORD operator [](FTextureID tex) const { - return Types[tex.GetIndex()]; + WORD type = Types[tex.GetIndex()]; + return type == 0xffff? DefaultTerrainType : type; } - BYTE &operator [](int texnum) + WORD operator [](int texnum) const { - return Types[texnum]; + WORD type = Types[texnum]; + return type == 0xffff? DefaultTerrainType : type; } void Resize(unsigned newsize) { Types.Resize(newsize); } + void Clear() + { + memset (&Types[0], 0xff, Types.Size()*sizeof(WORD)); + } + void Set(int index, int value) + { + Types[index] = value; + } }; extern FTerrainTypeArray TerrainTypes; diff --git a/src/p_things.cpp b/src/p_things.cpp index 0b166cdc3..cd1540f4f 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -142,9 +142,9 @@ bool P_MoveThing(AActor *source, fixed_t x, fixed_t y, fixed_t z, bool fog) Spawn (x, y, z + TELEFOGHEIGHT, ALLOW_REPLACE); Spawn (oldx, oldy, oldz + TELEFOGHEIGHT, ALLOW_REPLACE); } - source->PrevX=x; - source->PrevY=y; - source->PrevZ=z; + source->PrevX = x; + source->PrevY = y; + source->PrevZ = z; return true; } else @@ -466,6 +466,8 @@ bool P_Thing_Raise(AActor *thing) thing->flags2 = info->flags2; thing->flags3 = info->flags3; thing->flags4 = info->flags4; + thing->flags5 = info->flags5; + thing->flags6 = info->flags6; thing->health = info->health; thing->target = NULL; thing->lastenemy = NULL; diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 79b64a1de..5dfa06dd7 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -691,7 +691,7 @@ struct UDMFParser Flag(ld->flags, ML_RAILING, key); continue; - case NAME_Blockfloating: + case NAME_Blockfloaters: CHECK_N(St | Zd | Zdt | Va) Flag(ld->flags, ML_BLOCK_FLOATERS, key); continue; diff --git a/src/p_user.cpp b/src/p_user.cpp index a99a0d4b5..fe1aab08c 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -222,6 +222,7 @@ player_t::player_t() centering(0), turnticks(0), attackdown(0), + usedown(0), oldbuttons(0), health(0), inventorytics(0), @@ -614,6 +615,12 @@ bool APlayerPawn::UseInventory (AInventory *item) { // You can't use items if you're totally frozen return false; } + if (( level.flags2 & LEVEL2_FROZEN ) && ( player == NULL || !( player->cheats & CF_TIMEFREEZE ))) + { + // Time frozen + return false; + } + if (!Super::UseInventory (item)) { // Heretic and Hexen advance the inventory cursor if the use failed. @@ -1400,10 +1407,12 @@ void P_CheckPlayerSprites() { int crouchspriteno; fixed_t defscaleY = mo->GetDefault()->scaleY; + fixed_t defscaleX = mo->GetDefault()->scaleX; - if (player->userinfo.skin != 0) + if (player->userinfo.skin != 0 && !(player->mo->flags4 & MF4_NOSKIN)) { defscaleY = skins[player->userinfo.skin].ScaleY; + defscaleX = skins[player->userinfo.skin].ScaleX; } // Set the crouch sprite @@ -1414,8 +1423,9 @@ void P_CheckPlayerSprites() { crouchspriteno = mo->crouchsprite; } - else if (mo->sprite == skins[player->userinfo.skin].sprite || - mo->sprite == skins[player->userinfo.skin].crouchsprite) + else if (!(player->mo->flags4 & MF4_NOSKIN) && + (mo->sprite == skins[player->userinfo.skin].sprite || + mo->sprite == skins[player->userinfo.skin].crouchsprite)) { crouchspriteno = skins[player->userinfo.skin].crouchsprite; } @@ -1447,6 +1457,7 @@ void P_CheckPlayerSprites() } mo->scaleY = defscaleY; } + mo->scaleX = defscaleX; } } } @@ -2060,8 +2071,12 @@ void P_PlayerThink (player_t *player) player->mo->flags &= ~MF_JUSTATTACKED; } + bool totallyfrozen = (player->cheats & CF_TOTALLYFROZEN || gamestate == GS_TITLELEVEL || + (( level.flags2 & LEVEL2_FROZEN ) && ( player == NULL || !( player->cheats & CF_TIMEFREEZE ))) + ); + // [RH] Being totally frozen zeros out most input parameters. - if (player->cheats & CF_TOTALLYFROZEN || gamestate == GS_TITLELEVEL) + if (totallyfrozen) { if (gamestate == GS_TITLELEVEL) { @@ -2093,7 +2108,7 @@ void P_PlayerThink (player_t *player) } if (player->morphTics == 0 && player->health > 0 && level.IsCrouchingAllowed()) { - if (!(player->cheats & CF_TOTALLYFROZEN)) + if (!totallyfrozen) { int crouchdir = player->crouching; @@ -2163,13 +2178,11 @@ void P_PlayerThink (player_t *player) player->mo->pitch -= look; if (look > 0) { // look up - if (player->mo->pitch < -ANGLE_1*MAX_UP_ANGLE) - player->mo->pitch = -ANGLE_1*MAX_UP_ANGLE; + player->mo->pitch = MAX(player->mo->pitch, screen->GetMaxViewPitch(false)); } else { // look down - if (player->mo->pitch > ANGLE_1*MAX_DN_ANGLE) - player->mo->pitch = ANGLE_1*MAX_DN_ANGLE; + player->mo->pitch = MIN(player->mo->pitch, screen->GetMaxViewPitch(true)); } } } @@ -2282,9 +2295,20 @@ void P_PlayerThink (player_t *player) } } // check for use - if ((cmd->ucmd.buttons & BT_USE) && !(player->oldbuttons & BT_USE)) + if (cmd->ucmd.buttons & BT_USE) { - P_UseLines (player); + if (!player->usedown) + { + player->usedown = true; + if (!P_TalkFacing(player->mo)) + { + P_UseLines(player); + } + } + } + else + { + player->usedown = false; } // Morph counter if (player->morphTics) @@ -2295,7 +2319,7 @@ void P_PlayerThink (player_t *player) } if (!--player->morphTics) { // Attempt to undo the chicken/pig - P_UndoPlayerMorph (player, player); + P_UndoPlayerMorph (player, player, MORPH_UNDOBYTIMEOUT); } } // Cycle psprites @@ -2521,19 +2545,25 @@ void player_t::Serialize (FArchive &arc) int fixedmap; arc << fixedmap; fixedcolormap = NOFIXEDCOLORMAP; - fixedlightlev = -1; + fixedlightlevel = -1; if (fixedmap >= NUMCOLORMAPS) { fixedcolormap = fixedmap - NUMCOLORMAPS; } else if (fixedmap > 0) { - fixedlightlev = fixedmap; + fixedlightlevel = fixedmap; } } + else if (SaveVersion < 1893) + { + int ll; + arc << fixedcolormap << ll; + fixedlightlevel = ll; + } else { - arc << fixedcolormap << fixedlightlev; + arc << fixedcolormap << fixedlightlevel; } arc << morphTics << MorphedPlayerClass diff --git a/src/p_xlat.cpp b/src/p_xlat.cpp index da5695ece..9e87d13ba 100644 --- a/src/p_xlat.cpp +++ b/src/p_xlat.cpp @@ -66,40 +66,37 @@ void P_TranslateLineDef (line_t *ld, maplinedef_t *mld) unsigned short special = (unsigned short) LittleShort(mld->special); short tag = LittleShort(mld->tag); DWORD flags = LittleShort(mld->flags); - INTBOOL passthrough; + INTBOOL passthrough = 0; - if (flags & ML_TRANSLUCENT_STRIFE) + DWORD flags1 = flags; + DWORD newflags = 0; + + for(int i=0;i<16;i++) { - ld->Alpha = FRACUNIT*3/4; - } - if (gameinfo.gametype == GAME_Strife) - { - if (flags & ML_RAILING_STRIFE) + if ((flags & (1<Alpha = FRACUNIT*3/4; + break; + default: + newflags |= LineFlagTranslations[i].newvalue; + break; } } - if (flags & ML_3DMIDTEX_ETERNITY) - { - flags |= ML_3DMIDTEX; - } - passthrough = (flags & ML_PASSUSE_BOOM); } - flags = flags & 0xFFFF01FF; // Ignore flags unknown to DOOM + flags = newflags; // For purposes of maintaining BOOM compatibility, each // line also needs to have its ID set to the same as its tag. diff --git a/src/po_man.cpp b/src/po_man.cpp index d88c67589..8ed58cf8c 100644 --- a/src/po_man.cpp +++ b/src/po_man.cpp @@ -120,7 +120,6 @@ void PO_Init (void); // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- -static FPolyObj *GetPolyobj (int polyNum); static int GetPolyobjMirror (int poly); static void UpdateSegBBox (seg_t *seg); static void RotatePt (int an, fixed_t *x, fixed_t *y, fixed_t startSpotX, @@ -184,7 +183,7 @@ DPolyAction::DPolyAction (int polyNum) void DPolyAction::Destroy() { - FPolyObj *poly = GetPolyobj (m_PolyObj); + FPolyObj *poly = PO_GetPolyobj (m_PolyObj); if (poly->specialdata == NULL || poly->specialdata == this) { @@ -197,7 +196,7 @@ void DPolyAction::Destroy() void DPolyAction::SetInterpolation () { - FPolyObj *poly = GetPolyobj (m_PolyObj); + FPolyObj *poly = PO_GetPolyobj (m_PolyObj); m_Interpolation = poly->SetInterpolation(); } @@ -278,7 +277,7 @@ void DRotatePoly::Tick () m_Dist -= absSpeed; if (m_Dist == 0) { - FPolyObj *poly = GetPolyobj (m_PolyObj); + FPolyObj *poly = PO_GetPolyobj (m_PolyObj); if (poly->specialdata == this) { poly->specialdata = NULL; @@ -307,7 +306,7 @@ bool EV_RotatePoly (line_t *line, int polyNum, int speed, int byteAngle, DRotatePoly *pe; FPolyObj *poly; - if ( (poly = GetPolyobj(polyNum)) ) + if ( (poly = PO_GetPolyobj(polyNum)) ) { if (poly->specialdata && !overRide) { // poly is already moving @@ -338,9 +337,9 @@ bool EV_RotatePoly (line_t *line, int polyNum, int speed, int byteAngle, poly->specialdata = pe; SN_StartSequence (poly, poly->seqType, SEQ_DOOR, 0); - while ( (mirror = GetPolyobjMirror( polyNum)) ) + while ( (mirror = GetPolyobjMirror(polyNum)) ) { - poly = GetPolyobj(mirror); + poly = PO_GetPolyobj(mirror); if (poly == NULL) { I_Error ("EV_RotatePoly: Invalid polyobj num: %d\n", polyNum); @@ -390,7 +389,7 @@ void DMovePoly::Tick () m_Dist -= absSpeed; if (m_Dist <= 0) { - poly = GetPolyobj (m_PolyObj); + poly = PO_GetPolyobj (m_PolyObj); if (poly->specialdata == this) { poly->specialdata = NULL; @@ -421,7 +420,7 @@ bool EV_MovePoly (line_t *line, int polyNum, int speed, angle_t angle, FPolyObj *poly; angle_t an; - if ( (poly = GetPolyobj(polyNum)) ) + if ( (poly = PO_GetPolyobj(polyNum)) ) { if (poly->specialdata && !overRide) { // poly is already moving @@ -455,7 +454,7 @@ bool EV_MovePoly (line_t *line, int polyNum, int speed, angle_t angle, while ( (mirror = GetPolyobjMirror(polyNum)) ) { - poly = GetPolyobj(mirror); + poly = PO_GetPolyobj(mirror); if (poly && poly->specialdata && !overRide) { // mirroring poly is already in motion break; @@ -493,7 +492,7 @@ void DPolyDoor::Tick () { if (!--m_Tics) { - poly = GetPolyobj (m_PolyObj); + poly = PO_GetPolyobj (m_PolyObj); SN_StartSequence (poly, poly->seqType, SEQ_DOOR, m_Close); } return; @@ -507,7 +506,7 @@ void DPolyDoor::Tick () m_Dist -= absSpeed; if (m_Dist <= 0) { - poly = GetPolyobj (m_PolyObj); + poly = PO_GetPolyobj (m_PolyObj); SN_StopSequence (poly); if (!m_Close) { @@ -531,7 +530,7 @@ void DPolyDoor::Tick () } else { - poly = GetPolyobj (m_PolyObj); + poly = PO_GetPolyobj (m_PolyObj); if (poly->crush || !m_Close) { // continue moving if the poly is a crusher, or is opening return; @@ -560,7 +559,7 @@ void DPolyDoor::Tick () m_Dist -= absSpeed; if (m_Dist <= 0) { - poly = GetPolyobj (m_PolyObj); + poly = PO_GetPolyobj (m_PolyObj); SN_StopSequence (poly); if (!m_Close) { @@ -581,7 +580,7 @@ void DPolyDoor::Tick () } else { - poly = GetPolyobj (m_PolyObj); + poly = PO_GetPolyobj (m_PolyObj); if(poly->crush || !m_Close) { // continue moving if the poly is a crusher, or is opening return; @@ -614,7 +613,7 @@ bool EV_OpenPolyDoor (line_t *line, int polyNum, int speed, angle_t angle, DPolyDoor *pd; FPolyObj *poly; - if( (poly = GetPolyobj(polyNum)) ) + if( (poly = PO_GetPolyobj(polyNum)) ) { if (poly->specialdata) { // poly is already moving @@ -649,7 +648,7 @@ bool EV_OpenPolyDoor (line_t *line, int polyNum, int speed, angle_t angle, while ( (mirror = GetPolyobjMirror (polyNum)) ) { - poly = GetPolyobj (mirror); + poly = PO_GetPolyobj (mirror); if (poly && poly->specialdata) { // mirroring poly is already in motion break; @@ -683,11 +682,11 @@ bool EV_OpenPolyDoor (line_t *line, int polyNum, int speed, angle_t angle, //========================================================================== // -// GetPolyobj +// PO_GetPolyobj // //========================================================================== -static FPolyObj *GetPolyobj (int polyNum) +FPolyObj *PO_GetPolyobj (int polyNum) { int i; @@ -844,7 +843,7 @@ bool PO_MovePolyobj (int num, int x, int y, bool force) { FPolyObj *po; - if (!(po = GetPolyobj (num))) + if (!(po = PO_GetPolyobj (num))) { I_Error ("PO_MovePolyobj: Invalid polyobj number: %d\n", num); } @@ -954,7 +953,7 @@ bool PO_RotatePolyobj (int num, angle_t angle) FPolyObj *po; bool blocked; - if(!(po = GetPolyobj(num))) + if(!(po = PO_GetPolyobj(num))) { I_Error("PO_RotatePolyobj: Invalid polyobj number: %d\n", num); } @@ -1546,6 +1545,8 @@ static void TranslateToStartSpot (int tag, int originX, int originY) validcount++; for (i = 0; i < po->numsegs; i++, tempSeg++, tempPt++) { + (*tempSeg)->bPolySeg = true; // this is not set for all segs + (*tempSeg)->sidedef->Flags |= WALLF_POLYOBJ; if ((*tempSeg)->linedef->validcount != validcount) { (*tempSeg)->linedef->bbox[BOXTOP] -= deltaY; @@ -1566,16 +1567,17 @@ static void TranslateToStartSpot (int tag, int originX, int originY) (*tempSeg)->v1->x -= deltaX; (*tempSeg)->v1->y -= deltaY; } - avg.x += (*tempSeg)->v1->x>>FRACBITS; - avg.y += (*tempSeg)->v1->y>>FRACBITS; + avg.x += (*tempSeg)->v1->x >> FRACBITS; + avg.y += (*tempSeg)->v1->y >> FRACBITS; // the original Pts are based off the startSpot Pt, and are // unique to each seg, not each linedef tempPt->x = (*tempSeg)->v1->x-po->startSpot[0]; tempPt->y = (*tempSeg)->v1->y-po->startSpot[1]; } + // Put polyobj in its subsector. avg.x /= po->numsegs; avg.y /= po->numsegs; - sub = R_PointInSubsector (avg.x<poly != NULL) { I_Error ("PO_TranslateToStartSpot: Multiple polyobjs in a single subsector.\n"); @@ -1662,7 +1664,7 @@ bool PO_Busy (int polyobj) { FPolyObj *poly; - poly = GetPolyobj (polyobj); + poly = PO_GetPolyobj (polyobj); if (poly == NULL || poly->specialdata == NULL) { return false; diff --git a/src/r_anim.cpp b/src/r_anim.cpp index 22c85c50d..7daeafd83 100644 --- a/src/r_anim.cpp +++ b/src/r_anim.cpp @@ -179,7 +179,7 @@ void R_InitPicAnims (void) if (pic1 == pic2) { // This animation only has one frame. Skip it. (Doom aborted instead.) - Printf ("Animation %s in ANIMATED has only one frame", anim_p + 10); + Printf ("Animation %s in ANIMATED has only one frame\n", anim_p + 10); continue; } @@ -405,6 +405,17 @@ static void R_InitAnimDefs () { P_ParseAnimatedDoor (sc); } + else if (sc.Compare("skyoffset")) + { + sc.MustGetString (); + FTextureID picnum = TexMan.CheckForTexture (sc.String, FTexture::TEX_Wall, texflags); + sc.MustGetNumber(); + if (picnum.Exists()) + { + FTexture *tex = TexMan[picnum]; + tex->SkyOffset = sc.Number; + } + } else { sc.ScriptError (NULL); @@ -834,6 +845,6 @@ void R_UpdateAnimations (DWORD mstime) // Scroll the sky double ms = (double)mstime * FRACUNIT; - sky1pos = fixed_t(fmod (ms * level.skyspeed1, double(TexMan[sky1texture]->GetWidth() << FRACBITS))); - sky2pos = fixed_t(fmod (ms * level.skyspeed2, double(TexMan[sky2texture]->GetWidth() << FRACBITS))); + sky1pos = ms * level.skyspeed1; + sky2pos = ms * level.skyspeed2; } diff --git a/src/r_data.h b/src/r_data.h index 65a7f9740..487860e58 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -49,7 +49,7 @@ public: FWarpTexture (FTexture *source); ~FWarpTexture (); - virtual int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w=-1, int h=-1, int rotate=0, FCopyInfo *inf = NULL); + virtual int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate=0, FCopyInfo *inf = NULL); const BYTE *GetColumn (unsigned int column, const Span **spans_out); const BYTE *GetPixels (); void Unload (); @@ -93,7 +93,6 @@ public: void Unload (); bool CheckModified (); void RenderView (AActor *viewpoint, int fov); - void RenderGLView(AActor *viewpoint, int fov); void NeedUpdate() { bNeedsUpdate=true; } protected: diff --git a/src/r_defs.h b/src/r_defs.h index 88f4e98a8..f19656afa 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -588,7 +588,7 @@ struct sector_t sector_t *GetHeightSec() const { - return (MoreFlags & SECF_IGNOREHEIGHTSEC)? NULL : heightsec; + return (heightsec && !(heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC))? heightsec : NULL; } void ChangeLightLevel(int newval) @@ -606,6 +606,11 @@ struct sector_t return lightlevel; } + secplane_t &GetSecPlane(int pos) + { + return pos == floor? floorplane:ceilingplane; + } + bool PlaneMoving(int pos); @@ -725,6 +730,7 @@ enum WALLF_SMOOTHLIGHTING = 8, // Similar to autocontrast but applies to all angles. WALLF_CLIP_MIDTEX = 16, // Like the line counterpart, but only for this side. WALLF_WRAP_MIDTEX = 32, // Like the line counterpart, but only for this side. + WALLF_POLYOBJ = 64, // This wall belongs to a polyobject. }; struct side_t @@ -871,7 +877,7 @@ struct line_t DWORD flags; DWORD activation; // activation type int special; - fixed_t Alpha; // <--- translucency (0-255/255=opaque) + fixed_t Alpha; // <--- translucency (0=invisibile, FRACUNIT=opaque) int id; // <--- same as tag or set with Line_SetIdentification int args[5]; // <--- hexen-style arguments (expanded to ZDoom's full width) int firstid, nextid; diff --git a/src/r_main.cpp b/src/r_main.cpp index e5caada3d..789388842 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -264,58 +264,69 @@ angle_t R_PointToAngle2 (fixed_t x1, fixed_t y1, fixed_t x, fixed_t y) return 0; } - if (x >= 0) + // We need to be aware of overflows here. If the values get larger than INT_MAX/4 + // this code won't work anymore. + + if (x < INT_MAX/4 && x > -INT_MAX/4 && y < INT_MAX/4 && y > -INT_MAX/4) { - if (y >= 0) + if (x >= 0) { - if (x > y) - { // octant 0 - return SlopeDiv(y, x); + if (y >= 0) + { + if (x > y) + { // octant 0 + return SlopeDiv(y, x); + } + else + { // octant 1 + return ANG90 - 1 - SlopeDiv(x, y); + } } - else - { // octant 1 - return ANG90 - 1 - SlopeDiv(x, y); + else // y < 0 + { + y = -y; + if (x > y) + { // octant 8 + return 0 - SlopeDiv(y, x); + } + else + { // octant 7 + return ANG270 + SlopeDiv(x, y); + } } } - else // y < 0 + else // x < 0 { - y = -y; - if (x > y) - { // octant 8 - return 0 - SlopeDiv(y, x); + x = -x; + if (y >= 0) + { + if (x > y) + { // octant 3 + return ANG180 - 1 - SlopeDiv(y, x); + } + else + { // octant 2 + return ANG90 + SlopeDiv(x, y); + } } - else - { // octant 7 - return ANG270 + SlopeDiv(x, y); + else // y < 0 + { + y = -y; + if (x > y) + { // octant 4 + return ANG180 + SlopeDiv(y, x); + } + else + { // octant 5 + return ANG270 - 1 - SlopeDiv(x, y); + } } } } - else // x < 0 + else { - x = -x; - if (y >= 0) - { - if (x > y) - { // octant 3 - return ANG180 - 1 - SlopeDiv(y, x); - } - else - { // octant 2 - return ANG90 + SlopeDiv(x, y); - } - } - else // y < 0 - { - y = -y; - if (x > y) - { // octant 4 - return ANG180 + SlopeDiv(y, x); - } - else - { // octant 5 - return ANG270 - 1 - SlopeDiv(x, y); - } - } + // we have to use the slower but more precise floating point atan2 function here. + return xs_RoundToUInt(atan2(double(y), double(x)) * (ANGLE_180/M_PI)); } } @@ -521,7 +532,7 @@ void R_SetVisibility (float vis) return; } - r_BaseVisibility = toint (vis * 65536.f); + r_BaseVisibility = xs_RoundToInt(vis * 65536.f); // Prevent overflow on walls if (r_BaseVisibility < 0 && r_BaseVisibility < -MaxVisForWall) @@ -852,11 +863,11 @@ void R_InterpolateView (player_t *player, fixed_t frac, InterpolationViewer *ivi // Avoid overflowing viewpitch (can happen when a netgame is stalled) if (viewpitch + delta <= viewpitch) { - viewpitch = +ANGLE_1*MAX_DN_ANGLE; + viewpitch = screen->GetMaxViewPitch(true); } else { - viewpitch = MIN(viewpitch + delta, +ANGLE_1*MAX_DN_ANGLE); + viewpitch = MIN(viewpitch + delta, screen->GetMaxViewPitch(true)); } } else if (delta < 0) @@ -864,11 +875,11 @@ void R_InterpolateView (player_t *player, fixed_t frac, InterpolationViewer *ivi // Avoid overflowing viewpitch (can happen when a netgame is stalled) if (viewpitch + delta >= viewpitch) { - viewpitch = -ANGLE_1*MAX_UP_ANGLE; + viewpitch = screen->GetMaxViewPitch(false); } else { - viewpitch = MAX(viewpitch + delta, -ANGLE_1*MAX_UP_ANGLE); + viewpitch = MAX(viewpitch + delta, screen->GetMaxViewPitch(false)); } } } diff --git a/src/r_plane.cpp b/src/r_plane.cpp index 69af40223..aae870fbe 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -336,7 +336,7 @@ void R_MapTiltedPlane (int y, int x1) { uz = (iz + plane_sz[0]*width) * planelightfloat; vz = iz * planelightfloat; - R_CalcTiltedLighting (toint (vz), toint (uz), width); + R_CalcTiltedLighting (xs_RoundToInt(vz), xs_RoundToInt(uz), width); } uz = plane_su[2] + plane_su[1]*(centery-y) + plane_su[0]*(x1-centerx); @@ -745,8 +745,10 @@ inline void R_MakeSpans (int x, int t1, int b1, int t2, int b2, void (*mapfunc)( static FTexture *frontskytex, *backskytex; static angle_t skyflip; static int frontpos, backpos; -static fixed_t frontxScale, backxScale; static fixed_t frontyScale; +static fixed_t frontcyl, backcyl; +static fixed_t skymid; +static angle_t skyangle; int frontiScale; extern fixed_t swall[MAXWIDTH]; @@ -764,17 +766,16 @@ static int skycolplace; // Get a column of sky when there is only one sky texture. static const BYTE *R_GetOneSkyColumn (FTexture *fronttex, int x) { - angle_t column = MulScale16 (frontxScale, viewangle + xtoviewangle[x]); - - return fronttex->GetColumn ((((column^skyflip) >> sky1shift) + frontpos) >> FRACBITS, NULL); + angle_t column = (skyangle + xtoviewangle[x]) ^ skyflip; + return fronttex->GetColumn((UMulScale16(column, frontcyl) + frontpos) >> FRACBITS, NULL); } // Get a column of sky when there are two overlapping sky textures static const BYTE *R_GetTwoSkyColumns (FTexture *fronttex, int x) { - DWORD ang = (viewangle + xtoviewangle[x])^skyflip; - DWORD angle1 = (((DWORD)MulScale16 (frontxScale, ang) >> sky1shift) + frontpos) >> FRACBITS; - DWORD angle2 = (((DWORD)MulScale16 (backxScale, ang) >> sky2shift) + backpos) >> FRACBITS; + DWORD ang = (skyangle + xtoviewangle[x]) ^ skyflip; + DWORD angle1 = (DWORD)((UMulScale16(ang, frontcyl) + frontpos) >> FRACBITS); + DWORD angle2 = (DWORD)((UMulScale16(ang, backcyl) + backpos) >> FRACBITS); // Check if this column has already been built. If so, there's // no reason to waste time building it again. @@ -822,10 +823,9 @@ static void R_DrawSky (visplane_t *pl) if (pl->minx > pl->maxx) return; - dc_iscale = skyiscale >> skystretch; + dc_iscale = skyiscale; clearbuf (swall+pl->minx, pl->maxx-pl->minx+1, dc_iscale<<2); - rw_offset = frontpos; if (MirrorFlags & RF_XFLIP) { @@ -850,14 +850,8 @@ static void R_DrawSky (visplane_t *pl) rw_pic = frontskytex; rw_offset = 0; - frontxScale = rw_pic->xScale; - if (backskytex != NULL) - { - backxScale = backskytex->xScale; - } - frontyScale = rw_pic->yScale; - dc_texturemid = MulScale16 (skytexturemid/*-viewz*/, frontyScale); + dc_texturemid = MulScale16 (skymid, frontyScale); if (1 << frontskytex->HeightBits == frontskytex->GetHeight()) { // The texture tiles nicely @@ -870,8 +864,16 @@ static void R_DrawSky (visplane_t *pl) } else { // The texture does not tile nicely - frontyScale = DivScale16 (skyscale << skystretch, frontyScale); + frontyScale = DivScale16 (skyscale, frontyScale); frontiScale = DivScale32 (1, frontyScale); + // Sodding crap. Fixed point sucks when you want precision. + // TODO (if I'm feeling adventurous): Rewrite the renderer to use floating point + // coordinates to keep as much precision as possible until the final + // rasterization stage so fudges like this aren't needed. + if (viewheight <= 600) + { + skymid -= FRACUNIT; + } R_DrawSkyStriped (pl); } } @@ -889,7 +891,7 @@ static void R_DrawSkyStriped (visplane_t *pl) // So that I don't have to worry about fractional precision, chop off the // fractional part of centeryfrac. centeryfrac = centery << FRACBITS; - topfrac = (skytexturemid + iscale * (1-centery)) % (frontskytex->GetHeight() << FRACBITS); + topfrac = (skymid + iscale * (1-centery)) % (frontskytex->GetHeight() << FRACBITS); if (topfrac < 0) topfrac += frontskytex->GetHeight() << FRACBITS; yl = 0; yh = (short)MulScale32 ((frontskytex->GetHeight() << FRACBITS) - topfrac, frontyScale); @@ -1103,10 +1105,12 @@ void R_DrawSkyBoxes () // Don't let gun flashes brighten the sky box extralight = 0; R_SetVisibility (sky->args[0] * 0.25f); - viewx = sky->x; - viewy = sky->y; - viewz = sky->z; - viewangle = savedangle + sky->angle; + + viewx = sky->PrevX + FixedMul(r_TicFrac, sky->x - sky->PrevX); + viewy = sky->PrevY + FixedMul(r_TicFrac, sky->y - sky->PrevY); + viewz = sky->PrevZ + FixedMul(r_TicFrac, sky->z - sky->PrevZ); + viewangle = savedangle + sky->PrevAngle + FixedMul(r_TicFrac, sky->angle - sky->PrevAngle); + R_CopyStackedViewParameters(); } else @@ -1252,6 +1256,7 @@ ADD_STAT(skyboxes) void R_DrawSkyPlane (visplane_t *pl) { FTextureID sky1tex, sky2tex; + double frontdpos = 0, backdpos = 0; if ((level.flags & LEVEL_SWAPSKIES) && !(level.flags & LEVEL_DOUBLESKY)) { @@ -1262,6 +1267,8 @@ void R_DrawSkyPlane (visplane_t *pl) sky1tex = sky1texture; } sky2tex = sky2texture; + skymid = skytexturemid; + skyangle = viewangle; if (pl->picnum == skyflatnum) { @@ -1274,15 +1281,18 @@ void R_DrawSkyPlane (visplane_t *pl) else backskytex = NULL; skyflip = 0; - frontpos = sky1pos; - backpos = sky2pos; + frontdpos = sky1pos; + backdpos = sky2pos; + frontcyl = sky1cyl; + backcyl = sky2cyl; } else if (pl->sky == PL_SKYFLAT) { // use sky2 frontskytex = TexMan(sky2tex); backskytex = NULL; + frontcyl = sky2cyl; skyflip = 0; - frontpos = sky2pos; + frontdpos = sky2pos; } else { // MBF's linedef-controlled skies @@ -1305,7 +1315,7 @@ void R_DrawSkyPlane (visplane_t *pl) } frontskytex = TexMan(s->GetTexture(pos)); - if (frontskytex->UseType == FTexture::TEX_Null) + if (frontskytex == NULL || frontskytex->UseType == FTexture::TEX_Null) { // [RH] The blank texture: Use normal sky instead. goto sky1; } @@ -1315,10 +1325,10 @@ void R_DrawSkyPlane (visplane_t *pl) // to allow sky rotation as well as careful positioning. // However, the offset is scaled very small, so that it // allows a long-period of sky rotation. - frontpos = (-s->GetTextureXOffset(pos)) >> 6; + skyangle += s->GetTextureXOffset(pos); // Vertical offset allows careful sky positioning. - dc_texturemid = s->GetTextureYOffset(pos) - 28*FRACUNIT; + skymid = s->GetTextureYOffset(pos) - 28*FRACUNIT; // We sometimes flip the picture horizontally. // @@ -1326,8 +1336,19 @@ void R_DrawSkyPlane (visplane_t *pl) // to make it easier to use the new feature, while to still // allow old sky textures to be used. skyflip = l->args[2] ? 0u : ~0u; + + frontcyl = MAX(frontskytex->GetWidth(), frontskytex->xScale >> (16 - 10)); + if (skystretch) + { + skymid = Scale(skymid, frontskytex->GetScaledHeight(), SKYSTRETCH_HEIGHT); + } } } + frontpos = int(fmod(frontdpos, sky1cyl * 65536.0)); + if (backskytex != NULL) + { + backpos = int(fmod(backdpos, sky2cyl * 65536.0)); + } bool fakefixed = false; if (fixedcolormap) diff --git a/src/r_segs.cpp b/src/r_segs.cpp index e58d042ab..06b793af4 100644 --- a/src/r_segs.cpp +++ b/src/r_segs.cpp @@ -2006,29 +2006,29 @@ void PrepWall (fixed_t *swall, fixed_t *lwall, fixed_t walxrepeat) x = WallSX1; l = top / bot; - swall[x] = quickertoint (l * WallDepthScale + WallDepthOrg); - lwall[x] = quickertoint (l * xrepeat); + swall[x] = xs_RoundToInt(l * WallDepthScale + WallDepthOrg); + lwall[x] = xs_RoundToInt(l * xrepeat); // As long as l is invalid, step one column at a time so that // we can get as many correct texture columns as possible. while (l > 1.0 && x+1 < WallSX2) { l = (top += WallUoverZstep) / (bot += WallInvZstep); x++; - swall[x] = quickertoint (l * WallDepthScale + WallDepthOrg); - lwall[x] = quickertoint (l * xrepeat); + swall[x] = xs_RoundToInt(l * WallDepthScale + WallDepthOrg); + lwall[x] = xs_RoundToInt(l * xrepeat); } l *= xrepeat; while (x+4 < WallSX2) { top += topinc; bot += botinc; ol = l; l = top / bot; - swall[x+4] = quickertoint (l * WallDepthScale + WallDepthOrg); - lwall[x+4] = quickertoint (l *= xrepeat); + swall[x+4] = xs_RoundToInt(l * WallDepthScale + WallDepthOrg); + lwall[x+4] = xs_RoundToInt(l *= xrepeat); i = (ol+l) * 0.5f; - lwall[x+2] = quickertoint (i); - lwall[x+1] = quickertoint ((ol+i) * 0.5f); - lwall[x+3] = quickertoint ((l+i) * 0.5f); + lwall[x+2] = xs_RoundToInt(i); + lwall[x+1] = xs_RoundToInt((ol+i) * 0.5f); + lwall[x+3] = xs_RoundToInt((l+i) * 0.5f); swall[x+2] = ((swall[x]+swall[x+4])>>1); swall[x+1] = ((swall[x]+swall[x+2])>>1); swall[x+3] = ((swall[x+4]+swall[x+2])>>1); @@ -2038,25 +2038,25 @@ void PrepWall (fixed_t *swall, fixed_t *lwall, fixed_t walxrepeat) { top += topinc * 0.5f; bot += botinc * 0.5f; ol = l; l = top / bot; - swall[x+2] = quickertoint (l * WallDepthScale + WallDepthOrg); - lwall[x+2] = quickertoint (l *= xrepeat); + swall[x+2] = xs_RoundToInt(l * WallDepthScale + WallDepthOrg); + lwall[x+2] = xs_RoundToInt(l *= xrepeat); - lwall[x+1] = quickertoint ((l+ol)*0.5f); + lwall[x+1] = xs_RoundToInt((l+ol)*0.5f); swall[x+1] = (swall[x]+swall[x+2])>>1; x += 2; } if (x+1 < WallSX2) { l = (top + WallUoverZstep) / (bot + WallInvZstep); - swall[x+1] = quickertoint (l * WallDepthScale + WallDepthOrg); - lwall[x+1] = quickertoint (l * xrepeat); + swall[x+1] = xs_RoundToInt(l * WallDepthScale + WallDepthOrg); + lwall[x+1] = xs_RoundToInt(l * xrepeat); } /* for (x = WallSX1; x < WallSX2; x++) { frac = top / bot; - lwall[x] = quickertoint (frac * xrepeat); - swall[x] = quickertoint (frac * WallDepthScale + WallDepthOrg); + lwall[x] = xs_RoundToInt(frac * xrepeat); + swall[x] = xs_RoundToInt(frac * WallDepthScale + WallDepthOrg); top += WallUoverZstep; bot += WallInvZstep; } @@ -2108,39 +2108,39 @@ void PrepLWall (fixed_t *lwall, fixed_t walxrepeat) x = WallSX1; l = top / bot; - lwall[x] = quickertoint (l * xrepeat); + lwall[x] = xs_RoundToInt(l * xrepeat); // As long as l is invalid, step one column at a time so that // we can get as many correct texture columns as possible. while (l > 1.0 && x+1 < WallSX2) { l = (top += WallUoverZstep) / (bot += WallInvZstep); - lwall[++x] = quickertoint (l * xrepeat); + lwall[++x] = xs_RoundToInt(l * xrepeat); } l *= xrepeat; while (x+4 < WallSX2) { top += topinc; bot += botinc; ol = l; l = top / bot; - lwall[x+4] = quickertoint (l *= xrepeat); + lwall[x+4] = xs_RoundToInt(l *= xrepeat); i = (ol+l) * 0.5f; - lwall[x+2] = quickertoint (i); - lwall[x+1] = quickertoint ((ol+i) * 0.5f); - lwall[x+3] = quickertoint ((l+i) * 0.5f); + lwall[x+2] = xs_RoundToInt(i); + lwall[x+1] = xs_RoundToInt((ol+i) * 0.5f); + lwall[x+3] = xs_RoundToInt((l+i) * 0.5f); x += 4; } if (x+2 < WallSX2) { top += topinc * 0.5f; bot += botinc * 0.5f; ol = l; l = top / bot; - lwall[x+2] = quickertoint (l *= xrepeat); - lwall[x+1] = quickertoint ((l+ol)*0.5f); + lwall[x+2] = xs_RoundToInt(l *= xrepeat); + lwall[x+1] = xs_RoundToInt((l+ol)*0.5f); x += 2; } if (x+1 < WallSX2) { l = (top + WallUoverZstep) / (bot + WallInvZstep); - lwall[x+1] = quickertoint (l * xrepeat); + lwall[x+1] = xs_RoundToInt(l * xrepeat); } // fix for rounding errors @@ -2187,6 +2187,9 @@ static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, fixed_t zpos; int needrepeat = 0; sector_t *front, *back; + bool calclighting; + bool rereadcolormap; + FDynamicColormap *usecolormap; if (decal->RenderFlags & RF_INVISIBLE || !viewactive || !decal->PicNum.isValid()) return; @@ -2246,6 +2249,15 @@ static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, // to a wall, we use the wall's angle instead of the decal's. This is // pretty much the same as what R_AddLine() does. + fixed_t savetx1, savetx2, savety1, savety2, savesz1, savesz2; + + savetx1 = WallTX1; + savetx2 = WallTX2; + savety1 = WallTY1; + savety2 = WallTY2; + savesz1 = WallSZ1; + savesz2 = WallSZ2; + x2 = WallSpriteTile->GetWidth(); x1 = WallSpriteTile->LeftOffset; x2 = x2 - x1; @@ -2277,43 +2289,43 @@ static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, if (WallTX1 >= -WallTY1) { - if (WallTX1 > WallTY1) return; // left edge is off the right side - if (WallTY1 == 0) return; + if (WallTX1 > WallTY1) goto done; // left edge is off the right side + if (WallTY1 == 0) goto done; x1 = (centerxfrac + Scale (WallTX1, centerxfrac, WallTY1)) >> FRACBITS; if (WallTX1 >= 0) x1 = MIN (viewwidth, x1+1); // fix for signed divide WallSZ1 = WallTY1; } else { - if (WallTX2 < -WallTY2) return; // wall is off the left side + if (WallTX2 < -WallTY2) goto done; // wall is off the left side fixed_t den = WallTX1 - WallTX2 - WallTY2 + WallTY1; - if (den == 0) return; + if (den == 0) goto done; x1 = 0; WallSZ1 = WallTY1 + Scale (WallTY2 - WallTY1, WallTX1 + WallTY1, den); } if (WallSZ1 < TOO_CLOSE_Z) - return; + goto done; if (WallTX2 <= WallTY2) { - if (WallTX2 < -WallTY2) return; // right edge is off the left side - if (WallTY2 == 0) return; + if (WallTX2 < -WallTY2) goto done; // right edge is off the left side + if (WallTY2 == 0) goto done; x2 = (centerxfrac + Scale (WallTX2, centerxfrac, WallTY2)) >> FRACBITS; if (WallTX2 >= 0) x2 = MIN (viewwidth, x2+1); // fix for signed divide WallSZ2 = WallTY2; } else { - if (WallTX1 > WallTY1) return; // wall is off the right side + if (WallTX1 > WallTY1) goto done; // wall is off the right side fixed_t den = WallTY2 - WallTY1 - WallTX2 + WallTX1; - if (den == 0) return; + if (den == 0) goto done; x2 = viewwidth; WallSZ2 = WallTY1 + Scale (WallTY2 - WallTY1, WallTX1 - WallTY1, den); } if (x1 >= x2 || x1 > clipper->x2 || x2 <= clipper->x1 || WallSZ2 < TOO_CLOSE_Z) - return; + goto done; if (MirrorFlags & RF_XFLIP) { @@ -2340,7 +2352,7 @@ static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, { if (pass != 0) { - return; + goto done; } mceilingclip = walltop; mfloorclip = wallbottom; @@ -2361,7 +2373,7 @@ static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, case RF_CLIPUPPER: if (pass != 0) { - return; + goto done; } mceilingclip = walltop; mfloorclip = ceilingclip; @@ -2370,7 +2382,7 @@ static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, case RF_CLIPMID: if (curline->backsector != NULL && pass != 2) { - return; + goto done; } mceilingclip = openings + clipper->sprtopclip - clipper->x1; mfloorclip = openings + clipper->sprbottomclip - clipper->x1; @@ -2379,7 +2391,7 @@ static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, case RF_CLIPLOWER: if (pass != 0) { - return; + goto done; } mceilingclip = floorclip; mfloorclip = wallbottom; @@ -2400,7 +2412,7 @@ static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, } if (x1 >= x2) { - return; + goto done; } swap (x1, WallSX1); @@ -2421,15 +2433,24 @@ static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, } // Prepare lighting - bool calclighting = false; + calclighting = false; + usecolormap = basecolormap; + rereadcolormap = true; + + // Decals that are added to the scene must fade to black. + if (decal->RenderStyle == LegacyRenderStyles[STYLE_Add] && usecolormap->Fade != 0) + { + usecolormap = GetSpecialLights(usecolormap->Color, 0, usecolormap->Desaturate); + rereadcolormap = false; + } rw_light = rw_lightleft + (x1 - WallSX1) * rw_lightstep; if (fixedlightlev >= 0) - dc_colormap = basecolormap->Maps + fixedlightlev; + dc_colormap = usecolormap->Maps + fixedlightlev; else if (fixedcolormap != NULL) dc_colormap = fixedcolormap; else if (!foggy && (decal->RenderFlags & RF_FULLBRIGHT)) - dc_colormap = basecolormap->Maps; + dc_colormap = usecolormap->Maps; else calclighting = true; @@ -2455,6 +2476,12 @@ static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, mode = R_SetPatchStyle (decal->RenderStyle, decal->Alpha, decal->Translation, decal->AlphaColor); + // R_SetPatchStyle can modify basecolormap. + if (rereadcolormap) + { + usecolormap = basecolormap; + } + if (mode == DontDraw) { needrepeat = 0; @@ -2476,7 +2503,7 @@ static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, { if (calclighting) { // calculate lighting - dc_colormap = basecolormap->Maps + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT); + dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT); } WallSpriteColumn (R_DrawMaskedColumn); @@ -2487,7 +2514,7 @@ static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, { if (calclighting) { // calculate lighting - dc_colormap = basecolormap->Maps + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT); + dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT); } rt_initcols(); for (int zz = 4; zz; --zz) @@ -2502,7 +2529,7 @@ static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, { if (calclighting) { // calculate lighting - dc_colormap = basecolormap->Maps + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT); + dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT); } WallSpriteColumn (R_DrawMaskedColumn); @@ -2523,6 +2550,13 @@ static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, hcolfunc_post4 = rt_map4cols; R_FinishSetPatchStyle (); +done: + WallTX1 = savetx1; + WallTX2 = savetx2; + WallTY1 = savety1; + WallTY2 = savety2; + WallSZ1 = savesz1; + WallSZ2 = savesz2; } static void WallSpriteColumn (void (*drawfunc)(const BYTE *column, const FTexture::Span *spans)) diff --git a/src/r_sky.cpp b/src/r_sky.cpp index 76e4d9d62..544c1ded9 100644 --- a/src/r_sky.cpp +++ b/src/r_sky.cpp @@ -43,13 +43,11 @@ FTextureID skyflatnum; FTextureID sky1texture, sky2texture; fixed_t skytexturemid; fixed_t skyscale; -int skystretch; -fixed_t skyheight; fixed_t skyiscale; +bool skystretch; -int sky1shift, sky2shift; -fixed_t sky1pos=0, sky1speed=0; -fixed_t sky2pos=0, sky2speed=0; +fixed_t sky1cyl, sky2cyl; +double sky1pos, sky2pos; // [RH] Stretch sky texture if not taller than 128 pixels? CUSTOM_CVAR (Bool, r_stretchsky, true, CVAR_ARCHIVE) @@ -69,7 +67,7 @@ extern fixed_t freelookviewheight; void R_InitSkyMap () { - fixed_t fskyheight; + int skyheight; FTexture *skytex1, *skytex2; skytex1 = TexMan[sky1texture]; @@ -84,27 +82,37 @@ void R_InitSkyMap () sky2texture = sky1texture; } - fskyheight = skytex1->GetHeight() << FRACBITS; - if (skytex1->GetScaledHeight() <= 128) + // There are various combinations for sky rendering depending on how tall the sky is: + // h < 128: Unstretched and tiled, centered on horizon + // 128 <= h < 200: Can possibly be stretched. When unstretched, the baseline is + // 28 rows below the horizon so that the top of the texture + // aligns with the top of the screen when looking straight ahead. + // When stretched, it is scaled to 228 pixels with the baseline + // in the same location as an unstretched 128-tall sky, so the top + // of the texture aligns with the top of the screen when looking + // fully up. + // h == 200: Unstretched, baseline is on horizon, and top is at the top of + // the screen when looking fully up. + // h > 200: Unstretched, but the baseline is shifted down so that the top + // of the texture is at the top of the screen when looking fully up. + skyheight = skytex1->GetScaledHeight(); + skystretch = false; + skytexturemid = 0; + if (skyheight >= 128 && skyheight < 200) { - skytexturemid = r_Yaspect/2*FRACUNIT; skystretch = (r_stretchsky + && skyheight >= 128 && level.IsFreelookAllowed() && !(level.flags & LEVEL_FORCENOSKYSTRETCH)) ? 1 : 0; + skytexturemid = -28*FRACUNIT; } - else + else if (skyheight > 200) { - skytexturemid = 199 * skytex1->yScale; - skystretch = 0; - // At heights above 600 pixels, the sky is drawn slightly too low. - if (SCREENHEIGHT > 600) - { - skytexturemid += FRACUNIT; - } + skytexturemid = (200 - skyheight) << FRACBITS; } - skyheight = fskyheight << skystretch; + skytexturemid = FixedMul(skytexturemid, skytex1->yScale); - if (viewwidth && viewheight) + if (viewwidth != 0 && viewheight != 0) { skyiscale = (r_Yaspect*FRACUNIT) / ((freelookviewheight * viewwidth) / viewwidth); skyscale = (((freelookviewheight * viewwidth) / viewwidth) << FRACBITS) / @@ -114,12 +122,18 @@ void R_InitSkyMap () skyscale = Scale (skyscale, 2048, FieldOfView); } - // The (standard Doom) sky map is 256*128*4 maps. - sky1shift = 22+skystretch-16; - sky2shift = 22+skystretch-16; - if (skytex1->WidthBits >= 9) - sky1shift -= skystretch; - if (skytex2->WidthBits >= 9) - sky2shift -= skystretch; + if (skystretch) + { + skyscale = Scale(skyscale, SKYSTRETCH_HEIGHT, skyheight); + skyiscale = Scale(skyiscale, skyheight, SKYSTRETCH_HEIGHT); + skytexturemid = Scale(skytexturemid, skyheight, SKYSTRETCH_HEIGHT); + } + + // The standard Doom sky texture is 256 pixels wide, repeated 4 times over 360 degrees, + // giving a total sky width of 1024 pixels. So if the sky texture is no wider than 1024, + // we map it to a cylinder with circumfrence 1024. For larger ones, we use the width of + // the texture as the cylinder's circumfrence. + sky1cyl = MAX(skytex1->GetWidth(), skytex1->xScale >> (16 - 10)); + sky2cyl = MAX(skytex2->GetWidth(), skytex2->xScale >> (16 - 10)); } diff --git a/src/r_sky.h b/src/r_sky.h index b29deb61b..3e836ec3e 100644 --- a/src/r_sky.h +++ b/src/r_sky.h @@ -24,16 +24,17 @@ #include "textures/textures.h" -extern int sky1shift, sky2shift; extern FTextureID skyflatnum; +extern fixed_t sky1cyl, sky2cyl; extern FTextureID sky1texture, sky2texture; -extern fixed_t sky1pos, sky2pos; +extern double sky1pos, sky2pos; extern fixed_t skytexturemid; -extern int skystretch; extern fixed_t skyiscale; extern fixed_t skyscale; -extern fixed_t skyheight; +extern bool skystretch; + +#define SKYSTRETCH_HEIGHT 228 // Called whenever the sky changes. void R_InitSkyMap (); diff --git a/src/r_things.cpp b/src/r_things.cpp index 3e9b8b0b3..a2f18de5f 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -23,6 +23,7 @@ #include #include +#include #include "templates.h" #include "doomdef.h" @@ -1432,6 +1433,12 @@ void R_ProjectSprite (AActor *thing, int fakeside) FDynamicColormap *mybasecolormap = basecolormap; + // Sprites that are added to the scene must fade to black. + if (vis->RenderStyle == LegacyRenderStyles[STYLE_Add] && mybasecolormap->Fade != 0) + { + mybasecolormap = GetSpecialLights(mybasecolormap->Color, 0, mybasecolormap->Desaturate); + } + if (vis->RenderStyle.Flags & STYLEF_FadeToBlack) { if (invertcolormap) @@ -1699,6 +1706,7 @@ void R_DrawPSprite (pspdef_t* psp, int pspnum, AActor *owner, fixed_t sx, fixed_ else { VisPSpritesBaseColormap[pspnum] = basecolormap; + vis->colormap = basecolormap->Maps; vis->RenderStyle = STYLE_Normal; } @@ -1841,9 +1849,9 @@ void R_DrawRemainingPlayerSprites() } screen->DrawTexture(vis->pic, viewwindowx + VisPSpritesX1[i], - viewwindowy + viewheight/2 - MulScale32(vis->texturemid, vis->yscale) - 1, - DTA_DestWidth, FixedMul(vis->pic->GetWidth(), vis->xscale), - DTA_DestHeight, FixedMul(vis->pic->GetHeight(), vis->yscale), + viewwindowy + viewheight/2 - (vis->texturemid / 65536.0) * (vis->yscale / 65536.0) - 0.5, + DTA_DestWidthF, FIXED2FLOAT(vis->pic->GetWidth() * vis->xscale), + DTA_DestHeightF, FIXED2FLOAT(vis->pic->GetHeight() * vis->yscale), DTA_Translation, TranslationToTable(vis->Translation), DTA_FlipX, flip, DTA_TopOffset, 0, @@ -1856,7 +1864,7 @@ void R_DrawRemainingPlayerSprites() DTA_RenderStyle, vis->RenderStyle, DTA_FillColor, vis->FillColor, DTA_SpecialColormap, special, - DTA_ColorOverlay, overlay, + DTA_ColorOverlay, overlay.d, DTA_ColormapStyle, usecolormapstyle ? &colormapstyle : NULL, TAG_DONE); } @@ -1876,13 +1884,9 @@ void R_DrawRemainingPlayerSprites() // gain compared to the old function. // // Sort vissprites by depth, far to near -static int STACK_ARGS sv_compare (const void *arg1, const void *arg2) +static bool sv_compare(vissprite_t *a, vissprite_t *b) { - int diff = (*(vissprite_t **)arg2)->idepth - (*(vissprite_t **)arg1)->idepth; - // If two sprites are the same distance, then the higher one gets precedence - if (diff == 0) - return (*(vissprite_t **)arg2)->gzt - (*(vissprite_t **)arg1)->gzt; - return diff; + return a->idepth > b->idepth; } #if 0 @@ -2016,7 +2020,16 @@ void R_SplitVisSprites () } #endif -void R_SortVisSprites (int (STACK_ARGS *compare)(const void *, const void *), size_t first) +#ifdef __GNUC__ +static void swap(vissprite_t *&a, vissprite_t *&b) +{ + vissprite_t *t = a; + a = b; + b = t; +} +#endif + +void R_SortVisSprites (bool (*compare)(vissprite_t *, vissprite_t *), size_t first) { int i; vissprite_t **spr; @@ -2034,12 +2047,25 @@ void R_SortVisSprites (int (STACK_ARGS *compare)(const void *, const void *), si spritesortersize = MaxVisSprites; } - for (i = 0, spr = firstvissprite; i < vsprcount; i++, spr++) + if (!(i_compatflags & COMPATF_SPRITESORT)) { - spritesorter[i] = *spr; + for (i = 0, spr = firstvissprite; i < vsprcount; i++, spr++) + { + spritesorter[i] = *spr; + } + } + else + { + // If the compatibility option is on sprites of equal distance need to + // be sorted in inverse order. This is most easily achieved by + // filling the sort array backwards before the sort. + for (i = 0, spr = firstvissprite + vsprcount-1; i < vsprcount; i++, spr--) + { + spritesorter[i] = *spr; + } } - qsort (spritesorter, vsprcount, sizeof (vissprite_t *), compare); + std::stable_sort(&spritesorter[0], &spritesorter[vsprcount], compare); } diff --git a/src/r_translate.cpp b/src/r_translate.cpp index 212253288..8e74f4443 100644 --- a/src/r_translate.cpp +++ b/src/r_translate.cpp @@ -315,18 +315,21 @@ void FRemapTable::AddIndexRange(int start, int end, int pal1, int pal2) } else if (start == end) { + start = GPalette.Remap[start]; + pal1 = GPalette.Remap[pal1]; Remap[start] = pal1; Palette[start] = GPalette.BaseColors[pal1]; - Palette[start].a = start==0? 0:255; + Palette[start].a = start == 0 ? 0 : 255; return; } palcol = pal1 << FRACBITS; palstep = ((pal2 << FRACBITS) - palcol) / (end - start); for (int i = start; i <= end; palcol += palstep, ++i) { - Remap[i] = palcol >> FRACBITS; - Palette[i] = GPalette.BaseColors[palcol >> FRACBITS]; - Palette[i].a = i==0? 0:255; + int j = GPalette.Remap[i], k = GPalette.Remap[palcol >> FRACBITS]; + Remap[j] = k; + Palette[j] = GPalette.BaseColors[k]; + Palette[j].a = j == 0 ? 0 : 255; } } @@ -368,10 +371,10 @@ void FRemapTable::AddColorRange(int start, int end, int _r1,int _g1, int _b1, in } if (start == end) { - Remap[start] = ColorMatcher.Pick - (r >> FRACBITS, g >> FRACBITS, b >> FRACBITS); + start = GPalette.Remap[start]; + Remap[start] = ColorMatcher.Pick(r >> FRACBITS, g >> FRACBITS, b >> FRACBITS); Palette[start] = PalEntry(r >> FRACBITS, g >> FRACBITS, b >> FRACBITS); - Palette[start].a = start==0? 0:255; + Palette[start].a = start == 0 ? 0 : 255; } else { @@ -380,9 +383,9 @@ void FRemapTable::AddColorRange(int start, int end, int _r1,int _g1, int _b1, in bs /= (end - start); for (int i = start; i <= end; ++i) { - Remap[i] = ColorMatcher.Pick - (r >> FRACBITS, g >> FRACBITS, b >> FRACBITS); - Palette[i] = PalEntry(start==0? 0:255, r >> FRACBITS, g >> FRACBITS, b >> FRACBITS); + int j = GPalette.Remap[i]; + Remap[j] = ColorMatcher.Pick(r >> FRACBITS, g >> FRACBITS, b >> FRACBITS); + Palette[j] = PalEntry(j == 0 ? 0 : 255, r >> FRACBITS, g >> FRACBITS, b >> FRACBITS); r += rs; g += gs; b += bs; @@ -396,9 +399,50 @@ void FRemapTable::AddColorRange(int start, int end, int _r1,int _g1, int _b1, in // //---------------------------------------------------------------------------- +void FRemapTable::AddDesaturation(int start, int end, float r1,float g1, float b1, float r2, float g2, float b2) +{ + r1 = clamp(r1, 0.0f, 2.0f); + g1 = clamp(g1, 0.0f, 2.0f); + b1 = clamp(b1, 0.0f, 2.0f); + r2 = clamp(r2, 0.0f, 2.0f); + g2 = clamp(g2, 0.0f, 2.0f); + b2 = clamp(b2, 0.0f, 2.0f); + + r2 -= r1; + g2 -= g1; + b2 -= b1; + r1 *= 255; + g1 *= 255; + b1 *= 255; + + for(int c = start; c < end; c++) + { + double intensity = (GPalette.BaseColors[c].r * 77 + + GPalette.BaseColors[c].g * 143 + + GPalette.BaseColors[c].b * 37) / 256.0; + + PalEntry pe = PalEntry( MIN(255, int(r1 + intensity*r2)), + MIN(255, int(g1 + intensity*g2)), + MIN(255, int(b1 + intensity*b2))); + + int cc = GPalette.Remap[c]; + + Remap[cc] = ColorMatcher.Pick(pe); + Palette[cc] = pe; + Palette[cc].a = cc == 0 ? 0:255; + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + void FRemapTable::AddToTranslation(const char * range) { int start,end; + bool desaturated = false; FScanner sc; sc.OpenMem("translation", range, int(strlen(range))); @@ -412,18 +456,26 @@ void FRemapTable::AddToTranslation(const char * range) sc.MustGetToken(TK_IntConst); end = sc.Number; sc.MustGetToken('='); - if (!sc.CheckToken('[')) + if (start < 0 || start > 255 || end < 0 || end > 255) + { + sc.ScriptError("Palette index out of range"); + return; + } + + sc.MustGetAnyToken(); + + if (sc.TokenType != '[' && sc.TokenType != '%') { int pal1,pal2; - sc.MustGetToken(TK_IntConst); + sc.TokenMustBe(TK_IntConst); pal1 = sc.Number; sc.MustGetToken(':'); sc.MustGetToken(TK_IntConst); pal2 = sc.Number; AddIndexRange(start, end, pal1, pal2); } - else + else if (sc.TokenType == '[') { // translation using RGB values int r1,g1,b1,r2,g2,b2; @@ -456,6 +508,46 @@ void FRemapTable::AddToTranslation(const char * range) AddColorRange(start, end, r1, g1, b1, r2, g2, b2); } + else if (sc.TokenType == '%') + { + // translation using RGB values + float r1,g1,b1,r2,g2,b2; + + sc.MustGetToken('['); + sc.MustGetAnyToken(); + if (sc.TokenType != TK_IntConst) sc.TokenMustBe(TK_FloatConst); + r1 = float(sc.Float); + sc.MustGetToken(','); + + sc.MustGetAnyToken(); + if (sc.TokenType != TK_IntConst) sc.TokenMustBe(TK_FloatConst); + g1 = float(sc.Float); + sc.MustGetToken(','); + + sc.MustGetAnyToken(); + if (sc.TokenType != TK_IntConst) sc.TokenMustBe(TK_FloatConst); + b1 = float(sc.Float); + sc.MustGetToken(']'); + sc.MustGetToken(':'); + sc.MustGetToken('['); + + sc.MustGetAnyToken(); + if (sc.TokenType != TK_IntConst) sc.TokenMustBe(TK_FloatConst); + r2 = float(sc.Float); + sc.MustGetToken(','); + + sc.MustGetAnyToken(); + if (sc.TokenType != TK_IntConst) sc.TokenMustBe(TK_FloatConst); + g2 = float(sc.Float); + sc.MustGetToken(','); + + sc.MustGetAnyToken(); + if (sc.TokenType != TK_IntConst) sc.TokenMustBe(TK_FloatConst); + b2 = float(sc.Float); + sc.MustGetToken(']'); + + AddDesaturation(start, end, r1, g1, b1, r2, g2, b2); + } } catch (CRecoverableError &err) { diff --git a/src/r_translate.h b/src/r_translate.h index 098edb699..730dac541 100644 --- a/src/r_translate.h +++ b/src/r_translate.h @@ -38,6 +38,7 @@ struct FRemapTable void Serialize(FArchive &ar); void AddIndexRange(int start, int end, int pal1, int pal2); void AddColorRange(int start, int end, int r1,int g1, int b1, int r2, int g2, int b2); + void AddDesaturation(int start, int end, float r1,float g1, float b1, float r2, float g2, float b2); void AddToTranslation(const char * range); int StoreTranslation(); diff --git a/src/resourcefiles/file_7z.cpp b/src/resourcefiles/file_7z.cpp index 290792140..2943d00aa 100644 --- a/src/resourcefiles/file_7z.cpp +++ b/src/resourcefiles/file_7z.cpp @@ -310,7 +310,14 @@ bool F7ZFile::Open(bool quiet) F7ZFile::~F7ZFile() { - if (Lumps != NULL) delete [] Lumps; + if (Lumps != NULL) + { + delete[] Lumps; + } + if (Archive != NULL) + { + delete Archive; + } } //========================================================================== diff --git a/src/resourcefiles/file_directory.cpp b/src/resourcefiles/file_directory.cpp index cb250ed1e..89fb2be9c 100644 --- a/src/resourcefiles/file_directory.cpp +++ b/src/resourcefiles/file_directory.cpp @@ -41,8 +41,10 @@ #define stat _stat #else #include +#ifndef __sun #include #endif +#endif #include #include #include @@ -197,6 +199,46 @@ int FDirectory::AddDirectory(const char *dirpath) return count; } +#elif defined(__sun) + +int FDirectory::AddDirectory(const char *dirpath) +{ + int count = 0; + TArray scanDirectories; + scanDirectories.Push(dirpath); + for(unsigned int i = 0;i < scanDirectories.Size();i++) + { + DIR* directory = opendir(scanDirectories[i].GetChars()); + if (directory == NULL) + { + Printf("Could not ready directory: %s\n", strerror(errno)); + return NULL; + } + + struct dirent *file; + while((file = readdir(directory)) != NULL) + { + if(file->d_name[0] == '.') //File is hidden or ./.. directory so ignore it. + continue; + + FString fullFileName = scanDirectories[i] + file->d_name; + + struct stat fileStat; + stat(fullFileName.GetChars(), &fileStat); + + if(S_ISDIR(fileStat.st_mode)) + { + scanDirectories.Push(scanDirectories[i] + file->d_name + "/"); + continue; + } + AddEntry(scanDirectories[i] + file->d_name, fileStat.st_size); + count++; + } + closedir(directory); + } + return count; +} + #else //========================================================================== @@ -294,6 +336,7 @@ FileReader *FDirectoryLump::NewReader() { FString fullpath = Owner->Filename; fullpath += FullName; + printf("%s\n", fullpath.GetChars()); return new FileReader(fullpath); } catch (CRecoverableError &) diff --git a/src/resourcefiles/resourcefile.cpp b/src/resourcefiles/resourcefile.cpp index 9831ec70c..c5bde5712 100644 --- a/src/resourcefiles/resourcefile.cpp +++ b/src/resourcefiles/resourcefile.cpp @@ -283,9 +283,9 @@ FResourceFile *FResourceFile::OpenResourceFile(const char *filename, FileReader return NULL; } -FResourceFile *FResourceFile::OpenDirectory(const char *filename) +FResourceFile *FResourceFile::OpenDirectory(const char *filename, bool quiet) { - return CheckDir(filename, NULL, false); + return CheckDir(filename, NULL, quiet); } //========================================================================== diff --git a/src/resourcefiles/resourcefile.h b/src/resourcefiles/resourcefile.h index 39c9594f8..ac8499944 100644 --- a/src/resourcefiles/resourcefile.h +++ b/src/resourcefiles/resourcefile.h @@ -67,7 +67,7 @@ private: public: static FResourceFile *OpenResourceFile(const char *filename, FileReader *file, bool quiet = false); - static FResourceFile *OpenDirectory(const char *filename); + static FResourceFile *OpenDirectory(const char *filename, bool quiet = false); virtual ~FResourceFile(); FileReader *GetReader() const { return Reader; } DWORD LumpCount() const { return NumLumps; } diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index 31d2d94f2..14742f55d 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -1917,11 +1917,34 @@ void AAmbientSound::Serialize (FArchive &arc) Super::Serialize (arc); arc << bActive; - int checktime = NextCheck - gametic; - arc << checktime; - if (arc.IsLoading ()) + if (SaveVersion < 1902) { - NextCheck = checktime + gametic; + arc << NextCheck; + NextCheck += gametic; + if (NextCheck < 0) NextCheck = INT_MAX; + } + else + { + if (arc.IsStoring()) + { + if (NextCheck != INT_MAX) + { + int checktime = NextCheck - gametic; + arc << checktime; + } + else + { + arc << NextCheck; + } + } + else + { + arc << NextCheck; + if (NextCheck != INT_MAX) + { + NextCheck += gametic; + } + } } } diff --git a/src/s_sndseq.cpp b/src/s_sndseq.cpp index ecfed1246..381a27866 100644 --- a/src/s_sndseq.cpp +++ b/src/s_sndseq.cpp @@ -849,7 +849,7 @@ DSeqNode *SN_StartSequence (sector_t *sector, int chan, int sequence, seqtype_t { if (!nostop) { - SN_StopSequence (sector); + SN_StopSequence (sector, chan); } if (TwiddleSeqNum (sequence, type)) { @@ -952,6 +952,33 @@ static int FindSequence (FName seqname) return -1; } +//========================================================================== +// +// SN_CheckSequence +// +// Returns the sound sequence playing in the sector on the given channel, +// if any. +// +//========================================================================== + +DSeqNode *SN_CheckSequence(sector_t *sector, int chan) +{ + for (DSeqNode *node = DSeqNode::FirstSequence(); node; ) + { + DSeqNode *next = node->NextSequence(); + if (node->Source() == sector) + { + assert(node->IsKindOf(RUNTIME_CLASS(DSeqSectorNode))); + if ((static_cast(node)->Channel & 7) == chan) + { + return node; + } + } + node = next; + } + return NULL; +} + //========================================================================== // // SN_StopSequence @@ -963,9 +990,13 @@ void SN_StopSequence (AActor *actor) SN_DoStop (actor); } -void SN_StopSequence (sector_t *sector) +void SN_StopSequence (sector_t *sector, int chan) { - SN_DoStop (sector); + DSeqNode *node = SN_CheckSequence(sector, chan); + if (node != NULL) + { + node->StopAndDestroy(); + } } void SN_StopSequence (FPolyObj *poly) diff --git a/src/s_sndseq.h b/src/s_sndseq.h index e54010075..efadc0565 100644 --- a/src/s_sndseq.h +++ b/src/s_sndseq.h @@ -85,8 +85,9 @@ DSeqNode *SN_StartSequence (sector_t *sector, int chan, const char *name, int mo DSeqNode *SN_StartSequence (sector_t *sec, int chan, FName seqname, int modenum); DSeqNode *SN_StartSequence (FPolyObj *poly, int sequence, seqtype_t type, int modenum, bool nostop=false); DSeqNode *SN_StartSequence (FPolyObj *poly, const char *name, int modenum); +DSeqNode *SN_CheckSequence (sector_t *sector, int chan); void SN_StopSequence (AActor *mobj); -void SN_StopSequence (sector_t *sector); +void SN_StopSequence (sector_t *sector, int chan); void SN_StopSequence (FPolyObj *poly); void SN_UpdateActiveSequences (void); ptrdiff_t SN_GetSequenceOffset (int sequence, SDWORD *sequencePtr); diff --git a/src/s_sound.cpp b/src/s_sound.cpp index be1023039..b0b68897b 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -164,6 +164,7 @@ void S_NoiseDebug (void) screen->DrawText (SmallFont, CR_GOLD, 300, y, "chan", TAG_DONE); screen->DrawText (SmallFont, CR_GOLD, 340, y, "pri", TAG_DONE); screen->DrawText (SmallFont, CR_GOLD, 380, y, "flags", TAG_DONE); + screen->DrawText (SmallFont, CR_GOLD, 460, y, "aud", TAG_DONE); y += 8; if (Channels == NULL) @@ -186,7 +187,7 @@ void S_NoiseDebug (void) color = (chan->ChanFlags & CHAN_LOOP) ? CR_BROWN : CR_GREY; // Name - Wads.GetLumpName (temp, chan->SfxInfo->lumpnum); + Wads.GetLumpName (temp, S_sfx[chan->SoundID].lumpnum); temp[8] = 0; screen->DrawText (SmallFont, color, 0, y, temp, TAG_DONE); @@ -236,7 +237,7 @@ void S_NoiseDebug (void) screen->DrawText(SmallFont, color, 340, y, temp, TAG_DONE); // Flags - mysnprintf(temp, countof(temp), "%s3%sZ%sU%sM%sN%sA%sL%sE", + mysnprintf(temp, countof(temp), "%s3%sZ%sU%sM%sN%sA%sL%sE%sV", (chan->ChanFlags & CHAN_IS3D) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, (chan->ChanFlags & CHAN_LISTENERZ) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, (chan->ChanFlags & CHAN_UI) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, @@ -244,9 +245,14 @@ void S_NoiseDebug (void) (chan->ChanFlags & CHAN_NOPAUSE) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, (chan->ChanFlags & CHAN_AREA) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, (chan->ChanFlags & CHAN_LOOP) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHAN_EVICTED) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK); + (chan->ChanFlags & CHAN_EVICTED) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, + (chan->ChanFlags & CHAN_VIRTUAL) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK); screen->DrawText(SmallFont, color, 380, y, temp, TAG_DONE); + // Audibility + mysnprintf(temp, countof(temp), "%.4f", GSnd->GetAudibility(chan)); + screen->DrawText(SmallFont, color, 460, y, temp, TAG_DONE); + y += 8; if (chan->PrevChan == &Channels) { @@ -1047,6 +1053,7 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO if (chan == NULL && (chanflags & CHAN_LOOP)) { chan = (FSoundChan*)S_GetChannel(NULL); + GSnd->MarkStartTime(chan); chanflags |= CHAN_EVICTED; } if (attenuation > 0) @@ -1061,7 +1068,6 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO { chan->SoundID = sound_id; chan->OrgID = FSoundID(org_id); - chan->SfxInfo = sfx; chan->EntChannel = channel; chan->Volume = float(volume); chan->ChanFlags |= chanflags; @@ -1094,10 +1100,9 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO void S_RestartSound(FSoundChan *chan) { assert(chan->ChanFlags & CHAN_EVICTED); - assert(chan->SfxInfo != NULL); FSoundChan *ochan; - sfxinfo_t *sfx = chan->SfxInfo; + sfxinfo_t *sfx = &S_sfx[chan->SoundID]; // If this is a singular sound, don't play it if it's already playing. if (sfx->bSingular && S_CheckSingular(chan->SoundID)) @@ -1119,7 +1124,6 @@ void S_RestartSound(FSoundChan *chan) if (chan->ChanFlags & (CHAN_UI|CHAN_NOPAUSE)) startflags |= SNDF_NOPAUSE; if (chan->ChanFlags & CHAN_ABSTIME) startflags |= SNDF_ABSTIME; - chan->ChanFlags &= ~(CHAN_EVICTED|CHAN_ABSTIME); if (chan->ChanFlags & CHAN_IS3D) { FVector3 pos, vel; @@ -1136,11 +1140,13 @@ void S_RestartSound(FSoundChan *chan) SoundListener listener; S_SetListener(listener, players[consoleplayer].camera); + chan->ChanFlags &= ~(CHAN_EVICTED|CHAN_ABSTIME); ochan = (FSoundChan*)GSnd->StartSound3D(sfx->data, &listener, chan->Volume, &chan->Rolloff, chan->DistanceScale, chan->Pitch, chan->Priority, pos, vel, chan->EntChannel, startflags, chan); } else { + chan->ChanFlags &= ~(CHAN_EVICTED|CHAN_ABSTIME); ochan = (FSoundChan*)GSnd->StartSound(sfx->data, chan->Volume, chan->Pitch, startflags, chan); } assert(ochan == NULL || ochan == chan); @@ -1344,7 +1350,7 @@ bool S_CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, floa for (chan = Channels, count = 0; chan != NULL && count < near_limit; chan = chan->NextChan) { - if (!(chan->ChanFlags & CHAN_EVICTED) && chan->SfxInfo == sfx) + if (!(chan->ChanFlags & CHAN_EVICTED) && &S_sfx[chan->SoundID] == sfx) { FVector3 chanorigin; @@ -1741,6 +1747,11 @@ void S_EvictAllChannels() chan->ChanFlags |= CHAN_EVICTED; if (chan->SysChannel != NULL) { + if (!(chan->ChanFlags & CHAN_ABSTIME)) + { + chan->StartTime.AsOne = GSnd ? GSnd->GetPosition(chan) : 0; + chan->ChanFlags |= CHAN_ABSTIME; + } S_StopChannel(chan); } // assert(chan->NextChan == next); @@ -1969,10 +1980,10 @@ void S_ChannelEnded(FISoundChannel *ichan) { evicted = true; } - else if (schan->SfxInfo != NULL) + else { unsigned int pos = GSnd->GetPosition(schan); - unsigned int len = GSnd->GetSampleLength(schan->SfxInfo->data); + unsigned int len = GSnd->GetSampleLength(S_sfx[schan->SoundID].data); if (pos == 0) { evicted = !!(schan->ChanFlags & CHAN_JUSTSTARTED); @@ -1982,10 +1993,12 @@ void S_ChannelEnded(FISoundChannel *ichan) evicted = (pos < len); } } + /* else { evicted = false; } + */ if (!evicted) { S_ReturnChannel(schan); @@ -1998,6 +2011,25 @@ void S_ChannelEnded(FISoundChannel *ichan) } } +//========================================================================== +// +// S_ChannelVirtualChanged (callback for sound interface code) +// +//========================================================================== + +void S_ChannelVirtualChanged(FISoundChannel *ichan, bool is_virtual) +{ + FSoundChan *schan = static_cast(ichan); + if (is_virtual) + { + schan->ChanFlags |= CHAN_VIRTUAL; + } + else + { + schan->ChanFlags &= ~CHAN_VIRTUAL; + } +} + //========================================================================== // // S_StopChannel @@ -2016,13 +2048,11 @@ void S_StopChannel(FSoundChan *chan) if (!(chan->ChanFlags & CHAN_EVICTED)) { chan->ChanFlags |= CHAN_FORGETTABLE; + if (chan->SourceType == SOURCE_Actor) + { + chan->Actor = NULL; + } } - - if (chan->SourceType == SOURCE_Actor) - { - chan->Actor = NULL; - } - GSnd->StopChannel(chan); } else @@ -2084,10 +2114,6 @@ static FArchive &operator<<(FArchive &arc, FSoundChan &chan) << chan.Rolloff.MaxDistance << chan.LimitRange; - if (arc.IsLoading()) - { - chan.SfxInfo = &S_sfx[chan.SoundID]; - } return arc; } @@ -2276,7 +2302,10 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) } } - if (!mus_playing.name.IsEmpty() && stricmp (mus_playing.name, musicname) == 0) + if (!mus_playing.name.IsEmpty() && + mus_playing.handle != NULL && + stricmp (mus_playing.name, musicname) == 0 && + mus_playing.handle->m_Looping == looping) { if (order != mus_playing.baseorder) { diff --git a/src/s_sound.h b/src/s_sound.h index 78d450e20..98e9b517e 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -169,7 +169,6 @@ struct FSoundChan : public FISoundChannel { FSoundChan *NextChan; // Next channel in this list. FSoundChan **PrevChan; // Previous channel in this list. - sfxinfo_t *SfxInfo; // Sound information. FSoundID SoundID; // Sound ID of playing sound. FSoundID OrgID; // Sound ID of sound used to start this channel. float Volume; @@ -262,6 +261,7 @@ void S_Sound (fixed_t x, fixed_t y, fixed_t z, int channel, FSoundID sfxid, doub #define CHAN_FORGETTABLE 4 // internal: Forget channel data when sound stops. #define CHAN_JUSTSTARTED 512 // internal: Sound has not been updated yet. #define CHAN_ABSTIME 1024// internal: Start time is absolute and does not depend on current time. +#define CHAN_VIRTUAL 2048// internal: Channel is currently virtual // sound attenuation values #define ATTN_NONE 0.f // full volume the entire level diff --git a/src/sc_man.h b/src/sc_man.h index cffc07e5d..1e5dc289d 100644 --- a/src/sc_man.h +++ b/src/sc_man.h @@ -175,6 +175,7 @@ enum TK_Exec, TK_DefaultProperties, TK_Native, + TK_Var, TK_Out, TK_Ref, TK_Event, diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index 904e9b7d5..ff0945db3 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -109,6 +109,7 @@ std2: 'exec' { RET(TK_Exec); } 'defaultproperties' { RET(TK_DefaultProperties); } 'native' { RET(TK_Native); } + 'var' { RET(TK_Var); } 'out' { RET(TK_Out); } 'ref' { RET(TK_Ref); } 'event' { RET(TK_Event); } diff --git a/src/sdl/crashcatcher.c b/src/sdl/crashcatcher.c index 3a4c5a0ce..7dc928407 100644 --- a/src/sdl/crashcatcher.c +++ b/src/sdl/crashcatcher.c @@ -8,6 +8,12 @@ #include #include +// Solaris doesn't have SA_ONESHOT +// According to the Linux header this is the same. +#ifndef SA_ONESHOT +#define SA_ONESHOT SA_RESETHAND +#endif + static const char *cc_logfile = NULL; static char respfile[256]; diff --git a/src/sdl/i_system.cpp b/src/sdl/i_system.cpp index 8d0a8b947..931b8a37d 100644 --- a/src/sdl/i_system.cpp +++ b/src/sdl/i_system.cpp @@ -124,6 +124,12 @@ unsigned int I_MSTime (void) return time - BaseTime; } +// Exactly the same thing, but based does no modification to the time. +unsigned int I_FPSTime() +{ + return SDL_GetTicks(); +} + // // I_GetTime // returns time in 1/35th second tics diff --git a/src/sdl/i_system.h b/src/sdl/i_system.h index 768240074..778af53f5 100644 --- a/src/sdl/i_system.h +++ b/src/sdl/i_system.h @@ -126,6 +126,7 @@ bool I_WriteIniFailed (); // [RH] Returns millisecond-accurate time unsigned int I_MSTime (void); +unsigned int I_FPSTime(); // Directory searching routines diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index 2fc37322d..f8f01f2a9 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -46,6 +46,8 @@ extern HWND Window; #endif #ifdef __APPLE__ #include +#elif __sun +#include #else #include #endif @@ -1541,7 +1543,7 @@ FISoundChannel *FMODSoundRenderer::StartSound(SoundHandle sfx, float vol, int pi chan->setFrequency(freq); } chan->setVolume(vol); - if (!HandleChannelDelay(chan, reuse_chan, !!(flags & SNDF_ABSTIME), freq)) + if (!HandleChannelDelay(chan, reuse_chan, flags & (SNDF_ABSTIME | SNDF_LOOP), freq)) { chan->stop(); return NULL; @@ -1648,8 +1650,12 @@ FISoundChannel *FMODSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener * chan->set3DAttributes((FMOD_VECTOR *)&pos[0], (FMOD_VECTOR *)&vel[0]); chan->set3DSpread(snd_3dspread); } - if (!HandleChannelDelay(chan, reuse_chan, !!(flags & SNDF_ABSTIME), freq)) + if (!HandleChannelDelay(chan, reuse_chan, flags & (SNDF_ABSTIME | SNDF_LOOP), freq)) { + // FMOD seems to get confused if you stop a channel right after + // starting it, so hopefully this function will never fail. + // (Presumably you need an update between them, but I haven't + // tested this hypothesis.) chan->stop(); return NULL; } @@ -1674,6 +1680,19 @@ FISoundChannel *FMODSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener * return 0; } +//========================================================================== +// +// FMODSoundRenderer :: MarkStartTime +// +// Marks a channel's start time without actually playing it. +// +//========================================================================== + +void FMODSoundRenderer::MarkStartTime(FISoundChannel *chan) +{ + Sys->getDSPClock(&chan->StartTime.Hi, &chan->StartTime.Lo); +} + //========================================================================== // // FMODSoundRenderer :: HandleChannelDelay @@ -1685,7 +1704,7 @@ FISoundChannel *FMODSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener * // //========================================================================== -bool FMODSoundRenderer::HandleChannelDelay(FMOD::Channel *chan, FISoundChannel *reuse_chan, bool abstime, float freq) const +bool FMODSoundRenderer::HandleChannelDelay(FMOD::Channel *chan, FISoundChannel *reuse_chan, int flags, float freq) const { if (reuse_chan != NULL) { // Sound is being restarted, so seek it to the position @@ -1695,7 +1714,7 @@ bool FMODSoundRenderer::HandleChannelDelay(FMOD::Channel *chan, FISoundChannel * // If abstime is set, the sound is being restored, and // the channel's start time is actually its seek position. - if (abstime) + if (flags & SNDF_ABSTIME) { unsigned int seekpos = reuse_chan->StartTime.Lo; if (seekpos > 0) @@ -1704,11 +1723,26 @@ bool FMODSoundRenderer::HandleChannelDelay(FMOD::Channel *chan, FISoundChannel * } reuse_chan->StartTime.AsOne = QWORD(nowtime.AsOne - seekpos * OutputRate / freq); } - else + else if (reuse_chan->StartTime.AsOne != 0) { QWORD difftime = nowtime.AsOne - reuse_chan->StartTime.AsOne; if (difftime > 0) { + // Clamp the position of looping sounds to be within the sound. + // If we try to start it several minutes past its normal end, + // FMOD doesn't like that. + if (flags & SNDF_LOOP) + { + FMOD::Sound *sound; + if (FMOD_OK == chan->getCurrentSound(&sound)) + { + unsigned int len; + if (FMOD_OK == sound->getLength(&len, FMOD_TIMEUNIT_MS)) + { + difftime %= len; + } + } + } return chan->setPosition((unsigned int)(difftime / OutputRate), FMOD_TIMEUNIT_MS) == FMOD_OK; } } @@ -1846,6 +1880,27 @@ unsigned int FMODSoundRenderer::GetPosition(FISoundChannel *chan) return pos; } +//========================================================================== +// +// FMODSoundRenderer :: GetAudibility +// +// Returns the audible volume of the channel, after rollof and any other +// factors are applied. +// +//========================================================================== + +float FMODSoundRenderer::GetAudibility(FISoundChannel *chan) +{ + float aud; + + if (chan == NULL || chan->SysChannel == NULL) + { + return 0; + } + ((FMOD::Channel *)chan->SysChannel)->getAudibility(&aud); + return aud; +} + //========================================================================== // // FMODSoundRenderer :: SetSfxPaused @@ -2284,16 +2339,19 @@ unsigned int FMODSoundRenderer::GetSampleLength(SoundHandle sfx) FMOD_RESULT F_CALLBACK FMODSoundRenderer::ChannelCallback (FMOD_CHANNEL *channel, FMOD_CHANNEL_CALLBACKTYPE type, void *data1, void *data2) { - if (type != FMOD_CHANNEL_CALLBACKTYPE_END) - { - return FMOD_OK; - } FMOD::Channel *chan = (FMOD::Channel *)channel; FISoundChannel *schan; if (chan->getUserData((void **)&schan) == FMOD_OK && schan != NULL) { - S_ChannelEnded(schan); + if (type == FMOD_CHANNEL_CALLBACKTYPE_END) + { + S_ChannelEnded(schan); + } + else if (type == FMOD_CHANNEL_CALLBACKTYPE_VIRTUALVOICE) + { + S_ChannelVirtualChanged(schan, data1 != 0); + } } return FMOD_OK; } diff --git a/src/sound/fmodsound.h b/src/sound/fmodsound.h index 2fe497365..3f31481bf 100644 --- a/src/sound/fmodsound.h +++ b/src/sound/fmodsound.h @@ -33,9 +33,15 @@ public: // Stops a sound channel. void StopChannel (FISoundChannel *chan); + // Marks a channel's start time without actually playing it. + void MarkStartTime (FISoundChannel *chan); + // Returns position of sound on this channel, in samples. unsigned int GetPosition(FISoundChannel *chan); + // Gets a channel's audibility (real volume). + float GetAudibility(FISoundChannel *chan); + // Synchronizes following sound startups. void Sync (bool sync); @@ -69,7 +75,7 @@ private: static FMOD_RESULT F_CALLBACK ChannelCallback(FMOD_CHANNEL *channel, FMOD_CHANNEL_CALLBACKTYPE type, void *data1, void *data2); static float F_CALLBACK RolloffCallback(FMOD_CHANNEL *channel, float distance); - bool HandleChannelDelay(FMOD::Channel *chan, FISoundChannel *reuse_chan, bool abstime, float freq) const; + bool HandleChannelDelay(FMOD::Channel *chan, FISoundChannel *reuse_chan, int flags, float freq) const; FISoundChannel *CommonChannelSetup(FMOD::Channel *chan, FISoundChannel *reuse_chan) const; FMOD_MODE SetChanHeadSettings(SoundListener *listener, FMOD::Channel *chan, const FVector3 &pos, bool areasound, FMOD_MODE oldmode) const; diff --git a/src/sound/i_sound.cpp b/src/sound/i_sound.cpp index 3370bd92f..0009b48e2 100644 --- a/src/sound/i_sound.cpp +++ b/src/sound/i_sound.cpp @@ -159,7 +159,7 @@ public: return NULL; } - // Starts a sound. (No, not really.) + // Starts a sound. FISoundChannel *StartSound (SoundHandle sfx, float vol, int pitch, int chanflags, FISoundChannel *reuse_chan) { return NULL; @@ -169,12 +169,23 @@ public: return NULL; } + // Marks a channel's start time without actually playing it. + void MarkStartTime (FISoundChannel *chan) + { + } + // Returns position of sound on this channel, in samples. unsigned int GetPosition(FISoundChannel *chan) { return 0; } + // Gets a channel's audibility (real volume). + float GetAudibility(FISoundChannel *chan) + { + return 0; + } + // Synchronizes following sound startups. void Sync (bool sync) { diff --git a/src/sound/i_sound.h b/src/sound/i_sound.h index b402fa3d2..31f401093 100644 --- a/src/sound/i_sound.h +++ b/src/sound/i_sound.h @@ -109,9 +109,15 @@ public: // Stops a sound channel. virtual void StopChannel (FISoundChannel *chan) = 0; + // Marks a channel's start time without actually playing it. + virtual void MarkStartTime (FISoundChannel *chan) = 0; + // Returns position of sound on this channel, in samples. virtual unsigned int GetPosition(FISoundChannel *chan) = 0; + // Gets a channel's audibility (real volume). + virtual float GetAudibility(FISoundChannel *chan) = 0; + // Synchronizes following sound startups. virtual void Sync (bool sync) = 0; @@ -142,6 +148,7 @@ void I_InitSound (); void I_ShutdownSound (); void S_ChannelEnded(FISoundChannel *schan); +void S_ChannelVirtualChanged(FISoundChannel *schan, bool is_virtual); float S_GetRolloff(FRolloffInfo *rolloff, float distance, bool logarithmic); FISoundChannel *S_GetChannel(void *syschan); diff --git a/src/sound/i_soundinternal.h b/src/sound/i_soundinternal.h index 9f6fe6d87..157c9c87b 100644 --- a/src/sound/i_soundinternal.h +++ b/src/sound/i_soundinternal.h @@ -96,7 +96,6 @@ struct FISoundChannel // callback that can't be passed a sound channel pointer FRolloffInfo Rolloff; float DistanceScale; - }; diff --git a/src/sound/music_dumb.cpp b/src/sound/music_dumb.cpp index 3b6f29c5c..101368934 100644 --- a/src/sound/music_dumb.cpp +++ b/src/sound/music_dumb.cpp @@ -73,7 +73,7 @@ protected: static bool read(SoundStream *stream, void *buff, int len, void *userdata); }; -#pragma pack(push, 1) +#pragma pack(1) typedef struct tagITFILEHEADER { @@ -108,7 +108,7 @@ typedef struct MODMIDICFG char szMidiZXXExt[128*32]; // changed from CHAR } MODMIDICFG, *LPMODMIDICFG; -#pragma pack(pop) +#pragma pack() // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- diff --git a/src/sound/music_win_mididevice.cpp b/src/sound/music_win_mididevice.cpp index 6a55855f3..151728662 100644 --- a/src/sound/music_win_mididevice.cpp +++ b/src/sound/music_win_mididevice.cpp @@ -41,6 +41,10 @@ #include "doomdef.h" #include "m_swap.h" +#ifndef __GNUC__ +#include +#endif + // MACROS ------------------------------------------------------------------ // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -49,6 +53,8 @@ // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- +static bool IgnoreMIDIVolume(UINT id); + // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PRIVATE DATA DEFINITIONS ------------------------------------------------ @@ -100,11 +106,18 @@ int WinMIDIDevice::Open(void (*callback)(UINT, void *, DWORD, DWORD), void *user if (err == MMSYSERR_NOERROR) { - // Set master volume to full, if the device allows it on this interface. - VolumeWorks = (MMSYSERR_NOERROR == midiOutGetVolume((HMIDIOUT)MidiOut, &SavedVolume)); - if (VolumeWorks) + if (IgnoreMIDIVolume(DeviceID)) { - VolumeWorks &= (MMSYSERR_NOERROR == midiOutSetVolume((HMIDIOUT)MidiOut, 0xffffffff)); + VolumeWorks = false; + } + else + { + // Set master volume to full, if the device allows it on this interface. + VolumeWorks = (MMSYSERR_NOERROR == midiOutGetVolume((HMIDIOUT)MidiOut, &SavedVolume)); + if (VolumeWorks) + { + VolumeWorks &= (MMSYSERR_NOERROR == midiOutSetVolume((HMIDIOUT)MidiOut, 0xffffffff)); + } } } } @@ -391,4 +404,61 @@ void CALLBACK WinMIDIDevice::CallbackFunc(HMIDIOUT hOut, UINT uMsg, DWORD_PTR dw } } +//========================================================================== +// +// IgnoreMIDIVolume +// +// Should we ignore this MIDI device's volume control even if it works? +// +// Under Windows Vista and up, when using the standard "Microsoft GS +// Wavetable Synth", midiOutSetVolume() will affect the application's audio +// session volume rather than the volume for just the MIDI stream. At first, +// I thought I could get around this by enumerating the streams in the +// audio session to find the MIDI device's stream to set its volume +// manually, but there doesn't appear to be any way to enumerate the +// individual streams in a session. Consequently, we'll just assume the MIDI +// device gets created at full volume like we want. (Actual volume changes +// are done by sending MIDI channel volume messages to the stream, not +// through midiOutSetVolume().) +// +// This is using VC++'s __uuidof extension instead of the the CLSID_ and +// IID_ definitions because I couldn't figure out why it wasn't finding them +// when linking, and __uuidof circumvents that problem. I'd also be +// surprised if w32api includes any WASAPI stuff any time soon, so it's no +// big loss making this VC++-specific for the time being +// +//========================================================================== + +static bool IgnoreMIDIVolume(UINT id) +{ +#ifndef __GNUC__ + MIDIOUTCAPS caps; + + if (MMSYSERR_NOERROR == midiOutGetDevCaps(id, &caps, sizeof(caps))) + { + // The Microsoft GS Wavetable Synth advertises itself as MOD_SWSYNTH with a VOLUME control. + // If the one we're using doesn't match that, we don't need to bother checking the name. + if (caps.wTechnology == MOD_SWSYNTH && (caps.dwSupport & MIDICAPS_VOLUME)) + { + if (strncmp(caps.szPname, "Microsoft GS", 12) == 0) + { + IMMDeviceEnumerator *enumerator; + + // Now try to create an IMMDeviceEnumerator interface. If it succeeds, + // we know we're using the new audio stack introduced with Vista and + // should ignore this MIDI device's volume control. + if (SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, + __uuidof(IMMDeviceEnumerator), (void**)&enumerator)) + && enumerator != NULL) + { + enumerator->Release(); + return true; + } + } + } + } +#endif + return false; +} + #endif diff --git a/src/statnums.h b/src/statnums.h index fd6831dc8..f699b9c39 100644 --- a/src/statnums.h +++ b/src/statnums.h @@ -1,3 +1,5 @@ +#ifndef __STATNUMS_H +#define __STATNUMS_H /* ** statnums.h ** @@ -56,4 +58,11 @@ enum STAT_LIGHTTRANSFER, // A sector light transfer. These must be ticked after the light effects!!! STAT_EARTHQUAKE, // Earthquake actors STAT_MAPMARKER, // Map marker actors + + STAT_DEFAULT = 100, + STAT_SECTOREFFECT, // All sector effects that cause floor and ceiling movement + STAT_ACTORMOVER, // actor movers + STAT_SCRIPTS, // The ACS thinker. This is to ensure that it can't tick before all actors calles PostBeginPlay }; + +#endif \ No newline at end of file diff --git a/src/textures/bitmap.cpp b/src/textures/bitmap.cpp index 913aa507e..20dfd38bd 100644 --- a/src/textures/bitmap.cpp +++ b/src/textures/bitmap.cpp @@ -280,7 +280,7 @@ static const CopyFunc copyfuncs[][9]={ // Clips the copy area for CopyPixelData functions // //=========================================================================== -bool ClipCopyPixelRect(int texwidth, int texheight, int &originx, int &originy, +bool ClipCopyPixelRect(const FClipRect *cr, int &originx, int &originy, const BYTE *&patch, int &srcwidth, int &srcheight, int &pstep_x, int &pstep_y, int rotate) { @@ -290,6 +290,7 @@ bool ClipCopyPixelRect(int texwidth, int texheight, int &originx, int &originy, int step_x; int step_y; + assert(cr != NULL); // First adjust the settings for the intended rotation switch (rotate) { @@ -362,35 +363,71 @@ bool ClipCopyPixelRect(int texwidth, int texheight, int &originx, int &originy, pstep_y = step_y; // clip source rectangle to destination - if (originx<0) + if (originx < cr->x) { - srcwidth+=originx; - patch-=originx*step_x; - originx=0; + int skip = cr->x - originx; + + srcwidth -= skip; + patch +=skip * step_x; + originx = cr->x; if (srcwidth<=0) return false; } - if (originx+srcwidth>texwidth) + if (originx + srcwidth > cr->x + cr->width) { - srcwidth=texwidth-originx; + srcwidth = cr->x + cr->width - originx; if (srcwidth<=0) return false; } - if (originy<0) + if (originy < cr->y) { - srcheight+=originy; - patch-=originy*step_y; - originy=0; - if (srcheight<=0) return false; + int skip = cr->y - originy; + + srcheight -= skip; + patch += skip*step_y; + originy = cr->y; + if (srcheight <= 0) return false; } - if (originy+srcheight>texheight) + if (originy + srcheight > cr->y + cr->height) { - srcheight=texheight-originy; - if (srcheight<=0) return false; + srcheight = cr->y + cr->height - originy; + if (srcheight <= 0) return false; } + return true; } +//=========================================================================== +// +// +// +//=========================================================================== + +bool FClipRect::Intersect(int ix, int iy, int iw, int ih) +{ + if (ix > x) + { + width -= (ix-x); + x = ix; + } + else + { + iw -= (x-ix); + } + if (iy > y) + { + height -= (iy-y); + y = iy; + } + else + { + ih -= (x-ih); + } + if (iw < width) width = iw; + if (ih < height) height = ih; + return width > 0 && height > 0; +} + //=========================================================================== // // True Color texture copy function @@ -399,7 +436,7 @@ bool ClipCopyPixelRect(int texwidth, int texheight, int &originx, int &originy, void FBitmap::CopyPixelDataRGB(int originx, int originy, const BYTE *patch, int srcwidth, int srcheight, int step_x, int step_y, int rotate, int ct, FCopyInfo *inf) { - if (ClipCopyPixelRect(Width, Height, originx, originy, patch, srcwidth, srcheight, step_x, step_y, rotate)) + if (ClipCopyPixelRect(&ClipRect, originx, originy, patch, srcwidth, srcheight, step_x, step_y, rotate)) { BYTE *buffer = data + 4 * originx + Pitch * originy; int op = inf==NULL? OP_COPY : inf->op; @@ -460,7 +497,7 @@ static const CopyPalettedFunc copypalettedfuncs[]= void FBitmap::CopyPixelData(int originx, int originy, const BYTE * patch, int srcwidth, int srcheight, int step_x, int step_y, int rotate, PalEntry * palette, FCopyInfo *inf) { - if (ClipCopyPixelRect(Width, Height, originx, originy, patch, srcwidth, srcheight, step_x, step_y, rotate)) + if (ClipCopyPixelRect(&ClipRect, originx, originy, patch, srcwidth, srcheight, step_x, step_y, rotate)) { BYTE *buffer = data + 4*originx + Pitch*originy; PalEntry penew[256]; @@ -477,3 +514,17 @@ void FBitmap::CopyPixelData(int originx, int originy, const BYTE * patch, int sr } } +//=========================================================================== +// +// Clear buffer +// +//=========================================================================== +void FBitmap::Zero() +{ + BYTE *buffer = data; + for (int y = ClipRect.y; y < ClipRect.height; ++y) + { + memset(buffer + ClipRect.x, 0, ClipRect.width*4); + buffer += Pitch; + } +} diff --git a/src/textures/bitmap.h b/src/textures/bitmap.h index 04bd580cd..4bd7f32bb 100644 --- a/src/textures/bitmap.h +++ b/src/textures/bitmap.h @@ -41,6 +41,13 @@ struct FCopyInfo; +struct FClipRect +{ + int x, y, width, height; + + bool Intersect(int ix, int iy, int iw, int ih); +}; + class FBitmap { protected: @@ -49,6 +56,8 @@ protected: int Height; int Pitch; bool FreeBuffer; + FClipRect ClipRect; + public: FBitmap() @@ -57,6 +66,7 @@ public: Width = Height = 0; Pitch = 0; FreeBuffer = false; + ClipRect.x = ClipRect.y = ClipRect.width = ClipRect.height = 0; } FBitmap(BYTE *buffer, int pitch, int width, int height) @@ -67,6 +77,9 @@ public: Width = width; Height = height; FreeBuffer = false; + ClipRect.x = ClipRect.y = 0; + ClipRect.width = width; + ClipRect.height = height; } virtual ~FBitmap() @@ -88,6 +101,9 @@ public: Height = h; data = new BYTE[4*w*h]; FreeBuffer = true; + ClipRect.x = ClipRect.y = 0; + ClipRect.width = w; + ClipRect.height = h; return data != NULL; } @@ -116,6 +132,28 @@ public: return data; } + void SetClipRect(FClipRect &clip) + { + ClipRect = clip; + } + + void IntersectClipRect(FClipRect &clip) + { + ClipRect.Intersect(clip.x, clip.y, clip.width, clip.height); + } + + void IntersectClipRect(int cx, int cy, int cw, int ch) + { + ClipRect.Intersect(cx, cy, cw, ch); + } + + const FClipRect &GetClipRect() const + { + return ClipRect; + } + + void Zero(); + virtual void CopyPixelDataRGB(int originx, int originy, const BYTE *patch, int srcwidth, int srcheight, int step_x, int step_y, int rotate, int ct, FCopyInfo *inf = NULL); @@ -125,9 +163,9 @@ public: }; -bool ClipCopyPixelRect(int texwidth, int texheight, int &originx, int &originy, - const BYTE *&patch, int &srcwidth, int &srcheight, - int &step_x, int &step_y, int rotate); +bool ClipCopyPixelRect(const FClipRect *cr, int &originx, int &originy, + const BYTE *&patch, int &srcwidth, int &srcheight, + int &step_x, int &step_y, int rotate); //=========================================================================== // diff --git a/src/textures/ddstexture.cpp b/src/textures/ddstexture.cpp index 82b96db05..3d68c4177 100644 --- a/src/textures/ddstexture.cpp +++ b/src/textures/ddstexture.cpp @@ -182,7 +182,7 @@ protected: void DecompressDXT3 (FWadLump &lump, bool premultiplied, BYTE *tcbuf = NULL); void DecompressDXT5 (FWadLump &lump, bool premultiplied, BYTE *tcbuf = NULL); - int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, int rotate, FCopyInfo *inf = NULL); + int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf = NULL); bool UseBasePalette(); friend class FTexture; @@ -874,7 +874,7 @@ void FDDSTexture::DecompressDXT5 (FWadLump &lump, bool premultiplied, BYTE *tcbu // //=========================================================================== -int FDDSTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, int rotate, FCopyInfo *inf) +int FDDSTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf) { FWadLump lump = Wads.OpenLumpNum (SourceLump); @@ -899,11 +899,8 @@ int FDDSTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, i DecompressDXT5 (lump, Format == ID_DXT4, TexBuffer); } - if (w < 0 || w > Width) w = Width; - if (h < 0 || h > Height) h = Height; - // All formats decompress to RGBA. - bmp->CopyPixelDataRGB(x, y, TexBuffer, w, h, 4, Width*4, rotate, CF_RGBA, inf); + bmp->CopyPixelDataRGB(x, y, TexBuffer, Width, Height, 4, Width*4, rotate, CF_RGBA, inf); delete [] TexBuffer; return -1; } diff --git a/src/textures/jpegtexture.cpp b/src/textures/jpegtexture.cpp index 5e41af22f..7a45e6001 100644 --- a/src/textures/jpegtexture.cpp +++ b/src/textures/jpegtexture.cpp @@ -168,7 +168,7 @@ public: const BYTE *GetPixels (); void Unload (); FTextureFormat GetFormat (); - int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, int rotate, FCopyInfo *inf = NULL); + int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf = NULL); bool UseBasePalette(); protected: @@ -445,7 +445,7 @@ void FJPEGTexture::MakeTexture () // //=========================================================================== -int FJPEGTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, int rotate, FCopyInfo *inf) +int FJPEGTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf) { PalEntry pe[256]; @@ -460,9 +460,6 @@ int FJPEGTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, cinfo.err->error_exit = JPEG_ErrorExit; jpeg_create_decompress(&cinfo); - if (w < 0 || w > Width) w = Width; - if (h < 0 || h > Height) h = Height; - try { FLumpSourceMgr sourcemgr(&lump, &cinfo); @@ -491,18 +488,18 @@ int FJPEGTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, switch (cinfo.out_color_space) { case JCS_RGB: - bmp->CopyPixelDataRGB(x, y, buff, w, h, + bmp->CopyPixelDataRGB(x, y, buff, cinfo.output_width, cinfo.output_height, 3, cinfo.output_width * cinfo.output_components, rotate, CF_RGB, inf); break; case JCS_GRAYSCALE: for(int i=0;i<256;i++) pe[i]=PalEntry(255,i,i,i); // default to a gray map - bmp->CopyPixelData(x, y, buff, w, h, + bmp->CopyPixelData(x, y, buff, cinfo.output_width, cinfo.output_height, 1, cinfo.output_width, rotate, pe, inf); break; case JCS_CMYK: - bmp->CopyPixelDataRGB(x, y, buff, w, h, + bmp->CopyPixelDataRGB(x, y, buff, cinfo.output_width, cinfo.output_height, 4, cinfo.output_width * cinfo.output_components, rotate, CF_CMYK, inf); break; diff --git a/src/textures/multipatchtexture.cpp b/src/textures/multipatchtexture.cpp index 4b9bd5e1b..faaa5db6c 100644 --- a/src/textures/multipatchtexture.cpp +++ b/src/textures/multipatchtexture.cpp @@ -158,7 +158,7 @@ public: void Unload (); virtual void SetFrontSkyLayer (); - int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, int rotate, FCopyInfo *inf = NULL); + int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf = NULL); int GetSourceLump() { return DefinitionLump; } protected: @@ -218,6 +218,7 @@ FMultiPatchTexture::FMultiPatchTexture (const void *texdef, FPatchLookup *patchl int i; mtexture.d = (const maptexture_t *)texdef; + bMultiPatch = true; if (strife) { @@ -429,7 +430,7 @@ BYTE *GetBlendMap(PalEntry blend, BYTE *blendwork) return TranslationToTable(TRANSLATION(TRANSLATION_Standard, 7))->Remap; default: - if (blend >= BLEND_SPECIALCOLORMAP1) + if (blend >= BLEND_SPECIALCOLORMAP1 && blend < BLEND_SPECIALCOLORMAP1 + SpecialColormaps.Size()) { return SpecialColormaps[blend - BLEND_SPECIALCOLORMAP1].Colormap; } @@ -542,19 +543,29 @@ void FMultiPatchTexture::MakeTexture () // //=========================================================================== -int FMultiPatchTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, int rotate, FCopyInfo *inf) +int FMultiPatchTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf) { int retv = -1; FCopyInfo info; - if (w < 0 || w > Width) w = Width; - if (h < 0 || h > Height) h = Height; + // When compositing a multipatch texture with multipatch parts + // drawing must be restricted to the actual area which is covered by this texture. + FClipRect saved_cr = bmp->GetClipRect(); + bmp->IntersectClipRect(x, y, Width, Height); - for(int i=0;iop == OP_OVERWRITE) + { + bmp->Zero(); + } + + for(int i = 0; i < NumParts; i++) { int ret = -1; - if (!Parts[i].Texture->bComplex || inf == NULL) + // rotated multipatch parts cannot be composited directly + bool rotatedmulti = Parts[i].Rotate != 0 && Parts[i].Texture->bMultiPatch; + + if ((!Parts[i].Texture->bComplex || inf == NULL) && !rotatedmulti) { memset (&info, 0, sizeof (info)); info.alpha = Parts[i].Alpha; @@ -563,17 +574,14 @@ int FMultiPatchTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, i if (Parts[i].Translation != NULL) { // Using a translation forces downconversion to the base palette - ret = Parts[i].Texture->CopyTrueColorTranslated(bmp, - x+Parts[i].OriginX, y+Parts[i].OriginY, - w-Parts[i].OriginX, h-Parts[i].OriginY, - Parts[i].Rotate, Parts[i].Translation, &info); + ret = Parts[i].Texture->CopyTrueColorTranslated(bmp, x+Parts[i].OriginX, y+Parts[i].OriginY, Parts[i].Rotate, Parts[i].Translation, &info); } else { PalEntry b = Parts[i].Blend; - if (b.a == 0 && b.r != BLEND_NONE) + if (b.a == 0 && b != BLEND_NONE) { - info.blend = EBlend(b.r); + info.blend = EBlend(b.d); } else if (b.a != 0) { @@ -594,10 +602,7 @@ int FMultiPatchTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, i info.blend = BLEND_OVERLAY; } } - ret = Parts[i].Texture->CopyTrueColorPixels(bmp, - x+Parts[i].OriginX, y+Parts[i].OriginY, - w-Parts[i].OriginX, h-Parts[i].OriginY, - Parts[i].Rotate, &info); + ret = Parts[i].Texture->CopyTrueColorPixels(bmp, x+Parts[i].OriginX, y+Parts[i].OriginY, Parts[i].Rotate, &info); } } else @@ -610,16 +615,15 @@ int FMultiPatchTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, i if (bmp1.Create(Parts[i].Texture->GetWidth(), Parts[i].Texture->GetHeight())) { Parts[i].Texture->CopyTrueColorPixels(&bmp1, 0, 0); - bmp->CopyPixelDataRGB( - x+Parts[i].OriginX, y+Parts[i].OriginY, bmp1.GetPixels(), - MIN(w-Parts[i].OriginX, bmp1.GetWidth()), - MIN(h-Parts[i].OriginY, bmp1.GetHeight()), - 4, bmp1.GetPitch()*4, Parts[i].Rotate, CF_BGRA, inf); + bmp->CopyPixelDataRGB(x+Parts[i].OriginX, y+Parts[i].OriginY, bmp1.GetPixels(), + bmp1.GetWidth(), bmp1.GetHeight(), 4, bmp1.GetPitch(), Parts[i].Rotate, CF_BGRA, inf); } } if (ret > retv) retv = ret; } + // Restore previous clipping rectangle. + bmp->SetClipRect(saved_cr); return retv; } @@ -1179,6 +1183,7 @@ FMultiPatchTexture::FMultiPatchTexture (FScanner &sc, int usetype) { TArray parts; + bMultiPatch = true; sc.SetCMode(true); sc.MustGetString(); uppercopy(Name, sc.String); diff --git a/src/textures/pcxtexture.cpp b/src/textures/pcxtexture.cpp index 283550a95..a3b72c488 100644 --- a/src/textures/pcxtexture.cpp +++ b/src/textures/pcxtexture.cpp @@ -91,7 +91,7 @@ public: void Unload (); FTextureFormat GetFormat (); - int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, int rotate, FCopyInfo *inf = NULL); + int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf = NULL); bool UseBasePalette(); protected: @@ -543,7 +543,7 @@ void FPCXTexture::MakeTexture() // //=========================================================================== -int FPCXTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, int rotate, FCopyInfo *inf) +int FPCXTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf) { PalEntry pe[256]; PCXHeader header; @@ -556,9 +556,6 @@ int FPCXTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, i bitcount = header.bitsPerPixel * header.numColorPlanes; - if (w < 0 || w > Width) w = Width; - if (h < 0 || h > Height) h = Height; - if (bitcount < 24) { Pixels = new BYTE[Width*Height]; @@ -600,13 +597,13 @@ int FPCXTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, i lump.Seek(sizeof(header), SEEK_SET); ReadPCX8bits (Pixels, lump, &header); } - bmp->CopyPixelData(x, y, Pixels, w, h, 1, Width, rotate, pe, inf); + bmp->CopyPixelData(x, y, Pixels, Width, Height, 1, Width, rotate, pe, inf); } else { Pixels = new BYTE[Width*Height * 3]; ReadPCX24bits (Pixels, lump, &header, 3); - bmp->CopyPixelDataRGB(x, y, Pixels, w, h, 3, Width*3, rotate, CF_RGB, inf); + bmp->CopyPixelDataRGB(x, y, Pixels, Width, Height, 3, Width*3, rotate, CF_RGB, inf); } delete [] Pixels; return 0; diff --git a/src/textures/pngtexture.cpp b/src/textures/pngtexture.cpp index f612681e5..d1fe3e8f5 100644 --- a/src/textures/pngtexture.cpp +++ b/src/textures/pngtexture.cpp @@ -58,7 +58,7 @@ public: const BYTE *GetPixels (); void Unload (); FTextureFormat GetFormat (); - int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, int rotate, FCopyInfo *inf = NULL); + int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf = NULL); bool UseBasePalette(); protected: @@ -583,7 +583,7 @@ void FPNGTexture::MakeTexture () // //=========================================================================== -int FPNGTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, int rotate, FCopyInfo *inf) +int FPNGTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf) { // Parse pre-IDAT chunks. I skip the CRCs. Is that bad? PalEntry pe[256]; @@ -593,9 +593,6 @@ int FPNGTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, i int pixwidth = Width * bpp[ColorType]; int transpal = false; - if (w < 0 || w > Width) w = Width; - if (h < 0 || h > Height) h = Height; - if (SourceLump >= 0) { lump = new FWadLump(Wads.OpenLumpNum(SourceLump)); @@ -654,20 +651,20 @@ int FPNGTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, i { case 0: case 3: - bmp->CopyPixelData(x, y, Pixels, w, h, 1, Width, rotate, pe, inf); + bmp->CopyPixelData(x, y, Pixels, Width, Height, 1, Width, rotate, pe, inf); break; case 2: - bmp->CopyPixelDataRGB(x, y, Pixels, w, h, 3, pixwidth, rotate, CF_RGB, inf); + bmp->CopyPixelDataRGB(x, y, Pixels, Width, Height, 3, pixwidth, rotate, CF_RGB, inf); break; case 4: - bmp->CopyPixelDataRGB(x, y, Pixels, w, h, 2, pixwidth, rotate, CF_IA, inf); + bmp->CopyPixelDataRGB(x, y, Pixels, Width, Height, 2, pixwidth, rotate, CF_IA, inf); transpal = -1; break; case 6: - bmp->CopyPixelDataRGB(x, y, Pixels, w, h, 4, pixwidth, rotate, CF_RGBA, inf); + bmp->CopyPixelDataRGB(x, y, Pixels, Width, Height, 4, pixwidth, rotate, CF_RGBA, inf); transpal = -1; break; diff --git a/src/textures/texture.cpp b/src/textures/texture.cpp index 8125c56b2..97f393cb2 100644 --- a/src/textures/texture.cpp +++ b/src/textures/texture.cpp @@ -143,9 +143,10 @@ FTexture::FTexture (const char *name, int lumpnum) : LeftOffset(0), TopOffset(0), WidthBits(0), HeightBits(0), xScale(FRACUNIT), yScale(FRACUNIT), SourceLump(lumpnum), UseType(TEX_Any), bNoDecals(false), bNoRemap0(false), bWorldPanning(false), - bMasked(true), bAlphaTexture(false), bHasCanvas(false), bWarped(0), bComplex(false), - Rotations(0xFFFF), Width(0), Height(0), WidthMask(0), Native(NULL) + bMasked(true), bAlphaTexture(false), bHasCanvas(false), bWarped(0), bComplex(false), bMultiPatch(false), + Rotations(0xFFFF), SkyOffset(0), Width(0), Height(0), WidthMask(0), Native(NULL) { + id.SetInvalid(); if (name != NULL) { uppercopy(Name, name); @@ -316,8 +317,9 @@ void FTexture::CopyToBlock (BYTE *dest, int dwidth, int dheight, int xpos, int y int srcheight = Height; int step_x = Height; int step_y = 1; + FClipRect cr = {0, 0, dwidth, dheight}; - if (ClipCopyPixelRect(dwidth, dheight, xpos, ypos, pixels, srcwidth, srcheight, step_x, step_y, rotate)) + if (ClipCopyPixelRect(&cr, xpos, ypos, pixels, srcwidth, srcheight, step_x, step_y, rotate)) { dest += ypos + dheight * xpos; if (translation == NULL) @@ -498,7 +500,7 @@ void FTexture::FillBuffer(BYTE *buff, int pitch, int height, FTextureFormat fmt) { FCopyInfo inf = {OP_OVERWRITE, }; FBitmap bmp(buff, pitch, pitch/4, height); - CopyTrueColorPixels(&bmp, 0, 0, -1, -1, 0, &inf); + CopyTrueColorPixels(&bmp, 0, 0, 0, &inf); break; } @@ -519,23 +521,19 @@ void FTexture::FillBuffer(BYTE *buff, int pitch, int height, FTextureFormat fmt) // //=========================================================================== -int FTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, int rotate, FCopyInfo *inf) +int FTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf) { PalEntry *palette = screen->GetPalette(); for(int i=1;i<256;i++) palette[i].a = 255; // set proper alpha values - if (w < 0 || w > Width) w = Width; - if (h < 0 || h > Height) h = Height; - bmp->CopyPixelData(x, y, GetPixels(), w, h, Height, 1, rotate, palette, inf); + bmp->CopyPixelData(x, y, GetPixels(), Width, Height, Height, 1, rotate, palette, inf); for(int i=1;i<256;i++) palette[i].a = 0; return 0; } -int FTexture::CopyTrueColorTranslated(FBitmap *bmp, int x, int y, int w, int h, int rotate, FRemapTable *remap, FCopyInfo *inf) +int FTexture::CopyTrueColorTranslated(FBitmap *bmp, int x, int y, int rotate, FRemapTable *remap, FCopyInfo *inf) { PalEntry *palette = remap->Palette; - if (w < 0 || w > Width) w = Width; - if (h < 0 || h > Height) h = Height; - bmp->CopyPixelData(x, y, GetPixels(), w, h, Height, 1, rotate, palette, inf); + bmp->CopyPixelData(x, y, GetPixels(), Width, Height, Height, 1, rotate, palette, inf); return 0; } diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index 7a66a7202..2d3e2fc9f 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -320,7 +320,7 @@ FTextureID FTextureManager::AddTexture (FTexture *texture) int trans = Textures.Push (hasher); Translation.Push (trans); if (bucket >= 0) HashFirst[bucket] = trans; - return FTextureID(trans); + return (texture->id = FTextureID(trans)); } //========================================================================== @@ -365,10 +365,15 @@ void FTextureManager::ReplaceTexture (FTextureID picnum, FTexture *newtexture, b newtexture->UseType = oldtexture->UseType; Textures[index].Texture = newtexture; + newtexture->id = oldtexture->id; if (free) { delete oldtexture; } + else + { + oldtexture->id.SetInvalid(); + } } //========================================================================== diff --git a/src/textures/textures.h b/src/textures/textures.h index 195295a5c..4bfffe54b 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -100,6 +100,7 @@ public: fixed_t yScale; int SourceLump; + FTextureID id; union { @@ -118,8 +119,10 @@ public: BYTE bComplex:1; // Will be used to mark extended MultipatchTextures that have to be // fully composited before subjected to any kinf of postprocessing instead of // doing it per patch. + BYTE bMultiPatch:1; // This is a multipatch texture (we really could use real type info for textures...) WORD Rotations; + SWORD SkyOffset; enum // UseTypes { @@ -151,11 +154,12 @@ public: // Returns the whole texture, stored in column-major order virtual const BYTE *GetPixels () = 0; - virtual int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w=-1, int h=-1, int rotate=0, FCopyInfo *inf = NULL); - int CopyTrueColorTranslated(FBitmap *bmp, int x, int y,int w, int h, int rotate, FRemapTable *remap, FCopyInfo *inf = NULL); + virtual int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate=0, FCopyInfo *inf = NULL); + int CopyTrueColorTranslated(FBitmap *bmp, int x, int y, int rotate, FRemapTable *remap, FCopyInfo *inf = NULL); virtual bool UseBasePalette(); virtual int GetSourceLump() { return SourceLump; } virtual FTexture *GetRedirect(bool wantwarped); + FTextureID GetID() const { return id; } virtual void Unload () = 0; @@ -176,9 +180,13 @@ public: int GetScaledWidth () { int foo = (Width << 17) / xScale; return (foo >> 1) + (foo & 1); } int GetScaledHeight () { int foo = (Height << 17) / yScale; return (foo >> 1) + (foo & 1); } + double GetScaledWidthDouble () { return (Width * 65536.f) / xScale; } + double GetScaledHeightDouble () { return (Height * 65536.f) / yScale; } int GetScaledLeftOffset () { int foo = (LeftOffset << 17) / xScale; return (foo >> 1) + (foo & 1); } int GetScaledTopOffset () { int foo = (TopOffset << 17) / yScale; return (foo >> 1) + (foo & 1); } + double GetScaledLeftOffsetDouble() { return (LeftOffset * 65536.f) / xScale; } + double GetScaledTopOffsetDouble() { return (TopOffset * 65536.f) / yScale; } virtual void SetFrontSkyLayer(); @@ -278,6 +286,12 @@ public: return Textures[Translation[texnum.texnum]].Texture; } + FTexture *ByIndexTranslated(int i) + { + if (unsigned(i) >= Textures.Size()) return NULL; + return Textures[Translation[i]].Texture; + } + void SetTranslation (FTextureID fromtexnum, FTextureID totexnum) { if ((size_t)fromtexnum.texnum < Translation.Size()) diff --git a/src/textures/tgatexture.cpp b/src/textures/tgatexture.cpp index 41bbb11b8..3e30a13d1 100644 --- a/src/textures/tgatexture.cpp +++ b/src/textures/tgatexture.cpp @@ -86,7 +86,7 @@ public: void Unload (); FTextureFormat GetFormat (); - int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, int rotate, FCopyInfo *inf = NULL); + int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf = NULL); bool UseBasePalette(); protected: @@ -487,12 +487,12 @@ void FTGATexture::MakeTexture () // //=========================================================================== -int FTGATexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, int rotate, FCopyInfo *inf) +int FTGATexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf) { PalEntry pe[256]; FWadLump lump = Wads.OpenLumpNum (SourceLump); TGAHeader hdr; - WORD wr; + WORD w; BYTE r,g,b,a; BYTE * sbuffer; int transval = 0; @@ -500,9 +500,6 @@ int FTGATexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, i lump.Read(&hdr, sizeof(hdr)); lump.Seek(hdr.id_len, SEEK_CUR); - if (w < 0 || w > Width) w = Width; - if (h < 0 || h > Height) h = Height; - hdr.width = LittleShort(hdr.width); hdr.height = LittleShort(hdr.height); hdr.cm_first = LittleShort(hdr.cm_first); @@ -517,10 +514,10 @@ int FTGATexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, i { case 15: case 16: - lump >> wr; - r = (wr & 0x001F) << 3; - g = (wr & 0x03E0) >> 2; - b = (wr & 0x7C00) >> 7; + lump >> w; + r = (w & 0x001F) << 3; + g = (w & 0x03E0) >> 2; + b = (w & 0x7C00) >> 7; a = 255; break; @@ -575,7 +572,7 @@ int FTGATexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, i switch (hdr.img_type & 7) { case 1: // paletted - bmp->CopyPixelData(x, y, ptr, w, h, step_x, Pitch, rotate, pe, inf); + bmp->CopyPixelData(x, y, ptr, Width, Height, step_x, Pitch, rotate, pe, inf); break; case 2: // RGB @@ -583,21 +580,21 @@ int FTGATexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, i { case 15: case 16: - bmp->CopyPixelDataRGB(x, y, ptr, w, h, step_x, Pitch, rotate, CF_RGB555, inf); + bmp->CopyPixelDataRGB(x, y, ptr, Width, Height, step_x, Pitch, rotate, CF_RGB555, inf); break; case 24: - bmp->CopyPixelDataRGB(x, y, ptr, w, h, step_x, Pitch, rotate, CF_BGR, inf); + bmp->CopyPixelDataRGB(x, y, ptr, Width, Height, step_x, Pitch, rotate, CF_BGR, inf); break; case 32: if ((hdr.img_desc&15)!=8) // 32 bits without a valid alpha channel { - bmp->CopyPixelDataRGB(x, y, ptr, w, h, step_x, Pitch, rotate, CF_BGR, inf); + bmp->CopyPixelDataRGB(x, y, ptr, Width, Height, step_x, Pitch, rotate, CF_BGR, inf); } else { - bmp->CopyPixelDataRGB(x, y, ptr, w, h, step_x, Pitch, rotate, CF_BGRA, inf); + bmp->CopyPixelDataRGB(x, y, ptr, Width, Height, step_x, Pitch, rotate, CF_BGRA, inf); transval = -1; } break; @@ -612,11 +609,11 @@ int FTGATexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, i { case 8: for(int i=0;i<256;i++) pe[i]=PalEntry(255,i,i,i); // gray map - bmp->CopyPixelData(x, y, ptr, w, h, step_x, Pitch, rotate, pe, inf); + bmp->CopyPixelData(x, y, ptr, Width, Height, step_x, Pitch, rotate, pe, inf); break; case 16: - bmp->CopyPixelDataRGB(x, y, ptr, w, h, step_x, Pitch, rotate, CF_I16, inf); + bmp->CopyPixelDataRGB(x, y, ptr, Width, Height, step_x, Pitch, rotate, CF_I16, inf); break; default: diff --git a/src/textures/warptexture.cpp b/src/textures/warptexture.cpp index 8d6469779..360d1d38a 100644 --- a/src/textures/warptexture.cpp +++ b/src/textures/warptexture.cpp @@ -229,7 +229,7 @@ void FWarp2Texture::MakeTexture (DWORD time) FTexture *FWarpTexture::GetRedirect(bool wantwarped) { - if (!wantwarped) return SourcePic; + if (!wantwarped) return SourcePic->GetRedirect(false); else return this; } @@ -242,8 +242,8 @@ FTexture *FWarpTexture::GetRedirect(bool wantwarped) // //========================================================================== -int FWarpTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int w, int h, int rotate, FCopyInfo *inf) +int FWarpTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf) { - return SourcePic->CopyTrueColorPixels(bmp, x, y, w, h, rotate, inf); + return SourcePic->CopyTrueColorPixels(bmp, x, y, rotate, inf); } diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index c12f41a52..508467d16 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -364,12 +364,24 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StopSoundEx) // Generic seeker missile function // //========================================================================== +static FRandom pr_seekermissile ("SeekerMissile"); +enum +{ + SMF_LOOK = 1, +}; DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SeekerMissile) { PARAM_ACTION_PROLOGUE; PARAM_INT(ang1); PARAM_INT(ang2); + PARAM_INT_OPT(flags) { flags = 0; } + PARAM_INT_OPT(chance) { chance = 50; } + PARAM_INT_OPT(distance) { distance = 10; } + if ((flags & SMF_LOOK) && (self->tracer == 0) && (pr_seekermissile()tracer = P_RoughMonsterSearch (self, distance); + } P_SeekerMissile(self, clamp(ang1, 0, 90) * ANGLE_1, clamp(ang2, 0, 90) * ANGLE_1); return 0; } @@ -636,10 +648,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Explode) } P_RadiusAttack (self, self->target, damage, distance, self->DamageType, hurtSource, true, fulldmgdistance); - if (self->z <= self->floorz + (distance<target != NULL && self->target->player != NULL) { validcount++; @@ -665,10 +674,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusThrust) if (distance <= 0) distance = force; P_RadiusAttack (self, self->target, force, distance, self->DamageType, affectSource, false); - if (self->z <= self->floorz + (distance << FRACBITS)) - { - P_HitFloor (self); - } + P_CheckSplash(self, distance<DamageType, pufftype); + P_LineAttack(self, angle, range, slope, damage, NAME_None, pufftype); } } return 0; @@ -1038,7 +1044,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets) if ((numbullets == 1 && !player->refire) || numbullets == 0) { int damage = ((pr_cwbullet()%3)+1) * damageperbullet; - P_LineAttack(self, bangle, range, bslope, damage, GetDefaultByType(pufftype)->DamageType, pufftype); + P_LineAttack(self, bangle, range, bslope, damage, NAME_None, pufftype); } else { @@ -1049,7 +1055,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets) int angle = bangle + pr_cwbullet.Random2() * (spread_xy / 255); int slope = bslope + pr_cwbullet.Random2() * (spread_z / 255); int damage = ((pr_cwbullet()%3)+1) * damageperbullet; - P_LineAttack(self, angle, range, slope, damage, GetDefaultByType(pufftype)->DamageType, pufftype); + P_LineAttack(self, angle, range, slope, damage, NAME_None, pufftype); } } return 0; @@ -1167,7 +1173,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) if (pufftype == NULL) pufftype = PClass::FindClass(NAME_BulletPuff); - P_LineAttack (self, angle, range, pitch, damage, GetDefaultByType(pufftype)->DamageType, pufftype, true); + P_LineAttack (self, angle, range, pitch, damage, NAME_None, pufftype, true); // turn to face target if (linetarget) @@ -1243,9 +1249,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun) PARAM_FLOAT_OPT (maxdiff) { maxdiff = 0; } PARAM_CLASS_OPT (pufftype, AActor) { pufftype = PClass::FindClass(NAME_BulletPuff); } + AActor *linetarget; + fixed_t saved_x = self->x; fixed_t saved_y = self->y; angle_t saved_angle = self->angle; + fixed_t saved_pitch = self->pitch; if (aim && self->target == NULL) { @@ -1267,9 +1276,15 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun) self->target->x, self->target->y); } - - self->pitch = P_AimLineAttack (self, self->angle, MISSILERANGE); - + self->pitch = P_AimLineAttack (self, self->angle, MISSILERANGE, &linetarget, ANGLE_1*60, false, false, false, aim ? self->target : NULL); + if (linetarget == NULL && aim) + { + // We probably won't hit the target, but aim at it anyway so we don't look stupid. + FVector2 xydiff(self->target->x - self->x, self->target->y - self->y); + double zdiff = (self->target->z + (self->target->height>>1)) - + (self->z + (self->height>>1) - self->floorclip); + self->pitch = int(atan2(zdiff, xydiff.Length()) * ANGLE_180 / -M_PI); + } // Let the aim trail behind the player if (aim) { @@ -1304,6 +1319,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun) self->x = saved_x; self->y = saved_y; self->angle = saved_angle; + self->pitch = saved_pitch; return 0; } @@ -1692,17 +1708,33 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ThrowGrenade) ALLOW_REPLACE); if (bo) { - int pitch = self->pitch; - P_PlaySpawnSound(bo, self); - if (xyvel) + if (xyvel != 0) bo->Speed = xyvel; bo->angle = self->angle + (((pr_grenade()&7) - 4) << 24); - bo->velz = zvel + 2*finesine[pitch>>ANGLETOFINESHIFT]; - bo->z += 2 * finesine[pitch>>ANGLETOFINESHIFT]; - P_ThrustMobj(bo, bo->angle, bo->Speed); - bo->velx += self->velx >> 1; - bo->vely += self->vely >> 1; + + angle_t pitch = angle_t(-self->pitch) >> ANGLETOFINESHIFT; + angle_t angle = bo->angle >> ANGLETOFINESHIFT; + + // There are two vectors we are concerned about here: xy and z. We rotate + // them separately according to the shooter's pitch and then sum them to + // get the final velocity vector to shoot with. + + fixed_t xy_xyscale = FixedMul(bo->Speed, finecosine[pitch]); + fixed_t xy_velz = FixedMul(bo->Speed, finesine[pitch]); + fixed_t xy_velx = FixedMul(xy_xyscale, finecosine[angle]); + fixed_t xy_vely = FixedMul(xy_xyscale, finesine[angle]); + + pitch = angle_t(self->pitch) >> ANGLETOFINESHIFT; + fixed_t z_xyscale = FixedMul(zvel, finesine[pitch]); + fixed_t z_velz = FixedMul(zvel, finecosine[pitch]); + fixed_t z_velx = FixedMul(z_xyscale, finecosine[angle]); + fixed_t z_vely = FixedMul(z_xyscale, finesine[angle]); + + bo->velx = xy_velx + z_velx + (self->velx >> 1); + bo->vely = xy_vely + z_vely + (self->vely >> 1); + bo->velz = xy_velz + z_velz; + bo->target= self; P_CheckMissileSpawn (bo); } @@ -1793,7 +1825,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Print) { con_midtime = float(time); } - C_MidPrint(font != NULL ? font : SmallFont, text); + FString formatted = strbin1(text); + C_MidPrint(font != NULL ? font : SmallFont, formatted.GetChars()); con_midtime = saved; } return 0; @@ -1823,7 +1856,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PrintBold) { con_midtime = float(time); } - C_MidPrintBold(font != NULL ? font : SmallFont, text); + FString formatted = strbin1(text); + C_MidPrintBold(font != NULL ? font : SmallFont, formatted.GetChars()); con_midtime = saved; return 0; } @@ -1842,6 +1876,20 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Log) return 0; } +//=========================================================================== +// +// A_LogInt +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogInt) +{ + PARAM_ACTION_PROLOGUE; + PARAM_INT(num); + Printf("%d\n", num); + return 0; +} + //=========================================================================== // // A_SetTranslucent @@ -2044,12 +2092,14 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIf) // A_KillMaster // //=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_KillMaster) +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillMaster) { PARAM_ACTION_PROLOGUE; + PARAM_NAME_OPT(damagetype) { damagetype = NAME_None; } + if (self->master != NULL) { - P_DamageMobj(self->master, self, self, self->master->health, NAME_None, DMG_NO_ARMOR); + P_DamageMobj(self->master, self, self, self->master->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); } return 0; } @@ -2059,9 +2109,11 @@ DEFINE_ACTION_FUNCTION(AActor, A_KillMaster) // A_KillChildren // //=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_KillChildren) +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillChildren) { PARAM_ACTION_PROLOGUE; + PARAM_NAME_OPT(damagetype) { damagetype = NAME_None; } + TThinkerIterator it; AActor *mo; @@ -2069,7 +2121,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_KillChildren) { if (mo->master == self) { - P_DamageMobj(mo, self, self, mo->health, NAME_None, DMG_NO_ARMOR); + P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); } } return 0; @@ -2080,9 +2132,11 @@ DEFINE_ACTION_FUNCTION(AActor, A_KillChildren) // A_KillSiblings // //=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_KillSiblings) +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_KillSiblings) { PARAM_ACTION_PROLOGUE; + PARAM_NAME_OPT(damagetype) { damagetype = NAME_None; } + TThinkerIterator it; AActor *mo; @@ -2090,7 +2144,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_KillSiblings) { if (mo->master == self->master && mo != self) { - P_DamageMobj(mo, self, self, mo->health, NAME_None, DMG_NO_ARMOR); + P_DamageMobj(mo, self, self, mo->health, damagetype, DMG_NO_ARMOR | DMG_NO_FACTOR); } } return 0; @@ -2105,6 +2159,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CountdownArg) { PARAM_ACTION_PROLOGUE; PARAM_INT(argnum); + PARAM_STATE_OPT(state) { state = self->FindState(NAME_Death); } if (argnum > 0 && argnum < countof(self->args)) { @@ -2123,6 +2178,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CountdownArg) self->SetState(self->FindState(NAME_Death)); } } + else + { + self->SetState(state); + } } return 0; } @@ -3059,21 +3118,64 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpecial) //=========================================================================== // -// A_SetVar +// A_SetUserVar // //=========================================================================== DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVar) { PARAM_ACTION_PROLOGUE; - PARAM_INT(pos); - PARAM_INT(value); + PARAM_NAME (varname); + PARAM_INT (value); - if (pos < 0 || pos >= countof(self->uservar)) + PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true); + PSymbolVariable *var; + + if (sym == NULL || sym->SymbolType != SYM_Variable || + !(var = static_cast(sym))->bUserVar || + var->ValueType.Type != VAL_Int) + { + Printf("%s is not a user variable in class %s\n", varname.GetChars(), + self->GetClass()->TypeName.GetChars()); return 0; - - // Set the value of the specified arg - self->uservar[pos] = value; + } + // Set the value of the specified user variable. + *(int *)(reinterpret_cast(self) + var->offset) = value; + return 0; +} + +//=========================================================================== +// +// A_SetUserArray +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray) +{ + PARAM_ACTION_PROLOGUE; + PARAM_NAME (varname); + PARAM_INT (pos); + PARAM_INT (value); + + PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true); + PSymbolVariable *var; + + if (sym == NULL || sym->SymbolType != SYM_Variable || + !(var = static_cast(sym))->bUserVar || + var->ValueType.Type != VAL_Array || var->ValueType.BaseType != VAL_Int) + { + Printf("%s is not a user array in class %s\n", varname.GetChars(), + self->GetClass()->TypeName.GetChars()); + return 0; + } + if (pos < 0 || pos >= var->ValueType.size) + { + Printf("%d is out of bounds in array %s in class %s\n", pos, varname.GetChars(), + self->GetClass()->TypeName.GetChars()); + return 0; + } + // Set the value of the specified user array at index pos. + ((int *)(reinterpret_cast(self) + var->offset))[pos] = value; return 0; } @@ -3091,6 +3193,78 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Turn) return 0; } +//=========================================================================== +// +// A_Quake +// +//=========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Quake) +{ + PARAM_ACTION_PROLOGUE; + PARAM_INT (intensity); + PARAM_INT (duration); + PARAM_INT (damrad); + PARAM_INT (tremrad); + PARAM_SOUND_OPT (sound) { sound = "world/quake"; } + + P_StartQuake(self, 0, intensity, duration, damrad, tremrad, sound); + return 0; +} + +//=========================================================================== +// +// A_Weave +// +//=========================================================================== + +void A_Weave(AActor *self, int xyspeed, int zspeed, fixed_t xydist, fixed_t zdist) +{ + fixed_t newX, newY; + int weaveXY, weaveZ; + int angle; + fixed_t dist; + + weaveXY = self->WeaveIndexXY & 63; + weaveZ = self->WeaveIndexZ & 63; + angle = (self->angle + ANG90) >> ANGLETOFINESHIFT; + + if (xydist != 0 && xyspeed != 0) + { + dist = FixedMul(FloatBobOffsets[weaveXY], xydist); + newX = self->x - FixedMul (finecosine[angle], dist); + newY = self->y - FixedMul (finesine[angle], dist); + weaveXY = (weaveXY + xyspeed) & 63; + dist = FixedMul(FloatBobOffsets[weaveXY], xydist); + newX += FixedMul (finecosine[angle], dist); + newY += FixedMul (finesine[angle], dist); + P_TryMove (self, newX, newY, true); + self->WeaveIndexXY = weaveXY; + } + + if (zdist != 0 && zspeed != 0) + { + self->z -= FixedMul(FloatBobOffsets[weaveZ], zdist); + weaveZ = (weaveZ + zspeed) & 63; + self->z += FixedMul(FloatBobOffsets[weaveZ], zdist); + self->WeaveIndexZ = weaveZ; + } +} + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Weave) +{ + PARAM_ACTION_PROLOGUE; + PARAM_INT (xspeed); + PARAM_INT (yspeed); + PARAM_FIXED (xdist); + PARAM_FIXED (ydist); + A_Weave(self, xspeed, yspeed, xdist, ydist); + return 0; +} + + + + //=========================================================================== // // A_LineEffect diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index b063d98dc..a3e17351c 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -92,6 +92,7 @@ static FFlagDef ActorFlags[]= DEFINE_FLAG(MF, NOLIFTDROP, AActor, flags), DEFINE_FLAG(MF, STEALTH, AActor, flags), DEFINE_FLAG(MF, ICECORPSE, AActor, flags), + DEFINE_FLAG(MF2, DONTREFLECT, AActor, flags2), DEFINE_FLAG(MF2, WINDTHRUST, AActor, flags2), DEFINE_FLAG(MF2, DONTSEEKINVISIBLE, AActor, flags2), @@ -121,6 +122,7 @@ static FFlagDef ActorFlags[]= DEFINE_FLAG(MF2, DORMANT, AActor, flags2), DEFINE_FLAG(MF2, SEEKERMISSILE, AActor, flags2), DEFINE_FLAG(MF2, REFLECTIVE, AActor, flags2), + DEFINE_FLAG(MF3, FLOORHUGGER, AActor, flags3), DEFINE_FLAG(MF3, CEILINGHUGGER, AActor, flags3), DEFINE_FLAG(MF3, NORADIUSDMG, AActor, flags3), @@ -141,13 +143,15 @@ static FFlagDef ActorFlags[]= DEFINE_FLAG(MF3, NOTARGET, AActor, flags3), DEFINE_FLAG(MF3, DONTGIB, AActor, flags3), DEFINE_FLAG(MF3, NOBLOCKMONST, AActor, flags3), - DEFINE_FLAG(MF3, AVOIDMELEE, AActor, flags3), DEFINE_FLAG(MF3, FULLVOLDEATH, AActor, flags3), + DEFINE_FLAG(MF3, AVOIDMELEE, AActor, flags3), + DEFINE_FLAG(MF3, SCREENSEEKER, AActor, flags3), DEFINE_FLAG(MF3, FOILINVUL, AActor, flags3), DEFINE_FLAG(MF3, NOTELEOTHER, AActor, flags3), DEFINE_FLAG(MF3, BLOODLESSIMPACT, AActor, flags3), DEFINE_FLAG(MF3, NOEXPLODEFLOOR, AActor, flags3), DEFINE_FLAG(MF3, PUFFONACTORS, AActor, flags3), + DEFINE_FLAG(MF4, QUICKTORETALIATE, AActor, flags4), DEFINE_FLAG(MF4, NOICEDEATH, AActor, flags4), DEFINE_FLAG(MF4, RANDOMIZE, AActor, flags4), @@ -219,6 +223,7 @@ static FFlagDef ActorFlags[]= DEFINE_FLAG(MF6, CANJUMP, AActor, flags6), DEFINE_FLAG(MF6, JUMPDOWN, AActor, flags6), DEFINE_FLAG(MF6, VULNERABLE, AActor, flags6), + DEFINE_FLAG(MF6, NOTRIGGER, AActor, flags6), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 5167bba6a..e43dbcb5b 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -50,6 +50,7 @@ #include "p_lnspec.h" #include "doomstat.h" #include "thingdef_exp.h" +#include "m_fixed.h" #include "vmbuilder.h" // Accessible actor member variables @@ -77,7 +78,6 @@ DEFINE_MEMBER_VARIABLE_ALIAS(momy, vely, AActor) DEFINE_MEMBER_VARIABLE_ALIAS(momz, velz, AActor) DEFINE_MEMBER_VARIABLE(Damage, AActor) DEFINE_MEMBER_VARIABLE(Score, AActor) -DEFINE_MEMBER_VARIABLE(uservar, AActor) ExpEmit::ExpEmit(VMFunctionBuilder *build, int type) : RegNum(build->Registers[type].Get(1)), RegType(type), Konst(false), Fixed(false) diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index 42d1f0c04..ed3ed4b15 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -158,7 +158,8 @@ FxExpression *ParseParameter(FScanner &sc, PClass *cls, char type, bool constant x = ParseExpression (sc, cls); if (constant && !x->isConstant()) { - sc.ScriptError("Default parameter must be constant."); + sc.ScriptMessage("Default parameter must be constant."); + FScriptPosition::ErrorCounter++; } // Do automatic coercion between ints and floats. if (type == 'x' || type == 'X') @@ -220,13 +221,15 @@ static void ParseConstant (FScanner &sc, PSymbolTable * symt, PClass *cls) if (symt->AddSymbol (sym) == NULL) { delete sym; - sc.ScriptError ("'%s' is already defined in '%s'.", + sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls? cls->TypeName.GetChars() : "Global"); + FScriptPosition::ErrorCounter++; } } else { - sc.ScriptError("Numeric type required for constant"); + sc.ScriptMessage("Numeric type required for constant"); + FScriptPosition::ErrorCounter++; } } @@ -259,8 +262,9 @@ static void ParseEnum (FScanner &sc, PSymbolTable *symt, PClass *cls) if (symt->AddSymbol (sym) == NULL) { delete sym; - sc.ScriptError ("'%s' is already defined in '%s'.", + sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls? cls->TypeName.GetChars() : "Global"); + FScriptPosition::ErrorCounter++; } // This allows a comma after the last value but doesn't enforce it. if (sc.CheckToken('}')) break; @@ -272,19 +276,20 @@ static void ParseEnum (FScanner &sc, PSymbolTable *symt, PClass *cls) //========================================================================== // -// ActorConstDef +// ParseNativeVariable // -// Parses a constant definition. +// Parses a native variable declaration. // //========================================================================== -static void ParseVariable (FScanner &sc, PSymbolTable * symt, PClass *cls) +static void ParseNativeVariable (FScanner &sc, PSymbolTable * symt, PClass *cls) { FExpressionType valuetype; if (sc.LumpNum == -1 || Wads.GetLumpFile(sc.LumpNum) > 0) { - sc.ScriptError ("variables can only be imported by internal class and actor definitions!"); + sc.ScriptMessage ("variables can only be imported by internal class and actor definitions!"); + FScriptPosition::ErrorCounter++; } // Read the type and make sure it's int or float. @@ -343,12 +348,80 @@ static void ParseVariable (FScanner &sc, PSymbolTable * symt, PClass *cls) PSymbolVariable *sym = new PSymbolVariable(symname); sym->offset = vi->address; // todo sym->ValueType = valuetype; + sym->bUserVar = false; if (symt->AddSymbol (sym) == NULL) { delete sym; - sc.ScriptError ("'%s' is already defined in '%s'.", + sc.ScriptMessage ("'%s' is already defined in '%s'.", symname.GetChars(), cls? cls->TypeName.GetChars() : "Global"); + FScriptPosition::ErrorCounter++; + } +} + +//========================================================================== +// +// ParseUserVariable +// +// Parses a user variable declaration. +// +//========================================================================== + +static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClass *cls) +{ + FExpressionType valuetype; + + // Only non-native classes may have user variables. + if (!cls->bRuntimeClass) + { + sc.ScriptError("Native classes may not have user variables"); + } + + // Read the type and make sure it's int. + sc.MustGetAnyToken(); + if (sc.TokenType != TK_Int) + { + sc.ScriptMessage("User variables must be of type int"); + FScriptPosition::ErrorCounter++; + } + valuetype = VAL_Int; + + sc.MustGetToken(TK_Identifier); + // For now, restrict user variables to those that begin with "user_" to guarantee + // no clashes with internal member variable names. + if (sc.StringLen < 6 || strnicmp("user_", sc.String, 5) != 0) + { + sc.ScriptMessage("User variable names must begin with \"user_\""); + FScriptPosition::ErrorCounter++; + } + + FName symname = sc.String; + if (sc.CheckToken('[')) + { + FxExpression *expr = ParseExpression(sc, cls); + int maxelems = expr->EvalExpression(NULL).GetInt(); + delete expr; + sc.MustGetToken(']'); + if (maxelems <= 0) + { + sc.ScriptMessage("Array size must be positive"); + FScriptPosition::ErrorCounter++; + maxelems = 1; + } + valuetype.MakeArray(maxelems); + } + sc.MustGetToken(';'); + + PSymbolVariable *sym = new PSymbolVariable(symname); + sym->offset = cls->Extend(sizeof(int) * (valuetype.Type == VAL_Array ? valuetype.size : 1)); + sym->ValueType = valuetype; + sym->bUserVar = true; + if (symt->AddSymbol(sym) == NULL) + { + delete sym; + sc.ScriptMessage ("'%s' is already defined in '%s'.", + symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global"); + FScriptPosition::ErrorCounter++; } } @@ -404,12 +477,13 @@ void HandleActorFlag(FScanner &sc, Baggage &bag, const char *part1, const char * { if (part2 == NULL) { - sc.ScriptError("\"%s\" is an unknown flag\n", part1); + sc.ScriptMessage("\"%s\" is an unknown flag\n", part1); } else { - sc.ScriptError("\"%s.%s\" is an unknown flag\n", part1, part2); + sc.ScriptMessage("\"%s.%s\" is an unknown flag\n", part1, part2); } + FScriptPosition::ErrorCounter++; } } @@ -487,6 +561,11 @@ static int ParseThingActivation (FScanner &sc) { "THINGSPEC_MonsterTrigger", THINGSPEC_MonsterTrigger}, { "THINGSPEC_MissileTrigger", THINGSPEC_MissileTrigger}, { "THINGSPEC_ClearSpecial", THINGSPEC_ClearSpecial}, + { "THINGSPEC_NoDeathSpecial", THINGSPEC_NoDeathSpecial}, + { "THINGSPEC_TriggerActs", THINGSPEC_TriggerActs}, + { "THINGSPEC_Activate", THINGSPEC_Activate}, + { "THINGSPEC_Deactivate", THINGSPEC_Deactivate}, + { "THINGSPEC_Switch", THINGSPEC_Switch}, { NULL, 0 } }; @@ -537,13 +616,18 @@ static FState *CheckState(FScanner &sc, PClass *type) if (v!=0 && state==NULL) { - sc.ScriptError("Attempt to get invalid state from actor %s\n", type->ParentClass->TypeName.GetChars()); + sc.ScriptMessage("Attempt to get invalid state from actor %s\n", type->ParentClass->TypeName.GetChars()); + FScriptPosition::ErrorCounter++; return NULL; } state+=v; return state; } - else sc.ScriptError("Invalid state assignment"); + else + { + sc.ScriptMessage("Invalid state assignment"); + FScriptPosition::ErrorCounter++; + } } return NULL; } @@ -611,11 +695,15 @@ static bool ParsePropertyParams(FScanner &sc, FPropertyInfo *prop, AActor *defau // fall through case 'S': - case 'T': sc.MustGetString(); conv.s = strings[strings.Reserve(1)] = sc.String; break; + case 'T': + sc.MustGetString(); + conv.s = strings[strings.Reserve(1)] = strbin1(sc.String); + break; + case 'C': if (sc.CheckNumber ()) { @@ -1008,6 +1096,12 @@ static FActorInfo *ParseActorHeader(FScanner &sc, Baggage *bag) // Get actor name sc.MustGetString (); replaceName = sc.String; + + if (replaceName == typeName) + { + sc.ScriptMessage ("Cannot replace class %s with itself", typeName.GetChars()); + FScriptPosition::ErrorCounter++; + } } // Now, after the actor names have been parsed, it is time to switch to C-mode @@ -1079,7 +1173,11 @@ static void ParseActor(FScanner &sc) break; case TK_Native: - ParseVariable (sc, &info->Class->Symbols, info->Class); + ParseNativeVariable (sc, &info->Class->Symbols, info->Class); + break; + + case TK_Var: + ParseUserVariable (sc, &info->Class->Symbols, info->Class); break; case TK_Identifier: @@ -1140,7 +1238,7 @@ void ParseDecorate (FScanner &sc) break; case TK_Native: - ParseVariable(sc, &GlobalSymbols, NULL); + ParseNativeVariable(sc, &GlobalSymbols, NULL); break; case ';': @@ -1175,9 +1273,6 @@ void ParseDecorate (FScanner &sc) break; } default: - // without the option of game filters following, anything but an opening brace - // here means a syntax error. - sc.MustGetStringName("{"); sc.RestorePos(pos); ParseOldDecoration(sc, DEF_Decoration); break; diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index 92e259f0c..cd2537db0 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -186,7 +186,7 @@ int MatchString (const char *in, const char **strings) //========================================================================== // //========================================================================== -DEFINE_INFO_PROPERTY(game, T, Actor) +DEFINE_INFO_PROPERTY(game, S, Actor) { PROP_STRING_PARM(str, 0); if (!stricmp(str, "Doom")) @@ -363,6 +363,16 @@ DEFINE_PROPERTY(painchance, ZI, Actor) } } +//========================================================================== +// +//========================================================================== +DEFINE_PROPERTY(painthreshold, I, Actor) +{ + PROP_INT_PARM(id, 0); + + defaults->PainThreshold = id; +} + //========================================================================== // //========================================================================== @@ -892,6 +902,24 @@ DEFINE_PROPERTY(bouncecount, I, Actor) defaults->bouncecount = id; } +//========================================================================== +// +//========================================================================== +DEFINE_PROPERTY(weaveindexXY, I, Actor) +{ + PROP_INT_PARM(id, 0); + defaults->WeaveIndexXY = id; +} + +//========================================================================== +// +//========================================================================== +DEFINE_PROPERTY(weaveindexZ, I, Actor) +{ + PROP_INT_PARM(id, 0); + defaults->WeaveIndexZ = id; +} + //========================================================================== // //========================================================================== @@ -914,18 +942,25 @@ DEFINE_PROPERTY(damagetype, S, Actor) //========================================================================== // //========================================================================== -DEFINE_PROPERTY(damagefactor, SF, Actor) +DEFINE_PROPERTY(damagefactor, ZF, Actor) { PROP_STRING_PARM(str, 0); PROP_FIXED_PARM(id, 1); - if (info->DamageFactors == NULL) info->DamageFactors=new DmgFactors; + if (str == NULL) + { + defaults->DamageFactor = id; + } + else + { + if (info->DamageFactors == NULL) info->DamageFactors=new DmgFactors; - FName dmgType; - if (!stricmp(str, "Normal")) dmgType = NAME_None; - else dmgType=str; + FName dmgType; + if (!stricmp(str, "Normal")) dmgType = NAME_None; + else dmgType=str; - (*info->DamageFactors)[dmgType]=id; + (*info->DamageFactors)[dmgType]=id; + } } //========================================================================== @@ -1009,7 +1044,6 @@ DEFINE_PROPERTY(gravity, F, Actor) if (i < 0) I_Error ("Gravity must not be negative."); defaults->gravity = i; - if (i == 0) defaults->flags |= MF_NOGRAVITY; } //========================================================================== @@ -1062,7 +1096,7 @@ DEFINE_PROPERTY(projectile, 0, Actor) //========================================================================== DEFINE_PROPERTY(activation, N, Actor) { - // How the thing behaves when activated with MF5_USESPECIAL or MF6_BUMPSPECIAL + // How the thing behaves when activated by death, USESPECIAL or BUMPSPECIAL PROP_INT_PARM(val, 0); defaults->activationtype = val; } @@ -1285,7 +1319,7 @@ DEFINE_CLASS_PROPERTY(pickupflash, S, Inventory) //========================================================================== // //========================================================================== -DEFINE_CLASS_PROPERTY(pickupmessage, S, Inventory) +DEFINE_CLASS_PROPERTY(pickupmessage, T, Inventory) { PROP_STRING_PARM(str, 0); info->Class->Meta.SetMetaString(AIMETA_PickupMessage, str); @@ -1337,7 +1371,7 @@ DEFINE_CLASS_PROPERTY(givequest, I, Inventory) //========================================================================== // //========================================================================== -DEFINE_CLASS_PROPERTY(lowmessage, IS, Health) +DEFINE_CLASS_PROPERTY(lowmessage, IT, Health) { PROP_INT_PARM(i, 0); PROP_STRING_PARM(str, 1); @@ -1366,7 +1400,7 @@ DEFINE_CLASS_PROPERTY(number, I, PuzzleItem) //========================================================================== // //========================================================================== -DEFINE_CLASS_PROPERTY(failmessage, S, PuzzleItem) +DEFINE_CLASS_PROPERTY(failmessage, T, PuzzleItem) { PROP_STRING_PARM(str, 0); info->Class->Meta.SetMetaString(AIMETA_PuzzFailMessage, str); @@ -1405,7 +1439,8 @@ DEFINE_CLASS_PROPERTY(ammogive2, I, Weapon) DEFINE_CLASS_PROPERTY(ammotype, S, Weapon) { PROP_STRING_PARM(str, 0); - defaults->AmmoType1 = FindClassTentative(str, "Ammo"); + if (!stricmp(str, "none") || *str == 0) defaults->AmmoType1 = NULL; + else defaults->AmmoType1 = FindClassTentative(str, "Ammo"); } //========================================================================== @@ -1414,7 +1449,8 @@ DEFINE_CLASS_PROPERTY(ammotype, S, Weapon) DEFINE_CLASS_PROPERTY(ammotype1, S, Weapon) { PROP_STRING_PARM(str, 0); - defaults->AmmoType1 = FindClassTentative(str, "Ammo"); + if (!stricmp(str, "none") || *str == 0) defaults->AmmoType1 = NULL; + else defaults->AmmoType1 = FindClassTentative(str, "Ammo"); } //========================================================================== @@ -1423,7 +1459,8 @@ DEFINE_CLASS_PROPERTY(ammotype1, S, Weapon) DEFINE_CLASS_PROPERTY(ammotype2, S, Weapon) { PROP_STRING_PARM(str, 0); - defaults->AmmoType2 = FindClassTentative(str, "Ammo"); + if (!stricmp(str, "none") || *str == 0) defaults->AmmoType1 = NULL; + else defaults->AmmoType2 = FindClassTentative(str, "Ammo"); } //========================================================================== diff --git a/src/thingdef/thingdef_states.cpp b/src/thingdef/thingdef_states.cpp index 208581b51..6d0a12d1a 100644 --- a/src/thingdef/thingdef_states.cpp +++ b/src/thingdef/thingdef_states.cpp @@ -55,6 +55,7 @@ #include "i_system.h" #include "colormatcher.h" #include "thingdef_exp.h" +#include "version.h" TDeletingArray StateTempCalls; @@ -248,6 +249,20 @@ do_stop: sc.MustGetStringName(")"); continue; } + if (sc.Compare("LIGHT")) + { + sc.MustGetStringName("("); + do + { + sc.MustGetString(); + #ifdef DYNLIGHT + AddStateLight(&state, sc.String); + #endif + } + while (sc.CheckString(",")); + sc.MustGetStringName(")"); + continue; + } // Make the action name lowercase to satisfy the gperf hashers strlwr (sc.String); diff --git a/src/timidity/timidity.h b/src/timidity/timidity.h index e704b6b78..6e630554e 100644 --- a/src/timidity/timidity.h +++ b/src/timidity/timidity.h @@ -161,7 +161,7 @@ extern __inline__ double pow_x87_inline(double x,double y) "fmulp\n\t" : "=t" (result) : "0" (x), "u" (y) - : "st(1)", "st(7)", "%3", "%4" ); + : "st(1)", "st(7)" ); return result; } #define pow pow_x87_inline diff --git a/src/v_draw.cpp b/src/v_draw.cpp index 05cd73c5c..e46a99d12 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -59,6 +59,9 @@ int CleanXfac, CleanYfac; // [RH] Effective screen sizes that the above scale values give you int CleanWidth, CleanHeight; +// Above minus 1 (or 1, if they are already 1) +int CleanXfac_1, CleanYfac_1, CleanWidth_1, CleanHeight_1; + CVAR (Bool, hud_scale, false, CVAR_ARCHIVE); // For routines that take RGB colors, cache the previous lookup in case there @@ -89,14 +92,14 @@ static int PalFromRGB(uint32 rgb) return LastPal; } -void STACK_ARGS DCanvas::DrawTexture (FTexture *img, int x, int y, int tags_first, ...) +void STACK_ARGS DCanvas::DrawTexture (FTexture *img, double x, double y, int tags_first, ...) { va_list tags; va_start(tags, tags_first); DrawTextureV(img, x, y, tags_first, tags); } -void STACK_ARGS DCanvas::DrawTextureV(FTexture *img, int x, int y, uint32 tag, va_list tags) +void STACK_ARGS DCanvas::DrawTextureV(FTexture *img, double x, double y, uint32 tag, va_list tags) { FTexture::Span unmaskedSpan[2]; const FTexture::Span **spanptr, *spans; @@ -154,8 +157,8 @@ void STACK_ARGS DCanvas::DrawTextureV(FTexture *img, int x, int y, uint32 tag, v BYTE *destorgsave = dc_destorg; dc_destorg = screen->GetBuffer(); - fixed_t x0 = parms.x - Scale (parms.left, parms.destwidth, parms.texwidth); - fixed_t y0 = parms.y - Scale (parms.top, parms.destheight, parms.texheight); + double x0 = parms.x - parms.left * parms.destwidth / parms.texwidth; + double y0 = parms.y - parms.top * parms.destheight / parms.texheight; if (mode != DontDraw) { @@ -174,31 +177,43 @@ void STACK_ARGS DCanvas::DrawTextureV(FTexture *img, int x, int y, uint32 tag, v fixed_t centeryback = centeryfrac; centeryfrac = 0; - sprtopscreen = y0; - spryscale = parms.destheight / img->GetHeight(); + sprtopscreen = FLOAT2FIXED(y0); + // There is not enough precision in the drawing routines to keep the full + // precision for y0. :( + sprtopscreen &= ~(FRACUNIT - 1); + double yscale = parms.destheight / img->GetHeight(); + double iyscale = 1 / yscale; + + spryscale = FLOAT2FIXED(yscale); + +#if 0 // Fix precision errors that are noticeable at some resolutions - if (((y0 + parms.destheight) >> FRACBITS) > ((y0 + spryscale * img->GetHeight()) >> FRACBITS)) + if ((y0 + parms.destheight) > (y0 + yscale * img->GetHeight())) { spryscale++; } +#endif sprflipvert = false; - dc_iscale = 0xffffffffu / (unsigned)spryscale; - dc_texturemid = FixedMul (-y0, dc_iscale); + //dc_iscale = FLOAT2FIXED(iyscale); + //dc_texturemid = FLOAT2FIXED((-y0) * iyscale); + //dc_iscale = 0xffffffffu / (unsigned)spryscale; + dc_iscale = DivScale32(1, spryscale); + dc_texturemid = FixedMul(-sprtopscreen, dc_iscale) + FixedMul(centeryfrac-FRACUNIT, dc_iscale); fixed_t frac = 0; - fixed_t xiscale = DivScale32 (img->GetWidth(), parms.destwidth); - int x2 = (x0 + parms.destwidth) >> FRACBITS; + double xiscale = img->GetWidth() / parms.destwidth; + double x2 = x0 + parms.destwidth; if (bottomclipper[0] != parms.dclip) { - clearbufshort (bottomclipper, screen->GetWidth(), (short)parms.dclip); + clearbufshort(bottomclipper, screen->GetWidth(), (short)parms.dclip); } if (parms.uclip != 0) { if (topclipper[0] != parms.uclip) { - clearbufshort (topclipper, screen->GetWidth(), (short)parms.uclip); + clearbufshort(topclipper, screen->GetWidth(), (short)parms.uclip); } mceilingclip = topclipper; } @@ -214,48 +229,53 @@ void STACK_ARGS DCanvas::DrawTextureV(FTexture *img, int x, int y, uint32 tag, v xiscale = -xiscale; } - dc_x = x0 >> FRACBITS; if (parms.windowleft > 0 || parms.windowright < parms.texwidth) { - fixed_t xscale = parms.destwidth / parms.texwidth; - dc_x += (parms.windowleft * xscale) >> FRACBITS; - frac += parms.windowleft << FRACBITS; - x2 -= ((parms.texwidth - parms.windowright) * xscale) >> FRACBITS; + double xscale = parms.destwidth / parms.texwidth; + x0 += parms.windowleft * xscale; + frac += FLOAT2FIXED(parms.windowleft); + x2 -= (parms.texwidth - parms.windowright) * xscale; } - if (dc_x < parms.lclip) + if (x0 < parms.lclip) { - frac += (parms.lclip - dc_x) * xiscale; - dc_x = parms.lclip; + frac += FLOAT2FIXED((parms.lclip - x0) * xiscale); + x0 = parms.lclip; } if (x2 > parms.rclip) { x2 = parms.rclip; } - if (parms.destheight < 32*FRACUNIT) + // Drawing short output ought to fit in the data cache well enough + // if we draw one column at a time, so do that, since it's simpler. + if (parms.destheight < 32 || (parms.dclip - parms.uclip) < 32) { mode = DoDraw0; } + dc_x = int(x0); + int x2_i = int(x2); + fixed_t xiscale_i = FLOAT2FIXED(xiscale); + if (mode == DoDraw0) { // One column at a time stop4 = dc_x; } - else // DoDraw1 + else // DoDraw1` { // Up to four columns at a time - stop4 = x2 & ~3; + stop4 = x2_i & ~3; } - if (dc_x < x2) + if (dc_x < x2_i) { while ((dc_x < stop4) && (dc_x & 3)) { - pixels = img->GetColumn (frac >> FRACBITS, spanptr); - R_DrawMaskedColumn (pixels, spans); + pixels = img->GetColumn(frac >> FRACBITS, spanptr); + R_DrawMaskedColumn(pixels, spans); dc_x++; - frac += xiscale; + frac += xiscale_i; } while (dc_x < stop4) @@ -263,20 +283,20 @@ void STACK_ARGS DCanvas::DrawTextureV(FTexture *img, int x, int y, uint32 tag, v rt_initcols(); for (int zz = 4; zz; --zz) { - pixels = img->GetColumn (frac >> FRACBITS, spanptr); - R_DrawMaskedColumnHoriz (pixels, spans); + pixels = img->GetColumn(frac >> FRACBITS, spanptr); + R_DrawMaskedColumnHoriz(pixels, spans); dc_x++; - frac += xiscale; + frac += xiscale_i; } - rt_draw4cols (dc_x - 4); + rt_draw4cols(dc_x - 4); } - while (dc_x < x2) + while (dc_x < x2_i) { - pixels = img->GetColumn (frac >> FRACBITS, spanptr); - R_DrawMaskedColumn (pixels, spans); + pixels = img->GetColumn(frac >> FRACBITS, spanptr); + R_DrawMaskedColumn(pixels, spans); dc_x++; - frac += xiscale; + frac += xiscale_i; } } centeryfrac = centeryback; @@ -287,11 +307,11 @@ void STACK_ARGS DCanvas::DrawTextureV(FTexture *img, int x, int y, uint32 tag, v if (ticdup != 0 && menuactive == MENU_Off) { - NetUpdate (); + NetUpdate(); } } -bool DCanvas::ParseDrawTextureTags (FTexture *img, int x, int y, DWORD tag, va_list tags, DrawParms *parms, bool hw) const +bool DCanvas::ParseDrawTextureTags (FTexture *img, double x, double y, DWORD tag, va_list tags, DrawParms *parms, bool hw) const { INTBOOL boolval; int intval; @@ -313,8 +333,8 @@ bool DCanvas::ParseDrawTextureTags (FTexture *img, int x, int y, DWORD tag, va_l virtBottom = false; - parms->texwidth = img->GetScaledWidth(); - parms->texheight = img->GetScaledHeight(); + parms->texwidth = img->GetScaledWidthDouble(); + parms->texheight = img->GetScaledHeightDouble(); parms->windowleft = 0; parms->windowright = parms->texwidth; @@ -322,8 +342,8 @@ bool DCanvas::ParseDrawTextureTags (FTexture *img, int x, int y, DWORD tag, va_l parms->uclip = 0; parms->lclip = 0; parms->rclip = this->GetWidth(); - parms->destwidth = parms->windowright << FRACBITS; - parms->destheight = parms->texheight << FRACBITS; + parms->destwidth = parms->windowright; + parms->destheight = parms->texheight; parms->top = img->GetScaledTopOffset(); parms->left = img->GetScaledLeftOffset(); parms->alpha = FRACUNIT; @@ -344,10 +364,12 @@ bool DCanvas::ParseDrawTextureTags (FTexture *img, int x, int y, DWORD tag, va_l parms->specialcolormap = NULL; parms->colormapstyle = NULL; - parms->x = x << FRACBITS; - parms->y = y << FRACBITS; + parms->x = x; + parms->y = y; - // Parse the tag list for attributes + // Parse the tag list for attributes. (For floating point attributes, + // consider that the C ABI dictates that all floats be promoted to + // doubles when passed as function arguments.) while (tag != TAG_DONE) { va_list *more_p; @@ -357,11 +379,11 @@ bool DCanvas::ParseDrawTextureTags (FTexture *img, int x, int y, DWORD tag, va_l { case TAG_IGNORE: default: - data = va_arg (tags, DWORD); + data = va_arg(tags, DWORD); break; case TAG_MORE: - more_p = va_arg (tags, va_list *); + more_p = va_arg(tags, va_list *); va_end (tags); #ifndef NO_VA_COPY va_copy (tags, *more_p); @@ -371,35 +393,52 @@ bool DCanvas::ParseDrawTextureTags (FTexture *img, int x, int y, DWORD tag, va_l break; case DTA_DestWidth: - parms->destwidth = va_arg (tags, int) << FRACBITS; + parms->destwidth = va_arg(tags, int); + break; + + case DTA_DestWidthF: + parms->destwidth = va_arg(tags, double); break; case DTA_DestHeight: - parms->destheight = va_arg (tags, int) << FRACBITS; + parms->destheight = va_arg(tags, int); + break; + + case DTA_DestHeightF: + parms->destheight = va_arg(tags, double); break; case DTA_Clean: - boolval = va_arg (tags, INTBOOL); + boolval = va_arg(tags, INTBOOL); if (boolval) { - parms->x = (parms->x - 160*FRACUNIT) * CleanXfac + (Width * (FRACUNIT/2)); - parms->y = (parms->y - 100*FRACUNIT) * CleanYfac + (Height * (FRACUNIT/2)); - parms->destwidth = parms->texwidth * CleanXfac * FRACUNIT; - parms->destheight = parms->texheight * CleanYfac * FRACUNIT; + parms->x = (parms->x - 160.0) * CleanXfac + (Width * 0.5); + parms->y = (parms->y - 100.0) * CleanYfac + (Height * 0.5); + parms->destwidth = parms->texwidth * CleanXfac; + parms->destheight = parms->texheight * CleanYfac; } break; case DTA_CleanNoMove: - boolval = va_arg (tags, INTBOOL); + boolval = va_arg(tags, INTBOOL); if (boolval) { - parms->destwidth = parms->texwidth * CleanXfac * FRACUNIT; - parms->destheight = parms->texheight * CleanYfac * FRACUNIT; + parms->destwidth = parms->texwidth * CleanXfac; + parms->destheight = parms->texheight * CleanYfac; + } + break; + + case DTA_CleanNoMove_1: + boolval = va_arg(tags, INTBOOL); + if (boolval) + { + parms->destwidth = parms->texwidth * CleanXfac_1; + parms->destheight = parms->texheight * CleanYfac_1; } break; case DTA_320x200: - boolval = va_arg (tags, INTBOOL); + boolval = va_arg(tags, INTBOOL); if (boolval) { parms->virtWidth = 320; @@ -408,7 +447,7 @@ bool DCanvas::ParseDrawTextureTags (FTexture *img, int x, int y, DWORD tag, va_l break; case DTA_Bottom320x200: - boolval = va_arg (tags, INTBOOL); + boolval = va_arg(tags, INTBOOL); if (boolval) { parms->virtWidth = 320; @@ -421,99 +460,133 @@ bool DCanvas::ParseDrawTextureTags (FTexture *img, int x, int y, DWORD tag, va_l { bool xright = parms->x < 0; bool ybot = parms->y < 0; - intval = va_arg (tags, int); + intval = va_arg(tags, int); if (hud_scale) { parms->x *= CleanXfac; if (intval == HUD_HorizCenter) - parms->x += Width * FRACUNIT / 2; + parms->x += Width * 0.5; else if (xright) - parms->x = Width * FRACUNIT + parms->x; + parms->x = Width + parms->x; parms->y *= CleanYfac; if (ybot) - parms->y = Height * FRACUNIT + parms->y; - parms->destwidth = parms->texwidth * CleanXfac * FRACUNIT; - parms->destheight = parms->texheight * CleanYfac * FRACUNIT; + parms->y = Height + parms->y; + parms->destwidth = parms->texwidth * CleanXfac; + parms->destheight = parms->texheight * CleanYfac; } else { if (intval == HUD_HorizCenter) - parms->x += Width * FRACUNIT / 2; + parms->x += Width * 0.5; else if (xright) - parms->x = Width * FRACUNIT + parms->x; + parms->x = Width + parms->x; if (ybot) - parms->y = Height * FRACUNIT + parms->y; + parms->y = Height + parms->y; } } break; case DTA_VirtualWidth: - parms->virtWidth = va_arg (tags, int); + parms->virtWidth = va_arg(tags, int); + break; + + case DTA_VirtualWidthF: + parms->virtWidth = va_arg(tags, double); break; case DTA_VirtualHeight: - parms->virtHeight = va_arg (tags, int); + parms->virtHeight = va_arg(tags, int); + break; + + case DTA_VirtualHeightF: + parms->virtHeight = va_arg(tags, double); + break; + + case DTA_Fullscreen: + boolval = va_arg(tags, INTBOOL); + if (boolval) + { + parms->x = parms->y = 0; + parms->virtWidth = img->GetScaledWidthDouble(); + parms->virtHeight = img->GetScaledHeightDouble(); + } break; case DTA_Alpha: - parms->alpha = MIN (FRACUNIT, va_arg (tags, fixed_t)); + parms->alpha = MIN(FRACUNIT, va_arg (tags, fixed_t)); break; case DTA_AlphaChannel: - parms->alphaChannel = va_arg (tags, INTBOOL); + parms->alphaChannel = va_arg(tags, INTBOOL); break; case DTA_FillColor: - parms->fillcolor = va_arg (tags, int); + parms->fillcolor = va_arg(tags, uint32); break; case DTA_Translation: - parms->remap = va_arg (tags, FRemapTable *); + parms->remap = va_arg(tags, FRemapTable *); break; case DTA_ColorOverlay: - parms->colorOverlay = va_arg (tags, DWORD); + parms->colorOverlay = va_arg(tags, DWORD); break; case DTA_FlipX: - parms->flipX = va_arg (tags, INTBOOL); + parms->flipX = va_arg(tags, INTBOOL); break; case DTA_TopOffset: - parms->top = va_arg (tags, int); + parms->top = va_arg(tags, int); + break; + + case DTA_TopOffsetF: + parms->top = va_arg(tags, double); break; case DTA_LeftOffset: - parms->left = va_arg (tags, int); + parms->left = va_arg(tags, int); + break; + + case DTA_LeftOffsetF: + parms->left = va_arg(tags, double); break; case DTA_CenterOffset: - if (va_arg (tags, int)) + if (va_arg(tags, int)) { - parms->left = parms->texwidth / 2; - parms->top = parms->texheight / 2; + parms->left = parms->texwidth * 0.5; + parms->top = parms->texheight * 0.5; } break; case DTA_CenterBottomOffset: - if (va_arg (tags, int)) + if (va_arg(tags, int)) { - parms->left = parms->texwidth / 2; + parms->left = parms->texwidth * 0.5; parms->top = parms->texheight; } break; case DTA_WindowLeft: - parms->windowleft = va_arg (tags, int); + parms->windowleft = va_arg(tags, int); + break; + + case DTA_WindowLeftF: + parms->windowleft = va_arg(tags, double); break; case DTA_WindowRight: - parms->windowright = va_arg (tags, int); + parms->windowright = va_arg(tags, int); + break; + + case DTA_WindowRightF: + parms->windowright = va_arg(tags, double); break; case DTA_ClipTop: - parms->uclip = va_arg (tags, int); + parms->uclip = va_arg(tags, int); if (parms->uclip < 0) { parms->uclip = 0; @@ -521,7 +594,7 @@ bool DCanvas::ParseDrawTextureTags (FTexture *img, int x, int y, DWORD tag, va_l break; case DTA_ClipBottom: - parms->dclip = va_arg (tags, int); + parms->dclip = va_arg(tags, int); if (parms->dclip > this->GetHeight()) { parms->dclip = this->GetHeight(); @@ -529,7 +602,7 @@ bool DCanvas::ParseDrawTextureTags (FTexture *img, int x, int y, DWORD tag, va_l break; case DTA_ClipLeft: - parms->lclip = va_arg (tags, int); + parms->lclip = va_arg(tags, int); if (parms->lclip < 0) { parms->lclip = 0; @@ -537,7 +610,7 @@ bool DCanvas::ParseDrawTextureTags (FTexture *img, int x, int y, DWORD tag, va_l break; case DTA_ClipRight: - parms->rclip = va_arg (tags, int); + parms->rclip = va_arg(tags, int); if (parms->rclip > this->GetWidth()) { parms->rclip = this->GetWidth(); @@ -545,15 +618,15 @@ bool DCanvas::ParseDrawTextureTags (FTexture *img, int x, int y, DWORD tag, va_l break; case DTA_ShadowAlpha: - parms->shadowAlpha = MIN (FRACUNIT, va_arg (tags, fixed_t)); + parms->shadowAlpha = MIN(FRACUNIT, va_arg (tags, fixed_t)); break; case DTA_ShadowColor: - parms->shadowColor = va_arg (tags, int); + parms->shadowColor = va_arg(tags, int); break; case DTA_Shadow: - boolval = va_arg (tags, INTBOOL); + boolval = va_arg(tags, INTBOOL); if (boolval) { parms->shadowAlpha = FRACUNIT/2; @@ -566,32 +639,32 @@ bool DCanvas::ParseDrawTextureTags (FTexture *img, int x, int y, DWORD tag, va_l break; case DTA_Masked: - parms->masked = va_arg (tags, INTBOOL); + parms->masked = va_arg(tags, INTBOOL); break; case DTA_BilinearFilter: - parms->bilinear = va_arg (tags, INTBOOL); + parms->bilinear = va_arg(tags, INTBOOL); break; case DTA_KeepRatio: // I think this is a terribly misleading name, since it actually turns // *off* aspect ratio correction. - parms->keepratio = va_arg (tags, INTBOOL); + parms->keepratio = va_arg(tags, INTBOOL); break; case DTA_RenderStyle: - parms->style.AsDWORD = va_arg (tags, DWORD); + parms->style.AsDWORD = va_arg(tags, DWORD); break; case DTA_SpecialColormap: - parms->specialcolormap = va_arg (tags, FSpecialColormap *); + parms->specialcolormap = va_arg(tags, FSpecialColormap *); break; case DTA_ColormapStyle: - parms->colormapstyle = va_arg (tags, FColormapStyle *); + parms->colormapstyle = va_arg(tags, FColormapStyle *); break; } - tag = va_arg (tags, DWORD); + tag = va_arg(tags, DWORD); } va_end (tags); @@ -618,7 +691,7 @@ bool DCanvas::ParseDrawTextureTags (FTexture *img, int x, int y, DWORD tag, va_l if (parms->style.BlendOp == 255) { - if (parms->fillcolor != -1) + if (parms->fillcolor != ~0u) { if (parms->alphaChannel) { @@ -645,66 +718,72 @@ bool DCanvas::ParseDrawTextureTags (FTexture *img, int x, int y, DWORD tag, va_l return true; } -void DCanvas::VirtualToRealCoords(fixed_t &x, fixed_t &y, fixed_t &w, fixed_t &h, - int vwidth, int vheight, bool vbottom, bool handleaspect) const +void DCanvas::VirtualToRealCoords(double &x, double &y, double &w, double &h, + double vwidth, double vheight, bool vbottom, bool handleaspect) const { int myratio = handleaspect ? CheckRatio (Width, Height) : 0; - int right = x + w; - int bottom = y + h; + double right = x + w; + double bottom = y + h; if (myratio != 0 && myratio != 4) { // The target surface is either 16:9 or 16:10, so expand the // specified virtual size to avoid undesired stretching of the // image. Does not handle non-4:3 virtual sizes. I'll worry about // those if somebody expresses a desire to use them. - x = Scale(x - vwidth*FRACUNIT/2, - Width*960, - vwidth*BaseRatioSizes[myratio][0]) - + Width*FRACUNIT/2; - w = Scale(right - vwidth*FRACUNIT/2, - Width*960, - vwidth*BaseRatioSizes[myratio][0]) - + Width*FRACUNIT/2 - x; + x = (x - vwidth * 0.5) * Width * 960 / (vwidth * BaseRatioSizes[myratio][0]) + Width * 0.5; + w = (right - vwidth * 0.5) * Width * 960 / (vwidth * BaseRatioSizes[myratio][0]) + Width * 0.5 - x; } else { - x = Scale (x, Width, vwidth); - w = Scale (right, Width, vwidth) - x; + x = x * Width / vwidth; + w = right * Width / vwidth - x; } if (myratio == 4) { // The target surface is 5:4 - y = Scale(y - vheight*FRACUNIT/2, - Height*600, - vheight*BaseRatioSizes[myratio][1]) - + Height*FRACUNIT/2; - h = Scale(bottom - vheight*FRACUNIT/2, - Height*600, - vheight*BaseRatioSizes[myratio][1]) - + Height*FRACUNIT/2 - y; + y = (y - vheight * 0.5) * Height * 600 / (vheight * BaseRatioSizes[myratio][1]) + Height * 0.5; + h = (bottom - vheight * 0.5) * Height * 600 / (vheight * BaseRatioSizes[myratio][1]) + Height * 0.5 - y; if (vbottom) { - y += (Height - Height * BaseRatioSizes[myratio][3] / 48) << (FRACBITS - 1); + y += (Height - Height * BaseRatioSizes[myratio][3] / 48.0) * 0.5; } } else { - y = Scale (y, Height, vheight); - h = Scale (bottom, Height, vheight) - y; + y = y * Height / vheight; + h = bottom * Height / vheight - y; } } +void DCanvas::VirtualToRealCoordsFixed(fixed_t &x, fixed_t &y, fixed_t &w, fixed_t &h, + int vwidth, int vheight, bool vbottom, bool handleaspect) const +{ + double dx, dy, dw, dh; + + dx = FIXED2FLOAT(x); + dy = FIXED2FLOAT(y); + dw = FIXED2FLOAT(w); + dh = FIXED2FLOAT(h); + VirtualToRealCoords(dx, dy, dw, dh, vwidth, vheight, vbottom, handleaspect); + x = FLOAT2FIXED(dx); + y = FLOAT2FIXED(dy); + w = FLOAT2FIXED(dw); + h = FLOAT2FIXED(dh); +} + void DCanvas::VirtualToRealCoordsInt(int &x, int &y, int &w, int &h, int vwidth, int vheight, bool vbottom, bool handleaspect) const { - x <<= FRACBITS; - y <<= FRACBITS; - w <<= FRACBITS; - h <<= FRACBITS; - VirtualToRealCoords(x, y, w, h, vwidth, vheight, vbottom, handleaspect); - x >>= FRACBITS; - y >>= FRACBITS; - w >>= FRACBITS; - h >>= FRACBITS; + double dx, dy, dw, dh; + + dx = x; + dy = y; + dw = w; + dh = h; + VirtualToRealCoords(dx, dy, dw, dh, vwidth, vheight, vbottom, handleaspect); + x = int(dx + 0.5); + y = int(dy + 0.5); + w = int(dx + dw + 0.5) - x; + h = int(dy + dh + 0.5) - y; } void DCanvas::FillBorder (FTexture *img) diff --git a/src/v_font.cpp b/src/v_font.cpp index e36bd1cac..ead32a8c5 100644 --- a/src/v_font.cpp +++ b/src/v_font.cpp @@ -736,19 +736,19 @@ FTexture *FFont::GetChar (int code, int *const width) const code > LastChar || Chars[code - FirstChar].Pic == NULL) { - *width = SpaceWidth; + if (width != NULL) *width = SpaceWidth; return NULL; } } else { - *width = SpaceWidth; + if (width != NULL) *width = SpaceWidth; return NULL; } } code -= FirstChar; - *width = Chars[code].Pic->GetScaledWidth(); + if (width != NULL) *width = Chars[code].Pic->GetScaledWidth(); return Chars[code].Pic; } diff --git a/src/v_palette.cpp b/src/v_palette.cpp index b9279779d..c47f75370 100644 --- a/src/v_palette.cpp +++ b/src/v_palette.cpp @@ -469,7 +469,7 @@ void InitPalette () // build default special maps (e.g. invulnerability) SpecialColormaps.Clear(); - for (int i = 0; i < countof(SpecialColormapParms); ++i) + for (unsigned i = 0; i < countof(SpecialColormapParms); ++i) { AddSpecialColormap(SpecialColormapParms[i].Start[0], SpecialColormapParms[i].Start[1], SpecialColormapParms[i].Start[2], SpecialColormapParms[i].End[0], diff --git a/src/v_text.cpp b/src/v_text.cpp index 2c758d5fa..be386e992 100644 --- a/src/v_text.cpp +++ b/src/v_text.cpp @@ -156,13 +156,23 @@ void STACK_ARGS DCanvas::DrawText (FFont *font, int normalcolor, int x, int y, c ptrval = va_arg (tags, void*); break; + case DTA_CleanNoMove_1: + boolval = va_arg (tags, INTBOOL); + if (boolval) + { + scalex = CleanXfac_1; + scaley = CleanYfac_1; + maxwidth = Width - (Width % scalex); + } + break; + case DTA_CleanNoMove: boolval = va_arg (tags, INTBOOL); if (boolval) { scalex = CleanXfac; scaley = CleanYfac; - maxwidth = Width - (Width % CleanYfac); + maxwidth = Width - (Width % scalex); } break; diff --git a/src/v_video.cpp b/src/v_video.cpp index acd481f68..f6e0eb546 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -838,7 +838,7 @@ void DFrameBuffer::DrawRateStuff () // Draws frame time and cumulative fps if (vid_fps) { - DWORD ms = I_MSTime (); + DWORD ms = I_FPSTime(); DWORD howlong = ms - LastMS; if (howlong >= 0) { @@ -876,7 +876,7 @@ void DFrameBuffer::DrawRateStuff () // Buffer can be NULL if we're doing hardware accelerated 2D if (buffer != NULL) { - buffer += (GetHeight()-1)*GetPitch(); + buffer += (GetHeight()-1) * GetPitch(); for (i = 0; i < tics*2; i += 2) buffer[i] = 0xff; for ( ; i < 20*2; i += 2) buffer[i] = 0x00; @@ -1269,7 +1269,48 @@ void DFrameBuffer::DrawRemainingPlayerSprites() R_DrawRemainingPlayerSprites(); } +//=========================================================================== +// +// notify the renderer that an actor has changed state +// +//=========================================================================== +void DFrameBuffer::StateChanged(AActor *actor) +{ +} + +//=========================================================================== +// +// notify the renderer that serialization of the curent level is about to start/end +// +//=========================================================================== + +void DFrameBuffer::StartSerialize(FArchive &arc) +{ +} + +void DFrameBuffer::EndSerialize(FArchive &arc) +{ +} + +//=========================================================================== +// +// Get max. view angle (renderer specific information so it goes here now) +// +//=========================================================================== +#define MAX_DN_ANGLE 56 // Max looking down angle +#define MAX_UP_ANGLE 32 // Max looking up angle + +int DFrameBuffer::GetMaxViewPitch(bool down) +{ + return down? MAX_DN_ANGLE*ANGLE_1 : -MAX_UP_ANGLE*ANGLE_1; +} + +//=========================================================================== +// +// +// +//=========================================================================== FNativePalette::~FNativePalette() { @@ -1295,6 +1336,10 @@ CCMD(clean) bool V_DoModeSetup (int width, int height, int bits) { DFrameBuffer *buff = I_SetMode (width, height, screen); + int ratio; + int cwidth; + int cheight; + int cx1, cy1, cx2, cy2; if (buff == NULL) { @@ -1309,39 +1354,32 @@ bool V_DoModeSetup (int width, int height, int bits) // if D3DFB is being used for the display. FFont::StaticPreloadFonts(); + ratio = CheckRatio (width, height); + if (ratio & 4) { - int ratio; - int cwidth; - int cheight; - int cx1, cy1, cx2, cy2; - - ratio = CheckRatio (width, height); - if (ratio & 4) - { - cwidth = width; - cheight = height * BaseRatioSizes[ratio][3] / 48; - } - else - { - cwidth = width * BaseRatioSizes[ratio][3] / 48; - cheight = height; - } - // Use whichever pair of cwidth/cheight or width/height that produces less difference - // between CleanXfac and CleanYfac. - cx1 = MAX(cwidth / 320, 1); - cy1 = MAX(cheight / 200, 1); - cx2 = MAX(width / 320, 1); - cy2 = MAX(height / 200, 1); - if (abs(cx1 - cy1) <= abs(cx2 - cy2)) - { // e.g. 640x360 looks better with this. - CleanXfac = cx1; - CleanYfac = cy1; - } - else - { // e.g. 720x480 looks better with this. - CleanXfac = cx2; - CleanYfac = cy2; - } + cwidth = width; + cheight = height * BaseRatioSizes[ratio][3] / 48; + } + else + { + cwidth = width * BaseRatioSizes[ratio][3] / 48; + cheight = height; + } + // Use whichever pair of cwidth/cheight or width/height that produces less difference + // between CleanXfac and CleanYfac. + cx1 = MAX(cwidth / 320, 1); + cy1 = MAX(cheight / 200, 1); + cx2 = MAX(width / 320, 1); + cy2 = MAX(height / 200, 1); + if (abs(cx1 - cy1) <= abs(cx2 - cy2)) + { // e.g. 640x360 looks better with this. + CleanXfac = cx1; + CleanYfac = cy1; + } + else + { // e.g. 720x480 looks better with this. + CleanXfac = cx2; + CleanYfac = cy2; } if (CleanXfac > 1 && CleanYfac > 1 && CleanXfac != CleanYfac) @@ -1357,6 +1395,31 @@ bool V_DoModeSetup (int width, int height, int bits) assert(CleanWidth >= 320); assert(CleanHeight >= 200); + if (width < 800 || width >= 960) + { + if (cx1 < cx2) + { + // Special case in which we don't need to scale down. + CleanXfac_1 = + CleanYfac_1 = cx1; + } + else + { + CleanXfac_1 = MAX(CleanXfac - 1, 1); + CleanYfac_1 = MAX(CleanYfac - 1, 1); + } + CleanWidth_1 = width / CleanXfac_1; + CleanHeight_1 = height / CleanYfac_1; + } + else // if the width is between 800 and 960 the ratio between the screensize and CleanXFac-1 becomes too large. + { + CleanXfac_1 = CleanXfac; + CleanYfac_1 = CleanYfac; + CleanWidth_1 = CleanWidth; + CleanHeight_1 = CleanHeight; + } + + DisplayWidth = width; DisplayHeight = height; DisplayBits = bits; diff --git a/src/v_video.h b/src/v_video.h index 98bf420de..6beaeea7b 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -42,6 +42,7 @@ #include "c_cvars.h" extern int CleanWidth, CleanHeight, CleanXfac, CleanYfac; +extern int CleanWidth_1, CleanHeight_1, CleanXfac_1, CleanYfac_1; extern int DisplayWidth, DisplayHeight, DisplayBits; bool V_DoModeSetup (int width, int height, int bits); @@ -79,6 +80,7 @@ enum DTA_320x200, // bool: scale texture size and position to fit on a virtual 320x200 screen DTA_Bottom320x200, // bool: same as DTA_320x200 but centers virtual screen on bottom for 1280x1024 targets DTA_CleanNoMove, // bool: like DTA_Clean but does not reposition output position + DTA_CleanNoMove_1, // bool: like DTA_CleanNoMove, but uses Clean[XY]fac_1 instead DTA_FlipX, // bool: flip image horizontally //FIXME: Does not work with DTA_Window(Left|Right) DTA_ShadowColor, // color of shadow DTA_ShadowAlpha, // alpha of shadow @@ -87,8 +89,8 @@ enum DTA_VirtualHeight, // pretend the canvas is this tall DTA_TopOffset, // override texture's top offset DTA_LeftOffset, // override texture's left offset - DTA_CenterOffset, // override texture's left and top offsets and set them for the texture's middle - DTA_CenterBottomOffset,// override texture's left and top offsets and set them for the texture's bottom middle + DTA_CenterOffset, // bool: override texture's left and top offsets and set them for the texture's middle + DTA_CenterBottomOffset,// bool: override texture's left and top offsets and set them for the texture's bottom middle DTA_WindowLeft, // don't draw anything left of this column (on source, not dest) DTA_WindowRight, // don't draw anything at or to the right of this column (on source, not dest) DTA_ClipTop, // don't draw anything above this row (on dest, not source) @@ -103,6 +105,17 @@ enum DTA_BilinearFilter, // bool: apply bilinear filtering to the image DTA_SpecialColormap,// pointer to FSpecialColormapParameters (likely to be forever hardware-only) DTA_ColormapStyle, // pointer to FColormapStyle (hardware-only) + DTA_Fullscreen, // Draw image fullscreen (same as DTA_VirtualWidth/Height with graphics size.) + + // floating point duplicates of some of the above: + DTA_DestWidthF, + DTA_DestHeightF, + DTA_TopOffsetF, + DTA_LeftOffsetF, + DTA_VirtualWidthF, + DTA_VirtualHeightF, + DTA_WindowLeftF, + DTA_WindowRightF, // For DrawText calls: DTA_TextLen, // stop after this many characters, even if \0 not hit @@ -188,9 +201,12 @@ public: // Text drawing functions ----------------------------------------------- // 2D Texture drawing - void STACK_ARGS DrawTexture (FTexture *img, int x, int y, int tags, ...); + void STACK_ARGS DrawTexture (FTexture *img, double x, double y, int tags, ...); void FillBorder (FTexture *img); // Fills the border around a 4:3 part of the screen on non-4:3 displays - void VirtualToRealCoords(fixed_t &x, fixed_t &y, fixed_t &w, fixed_t &h, int vwidth, int vheight, bool vbottom=false, bool handleaspect=true) const; + void VirtualToRealCoords(double &x, double &y, double &w, double &h, double vwidth, double vheight, bool vbottom=false, bool handleaspect=true) const; + + // Code that uses these (i.e. SBARINFO) should probably be evaluated for using doubles all around instead. + void VirtualToRealCoordsFixed(fixed_t &x, fixed_t &y, fixed_t &w, fixed_t &h, int vwidth, int vheight, bool vbottom=false, bool handleaspect=true) const; void VirtualToRealCoordsInt(int &x, int &y, int &w, int &h, int vwidth, int vheight, bool vbottom=false, bool handleaspect=true) const; // 2D Text drawing @@ -199,30 +215,30 @@ public: struct DrawParms { - fixed_t x, y; - int texwidth; - int texheight; - int windowleft; - int windowright; + double x, y; + double texwidth; + double texheight; + double destwidth; + double destheight; + double virtWidth; + double virtHeight; + double windowleft; + double windowright; int dclip; int uclip; int lclip; int rclip; - fixed_t destwidth; - fixed_t destheight; - int top; - int left; + double top; + double left; fixed_t alpha; - int fillcolor; + uint32 fillcolor; FRemapTable *remap; const BYTE *translation; - DWORD colorOverlay; + uint32 colorOverlay; INTBOOL alphaChannel; INTBOOL flipX; fixed_t shadowAlpha; int shadowColor; - int virtWidth; - int virtHeight; INTBOOL keepratio; INTBOOL masked; INTBOOL bilinear; @@ -239,8 +255,8 @@ protected: int LockCount; bool ClipBox (int &left, int &top, int &width, int &height, const BYTE *&src, const int srcpitch) const; - virtual void STACK_ARGS DrawTextureV (FTexture *img, int x, int y, uint32 tag, va_list tags); - bool ParseDrawTextureTags (FTexture *img, int x, int y, uint32 tag, va_list tags, DrawParms *parms, bool hw) const; + virtual void STACK_ARGS DrawTextureV (FTexture *img, double x, double y, uint32 tag, va_list tags); + bool ParseDrawTextureTags (FTexture *img, double x, double y, uint32 tag, va_list tags, DrawParms *parms, bool hw) const; DCanvas() {} @@ -339,7 +355,7 @@ public: // Tells the device to recreate itself with the new setting from vid_refreshrate. virtual void NewRefreshRate (); - // Set the rect defining the area effected by blending. + // Set the rect defining the area affected by blending. virtual void SetBlendingRect (int x1, int y1, int x2, int y2); // render 3D view @@ -351,6 +367,15 @@ public: // draws player sprites with hardware acceleration (only useful for software rendering) virtual void DrawRemainingPlayerSprites(); + // notifies the renderer that an actor has changed state. + virtual void StateChanged(AActor *actor); + + // notify the renderer that serialization of the curent level is about to start/end + virtual void StartSerialize(FArchive &arc); + virtual void EndSerialize(FArchive &arc); + + virtual int GetMaxViewPitch(bool down); + bool Accel2D; // If true, 2D drawing can be accelerated. // Begin 2D drawing operations. This is like Update, but it doesn't end @@ -381,6 +406,8 @@ public: virtual bool WipeDo(int ticks); virtual void WipeCleanup(); + uint32 GetLastFPS() const { return LastCount; } + #ifdef _WIN32 virtual void PaletteChanged () = 0; virtual int QueryNewPalette () = 0; @@ -461,6 +488,7 @@ extern "C" void ASM_PatchPitch (void); #endif int CheckRatio (int width, int height); +static inline int CheckRatio (double width, double height) { return CheckRatio(int(width), int(height)); } extern const int BaseRatioSizes[5][4]; diff --git a/src/vectors.h b/src/vectors.h index f3b154aa2..013077042 100644 --- a/src/vectors.h +++ b/src/vectors.h @@ -1230,7 +1230,4 @@ typedef TRotator FRotator; typedef TMatrix3x3 FMatrix3x3; typedef TAngle FAngle; -#define FLOAT2FIXED(f) fixed_t((f) * float(65536)) -#define FIXED2FLOAT(f) (float(f) / float(65536)) - #endif diff --git a/src/version.h b/src/version.h index 8896eb713..de017995c 100644 --- a/src/version.h +++ b/src/version.h @@ -40,35 +40,35 @@ /** Lots of different version numbers **/ -#define DOTVERSIONSTR_NOREV "2.3.1" +#define DOTVERSIONSTR_NOREV "2.4.0" // The version string the user actually sees. #define DOTVERSIONSTR DOTVERSIONSTR_NOREV " (r" SVN_REVISION_STRING ")" // The version as seen in the Windows resource -#define RC_FILEVERSION 2,3,1,SVN_REVISION_NUMBER -#define RC_PRODUCTVERSION 2,3,1,0 +#define RC_FILEVERSION 2,4,0,SVN_REVISION_NUMBER +#define RC_PRODUCTVERSION 2,4,0,0 #define RC_FILEVERSION2 DOTVERSIONSTR -#define RC_PRODUCTVERSION2 "2.3" +#define RC_PRODUCTVERSION2 "2.4" // Version identifier for network games. // Bump it every time you do a release unless you're certain you // didn't change anything that will affect sync. -#define NETGAMEVERSION 223 +#define NETGAMEVERSION 224 // Version stored in the ini's [LastRun] section. // Bump it if you made some configuration change that you want to // be able to migrate in FGameConfigFile::DoGlobalSetup(). -#define LASTRUNVERSION "209" +#define LASTRUNVERSION "210" // Protocol version used in demos. // Bump it if you change existing DEM_ commands or add new ones. // Otherwise, it should be safe to leave it alone. -#define DEMOGAMEVERSION 0x212 +#define DEMOGAMEVERSION 0x213 // Minimum demo version we can play. // Bump it whenever you change or remove existing DEM_ commands. -#define MINDEMOVERSION 0x211 +#define MINDEMOVERSION 0x213 // SAVEVER is the version of the information stored in level snapshots. // Note that SAVEVER is not directly comparable to VERSION. diff --git a/src/w_wad.cpp b/src/w_wad.cpp index 4d1d554d9..bb8deb384 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -160,7 +160,7 @@ void FWadCollection::DeleteAll () // //========================================================================== -void FWadCollection::InitMultipleFiles (wadlist_t **filenames) +void FWadCollection::InitMultipleFiles (TArray &filenames) { int numfiles; @@ -168,13 +168,10 @@ void FWadCollection::InitMultipleFiles (wadlist_t **filenames) DeleteAll(); numfiles = 0; - while (*filenames) + for(unsigned i=0;inext; int baselump = NumLumps; - AddFile ((*filenames)->name); - M_Free (*filenames); - *filenames = next; + AddFile (filenames[i]); } NumLumps = LumpInfo.Size(); @@ -832,6 +829,7 @@ int FWadCollection::FindLump (const char *name, int *lastlump, bool anyns) uppercopy (name8, name); + assert(lastlump != NULL && *lastlump >= 0); lump_p = &LumpInfo[*lastlump]; while (lump_p < &LumpInfo[NumLumps]) { diff --git a/src/w_wad.h b/src/w_wad.h index 293a328f5..2edd71587 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -49,14 +49,6 @@ struct wadlump_t #define PWAD_ID MAKE_ID('P','W','A','D') -// [RH] Remove limit on number of WAD files -struct wadlist_t -{ - wadlist_t *next; - char name[1]; // +size of string -}; -extern wadlist_t *wadfiles; - // [RH] Namespaces from BOOM. typedef enum { ns_global = 0, @@ -153,7 +145,7 @@ public: // The wadnum for the IWAD enum { IWAD_FILENUM = 1 }; - void InitMultipleFiles (wadlist_t **filenames); + void InitMultipleFiles (TArray &filenames); void AddFile (const char *filename, FileReader *wadinfo = NULL); int CheckIfWadLoaded (const char *name); diff --git a/src/w_zip.h b/src/w_zip.h index 299e75589..40a8a4b1d 100644 --- a/src/w_zip.h +++ b/src/w_zip.h @@ -1,7 +1,7 @@ #ifndef __W_ZIP #define __W_ZIP -#pragma pack(push, 1) +#pragma pack(1) // FZipCentralInfo struct FZipEndOfCentralDirectory { @@ -56,7 +56,7 @@ struct FZipLocalFileHeader }; -#pragma pack(pop) +#pragma pack() #define ZIP_LOCALFILE MAKE_ID('P','K',3,4) #define ZIP_CENTRALFILE MAKE_ID('P','K',1,2) diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index ca7b11e37..a01d15346 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -595,8 +595,8 @@ void WI_updateAnimatedBack() void WI_drawBackground() { unsigned int i; - int animwidth=320; // For a flat fill or clear background scale animations to 320x200 - int animheight=200; + double animwidth=320; // For a flat fill or clear background scale animations to 320x200 + double animheight=200; if (background) { @@ -606,11 +606,10 @@ void WI_drawBackground() // scale all animations below to fit the size of the base pic // The base pic is always scaled to fit the screen so this allows // placing the animations precisely where they belong on the base pic - animwidth = background->GetWidth(); - animheight = background->GetHeight(); + animwidth = background->GetScaledWidth(); + animheight = background->GetScaledHeight(); screen->FillBorder (NULL); - screen->DrawTexture(background, 0, 0, DTA_VirtualWidth, animwidth, - DTA_VirtualHeight, animheight, TAG_DONE); + screen->DrawTexture(background, 0, 0, DTA_Fullscreen, true, TAG_DONE); } else { @@ -666,7 +665,7 @@ void WI_drawBackground() } if (a->ctr >= 0) screen->DrawTexture(a->p[a->ctr], a->loc.x, a->loc.y, - DTA_VirtualWidth, animwidth, DTA_VirtualHeight, animheight, TAG_DONE); + DTA_VirtualWidthF, animwidth, DTA_VirtualHeightF, animheight, TAG_DONE); } } diff --git a/src/win32/fb_d3d9.cpp b/src/win32/fb_d3d9.cpp index 562abf13d..561891e29 100644 --- a/src/win32/fb_d3d9.cpp +++ b/src/win32/fb_d3d9.cpp @@ -3,7 +3,7 @@ ** Code to let ZDoom use Direct3D 9 as a simple framebuffer ** **--------------------------------------------------------------------------- -** Copyright 1998-2008 Randy Heit +** Copyright 1998-2009 Randy Heit ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -96,6 +96,9 @@ struct D3DFB::PackedTexture // Texture coordinates for this image float Left, Top, Right, Bottom; + + // Texture has extra space on the border? + bool Padded; }; struct D3DFB::PackingTexture @@ -159,30 +162,6 @@ public: int RoundedPaletteSize; }; -// Flags for a buffered quad -enum -{ - BQF_GamePalette = 1, - BQF_CustomPalette = 7, - BQF_Paletted = 7, - BQF_Bilinear = 8, - BQF_WrapUV = 16, - BQF_InvertSource = 32, - BQF_DisableAlphaTest= 64, - BQF_Desaturated = 128, -}; - -// Shaders for a buffered quad -enum -{ - BQS_PalTex, - BQS_Plain, - BQS_RedToAlpha, - BQS_ColorOnly, - BQS_SpecialColormap, - BQS_InGameColormap, -}; - // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -268,19 +247,22 @@ D3DFB::D3DFB (int width, int height, bool fullscreen) IndexBuffer = NULL; FBTexture = NULL; TempRenderTexture = NULL; + RenderTexture[0] = NULL; + RenderTexture[1] = NULL; InitialWipeScreen = NULL; ScreenshotTexture = NULL; ScreenshotSurface = NULL; FinalWipeScreen = NULL; PaletteTexture = NULL; + GammaTexture = NULL; + FrontCopySurface = NULL; for (int i = 0; i < NUM_SHADERS; ++i) { Shaders[i] = NULL; } + GammaShader = NULL; BlockSurface[0] = NULL; BlockSurface[1] = NULL; - FBFormat = D3DFMT_UNKNOWN; - PalFormat = D3DFMT_UNKNOWN; VSync = vid_vsync; BlendingRect.left = 0; BlendingRect.top = 0; @@ -296,6 +278,9 @@ D3DFB::D3DFB (int width, int height, bool fullscreen) QuadExtra = new BufferedQuad[MAX_QUAD_BATCH]; Packs = NULL; PixelDoubling = 0; + SkipAt = -1; + CurrRenderTexture = 0; + RenderTextureToggle = 0; Gamma = 1.0; FlashColor0 = 0; @@ -416,12 +401,17 @@ void D3DFB::SetInitialState() CurPixelShader = NULL; memset(Constant, 0, sizeof(Constant)); - Texture[0] = NULL; - Texture[1] = NULL; - D3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); - D3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); - D3DDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, SM14 ? D3DTADDRESS_BORDER : D3DTADDRESS_CLAMP); - D3DDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, SM14 ? D3DTADDRESS_BORDER : D3DTADDRESS_CLAMP); + for (unsigned i = 0; i < countof(Texture); ++i) + { + Texture[i] = NULL; + D3DDevice->SetSamplerState(i, D3DSAMP_ADDRESSU, (i == 1 && SM14) ? D3DTADDRESS_BORDER : D3DTADDRESS_CLAMP); + D3DDevice->SetSamplerState(i, D3DSAMP_ADDRESSV, (i == 1 && SM14) ? D3DTADDRESS_BORDER : D3DTADDRESS_CLAMP); + if (i > 1) + { + // Set linear filtering for the SM14 gamma texture. + D3DDevice->SetSamplerState(i, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + } + } NeedGammaUpdate = true; NeedPalUpdate = true; @@ -455,6 +445,9 @@ void D3DFB::SetInitialState() AlphaTestEnabled = FALSE; CurBorderColor = 0; + + // Clear to black, just in case it wasn't done already. + D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 0, 0); } //========================================================================== @@ -534,6 +527,7 @@ bool D3DFB::CreateResources() { return false; } + CreateGammaTexture(); CreateBlockSurfaces(); return true; } @@ -549,9 +543,10 @@ bool D3DFB::CreateResources() bool D3DFB::LoadShaders() { - static const char *const models[] = { "30/", "20/", "14/" }; + static const char models[][4] = { "30/", "20/", "14/" }; FString shaderdir, shaderpath; - int model, i, lump; + unsigned model, i; + int lump; // We determine the best available model simply by trying them all in // order of decreasing preference. @@ -568,7 +563,7 @@ bool D3DFB::LoadShaders() { FMemLump data = Wads.ReadLump(lump); if (FAILED(D3DDevice->CreatePixelShader((DWORD *)data.GetMem(), &Shaders[i])) && - i != SHADER_GammaCorrection && i != SHADER_BurnWipe) + i < SHADER_BurnWipe) { break; } @@ -607,6 +602,7 @@ void D3DFB::ReleaseResources () { SAFE_RELEASE( Shaders[i] ); } + GammaShader = NULL; if (ScreenWipe != NULL) { delete ScreenWipe; @@ -632,20 +628,15 @@ void D3DFB::ReleaseResources () void D3DFB::ReleaseDefaultPoolItems() { SAFE_RELEASE( FBTexture ); - if (FinalWipeScreen != NULL) - { - if (FinalWipeScreen != TempRenderTexture) - { - FinalWipeScreen->Release(); - } - FinalWipeScreen = NULL; - } - SAFE_RELEASE( TempRenderTexture ); + SAFE_RELEASE( FinalWipeScreen ); + SAFE_RELEASE( RenderTexture[0] ); + SAFE_RELEASE( RenderTexture[1] ); SAFE_RELEASE( InitialWipeScreen ); SAFE_RELEASE( VertexBuffer ); SAFE_RELEASE( IndexBuffer ); SAFE_RELEASE( BlockSurface[0] ); SAFE_RELEASE( BlockSurface[1] ); + SAFE_RELEASE( FrontCopySurface ); } //========================================================================== @@ -741,16 +732,30 @@ void D3DFB::KillNativeTexs() } } +//========================================================================== +// +// D3DFB :: CreateFBTexture +// +// Creates the "Framebuffer" texture. With the advent of hardware-assisted +// 2D, this is something of a misnomer now. The FBTexture is only used for +// uploading the software 3D image to video memory so that it can be drawn +// to the real frame buffer. +// +// It also creates the TempRenderTexture, since this seemed like a +// convenient place to do so. +// +//========================================================================== + bool D3DFB::CreateFBTexture () { - if (FAILED(D3DDevice->CreateTexture (Width, Height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &FBTexture, NULL))) + if (FAILED(D3DDevice->CreateTexture(Width, Height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &FBTexture, NULL))) { int pow2width, pow2height, i; for (i = 1; i < Width; i <<= 1) {} pow2width = i; for (i = 1; i < Height; i <<= 1) {} pow2height = i; - if (FAILED(D3DDevice->CreateTexture (pow2width, pow2height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &FBTexture, NULL))) + if (FAILED(D3DDevice->CreateTexture(pow2width, pow2height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &FBTexture, NULL))) { return false; } @@ -765,23 +770,85 @@ bool D3DFB::CreateFBTexture () FBWidth = Width; FBHeight = Height; } - if (FAILED(D3DDevice->CreateTexture (FBWidth, FBHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &TempRenderTexture, NULL))) + RenderTextureToggle = 0; + RenderTexture[0] = NULL; + RenderTexture[1] = NULL; + if (FAILED(D3DDevice->CreateTexture(FBWidth, FBHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &RenderTexture[0], NULL))) { - TempRenderTexture = NULL; + return false; } + if (Windowed || PixelDoubling) + { + // Windowed or pixel doubling: Create another render texture so we can flip between them. + RenderTextureToggle = 1; + if (FAILED(D3DDevice->CreateTexture(FBWidth, FBHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &RenderTexture[1], NULL))) + { + return false; + } + } + else + { + // Fullscreen and not pixel doubling: Create a render target to have the back buffer copied to. + if (FAILED(D3DDevice->CreateRenderTarget(Width, Height, D3DFMT_X8R8G8B8, D3DMULTISAMPLE_NONE, 0, FALSE, &FrontCopySurface, NULL))) + { + return false; + } + } + // Initialize the TempRenderTextures to black. + for (int i = 0; i <= RenderTextureToggle; ++i) + { + IDirect3DSurface9 *surf; + if (SUCCEEDED(RenderTexture[i]->GetSurfaceLevel(0, &surf))) + { + D3DDevice->ColorFill(surf, NULL, D3DCOLOR_XRGB(0,0,0)); + surf->Release(); + } + } + TempRenderTexture = RenderTexture[0]; + CurrRenderTexture = 0; return true; } +//========================================================================== +// +// D3DFB :: CreatePaletteTexture +// +//========================================================================== + bool D3DFB::CreatePaletteTexture () { if (FAILED(D3DDevice->CreateTexture (256, 1, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &PaletteTexture, NULL))) { return false; } - PalFormat = D3DFMT_A8R8G8B8; return true; } +//========================================================================== +// +// D3DFB :: CreateGammaTexture +// +//========================================================================== + +bool D3DFB::CreateGammaTexture () +{ + // If this fails, you just won't get gamma correction in a window + // on SM14 cards. + assert(GammaTexture == NULL); + if (SM14) + { + return SUCCEEDED(D3DDevice->CreateTexture(256, 1, 1, 0, D3DFMT_A8R8G8B8, + D3DPOOL_MANAGED, &GammaTexture, NULL)); + } + return false; +} + +//========================================================================== +// +// D3DFB :: CreateVertexes +// +//========================================================================== + bool D3DFB::CreateVertexes () { VertexPos = -1; @@ -801,6 +868,12 @@ bool D3DFB::CreateVertexes () return true; } +//========================================================================== +// +// D3DFB :: CalcFullscreenCoords +// +//========================================================================== + void D3DFB::CalcFullscreenCoords (FBVERTEX verts[4], bool viewarea_only, bool can_double, D3DCOLOR color0, D3DCOLOR color1) const { float offset = OldRenderTarget != NULL ? 0 : LBOffset; @@ -874,35 +947,77 @@ void D3DFB::CalcFullscreenCoords (FBVERTEX verts[4], bool viewarea_only, bool ca verts[3].tv = tmyb; } +//========================================================================== +// +// D3DFB :: GetPageCount +// +//========================================================================== + int D3DFB::GetPageCount () { return 1; } +//========================================================================== +// +// D3DFB :: PaletteChanged +// +//========================================================================== + void D3DFB::PaletteChanged () { } +//========================================================================== +// +// D3DFB :: QueryNewPalette +// +//========================================================================== + int D3DFB::QueryNewPalette () { return 0; } +//========================================================================== +// +// D3DFB :: IsValid +// +//========================================================================== + bool D3DFB::IsValid () { return D3DDevice != NULL; } +//========================================================================== +// +// D3DFB :: GetHR +// +//========================================================================== + HRESULT D3DFB::GetHR () { return 0; } +//========================================================================== +// +// D3DFB :: IsFullscreen +// +//========================================================================== + bool D3DFB::IsFullscreen () { return !Windowed; } +//========================================================================== +// +// D3DFB :: Lock +// +//========================================================================== + bool D3DFB::Lock () { return Lock(true); @@ -920,6 +1035,12 @@ bool D3DFB::Lock (bool buffered) return false; } +//========================================================================== +// +// D3DFB :: Unlock +// +//========================================================================== + void D3DFB::Unlock () { LOG1 ("Unlock <%d>\n", LockCount); @@ -939,10 +1060,17 @@ void D3DFB::Unlock () } } +//========================================================================== +// +// D3DFB :: Update +// // When In2D == 0: Copy buffer to screen and present // When In2D == 1: Copy buffer to screen but do not present // When In2D == 2: Set up for 2D drawing but do not draw anything // When In2D == 3: Present and set In2D to 0 +// +//========================================================================== + void D3DFB::Update () { if (In2D == 3) @@ -992,8 +1120,20 @@ void D3DFB::Update () LOG("SetGammaRamp\n"); D3DDevice->SetGammaRamp(0, D3DSGR_CALIBRATE, &ramp); } + else + { + if (igamma != 1) + { + UpdateGammaTexture(igamma); + GammaShader = Shaders[SHADER_GammaCorrection]; + } + else + { + GammaShader = NULL; + } + } psgamma[2] = psgamma[1] = psgamma[0] = igamma; - psgamma[3] = 1; + psgamma[3] = 0.5; // For SM14 version D3DDevice->SetPixelShaderConstantF(PSCONST_Gamma, psgamma, 1); } @@ -1025,6 +1165,12 @@ void D3DFB::Update () UpdatePending = false; } +//========================================================================== +// +// D3DFB :: Flip +// +//========================================================================== + void D3DFB::Flip() { assert(InScene); @@ -1032,6 +1178,8 @@ void D3DFB::Flip() DoWindowedGamma(); D3DDevice->EndScene(); + CopyNextFrontBuffer(); + // Attempt to counter input lag. if (d3d_antilag && BlockSurface[0] != NULL) { @@ -1047,8 +1195,57 @@ void D3DFB::Flip() } D3DDevice->Present(NULL, NULL, NULL, NULL); InScene = false; + + if (RenderTextureToggle) + { + // Flip the TempRenderTexture to the other one now. + CurrRenderTexture ^= RenderTextureToggle; + TempRenderTexture = RenderTexture[CurrRenderTexture]; + } } +//========================================================================== +// +// D3DFB :: CopyNextFrontBuffer +// +// Duplicates the contents of the back buffer that will become the front +// buffer upon Present into FrontCopySurface so that we can get the +// contents of the display without wasting time in GetFrontBufferData(). +// +//========================================================================== + +void D3DFB::CopyNextFrontBuffer() +{ + IDirect3DSurface9 *backbuff; + + if (Windowed || PixelDoubling) + { + // Windowed mode or pixel doubling: TempRenderTexture has what we want + SAFE_RELEASE( FrontCopySurface ); + if (SUCCEEDED(TempRenderTexture->GetSurfaceLevel(0, &backbuff))) + { + FrontCopySurface = backbuff; + } + } + else + { + // Fullscreen, not pixel doubled: The back buffer has what we want, + // but it might be letter boxed. + if (SUCCEEDED(D3DDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuff))) + { + RECT srcrect = { 0, LBOffsetI, Width, LBOffsetI + Height }; + D3DDevice->StretchRect(backbuff, &srcrect, FrontCopySurface, NULL, D3DTEXF_NONE); + backbuff->Release(); + } + } +} + +//========================================================================== +// +// D3DFB :: PaintToWindow +// +//========================================================================== + bool D3DFB::PaintToWindow () { HRESULT hr; @@ -1070,6 +1267,14 @@ bool D3DFB::PaintToWindow () return true; } +//========================================================================== +// +// D3DFB :: Draw3DPart +// +// The software 3D part, to be exact. +// +//========================================================================== + void D3DFB::Draw3DPart(bool copy3d) { if (copy3d) @@ -1105,7 +1310,7 @@ void D3DFB::Draw3DPart(bool copy3d) D3DDevice->SetRenderState(D3DRS_ANTIALIASEDLINEENABLE, vid_hwaalines); assert(OldRenderTarget == NULL); if (TempRenderTexture != NULL && - ((Windowed && Shaders[SHADER_GammaCorrection] && TempRenderTexture != FinalWipeScreen) || GatheringWipeScreen || PixelDoubling)) + ((Windowed && TempRenderTexture != FinalWipeScreen) || GatheringWipeScreen || PixelDoubling)) { IDirect3DSurface9 *targetsurf; if (SUCCEEDED(TempRenderTexture->GetSurfaceLevel(0, &targetsurf))) @@ -1196,34 +1401,191 @@ void D3DFB::DoWindowedGamma() D3DDevice->SetRenderTarget(0, OldRenderTarget); D3DDevice->SetFVF(D3DFVF_FBVERTEX); SetTexture(0, TempRenderTexture); - SetPixelShader(Shaders[(Windowed && Shaders[SHADER_GammaCorrection]) ? SHADER_GammaCorrection : SHADER_NormalColor]); + SetPixelShader(Windowed && GammaShader ? GammaShader : Shaders[SHADER_NormalColor]); + if (SM14 && Windowed && GammaShader) + { + SetTexture(2, GammaTexture); + SetTexture(3, GammaTexture); + SetTexture(4, GammaTexture); + } SetAlphaBlend(D3DBLENDOP(0)); EnableAlphaTest(FALSE); D3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, sizeof(FBVERTEX)); OldRenderTarget->Release(); OldRenderTarget = NULL; + if (SM14 && Windowed && GammaShader) + { +// SetTexture(0, GammaTexture); +// SetPixelShader(Shaders[SHADER_NormalColor]); +// D3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, sizeof(FBVERTEX)); + } } } +//========================================================================== +// +// D3DFB :: UpdateGammaTexture +// +// Updates the gamma texture used by the PS14 shader. We only use the first +// half of the texture so that we needn't worry about imprecision causing +// it to grab from the border. +// +//========================================================================== + +void D3DFB::UpdateGammaTexture(float igamma) +{ + D3DLOCKED_RECT lockrect; + + if (GammaTexture != NULL && SUCCEEDED(GammaTexture->LockRect(0, &lockrect, NULL, 0))) + { + BYTE *pix = (BYTE *)lockrect.pBits; + for (int i = 0; i <= 128; ++i) + { + pix[i*4+2] = pix[i*4+1] = pix[i*4] = BYTE(255.f * powf(i / 128.f, igamma)); + pix[i*4+3] = 255; + } + GammaTexture->UnlockRect(0); + } +} + +//========================================================================== +// +// D3DFB :: DoOffByOneCheck +// +// Pixel Shader 1.x does not have enough precision to properly map a "color" +// from the source texture to an index in the palette texture. The best we +// can do is use 255 pixels of the palette and get the 256th from the +// texture border color. This routine determines which pixel of the texture +// is skipped so that we don't use it for palette data. +// +//========================================================================== + +void D3DFB::DoOffByOneCheck () +{ + IDirect3DSurface9 *savedrendertarget; + IDirect3DSurface9 *testsurf, *readsurf; + D3DLOCKED_RECT lockrect; + RECT testrect = { 0, 0, 256, 1 }; + float texright = 256.f / float(FBWidth); + float texbot = 1.f / float(FBHeight); + FBVERTEX verts[4] = + { + { -0.5f, -0.5f, 0.5f, 1.f, 0, ~0, 0.f, 0.f }, + { 255.5f, -0.5f, 0.5f, 1.f, 0, ~0, texright, 0.f }, + { 255.5f, 0.5f, 0.5f, 1.f, 0, ~0, texright, texbot }, + { -0.5f, 0.5f, 0.5f, 1.f, 0, ~0, 0.f, texbot } + }; + int i, c; + + if (SkipAt >= 0) + { + return; + } + + // Create an easily recognizable R3G3B2 palette. + if (SUCCEEDED(PaletteTexture->LockRect(0, &lockrect, NULL, 0))) + { + BYTE *pal = (BYTE *)(lockrect.pBits); + for (i = 0; i < 256; ++i) + { + pal[i*4+0] = (i & 0x03) << 6; // blue + pal[i*4+1] = (i & 0x1C) << 3; // green + pal[i*4+2] = (i & 0xE0); // red; + pal[i*4+3] = 255; + } + PaletteTexture->UnlockRect (0); + } + else + { + return; + } + // Prepare a texture with values 0-256. + if (SUCCEEDED(FBTexture->LockRect(0, &lockrect, &testrect, 0))) + { + for (i = 0; i < 256; ++i) + { + ((BYTE *)lockrect.pBits)[i] = i; + } + FBTexture->UnlockRect(0); + } + else + { + return; + } + // Create a render target that we can draw it to. + if (FAILED(D3DDevice->GetRenderTarget(0, &savedrendertarget))) + { + return; + } + if (FAILED(D3DDevice->CreateRenderTarget(256, 1, D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0, FALSE, &testsurf, NULL))) + { + return; + } + if (FAILED(D3DDevice->CreateOffscreenPlainSurface(256, 1, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &readsurf, NULL))) + { + testsurf->Release(); + return; + } + if (FAILED(D3DDevice->SetRenderTarget(0, testsurf))) + { + testsurf->Release(); + readsurf->Release(); + return; + } + // Write it to the render target using the pixel shader. + D3DDevice->BeginScene(); + D3DDevice->SetTexture(0, FBTexture); + D3DDevice->SetTexture(1, PaletteTexture); + D3DDevice->SetFVF(D3DFVF_FBVERTEX); + D3DDevice->SetPixelShader(Shaders[SHADER_NormalColorPal]); + SetConstant(PSCONST_PaletteMod, 1.f, 0.5f / 256.f, 0, 0); + D3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, sizeof(FBVERTEX)); + D3DDevice->EndScene(); + D3DDevice->SetRenderTarget(0, savedrendertarget); + savedrendertarget->Release(); + // Now read it back and see where it skips an entry + if (SUCCEEDED(D3DDevice->GetRenderTargetData(testsurf, readsurf)) && + SUCCEEDED(readsurf->LockRect(&lockrect, &testrect, D3DLOCK_READONLY))) + { + const BYTE *pix = (const BYTE *)lockrect.pBits; + for (i = 0; i < 256; ++i, pix += 4) + { + c = (pix[0] >> 6) | // blue + ((pix[1] >> 5) << 2) | // green + ((pix[2] >> 5) << 5); // red + if (c != i) + { + break; + } + } + } + readsurf->UnlockRect(); + readsurf->Release(); + testsurf->Release(); + SkipAt = i; +} + void D3DFB::UploadPalette () { D3DLOCKED_RECT lockrect; - if (SUCCEEDED(PaletteTexture->LockRect (0, &lockrect, NULL, 0))) + if (SkipAt < 0) + { + if (SM14) + { + DoOffByOneCheck(); + } + else + { + SkipAt = 256; + } + } + if (SUCCEEDED(PaletteTexture->LockRect(0, &lockrect, NULL, 0))) { BYTE *pix = (BYTE *)lockrect.pBits; - int i, skipat; + int i; - // It is impossible to get the Radeon 9000 to do the proper palette - // lookup. It *will* skip at least one entry in the palette. So we - // let it and have it look at the texture border color for color 255. - // I assume that every other card based on a related graphics chipset - // is similarly affected, which basically means that all Shader Model - // 1.4 cards suffer from this problem, since they all use some variant - // of the ATI R200. - skipat = SM14 ? 256 - 8 : 256; - - for (i = 0; i < skipat; ++i, pix += 4) + for (i = 0; i < SkipAt; ++i, pix += 4) { pix[0] = SourcePalette[i].b; pix[1] = SourcePalette[i].g; @@ -1239,7 +1601,7 @@ void D3DFB::UploadPalette () pix[2] = SourcePalette[i].r; pix[3] = 255; } - PaletteTexture->UnlockRect (0); + PaletteTexture->UnlockRect(0); BorderColor = D3DCOLOR_XRGB(SourcePalette[255].r, SourcePalette[255].g, SourcePalette[255].b); } } @@ -1390,51 +1752,28 @@ void D3DFB::ReleaseScreenshotBuffer() // //========================================================================== -IDirect3DTexture9 *D3DFB::GetCurrentScreen() +IDirect3DTexture9 *D3DFB::GetCurrentScreen(D3DPOOL pool) { IDirect3DTexture9 *tex; - IDirect3DSurface9 *tsurf, *surf; + IDirect3DSurface9 *surf; D3DSURFACE_DESC desc; + HRESULT hr; - if (Windowed || PixelDoubling) + assert(pool == D3DPOOL_SYSTEMMEM || pool == D3DPOOL_DEFAULT); + + if (FAILED(FrontCopySurface->GetDesc(&desc))) { - // The texture we read into must have the same pixel format as - // the TempRenderTexture. - if (SUCCEEDED(TempRenderTexture->GetSurfaceLevel(0, &tsurf))) - { - if (FAILED(tsurf->GetDesc(&desc))) - { - tsurf->Release(); - return NULL; - } - tsurf->Release(); - } - else - { - return NULL; - } + return NULL; + } + if (pool == D3DPOOL_SYSTEMMEM) + { + hr = D3DDevice->CreateTexture(desc.Width, desc.Height, 1, 0, desc.Format, D3DPOOL_SYSTEMMEM, &tex, NULL); } else { - if (SUCCEEDED(D3DDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &tsurf))) - { - if (FAILED(tsurf->GetDesc(&desc))) - { - tsurf->Release(); - return NULL; - } - tsurf->Release(); - } - else - { - return NULL; - } - // GetFrontBufferData works only with this format - desc.Format = D3DFMT_A8R8G8B8; + hr = D3DDevice->CreateTexture(FBWidth, FBHeight, 1, D3DUSAGE_RENDERTARGET, desc.Format, D3DPOOL_DEFAULT, &tex, NULL); } - - if (FAILED(D3DDevice->CreateTexture(desc.Width, desc.Height, 1, 0, - desc.Format, D3DPOOL_SYSTEMMEM, &tex, NULL))) + if (FAILED(hr)) { return NULL; } @@ -1443,35 +1782,23 @@ IDirect3DTexture9 *D3DFB::GetCurrentScreen() tex->Release(); return NULL; } - - if (!Windowed && !PixelDoubling) + if (pool == D3DPOOL_SYSTEMMEM) { - if (FAILED(D3DDevice->GetFrontBufferData(0, surf))) - { - surf->Release(); - tex->Release(); - return NULL; - } + // Video -> System memory : use GetRenderTargetData + hr = D3DDevice->GetRenderTargetData(FrontCopySurface, surf); } else { - if (SUCCEEDED(TempRenderTexture->GetSurfaceLevel(0, &tsurf))) - { - if (FAILED(D3DDevice->GetRenderTargetData(tsurf, surf))) - { - tsurf->Release(); - tex->Release(); - return NULL; - } - tsurf->Release(); - } - else - { - tex->Release(); - return NULL; - } + // Video -> Video memory : use StretchRect + RECT destrect = { 0, 0, Width, Height }; + hr = D3DDevice->StretchRect(FrontCopySurface, NULL, surf, &destrect, D3DTEXF_POINT); } surf->Release(); + if (FAILED(hr)) + { + tex->Release(); + return NULL; + } return tex; } @@ -1542,7 +1869,7 @@ void D3DFB::DrawPackedTextures(int packnum) quad->ShaderNum = BQS_Plain; } quad->Palette = NULL; - quad->Texture = pack; + quad->Texture = pack->Tex; float x0 = float(x) - 0.5f; float y0 = float(y) - 0.5f; @@ -1635,14 +1962,18 @@ D3DFB::PackedTexture *D3DFB::AllocPackedTexture(int w, int h, bool wrapping, D3D PackedTexture *bestbox; int area; - if (w > 256 || h > 256 || wrapping) + // check for 254 to account for padding + if (w > 254 || h > 254 || wrapping) { // Create a new packing texture. bestpack = new PackingTexture(this, w, h, format); bestpack->OneUse = true; bestbox = bestpack->GetBestFit(w, h, area); + bestbox->Padded = false; } else { // Try to find space in an existing packing texture. + w += 2; // Add padding + h += 2; int bestarea = INT_MAX; int bestareaever = w * h; bestpack = NULL; @@ -1671,6 +2002,7 @@ D3DFB::PackedTexture *D3DFB::AllocPackedTexture(int w, int h, bool wrapping, D3D bestpack = new PackingTexture(this, 256, 256, format); bestbox = bestpack->GetBestFit(w, h, bestarea); } + bestbox->Padded = true; } bestpack->AllocateImage(bestbox, w, h); return bestbox; @@ -1800,10 +2132,10 @@ void D3DFB::PackingTexture::AllocateImage(D3DFB::PackedTexture *box, int w, int box->Area.right = box->Area.left + w; box->Area.bottom = box->Area.top + h; - box->Left = float(box->Area.left) / Width; - box->Right = float(box->Area.right) / Width; - box->Top = float(box->Area.top) / Height; - box->Bottom = float(box->Area.bottom) / Height; + box->Left = float(box->Area.left + box->Padded) / Width; + box->Right = float(box->Area.right - box->Padded) / Width; + box->Top = float(box->Area.top + box->Padded) / Height; + box->Bottom = float(box->Area.bottom - box->Padded) / Height; // Remove it from the empty list. *(box->Prev) = box->Next; @@ -1822,7 +2154,6 @@ void D3DFB::PackingTexture::AllocateImage(D3DFB::PackedTexture *box, int w, int box->Prev = &UsedList; // If we didn't use the whole box, split the remainder into the empty list. - if (box->Area.bottom + 7 < start.bottom && box->Area.right + 7 < start.right) { // Split like this: @@ -2046,6 +2377,7 @@ bool D3DTex::CheckWrapping(bool wrapping) // If it needs to wrap, then it can't be packed inside another texture. return Box->Owner->OneUse; } + //========================================================================== // // D3DTex :: Create @@ -2090,6 +2422,7 @@ bool D3DTex::Update() D3DSURFACE_DESC desc; D3DLOCKED_RECT lrect; RECT rect; + BYTE *dest; assert(Box != NULL); assert(Box->Owner != NULL); @@ -2105,7 +2438,45 @@ bool D3DTex::Update() { return false; } - GameTex->FillBuffer((BYTE *)lrect.pBits, lrect.Pitch, GameTex->GetHeight(), ToTexFmt(desc.Format)); + dest = (BYTE *)lrect.pBits; + if (Box->Padded) + { + dest += lrect.Pitch + (desc.Format == D3DFMT_L8 ? 1 : 4); + } + GameTex->FillBuffer(dest, lrect.Pitch, GameTex->GetHeight(), ToTexFmt(desc.Format)); + if (Box->Padded) + { + // Clear top padding row. + dest = (BYTE *)lrect.pBits; + int numbytes = GameTex->GetWidth() + 2; + if (desc.Format != D3DFMT_L8) + { + numbytes <<= 2; + } + memset(dest, 0, numbytes); + dest += lrect.Pitch; + // Clear left and right padding columns. + if (desc.Format == D3DFMT_L8) + { + for (int y = Box->Area.bottom - Box->Area.top - 2; y > 0; --y) + { + dest[0] = 0; + dest[numbytes-1] = 0; + dest += lrect.Pitch; + } + } + else + { + for (int y = Box->Area.bottom - Box->Area.top - 2; y > 0; --y) + { + *(DWORD *)dest = 0; + *(DWORD *)(dest + numbytes - 4) = 0; + dest += lrect.Pitch; + } + } + // Clear bottom padding row. + memset(dest, 0, numbytes); + } Box->Owner->Tex->UnlockRect(0); return true; } @@ -2545,7 +2916,7 @@ void D3DFB::DrawPixel(int x, int y, int palcolor, uint32 color) // //========================================================================== -void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, int x, int y, uint32 tags_first, va_list tags) +void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, double x, double y, uint32 tags_first, va_list tags) { if (In2D < 2) { @@ -2570,17 +2941,17 @@ void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, int x, int y, uint32 tags_fi CheckQuadBatch(); - float xscale = float(parms.destwidth) / parms.texwidth / 65536.f; - float yscale = float(parms.destheight) / parms.texheight / 65536.f; - float x0 = float(parms.x) / 65536.f - float(parms.left) * xscale; - float y0 = float(parms.y) / 65536.f - float(parms.top) * yscale; - float x1 = x0 + float(parms.destwidth) / 65536.f; - float y1 = y0 + float(parms.destheight) / 65536.f; + double xscale = parms.destwidth / parms.texwidth; + double yscale = parms.destheight / parms.texheight; + double x0 = parms.x - parms.left * xscale; + double y0 = parms.y - parms.top * yscale; + double x1 = x0 + parms.destwidth; + double y1 = y0 + parms.destheight; float u0 = tex->Box->Left; float v0 = tex->Box->Top; float u1 = tex->Box->Right; float v1 = tex->Box->Bottom; - float uscale = 1.f / tex->Box->Owner->Width; + double uscale = 1.f / tex->Box->Owner->Width; bool scissoring = false; if (parms.flipX) @@ -2590,9 +2961,9 @@ void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, int x, int y, uint32 tags_fi if (parms.windowleft > 0 || parms.windowright < parms.texwidth) { x0 += parms.windowleft * xscale; - u0 += parms.windowleft * uscale; + u0 = float(u0 + parms.windowleft * uscale); x1 -= (parms.texwidth - parms.windowright) * xscale; - u1 -= (parms.texwidth - parms.windowright) * uscale; + u1 = float(u1 - (parms.texwidth - parms.windowright) * uscale); } #if 0 float vscale = 1.f / tex->Box->Owner->Height / yscale; @@ -2644,7 +3015,7 @@ void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, int x, int y, uint32 tags_fi return; } - QuadExtra[QuadBatchPos].Texture = tex->Box->Owner; + QuadExtra[QuadBatchPos].Texture = tex->Box->Owner->Tex; if (parms.bilinear) { QuadExtra[QuadBatchPos].Flags |= BQF_Bilinear; @@ -2652,6 +3023,7 @@ void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, int x, int y, uint32 tags_fi float yoffs = GatheringWipeScreen ? 0.5f : 0.5f - LBOffset; +#if 0 // Coordinates are truncated to integers, because that's effectively // what the software renderer does. The hardware will instead round // to nearest, it seems. @@ -2659,11 +3031,18 @@ void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, int x, int y, uint32 tags_fi y0 = floorf(y0) - yoffs; x1 = floorf(x1) - 0.5f; y1 = floorf(y1) - yoffs; +#else + x0 = x0 - 0.5f; + y0 = y0 - yoffs; + x1 = x1 - 0.5f; + y1 = y1 - yoffs; +#endif FBVERTEX *vert = &VertexData[VertexPos]; - vert[0].x = x0; - vert[0].y = y0; + // Fill the vertex buffer. + vert[0].x = float(x0); + vert[0].y = float(y0); vert[0].z = 0; vert[0].rhw = 1; vert[0].color0 = color0; @@ -2671,8 +3050,8 @@ void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, int x, int y, uint32 tags_fi vert[0].tu = u0; vert[0].tv = v0; - vert[1].x = x1; - vert[1].y = y0; + vert[1].x = float(x1); + vert[1].y = float(y0); vert[1].z = 0; vert[1].rhw = 1; vert[1].color0 = color0; @@ -2680,8 +3059,8 @@ void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, int x, int y, uint32 tags_fi vert[1].tu = u1; vert[1].tv = v0; - vert[2].x = x1; - vert[2].y = y1; + vert[2].x = float(x1); + vert[2].y = float(y1); vert[2].z = 0; vert[2].rhw = 1; vert[2].color0 = color0; @@ -2689,8 +3068,8 @@ void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, int x, int y, uint32 tags_fi vert[2].tu = u1; vert[2].tv = v1; - vert[3].x = x0; - vert[3].y = y1; + vert[3].x = float(x0); + vert[3].y = float(y1); vert[3].z = 0; vert[3].rhw = 1; vert[3].color0 = color0; @@ -2698,6 +3077,7 @@ void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, int x, int y, uint32 tags_fi vert[3].tu = u0; vert[3].tv = v1; + // Fill the vertex index buffer. IndexData[IndexPos ] = VertexPos; IndexData[IndexPos + 1] = VertexPos + 1; IndexData[IndexPos + 2] = VertexPos + 2; @@ -2705,6 +3085,7 @@ void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, int x, int y, uint32 tags_fi IndexData[IndexPos + 4] = VertexPos + 2; IndexData[IndexPos + 5] = VertexPos + 3; + // Batch the quad. QuadBatchPos++; VertexPos += 4; IndexPos += 6; @@ -2775,7 +3156,7 @@ void D3DFB::FlatFill(int left, int top, int right, int bottom, FTexture *src, bo quad->ShaderNum = BQS_Plain; } quad->Palette = NULL; - quad->Texture = tex->Box->Owner; + quad->Texture = tex->Box->Owner->Tex; vert[0].x = x0; vert[0].y = y0; @@ -3007,11 +3388,13 @@ void D3DFB::EndQuadBatch() { SetPaletteTexture(quad->Palette->Tex, quad->Palette->RoundedPaletteSize, quad->Palette->BorderColor); } +#if 0 // Set paletted bilinear filtering (IF IT WORKED RIGHT!) if ((quad->Flags & (BQF_Paletted | BQF_Bilinear)) == (BQF_Paletted | BQF_Bilinear)) { SetPalTexBilinearConstants(quad->Texture); } +#endif // Set the alpha blending SetAlphaBlend(D3DBLENDOP(quad->BlendOp), D3DBLEND(quad->SrcBlend), D3DBLEND(quad->DestBlend)); @@ -3069,7 +3452,7 @@ void D3DFB::EndQuadBatch() // Set the texture if (quad->Texture != NULL) { - SetTexture(0, quad->Texture->Tex); + SetTexture(0, quad->Texture); } // Draw the quad @@ -3395,6 +3778,7 @@ void D3DFB::SetPixelShader(IDirect3DPixelShader9 *shader) void D3DFB::SetTexture(int tnum, IDirect3DTexture9 *texture) { + assert(unsigned(tnum) < countof(Texture)); if (Texture[tnum] != texture) { Texture[tnum] = texture; @@ -3402,8 +3786,6 @@ void D3DFB::SetTexture(int tnum, IDirect3DTexture9 *texture) } } -CVAR(Float, pal, 0.5f, 0) -CVAR(Float, pc, 255.f, 0) void D3DFB::SetPaletteTexture(IDirect3DTexture9 *texture, int count, D3DCOLOR border_color) { if (SM14) @@ -3430,7 +3812,7 @@ void D3DFB::SetPaletteTexture(IDirect3DTexture9 *texture, int count, D3DCOLOR bo // The constant register c2 is used to hold the multiplier in the // x part and the adder in the y part. float fcount = 1 / float(count); - SetConstant(PSCONST_PaletteMod, pc * fcount, pal * fcount, 0, 0); + SetConstant(PSCONST_PaletteMod, 255 * fcount, 0.5f * fcount, 0, 0); } SetTexture(1, texture); } diff --git a/src/win32/fb_d3d9_wipe.cpp b/src/win32/fb_d3d9_wipe.cpp index 699407b85..9a3b34412 100644 --- a/src/win32/fb_d3d9_wipe.cpp +++ b/src/win32/fb_d3d9_wipe.cpp @@ -159,12 +159,12 @@ bool D3DFB::WipeStartScreen(int type) return false; } - InitialWipeScreen = GetCurrentScreen(); + InitialWipeScreen = GetCurrentScreen(D3DPOOL_DEFAULT); // Create another texture to copy the final wipe screen to so // we can still gamma correct the wipe. Since this is just for // gamma correction, it's okay to fail (though not desirable.) - if (PixelDoubling || (Shaders[SHADER_GammaCorrection] != NULL && Windowed)) + if (PixelDoubling || Windowed) { if (SUCCEEDED(TempRenderTexture->GetSurfaceLevel(0, &tsurf))) { @@ -173,14 +173,14 @@ bool D3DFB::WipeStartScreen(int type) 1, D3DUSAGE_RENDERTARGET, desc.Format, D3DPOOL_DEFAULT, &FinalWipeScreen, NULL))) { - FinalWipeScreen = TempRenderTexture; + (FinalWipeScreen = TempRenderTexture)->AddRef(); } tsurf->Release(); } } else { - FinalWipeScreen = TempRenderTexture; + (FinalWipeScreen = TempRenderTexture)->AddRef(); } // Make even fullscreen model render to the TempRenderTexture, so @@ -229,7 +229,11 @@ void D3DFB::WipeEndScreen() // If these are different, reverse their roles so we don't need to // waste time copying from TempRenderTexture to FinalWipeScreen. - swap(FinalWipeScreen, TempRenderTexture); + if (FinalWipeScreen != TempRenderTexture) + { + swap(RenderTexture[CurrRenderTexture], FinalWipeScreen); + TempRenderTexture = RenderTexture[CurrRenderTexture]; + } // At this point, InitialWipeScreen holds the screen we are wiping from. // FinalWipeScreen holds the screen we are wiping to, which may be the @@ -315,11 +319,7 @@ void D3DFB::WipeCleanup() ScreenWipe = NULL; } SAFE_RELEASE( InitialWipeScreen ); - if (FinalWipeScreen != NULL && FinalWipeScreen != TempRenderTexture) - { - FinalWipeScreen->Release(); - } - FinalWipeScreen = NULL; + SAFE_RELEASE( FinalWipeScreen ); GatheringWipeScreen = false; if (!Accel2D) { @@ -338,6 +338,27 @@ D3DFB::Wiper::~Wiper() { } +//========================================================================== +// +// D3DFB :: Wiper :: DrawScreen +// +// Draw either the initial or target screen completely to the screen. +// +//========================================================================== + +void D3DFB::Wiper::DrawScreen(D3DFB *fb, IDirect3DTexture9 *tex, + D3DBLENDOP blendop, D3DCOLOR color0, D3DCOLOR color1) +{ + FBVERTEX verts[4]; + + fb->CalcFullscreenCoords(verts, false, false, color0, color1); + fb->D3DDevice->SetFVF(D3DFVF_FBVERTEX); + fb->SetTexture(0, tex); + fb->SetAlphaBlend(blendop, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA); + fb->SetPixelShader(fb->Shaders[SHADER_NormalColor]); + fb->D3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, sizeof(FBVERTEX)); +} + // WIPE: CROSSFADE --------------------------------------------------------- //========================================================================== @@ -363,29 +384,12 @@ bool D3DFB::Wiper_Crossfade::Run(int ticks, D3DFB *fb) { Clock += ticks; - // Put the initial screen back to the buffer, presumably with DMA. - IDirect3DSurface9 *source = NULL, *target = NULL; - - if (SUCCEEDED(fb->InitialWipeScreen->GetSurfaceLevel(0, &source)) && - SUCCEEDED(fb->D3DDevice->GetRenderTarget(0, &target))) - { - fb->D3DDevice->UpdateSurface(source, NULL, target, NULL); - target->Release(); - } - if (source != NULL) - { - source->Release(); - } + // Put the initial screen back to the buffer. + DrawScreen(fb, fb->InitialWipeScreen); // Draw the new screen on top of it. - FBVERTEX verts[4]; - - fb->CalcFullscreenCoords(verts, false, false, D3DCOLOR_COLORVALUE(0,0,0,Clock / 32.f), D3DCOLOR_RGBA(255,255,255,0)); - fb->D3DDevice->SetFVF(D3DFVF_FBVERTEX); - fb->SetTexture(0, fb->FinalWipeScreen); - fb->SetAlphaBlend(D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA); - fb->SetPixelShader(fb->Shaders[SHADER_NormalColor]); - fb->D3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, sizeof(FBVERTEX)); + DrawScreen(fb, fb->FinalWipeScreen, D3DBLENDOP_ADD, + D3DCOLOR_COLORVALUE(0,0,0,Clock / 32.f), D3DCOLOR_RGBA(255,255,255,0)); return Clock >= 32; } @@ -422,28 +426,8 @@ D3DFB::Wiper_Melt::Wiper_Melt() bool D3DFB::Wiper_Melt::Run(int ticks, D3DFB *fb) { - IDirect3DSurface9 *source = NULL, *target; - - if (FAILED(fb->InitialWipeScreen->GetSurfaceLevel(0, &source)) || - FAILED(fb->D3DDevice->GetRenderTarget(0, &target))) - { - // A fat lot of good we can do if we can't get these two surfaces. - if (source != NULL) - { - source->Release(); - } - return true; - } - // Draw the new screen on the bottom. - FBVERTEX verts[4]; - - fb->CalcFullscreenCoords(verts, false, false, 0, 0xFFFFFFFF); - fb->D3DDevice->SetFVF(D3DFVF_FBVERTEX); - fb->SetTexture(0, fb->FinalWipeScreen); - fb->SetAlphaBlend(D3DBLENDOP(0)); - fb->SetPixelShader(fb->Shaders[SHADER_NormalColor]); - fb->D3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, sizeof(FBVERTEX)); + DrawScreen(fb, fb->FinalWipeScreen); int i, dy; int fbwidth = fb->Width; @@ -475,19 +459,87 @@ bool D3DFB::Wiper_Melt::Run(int ticks, D3DFB *fb) dpt.x = i * fbwidth / WIDTH; dpt.y = MAX(0, y[i] * fbheight / HEIGHT); rect.left = dpt.x; - rect.top = fb->LBOffsetI; + rect.top = 0; rect.right = (i + 1) * fbwidth / WIDTH; - rect.bottom = fbheight - dpt.y + fb->LBOffsetI; - dpt.y += fb->LBOffsetI; + rect.bottom = fbheight - dpt.y; if (rect.bottom > rect.top) { - fb->D3DDevice->UpdateSurface(source, &rect, target, &dpt); + fb->CheckQuadBatch(); + + BufferedQuad *quad = &fb->QuadExtra[fb->QuadBatchPos]; + FBVERTEX *vert = &fb->VertexData[fb->VertexPos]; + WORD *index = &fb->IndexData[fb->IndexPos]; + + quad->Group1 = 0; + quad->Flags = BQF_DisableAlphaTest; + quad->ShaderNum = BQS_Plain; + quad->Palette = NULL; + quad->Texture = fb->InitialWipeScreen; + + // Fill the vertex buffer. + float u0 = rect.left / float(fb->FBWidth); + float v0 = 0; + float u1 = rect.right / float(fb->FBWidth); + float v1 = (rect.bottom - rect.top) / float(fb->FBHeight); + + float x0 = float(rect.left) - 0.5f; + float x1 = float(rect.right) - 0.5f; + float y0 = float(dpt.y + fb->LBOffsetI) - 0.5f; + float y1 = float(fbheight + fb->LBOffsetI) - 0.5f; + + vert[0].x = x0; + vert[0].y = y0; + vert[0].z = 0; + vert[0].rhw = 1; + vert[0].color0 = 0; + vert[0].color1 = 0xFFFFFFF; + vert[0].tu = u0; + vert[0].tv = v0; + + vert[1].x = x1; + vert[1].y = y0; + vert[1].z = 0; + vert[1].rhw = 1; + vert[1].color0 = 0; + vert[1].color1 = 0xFFFFFFF; + vert[1].tu = u1; + vert[1].tv = v0; + + vert[2].x = x1; + vert[2].y = y1; + vert[2].z = 0; + vert[2].rhw = 1; + vert[2].color0 = 0; + vert[2].color1 = 0xFFFFFFF; + vert[2].tu = u1; + vert[2].tv = v1; + + vert[3].x = x0; + vert[3].y = y1; + vert[3].z = 0; + vert[3].rhw = 1; + vert[3].color0 = 0; + vert[3].color1 = 0xFFFFFFF; + vert[3].tu = u0; + vert[3].tv = v1; + + // Fill the vertex index buffer. + index[0] = fb->VertexPos; + index[1] = fb->VertexPos + 1; + index[2] = fb->VertexPos + 2; + index[3] = fb->VertexPos; + index[4] = fb->VertexPos + 2; + index[5] = fb->VertexPos + 3; + + // Batch the quad. + fb->QuadBatchPos++; + fb->VertexPos += 4; + fb->IndexPos += 6; } } } } - target->Release(); - source->Release(); + fb->EndQuadBatch(); return done; } @@ -561,18 +613,7 @@ bool D3DFB::Wiper_Burn::Run(int ticks, D3DFB *fb) } // Put the initial screen back to the buffer. - IDirect3DSurface9 *source = NULL, *target; - - if (SUCCEEDED(fb->InitialWipeScreen->GetSurfaceLevel(0, &source)) && - SUCCEEDED(fb->D3DDevice->GetRenderTarget(0, &target))) - { - fb->D3DDevice->UpdateSurface(source, NULL, target, NULL); - target->Release(); - } - if (source != NULL) - { - source->Release(); - } + DrawScreen(fb, fb->InitialWipeScreen); // Burn the new screen on top of it. float top = fb->LBOffset - 0.5f; diff --git a/src/win32/i_input.cpp b/src/win32/i_input.cpp index 48aa2b4d8..e9d2a26bb 100644 --- a/src/win32/i_input.cpp +++ b/src/win32/i_input.cpp @@ -101,6 +101,7 @@ #include "cmdlib.h" #include "d_event.h" #include "v_text.h" +#include "version.h" // Prototypes and declarations. #include "rawinput.h" @@ -159,6 +160,11 @@ int SessionState = 0; CVAR (Bool, k_allowfullscreentoggle, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Bool, norawinput, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) +{ + Printf("This won't take effect until "GAMENAME" is restarted.\n"); +} + extern int chatmodeon; static void I_CheckGUICapture () @@ -431,6 +437,11 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { ToggleFullscreen = !ToggleFullscreen; } + // Pressing Alt+F4 quits the program. + if (wParam == VK_F4 && !(lParam & 0x40000000)) + { + PostQuitMessage(0); + } break; case WM_SYSCOMMAND: @@ -927,13 +938,16 @@ bool FInputDevice::WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lP static void FindRawInputFunctions() { - HMODULE user32 = GetModuleHandle("user32.dll"); - - if (user32 == NULL) + if (!norawinput) { - return; // WTF kind of broken system are we running on? - } + HMODULE user32 = GetModuleHandle("user32.dll"); + + if (user32 == NULL) + { + return; // WTF kind of broken system are we running on? + } #define RIF(name,ret,args) \ - My##name = (name##Proto)GetProcAddress(user32, #name); + My##name = (name##Proto)GetProcAddress(user32, #name); #include "rawinput.h" + } } diff --git a/src/win32/i_keyboard.cpp b/src/win32/i_keyboard.cpp index 47419d541..8e1488fc3 100644 --- a/src/win32/i_keyboard.cpp +++ b/src/win32/i_keyboard.cpp @@ -121,7 +121,8 @@ FKeyboard::~FKeyboard() // // FKeyboard :: CheckAndSetKey // -// Returns true if the key was already in the desired, false if it wasn't. +// Returns true if the key was already in the desired state, false if it +// wasn't. // //========================================================================== @@ -437,7 +438,7 @@ bool FRawKeyboard::GetDevice() // Convert scan codes to DirectInput key codes. For the most part, this is // straight forward: Scan codes without any prefix are passed unmodified. // Scan codes with an 0xE0 prefix byte are generally passed by ORing them -// with 0x80. And scan codes with an 0xE1 prefix are the annowing Pause key +// with 0x80. And scan codes with an 0xE1 prefix are the annoying Pause key // which will generate another scan code that looks like Num Lock. // // This is a bit complicated only because the state of PC key codes is a bit diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index 8db15225b..6320c33f5 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -1,25 +1,38 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// $Log:$ -// -// DESCRIPTION: -// -//----------------------------------------------------------------------------- +/* +** i_system.cpp +** Timers, pre-console output, IWAD selection, and misc system routines. +** +**--------------------------------------------------------------------------- +** Copyright 1998-2009 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ +// HEADER FILES ------------------------------------------------------------ #include #include @@ -39,10 +52,6 @@ #include #include -#ifdef _MSC_VER -#pragma warning(disable:4244) -#endif - #define USE_WINDOWS_DWORD #include "hardware.h" #include "doomerrors.h" @@ -75,23 +84,51 @@ #include "v_palette.h" #include "stats.h" -EXTERN_CVAR (String, language) +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- extern void CheckCPUID(CPUInfo *cpu); +extern void LayoutMainWindow(HWND hWnd, HWND pane); + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void CalculateCPUSpeed(); +static void I_SelectTimer(); + +static int I_GetTimePolled(bool saveMS); +static int I_WaitForTicPolled(int prevtic); +static void I_FreezeTimePolled(bool frozen); +static int I_GetTimeEventDriven(bool saveMS); +static int I_WaitForTicEvent(int prevtic); +static void I_FreezeTimeEventDriven(bool frozen); +static void CALLBACK TimerTicked(UINT id, UINT msg, DWORD_PTR user, DWORD_PTR dw1, DWORD_PTR dw2); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +EXTERN_CVAR(String, language); +EXTERN_CVAR (Bool, queryiwad); extern HWND Window, ConWindow, GameTitleWindow; extern HANDLE StdOut; extern bool FancyStdOut; extern HINSTANCE g_hInst; +extern FILE *Logfile; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +CVAR (String, queryiwad_key, "shift", CVAR_GLOBALCONFIG|CVAR_ARCHIVE); double PerfToSec, PerfToMillisec; - UINT TimerPeriod; UINT TimerEventID; UINT MillisecondsPerTic; HANDLE NewTicArrived; uint32 LanguageIDs[4]; -void CalculateCPUSpeed (); const IWADInfo *DoomStartupInfo; @@ -100,42 +137,175 @@ int (*I_WaitForTic) (int); void (*I_FreezeTime) (bool frozen); os_t OSPlatform; +bool gameisdead; -void I_Tactile (int on, int off, int total) +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static ticcmd_t emptycmd; +static bool HasExited; + +static DWORD basetime = 0; +// These are for the polled timer. +static DWORD TicStart; +static DWORD TicNext; +static int TicFrozen; + +// These are for the event-driven timer. +static int tics; +static DWORD ted_start, ted_next; + +static WadStuff *WadList; +static int NumWads; +static int DefaultWad; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// I_Tactile +// +// Doom calls it when you take damage, so presumably it could be converted +// to something compatible with force feedback. +// +//========================================================================== + +void I_Tactile(int on, int off, int total) { // UNUSED. on = off = total = 0; } -ticcmd_t emptycmd; -ticcmd_t *I_BaseTiccmd(void) +//========================================================================== +// +// I_BaseTiccmd +// +// Returns an empty ticcmd. I have no idea why this should be system- +// specific. +// +//========================================================================== + +ticcmd_t *I_BaseTiccmd() { return &emptycmd; } -static DWORD basetime = 0; +// Stubs that select the timer to use and then call into it ---------------- -// [RH] Returns time in milliseconds -unsigned int I_MSTime (void) +//========================================================================== +// +// I_GetTimeSelect +// +//========================================================================== + +static int I_GetTimeSelect(bool saveMS) { - DWORD tm; - - tm = timeGetTime(); - if (!basetime) - basetime = tm; - - return tm - basetime; + I_SelectTimer(); + return I_GetTime(saveMS); } -static DWORD TicStart; -static DWORD TicNext; -static int TicFrozen; +//========================================================================== +// +// I_WaitForTicSelect +// +//========================================================================== +static int I_WaitForTicSelect(int prevtic) +{ + I_SelectTimer(); + return I_WaitForTic(prevtic); +} + +//========================================================================== // -// I_GetTime -// returns time in 1/35th second tics +// I_SelectTimer // -int I_GetTimePolled (bool saveMS) +// Tries to create a timer event for efficent CPU use when the FPS is +// capped. Failing that, it sets things up for a polling timer instead. +// +//========================================================================== + +static void I_SelectTimer() +{ + assert(basetime == 0); + + // Use a timer event if possible. + NewTicArrived = CreateEvent(NULL, FALSE, FALSE, NULL); + if (NewTicArrived) + { + UINT delay; + char *cmdDelay; + + cmdDelay = Args->CheckValue("-timerdelay"); + delay = 0; + if (cmdDelay != 0) + { + delay = atoi(cmdDelay); + } + if (delay == 0) + { + delay = 1000/TICRATE; + } + MillisecondsPerTic = delay; + TimerEventID = timeSetEvent(delay, 0, TimerTicked, 0, TIME_PERIODIC); + } + // Get the current time as the basetime. + basetime = timeGetTime(); + // Set timer functions. + if (TimerEventID != 0) + { + I_GetTime = I_GetTimeEventDriven; + I_WaitForTic = I_WaitForTicEvent; + I_FreezeTime = I_FreezeTimeEventDriven; + } + else + { + I_GetTime = I_GetTimePolled; + I_WaitForTic = I_WaitForTicPolled; + I_FreezeTime = I_FreezeTimePolled; + } +} + +//========================================================================== +// +// I_MSTime +// +// Returns the current time in milliseconds, where 0 is the first call +// to I_GetTime or I_WaitForTic. +// +//========================================================================== + +unsigned int I_MSTime() +{ + assert(basetime != 0); + return timeGetTime() - basetime; +} + +//========================================================================== +// +// I_FPSTime +// +// Returns the current system time in milliseconds. This is used by the FPS +// meter of DFrameBuffer::DrawRateStuff(). Since the screen can display +// before the play simulation is ready to begin, this needs to be +// separate from I_MSTime(). +// +//========================================================================== + +unsigned int I_FPSTime() +{ + return timeGetTime(); +} + +//========================================================================== +// +// I_GetTimePolled +// +// Returns the current time in tics. If saveMS is true, then calls to +// I_GetTimeFrac() will use this tic as 0 and the next tic as 1. +// +//========================================================================== + +static int I_GetTimePolled(bool saveMS) { DWORD tm; @@ -145,9 +315,10 @@ int I_GetTimePolled (bool saveMS) } tm = timeGetTime(); - if (!basetime) + if (basetime == 0) + { basetime = tm; - + } if (saveMS) { TicStart = tm; @@ -157,18 +328,35 @@ int I_GetTimePolled (bool saveMS) return ((tm-basetime)*TICRATE)/1000; } -int I_WaitForTicPolled (int prevtic) +//========================================================================== +// +// I_WaitForTicPolled +// +// Busy waits until the current tic is greater than prevtic. Time must not +// be frozen. +// +//========================================================================== + +static int I_WaitForTicPolled(int prevtic) { int time; assert(TicFrozen == 0); while ((time = I_GetTimePolled(false)) <= prevtic) - ; + { } return time; } -void I_FreezeTimePolled (bool frozen) +//========================================================================== +// +// I_FreezeTimePolled +// +// Freeze/unfreeze the timer. +// +//========================================================================== + +static void I_FreezeTimePolled(bool frozen) { if (frozen) { @@ -185,11 +373,16 @@ void I_FreezeTimePolled (bool frozen) } } +//========================================================================== +// +// I_GetTimeEventDriven +// +// Returns the current tick counter. This is incremented asynchronously as +// the timer event fires. +// +//========================================================================== -static int tics; -static DWORD ted_start, ted_next; - -int I_GetTimeEventDriven (bool saveMS) +static int I_GetTimeEventDriven(bool saveMS) { if (saveMS) { @@ -199,18 +392,47 @@ int I_GetTimeEventDriven (bool saveMS) return tics; } -int I_WaitForTicEvent (int prevtic) +//========================================================================== +// +// I_WaitForTicEvent +// +// Waits on the timer event as long as the current tic is not later than +// prevtic. +// +//========================================================================== + +static int I_WaitForTicEvent(int prevtic) { assert(!TicFrozen); while (prevtic >= tics) { WaitForSingleObject(NewTicArrived, 1000/TICRATE); } - return tics; } -void CALLBACK TimerTicked (UINT id, UINT msg, DWORD_PTR user, DWORD_PTR dw1, DWORD_PTR dw2) +//========================================================================== +// +// I_FreezeTimeEventDriven +// +// Freeze/unfreeze the ticker. +// +//========================================================================== + +static void I_FreezeTimeEventDriven(bool frozen) +{ + TicFrozen = frozen; +} + +//========================================================================== +// +// TimerTicked +// +// Advance the tick count and signal the NewTicArrived event. +// +//========================================================================== + +static void CALLBACK TimerTicked(UINT id, UINT msg, DWORD_PTR user, DWORD_PTR dw1, DWORD_PTR dw2) { if (!TicFrozen) { @@ -218,19 +440,25 @@ void CALLBACK TimerTicked (UINT id, UINT msg, DWORD_PTR user, DWORD_PTR dw1, DWO } ted_start = timeGetTime (); ted_next = ted_start + MillisecondsPerTic; - SetEvent (NewTicArrived); + SetEvent(NewTicArrived); } -void I_FreezeTimeEventDriven(bool frozen) -{ - TicFrozen = frozen; -} +//========================================================================== +// +// I_GetTimeFrac +// +// Returns the fractional amount of a tic passed since the most recently +// saved tic. +// +//========================================================================== -// Returns the fractional amount of a tic passed since the most recent tic -fixed_t I_GetTimeFrac (uint32 *ms) +fixed_t I_GetTimeFrac(uint32 *ms) { DWORD now = timeGetTime(); - if (ms) *ms = TicNext; + if (ms != NULL) + { + *ms = TicNext; + } DWORD step = TicNext - TicStart; if (step == 0) { @@ -243,21 +471,41 @@ fixed_t I_GetTimeFrac (uint32 *ms) } } -void I_WaitVBL (int count) +//========================================================================== +// +// I_WaitVBL +// +// I_WaitVBL is never used to actually synchronize to the vertical blank. +// Instead, it's used for delay purposes. Doom used a 70 Hz display mode, +// so that's what we use to determine how long to wait for. +// +//========================================================================== + +void I_WaitVBL(int count) { - // I_WaitVBL is never used to actually synchronize to the - // vertical blank. Instead, it's used for delay purposes. - Sleep (1000 * count / 70); + Sleep(1000 * count / 70); } -// [RH] Detect the OS the game is running under -void I_DetectOS (void) +//========================================================================== +// +// I_DetectOS +// +// Determine which version of Windows the game is running on. +// +//========================================================================== + +void I_DetectOS(void) { - OSVERSIONINFO info; + OSVERSIONINFOEX info; const char *osname; - info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx (&info); + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + if (!GetVersionEx((OSVERSIONINFO *)&info)) + { + // Retry with the older OSVERSIONINFO structure. + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx((OSVERSIONINFO *)&info); + } switch (info.dwPlatformId) { @@ -295,9 +543,30 @@ void I_DetectOS (void) osname = "Server 2003"; } } - else if (info.dwMajorVersion == 6 && info.dwMinorVersion == 0) + else if (info.dwMajorVersion == 6) { - osname = "Vista"; + if (info.dwMinorVersion == 0) + { + if (info.wProductType == VER_NT_WORKSTATION) + { + osname = "Vista"; + } + else + { + osname = "Server 2008"; + } + } + else if (info.dwMinorVersion == 1) + { + if (info.wProductType == VER_NT_WORKSTATION) + { + osname = "7"; + } + else + { + osname = "Server 2008 R2"; + } + } } break; @@ -316,7 +585,7 @@ void I_DetectOS (void) } else { - Printf ("OS: Windows %s %lu.%lu (Build %lu)\n %s\n", + Printf ("OS: Windows %s (NT %lu.%lu) Build %lu\n %s\n", osname, info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber, info.szCSDVersion); @@ -329,19 +598,24 @@ void I_DetectOS (void) } } +//========================================================================== // // SubsetLanguageIDs // -static void SubsetLanguageIDs (LCID id, LCTYPE type, int idx) +// Helper function for SetLanguageIDs. +// +//========================================================================== + +static void SubsetLanguageIDs(LCID id, LCTYPE type, int idx) { char buf[8]; LCID langid; char *idp; - if (!GetLocaleInfo (id, type, buf, 8)) + if (!GetLocaleInfo(id, type, buf, 8)) return; - langid = MAKELCID (strtoul(buf, NULL, 16), SORT_DEFAULT); - if (!GetLocaleInfo (langid, LOCALE_SABBREVLANGNAME, buf, 8)) + langid = MAKELCID(strtoul(buf, NULL, 16), SORT_DEFAULT); + if (!GetLocaleInfo(langid, LOCALE_SABBREVLANGNAME, buf, 8)) return; idp = (char *)(&LanguageIDs[idx]); memset (idp, 0, 4); @@ -351,20 +625,23 @@ static void SubsetLanguageIDs (LCID id, LCTYPE type, int idx) idp[3] = 0; } +//========================================================================== // // SetLanguageIDs // -void SetLanguageIDs () +//========================================================================== + +void SetLanguageIDs() { - size_t langlen = strlen (language); + size_t langlen = strlen(language); if (langlen < 2 || langlen > 3) { - memset (LanguageIDs, 0, sizeof(LanguageIDs)); - SubsetLanguageIDs (LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, 0); - SubsetLanguageIDs (LOCALE_USER_DEFAULT, LOCALE_IDEFAULTLANGUAGE, 1); - SubsetLanguageIDs (LOCALE_SYSTEM_DEFAULT, LOCALE_ILANGUAGE, 2); - SubsetLanguageIDs (LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTLANGUAGE, 3); + memset(LanguageIDs, 0, sizeof(LanguageIDs)); + SubsetLanguageIDs(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, 0); + SubsetLanguageIDs(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTLANGUAGE, 1); + SubsetLanguageIDs(LOCALE_SYSTEM_DEFAULT, LOCALE_ILANGUAGE, 2); + SubsetLanguageIDs(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTLANGUAGE, 3); } else { @@ -380,6 +657,16 @@ void SetLanguageIDs () } } +//========================================================================== +// +// CalculateCPUSpeed +// +// Make a decent guess at how much time elapses between TSC steps. This can +// vary over runtime depending on power management settings, so should not +// be used anywhere that truely accurate timing actually matters. +// +//========================================================================== + void CalculateCPUSpeed() { LARGE_INTEGER freq; @@ -426,88 +713,60 @@ void CalculateCPUSpeed() Printf ("CPU Speed: %.0f MHz\n", 0.001 / PerfToMillisec); } +//========================================================================== // // I_Init // +//========================================================================== -void I_Init (void) +void I_Init() { CheckCPUID(&CPU); CalculateCPUSpeed(); DumpCPUInfo(&CPU); - // Use a timer event if possible - NewTicArrived = CreateEvent (NULL, FALSE, FALSE, NULL); - if (NewTicArrived) - { - UINT delay; - char *cmdDelay; - - cmdDelay = Args->CheckValue ("-timerdelay"); - delay = 0; - if (cmdDelay != 0) - { - delay = atoi (cmdDelay); - } - if (delay == 0) - { - delay = 1000/TICRATE; - } - TimerEventID = timeSetEvent - ( - delay, - 0, - TimerTicked, - 0, - TIME_PERIODIC - ); - MillisecondsPerTic = delay; - } - if (TimerEventID != 0) - { - I_GetTime = I_GetTimeEventDriven; - I_WaitForTic = I_WaitForTicEvent; - I_FreezeTime = I_FreezeTimeEventDriven; - } - else - { // If no timer event, busy-loop with timeGetTime - I_GetTime = I_GetTimePolled; - I_WaitForTic = I_WaitForTicPolled; - I_FreezeTime = I_FreezeTimePolled; - } + I_GetTime = I_GetTimeSelect; + I_WaitForTic = I_WaitForTicSelect; atterm (I_ShutdownSound); I_InitSound (); } +//========================================================================== // // I_Quit // -static int has_exited; +//========================================================================== -void I_Quit (void) +void I_Quit() { - has_exited = 1; /* Prevent infinitely recursive exits -- killough */ - - if (TimerEventID) - timeKillEvent (TimerEventID); - if (NewTicArrived) - CloseHandle (NewTicArrived); - - timeEndPeriod (TimerPeriod); + HasExited = true; /* Prevent infinitely recursive exits -- killough */ + if (TimerEventID != 0) + { + timeKillEvent(TimerEventID); + } + if (NewTicArrived != NULL) + { + CloseHandle(NewTicArrived); + } + timeEndPeriod(TimerPeriod); if (demorecording) + { G_CheckDemoStatus(); + } } +//========================================================================== // -// I_Error +// I_FatalError // -extern FILE *Logfile; -bool gameisdead; +// Throw an error that will end the game. +// +//========================================================================== -void STACK_ARGS I_FatalError (const char *error, ...) +void STACK_ARGS I_FatalError(const char *error, ...) { static BOOL alreadyThrown = false; gameisdead = true; @@ -517,50 +776,72 @@ void STACK_ARGS I_FatalError (const char *error, ...) alreadyThrown = true; char errortext[MAX_ERRORTEXT]; va_list argptr; - va_start (argptr, error); - myvsnprintf (errortext, MAX_ERRORTEXT, error, argptr); - va_end (argptr); + va_start(argptr, error); + myvsnprintf(errortext, MAX_ERRORTEXT, error, argptr); + va_end(argptr); // Record error to log (if logging) if (Logfile) { - fprintf (Logfile, "\n**** DIED WITH FATAL ERROR:\n%s\n", errortext); - fflush (Logfile); + fprintf(Logfile, "\n**** DIED WITH FATAL ERROR:\n%s\n", errortext); + fflush(Logfile); } - throw CFatalError (errortext); + throw CFatalError(errortext); } - if (!has_exited) // If it hasn't exited yet, exit now -- killough + if (!HasExited) // If it hasn't exited yet, exit now -- killough { - has_exited = 1; // Prevent infinitely recursive exits -- killough + HasExited = 1; // Prevent infinitely recursive exits -- killough exit(-1); } } -void STACK_ARGS I_Error (const char *error, ...) +//========================================================================== +// +// I_Error +// +// Throw an error that will send us to the console if we are far enough +// along in the startup process. +// +//========================================================================== + +void STACK_ARGS I_Error(const char *error, ...) { va_list argptr; char errortext[MAX_ERRORTEXT]; - va_start (argptr, error); - myvsnprintf (errortext, MAX_ERRORTEXT, error, argptr); - va_end (argptr); + va_start(argptr, error); + myvsnprintf(errortext, MAX_ERRORTEXT, error, argptr); + va_end(argptr); - throw CRecoverableError (errortext); + throw CRecoverableError(errortext); } -extern void LayoutMainWindow (HWND hWnd, HWND pane); +//========================================================================== +// +// I_SetIWADInfo +// +//========================================================================== -void I_SetIWADInfo (const IWADInfo *info) +void I_SetIWADInfo(const IWADInfo *info) { DoomStartupInfo = info; // Make the startup banner show itself - LayoutMainWindow (Window, NULL); + LayoutMainWindow(Window, NULL); } -void I_PrintStr (const char *cp) +//========================================================================== +// +// I_PrintStr +// +// Send output to the list box shown during startup (and hidden during +// gameplay). +// +//========================================================================== + +void I_PrintStr(const char *cp) { if (ConWindow == NULL && StdOut == NULL) return; @@ -576,16 +857,16 @@ void I_PrintStr (const char *cp) if (edit != NULL) { // Store the current selection and set it to the end so we can append text. - SendMessage (edit, EM_EXGETSEL, 0, (LPARAM)&selection); - endselection.cpMax = endselection.cpMin = GetWindowTextLength (edit); - SendMessage (edit, EM_EXSETSEL, 0, (LPARAM)&endselection); + SendMessage(edit, EM_EXGETSEL, 0, (LPARAM)&selection); + endselection.cpMax = endselection.cpMin = GetWindowTextLength(edit); + SendMessage(edit, EM_EXSETSEL, 0, (LPARAM)&endselection); // GetWindowTextLength and EM_EXSETSEL can disagree on where the end of // the text is. Find out what EM_EXSETSEL thought it was and use that later. - SendMessage (edit, EM_EXGETSEL, 0, (LPARAM)&endselection); + SendMessage(edit, EM_EXGETSEL, 0, (LPARAM)&endselection); // Remember how many lines there were before we added text. - lines_before = SendMessage (edit, EM_GETLINECOUNT, 0, 0); + lines_before = (LONG)SendMessage(edit, EM_GETLINECOUNT, 0, 0); } while (*cp != 0) @@ -596,7 +877,7 @@ void I_PrintStr (const char *cp) buf[bpos] = 0; if (edit != NULL) { - SendMessage (edit, EM_REPLACESEL, FALSE, (LPARAM)buf); + SendMessage(edit, EM_REPLACESEL, FALSE, (LPARAM)buf); } if (StdOut != NULL) { @@ -612,13 +893,13 @@ void I_PrintStr (const char *cp) else { const BYTE *color_id = (const BYTE *)cp + 1; - EColorRange range = V_ParseFontColor (color_id, CR_UNTRANSLATED, CR_YELLOW); + EColorRange range = V_ParseFontColor(color_id, CR_UNTRANSLATED, CR_YELLOW); cp = (const char *)color_id; if (range != CR_UNDEFINED) { // Change the color of future text added to the control. - PalEntry color = V_LogColorFromColorRange (range); + PalEntry color = V_LogColorFromColorRange(range); if (StdOut != NULL && FancyStdOut) { // Unfortunately, we are pretty limited here: There are only @@ -627,7 +908,7 @@ void I_PrintStr (const char *cp) float h, s, v, r, g, b; WORD attrib = 0; - RGBtoHSV(color.r / 255.0, color.g / 255.0, color.b / 255.0, &h, &s, &v); + RGBtoHSV(color.r / 255.f, color.g / 255.f, color.b / 255.f, &h, &s, &v); if (s != 0) { // color HSVtoRGB(&r, &g, &b, h, 1, 1); @@ -647,13 +928,13 @@ void I_PrintStr (const char *cp) if (edit != NULL) { // GDI uses BGR colors, but color is RGB, so swap the R and the B. - swap (color.r, color.b); + swap(color.r, color.b); // Change the color. format.cbSize = sizeof(format); format.dwMask = CFM_COLOR; format.dwEffects = 0; format.crTextColor = color; - SendMessage (edit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format); + SendMessage(edit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format); } } } @@ -663,7 +944,7 @@ void I_PrintStr (const char *cp) buf[bpos] = 0; if (edit != NULL) { - SendMessage (edit, EM_REPLACESEL, FALSE, (LPARAM)buf); + SendMessage(edit, EM_REPLACESEL, FALSE, (LPARAM)buf); } if (StdOut != NULL) { @@ -679,16 +960,16 @@ void I_PrintStr (const char *cp) if (selection.cpMin == endselection.cpMin && selection.cpMax == endselection.cpMax) { selection.cpMax = selection.cpMin = GetWindowTextLength (edit); - lines_after = SendMessage (edit, EM_GETLINECOUNT, 0, 0); + lines_after = (LONG)SendMessage(edit, EM_GETLINECOUNT, 0, 0); if (lines_after > lines_before) { - SendMessage (edit, EM_LINESCROLL, 0, lines_after - lines_before); + SendMessage(edit, EM_LINESCROLL, 0, lines_after - lines_before); } } // Restore the previous selection. - SendMessage (edit, EM_EXSETSEL, 0, (LPARAM)&selection); + SendMessage(edit, EM_EXSETSEL, 0, (LPARAM)&selection); // Give the edit control a chance to redraw itself. - I_GetEvent (); + I_GetEvent(); } if (StdOut != NULL && FancyStdOut) { // Set text back to gray, in case it was changed. @@ -696,21 +977,24 @@ void I_PrintStr (const char *cp) } } -EXTERN_CVAR (Bool, queryiwad); -CVAR (String, queryiwad_key, "shift", CVAR_GLOBALCONFIG|CVAR_ARCHIVE); -static WadStuff *WadList; -static int NumWads; -static int DefaultWad; +//========================================================================== +// +// SetQueryIWAD +// +// The user had the "Don't ask again" box checked when they closed the +// IWAD selection dialog. +// +//========================================================================== -static void SetQueryIWad (HWND dialog) +static void SetQueryIWad(HWND dialog) { - HWND checkbox = GetDlgItem (dialog, IDC_DONTASKIWAD); - int state = SendMessage (checkbox, BM_GETCHECK, 0, 0); + HWND checkbox = GetDlgItem(dialog, IDC_DONTASKIWAD); + int state = (int)SendMessage(checkbox, BM_GETCHECK, 0, 0); bool query = (state != BST_CHECKED); if (!query && queryiwad) { - MessageBox (dialog, + MessageBox(dialog, "You have chosen not to show this dialog box in the future.\n" "If you wish to see it again, hold down SHIFT while starting " GAMENAME ".", "Don't ask me this again", @@ -720,7 +1004,15 @@ static void SetQueryIWad (HWND dialog) queryiwad = query; } -BOOL CALLBACK IWADBoxCallback (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +//========================================================================== +// +// IWADBoxCallback +// +// Dialog proc for the IWAD selector. +// +//========================================================================== + +BOOL CALLBACK IWADBoxCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { HWND ctrl; int i; @@ -733,32 +1025,32 @@ BOOL CALLBACK IWADBoxCallback (HWND hDlg, UINT message, WPARAM wParam, LPARAM lP TCHAR label[256]; FString newlabel; - GetWindowText (hDlg, label, countof(label)); - newlabel.Format (GAMESIG " " DOTVERSIONSTR_NOREV ": %s", label); - SetWindowText (hDlg, newlabel.GetChars()); + GetWindowText(hDlg, label, countof(label)); + newlabel.Format(GAMESIG " " DOTVERSIONSTR_NOREV ": %s", label); + SetWindowText(hDlg, newlabel.GetChars()); } // Populate the list with all the IWADs found - ctrl = GetDlgItem (hDlg, IDC_IWADLIST); + ctrl = GetDlgItem(hDlg, IDC_IWADLIST); for (i = 0; i < NumWads; i++) { FString work; - const char *filepart = strrchr (WadList[i].Path, '/'); + const char *filepart = strrchr(WadList[i].Path, '/'); if (filepart == NULL) filepart = WadList[i].Path; else filepart++; - work.Format ("%s (%s)", IWADInfos[WadList[i].Type].Name, filepart); - SendMessage (ctrl, LB_ADDSTRING, 0, (LPARAM)work.GetChars()); - SendMessage (ctrl, LB_SETITEMDATA, i, (LPARAM)i); + work.Format("%s (%s)", IWADInfos[WadList[i].Type].Name, filepart); + SendMessage(ctrl, LB_ADDSTRING, 0, (LPARAM)work.GetChars()); + SendMessage(ctrl, LB_SETITEMDATA, i, (LPARAM)i); } - SendMessage (ctrl, LB_SETCURSEL, DefaultWad, 0); - SetFocus (ctrl); + SendMessage(ctrl, LB_SETCURSEL, DefaultWad, 0); + SetFocus(ctrl); // Set the state of the "Don't ask me again" checkbox - ctrl = GetDlgItem (hDlg, IDC_DONTASKIWAD); - SendMessage (ctrl, BM_SETCHECK, queryiwad ? BST_UNCHECKED : BST_CHECKED, 0); + ctrl = GetDlgItem(hDlg, IDC_DONTASKIWAD); + SendMessage(ctrl, BM_SETCHECK, queryiwad ? BST_UNCHECKED : BST_CHECKED, 0); // Make sure the dialog is in front. If SHIFT was pressed to force it visible, // then the other window will normally be on top. - SetForegroundWindow (hDlg); + SetForegroundWindow(hDlg); break; case WM_COMMAND: @@ -769,24 +1061,32 @@ BOOL CALLBACK IWADBoxCallback (HWND hDlg, UINT message, WPARAM wParam, LPARAM lP else if (LOWORD(wParam) == IDOK || (LOWORD(wParam) == IDC_IWADLIST && HIWORD(wParam) == LBN_DBLCLK)) { - SetQueryIWad (hDlg); + SetQueryIWad(hDlg); ctrl = GetDlgItem (hDlg, IDC_IWADLIST); - EndDialog (hDlg, SendMessage (ctrl, LB_GETCURSEL, 0, 0)); + EndDialog(hDlg, SendMessage (ctrl, LB_GETCURSEL, 0, 0)); } break; } return FALSE; } -int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad) +//========================================================================== +// +// I_PickIWad +// +// Open a dialog to pick the IWAD, if there is more than one found. +// +//========================================================================== + +int I_PickIWad(WadStuff *wads, int numwads, bool showwin, int defaultiwad) { int vkey; - if (stricmp (queryiwad_key, "shift") == 0) + if (stricmp(queryiwad_key, "shift") == 0) { vkey = VK_SHIFT; } - else if (stricmp (queryiwad_key, "control") == 0 || stricmp (queryiwad_key, "ctrl") == 0) + else if (stricmp(queryiwad_key, "control") == 0 || stricmp (queryiwad_key, "ctrl") == 0) { vkey = VK_CONTROL; } @@ -800,13 +1100,21 @@ int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad) NumWads = numwads; DefaultWad = defaultiwad; - return DialogBox (g_hInst, MAKEINTRESOURCE(IDD_IWADDIALOG), + return (int)DialogBox(g_hInst, MAKEINTRESOURCE(IDD_IWADDIALOG), (HWND)Window, (DLGPROC)IWADBoxCallback); } return defaultiwad; } -bool I_WriteIniFailed () +//========================================================================== +// +// I_WriteIniFailed +// +// Display a message when the config failed to save. +// +//========================================================================== + +bool I_WriteIniFailed() { char *lpMsgBuf; FString errortext; @@ -823,23 +1131,56 @@ bool I_WriteIniFailed () ); errortext.Format ("The config file %s could not be written:\n%s", GameConfig->GetPathName(), lpMsgBuf); LocalFree (lpMsgBuf); - return MessageBox (Window, errortext.GetChars(), GAMENAME " configuration not saved", MB_ICONEXCLAMATION | MB_RETRYCANCEL) == IDRETRY; + return MessageBox(Window, errortext.GetChars(), GAMENAME " configuration not saved", MB_ICONEXCLAMATION | MB_RETRYCANCEL) == IDRETRY; } -void *I_FindFirst (const char *filespec, findstate_t *fileinfo) +//========================================================================== +// +// I_FindFirst +// +// Start a pattern matching sequence. +// +//========================================================================== + +void *I_FindFirst(const char *filespec, findstate_t *fileinfo) { - return FindFirstFileA (filespec, (LPWIN32_FIND_DATAA)fileinfo); -} -int I_FindNext (void *handle, findstate_t *fileinfo) -{ - return !FindNextFileA ((HANDLE)handle, (LPWIN32_FIND_DATAA)fileinfo); + return FindFirstFileA(filespec, (LPWIN32_FIND_DATAA)fileinfo); } -int I_FindClose (void *handle) +//========================================================================== +// +// I_FindNext +// +// Return the next file in a pattern matching sequence. +// +//========================================================================== + +int I_FindNext(void *handle, findstate_t *fileinfo) { - return FindClose ((HANDLE)handle); + return !FindNextFileA((HANDLE)handle, (LPWIN32_FIND_DATAA)fileinfo); } +//========================================================================== +// +// I_FindClose +// +// Finish a pattern matching sequence. +// +//========================================================================== + +int I_FindClose(void *handle) +{ + return FindClose((HANDLE)handle); +} + +//========================================================================== +// +// QueryPathKey +// +// Returns the value of a registry key into the output variable value. +// +//========================================================================== + static bool QueryPathKey(HKEY key, const char *keypath, const char *valname, FString &value) { HKEY steamkey; @@ -866,6 +1207,15 @@ static bool QueryPathKey(HKEY key, const char *keypath, const char *valname, FSt return value.IsNotEmpty(); } +//========================================================================== +// +// I_GetSteamPath +// +// Check the registry for the path to Steam, so that we can search for +// IWADs that were bought with Steam. +// +//========================================================================== + FString I_GetSteamPath() { FString path; @@ -882,7 +1232,14 @@ FString I_GetSteamPath() return path; } -// Return a random seed, preferably one with lots of entropy. +//========================================================================== +// +// I_MakeRNGSeed +// +// Returns a 32-bit random seed, preferably one with lots of entropy. +// +//========================================================================== + unsigned int I_MakeRNGSeed() { unsigned int seed; diff --git a/src/win32/i_system.h b/src/win32/i_system.h index f268358d7..66b2bac7d 100644 --- a/src/win32/i_system.h +++ b/src/win32/i_system.h @@ -132,6 +132,7 @@ bool I_WriteIniFailed (); // [RH] Returns millisecond-accurate time unsigned int I_MSTime (void); +unsigned int I_FPSTime(); // [RH] Title banner to display during startup extern const IWADInfo *DoomStartupInfo; diff --git a/src/win32/win32iface.h b/src/win32/win32iface.h index e4c21a015..9bdfd1072 100644 --- a/src/win32/win32iface.h +++ b/src/win32/win32iface.h @@ -248,7 +248,7 @@ public: void DrawBlendingRect (); FNativeTexture *CreateTexture (FTexture *gametex, bool wrapping); FNativePalette *CreatePalette (FRemapTable *remap); - void STACK_ARGS DrawTextureV (FTexture *img, int x, int y, uint32 tag, va_list tags); + void STACK_ARGS DrawTextureV (FTexture *img, double x, double y, uint32 tag, va_list tags); void Clear (int left, int top, int right, int bottom, int palcolor, uint32 color); void Dim (PalEntry color, float amount, int x1, int y1, int w, int h); void FlatFill (int left, int top, int right, int bottom, FTexture *src, bool local_origin); @@ -289,7 +289,7 @@ private: DWORD Group1; }; D3DPal *Palette; - PackingTexture *Texture; + IDirect3DTexture9 *Texture; }; enum @@ -336,13 +336,15 @@ private: void CreateBlockSurfaces(); bool CreateFBTexture(); bool CreatePaletteTexture(); - bool CreateGrayPaletteTexture(); + bool CreateGammaTexture(); bool CreateVertexes(); + void DoOffByOneCheck(); void UploadPalette(); + void UpdateGammaTexture(float igamma); void FillPresentParameters (D3DPRESENT_PARAMETERS *pp, bool fullscreen, bool vsync); void CalcFullscreenCoords (FBVERTEX verts[4], bool viewarea_only, bool can_double, D3DCOLOR color0, D3DCOLOR color1) const; bool Reset(); - IDirect3DTexture9 *GetCurrentScreen(); + IDirect3DTexture9 *GetCurrentScreen(D3DPOOL pool=D3DPOOL_SYSTEMMEM); void ReleaseDefaultPoolItems(); void KillNativePals(); void KillNativeTexs(); @@ -361,6 +363,7 @@ private: void BeginLineBatch(); void EndLineBatch(); void EndBatch(); + void CopyNextFrontBuffer(); D3DCAPS9 DeviceCaps; @@ -381,7 +384,7 @@ private: float Constant[3][4]; D3DCOLOR CurBorderColor; IDirect3DPixelShader9 *CurPixelShader; - IDirect3DTexture9 *Texture[2]; + IDirect3DTexture9 *Texture[5]; PalEntry SourcePalette[256]; D3DCOLOR BorderColor; @@ -390,14 +393,15 @@ private: int FlashAmount; int TrueHeight; int PixelDoubling; + int SkipAt; int LBOffsetI; + int RenderTextureToggle; + int CurrRenderTexture; float LBOffset; float Gamma; bool UpdatePending; bool NeedPalUpdate; bool NeedGammaUpdate; - D3DFORMAT FBFormat; - D3DFORMAT PalFormat; int FBWidth, FBHeight; bool VSync; RECT BlendingRect; @@ -413,10 +417,12 @@ private: IDirect3DDevice9 *D3DDevice; IDirect3DTexture9 *FBTexture; - IDirect3DTexture9 *TempRenderTexture; + IDirect3DTexture9 *TempRenderTexture, *RenderTexture[2]; IDirect3DTexture9 *PaletteTexture; + IDirect3DTexture9 *GammaTexture; IDirect3DTexture9 *ScreenshotTexture; IDirect3DSurface9 *ScreenshotSurface; + IDirect3DSurface9 *FrontCopySurface; IDirect3DVertexBuffer9 *VertexBuffer; FBVERTEX *VertexData; @@ -429,6 +435,7 @@ private: enum { BATCH_None, BATCH_Quads, BATCH_Lines } BatchType; IDirect3DPixelShader9 *Shaders[NUM_SHADERS]; + IDirect3DPixelShader9 *GammaShader; IDirect3DSurface9 *BlockSurface[2]; IDirect3DSurface9 *OldRenderTarget; @@ -441,6 +448,9 @@ private: public: virtual ~Wiper(); virtual bool Run(int ticks, D3DFB *fb) = 0; + + void DrawScreen(D3DFB *fb, IDirect3DTexture9 *tex, + D3DBLENDOP blendop=D3DBLENDOP(0), D3DCOLOR color0=0, D3DCOLOR color1=0xFFFFFFF); }; class Wiper_Melt; friend class Wiper_Melt; @@ -450,6 +460,30 @@ private: Wiper *ScreenWipe; }; +// Flags for a buffered quad +enum +{ + BQF_GamePalette = 1, + BQF_CustomPalette = 7, + BQF_Paletted = 7, + BQF_Bilinear = 8, + BQF_WrapUV = 16, + BQF_InvertSource = 32, + BQF_DisableAlphaTest= 64, + BQF_Desaturated = 128, +}; + +// Shaders for a buffered quad +enum +{ + BQS_PalTex, + BQS_Plain, + BQS_RedToAlpha, + BQS_ColorOnly, + BQS_SpecialColormap, + BQS_InGameColormap, +}; + #if 0 #define STARTLOG do { if (!dbg) dbg = fopen ("k:/vid.log", "w"); } while(0) #define STOPLOG do { if (dbg) { fclose (dbg); dbg=NULL; } } while(0) diff --git a/src/xlat/parse_xlat.cpp b/src/xlat/parse_xlat.cpp index 75f2d3d27..6221c5497 100644 --- a/src/xlat/parse_xlat.cpp +++ b/src/xlat/parse_xlat.cpp @@ -61,6 +61,7 @@ FBoomTranslator Boomish[MAX_BOOMISH]; int NumBoomish; TAutoGrowArray SectorTranslations; TArray SectorMasks; +FLineFlagTrans LineFlagTranslations[16]; struct SpecialArgs @@ -112,13 +113,13 @@ struct XlatParseContext : public FParseContext static const char *tokens[] = { "arg2", "arg3", "arg4", "arg5", "bitmask", "clear", - "define", "enum", "flags", "include", "lineid", + "define", "enum", "flags", "include", "lineflag", "lineid", "maxlinespecial", "nobitmask", "sector", "tag" }; static const short types[] = { XLAT_ARG2, XLAT_ARG3, XLAT_ARG4, XLAT_ARG5, XLAT_BITMASK, XLAT_CLEAR, - XLAT_DEFINE, XLAT_ENUM, XLAT_FLAGS, XLAT_INCLUDE, XLAT_TAG, + XLAT_DEFINE, XLAT_ENUM, XLAT_FLAGS, XLAT_INCLUDE, XLAT_LINEFLAG, XLAT_TAG, XLAT_MAXLINESPECIAL, XLAT_NOBITMASK, XLAT_SECTOR, XLAT_TAG }; diff --git a/src/xlat/xlat.h b/src/xlat/xlat.h index 3ba4381e6..018a2639e 100644 --- a/src/xlat/xlat.h +++ b/src/xlat/xlat.h @@ -80,10 +80,18 @@ struct FSectorMask int shift; }; +struct FLineFlagTrans +{ + int newvalue; + bool ismask; +}; + + extern TAutoGrowArray SimpleLineTranslations; extern FBoomTranslator Boomish[MAX_BOOMISH]; extern int NumBoomish; extern TAutoGrowArray SectorTranslations; extern TArray SectorMasks; +extern FLineFlagTrans LineFlagTranslations[16]; #endif diff --git a/src/xlat/xlat_parser.y b/src/xlat/xlat_parser.y index 420989338..d6f71b6a3 100644 --- a/src/xlat/xlat_parser.y +++ b/src/xlat/xlat_parser.y @@ -17,6 +17,7 @@ external_declaration ::= enum_statement. external_declaration ::= linetype_declaration. external_declaration ::= boom_declaration. external_declaration ::= sector_declaration. +external_declaration ::= lineflag_declaration. external_declaration ::= sector_bitmask. external_declaration ::= maxlinespecial_def. external_declaration ::= NOP. @@ -512,3 +513,22 @@ sector_bitmask ::= SECTOR BITMASK exp(mask) CLEAR SEMICOLON. sector_op(A) ::= LSHASSIGN. { A = 1; } sector_op(A) ::= RSHASSIGN. { A = -1; } +%type lineflag_op {int} + +lineflag_declaration ::= LINEFLAG exp(from) EQUALS exp(to) SEMICOLON. +{ + if (from >= 0 && from < 16) + { + LineFlagTranslations[from].newvalue = to; + LineFlagTranslations[from].ismask = false; + } +} + +lineflag_declaration ::= LINEFLAG exp(from) AND exp(mask) SEMICOLON. +{ + if (from >= 0 && from < 16) + { + LineFlagTranslations[from].newvalue = mask; + LineFlagTranslations[from].ismask = true; + } +} \ No newline at end of file diff --git a/src/xs_Float.h b/src/xs_Float.h new file mode 100644 index 000000000..19300b2fc --- /dev/null +++ b/src/xs_Float.h @@ -0,0 +1,224 @@ +// ==================================================================================================================== +// ==================================================================================================================== +// xs_Float.h +// +// Source: "Know Your FPU: Fixing Floating Fast" +// http://www.stereopsis.com/sree/fpu2006.html +// +// xs_CRoundToInt: Round toward nearest, but ties round toward even (just like FISTP) +// xs_ToInt: Round toward zero, just like the C (int) cast +// xs_FloorToInt: Round down +// xs_CeilToInt: Round up +// xs_RoundToInt: Round toward nearest, but ties round up +// ==================================================================================================================== +// ==================================================================================================================== +#ifndef _xs_FLOAT_H_ +#define _xs_FLOAT_H_ + +// ==================================================================================================================== +// Defines +// ==================================================================================================================== +#ifndef _xs_DEFAULT_CONVERSION +#define _xs_DEFAULT_CONVERSION 0 +#endif //_xs_DEFAULT_CONVERSION + + +#if __BIG_ENDIAN__ + #define _xs_iexp_ 0 + #define _xs_iman_ 1 +#else + #define _xs_iexp_ 1 //intel is little endian + #define _xs_iman_ 0 +#endif //BigEndian_ + +#ifdef __GNUC__ +#define finline inline +#else +#define finline __forceinline +#endif + +union _xs_doubleints +{ + real64 val; + uint32 ival[2]; +}; + +#if 0 +#define _xs_doublecopysgn(a,b) ((int32*)&a)[_xs_iexp_]&=~(((int32*)&b)[_xs_iexp_]&0x80000000) +#define _xs_doubleisnegative(a) ((((int32*)&a)[_xs_iexp_])|0x80000000) +#endif + +// ==================================================================================================================== +// Constants +// ==================================================================================================================== +const real64 _xs_doublemagic = real64 (6755399441055744.0); //2^52 * 1.5, uses limited precisicion to floor +const real64 _xs_doublemagicdelta = (1.5e-8); //almost .5f = .5f + 1e^(number of exp bit) +const real64 _xs_doublemagicroundeps = (.5f-_xs_doublemagicdelta); //almost .5f = .5f - 1e^(number of exp bit) + + +// ==================================================================================================================== +// Prototypes +// ==================================================================================================================== +static int32 xs_CRoundToInt (real64 val, real64 dmr = _xs_doublemagic); +static int32 xs_ToInt (real64 val, real64 dme = -_xs_doublemagicroundeps); +static int32 xs_FloorToInt (real64 val, real64 dme = _xs_doublemagicroundeps); +static int32 xs_CeilToInt (real64 val, real64 dme = _xs_doublemagicroundeps); +static int32 xs_RoundToInt (real64 val); + +//int32 versions +finline static int32 xs_CRoundToInt (int32 val) {return val;} +finline static int32 xs_ToInt (int32 val) {return val;} + + + +// ==================================================================================================================== +// Fix Class +// ==================================================================================================================== +template class xs_Fix +{ +public: + typedef int32 Fix; + + // ==================================================================================================================== + // Basic Conversion from Numbers + // ==================================================================================================================== + finline static Fix ToFix (int32 val) {return val<>N;} + + + +protected: + // ==================================================================================================================== + // Helper function - mainly to preserve _xs_DEFAULT_CONVERSION + // ==================================================================================================================== + finline static int32 xs_ConvertToFixed (real64 val) + { + #if _xs_DEFAULT_CONVERSION==0 + return xs_CRoundToInt(val, _xs_doublemagic/(1<= 1400 + // VC++ 2005's standard cast is a little bit faster than this + // magic number code. (Which is pretty amazing!) SSE has the + // fastest C-style float->int conversion, but unfortunately, + // checking for SSE support every time you need to do a + // conversion completely negates its performance advantage. + return int32(val); +#else +#if _xs_DEFAULT_CONVERSION==0 + return (val<0) ? xs_CRoundToInt(val-dme) : + xs_CRoundToInt(val+dme); +#else + return int32(val); +#endif +#endif +} + + +// ==================================================================================================================== +finline static int32 xs_FloorToInt(real64 val, real64 dme) +{ +#if _xs_DEFAULT_CONVERSION==0 + return xs_CRoundToInt (val - dme); +#else + return floor(val); +#endif +} + + +// ==================================================================================================================== +finline static int32 xs_CeilToInt(real64 val, real64 dme) +{ +#if _xs_DEFAULT_CONVERSION==0 + return xs_CRoundToInt (val + dme); +#else + return ceil(val); +#endif +} + + +// ==================================================================================================================== +finline static int32 xs_RoundToInt(real64 val) +{ +#if _xs_DEFAULT_CONVERSION==0 + // Yes, it is important that two fadds be generated, so you cannot override the dmr + // passed to xs_CRoundToInt with _xs_doublemagic + _xs_doublemagicdelta. If you do, + // you'll end up with Banker's Rounding again. + return xs_CRoundToInt (val + _xs_doublemagicdelta); +#else + return floor(val+.5); +#endif +} + + + +// ==================================================================================================================== +// ==================================================================================================================== +// Unsigned variants +// ==================================================================================================================== +// ==================================================================================================================== +finline static uint32 xs_CRoundToUInt(real64 val) +{ + return (uint32)xs_CRoundToInt(val); +} + +finline static uint32 xs_FloorToUInt(real64 val) +{ + return (uint32)xs_FloorToInt(val); +} + +finline static uint32 xs_CeilToUInt(real64 val) +{ + return (uint32)xs_CeilToUInt(val); +} + +finline static uint32 xs_RoundToUInt(real64 val) +{ + return (uint32)xs_RoundToInt(val); +} + + + +// ==================================================================================================================== +// ==================================================================================================================== +#endif // _xs_FLOAT_H_ \ No newline at end of file diff --git a/tools/updaterevision/updaterevision.c b/tools/updaterevision/updaterevision.c index 3f0dce4e9..55e81bc40 100644 --- a/tools/updaterevision/updaterevision.c +++ b/tools/updaterevision/updaterevision.c @@ -30,15 +30,21 @@ int main(int argc, char **argv) // Use svnversion to get the revision number. If that fails, pretend it's // revision 0. Note that this requires you have the command-line svn tools installed. sprintf (run, "svnversion -cn %s", argv[1]); - if ((name = tempnam(NULL, "svnout")) != NULL && - (stream = freopen(name, "w+b", stdout)) != NULL && - system(run) == 0 && - errno == 0 && - fseek(stream, 0, SEEK_SET) == 0 && - fgets(currev, sizeof currev, stream) == currev && - (isdigit(currev[0]) || (currev[0] == '-' && currev[1] == '1'))) + if ((name = tempnam(NULL, "svnout")) != NULL) { - gotrev = 1; +#ifdef __APPLE__ + // tempnam will return errno of 2 even though it is successful for our purposes. + errno = 0; +#endif + if((stream = freopen(name, "w+b", stdout)) != NULL && + system(run) == 0 && + errno == 0 && + fseek(stream, 0, SEEK_SET) == 0 && + fgets(currev, sizeof currev, stream) == currev && + (isdigit(currev[0]) || (currev[0] == '-' && currev[1] == '1'))) + { + gotrev = 1; + } } if (stream != NULL) { diff --git a/tools/zipdir/zipdir.c b/tools/zipdir/zipdir.c index 584cf98ae..01a997958 100644 --- a/tools/zipdir/zipdir.c +++ b/tools/zipdir/zipdir.c @@ -36,8 +36,10 @@ #define stat _stat #else #include +#if !defined(__sun) #include #endif +#endif #include #include #include @@ -131,7 +133,10 @@ typedef unsigned int DWORD; typedef unsigned short WORD; typedef unsigned char BYTE; -#pragma pack(push,1) +// [BL] Solaris (well GCC on Solaris) doesn't seem to support pack(push/pop, 1) so we'll need to use use it +// on the whole file. +#pragma pack(1) +//#pragma pack(push,1) typedef struct { DWORD Magic; // 0 @@ -179,7 +184,7 @@ typedef struct DWORD DirectoryOffset; WORD ZipCommentLength; } EndOfCentralDirectory; -#pragma pack(pop) +//#pragma pack(pop) // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -484,6 +489,97 @@ dir_tree_t *add_dirs(char **argv) return trees; } +#elif defined(__sun) + +//========================================================================== +// +// add_dirs +// Solaris version +// +// Given NULL-terminated array of directory paths, create trees for them. +// +//========================================================================== + +void add_dir(dir_tree_t *tree, char* dirpath) +{ + DIR *directory = opendir(dirpath); + if(directory == NULL) + return; + + struct dirent *file; + while((file = readdir(directory)) != NULL) + { + if(file->d_name[0] == '.') //File is hidden or ./.. directory so ignore it. + continue; + + int isDirectory = 0; + int time = 0; + + char* fullFileName = malloc(strlen(dirpath) + strlen(file->d_name) + 1); + strcpy(fullFileName, dirpath); + strcat(fullFileName, file->d_name); + + struct stat *fileStat; + fileStat = malloc(sizeof(struct stat)); + stat(fullFileName, fileStat); + isDirectory = S_ISDIR(fileStat->st_mode); + time = fileStat->st_mtime; + free(stat); + + free(fullFileName); + + if(isDirectory) + { + char* newdir; + newdir = malloc(strlen(dirpath) + strlen(file->d_name) + 2); + strcpy(newdir, dirpath); + strcat(newdir, file->d_name); + strcat(newdir, "/"); + add_dir(tree, newdir); + free(newdir); + continue; + } + + file_entry_t *entry; + entry = alloc_file_entry(dirpath, file->d_name, time); + if (entry == NULL) + { + //no_mem = 1; + break; + } + entry->next = tree->files; + tree->files = entry; + } + + closedir(directory); +} + +dir_tree_t *add_dirs(char **argv) +{ + dir_tree_t *tree, *trees = NULL; + + int i = 0; + while(argv[i] != NULL) + { + tree = alloc_dir_tree(argv[i]); + tree->next = trees; + trees = tree; + + if(tree != NULL) + { + char* dirpath = malloc(sizeof(argv[i]) + 2); + strcpy(dirpath, argv[i]); + if(dirpath[strlen(dirpath)] != '/') + strcat(dirpath, "/"); + add_dir(tree, dirpath); + free(dirpath); + } + + i++; + } + return trees; +} + #else //========================================================================== @@ -931,9 +1027,13 @@ int append_to_zip(FILE *zip_file, file_sorted_t *filep, FILE *ozip, BYTE *odir) } fprintf(stderr, "Unable to write %s to zip\n", file->path); free(readbuf); - if (compbuf != NULL) + if (compbuf[0] != NULL) { - free(compbuf); + free(compbuf[0]); + } + if (compbuf[1] != NULL) + { + free(compbuf[1]); } return 1; } diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 568ecf262..5e0c3c992 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -17,7 +17,10 @@ ACTOR Actor native //: Thinker BounceCount -1 FloatSpeed 4 Gravity 1 + DamageFactor 1.0 PushFactor 0.25 + WeaveIndexXY 0 + WeaveIndexZ 16 // Variables for the expression evaluator // NOTE: fixed_t and angle_t are only used here to ensure proper conversion @@ -45,6 +48,7 @@ ACTOR Actor native //: Thinker native fixed_t momy; // alias for vely native fixed_t momz; // alias for velz native int score; + // Meh, MBF redundant functions. Only for DeHackEd support. action native A_Turn(float angle = 0); action native A_LineEffect(int boomspecial = 0, int tag = 0); @@ -177,7 +181,7 @@ ACTOR Actor native //: Thinker action native A_StopSound(int slot = CHAN_VOICE); // Bad default but that's what is originally was... action native A_PlaySoundEx(sound whattoplay, coerce name slot, bool looping = false, int attenuation = 0); action native A_StopSoundEx(coerce name slot); - action native A_SeekerMissile(int threshold, int turnmax); + action native A_SeekerMissile(int threshold, int turnmax, int flags = 0, int chance = 50, int distance = 10); action native A_Jump(int chance = 256, state label, ...); action native A_CustomMissile(class missiletype, float spawnheight = 32, int spawnofs_xy = 0, float angle = 0, int flags = 0, float pitch = 0); action native A_CustomBulletAttack(float spread_xy, float spread_z, int numbullets, int damageperbullet, class pufftype = "BulletPuff", float range = 0, bool aimfacing = false); @@ -193,6 +197,7 @@ ACTOR Actor native //: Thinker action native A_Print(string whattoprint, float time = 0, name fontname = ""); action native A_PrintBold(string whattoprint, float time = 0, name fontname = ""); action native A_Log(string whattoprint); + action native A_LogInt(int whattoprint); action native A_SetTranslucent(float alpha, int style = 0); action native A_FadeIn(float reduce = 0.1); action native A_FadeOut(float reduce = 0.1, bool remove = true); @@ -206,9 +211,9 @@ ACTOR Actor native //: Thinker action native A_RemoveMaster(); action native A_RemoveChildren(bool removeall = false); action native A_RemoveSiblings(bool removeall = false); - action native A_KillMaster(); - action native A_KillChildren(); - action native A_KillSiblings(); + action native A_KillMaster(name damagetype = "none"); + action native A_KillChildren(name damagetype = "none"); + action native A_KillSiblings(name damagetype = "none"); action native A_RaiseMaster(); action native A_RaiseChildren(); action native A_RaiseSiblings(); @@ -217,15 +222,17 @@ ACTOR Actor native //: Thinker action native A_PlayerSkinCheck(state label); action native A_BasicAttack(int meleedamage, sound meleesound, class missiletype, float missileheight); action native A_ThrowGrenade(class itemtype, float zheight = 0, float xyvel = 0, float zvel = 0, bool useammo = true); + action native A_Weave(int xspeed, int yspeed, float xdist, float ydist); action native A_Recoil(float xyvel); action native A_JumpIfInTargetInventory(class itemtype, int amount, state label); action native A_GiveToTarget(class itemtype, int amount = 0); action native A_TakeFromTarget(class itemtype, int amount = 0); - action native A_CountdownArg(int argnum); + action native A_CountdownArg(int argnum, state targstate = ""); action native A_CustomMeleeAttack(int damage = 0, sound meleesound = "", sound misssound = "", name damagetype = "none", bool bleed = true); action native A_CustomComboAttack(class missiletype, float spawnheight, int damage, sound meleesound = "", name damagetype = "none", bool bleed = true); action native A_Burst(class chunktype); + action native A_Blast(int flags = 0, float strength = 255, float radius = 255, float speed = 20, class blasteffect = "BlastEffect", sound blastsound = "BlastRadius"); action native A_RadiusThrust(int force = 128, int distance = -1, bool affectsource = true); action native A_Explode(int damage = -1, int distance = -1, bool hurtsource = true, bool alert = false, int fulldamagedistance = 0, int nails = 0, int naildamage = 10); action native A_Stop(); @@ -258,8 +265,10 @@ ACTOR Actor native //: Thinker action native A_ScaleVelocity(float scale); action native A_ChangeVelocity(float x = 0, float y = 0, float z = 0, int flags = 0); action native A_SetArg(int pos, int value); - action native A_SetUserVar(int pos, int value); + action native A_SetUserVar(name varname, int value); + action native A_SetUserArray(name varname, int index, int value); action native A_SetSpecial(int spec, int arg0 = 0, int arg1 = 0, int arg2 = 0, int arg3 = 0, int arg4 = 0); + action native A_Quake(int intensity, int duration, int damrad, int tremrad, sound sfx = "world/quake"); States { diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 151a47686..8c60430ed 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -70,6 +70,15 @@ const int RGF_NOPIERCING = 2; // Flags for A_Mushroom const int MSF_Standard = 0; const int MSF_Classic = 1; +const int MSF_DontHurt = 2; + +// Flags for A_Blast +const int BF_USEAMMO = 1; +const int BF_DONTWARN = 2; +const int BF_AFFECTBOSSES = 4; + +// Flags for A_SeekerMissile +const int SMF_LOOK = 1; // Activation flags enum @@ -81,7 +90,19 @@ enum THINGSPEC_MonsterTrigger = 8, THINGSPEC_MissileTrigger = 16, THINGSPEC_ClearSpecial = 32, + THINGSPEC_NoDeathSpecial = 64, + THINGSPEC_TriggerActs = 128, }; +// Shorter aliases for same +const int AF_Default = 0; +const int AF_ThingActs = 1; +const int AF_ThingTargets = 2; +const int AF_TriggerTargets = 4; +const int AF_MonsterTrigger = 8; +const int AF_MissileTrigger = 16; +const int AF_ClearSpecial = 32; +const int AF_NoDeathSpecial = 64; +const int AF_TriggerActs = 128; // constants for A_PlaySound enum diff --git a/wadsrc/static/actors/heretic/beast.txt b/wadsrc/static/actors/heretic/beast.txt index c89e6a02c..7f54c3442 100644 --- a/wadsrc/static/actors/heretic/beast.txt +++ b/wadsrc/static/actors/heretic/beast.txt @@ -28,7 +28,6 @@ ACTOR Beast 70 See: BEAS ABCDEF 3 A_Chase Loop - Melee: Missile: BEAS H 10 A_FaceTarget BEAS I 10 A_CustomComboAttack("BeastBall", 32, random[BeastAttack](1,8)*3, "beast/attack") diff --git a/wadsrc/static/actors/heretic/hereticimp.txt b/wadsrc/static/actors/heretic/hereticimp.txt index 1d663b662..25f64a2ea 100644 --- a/wadsrc/static/actors/heretic/hereticimp.txt +++ b/wadsrc/static/actors/heretic/hereticimp.txt @@ -25,6 +25,7 @@ ACTOR HereticImp 66 Obituary "$OB_HERETICIMP" HitObituary "$OB_HERETICIMPHIT" + action native A_ImpMsAttack(); action native A_ImpDeath(); action native A_ImpXDeath1(); action native A_ImpExplode(); @@ -44,7 +45,7 @@ ACTOR HereticImp 66 Goto See Missile: IMPX A 10 A_FaceTarget - IMPX B 6 A_SkullAttack(12) + IMPX B 6 A_ImpMsAttack IMPX CBAB 6 Goto Missile+2 Pain: diff --git a/wadsrc/static/actors/hexen/blastradius.txt b/wadsrc/static/actors/hexen/blastradius.txt index 5c5e05613..7e4259cf8 100644 --- a/wadsrc/static/actors/hexen/blastradius.txt +++ b/wadsrc/static/actors/hexen/blastradius.txt @@ -1,5 +1,5 @@ -ACTOR ArtiBlastRadius : Inventory 10110 native +ACTOR ArtiBlastRadius : CustomInventory 10110 { Game Hexen SpawnID 74 @@ -15,6 +15,8 @@ ACTOR ArtiBlastRadius : Inventory 10110 native Spawn: BLST ABCDEFGH 4 Bright Loop + Use: + TNT1 A 0 A_Blast } } diff --git a/wadsrc/static/actors/hexen/magestaff.txt b/wadsrc/static/actors/hexen/magestaff.txt index 4b1887f55..e402e57c0 100644 --- a/wadsrc/static/actors/hexen/magestaff.txt +++ b/wadsrc/static/actors/hexen/magestaff.txt @@ -125,6 +125,7 @@ ACTOR MageStaffFX2 native DamageType "Fire" Projectile +SEEKERMISSILE + +SCREENSEEKER +EXTREMEDEATH DeathSound "MageStaffExplode" diff --git a/wadsrc/static/actors/hexen/magewand.txt b/wadsrc/static/actors/hexen/magewand.txt index c3608b5d2..ebb8e2ca1 100644 --- a/wadsrc/static/actors/hexen/magewand.txt +++ b/wadsrc/static/actors/hexen/magewand.txt @@ -46,7 +46,7 @@ ACTOR MageWandSmoke // Wand Missile ------------------------------------------------------------- -ACTOR MageWandMissile : FastProjectile native +ACTOR MageWandMissile : FastProjectile { Speed 184 Radius 12 @@ -54,6 +54,7 @@ ACTOR MageWandMissile : FastProjectile native Damage 2 +RIPPER +CANNOTPUSH +NODAMAGETHRUST +SPAWNSOUNDSOURCE + MissileType "MageWandSmoke" SeeSound "MageWandFire" States { diff --git a/wadsrc/static/actors/raven/artiegg.txt b/wadsrc/static/actors/raven/artiegg.txt index 18a2d83f5..6b8de50c9 100644 --- a/wadsrc/static/actors/raven/artiegg.txt +++ b/wadsrc/static/actors/raven/artiegg.txt @@ -94,7 +94,7 @@ ACTOR ArtiPork : CustomInventory 30 States { Spawn: - PORK ABCB 6 + PORK ABCDEFGH 6 Loop Use: TNT1 A 0 A_FireCustomMissile("PorkFX", -15, 0, 0, 0, 1) diff --git a/wadsrc/static/actors/shared/blood.txt b/wadsrc/static/actors/shared/blood.txt index 5a7ae201d..1a294c6af 100644 --- a/wadsrc/static/actors/shared/blood.txt +++ b/wadsrc/static/actors/shared/blood.txt @@ -7,6 +7,7 @@ ACTOR Blood Mass 5 +NOBLOCKMAP +NOTELEPORT + +ALLOWPARTICLES States { Spawn: @@ -30,6 +31,7 @@ ACTOR BloodSplatter +DROPOFF +NOTELEPORT +CANNOTPUSH + +ALLOWPARTICLES Mass 5 States { @@ -53,6 +55,7 @@ ACTOR AxeBlood +DROPOFF +NOTELEPORT +CANNOTPUSH + +ALLOWPARTICLES Mass 5 States { diff --git a/wadsrc/static/actors/doom/dog.txt b/wadsrc/static/actors/shared/dog.txt similarity index 56% rename from wadsrc/static/actors/doom/dog.txt rename to wadsrc/static/actors/shared/dog.txt index 0feb393f5..439060d41 100644 --- a/wadsrc/static/actors/doom/dog.txt +++ b/wadsrc/static/actors/shared/dog.txt @@ -1,20 +1,19 @@ ACTOR MBFHelperDog 888 { - Game Doom Health 500 - Speed 10 + Speed 10 PainChance 180 - Radius 12 - Height 28 - Mass 100 - Monster - +JUMPDOWN - ActiveSound "dog/active" - AttackSound "dog/attack" - DeathSound "dog/death" - PainSound "dog/pain" - SeeSound "dog/sight" - Obituary "$OB_DOG" + Radius 12 + Height 28 + Mass 100 + Monster + +JUMPDOWN + ActiveSound "dog/active" + AttackSound "dog/attack" + DeathSound "dog/death" + PainSound "dog/pain" + SeeSound "dog/sight" + Obituary "$OB_DOG" States { Spawn: diff --git a/wadsrc/static/actors/shared/inventory.txt b/wadsrc/static/actors/shared/inventory.txt index 359fd51fe..6aa43a55f 100644 --- a/wadsrc/static/actors/shared/inventory.txt +++ b/wadsrc/static/actors/shared/inventory.txt @@ -36,10 +36,10 @@ ACTOR Inventory native action native A_BFGsound(); action native A_FireBFG(); action native A_FireOldBFG(); - action native A_ReFire(); + action native A_ReFire(state flash = ""); action native A_ClearReFire(); action native A_CheckReload(); - action native A_GunFlash(); + action native A_GunFlash(state flash = ""); action native A_Saw(sound fullsound = "weapons/sawfull", sound hitsound = "weapons/sawhit", int damage = 2, class pufftype = "BulletPuff"); action native A_CheckForReload(int counter, state label, bool dontincrement = false); action native A_ResetReloadCounter(); diff --git a/wadsrc/static/actors/shared/skies.txt b/wadsrc/static/actors/shared/skies.txt index a4dce7db4..3b531d527 100644 --- a/wadsrc/static/actors/shared/skies.txt +++ b/wadsrc/static/actors/shared/skies.txt @@ -14,16 +14,19 @@ ACTOR SkyPicker 9081 native +DONTSPLASH } +Actor SkyCamCompat : SkyViewpoint 9083 native +{ +} ACTOR StackPoint : SkyViewpoint native { } -ACTOR UpperStackLookOnly : StackPoint 9077 native +ACTOR UpperStackLookOnly : StackPoint 9077 { } -ACTOR LowerStackLookOnly : StackPoint 9078 native +ACTOR LowerStackLookOnly : StackPoint 9078 { } diff --git a/wadsrc/static/actors/strife/alienspectres.txt b/wadsrc/static/actors/strife/alienspectres.txt index f0da99f28..bdd30d8e4 100644 --- a/wadsrc/static/actors/strife/alienspectres.txt +++ b/wadsrc/static/actors/strife/alienspectres.txt @@ -109,6 +109,7 @@ ACTOR AlienSpectre3 : AlienSpectre1 76 Radius 24 +SPAWNCEILING DropItem "Sigil3" + DamageFactor "SpectralLow", 0 states { Spawn: diff --git a/wadsrc/static/actors/strife/macil.txt b/wadsrc/static/actors/strife/macil.txt index fbd52ecad..ea701dd9f 100644 --- a/wadsrc/static/actors/strife/macil.txt +++ b/wadsrc/static/actors/strife/macil.txt @@ -1,7 +1,7 @@ // Macil (version 1) --------------------------------------------------------- -ACTOR Macil1 64 native +ACTOR Macil1 64 { Game Strife ConversationID 49, 48, 49 @@ -66,6 +66,7 @@ ACTOR Macil2 : Macil1 200 -NODAMAGE DeathSound "macil/slop" DropItem "None" + DamageFactor "SpectralLow", 0 States { Missile: diff --git a/wadsrc/static/actors/strife/oracle.txt b/wadsrc/static/actors/strife/oracle.txt index 6c0639a96..ca25f2526 100644 --- a/wadsrc/static/actors/strife/oracle.txt +++ b/wadsrc/static/actors/strife/oracle.txt @@ -1,7 +1,7 @@ // Oracle ------------------------------------------------------------------- -ACTOR Oracle 199 native +ACTOR Oracle 199 { Game Strife ConversationID 65, 62, 63 @@ -10,7 +10,9 @@ ACTOR Oracle 199 native Height 56 Monster +NOTDMATCH + +NOBLOOD DamageFactor "Fire", 0.5 + DamageFactor "SpectralLow", 0 MaxDropoffHeight 32 Tag "Oracle" DropItem "Meat" diff --git a/wadsrc/static/actors/strife/spectral.txt b/wadsrc/static/actors/strife/spectral.txt index e3048baa9..fecd77e1f 100644 --- a/wadsrc/static/actors/strife/spectral.txt +++ b/wadsrc/static/actors/strife/spectral.txt @@ -198,6 +198,7 @@ ACTOR SpectralLightningV1 : SpectralLightningDeathShort Height 24 Damage 100 Projectile + DamageType "SpectralLow" +SPECTRAL States { diff --git a/wadsrc/static/actors/strife/strifeplayer.txt b/wadsrc/static/actors/strife/strifeplayer.txt index 83d8110dd..d42dc1ba4 100644 --- a/wadsrc/static/actors/strife/strifeplayer.txt +++ b/wadsrc/static/actors/strife/strifeplayer.txt @@ -12,7 +12,7 @@ ACTOR StrifePlayer : PlayerPawn Player.ColorRange 128, 143 Player.DisplayName "Rebel" Player.StartItem "PunchDagger" - Player.RunHealth 16 + Player.RunHealth 15 Player.WeaponSlot 1, PunchDagger Player.WeaponSlot 2, StrifeCrossbow2, StrifeCrossbow Player.WeaponSlot 3, AssaultGun diff --git a/wadsrc/static/actors/strife/strifeweapons.txt b/wadsrc/static/actors/strife/strifeweapons.txt index a6de22968..d287ace08 100644 --- a/wadsrc/static/actors/strife/strifeweapons.txt +++ b/wadsrc/static/actors/strife/strifeweapons.txt @@ -708,6 +708,7 @@ ACTOR PhosphorousFire native +FLOORCLIP +NOTELEPORT +NODAMAGETHRUST + +DONTSPLASH RenderStyle Add action native A_Burnarea (); diff --git a/wadsrc/static/animdefs.txt b/wadsrc/static/animdefs.txt index 2c492ddb0..388a4f360 100644 --- a/wadsrc/static/animdefs.txt +++ b/wadsrc/static/animdefs.txt @@ -156,8 +156,8 @@ switch strife WALTEK15 on sound world/glassbreak pic WALTEKB1 tics 0 switch strife SWFORC01 on pic SWFORC02 tics 0 switch strife SWEXIT01 on pic SWEXIT02 tics 0 switch strife DORSBK01 on sound switches/stone pic DORSBK02 tics 0 -switch strife SWSLD01 on sound switches/keycard pic SWSLD02 tics 0 -switch strife DORWS04 on sound switches/bolt pic DORWS05 tics 0 +switch strife SWSLD01 on pic SWSLD02 tics 0 +switch strife DORWS04 on sound switches/bolt pic DORWS05 tics 0 off sound switches/boltback pic DORWS04 tics 0 switch strife SWIRON01 on pic SWIRON02 tics 0 switch strife GLASS09 on sound world/glassbreak pic GLASS10 tics 0 switch strife GLASS11 on sound world/glassbreak pic GLASS12 tics 0 @@ -169,7 +169,7 @@ switch strife SWTRMG01 on pic SWTRMG04 tics 0 switch strife SWMETL01 on pic SWMETL02 tics 0 switch strife SWWOOD01 on pic SWWOOD02 tics 0 switch strife SWTKBL01 on pic SWTKBL02 tics 0 -switch strife AZWAL21 on pic AZWAL22 tics 0 +switch strife AZWAL21 on sound world/glassbreak pic AZWAL22 tics 0 switch strife SWINDT01 on pic SWINDT02 tics 0 switch strife SWRUST01 on pic SWRUST02 tics 0 switch strife SWCHAP01 on pic SWCHAP02 tics 0 @@ -183,15 +183,15 @@ switch strife SWBRIK01 on pic SWBRIK02 tics 0 switch strife SWIRON03 on pic SWIRON04 tics 0 switch strife SWIRON05 on pic SWIRON06 tics 0 switch strife SWIRON07 on pic SWIRON08 tics 0 -switch strife SWCARD01 on pic SWCARD02 tics 0 +switch strife SWCARD01 on sound switches/keycard pic SWCARD02 tics 0 switch strife SWSIGN01 on pic SWSIGN02 tics 0 switch strife SWLEV01 on pic SWLEV02 tics 0 switch strife SWLEV03 on pic SWLEV04 tics 0 switch strife SWLEV05 on pic SWLEV06 tics 0 -switch strife SWBRN01 on pic SWBRN02 tics 0 +switch strife SWBRN01 on sound switches/keycard pic SWBRN02 tics 0 switch strife SWPIP01 on sound switches/valve pic SWPIP02 tics 0 switch strife SWPALM01 on sound switches/scanner pic SWPALM02 tics 0 -switch strife SWKNOB03 on pic SWKNOB04 tics 0 +switch strife SWKNOB03 on sound switches/knob pic SWKNOB04 tics 0 switch strife ALTSW01 on pic ALTSW02 tics 0 switch strife COMP25 on sound world/glassbreak pic COMP28B tics 0 switch strife COMP29 on sound world/glassbreak pic COMP20B tics 0 diff --git a/wadsrc/static/compatibility.txt b/wadsrc/static/compatibility.txt index dbaadf6fc..685fda086 100644 --- a/wadsrc/static/compatibility.txt +++ b/wadsrc/static/compatibility.txt @@ -24,6 +24,11 @@ A80E7EE40E0D0C76A6FBD242BE29FE27 // map15 resetplayerspeed } +6DA6FCBA8089161BDEC6A1D3F6C8D60F // Eternal Doom MAP25 +{ + stairs +} + 10E1E2B36302D31AC4AE68C84B5DC457 // Eternal Doom MAP28 { // What's really sad is that I got a separate bug report for this map @@ -86,7 +91,40 @@ F84AB4557464A383E93F37CD3A82AC48 // MM2 map03 2EEB1E12FA9F9545DE9D99990A4A78E5 // Icarus map24 65A53A09A09525AE42EA210BF879CD37 // Plutonia 2 map32 2499CF9A9351BE9BC4E9C66FC9F291A7 // Requiem map23 +3CA5493FEFF2E27BFD4181E6C4A3C2BF // The Waterfront map01 { corpsegibs vileghosts } + +// invert the sorting order of overlapping sprites at the same spot +551D6B416EB3324790BC0F0F74B49600 // Strain map13 +2F49C29691F8565F0B99B27FCF2627E6 // Astrostein 1 MAP01 +55A741F9C2955C2B06F2387E45ED6C62 // MAP02 +4E7286B735671A942C54FAC6CB52A8C3 // MAP03 +825772094FF3569FC3722145F82F820A // MAP04 +CB6CF6BE0EA4A6AB9830FBB7F9192B80 // MAP05 +AB24CCF84C2FE1A543FE033589BD1FBC // MAP06 +6BE0908B4DAFF53AA0A7493C04A608A4 // MAP07 +305275E5E07755E17AAB064981279295 // MAP08 +A8FBF3600088E79D02283C40D12B7F26 // MAP09 +A8FBF3600088E79D02283C40D12B7F26 // MAP09 +F0C95C76237DF617560577767EC21E1C // MAP10 +2A2AF2CDAB7E7A66D28AFC3AA6243DCC // Astrostein 2 map01 +0667DA831EB293D3387579565C11F0DD // map02 +76DA72420EBE0A53D861373D7123DE33 // map03 +5A4F8186580FFE41BCD80960B7F19CA8 // map04 +E3A1EE2A0A2FB27496074057E3FA82F0 // map05 +1CA60DE4062F41DC1A39396228913882 // map06 +DABC3033A734DEF402A8FEF6C2FEDCC2 // map07 +2E8211EA051EA8C3241859D1854386D6 // map08 +EAD27C506AFC856BE07DDDDED20D7ED0 // map09 +7DA47B044E2D5D17EAE2960BBFAEDE1A // Astrostein 3 map10 +46E0F4529E8E396DEDC8DB83443078A7 // map13 +2742D556921FBE753C16175F0C980091 // map14 +AB1A6C1D3898D4259C0645306939374C // map15 +0599F0D0CC1F41F52B7E8214D0348EEA // map17 +CA267398C9B3A8F79349D3394F8B2106 // map20 +{ + spritesort +} diff --git a/wadsrc/static/decorate.txt b/wadsrc/static/decorate.txt index ba3bf4b7e..465ce3287 100644 --- a/wadsrc/static/decorate.txt +++ b/wadsrc/static/decorate.txt @@ -28,6 +28,7 @@ #include "actors/shared/setcolor.txt" #include "actors/shared/sectoraction.txt" #include "actors/shared/action.txt" +#include "actors/shared/dog.txt" #include "actors/doom/doomplayer.txt" #include "actors/doom/possessed.txt" @@ -45,7 +46,6 @@ #include "actors/doom/spidermaster.txt" #include "actors/doom/keen.txt" #include "actors/doom/bossbrain.txt" -#include "actors/doom/dog.txt" #include "actors/doom/deadthings.txt" #include "actors/doom/doomammo.txt" diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index a6f2ecd8a..4cb444d0f 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -1086,7 +1086,7 @@ TXT_LEATHERARMOR = "You picked up the Leather Armor."; TXT_MEDPATCH = "You picked up the Med patch."; TXT_MEDICALKIT = "You picked up the Medical kit."; TXT_SURGERYKIT = "You picked up the Surgery Kit."; -TXT_STRIFEMAP = "You picked up the map"; +TXT_STRIFEMAP = "You picked up the map."; TXT_BELDINSRING = "You picked up the ring."; TXT_OFFERINGCHALICE = "You picked up the Offering Chalice."; TXT_EAR = "You picked up the ear."; @@ -1099,7 +1099,7 @@ TXT_FTHROWERPARTS = "You picked up the flame thrower parts."; TXT_REPORT = "You picked up the report."; TXT_INFO = "You picked up the info."; TXT_TARGETER = "You picked up the Targeter."; -TXT_COMMUNICATOR = "You picked up the Communicator"; +TXT_COMMUNICATOR = "You picked up the Communicator."; TXT_COIN = "You picked up the coin."; TXT_XGOLD = "You picked up %d gold."; TXT_BEACON = "You picked up the Teleporter Beacon."; @@ -1108,12 +1108,12 @@ TXT_SCANNER = "You picked up the scanner."; TXT_NEEDMAP = "The scanner won't work without a map!\n"; TXT_PRISONPASS = "You picked up the Prison pass."; -TXT_STRIFECROSSBOW = "You picked up the crossbow"; -TXT_ASSAULTGUN = "You picked up the assault gun"; -TXT_MMLAUNCHER = "You picked up the mini missile launcher"; -TXT_FLAMER = "You picked up the flame thrower"; -TXT_MAULER = "You picked up the mauler"; -TXT_GLAUNCHER = "You picked up the Grenade launcher"; +TXT_STRIFECROSSBOW = "You picked up the crossbow."; +TXT_ASSAULTGUN = "You picked up the assault gun."; +TXT_MMLAUNCHER = "You picked up the mini missile launcher."; +TXT_FLAMER = "You picked up the flame thrower."; +TXT_MAULER = "You picked up the mauler."; +TXT_GLAUNCHER = "You picked up the Grenade launcher."; TXT_SIGIL = "You picked up the SIGIL."; @@ -1147,7 +1147,7 @@ TXT_NEWKEY5 = "You picked up the New Key5."; TXT_ORACLEPASS = "You picked up the Oracle Pass."; TXT_HEGRENADES = "You picked up the HE-Grenade Rounds."; -TXT_PHGRENADES = "You picked up the Phoshorus-Grenade Rounds."; +TXT_PHGRENADES = "You picked up the Phosphorus-Grenade Rounds."; TXT_CLIPOFBULLETS = "You picked up the clip of bullets."; TXT_BOXOFBULLETS = "You picked up the box of bullets."; TXT_MINIMISSILES = "You picked up the mini missiles."; @@ -1156,7 +1156,7 @@ TXT_ENERGYPOD = "You picked up the energy pod."; TXT_ENERGYPACK = "You picked up the energy pack."; TXT_POISONBOLTS = "You picked up the poison bolts."; TXT_ELECTRICBOLTS = "You picked up the electric bolts."; -TXT_AMMOSATCHEL = "You picked up the ammo satchel"; +TXT_AMMOSATCHEL = "You picked up the ammo satchel."; // Random dialogs diff --git a/wadsrc/static/language.ita b/wadsrc/static/language.ita index b284a2e41..a46237991 100644 --- a/wadsrc/static/language.ita +++ b/wadsrc/static/language.ita @@ -31,6 +31,25 @@ QUITMSG12 = "senti amico, esci adesso\ne perdi il tuo body count!"; QUITMSG13 = "esci pure. quanto tornerai\nti aspettero' con un bastone."; QUITMSG14 = "sei fortunato che non ti picchi\nper aver pensato di andartene."; +// Quit Strife messages +QUITMSG15 = "dove stai andando?!\ne la ribellione?"; +QUITMSG16 = "macellus interruptus...\nche presa in giro!"; +QUITMSG17 = "ma tu sei la speranza\n-- la mia unica possibilita'!!"; +QUITMSG18 = "nessuno gira le spalle a Blackbird."; +QUITMSG19 = "pensavo tu fossi diverso..."; +QUITMSG20 = "ma bravo! uccidi e scappi!"; +QUITMSG21 = "puoi uscire...\nma non puoi nasconderti..."; +QUITMSG22 = "eehi, che problema c'e'?\nla mamma ha detto che e' ora di cena?"; + +// Quit Chex messages +QUITMSG23 = "Non uscire ora, ci sono ancora\nflemoidi a piede libero!"; +QUITMSG24 = "Non arrenderti -- i flemoidi\nci sopraffaranno!"; +QUITMSG25 = "Non andartene ora.\nCi serve il tuo aiuto!"; +QUITMSG26 = "Spero tu stia solo facendo un\nbreak per prendere un Chex(R) party mix."; +QUITMSG27 = "non uscire ora!\nCi serve il tuo aiuto!"; +QUITMSG28 = "Non abbandonare la\nFederazione Intergalattica dei Cereali!"; +QUITMSG29 = "Il vero guerriero Chex(R)\nnon si arrenderebbe cosi' in fretta!"; + LOADNET = "non puoi caricare una partita durante un netgame!\n\npremi un tasto."; QLOADNET = "non puoi fare un quickload durante un netgame!\n\npremi un tasto."; QSAVESPOT = "non hai ancora scelto uno slot per il quicksave!\n\npremi un tasto."; @@ -91,6 +110,7 @@ PD_REDK = "Ti serve una chiave rossa per aprire questa porta"; PD_YELLOWK = "Ti serve una chiave gialla per aprire questa porta"; GGSAVED = "gioco salvato"; HUSTR_MSGU = "[Messaggio non inviato]"; +PICKUP_PISTOL_DROPPED = "Raccolta una pistola."; // Level names HUSTR_E1M1 = "E1M1: Hangar"; @@ -254,6 +274,8 @@ STSTR_BEHOLD = "inVuln, BerSerk, Invisib, Tuta anti-Rad, MAppa, o Amplif-Luce"; STSTR_BEHOLDX = "Power-up concesso/tolto"; STSTR_CHOPPERS = "Ora vai e segali tutti!"; STSTR_CLEV = "Cambio di livello...\n"; +TXT_BUDDHAON = "Modalita' Buddha attivata"; +TXT_BUDDHAOFF = "Modalita' Buddha disattivata"; E1TEXT = "Una volta che hai eliminato i cattivi e\n" @@ -559,13 +581,15 @@ PD_BLUESO = "ti serve una chiave-teschio blu per attivare questo oggetto "; PD_REDSO = "ti serve una chiave-teschio rossa per attivare questo oggetto "; PD_YELLOWSO = "ti serve una chiave-teschio gialla per attivare questo oggetto "; +PD_ALLKEYS = "Ti servono tutte le chiavi"; + // Gameflow messages TXT_FRAGLIMIT = "Fraglimit raggiunto."; TXT_TIMELIMIT = "Timelimit raggiunto."; // Spree messages -SPREEKILLSELF = "%o stava bene finche' %g non si e' ucciso!"; +SPREEKILLSELF = "%o stava andando bene finche' non si e' ucciso da solo!"; SPREEOVER = "La sequela di frag di %o e' stata terminata da %k"; SPREE5 = "%k e' preda di un momento omicida!"; SPREE10 = "%k sta facendo un massacro!"; @@ -578,6 +602,10 @@ MULTI2 = "Frag doppio!"; MULTI3 = "Frag multiplo!"; MULTI4 = "Frag ultra!"; MULTI5 = "Frag DA PAURA!"; +//MULTI2 = "Double kill!"; +//MULTI3 = "Multi kill!"; +//MULTI4 = "Ultra kill!"; +//MULTI5 = "Monster kill!"; // Obituary strings // First the self-kills, then the other-kills @@ -588,24 +616,24 @@ OB_EXIT = "%o ha provato a uscire."; OB_WATER = "%o non sapeva nuotare."; OB_SLIME = "%o e' mutato."; OB_LAVA = "%o si e' sciolto."; -OB_BARREL = "%o e' esploso."; +OB_BARREL = "%o ha fatto bum."; OB_SPLASH = "%o si e' trovato nel posto sbagliato."; OB_R_SPLASH = "%o avrebbe dovuto allontanarsi."; OB_ROCKET = "%o avrebbe dovuto allontanarsi."; OB_KILLEDSELF = "%o ha ucciso se stesso."; -OB_STEALTHBABY = "%o pensava che %g avesse visto un arachnotron."; -OB_STEALTHVILE = "%o pensava che %g avesse visto un Arch-Vile."; -OB_STEALTHBARON = "%o pensava che %g avesse visto un Barone Infernale."; -OB_STEALTHCACO = "%o pensava che %g avesse visto un cacodemone."; -OB_STEALTHCHAINGUY = "%o pensava che %g avesse visto uno zombie commando."; -OB_STEALTHDEMON = "%o pensava che %g avesse visto un demone."; -OB_STEALTHKNIGHT = "%o pensava che %g avesse visto un Cavaliere Infernale."; -OB_STEALTHIMP = "%o pensava che %g avesse visto un imp."; -OB_STEALTHFATSO = "%o pensava che %g avesse visto un mancubo."; -OB_STEALTHUNDEAD = "%o pensava che %g avesse visto un revenant."; -OB_STEALTHSHOTGUY = "%o pensava che %g avesse visto un sergente."; -OB_STEALTHZOMBIE = "%o pensava che %g avesse visto uno zombie."; +OB_STEALTHBABY = "a %o sembrava di aver visto un arachnotron."; +OB_STEALTHVILE = "a %o sembrava di aver visto un Arch-Vile."; +OB_STEALTHBARON = "a %o sembrava di aver visto un Barone Infernale."; +OB_STEALTHCACO = "a %o sembrava di aver visto un cacodemone."; +OB_STEALTHCHAINGUY = "a %o sembrava di aver visto uno zombie commando."; +OB_STEALTHDEMON = "a %o sembrava di aver visto un demone."; +OB_STEALTHKNIGHT = "a %o sembrava di aver visto un Cavaliere Infernale."; +OB_STEALTHIMP = "a %o sembrava di aver visto un imp."; +OB_STEALTHFATSO = "a %o sembrava di aver visto un mancubo."; +OB_STEALTHUNDEAD = "a %o sembrava di aver visto un revenant."; +OB_STEALTHSHOTGUY = "a %o sembrava di aver visto un sergente."; +OB_STEALTHZOMBIE = "a %o sembrava di aver visto uno zombie."; OB_UNDEADHIT = "%o e' stato massacrato da un revenant."; OB_IMPHIT = "%o e' stato squarciato da un imp."; OB_CACOHIT = "%o si e' avvicinato troppo a un cacodemone."; @@ -629,6 +657,7 @@ OB_BABY = "%o ha permesso che un arachnotron l'uccidesse."; OB_CYBORG = "%o e' stato spiaccicato da un Cyberdemonio."; OB_WOLFSS = "%o ha incontrato un Nazista."; +OB_DOG = "%o e' stato sbranato da un cane."; OB_CHICKEN = "%o e' stato beccato a morte."; OB_BEAST = "%o e' stato carbonizzato da una bestia."; OB_CLINK = "%o e' stato squarciato da un lapillo."; @@ -667,7 +696,7 @@ OB_MONTELEFRAG = "%o si e' fatto telefraggare."; OB_DEFAULT = "%o e' morto."; OB_FRIENDLY1 = "%k falcia via un compagno."; -OB_FRIENDLY2 = "%k controlla gli occhiali di %p."; +OB_FRIENDLY2 = "%k si controlla gli occhiali."; OB_FRIENDLY3 = "%k regala un frag all'altro team."; OB_FRIENDLY4 = "%k perde un altro amico."; @@ -1016,3 +1045,95 @@ BBA_SCROTUM = "%o ha subito la separazione dello scroto"; BBA_POPULATION = "%o si e' offerto per il controllo della popolazione"; BBA_SUICIDE = "%o si e' suicidato"; BBA_DARWIN = "%o ha ricevuto il Darwin Award"; + +// Chex Quest Strings +CHUSTR_E1M1 = "E1M1: Zona di Atterraggio"; +CHUSTR_E1M2 = "E1M2: Struttura di Immagazzinamento"; +CHUSTR_E1M3 = "E1M3: Laboratorio Sperimentale"; +CHUSTR_E1M4 = "E1M4: Arboreto"; +CHUSTR_E1M5 = "E1M5: Caverne di Bazoik"; + +CE1TEXT = + "Missione compiuta.\n" + "\n" + "Sei preparato per la prossima missione?\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "Premi il tasto 'esc' per continuare..."; + +CE2TEXT = "Ce l'hai fatta!"; +CE3TEXT = "Meraviglioso!"; +CE4TEXT = "Fantastico"; + +CLOADNET = "non puoi caricare una quest durante una net quest!\n\npress a key."; +CQSPROMPT = "sovrascrivere il salvataggio\n\n'%s'?\n\npremi y oppure n."; +CQLOADNET = "non puoi fare un quickload durante una netquest!\n\npremi un tasto."; +CQLPROMPT = "vuoi fare un quickload della quest\n\n'%s'?\n\npremi y oppure n."; +CNEWGAME = "non puoi iniziare una nuova quest\ndurante una quest in rete.\n\npremi un tasto."; + +CNIGHTMARE = "Attento, sara' dura.\nVuoi continuare?\n\npremi y oppure n."; + +CSWSTRING = "questa e' Chex(R) Quest. cerca\n\nfuturi livelli su www.chexquest.com.\n\npremi un tasto."; + +CNETEND = "non puoi terminare una netquest!\n\npremi un tasto"; +CENDGAME = "sei sicuro di voler terminare la quest?\n\npremi y oppure n."; + +GOTCHEXARMOR = "Raccolta un'Armatura Chex(R)."; +GOTSUPERCHEXARMOR = "Raccolta la Super Armatura Chex(R)!"; +GOTWATER = "Raccolto un bicchiere d'acqua."; +GOTREPELLENT = "Raccolto del repellente allo slime."; +GOTBREAKFAST = "Colazione da supercarica!"; +GOTCBLUEKEY = "Raccolta una chiave blu."; +GOTCYELLOWKEY = "Raccolta una chiave gialla."; +GOTCREDKEY = "Raccolta una chiave rossa."; +GOTFRUIT = "Raccolto un vassoio di frutta."; +GOTVEGETABLESNEED = "La verdura ti fa DAVVERO bene!"; +GOTVEGETABLES = "Raccolto un vassoio di verdura."; +GOTSLIMESUIT = "Raccolto un vestito a prova di slime"; +GOTCHEXMAP = "Trovata una mappa computerizzata"; + +GOTZORCHRECHARGE = "Raccolta una ricarica di mini zorch."; +GOTMINIZORCHPACK = "Raccolta una scatola di mini zorch."; +GOTPROPULSORRECHARGE = "Raccolta una ricarica di zorch a propulsione."; +GOTPROPULSORPACK = "Raccolta una scatola di zorch a propulsione."; +GOTPHASINGZORCHERRECHARGE = "Raccolta una ricarica per zorcher a phasing"; +GOTPHASINGZORCHERPACK = "Raccolta una scatola per zorcher a phasing."; +GOTLARGEZORCHERRECHARGE = "Raccolta una ricarica per zorcher grande."; +GOTLARGEZORCHERPACK = "Raccolta una scatola per zorcher grande."; +GOTZORCHPACK = "Raccolto uno ZorchZaino!"; + +GOTLAZDEVICE = "Hai trovato il dispositivo LAZ! Cavolo!"; +GOTRAPIDZORCHER = "Hai trovato il zorcher rapido!"; +GOTSUPERBOOTSPORK = "Hai trovato il super bootspork!"; +GOTZORCHPROPULSOR = "Hai trovato il propulsore zorch!"; +GOTPHASINGZORCHER = "Hai trovato il zorcher a phasing!"; +GOTLARGEZORCHER = "Hai trovato il zorcher grande!"; +GOTSUPERLARGEZORCHER = "Hai trovato il mega zorcher!"; +GOTMINIZORCHER = "Raccolto un mini zorcher."; + +STSTR_CDQDON = "Modalita' invincibile attivata"; +STSTR_CDQDOFF = "Modalita' invincibile disattivata"; +STSTR_CFAADDED = "Zorch aggiunto"; +STSTR_CKFAADDED = "Super Zorch aggiunto"; +STSTR_CCHOPPERS = "... Mangia Chex(R)!"; + +OB_COMMONUS = "%o e' stato coperto di slime da un flemoid."; +OB_BIPEDICUS = "%o e' stato coperto di slime da un bipedicus."; +OB_BIPEDICUS2 = "%o e' stato coperto di slime da un bipedicus corazzato."; +OB_CYCLOPTIS = "%o e' stato coperto di slime da un cycloptis."; +OB_FLEMBRANE = "%o e' stato sconfitto dalla Flembrana."; + +OB_MPSPOON = "%o e' stato imboccato da %k."; +OB_MPBOOTSPORK = "%o e' stato mischiato ben bene dal bootspork di %k."; +OB_MPZORCH = "%o e' stato zorchato da %k."; +OB_MPMEGAZORCH = "%o e' stato colpito dal mega-zorcher di %k."; +OB_MPRAPIDZORCH = "%o e' stato rapid-zorchato da %k."; +OB_MPPROPULSOR = "%o e' stato zorchato dal propulsore di %k."; +OB_MPP_SPLASH = "%o e' stato colpito dal propulsore di %k."; +OB_MPPHASEZORCH = "%o e' stato phase-zorchato da %k."; +OB_MPLAZ_BOOM = "%o e' stato preda del dispositivo LAZ di %k."; +OB_MPLAZ_SPLASH = "%o e' stato lazzato da %k."; diff --git a/wadsrc/static/mapinfo/chex.txt b/wadsrc/static/mapinfo/chex.txt index 3401551eb..edfbc8a46 100644 --- a/wadsrc/static/mapinfo/chex.txt +++ b/wadsrc/static/mapinfo/chex.txt @@ -11,7 +11,7 @@ gameinfo chatsound = "misc/chat2" finalemusic = "$MUSIC_VICTOR" finaleflat = "FLOOR4_8" - finalepage = "HELP2", "VICTORY2", "ENDPIC" + finalepage = "CREDIT", "VICTORY2", "ENDPIC" infopage = "HELP1", "CREDIT" quitsound = "menu/quit1" borderflat = "FLOOR7_2" @@ -40,6 +40,7 @@ gameinfo defaultrespawntime = 12 defaultdropstyle = 1 endoom = "ENDOOM" + player5start = 4001 } skill baby diff --git a/wadsrc/static/mapinfo/doomcommon.txt b/wadsrc/static/mapinfo/doomcommon.txt index 0a5abbcda..4aeee99ad 100644 --- a/wadsrc/static/mapinfo/doomcommon.txt +++ b/wadsrc/static/mapinfo/doomcommon.txt @@ -39,6 +39,7 @@ gameinfo defaultrespawntime = 12 defaultdropstyle = 1 endoom = "ENDOOM" + player5start = 4001 } skill baby diff --git a/wadsrc/static/mapinfo/heretic.txt b/wadsrc/static/mapinfo/heretic.txt index 6136baca2..955574891 100644 --- a/wadsrc/static/mapinfo/heretic.txt +++ b/wadsrc/static/mapinfo/heretic.txt @@ -40,6 +40,7 @@ gameinfo defaultrespawntime = 12 defaultdropstyle = 1 endoom = "ENDTEXT" + player5start = 4001 } skill baby diff --git a/wadsrc/static/mapinfo/hexen.txt b/wadsrc/static/mapinfo/hexen.txt index b4ab056fb..cdababe39 100644 --- a/wadsrc/static/mapinfo/hexen.txt +++ b/wadsrc/static/mapinfo/hexen.txt @@ -38,6 +38,7 @@ gameinfo definventorymaxamount = 25 defaultrespawntime = 12 defaultdropstyle = 1 + player5start = 9100 } skill baby diff --git a/wadsrc/static/mapinfo/strife.txt b/wadsrc/static/mapinfo/strife.txt index 28b87f357..7495c860b 100644 --- a/wadsrc/static/mapinfo/strife.txt +++ b/wadsrc/static/mapinfo/strife.txt @@ -41,6 +41,7 @@ gameinfo defaultrespawntime = 16 defaultdropstyle = 2 endoom = "ENDSTRF" + player5start = 5 } skill baby diff --git a/wadsrc/static/sbarinfo/doom.txt b/wadsrc/static/sbarinfo/doom.txt index fc0784311..cb7f75cb6 100644 --- a/wadsrc/static/sbarinfo/doom.txt +++ b/wadsrc/static/sbarinfo/doom.txt @@ -107,7 +107,7 @@ statusbar normal // Standard Doom Status bar } gamemode cooperative, deathmatch, teamgame { - drawimage translatable "STFBANY", 144, 169; + drawimage translatable "STFBANY", 143, 169; } drawselectedinventory alternateonempty, INDEXFONT, 143, 168 { diff --git a/wadsrc/static/shaders/d3d/shaders.ps b/wadsrc/static/shaders/d3d/shaders.ps index 3039c7e2a..a650cd84a 100644 --- a/wadsrc/static/shaders/d3d/shaders.ps +++ b/wadsrc/static/shaders/d3d/shaders.ps @@ -1,5 +1,10 @@ sampler2D Image : register(s0); sampler1D Palette : register(s1); +#if PS14 +sampler1D Gamma1 : register(s2); +sampler1D Gamma2 : register(s3); +sampler1D Gamma3 : register(s4); +#endif float4 PaletteMod : register(c2); float4 Weights : register(c6); // RGB->Gray weighting { 77/256.0, 143/256.0, 37/256.0, 1 } @@ -100,7 +105,19 @@ float4 InGameColormap(float2 tex_coord : TEXCOORD0, float4 color : COLOR0, float float4 GammaCorrection(float2 tex_coord : TEXCOORD0) : COLOR { float4 color = tex2D(Image, tex_coord); +#if !PS14 color.rgb = pow(color.rgb, Gamma.rgb); +#else + // On PS14 targets, we can only sample once from each sampler + // per stage. Fortunately, we have 16 samplers to play with, + // so we can just set three of them to the gamma texture and + // use one for each component. Unfortunately, all these + // texture lookups are probably not as efficient as the pow() + // solution that later targets make possible. + color.r = tex1D(Gamma1, color.r * Gamma.w).r; + color.g = tex1D(Gamma2, color.g * Gamma.w).g; + color.b = tex1D(Gamma3, color.b * Gamma.w).b; +#endif return color; } diff --git a/wadsrc/static/shaders/d3d/sm14/GammaCorrection.pso b/wadsrc/static/shaders/d3d/sm14/GammaCorrection.pso new file mode 100644 index 000000000..8abf541bc Binary files /dev/null and b/wadsrc/static/shaders/d3d/sm14/GammaCorrection.pso differ diff --git a/wadsrc/static/shaders/d3d/sm14/build.bat b/wadsrc/static/shaders/d3d/sm14/build.bat index 1ba201285..71b287ec3 100644 --- a/wadsrc/static/shaders/d3d/sm14/build.bat +++ b/wadsrc/static/shaders/d3d/sm14/build.bat @@ -22,4 +22,4 @@ fxc ..\shaders.ps /Tps_1_4 /LD -DPS14=1 /EInGameColormap -DPALTEX=1 -DINVERT=1 - fxc ..\shaders.ps /Tps_1_4 /LD -DPS14=1 /EBurnWipe /FoBurnWipe.pso -@rem PS1.4 does not support the pow instruction, so no windowed gamma correction for it. \ No newline at end of file +fxc ..\shaders.ps /Tps_1_4 /LD -DPS14=1 /EGammaCorrection /FoGammaCorrection.pso diff --git a/wadsrc/static/sndinfo.txt b/wadsrc/static/sndinfo.txt index c8840310e..c534808bb 100644 --- a/wadsrc/static/sndinfo.txt +++ b/wadsrc/static/sndinfo.txt @@ -545,6 +545,10 @@ $playersound player male *jump plrjmp $playersound player male *burndeath hedat1 $playeralias chicken male *usefail chicken/peck +$PlayerAlias Chicken Male *Grunt chicken/pain +$PlayerAlias Chicken Male *Land chicken/pain +$PlayerAlias Chicken Male *Jump chicken/active +$PlayerAlias Chicken Male *EvilLaugh chicken/active chicken/sight chicpai chicken/pain chicpai @@ -887,6 +891,14 @@ $playeralias mage male *puzzfail PuzzleFailMage $playersound mage male *jump mgjump $playeralias pig male *usefail PigActive1 +$PlayerAlias Pig Male *UseFail PigActive1 +$playeralias Pig Male *PuzzFail PigActive2 +$PlayerAlias Pig Male *Grunt PigActive1 +$PlayerAlias Pig Male *Land PigActive2 +$PlayerAlias Pig Male *Jump PigActive1 +$PlayerAlias Pig Male *Poison PigActive2 +$PlayerAlias Pig Male *Falling PigPain +$PlayerAlias Pig Male *Splat PigDeath $alias world/drip Ambient10 $alias world/watersplash WaterSplash @@ -1074,6 +1086,7 @@ switches/knob dsswknob switches/keycard dskeycrd switches/stone dsswston switches/bolt dsswbolt +switches/boltback dsempty switches/scanner dsswscan switches/fool dsdifool switches/valve dsvalve diff --git a/wadsrc/static/sndseq.txt b/wadsrc/static/sndseq.txt index 38c0dcc55..cdf760944 100644 --- a/wadsrc/static/sndseq.txt +++ b/wadsrc/static/sndseq.txt @@ -94,7 +94,7 @@ end end :DoorCloseSmallWood - play doors/large_wood_open + play doors/small_wood_close nostopcutoff end diff --git a/wadsrc/static/sprites/PLS1A0.png b/wadsrc/static/sprites/PLS1A0.png index 029d0bc75..303e203c7 100644 Binary files a/wadsrc/static/sprites/PLS1A0.png and b/wadsrc/static/sprites/PLS1A0.png differ diff --git a/wadsrc/static/sprites/PLS1C0.png b/wadsrc/static/sprites/PLS1C0.png index 029d0bc75..303e203c7 100644 Binary files a/wadsrc/static/sprites/PLS1C0.png and b/wadsrc/static/sprites/PLS1C0.png differ diff --git a/wadsrc/static/sprites/PLS1G0.png b/wadsrc/static/sprites/PLS1G0.png index df4e1f6d4..e3a1629d8 100644 Binary files a/wadsrc/static/sprites/PLS1G0.png and b/wadsrc/static/sprites/PLS1G0.png differ diff --git a/wadsrc/static/sprites/PLS2C0.png b/wadsrc/static/sprites/PLS2C0.png index 1d4148d1d..2593cdc06 100644 Binary files a/wadsrc/static/sprites/PLS2C0.png and b/wadsrc/static/sprites/PLS2C0.png differ diff --git a/wadsrc/static/sprites/PLS2D0.png b/wadsrc/static/sprites/PLS2D0.png index b5be3aaf1..a3850aba6 100644 Binary files a/wadsrc/static/sprites/PLS2D0.png and b/wadsrc/static/sprites/PLS2D0.png differ diff --git a/wadsrc/static/sprites/amrka0.png b/wadsrc/static/sprites/amrka0.png index c95d00afe..159fe75c2 100644 Binary files a/wadsrc/static/sprites/amrka0.png and b/wadsrc/static/sprites/amrka0.png differ diff --git a/wadsrc/static/sprites/iceca0.png b/wadsrc/static/sprites/iceca0.png index bc94b0426..d6b304923 100644 Binary files a/wadsrc/static/sprites/iceca0.png and b/wadsrc/static/sprites/iceca0.png differ diff --git a/wadsrc/static/sprites/icecb0.png b/wadsrc/static/sprites/icecb0.png index d94cb04fb..5a7f69e2e 100644 Binary files a/wadsrc/static/sprites/icecb0.png and b/wadsrc/static/sprites/icecb0.png differ diff --git a/wadsrc/static/sprites/icecc0.png b/wadsrc/static/sprites/icecc0.png index d8f4a3786..8865d9909 100644 Binary files a/wadsrc/static/sprites/icecc0.png and b/wadsrc/static/sprites/icecc0.png differ diff --git a/wadsrc/static/sprites/icecd0.png b/wadsrc/static/sprites/icecd0.png index a90eb3052..43456f9c0 100644 Binary files a/wadsrc/static/sprites/icecd0.png and b/wadsrc/static/sprites/icecd0.png differ diff --git a/wadsrc/static/sprites/rsmka0.png b/wadsrc/static/sprites/rsmka0.png index cee476cd1..761e09170 100644 Binary files a/wadsrc/static/sprites/rsmka0.png and b/wadsrc/static/sprites/rsmka0.png differ diff --git a/wadsrc/static/sprites/rsmkb0.png b/wadsrc/static/sprites/rsmkb0.png index cc7046ac6..0bcfef43d 100644 Binary files a/wadsrc/static/sprites/rsmkb0.png and b/wadsrc/static/sprites/rsmkb0.png differ diff --git a/wadsrc/static/sprites/rsmkc0.png b/wadsrc/static/sprites/rsmkc0.png index 17b121bf9..3e952cdba 100644 Binary files a/wadsrc/static/sprites/rsmkc0.png and b/wadsrc/static/sprites/rsmkc0.png differ diff --git a/wadsrc/static/sprites/rsmkd0.png b/wadsrc/static/sprites/rsmkd0.png index 20d9aa6c9..60827b6dc 100644 Binary files a/wadsrc/static/sprites/rsmkd0.png and b/wadsrc/static/sprites/rsmkd0.png differ diff --git a/wadsrc/static/sprites/rsmke0.png b/wadsrc/static/sprites/rsmke0.png index 930b30a58..9d5652bd2 100644 Binary files a/wadsrc/static/sprites/rsmke0.png and b/wadsrc/static/sprites/rsmke0.png differ diff --git a/wadsrc/static/sprites/sgrna5.png b/wadsrc/static/sprites/sgrna5.png index a99066b52..f3b2e1787 100644 Binary files a/wadsrc/static/sprites/sgrna5.png and b/wadsrc/static/sprites/sgrna5.png differ diff --git a/wadsrc/static/sprites/sgrna6a4.png b/wadsrc/static/sprites/sgrna6a4.png index 7a78f8ca3..7fd549793 100644 Binary files a/wadsrc/static/sprites/sgrna6a4.png and b/wadsrc/static/sprites/sgrna6a4.png differ diff --git a/wadsrc/static/sprites/sgrna7a3.png b/wadsrc/static/sprites/sgrna7a3.png index 83cc8dcfc..99755ae05 100644 Binary files a/wadsrc/static/sprites/sgrna7a3.png and b/wadsrc/static/sprites/sgrna7a3.png differ diff --git a/wadsrc/static/sprites/sgrna8a2.png b/wadsrc/static/sprites/sgrna8a2.png index 5bbf9da78..10bfa9462 100644 Binary files a/wadsrc/static/sprites/sgrna8a2.png and b/wadsrc/static/sprites/sgrna8a2.png differ diff --git a/wadsrc/static/sprites/tlgla0.png b/wadsrc/static/sprites/tlgla0.png index 829a25526..de3dccc34 100644 Binary files a/wadsrc/static/sprites/tlgla0.png and b/wadsrc/static/sprites/tlgla0.png differ diff --git a/wadsrc/static/sprites/tlglb0.png b/wadsrc/static/sprites/tlglb0.png index 829a25526..de3dccc34 100644 Binary files a/wadsrc/static/sprites/tlglb0.png and b/wadsrc/static/sprites/tlglb0.png differ diff --git a/wadsrc/static/sprites/tlglc0.png b/wadsrc/static/sprites/tlglc0.png index 829a25526..de3dccc34 100644 Binary files a/wadsrc/static/sprites/tlglc0.png and b/wadsrc/static/sprites/tlglc0.png differ diff --git a/wadsrc/static/sprites/tlgld0.png b/wadsrc/static/sprites/tlgld0.png index 829a25526..de3dccc34 100644 Binary files a/wadsrc/static/sprites/tlgld0.png and b/wadsrc/static/sprites/tlgld0.png differ diff --git a/wadsrc/static/sprites/tlgle0.png b/wadsrc/static/sprites/tlgle0.png index 829a25526..de3dccc34 100644 Binary files a/wadsrc/static/sprites/tlgle0.png and b/wadsrc/static/sprites/tlgle0.png differ diff --git a/wadsrc/static/xlat/base.txt b/wadsrc/static/xlat/base.txt index ca13bc244..a7aa69f1c 100644 --- a/wadsrc/static/xlat/base.txt +++ b/wadsrc/static/xlat/base.txt @@ -43,7 +43,7 @@ include "xlat/defines.i" 41 = USE, Ceiling_LowerToFloor (tag, C_SLOW) 42 = USE|REP, Door_Close (tag, D_SLOW) 43 = USE|REP, Ceiling_LowerToFloor (tag, C_SLOW) - 44 = WALK, Ceiling_LowerAndCrush (tag, C_SLOW, 0) + 44 = WALK, Ceiling_LowerAndCrush (tag, C_SLOW, 0, 2) 45 = USE|REP, Floor_LowerToHighest (tag, F_SLOW, 128) 46 = SHOOT|REP|MONST, Door_Open (tag, D_SLOW) 47 = SHOOT, Plat_RaiseAndStayTx0 (tag, P_SLOW/2) @@ -71,7 +71,7 @@ include "xlat/defines.i" 69 = USE|REP, Floor_RaiseToNearest (tag, F_SLOW) 70 = USE|REP, Floor_LowerToHighest (tag, F_FAST, 136) 71 = USE, Floor_LowerToHighest (tag, F_FAST, 136) - 72 = WALK|REP, Ceiling_LowerAndCrush (tag, C_SLOW, 0) + 72 = WALK|REP, Ceiling_LowerAndCrush (tag, C_SLOW, 0, 2) 73 = WALK|REP, Ceiling_CrushAndRaiseA (tag, C_SLOW, C_SLOW, 10) 74 = WALK|REP, Ceiling_CrushStop (tag) 75 = WALK|REP, Door_Close (tag, D_SLOW) @@ -169,7 +169,7 @@ include "xlat/defines.i" 164 = USE, Ceiling_CrushAndRaiseA (tag, C_NORMAL, C_NORMAL, 10) 165 = USE, Ceiling_CrushAndRaiseSilentA (tag, C_SLOW, C_SLOW, 10) 166 = USE, FloorAndCeiling_LowerRaise (tag, F_SLOW, C_SLOW) -167 = USE, Ceiling_LowerAndCrush (tag, C_SLOW, 0) +167 = USE, Ceiling_LowerAndCrush (tag, C_SLOW, 0, 2) 168 = USE, Ceiling_CrushStop (tag) 169 = USE, Light_MaxNeighbor (tag) 170 = USE, Light_ChangeToValue (tag, 35) @@ -189,7 +189,7 @@ include "xlat/defines.i" 184 = USE|REP, Ceiling_CrushAndRaiseA (tag, C_SLOW, C_SLOW, 10) 185 = USE|REP, Ceiling_CrushAndRaiseSilentA (tag, C_SLOW, C_SLOW, 10) 186 = USE|REP, FloorAndCeiling_LowerRaise (tag, F_SLOW, C_SLOW) -187 = USE|REP, Ceiling_LowerAndCrush (tag, C_SLOW, 0) +187 = USE|REP, Ceiling_LowerAndCrush (tag, C_SLOW, 0, 2) 188 = USE|REP, Ceiling_CrushStop (tag) 189 = USE, Floor_TransferTrigger (tag) 190 = USE|REP, Floor_TransferTrigger (tag) diff --git a/wadsrc/static/xlat/defines.i b/wadsrc/static/xlat/defines.i index 6673b5ef5..648679894 100644 --- a/wadsrc/static/xlat/defines.i +++ b/wadsrc/static/xlat/defines.i @@ -209,3 +209,34 @@ define FRICTION_MASK (0x0800) define PUSH_MASK (0x1000) +enum +{ + ML_BLOCKING = 0x00000001, + ML_BLOCKMONSTERS = 0x00000002, + ML_TWOSIDED = 0x00000004, + ML_DONTPEGTOP = 0x00000008, + ML_DONTPEGBOTTOM = 0x00000010, + ML_SECRET = 0x00000020, + ML_SOUNDBLOCK = 0x00000040, + ML_DONTDRAW = 0x00000080, + ML_MAPPED = 0x00000100, + + // Extended flags + ML_MONSTERSCANACTIVATE = 0x00002000, + ML_BLOCK_PLAYERS = 0x00004000, + ML_BLOCKEVERYTHING = 0x00008000, + ML_ZONEBOUNDARY = 0x00010000, + ML_RAILING = 0x00020000, + ML_BLOCK_FLOATERS = 0x00040000, + ML_CLIP_MIDTEX = 0x00080000, + ML_WRAP_MIDTEX = 0x00100000, + ML_3DMIDTEX = 0x00200000, + ML_CHECKSWITCHRANGE = 0x00400000, + ML_FIRSTSIDEONLY = 0x00800000, + ML_BLOCKPROJECTILE = 0x01000000, + ML_BLOCKUSE = 0x02000000, + + // + ML_PASSTHROUGH = -1, + ML_TRANSLUCENT = -2 +} \ No newline at end of file diff --git a/wadsrc/static/xlat/doom.txt b/wadsrc/static/xlat/doom.txt index 25b84b2f0..079f5462e 100644 --- a/wadsrc/static/xlat/doom.txt +++ b/wadsrc/static/xlat/doom.txt @@ -28,3 +28,15 @@ sector 22 = LightSequenceStart; sector 23 = LightSequenceSpecial1; sector 24 = LightSequenceSpecial2; +lineflag 0 = ML_BLOCKING; +lineflag 1 = ML_BLOCKMONSTERS; +lineflag 2 = ML_TWOSIDED; +lineflag 3 = ML_DONTPEGTOP; +lineflag 4 = ML_DONTPEGBOTTOM; +lineflag 5 = ML_SECRET; +lineflag 6 = ML_SOUNDBLOCK; +lineflag 7 = ML_DONTDRAW; +lineflag 8 = ML_MAPPED; +lineflag 9 = ML_PASSTHROUGH; +lineflag 10 = ML_3DMIDTEX; +lineflag 11 & (ML_BLOCKING|ML_BLOCKMONSTERS|ML_TWOSIDED|ML_DONTPEGTOP|ML_DONTPEGBOTTOM|ML_SECRET|ML_SOUNDBLOCK|ML_DONTDRAW|ML_MAPPED); diff --git a/wadsrc/static/xlat/eternity.txt b/wadsrc/static/xlat/eternity.txt new file mode 100644 index 000000000..ad270beb4 --- /dev/null +++ b/wadsrc/static/xlat/eternity.txt @@ -0,0 +1,192 @@ +#include "xlat/doom.txt" + +// xlat file for Eternity levels. +// Many specials are unsupported, especially portal stuff. +// Some unsupported linedefs wouldn't be hard to add to ZDoom, +// or are already there but implemented differently. Others are +// practically impossible, or aren't worth the effort. + +define Unsupported (0) + +// The tag for such a line is actually a key to find, in an ExtraData lump +// indicated for the current level by the EMAPINFO lump, what line special +// to actually use. This is how parameterized linedefs are used by Eternity +// in the Doom format. "xlating" this would thus be quite complicated... +270 = 0, Unsupported() // "ExtraDataSpecial" + +// These two are standard MBF specials, no need to redefine them, they're in xlat/doom.txt +// 271 = 0, Static_Init (tag, Init_TransferSky, 0) +// 272 = 0, Static_Init (tag, Init_TransferSky, 1) + +// Small script starters. Small is considered deprecated now anyway. +273 = 0, Unsupported() // "WR_StartScript_1S" +274 = 0, Unsupported() // "W1_StartScript" +275 = 0, Unsupported() // "W1_StartScript_1S" +276 = 0, Unsupported() // "SR_StartScript" +277 = 0, Unsupported() // "S1_StartScript" +278 = 0, Unsupported() // "GR_StartScript" +279 = 0, Unsupported() // "G1_StartScript" +280 = 0, Unsupported() // "WR_StartScript" + +// 3D mid-textures +281 = 0, Sector_Attach3DMidtex(tag, 0, 0) // "3DMidTex_MoveWithFloor" +282 = 0, Sector_Attach3DMidtex(tag, 0, 1) // "3DMidTex_MoveWithCeiling" + +// Plane portals are not supported in ZDoom, though they probably wouldn't be too hard to implement. +283 = 0, Unsupported() // "Portal_PlaneCeiling" +284 = 0, Unsupported() // "Portal_PlaneFloor" +285 = 0, Unsupported() // "Portal_PlaneFloorCeiling" +286 = 0, Unsupported() // "Portal_HorizonCeiling" +287 = 0, Unsupported() // "Portal_HorizonFloor" +288 = 0, Unsupported() // "Portal_HorizonFloorCeiling" +289 = 0, Unsupported() // "Portal_LineTransfer" + +// Skybox portals +290 = 0, Sector_SetPortal(tag, 2, 1, 1, 0) // "Portal_SkyboxCeiling" +291 = 0, Sector_SetPortal(tag, 2, 0, 1, 0) // "Portal_SkyboxFloor" +292 = 0, Sector_SetPortal(tag, 2, 2, 1, 0) // "Portal_SkyboxFloorCeiling" + +// Sector specials +293 = 0, Sector_SetWind(tag, 0, 0, 1) // "TransferHereticWind" +294 = 0, Sector_SetCurrent(tag, 0, 0, 1) // "TransferHereticCurrent" + +// Anchored portals -- Sector_SetPortal needs to allow to set both floor and ceiling, though. +295 = 0, Sector_SetPortal(tag, 0, 1, 1, 0) // "Portal_AnchoredCeiling" +296 = 0, Sector_SetPortal(tag, 0, 0, 1, 0) // "Portal_AnchoredFloor" +297 = 0, Sector_SetPortal(tag, 0, 2, 1, 0) // "Portal_AnchoredFloorCeiling" +298 = 0, Sector_SetPortal(tag, 0, 1, 0, 0) // "Portal_AnchorLine" +299 = 0, Sector_SetPortal(tag, 0, 0, 0, 0) // "Portal_AnchorLineFloor" + +// Parameterized linedefs +// They are never used directly in Doom-format maps. Instead, it passes through ExtraData and 270. +// Hexen format is incomplete; and Quasar wants to use ZDoom-compatible special values for UDMF. +// So there is no need to bother with them and they are listed only for completeness' sake. +/* + 300: "Door_Raise" + 301: "Door_Open" + 302: "Door_Close" + 303: "Door_CloseWaitOpen" + 304: "Door_WaitRaise" + 305: "Door_WaitClose" + 306: "Floor_RaiseToHighest" + 307: "Floor_LowerToHighest" + 308: "Floor_RaiseToLowest" + 309: "Floor_LowerToLowest" + 310: "Floor_RaiseToNearest" + 311: "Floor_LowerToNearest" + 312: "Floor_RaiseToLowestCeiling" + 313: "Floor_LowerToLowestCeiling" + 314: "Floor_RaiseToCeiling" + 315: "Floor_RaiseByTexture" + 316: "Floor_LowerByTexture" + 317: "Floor_RaiseByValue" + 318: "Floor_LowerByValue" + 319: "Floor_MoveToValue" + 320: "Floor_RaiseInstant" + 321: "Floor_LowerInstant" + 322: "Floor_ToCeilingInstant" + 323: "Ceiling_RaiseToHighest" + 324: "Ceiling_ToHighestInstant" + 325: "Ceiling_RaiseToNearest" + 326: "Ceiling_LowerToNearest" + 327: "Ceiling_RaiseToLowest" + 328: "Ceiling_LowerToLowest" + 329: "Ceiling_RaiseToHighestFloor" + 330: "Ceiling_LowerToHighestFloor" + 331: "Ceiling_ToFloorInstant" + 332: "Ceiling_LowerToFloor" + 333: "Ceiling_RaiseByTexture" + 334: "Ceiling_LowerByTexture" + 335: "Ceiling_RaiseByValue" + 336: "Ceiling_LowerByValue" + 337: "Ceiling_MoveToValue" + 338: "Ceiling_RaiseInstant" + 339: "Ceiling_LowerInstant" + 340: "Stairs_BuildUpDoom" + 341: "Stairs_BuildDownDoom" + 342: "Stairs_BuildUpDoomSync" + 343: "Stairs_BuildDownDoomSync" +*/ + +// Two-way portals are not supported yet either +344 = 0, Unsupported() // "Portal_TwowayCeiling" +345 = 0, Unsupported() // "Portal_TwowayFloor" +346 = 0, Unsupported() // "Portal_TwowayAnchorLine" +347 = 0, Unsupported() // "Portal_TwowayAnchorLineFloor" + +// More parameterized linedefs +/* + 348: "Polyobj_StartLine" + 349: "Polyobj_ExplicitLine" + 350: "Polyobj_DoorSlide" + 351: "Polyobj_DoorSwing" + 352: "Polyobj_Move" + 353: "Polyobj_OR_Move" + 354: "Polyobj_RotateRight" + 355: "Polyobj_OR_RotateRight" + 356: "Polyobj_RotateLeft" + 357: "Polyobj_OR_RotateLeft" +*/ + +// Eternity's linked portals, vertical link version (floor-to-ceiling) +358 = 0, Unsupported() // "Portal_LinkedCeiling" +359 = 0, Unsupported() // "Portal_LinkedFloor" +360 = 0, Unsupported() // "Portal_LinkedAnchorLine" +361 = 0, Unsupported() // "Portal_LinkedAnchorLineFloor" + +// Even more parameterized linedefs +/* + 362: "Pillar_Build" + 363: "Pillar_BuildAndCrush" + 364: "Pillar_Open" + 365: "ACS_Execute" + 366: "ACS_Suspend" + 367: "ACS_Terminate" + 368: "Light_RaiseByValue" + 369: "Light_LowerByValue" + 370: "Light_ChangeToValue" + 371: "Light_Fade" + 372: "Light_Glow" + 373: "Light_Flicker" + 374: "Light_Strobe" + 375: "Radius_Quake" +*/ + +// Eternity's linked portals, horizontal link version (wall-to-wall) +376 = 0, Unsupported() // "Portal_LinkedLineToLine" +377 = 0, Unsupported() // "Portal_LinkedLineToLineAnchor" + +// The famous Hexen linedef +// 378 = Line_SetIdentification + +// Attached sectors == linked sectors; However, the implementation in Eternity +// is based on front sectors of tagged lines, not on sector tags. So instead +// of Sector_SetLink, we pass through Static_Init to translate those. +379 = 0, Static_Init(tag, 3, 1) // "Attach_SetCeilingControl" +380 = 0, Static_Init(tag, 3, 0) // "Attach_SetFloorControl" +381 = 0, Static_Init(0, 3, 0, 1) // "Attach_FloorToControl" +382 = 0, Static_Init(0, 3, 1, 2) // "Attach_CeilingToControl" +383 = 0, Static_Init(0, 3, 0, 5) // "Attach_MirrorFloorToControl" +384 = 0, Static_Init(0, 3, 0, 10) // "Attach_MirrorCeilingToControl" + +// Attach tagged portal to front sector +385 = 0, Sector_SetPortal(0, 1, 2, tag) // "Apply_PortalToFrontsector" + +// Slopes! +386 = 0, Plane_Align (1, 0) // "Slope_FrontsectorFloor" +387 = 0, Plane_Align (0, 1) // "Slope_FrontsectorCeiling" +388 = 0, Plane_Align (1, 1) // "Slope_FrontsectorFloorAndCeiling" +389 = 0, Plane_Align (2, 0) // "Slope_BacksectorFloor" +390 = 0, Plane_Align (0, 2) // "Slope_BacksectorCeiling" +391 = 0, Plane_Align (2, 2) // "Slope_BacksectorFloorAndCeiling" +392 = 0, Plane_Align (2, 1) // "Slope_BackFloorAndFrontCeiling" +393 = 0, Plane_Align (1, 2) // "Slope_BackCeilingAndFrontFloor" +394 = 0, Plane_Copy (tag, 0) // "Slope_FrontFloorToTaggedSlope" +395 = 0, Plane_Copy (0, tag) // "Slope_FrontCeilingToTaggedSlope" +396 = 0, Plane_Copy(tag, tag)// "Slope_FrontFloorAndCeilingToTaggedSlope" + +// Last parameterized linedefs +// 397 = Floor_Waggle +// 398 = Thing_Spawn +// 399 = Thing_SpawnNoFog +// 400 = Teleport_EndGame diff --git a/wadsrc/static/xlat/heretic.txt b/wadsrc/static/xlat/heretic.txt index b1a4908b0..da4d103a5 100644 --- a/wadsrc/static/xlat/heretic.txt +++ b/wadsrc/static/xlat/heretic.txt @@ -65,3 +65,14 @@ sector 50 = Wind_West_Medium; sector 51 = Wind_West_Strong; +lineflag 0 = ML_BLOCKING; +lineflag 1 = ML_BLOCKMONSTERS; +lineflag 2 = ML_TWOSIDED; +lineflag 3 = ML_DONTPEGTOP; +lineflag 4 = ML_DONTPEGBOTTOM; +lineflag 5 = ML_SECRET; +lineflag 6 = ML_SOUNDBLOCK; +lineflag 7 = ML_DONTDRAW; +lineflag 8 = ML_MAPPED; +lineflag 9 = ML_PASSTHROUGH; +lineflag 10 = ML_3DMIDTEX; diff --git a/wadsrc/static/xlat/strife.txt b/wadsrc/static/xlat/strife.txt index 72ae2686b..0c141c855 100644 --- a/wadsrc/static/xlat/strife.txt +++ b/wadsrc/static/xlat/strife.txt @@ -352,3 +352,16 @@ sector 23 = LightSequenceSpecial1; sector 24 = LightSequenceSpecial2; +lineflag 0 = ML_BLOCKING; +lineflag 1 = ML_BLOCKMONSTERS; +lineflag 2 = ML_TWOSIDED; +lineflag 3 = ML_DONTPEGTOP; +lineflag 4 = ML_DONTPEGBOTTOM; +lineflag 5 = ML_SECRET; +lineflag 6 = ML_SOUNDBLOCK; +lineflag 7 = ML_DONTDRAW; +lineflag 8 = ML_MAPPED; +lineflag 9 = ML_RAILING; +lineflag 10 = ML_BLOCK_FLOATERS; +lineflag 12 = ML_TRANSLUCENT; + diff --git a/zdoom.sln b/zdoom.sln index 0f4d541a6..988e84d68 100644 --- a/zdoom.sln +++ b/zdoom.sln @@ -165,9 +165,11 @@ Global {9B465A9E-E5C7-4577-B559-3CA2F7AE7D96}.Debug|Win32.ActiveCfg = Release|Win32 {9B465A9E-E5C7-4577-B559-3CA2F7AE7D96}.Debug|Win32.Build.0 = Release|Win32 {9B465A9E-E5C7-4577-B559-3CA2F7AE7D96}.Debug|x64.ActiveCfg = Release|x64 + {9B465A9E-E5C7-4577-B559-3CA2F7AE7D96}.Debug|x64.Build.0 = Release|x64 {9B465A9E-E5C7-4577-B559-3CA2F7AE7D96}.Release|Win32.ActiveCfg = Release|Win32 {9B465A9E-E5C7-4577-B559-3CA2F7AE7D96}.Release|Win32.Build.0 = Release|Win32 - {9B465A9E-E5C7-4577-B559-3CA2F7AE7D96}.Release|x64.ActiveCfg = Release|Win32 + {9B465A9E-E5C7-4577-B559-3CA2F7AE7D96}.Release|x64.ActiveCfg = Release|x64 + {9B465A9E-E5C7-4577-B559-3CA2F7AE7D96}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/zdoom.vcproj b/zdoom.vcproj index 9e7256cb3..c37dd5d91 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -1593,6 +1593,10 @@ RelativePath=".\src\x86.h" > + + @@ -2256,17 +2260,33 @@ RelativePath=".\src\g_shared\sbar_mugshot.cpp" > + + - - + + + + + + - - - - - - - - - - - - - -