mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2024-11-10 23:01:59 +00:00
This commit is contained in:
commit
2a847ca570
32 changed files with 330 additions and 118 deletions
|
@ -197,6 +197,7 @@ Note: All <bool> fields default to false unless mentioned otherwise.
|
|||
desaturation = <float>; // Color desaturation factor. 0 = none, 1 = full, default = 0.
|
||||
silent = <bool>; // Actors in this sector make no sound,
|
||||
nofallingdamage = <bool>; // Falling damage is disabled in this sector
|
||||
noattack = <bool>; // Blocks monster attacks in this sector.
|
||||
dropactors = <bool>; // Actors drop with instantly moving floors (*)
|
||||
norespawn = <bool>; // Players can not respawn in this sector
|
||||
soundsequence = <string>; // The sound sequence to play when this sector moves. Placing a
|
||||
|
@ -262,8 +263,8 @@ Note: All <bool> fields default to false unless mentioned otherwise.
|
|||
gravity = <float>; // Set per-actor gravity. Positive values are multiplied with the class's property,
|
||||
// negative values are used as their absolute. Default = 1.0.
|
||||
|
||||
health = <int>; // Set per-actor health. Positive values are multiplied with the class's property,
|
||||
// negative values are used as their absolute. Default = 1.
|
||||
health = <int>; // Set per-actor health as an absolute value. Default = actor default.
|
||||
healthfactor = <float>; // Set per-actor health as a factor to the original. Default = 1.
|
||||
|
||||
renderstyle = <string>; // Set per-actor render style, overriding the class default. Possible values can be "normal",
|
||||
// "none", "add" or "additive", "subtract" or "subtractive", "stencil", "translucentstencil",
|
||||
|
|
|
@ -47,7 +47,7 @@ DEFINE_SPECIAL(Ceiling_CrushRaiseAndStay, 45, 3, 4, 4)
|
|||
DEFINE_SPECIAL(Floor_CrushStop, 46, 1, 1, 1)
|
||||
DEFINE_SPECIAL(Ceiling_MoveToValue, 47, 3, 5, 5)
|
||||
DEFINE_SPECIAL(Sector_Attach3dMidtex, 48, -1, -1, 3)
|
||||
DEFINE_SPECIAL(GlassBreak, 49, 0, 1, 1)
|
||||
DEFINE_SPECIAL(GlassBreak, 49, 0, 1, 2)
|
||||
DEFINE_SPECIAL(ExtraFloor_LightOnly, 50, -1, -1, 2)
|
||||
DEFINE_SPECIAL(Sector_SetLink, 51, 4, 4, 4)
|
||||
DEFINE_SPECIAL(Scroll_Wall, 52, 5, 5, 5)
|
||||
|
|
|
@ -430,6 +430,7 @@ enum ActorRenderFlag
|
|||
RF_ABSMASKPITCH = 0x00800000, // [MC] The mask rotation does not offset by the actor's pitch.
|
||||
RF_INTERPOLATEANGLES = 0x01000000, // [MC] Allow interpolation of the actor's angle, pitch and roll.
|
||||
RF_MAYBEINVISIBLE = 0x02000000,
|
||||
RF_DONTINTERPOLATE = 0x04000000, // no render interpolation ever!
|
||||
};
|
||||
|
||||
// This translucency value produces the closest match to Heretic's TINTTAB.
|
||||
|
@ -467,6 +468,7 @@ enum ActorBounceFlag
|
|||
BOUNCE_MBF = 1<<12, // This in itself is not a valid mode, but replaces MBF's MF_BOUNCE flag.
|
||||
BOUNCE_AutoOffFloorOnly = 1<<13, // like BOUNCE_AutoOff, but only on floors
|
||||
BOUNCE_UseBounceState = 1<<14, // Use Bounce[.*] states
|
||||
BOUNCE_NotOnShootables = 1<<15, // do not bounce off shootable actors if we are a projectile. Explode instead.
|
||||
|
||||
BOUNCE_TypeMask = BOUNCE_Walls | BOUNCE_Floors | BOUNCE_Ceilings | BOUNCE_Actors | BOUNCE_AutoOff | BOUNCE_HereticType | BOUNCE_MBF,
|
||||
|
||||
|
@ -1321,7 +1323,8 @@ public:
|
|||
}
|
||||
DVector3 InterpolatedPosition(double ticFrac) const
|
||||
{
|
||||
return Prev + (ticFrac * (Pos() - Prev));
|
||||
if (renderflags & RF_DONTINTERPOLATE) return Pos();
|
||||
else return Prev + (ticFrac * (Pos() - Prev));
|
||||
}
|
||||
DRotator InterpolatedAngles(double ticFrac) const
|
||||
{
|
||||
|
|
|
@ -2666,12 +2666,13 @@ void Net_DoCommand (int type, BYTE **stream, int player)
|
|||
|
||||
case DEM_NETEVENT:
|
||||
{
|
||||
FString ename = ReadString(stream);
|
||||
const char *ename = ReadString(stream);
|
||||
int argn = ReadByte(stream);
|
||||
int arg[3] = { 0, 0, 0 };
|
||||
for (int i = 0; i < argn; i++)
|
||||
for (int i = 0; i < 3; i++)
|
||||
arg[i] = ReadLong(stream);
|
||||
E_Console(player, ename, arg[0], arg[1], arg[2]);
|
||||
delete[] ename;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -2721,6 +2722,10 @@ void Net_SkipCommand (int type, BYTE **stream)
|
|||
skip = strlen ((char *)(*stream)) + 5;
|
||||
break;
|
||||
|
||||
case DEM_NETEVENT:
|
||||
skip = strlen((char *)(*stream)) + 13;
|
||||
break;
|
||||
|
||||
case DEM_SUMMON2:
|
||||
case DEM_SUMMONFRIEND2:
|
||||
case DEM_SUMMONFOE2:
|
||||
|
|
|
@ -1139,7 +1139,7 @@ CCMD(netevent)
|
|||
Net_WriteByte(DEM_NETEVENT);
|
||||
Net_WriteString(argv[1]);
|
||||
Net_WriteByte(argn);
|
||||
for (int i = 0; i < argn; i++)
|
||||
for (int i = 0; i < 3; i++)
|
||||
Net_WriteLong(arg[i]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -524,6 +524,7 @@ struct FSkillInfo
|
|||
|
||||
bool EasyBossBrain;
|
||||
bool EasyKey;
|
||||
bool NoMenu;
|
||||
int RespawnCounter;
|
||||
int RespawnLimit;
|
||||
double Aggressiveness;
|
||||
|
|
|
@ -701,3 +701,30 @@ void AMorphedMonster::Tick ()
|
|||
Super::Tick ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_Morph)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_CLASS(type, AActor);
|
||||
PARAM_INT_DEF(duration);
|
||||
PARAM_INT_DEF(flags);
|
||||
PARAM_CLASS_DEF(enter_flash, AActor);
|
||||
PARAM_CLASS_DEF(exit_flash, AActor);
|
||||
bool res = false;
|
||||
if (self->player)
|
||||
{
|
||||
if (type->IsKindOf(RUNTIME_CLASS(APlayerPawn)))
|
||||
{
|
||||
res = P_MorphPlayer(self->player, self->player, type, duration, flags, enter_flash, exit_flash);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (type->IsKindOf(RUNTIME_CLASS(AMorphedMonster)))
|
||||
{
|
||||
res = P_MorphMonster(self, type, duration, flags, enter_flash, exit_flash);
|
||||
}
|
||||
}
|
||||
ACTION_RETURN_BOOL(res);
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ void FMapInfoParser::ParseSkill ()
|
|||
bool thisisdefault = false;
|
||||
bool acsreturnisset = false;
|
||||
|
||||
skill.NoMenu = false;
|
||||
skill.AmmoFactor = 1.;
|
||||
skill.DoubleAmmoFactor = 2.;
|
||||
skill.DropAmmoFactor = -1.;
|
||||
|
@ -149,6 +150,10 @@ void FMapInfoParser::ParseSkill ()
|
|||
{
|
||||
skill.AutoUseHealth = true;
|
||||
}
|
||||
else if (sc.Compare("nomenu"))
|
||||
{
|
||||
skill.NoMenu = true;
|
||||
}
|
||||
else if (sc.Compare("respawntime"))
|
||||
{
|
||||
ParseAssign();
|
||||
|
@ -508,6 +513,7 @@ FSkillInfo &FSkillInfo::operator=(const FSkillInfo &other)
|
|||
{
|
||||
Name = other.Name;
|
||||
AmmoFactor = other.AmmoFactor;
|
||||
NoMenu = other.NoMenu;
|
||||
DoubleAmmoFactor = other.DoubleAmmoFactor;
|
||||
DropAmmoFactor = other.DropAmmoFactor;
|
||||
DamageFactor = other.DamageFactor;
|
||||
|
|
|
@ -109,6 +109,7 @@ void AdjustSpriteOffsets()
|
|||
{
|
||||
char str[9];
|
||||
Wads.GetLumpName(str, i);
|
||||
str[8] = 0;
|
||||
FTextureID texid = TexMan.CheckForTexture(str, FTexture::TEX_Sprite, 0);
|
||||
if (texid.isValid() && Wads.GetLumpFile(TexMan[texid]->SourceLump) > FWadCollection::IWAD_FILENUM)
|
||||
{
|
||||
|
|
13
src/info.cpp
13
src/info.cpp
|
@ -767,6 +767,13 @@ DEFINE_ACTION_FUNCTION(_DamageTypeDefinition, IgnoreArmor)
|
|||
ACTION_RETURN_BOOL(DamageTypeDefinition::IgnoreArmor(type));
|
||||
}
|
||||
|
||||
FString DamageTypeDefinition::GetObituary(FName type)
|
||||
{
|
||||
DamageTypeDefinition *dtd = Get(type);
|
||||
if (dtd) return dtd->Obituary;
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
@ -859,6 +866,12 @@ void FMapInfoParser::ParseDamageDefinition()
|
|||
dtd.DefaultFactor = sc.Float;
|
||||
if (dtd.DefaultFactor == 0) dtd.ReplaceFactor = true;
|
||||
}
|
||||
if (sc.Compare("OBITUARY"))
|
||||
{
|
||||
sc.MustGetStringName("=");
|
||||
sc.MustGetString();
|
||||
dtd.Obituary = sc.String;
|
||||
}
|
||||
else if (sc.Compare("REPLACEFACTOR"))
|
||||
{
|
||||
dtd.ReplaceFactor = true;
|
||||
|
|
|
@ -214,6 +214,7 @@ struct DamageTypeDefinition
|
|||
public:
|
||||
DamageTypeDefinition() { Clear(); }
|
||||
|
||||
FString Obituary;
|
||||
double DefaultFactor;
|
||||
bool ReplaceFactor;
|
||||
bool NoArmor;
|
||||
|
@ -221,6 +222,7 @@ public:
|
|||
void Apply(FName type);
|
||||
void Clear()
|
||||
{
|
||||
Obituary = "";
|
||||
DefaultFactor = 1.;
|
||||
ReplaceFactor = false;
|
||||
NoArmor = false;
|
||||
|
@ -228,6 +230,7 @@ public:
|
|||
|
||||
static bool IgnoreArmor(FName type);
|
||||
static int ApplyMobjDamageFactor(int damage, FName type, DmgFactors const * const factors);
|
||||
static FString GetObituary(FName type);
|
||||
|
||||
private:
|
||||
static double GetMobjDamageFactor(FName type, DmgFactors const * const factors);
|
||||
|
|
|
@ -68,6 +68,7 @@ IMPLEMENT_POINTERS_END
|
|||
|
||||
extern int NoWipe;
|
||||
|
||||
CVAR(Bool, nointerscrollabort, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
|
@ -647,7 +648,7 @@ void DIntermissionScreenScroller::Init(FIntermissionAction *desc, bool first)
|
|||
int DIntermissionScreenScroller::Responder (event_t *ev)
|
||||
{
|
||||
int res = Super::Responder(ev);
|
||||
if (res == -1)
|
||||
if (res == -1 && !nointerscrollabort)
|
||||
{
|
||||
mBackground = mSecondPic;
|
||||
mTicker = mScrollDelay + mScrollTime;
|
||||
|
|
|
@ -64,6 +64,7 @@ CVAR (Float, mouse_sensitivity, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|||
CVAR (Bool, show_messages, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
CVAR (Bool, show_obituaries, true, CVAR_ARCHIVE)
|
||||
CVAR(Bool, m_showinputgrid, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Bool, m_blockcontrollers, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
|
||||
|
||||
CVAR (Float, snd_menuvolume, 0.6f, CVAR_ARCHIVE)
|
||||
|
@ -582,6 +583,9 @@ bool M_Responder (event_t *ev)
|
|||
}
|
||||
else if (menuactive != MENU_WaitKey && (ev->type == EV_KeyDown || ev->type == EV_KeyUp))
|
||||
{
|
||||
// eat blocked controller events without dispatching them.
|
||||
if (ev->data1 >= KEY_FIRSTJOYBUTTON && m_blockcontrollers) return true;
|
||||
|
||||
keyup = ev->type == EV_KeyUp;
|
||||
|
||||
ch = ev->data1;
|
||||
|
|
|
@ -339,7 +339,6 @@ void M_ActivateMenu(DMenu *menu);
|
|||
void M_ClearMenus ();
|
||||
void M_ParseMenuDefs();
|
||||
void M_StartupSkillMenu(FGameStartup *gs);
|
||||
int M_GetDefaultSkill();
|
||||
void M_StartControlPanel (bool makeSound);
|
||||
void M_SetMenu(FName menu, int param = -1);
|
||||
void M_StartMessage(const char *message, int messagemode, FName action = NAME_None);
|
||||
|
|
|
@ -1327,6 +1327,43 @@ void M_StartupSkillMenu(FGameStartup *gs)
|
|||
{
|
||||
static int done = -1;
|
||||
bool success = false;
|
||||
TArray<FSkillInfo*> MenuSkills;
|
||||
TArray<int> SkillIndices;
|
||||
if (MenuSkills.Size() == 0)
|
||||
{
|
||||
for (unsigned ind = 0; ind < AllSkills.Size(); ind++)
|
||||
{
|
||||
if (!AllSkills[ind].NoMenu)
|
||||
{
|
||||
MenuSkills.Push(&AllSkills[ind]);
|
||||
SkillIndices.Push(ind);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (MenuSkills.Size() == 0) I_Error("No valid skills for menu found. At least one must be defined.");
|
||||
|
||||
int defskill = DefaultSkill;
|
||||
if ((unsigned int)defskill >= MenuSkills.Size())
|
||||
{
|
||||
defskill = SkillIndices[(MenuSkills.Size() - 1) / 2];
|
||||
}
|
||||
if (AllSkills[defskill].NoMenu)
|
||||
{
|
||||
for (defskill = 0; defskill < (int)AllSkills.Size(); defskill++)
|
||||
{
|
||||
if (!AllSkills[defskill].NoMenu) break;
|
||||
}
|
||||
}
|
||||
int defindex = 0;
|
||||
for (unsigned i = 0; i < MenuSkills.Size(); i++)
|
||||
{
|
||||
if (MenuSkills[i] == &AllSkills[defskill])
|
||||
{
|
||||
defindex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_Skillmenu);
|
||||
if (desc != nullptr)
|
||||
{
|
||||
|
@ -1350,12 +1387,7 @@ void M_StartupSkillMenu(FGameStartup *gs)
|
|||
if (done != restart)
|
||||
{
|
||||
done = restart;
|
||||
int defskill = DefaultSkill;
|
||||
if ((unsigned int)defskill >= AllSkills.Size())
|
||||
{
|
||||
defskill = (AllSkills.Size() - 1) / 2;
|
||||
}
|
||||
ld->mSelectedItem = ld->mItems.Size() + defskill;
|
||||
ld->mSelectedItem = ld->mItems.Size() + defindex;
|
||||
|
||||
int posy = y;
|
||||
int topy = posy;
|
||||
|
@ -1368,9 +1400,9 @@ void M_StartupSkillMenu(FGameStartup *gs)
|
|||
}
|
||||
|
||||
// center the menu on the screen if the top space is larger than the bottom space
|
||||
int totalheight = posy + AllSkills.Size() * ld->mLinespacing - topy;
|
||||
int totalheight = posy + MenuSkills.Size() * ld->mLinespacing - topy;
|
||||
|
||||
if (totalheight < 190 || AllSkills.Size() == 1)
|
||||
if (totalheight < 190 || MenuSkills.Size() == 1)
|
||||
{
|
||||
int newtop = (200 - totalheight + topy) / 2;
|
||||
int topdelta = newtop - topy;
|
||||
|
@ -1393,9 +1425,9 @@ void M_StartupSkillMenu(FGameStartup *gs)
|
|||
}
|
||||
|
||||
unsigned firstitem = ld->mItems.Size();
|
||||
for(unsigned int i = 0; i < AllSkills.Size(); i++)
|
||||
for(unsigned int i = 0; i < MenuSkills.Size(); i++)
|
||||
{
|
||||
FSkillInfo &skill = AllSkills[i];
|
||||
FSkillInfo &skill = *MenuSkills[i];
|
||||
DMenuItemBase *li;
|
||||
// Using a different name for skills that must be confirmed makes handling this easier.
|
||||
FName action = (skill.MustConfirm && !AllEpisodes[gs->Episode].mNoSkill) ?
|
||||
|
@ -1409,22 +1441,22 @@ void M_StartupSkillMenu(FGameStartup *gs)
|
|||
if (skill.PicName.Len() != 0 && pItemText == nullptr)
|
||||
{
|
||||
FTextureID tex = GetMenuTexture(skill.PicName);
|
||||
li = CreateListMenuItemPatch(ld->mXpos, y, ld->mLinespacing, skill.Shortcut, tex, action, i);
|
||||
li = CreateListMenuItemPatch(ld->mXpos, y, ld->mLinespacing, skill.Shortcut, tex, action, SkillIndices[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
EColorRange color = (EColorRange)skill.GetTextColor();
|
||||
if (color == CR_UNTRANSLATED) color = ld->mFontColor;
|
||||
li = CreateListMenuItemText(x, y, ld->mLinespacing, skill.Shortcut,
|
||||
pItemText? *pItemText : skill.MenuName, ld->mFont, color,ld->mFontColor2, action, i);
|
||||
pItemText? *pItemText : skill.MenuName, ld->mFont, color,ld->mFontColor2, action, SkillIndices[i]);
|
||||
}
|
||||
ld->mItems.Push(li);
|
||||
GC::WriteBarrier(*desc, li);
|
||||
y += ld->mLinespacing;
|
||||
}
|
||||
if (AllEpisodes[gs->Episode].mNoSkill || AllSkills.Size() == 1)
|
||||
if (AllEpisodes[gs->Episode].mNoSkill || MenuSkills.Size() == 1)
|
||||
{
|
||||
ld->mAutoselect = firstitem + M_GetDefaultSkill();
|
||||
ld->mAutoselect = firstitem + defindex;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1443,7 +1475,7 @@ fail:
|
|||
MenuDescriptors[NAME_Skillmenu] = od;
|
||||
od->mMenuName = NAME_Skillmenu;
|
||||
od->mTitle = "$MNU_CHOOSESKILL";
|
||||
od->mSelectedItem = 0;
|
||||
od->mSelectedItem = defindex;
|
||||
od->mScrollPos = 0;
|
||||
od->mClass = nullptr;
|
||||
od->mPosition = -15;
|
||||
|
@ -1457,9 +1489,9 @@ fail:
|
|||
od = static_cast<DOptionMenuDescriptor*>(*desc);
|
||||
od->mItems.Clear();
|
||||
}
|
||||
for(unsigned int i = 0; i < AllSkills.Size(); i++)
|
||||
for(unsigned int i = 0; i < MenuSkills.Size(); i++)
|
||||
{
|
||||
FSkillInfo &skill = AllSkills[i];
|
||||
FSkillInfo &skill = *MenuSkills[i];
|
||||
DMenuItemBase *li;
|
||||
// Using a different name for skills that must be confirmed makes handling this easier.
|
||||
const char *action = (skill.MustConfirm && !AllEpisodes[gs->Episode].mNoSkill) ?
|
||||
|
@ -1470,29 +1502,13 @@ fail:
|
|||
{
|
||||
pItemText = skill.MenuNamesForPlayerClass.CheckKey(gs->PlayerClass);
|
||||
}
|
||||
li = CreateOptionMenuItemSubmenu(pItemText? *pItemText : skill.MenuName, action, i);
|
||||
li = CreateOptionMenuItemSubmenu(pItemText? *pItemText : skill.MenuName, action, SkillIndices[i]);
|
||||
od->mItems.Push(li);
|
||||
GC::WriteBarrier(od, li);
|
||||
if (!done)
|
||||
{
|
||||
done = true;
|
||||
od->mSelectedItem = M_GetDefaultSkill();
|
||||
od->mSelectedItem = defindex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Returns the default skill level.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
int M_GetDefaultSkill()
|
||||
{
|
||||
int defskill = DefaultSkill;
|
||||
if ((unsigned int)defskill >= AllSkills.Size())
|
||||
{
|
||||
defskill = (AllSkills.Size() - 1) / 2;
|
||||
}
|
||||
return defskill;
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ xx(Shadow)
|
|||
xx(Subtract)
|
||||
xx(Subtractive)
|
||||
xx(FillColor)
|
||||
xx(HealthFactor)
|
||||
|
||||
// Healingradius types
|
||||
xx(Mana)
|
||||
|
@ -773,6 +774,7 @@ xx(BuiltinGetDefault)
|
|||
xx(BuiltinClassCast)
|
||||
xx(BuiltinFormat)
|
||||
xx(Damage)
|
||||
xx(Noattack)
|
||||
|
||||
// basic type names
|
||||
xx(Default)
|
||||
|
|
|
@ -818,41 +818,57 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou
|
|||
newvert.y += fixed_t(frac * double(Vertices[seg->v2].y - newvert.y));
|
||||
vertnum = VertexMap->SelectVertexClose (newvert);
|
||||
|
||||
if (vertnum == (unsigned int)seg->v1 || vertnum == (unsigned int)seg->v2)
|
||||
if (vertnum != (unsigned int)seg->v1 && vertnum != (unsigned int)seg->v2)
|
||||
{
|
||||
Printf("SelectVertexClose selected endpoint of seg %u\n", set);
|
||||
seg2 = SplitSeg(set, vertnum, sidev[0]);
|
||||
|
||||
Segs[seg2].next = outset0;
|
||||
outset0 = seg2;
|
||||
Segs[set].next = outset1;
|
||||
outset1 = set;
|
||||
_count0++;
|
||||
_count1++;
|
||||
|
||||
// Also split the seg on the back side
|
||||
if (Segs[set].partner != DWORD_MAX)
|
||||
{
|
||||
int partner1 = Segs[set].partner;
|
||||
int partner2 = SplitSeg(partner1, vertnum, sidev[1]);
|
||||
// The newly created seg stays in the same set as the
|
||||
// back seg because it has not been considered for splitting
|
||||
// yet. If it had been, then the front seg would have already
|
||||
// been split, and we would not be in this default case.
|
||||
// Moreover, the back seg may not even be in the set being
|
||||
// split, so we must not move its pieces into the out sets.
|
||||
Segs[partner1].next = partner2;
|
||||
Segs[partner2].partner = seg2;
|
||||
Segs[seg2].partner = partner2;
|
||||
}
|
||||
|
||||
if (GLNodes)
|
||||
{
|
||||
AddIntersection(node, vertnum);
|
||||
}
|
||||
}
|
||||
|
||||
seg2 = SplitSeg (set, vertnum, sidev[0]);
|
||||
|
||||
Segs[seg2].next = outset0;
|
||||
outset0 = seg2;
|
||||
Segs[set].next = outset1;
|
||||
outset1 = set;
|
||||
_count0++;
|
||||
_count1++;
|
||||
|
||||
// Also split the seg on the back side
|
||||
if (Segs[set].partner != DWORD_MAX)
|
||||
else
|
||||
{
|
||||
int partner1 = Segs[set].partner;
|
||||
int partner2 = SplitSeg (partner1, vertnum, sidev[1]);
|
||||
// The newly created seg stays in the same set as the
|
||||
// back seg because it has not been considered for splitting
|
||||
// yet. If it had been, then the front seg would have already
|
||||
// been split, and we would not be in this default case.
|
||||
// Moreover, the back seg may not even be in the set being
|
||||
// split, so we must not move its pieces into the out sets.
|
||||
Segs[partner1].next = partner2;
|
||||
Segs[partner2].partner = seg2;
|
||||
Segs[seg2].partner = partner2;
|
||||
// all that matters here is to prevent a crash so we must make sure that we do not end up with all segs being sorted to the same side - even if this may not be correct.
|
||||
// But if we do not do that this code would not be able to move on. Just discarding the seg is also not an option because it won't guarantee that we achieve an actual split.
|
||||
if (_count0 == 0)
|
||||
{
|
||||
side = 0;
|
||||
seg->next = outset0;
|
||||
outset0 = set;
|
||||
_count0++;
|
||||
}
|
||||
else
|
||||
{
|
||||
side = 1;
|
||||
seg->next = outset1;
|
||||
outset1 = set;
|
||||
_count1++;
|
||||
}
|
||||
}
|
||||
|
||||
if (GLNodes)
|
||||
{
|
||||
AddIntersection (node, vertnum);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
if (side >= 0 && GLNodes)
|
||||
|
|
|
@ -349,7 +349,7 @@ bool AActor::CheckMeleeRange ()
|
|||
|
||||
double dist;
|
||||
|
||||
if (!pl)
|
||||
if (!pl || (Sector->Flags & SECF_NOATTACK))
|
||||
return false;
|
||||
|
||||
dist = Distance2D (pl);
|
||||
|
@ -398,7 +398,7 @@ bool P_CheckMeleeRange2 (AActor *actor)
|
|||
double dist;
|
||||
|
||||
|
||||
if (!actor->target)
|
||||
if (!actor->target || (actor->Sector->Flags & SECF_NOATTACK))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -445,6 +445,8 @@ bool P_CheckMissileRange (AActor *actor)
|
|||
{
|
||||
double dist;
|
||||
|
||||
if ((actor->Sector->Flags & SECF_NOATTACK)) return false;
|
||||
|
||||
if (!P_CheckSight (actor, actor->target, SF_SEEPASTBLOCKEVERYTHING))
|
||||
return false;
|
||||
|
||||
|
|
|
@ -216,15 +216,20 @@ void ClientObituary (AActor *self, AActor *inflictor, AActor *attacker, int dmgf
|
|||
}
|
||||
}
|
||||
|
||||
switch (mod)
|
||||
FString obit = DamageTypeDefinition::GetObituary(mod);
|
||||
if (obit.IsNotEmpty()) messagename = obit;
|
||||
else
|
||||
{
|
||||
case NAME_Suicide: messagename = "OB_SUICIDE"; break;
|
||||
case NAME_Falling: messagename = "OB_FALLING"; break;
|
||||
case NAME_Crush: messagename = "OB_CRUSH"; break;
|
||||
case NAME_Exit: messagename = "OB_EXIT"; break;
|
||||
case NAME_Drowning: messagename = "OB_WATER"; break;
|
||||
case NAME_Slime: messagename = "OB_SLIME"; break;
|
||||
case NAME_Fire: if (attacker == NULL) messagename = "OB_LAVA"; break;
|
||||
switch (mod)
|
||||
{
|
||||
case NAME_Suicide: messagename = "OB_SUICIDE"; break;
|
||||
case NAME_Falling: messagename = "OB_FALLING"; break;
|
||||
case NAME_Crush: messagename = "OB_CRUSH"; break;
|
||||
case NAME_Exit: messagename = "OB_EXIT"; break;
|
||||
case NAME_Drowning: messagename = "OB_WATER"; break;
|
||||
case NAME_Slime: messagename = "OB_SLIME"; break;
|
||||
case NAME_Fire: if (attacker == NULL) messagename = "OB_LAVA"; break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for being killed by a voodoo doll.
|
||||
|
|
|
@ -329,11 +329,13 @@ bool P_AddSectorLinks(sector_t *control, int tag, INTBOOL ceiling, int movetype)
|
|||
FSectorTagIterator itr(tag);
|
||||
while ((sec = itr.Next()) >= 0)
|
||||
{
|
||||
// Don't attach to self!
|
||||
if (control != &level.sectors[sec])
|
||||
// Don't attach to self (but allow attaching to this sector's oposite plane.
|
||||
if (control == &level.sectors[sec])
|
||||
{
|
||||
AddSingleSector(scrollplane, &level.sectors[sec], movetype);
|
||||
if (ceiling == sector_t::floor && movetype & LINK_FLOOR) continue;
|
||||
if (ceiling == sector_t::ceiling && movetype & LINK_CEILING) continue;
|
||||
}
|
||||
AddSingleSector(scrollplane, &level.sectors[sec], movetype);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -3177,7 +3177,7 @@ FUNC(LS_ClearForceField)
|
|||
}
|
||||
|
||||
FUNC(LS_GlassBreak)
|
||||
// GlassBreak (bNoJunk)
|
||||
// GlassBreak (bNoJunk, junkID)
|
||||
{
|
||||
bool switched;
|
||||
bool quest1, quest2;
|
||||
|
@ -3197,7 +3197,6 @@ FUNC(LS_GlassBreak)
|
|||
{
|
||||
if (!arg0)
|
||||
{ // Break some glass
|
||||
AActor *glass;
|
||||
|
||||
DVector2 linemid((ln->v1->fX() + ln->v2->fX()) / 2, (ln->v1->fY() + ln->v2->fY()) / 2);
|
||||
|
||||
|
@ -3209,18 +3208,32 @@ FUNC(LS_GlassBreak)
|
|||
y += (ln->frontsector->centerspot.y - y) / 5;
|
||||
*/
|
||||
|
||||
auto type = SpawnableThings.CheckKey(arg1);
|
||||
for (int i = 0; i < 7; ++i)
|
||||
{
|
||||
glass = Spawn("GlassJunk", DVector3(linemid, ONFLOORZ), ALLOW_REPLACE);
|
||||
|
||||
glass->AddZ(24.);
|
||||
glass->SetState (glass->SpawnState + (pr_glass() % glass->health));
|
||||
|
||||
glass->Angles.Yaw = pr_glass() * (360 / 256.);
|
||||
glass->VelFromAngle(pr_glass() & 3);
|
||||
glass->Vel.Z = (pr_glass() & 7);
|
||||
// [RH] Let the shards stick around longer than they did in Strife.
|
||||
glass->tics += pr_glass();
|
||||
AActor *glass = nullptr;
|
||||
if (arg1 > 0)
|
||||
{
|
||||
if (type != nullptr)
|
||||
{
|
||||
glass = Spawn(*type, DVector3(linemid, ONFLOORZ), ALLOW_REPLACE);
|
||||
glass->AddZ(24.);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
glass = Spawn("GlassJunk", DVector3(linemid, ONFLOORZ), ALLOW_REPLACE);
|
||||
glass->AddZ(24.);
|
||||
glass->SetState(glass->SpawnState + (pr_glass() % glass->health));
|
||||
}
|
||||
if (glass != nullptr)
|
||||
{
|
||||
glass->Angles.Yaw = pr_glass() * (360 / 256.);
|
||||
glass->VelFromAngle(pr_glass() & 3);
|
||||
glass->Vel.Z = (pr_glass() & 7);
|
||||
// [RH] Let the shards stick around longer than they did in Strife.
|
||||
glass->tics += pr_glass();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (quest1 || quest2)
|
||||
|
|
|
@ -3537,6 +3537,11 @@ bool P_BounceActor(AActor *mo, AActor *BlockingMobj, bool ontop)
|
|||
if ((mo->flags & MF_MISSILE) && (mo->flags2 & MF2_RIP) && BlockingMobj->flags & MF_SHOOTABLE)
|
||||
return true;
|
||||
|
||||
if (BlockingMobj->flags & MF_SHOOTABLE && mo->BounceFlags & BOUNCE_NotOnShootables)
|
||||
{
|
||||
mo->bouncecount = 1; // let it explode now.
|
||||
}
|
||||
|
||||
if (mo->bouncecount>0 && --mo->bouncecount == 0)
|
||||
{
|
||||
if (mo->flags & MF_MISSILE)
|
||||
|
|
|
@ -902,7 +902,7 @@ bool AActor::TakeInventory(PClassActor *itemclass, int amount, bool fromdecorate
|
|||
// Do not take ammo if the "no take infinite/take as ammo depletion" flag is set
|
||||
// and infinite ammo is on
|
||||
if (notakeinfinite &&
|
||||
((dmflags & DF_INFINITE_AMMO) || (player && FindInventory(NAME_PowerInfiniteAmmo))) && item->IsKindOf(NAME_Ammo))
|
||||
((dmflags & DF_INFINITE_AMMO) || (player && FindInventory(NAME_PowerInfiniteAmmo, true))) && item->IsKindOf(NAME_Ammo))
|
||||
{
|
||||
// Nothing to do here, except maybe res = false;? Would it make sense?
|
||||
result = false;
|
||||
|
@ -4437,7 +4437,8 @@ void AActor::Tick ()
|
|||
return; // freed itself
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (tics == -1 || state->GetCanRaise())
|
||||
{
|
||||
int respawn_monsters = G_SkillProperty(SKILLP_Respawn);
|
||||
// check for nightmare respawn
|
||||
|
|
|
@ -515,6 +515,7 @@ public:
|
|||
FString arg0str, arg1str;
|
||||
|
||||
memset(th, 0, sizeof(*th));
|
||||
double healthfactor = 1;
|
||||
th->Gravity = 1;
|
||||
th->RenderStyle = STYLE_Count;
|
||||
th->Alpha = -1;
|
||||
|
@ -738,38 +739,52 @@ public:
|
|||
break;
|
||||
|
||||
case NAME_Alpha:
|
||||
CHECK_N(Zd | Zdt)
|
||||
th->Alpha = CheckFloat(key);
|
||||
break;
|
||||
|
||||
case NAME_FillColor:
|
||||
CHECK_N(Zd | Zdt)
|
||||
th->fillcolor = CheckInt(key);
|
||||
break;
|
||||
|
||||
case NAME_Health:
|
||||
CHECK_N(Zd | Zdt)
|
||||
th->health = CheckInt(key);
|
||||
break;
|
||||
|
||||
case NAME_HealthFactor:
|
||||
CHECK_N(Zd | Zdt)
|
||||
healthfactor = CheckFloat(key);
|
||||
break;
|
||||
|
||||
case NAME_Score:
|
||||
CHECK_N(Zd | Zdt)
|
||||
th->score = CheckInt(key);
|
||||
break;
|
||||
|
||||
case NAME_Pitch:
|
||||
CHECK_N(Zd | Zdt)
|
||||
th->pitch = (short)CheckInt(key);
|
||||
break;
|
||||
|
||||
case NAME_Roll:
|
||||
CHECK_N(Zd | Zdt)
|
||||
th->roll = (short)CheckInt(key);
|
||||
break;
|
||||
|
||||
case NAME_ScaleX:
|
||||
CHECK_N(Zd | Zdt)
|
||||
th->Scale.X = CheckFloat(key);
|
||||
break;
|
||||
|
||||
case NAME_ScaleY:
|
||||
CHECK_N(Zd | Zdt)
|
||||
th->Scale.Y = CheckFloat(key);
|
||||
break;
|
||||
|
||||
case NAME_Scale:
|
||||
CHECK_N(Zd | Zdt)
|
||||
th->Scale.X = th->Scale.Y = CheckFloat(key);
|
||||
break;
|
||||
|
||||
|
@ -793,6 +808,7 @@ public:
|
|||
{
|
||||
th->args[1] = -FName(arg1str);
|
||||
}
|
||||
th->health = int(th->health * healthfactor);
|
||||
// Thing specials are only valid in namespaces with Hexen-type specials
|
||||
// and in ZDoomTranslated - which will use the translator on them.
|
||||
if (namespc == NAME_ZDoomTranslated)
|
||||
|
@ -1658,6 +1674,10 @@ public:
|
|||
sec->planes[sector_t::ceiling].GlowHeight = (float)CheckFloat(key);
|
||||
break;
|
||||
|
||||
case NAME_Noattack:
|
||||
Flag(sec->Flags, SECF_NOATTACK, key);
|
||||
break;
|
||||
|
||||
case NAME_MoreIds:
|
||||
// delay parsing of the tag string until parsing of the sector is complete
|
||||
// This ensures that the ID is always the first tag in the list.
|
||||
|
|
|
@ -493,6 +493,7 @@ enum
|
|||
SECF_ENDGODMODE = 256, // getting damaged by this sector ends god mode
|
||||
SECF_ENDLEVEL = 512, // ends level when health goes below 10
|
||||
SECF_HAZARD = 1024, // Change to Strife's delayed damage handling.
|
||||
SECF_NOATTACK = 2048, // monsters cannot start attacks in this sector.
|
||||
|
||||
SECF_WASSECRET = 1 << 30, // a secret that was discovered
|
||||
SECF_SECRET = 1 << 31, // a secret sector
|
||||
|
|
|
@ -217,12 +217,67 @@ bool FZipFile::Open(bool quiet)
|
|||
|
||||
char *dirptr = (char*)directory;
|
||||
FZipLump *lump_p = Lumps;
|
||||
|
||||
// Check if all files have the same prefix so that this can be stripped out.
|
||||
FString name0;
|
||||
if (NumLumps > 1) for (DWORD i = 0; i < NumLumps; i++)
|
||||
{
|
||||
FZipCentralDirectoryInfo *zip_fh = (FZipCentralDirectoryInfo *)dirptr;
|
||||
|
||||
int len = LittleShort(zip_fh->NameLength);
|
||||
FString name(dirptr + sizeof(FZipCentralDirectoryInfo), len);
|
||||
|
||||
dirptr += sizeof(FZipCentralDirectoryInfo) +
|
||||
LittleShort(zip_fh->NameLength) +
|
||||
LittleShort(zip_fh->ExtraLength) +
|
||||
LittleShort(zip_fh->CommentLength);
|
||||
|
||||
if (dirptr > ((char*)directory) + dirsize) // This directory entry goes beyond the end of the file.
|
||||
{
|
||||
free(directory);
|
||||
if (!quiet) Printf(TEXTCOLOR_RED "\n%s: Central directory corrupted.", Filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
name.ToLower();
|
||||
if (i == 0)
|
||||
{
|
||||
// check for special names, if one of these gets found this must be treated as a normal zip.
|
||||
bool isspecial = !name.Compare("flats/") ||
|
||||
name.IndexOf("/") < 0 ||
|
||||
!name.Compare("textures/") ||
|
||||
!name.Compare("hires/") ||
|
||||
!name.Compare("sprites/") ||
|
||||
!name.Compare("voxels/") ||
|
||||
!name.Compare("colormaps/") ||
|
||||
!name.Compare("acs/") ||
|
||||
!name.Compare("voices/") ||
|
||||
!name.Compare("patches/") ||
|
||||
!name.Compare("graphics/") ||
|
||||
!name.Compare("sounds/") ||
|
||||
!name.Compare("music/");
|
||||
if (isspecial) break;
|
||||
name0 = name;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (name.IndexOf(name0) != 0)
|
||||
{
|
||||
name0 = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dirptr = (char*)directory;
|
||||
lump_p = Lumps;
|
||||
for (DWORD i = 0; i < NumLumps; i++)
|
||||
{
|
||||
FZipCentralDirectoryInfo *zip_fh = (FZipCentralDirectoryInfo *)dirptr;
|
||||
|
||||
int len = LittleShort(zip_fh->NameLength);
|
||||
FString name(dirptr + sizeof(FZipCentralDirectoryInfo), len);
|
||||
if (name0.IsNotEmpty()) name = name.Mid(name0.Len());
|
||||
dirptr += sizeof(FZipCentralDirectoryInfo) +
|
||||
LittleShort(zip_fh->NameLength) +
|
||||
LittleShort(zip_fh->ExtraLength) +
|
||||
|
|
|
@ -339,6 +339,7 @@ static FFlagDef ActorFlagDefs[]=
|
|||
DEFINE_FLAG(RF, XFLIP, AActor, renderflags),
|
||||
DEFINE_FLAG(RF, YFLIP, AActor, renderflags),
|
||||
DEFINE_FLAG(RF, INTERPOLATEANGLES, AActor, renderflags),
|
||||
DEFINE_FLAG(RF, DONTINTERPOLATE, AActor, renderflags),
|
||||
|
||||
// Bounce flags
|
||||
DEFINE_FLAG2(BOUNCE_Walls, BOUNCEONWALLS, AActor, BounceFlags),
|
||||
|
@ -355,6 +356,7 @@ static FFlagDef ActorFlagDefs[]=
|
|||
DEFINE_FLAG2(BOUNCE_MBF, MBFBOUNCER, AActor, BounceFlags),
|
||||
DEFINE_FLAG2(BOUNCE_AutoOffFloorOnly, BOUNCEAUTOOFFFLOORONLY, AActor, BounceFlags),
|
||||
DEFINE_FLAG2(BOUNCE_UseBounceState, USEBOUNCESTATE, AActor, BounceFlags),
|
||||
DEFINE_FLAG2(BOUNCE_NotOnShootables, DONTBOUNCEONSHOOTABLES, AActor, BounceFlags),
|
||||
};
|
||||
|
||||
// These won't be accessible through bitfield variables
|
||||
|
|
|
@ -1155,7 +1155,8 @@ public:
|
|||
|
||||
void WI_initShowNextLoc ()
|
||||
{
|
||||
if (wbs->next_ep == -1)
|
||||
auto info = FindLevelInfo(wbs->next, false);
|
||||
if (info == nullptr)
|
||||
{
|
||||
// Last map in episode - there is no next location!
|
||||
WI_End();
|
||||
|
|
|
@ -1777,6 +1777,7 @@ MOUSEMNU_LOOKSTRAFE = "Lookstrafe";
|
|||
|
||||
JOYMNU_CONFIG = "CONFIGURE CONTROLLER";
|
||||
JOYMNU_OPTIONS = "CONTROLLER OPTIONS";
|
||||
JOYMNU_NOMENU = "Block controller input in menu";
|
||||
|
||||
// Player Setup Menu
|
||||
MNU_PLAYERSETUP = "PLAYER SETUP";
|
||||
|
@ -1900,7 +1901,7 @@ MISCMNU_DEHLOAD = "Load *.deh/*.bex lumps";
|
|||
MISCMNU_CACHENODES = "Cache nodes";
|
||||
MISCMNU_CACHETIME = "Time threshold for node caching";
|
||||
MISCMNU_CLEARNODECACHE = "Clear node cache";
|
||||
|
||||
MISCMNU_INTERSCROLL = "Allow skipping of intermission scrollers";
|
||||
// Automap Options
|
||||
AUTOMAPMNU_TITLE = "AUTOMAP OPTIONS";
|
||||
AUTOMAPMNU_COLORSET = "Map color set";
|
||||
|
|
|
@ -563,6 +563,7 @@ OptionMenu "JoystickOptionsDefaults"
|
|||
{
|
||||
Title "$JOYMNU_OPTIONS"
|
||||
Option "$JOYMNU_ENABLE", "use_joystick", "YesNo"
|
||||
Option "$JOYMNU_NOMENU", "m_blockcontrollers", "YesNo"
|
||||
IfOption(Windows)
|
||||
{
|
||||
Option "$JOYMNU_DINPUT", "joy_dinput", "YesNo"
|
||||
|
@ -972,6 +973,7 @@ OptionMenu "MiscOptions"
|
|||
Option "$MISCMNU_SAVELOADCONFIRMATION", "saveloadconfirmation", "OnOff"
|
||||
Slider "$MISCMNU_AUTOSAVECOUNT", "autosavecount", 1, 20, 1, 0
|
||||
Option "$MISCMNU_DEHLOAD", "dehload", "dehopt"
|
||||
Option "$MISCMNU_INTERSCROLL", "nointerscrollabort", "OffOn"
|
||||
StaticText " "
|
||||
Option "$MISCMNU_CACHENODES", "gl_cachenodes", "OnOff"
|
||||
Slider "$MISCMNU_CACHETIME", "gl_cachetime", 0.0, 2.0, 0.1
|
||||
|
|
|
@ -797,6 +797,7 @@ class Actor : Thinker native
|
|||
deprecated native void A_BasicAttack(int meleedamage, sound meleesound, class<actor> missiletype, double missileheight);
|
||||
action native bool, Actor A_ThrowGrenade(class<Actor> itemtype, double zheight = 0, double xyvel = 0, double zvel = 0, bool useammo = true);
|
||||
native void A_Weave(int xspeed, int yspeed, double xdist, double ydist);
|
||||
native bool A_Morph(class<Actor> type, int duration = 0, int flags = 0, class<Actor> enter_flash = null, class<Actor> exit_flash = null);
|
||||
|
||||
|
||||
action native state, bool A_Teleport(statelabel teleportstate = null, class<SpecialSpot> targettype = "BossSpot", class<Actor> fogtype = "TeleportFog", int flags = 0, double mindist = 128, double maxdist = 0, int ptr = AAPTR_DEFAULT);
|
||||
|
|
|
@ -214,20 +214,23 @@ enum ESelectWeaponFlags
|
|||
// Morph constants
|
||||
enum EMorphFlags
|
||||
{
|
||||
MRF_ADDSTAMINA = 1,
|
||||
MRF_FULLHEALTH = 2,
|
||||
MRF_UNDOBYTOMEOFPOWER = 4,
|
||||
MRF_UNDOBYCHAOSDEVICE = 8,
|
||||
MRF_FAILNOTELEFRAG = 16,
|
||||
MRF_FAILNOLAUGH = 32,
|
||||
MRF_WHENINVULNERABLE = 64,
|
||||
MRF_LOSEACTUALWEAPON = 128,
|
||||
MRF_NEWTIDBEHAVIOUR = 256,
|
||||
MRF_UNDOBYDEATH = 512,
|
||||
MRF_UNDOBYDEATHFORCED = 1024,
|
||||
MRF_UNDOBYDEATHSAVES = 2048,
|
||||
MRF_UNDOALWAYS = 4096,
|
||||
MRF_TRANSFERTRANSLATION = 8192,
|
||||
MRF_OLDEFFECTS = 0x00000000,
|
||||
MRF_ADDSTAMINA = 0x00000001,
|
||||
MRF_FULLHEALTH = 0x00000002,
|
||||
MRF_UNDOBYTOMEOFPOWER = 0x00000004,
|
||||
MRF_UNDOBYCHAOSDEVICE = 0x00000008,
|
||||
MRF_FAILNOTELEFRAG = 0x00000010,
|
||||
MRF_FAILNOLAUGH = 0x00000020,
|
||||
MRF_WHENINVULNERABLE = 0x00000040,
|
||||
MRF_LOSEACTUALWEAPON = 0x00000080,
|
||||
MRF_NEWTIDBEHAVIOUR = 0x00000100,
|
||||
MRF_UNDOBYDEATH = 0x00000200,
|
||||
MRF_UNDOBYDEATHFORCED = 0x00000400,
|
||||
MRF_UNDOBYDEATHSAVES = 0x00000800,
|
||||
MRF_UNDOBYTIMEOUT = 0x00001000,
|
||||
MRF_UNDOALWAYS = 0x00002000,
|
||||
MRF_TRANSFERTRANSLATION = 0x00004000,
|
||||
MRF_STANDARDUNDOING = MRF_UNDOBYTOMEOFPOWER | MRF_UNDOBYCHAOSDEVICE | MRF_UNDOBYTIMEOUT,
|
||||
};
|
||||
|
||||
// Flags for A_RailAttack and A_CustomRailgun
|
||||
|
|
Loading…
Reference in a new issue