- Fixed: The hitscan tracer had the current sector point to a temporary variable

when 3D floors were involved.

Update to ZDoom r965:
- Fixed: SPAC_AnyCross didn't work.
- Fixed: Pushable doors must also check for SPAC_MPush.
- Fixed: P_LoadThings2 did not adjust the byte order for the thingid field.
- Changed: HIRESTEX 'define' textures now replace existing textures
  of type MiscPatch with the same name.
- Added UDMF line trigger types MonsterUse and MonsterPush.
- Separated skill and class filter bits from FMapThing::flags so that
  UDMF can define up to 16 of each. Also separated easy/baby and
  hard/nightmare and changed default MAPINFO definitions.
- Fixed: FWadCollection::MergeLumps() did not initialize the flags for any
  marker lumps it inserted.
- Fixed: Need write barriers when modifying SequenceListHead.
- Added a new cvar: midi_timiditylike. This re-enables TiMidity handling of
  GUS patch flags, envelopes, and volume levels, while trying to be closer
  to TiMidity++ than original TiMidity.
- Renamed timidity_config and timidity_voices to midi_config and midi_voices
  respectively.
- Changed: Crosshair drawing uses the current player class's default health instead
  of 100 to calculate the color for the crosshair.
- Added SECF_NOFALLINGDAMAGE flag plus Sector_ChangeFlags to set it. Also separated
  all user settable flags from MoreFlags into their own Flags variable.
- Reduced volume, expression, and panning controllers back to 7 bits.
- Added very basic Soundfont support to the internal TiMidity. Things missing:
  filter, LFOs, modulation envelope, chorus, reverb, and modulators. May or
  may not be compatible with TiMidity++'s soundfont extensions.
- Changed all thing coordinates that were stored as shorts into fixed_t.
- Separated mapthing2_t into mapthinghexen_t and the internal FMapThing so
  that it is easier to add new features in the UDMF map format.
- Added some initial code to read UDMF maps.
- Added support for quoted strings to the TiMidity config parser.
- Split off the slope creation code from p_Setup.cpp into its own file.
- Separated the linedef activation types into a bit mask that allows combination
  of all types on the same linedef. Also added a 'first side only' flag. This
  is not usable from Hexen or Doom format maps though but in preparation of
  the UDMF format discussed here:
  http://www.doomworld.com/vb/source-ports/43145-udmf-v0-99-specification-draft-aka-textmap/
- Changed linedef's alpha property from a byte to fixed point after seeing that
  255 wasn't handled to be fully opaque.
- fixed a GCC warning in fmodsound.cpp
- Fixed: Warped textures didn't work anymore because the default speed was 0.
- Fixed: I had instrument vibrato setting the tremolo_sweep_increment value
  in the instrument loader, effectively disabling vibrato. 

git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@103 b0f79afe-0144-0410-b225-9a4edf0717df
This commit is contained in:
Christoph Oelckers 2008-05-12 09:58:47 +00:00
parent bfaeb75abf
commit 7708436a98
73 changed files with 5591 additions and 1759 deletions

View file

@ -841,6 +841,10 @@
RelativePath=".\src\p_sight.cpp"
>
</File>
<File
RelativePath=".\src\p_slopes.cpp"
>
</File>
<File
RelativePath=".\src\p_spec.cpp"
>
@ -869,6 +873,10 @@
RelativePath=".\src\p_trace.cpp"
>
</File>
<File
RelativePath=".\src\p_udmf.cpp"
>
</File>
<File
RelativePath=".\src\p_user.cpp"
>
@ -2956,9 +2964,32 @@
Name="Timidity"
>
<File
RelativePath=".\src\timidity\common.cpp"
RelativePath=".\src\timidity\instrum_font.cpp"
>
</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
Name="Headers"
>
<File
RelativePath=".\src\timidity\dls1.h"
>
@ -2967,6 +2998,26 @@
RelativePath=".\src\timidity\dls2.h"
>
</File>
<File
RelativePath=".\src\timidity\gf1patch.h"
>
</File>
<File
RelativePath=".\src\timidity\sf2.h"
>
</File>
<File
RelativePath=".\src\timidity\timidity.h"
>
</File>
</Filter>
<Filter
Name="Source"
>
<File
RelativePath=".\src\timidity\common.cpp"
>
</File>
<File
RelativePath=".\src\timidity\instrum.cpp"
>
@ -2975,6 +3026,10 @@
RelativePath=".\src\timidity\instrum_dls.cpp"
>
</File>
<File
RelativePath=".\src\timidity\instrum_sf2.cpp"
>
</File>
<File
RelativePath=".\src\timidity\mix.cpp"
>
@ -3007,29 +3062,6 @@
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>
@ -3683,6 +3715,22 @@
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
ExcludedFromBuild="true"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\src\xlat\xlat_parser.h"

View file

@ -51,6 +51,7 @@ DEFINE_SPECIAL(ExtraFloor_LightOnly, 50, -1, -1)
DEFINE_SPECIAL(Sector_SetLink, 51, 4, 4)
DEFINE_SPECIAL(Scroll_Wall, 52, 5, 5)
DEFINE_SPECIAL(Line_SetTextureOffset, 53, 5, 5)
DEFINE_SPECIAL(Sector_ChangeFlags, 54, 3, 3)
DEFINE_SPECIAL(Plat_PerpetualRaise, 60, 3, 3)
DEFINE_SPECIAL(Plat_Stop, 61, 1, 1)

View file

@ -667,7 +667,7 @@ public:
// no matter what (even if shot)
player_s *player; // only valid if type of APlayerPawn
TObjPtr<AActor> LastLookActor; // Actor last looked for (if TIDtoHate != 0)
WORD SpawnPoint[3]; // For nightmare respawn
fixed_t SpawnPoint[3]; // For nightmare respawn
WORD SpawnAngle;
int skillrespawncount;
TObjPtr<AActor> tracer; // Thing being chased/attacked for tracers

View file

@ -1353,8 +1353,9 @@ void AM_drawWalls (bool allmap)
{
if ((lines[i].special == Teleport ||
lines[i].special == Teleport_NoFog ||
lines[i].special == Teleport_ZombieChanger ||
lines[i].special == Teleport_Line) &&
GET_SPAC(lines[i].flags) != SPAC_MCROSS &&
(lines[i].activation & SPAC_PlayerActivate) &&
am_usecustomcolors)
{ // intra-level teleporters
AM_drawMline(&l, IntraTeleportColor);

View file

@ -109,8 +109,8 @@ bool FCajunMaster::Move (AActor *actor, ticcmd_t *cmd)
tryit = false;
if (tryit &&
(P_TestActivateLine (ld, actor, 0, SPAC_USE) ||
P_TestActivateLine (ld, actor, 0, SPAC_PUSH)))
(P_TestActivateLine (ld, actor, 0, SPAC_Use) ||
P_TestActivateLine (ld, actor, 0, SPAC_Push)))
{
good |= ld == actor->BlockingLine ? 1 : 2;
}

View file

@ -101,7 +101,7 @@ struct FActionMap
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static long ParseCommandLine (const char *args, int *argc, char **argv);
static long ParseCommandLine (const char *args, int *argc, char **argv, bool no_escapes);
static FConsoleCommand *FindNameInHashTable (FConsoleCommand **table, const char *name, size_t namelen);
static FConsoleCommand *ScanChainForName (FConsoleCommand *start, const char *name, size_t namelen, FConsoleCommand **prev);
@ -724,7 +724,7 @@ void AddCommandString (char *cmd, int keynum)
// \c becomes just TEXTCOLOR_ESCAPE
// $<cvar> is replaced by the contents of <cvar>
static long ParseCommandLine (const char *args, int *argc, char **argv)
static long ParseCommandLine (const char *args, int *argc, char **argv, bool no_escapes)
{
int count;
char *buffplace;
@ -758,15 +758,15 @@ static long ParseCommandLine (const char *args, int *argc, char **argv)
do
{
stuff = *args++;
if (stuff == '\\' && *args == '\"')
if (!no_escapes && stuff == '\\' && *args == '\"')
{
stuff = '\"', args++;
}
else if (stuff == '\\' && *args == '\\')
else if (!no_escapes && stuff == '\\' && *args == '\\')
{
args++;
}
else if (stuff == '\\' && *args == 'c')
else if (!no_escapes && stuff == '\\' && *args == 'c')
{
stuff = TEXTCOLOR_ESCAPE, args++;
}
@ -824,11 +824,12 @@ static long ParseCommandLine (const char *args, int *argc, char **argv)
return (long)(buffplace - (char *)0);
}
FCommandLine::FCommandLine (const char *commandline)
FCommandLine::FCommandLine (const char *commandline, bool no_escapes)
{
cmd = commandline;
_argc = -1;
_argv = NULL;
noescapes = no_escapes;
}
FCommandLine::~FCommandLine ()
@ -839,11 +840,20 @@ FCommandLine::~FCommandLine ()
}
}
void FCommandLine::Shift()
{
// Only valid after _argv has been filled.
for (int i = 1; i < _argc; ++i)
{
_argv[i - 1] = _argv[i];
}
}
int FCommandLine::argc ()
{
if (_argc == -1)
{
argsize = ParseCommandLine (cmd, &_argc, NULL);
argsize = ParseCommandLine (cmd, &_argc, NULL, noescapes);
}
return _argc;
}
@ -855,7 +865,7 @@ char *FCommandLine::operator[] (int i)
int count = argc();
_argv = new char *[count + (argsize+sizeof(char*)-1)/sizeof(char*)];
_argv[0] = (char *)_argv + count*sizeof(char *);
ParseCommandLine (cmd, NULL, _argv);
ParseCommandLine (cmd, NULL, _argv, noescapes);
}
return _argv[i];
}

View file

@ -64,17 +64,19 @@ FString BuildString (int argc, char **argv);
class FCommandLine
{
public:
FCommandLine (const char *commandline);
FCommandLine (const char *commandline, bool no_escapes = false);
~FCommandLine ();
int argc ();
char *operator[] (int i);
const char *args () { return cmd; }
void Shift();
private:
const char *cmd;
int _argc;
char **_argv;
long argsize;
bool noescapes;
};
typedef void (*CCmdRun) (FCommandLine &argv, APlayerPawn *instigator, int key);

View file

@ -405,7 +405,7 @@ void FDecalLib::ReadDecals(FScanner &sc)
}
}
BYTE FDecalLib::GetDecalID (FScanner &sc)
WORD FDecalLib::GetDecalID (FScanner &sc)
{
sc.MustGetString ();
if (!IsNum (sc.String))
@ -416,18 +416,18 @@ BYTE FDecalLib::GetDecalID (FScanner &sc)
else
{
unsigned long num = strtoul (sc.String, NULL, 10);
if (num < 1 || num > 255)
if (num < 1 || num > 65535)
{
sc.MustGetStringName ("Decal ID must be between 1 and 255");
sc.MustGetStringName ("Decal ID must be between 1 and 65535");
}
return (BYTE)num;
return (WORD)num;
}
}
void FDecalLib::ParseDecal (FScanner &sc)
{
FString decalName;
BYTE decalNum;
WORD decalNum;
FDecalTemplate newdecal;
int code, picnum;
@ -544,7 +544,7 @@ void FDecalLib::ParseDecal (FScanner &sc)
void FDecalLib::ParseDecalGroup (FScanner &sc)
{
FString groupName;
BYTE decalNum;
WORD decalNum;
FDecalBase *targetDecal;
FDecalGroup *group;
@ -839,7 +839,7 @@ void FDecalLib::ReplaceDecalRef (FDecalBase *from, FDecalBase *to, FDecalBase *r
root->ReplaceDecalRef (from, to);
}
void FDecalLib::AddDecal (const char *name, BYTE num, const FDecalTemplate &decal)
void FDecalLib::AddDecal (const char *name, WORD num, const FDecalTemplate &decal)
{
FDecalTemplate *newDecal = new FDecalTemplate;
@ -912,7 +912,7 @@ void FDecalLib::AddDecal (FDecalBase *decal)
}
}
const FDecalTemplate *FDecalLib::GetDecalByNum (BYTE num) const
const FDecalTemplate *FDecalLib::GetDecalByNum (WORD num) const
{
if (num == 0)
{
@ -940,7 +940,7 @@ const FDecalTemplate *FDecalLib::GetDecalByName (const char *name) const
return NULL;
}
FDecalBase *FDecalLib::ScanTreeForNum (const BYTE num, FDecalBase *root)
FDecalBase *FDecalLib::ScanTreeForNum (const WORD num, FDecalBase *root)
{
while (root != NULL)
{

View file

@ -61,7 +61,7 @@ protected:
FDecalBase *Left, *Right;
FName Name;
BYTE SpawnID;
WORD SpawnID;
TArray<const PClass *> Users; // Which actors generate this decal
};
@ -98,22 +98,22 @@ public:
void ReadDecals (FScanner &sc);
void ReadAllDecals ();
const FDecalTemplate *GetDecalByNum (BYTE num) const;
const FDecalTemplate *GetDecalByNum (WORD num) const;
const FDecalTemplate *GetDecalByName (const char *name) const;
private:
struct FTranslation;
static void DelTree (FDecalBase *root);
static FDecalBase *ScanTreeForNum (const BYTE num, FDecalBase *root);
static FDecalBase *ScanTreeForNum (const WORD num, FDecalBase *root);
static FDecalBase *ScanTreeForName (const char *name, FDecalBase *root);
static void ReplaceDecalRef (FDecalBase *from, FDecalBase *to, FDecalBase *root);
FTranslation *GenerateTranslation (DWORD start, DWORD end);
void AddDecal (const char *name, BYTE num, const FDecalTemplate &decal);
void AddDecal (const char *name, WORD num, const FDecalTemplate &decal);
void AddDecal (FDecalBase *decal);
FDecalAnimator *FindAnimator (const char *name);
BYTE GetDecalID (FScanner &sc);
WORD GetDecalID (FScanner &sc);
void ParseDecal (FScanner &sc);
void ParseDecalGroup (FScanner &sc);
void ParseGenerator (FScanner &sc);

View file

@ -29,6 +29,7 @@
// Some global defines, that configure the game.
#include "doomdef.h"
#include "m_swap.h"
@ -55,12 +56,16 @@ enum
ML_BLOCKMAP, // LUT, motion clipping, walls/grid element
ML_BEHAVIOR, // [RH] Hexen-style scripts. If present, THINGS
// and LINEDEFS are also Hexen-style.
ML_CONVERSATION, // Strife dialog (only for TEXTMAP format)
ML_MAX,
// [RH] These are compressed (and extended) nodes. They combine the data from
// vertexes, segs, ssectors, and nodes into a single lump.
ML_ZNODES = ML_NODES,
ML_GLZNODES = ML_SSECTORS
ML_GLZNODES = ML_SSECTORS,
// for text map format
ML_TEXTMAP = ML_THINGS,
};
@ -130,41 +135,9 @@ typedef struct
#define ML_DONTDRAW 0x0080 // don't draw on the automap
#define ML_MAPPED 0x0100 // set if already drawn in automap
#define ML_REPEAT_SPECIAL 0x0200 // special is repeatable
#define ML_SPAC_SHIFT 10
#define ML_SPAC_MASK 0x1c00
static inline int GET_SPAC (int flags)
{
return (flags&ML_SPAC_MASK) >> ML_SPAC_SHIFT;
}
// Special activation types
#define SPAC_CROSS 0 // when player crosses line
#define SPAC_USE 1 // when player uses line
#define SPAC_MCROSS 2 // when monster crosses line
#define SPAC_IMPACT 3 // when projectile hits line
#define SPAC_PUSH 4 // when player/monster pushes line
#define SPAC_PCROSS 5 // when projectile crosses line
#define SPAC_USETHROUGH 6 // SPAC_USE, but passes it through
#define SPAC_PTOUCH 7 // when a projectiles crosses or hits line
#define SPAC_OTHERCROSS 8 // [RH] Not a real activation type. Here for compatibility.
// [RH] BOOM's ML_PASSUSE flag (conflicts with ML_REPEATSPECIAL)
#define ML_PASSUSE_BOOM 0x0200
// [RH] In case I feel like it, here it is...
#define ML_3DMIDTEX_ETERNITY 0x0400
// If this bit is set, then all non-original-Doom bits are cleared when
// translating the line. Only applies when playing Doom with Doom-format maps.
// Hexen format maps and the other games are not affected by this.
#define ML_RESERVED_ETERNITY 0x0800
// [RH] Extra flags for Strife compatibility
#define ML_TRANSLUCENT_STRIFE 0x1000
#define ML_RAILING_STRIFE 0x0200
#define ML_BLOCK_FLOATERS_STRIFE 0x0400
#define ML_SPAC_MASK 0x1c00 // Hexen's activator mask. These flags are no longer used.
// Extended flags
#define ML_MONSTERSCANACTIVATE 0x00002000 // [RH] Monsters (as well as players) can active the line
@ -177,6 +150,47 @@ static inline int GET_SPAC (int flags)
#define ML_WRAP_MIDTEX 0x00100000
#define ML_3DMIDTEX 0x00200000
#define ML_CHECKSWITCHRANGE 0x00400000
#define ML_FIRSTSIDEONLY 0x00800000 // activated only when crossed from front side
static inline int GET_SPAC (int flags)
{
return (flags&ML_SPAC_MASK) >> ML_SPAC_SHIFT;
}
// Special activation types
enum SPAC
{
SPAC_Cross = 1<<0, // when player crosses line
SPAC_Use = 1<<1, // when player uses line
SPAC_MCross = 1<<2, // when monster crosses line
SPAC_Impact = 1<<3, // when projectile hits line
SPAC_Push = 1<<4, // when player pushes line
SPAC_PCross = 1<<5, // when projectile crosses line
SPAC_UseThrough = 1<<6, // when player uses line (doesn't block)
// SPAC_PTOUCH is mapped to SPAC_PCross|SPAC_Impact
SPAC_AnyCross = 1<<7, // when anything without the MF2_TELEPORT flag crosses the line
SPAC_MUse = 1<<8, // monsters can use
SPAC_MPush = 1<<9, // monsters can push
SPAC_PlayerActivate = (SPAC_Cross|SPAC_Use|SPAC_Impact|SPAC_Push|SPAC_AnyCross|SPAC_UseThrough),
};
// [RH] BOOM's ML_PASSUSE flag (conflicts with ML_REPEATSPECIAL)
#define ML_PASSUSE_BOOM 0x0200
#define ML_3DMIDTEX_ETERNITY 0x0400
// If this bit is set, then all non-original-Doom bits are cleared when
// translating the line. Only applies when playing Doom with Doom-format maps.
// Hexen format maps and the other games are not affected by this.
#define ML_RESERVED_ETERNITY 0x0800
// [RH] Extra flags for Strife compatibility
#define ML_TRANSLUCENT_STRIFE 0x1000
#define ML_RAILING_STRIFE 0x0200
#define ML_BLOCK_FLOATERS_STRIFE 0x0400
// Sector definition, from editing
typedef struct
@ -241,7 +255,7 @@ typedef struct
} mapthing_t;
// [RH] Hexen-compatible MapThing.
typedef struct MapThing
typedef struct
{
unsigned short thingid;
short x;
@ -252,9 +266,25 @@ typedef struct MapThing
short flags;
BYTE special;
BYTE args[5];
} mapthinghexen_t;
// Internal representation of a mapthing
struct FMapThing
{
int thingid;
fixed_t x;
fixed_t y;
fixed_t z;
short angle;
short type;
WORD SkillFilter;
WORD ClassFilter;
DWORD flags;
int special;
int args[5];
void Serialize (FArchive &);
} mapthing2_t;
};
// [RH] MapThing flags.
@ -266,9 +296,14 @@ typedef struct MapThing
#define MTF_AMBUSH 0x0008 // Thing is deaf
*/
#define MTF_DORMANT 0x0010 // Thing is dormant (use Thing_Activate)
/*
#define MTF_FIGHTER 0x0020
#define MTF_CLERIC 0x0040
#define MTF_MAGE 0x0080
*/
#define MTF_CLASS_MASK 0x00e0
#define MTF_CLASS_SHIFT 5
#define MTF_SINGLE 0x0100 // Thing appears in single-player games
#define MTF_COOPERATIVE 0x0200 // Thing appears in cooperative games
#define MTF_DEATHMATCH 0x0400 // Thing appears in deathmatch games

View file

@ -95,9 +95,13 @@ typedef enum
//
// Skill flags.
/*
#define MTF_EASY 1
#define MTF_NORMAL 2
#define MTF_HARD 4
*/
#define MTF_SKILLMASK 7
#define MTF_SKILLSHIFT 1
// Deaf monsters/do not react to sound.
#define MTF_AMBUSH 8

View file

@ -188,10 +188,10 @@ extern bool playeringame[MAXPLAYERS];
// Player spawn spots for deathmatch.
extern TArray<mapthing2_t> deathmatchstarts;
extern TArray<FMapThing> deathmatchstarts;
// Player spawn spots.
extern mapthing2_t playerstarts[MAXPLAYERS];
extern FMapThing playerstarts[MAXPLAYERS];
// Intermission stats.
// Parameters for world map / intermission.

View file

@ -1236,20 +1236,20 @@ void G_PlayerReborn (int player)
//
// G_CheckSpot
// Returns false if the player cannot be respawned
// at the given mapthing2_t spot
// at the given mapthing spot
// because something is occupying it
//
bool G_CheckSpot (int playernum, mapthing2_t *mthing)
bool G_CheckSpot (int playernum, FMapThing *mthing)
{
fixed_t x;
fixed_t y;
fixed_t z, oldz;
int i;
x = mthing->x << FRACBITS;
y = mthing->y << FRACBITS;
z = mthing->z << FRACBITS;
x = mthing->x;
y = mthing->y;
z = mthing->z;
z += P_PointInSector (x, y)->floorplane.ZatPoint (x, y);
@ -1288,8 +1288,8 @@ bool G_CheckSpot (int playernum, mapthing2_t *mthing)
// called at level load and each death
//
// [RH] Returns the distance of the closest player to the given mapthing2_t.
static fixed_t PlayersRangeFromSpot (mapthing2_t *spot)
// [RH] Returns the distance of the closest player to the given mapthing
static fixed_t PlayersRangeFromSpot (FMapThing *spot)
{
fixed_t closest = INT_MAX;
fixed_t distance;
@ -1300,8 +1300,8 @@ static fixed_t PlayersRangeFromSpot (mapthing2_t *spot)
if (!playeringame[i] || !players[i].mo || players[i].health <= 0)
continue;
distance = P_AproxDistance (players[i].mo->x - spot->x * FRACUNIT,
players[i].mo->y - spot->y * FRACUNIT);
distance = P_AproxDistance (players[i].mo->x - spot->x,
players[i].mo->y - spot->y);
if (distance < closest)
closest = distance;
@ -1311,10 +1311,10 @@ static fixed_t PlayersRangeFromSpot (mapthing2_t *spot)
}
// [RH] Select the deathmatch spawn spot farthest from everyone.
static mapthing2_t *SelectFarthestDeathmatchSpot (size_t selections)
static FMapThing *SelectFarthestDeathmatchSpot (size_t selections)
{
fixed_t bestdistance = 0;
mapthing2_t *bestspot = NULL;
FMapThing *bestspot = NULL;
unsigned int i;
for (i = 0; i < selections; i++)
@ -1332,7 +1332,7 @@ static mapthing2_t *SelectFarthestDeathmatchSpot (size_t selections)
}
// [RH] Select a deathmatch spawn spot at random (original mechanism)
static mapthing2_t *SelectRandomDeathmatchSpot (int playernum, unsigned int selections)
static FMapThing *SelectRandomDeathmatchSpot (int playernum, unsigned int selections)
{
unsigned int i, j;
@ -1352,7 +1352,7 @@ static mapthing2_t *SelectRandomDeathmatchSpot (int playernum, unsigned int sele
void G_DeathMatchSpawnPlayer (int playernum)
{
unsigned int selections;
mapthing2_t *spot;
FMapThing *spot;
selections = deathmatchstarts.Size ();
// [RH] We can get by with just 1 deathmatch start

View file

@ -279,8 +279,8 @@ void P_TeleportToPlayerStarts (AActor *victim)
selections++;
}
i = pr_telestarts() % selections;
destX = playerstarts[i].x << FRACBITS;
destY = playerstarts[i].y << FRACBITS;
destX = playerstarts[i].x;
destY = playerstarts[i].y;
destAngle = ANG45 * (playerstarts[i].angle/45);
P_Teleport (victim, destX, destY, ONFLOORZ, destAngle, true, true, false);
}
@ -301,8 +301,8 @@ void P_TeleportToDeathmatchStarts (AActor *victim)
if (selections > 0)
{
i = pr_teledm() % selections;
destX = deathmatchstarts[i].x << FRACBITS;
destY = deathmatchstarts[i].y << FRACBITS;
destX = deathmatchstarts[i].x;
destY = deathmatchstarts[i].y;
destAngle = ANG45 * (deathmatchstarts[i].angle/45);
P_Teleport (victim, destX, destY, ONFLOORZ, destAngle, true, true, false);
}

View file

@ -3174,12 +3174,20 @@ static void ParseSkill (FScanner &sc)
skill.Aggressiveness = FRACUNIT - FLOAT2FIXED(clamp<float>(sc.Float, 0,1));
}
else if (sc.Compare("SpawnFilter"))
{
if (sc.CheckNumber())
{
if (sc.Number > 0) skill.SpawnFilter |= (1<<(sc.Number-1));
}
else
{
sc.MustGetString ();
strlwr(sc.String);
if (strstr(sc.String, "easy")) skill.SpawnFilter|=MTF_EASY;
if (strstr(sc.String, "normal")) skill.SpawnFilter|=MTF_NORMAL;
if (strstr(sc.String, "hard")) skill.SpawnFilter|=MTF_HARD;
if (sc.Compare("baby")) skill.SpawnFilter |= 1;
else if (sc.Compare("easy")) skill.SpawnFilter |= 2;
else if (sc.Compare("normal")) skill.SpawnFilter |= 4;
else if (sc.Compare("hard")) skill.SpawnFilter |= 8;
else if (sc.Compare("nightmare")) skill.SpawnFilter |= 16;
}
}
else if (sc.Compare("ACSReturn"))
{

View file

@ -48,14 +48,14 @@ bool AArtiTeleport::Use (bool pickup)
{
unsigned int selections = deathmatchstarts.Size ();
unsigned int i = pr_tele() % selections;
destX = deathmatchstarts[i].x << FRACBITS;
destY = deathmatchstarts[i].y << FRACBITS;
destX = deathmatchstarts[i].x;
destY = deathmatchstarts[i].y;
destAngle = ANG45 * (deathmatchstarts[i].angle/45);
}
else
{
destX = playerstarts[Owner->player - players].x << FRACBITS;
destY = playerstarts[Owner->player - players].y << FRACBITS;
destX = playerstarts[Owner->player - players].x;
destY = playerstarts[Owner->player - players].y;
destAngle = ANG45 * (playerstarts[Owner->player - players].angle/45);
}
P_Teleport (Owner, destX, destY, ONFLOORZ, destAngle, true, true, false);

View file

@ -745,8 +745,10 @@ void ADecal::BeginPlay ()
Super::BeginPlay ();
int decalid = args[0] + (args[1] << 8); // [KS] High byte for decals.
// If no decal is specified, don't try to create one.
if (args[0] != 0 && (tpl = DecalLibrary.GetDecalByNum (args[0])) != 0)
if (decalid != 0 && (tpl = DecalLibrary.GetDecalByNum (decalid)) != 0)
{
if (tpl->PicNum == 65535)
{

View file

@ -332,8 +332,8 @@ void A_RestoreSpecialPosition (AActor *self)
fixed_t _x, _y;
sector_t *sec;
_x = self->SpawnPoint[0] << FRACBITS;
_y = self->SpawnPoint[1] << FRACBITS;
_x = self->SpawnPoint[0];
_y = self->SpawnPoint[1];
sec = P_PointInSector (_x, _y);
self->SetOrigin (_x, _y, sec->floorplane.ZatPoint (_x, _y));
@ -341,7 +341,7 @@ void A_RestoreSpecialPosition (AActor *self)
if (self->flags & MF_SPAWNCEILING)
{
self->z = self->ceilingz - self->height - (self->SpawnPoint[2] << FRACBITS);
self->z = self->ceilingz - self->height - self->SpawnPoint[2];
}
else if (self->flags2 & MF2_SPAWNFLOAT)
{
@ -358,7 +358,7 @@ void A_RestoreSpecialPosition (AActor *self)
}
else
{
self->z = (self->SpawnPoint[2] << FRACBITS) + self->floorz;
self->z = self->SpawnPoint[2] + self->floorz;
if (self->flags2 & MF2_FLOATBOB)
{
self->z += FloatBobOffsets[(self->FloatBobPhase + level.maptime) & 63];

View file

@ -39,10 +39,13 @@ bool APuzzleItem::Use (bool pickup)
}
// [RH] Always play the sound if the use fails.
S_Sound (Owner, CHAN_VOICE, "*puzzfail", 1, ATTN_IDLE);
if (Owner != NULL && Owner->CheckLocalView (consoleplayer))
{
const char *message = GetClass()->Meta.GetMetaString (AIMETA_PuzzFailMessage);
if (message != NULL && *message=='$') message = GStrings[message + 1];
if (message == NULL) message = GStrings("TXT_USEPUZZLEFAILED");
C_MidPrintBold (message);
}
return false;
}

View file

@ -232,11 +232,11 @@ END_DEFAULTS
void ASectorSilencer::BeginPlay ()
{
Super::BeginPlay ();
Sector->MoreFlags |= SECF_SILENT;
Sector->Flags |= SECF_SILENT;
}
void ASectorSilencer::Destroy ()
{
Sector->MoreFlags &= ~SECF_SILENT;
Sector->Flags &= ~SECF_SILENT;
Super::Destroy ();
}

View file

@ -1026,7 +1026,7 @@ void DBaseStatusBar::DrawCrosshair ()
if (crosshairhealth)
{
int health = CPlayer->health;
int health = Scale(CPlayer->health, 100, CPlayer->mo->GetDefault()->health);
if (health >= 85)
{

View file

@ -49,6 +49,7 @@
#include "gl/gl_texture.h"
#include "gl/gl_basic.h"
#include "gl/gl_functions.h"
#include "vectors.h"
UniqueList<GLSkyInfo> UniqueSkies;
UniqueList<GLHorizonInfo> UniqueHorizons;
@ -892,7 +893,7 @@ void GLWall::DoMidTexture(seg_t * seg, bool drawfogboundary,
// set up alpha blending
//
//
if (seg->linedef->alpha)// && seg->linedef->special!=Line_Fogsheet)
if (seg->linedef->Alpha)// && seg->linedef->special!=Line_Fogsheet)
{
bool translucent;
@ -900,13 +901,13 @@ void GLWall::DoMidTexture(seg_t * seg, bool drawfogboundary,
{
case 0:
RenderStyle=STYLE_Translucent;
alpha=(float)seg->linedef->alpha/255.0f;
translucent = seg->linedef->alpha<255 || (gltexture && gltexture->GetTransparent());
alpha=FIXED2FLOAT(seg->linedef->Alpha);
translucent = seg->linedef->Alpha < FRACUNIT || (gltexture && gltexture->GetTransparent());
break;
case WALLF_ADDTRANS:
RenderStyle=STYLE_Add;
alpha=(float)seg->linedef->alpha/255.0f;
alpha=FIXED2FLOAT(seg->linedef->Alpha);
translucent=true;
break;
}

View file

@ -220,3 +220,117 @@ xx(MapSpot)
xx(PatrolPoint)
xx(PatrolSpecial)
xx(Communicator)
// Textmap properties
//xx(X)
//xx(Y)
xx(Height)
//xx(Tid)
//xx(Angle)
xx(Type)
//xx(Special)
xx(Arg0)
xx(Arg1)
xx(Arg2)
xx(Arg3)
xx(Arg4)
xx(Id)
xx(V1)
xx(V2)
xx(Sidefront)
xx(Sideback)
xx(Offsetx)
xx(Offsety)
xx(Texturetop)
xx(Texturebottom)
xx(Texturemiddle)
xx(Sector)
xx(Heightfloor)
xx(Heightceiling)
xx(Lightlevel)
xx(Tag)
xx(Texturefloor)
xx(Textureceiling)
xx(Skill1)
xx(Skill2)
xx(Skill3)
xx(Skill4)
xx(Skill5)
xx(Skill6)
xx(Skill7)
xx(Skill8)
xx(Skill9)
xx(Skill10)
xx(Skill11)
xx(Skill12)
xx(Skill13)
xx(Skill14)
xx(Skill15)
xx(Skill16)
xx(Medium)
xx(Hard)
xx(Ambush)
xx(Dormant)
xx(Class0)
xx(Class1)
xx(Class2)
xx(Class3)
xx(Class4)
xx(Class5)
xx(Class6)
xx(Class7)
xx(Class8)
xx(Class9)
xx(Class10)
xx(Class11)
xx(Class12)
xx(Class13)
xx(Class14)
xx(Class15)
xx(Class16)
xx(Single)
xx(Coop)
xx(Dm)
xx(Translucent)
xx(Invisible)
xx(Friend)
xx(Strifeally)
xx(Standing)
xx(Blocking)
xx(Blockmonsters)
xx(Twosided)
xx(Dontpegtop)
xx(Dontpegbottom)
xx(Secret)
xx(Soundblock)
xx(Dontdraw)
xx(Mapped)
xx(Monsteractivate)
xx(Blockplayers)
xx(Blockeverything)
xx(Zoneboundary)
xx(Jumpover)
xx(Blockfloating)
xx(Clipmidtex)
xx(Wrapmidtex)
xx(Midtex3d)
xx(Checkswitchrange)
xx(Firstsideonly)
xx(Transparent)
xx(Passuse)
xx(Playercross)
xx(Playeruse)
xx(Monstercross)
xx(Impact)
xx(Playerpush)
xx(Missilecross)
xx(Anycross)
xx(Monsteruse)
xx(Monsterpush)
xx(ZDoom)
xx(ZDoomTranslated)

View file

@ -124,12 +124,12 @@ void P_AdjustLine (line_t *line);
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static bool P_LoadBloodMap (BYTE *data, size_t len, mapthing2_t **sprites, int *numsprites);
static bool P_LoadBloodMap (BYTE *data, size_t len, FMapThing **sprites, int *numsprites);
static void LoadSectors (sectortype *bsectors);
static void LoadWalls (walltype *walls, int numwalls, sectortype *bsectors);
static int LoadSprites (spritetype *sprites, int numsprites, sectortype *bsectors, mapthing2_t *mapthings);
static int LoadSprites (spritetype *sprites, int numsprites, sectortype *bsectors, FMapThing *mapthings);
static vertex_t *FindVertex (fixed_t x, fixed_t y);
static void CreateStartSpot (fixed_t *pos, mapthing2_t *start);
static void CreateStartSpot (fixed_t *pos, FMapThing *start);
static void CalcPlane (SlopeWork &slope, secplane_t &plane);
static void Decrypt (void *to, const void *from, int len, int key);
@ -147,7 +147,7 @@ static void Decrypt (void *to, const void *from, int len, int key);
//
//==========================================================================
bool P_LoadBuildMap (BYTE *data, size_t len, mapthing2_t **sprites, int *numspr)
bool P_LoadBuildMap (BYTE *data, size_t len, FMapThing **sprites, int *numspr)
{
if (len < 26)
{
@ -178,7 +178,7 @@ bool P_LoadBuildMap (BYTE *data, size_t len, mapthing2_t **sprites, int *numspr)
(sectortype *)(data + 22));
numsprites = *(WORD *)(data + 24 + numsectors*sizeof(sectortype) + numwalls*sizeof(walltype));
*sprites = new mapthing2_t[numsprites + 1];
*sprites = new FMapThing[numsprites + 1];
CreateStartSpot ((fixed_t *)(data + 4), *sprites);
*numspr = 1 + LoadSprites ((spritetype *)(data + 26 + numsectors*sizeof(sectortype) + numwalls*sizeof(walltype)),
numsprites, (sectortype *)(data + 22), *sprites + 1);
@ -192,7 +192,7 @@ bool P_LoadBuildMap (BYTE *data, size_t len, mapthing2_t **sprites, int *numspr)
//
//==========================================================================
static bool P_LoadBloodMap (BYTE *data, size_t len, mapthing2_t **mapthings, int *numspr)
static bool P_LoadBloodMap (BYTE *data, size_t len, FMapThing **mapthings, int *numspr)
{
BYTE infoBlock[37];
int mapver = data[5];
@ -303,7 +303,7 @@ static bool P_LoadBloodMap (BYTE *data, size_t len, mapthing2_t **mapthings, int
// BUILD info from the map we need. (Sprites are ignored.)
LoadSectors (bsec);
LoadWalls (bwal, numWalls, bsec);
*mapthings = new mapthing2_t[numsprites + 1];
*mapthings = new FMapThing[numsprites + 1];
CreateStartSpot ((fixed_t *)infoBlock, *mapthings);
*numspr = 1 + LoadSprites (bspr, numsprites, bsec, *mapthings + 1);
@ -525,11 +525,11 @@ static void LoadWalls (walltype *walls, int numwalls, sectortype *bsec)
{
if (walls[i].cstat & 512)
{
lines[j].alpha = 255/3;
lines[j].Alpha = FRACUNIT/3;
}
else
{
lines[j].alpha = 255*2/3;
lines[j].Alpha = FRACUNIT*2/3;
}
}
if (walls[i].cstat & 1)
@ -606,7 +606,7 @@ static void LoadWalls (walltype *walls, int numwalls, sectortype *bsec)
//==========================================================================
static int LoadSprites (spritetype *sprites, int numsprites, sectortype *bsectors,
mapthing2_t *mapthings)
FMapThing *mapthings)
{
char name[9];
int picnum;
@ -621,12 +621,14 @@ static int LoadSprites (spritetype *sprites, int numsprites, sectortype *bsector
picnum = TexMan.GetTexture (name, FTexture::TEX_Build);
mapthings[count].thingid = 0;
mapthings[count].x = (short)(sprites[i].x >> 4);
mapthings[count].y = -(short)(sprites[i].y >> 4);
mapthings[count].z = (short)((bsectors[sprites[i].sectnum].floorz - sprites[i].z) >> 8);
mapthings[count].x = (sprites[i].x << 12);
mapthings[count].y = -(sprites[i].y << 12);
mapthings[count].z = (bsectors[sprites[i].sectnum].floorz - sprites[i].z) << 8;
mapthings[count].angle = (((2048-sprites[i].ang) & 2047) * 360) >> 11;
mapthings[count].type = 9988;
mapthings[count].flags = MTF_FIGHTER|MTF_CLERIC|MTF_MAGE|MTF_SINGLE|MTF_COOPERATIVE|MTF_DEATHMATCH|MTF_EASY|MTF_NORMAL|MTF_HARD;
mapthings[count].ClassFilter = 0xffff;
mapthings[count].SkillFilter = 0xffff;
mapthings[count].flags = MTF_SINGLE|MTF_COOPERATIVE|MTF_DEATHMATCH;
mapthings[count].special = 0;
mapthings[count].args[0] = picnum & 255;
@ -671,12 +673,12 @@ vertex_t *FindVertex (fixed_t x, fixed_t y)
//
//==========================================================================
static void CreateStartSpot (fixed_t *pos, mapthing2_t *start)
static void CreateStartSpot (fixed_t *pos, FMapThing *start)
{
short angle = LittleShort(*(WORD *)(&pos[3]));
mapthing2_t mt =
FMapThing mt =
{
0, short(LittleLong(pos[0])>>4), short((-LittleLong(pos[1]))>>4), 0,// tid, x, y, z
0, (LittleLong(pos[0])<<12), ((-LittleLong(pos[1]))<<12), 0,// tid, x, y, z
short(Scale ((2048-angle)&2047, 360, 2048)), 1, // angle, type
7|MTF_SINGLE|224, // flags
// special and args are 0

View file

@ -51,6 +51,7 @@
#include "p_enemy.h"
#include "gstrings.h"
#include "sound/i_music.h"
#include "p_setup.h"
// The conversations as they exist inside a SCRIPTxx lump.
struct Response
@ -107,8 +108,9 @@ static int ConversationPauseTic;
static bool ShowGold;
static void LoadScriptFile (const char *name);
static FStrifeDialogueNode *ReadRetailNode (FWadLump *lump, DWORD &prevSpeakerType);
static FStrifeDialogueNode *ReadTeaserNode (FWadLump *lump, DWORD &prevSpeakerType);
static void LoadScriptFile(FileReader *lump, int numnodes);
static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeakerType);
static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeakerType);
static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses);
static void DrawConversationMenu ();
static void PickConversationReply ();
@ -148,7 +150,15 @@ static const PClass *GetStrifeType (int typenum)
//
//============================================================================
void P_LoadStrifeConversations (const char *mapname)
void P_LoadStrifeConversations (MapData *map, const char *mapname)
{
if (map->Size(ML_CONVERSATION) > 0)
{
LoadScriptFile ("SCRIPT00");
map->Seek(ML_CONVERSATION);
LoadScriptFile (map->file, map->Size(ML_CONVERSATION));
}
else
{
if (strnicmp (mapname, "MAP", 3) != 0)
{
@ -159,6 +169,7 @@ void P_LoadStrifeConversations (const char *mapname)
LoadScriptFile ("SCRIPT00");
LoadScriptFile (scriptname);
}
}
//============================================================================
//
@ -216,17 +227,23 @@ static char *ncopystring (const char *string)
static void LoadScriptFile (const char *name)
{
int lumpnum = Wads.CheckNumForName (name);
int numnodes, i;
DWORD prevSpeakerType;
FStrifeDialogueNode *node;
FWadLump *lump;
FileReader *lump;
if (lumpnum < 0)
{
return;
}
lump = Wads.ReopenLumpNum (lumpnum);
LoadScriptFile(lump, Wads.LumpLength(lumpnum));
}
static void LoadScriptFile(FileReader *lump, int numnodes)
{
int i;
DWORD prevSpeakerType;
FStrifeDialogueNode *node;
numnodes = Wads.LumpLength (lumpnum);
if (!(gameinfo.flags & GI_SHAREWARE))
{
// Strife scripts are always a multiple of 1516 bytes because each entry
@ -247,7 +264,6 @@ static void LoadScriptFile (const char *name)
numnodes /= 1488;
}
lump = Wads.ReopenLumpNum (lumpnum);
prevSpeakerType = 0;
for (i = 0; i < numnodes; ++i)
@ -273,7 +289,7 @@ static void LoadScriptFile (const char *name)
//
//============================================================================
static FStrifeDialogueNode *ReadRetailNode (FWadLump *lump, DWORD &prevSpeakerType)
static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeakerType)
{
FStrifeDialogueNode *node;
Speech speech;
@ -343,7 +359,7 @@ static FStrifeDialogueNode *ReadRetailNode (FWadLump *lump, DWORD &prevSpeakerTy
//
//============================================================================
static FStrifeDialogueNode *ReadTeaserNode (FWadLump *lump, DWORD &prevSpeakerType)
static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeakerType)
{
FStrifeDialogueNode *node;
TeaserSpeech speech;

View file

@ -65,7 +65,9 @@ extern TArray<FStrifeDialogueNode *> StrifeDialogues;
// initialized as part of the actor's setup in infodefaults.cpp.
extern const PClass *StrifeTypes[1001];
void P_LoadStrifeConversations (const char *mapname);
struct MapData;
void P_LoadStrifeConversations (MapData *map, const char *mapname);
void P_FreeStrifeConversations ();
void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveangle);

View file

@ -409,7 +409,7 @@ bool EV_DoDoor (DDoor::EVlDoor type, line_t *line, AActor *thing,
door->m_Direction = 1; // go back up
door->DoorSound (true); // [RH] Make noise
}
else if (GET_SPAC(line->flags) != SPAC_PUSH)
else if (!(line->activation & (SPAC_Push|SPAC_MPush)))
// [RH] activate push doors don't go back down when you
// run into them (otherwise opening them would be
// a real pain).

View file

@ -525,8 +525,8 @@ bool P_Move (AActor *actor)
while (spechit.Pop (ld))
{
// [RH] let monsters push lines, as well as use them
if (((actor->flags4 & MF4_CANUSEWALLS) && P_ActivateLine (ld, actor, 0, SPAC_USE)) ||
((actor->flags2 & MF2_PUSHWALL) && P_ActivateLine (ld, actor, 0, SPAC_PUSH)))
if (((actor->flags4 & MF4_CANUSEWALLS) && P_ActivateLine (ld, actor, 0, SPAC_Use)) ||
((actor->flags2 & MF2_PUSHWALL) && P_ActivateLine (ld, actor, 0, SPAC_Push)))
{
good |= ld == actor->BlockingLine ? 1 : 2;
}

View file

@ -1802,6 +1802,25 @@ FUNC(LS_Sector_ChangeSound)
return rtn;
}
FUNC(LS_Sector_ChangeFlags)
// Sector_ChangeFlags (tag, set, clear)
{
int secNum;
bool rtn;
if (!arg0)
return false;
secNum = -1;
rtn = false;
while ((secNum = P_FindSectorFromTag (arg0, secNum)) >= 0)
{
sectors[secNum].Flags = (sectors[secNum].Flags | arg1) & ~arg2;
rtn = true;
}
return rtn;
}
struct FThinkerCollection
{
int RefNum;
@ -2608,7 +2627,7 @@ FUNC(LS_TranslucentLine)
int linenum = -1;
while ((linenum = P_FindLineFromID (arg0, linenum)) >= 0)
{
lines[linenum].alpha = arg1 & 255;
lines[linenum].Alpha = Scale(clamp(arg1, 0, 255), FRACUNIT, 255);
if (arg2 == 0)
{
sides[lines[linenum].sidenum[0]].Flags &= ~WALLF_ADDTRANS;

View file

@ -87,7 +87,7 @@ void P_UnPredictPlayer ();
extern fixed_t FloatBobOffsets[64];
APlayerPawn *P_SpawnPlayer (mapthing2_t* mthing, bool tempplayer=false);
APlayerPawn *P_SpawnPlayer (FMapThing *mthing, bool tempplayer=false);
void P_ThrustMobj (AActor *mo, angle_t angle, fixed_t move);
int P_FaceMobj (AActor *source, AActor *target, angle_t *delta);

View file

@ -80,7 +80,6 @@ TArray<line_t *> spechit;
// Temporary holder for thing_sectorlist threads
msecnode_t* sector_list = NULL; // phares 3/16/98
//==========================================================================
//
// PIT_FindFloorCeiling
@ -1342,7 +1341,7 @@ static void CheckForPushSpecial (line_t *line, int side, AActor *mobj)
{
if (mobj->flags2 & MF2_PUSHWALL)
{
P_ActivateLine (line, mobj, side, SPAC_PUSH);
P_ActivateLine (line, mobj, side, SPAC_Push);
}
else if (mobj->flags2 & MF2_IMPACT)
{
@ -1350,11 +1349,11 @@ static void CheckForPushSpecial (line_t *line, int side, AActor *mobj)
!(mobj->flags & MF_MISSILE) ||
(mobj->target == NULL))
{
P_ActivateLine (line, mobj, side, SPAC_IMPACT);
P_ActivateLine (line, mobj, side, SPAC_Impact);
}
else
{
P_ActivateLine (line, mobj->target, side, SPAC_IMPACT);
P_ActivateLine (line, mobj->target, side, SPAC_Impact);
}
}
}
@ -1598,27 +1597,25 @@ bool P_TryMove (AActor *thing, fixed_t x, fixed_t y,
{
if (thing->player)
{
P_ActivateLine (ld, thing, oldside, SPAC_CROSS);
P_ActivateLine (ld, thing, oldside, SPAC_Cross);
}
else if (thing->flags2 & MF2_MCROSS)
{
P_ActivateLine (ld, thing, oldside, SPAC_MCROSS);
P_ActivateLine (ld, thing, oldside, SPAC_MCross);
}
else if (thing->flags2 & MF2_PCROSS)
{
P_ActivateLine (ld, thing, oldside, SPAC_PCROSS);
P_ActivateLine (ld, thing, oldside, SPAC_PCross);
}
else if ((ld->special == Teleport ||
ld->special == Teleport_NoFog ||
ld->special == Teleport_Line))
{ // [RH] Just a little hack for BOOM compatibility
P_ActivateLine (ld, thing, oldside, SPAC_MCROSS);
P_ActivateLine (ld, thing, oldside, SPAC_MCross);
}
else
{
// I don't think allowing non-monsters to activate
// monster-allowed lines will hurt Hexen compatibility.
P_ActivateLine (ld, thing, oldside, SPAC_OTHERCROSS);
P_ActivateLine (ld, thing, oldside, SPAC_AnyCross);
}
}
}
@ -3432,8 +3429,7 @@ bool P_UseTraverse(AActor *usething, fixed_t endx, fixed_t endy, bool &foundline
if (open.range <= 0) return false;
else continue;
}
if (in->d.line->special == 0 || (GET_SPAC(in->d.line->flags) != SPAC_USETHROUGH &&
GET_SPAC(in->d.line->flags) != SPAC_USE))
if (in->d.line->special == 0 || !(in->d.line->activation & (SPAC_Use|SPAC_UseThrough)))
{
blocked:
if (in->d.line->flags & ML_BLOCKEVERYTHING)
@ -3483,7 +3479,7 @@ bool P_UseTraverse(AActor *usething, fixed_t endx, fixed_t endy, bool &foundline
//return in->d.line->backsector != NULL; // don't use back side
goto blocked; // do a proper check for back sides of triggers
P_ActivateLine (in->d.line, usething, 0, SPAC_USE);
P_ActivateLine (in->d.line, usething, 0, SPAC_Use);
//WAS can't use more than one special line in a row
//jff 3/21/98 NOW multiple use allowed with enabling line flag
@ -3492,12 +3488,12 @@ bool P_UseTraverse(AActor *usething, fixed_t endx, fixed_t endy, bool &foundline
// it through, including SPAC_USETHROUGH.
if (i_compatflags & COMPATF_USEBLOCKING)
{
if (GET_SPAC(in->d.line->flags) == SPAC_USETHROUGH) continue;
if (in->d.line->activation & SPAC_UseThrough) continue;
else return true;
}
else
{
if (GET_SPAC(in->d.line->flags) != SPAC_USE) continue;
if (!(in->d.line->activation & SPAC_Use)) continue;
else return true;
}
}

View file

@ -418,7 +418,7 @@ void AActor::Serialize (FArchive &arc)
}
}
void MapThing::Serialize (FArchive &arc)
void FMapThing::Serialize (FArchive &arc)
{
arc << thingid << x << y << z << angle << type << flags << special
<< args[0] << args[1] << args[2] << args[3] << args[4];
@ -1862,6 +1862,8 @@ void P_MonsterFallingDamage (AActor *mo)
if (!(level.flags&LEVEL_MONSTERFALLINGDAMAGE))
return;
if (mo->floorsector->Flags & SECF_NOFALLINGDAMAGE)
return;
mom = abs(mo->momz);
if (mom > 35*FRACUNIT)
@ -2247,19 +2249,19 @@ void P_NightmareRespawn (AActor *mobj)
else if (info->flags2 & MF2_SPAWNFLOAT)
z = FLOATRANDZ;
else if (info->flags2 & MF2_FLOATBOB)
z = mobj->SpawnPoint[2] << FRACBITS;
z = mobj->SpawnPoint[2];
else
z = ONFLOORZ;
// spawn it
x = mobj->SpawnPoint[0] << FRACBITS;
y = mobj->SpawnPoint[1] << FRACBITS;
x = mobj->SpawnPoint[0];
y = mobj->SpawnPoint[1];
mo = Spawn (RUNTIME_TYPE(mobj), x, y, z, NO_REPLACE);
if (z == ONFLOORZ)
mo->z += mo->SpawnPoint[2] << FRACBITS;
mo->z += mo->SpawnPoint[2];
else if (z == ONCEILINGZ)
mo->z -= mo->SpawnPoint[2] << FRACBITS;
mo->z -= mo->SpawnPoint[2];
// something is occupying its position?
if (!P_TestMobjLocation (mo))
@ -3364,8 +3366,8 @@ AActor *AActor::StaticSpawn (const PClass *type, fixed_t ix, fixed_t iy, fixed_t
actor->ceilingsector = actor->Sector;
}
actor->SpawnPoint[0] = ix >> FRACBITS;
actor->SpawnPoint[1] = iy >> FRACBITS;
actor->SpawnPoint[0] = ix;
actor->SpawnPoint[1] = iy;
if (iz == ONFLOORZ)
{
@ -3390,7 +3392,7 @@ AActor *AActor::StaticSpawn (const PClass *type, fixed_t ix, fixed_t iy, fixed_t
}
else
{
actor->SpawnPoint[2] = (actor->z - actor->floorz) >> FRACBITS;
actor->SpawnPoint[2] = (actor->z - actor->floorz);
}
if (actor->flags2 & MF2_FLOATBOB)
@ -3625,7 +3627,7 @@ EXTERN_CVAR (Bool, chasedemo)
extern bool demonew;
APlayerPawn *P_SpawnPlayer (mapthing2_t *mthing, bool tempplayer)
APlayerPawn *P_SpawnPlayer (FMapThing *mthing, bool tempplayer)
{
int playernum;
player_t *p;
@ -3696,8 +3698,8 @@ APlayerPawn *P_SpawnPlayer (mapthing2_t *mthing, bool tempplayer)
}
else
{
spawn_x = mthing->x << FRACBITS;
spawn_y = mthing->y << FRACBITS;
spawn_x = mthing->x;
spawn_y = mthing->y;
spawn_angle = ANG45 * (mthing->angle/45);
}
@ -3863,7 +3865,7 @@ APlayerPawn *P_SpawnPlayer (mapthing2_t *mthing, bool tempplayer)
// already be in host byte order.
//
// [RH] position is used to weed out unwanted start spots
AActor *P_SpawnMapThing (mapthing2_t *mthing, int position)
AActor *P_SpawnMapThing (FMapThing *mthing, int position)
{
const PClass *i;
int mask;
@ -3911,8 +3913,8 @@ AActor *P_SpawnMapThing (mapthing2_t *mthing, int position)
{
polyspawns_t *polyspawn = new polyspawns_t;
polyspawn->next = polyspawns;
polyspawn->x = mthing->x << FRACBITS;
polyspawn->y = mthing->y << FRACBITS;
polyspawn->x = mthing->x;
polyspawn->y = mthing->y;
polyspawn->angle = mthing->angle;
polyspawn->type = mthing->type;
polyspawns = polyspawn;
@ -3960,7 +3962,7 @@ AActor *P_SpawnMapThing (mapthing2_t *mthing, int position)
}
mask = G_SkillProperty(SKILLP_SpawnFilter);
if (!(mthing->flags & mask))
if (!(mthing->SkillFilter & mask))
{
return NULL;
}
@ -3970,7 +3972,7 @@ AActor *P_SpawnMapThing (mapthing2_t *mthing, int position)
if (!multiplayer)
{ // Single player
int spawnmask = players[consoleplayer].GetSpawnClass();
if (spawnmask != 0 && (mthing->flags & spawnmask) == 0)
if (spawnmask != 0 && (mthing->ClassFilter & spawnmask) == 0)
{ // Not for current class
return NULL;
}
@ -4013,8 +4015,7 @@ AActor *P_SpawnMapThing (mapthing2_t *mthing, int position)
// [RH] sound sequence overriders
if (mthing->type >= 1400 && mthing->type < 1410)
{
P_PointInSector (mthing->x<<FRACBITS,
mthing->y<<FRACBITS)->seqType = mthing->type - 1400;
P_PointInSector (mthing->x, mthing->y)->seqType = mthing->type - 1400;
return NULL;
}
else if (mthing->type == 1411)
@ -4032,8 +4033,7 @@ AActor *P_SpawnMapThing (mapthing2_t *mthing, int position)
}
else
{
P_PointInSector (mthing->x << FRACBITS,
mthing->y << FRACBITS)->seqType = type;
P_PointInSector (mthing->x, mthing->y)->seqType = type;
}
return NULL;
}
@ -4057,7 +4057,7 @@ AActor *P_SpawnMapThing (mapthing2_t *mthing, int position)
// [RH] Don't die if the map tries to spawn an unknown thing
Printf ("Unknown type %i at (%i, %i)\n",
mthing->type,
mthing->x, mthing->y);
mthing->x>>FRACBITS, mthing->y>>FRACBITS);
i = PClass::FindClass("Unknown");
}
// [RH] If the thing's corresponding sprite has no frames, also map
@ -4073,7 +4073,7 @@ AActor *P_SpawnMapThing (mapthing2_t *mthing, int position)
sprites[defaults->SpawnState->sprite.index].numframes == 0)
{
Printf ("%s at (%i, %i) has no frames\n",
i->TypeName.GetChars(), mthing->x, mthing->y);
i->TypeName.GetChars(), mthing->x>>FRACBITS, mthing->y>>FRACBITS);
i = PClass::FindClass("Unknown");
}
}
@ -4127,8 +4127,8 @@ AActor *P_SpawnMapThing (mapthing2_t *mthing, int position)
}
// spawn it
x = mthing->x << FRACBITS;
y = mthing->y << FRACBITS;
x = mthing->x;
y = mthing->y;
if (info->flags & MF_SPAWNCEILING)
z = ONCEILINGZ;
@ -4142,9 +4142,9 @@ AActor *P_SpawnMapThing (mapthing2_t *mthing, int position)
SpawningMapThing = false;
if (z == ONFLOORZ)
mobj->z += mthing->z << FRACBITS;
mobj->z += mthing->z;
else if (z == ONCEILINGZ)
mobj->z -= mthing->z << FRACBITS;
mobj->z -= mthing->z;
mobj->SpawnPoint[0] = mthing->x;
mobj->SpawnPoint[1] = mthing->y;
@ -4662,7 +4662,7 @@ void P_PlaySpawnSound(AActor *missile, AActor *spawner)
{
// If there is no spawner use the spawn position.
// But not in a silenced sector.
if (!(missile->Sector->MoreFlags & SECF_SILENT))
if (!(missile->Sector->Flags & SECF_SILENT))
S_SoundID (&missile->x, CHAN_WEAPON, missile->SeeSound, 1, ATTN_NORM);
}
}

View file

@ -330,6 +330,7 @@ void P_SerializeWorld (FArchive &arc)
<< sec->CeilingFlags
<< sec->sky
<< sec->MoreFlags
<< sec->Flags
<< sec->FloorSkyBox << sec->CeilingSkyBox
<< sec->ZoneNumber
<< sec->oldspecial;
@ -357,8 +358,9 @@ void P_SerializeWorld (FArchive &arc)
for (i = 0, li = lines; i < numlines; i++, li++)
{
arc << li->flags
<< li->activation
<< li->special
<< li->alpha
<< li->Alpha
<< li->id
<< li->args[0] << li->args[1] << li->args[2] << li->args[3] << li->args[4];

File diff suppressed because it is too large Load diff

View file

@ -34,6 +34,7 @@ struct MapData
bool HasBehavior;
bool CloseOnDestruct;
bool Encrypted;
bool isText;
int lumpnum;
FileReader * file;
@ -45,6 +46,7 @@ struct MapData
HasBehavior = false;
CloseOnDestruct = true;
Encrypted = false;
isText = false;
}
~MapData()
@ -83,6 +85,7 @@ struct MapData
MapData * P_OpenMapData(const char * mapname);
bool P_CheckMapData(const char * mapname);
// NOT called by W_Ticker. Fixme. [RH] Is that bad?
//
// [RH] The only parameter used is mapname, so I removed playermask and skill.

View file

@ -256,8 +256,8 @@ bool SightCheck::P_SightCheckLine (line_t *ld)
return false;
}
if (SeePastShootableLines &&
(GET_SPAC(ld->flags) != SPAC_IMPACT ||
(ld->special != ACS_Execute && ld->special != ACS_ExecuteAlways)) ||
!(ld->activation & SPAC_Impact) ||
(ld->special != ACS_Execute && ld->special != ACS_ExecuteAlways) ||
(ld->args[1] != 0 && ld->args[1] != level.levelnum))
{
// Pretend the other side is invisible if this is not an impact line
@ -537,8 +537,6 @@ sightcounts[1]++;
if ((mapxstep | mapystep) == 0)
break;
switch ((((yintercept >> FRACBITS) == mapy) << 1) | ((xintercept >> FRACBITS) == mapx))
{
case 0: // neither xintercept nor yintercept match!

571
src/p_slopes.cpp Normal file
View file

@ -0,0 +1,571 @@
/*
** p_slopes.cpp
** Slope creation
**
**---------------------------------------------------------------------------
** Copyright 1998-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.
**---------------------------------------------------------------------------
**
*/
#include "doomtype.h"
#include "vectors.h"
#include "p_local.h"
#include "r_data.h"
#include "cmdlib.h"
#include "p_lnspec.h"
//===========================================================================
//
// P_SpawnSlopeMakers
//
//===========================================================================
static void P_SlopeLineToPoint (int lineid, fixed_t x, fixed_t y, fixed_t z, bool slopeCeil)
{
int linenum = -1;
while ((linenum = P_FindLineFromID (lineid, linenum)) != -1)
{
const line_t *line = &lines[linenum];
sector_t *sec;
secplane_t *plane;
if (P_PointOnLineSide (x, y, line) == 0)
{
sec = line->frontsector;
}
else
{
sec = line->backsector;
}
if (sec == NULL)
{
continue;
}
if (slopeCeil)
{
plane = &sec->ceilingplane;
}
else
{
plane = &sec->floorplane;
}
FVector3 p, v1, v2, cross;
p[0] = FIXED2FLOAT (line->v1->x);
p[1] = FIXED2FLOAT (line->v1->y);
p[2] = FIXED2FLOAT (plane->ZatPoint (line->v1->x, line->v1->y));
v1[0] = FIXED2FLOAT (line->dx);
v1[1] = FIXED2FLOAT (line->dy);
v1[2] = FIXED2FLOAT (plane->ZatPoint (line->v2->x, line->v2->y)) - p[2];
v2[0] = FIXED2FLOAT (x - line->v1->x);
v2[1] = FIXED2FLOAT (y - line->v1->y);
v2[2] = FIXED2FLOAT (z) - p[2];
cross = v1 ^ v2;
double len = cross.Length();
if (len == 0)
{
Printf ("Slope thing at (%d,%d) lies directly on its target line.\n", int(x>>16), int(y>>16));
return;
}
cross /= len;
// Fix backward normals
if ((cross.Z < 0 && !slopeCeil) || (cross.Z > 0 && slopeCeil))
{
cross = -cross;
}
plane->a = FLOAT2FIXED (cross[0]);
plane->b = FLOAT2FIXED (cross[1]);
plane->c = FLOAT2FIXED (cross[2]);
//plane->ic = FLOAT2FIXED (1.f/cross[2]);
plane->ic = DivScale32 (1, plane->c);
plane->d = -TMulScale16 (plane->a, x,
plane->b, y,
plane->c, z);
}
}
//===========================================================================
//
// P_CopyPlane
//
//===========================================================================
static void P_CopyPlane (int tag, fixed_t x, fixed_t y, bool copyCeil)
{
sector_t *dest = P_PointInSector (x, y);
sector_t *source;
int secnum;
size_t planeofs;
secnum = P_FindSectorFromTag (tag, -1);
if (secnum == -1)
{
return;
}
source = &sectors[secnum];
if (copyCeil)
{
planeofs = myoffsetof(sector_t, ceilingplane);
}
else
{
planeofs = myoffsetof(sector_t, floorplane);
}
*(secplane_t *)((BYTE *)dest + planeofs) = *(secplane_t *)((BYTE *)source + planeofs);
}
//===========================================================================
//
// P_SetSlope
//
//===========================================================================
void P_SetSlope (secplane_t *plane, bool setCeil, int xyangi, int zangi,
fixed_t x, fixed_t y, fixed_t z)
{
angle_t xyang;
angle_t zang;
if (zangi >= 180)
{
zang = ANGLE_180-ANGLE_1;
}
else if (zangi <= 0)
{
zang = ANGLE_1;
}
else
{
zang = Scale (zangi, ANGLE_90, 90);
}
if (setCeil)
{
zang += ANGLE_180;
}
zang >>= ANGLETOFINESHIFT;
xyang = (angle_t)Scale (xyangi, ANGLE_90, 90 << ANGLETOFINESHIFT);
FVector3 norm;
norm[0] = float(finecosine[zang]) * float(finecosine[xyang]);
norm[1] = float(finecosine[zang]) * float(finesine[xyang]);
norm[2] = float(finesine[zang]) * 65536.f;
norm.MakeUnit();
plane->a = (int)(norm[0] * 65536.f);
plane->b = (int)(norm[1] * 65536.f);
plane->c = (int)(norm[2] * 65536.f);
//plane->ic = (int)(65536.f / norm[2]);
plane->ic = DivScale32 (1, plane->c);
plane->d = -TMulScale16 (plane->a, x,
plane->b, y,
plane->c, z);
}
//===========================================================================
//
// P_VavoomSlope
//
//===========================================================================
void P_VavoomSlope(sector_t * sec, int id, fixed_t x, fixed_t y, fixed_t z, int which)
{
for (int i=0;i<sec->linecount;i++)
{
line_t * l=sec->lines[i];
if (l->args[0]==id)
{
FVector3 v1, v2, cross;
secplane_t *srcplane = (which == 0) ? &sec->floorplane : &sec->ceilingplane;
fixed_t srcheight = (which == 0) ? sec->floortexz : sec->ceilingtexz;
v1[0] = FIXED2FLOAT (x - l->v2->x);
v1[1] = FIXED2FLOAT (y - l->v2->y);
v1[2] = FIXED2FLOAT (z - srcheight);
v2[0] = FIXED2FLOAT (x - l->v1->x);
v2[1] = FIXED2FLOAT (y - l->v1->y);
v2[2] = FIXED2FLOAT (z - srcheight);
cross = v1 ^ v2;
double len = cross.Length();
if (len == 0)
{
Printf ("Slope thing at (%d,%d) lies directly on its target line.\n", int(x>>16), int(y>>16));
return;
}
cross /= len;
// Fix backward normals
if ((cross.Z < 0 && which == 0) || (cross.Z > 0 && which == 1))
{
cross = -cross;
}
srcplane->a = FLOAT2FIXED (cross[0]);
srcplane->b = FLOAT2FIXED (cross[1]);
srcplane->c = FLOAT2FIXED (cross[2]);
//plane->ic = FLOAT2FIXED (1.f/cross[2]);
srcplane->ic = DivScale32 (1, srcplane->c);
srcplane->d = -TMulScale16 (srcplane->a, x,
srcplane->b, y,
srcplane->c, z);
return;
}
}
}
enum
{
THING_SlopeFloorPointLine = 9500,
THING_SlopeCeilingPointLine = 9501,
THING_SetFloorSlope = 9502,
THING_SetCeilingSlope = 9503,
THING_CopyFloorPlane = 9510,
THING_CopyCeilingPlane = 9511,
THING_VavoomFloor=1500,
THING_VavoomCeiling=1501,
THING_VertexFloorZ=1504,
THING_VertexCeilingZ=1505,
};
//==========================================================================
//
// P_SetSlopesFromVertexHeights
//
//==========================================================================
static void P_SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt)
{
TMap<int, fixed_t> vt_heights[2];
FMapThing *mt;
bool vt_found = false;
for (mt = firstmt; mt < lastmt; ++mt)
{
if (mt->type == THING_VertexFloorZ || mt->type == THING_VertexCeilingZ)
{
for(int i=0; i<numvertexes; i++)
{
if (vertexes[i].x == mt->x && vertexes[i].y == mt->y)
{
if (mt->type == THING_VertexFloorZ)
{
vt_heights[0][i] = mt->z;
}
else
{
vt_heights[1][i] = mt->z;
}
vt_found = true;
}
}
mt->type = 0;
}
}
if (vt_found)
{
for (int i = 0; i < numsectors; i++)
{
sector_t *sec = &sectors[i];
if (sec->linecount != 3) continue; // only works with triangular sectors
FVector3 vt1, vt2, vt3, cross;
FVector3 vec1, vec2;
int vi1, vi2, vi3;
vi1 = sec->lines[0]->v1 - vertexes;
vi2 = sec->lines[0]->v2 - vertexes;
vi3 = (sec->lines[1]->v1 == sec->lines[0]->v1 || sec->lines[1]->v1 == sec->lines[0]->v2)?
sec->lines[1]->v2 - vertexes : sec->lines[1]->v1 - vertexes;
vt1.X = FIXED2FLOAT(vertexes[vi1].x);
vt1.Y = FIXED2FLOAT(vertexes[vi1].y);
vt2.X = FIXED2FLOAT(vertexes[vi2].x);
vt2.Y = FIXED2FLOAT(vertexes[vi2].y);
vt3.X = FIXED2FLOAT(vertexes[vi3].x);
vt3.Y = FIXED2FLOAT(vertexes[vi3].y);
for(int j=0; j<2; j++)
{
fixed_t *h1 = vt_heights[j].CheckKey(vi1);
fixed_t *h2 = vt_heights[j].CheckKey(vi2);
fixed_t *h3 = vt_heights[j].CheckKey(vi3);
fixed_t z3;
if (h1==NULL && h2==NULL && h3==NULL) continue;
vt1.Z = FIXED2FLOAT(h1? *h1 : j==0? sec->floortexz : sec->ceilingtexz);
vt2.Z = FIXED2FLOAT(h2? *h2 : j==0? sec->floortexz : sec->ceilingtexz);
z3 = h3? *h3 : j==0? sec->floortexz : sec->ceilingtexz;
vt3.Z = FIXED2FLOAT(z3);
if (P_PointOnLineSide(vertexes[vi3].x, vertexes[vi3].y, sec->lines[0]) == 0)
{
vec1 = vt2 - vt3;
vec2 = vt1 - vt3;
}
else
{
vec1 = vt1 - vt3;
vec2 = vt2 - vt3;
}
FVector3 cross = vec1 ^ vec2;
double len = cross.Length();
if (len == 0)
{
// Only happens when all vertices in this sector are on the same line.
// Let's just ignore this case.
continue;
}
cross /= len;
// Fix backward normals
if ((cross.Z < 0 && j == 0) || (cross.Z > 0 && j == 1))
{
cross = -cross;
}
secplane_t *srcplane = j==0? &sec->floorplane : &sec->ceilingplane;
srcplane->a = FLOAT2FIXED (cross[0]);
srcplane->b = FLOAT2FIXED (cross[1]);
srcplane->c = FLOAT2FIXED (cross[2]);
srcplane->ic = DivScale32 (1, srcplane->c);
srcplane->d = -TMulScale16 (srcplane->a, vertexes[vi3].x,
srcplane->b, vertexes[vi3].y,
srcplane->c, z3);
}
}
}
}
//===========================================================================
//
// P_SpawnSlopeMakers
//
//===========================================================================
void P_SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt)
{
FMapThing *mt;
for (mt = firstmt; mt < lastmt; ++mt)
{
if ((mt->type >= THING_SlopeFloorPointLine &&
mt->type <= THING_SetCeilingSlope) ||
mt->type==THING_VavoomFloor || mt->type==THING_VavoomCeiling)
{
fixed_t x, y, z;
secplane_t *refplane;
sector_t *sec;
x = mt->x;
y = mt->y;
sec = P_PointInSector (x, y);
if (mt->type & 1)
{
refplane = &sec->ceilingplane;
}
else
{
refplane = &sec->floorplane;
}
z = refplane->ZatPoint (x, y) + (mt->z);
if (mt->type==THING_VavoomFloor || mt->type==THING_VavoomCeiling)
{
P_VavoomSlope(sec, mt->thingid, x, y, mt->z, mt->type & 1);
}
else if (mt->type <= THING_SlopeCeilingPointLine)
{
P_SlopeLineToPoint (mt->args[0], x, y, z, mt->type & 1);
}
else
{
P_SetSlope (refplane, mt->type & 1, mt->angle, mt->args[0], x, y, z);
}
mt->type = 0;
}
}
for (mt = firstmt; mt < lastmt; ++mt)
{
if (mt->type == THING_CopyFloorPlane ||
mt->type == THING_CopyCeilingPlane)
{
P_CopyPlane (mt->args[0], mt->x, mt->y, mt->type & 1);
mt->type = 0;
}
}
P_SetSlopesFromVertexHeights(firstmt, lastmt);
}
//===========================================================================
//
// [RH] Set slopes for sectors, based on line specials
//
// P_AlignPlane
//
// Aligns the floor or ceiling of a sector to the corresponding plane
// on the other side of the reference line. (By definition, line must be
// two-sided.)
//
// If (which & 1), sets floor.
// If (which & 2), sets ceiling.
//
//===========================================================================
static void P_AlignPlane (sector_t *sec, line_t *line, int which)
{
sector_t *refsec;
int bestdist;
vertex_t *refvert = (*sec->lines)->v1; // Shut up, GCC
int i;
line_t **probe;
if (line->backsector == NULL)
return;
// Find furthest vertex from the reference line. It, along with the two ends
// of the line will define the plane.
bestdist = 0;
for (i = sec->linecount*2, probe = sec->lines; i > 0; i--)
{
int dist;
vertex_t *vert;
// Do calculations with only the upper bits, because the lower ones
// are all zero, and we would overflow for a lot of distances if we
// kept them around.
if (i & 1)
vert = (*probe++)->v2;
else
vert = (*probe)->v1;
dist = abs (((line->v1->y - vert->y) >> FRACBITS) * (line->dx >> FRACBITS) -
((line->v1->x - vert->x) >> FRACBITS) * (line->dy >> FRACBITS));
if (dist > bestdist)
{
bestdist = dist;
refvert = vert;
}
}
refsec = line->frontsector == sec ? line->backsector : line->frontsector;
FVector3 p, v1, v2, cross;
const secplane_t *refplane;
secplane_t *srcplane;
fixed_t srcheight, destheight;
refplane = (which == 0) ? &refsec->floorplane : &refsec->ceilingplane;
srcplane = (which == 0) ? &sec->floorplane : &sec->ceilingplane;
srcheight = (which == 0) ? sec->floortexz : sec->ceilingtexz;
destheight = (which == 0) ? refsec->floortexz : refsec->ceilingtexz;
p[0] = FIXED2FLOAT (line->v1->x);
p[1] = FIXED2FLOAT (line->v1->y);
p[2] = FIXED2FLOAT (destheight);
v1[0] = FIXED2FLOAT (line->dx);
v1[1] = FIXED2FLOAT (line->dy);
v1[2] = 0;
v2[0] = FIXED2FLOAT (refvert->x - line->v1->x);
v2[1] = FIXED2FLOAT (refvert->y - line->v1->y);
v2[2] = FIXED2FLOAT (srcheight - destheight);
cross = (v1 ^ v2).Unit();
// Fix backward normals
if ((cross.Z < 0 && which == 0) || (cross.Z > 0 && which == 1))
{
cross = -cross;
}
srcplane->a = FLOAT2FIXED (cross[0]);
srcplane->b = FLOAT2FIXED (cross[1]);
srcplane->c = FLOAT2FIXED (cross[2]);
//srcplane->ic = FLOAT2FIXED (1.f/cross[2]);
srcplane->ic = DivScale32 (1, srcplane->c);
srcplane->d = -TMulScale16 (srcplane->a, line->v1->x,
srcplane->b, line->v1->y,
srcplane->c, destheight);
}
//===========================================================================
//
// P_SetSlopes
//
//===========================================================================
void P_SetSlopes ()
{
int i, s;
for (i = 0; i < numlines; i++)
{
if (lines[i].special == Plane_Align)
{
lines[i].special = 0;
lines[i].id = lines[i].args[2];
if (lines[i].backsector != NULL)
{
// args[0] is for floor, args[1] is for ceiling
//
// As a special case, if args[1] is 0,
// then args[0], bits 2-3 are for ceiling.
for (s = 0; s < 2; s++)
{
int bits = lines[i].args[s] & 3;
if (s == 1 && bits == 0)
bits = (lines[i].args[0] >> 2) & 3;
if (bits == 1) // align front side to back
P_AlignPlane (lines[i].frontsector, lines + i, s);
else if (bits == 2) // align back side to front
P_AlignPlane (lines[i].backsector, lines + i, s);
}
}
}
}
}

View file

@ -206,11 +206,7 @@ bool P_ActivateLine (line_t *line, AActor *mo, int side, int activationType)
{
return false;
}
lineActivation = GET_SPAC(line->flags);
if (lineActivation == SPAC_PTOUCH)
{
lineActivation = activationType;
}
lineActivation = line->activation;
repeat = line->flags & ML_REPEAT_SPECIAL;
buttonSuccess = false;
buttonSuccess = LineSpecials[line->special]
@ -223,23 +219,16 @@ bool P_ActivateLine (line_t *line, AActor *mo, int side, int activationType)
{ // clear the special on non-retriggerable lines
line->special = 0;
}
// Graf Zahl says: "If you check out the WolfenDoom WAD Operation Rheingold 2
// you will find that there are lots of shoot triggers that don't have any
// attached sector. In Doom2.exe such switches are changed and this WAD uses
// this to create a lot of shootable stuff on walls (like clocks that get
// destroyed etc.) None of those work in ZDoom. Interestingly this works in
// almost no source port."
// begin of changed code
if (buttonSuccess)
{
if (lineActivation == SPAC_USE || lineActivation == SPAC_IMPACT || lineActivation == SPAC_USETHROUGH)
if (activationType == SPAC_Use || activationType == SPAC_Impact)
{
P_ChangeSwitchTexture (&sides[line->sidenum[0]], repeat, special);
}
}
// some old WADs use this method to create walls that change the texture when shot.
else if (lineActivation == SPAC_IMPACT && // only for shootable triggers
else if (activationType == SPAC_Impact && // only for shootable triggers
!(level.flags & LEVEL_HEXENFORMAT) && // only in Doom-format maps
!repeat && // only non-repeatable triggers
(special<Generic_Floor || special>Generic_Crusher) && // not for Boom's generalized linedefs
@ -266,53 +255,58 @@ bool P_ActivateLine (line_t *line, AActor *mo, int side, int activationType)
bool P_TestActivateLine (line_t *line, AActor *mo, int side, int activationType)
{
int lineActivation;
int lineActivation = line->activation;
lineActivation = GET_SPAC(line->flags);
if (lineActivation == SPAC_PTOUCH &&
(activationType == SPAC_PCROSS || activationType == SPAC_IMPACT))
if (line->flags & ML_FIRSTSIDEONLY && side == 1)
{
lineActivation = activationType;
return false;
}
else if (lineActivation == SPAC_USETHROUGH)
if (lineActivation & SPAC_UseThrough)
{
lineActivation = SPAC_USE;
lineActivation |= SPAC_Use;
}
else if (line->special == Teleport &&
lineActivation == SPAC_CROSS &&
activationType == SPAC_PCROSS &&
(lineActivation & SPAC_Cross) &&
activationType == SPAC_PCross &&
mo != NULL &&
mo->flags & MF_MISSILE)
{ // Let missiles use regular player teleports
lineActivation = SPAC_PCROSS;
lineActivation |= SPAC_PCross;
}
// BOOM's generalized line types that allow monster use can actually be
// activated by anything!
if (activationType == SPAC_OTHERCROSS)
// activated by anything except projectiles.
if (lineActivation & SPAC_AnyCross)
{
if (lineActivation == SPAC_CROSS && line->special >= Generic_Floor &&
line->special <= Generic_Crusher && !(mo->flags2&MF2_NOTELEPORT))
{
return (line->flags & ML_MONSTERSCANACTIVATE) != 0;
lineActivation |= SPAC_Cross|SPAC_MCross;
}
return false;
}
if (lineActivation != activationType &&
!(activationType == SPAC_MCROSS && lineActivation == SPAC_CROSS))
{
return false;
}
if (activationType == SPAC_USE)
if (activationType == SPAC_Use)
{
if (!P_CheckSwitchRange(mo, line, side)) return false;
}
if ((lineActivation & activationType) == 0)
{
if (activationType == SPAC_Use && (lineActivation & SPAC_MUse) && !mo->player && mo->flags4 & MF4_CANUSEWALLS)
{
return true;
}
if (activationType == SPAC_Push && (lineActivation & SPAC_MPush) && !mo->player && mo->flags2 & MF2_PUSHWALL)
{
return true;
}
if (activationType != SPAC_MCross || lineActivation != SPAC_Cross)
{
return false;
}
}
if (mo && !mo->player &&
!(mo->flags & MF_MISSILE) &&
!(line->flags & ML_MONSTERSCANACTIVATE) &&
(activationType != SPAC_MCROSS || lineActivation != SPAC_MCROSS))
{ // [RH] monsters' ability to activate this line depends on its type
(activationType != SPAC_MCross || (!(lineActivation & SPAC_MCross))))
{
// [RH] monsters' ability to activate this line depends on its type
// In Hexen, only MCROSS lines could be activated by monsters. With
// lax activation checks, monsters can also activate certain lines
// even without them being marked as monster activate-able. This is
@ -321,22 +315,31 @@ bool P_TestActivateLine (line_t *line, AActor *mo, int side, int activationType)
{
return false;
}
if ((activationType == SPAC_USE || activationType == SPAC_PUSH)
if ((activationType == SPAC_Use || activationType == SPAC_Push)
&& (line->flags & ML_SECRET))
return false; // never open secret doors
bool noway = true;
switch (lineActivation)
switch (activationType)
{
case SPAC_IMPACT:
case SPAC_PCROSS:
// shouldn't really be here if not a missile
case SPAC_MCROSS:
case SPAC_Use:
case SPAC_Push:
switch (line->special)
{
case Door_Raise:
if (line->args[0] == 0 && line->args[1] < 64)
noway = false;
break;
case Teleport:
case Teleport_NoFog:
noway = false;
}
break;
case SPAC_CROSS:
case SPAC_MCross:
if (!(lineActivation & SPAC_MCross))
{
switch (line->special)
{
case Door_Raise:
@ -351,26 +354,16 @@ bool P_TestActivateLine (line_t *line, AActor *mo, int side, int activationType)
case Plat_DownWaitUpStay:
noway = false;
}
}
else noway = false;
break;
case SPAC_USE:
case SPAC_PUSH:
switch (line->special)
{
case Door_Raise:
if (line->args[0] == 0 && line->args[1] < 64)
default:
noway = false;
break;
case Teleport:
case Teleport_NoFog:
noway = false;
}
break;
}
return !noway;
}
if (activationType == SPAC_MCROSS &&
lineActivation != activationType &&
if (activationType == SPAC_MCross && !(lineActivation & SPAC_MCross) &&
!(line->flags & ML_MONSTERSCANACTIVATE))
{
return false;

View file

@ -93,7 +93,7 @@ bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector,
res.HitType = TRACE_HitNone;
// Do a 3D floor check in the starting sector
res.ffloor=NULL;
memset(&res, 0, sizeof(res));
inf.sectorsel=0;
TDeletingArray<F3DFloor*> &ff = sector->e->XFloor.ffloors;
@ -166,7 +166,7 @@ bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector,
// recalculate the trace's end points for robustness
if (inf.TraceTraverse (ptflags))
{ // check for intersection with floor/ceiling
res.Sector = inf.CurSector;
res.Sector = &sectors[inf.CurSector->sectornum];
if (inf.CheckSectorPlane (inf.CurSector, true))
{
@ -306,7 +306,7 @@ bool FTraceInfo::TraceTraverse (int ptflags)
hitz >= bc ? TIER_Upper : TIER_Middle;
if (TraceFlags & TRACE_Impact)
{
P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_IMPACT);
P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact);
}
}
else
@ -356,7 +356,7 @@ bool FTraceInfo::TraceTraverse (int ptflags)
Results->ffloor = rover;
if ((TraceFlags & TRACE_Impact) && in->d.line->special)
{
P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_IMPACT);
P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact);
}
goto cont;
}
@ -369,12 +369,12 @@ bool FTraceInfo::TraceTraverse (int ptflags)
Results->HitType = TRACE_HitNone;
if (TraceFlags & TRACE_PCross)
{
P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_PCROSS);
P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_PCross);
}
if (TraceFlags & TRACE_Impact)
{ // This is incorrect for "impact", but Hexen did this, so
// we need to as well, for compatibility
P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_IMPACT);
P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact);
}
}
cont:
@ -382,7 +382,7 @@ cont:
if (Results->HitType != TRACE_HitNone)
{
// We hit something, so figure out where exactly
Results->Sector = CurSector;
Results->Sector = &sectors[CurSector->sectornum];
if (Results->HitType != TRACE_HitWall &&
!CheckSectorPlane (CurSector, Results->HitType == TRACE_HitFloor))
@ -408,7 +408,7 @@ cont:
}
if (Results->HitType == TRACE_HitWall && TraceFlags & TRACE_Impact)
{
P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_IMPACT);
P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact);
}
}
@ -507,7 +507,7 @@ cont:
// the trace hit a 3D-floor before the thing.
// Calculate an intersection and abort.
Results->Sector = CurSector;
Results->Sector = &sector[CurSector->sectornum];
if (!CheckSectorPlane(CurSector, Results->HitType==TRACE_HitFloor))
{
Results->HitType=TRACE_HitNone;

695
src/p_udmf.cpp Normal file
View file

@ -0,0 +1,695 @@
/*
** p_udmf.cpp
**
** UDMF text map parser
**
**---------------------------------------------------------------------------
** Copyright 2008 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "r_data.h"
#include "p_setup.h"
#include "sc_man.h"
#include "vectors.h"
#include "p_lnspec.h"
#include "templates.h"
#include "i_system.h"
extern void P_TranslateLineDef (line_t *ld, maplinedef_t *mld);
extern int P_TranslateSectorSpecial (int);
void P_ProcessSideTextures(bool checktranmap, side_t *sd, sector_t *sec, mapsidedef_t *msd, int special, int tag, short *alpha);
void P_AdjustLine (line_t *ld);
void P_FinishLoadingLineDef(line_t *ld, int alpha);
void SpawnMapThing(int index, FMapThing *mt, int position);
extern bool ForceNodeBuild;
extern TArray<FMapThing> MapThingsConverted;
extern TArray<int> linemap;
struct UDMFParser
{
FScanner sc;
FName namespc;
bool isTranslated;
bool isExtended;
TArray<line_t> ParsedLines;
TArray<side_t> ParsedSides;
TArray<mapsidedef_t> ParsedSideTextures;
TArray<sector_t> ParsedSectors;
TArray<vertex_t> ParsedVertices;
FDynamicColormap *fogMap, *normMap;
UDMFParser()
{
linemap.Clear();
fogMap = normMap = NULL;
}
void Flag(DWORD &value, int mask, const FString &svalue)
{
if (!svalue.CompareNoCase("true"))
{
value |= mask;
}
else
{
value &= ~mask;
}
}
void ParseThing(FMapThing *th)
{
memset(th, 0, sizeof(*th));
sc.MustGetStringName("{");
while (!sc.CheckString("}"))
{
sc.MustGetString();
FName key = sc.String;
sc.MustGetStringName("=");
sc.MustGetString();
FString value = sc.String;
sc.MustGetStringName(";");
switch(key)
{
case NAME_TID:
th->thingid = (WORD)strtol(value, NULL, 0);
break;
case NAME_X:
th->x = FLOAT2FIXED(strtod(value, NULL));
break;
case NAME_Y:
th->y = FLOAT2FIXED(strtod(value, NULL));
break;
case NAME_Height:
th->z = FLOAT2FIXED(strtod(value, NULL));
break;
case NAME_Angle:
th->angle = (short)strtol(value, NULL, 0);
break;
case NAME_Type:
th->type = (short)strtol(value, NULL, 0);
break;
case NAME_Special:
th->special = (short)strtol(value, NULL, 0);
break;
case NAME_Arg0:
case NAME_Arg1:
case NAME_Arg2:
case NAME_Arg3:
case NAME_Arg4:
th->args[int(key)-int(NAME_Arg0)] = strtol(value, NULL, 0);
break;
case NAME_Skill1:
case NAME_Skill2:
case NAME_Skill3:
case NAME_Skill4:
case NAME_Skill5:
case NAME_Skill6:
case NAME_Skill7:
case NAME_Skill8:
case NAME_Skill9:
case NAME_Skill10:
case NAME_Skill11:
case NAME_Skill12:
case NAME_Skill13:
case NAME_Skill14:
case NAME_Skill15:
case NAME_Skill16:
if (!value.CompareNoCase("true")) th->SkillFilter |= (1<<(int(key)-NAME_Skill1));
else th->SkillFilter &= ~(1<<(int(key)-NAME_Skill1));
break;
case NAME_Class0:
case NAME_Class1:
case NAME_Class2:
case NAME_Class3:
case NAME_Class4:
case NAME_Class5:
case NAME_Class6:
case NAME_Class7:
case NAME_Class8:
case NAME_Class9:
case NAME_Class10:
case NAME_Class11:
case NAME_Class12:
case NAME_Class13:
case NAME_Class14:
case NAME_Class15:
case NAME_Class16:
if (!value.CompareNoCase("true")) th->ClassFilter |= (1<<(int(key)-NAME_Class1));
else th->SkillFilter &= ~(1<<(int(key)-NAME_Class1));
break;
case NAME_Ambush:
Flag(th->flags, MTF_AMBUSH, value); break;
case NAME_Dormant:
Flag(th->flags, MTF_DORMANT, value); break;
case NAME_Single:
Flag(th->flags, MTF_SINGLE, value); break;
case NAME_Coop:
Flag(th->flags, MTF_COOPERATIVE, value); break;
case NAME_Dm:
Flag(th->flags, MTF_DEATHMATCH, value); break;
case NAME_Translucent:
Flag(th->flags, MTF_SHADOW, value); break;
case NAME_Invisible:
Flag(th->flags, MTF_ALTSHADOW, value); break;
case NAME_Friend:
case NAME_Strifeally:
Flag(th->flags, MTF_FRIENDLY, value); break;
case NAME_Standing:
Flag(th->flags, MTF_STANDSTILL, value); break;
default:
break;
}
}
if (isTranslated)
{
// NOTE: Handling of this is undefined in the UDMF spec yet!
maplinedef_t mld;
line_t ld;
mld.flags = 0;
mld.special = th->special;
mld.tag = th->args[0];
P_TranslateLineDef(&ld, &mld);
th->special = ld.special;
memcpy(th->args, ld.args, sizeof (ld.args));
}
}
void ParseLinedef(line_t *ld)
{
bool passuse = false;
memset(ld, 0, sizeof(*ld));
ld->Alpha = FRACUNIT;
ld->id = -1;
ld->sidenum[0] = ld->sidenum[1] = NO_SIDE;
if (level.flags & LEVEL_CLIPMIDTEX) ld->flags |= ML_CLIP_MIDTEX;
if (level.flags & LEVEL_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX;
if (level.flags & LEVEL_CHECKSWITCHRANGE) ld->flags |= ML_CHECKSWITCHRANGE;
sc.MustGetStringName("{");
while (!sc.CheckString("}"))
{
sc.MustGetString();
FName key = sc.String;
sc.MustGetStringName("=");
sc.MustGetString();
FString value = sc.String;
sc.MustGetStringName(";");
switch(key)
{
case NAME_V1:
ld->v1 = (vertex_t*)(intptr_t)strtol(value, NULL, 0); // must be relocated later
break;
case NAME_V2:
ld->v2 = (vertex_t*)(intptr_t)strtol(value, NULL, 0); // must be relocated later
break;
case NAME_Special:
ld->special = strtol(value, NULL, 0);
break;
case NAME_Id:
ld->id = strtol(value, NULL, 0);
break;
case NAME_Sidefront:
ld->sidenum[0] = strtol(value, NULL, 0);
break;
case NAME_Sideback:
ld->sidenum[1] = strtol(value, NULL, 0);
break;
case NAME_Arg0:
case NAME_Arg1:
case NAME_Arg2:
case NAME_Arg3:
case NAME_Arg4:
ld->args[int(key)-int(NAME_Arg0)] = strtol(value, NULL, 0);
break;
case NAME_Blocking:
Flag(ld->flags, ML_BLOCKING, value); break;
case NAME_Blockmonsters:
Flag(ld->flags, ML_BLOCKMONSTERS, value); break;
case NAME_Twosided:
Flag(ld->flags, ML_TWOSIDED, value); break;
case NAME_Dontpegtop:
Flag(ld->flags, ML_DONTPEGTOP, value); break;
case NAME_Dontpegbottom:
Flag(ld->flags, ML_DONTPEGBOTTOM, value); break;
case NAME_Secret:
Flag(ld->flags, ML_SECRET, value); break;
case NAME_Soundblock:
Flag(ld->flags, ML_SOUNDBLOCK, value); break;
case NAME_Dontdraw:
Flag(ld->flags, ML_DONTDRAW, value); break;
case NAME_Mapped:
Flag(ld->flags, ML_MAPPED, value); break;
case NAME_Monsteractivate:
Flag(ld->flags, ML_MONSTERSCANACTIVATE, value); break;
case NAME_Blockplayers:
if (isExtended) Flag(ld->flags, ML_BLOCK_PLAYERS, value); break;
case NAME_Blockeverything:
if (isExtended) Flag(ld->flags, ML_BLOCKEVERYTHING, value); break;
case NAME_Zoneboundary:
if (isExtended) Flag(ld->flags, ML_ZONEBOUNDARY, value); break;
case NAME_Jumpover:
if (isExtended || namespc == NAME_Strife) Flag(ld->flags, ML_RAILING, value); break;
case NAME_Blockfloating:
if (isExtended || namespc == NAME_Strife) Flag(ld->flags, ML_BLOCK_FLOATERS, value); break;
case NAME_Clipmidtex:
if (isExtended) Flag(ld->flags, ML_CLIP_MIDTEX, value); break;
case NAME_Wrapmidtex:
if (isExtended) Flag(ld->flags, ML_WRAP_MIDTEX, value); break;
case NAME_Midtex3d:
if (isExtended) Flag(ld->flags, ML_3DMIDTEX, value); break;
case NAME_Checkswitchrange:
if (isExtended) Flag(ld->flags, ML_CHECKSWITCHRANGE, value); break;
case NAME_Firstsideonly:
if (isExtended) Flag(ld->flags, ML_FIRSTSIDEONLY, value); break;
case NAME_Transparent:
ld->Alpha = !value.CompareNoCase("true")? FRACUNIT*3/4 : FRACUNIT; break;
case NAME_Passuse:
passuse = !value.CompareNoCase("true");
case NAME_Playercross:
if (isExtended) Flag(ld->activation, SPAC_Cross, value); break;
case NAME_Playeruse:
if (isExtended) Flag(ld->activation, SPAC_Use, value); break;
case NAME_Monstercross:
if (isExtended) Flag(ld->activation, SPAC_MCross, value); break;
case NAME_Impact:
if (isExtended) Flag(ld->activation, SPAC_Impact, value); break;
case NAME_Playerpush:
if (isExtended) Flag(ld->activation, SPAC_Push, value); break;
case NAME_Missilecross:
if (isExtended) Flag(ld->activation, SPAC_PCross, value); break;
case NAME_Monsteruse:
if (isExtended) Flag(ld->activation, SPAC_MUse, value); break;
case NAME_Monsterpush:
if (isExtended) Flag(ld->activation, SPAC_MPush, value); break;
default:
break;
}
}
if (isTranslated)
{
}
else
{
int saved = ld->flags;
maplinedef_t mld;
memset(&mld, 0, sizeof(mld));
mld.special = ld->special;
mld.tag = ld->id;
P_TranslateLineDef(ld, &mld);
ld->flags = saved | (ld->flags&(ML_MONSTERSCANACTIVATE|ML_REPEAT_SPECIAL|ML_FIRSTSIDEONLY));
}
if (passuse && (ld->activation & SPAC_Use))
{
ld->activation = (ld->activation & ~SPAC_Use) | SPAC_UseThrough;
}
}
void ParseSidedef(side_t *sd, mapsidedef_t *sdt)
{
fixed_t texofs[2]={0,0};
memset(sd, 0, sizeof(*sd));
strncpy(sdt->bottomtexture, "-", 8);
strncpy(sdt->toptexture, "-", 8);
strncpy(sdt->midtexture, "-", 8);
sc.MustGetStringName("{");
while (!sc.CheckString("}"))
{
sc.MustGetString();
FName key = sc.String;
sc.MustGetStringName("=");
sc.MustGetString();
FString value = sc.String;
sc.MustGetStringName(";");
switch(key)
{
case NAME_Offsetx:
texofs[0] = strtol(value, NULL, 0) << FRACBITS;
break;
case NAME_Offsety:
texofs[1] = strtol(value, NULL, 0) << FRACBITS;
break;
case NAME_Texturetop:
strncpy(sdt->toptexture, value, 8);
break;
case NAME_Texturebottom:
strncpy(sdt->bottomtexture, value, 8);
break;
case NAME_Texturemiddle:
strncpy(sdt->midtexture, value, 8);
break;
case NAME_Sector:
sd->sector = (sector_t*)(intptr_t)strtol(value, NULL, 0);
break;
default:
break;
}
}
// initialization of these is delayed to allow separate offsets and add them with the global ones.
sd->AddTextureXOffset(side_t::top, texofs[0]);
sd->AddTextureXOffset(side_t::mid, texofs[0]);
sd->AddTextureXOffset(side_t::bottom, texofs[0]);
sd->AddTextureYOffset(side_t::top, texofs[1]);
sd->AddTextureYOffset(side_t::mid, texofs[1]);
sd->AddTextureYOffset(side_t::bottom, texofs[1]);
}
void ParseSector(sector_t *sec)
{
memset(sec, 0, sizeof(*sec));
sec->lightlevel = 255;
sec->floor_xscale = FRACUNIT; // [RH] floor and ceiling scaling
sec->floor_yscale = FRACUNIT;
sec->ceiling_xscale = FRACUNIT;
sec->ceiling_yscale = FRACUNIT;
sec->oldspecial = !!(sec->special&SECRET_MASK);
sec->thinglist = NULL;
sec->touching_thinglist = NULL; // phares 3/14/98
sec->seqType = (level.flags & LEVEL_SNDSEQTOTALCTRL)? 0:-1;
sec->nextsec = -1; //jff 2/26/98 add fields to support locking out
sec->prevsec = -1; // stair retriggering until build completes
sec->heightsec = NULL; // sector used to get floor and ceiling height
// killough 3/7/98: end changes
sec->gravity = 1.f; // [RH] Default sector gravity of 1.0
sec->ZoneNumber = 0xFFFF;
// killough 8/28/98: initialize all sectors to normal friction
sec->friction = ORIG_FRICTION;
sec->movefactor = ORIG_FRICTION_FACTOR;
sc.MustGetStringName("{");
while (!sc.CheckString("}"))
{
sc.MustGetString();
FName key = sc.String;
sc.MustGetStringName("=");
sc.MustGetString();
FString value = sc.String;
sc.MustGetStringName(";");
switch(key)
{
case NAME_Heightfloor:
sec->floortexz = strtol(value, NULL, 0) << FRACBITS;
break;
case NAME_Heightceiling:
sec->ceilingtexz = strtol(value, NULL, 0) << FRACBITS;
break;
case NAME_Texturefloor:
sec->floorpic = TexMan.GetTexture (value, FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable);
break;
case NAME_Textureceiling:
sec->ceilingpic = TexMan.GetTexture (value, FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable);
break;
case NAME_Lightlevel:
sec->lightlevel = (BYTE)clamp<int>(strtol(value, NULL, 0), 0, 255);
break;
case NAME_Special:
sec->special = (short)strtol(value, NULL, 0);
if (isTranslated) sec->special = P_TranslateSectorSpecial(sec->special);
break;
case NAME_Tag:
sec->tag = (short)strtol(value, NULL, 0);
break;
default:
break;
}
}
sec->floorplane.d = -sec->floortexz;
sec->floorplane.c = FRACUNIT;
sec->floorplane.ic = FRACUNIT;
sec->ceilingplane.d = sec->ceilingtexz;
sec->ceilingplane.c = -FRACUNIT;
sec->ceilingplane.ic = -FRACUNIT;
// [RH] Sectors default to white light with the default fade.
// If they are outside (have a sky ceiling), they use the outside fog.
if (level.outsidefog != 0xff000000 && (sec->ceilingpic == skyflatnum || (sec->special&0xff) == Sector_Outside))
{
if (fogMap == NULL)
fogMap = GetSpecialLights (PalEntry (255,255,255), level.outsidefog, 0);
sec->ColorMap = fogMap;
}
else
{
if (normMap == NULL)
normMap = GetSpecialLights (PalEntry (255,255,255), level.fadeto, NormalLight.Desaturate);
sec->ColorMap = normMap;
}
}
void ParseVertex(vertex_t *vt)
{
vt->x = vt->y = 0;
sc.MustGetStringName("{");
while (!sc.CheckString("}"))
{
sc.MustGetString();
FName key = sc.String;
sc.MustGetStringName("=");
sc.MustGetString();
FString value = sc.String;
sc.MustGetStringName(";");
switch(key)
{
case NAME_X:
vt->x = FLOAT2FIXED(strtod(value, NULL));
break;
case NAME_Y:
vt->y = FLOAT2FIXED(strtod(value, NULL));
break;
default:
break;
}
}
}
void ProcessLineDefs()
{
int sidecount = 0;
for(unsigned i = 0, skipped = 0; i < ParsedLines.Size();)
{
// Relocate the vertices
intptr_t v1i = intptr_t(ParsedLines[i].v1);
intptr_t v2i = intptr_t(ParsedLines[i].v2);
if (v1i >= numvertexes || v2i >= numvertexes || v1i < 0 || v2i < 0)
{
I_Error ("Line %d has invalid vertices: %d and/or %d.\nThe map only contains %d vertices.", i+skipped, v1i, v2i, numvertexes);
}
else if (v1i == v2i ||
(vertexes[v1i].x == vertexes[v2i].x && vertexes[v1i].y == vertexes[v2i].y))
{
Printf ("Removing 0-length line %d\n", i+skipped);
ParsedLines.Delete(i);
ForceNodeBuild = true;
skipped++;
}
else
{
ParsedLines[i].v1 = &vertexes[v1i];
ParsedLines[i].v2 = &vertexes[v2i];
if (ParsedLines[i].sidenum[0] != NO_SIDE)
sidecount++;
if (ParsedLines[i].sidenum[1] != NO_SIDE)
sidecount++;
linemap.Push(i+skipped);
i++;
}
}
numlines = ParsedLines.Size();
numsides = sidecount;
lines = new line_t[numlines];
sides = new side_t[numsides];
for(int line = 0, side = 0; line < numlines; line++)
{
short tempalpha[2] = {-1,-1};
lines[line] = ParsedLines[line];
for(int sd = 0; sd < 2; sd++)
{
if (lines[line].sidenum[sd] != NO_SIDE)
{
int mapside = lines[line].sidenum[sd];
sides[side] = ParsedSides[mapside];
sides[side].linenum = line;
sides[side].sector = &sectors[intptr_t(sides[side].sector)];
lines[line].sidenum[sd] = side;
P_ProcessSideTextures(!isExtended, &sides[side], sides[side].sector, &ParsedSideTextures[mapside],
lines[line].special, lines[line].args[0], &tempalpha[sd]);
side++;
}
}
P_AdjustLine(&lines[line]);
P_FinishLoadingLineDef(&lines[line], tempalpha[0]);
}
}
void ParseTextMap(MapData *map)
{
char *buffer = new char[map->Size(ML_TEXTMAP)];
isTranslated = true;
isExtended = false;
map->Read(ML_TEXTMAP, buffer);
sc.OpenMem(Wads.GetLumpFullName(map->lumpnum), buffer, map->Size(ML_TEXTMAP));
sc.SetCMode(true);
while (sc.GetString())
{
if (sc.Compare("namespace"))
{
sc.MustGetStringName("=");
sc.MustGetString();
namespc = sc.String;
if (namespc == NAME_ZDoom)
{
isTranslated = false;
isExtended = true;
}
else if (namespc == NAME_Hexen)
{
isTranslated = false;
}
else if (namespc == NAME_ZDoomTranslated)
{
isExtended = true;
}
else if (namespc == NAME_Doom)
{
}
else if (namespc == NAME_Heretic)
{
}
else if (namespc == NAME_Strife)
{
}
else
{
sc.ScriptError("Unknown namespace %s", sc.String);
}
sc.MustGetStringName(";");
}
else if (sc.Compare("thing"))
{
FMapThing th;
ParseThing(&th);
MapThingsConverted.Push(th);
}
else if (sc.Compare("linedef"))
{
line_t li;
ParseLinedef(&li);
ParsedLines.Push(li);
}
else if (sc.Compare("sidedef"))
{
side_t si;
mapsidedef_t st;
ParseSidedef(&si, &st);
ParsedSides.Push(si);
ParsedSideTextures.Push(st);
}
else if (sc.Compare("sector"))
{
sector_t sec;
ParseSector(&sec);
ParsedSectors.Push(sec);
}
else if (sc.Compare("vertex"))
{
vertex_t vt;
ParseVertex(&vt);
ParsedVertices.Push(vt);
}
}
// Create the real vertices
numvertexes = ParsedVertices.Size();
vertexes = new vertex_t[numvertexes];
memcpy(vertexes, &ParsedVertices[0], numvertexes * sizeof(*vertexes));
// Create the real sectors
numsectors = ParsedSectors.Size();
sectors = new sector_t[numsectors];
sectors[0].e = new extsector_t[numsectors];
memcpy(sectors, &ParsedSectors[0], numsectors * sizeof(*sectors));
for(int i = 0; i < numsectors; i++)
{
sectors[i].e = &sectors[0].e[i];
}
// Create the real linedefs and decompress the sidedefs
ProcessLineDefs();
}
};
void P_ParseTextMap(MapData *map)
{
UDMFParser parse;
parse.ParseTextMap(map);
}
void P_SpawnTextThings(int position)
{
for(unsigned i=0; i<MapThingsConverted.Size(); i++)
{
SpawnMapThing (i, &MapThingsConverted[i], position);
}
}

View file

@ -1688,6 +1688,9 @@ void P_FallingDamage (AActor *actor)
if (damagestyle == 0)
return;
if (actor->floorsector->Flags & SECF_NOFALLINGDAMAGE)
return;
mom = abs (actor->momz);
// Since Hexen falling damage is stronger than ZDoom's, it takes

View file

@ -94,7 +94,7 @@ CCMD (dumpmap)
static int WriteTHINGS (FILE *file)
{
mapthing2_t mt = { 0 };
mapthinghexen_t mt = { 0 };
AActor *mo = players[consoleplayer].mo;
mt.x = LittleShort(short(mo->x >> FRACBITS));

View file

@ -63,7 +63,6 @@ typedef enum
void P_TranslateLineDef (line_t *ld, maplinedef_t *mld)
{
static FMemLump tlatebase;
unsigned short special = (unsigned short) LittleShort(mld->special);
short tag = LittleShort(mld->tag);
DWORD flags = LittleShort(mld->flags);
@ -71,7 +70,7 @@ void P_TranslateLineDef (line_t *ld, maplinedef_t *mld)
if (flags & ML_TRANSLUCENT_STRIFE)
{
ld->alpha = 255*3/4;
ld->Alpha = FRACUNIT*3/4;
}
if (gameinfo.gametype == GAME_Strife)
{
@ -130,13 +129,16 @@ void P_TranslateLineDef (line_t *ld, maplinedef_t *mld)
ld->args[4] = linetrans->args[4];
ld->flags = flags | ((linetrans->flags & 0x1f) << 9);
if (passthrough && (GET_SPAC(ld->flags) == SPAC_USE))
{
if (linetrans->flags & 0x20) ld->flags |= ML_FIRSTSIDEONLY;
ld->activation = 1 << GET_SPAC(ld->flags);
if (ld->activation == SPAC_AnyCross) ld->activation = SPAC_Impact|SPAC_PCross; // this is really PTouch
ld->flags &= ~ML_SPAC_MASK;
ld->flags |= SPAC_USETHROUGH << ML_SPAC_SHIFT;
if (passthrough && ld->activation == SPAC_Use)
{
ld->activation = SPAC_UseThrough;
}
switch (linetrans->flags & 0xe0)
switch (linetrans->flags & LINETRANS_TAGMASK)
{
case LINETRANS_HAS2TAGS: // First two arguments are tags
ld->args[1] = tag;
@ -160,7 +162,7 @@ void P_TranslateLineDef (line_t *ld, maplinedef_t *mld)
ld->args[4] = tag;
break;
}
if ((ld->flags & ML_SECRET) && (GET_SPAC(ld->flags) == SPAC_USE || GET_SPAC(ld->flags) == SPAC_USETHROUGH))
if ((ld->flags & ML_SECRET) && ld->activation & (SPAC_Use|SPAC_UseThrough))
{
ld->flags &= ~ML_MONSTERSCANACTIVATE;
}
@ -180,7 +182,7 @@ void P_TranslateLineDef (line_t *ld, maplinedef_t *mld)
case WalkMany:
flags |= ML_REPEAT_SPECIAL;
case WalkOnce:
flags |= SPAC_CROSS << ML_SPAC_SHIFT;
ld->activation = SPAC_Cross;
break;
case SwitchMany:
@ -189,15 +191,15 @@ void P_TranslateLineDef (line_t *ld, maplinedef_t *mld)
case SwitchOnce:
case PushOnce:
if (passthrough)
flags |= SPAC_USETHROUGH << ML_SPAC_SHIFT;
ld->activation = SPAC_UseThrough;
else
flags |= SPAC_USE << ML_SPAC_SHIFT;
ld->activation = SPAC_Use;
break;
case GunMany:
flags |= ML_REPEAT_SPECIAL;
case GunOnce:
flags |= SPAC_IMPACT << ML_SPAC_SHIFT;
ld->activation = SPAC_Impact;
break;
}
@ -267,6 +269,11 @@ void P_TranslateLineDef (line_t *ld, maplinedef_t *mld)
}
}
ld->flags = flags;
if (flags & ML_MONSTERSCANACTIVATE && ld->activation == SPAC_Cross)
{
// In Boom anything can activate such a line so set the proper type here.
ld->activation = SPAC_AnyCross;
}
return;
}
}

View file

@ -237,10 +237,9 @@ enum
SECF_ABSLIGHTING = 1 // floor/ceiling light is absolute, not relative
};
// Misc sector flags
// Internal sector flags
enum
{
SECF_SILENT = 1, // actors in sector make no noise
SECF_FAKEFLOORONLY = 2, // when used as heightsec in R_FakeFlat, only copies floor
SECF_CLIPFAKEPLANES = 4, // as a heightsec, clip planes to target sector's planes
SECF_NOFAKELIGHT = 8, // heightsec does not change lighting
@ -251,6 +250,12 @@ enum
SECF_DRAWN = 128, // sector has been drawn at least once
};
enum
{
SECF_SILENT = 1, // actors in sector make no noise
SECF_NOFALLINGDAMAGE= 2, // No falling damage in this sector
};
struct FDynamicColormap;
struct FLightStack
@ -422,7 +427,8 @@ struct sector_t
short mod; // [RH] Means-of-death for applied damage
WORD ZoneNumber; // [RH] Zone this sector belongs to
WORD MoreFlags; // [RH] Misc sector flags
WORD MoreFlags; // [RH] Internal sector flags
DWORD Flags; // Sector flags
// [RH] Action specials for sectors. Like Skull Tag, but more
// flexible in a Bloody way. SecActTarget forms a list of actors
@ -577,9 +583,10 @@ struct line_t
vertex_t *v1, *v2; // vertices, from v1 to v2
fixed_t dx, dy; // precalculated v2 - v1 for side checking
DWORD flags;
BYTE special; // [RH] specials are only one byte (like Hexen)
BYTE alpha; // <--- translucency (0-255/255=opaque)
short id; // <--- same as tag or set with Line_SetIdentification
DWORD activation; // activation type
int special;
fixed_t Alpha; // <--- translucency (0-255/255=opaque)
int id; // <--- same as tag or set with Line_SetIdentification
int args[5]; // <--- hexen-style arguments (expanded to ZDoom's full width)
int firstid, nextid;
DWORD sidenum[2]; // sidenum[1] will be NO_SIDE if one sided

View file

@ -209,8 +209,7 @@ void R_RenderMaskedSegRange (drawseg_t *ds, int x1, int x2)
ESPSResult drawmode;
drawmode = R_SetPatchStyle (LegacyRenderStyles[curline->sidedef->Flags & WALLF_ADDTRANS ? STYLE_Add : STYLE_Translucent],
curline->linedef->alpha < 255 ? curline->linedef->alpha<<8 : FRACUNIT,
0, 0);
MIN(curline->linedef->Alpha, FRACUNIT), 0, 0);
if ((drawmode == DontDraw && !ds->bFogBoundary))
{

View file

@ -153,7 +153,7 @@ FString FPlayList::NextLine (FILE *file)
void FPlayList::Shuffle ()
{
unsigned int numsongs = Songs.Size();
int i;
unsigned int i;
for (i = 0; i < numsongs; ++i)
{

View file

@ -335,7 +335,10 @@ void DSeqNode::Destroy()
m_ParentSeqNode = NULL;
}
if (SequenceListHead == this)
{
SequenceListHead = m_Next;
GC::WriteBarrier(m_Next);
}
if (m_Prev)
{
m_Prev->m_Next = m_Next;
@ -716,11 +719,12 @@ DSeqNode::DSeqNode (int sequence, int modenum)
}
else
{
SequenceListHead->m_Prev = this;
m_Next = SequenceListHead;
SequenceListHead->m_Prev = this; GC::WriteBarrier(SequenceListHead->m_Prev, this);
m_Next = SequenceListHead; GC::WriteBarrier(this, SequenceListHead);
SequenceListHead = this;
m_Prev = NULL;
}
GC::WriteBarrier(this);
m_ParentSeqNode = m_ChildSeqNode = NULL;
}

View file

@ -855,7 +855,7 @@ void S_SoundID (int channel, int sound_id, float volume, int attenuation)
void S_SoundID (AActor *ent, int channel, int sound_id, float volume, int attenuation)
{
if (ent->Sector->MoreFlags & SECF_SILENT)
if (ent->Sector->Flags & SECF_SILENT)
return;
S_StartSound (&ent->x, ent, channel, sound_id, volume, SELECT_ATTEN(attenuation));
}
@ -1335,7 +1335,7 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force)
else
{
int lumpnum = -1;
int offset, length;
int offset = 0, length = 0;
int device = MDEV_DEFAULT;
void *handle = NULL;
@ -1398,11 +1398,6 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force)
}
}
}
else
{
offset = 0;
length = 0;
}
// shutdown old music
S_StopMusic (true);

View file

@ -269,7 +269,7 @@ class FMODStreamCapsule : public SoundStream
public:
FMODStreamCapsule(FMOD::Sound *stream, FMODSoundRenderer *owner, const char *url)
: Owner(owner), Stream(NULL), Channel(NULL),
UserData(NULL), Callback(NULL), Ended(false), URL(url)
UserData(NULL), Callback(NULL), URL(url), Ended(false)
{
SetStream(stream);
}

View file

@ -623,13 +623,13 @@ FString TimidityMIDIDevice::GetStats()
{
dots << TEXTCOLOR_GREEN;
}
if (Renderer->voice[i].envelope_increment == 0)
if (!Renderer->voice[i].eg1.env.bUpdating)
{
dots << "+";
}
else
{
dots << ('0' + Renderer->voice[i].envelope_stage);
dots << ('0' + Renderer->voice[i].eg1.gf1.stage);
}
}
}

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 "952"
#define ZD_SVN_REVISION_NUMBER 952
#define ZD_SVN_REVISION_STRING "964"
#define ZD_SVN_REVISION_NUMBER 964

View file

@ -617,9 +617,13 @@ void FTextureManager::LoadHiresTex(int wadnum)
newtex->SetScaledSize(width, height);
memcpy(newtex->Name, src, sizeof(newtex->Name));
int oldtex = TexMan.CheckForTexture(src, FTexture::TEX_Override);
if (oldtex>=0) TexMan.ReplaceTexture(oldtex, newtex, true);
else TexMan.AddTexture(newtex);
int oldtex = TexMan.CheckForTexture(src, FTexture::TEX_MiscPatch);
if (oldtex>=0)
{
ReplaceTexture(oldtex, newtex, true);
newtex->UseType = FTexture::TEX_Override;
}
else AddTexture(newtex);
}
}
//else Printf("Unable to define hires texture '%s'\n", tex->Name);

View file

@ -2037,8 +2037,8 @@ void A_Stop (AActor *self)
//===========================================================================
void A_Respawn (AActor *actor)
{
fixed_t x = actor->SpawnPoint[0] << FRACBITS;
fixed_t y = actor->SpawnPoint[1] << FRACBITS;
fixed_t x = actor->SpawnPoint[0];
fixed_t y = actor->SpawnPoint[1];
sector_t *sec;
actor->flags |= MF_SOLID;

View file

@ -2303,15 +2303,15 @@ static void PlayerSpawnClass (FScanner &sc, APlayerPawn *defaults, Baggage &bag)
if (sc.Compare ("Any"))
defaults->SpawnMask = 0;
else if (sc.Compare ("Fighter"))
defaults->SpawnMask |= MTF_FIGHTER;
defaults->SpawnMask |= 1;
else if (sc.Compare ("Cleric"))
defaults->SpawnMask |= MTF_CLERIC;
defaults->SpawnMask |= 2;
else if (sc.Compare ("Mage"))
defaults->SpawnMask |= MTF_MAGE;
defaults->SpawnMask |= 4;
else if (IsNum(sc.String))
{
int val = strtol(sc.String, NULL, 0);
defaults->SpawnMask = (val*MTF_FIGHTER) & (MTF_FIGHTER|MTF_CLERIC|MTF_MAGE);
if (val > 0) defaults->SpawnMask |= 1<<(val-1);
}
}

85
src/timidity/gf1patch.h Normal file
View file

@ -0,0 +1,85 @@
/* GF1 Patch definition: */
enum
{
HEADER_SIZE = 12,
ID_SIZE = 10,
DESC_SIZE = 60,
RESERVED_SIZE = 40,
PATCH_HEADER_RESERVED_SIZE = 36,
LAYER_RESERVED_SIZE = 40,
PATCH_DATA_RESERVED_SIZE = 36,
INST_NAME_SIZE = 16,
ENVELOPES = 6,
MAX_LAYERS = 4
};
#define GF1_HEADER_TEXT "GF1PATCH110"
#ifdef _MSC_VER
#pragma pack(push, 1)
#define GCC_PACKED
#else
#define GCC_PACKED __attribute__((__packed__))
#endif
struct GF1PatchHeader
{
char Header[HEADER_SIZE];
char GravisID[ID_SIZE]; /* Id = "ID#000002" */
char Description[DESC_SIZE];
BYTE Instruments;
BYTE Voices;
BYTE Channels;
WORD WaveForms;
WORD MasterVolume;
DWORD DataSize;
BYTE Reserved[PATCH_HEADER_RESERVED_SIZE];
} GCC_PACKED;
struct GF1InstrumentData
{
WORD Instrument;
char InstrumentName[INST_NAME_SIZE];
int InstrumentSize;
BYTE Layers;
BYTE Reserved[RESERVED_SIZE];
} GCC_PACKED;
struct GF1LayerData
{
BYTE LayerDuplicate;
BYTE Layer;
int LayerSize;
BYTE Samples;
BYTE Reserved[LAYER_RESERVED_SIZE];
} GCC_PACKED;
struct GF1PatchData
{
char WaveName[7];
BYTE Fractions;
int WaveSize;
int StartLoop;
int EndLoop;
WORD SampleRate;
int LowFrequency;
int HighFrequency;
int RootFrequency;
SWORD Tune;
BYTE Balance;
BYTE EnvelopeRate[ENVELOPES];
BYTE EnvelopeOffset[ENVELOPES];
BYTE TremoloSweep;
BYTE TremoloRate;
BYTE TremoloDepth;
BYTE VibratoSweep;
BYTE VibratoRate;
BYTE VibratoDepth;
BYTE Modes;
SWORD ScaleFrequency;
WORD ScaleFactor; /* From 0 to 2048 or 0 to 2 */
BYTE Reserved[PATCH_DATA_RESERVED_SIZE];
} GCC_PACKED;
#ifdef _MSC_VER
#pragma pack(pop)
#endif
#undef GCC_PACKED

View file

@ -32,16 +32,15 @@
#include "m_swap.h"
#include "files.h"
#include "templates.h"
#include "gf1patch.h"
namespace Timidity
{
extern Instrument *load_instrument_dls(Renderer *song, int drum, int bank, int instrument);
extern int openmode;
Instrument::Instrument()
: type(INST_GUS), samples(0), sample(NULL)
: samples(0), sample(NULL)
{
}
@ -52,7 +51,7 @@ Instrument::~Instrument()
for (i = samples, sp = &(sample[0]); i != 0; i--, sp++)
{
if (sp->data != NULL)
if (sp->type == INST_GUS && sp->data != NULL)
{
free(sp->data);
}
@ -82,28 +81,6 @@ ToneBank::~ToneBank()
}
}
int convert_envelope_rate(Renderer *song, BYTE rate)
{
int r;
r = 3 - ((rate>>6) & 0x3);
r *= 3;
r = (int)(rate & 0x3f) << r; /* 6.9 fixed point */
/* 15.15 fixed point. */
return int(((r * 44100) / song->rate) * song->control_ratio) << 9;
}
int convert_envelope_offset(BYTE offset)
{
/* This is not too good... Can anyone tell me what these values mean?
Are they GUS-style "exponential" volumes? And what does that mean? */
/* 15.15 fixed point */
return offset << (7 + 15);
}
int convert_tremolo_sweep(Renderer *song, BYTE sweep)
{
if (sweep == 0)
@ -268,7 +245,6 @@ failread:
ip->samples = layer_data.Samples;
ip->sample = (Sample *)safe_malloc(sizeof(Sample) * layer_data.Samples);
memset(ip->sample, 0, sizeof(Sample) * layer_data.Samples);
ip->type = INST_GUS;
for (i = 0; i < layer_data.Samples; ++i)
{
if (sizeof(patch_data) != fp->Read(&patch_data, sizeof(patch_data)))
@ -286,22 +262,23 @@ fail:
sp->loop_start = LittleLong(patch_data.StartLoop);
sp->loop_end = LittleLong(patch_data.EndLoop);
sp->sample_rate = LittleShort(patch_data.SampleRate);
sp->low_freq = LittleLong(patch_data.LowFrequency);
sp->high_freq = LittleLong(patch_data.HighFrequency);
sp->root_freq = LittleLong(patch_data.RootFrequency);
sp->low_freq = float(LittleLong(patch_data.LowFrequency));
sp->high_freq = float(LittleLong(patch_data.HighFrequency)) + 0.9999f;
sp->root_freq = float(LittleLong(patch_data.RootFrequency));
sp->high_vel = 127;
sp->velocity = -1;
sp->type = INST_GUS;
// Expand to SF2 range.
if (panning == -1)
{
sp->panning = patch_data.Balance & 0x0F;
sp->panning = (sp->panning << 3) | (sp->panning >> 1);
sp->panning = (patch_data.Balance & 0x0F) * 1000 / 15 - 500;
}
else
{
sp->panning = panning & 0x7f;
sp->panning = (panning & 0x7f) * 1000 / 127 - 500;
}
sp->panning |= sp->panning << 7;
song->compute_pan(sp->panning, sp->left_offset, sp->right_offset);
song->compute_pan((sp->panning + 500) / 1000.0, INST_GUS, sp->left_offset, sp->right_offset);
/* tremolo */
if (patch_data.TremoloRate == 0 || patch_data.TremoloDepth == 0)
@ -353,6 +330,10 @@ fail:
{
sp->scale_factor *= 1024;
}
else if (sp->scale_factor > 2048)
{
sp->scale_factor = 1024;
}
if (sp->scale_factor != 1024)
{
cmsg(CMSG_INFO, VERB_DEBUG, " * Scale: note %d, factor %d\n",
@ -381,16 +362,18 @@ fail:
if (header.Description[j] != 0)
break;
}
if (j == DESC_SIZE)
{
/* Strip any loops and envelopes we're permitted to */
/* [RH] (But PATCH_BACKWARD isn't a loop flag at all!) */
if ((strip_loop == 1) &&
(sp->modes & (PATCH_SUSTAIN | PATCH_LOOPEN | PATCH_BIDIR | PATCH_BACKWARD)))
{
cmsg(CMSG_INFO, VERB_DEBUG, " - Removing loop and/or sustain\n");
if (j == DESC_SIZE)
{
sp->modes &= ~(PATCH_SUSTAIN | PATCH_LOOPEN | PATCH_BIDIR | PATCH_BACKWARD);
}
sp->modes |= PATCH_T_NO_LOOP;
}
if (strip_envelope == 1)
{
@ -399,35 +382,38 @@ fail:
* Gravis patches get that effect: All rates at maximum, and all offsets at
* a constant level.
*/
for (j = 1; j < ENVELOPES; ++j)
if (j == DESC_SIZE)
{
int k;
for (k = 1; k < ENVELOPES; ++k)
{ /* Find highest offset. */
if (patch_data.EnvelopeOffset[j] > patch_data.EnvelopeOffset[0])
if (patch_data.EnvelopeOffset[k] > patch_data.EnvelopeOffset[0])
{
patch_data.EnvelopeOffset[0] = patch_data.EnvelopeOffset[j];
patch_data.EnvelopeOffset[0] = patch_data.EnvelopeOffset[k];
}
}
for (j = 0; j < ENVELOPES; ++j)
for (k = 0; k < ENVELOPES; ++k)
{
patch_data.EnvelopeRate[j] = 63;
patch_data.EnvelopeOffset[j] = patch_data.EnvelopeOffset[0];
patch_data.EnvelopeRate[k] = 63;
patch_data.EnvelopeOffset[k] = patch_data.EnvelopeOffset[0];
}
}
sp->modes |= PATCH_T_NO_ENVELOPE;
}
#if 0
else if (strip_envelope != 0)
{
/* Have to make a guess. */
if (!(sp->modes & (PATCH_LOOPEN | PATCH_BIDIR | PATCH_BACKWARD)))
{
/* No loop? Then what's there to sustain? No envelope needed either... */
sp->modes &= ~(PATCH_SUSTAIN | PATCH_NO_SRELEASE);
sp->modes |= PATCH_T_NO_ENVELOPE;
cmsg(CMSG_INFO, VERB_DEBUG, " - No loop, removing sustain and envelope\n");
}
else if (memcmp(patch_data.EnvelopeRate, "??????", 6) == 0 || patch_data.EnvelopeOffset[RELEASEC] >= 100)
else if (memcmp(patch_data.EnvelopeRate, "??????", 6) == 0 || patch_data.EnvelopeOffset[GF1_RELEASEC] >= 100)
{
/* Envelope rates all maxed out? Envelope end at a high "offset"?
That's a weird envelope. Take it out. */
sp->modes &= ~PATCH_NO_SRELEASE;
sp->modes |= PATCH_T_NO_ENVELOPE;
cmsg(CMSG_INFO, VERB_DEBUG, " - Weirdness, removing envelope\n");
}
else if (!(sp->modes & PATCH_SUSTAIN))
@ -436,17 +422,20 @@ fail:
justified, but patches without sustain usually don't need the
envelope either... at least the Gravis ones. They're mostly
drums. I think. */
sp->modes &= ~PATCH_NO_SRELEASE;
sp->modes |= PATCH_T_NO_ENVELOPE;
cmsg(CMSG_INFO, VERB_DEBUG, " - No sustain, removing envelope\n");
}
}
#endif
if (!(sp->modes & PATCH_NO_SRELEASE))
{ // TiMidity thinks that this is an envelope enable flag.
sp->modes |= PATCH_T_NO_ENVELOPE;
}
for (j = 0; j < 6; j++)
{
sp->envelope_rate[j] = convert_envelope_rate(song, patch_data.EnvelopeRate[j]);
sp->envelope.gf1.rate[j] = patch_data.EnvelopeRate[j];
/* [RH] GF1NEW clamps the offsets to the range [5,251], so we do too. */
sp->envelope_offset[j] = convert_envelope_offset(clamp<BYTE>(patch_data.EnvelopeOffset[j], 5, 251));
sp->envelope.gf1.offset[j] = clamp<BYTE>(patch_data.EnvelopeOffset[j], 5, 251);
}
/* Then read the sample data */
@ -487,10 +476,10 @@ fail:
}
else
{
#if defined(ADJUST_SAMPLE_VOLUMES)
/* Try to determine a volume scaling factor for the sample.
This is a very crude adjustment, but things sound more
balanced with it. Still, this should be a runtime option. */
balanced with it. Still, this should be a runtime option.
(This is ignored unless midi_timiditylike is turned on.) */
int i;
sample_t maxamp = 0, a;
sample_t *tmp;
@ -502,9 +491,6 @@ fail:
}
sp->volume = 1 / maxamp;
cmsg(CMSG_INFO, VERB_DEBUG, " * volume comp: %f\n", sp->volume);
#else
sp->volume = 1;
#endif
}
/* Then fractional samples */
@ -648,53 +634,62 @@ static int fill_bank(Renderer *song, int dr, int b)
{
if (bank->instrument[i] == MAGIC_LOAD_INSTRUMENT)
{
bank->instrument[i] = NULL;
bank->instrument[i] = load_instrument_dls(song, dr, b, i);
if (bank->instrument[i] != NULL)
{
continue;
}
if (bank->tone[i].name.IsEmpty())
Instrument *ip;
ip = load_instrument_font_order(song, 0, dr, b, i);
if (ip == NULL)
{
cmsg(CMSG_WARNING, (b != 0) ? VERB_VERBOSE : VERB_NORMAL,
"No instrument mapped to %s %d, program %d%s\n",
(dr) ? "drum set" : "tone bank", b, i,
(b != 0) ? "" : " - this instrument will not be heard");
if (b != 0)
if (bank->tone[i].fontbank >= 0)
{
/* Mark the corresponding instrument in the default
bank / drumset for loading (if it isn't already) */
if (!dr)
{
if (tonebank[0]->instrument[i] != NULL)
{
tonebank[0]->instrument[i] = MAGIC_LOAD_INSTRUMENT;
}
ip = load_instrument_font(song, bank->tone[i].name, dr, b, i);
}
else
{
if (drumset[0]->instrument[i] != NULL)
{
drumset[0]->instrument[i] = MAGIC_LOAD_INSTRUMENT;
}
}
}
bank->instrument[i] = NULL;
errors++;
}
else if (!(bank->instrument[i] =
load_instrument(song, bank->tone[i].name,
ip = load_instrument(song, bank->tone[i].name,
(dr) ? 1 : 0,
bank->tone[i].pan,
bank->tone[i].amp,
(bank->tone[i].note != -1) ? bank->tone[i].note : ((dr) ? i : -1),
(bank->tone[i].strip_loop != -1) ? bank->tone[i].strip_loop : ((dr) ? 1 : -1),
(bank->tone[i].strip_envelope != -1) ? bank->tone[i].strip_envelope : ((dr) ? 1 : -1),
bank->tone[i].strip_tail)))
bank->tone[i].strip_tail);
}
if (ip == NULL)
{
ip = load_instrument_font_order(song, 1, dr, b, i);
}
}
bank->instrument[i] = ip;
if (ip == NULL)
{
if (bank->tone[i].name.IsEmpty())
{
cmsg(CMSG_WARNING, (b != 0) ? VERB_VERBOSE : VERB_NORMAL,
"No instrument mapped to %s %d, program %d%s\n",
(dr) ? "drum set" : "tone bank", b, i,
(b != 0) ? "" : " - this instrument will not be heard");
}
else
{
cmsg(CMSG_ERROR, VERB_NORMAL,
"Couldn't load instrument %s (%s %d, program %d)\n",
bank->tone[i].name.GetChars(),
(dr) ? "drum set" : "tone bank", b, i);
}
if (b != 0)
{
/* Mark the corresponding instrument in the default
bank / drumset for loading (if it isn't already) */
if (((dr) ? drumset[0] : tonebank[0])->instrument[i] != NULL)
{
((dr) ? drumset[0] : tonebank[0])->instrument[i] = MAGIC_LOAD_INSTRUMENT;
}
}
errors++;
}
}

View file

@ -1121,13 +1121,14 @@ static void load_region_dls(Renderer *song, Sample *sample, DLS_Instrument *ins,
DLS_Region *rgn = &ins->regions[index];
DLS_Wave *wave = &song->patches->waveList[rgn->wlnk->ulTableIndex];
sample->type = INST_DLS;
sample->self_nonexclusive = !!(rgn->header->fusOptions & F_RGN_OPTION_SELFNONEXCLUSIVE);
sample->key_group = (SBYTE)rgn->header->usKeyGroup;
sample->low_freq = SDWORD(note_to_freq(rgn->header->RangeKey.usLow));
sample->high_freq = SDWORD(note_to_freq(rgn->header->RangeKey.usHigh));
sample->root_freq = SDWORD(note_to_freq(rgn->wsmp->usUnityNote));
sample->low_vel = rgn->header->RangeVelocity.usLow;
sample->high_vel = rgn->header->RangeVelocity.usHigh;
sample->low_freq = note_to_freq(rgn->header->RangeKey.usLow);
sample->high_freq = note_to_freq(rgn->header->RangeKey.usHigh);
sample->root_freq = note_to_freq(rgn->wsmp->usUnityNote);
sample->low_vel = (BYTE)rgn->header->RangeVelocity.usLow;
sample->high_vel = (BYTE)rgn->header->RangeVelocity.usHigh;
sample->modes = wave->format->wBitsPerSample == 8 ? PATCH_UNSIGNED : PATCH_16;
sample->sample_rate = wave->format->dwSamplesPerSec;
@ -1174,25 +1175,12 @@ static void load_region_dls(Renderer *song, Sample *sample, DLS_Instrument *ins,
printf("%d, Rate=%d LV=%d HV=%d Low=%d Hi=%d Root=%d Pan=%d Attack=%f Hold=%f Sustain=%d Decay=%f Release=%f\n", index, sample->sample_rate, rgn->header->RangeVelocity.usLow, rgn->header->RangeVelocity.usHigh, sample->low_freq, sample->high_freq, sample->root_freq, sample->panning, attack, hold, sustain, decay, release);
*/
sample->envelope_offset[ATTACK] = to_offset(255);
sample->envelope_rate[ATTACK] = calc_rate(song, 255, sample->sample_rate, attack);
sample->envelope_offset[HOLD] = to_offset(250);
sample->envelope_rate[HOLD] = calc_rate(song, 5, sample->sample_rate, hold);
sample->envelope_offset[DECAY] = to_offset(sustain);
sample->envelope_rate[DECAY] = calc_rate(song, 255 - sustain, sample->sample_rate, decay);
sample->envelope_offset[RELEASE] = to_offset(0);
sample->envelope_rate[RELEASE] = calc_rate(song, 5 + sustain, sample->sample_rate, release);
sample->envelope_offset[RELEASEB] = to_offset(0);
sample->envelope_rate[RELEASEB] = to_offset(1);
sample->envelope_offset[RELEASEC] = to_offset(0);
sample->envelope_rate[RELEASEC] = to_offset(1);
sample->modes |= PATCH_NO_SRELEASE;
sample->envelope.sf2.decay_vol = 0;
sample->envelope.sf2.attack_vol = (short)attack;
sample->envelope.sf2.hold_vol = (short)hold;
sample->envelope.sf2.decay_vol = (short)decay;
sample->envelope.sf2.release_vol = (short)release;
sample->envelope.sf2.sustain_vol = (short)sustain;
}
sample->data_length <<= FRACTION_BITS;
@ -1236,7 +1224,6 @@ Instrument *load_instrument_dls(Renderer *song, int drum, int bank, int instrume
}
inst = (Instrument *)safe_malloc(sizeof(Instrument));
inst->type = INST_DLS;
inst->samples = dls_ins->header->cRegions;
inst->sample = (Sample *)safe_malloc(inst->samples * sizeof(Sample));
memset(inst->sample, 0, inst->samples * sizeof(Sample));

View file

@ -0,0 +1,131 @@
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "doomdef.h"
#include "m_swap.h"
#include "templates.h"
#include "timidity.h"
#include "sf2.h"
namespace Timidity
{
FontFile *Fonts;
FontFile *ReadDLS(const char *filename, FileReader *f)
{
return NULL;
}
void font_freeall()
{
FontFile *font, *next;
for (font = Fonts; font != NULL; font = next)
{
next = font->Next;
delete font;
}
Fonts = NULL;
}
FontFile *font_find(const char *filename)
{
for (FontFile *font = Fonts; font != NULL; font = font->Next)
{
if (stricmp(filename, font->Filename) == 0)
{
return font;
}
}
return NULL;
}
void font_add(const char *filename, int load_order)
{
FontFile *font;
font = font_find(filename);
if (font != NULL)
{
font->SetAllOrders(load_order);
}
else
{
FileReader *fp = open_filereader(filename, openmode, NULL);
if (fp != NULL)
{
if ((font = ReadSF2(filename, fp)) || (font = ReadDLS(filename, fp)))
{
font->SetAllOrders(load_order);
}
}
}
}
void font_remove(const char *filename)
{
FontFile *font;
font = font_find(filename);
if (font != NULL)
{
// Don't actually remove the font from the list, because instruments
// from it might be loaded using the %font extension.
font->SetAllOrders(255);
}
}
void font_order(int order, int bank, int preset, int keynote)
{
for (FontFile *font = Fonts; font != NULL; font = font->Next)
{
font->SetOrder(order, bank, preset, keynote);
}
}
Instrument *load_instrument_font(struct Renderer *song, const char *font, int drum, int bank, int instr)
{
FontFile *fontfile = font_find(font);
if (fontfile != NULL)
{
return fontfile->LoadInstrument(song, drum, bank, instr);
}
return NULL;
}
Instrument *load_instrument_font_order(struct Renderer *song, int order, int drum, int bank, int instr)
{
for (FontFile *font = Fonts; font != NULL; font = font->Next)
{
Instrument *ip = font->LoadInstrument(song, drum, bank, instr);
if (ip != NULL)
{
return ip;
}
}
return NULL;
}
FontFile::FontFile(FString filename)
: Filename(filename)
{
Next = Fonts;
Fonts = this;
}
FontFile::~FontFile()
{
for (FontFile **probe = &Fonts; *probe != NULL; probe = &(*probe)->Next)
{
if (*probe == this)
{
*probe = Next;
break;
}
}
}
}

1533
src/timidity/instrum_sf2.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -27,72 +27,149 @@
#include "timidity.h"
#include "templates.h"
#include "c_cvars.h"
EXTERN_CVAR(Bool, midi_timiditylike)
namespace Timidity
{
/* Returns 1 if envelope runs out */
int recompute_envelope(Voice *v)
static int convert_envelope_rate(Renderer *song, BYTE rate)
{
int stage;
int r;
stage = v->envelope_stage;
r = 3 - ((rate>>6) & 0x3);
r *= 3;
r = (int)(rate & 0x3f) << r; /* 6.9 fixed point */
if (stage >= ENVELOPES)
{
/* Envelope ran out. */
/* play sampled release */
v->status &= ~(VOICE_SUSTAINING | VOICE_LPE);
v->status |= VOICE_RELEASING;
v->envelope_increment = 0;
return 0;
/* 15.15 fixed point. */
return int(((r * 44100) / song->rate) * song->control_ratio) << 9;
}
if (stage == RELEASE && !(v->status & VOICE_RELEASING) && (v->sample->modes & PATCH_SUSTAIN))
void Envelope::Init(Renderer *song, Voice *v)
{
v->status |= VOICE_SUSTAINING;
/* Freeze envelope until note turns off. Trumpets want this. */
v->envelope_increment = 0;
Type = v->sample->type;
env.bUpdating = true;
if (Type == INST_GUS)
{
gf1.Init(song, v);
gf1.ApplyToAmp(v);
}
else
{
v->envelope_stage = stage + 1;
if (v->envelope_volume == v->sample->envelope_offset[stage])
{
return recompute_envelope(v);
sf2.Init(song, v);
sf2.ApplyToAmp(v);
}
v->envelope_target = v->sample->envelope_offset[stage];
v->envelope_increment = v->sample->envelope_rate[stage];
if (v->envelope_target < v->envelope_volume)
v->envelope_increment = -v->envelope_increment;
}
void GF1Envelope::Init(Renderer *song, Voice *v)
{
/* Ramp up from 0 */
stage = 0;
volume = 0;
for (int i = 0; i < 6; ++i)
{
offset[i] = v->sample->envelope.gf1.offset[i] << (7 + 15);
rate[i] = convert_envelope_rate(song, v->sample->envelope.gf1.rate[i]);
}
Recompute(v);
}
void GF1Envelope::Release(Voice *v)
{
if (midi_timiditylike)
{
if (!(v->sample->modes & PATCH_T_NO_ENVELOPE))
{
stage = GF1_RELEASE;
Recompute(v);
}
// else ... loop was already turned off by the caller
}
else if (!(v->sample->modes & PATCH_NO_SRELEASE) || (v->sample->modes & PATCH_FAST_REL))
{
/* ramp out to minimum volume with rate from final release stage */
stage = GF1_RELEASEC+1;
target = 0;
increment = -rate[GF1_RELEASEC];
}
else if (v->sample->modes & PATCH_SUSTAIN)
{
if (stage < GF1_RELEASE)
{
stage = GF1_RELEASE;
}
Recompute(v);
}
bUpdating = true;
}
/* Returns 1 if envelope runs out */
bool GF1Envelope::Recompute(Voice *v)
{
int newstage;
newstage = stage;
if (newstage > GF1_RELEASEC)
{
/* Envelope ran out. */
increment = 0;
bUpdating = false;
v->status &= ~(VOICE_SUSTAINING | VOICE_LPE);
v->status |= VOICE_RELEASING;
if (midi_timiditylike)
{ /* kill the voice ... or not */
if (volume <= 0)
{
v->status |= VOICE_STOPPING;
}
return 1;
}
else
{ /* play sampled release */
}
return 0;
}
if (newstage == GF1_RELEASE && !(v->status & VOICE_RELEASING) &&
((!midi_timiditylike && (v->sample->modes & PATCH_SUSTAIN)) ||
(midi_timiditylike && !(v->sample->modes & PATCH_T_NO_ENVELOPE))))
{
v->status |= VOICE_SUSTAINING;
/* Freeze envelope until note turns off. Trumpets want this. */
increment = 0;
bUpdating = false;
}
else
{
stage = newstage + 1;
if (volume == offset[newstage])
{
return Recompute(v);
}
target = offset[newstage];
increment = rate[newstage];
if (target < volume)
increment = -increment;
}
return 0;
}
void apply_envelope_to_amp(Voice *v)
bool GF1Envelope::Update(Voice *v)
{
float env_vol = v->attenuation;
float final_amp = v->sample->volume * FINAL_MIX_SCALE;
if (v->tremolo_phase_increment != 0)
if (midi_timiditylike && (v->sample->modes & PATCH_T_NO_ENVELOPE))
{
env_vol *= v->tremolo_volume;
return 0;
}
env_vol *= v->envelope_volume / float(1 << 30);
// Note: The pan offsets are negative.
v->left_mix = MAX(0.f, (float)calc_gf1_amp(env_vol + v->left_offset) * final_amp);
v->right_mix = MAX(0.f, (float)calc_gf1_amp(env_vol + v->right_offset) * final_amp);
}
static int update_envelope(Voice *v)
volume += increment;
if (((increment < 0) && (volume <= target)) || ((increment > 0) && (volume >= target)))
{
v->envelope_volume += v->envelope_increment;
if (((v->envelope_increment < 0) && (v->envelope_volume <= v->envelope_target)) ||
((v->envelope_increment > 0) && (v->envelope_volume >= v->envelope_target)))
{
v->envelope_volume = v->envelope_target;
if (recompute_envelope(v))
volume = target;
if (Recompute(v))
{
return 1;
}
@ -100,6 +177,239 @@ static int update_envelope(Voice *v)
return 0;
}
void GF1Envelope::ApplyToAmp(Voice *v)
{
double env_vol = v->attenuation;
double final_amp;
if (midi_timiditylike)
{
final_amp = v->sample->volume * FINAL_MIX_TIMIDITY_SCALE;
if (v->tremolo_phase_increment != 0)
{
env_vol *= v->tremolo_volume;
}
if (!(v->sample->modes & PATCH_T_NO_ENVELOPE))
{
if (stage > GF1_ATTACK)
{
env_vol *= pow(2.0, volume * (6.0 / (1 << 30)) - 6.0);
}
else
{
env_vol *= volume / float(1 << 30);
}
}
}
else
{
final_amp = FINAL_MIX_SCALE;
if (v->tremolo_phase_increment != 0)
{ // [RH] FIXME: This is wrong. Tremolo should offset the
// envelope volume, not scale it.
env_vol *= v->tremolo_volume;
}
env_vol *= volume / float(1 << 30);
env_vol = calc_gf1_amp(env_vol);
}
env_vol *= final_amp;
v->left_mix = float(env_vol * v->left_offset);
v->right_mix = float(env_vol * v->right_offset);
}
void SF2Envelope::Init(Renderer *song, Voice *v)
{
stage = 0;
volume = 0;
DelayTime = v->sample->envelope.sf2.delay_vol;
AttackTime = v->sample->envelope.sf2.attack_vol;
HoldTime = v->sample->envelope.sf2.hold_vol;
DecayTime = v->sample->envelope.sf2.decay_vol;
SustainLevel = v->sample->envelope.sf2.sustain_vol;
ReleaseTime = v->sample->envelope.sf2.release_vol;
SampleRate = song->rate;
HoldStart = 0;
RateMul = song->control_ratio / song->rate;
RateMul_cB = RateMul * 960;
bUpdating = true;
}
void SF2Envelope::Release(Voice *v)
{
if (stage == SF2_ATTACK)
{
// The attack stage does not use an attenuation in cB like all the rest.
volume = log10(volume) * -200;
}
stage = SF2_RELEASE;
bUpdating = true;
}
static double timecent_to_sec(float timecent)
{
if (timecent == -32768)
return 0;
return pow(2.0, timecent / 1200.0);
}
static double calc_rate(double ratemul, double sec)
{
if (sec < 0.006)
sec = 0.006;
return ratemul / sec;
}
static void shutoff_voice(Voice *v)
{
v->status &= ~(VOICE_SUSTAINING | VOICE_LPE);
v->status |= VOICE_RELEASING | VOICE_STOPPING;
}
static bool check_release(double RateMul, double sec)
{
double rate = calc_rate(960 * RateMul, sec);
// Is release rate very fast? If so, don't do the release, but do
// the voice off ramp instead.
return (rate < 960/20);
}
/* Returns 1 if envelope runs out */
bool SF2Envelope::Update(Voice *v)
{
double sec;
double newvolume;
switch (stage)
{
case SF2_DELAY:
if (v->sample_count >= timecent_to_sec(DelayTime) * SampleRate)
{
stage = SF2_ATTACK;
return Update(v);
}
return 0;
case SF2_ATTACK:
sec = timecent_to_sec(AttackTime);
if (sec <= 0)
{ // instantaneous attack
newvolume = 1;
}
else
{
newvolume = volume + calc_rate(RateMul, sec);
}
if (newvolume >= 1)
{
volume = 0;
HoldStart = v->sample_count;
if (HoldTime <= -32768)
{ // hold time is 0, so skip right to decay
stage = SF2_DECAY;
}
else
{
stage = SF2_HOLD;
}
return Update(v);
}
break;
case SF2_HOLD:
if (v->sample_count - HoldStart >= timecent_to_sec(HoldTime) * SampleRate)
{
stage = SF2_DECAY;
return Update(v);
}
return 0;
case SF2_DECAY:
sec = timecent_to_sec(DecayTime);
if (sec <= 0)
{ // instantaneous decay
newvolume = SustainLevel;
}
else
{
newvolume = volume + calc_rate(RateMul_cB, sec);
}
if (newvolume >= SustainLevel)
{
newvolume = SustainLevel;
stage = SF2_SUSTAIN;
bUpdating = false;
if (!(v->status & VOICE_RELEASING))
{
v->status |= VOICE_SUSTAINING;
}
}
break;
case SF2_SUSTAIN:
// Stay here until released.
return 0;
case SF2_RELEASE:
sec = timecent_to_sec(ReleaseTime);
if (sec <= 0)
{ // instantaneous release
newvolume = 1000;
}
else
{
newvolume = volume + calc_rate(RateMul_cB, sec);
}
if (newvolume >= 960)
{
stage = SF2_FINISHED;
shutoff_voice(v);
bUpdating = false;
return 1;
}
break;
case SF2_FINISHED:
return 1;
}
volume = (float)newvolume;
return 0;
}
/* EMU 8k/10k don't follow spec in regards to volume attenuation.
* This factor is used in the equation pow (10.0, cb / FLUID_ATTEN_POWER_FACTOR).
* By the standard this should be -200.0. */
#define FLUID_ATTEN_POWER_FACTOR (-531.509)
#define atten2amp(x) pow(10.0, (x) / FLUID_ATTEN_POWER_FACTOR)
void SF2Envelope::ApplyToAmp(Voice *v)
{
double amp;
if (stage == SF2_DELAY)
{
v->left_mix = 0;
v->right_mix = 0;
return;
}
else if (stage == SF2_ATTACK)
{
amp = atten2amp(v->attenuation) * volume;
}
else
{
amp = atten2amp(v->attenuation) * cb_to_amp(volume);
}
amp *= FINAL_MIX_SCALE * 0.5;
v->left_mix = float(amp * v->left_offset);
v->right_mix = float(amp * v->right_offset);
}
void apply_envelope_to_amp(Voice *v)
{
v->eg1.ApplyToAmp(v);
}
static void update_tremolo(Voice *v)
{
int depth = v->sample->tremolo_depth << 7;
@ -136,7 +446,7 @@ static void update_tremolo(Voice *v)
/* Returns 1 if the note died */
static int update_signal(Voice *v)
{
if (v->envelope_increment != 0 && update_envelope(v))
if (v->eg1.env.bUpdating && v->eg1.Update(v))
{
return 1;
}
@ -347,7 +657,7 @@ static void ramp_out(const sample_t *sp, float *lp, Voice *v, int c)
/* printf("Ramping out: left=%d, c=%d, li=%d\n", left, c, li); */
if (v->left_offset == 0) // All the way to the left
if (v->right_mix == 0) // All the way to the left
{
left = v->left_mix;
li = -(left/c);
@ -362,7 +672,7 @@ static void ramp_out(const sample_t *sp, float *lp, Voice *v, int c)
lp += 2;
}
}
else if (v->right_offset == 0) // All the way to the right
else if (v->left_mix == 0) // All the way to the right
{
right = v->right_mix;
ri = -(right/c);
@ -441,7 +751,7 @@ void mix_voice(Renderer *song, float *buf, Voice *v, int c)
}
if (v->right_mix == 0) // All the way to the left
{
if (v->envelope_increment != 0 || v->tremolo_phase_increment != 0)
if (v->eg1.env.bUpdating || v->tremolo_phase_increment != 0)
{
mix_single_left_signal(song->control_ratio, sp, buf, v, count);
}
@ -452,7 +762,7 @@ void mix_voice(Renderer *song, float *buf, Voice *v, int c)
}
else if (v->left_mix == 0) // All the way to the right
{
if (v->envelope_increment != 0 || v->tremolo_phase_increment != 0)
if (v->eg1.env.bUpdating || v->tremolo_phase_increment != 0)
{
mix_single_right_signal(song->control_ratio, sp, buf, v, count);
}
@ -463,7 +773,7 @@ void mix_voice(Renderer *song, float *buf, Voice *v, int c)
}
else // Somewhere in the middle
{
if (v->envelope_increment || v->tremolo_phase_increment)
if (v->eg1.env.bUpdating || v->tremolo_phase_increment)
{
mix_mystery_signal(song->control_ratio, sp, buf, v, count);
}
@ -472,6 +782,7 @@ void mix_voice(Renderer *song, float *buf, Voice *v, int c)
mix_mystery(song->control_ratio, sp, buf, v, count);
}
}
v->sample_count += count;
}
}

View file

@ -27,12 +27,13 @@
#include <math.h>
#include "timidity.h"
#include "c_cvars.h"
EXTERN_CVAR(Bool, midi_timiditylike)
namespace Timidity
{
static const double log_of_2 = 0.69314718055994529;
void Renderer::reset_voices()
{
for (int i = 0; i < voices; i++)
@ -44,8 +45,8 @@ void Renderer::reset_voices()
/* Process the Reset All Controllers event */
void Renderer::reset_controllers(int c)
{
channel[c].volume = (100 << 7) | 100;
channel[c].expression = 0x3fff;
channel[c].volume = 100;
channel[c].expression = 127;
channel[c].sustain = 0;
channel[c].pitchbend = 0x2000;
channel[c].pitchfactor = 0; /* to be computed */
@ -68,55 +69,6 @@ void Renderer::reset_midi()
reset_voices();
}
void Renderer::select_sample(int v, Instrument *ip, int vel)
{
double f, cdiff, diff;
int s, i;
Sample *sp, *closest;
s = ip->samples;
sp = ip->sample;
if (s == 1)
{
voice[v].sample = sp;
return;
}
f = voice[v].orig_frequency;
for (i = 0; i < s; i++)
{
if (sp->low_vel <= vel && sp->high_vel >= vel &&
sp->low_freq <= f && sp->high_freq >= f)
{
voice[v].sample = sp;
return;
}
sp++;
}
/*
No suitable sample found! We'll select the sample whose root
frequency is closest to the one we want. (Actually we should
probably convert the low, high, and root frequencies to MIDI note
values and compare those.) */
cdiff = 1e10;
closest = sp = ip->sample;
for (i = 0; i < s; i++)
{
diff = fabs(sp->root_freq - f);
if (diff < cdiff)
{
cdiff = diff;
closest = sp;
}
sp++;
}
voice[v].sample = closest;
return;
}
void Renderer::recompute_freq(int v)
{
Channel *ch = &channel[voice[v].channel];
@ -210,37 +162,72 @@ void Renderer::recompute_amp(Voice *v)
int chanvol = chan->volume;
int chanexpr = chan->expression;
v->attenuation = (vol_table[(chanvol * chanexpr) / 2113407] * vol_table[v->velocity]) * ((127 + 64) / 12419775.f);
}
void Renderer::compute_pan(int panning, float &left_offset, float &right_offset)
if (v->sample->type == INST_GUS)
{
// Round the left- and right-most positions to their extremes, since
// most songs only do coarse panning.
if (panning < 128)
if (midi_timiditylike)
{
panning = 0;
}
else if (panning > 127*128)
{
panning = 32767;
}
if (panning == 0)
{
left_offset = 0;
right_offset = (float)-HUGE_VAL;
}
else if (panning == 32767)
{
left_offset = (float)-HUGE_VAL;
right_offset = 0;
v->attenuation = float(timidityxx_perceived_vol(v->velocity / 127.0) *
timidityxx_perceived_vol(chanvol / 127.0) *
timidityxx_perceived_vol(chanexpr / 127.0));
}
else
{
double pan = panning * (1 / 32767.0);
right_offset = (float)(log(pan) * (1 / (log_of_2 * 32)));
left_offset = (float)(log(1 - pan) * (1 / (log_of_2 * 32)));
v->attenuation = (vol_table[(chanvol * chanexpr) / 127] * vol_table[v->velocity]) * ((127 + 64) / 12419775.f);
}
}
else
{
// Implicit modulators from SF2 spec
double velatten, cc7atten, cc11atten;
velatten = log10(127.0 / v->velocity);
cc7atten = log10(127.0 / chanvol);
cc11atten = log10(127.0 / chanexpr);
v->attenuation = float(400 * (velatten + cc7atten + cc11atten)) + v->sample->initial_attenuation;
}
}
// Pan must be in the range [0,1]
void Renderer::compute_pan(double pan, int type, float &left_offset, float &right_offset)
{
if (pan <= 0)
{
left_offset = 1;
right_offset = 0;
}
else if (pan >= 127/128.0)
{
left_offset = 0;
right_offset = 1;
}
else
{
if (type == INST_GUS && !midi_timiditylike)
{
/* Original amp equation looks like this:
* calc_gf1_amp(atten + offset)
* which expands to:
* 2^(16*(atten + offset) - 16)
* Keeping in mind that 2^(x + y) == 2^x * 2^y, we can
* rewrite this to avoid doing two pows in GF1Envelope::ApplyToAmp():
* 2^(16*atten + 16*offset - 16)
* 2^(16*atten - 16 + 16 * offset + 16 - 16)
* 2^(16*atten - 16) * 2^(16*offset + 16 - 16)
* 2^(16*atten - 16) * 2^(16*(offset + 1) - 16)
* calc_gf1_amp(atten) * calc_gf1_amp(offset + 1)
*/
right_offset = (float)calc_gf1_amp((log(pan) * (1 / (log_of_2 * 32))) + 1);
left_offset = (float)calc_gf1_amp((log(1 - pan) * (1 / (log_of_2 * 32))) + 1);
}
else
{
/* I have no idea what equation, if any, will reproduce the sc_pan_table
* that TiMidity++ uses, so midi_timiditylike gets the same Equal Power
* Panning as SF2/DLS.
*/
left_offset = (float)sqrt(1 - pan);
right_offset = (float)sqrt(pan);
}
}
}
@ -264,17 +251,112 @@ void Renderer::kill_key_group(int i)
float Renderer::calculate_scaled_frequency(Sample *sp, int note)
{
double scalednote = (note - sp->scale_note) * sp->scale_factor / 1024.0 + sp->scale_note;
double scalednote = (note - sp->scale_note) * sp->scale_factor / 1024.0 + sp->scale_note + sp->tune * 0.01;
return (float)note_to_freq(scalednote);
}
void Renderer::start_note(int chan, int note, int vel, int i)
bool Renderer::start_region(int chan, int note, int vel, Sample *sp, float f)
{
int voicenum;
Voice *v;
voicenum = allocate_voice();
if (voicenum < 0)
{
return false;
}
v = &voice[voicenum];
v->sample = sp;
if (sp->type == INST_GUS)
{
v->orig_frequency = f;
}
else
{
if (sp->scale_factor != 1024)
{
v->orig_frequency = calculate_scaled_frequency(sp, note);
}
else if (sp->tune != 0)
{
v->orig_frequency = note_to_freq(note + sp->tune * 0.01);
}
else
{
v->orig_frequency = note_to_freq(note);
}
}
v->status = VOICE_RUNNING;
v->channel = chan;
v->note = note;
v->velocity = vel;
v->sample_offset = 0;
v->sample_increment = 0; /* make sure it isn't negative */
v->sample_count = 0;
v->tremolo_phase = 0;
v->tremolo_phase_increment = v->sample->tremolo_phase_increment;
v->tremolo_sweep = v->sample->tremolo_sweep_increment;
v->tremolo_sweep_position = 0;
v->vibrato_sweep = v->sample->vibrato_sweep_increment;
v->vibrato_sweep_position = 0;
v->vibrato_control_ratio = v->sample->vibrato_control_ratio;
v->vibrato_control_counter = v->vibrato_phase = 0;
kill_key_group(voicenum);
memset(v->vibrato_sample_increment, 0, sizeof(v->vibrato_sample_increment));
if (sp->type == INST_SF2)
{
// Channel pan is added to instrument pan.
double pan;
if (channel[chan].panning == NO_PANNING)
{
pan = (sp->panning + 500) / 1000.0;
}
else
{
pan = channel[chan].panning / 128.0 + sp->panning / 1000.0;
}
compute_pan(pan, sp->type, v->left_offset, v->right_offset);
}
else if (channel[chan].panning != NO_PANNING)
{
compute_pan(channel[chan].panning / 128.0, sp->type, v->left_offset, v->right_offset);
}
else
{
v->left_offset = v->sample->left_offset;
v->right_offset = v->sample->right_offset;
}
recompute_freq(voicenum);
recompute_amp(v);
v->control_counter = 0;
v->eg1.Init(this, v);
if (v->sample->modes & PATCH_LOOPEN)
{
v->status |= VOICE_LPE;
}
return true;
}
void Renderer::start_note(int chan, int note, int vel)
{
Instrument *ip;
Sample *sp;
int bank = channel[chan].bank;
int prog = channel[chan].program;
Voice *v = &voice[i];
int i;
float f;
note &= 0x7f;
if (ISDRUMCHANNEL(chan))
{
if (NULL == drumset[bank] || NULL == (ip = drumset[bank]->instrument[note]))
@ -282,7 +364,12 @@ void Renderer::start_note(int chan, int note, int vel, int i)
if (!(ip = drumset[0]->instrument[note]))
return; /* No instrument? Then we can't play. */
}
if (ip->type == INST_GUS && ip->samples != 1)
assert(ip != MAGIC_LOAD_INSTRUMENT);
if (ip == MAGIC_LOAD_INSTRUMENT)
{
return;
}
if (ip->samples != 1 && ip->sample->type == INST_GUS)
{
cmsg(CMSG_WARNING, VERB_VERBOSE,
"Strange: percussion instrument with %d samples!", ip->samples);
@ -299,62 +386,85 @@ void Renderer::start_note(int chan, int note, int vel, int i)
if (NULL == (ip = tonebank[0]->instrument[prog]))
return; /* No instrument? Then we can't play. */
}
}
if (ip->sample->scale_factor != 1024)
assert(ip != MAGIC_LOAD_INSTRUMENT);
if (ip == MAGIC_LOAD_INSTRUMENT)
{
v->orig_frequency = calculate_scaled_frequency(ip->sample, note & 0x7F);
return;
}
}
if (NULL == ip->sample || ip->samples == 0)
return; /* No samples? Then nothing to play. */
// For GF1 patches, scaling is based solely on the first
// waveform in this layer.
if (ip->sample->type == INST_GUS && ip->sample->scale_factor != 1024)
{
f = calculate_scaled_frequency(ip->sample, note);
}
else
{
v->orig_frequency = note_to_freq(note & 0x7F);
f = note_to_freq(note);
}
select_sample(i, ip, vel);
v->status = VOICE_RUNNING;
v->channel = chan;
v->note = note;
v->velocity = vel;
v->sample_offset = 0;
v->sample_increment = 0; /* make sure it isn't negative */
v->tremolo_phase = 0;
v->tremolo_phase_increment = voice[i].sample->tremolo_phase_increment;
v->tremolo_sweep = voice[i].sample->tremolo_sweep_increment;
v->tremolo_sweep_position = 0;
v->vibrato_sweep = voice[i].sample->vibrato_sweep_increment;
v->vibrato_sweep_position = 0;
v->vibrato_control_ratio = voice[i].sample->vibrato_control_ratio;
v->vibrato_control_counter = voice[i].vibrato_phase = 0;
kill_key_group(i);
memset(v->vibrato_sample_increment, 0, sizeof(v->vibrato_sample_increment));
if (channel[chan].panning != NO_PANNING)
if (ip->sample->type == INST_GUS)
{
v->left_offset = channel[chan].left_offset;
v->right_offset = channel[chan].right_offset;
/* We're more lenient with matching ranges for GUS patches, since the
* official Gravis ones don't cover the full range of possible
* frequencies for every instrument.
*/
if (ip->samples == 1)
{ // If there's only one sample, definitely play it.
start_region(chan, note, vel, ip->sample, f);
}
for (i = ip->samples, sp = ip->sample; i != 0; --i, ++sp)
{
// GUS patches don't have velocity ranges, so no need to compare against them.
if (sp->low_freq <= f && sp->high_freq >= f)
{
if (i > 1 && (sp + 1)->low_freq <= f && (sp + 1)->high_freq >= f)
{ /* If there is a range of contiguous regions that match our
* desired frequency, the last one in that block is used.
*/
continue;
}
start_region(chan, note, vel, sp, f);
break;
}
}
if (i == 0)
{ /* Found nothing. Try again, but look for the one with the closest root frequency.
* As per the suggestion in the original TiMidity function, this search uses
* note values rather than raw frequencies.
*/
double cdiff = 1e10;
double want_note = freq_to_note(f);
Sample *closest = sp = ip->sample;
for (i = ip->samples; i != 0; --i, ++sp)
{
double diff = fabs(freq_to_note(sp->root_freq) - want_note);
if (diff < cdiff)
{
cdiff = diff;
closest = sp;
}
}
start_region(chan, note, vel, closest, f);
}
}
else
{
v->left_offset = v->sample->left_offset;
v->right_offset = v->sample->right_offset;
}
recompute_freq(i);
recompute_amp(v);
/* Ramp up from 0 */
v->envelope_stage = ATTACK;
v->envelope_volume = 0;
v->control_counter = 0;
recompute_envelope(v);
apply_envelope_to_amp(v);
if (v->sample->modes & PATCH_LOOPEN)
for (i = ip->samples, sp = ip->sample; i != 0; --i, ++sp)
{
v->status |= VOICE_LPE;
if ((sp->low_vel <= vel && sp->high_vel >= vel &&
sp->low_freq <= f && sp->high_freq >= f))
{
if (!start_region(chan, note, vel, sp, f))
{ // Ran out of voices
break;
}
}
}
}
}
@ -369,47 +479,22 @@ void Renderer::kill_note(int i)
}
}
/* Only one instance of a note can be playing on a single channel. */
void Renderer::note_on(int chan, int note, int vel)
int Renderer::allocate_voice()
{
if (vel == 0)
{
note_off(chan, note, 0);
return;
}
int i, lowest;
float lv, v;
int i = voices, lowest = -1;
float lv = 1e10, v;
while (i--)
for (i = 0; i < voices; ++i)
{
if (!(voice[i].status & VOICE_RUNNING))
{
lowest = i; /* Can't get a lower volume than silence */
return i; /* Can't get a lower volume than silence */
}
else if (voice[i].channel == chan && ((voice[i].note == note && !voice[i].sample->self_nonexclusive) || channel[chan].mono))
{
if (channel[chan].mono)
{
kill_note(i);
}
else
{
finish_note(i);
}
}
}
if (lowest != -1)
{
/* Found a free voice. */
start_note(chan, note, vel, lowest);
return;
}
/* Look for the decaying note with the lowest volume */
if (lowest == -1)
{
lowest = -1;
lv = 1e10;
i = voices;
while (i--)
{
@ -423,9 +508,8 @@ void Renderer::note_on(int chan, int note, int vel)
}
}
}
}
if (lowest != -1)
if (lowest >= 0)
{
/* This can still cause a click, but if we had a free voice to
spare for ramping down this note, we wouldn't need to kill it
@ -434,12 +518,41 @@ void Renderer::note_on(int chan, int note, int vel)
cut_notes++;
voice[lowest].status = 0;
start_note(chan, note, vel, lowest);
}
else
{
lost_notes++;
}
return lowest;
}
void Renderer::note_on(int chan, int note, int vel)
{
if (vel == 0)
{
note_off(chan, note, 0);
return;
}
int i = voices;
/* Only one instance of a note can be playing on a single channel. */
while (i--)
{
if (voice[i].channel == chan && ((voice[i].note == note && !voice[i].sample->self_nonexclusive) || channel[chan].mono))
{
if (channel[chan].mono)
{
kill_note(i);
}
else
{
finish_note(i);
}
}
}
start_note(chan, note, vel);
}
void Renderer::finish_note(int i)
@ -451,27 +564,12 @@ void Renderer::finish_note(int i)
v->status &= ~VOICE_SUSTAINING;
v->status |= VOICE_RELEASING;
if (!(v->sample->modes & PATCH_NO_SRELEASE))
if (!(v->sample->modes & PATCH_NO_SRELEASE) || midi_timiditylike)
{
v->status &= ~VOICE_LPE; /* sampled release */
}
if (!(v->sample->modes & PATCH_NO_SRELEASE) || (v->sample->modes & PATCH_FAST_REL))
{
/* ramp out to minimum volume with rate from final release stage */
v->envelope_stage = RELEASEC;
recompute_envelope(v);
// Get rate from the final release ramp, but force the target to 0.
v->envelope_target = 0;
v->envelope_increment = -v->sample->envelope_rate[RELEASEC];
}
else if (v->sample->modes & PATCH_SUSTAIN)
{
if (v->envelope_stage < RELEASE)
{
v->envelope_stage = RELEASE;
}
recompute_envelope(v);
}
v->eg1.Release(v);
v->eg2.Release(v);
}
}
@ -554,15 +652,19 @@ void Renderer::adjust_pressure(int chan, int note, int amount)
void Renderer::adjust_panning(int chan)
{
Channel *chanp = &channel[chan];
compute_pan(chanp->panning, chanp->left_offset, chanp->right_offset);
int i = voices;
while (i--)
{
if ((voice[i].channel == chan) && (voice[i].status & VOICE_RUNNING))
Voice *v = &voice[i];
if ((v->channel == chan) && (v->status & VOICE_RUNNING))
{
voice[i].left_offset = chanp->left_offset;
voice[i].right_offset = chanp->right_offset;
apply_envelope_to_amp(&voice[i]);
double pan = chanp->panning / 128.0;
if (v->sample->type == INST_SF2)
{ // Add instrument pan to channel pan.
pan += v->sample->panning / 500.0;
}
compute_pan(pan, v->sample->type, v->left_offset, v->right_offset);
apply_envelope_to_amp(v);
}
}
}
@ -674,32 +776,17 @@ void Renderer::HandleController(int chan, int ctrl, int val)
break;
case CTRL_VOLUME:
channel[chan].volume = (channel[chan].volume & 0x007F) | (val << 7);
adjust_volume(chan);
break;
case CTRL_VOLUME+32:
channel[chan].volume = (channel[chan].volume & 0x3F80) | (val);
channel[chan].volume = val;
adjust_volume(chan);
break;
case CTRL_EXPRESSION:
channel[chan].expression = (channel[chan].expression & 0x007F) | (val << 7);
adjust_volume(chan);
break;
case CTRL_EXPRESSION+32:
channel[chan].expression = (channel[chan].expression & 0x3F80) | (val);
channel[chan].expression = val;
adjust_volume(chan);
break;
case CTRL_PAN:
channel[chan].panning = (channel[chan].panning & 0x007F) | (val << 7);
adjust_panning(chan);
break;
case CTRL_PAN+32:
channel[chan].panning = (channel[chan].panning & 0x3F80) | (val);
channel[chan].panning = val;
adjust_panning(chan);
break;

View file

@ -26,6 +26,9 @@
#include <malloc.h>
#include "timidity.h"
#include "c_cvars.h"
EXTERN_CVAR(Bool, midi_timiditylike)
namespace Timidity
{
@ -484,7 +487,7 @@ static sample_t *rs_vib_bidir(sample_t *resample_buffer, float rate, Voice *vp,
sample_t *resample_voice(Renderer *song, Voice *vp, int *countptr)
{
int ofs;
BYTE modes;
WORD modes;
if (vp->sample->sample_rate == 0)
{
@ -509,9 +512,17 @@ sample_t *resample_voice(Renderer *song, Voice *vp, int *countptr)
/* Need to resample. Use the proper function. */
modes = vp->sample->modes;
if (vp->status & VOICE_LPE)
{
if (vp->sample->loop_end - vp->sample->loop_start < 2)
{ // Loop is too short; turn it off.
vp->status &= ~VOICE_LPE;
}
}
if (vp->vibrato_control_ratio)
{
if (vp->status & VOICE_LPE)
if (vp->status & VOICE_LPE && !(midi_timiditylike && vp->sample->modes & PATCH_T_NO_LOOP))
{
if (modes & PATCH_BIDIR)
return rs_vib_bidir(song->resample_buffer, song->rate, vp, *countptr);
@ -525,7 +536,7 @@ sample_t *resample_voice(Renderer *song, Voice *vp, int *countptr)
}
else
{
if (vp->status & VOICE_LPE)
if (vp->status & VOICE_LPE && !(midi_timiditylike && vp->sample->modes & PATCH_T_NO_LOOP))
{
if (modes & PATCH_BIDIR)
return rs_bidir(song->resample_buffer, vp, *countptr);

318
src/timidity/sf2.h Normal file
View file

@ -0,0 +1,318 @@
typedef WORD SFGenerator;
struct SFRange
{
BYTE Lo;
BYTE Hi;
};
struct SFPreset
{
char Name[21];
BYTE LoadOrder:7;
BYTE bHasGlobalZone:1;
WORD Program;
WORD Bank;
WORD BagIndex;
/* Don't care about library, genre, and morphology */
};
struct SFBag
{
WORD GenIndex;
// WORD ModIndex; // If I am feeling ambitious, I might add support for modulators some day.
SFRange KeyRange;
SFRange VelRange;
int Target; // Either an instrument or sample index
};
struct SFInst
{
char Name[21];
BYTE Pad:7;
BYTE bHasGlobalZone:1;
WORD BagIndex;
};
struct SFSample
{
float *InMemoryData;
DWORD Start;
DWORD End;
DWORD StartLoop;
DWORD EndLoop;
DWORD SampleRate;
BYTE OriginalPitch;
SBYTE PitchCorrection;
WORD SampleLink;
WORD SampleType;
char Name[21];
};
// Sample type bit fields (all but ROM are mutually exclusive)
enum
{
SFST_Mono = 1,
SFST_Right = 2,
SFST_Left = 4,
SFST_Linked = 8, /* SF2.04 defines this bit but not its function */
SFST_Bad = 16384, /* Used internally */
SFST_ROM = 32768
};
// Generator definitions
struct SFGenList
{
SFGenerator Oper;
union
{
SFRange Range;
SWORD Amount;
WORD uAmount;
};
};
enum
{
GEN_startAddrsOffset,
GEN_endAddrsOffset,
GEN_startloopAddrsOffset,
GEN_endloopAddrsOffset,
GEN_startAddrsCoarseOffset,
GEN_modLfoToPitch,
GEN_vibLfoToPitch,
GEN_modEnvToPitch,
GEN_initialFilterFC,
GEN_initialFilterQ,
GEN_modLfoToFilterFc,
GEN_modEnvToFilterFc,
GEN_endAddrsCoarseOffset,
GEN_modLfoToVolume,
GEN_unused1,
GEN_chorusEffectsSend,
GEN_reverbEffectsSend,
GEN_pan,
GEN_unused2,
GEN_unused3,
GEN_unused4,
GEN_delayModLFO,
GEN_freqModLFO,
GEN_delayVibLFO,
GEN_freqVibLFO,
GEN_delayModEnv,
GEN_attackModEnv,
GEN_holdModEnv,
GEN_decayModEnv,
GEN_sustainModEnv,
GEN_releaseModEnv,
GEN_keynumToModEnvHold,
GEN_keynumToModEnvDecay,
GEN_delayVolEnv,
GEN_attackVolEnv,
GEN_holdVolEnv,
GEN_decayVolEnv,
GEN_sustainVolEnv,
GEN_releaseVolEnv,
GEN_keynumToVolEnvHold,
GEN_keynumToVolEnvDecay,
GEN_instrument,
GEN_reserved1,
GEN_keyRange,
GEN_velRange,
GEN_startloopAddrsCoarseOffset,
GEN_keynum,
GEN_velocity,
GEN_initialAttenuation,
GEN_reserved2,
GEN_endloopAddrsCoarseOffset,
GEN_coarseTune,
GEN_fineTune,
GEN_sampleID,
GEN_sampleModes,
GEN_reserved3,
GEN_scaleTuning,
GEN_exclusiveClass,
GEN_overridingRootKey,
GEN_NumGenerators
};
// Modulator definitions
struct SFModulator
{
WORD Index:7;
WORD CC:1;
WORD Dir:1; /* 0 = min->max, 1 = max->min */
WORD Polarity:1; /* 0 = unipolar, 1 = bipolar */
WORD Type:6;
};
struct SFModList
{
SFModulator SrcOper;
SFGenerator DestOper;
SWORD Amount;
SFModulator AmtSrcOper;
WORD Transform;
};
// Modulator sources when CC is 0
enum
{
SFMod_One = 0, // Psuedo-controller that always has the value 1
SFMod_NoteVelocity = 2,
SFMod_KeyNumber = 3,
SFMod_PolyPressure = 10,
SFMod_ChannelPressure = 13,
SFMod_PitchWheel = 14,
SFMod_PitchSens = 16,
SFMod_Link = 127
};
// Modulator types
enum
{
SFModType_Linear,
SFModType_Concave, // log(fabs(value)/(max value)^2)
SFModType_Convex,
SFModType_Switch
};
// Modulator transforms
enum
{
SFModTrans_Linear = 0,
SFModTrans_Abs = 2
};
// All possible generators in a single structure
struct SFGenComposite
{
union
{
SFRange keyRange; // For normal use
struct // For intermediate percussion use
{
BYTE drumset;
BYTE key;
};
};
SFRange velRange;
union
{
WORD instrument; // At preset level
WORD sampleID; // At instrument level
};
SWORD modLfoToPitch;
SWORD vibLfoToPitch;
SWORD modEnvToPitch;
SWORD initialFilterFc;
SWORD initialFilterQ;
SWORD modLfoToFilterFc;
SWORD modEnvToFilterFc;
SWORD modLfoToVolume;
SWORD chorusEffectsSend;
SWORD reverbEffectsSend;
SWORD pan;
SWORD delayModLFO;
SWORD freqModLFO;
SWORD delayVibLFO;
SWORD freqVibLFO;
SWORD delayModEnv;
SWORD attackModEnv;
SWORD holdModEnv;
SWORD decayModEnv;
SWORD sustainModEnv;
SWORD releaseModEnv;
SWORD keynumToModEnvHold;
SWORD keynumToModEnvDecay;
SWORD delayVolEnv;
SWORD attackVolEnv;
SWORD holdVolEnv;
SWORD decayVolEnv;
SWORD sustainVolEnv;
SWORD releaseVolEnv;
SWORD keynumToVolEnvHold;
SWORD keynumToVolEnvDecay;
SWORD initialAttenuation;
SWORD coarseTune;
SWORD fineTune;
SWORD scaleTuning;
// The following are only for instruments:
SWORD startAddrsOffset, startAddrsCoarseOffset;
SWORD endAddrsOffset, endAddrsCoarseOffset;
SWORD startLoopAddrsOffset, startLoopAddrsCoarseOffset;
SWORD endLoopAddrsOffset, endLoopAddrsCoarseOffset;
SWORD keynum;
SWORD velocity;
WORD sampleModes;
SWORD exclusiveClass;
SWORD overridingRootKey;
};
// Intermediate percussion representation
struct SFPerc
{
SFPreset *Preset;
SFGenComposite Generators;
BYTE LoadOrder;
};
// Container for all parameters from a SoundFont file
struct SFFile : public Timidity::FontFile
{
SFFile(FString filename);
~SFFile();
Timidity::Instrument *LoadInstrument(struct Timidity::Renderer *song, int drum, int bank, int program);
Timidity::Instrument *LoadInstrumentOrder(struct Timidity::Renderer *song, int order, int drum, int bank, int program);
void SetOrder(int order, int drum, int bank, int program);
void SetAllOrders(int order);
bool FinalStructureTest();
void CheckBags();
void CheckZones(int start, int stop, bool instr);
void TranslatePercussions();
void TranslatePercussionPreset(SFPreset *preset);
void TranslatePercussionPresetZone(SFPreset *preset, SFBag *zone);
void SetInstrumentGenerators(SFGenComposite *composite, int start, int stop);
void AddPresetGenerators(SFGenComposite *composite, int start, int stop, SFPreset *preset);
void AddPresetGenerators(SFGenComposite *composite, int start, int stop, bool gen_set[GEN_NumGenerators]);
Timidity::Instrument *LoadPercussion(Timidity::Renderer *song, SFPerc *perc);
Timidity::Instrument *LoadPreset(Timidity::Renderer *song, SFPreset *preset);
void LoadSample(SFSample *sample);
void ApplyGeneratorsToRegion(SFGenComposite *gen, SFSample *sfsamp, Timidity::Renderer *song, Timidity::Sample *sp);
SFPreset *Presets;
SFBag *PresetBags;
SFGenList *PresetGenerators;
SFInst *Instruments;
SFBag *InstrBags;
SFGenList *InstrGenerators;
SFSample *Samples;
TArray<SFPerc> Percussion;
int MinorVersion;
DWORD SampleDataOffset;
DWORD SampleDataLSBOffset;
DWORD SizeSampleData;
DWORD SizeSampleDataLSB;
int NumPresets;
int NumPresetBags;
int NumPresetGenerators;
int NumInstruments;
int NumInstrBags;
int NumInstrGenerators;
int NumSamples;
};
SFFile *ReadSF2(const char *filename, FileReader *f);

View file

@ -28,11 +28,13 @@
#include "m_alloc.h"
#include "cmdlib.h"
#include "c_cvars.h"
#include "c_dispatch.h"
#include "i_system.h"
#include "files.h"
CVAR(String, timidity_config, CONFIG_FILE, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(Int, timidity_voices, 32, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(String, midi_config, CONFIG_FILE, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(Int, midi_voices, 32, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(Bool, midi_timiditylike, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
namespace Timidity
{
@ -42,13 +44,10 @@ ToneBank *tonebank[MAXBANK], *drumset[MAXBANK];
static FString def_instr_name;
int openmode = OM_FILEORLUMP;
#define MAXWORDS 10
static int read_config_file(const char *name, bool ismain)
{
FileReader *fp;
char tmp[1024], *w[MAXWORDS], *cp;
char tmp[1024], *cp;
ToneBank *bank = NULL;
int i, j, k, line = 0, words;
static int rcf_count = 0;
@ -81,17 +80,29 @@ static int read_config_file(const char *name, bool ismain)
while (fp->Gets(tmp, sizeof(tmp)))
{
line++;
w[words = 0] = strtok(tmp, " \t\r\n\240");
if (!w[0]) continue;
FCommandLine w(tmp, true);
words = w.argc();
if (words == 0) continue;
/* Originally the TiMidity++ extensions were prefixed like this */
if (strcmp(w[0], "#extension") == 0)
words = -1;
{
w.Shift();
words--;
}
else if (*w[0] == '#')
{
continue;
}
while (w[words] && *w[words] != '#' && (words < MAXWORDS))
w[++words] = strtok(0, " \t\r\n\240");
for (i = 0; i < words; ++i)
{
if (*w[i] == '#')
{
words = i;
break;
}
}
/*
* TiMidity++ adds a number of extensions to the config file format.
@ -155,19 +166,93 @@ static int read_config_file(const char *name, bool ismain)
*/
Printf("FIXME: Implement \"altassign\" in TiMidity config.\n");
}
else if (!strcmp(w[0], "soundfont") || !strcmp(w[0], "font"))
else if (!strcmp(w[0], "soundfont"))
{
/*
* I can't find any documentation for these, but I guess they're
* an alternative way of loading/unloading instruments.
*
* "soundfont" sf_file "remove"
* "soundfont" sf_file ["order=" order] ["cutoff=" cutoff]
* ["reso=" reso] ["amp=" amp]
*/
if (words < 2)
{
Printf("%s: line %d: No soundfont given\n", name, line);
delete fp;
return -2;
}
if (words > 2 && !strcmp(w[2], "remove"))
{
font_remove(w[1]);
}
else
{
int order = 0;
for (i = 2; i < words; ++i)
{
if (!(cp = strchr(w[i], '=')))
{
Printf("%s: line %d: bad soundfont option %s\n", name, line, w[i]);
delete fp;
return -2;
}
}
font_add(w[1], order);
}
}
else if (!strcmp(w[0], "font"))
{
/*
* "font" "exclude" bank preset keynote
* "font" "order" order bank preset keynote
*/
Printf("FIXME: Implmement \"%s\" in TiMidity config.\n", w[0]);
int order, drum = -1, bank = -1, instr = -1;
if (words < 3)
{
Printf("%s: line %d: syntax error\n", name, line);
delete fp;
return -2;
}
if (!strcmp(w[1], "exclude"))
{
order = 254;
i = 2;
}
else if (!strcmp(w[1], "order"))
{
order = atoi(w[2]);
i = 3;
}
else
{
Printf("%s: line %d: font subcommand must be 'order' or 'exclude'\n", name, line);
delete fp;
return -2;
}
if (i < words)
{
drum = atoi(w[i++]);
}
if (i < words)
{
bank = atoi(w[i++]);
}
if (i < words)
{
instr = atoi(w[i++]);
}
if (drum != 128)
{
instr = bank;
bank = drum;
drum = 0;
}
else
{
drum = 1;
}
font_order(order, drum, bank, instr);
}
else if (!strcmp(w[0], "progbase"))
{
@ -289,12 +374,34 @@ static int read_config_file(const char *name, bool ismain)
delete fp;
return -2;
}
bank->tone[i].name = w[1];
bank->tone[i].note = bank->tone[i].amp = bank->tone[i].pan =
bank->tone[i].strip_loop = bank->tone[i].strip_envelope =
bank->tone[i].strip_tail = -1;
bank->tone[i].fontbank = bank->tone[i].fontpreset =
bank->tone[i].fontnote = bank->tone[i].strip_loop =
bank->tone[i].strip_envelope = bank->tone[i].strip_tail = -1;
for (j = 2; j<words; j++)
if (!strcmp(w[1], "%font"))
{
bank->tone[i].name = w[2];
bank->tone[i].fontbank = atoi(w[3]);
bank->tone[i].fontpreset = atoi(w[4]);
if (bank->tone[i].fontbank == 128 || (w[5][0] >= '0' && w[5][0] <= '9'))
{
bank->tone[i].fontnote = atoi(w[5]);
j = 6;
}
else
{
j = 5;
}
font_add(w[2], 254);
}
else
{
bank->tone[i].name = w[1];
j = 2;
}
for (; j<words; j++)
{
if (!(cp=strchr(w[j], '=')))
{
@ -397,6 +504,7 @@ static int read_config_file(const char *name, bool ismain)
void FreeAll()
{
free_instruments();
font_freeall();
for (int i = 0; i < MAXBANK; ++i)
{
if (tonebank[i] != NULL)
@ -444,7 +552,7 @@ int LoadConfig(const char *filename)
int LoadConfig()
{
return LoadConfig(timidity_config);
return LoadConfig(midi_config);
}
Renderer::Renderer(float sample_rate)
@ -466,7 +574,7 @@ Renderer::Renderer(float sample_rate)
if (def_instr_name.IsNotEmpty())
set_default_instrument(def_instr_name);
voices = clamp<int>(timidity_voices, 16, 256);
voices = clamp<int>(midi_voices, 16, 256);
voice = new Voice[voices];
drumchannels = DEFAULT_DRUMCHANNELS;
}

View file

@ -53,7 +53,11 @@ config.h
/* A scalar applied to the final mix to try and approximate the
volume level of FMOD's built-in MIDI player. */
#define FINAL_MIX_SCALE 0.5f
#define FINAL_MIX_SCALE 0.5
/* This value is used instead when midi_timiditylike is turned on,
because TiMidity++ is louder than a GUS. */
#define FINAL_MIX_TIMIDITY_SCALE 0.3
/* How many bits to use for the fractional part of sample positions.
This affects tonal accuracy. The entire position counter must fit
@ -177,6 +181,7 @@ extern void clear_pathlist();
extern void *safe_malloc(size_t count);
FileReader *open_filereader(const char *name, int open, int *plumpnum);
extern int openmode;
/*
controls.h
@ -204,53 +209,6 @@ void cmsg(int type, int verbosity_level, const char *fmt, ...);
instrum.h
*/
struct Sample
{
SDWORD
loop_start, loop_end, data_length,
sample_rate, low_vel, high_vel, low_freq, high_freq, root_freq;
SDWORD
envelope_rate[6], envelope_offset[6];
float
volume;
sample_t *data;
SDWORD
tremolo_sweep_increment, tremolo_phase_increment,
vibrato_sweep_increment, vibrato_control_ratio;
BYTE
tremolo_depth, vibrato_depth,
modes;
WORD
panning, scale_factor;
SWORD
scale_note;
bool
self_nonexclusive;
BYTE
key_group;
float
left_offset, right_offset;
};
void convert_sample_data(Sample *sample, const void *data);
void free_instruments();
/* Patch definition: */
enum
{
HEADER_SIZE = 12,
ID_SIZE = 10,
DESC_SIZE = 60,
RESERVED_SIZE = 40,
PATCH_HEADER_RESERVED_SIZE = 36,
LAYER_RESERVED_SIZE = 40,
PATCH_DATA_RESERVED_SIZE = 36,
INST_NAME_SIZE = 16,
ENVELOPES = 6,
MAX_LAYERS = 4
};
#define GF1_HEADER_TEXT "GF1PATCH110"
enum
{
PATCH_16 = (1<<0),
@ -260,83 +218,98 @@ enum
PATCH_BACKWARD = (1<<4),
PATCH_SUSTAIN = (1<<5),
PATCH_NO_SRELEASE = (1<<6),
PATCH_FAST_REL = (1<<7)
PATCH_FAST_REL = (1<<7),
PATCH_T_NO_ENVELOPE = (1<<8),
PATCH_T_NO_LOOP = (1<<9)
};
#ifdef _MSC_VER
#pragma pack(push, 1)
#define GCC_PACKED
#else
#define GCC_PACKED __attribute__((__packed__))
#endif
struct GF1PatchHeader
struct Sample
{
char Header[HEADER_SIZE];
char GravisID[ID_SIZE]; /* Id = "ID#000002" */
char Description[DESC_SIZE];
BYTE Instruments;
BYTE Voices;
BYTE Channels;
WORD WaveForms;
WORD MasterVolume;
DWORD DataSize;
BYTE Reserved[PATCH_HEADER_RESERVED_SIZE];
} GCC_PACKED;
struct GF1InstrumentData
SDWORD
loop_start, loop_end, data_length,
sample_rate;
float
low_freq, high_freq, root_freq;
union
{
WORD Instrument;
char InstrumentName[INST_NAME_SIZE];
int InstrumentSize;
BYTE Layers;
BYTE Reserved[RESERVED_SIZE];
} GCC_PACKED;
struct GF1LayerData
struct
{
BYTE LayerDuplicate;
BYTE Layer;
int LayerSize;
BYTE Samples;
BYTE Reserved[LAYER_RESERVED_SIZE];
} GCC_PACKED;
struct GF1PatchData
BYTE rate[6], offset[6];
} gf1;
struct
{
char WaveName[7];
BYTE Fractions;
int WaveSize;
int StartLoop;
int EndLoop;
WORD SampleRate;
int LowFrequency;
int HighFrequency;
int RootFrequency;
SWORD Tune;
BYTE Balance;
BYTE EnvelopeRate[ENVELOPES];
BYTE EnvelopeOffset[ENVELOPES];
BYTE TremoloSweep;
BYTE TremoloRate;
BYTE TremoloDepth;
BYTE VibratoSweep;
BYTE VibratoRate;
BYTE VibratoDepth;
BYTE Modes;
SWORD ScaleFrequency;
WORD ScaleFactor; /* From 0 to 2048 or 0 to 2 */
BYTE Reserved[PATCH_DATA_RESERVED_SIZE];
} GCC_PACKED;
#ifdef _MSC_VER
#pragma pack(pop)
#endif
#undef GCC_PACKED
short delay_vol;
short attack_vol;
short hold_vol;
short decay_vol;
short sustain_vol;
short release_vol;
} sf2;
} envelope;
float
volume;
sample_t *data;
SDWORD
tremolo_sweep_increment, tremolo_phase_increment,
vibrato_sweep_increment, vibrato_control_ratio;
BYTE
tremolo_depth, vibrato_depth,
low_vel, high_vel,
type;
WORD
modes;
SWORD
panning;
WORD
scale_factor, key_group;
SWORD
scale_note;
bool
self_nonexclusive;
float
left_offset, right_offset;
// SF2 stuff
SWORD tune;
SBYTE velocity;
float initial_attenuation;
};
void convert_sample_data(Sample *sample, const void *data);
void free_instruments();
/* Magic file words */
#define ID_RIFF MAKE_ID('R','I','F','F')
#define ID_LIST MAKE_ID('L','I','S','T')
#define ID_INFO MAKE_ID('I','N','F','O')
#define ID_sfbk MAKE_ID('s','f','b','k')
#define ID_sdta MAKE_ID('s','d','t','a')
#define ID_pdta MAKE_ID('p','d','t','a')
#define ID_ifil MAKE_ID('i','f','i','l')
#define ID_iver MAKE_ID('i','v','e','r')
#define ID_irom MAKE_ID('i','r','o','m')
#define ID_smpl MAKE_ID('s','m','p','l')
#define ID_sm24 MAKE_ID('s','m','2','4')
#define ID_phdr MAKE_ID('p','h','d','r')
#define ID_pbag MAKE_ID('p','b','a','g')
#define ID_pmod MAKE_ID('p','m','o','d')
#define ID_pgen MAKE_ID('p','g','e','n')
#define ID_inst MAKE_ID('i','n','s','t')
#define ID_ibag MAKE_ID('i','b','a','g')
#define ID_imod MAKE_ID('i','m','o','d')
#define ID_igen MAKE_ID('i','g','e','n')
#define ID_shdr MAKE_ID('s','h','d','r')
/* Instrument definitions */
enum
{
INST_GUS,
INST_DLS
INST_DLS,
INST_SF2
};
struct Instrument
@ -344,7 +317,6 @@ struct Instrument
Instrument();
~Instrument();
int type;
int samples;
Sample *sample;
};
@ -356,11 +328,10 @@ struct ToneBankElement
{}
FString name;
int note, amp, pan, strip_loop, strip_envelope, strip_tail;
int note, amp, pan, fontbank, fontpreset, fontnote, strip_loop, strip_envelope, strip_tail;
};
/* A hack to delay instrument loading until after reading the
entire MIDI file. */
/* A hack to delay instrument loading until after reading the entire MIDI file. */
#define MAGIC_LOAD_INSTRUMENT ((Instrument *)(-1))
enum
@ -381,7 +352,34 @@ struct ToneBank
#define SPECIAL_PROGRAM -1
extern void pcmap(int *b, int *v, int *p, int *drums);
/*
instrum_font.cpp
*/
class FontFile
{
public:
FontFile(FString filename);
virtual ~FontFile();
FString Filename;
FontFile *Next;
virtual Instrument *LoadInstrument(struct Renderer *song, int drum, int bank, int program) = 0;
virtual Instrument *LoadInstrumentOrder(struct Renderer *song, int order, int drum, int bank, int program) = 0;
virtual void SetOrder(int order, int drum, int bank, int program) = 0;
virtual void SetAllOrders(int order) = 0;
};
void font_freeall();
FontFile *font_find(const char *filename);
void font_add(const char *filename, int load_order);
void font_remove(const char *filename);
void font_order(int order, int bank, int preset, int keynote);
Instrument *load_instrument_font(struct Renderer *song, const char *font, int drum, int bank, int instrument);
Instrument *load_instrument_font_order(struct Renderer *song, int order, int drum, int bank, int instrument);
FontFile *ReadDLS(const char *filename, FileReader *f);
/*
mix.h
@ -446,9 +444,9 @@ struct Channel
bank, program, sustain, pitchbend,
mono, /* one note only on this channel */
pitchsens;
WORD
BYTE
volume, expression;
SWORD
SBYTE
panning;
WORD
rpn, nrpn;
@ -456,13 +454,81 @@ struct Channel
nrpn_mode;
float
pitchfactor; /* precomputed pitch bend factor to save some fdiv's */
float
left_offset, right_offset; /* precomputed panning values */
};
/* Causes the instrument's default panning to be used. */
#define NO_PANNING -1
struct MinEnvelope
{
int stage;
BYTE bUpdating;
};
struct GF1Envelope : public MinEnvelope
{
int volume, target, increment;
int rate[6], offset[6];
void Init(struct Renderer *song, Voice *v);
bool Update(struct Voice *v);
bool Recompute(struct Voice *v);
void ApplyToAmp(struct Voice *v);
void Release(struct Voice *v);
};
struct SF2Envelope : public MinEnvelope
{
float volume;
float DelayTime; // timecents
float AttackTime; // timecents
float HoldTime; // timecents
float DecayTime; // timecents
float SustainLevel; // -0.1%
float ReleaseTime; // timecents
float SampleRate;
int HoldStart;
float RateMul;
float RateMul_cB;
void Init(struct Renderer *song, Voice *v);
bool Update(struct Voice *v);
void ApplyToAmp(struct Voice *v);
void Release(struct Voice *v);
};
struct Envelope
{
union
{
MinEnvelope env;
GF1Envelope gf1;
SF2Envelope sf2;
};
BYTE Type;
void Init(struct Renderer *song, struct Voice *v);
bool Update(struct Voice *v)
{
if (Type == INST_GUS)
return gf1.Update(v);
return sf2.Update(v);
}
void ApplyToAmp(struct Voice *v)
{
if (Type == INST_GUS)
return gf1.ApplyToAmp(v);
return sf2.ApplyToAmp(v);
}
void Release(struct Voice *v)
{
if (Type == INST_GUS)
return gf1.Release(v);
return sf2.Release(v);
}
};
struct Voice
{
BYTE
@ -472,11 +538,12 @@ struct Voice
orig_frequency, frequency;
int
sample_offset, sample_increment,
envelope_volume, envelope_target, envelope_increment,
tremolo_sweep, tremolo_sweep_position,
tremolo_phase, tremolo_phase_increment,
vibrato_sweep, vibrato_sweep_position;
Envelope eg1, eg2;
final_volume_t left_mix, right_mix;
float
@ -487,8 +554,10 @@ struct Voice
vibrato_sample_increment[VIBRATO_SAMPLE_INCREMENTS];
int
vibrato_phase, vibrato_control_ratio, vibrato_control_counter,
envelope_stage, control_counter;
control_counter;
int
sample_count;
};
/* Voice status options: */
@ -506,12 +575,23 @@ enum
/* Envelope stages: */
enum
{
ATTACK,
HOLD,
DECAY,
RELEASE,
RELEASEB,
RELEASEC
GF1_ATTACK,
GF1_HOLD,
GF1_DECAY,
GF1_RELEASE,
GF1_RELEASEB,
GF1_RELEASEC
};
enum
{
SF2_DELAY,
SF2_ATTACK,
SF2_HOLD,
SF2_DECAY,
SF2_SUSTAIN,
SF2_RELEASE,
SF2_FINISHED
};
#define ISDRUMCHANNEL(c) ((drumchannels & (1<<(c))))
@ -527,10 +607,17 @@ extern void pre_resample(struct Renderer *song, Sample *sp);
tables.h
*/
const double log_of_2 = 0.69314718055994529;
#define sine(x) (sin((2*PI/1024.0) * (x)))
#define note_to_freq(x) (float(8175.7989473096690661233836992789 * pow(2.0, (x) / 12.0)))
//#define calc_vol(x) (pow(2.0,((x)*6.0 - 6.0))) // Physically ideal equation
#define freq_to_note(x) (log((x) / 8175.7989473096690661233836992789) * (12.0 / log_of_2))
#define calc_gf1_amp(x) (pow(2.0,((x)*16.0 - 16.0))) // Actual GUS equation
#define cb_to_amp(x) (pow(10.0, (x) * (1 / -200.0))) // centibels to amp
#define db_to_amp(x) (pow(10.0, (x) * (1 / -20.0))) // decibels to map
#define timidityxx_perceived_vol(x) (pow((x), 1.66096404744))
/*
timidity.h
@ -582,7 +669,8 @@ struct Renderer
void kill_key_group(int voice);
float calculate_scaled_frequency(Sample *sample, int note);
void start_note(int chan, int note, int vel, int voice);
void start_note(int chan, int note, int vel);
bool start_region(int chan, int note, int vel, Sample *sp, float freq);
void note_on(int chan, int note, int vel);
void note_off(int chan, int note, int vel);
@ -598,7 +686,7 @@ struct Renderer
void reset_controllers(int chan);
void reset_midi();
void select_sample(int voice, Instrument *instr, int vel);
int allocate_voice();
void kill_note(int voice);
void finish_note(int voice);
@ -608,7 +696,7 @@ struct Renderer
void DataEntryCoarseNRPN(int chan, int nrpn, int val);
void DataEntryFineNRPN(int chan, int nrpn, int val);
static void compute_pan(int panning, float &left_offset, float &right_offset);
static void compute_pan(double panning, int type, float &left_offset, float &right_offset);
};
}

View file

@ -77,7 +77,7 @@
// SAVESIG should match SAVEVER.
// MINSAVEVER is the minimum level snapshot version that can be loaded.
#define MINSAVEVER 932
#define MINSAVEVER 964
#if ZD_SVN_REVISION_NUMBER < MINSAVEVER
// Never write a savegame with a version lower than what we need

View file

@ -1431,6 +1431,7 @@ int FWadCollection::MergeLumps (const char *start, const char *end, int space)
newlumpinfos[0].position =
newlumpinfos[0].size = 0;
newlumpinfos[0].namespc = ns_global;
newlumpinfos[0].flags = 0;
}
newlumpinfos[newlumps++] = LumpInfo[i];
@ -1452,6 +1453,7 @@ int FWadCollection::MergeLumps (const char *start, const char *end, int space)
newlumpinfos[0].position =
newlumpinfos[0].size = 0;
newlumpinfos[0].namespc = ns_global;
newlumpinfos[0].flags = 0;
}
}
else
@ -1499,6 +1501,7 @@ int FWadCollection::MergeLumps (const char *start, const char *end, int space)
LumpInfo[NumLumps].position =
LumpInfo[NumLumps].size = 0;
LumpInfo[NumLumps].namespc = ns_global;
LumpInfo[NumLumps].flags = 0;
NumLumps++;
}

View file

@ -6,13 +6,14 @@
enum
{
LINETRANS_HASTAGAT1 = (1<<5), // (tag, x, x, x, x)
LINETRANS_HASTAGAT2 = (2<<5), // (x, tag, x, x, x)
LINETRANS_HASTAGAT3 = (3<<5), // (x, x, tag, x, x)
LINETRANS_HASTAGAT4 = (4<<5), // (x, x, x, tag, x)
LINETRANS_HASTAGAT5 = (5<<5), // (x, x, x, x, tag)
LINETRANS_HASTAGAT1 = (1<<6), // (tag, x, x, x, x)
LINETRANS_HASTAGAT2 = (2<<6), // (x, tag, x, x, x)
LINETRANS_HASTAGAT3 = (3<<6), // (x, x, tag, x, x)
LINETRANS_HASTAGAT4 = (4<<6), // (x, x, x, tag, x)
LINETRANS_HASTAGAT5 = (5<<6), // (x, x, x, x, tag)
LINETRANS_HAS2TAGS = (7<<5), // (tag, tag, x, x, x)
LINETRANS_HAS2TAGS = (7<<6), // (tag, tag, x, x, x)
LINETRANS_TAGMASK = (7<<6)
};
struct FLineTrans

View file

@ -2,7 +2,7 @@ skill baby
AmmoFactor 2
DamageFactor 0.5
EasyBossBrain
SpawnFilter "Easy"
SpawnFilter "Baby"
PicName "M_JKILL"
Key i
@ -27,7 +27,7 @@ skill nightmare
FastMonsters
DisableCheats
RespawnTime 12
SpawnFilter "Hard"
SpawnFilter "Nightmare"
PicName "M_NMARE"
MustConfirm
Key n

View file

@ -5,7 +5,7 @@ skill baby
DoubleAmmoFactor 1.5
DamageFactor 0.5
EasyBossBrain
SpawnFilter "Easy"
SpawnFilter "Baby"
Name "$MNU_WETNURSE"
skill easy
@ -28,7 +28,7 @@ skill nightmare
DoubleAmmoFactor 1.5
FastMonsters
DisableCheats
SpawnFilter "Hard"
SpawnFilter "Nightmare"
Name "$MNU_BLACKPLAGUE"
clearepisodes

View file

@ -6,7 +6,7 @@ skill baby
DoubleAmmoFactor 1.5
DamageFactor 0.5
EasyBossBrain
SpawnFilter "Easy"
SpawnFilter "Baby"
Name "$MNU_WETNURSE"
PlayerClassName "fighter" "$MNU_SQUIRE"
PlayerClassName "cleric" "$MNU_ALTARBOY"
@ -42,7 +42,7 @@ skill nightmare
DoubleAmmoFactor 1.5
FastMonsters
DisableCheats
SpawnFilter "Hard"
SpawnFilter "Nightmare"
Name "$MNU_BLACKPLAGUE"
PlayerClassName "fighter" "$MNU_TITAN"
PlayerClassName "cleric" "$MNU_POPE"

View file

@ -4,7 +4,7 @@ skill baby
AmmoFactor 2
DamageFactor 0.5
EasyBossBrain
SpawnFilter "Easy"
SpawnFilter "Baby"
PicName "M_JKILL"
Key t
@ -28,7 +28,7 @@ skill nightmare
FastMonsters
DisableCheats
RespawnTime 16
SpawnFilter "Hard"
SpawnFilter "Nightmare"
PicName "M_NMARE"
Key b

View file

@ -75,6 +75,7 @@ define SHOOT (6)
define MONST (16)
define MONWALK (4)
define REP (1)
define FIRSTSIDE (32)
enum
{