This commit is contained in:
nashmuhandes 2017-02-27 08:25:07 +08:00
commit 13c3e512d7
26 changed files with 261 additions and 78 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. desaturation = <float>; // Color desaturation factor. 0 = none, 1 = full, default = 0.
silent = <bool>; // Actors in this sector make no sound, silent = <bool>; // Actors in this sector make no sound,
nofallingdamage = <bool>; // Falling damage is disabled in this sector 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 (*) dropactors = <bool>; // Actors drop with instantly moving floors (*)
norespawn = <bool>; // Players can not respawn in this sector norespawn = <bool>; // Players can not respawn in this sector
soundsequence = <string>; // The sound sequence to play when this sector moves. Placing a 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, 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. // 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, health = <int>; // Set per-actor health as an absolute value. Default = actor default.
// negative values are used as their absolute. Default = 1. 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", 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", // "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(Floor_CrushStop, 46, 1, 1, 1)
DEFINE_SPECIAL(Ceiling_MoveToValue, 47, 3, 5, 5) DEFINE_SPECIAL(Ceiling_MoveToValue, 47, 3, 5, 5)
DEFINE_SPECIAL(Sector_Attach3dMidtex, 48, -1, -1, 3) 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(ExtraFloor_LightOnly, 50, -1, -1, 2)
DEFINE_SPECIAL(Sector_SetLink, 51, 4, 4, 4) DEFINE_SPECIAL(Sector_SetLink, 51, 4, 4, 4)
DEFINE_SPECIAL(Scroll_Wall, 52, 5, 5, 5) DEFINE_SPECIAL(Scroll_Wall, 52, 5, 5, 5)

View file

@ -469,6 +469,7 @@ enum ActorBounceFlag
BOUNCE_MBF = 1<<12, // This in itself is not a valid mode, but replaces MBF's MF_BOUNCE flag. 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_AutoOffFloorOnly = 1<<13, // like BOUNCE_AutoOff, but only on floors
BOUNCE_UseBounceState = 1<<14, // Use Bounce[.*] states 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, BOUNCE_TypeMask = BOUNCE_Walls | BOUNCE_Floors | BOUNCE_Ceilings | BOUNCE_Actors | BOUNCE_AutoOff | BOUNCE_HereticType | BOUNCE_MBF,

View file

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

View file

