mirror of
https://github.com/ZDoom/gzdoom-last-svn.git
synced 2025-06-04 11:10:48 +00:00
- 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:
parent
bfaeb75abf
commit
7708436a98
73 changed files with 5591 additions and 1759 deletions
154
gzdoom.vcproj
154
gzdoom.vcproj
|
@ -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,59 +2964,7 @@
|
|||
Name="Timidity"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\src\timidity\common.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\timidity\dls1.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\timidity\dls2.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\timidity\instrum.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\timidity\instrum_dls.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\timidity\mix.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\timidity\playmidi.cpp"
|
||||
>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
EnableEnhancedInstructionSet="0"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\timidity\resample.cpp"
|
||||
>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
EnableEnhancedInstructionSet="0"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\timidity\timidity.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\timidity\timidity.h"
|
||||
RelativePath=".\src\timidity\instrum_font.cpp"
|
||||
>
|
||||
</File>
|
||||
<Filter
|
||||
|
@ -3031,6 +2987,82 @@
|
|||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Headers"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\src\timidity\dls1.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
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"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\timidity\instrum_dls.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\timidity\instrum_sf2.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\timidity\mix.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\timidity\playmidi.cpp"
|
||||
>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
EnableEnhancedInstructionSet="0"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\timidity\resample.cpp"
|
||||
>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
EnableEnhancedInstructionSet="0"
|
||||
/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\timidity\timidity.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
109
src/doomdata.h
109
src/doomdata.h
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -3175,11 +3175,19 @@ static void ParseSkill (FScanner &sc)
|
|||
}
|
||||
else if (sc.Compare("SpawnFilter"))
|
||||
{
|
||||
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.CheckNumber())
|
||||
{
|
||||
if (sc.Number > 0) skill.SpawnFilter |= (1<<(sc.Number-1));
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.MustGetString ();
|
||||
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"))
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
114
src/namedef.h
114
src/namedef.h
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,16 +150,25 @@ static const PClass *GetStrifeType (int typenum)
|
|||
//
|
||||
//============================================================================
|
||||
|
||||
void P_LoadStrifeConversations (const char *mapname)
|
||||
void P_LoadStrifeConversations (MapData *map, const char *mapname)
|
||||
{
|
||||
if (strnicmp (mapname, "MAP", 3) != 0)
|
||||
if (map->Size(ML_CONVERSATION) > 0)
|
||||
{
|
||||
return;
|
||||
LoadScriptFile ("SCRIPT00");
|
||||
map->Seek(ML_CONVERSATION);
|
||||
LoadScriptFile (map->file, map->Size(ML_CONVERSATION));
|
||||
}
|
||||
char scriptname[9] = { 'S','C','R','I','P','T',mapname[3],mapname[4],0 };
|
||||
else
|
||||
{
|
||||
if (strnicmp (mapname, "MAP", 3) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
char scriptname[9] = { 'S','C','R','I','P','T',mapname[3],mapname[4],0 };
|
||||
|
||||
LoadScriptFile ("SCRIPT00");
|
||||
LoadScriptFile (scriptname);
|
||||
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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
1254
src/p_setup.cpp
1254
src/p_setup.cpp
File diff suppressed because it is too large
Load diff
|
@ -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.
|
||||
|
|
|
@ -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
571
src/p_slopes.cpp
Normal 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 = §ors[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 = §ors[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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
137
src/p_spec.cpp
137
src/p_spec.cpp
|
@ -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;
|
||||
}
|
||||
return false;
|
||||
lineActivation |= SPAC_Cross|SPAC_MCross;
|
||||
}
|
||||
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,40 +315,16 @@ 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:
|
||||
noway = false;
|
||||
break;
|
||||
|
||||
case SPAC_CROSS:
|
||||
switch (line->special)
|
||||
{
|
||||
case Door_Raise:
|
||||
if (line->args[1] >= 64)
|
||||
{
|
||||
break;
|
||||
}
|
||||
case Teleport:
|
||||
case Teleport_NoFog:
|
||||
case Teleport_Line:
|
||||
case Plat_DownWaitUpStayLip:
|
||||
case Plat_DownWaitUpStay:
|
||||
noway = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case SPAC_USE:
|
||||
case SPAC_PUSH:
|
||||
case SPAC_Use:
|
||||
case SPAC_Push:
|
||||
switch (line->special)
|
||||
{
|
||||
case Door_Raise:
|
||||
|
@ -366,11 +336,34 @@ bool P_TestActivateLine (line_t *line, AActor *mo, int side, int activationType)
|
|||
noway = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case SPAC_MCross:
|
||||
if (!(lineActivation & SPAC_MCross))
|
||||
{
|
||||
switch (line->special)
|
||||
{
|
||||
case Door_Raise:
|
||||
if (line->args[1] >= 64)
|
||||
{
|
||||
break;
|
||||
}
|
||||
case Teleport:
|
||||
case Teleport_NoFog:
|
||||
case Teleport_Line:
|
||||
case Plat_DownWaitUpStayLip:
|
||||
case Plat_DownWaitUpStay:
|
||||
noway = false;
|
||||
}
|
||||
}
|
||||
else noway = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
noway = false;
|
||||
}
|
||||
return !noway;
|
||||
}
|
||||
if (activationType == SPAC_MCROSS &&
|
||||
lineActivation != activationType &&
|
||||
if (activationType == SPAC_MCross && !(lineActivation & SPAC_MCross) &&
|
||||
!(line->flags & ML_MONSTERSCANACTIVATE))
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -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 = §ors[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 = §ors[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 = §or[CurSector->sectornum];
|
||||
if (!CheckSectorPlane(CurSector, Results->HitType==TRACE_HitFloor))
|
||||
{
|
||||
Results->HitType=TRACE_HitNone;
|
||||
|
|
695
src/p_udmf.cpp
Normal file
695
src/p_udmf.cpp
Normal 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 = §ors[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 = §ors[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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 (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;
|
||||
|
||||
if (passthrough && (GET_SPAC(ld->flags) == SPAC_USE))
|
||||
if (passthrough && ld->activation == SPAC_Use)
|
||||
{
|
||||
ld->flags &= ~ML_SPAC_MASK;
|
||||
ld->flags |= SPAC_USETHROUGH << ML_SPAC_SHIFT;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
19
src/r_defs.h
19
src/r_defs.h
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
85
src/timidity/gf1patch.h
Normal 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
|
|
@ -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,53 +362,58 @@ 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)))
|
||||
{
|
||||
/* 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)
|
||||
{
|
||||
cmsg(CMSG_INFO, VERB_DEBUG, " - Removing loop and/or sustain\n");
|
||||
sp->modes &= ~(PATCH_SUSTAIN | PATCH_LOOPEN | PATCH_BIDIR | PATCH_BACKWARD);
|
||||
}
|
||||
sp->modes |= PATCH_T_NO_LOOP;
|
||||
}
|
||||
|
||||
if (strip_envelope == 1)
|
||||
if (strip_envelope == 1)
|
||||
{
|
||||
cmsg(CMSG_INFO, VERB_DEBUG, " - Removing envelope\n");
|
||||
/* [RH] The envelope isn't really removed, but this is the way the standard
|
||||
* Gravis patches get that effect: All rates at maximum, and all offsets at
|
||||
* a constant level.
|
||||
*/
|
||||
if (j == DESC_SIZE)
|
||||
{
|
||||
cmsg(CMSG_INFO, VERB_DEBUG, " - Removing envelope\n");
|
||||
/* [RH] The envelope isn't really removed, but this is the way the standard
|
||||
* Gravis patches get that effect: All rates at maximum, and all offsets at
|
||||
* a constant level.
|
||||
*/
|
||||
for (j = 1; j < ENVELOPES; ++j)
|
||||
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. */
|
||||
This is a very crude adjustment, but things sound more
|
||||
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 (bank->tone[i].fontbank >= 0)
|
||||
{
|
||||
ip = load_instrument_font(song, bank->tone[i].name, dr, b, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
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)
|
||||
if (((dr) ? drumset[0] : tonebank[0])->instrument[i] != NULL)
|
||||
{
|
||||
if (tonebank[0]->instrument[i] != NULL)
|
||||
{
|
||||
tonebank[0]->instrument[i] = MAGIC_LOAD_INSTRUMENT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (drumset[0]->instrument[i] != NULL)
|
||||
{
|
||||
drumset[0]->instrument[i] = MAGIC_LOAD_INSTRUMENT;
|
||||
}
|
||||
((dr) ? drumset[0] : tonebank[0])->instrument[i] = MAGIC_LOAD_INSTRUMENT;
|
||||
}
|
||||
}
|
||||
bank->instrument[i] = NULL;
|
||||
errors++;
|
||||
}
|
||||
else if (!(bank->instrument[i] =
|
||||
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)))
|
||||
{
|
||||
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);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
131
src/timidity/instrum_font.cpp
Normal file
131
src/timidity/instrum_font.cpp
Normal 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
1533
src/timidity/instrum_sf2.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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)
|
||||
/* 15.15 fixed point. */
|
||||
return int(((r * 44100) / song->rate) * song->control_ratio) << 9;
|
||||
}
|
||||
|
||||
void Envelope::Init(Renderer *song, Voice *v)
|
||||
{
|
||||
Type = v->sample->type;
|
||||
env.bUpdating = true;
|
||||
if (Type == INST_GUS)
|
||||
{
|
||||
/* Envelope ran out. */
|
||||
/* play sampled release */
|
||||
v->status &= ~(VOICE_SUSTAINING | VOICE_LPE);
|
||||
v->status |= VOICE_RELEASING;
|
||||
v->envelope_increment = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (stage == RELEASE && !(v->status & VOICE_RELEASING) && (v->sample->modes & PATCH_SUSTAIN))
|
||||
{
|
||||
v->status |= VOICE_SUSTAINING;
|
||||
/* Freeze envelope until note turns off. Trumpets want this. */
|
||||
v->envelope_increment = 0;
|
||||
gf1.Init(song, v);
|
||||
gf1.ApplyToAmp(v);
|
||||
}
|
||||
else
|
||||
{
|
||||
v->envelope_stage = stage + 1;
|
||||
sf2.Init(song, v);
|
||||
sf2.ApplyToAmp(v);
|
||||
}
|
||||
}
|
||||
|
||||
if (v->envelope_volume == v->sample->envelope_offset[stage])
|
||||
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))
|
||||
{
|
||||
return recompute_envelope(v);
|
||||
stage = GF1_RELEASE;
|
||||
Recompute(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;
|
||||
// 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)
|
||||
{
|
||||
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)))
|
||||
volume += increment;
|
||||
if (((increment < 0) && (volume <= target)) || ((increment > 0) && (volume >= 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
// Round the left- and right-most positions to their extremes, since
|
||||
// most songs only do coarse panning.
|
||||
if (panning < 128)
|
||||
if (v->sample->type == INST_GUS)
|
||||
{
|
||||
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;
|
||||
if (midi_timiditylike)
|
||||
{
|
||||
v->attenuation = float(timidityxx_perceived_vol(v->velocity / 127.0) *
|
||||
timidityxx_perceived_vol(chanvol / 127.0) *
|
||||
timidityxx_perceived_vol(chanexpr / 127.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
v->attenuation = (vol_table[(chanvol * chanexpr) / 127] * vol_table[v->velocity]) * ((127 + 64) / 12419775.f);
|
||||
}
|
||||
}
|
||||
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)));
|
||||
// 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. */
|
||||
}
|
||||
assert(ip != MAGIC_LOAD_INSTRUMENT);
|
||||
if (ip == MAGIC_LOAD_INSTRUMENT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (ip->sample->scale_factor != 1024)
|
||||
|
||||
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)
|
||||
{
|
||||
v->orig_frequency = calculate_scaled_frequency(ip->sample, note & 0x7F);
|
||||
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)
|
||||
{
|
||||
v->status |= VOICE_LPE;
|
||||
for (i = ip->samples, sp = ip->sample; i != 0; --i, ++sp)
|
||||
{
|
||||
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,7 +479,53 @@ void Renderer::kill_note(int i)
|
|||
}
|
||||
}
|
||||
|
||||
/* Only one instance of a note can be playing on a single channel. */
|
||||
int Renderer::allocate_voice()
|
||||
{
|
||||
int i, lowest;
|
||||
float lv, v;
|
||||
|
||||
for (i = 0; i < voices; ++i)
|
||||
{
|
||||
if (!(voice[i].status & VOICE_RUNNING))
|
||||
{
|
||||
return i; /* Can't get a lower volume than silence */
|
||||
}
|
||||
}
|
||||
|
||||
/* Look for the decaying note with the lowest volume */
|
||||
lowest = -1;
|
||||
lv = 1e10;
|
||||
i = voices;
|
||||
while (i--)
|
||||
{
|
||||
if ((voice[i].status & VOICE_RELEASING) && !(voice[i].status & VOICE_STOPPING))
|
||||
{
|
||||
v = voice[i].attenuation;
|
||||
if (v < lv)
|
||||
{
|
||||
lv = v;
|
||||
lowest = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
in the first place... Still, this needs to be fixed. Perhaps
|
||||
we could use a reserve of voices to play dying notes only. */
|
||||
|
||||
cut_notes++;
|
||||
voice[lowest].status = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
lost_notes++;
|
||||
}
|
||||
return lowest;
|
||||
}
|
||||
|
||||
void Renderer::note_on(int chan, int note, int vel)
|
||||
{
|
||||
if (vel == 0)
|
||||
|
@ -378,16 +534,12 @@ void Renderer::note_on(int chan, int note, int vel)
|
|||
return;
|
||||
}
|
||||
|
||||
int i = voices, lowest = -1;
|
||||
float lv = 1e10, v;
|
||||
int i = voices;
|
||||
|
||||
/* Only one instance of a note can be playing on a single channel. */
|
||||
while (i--)
|
||||
{
|
||||
if (!(voice[i].status & VOICE_RUNNING))
|
||||
{
|
||||
lowest = 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 (voice[i].channel == chan && ((voice[i].note == note && !voice[i].sample->self_nonexclusive) || channel[chan].mono))
|
||||
{
|
||||
if (channel[chan].mono)
|
||||
{
|
||||
|
@ -400,46 +552,7 @@ void Renderer::note_on(int chan, int note, int vel)
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
i = voices;
|
||||
while (i--)
|
||||
{
|
||||
if ((voice[i].status & VOICE_RELEASING) && !(voice[i].status & VOICE_STOPPING))
|
||||
{
|
||||
v = voice[i].attenuation;
|
||||
if (v < lv)
|
||||
{
|
||||
lv = v;
|
||||
lowest = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lowest != -1)
|
||||
{
|
||||
/* 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
|
||||
in the first place... Still, this needs to be fixed. Perhaps
|
||||
we could use a reserve of voices to play dying notes only. */
|
||||
|
||||
cut_notes++;
|
||||
voice[lowest].status = 0;
|
||||
start_note(chan, note, vel, lowest);
|
||||
}
|
||||
else
|
||||
{
|
||||
lost_notes++;
|
||||
}
|
||||
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;
|
||||
|
||||
|
|
|
@ -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
318
src/timidity/sf2.h
Normal 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);
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
SDWORD
|
||||
loop_start, loop_end, data_length,
|
||||
sample_rate;
|
||||
float
|
||||
low_freq, high_freq, root_freq;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
BYTE rate[6], offset[6];
|
||||
} gf1;
|
||||
struct
|
||||
{
|
||||
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;
|
||||
|
||||
struct GF1InstrumentData
|
||||
{
|
||||
WORD Instrument;
|
||||
char InstrumentName[INST_NAME_SIZE];
|
||||
int InstrumentSize;
|
||||
BYTE Layers;
|
||||
BYTE Reserved[RESERVED_SIZE];
|
||||
} GCC_PACKED;
|
||||
// SF2 stuff
|
||||
SWORD tune;
|
||||
SBYTE velocity;
|
||||
|
||||
struct GF1LayerData
|
||||
{
|
||||
BYTE LayerDuplicate;
|
||||
BYTE Layer;
|
||||
int LayerSize;
|
||||
BYTE Samples;
|
||||
BYTE Reserved[LAYER_RESERVED_SIZE];
|
||||
} GCC_PACKED;
|
||||
float initial_attenuation;
|
||||
};
|
||||
|
||||
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
|
||||
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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1426,11 +1426,12 @@ int FWadCollection::MergeLumps (const char *start, const char *end, int space)
|
|||
{
|
||||
newlumps++;
|
||||
strncpy (newlumpinfos[0].name, ustart, 8);
|
||||
newlumpinfos[0].fullname=NULL;
|
||||
newlumpinfos[0].fullname = NULL;
|
||||
newlumpinfos[0].wadnum = -1;
|
||||
newlumpinfos[0].position =
|
||||
newlumpinfos[0].size = 0;
|
||||
newlumpinfos[0].namespc = ns_global;
|
||||
newlumpinfos[0].flags = 0;
|
||||
}
|
||||
|
||||
newlumpinfos[newlumps++] = LumpInfo[i];
|
||||
|
@ -1447,11 +1448,12 @@ int FWadCollection::MergeLumps (const char *start, const char *end, int space)
|
|||
{
|
||||
newlumps++;
|
||||
strncpy (newlumpinfos[0].name, ustart, 8);
|
||||
newlumpinfos[0].fullname=NULL;
|
||||
newlumpinfos[0].fullname = NULL;
|
||||
newlumpinfos[0].wadnum = -1;
|
||||
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++;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -75,6 +75,7 @@ define SHOOT (6)
|
|||
define MONST (16)
|
||||
define MONWALK (4)
|
||||
define REP (1)
|
||||
define FIRSTSIDE (32)
|
||||
|
||||
enum
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue