* Updated to ZDoom r3383:

- Added writemidi console command. If the currently playing song is a MIDI variant, this will write it to disk.
- Fixed: XMISong::ProcessInitialMetaEvents() did not set the Division for tempo events. (Why does XMI modify the division based on the tempo?)
- Fixed: UDMFParser::AddUserKey() checked against the wrong token types when determining the values type. Also, GetUDMFInt() performed a float-to-fixed conversion on the value it returned.
- Fixed: The nodebuilder is highly likely to renumber vertices, so we need to remember how the original vertices map to the new ones until after vertex slopes are handled.
- Added custom flechettes patch, with ArtiPoisonBag4 renamed to ArtiPoisonBagGiver and ArtiPoisonBag5 renamed to ArtiPoisonBagShooter.
- Added thrupushups fix.
- Added HMI/XMI division fixes, and partially the XMI tempo fix (not currently used).
- Fix strife linetype 11 again.
- Added startup screen customization.
- Print named script's names for the scriptstat console command.
- Added ACS_NamedExecuteWithResult for DECORATE expressions. Since this is a pretty long name, you can also use CallACS, if you wish. The two are synonyms.

git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@1287 b0f79afe-0144-0410-b225-9a4edf0717df
This commit is contained in:
gez 2012-02-23 00:48:50 +00:00
parent 17f87f870f
commit 40af0eae39
25 changed files with 354 additions and 48 deletions

View file

@ -1701,6 +1701,25 @@ static FString ParseGameInfo(TArray<FString> &pwads, const char *fn, const char
sc.MustGetString();
DoomStartupInfo.BkColor = V_GetColor(NULL, sc.String);
}
else if (!nextKey.CompareNoCase("STARTUPTYPE"))
{
sc.MustGetString();
FString sttype = sc.String;
if (!sttype.CompareNoCase("DOOM"))
DoomStartupInfo.Type = FStartupInfo::DoomStartup;
else if (!sttype.CompareNoCase("HERETIC"))
DoomStartupInfo.Type = FStartupInfo::HereticStartup;
else if (!sttype.CompareNoCase("HEXEN"))
DoomStartupInfo.Type = FStartupInfo::HexenStartup;
else if (!sttype.CompareNoCase("STRIFE"))
DoomStartupInfo.Type = FStartupInfo::StrifeStartup;
else DoomStartupInfo.Type = FStartupInfo::DefaultStartup;
}
else if (!nextKey.CompareNoCase("STARTUPSONG"))
{
sc.MustGetString();
DoomStartupInfo.Song = sc.String;
}
}
return iwad;
}

View file

@ -94,6 +94,17 @@ struct FStartupInfo
FString Name;
DWORD FgColor; // Foreground color for title banner
DWORD BkColor; // Background color for title banner
FString Song;
int Type;
enum
{
DefaultStartup,
DoomStartup,
HereticStartup,
HexenStartup,
StrifeStartup,
};
};
extern FStartupInfo DoomStartupInfo;

View file

@ -119,7 +119,7 @@ bool AArtiPoisonBag3::Use (bool pickup)
*/
// When looking straight ahead, it uses a z velocity of 4 while the xy velocity
// is as set by the projectile. To accomodate this with a proper trajectory, we
// is as set by the projectile. To accommodate this with a proper trajectory, we
// aim the projectile ~20 degrees higher than we're looking at and increase the
// speed we fire at accordingly.
angle_t orgpitch = angle_t(-Owner->pitch) >> ANGLETOFINESHIFT;
@ -141,6 +141,72 @@ bool AArtiPoisonBag3::Use (bool pickup)
return false;
}
// Poison Bag 4 (Generic Giver) ----------------------------------------------
class AArtiPoisonBagGiver : public AArtiPoisonBag
{
DECLARE_CLASS (AArtiPoisonBagGiver, AArtiPoisonBag)
public:
bool Use (bool pickup);
};
IMPLEMENT_CLASS (AArtiPoisonBagGiver)
bool AArtiPoisonBagGiver::Use (bool pickup)
{
const PClass *MissileType = PClass::FindClass((ENamedName) this->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_None));
if (MissileType != NULL)
{
AActor *mo = Spawn (MissileType, Owner->x, Owner->y, Owner->z, ALLOW_REPLACE);
if (mo != NULL)
{
if (mo->IsKindOf(RUNTIME_CLASS(AInventory)))
{
AInventory *inv = static_cast<AInventory *>(mo);
if (inv->CallTryPickup(Owner))
return true;
}
mo->Destroy(); // Destroy if not inventory or couldn't be picked up
}
}
return false;
}
// Poison Bag 5 (Generic Thrower) ----------------------------------------------
class AArtiPoisonBagShooter : public AArtiPoisonBag
{
DECLARE_CLASS (AArtiPoisonBagShooter, AArtiPoisonBag)
public:
bool Use (bool pickup);
};
IMPLEMENT_CLASS (AArtiPoisonBagShooter)
bool AArtiPoisonBagShooter::Use (bool pickup)
{
const PClass *MissileType = PClass::FindClass((ENamedName) this->GetClass()->Meta.GetMetaInt (ACMETA_MissileName, NAME_None));
if (MissileType != NULL)
{
AActor *mo = P_SpawnPlayerMissile(Owner, MissileType);
if (mo != NULL)
{
// automatic handling of seeker missiles
if (mo->flags2 & MF2_SEEKERMISSILE)
{
mo->tracer = Owner->target;
}
// set the health value so that the missile works properly
if (mo->flags4 & MF4_SPECTRAL)
{
mo->health = -2;
}
return true;
}
}
return false;
}
//============================================================================
//
// GetFlechetteType

View file

@ -270,6 +270,8 @@ xx(MomX)
xx(MomY)
xx(MomZ)
xx(Abs)
xx(ACS_NamedExecuteWithResult)
xx(CallACS)
// Various actor names which are used internally
xx(MapSpot)

View file

@ -65,8 +65,8 @@ const int AAPreference = 16;
FNodeBuilder::FNodeBuilder(FLevel &level)
: Level(level), GLNodes(false), SegsStuffed(0)
{
VertexMap = NULL;
OldVertexTable = NULL;
}
FNodeBuilder::FNodeBuilder (FLevel &level,
@ -84,10 +84,14 @@ FNodeBuilder::FNodeBuilder (FLevel &level,
FNodeBuilder::~FNodeBuilder()
{
if (VertexMap != 0)
if (VertexMap != NULL)
{
delete VertexMap;
}
if (OldVertexTable != NULL)
{
delete OldVertexTable;
}
}
void FNodeBuilder::BuildMini(bool makeGLNodes)

View file

@ -209,6 +209,7 @@ public:
seg_t *&segs, glsegextra_t *&glsegextras, int &segCount,
subsector_t *&ssecs, int &subCount,
vertex_t *&verts, int &vertCount);
const int *GetOldVertexTable();
// These are used for building sub-BSP trees for polyobjects.
void Clear();
@ -227,6 +228,7 @@ public:
private:
IVertexMap *VertexMap;
int *OldVertexTable;
TArray<node_t> Nodes;
TArray<subsector_t> Subsectors;

View file

@ -75,7 +75,7 @@ angle_t FNodeBuilder::PointToAngle (fixed_t x, fixed_t y)
void FNodeBuilder::FindUsedVertices (vertex_t *oldverts, int max)
{
int *map = (int *)alloca (max*sizeof(int));
int *map = new int[max];
int i;
FPrivVert newvert;
@ -102,6 +102,17 @@ void FNodeBuilder::FindUsedVertices (vertex_t *oldverts, int max)
Level.Lines[i].v1 = (vertex_t *)(size_t)map[v1];
Level.Lines[i].v2 = (vertex_t *)(size_t)map[v2];
}
OldVertexTable = map;
}
// Retrieves the original vertex -> current vertex table.
// Doing so prevents the node builder from freeing it.
const int *FNodeBuilder::GetOldVertexTable()
{
int *table = OldVertexTable;
OldVertexTable = NULL;
return table;
}
// For every sidedef in the map, create a corresponding seg.

View file

@ -7375,7 +7375,14 @@ void DACSThinker::DumpScriptStatus ()
while (script != NULL)
{
Printf ("%d: %s\n", script->script, stateNames[script->state]);
if (script->script < 0)
{
Printf("\"%s\": %s\n", FName(ENamedName(-script->script)).GetChars(), stateNames[script->state]);
}
else
{
Printf("%d: %s\n", script->script, stateNames[script->state]);
}
script = script->next;
}
}

View file

@ -4763,11 +4763,20 @@ int P_PushUp (AActor *thing, FChangePosition *cpos)
{
return 1;
}
// [GZ] Skip thing intersect test for THRUACTORS things.
if (thing->flags2 & MF2_THRUACTORS)
return 0;
P_FindAboveIntersectors (thing);
lastintersect = intersectors.Size ();
for (; firstintersect < lastintersect; firstintersect++)
{
AActor *intersect = intersectors[firstintersect];
// [GZ] Skip this iteration for THRUSPECIES things
// Should there be MF2_THRUGHOST / MF3_GHOST checks there too for consistency?
// Or would that risk breaking established behavior? THRUGHOST, like MTHRUSPECIES,
// is normally for projectiles which would have exploded by now anyway...
if (thing->flags6 & MF6_THRUSPECIES && thing->GetSpecies() == intersect->GetSpecies())
continue;
if (!(intersect->flags2 & MF2_PASSMOBJ) ||
(!(intersect->flags3 & MF3_ISMONSTER) && intersect->Mass > mymass) ||
(intersect->flags4 & MF4_ACTLIKEBRIDGE)

View file

@ -73,7 +73,7 @@
#define MISSING_TEXTURE_WARN_LIMIT 20
void P_SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt);
void P_SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt, const int *oldvertextable);
void P_SetSlopes ();
void P_CopySlopes();
void BloodCrypt (void *data, int key, int len);
@ -3444,6 +3444,7 @@ void P_SetupLevel (char *lumpname, int position)
int numbuildthings;
int i;
bool buildmap;
const int *oldvertextable = NULL;
// This is motivated as follows:
@ -3758,7 +3759,7 @@ void P_SetupLevel (char *lumpname, int position)
bool BuildGLNodes;
if (ForceNodeBuild)
{
BuildGLNodes = Renderer->RequireGLNodes() || am_textured || multiplayer || demoplayback || demorecording || genglnodes;
BuildGLNodes = RequireGLNodes || multiplayer || demoplayback || demorecording || genglnodes;
startTime = I_FPSTime ();
TArray<FNodeBuilder::FPolyStart> polyspots, anchors;
@ -3782,6 +3783,7 @@ void P_SetupLevel (char *lumpname, int position)
vertexes, numvertexes);
endTime = I_FPSTime ();
DPrintf ("BSP generation took %.3f sec (%d segs)\n", (endTime - startTime) * 0.001, numsegs);
oldvertextable = builder.GetOldVertexTable();
reloop = true;
}
else
@ -3868,7 +3870,7 @@ void P_SetupLevel (char *lumpname, int position)
if (!buildmap)
{
// [RH] Spawn slope creating things first.
P_SpawnSlopeMakers (&MapThingsConverted[0], &MapThingsConverted[MapThingsConverted.Size()]);
P_SpawnSlopeMakers (&MapThingsConverted[0], &MapThingsConverted[MapThingsConverted.Size()], oldvertextable);
P_CopySlopes();
// Spawn 3d floors - must be done before spawning things so it can't be done in P_SpawnSpecials
@ -3898,6 +3900,10 @@ void P_SetupLevel (char *lumpname, int position)
delete[] buildthings;
}
delete map;
if (oldvertextable != NULL)
{
delete[] oldvertextable;
}
// set up world state
P_SpawnSpecials ();

View file

