diff --git a/src/common/audio/music/music.cpp b/src/common/audio/music/music.cpp index 13837e93e..01b3a5da3 100644 --- a/src/common/audio/music/music.cpp +++ b/src/common/audio/music/music.cpp @@ -130,6 +130,24 @@ static FileReader OpenMusic(const char* musicname) return reader; } +bool MusicExists(const char* music_name) +{ + if (music_name == nullptr) + return false; + + if (FileExists(music_name)) + return true; + else + { + int lumpnum; + lumpnum = mus_cb.FindMusic(music_name); + if (lumpnum == -1) lumpnum = fileSystem.CheckNumForName(music_name, FileSys::ns_music); + if (lumpnum != -1 && fileSystem.FileLength(lumpnum) != 0) + return true; + } + return false; +} + void S_SetMusicCallbacks(MusicCallbacks* cb) { mus_cb = *cb; diff --git a/src/common/audio/music/s_music.h b/src/common/audio/music/s_music.h index cddfd36d6..629f6f95d 100644 --- a/src/common/audio/music/s_music.h +++ b/src/common/audio/music/s_music.h @@ -45,6 +45,9 @@ bool S_StartMusic (const char *music_name); // Start music using , and set whether looping bool S_ChangeMusic (const char *music_name, int order=0, bool looping=true, bool force=false); +// Check if exists +bool MusicExists(const char* music_name); + void S_RestartMusic (); void S_MIDIDeviceChanged(int newdev); diff --git a/src/common/engine/serializer.cpp b/src/common/engine/serializer.cpp index dcf930842..97ded1adf 100644 --- a/src/common/engine/serializer.cpp +++ b/src/common/engine/serializer.cpp @@ -197,7 +197,10 @@ void FSerializer::Close() } if (mErrors > 0) { - I_Error("%d errors parsing JSON", mErrors); + if (mLumpName.IsNotEmpty()) + I_Error("%d errors parsing JSON lump %s", mErrors, mLumpName.GetChars()); + else + I_Error("%d errors parsing JSON", mErrors); } } @@ -331,6 +334,28 @@ bool FSerializer::HasObject(const char* name) // //========================================================================== +bool FSerializer::IsKeyNull(const char* name) +{ + if (isReading()) + { + auto val = r->FindKey(name); + if (val != nullptr) + { + if (val->IsNull()) + { + return true; + } + } + } + return false; +} + +//========================================================================== +// +// +// +//========================================================================== + void FSerializer::EndObject() { if (isWriting()) diff --git a/src/common/engine/serializer.h b/src/common/engine/serializer.h index 99ffb72dc..40b6ab706 100644 --- a/src/common/engine/serializer.h +++ b/src/common/engine/serializer.h @@ -94,6 +94,7 @@ public: void EndObject(); bool HasKey(const char* name); bool HasObject(const char* name); + bool IsKeyNull(const char* name); bool BeginArray(const char *name); void EndArray(); unsigned GetSize(const char *group); @@ -225,6 +226,7 @@ public: int mErrors = 0; int mObjectErrors = 0; + FString mLumpName; }; FSerializer& Serialize(FSerializer& arc, const char* key, char& value, char* defval); diff --git a/src/gamedata/g_mapinfo.cpp b/src/gamedata/g_mapinfo.cpp index 3d9bc8bd9..6cd4fdcdf 100644 --- a/src/gamedata/g_mapinfo.cpp +++ b/src/gamedata/g_mapinfo.cpp @@ -164,6 +164,20 @@ bool CheckWarpTransMap (FString &mapname, bool substitute) // //========================================================================== +bool SecretLevelVisited() +{ + for (unsigned int i = 0; i < wadlevelinfos.Size(); i++) + if ((wadlevelinfos[i].flags3 & LEVEL3_SECRET) && (wadlevelinfos[i].flags & LEVEL_VISITED)) + return true; + + return false; +} + +//========================================================================== +// +// +//========================================================================== + static int FindWadClusterInfo (int cluster) { for (unsigned int i = 0; i < wadclusterinfos.Size(); i++) @@ -280,6 +294,8 @@ void level_info_t::Reset() RedirectCVARMapName = ""; EnterPic = ""; ExitPic = ""; + EnterAnim = ""; + ExitAnim = ""; Intermission = NAME_None; deathsequence = NAME_None; slideshow = NAME_None; @@ -1256,6 +1272,20 @@ DEFINE_MAP_OPTION(enterpic, true) info->EnterPic = parse.sc.String; } +DEFINE_MAP_OPTION(exitanim, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->ExitAnim = parse.sc.String; +} + +DEFINE_MAP_OPTION(enteranim, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->EnterAnim = parse.sc.String; +} + DEFINE_MAP_OPTION(specialaction, true) { parse.ParseAssign(); @@ -2601,6 +2631,7 @@ void G_ParseMapInfo (FString basemapinfo) { int lump, lastlump = 0; level_info_t gamedefaults; + TArray secretMaps; int flags1 = 0, flags2 = 0; if (gameinfo.gametype == GAME_Doom) @@ -2717,5 +2748,23 @@ void G_ParseMapInfo (FString basemapinfo) { I_FatalError ("You cannot use clearskills in a MAPINFO if you do not define any new skills after it."); } + + // Find any and all secret maps. + for (unsigned int i = 0; i < wadlevelinfos.Size(); i++) + { + if (wadlevelinfos[i].NextSecretMap.IsNotEmpty() && wadlevelinfos[i].NextSecretMap != wadlevelinfos[i].NextMap) + { + secretMaps.Push(wadlevelinfos[i].NextSecretMap); + } + } + // ...and then mark them all as secret maps. + for (unsigned int i = 0; i < secretMaps.Size(); i++) + { + auto* li = FindLevelInfo(secretMaps[i].GetChars(), false); + if (li) + { + li->flags3 |= LEVEL3_SECRET; + } + } } diff --git a/src/gamedata/g_mapinfo.h b/src/gamedata/g_mapinfo.h index 8f311f6a2..b07e6e442 100644 --- a/src/gamedata/g_mapinfo.h +++ b/src/gamedata/g_mapinfo.h @@ -269,8 +269,9 @@ enum ELevelFlags : unsigned int LEVEL3_NOSHADOWMAP = 0x00010000, // disables shadowmaps for a given level. LEVEL3_AVOIDMELEE = 0x00020000, // global flag needed for proper MBF support. LEVEL3_NOJUMPDOWN = 0x00040000, // only for MBF21. Inverse of MBF's dog_jumping flag. - LEVEL3_LIGHTCREATED = 0x00080000, // a light had been created in the last frame + LEVEL3_LIGHTCREATED = 0x00080000, // a light had been created in the last frame LEVEL3_NOFOGOFWAR = 0x00100000, // disables effect of r_radarclipper CVAR on this map + LEVEL3_SECRET = 0x00200000, // level is a secret level }; @@ -389,6 +390,8 @@ struct level_info_t FString EnterPic; FString ExitPic; + FString EnterAnim; + FString ExitAnim; FString InterMusic; int intermusicorder; TMap > MapInterMusic; @@ -488,6 +491,8 @@ level_info_t *FindLevelInfo (const char *mapname, bool allowdefault=true); level_info_t *FindLevelByNum (int num); level_info_t *CheckLevelRedirect (level_info_t *info); +bool SecretLevelVisited(); + FString CalcMapName (int episode, int level); void G_ClearMapinfo(); diff --git a/src/gamedata/umapinfo.cpp b/src/gamedata/umapinfo.cpp index 08e16d9f8..50757e734 100644 --- a/src/gamedata/umapinfo.cpp +++ b/src/gamedata/umapinfo.cpp @@ -52,6 +52,8 @@ struct UMapEntry char endpic[9] = ""; char exitpic[9] = ""; char enterpic[9] = ""; + char exitanim[9] = ""; + char enteranim[9] = ""; char interbackdrop[9] = "FLOOR4_8"; char intermusic[9] = ""; int partime = 0; @@ -186,6 +188,14 @@ static int ParseStandardProperty(FScanner &scanner, UMapEntry *mape) { ParseLumpName(scanner, mape->enterpic); } + else if (!pname.CompareNoCase("exitanim")) + { + ParseLumpName(scanner, mape->exitanim); + } + else if (!pname.CompareNoCase("enteranim")) + { + ParseLumpName(scanner, mape->enteranim); + } else if (!pname.CompareNoCase("nointermission")) { scanner.MustGetBoolToken(); @@ -443,6 +453,8 @@ void CommitUMapinfo(level_info_t *defaultinfo) if (map.partime > 0) levelinfo->partime = map.partime; if (map.enterpic[0]) levelinfo->EnterPic = map.enterpic; if (map.exitpic[0]) levelinfo->ExitPic = map.exitpic; + if (map.enteranim[0]) levelinfo->EnterAnim = map.enteranim; + if (map.exitanim[0]) levelinfo->ExitAnim = map.exitanim; /* UMAPINFO's intermusic is for the text screen, not the summary. if (map.intermusic[0]) { diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index dddc37c31..8a5c7945c 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -57,6 +57,9 @@ #include "texturemanager.h" #include "v_draw.h" +#include "serializer.h" +#include "s_music.h" + CVAR(Bool, wi_percents, true, CVAR_ARCHIVE) CVAR(Bool, wi_showtotaltime, true, CVAR_ARCHIVE) CVAR(Bool, wi_noautostartmap, false, CVAR_USERINFO | CVAR_ARCHIVE) @@ -109,6 +112,8 @@ class DInterBackground : public DObject { ANIM_ALWAYS, // determined by patch entry ANIM_PIC, // continuous + ANIM_FRAME, // determined by frame properties + ANIM_NONE, // frozen (used for infinite duration frames) // condition bitflags ANIM_IFVISITED = 8, @@ -137,13 +142,41 @@ class DInterBackground : public DObject FString Level; }; + struct frame_t + { + FTextureID image; + uint32_t type; + // In tics! + int duration; + int max_duration; + }; + + enum ECondition + { + COND_NONE = 0, // None. + COND_GREATER = 1, // Parameter is greater than current map number. + COND_EQUAL = 2, // Parameter is equal to current map number. + COND_VISITED = 3, // Map number corresponding to parameter is visited. + COND_NOTSECRET = 4, // Current map is not a secret one. + COND_SECRETVISTED = 5, // Any secret map was visited. + COND_TALLY = 6, // Tally screen. + COND_ENTERING = 7, // "Entering" screen. + }; + + struct condition_t + { + ECondition condition; + int param; + }; + struct in_anim_t { int type; // Made an int so I can use '|' int period; // period in tics between animations yahpt_t loc; // location of animation int data; // ALWAYS: n/a, RANDOM: period deviation (<256) - TArray frames; // actual graphics for frames of animations + TArray frames; // actual graphics for frames of animations + TArray conditions; // conditions to display the animation // following must be initialized to zero before use! int nexttic; // next value of bcnt (used in conjunction with period) @@ -162,9 +195,15 @@ class DInterBackground : public DObject } }; + struct in_layer_t + { + TArray anims; + TArray conditions; // conditions to display the animations. + }; + private: TArray lnodes; - TArray anims; + TArray layers; int bcnt = 0; // used for timing of background animation TArray yah; // You Are Here graphic FTextureID splat{}; // splat @@ -174,12 +213,14 @@ private: int bgwidth = -1; int bgheight = -1; bool tilebackground = false; + FString muslump; public: DInterBackground(wbstartstruct_t *wbst); bool LoadBackground(bool isenterpic); + bool IsUsingMusic(); void updateAnimatedBack(); void drawBackground(int state, bool drawsplat, bool snl_pointeron); @@ -242,6 +283,70 @@ private: } } } + + //==================================================================== + // + // Draws the splats and the 'You are here' arrows + // + //==================================================================== + bool ConditionsMet(int state, TArray& conditions) + { + for (auto& condition : conditions) + { + switch (condition.condition) + { + case ECondition::COND_NONE: + break; + case ECondition::COND_EQUAL: + { + auto* li = FindLevelInfo(state != StatCount ? wbs->next.GetChars() : wbs->current.GetChars()); + if (!li) + return false; + if (li->levelnum != condition.param) + return false; + break; + } + case ECondition::COND_GREATER: + { + auto* li = FindLevelInfo(state != StatCount ? wbs->next.GetChars() : wbs->current.GetChars()); + if (!li) + return false; + if (li->levelnum <= condition.param) + return false; + break; + } + case ECondition::COND_VISITED: + { + auto* li = FindLevelByNum(condition.param); + if (li == NULL || !(li->flags & LEVEL_VISITED)) + return false; + break; + } + case ECondition::COND_NOTSECRET: + { + auto* li = FindLevelInfo(state != StatCount ? wbs->next.GetChars() : wbs->current.GetChars()); + if (!li) + return false; + if (li->flags3 & LEVEL3_SECRET) + return false; + break; + } + case ECondition::COND_SECRETVISTED: + if (!SecretLevelVisited()) + return false; + break; + case ECondition::COND_TALLY: + if (state != StatCount) + return false; + break; + case ECondition::COND_ENTERING: + if (state == StatCount) + return false; + break; + } + } + return true; + } }; DInterBackground:: DInterBackground(wbstartstruct_t *wbst) @@ -270,11 +375,13 @@ bool DInterBackground::LoadBackground(bool isenterpic) { const char* lumpname = nullptr; const char* exitpic = nullptr; + const char* exitanim = nullptr; char buffer[10]; in_anim_t an; lnode_t pt; FTextureID texture; bool noautostartmap = false; + bool id24anim = false; bcnt = 0; @@ -284,8 +391,17 @@ bool DInterBackground::LoadBackground(bool isenterpic) level_info_t* li = FindLevelInfo(wbs->current.GetChars()); if (li != nullptr) { - exitpic = li->ExitPic.GetChars(); - if (li->ExitPic.IsNotEmpty()) tilebackground = false; + if (li->ExitAnim.IsNotEmpty()) + { + id24anim = true; + exitpic = li->ExitAnim.GetChars(); + tilebackground = false; + } + else + { + exitpic = li->ExitPic.GetChars(); + if (li->ExitPic.IsNotEmpty()) tilebackground = false; + } } lumpname = exitpic; @@ -294,8 +410,17 @@ bool DInterBackground::LoadBackground(bool isenterpic) level_info_t* li = FindLevelInfo(wbs->next.GetChars()); if (li != NULL) { - lumpname = li->EnterPic.GetChars(); - if (li->EnterPic.IsNotEmpty()) tilebackground = false; + if (li->EnterAnim.IsNotEmpty()) + { + id24anim = true; + lumpname = li->EnterAnim.GetChars(); + tilebackground = false; + } + else + { + lumpname = li->EnterPic.GetChars(); + if (li->EnterPic.IsNotEmpty()) tilebackground = false; + } } } @@ -382,12 +507,251 @@ bool DInterBackground::LoadBackground(bool isenterpic) } lnodes.Clear(); - anims.Clear(); + layers.Clear(); yah.Clear(); splat.SetInvalid(); + if (!id24anim) + layers.Resize(1); + if (id24anim) + { + try + { + int lumpnum = fileSystem.CheckNumForFullName(lumpname, true); + if (lumpnum == -1) + { + I_Error("Intermission animation lump %s not found!", lumpname); + } + auto data = fileSystem.ReadFile(lumpnum); + FSerializer jsonReader; + jsonReader.mLumpName = fileSystem.GetFileFullPath(lumpnum); + lumpname = jsonReader.mLumpName.GetChars(); + if (jsonReader.OpenReader(data.string(), data.size())) + { + FString type = jsonReader.GetString("type"); + if (type.Compare("interlevel")) + { + I_Error("Interlevel lump %s is not interlevel!", lumpname); + } + if (jsonReader.BeginObject("data")) + { + FString music = jsonReader.GetString("music"); + if (music.IsEmpty()) + { + I_Error("No music lump specified for intermission animation %s!", lumpname); + } + muslump = music; + if (!MusicExists(muslump.GetChars())) + { + I_Error("Music lump %s not found!", muslump.GetChars()); + } + + // Check for background lump. + FString backgroundimage = jsonReader.GetString("backgroundimage"); + texture = TexMan.CheckForTexture(backgroundimage.GetChars(), ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny); + if (!texture.isValid()) + { + if (backgroundimage.IsEmpty()) + { + I_Error("No background image specified for intermission animation %s!", lumpname); + } + else + { + I_Error("Texture %s not found!", backgroundimage.GetChars()); + } + } + + if (!jsonReader.IsKeyNull("layers") && jsonReader.BeginArray("layers")) + { + int size = jsonReader.ArraySize(); + + if (size == 0) + I_Error("Zero-length 'layers' array in %s", lumpname); + + for (int i = 0; i < size; i++) + { + if (jsonReader.BeginObject(nullptr)) + { + layers.Reserve(1); + auto& layer = layers.back(); + if (!jsonReader.IsKeyNull("conditions") && jsonReader.BeginArray("conditions")) + { + int condition_size = jsonReader.ArraySize(); + if (condition_size == 0) + { + I_Error("Condition array empty and not null for layer %d in lump %s", layers.Size(), lumpname); + } + for (int j = 0; j < condition_size; j++) + { + if (jsonReader.BeginObject(nullptr)) + { + ECondition cond = COND_NONE; + int param = 0; + ::Serialize(jsonReader, "condition", (int&)cond, nullptr); + ::Serialize(jsonReader, "param", param, nullptr); + layer.conditions.Push(condition_t{ cond, param }); + jsonReader.EndObject(); + } + } + jsonReader.EndArray(); + } + + // Read anims. + if (jsonReader.BeginArray("anims")) + { + int anim_size = jsonReader.ArraySize(); + if (anim_size == 0) + { + I_Error("No animations defined for layer %d in lump %s", layers.Size(), lumpname); + } + + for (int j = 0; j < anim_size; j++) + { + if (jsonReader.BeginObject(nullptr)) + { + layer.anims.Reserve(1); + auto& anim = layer.anims.back(); + + anim.Reset(); + anim.type = ANIM_FRAME; + anim.ctr = 0; + anim.data = 0; + + ::Serialize(jsonReader, "x", anim.loc.x, nullptr); + ::Serialize(jsonReader, "y", anim.loc.y, nullptr); + + if (!jsonReader.IsKeyNull("conditions") && jsonReader.BeginArray("conditions")) + { + int condition_size = jsonReader.ArraySize(); + if (condition_size == 0) + { + I_Error("Condition array empty and not null for anim %d, layer %d in lump %s", layer.anims.Size(), layers.Size(), lumpname); + } + for (int k = 0; k < condition_size; k++) + { + if (jsonReader.BeginObject(nullptr)) + { + ECondition cond = COND_NONE; + int param = 0; + ::Serialize(jsonReader, "condition", (int&)cond, nullptr); + ::Serialize(jsonReader, "param", param, nullptr); + anim.conditions.Push(condition_t{ cond, param }); + jsonReader.EndObject(); + } + } + jsonReader.EndArray(); + } + + if (jsonReader.BeginArray("frames")) + { + int frames = jsonReader.ArraySize(); + + if (frames == 0) + { + I_Error("No frames defined for anim %d, layer %d in lump %s", layer.anims.Size(), layers.Size(), lumpname); + } + for (int k = 0; k < frames; k++) + { + if (jsonReader.BeginObject(nullptr)) + { + double duration = 0.0; + double maxduration = 0.0; + anim.frames.Reserve(1); + auto& frame = anim.frames.back(); + + ::Serialize(jsonReader, "duration", duration, nullptr); + ::Serialize(jsonReader, "maxduration", maxduration, nullptr); + ::Serialize(jsonReader, "type", frame.type, nullptr); + + FString image = jsonReader.GetString("image"); + if (image.IsEmpty()) + { + I_Error("No image defined for frame %d, anim %d, layer %d in lump %s", anim.frames.Size(), layer.anims.Size(), layers.Size(), lumpname); + } + + frame.image = TexMan.CheckForTexture(image.GetChars(), ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny); + if (!frame.image.isValid()) + { + I_Error("Texture '%s' not found!", image.GetChars()); + } + + frame.duration = round(duration * (double)TICRATE); + frame.max_duration = round(maxduration * (double)TICRATE); + + if (frame.duration <= 0 && duration > 0.0) + { + frame.duration = 1; + } + + if (frame.max_duration <= 0 && maxduration > 0.0) + { + frame.max_duration = 1; + } + + if (anim.frames.Size() == 1) + { + if (frame.type & 0x1000) + { + frame.type &= ~0x1000; + anim.nexttic = 1 + (M_Random() % frame.duration); + } + else + { + + switch (frame.type) + { + case 0x1: + anim.type = ANIM_NONE; + break; + case 0x4: + anim.nexttic = 1 + M_Random(frame.max_duration - frame.duration + 1) + frame.duration; + break; + case 0x2: + anim.nexttic = 1 + frame.duration; + break; + } + } + } + + jsonReader.EndObject(); + } + } + jsonReader.EndArray(); + } + + jsonReader.EndObject(); + } + } + + jsonReader.EndArray(); + } + + jsonReader.EndObject(); + } + } + jsonReader.EndArray(); + } + + jsonReader.EndObject(); + } + jsonReader.Close(); + } + } + // This is deliberate. Errors coming from here shouldn't cause a console abort. + catch (CRecoverableError& error) + { + Printf(TEXTCOLOR_RED "Failed to parse intermission anim definition %s: %s.\nFalling back to non-anim definitions.\n", lumpname, error.GetMessage()); + + if (isenterpic) + li->EnterAnim = ""; + else + li->ExitAnim = ""; + + return LoadBackground(isenterpic); + } + } // a name with a starting '$' indicates an intermission script - if (*lumpname != '$') + else if (*lumpname != '$') { texture = TexMan.CheckForTexture(lumpname, ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny); } @@ -520,18 +884,20 @@ bool DInterBackground::LoadBackground(bool isenterpic) if (!sc.CheckString("{")) { sc.MustGetString(); - an.frames.Push(TexMan.CheckForTexture(sc.String, ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny)); + auto textureId = TexMan.CheckForTexture(sc.String, ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny); + an.frames.Push(frame_t{ textureId, 0, 0, 0 }); } else { while (!sc.CheckString("}")) { sc.MustGetString(); - an.frames.Push(TexMan.CheckForTexture(sc.String, ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny)); + auto textureId = TexMan.CheckForTexture(sc.String, ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny); + an.frames.Push(frame_t{ textureId, 0, 0, 0 }); } } an.ctr = -1; - anims.Push(an); + layers[0].anims.Push(an); break; case 13: // Pic @@ -542,8 +908,8 @@ bool DInterBackground::LoadBackground(bool isenterpic) an.loc.y = sc.Number; sc.MustGetString(); an.frames.Reserve(1); // allocate exactly one element - an.frames[0] = TexMan.CheckForTexture(sc.String, ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny); - anims.Push(an); + an.frames[0].image = TexMan.CheckForTexture(sc.String, ETextureType::MiscPatch, FTextureManager::TEXMAN_TryAny); + layers[0].anims.Push(an); break; default: @@ -554,6 +920,7 @@ bool DInterBackground::LoadBackground(bool isenterpic) } else { + Printf("Intermission script %s not found!\n", lumpname + 1); texture = TexMan.GetTextureID("INTERPIC", ETextureType::MiscPatch); } @@ -585,27 +952,59 @@ void DInterBackground::updateAnimatedBack() unsigned int i; bcnt++; - for (i = 0; itype & ANIM_TYPE) + S_ChangeMusic(muslump.GetChars()); + } + + for (auto& layer : layers) + { + auto& anims = layer.anims; + for (i = 0; i < anims.Size(); i++) { - case ANIM_ALWAYS: - if (bcnt >= a->nexttic) + in_anim_t* a = &anims[i]; + switch (a->type & ANIM_TYPE) { - if (++a->ctr >= (int)a->frames.Size()) + case ANIM_NONE: + break; + + case ANIM_FRAME: + if (bcnt >= a->nexttic) { - if (a->data == 0) a->ctr = 0; - else a->ctr--; + if (++a->ctr >= (int)a->frames.Size()) + a->ctr = 0; + + switch (a->frames[a->ctr].type & 0x7) { + case 0x1: + a->type &= ANIM_CONDITION; + a->type = ANIM_NONE; + break; + case 0x4: + a->nexttic = bcnt + M_Random(a->frames[a->ctr].max_duration - a->frames[a->ctr].duration + 1) + a->frames[a->ctr].duration; + break; + case 0x2: + a->nexttic = bcnt + a->frames[a->ctr].duration; + break; + } } - a->nexttic = bcnt + a->period; + break; + case ANIM_ALWAYS: + if (bcnt >= a->nexttic) + { + if (++a->ctr >= (int)a->frames.Size()) + { + if (a->data == 0) a->ctr = 0; + else a->ctr--; + } + a->nexttic = bcnt + a->period; + } + break; + + case ANIM_PIC: + a->ctr = 0; + break; + } - break; - - case ANIM_PIC: - a->ctr = 0; - break; - } } } @@ -656,51 +1055,59 @@ void DInterBackground::drawBackground(int state, bool drawsplat, bool snl_pointe ClearRect(twod, 0, 0, twod->GetWidth(), twod->GetHeight(), 0, 0); } - for (i = 0; itype & ANIM_CONDITION) + if (!ConditionsMet(state, layer.conditions)) + continue; + + for (i = 0; i < anims.Size(); i++) { - case ANIM_IFVISITED: - li = FindLevelInfo(a->LevelName.GetChars()); - if (li == NULL || !(li->flags & LEVEL_VISITED)) continue; - break; + in_anim_t* a = &anims[i]; + level_info_t* li; - case ANIM_IFNOTVISITED: - li = FindLevelInfo(a->LevelName.GetChars()); - if (li == NULL || (li->flags & LEVEL_VISITED)) continue; - break; + switch (a->type & ANIM_CONDITION) + { + case ANIM_IFVISITED: + li = FindLevelInfo(a->LevelName.GetChars()); + if (li == NULL || !(li->flags & LEVEL_VISITED)) continue; + break; - // StatCount means 'leaving' - everything else means 'entering'! - case ANIM_IFENTERING: - if (state == StatCount || a->LevelName.CompareNoCase(wbs->next, 8)) continue; - break; + case ANIM_IFNOTVISITED: + li = FindLevelInfo(a->LevelName.GetChars()); + if (li == NULL || (li->flags & LEVEL_VISITED)) continue; + break; - case ANIM_IFNOTENTERING: - if (state != StatCount && !a->LevelName.CompareNoCase(wbs->next, 8)) continue; - break; + // StatCount means 'leaving' - everything else means 'entering'! + case ANIM_IFENTERING: + if (state == StatCount || a->LevelName.CompareNoCase(wbs->next, 8)) continue; + break; - case ANIM_IFLEAVING: - if (state != StatCount || a->LevelName.CompareNoCase(wbs->current, 8)) continue; - break; + case ANIM_IFNOTENTERING: + if (state != StatCount && !a->LevelName.CompareNoCase(wbs->next, 8)) continue; + break; - case ANIM_IFNOTLEAVING: - if (state == StatCount && !a->LevelName.CompareNoCase(wbs->current, 8)) continue; - break; + case ANIM_IFLEAVING: + if (state != StatCount || a->LevelName.CompareNoCase(wbs->current, 8)) continue; + break; - case ANIM_IFTRAVELLING: - if (a->LevelName2.CompareNoCase(wbs->current, 8) || a->LevelName.CompareNoCase(wbs->next, 8)) continue; - break; + case ANIM_IFNOTLEAVING: + if (state == StatCount && !a->LevelName.CompareNoCase(wbs->current, 8)) continue; + break; - case ANIM_IFNOTTRAVELLING: - if (!a->LevelName2.CompareNoCase(wbs->current, 8) && !a->LevelName.CompareNoCase(wbs->next, 8)) continue; - break; + case ANIM_IFTRAVELLING: + if (a->LevelName2.CompareNoCase(wbs->current, 8) || a->LevelName.CompareNoCase(wbs->next, 8)) continue; + break; + + case ANIM_IFNOTTRAVELLING: + if (!a->LevelName2.CompareNoCase(wbs->current, 8) && !a->LevelName.CompareNoCase(wbs->next, 8)) continue; + break; + } + if (a->ctr >= 0 && ConditionsMet(state, a->conditions)) + DrawTexture(twod, a->frames[a->ctr].image, false, a->loc.x, a->loc.y, + DTA_VirtualWidthF, animwidth, DTA_VirtualHeightF, animheight, DTA_FullscreenScale, FSMode_ScaleToFit43, TAG_DONE); } - if (a->ctr >= 0) - DrawTexture(twod, a->frames[a->ctr], false, a->loc.x, a->loc.y, - DTA_VirtualWidthF, animwidth, DTA_VirtualHeightF, animheight, DTA_FullscreenScale, FSMode_ScaleToFit43, TAG_DONE); } if (drawsplat) @@ -731,6 +1138,23 @@ DEFINE_ACTION_FUNCTION(DInterBackground, drawBackground) return 0; } +//==================================================================== +// +// +// +//==================================================================== + +bool DInterBackground::IsUsingMusic() +{ + return muslump.IsNotEmpty(); +} + +DEFINE_ACTION_FUNCTION(DInterBackground, IsUsingMusic) +{ + PARAM_SELF_PROLOGUE(DInterBackground); + ACTION_RETURN_BOOL(self->IsUsingMusic()); +} + IMPLEMENT_CLASS(DInterBackground, true, false) //==================================================================== diff --git a/wadsrc/static/zscript/ui/statscreen/statscreen.zs b/wadsrc/static/zscript/ui/statscreen/statscreen.zs index 4b50f0cf7..3ae9cf447 100644 --- a/wadsrc/static/zscript/ui/statscreen/statscreen.zs +++ b/wadsrc/static/zscript/ui/statscreen/statscreen.zs @@ -6,6 +6,7 @@ class InterBackground native ui version("2.5") native virtual bool LoadBackground(bool isenterpic); native virtual void updateAnimatedBack(); native virtual void drawBackground(int CurState, bool drawsplat, bool snl_pointeron); + native virtual bool IsUsingMusic(); } // This is obsolete. Hopefully this was never used... @@ -825,7 +826,8 @@ class StatusScreen : ScreenJob abstract version("2.5") virtual void StartMusic() { - Level.SetInterMusic(wbs.next); + if (!bg.IsUsingMusic()) + Level.SetInterMusic(wbs.next); } //====================================================================