diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index 24170bf41..25af308f7 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" @@ -680,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 = { @@ -702,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 = { @@ -1699,6 +1712,57 @@ 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_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) @@ -1925,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]); @@ -1963,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]); @@ -2044,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/call-funcs.hpp b/src/acs/call-funcs.hpp index 48ce72e84..86af95f15 100644 --- a/src/acs/call-funcs.hpp +++ b/src/acs/call-funcs.hpp @@ -81,6 +81,8 @@ 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_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 ad0c3e106..796abeceb 100644 --- a/src/acs/environment.cpp +++ b/src/acs/environment.cpp @@ -165,6 +165,8 @@ Environment::Environment() addFuncDataACS0( 310, addCallFunc(CallFunc_BreakTheCapsules)); 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)); @@ -299,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 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()); diff --git a/src/p_inter.c b/src/p_inter.c index 5cb73a6c8..ffb4cff53 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -692,6 +692,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) @@ -712,8 +731,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 670650848..4f1b97657 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12463,8 +12463,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; } @@ -12940,17 +12946,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