@ -280,7 +280,7 @@ enum
//
//==========================================================================
static void P_SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt)
static void P_SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt, const int *oldvertextable)
{
TMap<int, fixed_t> vt_heights[2];
FMapThing *mt;
@ -311,15 +311,17 @@ static void P_SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt)
for(int i = 0; i < numvertexdatas; i++)
{
int ii = oldvertextable == NULL ? i : oldvertextable[i];
if (vertexdatas[i].flags & VERTEXFLAG_ZCeilingEnabled)
{
vt_heights[1][i] = vertexdatas[i].zCeiling;
vt_heights[1][ii] = vertexdatas[i].zCeiling;
vt_found = true;
}
if (vertexdatas[i].flags & VERTEXFLAG_ZFloorEnabled)
{
vt_heights[0][i] = vertexdatas[i].zFloor;
vt_heights[0][ii] = vertexdatas[i].zFloor;
vt_found = true;
}
}
@ -413,7 +415,7 @@ static void P_SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt)
//
//===========================================================================
void P_SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt)
void P_SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt, const int *oldvertextable)
{
FMapThing *mt;
@ -421,7 +423,7 @@ void P_SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt)
{
if ((mt->type >= THING_SlopeFloorPointLine &&
mt->type <= THING_SetCeilingSlope) ||
mt->type==THING_VavoomFloor || mt->type==THING_VavoomCeiling)
mt->type == THING_VavoomFloor || mt->type == THING_VavoomCeiling)
{
fixed_t x, y, z;
secplane_t *refplane;
@ -439,7 +441,7 @@ void P_SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt)
refplane = &sec->floorplane;
}
z = refplane->ZatPoint (x, y) + (mt->z);
if (mt->type==THING_VavoomFloor || mt->type==THING_VavoomCeiling)
if (mt->type == THING_VavoomFloor || mt->type == THING_VavoomCeiling)
{
P_VavoomSlope(sec, mt->thingid, x, y, mt->z, mt->type & 1);
}
@ -465,7 +467,7 @@ void P_SpawnSlopeMakers (FMapThing *firstmt, FMapThing *lastmt)
}
}
P_SetSlopesFromVertexHeights(firstmt, lastmt);
P_SetSlopesFromVertexHeights(firstmt, lastmt, oldvertextable);
}

View file

