Update to ZDoom r905:

- Added Martin Howe's morph system update.
- Added support for defining composite textures in HIRESTEX. It is not fully tested
  and right now can't do much more than the old TEXTUREx method.
- Added a few NULL pointer checks to the texture code.
- Made duplicate class names in DECORATE non-fatal. There is really no stability
  concern here and the worst that can happen is that the wrong actor is spawned.
  This was a constant hassle when testing with WADs that contain duplicate resources.
- Removed some GCC warnings.
- Fixed: MinGW doesn't have _get_pgmptr(), so it couldn't compile i_main.cpp.
- Fixed: MOD_WAVETABLE and MOD_SWSYNTH are not defined by w32api, so MinGW
  failed compiling the new MIDI code.
- Fixed: LocalSndInfo and LocalSndSeq in S_Start() need to be const char
  pointers, since "" is a constant.
- Fixed: parsecontext.h was missing a newline at the end of the file.
- Fixed: Timidity::Channel::mono, rpn, and nrpn were not initialized. In
  particular, this meant that every channel was almost certainly in mono mode,
  which can sound pretty bad if the song isn't meant to be played that way.
- Added bank numbers to the MIDI precaching for Timidity, since I guess I do
  need to care about banks, if even the Duke MIDIs use various banks.
- Fixed: snd_midiprecache only exists in Win32 builds, so gameconfigfile.cpp
  shouldn't unconditionally link against it.
- Fixed: pre_resample() was still disabled, and it left two samples at the end
  of the new wave data uninitialized.
