This commit is contained in:
Rachael Alexanderson 2017-02-27 01:06:00 -05:00
commit 2a847ca570
32 changed files with 330 additions and 118 deletions

View file

@ -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",

View file

@ -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)

View file

@ -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
{

View file

@ -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:

View file

@ -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]);
}
}

View file

@ -524,6 +524,7 @@ struct FSkillInfo
bool EasyBossBrain;
bool EasyKey;
bool NoMenu;
int RespawnCounter;
int RespawnLimit;
double Aggressiveness;

View file

@ -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);
}

View file

@ -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;

View file

@ -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)
{

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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)

View file

@ -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)

View file

@ -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;

View file

@ -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.

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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) +

View file

@ -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

View file

@ -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();

View file

@ -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";

View file

@ -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

View file

@ -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);

View file

@ -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