@ -701,3 +701,30 @@ void AMorphedMonster::Tick ()
Super::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 thisisdefault = false;
bool acsreturnisset = false; bool acsreturnisset = false;
skill.NoMenu = false;
skill.AmmoFactor = 1.; skill.AmmoFactor = 1.;
skill.DoubleAmmoFactor = 2.; skill.DoubleAmmoFactor = 2.;
skill.DropAmmoFactor = -1.; skill.DropAmmoFactor = -1.;
@ -149,6 +150,10 @@ void FMapInfoParser::ParseSkill ()
{ {
skill.AutoUseHealth = true; skill.AutoUseHealth = true;
} }
else if (sc.Compare("nomenu"))
{
skill.NoMenu = true;
}
else if (sc.Compare("respawntime")) else if (sc.Compare("respawntime"))
{ {
ParseAssign(); ParseAssign();
@ -508,6 +513,7 @@ FSkillInfo &FSkillInfo::operator=(const FSkillInfo &other)
{ {
Name = other.Name; Name = other.Name;
AmmoFactor = other.AmmoFactor; AmmoFactor = other.AmmoFactor;
NoMenu = other.NoMenu;
DoubleAmmoFactor = other.DoubleAmmoFactor; DoubleAmmoFactor = other.DoubleAmmoFactor;
DropAmmoFactor = other.DropAmmoFactor; DropAmmoFactor = other.DropAmmoFactor;
DamageFactor = other.DamageFactor; DamageFactor = other.DamageFactor;

View file

@ -767,6 +767,13 @@ DEFINE_ACTION_FUNCTION(_DamageTypeDefinition, IgnoreArmor)
ACTION_RETURN_BOOL(DamageTypeDefinition::IgnoreArmor(type)); 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; dtd.DefaultFactor = sc.Float;
if (dtd.DefaultFactor == 0) dtd.ReplaceFactor = true; if (dtd.DefaultFactor == 0) dtd.ReplaceFactor = true;
} }
if (sc.Compare("OBITUARY"))
{
sc.MustGetStringName("=");
sc.MustGetString();
dtd.Obituary = sc.String;
}
else if (sc.Compare("REPLACEFACTOR")) else if (sc.Compare("REPLACEFACTOR"))
{ {
dtd.ReplaceFactor = true; dtd.ReplaceFactor = true;

View file

@ -214,6 +214,7 @@ struct DamageTypeDefinition
public: public:
DamageTypeDefinition() { Clear(); } DamageTypeDefinition() { Clear(); }
FString Obituary;
double DefaultFactor; double DefaultFactor;
bool ReplaceFactor; bool ReplaceFactor;
bool NoArmor; bool NoArmor;
@ -221,6 +222,7 @@ public:
void Apply(FName type); void Apply(FName type);
void Clear() void Clear()
{ {
Obituary = "";
DefaultFactor = 1.; DefaultFactor = 1.;
ReplaceFactor = false; ReplaceFactor = false;
NoArmor = false; NoArmor = false;
@ -228,6 +230,7 @@ public:
static bool IgnoreArmor(FName type); static bool IgnoreArmor(FName type);
static int ApplyMobjDamageFactor(int damage, FName type, DmgFactors const * const factors); static int ApplyMobjDamageFactor(int damage, FName type, DmgFactors const * const factors);
static FString GetObituary(FName type);
private: private:
static double GetMobjDamageFactor(FName type, DmgFactors const * const factors); static double GetMobjDamageFactor(FName type, DmgFactors const * const factors);

View file

@ -68,6 +68,7 @@ IMPLEMENT_POINTERS_END
extern int NoWipe; 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 DIntermissionScreenScroller::Responder (event_t *ev)
{ {
int res = Super::Responder(ev); int res = Super::Responder(ev);
if (res == -1) if (res == -1 && !nointerscrollabort)
{ {
mBackground = mSecondPic; mBackground = mSecondPic;
mTicker = mScrollDelay + mScrollTime; 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_messages, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, show_obituaries, true, CVAR_ARCHIVE) CVAR (Bool, show_obituaries, true, CVAR_ARCHIVE)
CVAR(Bool, m_showinputgrid, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) 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) 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)) 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; keyup = ev->type == EV_KeyUp;
ch = ev->data1; ch = ev->data1;

View file

@ -339,7 +339,6 @@ void M_ActivateMenu(DMenu *menu);
void M_ClearMenus (); void M_ClearMenus ();
void M_ParseMenuDefs(); void M_ParseMenuDefs();
void M_StartupSkillMenu(FGameStartup *gs); void M_StartupSkillMenu(FGameStartup *gs);
int M_GetDefaultSkill();
void M_StartControlPanel (bool makeSound); void M_StartControlPanel (bool makeSound);
void M_SetMenu(FName menu, int param = -1); void M_SetMenu(FName menu, int param = -1);
void M_StartMessage(const char *message, int messagemode, FName action = NAME_None); void M_StartMessage(const char *message, int messagemode, FName action = NAME_None);

View file

@ -1323,6 +1323,43 @@ void M_StartupSkillMenu(FGameStartup *gs)
{ {
static int done = -1; static int done = -1;
bool success = false; 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); DMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_Skillmenu);
if (desc != nullptr) if (desc != nullptr)
{ {
@ -1346,12 +1383,7 @@ void M_StartupSkillMenu(FGameStartup *gs)
if (done != restart) if (done != restart)
{ {
done = restart; done = restart;
int defskill = DefaultSkill; ld->mSelectedItem = ld->mItems.Size() + defindex;
if ((unsigned int)defskill >= AllSkills.Size())
{
defskill = (AllSkills.Size() - 1) / 2;
}
ld->mSelectedItem = ld->mItems.Size() + defskill;
int posy = y; int posy = y;
int topy = posy; int topy = posy;
@ -1364,9 +1396,9 @@ void M_StartupSkillMenu(FGameStartup *gs)
} }
// center the menu on the screen if the top space is larger than the bottom space // 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 newtop = (200 - totalheight + topy) / 2;
int topdelta = newtop - topy; int topdelta = newtop - topy;
@ -1389,9 +1421,9 @@ void M_StartupSkillMenu(FGameStartup *gs)
} }
unsigned firstitem = ld->mItems.Size(); 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; DMenuItemBase *li;
// Using a different name for skills that must be confirmed makes handling this easier. // Using a different name for skills that must be confirmed makes handling this easier.
FName action = (skill.MustConfirm && !AllEpisodes[gs->Episode].mNoSkill) ? FName action = (skill.MustConfirm && !AllEpisodes[gs->Episode].mNoSkill) ?
@ -1405,22 +1437,22 @@ void M_StartupSkillMenu(FGameStartup *gs)
if (skill.PicName.Len() != 0 && pItemText == nullptr) if (skill.PicName.Len() != 0 && pItemText == nullptr)
{ {
FTextureID tex = GetMenuTexture(skill.PicName); 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 else
{ {
EColorRange color = (EColorRange)skill.GetTextColor(); EColorRange color = (EColorRange)skill.GetTextColor();
if (color == CR_UNTRANSLATED) color = ld->mFontColor; if (color == CR_UNTRANSLATED) color = ld->mFontColor;
li = CreateListMenuItemText(x, y, ld->mLinespacing, skill.Shortcut, 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); ld->mItems.Push(li);
GC::WriteBarrier(*desc, li); GC::WriteBarrier(*desc, li);
y += ld->mLinespacing; 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 else
{ {
@ -1439,7 +1471,7 @@ fail:
MenuDescriptors[NAME_Skillmenu] = od; MenuDescriptors[NAME_Skillmenu] = od;
od->mMenuName = NAME_Skillmenu; od->mMenuName = NAME_Skillmenu;
od->mTitle = "$MNU_CHOOSESKILL"; od->mTitle = "$MNU_CHOOSESKILL";
od->mSelectedItem = 0; od->mSelectedItem = defindex;
od->mScrollPos = 0; od->mScrollPos = 0;
od->mClass = nullptr; od->mClass = nullptr;
od->mPosition = -15; od->mPosition = -15;
@ -1453,9 +1485,9 @@ fail:
od = static_cast<DOptionMenuDescriptor*>(*desc); od = static_cast<DOptionMenuDescriptor*>(*desc);
od->mItems.Clear(); 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; DMenuItemBase *li;
// Using a different name for skills that must be confirmed makes handling this easier. // Using a different name for skills that must be confirmed makes handling this easier.
const char *action = (skill.MustConfirm && !AllEpisodes[gs->Episode].mNoSkill) ? const char *action = (skill.MustConfirm && !AllEpisodes[gs->Episode].mNoSkill) ?
@ -1466,29 +1498,13 @@ fail:
{ {
pItemText = skill.MenuNamesForPlayerClass.CheckKey(gs->PlayerClass); 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); od->mItems.Push(li);
GC::WriteBarrier(od, li); GC::WriteBarrier(od, li);
if (!done) if (!done)
{ {
done = true; 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(Subtract)
xx(Subtractive) xx(Subtractive)
xx(FillColor) xx(FillColor)
xx(HealthFactor)
// Healingradius types // Healingradius types
xx(Mana) xx(Mana)
@ -773,6 +774,7 @@ xx(BuiltinGetDefault)
xx(BuiltinClassCast) xx(BuiltinClassCast)
xx(BuiltinFormat) xx(BuiltinFormat)
xx(Damage) xx(Damage)
xx(Noattack)
// basic type names // basic type names
xx(Default) xx(Default)

View file

@ -350,7 +350,7 @@ bool AActor::CheckMeleeRange ()
double dist; double dist;
if (!pl) if (!pl || (Sector->Flags & SECF_NOATTACK))
return false; return false;
dist = Distance2D (pl); dist = Distance2D (pl);
@ -399,7 +399,7 @@ bool P_CheckMeleeRange2 (AActor *actor)
double dist; double dist;
if (!actor->target) if (!actor->target || (actor->Sector->Flags & SECF_NOATTACK))
{ {
return false; return false;
} }
@ -446,6 +446,8 @@ bool P_CheckMissileRange (AActor *actor)
{ {
double dist; double dist;
if ((actor->Sector->Flags & SECF_NOATTACK)) return false;
if (!P_CheckSight (actor, actor->target, SF_SEEPASTBLOCKEVERYTHING)) if (!P_CheckSight (actor, actor->target, SF_SEEPASTBLOCKEVERYTHING))
return false; return false;

View file

@ -217,15 +217,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; switch (mod)
case NAME_Falling: messagename = "OB_FALLING"; break; {
case NAME_Crush: messagename = "OB_CRUSH"; break; case NAME_Suicide: messagename = "OB_SUICIDE"; break;
case NAME_Exit: messagename = "OB_EXIT"; break; case NAME_Falling: messagename = "OB_FALLING"; break;
case NAME_Drowning: messagename = "OB_WATER"; break; case NAME_Crush: messagename = "OB_CRUSH"; break;
case NAME_Slime: messagename = "OB_SLIME"; break; case NAME_Exit: messagename = "OB_EXIT"; break;
case NAME_Fire: if (attacker == NULL) messagename = "OB_LAVA"; 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. // Check for being killed by a voodoo doll.

View file

@ -3177,7 +3177,7 @@ FUNC(LS_ClearForceField)
} }
FUNC(LS_GlassBreak) FUNC(LS_GlassBreak)
// GlassBreak (bNoJunk) // GlassBreak (bNoJunk, junkID)
{ {
bool switched; bool switched;
bool quest1, quest2; bool quest1, quest2;
@ -3197,7 +3197,6 @@ FUNC(LS_GlassBreak)
{ {
if (!arg0) if (!arg0)
{ // Break some glass { // Break some glass
AActor *glass;
DVector2 linemid((ln->v1->fX() + ln->v2->fX()) / 2, (ln->v1->fY() + ln->v2->fY()) / 2); 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; y += (ln->frontsector->centerspot.y - y) / 5;
*/ */
auto type = SpawnableThings.CheckKey(arg1);
for (int i = 0; i < 7; ++i) for (int i = 0; i < 7; ++i)
{ {
glass = Spawn("GlassJunk", DVector3(linemid, ONFLOORZ), ALLOW_REPLACE); AActor *glass = nullptr;
if (arg1 > 0)
glass->AddZ(24.); {
glass->SetState (glass->SpawnState + (pr_glass() % glass->health)); if (type != nullptr)
{
glass->Angles.Yaw = pr_glass() * (360 / 256.); glass = Spawn(*type, DVector3(linemid, ONFLOORZ), ALLOW_REPLACE);
glass->VelFromAngle(pr_glass() & 3); glass->AddZ(24.);
glass->Vel.Z = (pr_glass() & 7); }
// [RH] Let the shards stick around longer than they did in Strife. }
glass->tics += pr_glass(); 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) if (quest1 || quest2)

View file

@ -3538,6 +3538,11 @@ bool P_BounceActor(AActor *mo, AActor *BlockingMobj, bool ontop)
if ((mo->flags & MF_MISSILE) && (mo->flags2 & MF2_RIP) && BlockingMobj->flags & MF_SHOOTABLE) if ((mo->flags & MF_MISSILE) && (mo->flags2 & MF2_RIP) && BlockingMobj->flags & MF_SHOOTABLE)
return true; 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->bouncecount>0 && --mo->bouncecount == 0)
{ {
if (mo->flags & MF_MISSILE) if (mo->flags & MF_MISSILE)

View file

@ -515,6 +515,7 @@ public:
FString arg0str, arg1str; FString arg0str, arg1str;
memset(th, 0, sizeof(*th)); memset(th, 0, sizeof(*th));
double healthfactor = 1;
th->Gravity = 1; th->Gravity = 1;
th->RenderStyle = STYLE_Count; th->RenderStyle = STYLE_Count;
th->Alpha = -1; th->Alpha = -1;
@ -738,38 +739,52 @@ public:
break; break;
case NAME_Alpha: case NAME_Alpha:
CHECK_N(Zd | Zdt)
th->Alpha = CheckFloat(key); th->Alpha = CheckFloat(key);
break; break;
case NAME_FillColor: case NAME_FillColor:
CHECK_N(Zd | Zdt)
th->fillcolor = CheckInt(key); th->fillcolor = CheckInt(key);
break; break;
case NAME_Health: case NAME_Health:
CHECK_N(Zd | Zdt)
th->health = CheckInt(key); th->health = CheckInt(key);
break; break;
case NAME_HealthFactor:
CHECK_N(Zd | Zdt)
healthfactor = CheckFloat(key);
break;
case NAME_Score: case NAME_Score:
CHECK_N(Zd | Zdt)
th->score = CheckInt(key); th->score = CheckInt(key);
break; break;
case NAME_Pitch: case NAME_Pitch:
CHECK_N(Zd | Zdt)
th->pitch = (short)CheckInt(key); th->pitch = (short)CheckInt(key);
break; break;
case NAME_Roll: case NAME_Roll:
CHECK_N(Zd | Zdt)
th->roll = (short)CheckInt(key); th->roll = (short)CheckInt(key);
break; break;
case NAME_ScaleX: case NAME_ScaleX:
CHECK_N(Zd | Zdt)
th->Scale.X = CheckFloat(key); th->Scale.X = CheckFloat(key);
break; break;
case NAME_ScaleY: case NAME_ScaleY:
CHECK_N(Zd | Zdt)
th->Scale.Y = CheckFloat(key); th->Scale.Y = CheckFloat(key);
break; break;
case NAME_Scale: case NAME_Scale:
CHECK_N(Zd | Zdt)
th->Scale.X = th->Scale.Y = CheckFloat(key); th->Scale.X = th->Scale.Y = CheckFloat(key);
break; break;
@ -793,6 +808,7 @@ public:
{ {
th->args[1] = -FName(arg1str); th->args[1] = -FName(arg1str);
} }
th->health = int(th->health * healthfactor);
// Thing specials are only valid in namespaces with Hexen-type specials // Thing specials are only valid in namespaces with Hexen-type specials
// and in ZDoomTranslated - which will use the translator on them. // and in ZDoomTranslated - which will use the translator on them.
if (namespc == NAME_ZDoomTranslated) if (namespc == NAME_ZDoomTranslated)
@ -1658,6 +1674,10 @@ public:
sec->planes[sector_t::ceiling].GlowHeight = (float)CheckFloat(key); sec->planes[sector_t::ceiling].GlowHeight = (float)CheckFloat(key);
break; break;
case NAME_Noattack:
Flag(sec->Flags, SECF_NOATTACK, key);
break;
case NAME_MoreIds: case NAME_MoreIds:
// delay parsing of the tag string until parsing of the sector is complete // 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. // This ensures that the ID is always the first tag in the list.

View file

@ -495,6 +495,7 @@ enum
SECF_ENDGODMODE = 256, // getting damaged by this sector ends god mode SECF_ENDGODMODE = 256, // getting damaged by this sector ends god mode
SECF_ENDLEVEL = 512, // ends level when health goes below 10 SECF_ENDLEVEL = 512, // ends level when health goes below 10
SECF_HAZARD = 1024, // Change to Strife's delayed damage handling. 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_WASSECRET = 1 << 30, // a secret that was discovered
SECF_SECRET = 1 << 31, // a secret sector SECF_SECRET = 1 << 31, // a secret sector

View file

@ -217,12 +217,66 @@ bool FZipFile::Open(bool quiet)
char *dirptr = (char*)directory; char *dirptr = (char*)directory;
FZipLump *lump_p = Lumps; FZipLump *lump_p = Lumps;
// Check if all files have the same prefix so that this can be stripped out.
FString name0;
for (DWORD i = 0; i < NumLumps; i++) for (DWORD i = 0; i < NumLumps; i++)
{ {
FZipCentralDirectoryInfo *zip_fh = (FZipCentralDirectoryInfo *)dirptr; FZipCentralDirectoryInfo *zip_fh = (FZipCentralDirectoryInfo *)dirptr;
int len = LittleShort(zip_fh->NameLength); int len = LittleShort(zip_fh->NameLength);
FString name(dirptr + sizeof(FZipCentralDirectoryInfo), len); 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.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) + dirptr += sizeof(FZipCentralDirectoryInfo) +
LittleShort(zip_fh->NameLength) + LittleShort(zip_fh->NameLength) +
LittleShort(zip_fh->ExtraLength) + LittleShort(zip_fh->ExtraLength) +

View file

@ -356,6 +356,7 @@ static FFlagDef ActorFlagDefs[]=
DEFINE_FLAG2(BOUNCE_MBF, MBFBOUNCER, AActor, BounceFlags), DEFINE_FLAG2(BOUNCE_MBF, MBFBOUNCER, AActor, BounceFlags),
DEFINE_FLAG2(BOUNCE_AutoOffFloorOnly, BOUNCEAUTOOFFFLOORONLY, AActor, BounceFlags), DEFINE_FLAG2(BOUNCE_AutoOffFloorOnly, BOUNCEAUTOOFFFLOORONLY, AActor, BounceFlags),
DEFINE_FLAG2(BOUNCE_UseBounceState, USEBOUNCESTATE, AActor, BounceFlags), DEFINE_FLAG2(BOUNCE_UseBounceState, USEBOUNCESTATE, AActor, BounceFlags),
DEFINE_FLAG2(BOUNCE_NotOnShootables, DONTBOUNCEONSHOOTABLES, AActor, BounceFlags),
}; };
// These won't be accessible through bitfield variables // These won't be accessible through bitfield variables

View file

@ -1156,7 +1156,8 @@ public:
void WI_initShowNextLoc () 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! // Last map in episode - there is no next location!
WI_End(); WI_End();

View file

@ -1776,6 +1776,7 @@ MOUSEMNU_LOOKSTRAFE = "Lookstrafe";
JOYMNU_CONFIG = "CONFIGURE CONTROLLER"; JOYMNU_CONFIG = "CONFIGURE CONTROLLER";
JOYMNU_OPTIONS = "CONTROLLER OPTIONS"; JOYMNU_OPTIONS = "CONTROLLER OPTIONS";
JOYMNU_NOMENU = "Block controller input in menu";
// Player Setup Menu // Player Setup Menu
MNU_PLAYERSETUP = "PLAYER SETUP"; MNU_PLAYERSETUP = "PLAYER SETUP";
@ -1895,7 +1896,7 @@ MISCMNU_DEHLOAD = "Load *.deh/*.bex lumps";
MISCMNU_CACHENODES = "Cache nodes"; MISCMNU_CACHENODES = "Cache nodes";
MISCMNU_CACHETIME = "Time threshold for node caching"; MISCMNU_CACHETIME = "Time threshold for node caching";
MISCMNU_CLEARNODECACHE = "Clear node cache"; MISCMNU_CLEARNODECACHE = "Clear node cache";
MISCMNU_INTERSCROLL = "Allow skipping of intermission scrollers";
// Automap Options // Automap Options
AUTOMAPMNU_TITLE = "AUTOMAP OPTIONS"; AUTOMAPMNU_TITLE = "AUTOMAP OPTIONS";
AUTOMAPMNU_COLORSET = "Map color set"; AUTOMAPMNU_COLORSET = "Map color set";

View file

@ -562,6 +562,7 @@ OptionMenu "JoystickOptionsDefaults"
{ {
Title "$JOYMNU_OPTIONS" Title "$JOYMNU_OPTIONS"
Option "$JOYMNU_ENABLE", "use_joystick", "YesNo" Option "$JOYMNU_ENABLE", "use_joystick", "YesNo"
Option "$JOYMNU_NOMENU", "m_blockcontrollers", "YesNo"
IfOption(Windows) IfOption(Windows)
{ {
Option "$JOYMNU_DINPUT", "joy_dinput", "YesNo" Option "$JOYMNU_DINPUT", "joy_dinput", "YesNo"
@ -945,6 +946,7 @@ OptionMenu "MiscOptions"
Option "$MISCMNU_SAVELOADCONFIRMATION", "saveloadconfirmation", "OnOff" Option "$MISCMNU_SAVELOADCONFIRMATION", "saveloadconfirmation", "OnOff"
Slider "$MISCMNU_AUTOSAVECOUNT", "autosavecount", 1, 20, 1, 0 Slider "$MISCMNU_AUTOSAVECOUNT", "autosavecount", 1, 20, 1, 0
Option "$MISCMNU_DEHLOAD", "dehload", "dehopt" Option "$MISCMNU_DEHLOAD", "dehload", "dehopt"
Option "$MISCMNU_INTERSCROLL", "nointerscrollabort", "OffOn"
StaticText " " StaticText " "
Option "$MISCMNU_CACHENODES", "gl_cachenodes", "OnOff" Option "$MISCMNU_CACHENODES", "gl_cachenodes", "OnOff"
Slider "$MISCMNU_CACHETIME", "gl_cachetime", 0.0, 2.0, 0.1 Slider "$MISCMNU_CACHETIME", "gl_cachetime", 0.0, 2.0, 0.1

View file

@ -796,6 +796,7 @@ class Actor : Thinker native
deprecated native void A_BasicAttack(int meleedamage, sound meleesound, class<actor> missiletype, double missileheight); 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); 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 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); 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 // Morph constants
enum EMorphFlags enum EMorphFlags
{ {
MRF_ADDSTAMINA = 1, MRF_OLDEFFECTS = 0x00000000,
MRF_FULLHEALTH = 2, MRF_ADDSTAMINA = 0x00000001,
MRF_UNDOBYTOMEOFPOWER = 4, MRF_FULLHEALTH = 0x00000002,
MRF_UNDOBYCHAOSDEVICE = 8, MRF_UNDOBYTOMEOFPOWER = 0x00000004,
MRF_FAILNOTELEFRAG = 16, MRF_UNDOBYCHAOSDEVICE = 0x00000008,
MRF_FAILNOLAUGH = 32, MRF_FAILNOTELEFRAG = 0x00000010,
MRF_WHENINVULNERABLE = 64, MRF_FAILNOLAUGH = 0x00000020,
MRF_LOSEACTUALWEAPON = 128, MRF_WHENINVULNERABLE = 0x00000040,
MRF_NEWTIDBEHAVIOUR = 256, MRF_LOSEACTUALWEAPON = 0x00000080,
MRF_UNDOBYDEATH = 512, MRF_NEWTIDBEHAVIOUR = 0x00000100,
MRF_UNDOBYDEATHFORCED = 1024, MRF_UNDOBYDEATH = 0x00000200,
MRF_UNDOBYDEATHSAVES = 2048, MRF_UNDOBYDEATHFORCED = 0x00000400,
MRF_UNDOALWAYS = 4096, MRF_UNDOBYDEATHSAVES = 0x00000800,
MRF_TRANSFERTRANSLATION = 8192, MRF_UNDOBYTIMEOUT = 0x00001000,
MRF_UNDOALWAYS = 0x00002000,
MRF_TRANSFERTRANSLATION = 0x00004000,
MRF_STANDARDUNDOING = MRF_UNDOBYTOMEOFPOWER | MRF_UNDOBYCHAOSDEVICE | MRF_UNDOBYTIMEOUT,
}; };
// Flags for A_RailAttack and A_CustomRailgun // Flags for A_RailAttack and A_CustomRailgun