- Moved the xmap table from timidity/tables.cpp to playmidi.cpp. Now I can get
  rid of timidity/tables.cpp, which conflicts in name with the main Doom
  tables.cpp. (And interestingly, VC++ automatically renamed the object file,
  so I wasn't aware of the problem with GCC.)
- Added a Gets function to the FileReader class which I planned to use
  to enable Timidity to read its config and sound patches from Zips. 
  I put this on hold though after finding out that the sound quality 
  isn't even near that of Timidity++.
- GCC-Fixes (FString::GetChars() for Printf calls)
- Added a dummy Weapon.NOLMS flag so that Skulltag weapons using this flag
  can be loaded
- Changed the MIDIStreamer to send the all notes off controller to each
  channel when restarting the song, rather than emitting a single note off
  event which only has 1 in 127 chance of being for a note that's playing
  on that channel. Then I decided it would probably be a good idea to reset
  all the controllers as well.
- Increasing the size of the internal Timidity stream buffer from 1/14 sec
  (copied from the OPL player) improved its sound dramatically, so apparently
  Timidity has issues with short stream buffers. It's now at 1/2 sec in
  length. However, there seems to be something weird going on with
  corazonazul_ff6boss.mid near the beginning where it stops and immediately
  restarts a guitar on the exact same note.
- Added a new sound debugging cvar: snd_drawoutput, which can show various
  oscilloscopes and spectrums.
- Eliminated some more global variables (onmobj, DoRipping, LastRipped,
  MissileActor, bulletpitch and linetarget.)
- Internal TiMidity now plays music. Unfortunately, it doesn't sound right. :(
- Changed the progdir global variable into an FString.

git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@90 b0f79afe-0144-0410-b225-9a4edf0717df
This commit is contained in:
Christoph Oelckers 2008-04-12 18:59:23 +00:00
parent b75a6dcea6
commit f5930d3fb5
71 changed files with 1794 additions and 535 deletions

View file

@ -1,4 +1,6 @@
# created on 4/12/2006 by James Bentler
FMOD_PREFIX = /usr/local
CXX ?= g++
CC ?= gcc
NASM ?= nasm
@ -12,13 +14,13 @@ CFLAGS += `pkg-config gtk+-2.0 --cflags`
CFLAGS += -DHAVE_FILELENGTH -D__forceinline=inline `sdl-config --cflags`
CFLAGS += -DNEED_STRUPR
CFLAGS += -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp
LDFLAGS += -lFLAC++ -lFLAC -lz -lmodplug -lfmodex `sdl-config --libs`
LDFLAGS += -lz $(FMOD_PREFIX)/lib/libfmodex.so `sdl-config --libs`
LDFLAGS += -ljpeg `pkg-config gtk+-2.0 --libs`
LDFLAGS += -lGL -lGLU
NASMFLAGS += -f elf -DM_TARGET_LINUX
SRCDIRS = src/ $(addprefix src/,g_doom/ g_heretic/ g_hexen/ g_raven/ g_shared/ g_strife/ oplsynth/ sound/ fragglescript/ thingdef/ Linux/ sdl/ gl/ gl/r_render/ textures/ xlat/)
INCLUDES = $(addprefix -I,$(SRCDIRS))
INCLUDES += -Isnes_spc/snes_spc/ -I/usr/include/fmodex/
INCLUDES += -Isnes_spc/snes_spc/ -I$(FMOD_PREFIX)/include/fmodex/
CFLAGS += $(INCLUDES)
RELEASEOBJ ?= releaseobj
@ -120,6 +122,10 @@ ifdef RESTART
@make -f $(firstword $(MAKEFILE_LIST)) RESTART=
endif
# This file needs special handling so that it actually gets compiled with SSE2 support.
$(OBJDIR)/nodebuild_classify_sse2.o: src/nodebuild_classify_sse2.cpp
$(CCDV) $(CXX) $(CXXFLAGS) -msse2 -mfpmath=sse -c -o $@ $<
# This file needs special handling because GCC misoptimizes it otherwise.
$(OBJDIR)/fmopl.o: src/oplsynth/fmopl.cpp
$(CCDV) $(CXX) $(CXXFLAGS) -fno-tree-dominator-opts -fno-tree-fre -c -o $@ $<
@ -130,26 +136,26 @@ src/xlat/xlat_parser.h src/xlat/xlat_parser.c: tools/lemon/lemon src/xlat/xlat_p
$(OBJDIR):
mkdir $(OBJDIR)
toolsandpk3: tools/makewad/makewad src/svnrevision_gz.h
make -C wadsrc/
toolsandpk3: ccdv tools/makewad/makewad src/svnrevision_gz.h
$(MAKE) -C wadsrc/
zdoom.pk3: toolsandpk3
ln -sf wadsrc/gzdoom.pk3 ./
snes_spc/libsnes_spc.a:
snes_spc/libsnes_spc.a: ccdv
$(MAKE) -C snes_spc/
tools/makewad/makewad: ccdv
make -C tools/makewad/
$(MAKE) -C tools/makewad/
tools/lemon/lemon:
tools/lemon/lemon: ccdv
$(MAKE) -C tools/lemon/
tools/updaterevision/updaterevision: ccdv
make -C tools/updaterevision/
$(MAKE) -C tools/updaterevision/
src/svnrevision_gz.h: tools/updaterevision/updaterevision
tools/updaterevision/updaterevision src src/svnrevision_gz.h
tools/updaterevision/updaterevision . src/svnrevision_gz.h
ccdv: ccdv-posix.c
@gcc -Os -s ccdv-posix.c -o ccdv

View file

@ -2884,6 +2884,10 @@
RelativePath="src\sound\music_stream.cpp"
>
</File>
<File
RelativePath=".\src\sound\music_timidity_mididevice.cpp"
>
</File>
<File
RelativePath=".\src\sound\music_win_mididevice.cpp"
>
@ -2940,6 +2944,86 @@
>
</File>
</Filter>
<Filter
Name="Timidity"
>
<File
RelativePath=".\src\timidity\common.cpp"
>
</File>
<File
RelativePath=".\src\timidity\dls1.h"
>
</File>
<File
RelativePath=".\src\timidity\dls2.h"
>
</File>
<File
RelativePath=".\src\timidity\instrum.cpp"
>
</File>
<File
RelativePath=".\src\timidity\instrum_dls.cpp"
>
</File>
<File
RelativePath=".\src\timidity\mix.cpp"
>
</File>
<File
RelativePath=".\src\timidity\playmidi.cpp"
>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
EnableEnhancedInstructionSet="0"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\src\timidity\resample.cpp"
>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
EnableEnhancedInstructionSet="0"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\src\timidity\timidity.cpp"
>
</File>
<File
RelativePath=".\src\timidity\timidity.h"
>
</File>
<Filter
Name="Docs"
>
<File
RelativePath=".\src\timidity\CHANGES"
>
</File>
<File
RelativePath=".\src\timidity\COPYING"
>
</File>
<File
RelativePath=".\src\timidity\FAQ"
>
</File>
<File
RelativePath=".\src\timidity\README"
>
</File>
</Filter>
</Filter>
</Filter>
<Filter
Name="SDL Files"

View file

@ -748,8 +748,10 @@ CCMD (wdir)
//-----------------------------------------------------------------------------
CCMD(linetarget)
{
AActor *linetarget;
if (CheckCheatmode () || players[consoleplayer].mo == NULL) return;
P_AimLineAttack(players[consoleplayer].mo,players[consoleplayer].mo->angle,MISSILERANGE, 0);
P_AimLineAttack(players[consoleplayer].mo,players[consoleplayer].mo->angle,MISSILERANGE, &linetarget, 0);
if (linetarget)
{
Printf("Target=%s, Health=%d, Spawnhealth=%d\n",

View file

@ -22,7 +22,7 @@ gamedir will hold progdir + the game directory (id1, id2, etc)
*/
char progdir[1024];
FString progdir;
static inline bool IsSeperator (int c)
{

View file

@ -28,7 +28,7 @@
int Q_filelength (FILE *f);
bool FileExists (const char *filename);
extern char progdir[1024];
extern FString progdir;
void FixPathSeperator (char *path);
static void inline FixPathSeperator (FString &path) { path.ReplaceChars('\\', '/'); }

View file

@ -179,6 +179,7 @@ CVAR (Float, timelimit, 0.f, CVAR_SERVERINFO);
CVAR (Bool, queryiwad, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
CVAR (String, defaultiwad, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
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
@ -666,6 +667,11 @@ void D_Display ()
NoWipe = 10;
}
if (snd_drawoutput && GSnd != NULL)
{
GSnd->DrawWaveDebug(snd_drawoutput);
}
if (!wipe || NoWipe < 0)
{
NetUpdate (); // send out any new accumulation
@ -1855,7 +1861,7 @@ static const char *BaseFileSearch (const char *file, const char *ext, bool lookf
if (lookfirstinprogdir)
{
sprintf (wad, "%s%s%s", progdir, progdir[strlen (progdir) - 1] != '/' ? "/" : "", file);
sprintf (wad, "%s%s%s", progdir.GetChars(), progdir[progdir.Len() - 1] != '/' ? "/" : "", file);
if (FileExists (wad))
{
return wad;

View file

@ -181,7 +181,9 @@ void PClass::InsertIntoHash ()
}
else if (lexx == 0)
{ // This type has already been inserted
I_FatalError ("Tried to register class '%s' more than once.", TypeName.GetChars());
// ... but there is no need whatsoever to make it a fatal error!
Printf ("Tried to register class '%s' more than once.\n", TypeName.GetChars());
break;
}
else
{ // Type comes right here

View file

@ -132,6 +132,38 @@ long FileReader::Read (void *buffer, long len)
return len;
}
char *FileReader::Gets(char *strbuf, int len)
{
if (FilePos + len > StartPos + Length)
{
len = Length - FilePos + StartPos;
}
if (len <= 0) return 0;
char *p = fgets(strbuf, len, File);
FilePos += len;
return p;
}
char *FileReader::GetsFromBuffer(const char * bufptr, char *strbuf, int len)
{
if (len>Length-FilePos) len=Length-FilePos;
if (len <= 0) return NULL;
char *p = strbuf;
while (len > 1 && bufptr[FilePos] != 0)
{
if (bufptr[FilePos] != '\r')
{
*p++ = bufptr[FilePos];
len--;
if (bufptr[FilePos] == '\n') break;
}
FilePos++;
}
*p++=0;
return strbuf;
}
long FileReader::CalcFileLen() const
{
long endpos;
@ -265,3 +297,9 @@ long MemoryReader::Read (void *buffer, long len)
FilePos+=len;
return len;
}
char *MemoryReader::Gets(char *strbuf, int len)
{
return GetsFromBuffer(bufptr, strbuf, len);
}

View file

@ -17,6 +17,7 @@ public:
virtual long Tell () const;
virtual long Seek (long offset, int origin);
virtual long Read (void *buffer, long len);
virtual char *Gets(char *strbuf, int len);
long GetLength () const { return Length; }
// If you use the underlying FILE without going through this class,
@ -58,10 +59,13 @@ public:
return *this;
}
protected:
FileReader (const FileReader &other, long length);
FileReader ();
char *GetsFromBuffer(const char * bufptr, char *strbuf, int len);
FILE *File;
long Length;
long StartPos;
@ -145,6 +149,7 @@ public:
virtual long Tell () const;
virtual long Seek (long offset, int origin);
virtual long Read (void *buffer, long len);
virtual char *Gets(char *strbuf, int len);
protected:
const char * bufptr;

View file

@ -28,6 +28,7 @@ void A_Punch (AActor *actor)
angle_t angle;
int damage;
int pitch;
AActor *linetarget;
if (actor->player != NULL)
{
@ -47,7 +48,7 @@ void A_Punch (AActor *actor)
angle = actor->angle;
angle += pr_punch.Random2() << 18;
pitch = P_AimLineAttack (actor, angle, MELEERANGE);
pitch = P_AimLineAttack (actor, angle, MELEERANGE, &linetarget);
P_LineAttack (actor, angle, MELEERANGE, pitch, damage, NAME_Melee, NAME_BulletPuff, true);
// turn to face target
@ -89,8 +90,7 @@ void A_FirePistol (AActor *actor)
S_Sound (actor, CHAN_WEAPON, "weapons/pistol", 1, ATTN_NORM);
P_BulletSlope (actor);
P_GunShot (actor, accurate, PClass::FindClass(NAME_BulletPuff));
P_GunShot (actor, accurate, PClass::FindClass(NAME_BulletPuff), P_BulletSlope (actor));
}
//
@ -101,6 +101,7 @@ void A_Saw (AActor *actor)
angle_t angle;
int damage=0;
player_t *player;
AActor *linetarget;
int fullsound;
int hitsound;
@ -140,7 +141,7 @@ void A_Saw (AActor *actor)
// use meleerange + 1 so the puff doesn't skip the flash (i.e. plays all states)
P_LineAttack (actor, angle, MELEERANGE+1,
P_AimLineAttack (actor, angle, MELEERANGE+1), damage,
P_AimLineAttack (actor, angle, MELEERANGE+1, &linetarget), damage,
GetDefaultByType(pufftype)->DamageType, pufftype);
if (!linetarget)
@ -193,10 +194,10 @@ void A_FireShotgun (AActor *actor)
}
player->mo->PlayAttacking2 ();
P_BulletSlope (actor);
angle_t pitch = P_BulletSlope (actor);
for (i=0 ; i<7 ; i++)
P_GunShot (actor, false, PClass::FindClass(NAME_BulletPuff));
P_GunShot (actor, false, PClass::FindClass(NAME_BulletPuff), pitch);
}
//
@ -225,7 +226,7 @@ void A_FireShotgun2 (AActor *actor)
player->mo->PlayAttacking2 ();
P_BulletSlope (actor);
angle_t pitch = P_BulletSlope (actor);
for (i=0 ; i<20 ; i++)
{
@ -242,7 +243,7 @@ void A_FireShotgun2 (AActor *actor)
P_LineAttack (actor,
angle,
PLAYERMISSILERANGE,
bulletpitch + (pr_fireshotgun2.Random2() * 332063), damage,
pitch + (pr_fireshotgun2.Random2() * 332063), damage,
NAME_None, NAME_BulletPuff);
}
}
@ -347,8 +348,7 @@ void A_FireCGun (AActor *actor)
}
player->mo->PlayAttacking2 ();
P_BulletSlope (actor);
P_GunShot (actor, !player->refire, PClass::FindClass(NAME_BulletPuff));
P_GunShot (actor, !player->refire, PClass::FindClass(NAME_BulletPuff), P_BulletSlope (actor));
}
//
@ -496,6 +496,7 @@ void A_BFGSpray (AActor *mo)
const PClass *spraytype = NULL;
int numrays = 40;
int damagecnt = 15;
AActor *linetarget;
int index = CheckIndex (3, NULL);
if (index >= 0)
@ -523,7 +524,7 @@ void A_BFGSpray (AActor *mo)
an = mo->angle - ANG90/2 + ANG90/numrays*i;
// mo->target is the originator (player) of the missile
P_AimLineAttack (mo->target, an, 16*64*FRACUNIT, ANGLE_1*32);
P_AimLineAttack (mo->target, an, 16*64*FRACUNIT, &linetarget, ANGLE_1*32);
if (!linetarget)
continue;

View file

@ -397,12 +397,13 @@ void A_M_Saw (AActor *self)
{
angle_t angle;
int damage;
AActor *linetarget;
damage = 2 * (pr_m_saw()%10+1);
angle = self->angle + (pr_m_saw.Random2() << 18);
P_LineAttack (self, angle, MELEERANGE+1,
P_AimLineAttack (self, angle, MELEERANGE+1), damage,
P_AimLineAttack (self, angle, MELEERANGE+1, &linetarget), damage,
NAME_Melee, NAME_BulletPuff);
if (!linetarget)
@ -447,6 +448,7 @@ void A_M_Punch (AActor *self)
angle_t angle;
int damage;
int pitch;
AActor *linetarget;
if (self->target == NULL)
return;
@ -455,7 +457,7 @@ void A_M_Punch (AActor *self)
A_FaceTarget (self);
angle = self->angle + (pr_m_punch.Random2() << 18);
pitch = P_AimLineAttack (self, angle, MELEERANGE);
pitch = P_AimLineAttack (self, angle, MELEERANGE, &linetarget);
P_LineAttack (self, angle, MELEERANGE, pitch, damage, NAME_Melee, NAME_BulletPuff, true);
// turn to face target
@ -477,6 +479,7 @@ void A_M_BerserkPunch (AActor *self)
angle_t angle;
int damage;
int pitch;
AActor *linetarget;
if (self->target == NULL)
return;
@ -485,7 +488,7 @@ void A_M_BerserkPunch (AActor *self)
A_FaceTarget (self);
angle = self->angle + (pr_m_punch.Random2() << 18);
pitch = P_AimLineAttack (self, angle, MELEERANGE);
pitch = P_AimLineAttack (self, angle, MELEERANGE, &linetarget);
P_LineAttack (self, angle, MELEERANGE, pitch, damage, NAME_Melee, NAME_BulletPuff, true);
// turn to face target

View file

@ -403,6 +403,7 @@ void A_BeakAttackPL1 (AActor *actor)
int damage;
int slope;
player_t *player;
AActor *linetarget;
if (NULL == (player = actor->player))
{
@ -411,7 +412,7 @@ void A_BeakAttackPL1 (AActor *actor)
damage = 1 + (pr_beakatkpl1()&3);
angle = player->mo->angle;
slope = P_AimLineAttack (player->mo, angle, MELEERANGE);
slope = P_AimLineAttack (player->mo, angle, MELEERANGE, &linetarget);
P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, RUNTIME_CLASS(ABeakPuff), true);
if (linetarget)
{
@ -435,6 +436,7 @@ void A_BeakAttackPL2 (AActor *actor)
int damage;
int slope;
player_t *player;
AActor *linetarget;
if (NULL == (player = actor->player))
{
@ -443,7 +445,7 @@ void A_BeakAttackPL2 (AActor *actor)
damage = pr_beakatkpl2.HitDice (4);
angle = player->mo->angle;
slope = P_AimLineAttack (player->mo, angle, MELEERANGE);
slope = P_AimLineAttack (player->mo, angle, MELEERANGE, &linetarget);
P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, RUNTIME_CLASS(ABeakPuff), true);
if (linetarget)
{

View file

@ -36,8 +36,11 @@ bool AArtiTomeOfPower::Use (bool pickup)
{ // Attempt to undo chicken
if (!P_UndoPlayerMorph (Owner->player))
{ // Failed
if (!(Owner->player->MorphStyle & MORPH_FAILNOTELEFRAG))
{
P_DamageMobj (Owner, NULL, NULL, 1000000, NAME_Telefrag);
}
}
else
{ // Succeeded
S_Sound (Owner, CHAN_VOICE, "*evillaugh", 1, ATTN_IDLE);

View file

@ -201,6 +201,7 @@ void A_StaffAttackPL1 (AActor *actor)
int damage;
int slope;
player_t *player;
AActor *linetarget;
if (NULL == (player = actor->player))
{
@ -216,7 +217,7 @@ void A_StaffAttackPL1 (AActor *actor)
damage = 5+(pr_sap()&15);
angle = player->mo->angle;
angle += pr_sap.Random2() << 18;
slope = P_AimLineAttack (player->mo, angle, MELEERANGE);
slope = P_AimLineAttack (player->mo, angle, MELEERANGE, &linetarget);
P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, RUNTIME_CLASS(AStaffPuff), true);
if (linetarget)
{
@ -239,6 +240,7 @@ void A_StaffAttackPL2 (AActor *actor)
int damage;
int slope;
player_t *player;
AActor *linetarget;
if (NULL == (player = actor->player))
{
@ -255,7 +257,7 @@ void A_StaffAttackPL2 (AActor *actor)
damage = 18+(pr_sap2()&63);
angle = player->mo->angle;
angle += pr_sap2.Random2() << 18;
slope = P_AimLineAttack (player->mo, angle, MELEERANGE);
slope = P_AimLineAttack (player->mo, angle, MELEERANGE, &linetarget);
P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, RUNTIME_CLASS(AStaffPuff2), true);
if (linetarget)
{
@ -447,14 +449,14 @@ void A_FireGoldWandPL1 (AActor *actor)
if (!weapon->DepleteAmmo (weapon->bAltFire))
return;
}
P_BulletSlope(mo);
angle_t pitch = P_BulletSlope(mo);
damage = 7+(pr_fgw()&7);
angle = mo->angle;
if (player->refire)
{
angle += pr_fgw.Random2() << 18;
}
P_LineAttack (mo, angle, PLAYERMISSILERANGE, bulletpitch, damage, NAME_None, RUNTIME_CLASS(AGoldWandPuff1));
P_LineAttack (mo, angle, PLAYERMISSILERANGE, pitch, damage, NAME_None, RUNTIME_CLASS(AGoldWandPuff1));
S_Sound (player->mo, CHAN_WEAPON, "weapons/wandhit", 1, ATTN_NORM);
}
@ -485,16 +487,16 @@ void A_FireGoldWandPL2 (AActor *actor)
if (!weapon->DepleteAmmo (weapon->bAltFire))
return;
}
P_BulletSlope (mo);
angle_t pitch = P_BulletSlope(mo);
momz = FixedMul (GetDefault<AGoldWandFX2>()->Speed,
finetangent[FINEANGLES/4-((signed)bulletpitch>>ANGLETOFINESHIFT)]);
finetangent[FINEANGLES/4-((signed)pitch>>ANGLETOFINESHIFT)]);
P_SpawnMissileAngle (mo, RUNTIME_CLASS(AGoldWandFX2), mo->angle-(ANG45/8), momz);
P_SpawnMissileAngle (mo, RUNTIME_CLASS(AGoldWandFX2), mo->angle+(ANG45/8), momz);
angle = mo->angle-(ANG45/8);
for(i = 0; i < 5; i++)
{
damage = 1+(pr_fgw2()&7);
P_LineAttack (mo, angle, PLAYERMISSILERANGE, bulletpitch, damage, NAME_None, RUNTIME_CLASS(AGoldWandPuff2));
P_LineAttack (mo, angle, PLAYERMISSILERANGE, pitch, damage, NAME_None, RUNTIME_CLASS(AGoldWandPuff2));
angle += ((ANG45/8)*2)/4;
}
S_Sound (player->mo, CHAN_WEAPON, "weapons/wandhit", 1, ATTN_NORM);
@ -1339,6 +1341,7 @@ void A_FireMacePL2 (AActor *actor)
{
AActor *mo;
player_t *player;
AActor *linetarget;
if (NULL == (player = actor->player))
{
@ -1351,7 +1354,7 @@ void A_FireMacePL2 (AActor *actor)
if (!weapon->DepleteAmmo (weapon->bAltFire))
return;
}
mo = P_SpawnPlayerMissile (player->mo, RUNTIME_CLASS(AMaceFX4));
mo = P_SpawnPlayerMissile (player->mo, 0,0,0, RUNTIME_CLASS(AMaceFX4), 0, &linetarget);
if (mo)
{
mo->momx += player->mo->momx;
@ -1378,6 +1381,7 @@ void A_DeathBallImpact (AActor *ball)
AActor *target;
angle_t angle = 0;
bool newAngle;
AActor *linetarget;
if ((ball->z <= ball->floorz) && P_HitFloor (ball))
{ // Landed in some sort of liquid
@ -1424,7 +1428,7 @@ void A_DeathBallImpact (AActor *ball)
angle = 0;
for (i = 0; i < 16; i++)
{
P_AimLineAttack (ball, angle, 10*64*FRACUNIT);
P_AimLineAttack (ball, angle, 10*64*FRACUNIT, &linetarget);
if (linetarget && ball->target != linetarget)
{
ball->tracer = linetarget;
@ -1624,6 +1628,7 @@ void A_GauntletAttack (AActor *actor)
player_t *player;
const PClass *pufftype;
AInventory *power;
AActor *linetarget;
if (NULL == (player = actor->player))
{
@ -1654,7 +1659,7 @@ void A_GauntletAttack (AActor *actor)
angle += pr_gatk.Random2() << 18;
pufftype = RUNTIME_CLASS(AGauntletPuff1);
}
slope = P_AimLineAttack (player->mo, angle, dist);
slope = P_AimLineAttack (player->mo, angle, dist, &linetarget);
P_LineAttack (player->mo, angle, dist, slope, damage, NAME_Melee, pufftype);
if (!linetarget)
{
@ -1973,14 +1978,14 @@ void A_FireBlasterPL1 (AActor *actor)
if (!weapon->DepleteAmmo (weapon->bAltFire))
return;
}
P_BulletSlope(actor);
angle_t pitch = P_BulletSlope(actor);
damage = pr_fb1.HitDice (4);
angle = actor->angle;
if (player->refire)
{
angle += pr_fb1.Random2() << 18;
}
P_LineAttack (actor, angle, PLAYERMISSILERANGE, bulletpitch, damage, NAME_None, RUNTIME_CLASS(ABlasterPuff));
P_LineAttack (actor, angle, PLAYERMISSILERANGE, pitch, damage, NAME_None, RUNTIME_CLASS(ABlasterPuff));
S_Sound (actor, CHAN_WEAPON, "weapons/blastershoot", 1, ATTN_NORM);
}
@ -2392,6 +2397,8 @@ void A_FireSkullRodPL1 (AActor *actor)
void A_FireSkullRodPL2 (AActor *actor)
{
player_t *player;
AActor *MissileActor;
AActor *linetarget;
if (NULL == (player = actor->player))
{
@ -2403,7 +2410,7 @@ void A_FireSkullRodPL2 (AActor *actor)
if (!weapon->DepleteAmmo (weapon->bAltFire))
return;
}
P_SpawnPlayerMissile (player->mo, RUNTIME_CLASS(AHornRodFX2));
P_SpawnPlayerMissile (player->mo, 0,0,0, RUNTIME_CLASS(AHornRodFX2), 0, &linetarget, &MissileActor);
// Use MissileActor instead of the return value from
// P_SpawnPlayerMissile because we need to give info to the mobj
// even if it exploded immediately.

View file

@ -559,6 +559,7 @@ void SpawnSpiritTail (AActor *spirit)
void A_CHolyAttack (AActor *actor)
{
player_t *player;
AActor *linetarget;
if (NULL == (player = actor->player))
{
@ -570,7 +571,7 @@ void A_CHolyAttack (AActor *actor)
if (!weapon->DepleteAmmo (weapon->bAltFire))
return;
}
AActor * missile = P_SpawnPlayerMissile (actor, RUNTIME_CLASS(AHolyMissile));
AActor * missile = P_SpawnPlayerMissile (actor, 0,0,0, RUNTIME_CLASS(AHolyMissile), 0, &linetarget);
if (missile != NULL) missile->tracer = linetarget;
weapon->CHolyCount = 3;

View file

@ -11,7 +11,7 @@
#include "gstrings.h"
#include "a_hexenglobal.h"
extern void AdjustPlayerAngle (AActor *pmo);
extern void AdjustPlayerAngle (AActor *pmo, AActor *linetarget);
static FRandom pr_atk ("CMaceAttack");
@ -81,6 +81,7 @@ void A_CMaceAttack (AActor *actor)
int slope;
int i;
player_t *player;
AActor *linetarget;
if (NULL == (player = actor->player))
{
@ -91,21 +92,21 @@ void A_CMaceAttack (AActor *actor)
for (i = 0; i < 16; i++)
{
angle = player->mo->angle+i*(ANG45/16);
slope = P_AimLineAttack (player->mo, angle, 2*MELEERANGE);
slope = P_AimLineAttack (player->mo, angle, 2*MELEERANGE, &linetarget);
if (linetarget)
{
P_LineAttack (player->mo, angle, 2*MELEERANGE, slope, damage, NAME_Melee, RUNTIME_CLASS(AHammerPuff), true);
AdjustPlayerAngle (player->mo);
AdjustPlayerAngle (player->mo, linetarget);
// player->mo->angle = R_PointToAngle2(player->mo->x,
// player->mo->y, linetarget->x, linetarget->y);
goto macedone;
}
angle = player->mo->angle-i*(ANG45/16);
slope = P_AimLineAttack (player->mo, angle, 2*MELEERANGE);
slope = P_AimLineAttack (player->mo, angle, 2*MELEERANGE, &linetarget);
if (linetarget)
{
P_LineAttack (player->mo, angle, 2*MELEERANGE, slope, damage, NAME_Melee, RUNTIME_CLASS(AHammerPuff), true);
AdjustPlayerAngle (player->mo);
AdjustPlayerAngle (player->mo, linetarget);
// player->mo->angle = R_PointToAngle2(player->mo->x,
// player->mo->y, linetarget->x, linetarget->y);
goto macedone;
@ -115,7 +116,7 @@ void A_CMaceAttack (AActor *actor)
player->mo->special1 = 0;
angle = player->mo->angle;
slope = P_AimLineAttack (player->mo, angle, MELEERANGE);
slope = P_AimLineAttack (player->mo, angle, MELEERANGE, &linetarget);
P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, RUNTIME_CLASS(AHammerPuff));
macedone:
return;

View file

@ -188,6 +188,7 @@ void A_CStaffCheck (AActor *actor)
int slope;
int i;
player_t *player;
AActor *linetarget;
if (NULL == (player = actor->player))
{
@ -200,7 +201,7 @@ void A_CStaffCheck (AActor *actor)
for (i = 0; i < 3; i++)
{
angle = pmo->angle+i*(ANG45/16);
slope = P_AimLineAttack (pmo, angle, fixed_t(1.5*MELEERANGE));
slope = P_AimLineAttack (pmo, angle, fixed_t(1.5*MELEERANGE), &linetarget);
if (linetarget)
{
P_LineAttack (pmo, angle, fixed_t(1.5*MELEERANGE), slope, damage, NAME_Melee, RUNTIME_CLASS(ACStaffPuff));
@ -224,7 +225,7 @@ void A_CStaffCheck (AActor *actor)
break;
}
angle = pmo->angle-i*(ANG45/16);
slope = P_AimLineAttack (player->mo, angle, fixed_t(1.5*MELEERANGE));
slope = P_AimLineAttack (player->mo, angle, fixed_t(1.5*MELEERANGE), &linetarget);
if (linetarget)
{
P_LineAttack (pmo, angle, fixed_t(1.5*MELEERANGE), slope, damage, NAME_Melee, RUNTIME_CLASS(ACStaffPuff));

View file

@ -23,7 +23,7 @@ void A_FAxeCheckReadyG (AActor *actor);
void A_FAxeCheckUpG (AActor *actor);
void A_FAxeAttack (AActor *actor);
extern void AdjustPlayerAngle (AActor *pmo);
extern void AdjustPlayerAngle (AActor *pmo, AActor *linetarget);
EXTERN_CVAR (Int, cl_bloodtype)
@ -323,6 +323,7 @@ void A_FAxeAttack (AActor *actor)
player_t *player;
AWeapon *weapon;
const PClass *pufftype;
AActor *linetarget;
if (NULL == (player = actor->player))
{
@ -349,7 +350,7 @@ void A_FAxeAttack (AActor *actor)
for (i = 0; i < 16; i++)
{
angle = pmo->angle+i*(ANG45/16);
slope = P_AimLineAttack (pmo, angle, AXERANGE);
slope = P_AimLineAttack (pmo, angle, AXERANGE, &linetarget);
if (linetarget)
{
P_LineAttack (pmo, angle, AXERANGE, slope, damage, NAME_Melee, pufftype, true);
@ -357,12 +358,12 @@ void A_FAxeAttack (AActor *actor)
{
P_ThrustMobj (linetarget, angle, power);
}
AdjustPlayerAngle (pmo);
AdjustPlayerAngle (pmo, linetarget);
useMana++;
goto axedone;
}
angle = pmo->angle-i*(ANG45/16);
slope = P_AimLineAttack (pmo, angle, AXERANGE);
slope = P_AimLineAttack (pmo, angle, AXERANGE, &linetarget);
if (linetarget)
{
P_LineAttack (pmo, angle, AXERANGE, slope, damage, NAME_Melee, pufftype, true);
@ -370,7 +371,7 @@ void A_FAxeAttack (AActor *actor)
{
P_ThrustMobj (linetarget, angle, power);
}
AdjustPlayerAngle (pmo);
AdjustPlayerAngle (pmo, linetarget);
useMana++;
goto axedone;
}
@ -379,7 +380,7 @@ void A_FAxeAttack (AActor *actor)
pmo->special1 = 0;
angle = pmo->angle;
slope = P_AimLineAttack (pmo, angle, MELEERANGE);
slope = P_AimLineAttack (pmo, angle, MELEERANGE, &linetarget);
P_LineAttack (pmo, angle, MELEERANGE, slope, damage, NAME_Melee, pufftype, true);
axedone:

View file

@ -15,7 +15,7 @@ const fixed_t HAMMER_RANGE = MELEERANGE+MELEERANGE/2;
static FRandom pr_atk ("FHammerAtk");
extern void AdjustPlayerAngle (AActor *pmo);
extern void AdjustPlayerAngle (AActor *pmo, AActor *linetarget);
void A_FHammerAttack (AActor *actor);
void A_FHammerThrow (AActor *actor);
@ -179,6 +179,7 @@ void A_FHammerAttack (AActor *actor)
int slope;
int i;
player_t *player;
AActor *linetarget;
if (NULL == (player = actor->player))
{
@ -191,11 +192,11 @@ void A_FHammerAttack (AActor *actor)
for (i = 0; i < 16; i++)
{
angle = pmo->angle + i*(ANG45/32);
slope = P_AimLineAttack (pmo, angle, HAMMER_RANGE);
slope = P_AimLineAttack (pmo, angle, HAMMER_RANGE, &linetarget);
if (linetarget)
{
P_LineAttack (pmo, angle, HAMMER_RANGE, slope, damage, NAME_Melee, RUNTIME_CLASS(AHammerPuff), true);
AdjustPlayerAngle(pmo);
AdjustPlayerAngle(pmo, linetarget);
if (linetarget->flags3&MF3_ISMONSTER || linetarget->player)
{
P_ThrustMobj (linetarget, angle, power);
@ -204,11 +205,11 @@ void A_FHammerAttack (AActor *actor)
goto hammerdone;
}
angle = pmo->angle-i*(ANG45/32);
slope = P_AimLineAttack(pmo, angle, HAMMER_RANGE);
slope = P_AimLineAttack(pmo, angle, HAMMER_RANGE, &linetarget);
if(linetarget)
{
P_LineAttack(pmo, angle, HAMMER_RANGE, slope, damage, NAME_Melee, RUNTIME_CLASS(AHammerPuff), true);
AdjustPlayerAngle(pmo);
AdjustPlayerAngle(pmo, linetarget);
if (linetarget->flags3&MF3_ISMONSTER || linetarget->player)
{
P_ThrustMobj(linetarget, angle, power);
@ -219,7 +220,7 @@ void A_FHammerAttack (AActor *actor)
}
// didn't find any targets in meleerange, so set to throw out a hammer
angle = pmo->angle;
slope = P_AimLineAttack (pmo, angle, HAMMER_RANGE);
slope = P_AimLineAttack (pmo, angle, HAMMER_RANGE, &linetarget);
if (P_LineAttack (pmo, angle, HAMMER_RANGE, slope, damage, NAME_Melee, RUNTIME_CLASS(AHammerPuff), true) != NULL)
{
pmo->special1 = false;

View file

@ -109,7 +109,7 @@ static FRandom pr_fpatk ("FPunchAttack");
#define MAX_ANGLE_ADJUST (5*ANGLE_1)
void AdjustPlayerAngle (AActor *pmo)
void AdjustPlayerAngle (AActor *pmo, AActor *linetarget)
{
angle_t angle;
int difference;
@ -235,6 +235,7 @@ void A_FPunchAttack (AActor *actor)
int i;
player_t *player;
const PClass *pufftype;
AActor *linetarget;
if (NULL == (player = actor->player))
{
@ -248,7 +249,7 @@ void A_FPunchAttack (AActor *actor)
for (i = 0; i < 16; i++)
{
angle = pmo->angle + i*(ANG45/16);
slope = P_AimLineAttack (pmo, angle, 2*MELEERANGE);
slope = P_AimLineAttack (pmo, angle, 2*MELEERANGE, &linetarget);
if (linetarget)
{
pmo->special1++;
@ -263,11 +264,11 @@ void A_FPunchAttack (AActor *actor)
{
P_ThrustMobj (linetarget, angle, power);
}
AdjustPlayerAngle (pmo);
AdjustPlayerAngle (pmo, linetarget);
goto punchdone;
}
angle = pmo->angle-i * (ANG45/16);
slope = P_AimLineAttack (pmo, angle, 2*MELEERANGE);
slope = P_AimLineAttack (pmo, angle, 2*MELEERANGE, &linetarget);
if (linetarget)
{
pmo->special1++;
@ -282,7 +283,7 @@ void A_FPunchAttack (AActor *actor)
{
P_ThrustMobj (linetarget, angle, power);
}
AdjustPlayerAngle (pmo);
AdjustPlayerAngle (pmo, linetarget);
goto punchdone;
}
}
@ -290,7 +291,7 @@ void A_FPunchAttack (AActor *actor)
pmo->special1 = 0;
angle = pmo->angle;
slope = P_AimLineAttack (pmo, angle, MELEERANGE);
slope = P_AimLineAttack (pmo, angle, MELEERANGE, &linetarget);
P_LineAttack (pmo, angle, MELEERANGE, slope, damage, NAME_Melee, pufftype, true);
punchdone:

View file

@ -159,6 +159,7 @@ void A_FireConePL1 (AActor *actor)
AActor *mo;
bool conedone=false;
player_t *player;
AActor *linetarget;
if (NULL == (player = actor->player))
{
@ -177,7 +178,7 @@ void A_FireConePL1 (AActor *actor)
for (i = 0; i < 16; i++)
{
angle = actor->angle+i*(ANG45/16);
slope = P_AimLineAttack (actor, angle, MELEERANGE);
slope = P_AimLineAttack (actor, angle, MELEERANGE, &linetarget);
if (linetarget)
{
P_DamageMobj (linetarget, actor, actor, damage, NAME_Ice);

View file

@ -387,9 +387,10 @@ void A_MStaffAttack2 (AActor *actor)
void MStaffSpawn (AActor *pmo, angle_t angle)
{
AActor *mo;
AActor *linetarget;
mo = P_SpawnPlayerMissile (pmo, 0, 0, 8*FRACUNIT,
RUNTIME_CLASS(AMageStaffFX2), angle);
RUNTIME_CLASS(AMageStaffFX2), angle, &linetarget);
if (mo)
{
mo->target = pmo;
@ -407,6 +408,7 @@ void A_MStaffAttack (AActor *actor)
{
angle_t angle;
player_t *player;
AActor *linetarget;
if (NULL == (player = actor->player))
{
@ -422,7 +424,7 @@ void A_MStaffAttack (AActor *actor)
angle = actor->angle;
// [RH] Let's try and actually track what the player aimed at
P_AimLineAttack (actor, angle, PLAYERMISSILERANGE, ANGLE_1*32);
P_AimLineAttack (actor, angle, PLAYERMISSILERANGE, &linetarget, ANGLE_1*32);
if (linetarget == NULL)
{
BlockCheckLine.x = actor->x;

View file

@ -124,7 +124,7 @@ void AMageWandMissile::Tick ()
PrevZ = z;
// [RH] Ripping is a little different than it was in Hexen
DoRipping = true;
FCheckPosition tm(!!(flags2 & MF2_RIP));
// Handle movement
if (momx || momy || (z != floorz) || momz)
@ -137,11 +137,10 @@ void AMageWandMissile::Tick ()
{
if (changexy)
{
LastRipped = NULL; // [RH] Do rip damage each step, like Hexen
if (!P_TryMove (this, x+xfrac,y+yfrac, true))
tm.LastRipped = NULL; // [RH] Do rip damage each step, like Hexen
if (!P_TryMove (this, x+xfrac,y+yfrac, true, false, tm))
{ // Blocked move
P_ExplodeMissile (this, BlockingLine, BlockingMobj);
DoRipping = false;
return;
}
}
@ -151,14 +150,12 @@ void AMageWandMissile::Tick ()
z = floorz;
P_HitFloor (this);
P_ExplodeMissile (this, NULL, NULL);
DoRipping = false;
return;
}
if (z+height > ceilingz)
{ // Hit the ceiling
z = ceilingz-height;
P_ExplodeMissile (this, NULL, NULL);
DoRipping = false;
return;
}
if (changexy)
@ -175,7 +172,7 @@ void AMageWandMissile::Tick ()
}
}
}
DoRipping = false;
// Advance the state
if (tics != -1)
{

View file

@ -16,7 +16,7 @@ static FRandom pr_snoutattack ("SnoutAttack");
static FRandom pr_pigattack ("PigAttack");
static FRandom pr_pigplayerthink ("PigPlayerThink");
extern void AdjustPlayerAngle (AActor *);
extern void AdjustPlayerAngle (AActor *, AActor *);
void A_SnoutAttack (AActor *actor);
@ -260,6 +260,7 @@ void A_SnoutAttack (AActor *actor)
int slope;
player_t *player;
AActor *puff;
AActor *linetarget;
if (NULL == (player = actor->player))
{
@ -268,12 +269,12 @@ void A_SnoutAttack (AActor *actor)
damage = 3+(pr_snoutattack()&3);
angle = player->mo->angle;
slope = P_AimLineAttack(player->mo, angle, MELEERANGE);
slope = P_AimLineAttack(player->mo, angle, MELEERANGE, &linetarget);
puff = P_LineAttack(player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, RUNTIME_CLASS(ASnoutPuff), true);
S_Sound(player->mo, CHAN_VOICE, "PigActive", 1, ATTN_NORM);
if(linetarget)
{
AdjustPlayerAngle(player->mo);
AdjustPlayerAngle(player->mo, linetarget);
if(puff != NULL)
{ // Bit something
S_Sound(player->mo, CHAN_VOICE, "PigAttack", 1, ATTN_NORM);

View file

@ -59,13 +59,15 @@ bool AArtiTeleport::Use (bool pickup)
destAngle = ANG45 * (playerstarts[Owner->player - players].angle/45);
}
P_Teleport (Owner, destX, destY, ONFLOORZ, destAngle, true, true, false);
bool canlaugh = true;
if (Owner->player->morphTics && (Owner->player->MorphStyle & MORPH_UNDOBYCHAOSDEVICE))
{ // Teleporting away will undo any morph effects (pig)
P_UndoPlayerMorph (Owner->player);
if (!P_UndoPlayerMorph (Owner->player) && (Owner->player->MorphStyle & MORPH_FAILNOLAUGH))
{
canlaugh = false;
}
// The game check is not necessary because only Heretic defines *evillaugh by default
// However if it is there no other game can use it if it wants to.
//if (gameinfo.gametype == GAME_Heretic)
}
if (canlaugh)
{ // Full volume laugh
S_Sound (Owner, CHAN_VOICE, "*evillaugh", 1, ATTN_NONE);
}

View file

@ -1755,12 +1755,16 @@ void APowerMorph::InitEffect( )
const PClass *morph_flash = PClass::FindClass (MorphFlash);
const PClass *unmorph_flash = PClass::FindClass (UnMorphFlash);
const PClass *player_class = PClass::FindClass (PlayerClass);
if (P_MorphPlayer(realplayer, player_class, -1/*INDEFINITELY*/, MorphStyle, morph_flash, unmorph_flash))
if (P_MorphPlayer(realplayer, realplayer, player_class, -1/*INDEFINITELY*/, MorphStyle, morph_flash, unmorph_flash))
{
Owner = realplayer->mo; // Replace the new owner in our owner; safe because we are not attached to anything yet
ItemFlags |= IF_CREATECOPYMOVED; // Let the caller know the "real" owner has changed (to the morphed actor)
player = realplayer; // Store the player identity (morphing clears the unmorphed actor's "player" field)
}
else // morph failed - give the caller an opportunity to fail the pickup completely
{
ItemFlags |= IF_INITEFFECTFAILED; // Let the caller know that the activation failed (can fail the pickup if appropriate)
}
}
}

View file

@ -21,7 +21,7 @@ static FRandom pr_morphmonst ("MorphMonster");
//
//---------------------------------------------------------------------------
bool P_MorphPlayer (player_t *p, const PClass *spawntype, int duration, int style, const PClass *enter_flash, const PClass *exit_flash)
bool P_MorphPlayer (player_t *activator, player_t *p, const PClass *spawntype, int duration, int style, const PClass *enter_flash, const PClass *exit_flash)
{
AInventory *item;
APlayerPawn *morphed;
@ -36,8 +36,8 @@ bool P_MorphPlayer (player_t *p, const PClass *spawntype, int duration, int styl
{
return false;
}
if (p->mo->flags2 & MF2_INVULNERABLE)
{ // Immune when invulnerable
if ((p->mo->flags2 & MF2_INVULNERABLE) && ((p != activator) || (!(style & MORPH_WHENINVULNERABLE))))
{ // Immune when invulnerable unless this is a power we activated
return false;
}
if (p->morphTics)
@ -402,7 +402,7 @@ int AMorphProjectile::DoSpecialDamage (AActor *target, int damage)
if (target->player)
{
const PClass *player_class = PClass::FindClass (PlayerClass);
P_MorphPlayer (target->player, player_class, Duration, MorphStyle, morph_flash, unmorph_flash);
P_MorphPlayer (NULL, target->player, player_class, Duration, MorphStyle, morph_flash, unmorph_flash);
}
else
{

View file

@ -7,30 +7,28 @@
// Morph style states how morphing affects health and
// other effects in the game; only valid for players.
// Default should be the old Heretic/HeXen behaviour,
// so the (int) value of MORPH_RAVEN *must* be zero.
// so (int) value of MORPH_OLDEFFECTS *must* be zero.
enum
{
MORPH_OLDEFFECTS = 0, // Default to old Heretic/HeXen behaviour unless flags given
MORPH_ADDSTAMINA = 1, // Power instead of curse (add stamina instead of limiting to health)
MORPH_FULLHEALTH = 2, // New health semantics (!POWER => MaxHealth of animal, POWER => Normal health behaviour)
MORPH_UNDOBYTOMEOFPOWER = 4,
MORPH_UNDOBYCHAOSDEVICE = 8,
MORPH_OLDEFFECTS = 0x00000000, // Default to old Heretic/HeXen behaviour unless flags given
MORPH_ADDSTAMINA = 0x00000001, // Power instead of curse (add stamina instead of limiting to health)
MORPH_FULLHEALTH = 0x00000002, // New health semantics (!POWER => MaxHealth of animal, POWER => Normal health behaviour)
MORPH_UNDOBYTOMEOFPOWER = 0x00000004, // Player unmorphs upon activating a Tome of Power
MORPH_UNDOBYCHAOSDEVICE = 0x00000008, // Player unmorphs upon activating a Chaos Device
MORPH_FAILNOTELEFRAG = 0x00000010, // Player stays morphed if unmorph by Tome of Power fails
MORPH_FAILNOLAUGH = 0x00000020, // Player doesn't laugh if unmorph by Chaos Device fails
MORPH_WHENINVULNERABLE = 0x00000040 // Player can morph when invulnerable but ONLY if doing it to themselves
};
struct PClass;
class AActor;
class player_s;
//
// A_MORPH
//
bool P_MorphPlayer (player_s *player, const PClass *morphclass, int duration = 0, int style = 0,
bool P_MorphPlayer (player_s *activator, player_s *player, const PClass *morphclass, int duration = 0, int style = 0,
const PClass *enter_flash = NULL, const PClass *exit_flash = NULL);
bool P_UndoPlayerMorph (player_s *player, bool force = false);
bool P_MorphMonster (AActor *actor, const PClass *morphclass, int duration = 0, int style = 0,
const PClass *enter_flash = NULL, const PClass *exit_flash = NULL);
bool P_UpdateMorphedMonster (AActor *actor);
#endif //__A_MORPH__

View file

@ -1227,6 +1227,14 @@ bool AInventory::TryPickup (AActor *toucher)
{
return false;
}
// Some powerups cannot activate absolutely, for
// example, PowerMorph; fail the pickup if so.
if (copy->ItemFlags & IF_INITEFFECTFAILED)
{
if (copy != this) copy->Destroy();
else ItemFlags &= ~IF_INITEFFECTFAILED;
return false;
}
// Handle owner-changing powerups
if (copy->ItemFlags & IF_CREATECOPYMOVED)
{

View file

@ -96,7 +96,8 @@ enum
IF_BIGPOWERUP = 1<<12, // Affected by RESPAWN_SUPER dmflag
IF_KEEPDEPLETED = 1<<13, // Items with this flag are retained even when they run out.
IF_IGNORESKILL = 1<<14, // Ignores any skill related multiplicators when giving this item.
IF_CREATECOPYMOVED = 1<<15 // CreateCopy changed the owner (copy's Owner field holds new owner).
IF_CREATECOPYMOVED = 1<<15, // CreateCopy changed the owner (copy's Owner field holds new owner).
IF_INITEFFECTFAILED = 1<<16 // CreateCopy tried to activate a powerup and activation failed (can happen with PowerMorph)
};
struct vissprite_t;

View file

@ -6,7 +6,7 @@
#include "actor.h"
class FDecalTemplate;
struct vertex_s;
struct vertex_t;
struct side_t;
struct F3DFloor;
@ -52,7 +52,7 @@ protected:
void CalcFracPos (side_t *wall, fixed_t x, fixed_t y);
void Remove ();
static void SpreadLeft (fixed_t r, vertex_s *v1, side_t *feelwall, F3DFloor * ffloor = NULL);
static void SpreadLeft (fixed_t r, vertex_t *v1, side_t *feelwall, F3DFloor * ffloor = NULL);
static void SpreadRight (fixed_t r, side_t *feelwall, fixed_t wallsize, F3DFloor * ffloor = NULL);
};

View file

@ -199,6 +199,7 @@ void A_JabDagger (AActor *actor)
int damage;
int pitch;
int power;
AActor *linetarget;
power = MIN(10, actor->player->stamina / 10);
damage = (pr_jabdagger() % (power + 8)) * (power + 2);
@ -209,7 +210,7 @@ void A_JabDagger (AActor *actor)
}
angle = actor->angle + (pr_jabdagger.Random2() << 18);
pitch = P_AimLineAttack (actor, angle, 80*FRACUNIT);
pitch = P_AimLineAttack (actor, angle, 80*FRACUNIT, &linetarget);
P_LineAttack (actor, angle, 80*FRACUNIT, pitch, damage, NAME_Melee, RUNTIME_CLASS(AStrifeSpark), true);
// turn to face target
@ -675,7 +676,7 @@ bool AAssaultGun::HandlePickup (AInventory *item)
//
//============================================================================
void P_StrifeGunShot (AActor *mo, bool accurate)
void P_StrifeGunShot (AActor *mo, bool accurate, angle_t pitch)
{
angle_t angle;
int damage;
@ -688,7 +689,7 @@ void P_StrifeGunShot (AActor *mo, bool accurate)
angle += pr_sgunshot.Random2() << (20 - mo->player->accuracy * 5 / 100);
}
P_LineAttack (mo, angle, PLAYERMISSILERANGE, bulletpitch, damage, NAME_None, RUNTIME_CLASS(AStrifePuff));
P_LineAttack (mo, angle, PLAYERMISSILERANGE, pitch, damage, NAME_None, RUNTIME_CLASS(AStrifePuff));
}
//============================================================================
@ -719,8 +720,7 @@ void A_FireAssaultGun (AActor *self)
accurate = true;
}
P_BulletSlope (self);
P_StrifeGunShot (self, accurate);
P_StrifeGunShot (self, accurate, P_BulletSlope (self));
}
// Standing variant of the assault gun --------------------------------------
@ -1303,13 +1303,13 @@ void A_FireMauler1 (AActor *self)
S_Sound (self, CHAN_WEAPON, "weapons/mauler1", 1, ATTN_NORM);
P_BulletSlope (self);
int bpitch = P_BulletSlope (self);
for (int i = 0; i < 20; ++i)
{
int damage = 5 * (pr_mauler1() % 3 + 1);
angle_t angle = self->angle + (pr_mauler1.Random2() << 19);
int pitch = bulletpitch + (pr_mauler1.Random2() * 332063);
int pitch = bpitch + (pr_mauler1.Random2() * 332063);
// Strife used a range of 2112 units for the mauler to signal that
// it should use a different puff. ZDoom's default range is longer
@ -2185,6 +2185,7 @@ void A_FireSigil1 (AActor *actor)
{
AActor *spot;
player_t *player = actor->player;
AActor *linetarget;
if (player == NULL || player->ReadyWeapon == NULL)
return;
@ -2192,7 +2193,7 @@ void A_FireSigil1 (AActor *actor)
P_DamageMobj (actor, actor, NULL, 1*4, 0, DMG_NO_ARMOR);
S_Sound (actor, CHAN_WEAPON, "weapons/sigilcharge", 1, ATTN_NORM);
P_BulletSlope (actor);
P_BulletSlope (actor, &linetarget);
if (linetarget != NULL)
{
spot = Spawn<ASpectralLightningSpot> (linetarget->x, linetarget->y, ONFLOORZ, ALLOW_REPLACE);
@ -2283,6 +2284,7 @@ void A_FireSigil4 (AActor *actor)
{
AActor *spot;
player_t *player = actor->player;
AActor *linetarget;
if (player == NULL || player->ReadyWeapon == NULL)
return;
@ -2290,10 +2292,10 @@ void A_FireSigil4 (AActor *actor)
P_DamageMobj (actor, actor, NULL, 4*4, 0, DMG_NO_ARMOR);
S_Sound (actor, CHAN_WEAPON, "weapons/sigilcharge", 1, ATTN_NORM);
P_BulletSlope (actor);
P_BulletSlope (actor, &linetarget);
if (linetarget != NULL)
{
spot = P_SpawnPlayerMissile (actor, RUNTIME_CLASS(ASpectralLightningBigV1));
spot = P_SpawnPlayerMissile (actor, 0,0,0, RUNTIME_CLASS(ASpectralLightningBigV1), 0, &linetarget);
if (spot != NULL)
{
spot->tracer = linetarget;

View file

@ -71,7 +71,6 @@ EXTERN_CVAR (Color, am_wallcolor)
EXTERN_CVAR (Color, am_fdwallcolor)
EXTERN_CVAR (Color, am_cdwallcolor)
EXTERN_CVAR (Float, spc_amp)
EXTERN_CVAR (Bool, snd_midiprecache)
FString WeaponSection;
@ -306,7 +305,11 @@ void FGameConfigFile::DoGlobalSetup ()
}
if (last < 207)
{ // Now that snd_midiprecache works again, you probably don't want it on.
snd_midiprecache = false;
FBaseCVar *precache = FindCVar ("snd_midiprecache", NULL);
if (precache != NULL)
{
precache->ResetToDefault();
}
}
}
}

View file

@ -6,7 +6,7 @@
#include "tarray.h"
#include "gl_values.h"
typedef struct vertex_s vertex_t;
struct vertex_t;
extern DWORD gl_boomcolormap;
extern DWORD gl_fixedcolormap;
class FGLTexture;

View file

@ -440,14 +440,14 @@ const char *cht_Morph (player_t *player, const PClass *morphclass, bool quickund
{
if (P_UndoPlayerMorph (player))
{
if (!quickundo && oldclass != morphclass && P_MorphPlayer (player, morphclass, 0, style))
if (!quickundo && oldclass != morphclass && P_MorphPlayer (player, player, morphclass, 0, style))
{
return "You feel even stranger.";
}
return "You feel like yourself again.";
}
}
else if (P_MorphPlayer (player, morphclass, 0, style))
else if (P_MorphPlayer (player, player, morphclass, 0, style))
{
return "You feel strange...";
}

View file

@ -349,11 +349,13 @@ bool AActor::SuggestMissileAttack (fixed_t dist)
bool P_HitFriend(AActor * self)
{
AActor *linetarget;
if (self->flags&MF_FRIENDLY && self->target != NULL)
{
angle_t angle = R_PointToAngle2 (self->x, self->y, self->target->x, self->target->y);
fixed_t dist = P_AproxDistance (self->x-self->target->x, self->y-self->target->y);
P_AimLineAttack (self, angle, dist, 0, true);
P_AimLineAttack (self, angle, dist, &linetarget, 0, true);
if (linetarget != NULL && linetarget != self->target)
{
return self->IsFriend (linetarget);

View file

@ -86,7 +86,6 @@ void P_UnPredictPlayer ();
#define FLOATRANDZ (FIXED_MAX-1)
extern fixed_t FloatBobOffsets[64];
extern AActor *MissileActor;
APlayerPawn *P_SpawnPlayer (mapthing2_t* mthing, bool tempplayer=false);
@ -120,7 +119,8 @@ AActor *P_SpawnMissileZAimed (AActor *source, fixed_t z, AActor *dest, const PCl
AActor *P_SpawnPlayerMissile (AActor* source, const PClass *type);
AActor *P_SpawnPlayerMissile (AActor *source, const PClass *type, angle_t angle);
AActor *P_SpawnPlayerMissile (AActor *source, fixed_t x, fixed_t y, fixed_t z, const PClass *type, angle_t angle);
AActor *P_SpawnPlayerMissile (AActor *source, fixed_t x, fixed_t y, fixed_t z, const PClass *type, angle_t angle,
AActor **pLineTarget = NULL, AActor **MissileActor = NULL);
void P_CheckFakeFloorTriggers (AActor *mo, fixed_t oldz, bool oldz_has_viewheight=false);
@ -167,8 +167,6 @@ typedef struct
} d;
} intercept_t;
extern TArray<intercept_t> intercepts;
typedef bool (*traverser_t) (intercept_t *in);
fixed_t P_AproxDistance (fixed_t dx, fixed_t dy);
@ -345,6 +343,17 @@ struct FCheckPosition
bool touchmidtex;
bool floatok;
line_t *ceilingline;
AActor *stepthing;
// [RH] These are used by PIT_CheckThing and P_XYMovement to apply
// ripping damage once per tic instead of once per move.
bool DoRipping;
AActor *LastRipped;
FCheckPosition(bool rip=false)
{
DoRipping = rip;
LastRipped = NULL;
}
};
@ -355,13 +364,9 @@ extern msecnode_t *sector_list; // phares 3/16/98
extern TArray<line_t *> spechit;
// [RH] These are used by PIT_CheckThing and P_XYMovement to apply
// ripping damage once per tic instead of once per move.
extern bool DoRipping;
extern AActor *LastRipped;
bool P_TestMobjLocation (AActor *mobj);
bool P_TestMobjZ (AActor *mobj, bool quick=true);
bool P_TestMobjZ (AActor *mobj, bool quick=true, AActor **pOnmobj = NULL);
bool P_CheckPosition (AActor *thing, fixed_t x, fixed_t y, FCheckPosition &tm);
bool P_CheckPosition (AActor *thing, fixed_t x, fixed_t y);
AActor *P_CheckOnmobj (AActor *thing);
@ -380,9 +385,7 @@ void P_FindFloorCeiling (AActor *actor);
bool P_ChangeSector (sector_t* sector, int crunch, int amt, int floorOrCeil, bool isreset);
extern AActor* linetarget; // who got hit (or NULL)
fixed_t P_AimLineAttack (AActor *t1, angle_t angle, fixed_t distance, fixed_t vrange=0, bool forcenosmart=false);
fixed_t P_AimLineAttack (AActor *t1, angle_t angle, fixed_t distance, AActor **pLineTarget = NULL, fixed_t vrange=0, bool forcenosmart=false);
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);

View file

@ -77,14 +77,9 @@ static FRandom pr_crunch ("DoCrunch");
// but don't process them until the move is proven valid
TArray<line_t *> spechit;
AActor *onmobj; // generic global onmobj...used for landing on pods/players
// Temporary holder for thing_sectorlist threads
msecnode_t* sector_list = NULL; // phares 3/16/98
bool DoRipping;
AActor *LastRipped;
//==========================================================================
//
// PIT_FindFloorCeiling
@ -672,8 +667,6 @@ bool PIT_CheckLine (line_t *ld, const FBoundingBox &box, FCheckPosition &tm)
//
//==========================================================================
static AActor *stepthing;
bool PIT_CheckThing (AActor *thing, FCheckPosition &tm)
{
fixed_t topz;
@ -704,7 +697,7 @@ bool PIT_CheckThing (AActor *thing, FCheckPosition &tm)
// if (abs(thing->x - tmx) <= thing->radius &&
// abs(thing->y - tmy) <= thing->radius)
{
stepthing = thing;
tm.stepthing = thing;
tm.floorz = topz;
}
}
@ -871,11 +864,11 @@ bool PIT_CheckThing (AActor *thing, FCheckPosition &tm)
{
return true;
}
if (DoRipping && !(thing->flags5 & MF5_DONTRIP))
if (tm.DoRipping && !(thing->flags5 & MF5_DONTRIP))
{
if (LastRipped != thing)
if (tm.LastRipped != thing)
{
LastRipped = thing;
tm.LastRipped = thing;
if (!(thing->flags & MF_NOBLOOD) &&
!(thing->flags2 & MF2_REFLECTIVE) &&
!(tm.thing->flags3 & MF3_BLOODLESSIMPACT) &&
@ -1102,7 +1095,7 @@ bool P_CheckPosition (AActor *thing, fixed_t x, fixed_t y, FCheckPosition &tm)
thing->height = realheight + thing->MaxStepHeight;
}
stepthing = NULL;
tm.stepthing = NULL;
FRadiusThingsIterator it2(x, y, thing->radius);
AActor *th;
@ -1185,7 +1178,7 @@ bool P_CheckPosition (AActor *thing, fixed_t x, fixed_t y, FCheckPosition &tm)
if (tm.ceilingz - tm.floorz < thing->height)
return false;
if (stepthing != NULL || tm.touchmidtex)
if (tm.stepthing != NULL || tm.touchmidtex)
{
tm.dropoffz = thingdropoffz;
}
@ -1210,10 +1203,11 @@ AActor *P_CheckOnmobj (AActor *thing)
{
fixed_t oldz;
bool good;
AActor *onmobj;
oldz = thing->z;
P_FakeZMovement (thing);
good = P_TestMobjZ (thing, false);
good = P_TestMobjZ (thing, false, &onmobj);
thing->z = oldz;
return good ? NULL : onmobj;
@ -1225,11 +1219,14 @@ AActor *P_CheckOnmobj (AActor *thing)
//
//=============================================================================
bool P_TestMobjZ (AActor *actor, bool quick)
bool P_TestMobjZ (AActor *actor, bool quick, AActor **pOnmobj)
{
onmobj = NULL;
AActor *onmobj = NULL;
if (actor->flags & MF_NOCLIP)
{
if (pOnmobj) *pOnmobj = NULL;
return true;
}
FRadiusThingsIterator it(actor->x, actor->y, actor->radius);
AActor *thing;
@ -1268,7 +1265,7 @@ bool P_TestMobjZ (AActor *actor, bool quick)
if (quick) break;
}
if (pOnmobj) *pOnmobj = onmobj;
return onmobj == NULL;
}
@ -2401,7 +2398,6 @@ bool P_BounceWall (AActor *mo)
// Aiming
//
//============================================================================
AActor* linetarget; // who got hit (or NULL)
struct aim_t
{
@ -2411,6 +2407,7 @@ struct aim_t
AActor* shootthing;
fixed_t toppitch, bottompitch;
AActor * linetarget;
AActor * thing_friend, * thing_other;
angle_t pitch_friend, pitch_other;
bool notsmart;
@ -2425,8 +2422,6 @@ struct aim_t
};
aim_t aim;
//============================================================================
//
// AimTraverse3DFloors
@ -2583,7 +2578,7 @@ void aim_t::AimTraverse (fixed_t startx, fixed_t starty, fixed_t endx, fixed_t e
return; // stop
if (!AimTraverse3DFloors(it.Trace(), in)) return;
continue;
continue; // shot continues
}
// shoot a thing
@ -2712,7 +2707,7 @@ 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, fixed_t vrange, bool forcenosmart)
fixed_t P_AimLineAttack (AActor *t1, angle_t angle, fixed_t distance, AActor **pLineTarget, fixed_t vrange, bool forcenosmart)
{
fixed_t x2;
fixed_t y2;
@ -2754,7 +2749,7 @@ fixed_t P_AimLineAttack (AActor *t1, angle_t angle, fixed_t distance, fixed_t vr
aim.notsmart = forcenosmart;
aim.attackrange = distance;
linetarget = NULL;
aim.linetarget = NULL;
// for smart aiming
aim.thing_friend=aim.thing_other=NULL;
@ -2779,20 +2774,21 @@ fixed_t P_AimLineAttack (AActor *t1, angle_t angle, fixed_t distance, fixed_t vr
aim.AimTraverse (t1->x, t1->y, x2, y2);
if (!linetarget)
if (!aim.linetarget)
{
if (aim.thing_other)
{
linetarget=aim.thing_other;
aim.linetarget=aim.thing_other;
aim.aimpitch=aim.pitch_other;
}
else if (aim.thing_friend)
{
linetarget=aim.thing_friend;
aim.linetarget=aim.thing_friend;
aim.aimpitch=aim.pitch_friend;
}
}
return linetarget ? aim.aimpitch : t1->pitch;
if (pLineTarget) *pLineTarget = aim.linetarget;
return aim.linetarget ? aim.aimpitch : t1->pitch;
}
@ -4306,7 +4302,8 @@ void PIT_CeilingRaise (AActor *thing, FChangePosition *cpos)
}
else if ((thing->flags2 & MF2_PASSMOBJ) && !isgood && thing->z + thing->height < thing->ceilingz)
{
if (!P_TestMobjZ (thing) && onmobj->z <= thing->z)
AActor *onmobj;
if (!P_TestMobjZ (thing, true, &onmobj) && onmobj->z <= thing->z)
{
thing->z = MIN (thing->ceilingz - thing->height,
onmobj->z + onmobj->height);

View file

@ -1535,11 +1535,7 @@ void P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly)
// passes through more than one actor this tic, each one takes damage
// and not just the first one.
if (mo->flags2 & MF2_RIP)
{
DoRipping = true;
LastRipped = NULL;
}
FCheckPosition tm(!!(mo->flags2 & MF2_RIP));
do
{
@ -1551,7 +1547,6 @@ void 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
FCheckPosition tm;
if (!P_TryMove (mo, ptryx, ptryy, true, walkplane, tm))
{
// blocked move
@ -1658,13 +1653,11 @@ void P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly)
{
S_SoundID (mo, CHAN_VOICE, mo->SeeSound, 1, ATTN_IDLE);
}
DoRipping = false;
return;
}
else
{ // Struck a player/creature
P_ExplodeMissile (mo, NULL, BlockingMobj);
DoRipping = false;
return;
}
}
@ -1678,7 +1671,6 @@ void P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly)
{
S_SoundID (mo, CHAN_VOICE, mo->SeeSound, 1, ATTN_IDLE);
}
DoRipping = false;
return;
}
}
@ -1706,7 +1698,6 @@ void P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly)
mo->tracer = mo->target;
}
mo->target = BlockingMobj;
DoRipping = false;
return;
}
explode:
@ -1720,18 +1711,15 @@ explode:
// Hack to prevent missiles exploding against the sky.
// Does not handle sky floors.
mo->Destroy ();
DoRipping = false;
return;
}
// [RH] Don't explode on horizon lines.
if (mo->BlockingLine != NULL && mo->BlockingLine->special == Line_Horizon)
{
mo->Destroy ();
DoRipping = false;
return;
}
P_ExplodeMissile (mo, mo->BlockingLine, BlockingMobj);
DoRipping = false;
return;
}
else
@ -1761,8 +1749,6 @@ explode:
}
} while (++step <= steps);
DoRipping = false;
// Friction
if (player && player->mo == mo && player->cheats & CF_NOMOMENTUM)
@ -4880,19 +4866,20 @@ AActor *P_SpawnPlayerMissile (AActor *source, const PClass *type, angle_t angle)
}
AActor *P_SpawnPlayerMissile (AActor *source, fixed_t x, fixed_t y, fixed_t z,
const PClass *type, angle_t angle)
const PClass *type, angle_t angle, AActor **pLineTarget, AActor **pMissileActor)
{
static const int angdiff[3] = { -1<<26, 1<<26, 0 };
int i;
angle_t an;
angle_t pitch;
AActor *linetarget;
// see which target is to be aimed at
i = 2;
do
{
an = angle + angdiff[i];
pitch = P_AimLineAttack (source, an, 16*64*FRACUNIT);
pitch = P_AimLineAttack (source, an, 16*64*FRACUNIT, &linetarget);
if (source->player != NULL &&
level.IsFreelookAllowed() &&
@ -4906,6 +4893,7 @@ AActor *P_SpawnPlayerMissile (AActor *source, fixed_t x, fixed_t y, fixed_t z,
{
an = angle;
}
if (pLineTarget) *pLineTarget = linetarget;
i = GetDefaultByType (type)->flags3;
@ -4935,7 +4923,8 @@ AActor *P_SpawnPlayerMissile (AActor *source, fixed_t x, fixed_t y, fixed_t z,
z = source->floorz;
}
}
MissileActor = Spawn (type, source->x + x, source->y + y, z, ALLOW_REPLACE);
AActor *MissileActor = Spawn (type, source->x + x, source->y + y, z, ALLOW_REPLACE);
if (pMissileActor) *pMissileActor = MissileActor;
P_PlaySpawnSound(MissileActor, source);
MissileActor->target = source;
MissileActor->angle = an;

View file

@ -45,8 +45,6 @@
// PUBLIC DATA DEFINITIONS -------------------------------------------------
angle_t bulletpitch;
// [SO] 1=Weapons states are all 1 tick
// 2=states with a function 1 tick, others 0 ticks.
CVAR(Int, sv_fastweapons, false, CVAR_SERVERINFO);
@ -632,18 +630,20 @@ void A_GunFlash (AActor *actor)
// the height of the intended target
//
void P_BulletSlope (AActor *mo)
angle_t P_BulletSlope (AActor *mo, AActor **pLineTarget)
{
static const int angdiff[3] = { -1<<26, 1<<26, 0 };
int i;
angle_t an;
angle_t pitch;
AActor *linetarget;
// see which target is to be aimed at
i = 2;
do
{
an = mo->angle + angdiff[i];
bulletpitch = P_AimLineAttack (mo, an, 16*64*FRACUNIT);
pitch = P_AimLineAttack (mo, an, 16*64*FRACUNIT, &linetarget);
if (mo->player != NULL &&
level.IsFreelookAllowed() &&
@ -652,13 +652,14 @@ void P_BulletSlope (AActor *mo)
break;
}
} while (linetarget == NULL && --i >= 0);
return pitch;
}
//
// P_GunShot
//
void P_GunShot (AActor *mo, bool accurate, const PClass *pufftype)
void P_GunShot (AActor *mo, bool accurate, const PClass *pufftype, angle_t pitch)
{
angle_t angle;
int damage;
@ -671,7 +672,7 @@ void P_GunShot (AActor *mo, bool accurate, const PClass *pufftype)
angle += pr_gunshot.Random2 () << 18;
}
P_LineAttack (mo, angle, PLAYERMISSILERANGE, bulletpitch, damage, NAME_None, pufftype);
P_LineAttack (mo, angle, PLAYERMISSILERANGE, pitch, damage, NAME_None, pufftype);
}
void A_Light0 (AActor *actor)

View file

@ -90,8 +90,8 @@ void P_BringUpWeapon (player_s *player);
void P_FireWeapon (player_s *player);
void P_DropWeapon (player_s *player);
void P_BobWeapon (player_s *player, pspdef_t *psp, fixed_t *x, fixed_t *y);
void P_BulletSlope (AActor *mo);
void P_GunShot (AActor *mo, bool accurate, const PClass *pufftype);
angle_t P_BulletSlope (AActor *mo, AActor **pLineTarget = NULL);
void P_GunShot (AActor *mo, bool accurate, const PClass *pufftype, angle_t pitch);
void A_WeaponReady (AActor *actor);
void A_ReFire (AActor *actor);
@ -103,6 +103,4 @@ void A_Light0 (AActor *actor);
void A_Light1 (AActor *actor);
void A_Light2 (AActor *actor);
extern angle_t bulletpitch;
#endif // __P_PSPR_H__

View file

@ -1067,6 +1067,8 @@ void APlayerPawn::ActivateMorphWeapon ()
}
}
P_SetPsprite (player, ps_flash, NULL);
player->PendingWeapon = WP_NOCHANGE;
}
//===========================================================================

View file

@ -83,6 +83,7 @@ class FMultiPatchTexture : public FTexture
{
public:
FMultiPatchTexture (const void *texdef, FPatchLookup *patchlookup, int maxpatchnum, bool strife, int deflump);
FMultiPatchTexture (FScanner &sc, int usetype);
~FMultiPatchTexture ();
const BYTE *GetColumn (unsigned int column, const Span **spans_out);
@ -103,7 +104,13 @@ protected:
struct TexPart
{
SWORD OriginX, OriginY;
BYTE Mirror:2;
BYTE Rotate:2;
BYTE textureOwned:1;
FTexture *Texture;
TexPart();
~TexPart();
};
int NumParts;
@ -114,6 +121,7 @@ protected:
private:
void CheckForHacks ();
void ParsePatch(FScanner &sc, TexPart & part);
};
// A texture defined between F_START and F_END markers

View file

@ -72,21 +72,21 @@ extern size_t MaxDrawSegs;
// Note: transformed values not buffered locally,
// like some DOOM-alikes ("wt", "WebView") did.
//
struct vertex_s
struct vertex_t
{
fixed_t x, y;
bool operator== (const vertex_s &other)
bool operator== (const vertex_t &other)
{
return x == other.x && y == other.y;
}
};
typedef struct vertex_s vertex_t;
// Forward of LineDefs, for Sectors.
struct line_t;
class player_s;
class FScanner;
//
// The SECTORS record, at runtime.
@ -774,7 +774,6 @@ public:
SWORD LeftOffset, TopOffset;
BYTE WidthBits, HeightBits;
//BYTE ScaleX, ScaleY;
fixed_t xScale;
fixed_t yScale;
@ -945,6 +944,7 @@ public:
FTexture *operator[] (const char *texname)
{
int texnum = GetTexture (texname, FTexture::TEX_MiscPatch);
if (texnum==-1) return NULL;
return Textures[texnum].Texture;
}
FTexture *FindTexture(const char *texname, int usetype = FTexture::TEX_MiscPatch, BITFIELD flags = TEXMAN_TryAny);
@ -958,6 +958,7 @@ public:
FTexture *operator() (const char *texname)
{
int texnum = GetTexture (texname, FTexture::TEX_MiscPatch);
if (texnum==-1) return NULL;
return Textures[Translation[texnum]].Texture;
}
@ -993,6 +994,7 @@ public:
void AddTiles (void *tileFile);
void AddHiresTextures (int wadnum);
void LoadHiresTex(int wadnum);
void ParseXTexture(FScanner &sc, int usetype);
int CreateTexture (int lumpnum, int usetype=FTexture::TEX_Any); // Also calls AddTexture
int AddTexture (FTexture *texture);

View file

@ -50,6 +50,7 @@
#include "gi.h"
#include "templates.h"
#include "zstring.h"
#include "timidity/timidity.h"
// MACROS ------------------------------------------------------------------
@ -358,8 +359,8 @@ void S_Start ()
// Check for local sound definitions. Only reload if they differ
// from the previous ones.
char *LocalSndInfo;
char *LocalSndSeq;
const char *LocalSndInfo;
const char *LocalSndSeq;
// To be certain better check whether level is valid!
if (level.info && level.info->soundinfo)

View file

@ -244,13 +244,20 @@ int main (int argc, char **argv)
atexit (call_terms);
atterm (I_Quit);
if (realpath (argv[0], progdir) == NULL)
strcpy (progdir, argv[0]);
char *slash = strrchr (progdir, '/');
if (slash)
// Should we even be doing anything with progdir on Unix systems?
char program[PATH_MAX];
if (realpath (argv[0], program) == NULL)
strcpy (program, argv[0]);
char *slash = strrchr (program, '/');
if (slash != NULL)
{
*(slash + 1) = '\0';
progdir = program;
}
else
progdir[0] = '.', progdir[1] = '/', progdir[2] = '\0';
{
progdir = "./";
}
C_InitConsole (80*8, 25*8, false);
D_DoomMain ();

View file

@ -72,6 +72,8 @@ extern HWND Window;
#define ERRCHECK(x)
#define SPECTRUM_SIZE 256
// TYPES -------------------------------------------------------------------
@ -739,6 +741,23 @@ void FMODSoundRenderer::Shutdown()
}
}
//==========================================================================
//
// FMODSoundRenderer :: GetOutputRate
//
//==========================================================================
float FMODSoundRenderer::GetOutputRate()
{
int rate;
if (FMOD_OK == Sys->getSoftwareFormat(&rate, NULL, NULL, NULL, NULL, NULL))
{
return float(rate);
}
return 48000.f; // Guess, but this should never happen.
}
//==========================================================================
//
// FMODSoundRenderer :: PrintStatus
@ -1715,3 +1734,298 @@ float F_CALLBACK FMODSoundRenderer::RolloffCallback(FMOD_CHANNEL *channel, float
return (powf(10.f, volume) - 1.f) / 9.f;
}
}
//==========================================================================
//
// FMODSoundRenderer :: DrawWaveDebug
//
// Bit 0: ( 1) Show oscilloscope for sfx.
// Bit 1: ( 2) Show spectrum for sfx.
// Bit 2: ( 4) Show oscilloscope for music.
// Bit 3: ( 8) Show spectrum for music.
// Bit 4: (16) Show oscilloscope for all sounds.
// Bit 5: (32) Show spectrum for all sounds.
//
//==========================================================================
void FMODSoundRenderer::DrawWaveDebug(int mode)
{
const int window_height = 100;
float wavearray[MAXWIDTH];
int window_size;
int numoutchans;
int y;
if (FMOD_OK != Sys->getSoftwareFormat(NULL, NULL, &numoutchans, NULL, NULL, NULL))
{
return;
}
// Scale all the channel windows so one group fits completely on one row, with
// 16 pixels of padding between each window.
window_size = (screen->GetWidth() - 16) / numoutchans - 16;
y = 16;
y = DrawChannelGroupOutput(SfxGroup, wavearray, window_size, window_height, y, mode);
y = DrawChannelGroupOutput(MusicGroup, wavearray, window_size, window_height, y, mode >> 2);
y = DrawSystemOutput(wavearray, window_size, window_height, y, mode >> 4);
}
//==========================================================================
//
// FMODSoundRenderer :: DrawChannelGroupOutput
//
// Draws an oscilloscope and/or a spectrum for a channel group.
//
//==========================================================================
int FMODSoundRenderer::DrawChannelGroupOutput(FMOD::ChannelGroup *group, float *wavearray, int width, int height, int y, int mode)
{
int y1, y2;
switch (mode & 0x03)
{
case 0x01: // Oscilloscope only
return DrawChannelGroupWaveData(group, wavearray, width, height, y, false);
case 0x02: // Spectrum only
return DrawChannelGroupSpectrum(group, wavearray, width, height, y, false);
case 0x03: // Oscilloscope + Spectrum
width = (width + 16) / 2 - 16;
y1 = DrawChannelGroupSpectrum(group, wavearray, width, height, y, true);
y2 = DrawChannelGroupWaveData(group, wavearray, width, height, y, true);
return MAX(y1, y2);
}
return y;
}
//==========================================================================
//
// FMODSoundRenderer :: DrawSystemOutput
//
// Like DrawChannelGroupOutput(), but uses the system object.
//
//==========================================================================
int FMODSoundRenderer::DrawSystemOutput(float *wavearray, int width, int height, int y, int mode)
{
int y1, y2;
switch (mode & 0x03)
{
case 0x01: // Oscilloscope only
return DrawSystemWaveData(wavearray, width, height, y, false);
case 0x02: // Spectrum only
return DrawSystemSpectrum(wavearray, width, height, y, false);
case 0x03: // Oscilloscope + Spectrum
width = (width + 16) / 2 - 16;
y1 = DrawSystemSpectrum(wavearray, width, height, y, true);
y2 = DrawSystemWaveData(wavearray, width, height, y, true);
return MAX(y1, y2);
}
return y;
}
//==========================================================================
//
// FMODSoundRenderer :: DrawChannelGroupWaveData
//
// Draws all the output channels for a specified channel group.
// Setting skip to true causes it to skip every other window.
//
//==========================================================================
int FMODSoundRenderer::DrawChannelGroupWaveData(FMOD::ChannelGroup *group, float *wavearray, int width, int height, int y, bool skip)
{
int drawn = 0;
int x = 16;
while (FMOD_OK == group->getWaveData(wavearray, width, drawn))
{
drawn++;
DrawWave(wavearray, x, y, width, height);
x += (width + 16) << int(skip);
}
if (drawn)
{
y += height + 16;
}
return y;
}
//==========================================================================
//
// FMODSoundRenderer::DrawSystemWaveData
//
// Like DrawChannelGroupWaveData, but it uses the system object to get the
// complete output.
//
//==========================================================================
int FMODSoundRenderer::DrawSystemWaveData(float *wavearray, int width, int height, int y, bool skip)
{
int drawn = 0;
int x = 16;
while (FMOD_OK == Sys->getWaveData(wavearray, width, drawn))
{
drawn++;
DrawWave(wavearray, x, y, width, height);
x += (width + 16) << int(skip);
}
if (drawn)
{
y += height + 16;
}
return y;
}
//==========================================================================
//
// FMODSoundRenderer :: DrawWave
//
// Draws an oscilloscope at the specified coordinates on the screen. Each
// entry in the wavearray buffer has its own column. IOW, there are <width>
// entries in wavearray.
//
//==========================================================================
void FMODSoundRenderer::DrawWave(float *wavearray, int x, int y, int width, int height)
{
float scale = height / 2.f;
float mid = y + scale;
int i;
// Draw a box around the oscilloscope.
screen->DrawLine(x - 1, y - 1, x + width, y - 1, -1, MAKEARGB(160, 0, 40, 200));
screen->DrawLine(x + width + 1, y - 1, x + width, y + height, -1, MAKEARGB(160, 0, 40, 200));
screen->DrawLine(x + width, y + height, x - 1, y + height, -1, MAKEARGB(160, 0, 40, 200));
screen->DrawLine(x - 1, y + height, x - 1, y - 1, -1, MAKEARGB(160, 0, 40, 200));
// Draw the actual oscilloscope.
if (screen->Accel2D)
{ // Drawing this with lines is super-slow without hardware acceleration, at least with
// the debug build.
float lasty = wavearray[0] * scale + mid;
float newy;
for (i = 1; i < width; ++i)
{
newy = wavearray[i] * scale + mid;
screen->DrawLine(x + i - 1, int(lasty), x + i, int(newy), -1, MAKEARGB(255,255,248,248));
lasty = newy;
}
}
else
{
for (i = 0; i < width; ++i)
{
float y = wavearray[i] * scale + mid;
screen->DrawPixel(x + i, int(y), -1, MAKEARGB(255,255,255,255));
}
}
}
//==========================================================================
//
// FMODSoundRenderer :: DrawChannelGroupSpectrum
//
// Draws all the spectrum for a specified channel group.
// Setting skip to true causes it to skip every other window, starting at
// the second one.
//
//==========================================================================
int FMODSoundRenderer::DrawChannelGroupSpectrum(FMOD::ChannelGroup *group, float *spectrumarray, int width, int height, int y, bool skip)
{
int drawn = 0;
int x = 16;
if (skip)
{
x += width + 16;
}
while (FMOD_OK == group->getSpectrum(spectrumarray, SPECTRUM_SIZE, drawn, FMOD_DSP_FFT_WINDOW_TRIANGLE))
{
drawn++;
DrawSpectrum(spectrumarray, x, y, width, height);
x += (width + 16) << int(skip);
}
if (drawn)
{
y += height + 16;
}
return y;
}
//==========================================================================
//
// FMODSoundRenderer::DrawSystemSpectrum
//
// Like DrawChannelGroupSpectrum, but it uses the system object to get the
// complete output.
//
//==========================================================================
int FMODSoundRenderer::DrawSystemSpectrum(float *spectrumarray, int width, int height, int y, bool skip)
{
int drawn = 0;
int x = 16;
if (skip)
{
x += width + 16;
}
while (FMOD_OK == Sys->getSpectrum(spectrumarray, SPECTRUM_SIZE, drawn, FMOD_DSP_FFT_WINDOW_TRIANGLE))
{
drawn++;
DrawSpectrum(spectrumarray, x, y, width, height);
x += (width + 16) << int(skip);
}
if (drawn)
{
y += height + 16;
}
return y;
}
//==========================================================================
//
// FMODSoundRenderer :: DrawSpectrum
//
// Draws a spectrum at the specified coordinates on the screen.
//
//==========================================================================
void FMODSoundRenderer::DrawSpectrum(float *spectrumarray, int x, int y, int width, int height)
{
float scale = height / 2.f;
float mid = y + scale;
float db;
int top;
// Draw a border and dark background for the spectrum.
screen->DrawLine(x - 1, y - 1, x + width, y - 1, -1, MAKEARGB(160, 0, 40, 200));
screen->DrawLine(x + width + 1, y - 1, x + width, y + height, -1, MAKEARGB(160, 0, 40, 200));
screen->DrawLine(x + width, y + height, x - 1, y + height, -1, MAKEARGB(160, 0, 40, 200));
screen->DrawLine(x - 1, y + height, x - 1, y - 1, -1, MAKEARGB(160, 0, 40, 200));
screen->Dim(MAKERGB(0,0,0), 0.3f, x, y, width, height);
// Draw the actual spectrum.
for (int i = 0; i < width; ++i)
{
db = spectrumarray[i * (SPECTRUM_SIZE - 2) / width + 1];
db = MAX(-150.f, 10 * log10f(db) * 2); // Convert to decibels and clamp
db = 1.f - (db / -150.f);
db *= height;
top = (int)db;
if (top >= height)
{
top = height - 1;
}
// screen->Clear(x + i, int(y + height - db), x + i + 1, y + height, -1, MAKEARGB(255, 255, 255, 40));
screen->Dim(MAKERGB(255,255,40), 0.65f, x + i, y + height - top, 1, top);
}
}

View file

@ -16,6 +16,7 @@ public:
void LoadSound (sfxinfo_t *sfx);
void UnloadSound (sfxinfo_t *sfx);
unsigned int GetMSLength(sfxinfo_t *sfx);
float GetOutputRate();
// Streaming sounds.
SoundStream *CreateStream (SoundStreamCallback callback, int buffsamples, int flags, int samplerate, void *userdata);
@ -48,6 +49,8 @@ public:
FString GatherStats ();
void ResetEnvironment ();
void DrawWaveDebug(int mode);
private:
bool SFXPaused;
bool InitSuccess;
@ -65,6 +68,17 @@ private:
void Shutdown ();
void DumpDriverCaps(FMOD_CAPS caps, int minfrequency, int maxfrequency);
int DrawChannelGroupOutput(FMOD::ChannelGroup *group, float *wavearray, int width, int height, int y, int mode);
int DrawSystemOutput(float *wavearray, int width, int height, int y, int mode);
int DrawChannelGroupWaveData(FMOD::ChannelGroup *group, float *wavearray, int width, int height, int y, bool skip);
int DrawSystemWaveData(float *wavearray, int width, int height, int y, bool skip);
void DrawWave(float *wavearray, int x, int y, int width, int height);
int DrawChannelGroupSpectrum(FMOD::ChannelGroup *group, float *wavearray, int width, int height, int y, bool skip);
int DrawSystemSpectrum(float *wavearray, int width, int height, int y, bool skip);
void DrawSpectrum(float *spectrumarray, int x, int y, int width, int height);
FMOD::System *Sys;
FMOD::ChannelGroup *SfxGroup, *PausableSfx;
FMOD::ChannelGroup *MusicGroup;

View file

@ -70,8 +70,7 @@ extern void ChildSigHandler (int signum);
#include "tempfiles.h"
#include "templates.h"
#include "stats.h"
#include <fmod.h>
#include "timidity/timidity.h"
EXTERN_CVAR (Int, snd_samplerate)
EXTERN_CVAR (Int, snd_mididevice)
@ -156,6 +155,8 @@ void I_InitMusic (void)
{
static bool setatterm = false;
Timidity::LoadConfig();
snd_musicvolume.Callback ();
nomusic = !!Args->CheckParm("-nomusic") || !!Args->CheckParm("-nosound");
@ -187,6 +188,7 @@ void I_ShutdownMusic(void)
S_StopMusic (true);
assert (currSong == NULL);
}
Timidity::FreeAll();
#ifdef _WIN32
I_ShutdownMusicWin32();
#endif // _WIN32
@ -327,12 +329,16 @@ void *I_RegisterSong (const char *filename, char *musiccache, int offset, int le
*/
if ((snd_mididevice == -3 && device == MDEV_DEFAULT) || device == MDEV_OPL)
{
info = new MUSSong2 (file, musiccache, len, true);
info = new MUSSong2 (file, musiccache, len, MIDI_OPL);
}
else if (device == MDEV_TIMIDITY || (device == MDEV_DEFAULT && snd_mididevice == -2))
{
info = new TimiditySong (file, musiccache, len);
}
else if ((snd_mididevice == -4 && device == MDEV_DEFAULT) && GSnd != NULL)
{
info = new MUSSong2(file, musiccache, len, MIDI_Timidity);
}
if (info != NULL && !info->IsValid())
{
delete info;
@ -373,7 +379,7 @@ void *I_RegisterSong (const char *filename, char *musiccache, int offset, int le
#ifdef _WIN32
if (info == NULL)
{
info = new MUSSong2 (file, musiccache, len, false);
info = new MUSSong2 (file, musiccache, len, MIDI_Win);
}
#endif // _WIN32
}
@ -406,12 +412,16 @@ void *I_RegisterSong (const char *filename, char *musiccache, int offset, int le
*/
if ((device == MDEV_OPL || (snd_mididevice == -3 && device == MDEV_DEFAULT)) && GSnd != NULL)
{
info = new MIDISong2 (file, musiccache, len, true);
info = new MIDISong2 (file, musiccache, len, MIDI_OPL);
}
else if ((device == MDEV_TIMIDITY || (snd_mididevice == -2 && device == MDEV_DEFAULT)) && GSnd != NULL)
{
info = new TimiditySong (file, musiccache, len);
}
else if ((snd_mididevice == -4 && device == MDEV_DEFAULT) && GSnd != NULL)
{
info = new MIDISong2(file, musiccache, len, MIDI_Timidity);
}
if (info != NULL && !info->IsValid())
{
delete info;
@ -421,7 +431,7 @@ void *I_RegisterSong (const char *filename, char *musiccache, int offset, int le
#ifdef _WIN32
if (info == NULL && device != MDEV_FMOD && (snd_mididevice >= 0 || device == MDEV_MMAPI))
{
info = new MIDISong2 (file, musiccache, len, false);
info = new MIDISong2 (file, musiccache, len, MIDI_Win);
}
#endif // _WIN32
}

View file

@ -89,6 +89,12 @@ typedef BYTE *LPSTR;
#define MEVT_EVENTPARM(x) ((x) & 0xffffff)
#define MOM_DONE 969
#else
// w32api does not define these
#ifndef MOD_WAVETABLE
#define MOD_WAVETABLE 6
#define MOD_SWSYNTH 7
#endif
#endif
class MIDIDevice
@ -112,7 +118,7 @@ public:
virtual bool FakeVolume() = 0;
virtual bool Pause(bool paused) = 0;
virtual bool NeedThreadedCallback() = 0;
virtual void PrecacheInstruments(const BYTE *instruments, int count);
virtual void PrecacheInstruments(const WORD *instruments, int count);
};
// WinMM implementation of a MIDI output device -----------------------------
@ -138,7 +144,7 @@ public:
bool FakeVolume();
bool NeedThreadedCallback();
bool Pause(bool paused);
void PrecacheInstruments(const BYTE *instruments, int count);
void PrecacheInstruments(const WORD *instruments, int count);
protected:
static void CALLBACK CallbackFunc(HMIDIOUT, UINT, DWORD_PTR, DWORD, DWORD);
@ -205,12 +211,69 @@ public:
void Stop();
};
// Internal TiMidity MIDI device --------------------------------------------
namespace Timidity { struct Renderer; }
class TimidityMIDIDevice : public MIDIDevice
{
public:
TimidityMIDIDevice();
~TimidityMIDIDevice();
int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata);
void Close();
bool IsOpen() const;
int GetTechnology() const;
int SetTempo(int tempo);
int SetTimeDiv(int timediv);
int StreamOut(MIDIHDR *data);
int StreamOutSync(MIDIHDR *data);
int Resume();
void Stop();
int PrepareHeader(MIDIHDR *data);
int UnprepareHeader(MIDIHDR *data);
bool FakeVolume();
bool Pause(bool paused);
bool NeedThreadedCallback();
void PrecacheInstruments(const WORD *instruments, int count);
protected:
static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata);
bool ServiceStream (void *buff, int numbytes);
void (*Callback)(unsigned int, void *, DWORD, DWORD);
void *CallbackData;
void CalcTickRate();
int PlayTick();
FCriticalSection CritSec;
SoundStream *Stream;
Timidity::Renderer *Renderer;
double Tempo;
double Division;
double SamplesPerTick;
double NextTickIn;
MIDIHDR *Events;
bool Started;
DWORD Position;
};
// Base class for streaming MUS and MIDI files ------------------------------
// MIDI device selection.
enum EMIDIDevice
{
MIDI_Win,
MIDI_OPL,
MIDI_Timidity
};
class MIDIStreamer : public MusInfo
{
public:
MIDIStreamer(bool opl);
MIDIStreamer(EMIDIDevice type);
~MIDIStreamer();
void MusicVolumeChanged();
@ -276,7 +339,7 @@ protected:
int InitialTempo;
BYTE ChannelVolumes[16];
DWORD Volume;
bool UseOPLDevice;
EMIDIDevice DeviceType;
bool CallbackIsThreaded;
FString DumpFilename;
};
@ -286,7 +349,7 @@ protected:
class MUSSong2 : public MIDIStreamer
{
public:
MUSSong2(FILE *file, char *musiccache, int length, bool opl);
MUSSong2(FILE *file, char *musiccache, int length, EMIDIDevice type);
~MUSSong2();
MusInfo *GetOPLDumper(const char *filename);
@ -311,7 +374,7 @@ protected:
class MIDISong2 : public MIDIStreamer
{
public:
MIDISong2(FILE *file, char *musiccache, int length, bool opl);
MIDISong2(FILE *file, char *musiccache, int length, EMIDIDevice type);
~MIDISong2();
MusInfo *GetOPLDumper(const char *filename);

View file

@ -204,6 +204,10 @@ FString SoundRenderer::GatherStats ()
return "No stats for this sound renderer.";
}
void SoundRenderer::DrawWaveDebug(int mode)
{
}
void SoundRenderer::ResetEnvironment ()
{
}
@ -221,3 +225,4 @@ FString SoundStream::GetStats()
{
return "No stream stats available.";
}

View file

@ -75,6 +75,7 @@ public:
virtual void LoadSound (sfxinfo_t *sfx) = 0; // load a sound from disk
virtual void UnloadSound (sfxinfo_t *sfx) = 0; // unloads a sound from memory
virtual unsigned int GetMSLength(sfxinfo_t *sfx) = 0; // Gets the length of a sound at its default frequency
virtual float GetOutputRate() = 0;
// Streaming sounds.
virtual SoundStream *CreateStream (SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) = 0;
@ -105,6 +106,8 @@ public:
virtual void PrintDriversList () = 0;
virtual FString GatherStats ();
virtual void ResetEnvironment ();
virtual void DrawWaveDebug(int mode);
};
extern SoundRenderer *GSnd;

View file

@ -19,7 +19,7 @@ CUSTOM_CVAR (Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
if (!nummididevicesset)
return;
if ((self >= (signed)nummididevices) || (self < -3))
if ((self >= (signed)nummididevices) || (self < -4))
{
Printf ("ID out of range. Using default device.\n");
self = 0;
@ -123,10 +123,8 @@ static void PrintMidiDevice (int id, const char *name, WORD tech, DWORD support)
case MOD_SQSYNTH: Printf ("SQSYNTH"); break;
case MOD_FMSYNTH: Printf ("FMSYNTH"); break;
case MOD_MAPPER: Printf ("MAPPER"); break;
#ifdef MOD_WAVETABLE
case MOD_WAVETABLE: Printf ("WAVETABLE"); break;
case MOD_SWSYNTH: Printf ("SWSYNTH"); break;
#endif
}
if (support & MIDICAPS_CACHE)
{
@ -154,8 +152,8 @@ CCMD (snd_listmididevices)
MMRESULT res;
PrintMidiDevice (-3, "Emulated OPL FM Synth", MOD_FMSYNTH, 0);
PrintMidiDevice (-2, "TiMidity++", 0, MOD_WAVETABLE | MOD_SWSYNTH);
PrintMidiDevice (-1, "FMOD", 0, MOD_WAVETABLE | MOD_SWSYNTH);
PrintMidiDevice (-2, "TiMidity++", 0, MOD_SWSYNTH);
PrintMidiDevice (-1, "FMOD", 0, MOD_SWSYNTH);
if (nummididevices != 0)
{
for (id = 0; id < nummididevices; ++id)

View file

@ -100,8 +100,8 @@ static BYTE CommonLengths[15] = { 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
//
//==========================================================================
MIDISong2::MIDISong2 (FILE *file, char *musiccache, int len, bool opl)
: MIDIStreamer(opl), MusHeader(0), Tracks(0)
MIDISong2::MIDISong2 (FILE *file, char *musiccache, int len, EMIDIDevice type)
: MIDIStreamer(type), MusHeader(0), Tracks(0)
{
int p;
int i;
@ -766,14 +766,17 @@ void MIDISong2::SetTempo(int new_tempo)
void MIDISong2::Precache()
{
int i, j;
// This array keeps track of instruments that are used. The first 128
// entries are for melodic instruments. The second 128 are for
// percussion.
BYTE found_instruments[256] = { 0, };
BYTE found_banks[256] = { 0, };
bool multiple_banks = false;
int i, j;
DoRestart();
found_banks[0] = true; // Bank 0 is always used.
found_banks[128] = true;
for (i = 0; i < NumTracks; ++i)
{
TrackInfo *track = &Tracks[i];
@ -781,25 +784,26 @@ void MIDISong2::Precache()
BYTE ev, data1, data2, command, channel;
int len;
data2 = 0; // Silence, GCC
while (track->TrackP < track->MaxTrackP)
{
ev = track->TrackBegin[track->TrackP++];
command = ev & 0xF0;
if (command == MIDI_SYSEX || command == MIDI_SYSEXEND)
{
len = track->ReadVarLen();
track->TrackP += len;
}
else if (command == MIDI_META)
if (ev == MIDI_META)
{
track->TrackP++;
len = track->ReadVarLen();
track->TrackP += len;
}
else if ((command & 0xF0) == 0xF0)
else if (ev == MIDI_SYSEX || ev == MIDI_SYSEXEND)
{
track->TrackP += CommonLengths[ev & 0xF];
len = track->ReadVarLen();
track->TrackP += len;
}
else if (command == 0xF0)
{
track->TrackP += CommonLengths[ev & 0x0F];
}
else
{
@ -821,27 +825,62 @@ void MIDISong2::Precache()
}
if (channel != 9 && command == (MIDI_PRGMCHANGE & 0x70))
{
found_instruments[data1 & 127] = 1;
found_instruments[data1 & 127] = true;
}
else if (channel == 9 && command == (MIDI_PRGMCHANGE & 0x70) && data1 != 0)
{ // On a percussion channel, program change also serves as bank select.
multiple_banks = true;
found_banks[data1 | 128] = true;
}
else if (channel == 9 && command == (MIDI_NOTEON & 0x70) && data2 != 0)
{
found_instruments[data1 | 128] = 1;
found_instruments[data1 | 128] = true;
}
else if (command == (MIDI_CTRLCHANGE & 0x70) && data1 == 0 && data2 != 0)
{
multiple_banks = true;
if (channel == 9)
{
found_banks[data2 | 128] = true;
}
else
{
found_banks[data2 & 127] = true;
}
}
}
track->ReadVarLen(); // Skip delay.
}
}
}
DoRestart();
// Now pack everything into a contiguous region for the PrecacheInstruments call().
for (i = j = 0; i < 256; ++i)
TArray<WORD> packed;
for (i = 0; i < 256; ++i)
{
if (found_instruments[i])
{
found_instruments[j++] = i;
WORD packnum = (i & 127) | ((i & 128) << 7);
if (!multiple_banks)
{
packed.Push(packnum);
}
else
{ // In order to avoid having to multiplex tracks in a type 1 file,
// precache every used instrument in every used bank, even if not
// all combinations are actually used.
for (j = 0; j < 128; ++j)
{
if (found_banks[j + (i & 128)])
{
packed.Push(packnum | (j << 7));
}
}
MIDI->PrecacheInstruments(found_instruments, j);
}
}
}
MIDI->PrecacheInstruments(&packed[0], packed.Size());
}
//==========================================================================

View file

@ -69,12 +69,12 @@ extern UINT mididevice;
//
//==========================================================================
MIDIStreamer::MIDIStreamer(bool opl)
MIDIStreamer::MIDIStreamer(EMIDIDevice type)
:
#ifdef _WIN32
PlayerThread(0), ExitEvent(0), BufferDoneEvent(0),
#endif
MIDI(0), Division(0), InitialTempo(500000), UseOPLDevice(opl)
MIDI(0), Division(0), InitialTempo(500000), DeviceType(type)
{
#ifdef _WIN32
BufferDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
@ -102,7 +102,7 @@ MIDIStreamer::MIDIStreamer(const char *dumpname)
#ifdef _WIN32
PlayerThread(0), ExitEvent(0), BufferDoneEvent(0),
#endif
MIDI(0), Division(0), InitialTempo(500000), UseOPLDevice(true), DumpFilename(dumpname)
MIDI(0), Division(0), InitialTempo(500000), DeviceType(MIDI_OPL), DumpFilename(dumpname)
{
#ifdef _WIN32
BufferDoneEvent = NULL;
@ -198,16 +198,23 @@ void MIDIStreamer::Play(bool looping)
{
MIDI = new OPLDumperMIDIDevice(DumpFilename);
}
else
else switch(DeviceType)
{
case MIDI_Win:
#ifdef _WIN32
if (!UseOPLDevice)
{
MIDI = new WinMIDIDevice(mididevice);
}
else
break;
#endif
{
assert(0);
// Intentional fall-through for non-Windows systems.
case MIDI_Timidity:
MIDI = new TimidityMIDIDevice;
break;
case MIDI_OPL:
MIDI = new OPLMIDIDevice;
break;
}
#ifndef _WIN32
@ -682,8 +689,11 @@ int MIDIStreamer::FillBuffer(int buffer_num, int max_events, DWORD max_time)
{
events[0] = 0; // dwDeltaTime
events[1] = 0; // dwStreamID
events[2] = MIDI_NOTEOFF | i | (60 << 8) | (64<<16);
events += 3;
events[2] = MIDI_CTRLCHANGE | i | (123 << 8); // All notes off
events[3] = 0;
events[4] = 0;
events[5] = MIDI_CTRLCHANGE | i | (121 << 8); // Reset controllers
events += 6;
}
DoRestart();
}
@ -723,12 +733,13 @@ MIDIDevice::~MIDIDevice()
// If the device can benefit from preloading the instruments, it can do so
// now.
//
// For each entry, bit 7 set indicates that the instrument is percussion and
// the lower 7 bits contain the note number to use on MIDI channel 10,
// otherwise it is melodic and the lower 7 bits are the program number.
// Each entry is packed as follows:
// Bits 0- 6: Instrument number
// Bits 7-13: Bank number
// Bit 14: Select drum set if 1, tone bank if 0
//
//==========================================================================
void MIDIDevice::PrecacheInstruments(const BYTE *instruments, int count)
void MIDIDevice::PrecacheInstruments(const WORD *instruments, int count)
{
}

View file

@ -84,8 +84,8 @@ static const BYTE CtrlTranslate[15] =
//
//==========================================================================
MUSSong2::MUSSong2 (FILE *file, char *musiccache, int len, bool opl)
: MIDIStreamer(opl), MusHeader(0), MusBuffer(0)
MUSSong2::MUSSong2 (FILE *file, char *musiccache, int len, EMIDIDevice type)
: MIDIStreamer(type), MusHeader(0), MusBuffer(0)
{
#ifdef _WIN32
if (ExitEvent == NULL)
@ -189,19 +189,20 @@ bool MUSSong2::CheckDone()
void MUSSong2::Precache()
{
BYTE *work = (BYTE *)alloca(MusHeader->NumInstruments);
const WORD *used = (WORD *)MusHeader + sizeof(MUSHeader) / 2;
WORD *work = (WORD *)alloca(MusHeader->NumInstruments * sizeof(WORD));
const WORD *used = (WORD *)MusHeader + sizeof(MUSHeader) / sizeof(WORD);
int i, j;
for (i = j = 0; i < MusHeader->NumInstruments; ++i)
{
if (used[i] < 128)
WORD instr = LittleShort(used[i]);
if (instr < 128)
{
work[j++] = (BYTE)used[i];
work[j++] = instr;
}
else if (used[i] >= 135 && used[i] <= 181)
{ // Percussions are 100-based, not 128-based, eh?
work[j++] = (used[i] - 100) | 0x80;
work[j++] = instr - 100 + (1 << 14);
}
}
MIDI->PrecacheInstruments(&work[0], j);

View file

@ -0,0 +1,520 @@
/*
** music_timidity_mididevice.cpp
** Provides access to TiMidity as a generic MIDI device.
**
**---------------------------------------------------------------------------
** Copyright 2008 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 "i_musicinterns.h"
#include "templates.h"
#include "doomdef.h"
#include "m_swap.h"
#include "w_wad.h"
#include "timidity/timidity.h"
// MACROS ------------------------------------------------------------------
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
// PRIVATE DATA DEFINITIONS ------------------------------------------------
// PUBLIC DATA DEFINITIONS -------------------------------------------------
CVAR(Bool, timidity_watch, false, 0)
// CODE --------------------------------------------------------------------
//==========================================================================
//
// TimidityMIDIDevice Constructor
//
//==========================================================================
TimidityMIDIDevice::TimidityMIDIDevice()
{
Stream = NULL;
Tempo = 0;
Division = 0;
Events = NULL;
Started = false;
Renderer = NULL;
Renderer = new Timidity::Renderer(GSnd->GetOutputRate());
}
//==========================================================================
//
// TimidityMIDIDevice Destructor
//
//==========================================================================
TimidityMIDIDevice::~TimidityMIDIDevice()
{
Close();
if (Renderer != NULL)
{
delete Renderer;
}
}
//==========================================================================
//
// TimidityMIDIDevice :: Open
//
// Returns 0 on success.
//
//==========================================================================
int TimidityMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata)
{
Stream = GSnd->CreateStream(FillStream, int(Renderer->rate / 2) * 4,
SoundStream::Float, int(Renderer->rate), this);
if (Stream == NULL)
{
return 2;
}
Callback = callback;
CallbackData = userdata;
Tempo = 500000;
Division = 100;
CalcTickRate();
Renderer->Reset();
return 0;
}
//==========================================================================
//
// TimidityMIDIDevice :: Close
//
//==========================================================================
void TimidityMIDIDevice::Close()
{
if (Stream != NULL)
{
delete Stream;
Stream = NULL;
}
Started = false;
}
//==========================================================================
//
// TimidityMIDIDevice :: IsOpen
//
//==========================================================================
bool TimidityMIDIDevice::IsOpen() const
{
return Stream != NULL;
}
//==========================================================================
//
// TimidityMIDIDevice :: GetTechnology
//
//==========================================================================
int TimidityMIDIDevice::GetTechnology() const
{
return MOD_SWSYNTH;
}
//==========================================================================
//
// TimidityMIDIDevice :: SetTempo
//
//==========================================================================
int TimidityMIDIDevice::SetTempo(int tempo)
{
Tempo = tempo;
CalcTickRate();
return 0;
}
//==========================================================================
//
// TimidityMIDIDevice :: SetTimeDiv
//
//==========================================================================
int TimidityMIDIDevice::SetTimeDiv(int timediv)
{
Division = timediv;
CalcTickRate();
return 0;
}
//==========================================================================
//
// TimidityMIDIDevice :: CalcTickRate
//
// Tempo is the number of microseconds per quarter note.
// Division is the number of ticks per quarter note.
//
//==========================================================================
void TimidityMIDIDevice::CalcTickRate()
{
SamplesPerTick = Renderer->rate / (1000000.0 / Tempo) / Division;
}
//==========================================================================
//
// TimidityMIDIDevice :: Resume
//
//==========================================================================
int TimidityMIDIDevice::Resume()
{
if (!Started)
{
if (Stream->Play(true, 1, false))
{
Started = true;
return 0;
}
return 1;
}
return 0;
}
//==========================================================================
//
// TimidityMIDIDevice :: Stop
//
//==========================================================================
void TimidityMIDIDevice::Stop()
{
if (Started)
{
Stream->Stop();
Started = false;
}
}
//==========================================================================
//
// TimidityMIDIDevice :: StreamOutSync
//
// This version is called from the main game thread and needs to
// synchronize with the player thread.
//
//==========================================================================
int TimidityMIDIDevice::StreamOutSync(MIDIHDR *header)
{
CritSec.Enter();
StreamOut(header);
CritSec.Leave();
return 0;
}
//==========================================================================
//
// TimidityMIDIDevice :: StreamOut
//
// This version is called from the player thread so does not need to
// arbitrate for access to the Events pointer.
//
//==========================================================================
int TimidityMIDIDevice::StreamOut(MIDIHDR *header)
{
header->lpNext = NULL;
if (Events == NULL)
{
Events = header;
NextTickIn = SamplesPerTick * *(DWORD *)header->lpData;
Position = 0;
}
else
{
MIDIHDR **p;
for (p = &Events; *p != NULL; p = &(*p)->lpNext)
{ }
*p = header;
}
return 0;
}
//==========================================================================
//
// TimidityMIDIDevice :: PrepareHeader
//
//==========================================================================
int TimidityMIDIDevice::PrepareHeader(MIDIHDR *header)
{
return 0;
}
//==========================================================================
//
// TimidityMIDIDevice :: UnprepareHeader
//
//==========================================================================
int TimidityMIDIDevice::UnprepareHeader(MIDIHDR *header)
{
return 0;
}
//==========================================================================
//
// TimidityMIDIDevice :: FakeVolume
//
// Since the TiMidity output is rendered as a normal stream, its volume is
// controlled through the GSnd interface, not here.
//
//==========================================================================
bool TimidityMIDIDevice::FakeVolume()
{
return false;
}
//==========================================================================
//
// TimidityMIDIDevice :: NeedThreadedCallabck
//
// OPL can service the callback directly rather than using a separate
// thread.
//
//==========================================================================
bool TimidityMIDIDevice::NeedThreadedCallback()
{
return false;
}
//==========================================================================
//
// TimidityMIDIDevice :: Pause
//
//==========================================================================
bool TimidityMIDIDevice::Pause(bool paused)
{
if (Stream != NULL)
{
return Stream->SetPaused(paused);
}
return true;
}
//==========================================================================
//
// TimidityMIDIDevice :: PrecacheInstruments
//
// Each entry is packed as follows:
// Bits 0- 6: Instrument number
// Bits 7-13: Bank number
// Bit 14: Select drum set if 1, tone bank if 0
//
//==========================================================================
void TimidityMIDIDevice::PrecacheInstruments(const WORD *instruments, int count)
{
for (int i = 0; i < count; ++i)
{
Renderer->MarkInstrument((instruments[i] >> 7) & 127, instruments[i] >> 14, instruments[i] & 127);
}
Renderer->load_missing_instruments();
}
//==========================================================================
//
// TimidityMIDIDevice :: PlayTick
//
// event[0] = delta time
// event[1] = unused
// event[2] = event
//
//==========================================================================
int TimidityMIDIDevice::PlayTick()
{
DWORD delay = 0;
while (delay == 0 && Events != NULL)
{
DWORD *event = (DWORD *)(Events->lpData + Position);
if (MEVT_EVENTTYPE(event[2]) == MEVT_TEMPO)
{
SetTempo(MEVT_EVENTPARM(event[2]));
}
else if (MEVT_EVENTTYPE(event[2]) == MEVT_LONGMSG)
{
Renderer->HandleLongMessage((BYTE *)&event[3], MEVT_EVENTPARM(event[2]));
}
else if (MEVT_EVENTTYPE(event[2]) == 0)
{ // Short MIDI event
int status = event[2] & 0xff;
int parm1 = (event[2] >> 8) & 0x7f;
int parm2 = (event[2] >> 16) & 0x7f;
Renderer->HandleEvent(status, parm1, parm2);
if (timidity_watch)
{
static const char *const commands[8] =
{
"Note off",
"Note on",
"Poly press",
"Ctrl change",
"Prgm change",
"Chan press",
"Pitch bend",
"SysEx"
};
#ifdef _WIN32
char buffer[128];
sprintf(buffer, "C%02d: %11s %3d %3d\n", (status & 15) + 1, commands[(status >> 4) & 7], parm1, parm2);
OutputDebugString(buffer);
#else
fprintf(stderr, "C%02d: %11s %3d %3d\n", (status & 15) + 1, commands[(status >> 4) & 7], parm1, parm2);
#endif
}
}
// Advance to next event.
if (event[2] < 0x80000000)
{ // Short message
Position += 12;
}
else
{ // Long message
Position += 12 + ((MEVT_EVENTPARM(event[2]) + 3) & ~3);
}
// Did we use up this buffer?
if (Position >= Events->dwBytesRecorded)
{
Events = Events->lpNext;
Position = 0;
if (Callback != NULL)
{
Callback(MOM_DONE, CallbackData, 0, 0);
}
}
if (Events == NULL)
{ // No more events. Just return something to keep the song playing
// while we wait for more to be submitted.
return int(Division);
}
delay = *(DWORD *)(Events->lpData + Position);
}
return delay;
}
//==========================================================================
//
// TimidityMIDIDevice :: ServiceStream
//
//==========================================================================
bool TimidityMIDIDevice::ServiceStream (void *buff, int numbytes)
{
float *samples = (float *)buff;
float *samples1;
int numsamples = numbytes / sizeof(float) / 2;
bool prev_ended = false;
bool res = true;
samples1 = samples;
memset(buff, 0, numbytes);
CritSec.Enter();
while (numsamples > 0)
{
double ticky = NextTickIn;
int tick_in = int(NextTickIn);
int samplesleft = MIN(numsamples, tick_in);
if (samplesleft > 0)
{
Renderer->ComputeOutput(samples1, samplesleft);
assert(NextTickIn == ticky);
NextTickIn -= samplesleft;
assert(NextTickIn >= 0);
numsamples -= samplesleft;
samples1 += samplesleft * 2;
}
if (NextTickIn < 1)
{
int next = PlayTick();
assert(next >= 0);
if (next == 0)
{ // end of song
if (numsamples > 0)
{
Renderer->ComputeOutput(samples1, samplesleft);
}
res = false;
break;
}
else
{
NextTickIn += SamplesPerTick * next;
assert(NextTickIn >= 0);
}
}
}
CritSec.Leave();
return res;
}
//==========================================================================
//
// TimidityMIDIDevice :: FillStream static
//
//==========================================================================
bool TimidityMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, void *userdata)
{
TimidityMIDIDevice *device = (TimidityMIDIDevice *)userdata;
return device->ServiceStream(buff, len);
}

View file

@ -209,9 +209,10 @@ void WinMIDIDevice::Stop()
//
// WinMIDIDevice :: PrecacheInstruments
//
// For each entry, bit 7 set indicates that the instrument is percussion and
// the lower 7 bits contain the note number to use on MIDI channel 10,
// otherwise it is melodic and the lower 7 bits are the program number.
// Each entry is packed as follows:
// Bits 0- 6: Instrument number
// Bits 7-13: Bank number
// Bit 14: Select drum set if 1, tone bank if 0
//
// My old GUS PnP needed the instruments to be preloaded, or it would miss
// some notes the first time through the song. I doubt any modern
@ -221,7 +222,7 @@ void WinMIDIDevice::Stop()
//
//==========================================================================
void WinMIDIDevice::PrecacheInstruments(const BYTE *instruments, int count)
void WinMIDIDevice::PrecacheInstruments(const WORD *instruments, int count)
{
// Setting snd_midiprecache to false disables this precaching, since it
// does involve sleeping for more than a miniscule amount of time.
@ -229,14 +230,31 @@ void WinMIDIDevice::PrecacheInstruments(const BYTE *instruments, int count)
{
return;
}
for (int i = 0, chan = 0; i < count; ++i)
BYTE bank[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
int i, chan;
for (i = 0, chan = 0; i < count; ++i)
{
if (instruments[i] & 0x80)
{ // Percussion
int instr = instruments[i] & 127;
int banknum = (instruments[i] >> 7) & 127;
int percussion = instruments[i] >> 14;
if (percussion)
{
if (bank[9] != banknum)
{
midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_CTRLCHANGE | 9 | (0 << 8) | (banknum << 16));
bank[9] = banknum;
}
midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_NOTEON | 9 | ((instruments[i] & 0x7f) << 8) | (1 << 16));
}
else
{ // Melodic
if (bank[chan] != banknum)
{
midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_CTRLCHANGE | 9 | (0 << 8) | (banknum << 16));
bank[chan] = banknum;
}
midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_PRGMCHANGE | chan | (instruments[i] << 8));
midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_NOTEON | chan | (60 << 8) | (1 << 16));
if (++chan == 9)
@ -258,6 +276,14 @@ void WinMIDIDevice::PrecacheInstruments(const BYTE *instruments, int count)
// And now chan is back at 0, ready to start the cycle over.
}
}
// Make sure all channels are set back to bank 0.
for (i = 0; i < 16; ++i)
{
if (bank[i] != 0)
{
midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_CTRLCHANGE | 9 | (0 << 8) | (0 << 16));
}
}
}
//==========================================================================

View file

@ -3,5 +3,5 @@
// This file was automatically generated by the
// updaterevision tool. Do not edit by hand.
#define ZD_SVN_REVISION_STRING "898"
#define ZD_SVN_REVISION_NUMBER 898
#define ZD_SVN_REVISION_STRING "905"
#define ZD_SVN_REVISION_NUMBER 905

View file

@ -33,6 +33,7 @@
**
*/
#include <ctype.h>
#include "doomtype.h"
#include "files.h"
#include "r_data.h"
@ -40,6 +41,9 @@
#include "i_system.h"
#include "gi.h"
#include "st_start.h"
#include "sc_man.h"
#include "templates.h"
#include "vectors.h"
// On the Alpha, accessing the shorts directly if they aren't aligned on a
// 4-byte boundary causes unaligned access warnings. Why it does this at
@ -462,162 +466,32 @@ FTextureFormat FMultiPatchTexture::GetFormat()
}
#if 0
//==========================================================================
//
// Stuff that checks whether a multipatch texture is merely the same
// as its previous definition. If so it's discarded and the old one
// kept. This way there is less interference with WADs that redefine
// the entire original list of textures
// FMultiPatchTexture :: TexPart :: TexPart
//
//==========================================================================
struct TextureCheckList
FMultiPatchTexture::TexPart::TexPart()
{
struct ComparePatch
{
SWORD originx;
SWORD originy;
union
{
char chars[8];
QWORD asint;
} patchtexture;
};
struct CompareTexture
{
union
{
char chars[8];
QWORD asint;
} name;
WORD Flags; // [RH] Was unused
BYTE ScaleX; // [RH] Scaling (8 is normal)
BYTE ScaleY; // [RH] Same as above
SWORD width;
SWORD height;
SWORD patchcount;
ComparePatch patches[1];
static CompareTexture *Create(const maptexture_t *orgdata, const FPatchLookup *patchlookup, int numpatches)
{
CompareTexture *c = (CompareTexture*)M_Calloc(sizeof(CompareTexture) + (orgdata->patchcount-1) * sizeof(ComparePatch), 1);
uppercopy(c->name.chars, (const char*)orgdata->name);
c->Flags = orgdata->Flags;
c->ScaleX = orgdata->ScaleX;
c->ScaleY = orgdata->ScaleY;
c->width = orgdata->width;
c->height = orgdata->height;
c->patchcount = orgdata->patchcount;
for(int i=0; i<orgdata->patchcount;i++)
{
c->patches[i].originx = orgdata->patches[i].originx;
c->patches[i].originy = orgdata->patches[i].originy;
int pnum = orgdata->patches[i].patch;
if (pnum >= 0 && pnum < numpatches)
{
uppercopy(c->patches[i].patchtexture.chars, patchlookup[pnum].Name);
}
else c->patches[i].patchtexture.asint = -1;
}
return c;
OriginX = OriginY = 0;
Mirror = Rotate = 0;
textureOwned = false;
Texture = NULL;
}
static CompareTexture *Create(const strifemaptexture_t *orgdata, const FPatchLookup *patchlookup, int numpatches)
{
CompareTexture *c = (CompareTexture*)M_Calloc(sizeof(CompareTexture) + (orgdata->patchcount-1) * sizeof(ComparePatch), 1);
uppercopy(c->name.chars, (const char *)orgdata->name);
c->Flags = orgdata->Flags;
c->ScaleX = orgdata->ScaleX;
c->ScaleY = orgdata->ScaleY;
c->width = orgdata->width;
c->height = orgdata->height;
c->patchcount = orgdata->patchcount;
for(int i=0; i<orgdata->patchcount;i++)
{
c->patches[i].originx = orgdata->patches[i].originx;
c->patches[i].originy = orgdata->patches[i].originy;
int pnum = orgdata->patches[i].patch;
if (pnum >= 0 && pnum < numpatches)
{
uppercopy(c->patches[i].patchtexture.chars, patchlookup[pnum].Name);
}
else c->patches[i].patchtexture.asint = -1;
}
return c;
}
};
//==========================================================================
//
// FMultiPatchTexture :: TexPart :: TexPart
//
//==========================================================================
typedef TMap<QWORD, CompareTexture *> TexMap;
TexMap data;
TextureCheckList()
FMultiPatchTexture::TexPart::~TexPart()
{
if (textureOwned && Texture != NULL) delete Texture;
Texture = NULL;
}
~TextureCheckList()
{
TexMap::Iterator it(data);
TexMap::Pair *pair;
while (it.NextPair (pair))
{
if (pair->Value != NULL) free(pair->Value);
}
}
bool Check(const void *tdata, FPatchLookup *patchlookup, int numpatches, bool isstrife)
{
CompareTexture * tex;
if (!isstrife)
{
tex = CompareTexture::Create((const maptexture_t *)tdata, patchlookup, numpatches);
}
else
{
tex = CompareTexture::Create((const strifemaptexture_t *)tdata, patchlookup, numpatches);
}
bool res = false;
if (!strnicmp(tex->name.chars, "BIGDOOR1", 8))
{
__asm nop
}
CompareTexture **ppc = data.CheckKey(tex->name.asint);
if (ppc != NULL)
{
// The texture is considered the same if the original
// is binary identical.
// First check the number of patches to avoid problems
if (tex->patchcount == (*ppc)->patchcount)
res = !memcmp(tex, *ppc, sizeof(CompareTexture) + (tex->patchcount-1) * sizeof(ComparePatch));
if (!res)
{
// Texture is not identical - remove the old one
free(*ppc);
*ppc=NULL;
}
}
if (!res)
{
data[tex->name.asint] = tex;
}
else
{
free (tex);
}
return res;
}
};
#endif
//==========================================================================
//
// FTextureManager :: AddTexturesLump
@ -626,7 +500,6 @@ struct TextureCheckList
void FTextureManager::AddTexturesLump (const void *lumpdata, int lumpsize, int deflumpnum, int patcheslump, int firstdup, bool texture1)
{
//TextureCheckList checklist;
FPatchLookup *patchlookup;
int i, j;
DWORD numpatches;
@ -753,8 +626,6 @@ void FTextureManager::AddTexturesLump (const void *lumpdata, int lumpsize, int d
break;
}
if (j + 1 == firstdup)
{
//if (!checklist.Check((const BYTE *)maptex + offset, patchlookup, numpatches, isStrife))
{
FMultiPatchTexture *tex = new FMultiPatchTexture ((const BYTE *)maptex + offset, patchlookup, numpatches, isStrife, deflumpnum);
if (i == 1 && texture1)
@ -762,11 +633,6 @@ void FTextureManager::AddTexturesLump (const void *lumpdata, int lumpsize, int d
tex->UseType = FTexture::TEX_Null;
}
TexMan.AddTexture (tex);
//Printf("Using texture %s\n", tex->Name);
}
//else Printf("skipping texture %.8s\n", (const BYTE *)maptex + offset);
StartScreen->Progress();
}
}
@ -795,3 +661,141 @@ void FTextureManager::AddTexturesLumps (int lump1, int lump2, int patcheslump)
}
}
void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part)
{
FString patchname;
sc.MustGetString();
int texno = TexMan.CheckForTexture(sc.String, TEX_WallPatch);
if (texno < 0)
{
int lumpnum = Wads.CheckNumForFullName(sc.String);
if (lumpnum >= 0)
{
part.Texture = FTexture::CreateTexture(lumpnum, TEX_WallPatch);
part.textureOwned = true;
}
}
else
{
part.Texture = TexMan[texno];
}
if (part.Texture == NULL)
{
Printf("Unknown patch '%s' in texture '%s'\n", sc.String, Name);
}
sc.MustGetStringName(",");
sc.MustGetNumber();
part.OriginX = sc.Number;
sc.MustGetStringName(",");
sc.MustGetNumber();
part.OriginY = sc.Number;
/* not yet implemented
if (sc.CheckString("{");
{
while (!sc.CheckString("}"))
{
sc.MustGetString();
if (sc.Compare("flipx"))
{
part.Mirror = 1;
}
else if (sc.Compare("flipy"))
{
part.Mirror = 2;
}
else if (sc.Compare("rotate"))
{
sc.MustGetNumber();
if (sc.Number != 0 && sc.Number !=90 && sc.Number != 180 && sc.Number != 270)
{
sc.ScriptError("Rotation must be 0, 90, 180 or 270 degrees");
}
part.Rotate = sc.Number / 90;
}
}
}
*/
}
FMultiPatchTexture::FMultiPatchTexture (FScanner &sc, int usetype)
{
TArray<TexPart> parts;
sc.MustGetString();
uppercopy(Name, sc.String);
sc.MustGetStringName(",");
sc.MustGetNumber();
Width = sc.Number;
sc.MustGetStringName(",");
sc.MustGetNumber();
Height = sc.Number;
if (sc.CheckString("{"))
{
while (!sc.CheckString("}"))
{
sc.MustGetString();
if (sc.Compare("XScale"))
{
sc.MustGetFloat();
xScale = FLOAT2FIXED(sc.Float);
}
else if (sc.Compare("YScale"))
{
sc.MustGetFloat();
yScale = FLOAT2FIXED(sc.Float);
}
else if (sc.Compare("WorldPanning"))
{
bWorldPanning = true;
}
else if (sc.Compare("NoDecals"))
{
bNoDecals = true;
}
else if (sc.Compare("Patch"))
{
TexPart part;
ParsePatch(sc, part);
parts.Push(part);
}
}
NumParts = parts.Size();
UseType = FTexture::TEX_Override;
Parts = new TexPart[NumParts];
memcpy(Parts, &parts[0], NumParts * sizeof(*Parts));
CalcBitSize ();
// If this texture is just a wrapper around a single patch, we can simply
// forward GetPixels() and GetColumn() calls to that patch.
if (NumParts == 1)
{
if (Parts->OriginX == 0 && Parts->OriginY == 0 &&
Parts->Texture->GetWidth() == Width &&
Parts->Texture->GetHeight() == Height &&
Parts->Mirror == 0 && Parts->Rotate == 0)
{
bRedirect = true;
}
}
//DefinitionLump = sc.G deflumpnum;
}
}
void FTextureManager::ParseXTexture(FScanner &sc, int usetype)
{
FTexture *tex = new FMultiPatchTexture(sc, usetype);
TexMan.AddTexture (tex);
}

View file

@ -611,6 +611,14 @@ void FTextureManager::LoadHiresTex(int wadnum)
}
//else Printf("Unable to define hires texture '%s'\n", tex->Name);
}
else if (sc.Compare("texture"))
{
ParseXTexture(sc, FTexture::TEX_Override);
}
else if (sc.Compare("sprite"))
{
ParseXTexture(sc, FTexture::TEX_Sprite);
}
}
}
}

View file

@ -297,10 +297,12 @@ static FActorInfo *CreateNewActor(FScanner &sc, FActorInfo **parentc, Baggage *b
*colon++ = 0;
}
/*
if (PClass::FindClass (sc.String) != NULL)
{
sc.ScriptError ("Actor %s is already defined.", sc.String);
}
*/
typeName = sc.String;

View file

@ -540,8 +540,7 @@ void A_JumpIfCloser(AActor * self)
else
{
// Does the player aim at something that can be shot?
P_BulletSlope(self);
target = linetarget;
P_BulletSlope(self, &target);
}
if (pStateCall != NULL) pStateCall->Result=false; // Jumps should never set the result for inventory state chains!
@ -1016,9 +1015,8 @@ void A_FireBullets (AActor *self)
static_cast<APlayerPawn *>(self)->PlayAttacking2 ();
P_BulletSlope(self);
bslope = P_BulletSlope(self);
bangle = self->angle;
bslope = bulletpitch;
PuffType = PClass::FindClass(PuffTypeName);
if (!PuffType) PuffType = PClass::FindClass(NAME_BulletPuff);
@ -1063,6 +1061,7 @@ void A_FireCustomMissile (AActor * self)
player_t *player=self->player;
AWeapon * weapon=player->ReadyWeapon;
AActor *linetarget;
if (UseAmmo && weapon)
{
@ -1080,7 +1079,7 @@ void A_FireCustomMissile (AActor * self)
if (AimAtAngle) shootangle+=Angle;
AActor * misl=P_SpawnPlayerMissile (self, x, y, z, ti, shootangle);
AActor * misl=P_SpawnPlayerMissile (self, x, y, z, ti, shootangle, &linetarget);
// automatic handling of seeker missiles
if (misl)
{
@ -1129,12 +1128,13 @@ void A_CustomPunch (AActor *self)
angle_t angle;
int pitch;
AActor * linetarget;
if (!norandom) Damage *= (pr_cwpunch()%8+1);
angle = self->angle + (pr_cwpunch.Random2() << 18);
if (Range == 0) Range = MELEERANGE;
pitch = P_AimLineAttack (self, angle, Range);
pitch = P_AimLineAttack (self, angle, Range, &linetarget);
// only use ammo when actually hitting something!
if (UseAmmo && linetarget && weapon)
@ -2164,8 +2164,7 @@ void A_JumpIfTargetInLOS(AActor * self)
else
{
// Does the player aim at something that can be shot?
P_BulletSlope(self);
target = linetarget;
P_BulletSlope(self, &target);
}
// No target - return

View file

@ -80,7 +80,8 @@
// [RH] Keep GCC quiet by not using offsetof on Actor types.
#define DEFINE_FLAG(prefix, name, type, variable) { prefix##_##name, #name, (int)(size_t)&((type*)1)->variable - 1 }
#define DEFINE_FLAG2(symbol, name, type, variable) { symbol, #name, (int)(size_t)&((type*)1)->variable - 1 }
#define DEFINE_DEPRECATED_FLAG(name, type) { DEPF_##name, #name, -1 }
#define DEFINE_DEPRECATED_FLAG(name) { DEPF_##name, #name, -1 }
#define DEFINE_DUMMY_FLAG(name) { DEPF_UNUSED, #name, -1 }
struct flagdef
{
@ -91,6 +92,7 @@ struct flagdef
enum
{
DEPF_UNUSED,
DEPF_FIREDAMAGE,
DEPF_ICEDAMAGE,
DEPF_LOWGRAVITY,
@ -248,11 +250,11 @@ static flagdef ActorFlags[]=
DEFINE_FLAG(RF, FORCEXYBILLBOARD, AActor, renderflags),
// Deprecated flags. Handling must be performed in HandleDeprecatedFlags
DEFINE_DEPRECATED_FLAG(FIREDAMAGE, AActor),
DEFINE_DEPRECATED_FLAG(ICEDAMAGE, AActor),
DEFINE_DEPRECATED_FLAG(LOWGRAVITY, AActor),
DEFINE_DEPRECATED_FLAG(SHORTMISSILERANGE, AActor),
DEFINE_DEPRECATED_FLAG(LONGMELEERANGE, AActor),
DEFINE_DEPRECATED_FLAG(FIREDAMAGE),
DEFINE_DEPRECATED_FLAG(ICEDAMAGE),
DEFINE_DEPRECATED_FLAG(LOWGRAVITY),
DEFINE_DEPRECATED_FLAG(SHORTMISSILERANGE),
DEFINE_DEPRECATED_FLAG(LONGMELEERANGE),
};
static flagdef InventoryFlags[] =
@ -271,7 +273,7 @@ static flagdef InventoryFlags[] =
DEFINE_FLAG(IF, IGNORESKILL, AInventory, ItemFlags),
DEFINE_FLAG(IF, ADDITIVETIME, AInventory, ItemFlags),
DEFINE_DEPRECATED_FLAG(PICKUPFLASH, AInventory),
DEFINE_DEPRECATED_FLAG(PICKUPFLASH),
};
@ -296,6 +298,8 @@ static flagdef WeaponFlags[] =
DEFINE_FLAG(WIF, CHEATNOTWEAPON, AWeapon, WeaponFlags),
DEFINE_FLAG(WIF, NO_AUTO_SWITCH, AWeapon, WeaponFlags),
//WIF_BOT_REACTION_SKILL_THING = 1<<31, // I don't understand this
DEFINE_DUMMY_FLAG(NOLMS),
};
static const struct { const PClass *Type; flagdef *Defs; int NumDefs; } FlagLists[] =
@ -749,10 +753,12 @@ static bool CheckFloatParm(FScanner &sc)
static int ParseMorphStyle (FScanner &sc)
{
static const char * morphstyles[]={
"MRF_ADDSTAMINA", "MRF_FULLHEALTH", "MRF_UNDOBYTOMEOFPOWER", "MRF_UNDOBYCHAOSDEVICE", NULL};
"MRF_ADDSTAMINA", "MRF_FULLHEALTH", "MRF_UNDOBYTOMEOFPOWER", "MRF_UNDOBYCHAOSDEVICE",
"MRF_FAILNOTELEFRAG", "MRF_FAILNOLAUGH", "MRF_WHENINVULNERABLE", NULL};
static const int morphstyle_values[]={
MORPH_ADDSTAMINA, MORPH_FULLHEALTH, MORPH_UNDOBYTOMEOFPOWER, MORPH_UNDOBYCHAOSDEVICE};
MORPH_ADDSTAMINA, MORPH_FULLHEALTH, MORPH_UNDOBYTOMEOFPOWER, MORPH_UNDOBYCHAOSDEVICE,
MORPH_FAILNOTELEFRAG, MORPH_FAILNOLAUGH, MORPH_WHENINVULNERABLE};
// May be given flags by number...
if (sc.CheckNumber())
@ -2560,7 +2566,7 @@ static void PowerMorphMorphStyle (FScanner &sc, APowerMorph *defaults, Baggage &
static void PowerMorphMorphFlash (FScanner &sc, APowerMorph *defaults, Baggage &bag)
{
sc.MustGetString ();
defaults->UnMorphFlash = FName(sc.String);
defaults->MorphFlash = FName(sc.String);
}
//==========================================================================

View file

@ -56,6 +56,34 @@ int CleanWidth, CleanHeight;
CVAR (Bool, hud_scale, false, CVAR_ARCHIVE);
// For routines that take RGB colors, cache the previous lookup in case there
// are several repetitions with the same color.
static int LastPal = -1;
static uint32 LastRGB;
static int PalFromRGB(uint32 rgb)
{
if (LastPal >= 0 && LastRGB == rgb)
{
return LastPal;
}
// Quick check for black and white.
if (rgb == MAKEARGB(255,0,0,0))
{
LastPal = GPalette.BlackIndex;
}
else if (rgb == MAKEARGB(255,255,255,255))
{
LastPal = GPalette.WhiteIndex;
}
else
{
LastPal = ColorMatcher.Pick(RPART(rgb), GPART(rgb), BPART(rgb));
}
LastRGB = rgb;
return LastPal;
}
void STACK_ARGS DCanvas::DrawTexture (FTexture *img, int x, int y, int tags_first, ...)
{
va_list tags;
@ -757,15 +785,7 @@ void DCanvas::DrawLine(int x0, int y0, int x1, int y1, int palColor, uint32 real
if (palColor < 0)
{
// Quick check for black.
if (realcolor == MAKEARGB(255,0,0,0))
{
palColor = 0;
}
else
{
palColor = ColorMatcher.Pick(RPART(realcolor), GPART(realcolor), BPART(realcolor));
}
palColor = PalFromRGB(realcolor);
}
Lock();
@ -918,22 +938,52 @@ void DCanvas::DrawPixel(int x, int y, int palColor, uint32 realcolor)
{
if (palColor < 0)
{
// Quick check for black.
if (realcolor == MAKEARGB(255,0,0,0))
{
palColor = 0;
}
else
{
palColor = ColorMatcher.Pick(RPART(realcolor), GPART(realcolor), BPART(realcolor));
}
palColor = PalFromRGB(realcolor);
}
Lock();
GetBuffer()[GetPitch() * y + x] = (BYTE)palColor;
Unlock();
Buffer[Pitch * y + x] = (BYTE)palColor;
}
//==========================================================================
//
// DCanvas :: Clear
//
// Set an area to a specified color.
//
//==========================================================================
void DCanvas::Clear (int left, int top, int right, int bottom, int palcolor, uint32 color)
{
int x, y;
BYTE *dest;
if (left == right || top == bottom)
{
return;
}
assert(left < right);
assert(top < bottom);
if (palcolor < 0)
{
if (APART(color) != 255)
{
Dim(color, APART(color)/255.f, left, top, right - left, bottom - top);
return;
}
palcolor = PalFromRGB(color);
}
dest = Buffer + top * Pitch + left;
x = right - left;
for (y = top; y < bottom; y++)
{
memset(dest, palcolor, x);
dest += Pitch;
}
}
/********************************/
/* */

View file

@ -322,14 +322,16 @@ FFont::FFont (const char *name, const char *nametemplate, int first, int count,
stcfn121 = true;
}
}
if (lump != -1 || i != 124-start || !stcfn121)
charlumps[i] = lump;
if (lump >= 0)
{
FTexture *pic = TexMan[buffer];
if (pic != NULL)
{
// set the lump here only if it represents a valid texture
if (i != 124-start || !stcfn121)
charlumps[i] = lump;
int height = pic->GetScaledHeight();
int yoffs = pic->GetScaledTopOffset();
@ -1200,7 +1202,7 @@ FFontChar1::FFontChar1 (int sourcelump, const BYTE *sourceremap)
Name[0] = 0; // Make this texture unnamed
// now copy all the properties from the base texture
CopySize(BaseTexture);
if (BaseTexture != NULL) CopySize(BaseTexture);
Pixels = NULL;
}

View file

@ -284,55 +284,6 @@ void DCanvas::FlatFill (int left, int top, int right, int bottom, FTexture *src,
}
}
//==========================================================================
//
// DCanvas :: Clear
//
// Set an area to a specified color.
//
//==========================================================================
void DCanvas::Clear (int left, int top, int right, int bottom, int palcolor, uint32 color)
{
int x, y;
BYTE *dest;
if (left == right || top == bottom)
{
return;
}
assert(left < right);
assert(top < bottom);
if (palcolor < 0)
{
if (APART(color) != 255)
{
Dim(color, APART(color)/255.f, left, top, right - left, bottom - top);
return;
}
// Quick check for black.
if (color == MAKEARGB(255,0,0,0))
{
palcolor = 0;
}
else
{
palcolor = ColorMatcher.Pick(RPART(color), GPART(color), BPART(color));
}
}
dest = Buffer + top * Pitch + left;
x = right - left;
for (y = top; y < bottom; y++)
{
memset(dest, palcolor, x);
dest += Pitch;
}
}
//==========================================================================
//
// DCanvas :: Dim

View file

@ -2274,6 +2274,27 @@ long FWadLump::Read (void *buffer, long len)
return numread;
}
char *FWadLump::Gets(char *strbuf, int len)
{
// Blood, you are so mean to me with your encryption.
// ... and since this function will never read from Blood
// files let's just return an error here.
if (Encrypted && FilePos - StartPos < 256)
{
return NULL;
}
if (SourceData != NULL)
{
return GetsFromBuffer(SourceData, strbuf, len);
}
else
{
return FileReader::Gets(strbuf, len);
}
return strbuf;
}
// FMemLump -----------------------------------------------------------------
FMemLump::FMemLump ()

View file

@ -119,6 +119,7 @@ public:
long Seek (long offset, int origin);
long Read (void *buffer, long len);
char *Gets(char *strbuf, int len);
private:
FWadLump (const FileReader &reader, long length, bool encrypted);

View file

@ -823,9 +823,26 @@ void DoMain (HINSTANCE hInstance)
atterm (I_Quit);
// Figure out what directory the program resides in.
GetModuleFileName (NULL, progdir, 1024);
*(strrchr (progdir, '\\') + 1) = 0;
FixPathSeperator (progdir);
char *program;
#ifdef _MSC_VER
if (_get_pgmptr(&program) != 0)
{
I_FatalError("Could not determine program location.");
}
#else
char progbuff[1024];
GetModuleFileName(0, progbuff, sizeof(progbuff));
progbuff[1023] = '\0';
program = progbuff;
#endif
progdir = program;
program = progdir.LockBuffer();
*(strrchr(program, '\\') + 1) = '\0';
FixPathSeperator(program);
progdir.Truncate((long)strlen(program));
progdir.UnlockBuffer();
/*
height = GetSystemMetrics (SM_CYFIXEDFRAME) * 2 +
GetSystemMetrics (SM_CYCAPTION) + 12 * 32;