From 8326a2d588a4f3710d8644ecc7ff8a809f5822e1 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 13 Sep 2023 17:53:07 +0100 Subject: [PATCH 1/5] Dialogue::WriteText improvements - Only have a long delay between symbols if the current character is std::ispunct() and the NEXT character is std::isspace - Add native color code support - Requires string concatenation to actually use it, though, which I don't know how to do in ACS --- src/k_dialogue.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/k_dialogue.cpp b/src/k_dialogue.cpp index e75e18a4d..7f3a03b18 100644 --- a/src/k_dialogue.cpp +++ b/src/k_dialogue.cpp @@ -157,9 +157,20 @@ void Dialogue::WriteText(void) while (textTimer <= 0 && !textDest.empty()) { - char c = textDest.back(); + char c = textDest.back(), nextc = '\n'; text.push_back(c); + textDest.pop_back(); + + if (c & 0x80) + { + // Color code support + continue; + } + + if (!textDest.empty()) + nextc = textDest.back(); + if (voicePlayed == false && std::isprint(c) && c != ' ') @@ -174,7 +185,8 @@ void Dialogue::WriteText(void) voicePlayed = true; } - if (c == '.' || c == ',' || c == ';' || c == '!' || c == '?') + if (std::ispunct(c) + && std::isspace(nextc)) { // slow down for punctuation textTimer += kTextPunctPause; @@ -183,8 +195,6 @@ void Dialogue::WriteText(void) { textTimer += FRACUNIT; } - - textDest.pop_back(); } textDone = (textTimer <= 0 && textDest.empty()); From 753b733f7e87afe6c61c286a8b7523fecbc36c31 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 13 Sep 2023 18:02:43 +0100 Subject: [PATCH 2/5] Permit multiple Spray Cans in one map - Up to 255 - Each Spray Can placed does, in order, the postion in the list from the current head - So if there were 3 cans, and your can list was Red Yellow Blue Green, then Red, Yellow, and Blue would be in the level. - Grabbing the nth colour in the list will swap that colour to the current head of the list - However, the game will print to the console if you do this outside of GS_TUTORIAL. --- src/p_inter.c | 45 +++++++++++++++++++++++++++++++++++++++++++-- src/p_mobj.c | 14 +++++++++----- src/p_setup.c | 3 ++- 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index 86004ad50..79ac76e9e 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -655,6 +655,25 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) { // Unassigned, get the next grabbable colour can_id = gamedata->gotspraycans; + + // Multiple cans in one map? + if (special->threshold != 0) + { + UINT16 ref_id = can_id + (special->threshold & UINT8_MAX); + if (ref_id >= gamedata->numspraycans) + return; + + // Swap this specific can to the head of the list. + UINT16 swapcol = gamedata->spraycans[ref_id].col; + + gamedata->spraycans[ref_id].col = + gamedata->spraycans[can_id].col; + skincolors[gamedata->spraycans[ref_id].col].cache_spraycan = ref_id; + + gamedata->spraycans[can_id].col = swapcol; + skincolors[swapcol].cache_spraycan = can_id; + } + } if (can_id >= gamedata->numspraycans) @@ -675,8 +694,30 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) gamedata->deferredsave = true; } - // Don't delete the object, just fade it. - P_SprayCanInit(special); + { + mobj_t *canmo = NULL; + mobj_t *next = NULL; + + for (canmo = trackercap; canmo; canmo = next) + { + next = canmo->itnext; + + if (canmo->type != MT_SPRAYCAN) + continue; + + // Don't delete the object(s), just fade it. + if (netgame || canmo == special) + { + P_SprayCanInit(canmo); + continue; + } + + // Get ready to get rid of these + canmo->renderflags |= (tr_trans50 << RF_TRANSSHIFT); + canmo->destscale = 0; + } + } + return; } diff --git a/src/p_mobj.c b/src/p_mobj.c index dad3be9d8..59cc5f1b1 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12443,8 +12443,14 @@ void P_SprayCanInit(mobj_t* mobj) // Prevent footguns - these won't persist when custom levels are unloaded else if (gamemap-1 < basenummapheaders) { - // Unassigned, get the next grabbable colour + // Unassigned, get the next grabbable colour (offset by threshold) can_id = gamedata->gotspraycans; + + // It's ok if this goes over gamedata->numspraycans, as they're + // capped below in this func... but NEVER let this go backwards!! + if (mobj->threshold != 0) + can_id += (mobj->threshold & UINT8_MAX); + mobj->renderflags = 0; } @@ -12920,17 +12926,15 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj) } case MT_SPRAYCAN: { - if (nummapspraycans) + if (nummapspraycans == UINT8_MAX) { - if (nummapspraycans != UINT8_MAX) - nummapspraycans++; - P_RemoveMobj(mobj); return false; } P_SetScale(mobj, mobj->destscale = 2*mobj->scale); + mobj->threshold = nummapspraycans; P_SprayCanInit(mobj); nummapspraycans++; break; diff --git a/src/p_setup.c b/src/p_setup.c index 8ab8a2588..da3f780b7 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -816,7 +816,8 @@ static void P_SpawnMapThings(boolean spawnemblems) Z_Free(loopends); - if (spawnemblems) + if (spawnemblems + && gametype != GT_TUTORIAL) { const UINT8 recommendedcans = #ifdef DEVELOP From ce86ebbd52b0e9e2f40436d670dc02b65f2f4110 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 13 Sep 2023 18:06:15 +0100 Subject: [PATCH 3/5] Add "GrandPrix" check function to ACS Resolves #669 Done in this branch to keep ACS changes together --- src/acs/call-funcs.cpp | 16 ++++++++++++++++ src/acs/call-funcs.hpp | 1 + src/acs/environment.cpp | 1 + 3 files changed, 18 insertions(+) diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index 24170bf41..6594d4840 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -40,6 +40,7 @@ #include "../m_cond.h" #include "../r_skins.h" #include "../k_battle.h" +#include "../k_grandprix.h" #include "../k_podium.h" #include "../k_bot.h" #include "../z_zone.h" @@ -1699,6 +1700,21 @@ bool CallFunc_TimeAttack(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM:: return false; } +/*-------------------------------------------------- + bool CallFunc_GrandPrix(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) + + Returns if a Grand Prix is active. +--------------------------------------------------*/ +bool CallFunc_GrandPrix(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + (void)argV; + (void)argC; + + thread->dataStk.push(grandprixinfo.gp); + return false; +} + + /*-------------------------------------------------- bool CallFunc_PodiumPosition(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) diff --git a/src/acs/call-funcs.hpp b/src/acs/call-funcs.hpp index 48ce72e84..39bf5adf8 100644 --- a/src/acs/call-funcs.hpp +++ b/src/acs/call-funcs.hpp @@ -81,6 +81,7 @@ bool CallFunc_LowestLap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::W bool CallFunc_EncoreMode(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_BreakTheCapsules(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_TimeAttack(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_GrandPrix(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_PodiumPosition(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_PodiumFinish(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); diff --git a/src/acs/environment.cpp b/src/acs/environment.cpp index ad0c3e106..9fba26211 100644 --- a/src/acs/environment.cpp +++ b/src/acs/environment.cpp @@ -165,6 +165,7 @@ Environment::Environment() addFuncDataACS0( 310, addCallFunc(CallFunc_BreakTheCapsules)); addFuncDataACS0( 311, addCallFunc(CallFunc_TimeAttack)); addFuncDataACS0( 312, addCallFunc(CallFunc_ThingCount)); + addFuncDataACS0( 313, addCallFunc(CallFunc_GrandPrix)); addFuncDataACS0( 500, addCallFunc(CallFunc_CameraWait)); addFuncDataACS0( 501, addCallFunc(CallFunc_PodiumPosition)); From e854ce53f53e5a046fffe68b0f81709917516198 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 13 Sep 2023 18:10:08 +0100 Subject: [PATCH 4/5] Add "GetGrabbedSprayCan" to ACS - If netgame or no Spray Can has been grabbed on this map, return 0 - If all Spray Cans have been grabbed in other maps, return "_Completed" - Otherwise, return the name for the Spray Can color attached to this map --- src/acs/call-funcs.cpp | 36 ++++++++++++++++++++++++++++++++++++ src/acs/call-funcs.hpp | 1 + src/acs/environment.cpp | 1 + 3 files changed, 38 insertions(+) diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index 6594d4840..07b8cd957 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -1714,6 +1714,42 @@ bool CallFunc_GrandPrix(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::W return false; } +/*-------------------------------------------------- + bool CallFunc_GetGrabbedSprayCan(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) + + Returns the level's associated Spray Can, if grabbed. +--------------------------------------------------*/ +bool CallFunc_GetGrabbedSprayCan(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + Environment *env = &ACSEnv; + + (void)argV; + (void)argC; + + if (netgame == false // cans are per-player and completely unsyncable + && gamemap-1 < basenummapheaders) + { + // See also P_SprayCanInit + UINT16 can_id = mapheaderinfo[gamemap-1]->cache_spraycan; + + if (can_id < gamedata->numspraycans) + { + UINT16 col = gamedata->spraycans[can_id].col; + + thread->dataStk.push(~env->getString( skincolors[col].name )->idx); + return false; + } + + if (gamedata->gotspraycans >= gamedata->numspraycans) + { + thread->dataStk.push(~env->getString( "_Completed" )->idx); + return false; + } + } + + thread->dataStk.push(0); + return false; +} /*-------------------------------------------------- bool CallFunc_PodiumPosition(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) diff --git a/src/acs/call-funcs.hpp b/src/acs/call-funcs.hpp index 39bf5adf8..86af95f15 100644 --- a/src/acs/call-funcs.hpp +++ b/src/acs/call-funcs.hpp @@ -82,6 +82,7 @@ bool CallFunc_EncoreMode(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM:: bool CallFunc_BreakTheCapsules(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_TimeAttack(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_GrandPrix(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_GetGrabbedSprayCan(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_PodiumPosition(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_PodiumFinish(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); diff --git a/src/acs/environment.cpp b/src/acs/environment.cpp index 9fba26211..289cfdb39 100644 --- a/src/acs/environment.cpp +++ b/src/acs/environment.cpp @@ -166,6 +166,7 @@ Environment::Environment() addFuncDataACS0( 311, addCallFunc(CallFunc_TimeAttack)); addFuncDataACS0( 312, addCallFunc(CallFunc_ThingCount)); addFuncDataACS0( 313, addCallFunc(CallFunc_GrandPrix)); + addFuncDataACS0( 314, addCallFunc(CallFunc_GetGrabbedSprayCan)); addFuncDataACS0( 500, addCallFunc(CallFunc_CameraWait)); addFuncDataACS0( 501, addCallFunc(CallFunc_PodiumPosition)); From 815e1251887915fd54dab814c848bb99a37f65f3 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 17 Sep 2023 13:50:32 +0100 Subject: [PATCH 5/5] Disable all g_dialogue-dependent ACS behaviour in netgame conditions Actual solution to Test Run dialogue in servers --- src/acs/call-funcs.cpp | 30 ++++++++++++++++++++++++++++++ src/acs/environment.cpp | 6 ++++++ 2 files changed, 36 insertions(+) diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index 07b8cd957..25af308f7 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -681,6 +681,12 @@ bool CallFunc_DialogueWaitDismiss(ACSVM::Thread *thread, const ACSVM::Word *argV (void)argV; (void)argC; + // TODO when we move away from g_dialogue + if (netgame) + { + return false; + } + g_dialogue.SetDismissable(true); thread->state = { @@ -703,6 +709,12 @@ bool CallFunc_DialogueWaitText(ACSVM::Thread *thread, const ACSVM::Word *argV, A (void)argV; (void)argC; + // TODO when we move away from g_dialogue + if (netgame) + { + return false; + } + g_dialogue.SetDismissable(false); thread->state = { @@ -1977,6 +1989,12 @@ bool CallFunc_DialogueSetSpeaker(ACSVM::Thread *thread, const ACSVM::Word *argV, (void)argC; + // TODO when we move away from g_dialogue + if (netgame) + { + return false; + } + map = thread->scopeMap; skinStr = map->getString(argV[0]); @@ -2015,6 +2033,12 @@ bool CallFunc_DialogueSetCustomSpeaker(ACSVM::Thread *thread, const ACSVM::Word (void)argC; + // TODO when we move away from g_dialogue + if (netgame) + { + return false; + } + map = thread->scopeMap; nametagStr = map->getString(argV[0]); @@ -2096,6 +2120,12 @@ bool CallFunc_DialogueNewText(ACSVM::Thread *thread, const ACSVM::Word *argV, AC (void)argC; + // TODO when we move away from g_dialogue + if (netgame) + { + return false; + } + map = thread->scopeMap; textStr = map->getString(argV[0]); diff --git a/src/acs/environment.cpp b/src/acs/environment.cpp index 289cfdb39..796abeceb 100644 --- a/src/acs/environment.cpp +++ b/src/acs/environment.cpp @@ -301,6 +301,12 @@ bool Environment::checkTag(ACSVM::Word type, ACSVM::Word tag) case ACS_TAGTYPE_DIALOGUE: { + // TODO when we move away from g_dialogue + if (netgame) + { + return true; + } + if (tag == 0) // cheeky reuse { // wait for dismissal