@ -346,7 +346,7 @@ int GetUDMFInt(int type, int index, const char *key)
FUDMFKey *pKey = pKeys->Find(key);
if (pKey != NULL)
{
return FLOAT2FIXED(pKey->IntVal);
return pKey->IntVal;
}
}
}
@ -414,14 +414,14 @@ public:
{
switch (sc.TokenType)
{
case TK_Int:
case TK_IntConst:
keyarray[i] = sc.Number;
break;
case TK_Float:
case TK_FloatConst:
keyarray[i] = sc.Float;
break;
default:
case TK_String:
case TK_StringConst:
keyarray[i] = parsedString;
break;
case TK_True:
@ -438,14 +438,14 @@ public:
ukey.Key = key;
switch (sc.TokenType)
{
case TK_Int:
case TK_IntConst:
ukey = sc.Number;
break;
case TK_Float:
case TK_FloatConst:
ukey = sc.Float;
break;
default:
case TK_String:
case TK_StringConst:
ukey = parsedString;
break;
case TK_True:

View file

@ -830,3 +830,51 @@ CCMD (writewave)
Printf ("Usage: writewave <filename> [sample rate]");
}
}
//==========================================================================
//
// CCMD writemidi
//
// If the currently playing song is a MIDI variant, write it to disk.
// If successful, the current song will restart, since MIDI file generation
// involves a simulated playthrough of the song.
//
//==========================================================================
CCMD (writemidi)
{
if (argv.argc() != 2)
{
Printf("Usage: writemidi <filename>");
return;
}
if (currSong == NULL)
{
Printf("No song is currently playing.\n");
return;
}
if (!currSong->IsMIDI())
{
Printf("Current song is not MIDI-based.\n");
return;
}
TArray<BYTE> midi;
FILE *f;
bool success;
static_cast<MIDIStreamer *>(currSong)->CreateSMF(midi, 1);
f = fopen(argv[1], "wb");
if (f == NULL)
{
Printf("Could not open %s.\n", argv[1]);
return;
}
success = (fwrite(&midi[0], 1, midi.Size(), f) == (size_t)midi.Size());
fclose (f);
if (!success)
{
Printf("Could not write to music file.\n");
}
}

View file

@ -425,7 +425,7 @@ public:
void FluidSettingInt(const char *setting, int value);
void FluidSettingNum(const char *setting, double value);
void FluidSettingStr(const char *setting, const char *value);
void CreateSMF(TArray<BYTE> &file);
void CreateSMF(TArray<BYTE> &file, int looplimit=0);
protected:
MIDIStreamer(const char *dumpname, EMidiDevice type);
@ -637,6 +637,7 @@ protected:
int FindXMIDforms(const BYTE *chunk, int len, TrackInfo *songs) const;
void FoundXMID(const BYTE *chunk, int len, TrackInfo *song) const;
void ScanForTempo(const TrackInfo *song);
bool SetMIDISubsong(int subsong);
void DoInitialSetup();
void DoRestart();

View file

@ -1,5 +1,5 @@
/*
** music_midi_midiout.cpp
** music_hmi_midiout.cpp
** Code to let ZDoom play HMI MIDI music through the MIDI streaming API.
**
**---------------------------------------------------------------------------
@ -54,7 +54,7 @@
}
// In song header
#define HMI_DIVISION_OFFSET 0xD2
#define HMI_DIVISION_OFFSET 0xD4
#define HMI_TRACK_COUNT_OFFSET 0xE4
#define HMI_TRACK_DIR_PTR_OFFSET 0xE8
@ -202,7 +202,10 @@ void HMISong::SetupForHMI(int len)
}
// The division is the number of pulses per quarter note (PPQN).
Division = GetShort(MusHeader + HMI_DIVISION_OFFSET);
// HMI files have two values here, a full value and a quarter value. Some games,
// notably Quarantines, have identical values for some reason, so it's safer to
// use the quarter value and multiply it by four than to trust the full value.
Division = GetShort(MusHeader + HMI_DIVISION_OFFSET) << 2;
InitialTempo = 4000000;
Tracks = new TrackInfo[NumTracks + 1];

View file

@ -1062,14 +1062,14 @@ void MIDIStreamer::Precache()
//
//==========================================================================
void MIDIStreamer::CreateSMF(TArray<BYTE> &file)
void MIDIStreamer::CreateSMF(TArray<BYTE> &file, int looplimit)
{
DWORD delay = 0;
BYTE running_status = 0;
// Always create songs aimed at GM devices.
CheckCaps(MOD_MIDIPORT);
LoopLimit = EXPORT_LOOP_LIMIT;
LoopLimit = looplimit <= 0 ? EXPORT_LOOP_LIMIT : looplimit;
DoRestart();
Tempo = InitialTempo;

View file

@ -134,14 +134,16 @@ XMISong::XMISong (FILE *file, BYTE *musiccache, int len, EMidiDevice type)
{
return;
}
// The division is the number of pulses per quarter note (PPQN).
// It is set by a specific MIDI event, but just in case these aren't used,
// set a default value. Specs indicate it should be 120.
Division = 120;
Songs = new TrackInfo[NumSongs];
memset(Songs, 0, sizeof(*Songs) * NumSongs);
FindXMIDforms(MusHeader, len, Songs);
CurrSong = Songs;
DPrintf("XMI song count: %d\n", NumSongs);
// The division is the number of pulses per quarter note (PPQN).
Division = 60;
DPrintf("Divisions: %d\n", Division);
}
//==========================================================================
@ -198,6 +200,8 @@ int XMISong::FindXMIDforms(const BYTE *chunk, int len, TrackInfo *songs) const
// IFF chunks are padded to even byte boundaries to avoid
// unaligned reads on 68k processors.
p += 8 + chunklen + (chunklen & 1);
// Avoid crashes from corrupt chunks which indicate a negative size.
if (chunklen < 0) p = len;
}
return count;
}
@ -233,6 +237,37 @@ void XMISong::FoundXMID(const BYTE *chunk, int len, TrackInfo *song) const
}
}
//==========================================================================
//
// XMISong :: ScanForTempo
//
// Look through events chunk to find one that sets the tempo.
//
//==========================================================================
void XMISong::ScanForTempo(const TrackInfo *song)
{
const BYTE *chunk = song->EventChunk;
for (size_t q = 0; q < song->EventLen - 3; )
{
int r = q;
int evmark = chunk[r];
int evtype = chunk[r + 1];
int evlen = chunk[r + 2];
// Is it a tempo event? If so, length should be 3.
if (evmark == MIDI_META && evtype == MIDI_META_TEMPO && evlen == 3)
{
// Tempo is given in microseconds per quarter notes, so for a base of
// 120 per second, we have to divide by a million a multiply by 120 to
// get the PPQN. This is simplified by 40 to manipulate smaller values.
Tempo = (chunk[r + 3] << 16) | (chunk[r + 4] << 8) | chunk[r + 5];
Division = Tempo * 3 / 25000;
}
q += 3 + evlen;
}
}
//==========================================================================
//
// XMISong :: SetMIDISubsong
@ -304,7 +339,7 @@ bool XMISong::CheckDone()
//
// XMISong :: MakeEvents
//
// Copies MIDI events from the SMF and puts them into a MIDI stream
// Copies MIDI events from the XMI and puts them into a MIDI stream
// buffer. Returns the new position in the buffer.
//
//==========================================================================
@ -542,6 +577,7 @@ DWORD *XMISong::SendCommand (DWORD *events, EventSource due, DWORD delay)
events[1] = 0;
events[2] = (MEVT_TEMPO << 24) | Tempo;
events += 3;
Division = Tempo * 3 / 25000;
break;
}
track->EventP += len;
@ -598,6 +634,7 @@ void XMISong::ProcessInitialMetaEvents ()
(track->EventChunk[track->EventP+1]<<8) |
(track->EventChunk[track->EventP+2])
);
Division = Tempo * 3 / 25000;
break;
}
}

View file

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

View file

@ -336,6 +336,18 @@ static FxExpression *ParseExpression0 (FScanner &sc, const PClass *cls)
{
return new FxConstant(sc.Float, scpos);
}
else if (sc.CheckToken(TK_NameConst))
{
return new FxConstant(sc.Name, scpos);
}
else if (sc.CheckToken(TK_StringConst))
{
// String parameters are converted to names. Technically, this should be
// done at a higher level, as needed, but since no functions take string
// arguments and ACS_NamedExecuteWithResult/CallACS need names, this is
// a cheap way to get them working when people use "name" instead of 'name'.
return new FxConstant(FName(sc.String), scpos);
}
else if (sc.CheckToken(TK_Random))
{
FRandom *rng;

View file

@ -1868,7 +1868,7 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
newex = new FxCVar(cv, ScriptPosition);
}
*/
// amd line specials
// and line specials
else if ((num = P_FindLineSpecial(Identifier, NULL, NULL)))
{
ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s' as line special %d\n", Identifier.GetChars(), num);
@ -2293,9 +2293,18 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
return x->Resolve(ctx);
}
int min, max;
int special = P_FindLineSpecial(MethodName.GetChars(), &min, &max);
if (special > 0 && min >= 0)
int min, max, special;
if (MethodName == NAME_ACS_NamedExecuteWithResult || MethodName == NAME_CallACS)
{
special = -ACS_ExecuteWithResult;
min = 1;
max = 5;
}
else
{
special = P_FindLineSpecial(MethodName.GetChars(), &min, &max);
}
if (special != 0 && min >= 0)
{
int paramcount = ArgList? ArgList->Size() : 0;
if (paramcount < min)
@ -2326,7 +2335,10 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
//==========================================================================
//
// FxActionSpecialCall
//
// If special is negative, then the first argument will be treated as a
// name for ACS_NamedExecuteWithResult.
//
//==========================================================================
@ -2367,7 +2379,15 @@ FxExpression *FxActionSpecialCall::Resolve(FCompileContext& ctx)
{
(*ArgList)[i] = (*ArgList)[i]->Resolve(ctx);
if ((*ArgList)[i] == NULL) failed = true;
if ((*ArgList)[i]->ValueType != VAL_Int)
if (Special < 0 && i == 0)
{
if ((*ArgList)[i]->ValueType != VAL_Name)
{
ScriptPosition.Message(MSG_ERROR, "Name expected for parameter %d", i);
failed = true;
}
}
else if ((*ArgList)[i]->ValueType != VAL_Int)
{
if (ctx.lax && ((*ArgList)[i]->ValueType == VAL_Float))
{
@ -2400,6 +2420,7 @@ FxExpression *FxActionSpecialCall::Resolve(FCompileContext& ctx)
ExpVal FxActionSpecialCall::EvalExpression (AActor *self)
{
int v[5] = {0,0,0,0,0};
int special = Special;
if (Self != NULL)
{
@ -2410,12 +2431,20 @@ ExpVal FxActionSpecialCall::EvalExpression (AActor *self)
{
for(unsigned i = 0; i < ArgList->Size(); i++)
{
v[i] = (*ArgList)[i]->EvalExpression(self).GetInt();
if (special < 0)
{
special = -special;
v[i] = -(*ArgList)[i]->EvalExpression(self).GetName();
}
else
{
v[i] = (*ArgList)[i]->EvalExpression(self).GetInt();
}
}
}
ExpVal ret;
ret.Type = VAL_Int;
ret.Int = P_ExecuteSpecial(Special, NULL, self, false, v[0], v[1], v[2], v[3], v[4]);
ret.Int = P_ExecuteSpecial(special, NULL, self, false, v[0], v[1], v[2], v[3], v[4]);
return ret;
}

View file

@ -52,6 +52,7 @@
#include "w_wad.h"
#include "s_sound.h"
#include "m_argv.h"
#include "d_main.h"
// MACROS ------------------------------------------------------------------
@ -276,15 +277,18 @@ FStartupScreen *FStartupScreen::CreateInstance(int max_progress)
if (!Args->CheckParm("-nostartup"))
{
if (gameinfo.gametype == GAME_Hexen)
if (DoomStartupInfo.Type == FStartupInfo::HexenStartup ||
(gameinfo.gametype == GAME_Hexen && DoomStartupInfo.Type == FStartupInfo::DefaultStartup))
{
scr = new FHexenStartupScreen(max_progress, hr);
}
else if (gameinfo.gametype == GAME_Heretic)
else if (DoomStartupInfo.Type == FStartupInfo::HereticStartup ||
(gameinfo.gametype == GAME_Heretic && DoomStartupInfo.Type == FStartupInfo::DefaultStartup))
{
scr = new FHereticStartupScreen(max_progress, hr);
}
else if (gameinfo.gametype == GAME_Strife)
else if (DoomStartupInfo.Type == FStartupInfo::StrifeStartup ||
(gameinfo.gametype == GAME_Strife && DoomStartupInfo.Type == FStartupInfo::DefaultStartup))
{
scr = new FStrifeStartupScreen(max_progress, hr);
}
@ -684,8 +688,14 @@ FHexenStartupScreen::FHexenStartupScreen(int max_progress, HRESULT &hr)
LayoutMainWindow (Window, NULL);
InvalidateRect (StartupScreen, NULL, TRUE);
S_ChangeMusic ("orb", true, true);
if (DoomStartupInfo.Song.IsNotEmpty())
{
S_ChangeMusic(DoomStartupInfo.Song.GetChars(), true, true);
}
else
{
S_ChangeMusic ("orb", true, true);
}
hr = S_OK;
}

View file

@ -133,6 +133,17 @@ script << 0 >> (int type, int tag)
}
break;
case 11:
if (tag == 0)
{
Exit_Normal (0);
}
else
{
Teleport_NewMap (tag, 0, FALSE);
}
break;
case 52:
tag /= 100;
if (tag == 0)

Binary file not shown.

View file

@ -137,6 +137,22 @@ ACTOR ArtiPoisonBag3 : ArtiPoisonBag native
Inventory.Icon "ARTIPSB3"
Tag "$TAG_ARTIPOISONBAG3"
}
// Poison Bag 4 (Custom Giver) ----------------------------------------------
ACTOR ArtiPoisonBagGiver : ArtiPoisonBag native
{
Inventory.Icon "ARTIPSB4"
}
// Poison Bag 5 (Custom Thrower) --------------------------------------------
ACTOR ArtiPoisonBagShooter : ArtiPoisonBag native
{
Inventory.Icon "ARTIPSB5"
}
// Poison Cloud -------------------------------------------------------------
ACTOR PoisonCloud native

View file

@ -278,7 +278,7 @@ RetailOnly = 121
235 = USE, ACS_ExecuteWithResult (0, 235, tag)
// Buttons
11 = USE|REP, ACS_ExecuteWithResult (0, 52, tag)
11 = USE|REP, ACS_ExecuteWithResult (0, 11, tag)
42 = USE|REP, Door_Close (tag, D_SLOW)
43 = USE|REP, Ceiling_LowerToFloor (tag, C_SLOW)
45 = USE|REP, Floor_LowerToHighest (tag, F_SLOW, 128)
@ -319,7 +319,7 @@ RetailOnly = 121
155 = USE|REP, Plat_UpNearestWaitDownStay (tag, P_NORMAL, PLATWAIT)
177 = USE|REP, ACS_LockedExecute (0, 0, 177, tag, 9)
207 = USE|REP, Door_Animated (tag, 4, 3*35)
214 = USE|REP, ACS_ExecuteWithResult (0, 214, tag)
214 = USE|REP, Plat_DownWaitUpStayLip (tag, 8, 1050, 0, 1)
229 = USE|REP, ACS_ExecuteWithResult (0, 229, tag)
233 = USE|REP, ACS_ExecuteWithResult (0, 233, tag)
234 = USE|REP, ACS_ExecuteWithResult (0, 234, tag)