diff options
author | Vittorio Romeo <vittorio.romeo@outlook.com> | 2021-10-29 01:22:29 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-29 01:22:29 +0100 |
commit | f36a5e40d776a1c879b67307f3cf9e2147f59668 (patch) | |
tree | fb54029b355e4818aa9ffe13d74797d8d726c490 | |
parent | 10eb8abbe7c124eca2d59bfeb6739bb447b5377f (diff) | |
parent | 909e57c84f1d0fffdc9b951ecf5b70bc58db463a (diff) |
Merge pull request #358 from SuperV1234/2.0.7
Open Hexagon v2.0.7
114 files changed, 2660 insertions, 951 deletions
Binary files differ diff --git a/_RELEASE/Assets/OpenSquare-Bold.ttf b/_RELEASE/Assets/OpenSquare-Bold.ttf Binary files differindex 4a2defa5..0ebf62e5 100644 --- a/_RELEASE/Assets/OpenSquare-Bold.ttf +++ b/_RELEASE/Assets/OpenSquare-Bold.ttf diff --git a/_RELEASE/Assets/OpenSquare-Regular.ttf b/_RELEASE/Assets/OpenSquare-Regular.ttf Binary files differindex 1198160b..b695c12c 100644 --- a/_RELEASE/Assets/OpenSquare-Regular.ttf +++ b/_RELEASE/Assets/OpenSquare-Regular.ttf diff --git a/_RELEASE/Assets/assets.json b/_RELEASE/Assets/assets.json index 9566ea66..25e90c62 100644 --- a/_RELEASE/Assets/assets.json +++ b/_RELEASE/Assets/assets.json @@ -1,43 +1,42 @@ { - "fonts": - [ - "forcedsquare.ttf", - "OpenSquare-Regular.ttf", - "OpenSquare-Bold.ttf" - ], - "textures": - [ - "bottomBar.png", - "creditsBar1.png", - "creditsBar2.png", - "creditsBar2b.png", - "creditsBar2c.png", - "creditsBar2d.png", - "titleBar.png", - "epilepsyWarning.png", - "keyArrow.png", - "keyFocus.png", - "keySwap.png", - "replayIcon.png", - "onlineIcon.png", - "onlineIconFail.png", - "starParticle.png" - ], - "soundBuffers": - [ - "beep.ogg", - "death.ogg", - "difficultyMultDown.ogg", - "difficultyMultUp.ogg", - "error.ogg", - "gameOver.ogg", - "go.ogg", - "increment.ogg", - "levelUp.ogg", - "openHexagon.ogg", - "personalBest.ogg", - "restart.ogg", - "select.ogg", - "swap.ogg" - ] + "fonts": + [ + "OpenSquare-Regular.ttf", + "OpenSquare-Bold.ttf" + ], + "textures": + [ + "bottomBar.png", + "creditsBar1.png", + "creditsBar2.png", + "creditsBar2b.png", + "creditsBar2c.png", + "creditsBar2d.png", + "titleBar.png", + "epilepsyWarning.png", + "keyArrow.png", + "keyFocus.png", + "keySwap.png", + "replayIcon.png", + "onlineIcon.png", + "onlineIconFail.png", + "starParticle.png" + ], + "soundBuffers": + [ + "beep.ogg", + "death.ogg", + "difficultyMultDown.ogg", + "difficultyMultUp.ogg", + "error.ogg", + "gameOver.ogg", + "go.ogg", + "increment.ogg", + "levelUp.ogg", + "openHexagon.ogg", + "personalBest.ogg", + "restart.ogg", + "select.ogg", + "swap.ogg" + ] } diff --git a/_RELEASE/ConfigOverrides/debug.json b/_RELEASE/ConfigOverrides/debug.json index 7c66d1ec..779e39f7 100644 --- a/_RELEASE/ConfigOverrides/debug.json +++ b/_RELEASE/ConfigOverrides/debug.json @@ -1,4 +1,4 @@ { - "debug": true, - "invincible": true -}
\ No newline at end of file + "debug": true, + "invincible": true +} diff --git a/_RELEASE/ConfigOverrides/fraps.json b/_RELEASE/ConfigOverrides/fraps.json index fa226a2b..e6f91980 100644 --- a/_RELEASE/ConfigOverrides/fraps.json +++ b/_RELEASE/ConfigOverrides/fraps.json @@ -1,7 +1,7 @@ { - "fullscreen": true, - "fullscreen_auto_resolution": false, - "fullscreen_width": 1280, - "fullscreen_height": 720, - "vsync": false -}
\ No newline at end of file + "fullscreen": true, + "fullscreen_auto_resolution": false, + "fullscreen_width": 1280, + "fullscreen_height": 720, + "vsync": false +} diff --git a/_RELEASE/ConfigOverrides/fullscreen.json b/_RELEASE/ConfigOverrides/fullscreen.json index 766a9374..9f1bf59f 100644 --- a/_RELEASE/ConfigOverrides/fullscreen.json +++ b/_RELEASE/ConfigOverrides/fullscreen.json @@ -1,3 +1,3 @@ { - "fullscreen": true -}
\ No newline at end of file + "fullscreen": true +} diff --git a/_RELEASE/ConfigOverrides/highfps.json b/_RELEASE/ConfigOverrides/highfps.json index 02332b3a..baa73b30 100644 --- a/_RELEASE/ConfigOverrides/highfps.json +++ b/_RELEASE/ConfigOverrides/highfps.json @@ -1,9 +1,9 @@ { - "fullscreen": true, - "fullscreen_auto_resolution": false, - "fullscreen_width": 640, - "fullscreen_height": 480, - "vsync": false, - "3D_enabled" : false, - "draw_text_outlines" : false -}
\ No newline at end of file + "fullscreen": true, + "fullscreen_auto_resolution": false, + "fullscreen_width": 640, + "fullscreen_height": 480, + "vsync": false, + "3D_enabled" : false, + "draw_text_outlines" : false +} diff --git a/_RELEASE/ConfigOverrides/localserver.json b/_RELEASE/ConfigOverrides/localserver.json index 2758744e..2f8f3236 100644 --- a/_RELEASE/ConfigOverrides/localserver.json +++ b/_RELEASE/ConfigOverrides/localserver.json @@ -1,4 +1,4 @@ { - "server_ip": "127.0.0.1", - "server_port": 50505 + "server_ip": "127.0.0.1", + "server_port": 50505 } diff --git a/_RELEASE/ConfigOverrides/no3D.json b/_RELEASE/ConfigOverrides/no3D.json index c793bf4d..c5627f83 100644 --- a/_RELEASE/ConfigOverrides/no3D.json +++ b/_RELEASE/ConfigOverrides/no3D.json @@ -1,3 +1,3 @@ { - "3D_enabled": false -}
\ No newline at end of file + "3D_enabled": false +} diff --git a/_RELEASE/ConfigOverrides/noaudio.json b/_RELEASE/ConfigOverrides/noaudio.json index a8cd4b36..d7b2a73e 100644 --- a/_RELEASE/ConfigOverrides/noaudio.json +++ b/_RELEASE/ConfigOverrides/noaudio.json @@ -1,4 +1,4 @@ { - "no_music": true, - "no_sound": true -}
\ No newline at end of file + "no_music": true, + "no_sound": true +} diff --git a/_RELEASE/ConfigOverrides/nocosmeticscript.json b/_RELEASE/ConfigOverrides/nocosmeticscript.json index eeaa0f79..a2c204fa 100644 --- a/_RELEASE/ConfigOverrides/nocosmeticscript.json +++ b/_RELEASE/ConfigOverrides/nocosmeticscript.json @@ -1,5 +1,5 @@ { - "show_messages": false, - "change_styles": false, - "change_music": false -}
\ No newline at end of file + "show_messages": false, + "change_styles": false, + "change_music": false +} diff --git a/_RELEASE/ConfigOverrides/remoteserver.json b/_RELEASE/ConfigOverrides/remoteserver.json index b0ee1403..5aaf3b7c 100644 --- a/_RELEASE/ConfigOverrides/remoteserver.json +++ b/_RELEASE/ConfigOverrides/remoteserver.json @@ -1,4 +1,4 @@ { - "server_ip": "139.162.199.162", - "server_port": 50505 + "server_ip": "139.162.199.162", + "server_port": 50505 } diff --git a/_RELEASE/ConfigOverrides/windowed.json b/_RELEASE/ConfigOverrides/windowed.json index f1fcefda..498e53b9 100644 --- a/_RELEASE/ConfigOverrides/windowed.json +++ b/_RELEASE/ConfigOverrides/windowed.json @@ -1,3 +1,3 @@ { - "fullscreen": false -}
\ No newline at end of file + "fullscreen": false +} diff --git a/_RELEASE/Packs/base/Scripts/common.lua b/_RELEASE/Packs/base/Scripts/common.lua index 60b1822b..1c5a306f 100644 --- a/_RELEASE/Packs/base/Scripts/common.lua +++ b/_RELEASE/Packs/base/Scripts/common.lua @@ -1,6 +1,9 @@ --- common variables +-- COMMON GLOBAL VARIABLES THICKNESS = 40.0 +-- COMMON FUNCTIONS + +-- enableSwapIfDMGreaterThan: If the player chooses to play a level that exceeds the given difficulty, turn on swap. function enableSwapIfDMGreaterThan(mDM) if(u_getDifficultyMult() > mDM) then e_messageAdd("difficulty > " ..mDM.. "\nswap enabled!", 65) @@ -8,6 +11,7 @@ function enableSwapIfDMGreaterThan(mDM) end end +-- enableSwapIfSpeedGEThan: If the player reaches a given speed on a level, turn on swap. function enableSwapIfSpeedGEThan(mSpeed) if (u_getSpeedMultDM() >= mSpeed and not l_getSwapEnabled()) then e_messageAddImportant("Speed >= "..mSpeed.."\nswap enabled!", 120) @@ -15,6 +19,7 @@ function enableSwapIfSpeedGEThan(mSpeed) end end +-- disableIncIfDMGreaterThan: If the player chooses to play a level that exceeds the given difficulty, disable incrementing. function disableIncIfDMGreaterThan(mDM) if(u_getDifficultyMult() > mDM) then e_messageAdd("difficulty > " ..mDM.. "\nincrement disabled!", 65) @@ -22,6 +27,7 @@ function disableIncIfDMGreaterThan(mDM) end end +-- disableSpeedIncIfDMGreaterThan: If the player chooses to play a level that exceeds the given difficulty, make speed not increase upon incrementing. function disableSpeedIncIfDMGreaterThan(mDM) if(u_getDifficultyMult() > mDM) then e_messageAdd("difficulty > " ..mDM.. "\nspeed increment disabled!", 65) @@ -53,32 +59,32 @@ end -- getPerfectDelay: returns time to wait for two walls to be next to each other function getPerfectDelay(mThickness) return mThickness / (5.02 * u_getSpeedMultDM()) * u_getDelayMultDM() end --- getPerfectDelayDM: returns getPerfectDelay calculated with difficulty mutliplier -function getPerfectDelayDM(mThickness) return mThickness / (5.02 * u_getSpeedMultDM()) * u_getDelayMultDM() end +-- getPerfectDelayDM's implementation has now been implemented into getPerfectDelay. Keeping the name for compatibility reasons. +getPerfectDelayDM = getPerfectDelay -- getPerfectThickness: returns a good THICKNESS value in relation to human reflexes function getPerfectThickness(mThickness) return mThickness * u_getSpeedMultDM() end -- getSideDistance: returns shortest distance from a side to another function getSideDistance(mSide1, mSide2) - start = mSide1 - rightSteps = 0 - while start ~= mSide2 do - rightSteps = rightSteps + 1 - start = start + 1 - if start > l_getSides() - 1 then start = 0 end + if (mSide1 == mSide2) then + return 0 end - - start = mSide1 - leftSteps = 0 - while start ~= mSide2 do - leftSteps = leftSteps + 1 - start = start - 1 - if start < 0 then start = l_getSides() - 1 end + -- Pick out the lower and higher side + local low = math.min(mSide1, mSide2) + local high = math.max(mSide1, mSide2) + -- We need to get both high and low positive for modulus to work properly + -- We only need to check the lower number for this. + if (low < 0) then + high = high - low + low = 0 end - - if rightSteps < leftSteps then return rightSteps end - return leftSteps + -- Calculate the difference and make any last minute adjustments accordingly + local diff = (high - low) % l_getSides() + if (diff > getHalfSides()) then + return l_getSides() - diff + end + return diff end -- cWall: creates a wall with the common THICKNESS @@ -131,7 +137,48 @@ end -- cAltBarrage: spawns a barrage of alternate walls function cAltBarrage(mSide, mStep) - for i = 0, l_getSides() / mStep, 1 do - cWall(mSide + i * mStep) + for i = 0, l_getSides(), mStep do + cWall(mSide + i) + end +end + +-- cSwapBarrageN: A barrage with mNeighbors to have an opening pocket, forcing the players to go into the pocket and swap. +function cSwapBarrageN(mSide, mNeighbors, mDelayMult) + mDelayMult = mDelayMult or 1 + local myThickness = getPerfectThickness(THICKNESS) * 2.5 * mDelayMult + local delay = getPerfectDelay(myThickness - THICKNESS) / u_getDelayMultDM() + cBarrageN(mSide, mNeighbors + 1) -- Create the initial barrage + w_wall(mSide + 1 + mNeighbors, myThickness) + w_wall(mSide + l_getSides() - 1 - mNeighbors, myThickness) + t_wait(delay) + for i = -mNeighbors, mNeighbors do + cWall(i + mSide) + end +end + +-- cSwapBarrage: A barrage, but the opening is instead a pocket that is fully enclosed, forcing the player to swap to escape the pocket. +function cSwapBarrage(mSide, mDelayMult) + mDelayMult = mDelayMult or 1 + local myThickness = getPerfectThickness(THICKNESS) * 2.5 * mDelayMult + local delay = getPerfectDelay(myThickness - THICKNESS) / u_getDelayMultDM() + cBarrageN(mSide, 1) + w_wall(mSide + 1, myThickness) + w_wall(mSide + l_getSides() - 1, myThickness) + t_wait(delay) + cWall(mSide) +end + +-- cSwapCorridor: A barrage, but the neighbors of the opening have elongated thicknesses +function cSwapCorridor(mSide, mEnding) + mEnding = mEnding or false + local myThickness = getPerfectThickness(THICKNESS) * 4 + local delay = getPerfectDelay(myThickness - THICKNESS) / u_getDelayMultDM() + cBarrageN(mSide, 1) + w_wall(mSide + 1, myThickness) + w_wall(mSide + l_getSides() - 1, myThickness) + t_wait(delay) + if (mEnding) then + cWall(mSide) end + t_wait(getPerfectDelay(THICKNESS) / u_getDelayMultDM()) end diff --git a/_RELEASE/Packs/base/Scripts/commonpatterns.lua b/_RELEASE/Packs/base/Scripts/commonpatterns.lua index 608befd9..cd471924 100644 --- a/_RELEASE/Packs/base/Scripts/commonpatterns.lua +++ b/_RELEASE/Packs/base/Scripts/commonpatterns.lua @@ -1,10 +1,21 @@ u_execScript("common.lua") +-- pBarrage: spawns a patternized cBarrageN with a delay +function pBarrage(mSide, mNeighbors) + mSide = mSide or getRandomSide() + mNeighbors = mNeighbors or 0 + local delay = getPerfectDelay(THICKNESS) * 6.5 + + cBarrageN(mSide, mNeighbors) + t_wait(delay) +end + -- pAltBarrage: spawns a series of cAltBarrage -function pAltBarrage(mTimes, mStep) - local delay = getPerfectDelayDM(THICKNESS) * 5.6 +function pAltBarrage(mTimes, mStep, mDelayMult) + mDelayMult = mDelayMult or 1 + local delay = getPerfectDelay(THICKNESS) * 5.6 * mDelayMult - for i = 0, mTimes do + for i = 1, mTimes do cAltBarrage(i, mStep) t_wait(delay) end @@ -12,29 +23,25 @@ function pAltBarrage(mTimes, mStep) t_wait(delay) end --- pSpiralDir: spawns a spiral of cWallEx -function pSpiralDir(mLoopDir, mTimes, mExtra) +-- pSpiral: spawns a spiral of cWallEx +function pSpiral(mTimes, mExtra, mDir) + mDir = mDir or getRandomDir() + local oldThickness = THICKNESS - THICKNESS = getPerfectThickness(THICKNESS) * l_getDelayMult() - local delay = getPerfectDelay(THICKNESS) / l_getDelayMult() * 0.9 -- overlap a bit to avoid going through gaps + THICKNESS = getPerfectThickness(THICKNESS) + local delay = getPerfectDelay(THICKNESS) local startSide = getRandomSide() - local loopDir = mLoopDir local j = 0 for i = 0, mTimes do cWallEx(startSide + j, mExtra) - j = j + loopDir + j = j + mDir t_wait(delay) end THICKNESS = oldThickness - t_wait(getPerfectDelayDM(THICKNESS) * 6.5) -end - --- pSpiral: spawns a spiral of cWallEx -function pSpiral(mTimes, mExtra) - pSpiralDir(getRandomDir(), mTimes, mExtra) + t_wait(getPerfectDelay(THICKNESS) * 6.5) end -- pMirrorSpiral: spawns a spiral of rWallEx @@ -54,14 +61,14 @@ function pMirrorSpiral(mTimes, mExtra) THICKNESS = oldThickness - t_wait(getPerfectDelayDM(THICKNESS) * 6.5) + t_wait(getPerfectDelay(THICKNESS) * 6.5) end -- pMirrorSpiralDouble: spawns a spiral of rWallEx where you need to change direction function pMirrorSpiralDouble(mTimes, mExtra) local oldThickness = THICKNESS THICKNESS = getPerfectThickness(THICKNESS) * l_getDelayMult() - local delay = getPerfectDelayDM(THICKNESS) / l_getDelayMult() * 0.9 -- overlap a bit to avoid going through gaps + local delay = getPerfectDelay(THICKNESS) / l_getDelayMult() * 0.9 -- overlap a bit to avoid going through gaps local startSide = getRandomSide() local loopDir = getRandomDir() local j = 0 @@ -82,12 +89,15 @@ function pMirrorSpiralDouble(mTimes, mExtra) end THICKNESS = oldThickness - t_wait(getPerfectDelayDM(THICKNESS) * 7.5) + t_wait(getPerfectDelay(THICKNESS) * 7.5) end -- pBarrageSpiral: spawns a spiral of cBarrage function pBarrageSpiral(mTimes, mDelayMult, mStep) - local delay = getPerfectDelayDM(THICKNESS) * 5.6 * mDelayMult + mDelayMult = mDelayMult or 1 + mStep = mStep or 1 + + local delay = getPerfectDelay(THICKNESS) * 5.6 * mDelayMult local startSide = getRandomSide() local loopDir = mStep * getRandomDir() local j = 0 @@ -99,12 +109,12 @@ function pBarrageSpiral(mTimes, mDelayMult, mStep) if(l_getSides() < 6) then t_wait(delay * 0.6) end end - t_wait(getPerfectDelayDM(THICKNESS) * 6.1) + t_wait(getPerfectDelay(THICKNESS) * 6.1) end -- pDMBarrageSpiral: spawns a spiral of cBarrage, with static delay function pDMBarrageSpiral(mTimes, mDelayMult, mStep) - local delay = (getPerfectDelayDM(THICKNESS) * 5.42) * (mDelayMult / (u_getDifficultyMult() ^ 0.4)) * (u_getSpeedMultDM() ^ 0.35) + local delay = (getPerfectDelay(THICKNESS) * 5.42) * (mDelayMult / (u_getDifficultyMult() ^ 0.4)) * (u_getSpeedMultDM() ^ 0.35) local startSide = getRandomSide() local loopDir = mStep * getRandomDir() local j = 0 @@ -116,12 +126,12 @@ function pDMBarrageSpiral(mTimes, mDelayMult, mStep) if(l_getSides() < 6) then t_wait(delay * 0.49) end end - t_wait(getPerfectDelayDM(THICKNESS) * (6.7 * (u_getDifficultyMult() ^ 0.7))) + t_wait(getPerfectDelay(THICKNESS) * (6.7 * (u_getDifficultyMult() ^ 0.7))) end -- pWallExVortex: spawns left-left right-right spiral patters function pWallExVortex(mTimes, mStep, mExtraMult) - local delay = getPerfectDelayDM(THICKNESS) * 5.0 + local delay = getPerfectDelay(THICKNESS) * 5.0 local startSide = getRandomSide() local loopDir = getRandomDir() local currentSide = startSide @@ -142,12 +152,12 @@ function pWallExVortex(mTimes, mStep, mExtraMult) end end - t_wait(getPerfectDelayDM(THICKNESS) * 5.5) + t_wait(getPerfectDelay(THICKNESS) * 5.5) end -- pInverseBarrage: spawns two barrages who force you to turn 180 degrees function pInverseBarrage(mTimes) - local delay = getPerfectDelayDM(THICKNESS) * 9.9 + local delay = getPerfectDelay(THICKNESS) * 9.9 local startSide = getRandomSide() for i = 0, mTimes do @@ -158,7 +168,7 @@ function pInverseBarrage(mTimes) t_wait(delay) end - t_wait(getPerfectDelayDM(THICKNESS) * 2.5) + t_wait(getPerfectDelay(THICKNESS) * 2.5) end -- pRandomBarrage: spawns barrages with random side, and waits humanly-possible times depending on the sides distance @@ -170,15 +180,15 @@ function pRandomBarrage(mTimes, mDelayMult) cBarrage(side) oldSide = side side = getRandomSide() - t_wait(getPerfectDelayDM(THICKNESS) * (2 + (getSideDistance(side, oldSide)*mDelayMult))) + t_wait(getPerfectDelay(THICKNESS) * (2 + (getSideDistance(side, oldSide)*mDelayMult))) end - t_wait(getPerfectDelayDM(THICKNESS) * 5.6) + t_wait(getPerfectDelay(THICKNESS) * 5.6) end -- pMirrorWallStrip: spawns rWalls close to one another on the same side function pMirrorWallStrip(mTimes, mExtra) - local delay = getPerfectDelayDM(THICKNESS) * 3.65 + local delay = getPerfectDelay(THICKNESS) * 3.65 local startSide = getRandomSide() for i = 0, mTimes do @@ -186,7 +196,7 @@ function pMirrorWallStrip(mTimes, mExtra) t_wait(delay) end - t_wait(getPerfectDelayDM(THICKNESS) * 5.00) + t_wait(getPerfectDelay(THICKNESS) * 5.00) end -- pTunnel: forces you to circle around a very thick wall @@ -212,3 +222,24 @@ function pTunnel(mTimes) THICKNESS = oldThickness end + +-- pSwapBarrage: a cSwapBarrage with additional pattern delay +function pSwapBarrage(mSide, mDelayMult) + mDelayMult = mDelayMult or 1 + cSwapBarrage(mSide, mDelayMult) + t_wait(getPerfectDelay(THICKNESS) * 7.5) +end + +-- pSwapCorridor: A series of cSwapCorridors, forcing you to swap and look around for new corridors going through. Has some similarity to pTunnel. +function pSwapCorridor(mTimes) + local currentSide = getRandomSide() + for i = 1, mTimes do + if (i == mTimes) then + cSwapCorridor(currentSide, true) + else + cSwapCorridor(currentSide) + end + currentSide = math.random(currentSide + getHalfSides() - 1, currentSide + getHalfSides() + 1) + end + t_wait(getPerfectDelay(THICKNESS) * 7) +end diff --git a/_RELEASE/Packs/base/Scripts/evolutionpatterns.lua b/_RELEASE/Packs/base/Scripts/evolutionpatterns.lua index 918ecc7a..c9a7a422 100644 --- a/_RELEASE/Packs/base/Scripts/evolutionpatterns.lua +++ b/_RELEASE/Packs/base/Scripts/evolutionpatterns.lua @@ -4,165 +4,264 @@ u_execScript("utils.lua") u_execScript("alternativepatterns.lua") u_execScript("nextpatterns.lua") -local hueModifier = 0.2 -local sync = false -local syncRndMin = 0 -local syncRndMax = 0 +--------------- +-- CONSTANTS -- +--------------- -local curveMult = 1 +globalHueModifier = 0.2 -- Constant used to shift the color offset to work with the style's hue. +local sync = false -- Constant used to sync curving walls with that rotation speed +local syncRndMin = 0 -- Minimum random displacement from the synced speed +local syncRndMax = 0 -- Maximum random displacement from the synced speed +local curveMult = 1 -- Constant used to multiply the global curve speed of curving walls + +--------------- +-- FUNCTIONS -- +--------------- + +-- syncCurveToSideDistance: Returns an appropriate constant that, if applied to a curving wall travelling at constant speed, will cause it go one full side. +function syncCurveToSideDistance() + return (.2 * u_getSpeedMultDM() + 0.005) * (6 / l_getSides()) * (u_getDifficultyMult() ^ -.25); +end + +-- getPerfectCurveDecel: Returns a constant that, if applied to a curving wall, will make the wall stop accelerating on a side panel. +function getPerfectCurveDecel() + -- Coordinates tested: (0, 0), (1, .134), (1.5, .295), (2, .52), (2.5, .81), (3, 1.162), (3.5, 1.579), (4, 2.06) + -- Cubic Formula (full accuracy): 0.000233918 x^3 + 0.125985 x^2 + 0.00730493 x + 0.0000701754 + -- Current formula has ~100% accuracy + return (0.127378 * u_getSpeedMultDM() ^ 2 + 0.00526331 * u_getSpeedMultDM() + 0.000431373) * (6 / l_getSides()); +end + +-- getRndMinDM: Returns a random integer of a number where the minimum is influenced by the difficulty multiplier +function getRndMinDM(mNum) + return math.random(math.floor(mNum - (u_getDifficultyMult() ^ 3)), math.ceil(mNum)) +end + +-- getRndMaxDM: Returns a random integer of a number where the maximum is influenced by the difficulty multiplier +function getRndMaxDM(mNum) + return math.random(math.floor(mNum), math.ceil(mNum + (u_getDifficultyMult() ^ 2.25))) +end + +-- The function to call when wanting to sync curving walls with the rotation speed. +-- mRndMin (OPTIONAL): Sets the syncRndMin const to this value +-- mRndMax (OPTIONAL): Sets the syncRndMax const to this value function syncCurveWithRotationSpeed(mRndMin, mRndMax) sync = true - syncRndMin = mRndMin - syncRndMax = mRndMax + syncRndMin = mRndMin or 0 + syncRndMax = mRndMax or 0 end +-- The function to call when wanting to alter curveMult constant +-- mMult: Sets the curveMult const to this value function setCurveMult(mMult) curveMult = mMult end -function wallHMCurveAcc(mSide, mCurve, mCurveAcc, mCurveMin, mCurveMax, mCurvePingPong) +-- Creates a curving wall, adhering to the constants presented by this script. +-- mSide: The side to spawn the curving wall on +-- mCurve: The curving speed the wall will travel at +-- mCurveAcc: How much acceleration to apply to the curving wall +-- mCurveMin: The lowest curving speed the wall is allowed to go +-- mCurveMax: The highest curving speed the wall is allowed to go +-- mCurvePingPong: If accelerated to mCurveMin or mCurveMax, should the acceleration switch directions and go the other way? +-- mThickness (OPTIONAL): The thickness of the curving wall. +function wallHMCurveAcc(mSide, mCurve, mCurveAcc, mCurveMin, mCurveMax, mCurvePingPong, mThickness) + mThickness = mThickness or THICKNESS if sync == true then - mCurve = l_getRotationSpeed() * 10.0 - mCurve = mCurve + (u_rndInt(syncRndMin, syncRndMax) / 100.0) + mCurve = l_getRotationSpeed() * (10.0 / syncCurveToSideDistance()) + mCurve = mCurve + (math.random(syncRndMin, syncRndMax) / 100.0) + end + w_wallHModCurveData(globalHueModifier, mSide, mThickness, mCurve * (u_getDifficultyMult() ^ 0.25) * curveMult * syncCurveToSideDistance(), mCurveAcc, mCurveMin, mCurveMax, mCurvePingPong) +end + +-- A simplification of wallHMCurve, only allowing customization of the curve speed +-- mSide: The side to spawn the curving wall on +-- mCurve: The curving speed the wall will travel at +-- mThickness (OPTIONAL): The thickness of the curving wall. +function wallHMCurve(mSide, mCurve, mThickness) + mThickness = mThickness or THICKNESS + wallHMCurveAcc(mSide, mCurve, 0, 0, 0, false, mThickness) +end + +-- A preset of wallHMCurveAcc where getPerfectCurveDecel is used to spawn a wall to stop acceleration on a side panel. +-- mSide: The side that the wall will stop on. +-- mOffset: How much offset from mSide the wall will have when spawned. A higher offset will make a faster wall. +-- mThickness (OPTIONAL): The thickness of the curving wall. +function wallHMStop(mSide, mOffset, mThickness) + mThickness = mThickness or THICKNESS + local curveAcc = getPerfectCurveDecel(); + if (getSign(mOffset) == -1) then + wallHMCurveAcc(mSide + mOffset, 2 * -mOffset, curveAcc * mOffset / 100, 0, 2 * -mOffset, false, mThickness); + else + wallHMCurveAcc(mSide + mOffset, -2 * mOffset, curveAcc * mOffset / 100, -2 * mOffset, 0, false, mThickness); end - - w_wallHModCurveData(hueModifier, mSide, THICKNESS, mCurve * (u_getDifficultyMult() ^ 0.25) * curveMult, mCurveAcc, mCurveMin, mCurveMax, mCurvePingPong) end -function wallHMCurve(mSide, mCurve) - wallHMCurveAcc(mSide, mCurve, 0, 0, 0, false) -end +------------------------------------------------------ +-- BEGINNING OF HMC FUNCTIONS (Hue Modified Common) -- +------------------------------------------------------ +-- A barrage with additional adjacent mNeighbors that composes of curving walls. +-- mSide: The side to spawn the curving wall on +-- mNeighbors: The expansion of the barrage opening by this many adjacent neighbors +-- mCurve: The curving speed the wall will travel at +-- mCurveAcc: How much acceleration to apply to the curving wall +-- mCurveMin: The lowest curving speed the wall is allowed to go +-- mCurveMax: The highest curving speed the wall is allowed to go +-- mCurvePingPong: If accelerated to mCurveMin or mCurveMax, should the acceleration switch directions and go the other way? function hmcBarrageN(mSide, mNeighbors, mCurve, mCurveAcc, mCurveMin, mCurveMax, mCurvePingPong) for i = mNeighbors, l_getSides() - 2 - mNeighbors, 1 do wallHMCurveAcc(mSide + i + 1, mCurve, mCurveAcc, mCurveMin, mCurveMax, mCurvePingPong) end end +-- hmcBarrageS: hmcBarrageN, but there are no additional neighbors, and the gap is only 1 side wide. function hmcBarrageS(mSide, mCurve, mCurveAcc, mCurveMin, mCurveMax, mCurvePingPong) - hmcBarrageN(mSide, 0, mCurve, mCurveAcc, mCurveMin, mCurveMax, mCurvePingPong) + hmcBarrageN(mSide, 0, mCurve, mCurveAcc, mCurveMin, mCurveMax, mCurvePingPong); end +-- hmcBarrage: hmcBarrageS, but the side is chosen at random for you. function hmcBarrage(mCurve, mCurveAcc, mCurveMin, mCurveMax, mCurvePingPong) - hmcBarrageS(getRandomSide(), mCurve, mCurveAcc, mCurveMin, mCurveMax, mCurvePingPong) + hmcBarrageS(getRandomSide(), mCurve, mCurveAcc, mCurveMin, mCurveMax, mCurvePingPong); end -function hmcSimpleBarrage(mCurve) - hmcBarrageN(getRandomSide(), 0, mCurve, 0, 0, 0, false) -end - -function hmcSimpleBarrageS(mSide, mCurve) - hmcBarrageN(mSide, 0, mCurve, 0, 0, 0, false) +-- hmcBarrageStop: A barrage that comes to a full stop, similar to how wallHMStop works. +function hmcBarrageStop(mSide, mOffset, mNeighbors) + mNeighbors = mNeighbors or 0 + local curveAcc = getPerfectCurveDecel(); + if (getSign(mOffset) == -1) then + hmcBarrageN(mSide + mOffset, mNeighbors, 2 * -mOffset, curveAcc * mOffset / 100, 0, 2 * -mOffset, false); + else + hmcBarrageN(mSide + mOffset, mNeighbors, -2 * mOffset, curveAcc * mOffset / 100, -2 * mOffset, 0, false); + end end -function hmcSimpleBarrageSNeigh(mSide, mCurve, mNeighbors) - hmcBarrageN(mSide, mNeighbors, mCurve, 0, 0, 0, false) +-- A simple curving barrage. +-- mSide (OPTIONAL, BUT HIGHLY RECOMMENDED): The side the barrage starts at +-- mCurve (OPTIONAL): The constant curving speed the barrage will travel +-- mNeighbors (OPTIONAL): The expansion of the barrage opening by this many adjacent neighbors +function hmcSimpleBarrage(mSide, mCurve, mNeighbors) + mSide = mSide or 0 + mCurve = mCurve or 0 + mNeighbors = mNeighbors or 0 + hmcBarrageN(mSide, mNeighbors, mCurve, 0, 0, 0, false); end +-- Compatibility mappings for legacy functions that did the same thing. +hmcSimpleBarrageS = hmcSimpleBarrage +hmcSimpleBarrageSNeigh = hmcSimpleBarrage +-- A series of barrages that curve at mCurve speed, increasing each iteration by mCurveAdd to recreate a barrage spiral +-- mTimes: How many repetitions of barrages there should be +-- mCurve: The starting curve speed for the pattern +-- mCurveAdd: How much to add to the curving speed each iteration function hmcSimpleTwirl(mTimes, mCurve, mCurveAdd) local startSide = getRandomSide() - local currentSide = startSide - local loopDir = getRandomDir() - local delay = getPerfectDelayDM(THICKNESS) * 5.7 - local j = 0 - + local delay = getPerfectDelay(THICKNESS) * 5.7 local currentCurve = mCurve for i = 0, mTimes do - hmcSimpleBarrageS(startSide + j, currentCurve) - j = j + loopDir + hmcSimpleBarrageS(startSide, currentCurve) currentCurve = currentCurve + mCurveAdd t_wait(delay) end end -function hmcSimpleCage(mCurve, mDir) - local side = getRandomSide() - local oppositeSide = side + getHalfSides() - - wallHMCurve(side, mCurve) - wallHMCurve(oppositeSide, mCurve * mDir) -end - -function hmcSimpleCageS(mCurve, mDir, mSide) +-- A wall that curves at mCurve speed, with an opposing wall that travel towards it, +-- "caging" the player if not aware of the movements. +-- mCurve: The curve speed for the pattern +-- mDir: The direction of the curving for the opposing wall. +-- mSide (OPTIONAL): Which side the pattern spawns on. +function hmcSimpleCage(mCurve, mDir, mSide) + mSide = mSide or getRandomSide() local oppositeSide = mSide + getHalfSides() wallHMCurve(mSide, mCurve) wallHMCurve(oppositeSide, mCurve * mDir) end -function hmcSimpleSpinner(mCurve) - local side = getRandomSide() +hmcSimpleCageS = hmcSimpleCage - for i = 0, l_getSides() / 2, 1 do - wallHMCurve(side + i * 2, mCurve) +-- An alt barrage that travels at a constant curving speed (Don't ask. Vee came up with the name). +-- mSide: The side the spinner starts at +-- mCurve: The constant curving speed the spinner will travel +-- mStep (OPTIONAL): How much to iterate before spawning the next wall +function hmcSimpleSpinner(mSide, mCurve, mStep) + mStep = mStep or 2 + for i = 0, l_getSides(), mStep do + wallHMCurve(mSide + i, mCurve) end end -function hmcSimpleSpinnerS(mSide, mCurve) - for i = 0, l_getSides() / 2, 1 do - wallHMCurve(mSide + i * 2, mCurve) - end -end +hmcSimpleSpinnerS = hmcSimpleSpinner +-- hmcSimpleSpinnerSAcc: A spinner but with more options in regards to accelerating the walls. function hmcSimpleSpinnerSAcc(mSide, mCurve, mCurveAcc, mCurveMin, mCurveMax, mCurvePingPong) for i = 0, l_getSides() / 2, 1 do wallHMCurveAcc(mSide + i * 2, mCurve, mCurveAcc, mCurveMin, mCurveMax, mCurvePingPong) end end -function hmcDefSpinner() - t_wait(getPerfectDelayDM(THICKNESS) * 3.2) - hmcSimpleSpinner(u_rndInt(10, 19) / 10.0 * getRandomDir()) - t_wait(getPerfectDelayDM(THICKNESS) * 5.9) +-- hmcGrowBarrage: Given a specific position to pivot, all walls will spawn there and spread out from that position to make a barrage. +function hmcGrowBarrage(mSide, mPivot) + for i = 0, l_getSides() - 2 do + wallHMStop(mSide + i + 1, mPivot - i); + end end -function hmcDefBarrage() - t_wait(getPerfectDelayDM(THICKNESS) * 3.1) - hmcSimpleBarrage(u_rndInt(10, 20) / 10.0 * getRandomDir()) - t_wait(getPerfectDelayDM(THICKNESS) * 5) +-- hmcAssembleBarrage: All individual walls will be randomly offset and then assemble together to create a barrage. +function hmcAssembleBarrage(mSide, mLower, mUpper) + mLower = mLower or 1 + mUpper = mUpper or l_getSides() + for i = 0, l_getSides() - 2 do + wallHMStop(mSide + i + 1, math.random(mLower, mUpper) * getRandomDir()); + end end +-- hmcDef2Cage: Spawns a series of hmcSimpleCage to create a trailing effect function hmcDef2Cage() - t_wait(getPerfectDelayDM(THICKNESS) * 2.1) local side = getRandomSide() - local rndspd = u_rndInt(10, 20) / 10.0 + local rndspd = math.random(1, 5) - t_wait(getPerfectDelayDM(THICKNESS) * 3.1) + t_wait(getPerfectDelay(THICKNESS) * 5.2) hmcSimpleCageS(rndspd, -1, side) - t_wait(getPerfectDelayDM(THICKNESS) * 1.1) + t_wait(getPerfectDelay(THICKNESS) * 1.1) hmcSimpleCageS(rndspd, -1, side) - t_wait(getPerfectDelayDM(THICKNESS) * 1.1) + t_wait(getPerfectDelay(THICKNESS) * 1.1) hmcSimpleCageS(rndspd, -1, side) - t_wait(getPerfectDelayDM(THICKNESS) * 5.3) + t_wait(getPerfectDelay(THICKNESS) * 5.3) end +-- hmcDef2CageD: A doubled version of hmcDef2Cage, creating another one opposite of the first pattern function hmcDef2CageD() - t_wait(getPerfectDelayDM(THICKNESS) * 2.1) - local side = getRandomSide() local oppositeSide = getHalfSides() + side - local rndspd = u_rndInt(10, 17) / 10.0 + local rndspd = math.random(1, 5) - t_wait(getPerfectDelayDM(THICKNESS) * 3.1) + t_wait(getPerfectDelay(THICKNESS) * 5.2) hmcSimpleCageS(rndspd, -1, side) - t_wait(getPerfectDelayDM(THICKNESS) * 1.1) + t_wait(getPerfectDelay(THICKNESS) * 1.1) hmcSimpleCageS(rndspd, -1, side) - t_wait(getPerfectDelayDM(THICKNESS) * 1.1) + t_wait(getPerfectDelay(THICKNESS) * 1.1) hmcSimpleCageS(rndspd, -1, side) - t_wait(getPerfectDelayDM(THICKNESS) * 6.0) + t_wait(getPerfectDelay(THICKNESS) * 6.0) hmcSimpleCageS(rndspd, -1, oppositeSide) - t_wait(getPerfectDelayDM(THICKNESS) * 1.1) + t_wait(getPerfectDelay(THICKNESS) * 1.1) hmcSimpleCageS(rndspd, -1, oppositeSide) - t_wait(getPerfectDelayDM(THICKNESS) * 1.1) + t_wait(getPerfectDelay(THICKNESS) * 1.1) hmcSimpleCageS(rndspd, -1, oppositeSide) - t_wait(getPerfectDelayDM(THICKNESS) * 9.2) + t_wait(getPerfectDelay(THICKNESS) * 9.2) end +-- A barrage spiral that travels at a constant curving speed. +-- mTimes: The amount of barrages to spawn during a barrage +-- mDelayMult: A custom delay multiplier used to customize spacing between barrages +-- mStep: How much sides to increment from barrage to barrage +-- mCurve: The curve speed for the pattern +-- mNeighbors (OPTIONAL): The expansion of the barrage opening by this many adjacent neighbors function hmcSimpleBarrageSpiral(mTimes, mDelayMult, mStep, mCurve, mNeighbors) - local delay = getPerfectDelayDM(THICKNESS) * 6.2 * mDelayMult + mNeighors = mNeighbors or 0 + local delay = getPerfectDelay(THICKNESS) * 6.2 * mDelayMult local startSide = getRandomSide() local loopDir = mStep * getRandomDir() local j = 0 @@ -174,128 +273,347 @@ function hmcSimpleBarrageSpiral(mTimes, mDelayMult, mStep, mCurve, mNeighbors) if(l_getSides() < 6) then t_wait(delay * 0.7) end end - t_wait(getPerfectDelayDM(THICKNESS) * 6.1) + t_wait(getPerfectDelay(THICKNESS) * 6.1) end -function hmcSimpleBarrageSpiralRnd(mTimes, mDelayMult, mCurve, mNeighbors) - local delay = getPerfectDelayDM(THICKNESS) * 6.2 * mDelayMult - local startSide = getRandomSide() +-- getPerfectDelay now has the same implementation as getPerfectDelay, so this pattern is now redundant. +hmcSimpleBarrageSpiralStatic = hmcSimpleBarrageSpiral - for i = 0, mTimes do - hmcSimpleBarrageSNeigh(getRandomSide(), mCurve, mNeighbors) - t_wait(delay) - if(l_getSides() < 6) then t_wait(delay * 0.7) end - end +-- hmcBarrageInv: pInverseBarrage but with curving walls. +function hmcBarrageInv(mMinCurve, mMaxCurve) + t_wait(getPerfectDelay(THICKNESS) * 2.0) + local delay = getPerfectDelay(THICKNESS) * 8 + local side = getRandomSide() + local rndSpd = math.random(mMinCurve, mMaxCurve); + local oppositeSide = getRandomSide() + getHalfSides() + + hmcSimpleBarrageSNeigh(side, rndSpd * getRandomDir(), 0) + t_wait(delay) - t_wait(getPerfectDelayDM(THICKNESS) * 6.1) + hmcSimpleBarrageSNeigh(oppositeSide, rndSpd * getRandomDir(), 0) + t_wait(delay) end -function hmcSimpleBarrageSpiralStatic(mTimes, mDelayMult, mStep, mCurve, mNeighbors) - local delay = getPerfectDelay(THICKNESS) * 5.6 * mDelayMult - local startSide = getRandomSide() - local loopDir = mStep * getRandomDir() - local j = 0 +-- hmcTunnelDynamic: A single segment tunnel piece where the tunnel (the thick wall) moves into a random position. +function hmcTunnelDynamic(mBarrageSide, mTunnelSide, mOffset) + local offset = mOffset or math.random(3, l_getSides()); + local myThickness = getPerfectThickness(THICKNESS) + local delay = getPerfectDelay(myThickness) * 4 - for i = 0, mTimes do - hmcSimpleBarrageSNeigh(startSide + j, mCurve, mNeighbors) - j = j + loopDir - t_wait(delay) - if(l_getSides() < 6) then t_wait(delay * 0.6) end + cBarrage(mBarrageSide); + t_wait(getPerfectDelay(THICKNESS) - 1) + wallHMStop(mTunnelSide % l_getSides(), offset, myThickness + 6 * u_getSpeedMultDM() * delay - myThickness * 2) + t_wait(delay) +end + +-- hmcTurnaroundSector: Spawns a segment with two openings. One of the openings will have a dead end and force the player to swap. +-- mReveal determines whether the dead end should reveal itself from a hidden spot. +function hmcTurnaroundSector(mSide, mReveal) + mReveal = mReveal or false; + + local myThickness = getPerfectThickness(THICKNESS) + local delay = getPerfectDelay(myThickness) * 2.5 + + if (mReveal) then + wallHMStop(mSide, math.random(1, l_getSides()) * getRandomDir()); + else + cWall(mSide) end + for i = 1, getHalfSides() - 1 do + w_wall(mSide + i, myThickness + 3.8 * u_getSpeedMultDM() * delay) + w_wall(mSide + i, -myThickness * 1.5) + end + for i = getHalfSides() + 1, l_getSides() - 1 do + w_wall(mSide + i, myThickness + 3.8 * u_getSpeedMultDM() * delay) + w_wall(mSide + i, -myThickness * 1.5) + end + t_wait(delay) +end - t_wait(getPerfectDelayDM(THICKNESS) * 6.1) +-------------------------------------------------------- +-- BEGINNING OF HMP FUNCTIONS (Hue Modified Patterns) -- +-------------------------------------------------------- + +-- A patternized hmcSimpleBarrage, with a randomized side placement and optional speed randomization set by the user +-- mMin (OPTIONAL): The lowest speed the barrage can travel +-- mMax (OPTIONAL): The highest speed the barrage can travel +function hmpBarrage(mMin, mMax) + mMin = mMin or 1; + mMax = mMax or l_getSides() - 1; + hmcSimpleBarrage(getRandomSide(), math.random(mMin, mMax) * getRandomDir()) + t_wait(getPerfectDelay(THICKNESS) * 8.1) end -function hmcDefBarrageSpiral() - hmcSimpleBarrageSpiral(u_rndInt(1, 3), 1, 1, u_rndInt(5, 15) / 10.0 * getRandomDir(), 0) +-- A patternized hmcBarrageStop, with a randomized side placement and optional offset randomization set by the user +-- mOffsetMin (OPTIONAL): The lowest offset a barrage can be placed +-- mOffsetMax (OPTIONAL): The highest offset a barrage can be placed +function hmpBarrageStop(mOffsetMin, mOffsetMax) + mOffsetMin = mOffsetMin or 2; + mOffsetMax = mOffsetMax or l_getSides() * 2; + hmcBarrageStop(getRandomSide(), math.random(mOffsetMin, mOffsetMax) * getRandomDir()); + t_wait(getPerfectDelay(THICKNESS) * 9) end -function hmcDefBarrageSpiralRnd() - hmcSimpleBarrageSpiralRnd(u_rndInt(1, 3), 1, u_rndInt(5, 15) / 10.0 * getRandomDir(), 0) +-- A patternized hmcSimpleSpinner, with a randomized side placement and optional speed randomization set by the user +-- mMin (OPTIONAL): The lowest speed the barrage can travel +-- mMax (OPTIONAL): The highest speed the barrage can travel +function hmpSpinner(mMin, mMax) + mMin = mMin or 1; + mMax = mMax or l_getSides() - 1; + hmcSimpleSpinner(getRandomSide(), math.random(mMin, mMax) * getRandomDir()) + t_wait(getPerfectDelay(THICKNESS) * 9.1) end -function hmcDefBarrageSpiralFast() - hmcSimpleBarrageSpiral(u_rndInt(1, 3), 0.8, 1, u_rndInt(5, 15) / 10.0 * getRandomDir(), 0) +-- hmpTwirl: A patternized hmcSimpleTwirl that has a shorter delay between barrages and curve and curveAdd must go in the same direction. +function hmpTwirl(mTimes, mCurve, mCurveAdd) + local startSide = getRandomSide() + local delay = getPerfectDelay(THICKNESS) * 4 + local dir = getRandomDir() + local currentCurve = mCurve * dir + + for i = 0, mTimes do + hmcSimpleBarrageS(startSide, currentCurve) + currentCurve = currentCurve + mCurveAdd * dir + t_wait(delay) + end + t_wait(delay * 1.5) end -function hmcDefBarrageSpiralSpin() - hmcSimpleBarrageSpiralStatic(u_rndInt(7, 14), 0.25, 1, u_rndInt(5, 18) / 10.0 * getRandomDir(), 2) +-- A patternized hmcSimpleBarrageSpiral, with a randomized side placement and optional speed randomization set by the user +-- mTimes (OPTIONAL): The amount of barrages to spawn +-- mMinCurve (OPTIONAL): The lowest speed the pattern can travel +-- mMaxCurve (OPTIONAL): The highest speed the pattern can travel +function hmpBarrageSpiral(mTimes, mMinCurve, mMaxCurve) + mTimes = mTimes or math.random(1, 3); + mMinCurve = mMinCurve or 1; + mMaxCurve = mMaxCurve or l_getSides() - 1; + hmcSimpleBarrageSpiral(mTimes, 1, 1, math.random(mMinCurve, mMaxCurve) * getRandomDir(), 0) end -function hmcDefBarrageInv() - t_wait(getPerfectDelayDM(THICKNESS) * 2.0) - local delay = getPerfectDelay(THICKNESS) * 5.6 +-- An hmcSimpleBarrageSpiral, but it uses hmcBarrageStop instead of normal hmcBarrages. +-- mTimes (OPTIONAL): The amount of barrages to spawn +-- mMinCurve (OPTIONAL): The lowest offset each barrage can travel +-- mMaxCurve (OPTIONAL): The highest offset each barrage can travel +function hmpBarrageSpiralStop(mTimes, mMinCurve, mMaxCurve) + mTimes = mTimes or math.random(1, 3); + mMinCurve = mMinCurve or 1; + mMaxCurve = mMaxCurve or l_getSides() - 1; + local side = getRandomSide() - local rndspd = u_rndInt(10, 20) / 10.0 - local oppositeSide = getRandomSide() + getHalfSides() + local loopDir = getRandomDir() + local delay = getPerfectDelay(THICKNESS) * 6.2 - hmcSimpleBarrageSNeigh(side, rndspd * getRandomDir(), 0) - t_wait(delay) + for i = 0, mTimes do + hmcBarrageStop(side + i * loopDir, math.random(mMinCurve, mMaxCurve) * getRandomDir()); + t_wait(delay) + if(l_getSides() < 6) then t_wait(delay * 0.7) end + end - hmcSimpleBarrageSNeigh(oppositeSide, rndspd * getRandomDir(), 0) - t_wait(delay) + t_wait(getPerfectDelay(THICKNESS) * 6.1) end -function hmcDefAccelBarrage() - t_wait(getPerfectDelayDM(THICKNESS) * 1.5) - local c = u_rndInt(50, 100) / 1000.0 * getRandomDir() - local minimum = u_rndInt(5, 35) / 10.0 * -1 - local maximum = u_rndInt(5, 35) / 10.0 - hmcBarrage(0, c, minimum, maximum, true) - t_wait(getPerfectDelayDM(THICKNESS) * 6.1) +-- hmpBarrageSpiralSpin: A subset of hmcSimpleBarrageSpiral to spawn barrages with 25% the normal delay +function hmpBarrageSpiralSpin(mTimes, mMinCurve, mMaxCurve) + mTimes = mTimes or math.random(7, 14); + mMinCurve = mMinCurve or 1; + mMaxCurve = mMaxCurve or l_getSides() - 1; + hmcSimpleBarrageSpiralStatic(mTimes, 0.25, 1, math.random(mMinCurve, mMaxCurve) * getRandomDir(), 2) end -function hmcDefAccelBarrageDouble() - t_wait(getPerfectDelayDM(THICKNESS) * 1.5) - local c = u_rndInt(50, 100) / 1000.0 * getRandomDir() - local minimum = u_rndInt(5, 35) / 10.0 * -1 - local maximum = u_rndInt(5, 35) / 10.0 - hmcBarrage(0, c, minimum, maximum, true) - t_wait(getPerfectDelayDM(THICKNESS) * 2.1) +-- hmpDefAccelBarrage: A barrage that is randomized in curving speed, minimum, and maximum accelerations. +function hmpDefAccelBarrage() + t_wait(getPerfectDelay(THICKNESS) * 1.5) + local c = math.random(50, 100) / 1000.0 * getRandomDir() + local minimum = math.random(5, 35) / 10.0 * -1 + local maximum = math.random(5, 35) / 10.0 hmcBarrage(0, c, minimum, maximum, true) - t_wait(getPerfectDelayDM(THICKNESS) * 6.1) + t_wait(getPerfectDelay(THICKNESS) * 6.1) +end + +-- hmpTunnelDynamic: A segment of hmcTunnelDynamics in a single pattern, written to avoid impossible outcomes. +function hmpTunnelDynamic(mTimes, mLower, mUpper) + mLower = mLower or 2 + mUpper = mUpper or l_getSides(); + local barrageSide, prevSide, tunnelSide; + for i = 1, mTimes + 1 do + if (prevSide ~= nil) then + repeat barrageSide = getRandomSide() until barrageSide ~= prevSide; + else + barrageSide = getRandomSide(); + end + repeat tunnelSide = getRandomSide() until tunnelSide ~= barrageSide; + if (i < mTimes + 1) then + hmcTunnelDynamic(barrageSide, tunnelSide, math.random(mLower, mUpper) * getRandomDir()); + else + cBarrage(barrageSide); + end + prevSide = tunnelSide; + end + + t_wait(getPerfectDelay(getPerfectThickness(THICKNESS)) * 5.6) end -function hmcDefSpinnerSpiral() - t_wait(getPerfectDelayDM(THICKNESS) * 1.5) +-- hmpStripeSnakeBarrage: Given a specific offset, this pattern creates repetitions of a barrage with a given offset to drift from. +function hmpStripeSnakeBarrage(mSide, mRepetitions, mLower, mUpper) + mLower = mLower or l_getSides() + mUpper = mUpper or l_getSides() * 2 + local offset = math.random(mLower, mUpper) * getRandomDir() + local delay = getPerfectDelay(THICKNESS) + for r = 0, mRepetitions do + for i = 0, l_getSides() - 2 do + wallHMStop(mSide + i + 1, offset) + end + t_wait(delay) + end + t_wait(delay * 7) +end + +-- hmpStripeSnakeBarrage: Given a specific offset, this pattern creates repetitions of a single alt barrage with a given offset to drift from. +function hmpStripeSnakeAltBarrage(mRepetitions, mStep, mLower, mUpper) + mLower = mLower or l_getSides() + mUpper = mUpper or l_getSides() * 2 local side = getRandomSide() - local c = u_rndInt(10, 20) / 10.0 * getRandomDir() + local offset = math.random(mLower, mUpper) * getRandomDir() + local delay = getPerfectDelay(THICKNESS) + for r = 0, mRepetitions do + for i = 0, l_getSides(), mStep do + wallHMStop(side + i, offset) + end + t_wait(delay) + end + t_wait(delay * 7) +end - t_wait(getPerfectDelayDM(THICKNESS) * 3.1) +-- hmpGrowTunnel: A tunnel pattern where the barrages consist of hmcGrowBarrages instead of regular ones. +function hmpGrowTunnel(mTimes) + local myThickness = getPerfectThickness(THICKNESS) + local delay = getPerfectDelay(myThickness) * 4.5 + local startSide = getRandomSide() + local barrageSide, offset; - for i = 0, u_rndInt(4, 8) do - hmcSimpleSpinnerS(side, c) - t_wait(getPerfectDelayDM(THICKNESS) * 1.15) + for i = 0, mTimes do + repeat barrageSide = getRandomSide() until barrageSide ~= startSide + offset = (l_getSides() - (barrageSide - startSide + 1)) % (l_getSides()) + hmcGrowBarrage(barrageSide, offset) + w_wall(startSide, -myThickness) -- Wall to prevent visual artifacts + if i < mTimes then + w_wall(startSide, myThickness + 5 * u_getSpeedMultDM() * delay) + else + w_wall(startSide, myThickness) + end + t_wait(delay) end - - t_wait(getPerfectDelayDM(THICKNESS) * 5) end -function getRndMinDM(mNum) - return u_rndInt(math.floor(mNum - (u_getDifficultyMult() ^ 3)), math.ceil(mNum)) -end +-- hmpAssembleTunnel: A tunnel pattern where the barrages consist of hmcAssembleBarrages instead of regular ones. +function hmpAssembleTunnel(mTimes, mLower, mUpper) + mLower = mLower or 1 + mUpper = mUpper or getHalfSides() -function getRndMaxDM(mNum) - return u_rndInt(math.floor(mNum), math.ceil(mNum + (u_getDifficultyMult() ^ 2.25))) + local myThickness = getPerfectThickness(THICKNESS) + local delay = getPerfectDelay(myThickness) * 4.5 + local startSide = getRandomSide() + local barrageSide; + + for i = 0, mTimes do + repeat barrageSide = getRandomSide() until barrageSide ~= startSide + hmcAssembleBarrage(barrageSide, mLower, mUpper) + w_wall(startSide, -myThickness) -- Wall to prevent visual artifacts + if i < mTimes then + w_wall(startSide, myThickness + 5 * u_getSpeedMultDM() * delay) + else + w_wall(startSide, myThickness) + end + t_wait(delay) + end end -function hmcDefSpinnerSpiralAcc() - t_wait(getPerfectDelayDM(THICKNESS) * 2.1) - t_wait(getPerfectDelayDM(THICKNESS) * 2.1) - local side = getRandomSide() +-- hmpChaserAltBarrage: pAltBarrage, but a huge tunnel curving wall moves throughout the pattern, restricting where the player moves. +function hmpChaserAltBarrage(mTimes, mStep, mSpeed, mTail) + mSpeed = mSpeed or math.random(getHalfSides(), l_getSides()); + mTail = mTail or false -- If true, the tunnel extends past the last alt barrage. Done for connectivity purposes + mTail = mTail and 1 or 0 -- Convert boolean to integer + local myThickness = getPerfectThickness(THICKNESS) + local delay = getPerfectDelay(myThickness) * 3 + + -- Create the tunnel wall (chaser) + -- For the side, we want to make sure that the chaser is on one of the first alt barrage walls (which is odd numbers) + -- This makes the pattern slightly easier to predict and doesn't close out one of the openings. + wallHMCurveAcc((getRandomSide() * 2 + 1 - mSpeed), mSpeed * getRandomDir(), 0, 0, 0, false, (THICKNESS * (mTimes - 1 + mTail)) + delay * 12.5 * (u_getDifficultyMult() ^ .65) * (mTimes - 1 + mTail)); + for i = 1, mTimes do + cAltBarrage(i, mStep) + t_wait(delay) + end +end - local acc = u_rndInt(getRndMinDM(50), getRndMaxDM(100)) / 1000.0 * getRandomDir() - local minimum = u_rndInt(getRndMinDM(12), getRndMaxDM(28)) / 10.0 * -1 - local maximum = u_rndInt(getRndMinDM(12), getRndMaxDM(28)) / 10.0 +-- hmpAlternate: A simple pattern involving one spinning barrage and one normal barrage. +function hmpAlternate() + local curveSide = getRandomSide(); + local force = math.random(2, 4); + hmcSimpleBarrageS(curveSide, force * getRandomDir()) + t_wait(getPerfectDelay(THICKNESS) * 10) + cBarrage(getRandomSide()); + t_wait(getPerfectDelay(THICKNESS) * 10) +end +-- hmpTunnelSpinner: A typical tunnel pattern that contains spinners (curving alt barrages) +function hmpTunnelSpinner(mTimes, mLower, mUpper) + mLower = mLower or 1 + mUpper = mUpper or getHalfSides() - t_wait(getPerfectDelayDM(THICKNESS) * 3.1) + local myThickness = getPerfectThickness(THICKNESS) + local delay = getPerfectDelay(myThickness) * 4.5 + local startSide = getRandomSide() - for i = 0, u_rndInt(4, 8) do - hmcSimpleSpinnerSAcc(side, 0, acc, minimum, maximum, true) - t_wait(getPerfectDelay(THICKNESS) * 0.8) + for i = 0, mTimes do + hmcSimpleSpinner(getRandomSide(), math.random(mLower, mUpper) * getRandomDir()) + w_wall(startSide, -myThickness) -- Wall to prevent visual artifacts + if i < mTimes then + w_wall(startSide, myThickness + 5 * u_getSpeedMultDM() * delay) + else + w_wall(startSide, myThickness) + end + t_wait(delay) end +end - t_wait(getPerfectDelayDM(THICKNESS) * 5.3) +-- hmpSwarm: A pattern that sends a flurry of single curving walls segments for the player to avoid. +-- The pattern takes in 3 parameters to allow which walls should be contained in the swarm or not. +-- An optional delay parameter can be supplied to lower or raise the delay between segments +function hmpSwarm(mLower, mUpper, mDecel, mConstant, mAccel, mDelayMult) + mDecel = mDecel or true + mConstant = mConstant or false + mAccel = mAccel or false + mDelayMult = mDelayMult or 1 + local delay = getPerfectDelay(getPerfectThickness(THICKNESS)) * 2 * mDelayMult + + -- Constuct wall pool + local wall_pool = {} + if mDecel then + wall_pool[#wall_pool + 1] = "d" + end + if mConstant then + wall_pool[#wall_pool + 1] = "c" + end + if mAccel then + wall_pool[#wall_pool + 1] = "a" + end + -- If all patterns are false, abruptly end the function + if (#wall_pool == 0) then + return + end + for i = 1, 10 do + local option = wall_pool[math.random(1, #wall_pool)] + local offset = math.random(mLower, mUpper) * getRandomDir() + if (option == "d") then + wallHMStop(getRandomSide(), offset) + elseif (option == "c") then + wallHMCurveAcc(getRandomSide(), offset, 0, 0, 0, false) + elseif (option == "a") then + local c = math.random(100, 300) / 500.0 * getRandomDir() + wallHMCurveAcc(getRandomSide(), 0, c, -offset, offset, true) + end + t_wait(delay) + end end diff --git a/_RELEASE/Packs/base/Scripts/nextpatterns.lua b/_RELEASE/Packs/base/Scripts/nextpatterns.lua index 2065de09..31741dc0 100644 --- a/_RELEASE/Packs/base/Scripts/nextpatterns.lua +++ b/_RELEASE/Packs/base/Scripts/nextpatterns.lua @@ -3,11 +3,47 @@ u_execScript("commonpatterns.lua") u_execScript("utils.lua") u_execScript("alternativepatterns.lua") +-- getPerfectAccelDM: Returns a constant that is used to perfectly adjust wallAcc acceleration to speed and difficulty multiplier +function getPerfectAccelDM() + local requiredDecel = (u_getSpeedMultDM() ^ 2.02); + local diffAdjust = (u_getDifficultyMult() ^ 0.65); + if (u_getDifficultyMult() < 1) then + if (u_getDifficultyMult() < 0.5) then + diffAdjust = 1.2 * (0.256289 * math.sin(u_getDifficultyMult()) - 36.5669 * math.cos(u_getDifficultyMult()) + 36.5674); + else + diffAdjust = 1 - (-2 * u_getDifficultyMult() + 2) ^ 2 / 2.7; + end + end + return diffAdjust * requiredDecel +end + +-- wallSAdj: Returns a wall with common THICKNESS and multiplies the speed by mAdj function wallSAdj(mSide, mAdj) w_wallAdj(mSide, THICKNESS, mAdj) end -function wallSAcc(mSide, mAdj, mAcc, mMinSpd, mMaxSpd) w_wallAcc(mSide, THICKNESS, mAdj, mAcc * (u_getDifficultyMult()), mMinSpd, mMaxSpd) end +-- wallSAcc: Returns a wallAcc perfectly adjusted to speed and difficulty multiplier +function wallSAcc(mSide, mAdj, mAcc, mMinSpd, mMaxSpd) + local speedMult = u_getSpeedMultDM() + local adjust = getPerfectAccelDM() + w_wallAcc(mSide, THICKNESS, mAdj * speedMult, mAcc * adjust, mMinSpd, mMaxSpd) +end + +-- wallSHAcc: Creates a wallHModSpeedData adjusted the perfect acceleration adjustment +function wallSHAcc(mHMod, mSide, mAdj, mAcc, mMinSpd, mMaxSpd, mPingPong) + local speedMult = u_getSpeedMultDM() + local adjust = getPerfectAccelDM() + w_wallHModSpeedData(mHMod, mSide, THICKNESS, mAdj * speedMult, mAcc * adjust, mMinSpd, mMaxSpd, mPingPong) +end + +----------------------------------------- +-- TRAP PATTERN FUNCTIONS (POLYHEDRUG) -- +----------------------------------------- +-- These are patterns that have openings, but after a specified amount of delay, a faster wall spawns to speed through the gap. +-- This tells the player to not immediately orient their cursor to the gap, and they must wait before they can. + +-- A trap version of a barrage +-- mSide: The side the pattern will spawn on function pTrapBarrage(mSide) - local delay = getPerfectDelayDM(THICKNESS) * 3.7 + local delay = getPerfectDelay(THICKNESS) * 3.7 cBarrage(mSide) t_wait(delay * 3) @@ -16,9 +52,11 @@ function pTrapBarrage(mSide) t_wait(delay * 2.5) end +-- A trap barrage with two gaps, both on opposing sides of each other +-- mSide: The side the pattern will spawn on function pTrapBarrageDouble(mSide) - local delay = getPerfectDelayDM(THICKNESS) * 3.7 - local side2 = mSide + getHalfSides() + local delay = getPerfectDelay(THICKNESS) * 3.7 + local side2 = mSide + getHalfSides(); for i = 0, l_getSides() - 1 do local currentSide = mSide + i @@ -32,8 +70,10 @@ function pTrapBarrageDouble(mSide) t_wait(delay * 2.5) end +-- pTrapBarrage, but the whole barrage is speeding instead of the "trap". +-- mSide: The side the pattern will spawn on function pTrapBarrageInverse(mSide) - local delay = getPerfectDelayDM(THICKNESS) * 3.7 + local delay = getPerfectDelay(THICKNESS) * 3.7 cWall(mSide) t_wait(delay * 3) @@ -46,8 +86,10 @@ function pTrapBarrageInverse(mSide) t_wait(delay * 2.5) end +-- A trap version of an alt barrage +-- mSide: The side the pattern will spawn on function pTrapBarrageAlt(mSide) - local delay = getPerfectDelayDM(THICKNESS) * 3.7 + local delay = getPerfectDelay(THICKNESS) * 3.7 for i = 0, l_getSides() - 1 do local currentSide = mSide + i @@ -64,101 +106,267 @@ function pTrapBarrageAlt(mSide) t_wait(delay * 2.5) end -function pTrapSpiral(mSide) - local delay = getPerfectDelayDM(THICKNESS) * 3.7 - local loopDir = getRandomDir() +-------------------------------------------- +-- RANDOM CONTROL PATTERNS (Incongruence) -- +-------------------------------------------- - if(l_getSides() < 6) then delay = delay + 4 end +-- Spawns a barrage with two opposing holes instead of one +function pRCBarrageDouble() + local currentSides = l_getSides() + local delay = getPerfectDelay(THICKNESS) * 3.7 + local startSide = getRandomSide() - for i = 0, l_getSides() + getHalfSides() do - local currentSide = (mSide + i) * loopDir - for j = 0, getHalfSides() do wallSAdj(currentSide + j, 1.2 + (i / 7.9)) end - t_wait((delay * 0.75) - (i * 0.45) + 3) + for i = 0, currentSides - 2 do + local currentSide = startSide + i + local holeSide = startSide + i + (currentSides / 2) + if(i ~= holeSide) then cWall(currentSide) end end - t_wait(delay * 2.5) end -function pRCBarrage() +-- Summons a series of barrages, but with the side count ascending, adding one new side for each barrage summoned. +-- This as a result can cause the barrages to either spiral or stay still, depending on the side it spawns on +-- This pattern can be unpredictable in it's nature. +function pRCAscendBarrage(mSide, mStartSides, mEndSides) + local delay = getPerfectDelay(THICKNESS) * 5.6 + for i = mStartSides, mEndSides do + t_eval([[l_setSides(]]..i..[[)]]) + for r = 0, i - 2 do + cWall(r + mSide) + end + t_wait(delay) + end + t_wait(delay) +end + +-- Summons a series of barrages in randomized positions, with the side count ascending per barrage +-- mStartSides: The starting number of sides for the pattern +-- mEndSides: The ending number of sides for the pattern +function pRCAscendBarrageRandom(mStartSides, mEndSides) + local delay = getPerfectDelay(THICKNESS) * 8.5 + for i = mStartSides, mEndSides do + t_eval([[l_setSides(]]..i..[[)]]) + local offset = math.random(1, i) + for r = 0, i - 2 do + cWall(r + offset) + end + t_wait(delay) + end + t_wait(delay) +end + +-- Summons a series of alt barrages, but the side count randomizes per alt barrage to make the pattern much harder to navigate +-- mStep: How many sides to move per wall spawn. +-- mTimes: How many alt barrage segments to make +-- mLowerSides: The lowest side count to select at random +-- mUpperSides: The highest side count to select at random +function pRCDynamicAltBarrage(mStep, mTimes, mLowerSides, mUpperSides) + local delay = getPerfectDelay(THICKNESS) * 5.6 + local prevSides = 0 + + for r = 0, mTimes do + local sides = math.random(mLowerSides, mUpperSides) + if (sides ~= prevSides) then + t_eval([[l_setSides(]]..sides..[[)]]) + end + prevSides = sides + for i = 0, sides, mStep do + cWall(i + r) + end + t_wait(delay) + end + t_wait(delay) +end + +------------------------------------------- +-- ACCELERATING PATTERNS (ACCELERADIANT) -- +------------------------------------------- + +-- pACBarrageAccelerate: A barrage that will slowly increase in speed over time, accelerating. +function pACBarrageAccelerate() local currentSides = l_getSides() - local delay = getPerfectDelayDM(THICKNESS) * 3.7 - local startSide = u_rndInt(0, 10) + local delay = getPerfectDelay(THICKNESS) * 3.7 + local startSide = getRandomSide() for i = 0, currentSides - 2 do local currentSide = startSide + i - cWall(currentSide) + wallSAcc(currentSide, 2, 0.015, 1, 10) end t_wait(delay * 2.5) end -function pRCBarrageDouble() +-- pACBarrageDecelerate: A barrage that will quickly approach the player and decelerate. +function pACBarrageDecelerate() local currentSides = l_getSides() - local delay = getPerfectDelayDM(THICKNESS) * 3.7 - local startSide = u_rndInt(0, 10) + local delay = getPerfectDelay(THICKNESS) * 3.7 + local startSide = getRandomSide() for i = 0, currentSides - 2 do local currentSide = startSide + i - local holeSide = startSide + i + (currentSides / 2) - if(i ~= holeSide) then cWall(currentSide) end + wallSAcc(currentSide, 10, -0.22, 0.6, 10) end - t_wait(delay * 2.5) + t_wait(delay * 3.5) end -function pRCBarrageSpin() +-- pACBarrageDeception: A barrage that never interacts with the player, going in and out of the view, tricking the player. +function pACBarrageDeception(mAdjMult, mAccMult) + mAdjMult = mAdjMult or 1; + mAccMult = mAccMult or 1; + local currentSides = l_getSides() - local delay = getPerfectDelayDM(THICKNESS) * 3.7 - local startSide = u_rndInt(0, 10) - local loopDir = getRandomDir() + local delay = getPerfectDelay(THICKNESS) * 3.7 + local startSide = getRandomSide() - for j = 0, 2 do - for i = 0, currentSides - 2 do - local currentSide = startSide + i - cWall(currentSide + (j * loopDir)) - end - t_wait(delay + 1) + for i = 0, currentSides - 2 do + local currentSide = startSide + i + wallSAcc(currentSide, 2.8 * mAdjMult, -0.05 * mAccMult, -2, 5) end - t_wait(delay * 2.5) + t_wait(delay * 4) end -function pACBarrage() +-- pACInverseBarrage: Two decelerating barrages, one slower than the other to create an inverse barrage +function pACInverseBarrage() local currentSides = l_getSides() - local delay = getPerfectDelayDM(THICKNESS) * 3.7 - local startSide = u_rndInt(0, 10) + local delay = getPerfectDelay(THICKNESS) * 3.7 + local startSide = getRandomSide() for i = 0, currentSides - 2 do local currentSide = startSide + i - wallSAcc(currentSide, 9 + u_rndInt(0, 1), -1.1, 1, 12) + wallSAcc(currentSide, 10, -0.21, 0.6, 10) end - t_wait(delay * 2.5) + for i = 0, currentSides - 2 do + local currentSide = startSide + getHalfSides() + i + wallSAcc(currentSide, 10, -0.28, 0.6, 10) + end + t_wait(delay * 6.5) end +-- pACBarrageMulti: A decelerating barrage, followed by a trail of accelerating barrages for visual effect function pACBarrageMulti() local currentSides = l_getSides() - local delay = getPerfectDelayDM(THICKNESS) * 3.7 - local startSide = u_rndInt(0, 10) + local delay = getPerfectDelay(THICKNESS) * 3.7 + local startSide = math.random(0, 10) for i = 0, currentSides - 2 do local currentSide = startSide + i - wallSAcc(currentSide, 10, -1.09, 0.31, 10) - wallSAcc(currentSide, 0, 0.05, 0, 4.0) - wallSAcc(currentSide, 0, 0.09, 0, 4.0) - wallSAcc(currentSide, 0, 0.12, 0, 4.0) + wallSAcc(currentSide, 10, -0.21, 0.31, 10) + wallSAcc(currentSide, 0, 0.01, 0, 2.0) + wallSAcc(currentSide, 0, 0.02, 0, 2.0) + wallSAcc(currentSide, 0, 0.025, 0, 2.0) end t_wait(delay * 8) end -function pACBarrageMultiAltDir() +-- pACSpiral: A pSpiral that decelerates, followed by an accelerating barrage similar to pACBarrageMulti +function pACSpiral() local currentSides = l_getSides() - local delay = getPerfectDelayDM(THICKNESS) * 4 - local mdiff = 1 + math.abs(1 - u_getDifficultyMult()) - local startSide = u_rndInt(0, 10) + local oldThickness = THICKNESS; + THICKNESS = getPerfectThickness(THICKNESS); + local delay = getPerfectDelay(THICKNESS) * 3 + local startSide = math.random(0, 10) local loopDir = getRandomDir() for i = 0, currentSides + getHalfSides() do local currentSide = startSide + i * loopDir - wallSAcc(currentSide, 10, -1.095, 0.40, 10) - t_wait((delay / 2.21) * (mdiff * 1.29)) + wallSAcc(currentSide, 10, -0.2, 0.40, 10) + t_wait((delay / 2.1)) wallSAcc(currentSide + (getHalfSides() * loopDir), 0, 0.128, 0, 1.4) end + THICKNESS = oldThickness; + t_wait(delay * 3) +end + +-- A series of alt barrages that decelerate. +-- mTimes: The number of alt barrage series to create +function pACAltBarrage(mTimes) + local currentSides = l_getSides() + local delay = getPerfectDelay(THICKNESS) * 3.7 + + for t = 1, mTimes do + for i = 1, currentSides, 2 do + wallSAcc(i, 10, -0.21, 0.6, 10) + end + t_wait(delay * 1.6); + for i = 0, currentSides - 1, 2 do + wallSAcc(i, 10, -0.21, 0.6, 10) + end + t_wait(delay * 1.6); + end + + t_wait(delay * 3.5) +end + +-- pACAltBarrageMulti: Alt Barrage with similar behavior to pACBarrageMulti +function pACAltBarrageMulti() + local currentSides = l_getSides() + local delay = getPerfectDelay(THICKNESS) * 3.7 + local offset = math.random(0, 1); + + for i = 1, currentSides, 2 do + wallSAcc(i + offset, 10, -0.2, 0.5, 10) + wallSAcc(i + offset, 0, 0.01, 0, 4.0) + wallSAcc(i + offset, 0, 0.015, 0, 4.0) + wallSAcc(i + offset, 0, 0.02, 0, 4.0) + end + t_wait(delay * 8) end + +-- Reveals randomly positioned Alt Barrages for the player to navigate +-- mTimes: The number of alt barrages to create +function pACAltBarrageReveal(mTimes) + local currentSides = l_getSides() + local delay = getPerfectDelay(THICKNESS) * 3.7 + local chooser = 0; + + for t = 1, mTimes do + chooser = math.random(0, 1); + for i = 1, currentSides do + if (i % 2 == chooser) then + wallSAcc(i, 10, -0.21, 0.6, 10) + else + wallSAcc(i, 10, -0.21, -2, 10) + end + end + t_wait(delay * 1.6); + end + + t_wait(delay * 3.5) +end + +-- A tunnel pattern that reveals random barrages for the player to navigate +-- mTimes: The number of tunnel segments to create +function pACTunnelReveal(mTimes) + local currentSides = l_getSides() + local oldThickness = THICKNESS + local myThickness = getPerfectThickness(THICKNESS); + local delay = getPerfectDelay(myThickness) * 5 + local startSide = getRandomSide() + local loopDir = getRandomDir() + + for t = 0, mTimes do + THICKNESS = myThickness + 5 * u_getSpeedMultDM() * delay + if t < mTimes then + wallSAcc(startSide, 10, -0.22, 0.6, 10) + end + + THICKNESS = oldThickness + + local revealSide = math.random(0, currentSides - 1) + if (revealSide == startSide) then + revealSide = (revealSide + getRandomDir()) % currentSides; + end + for i = 0, currentSides - 1 do + if (i == revealSide) then + wallSAcc(i, 10, -0.22, -2, 10) + else + wallSAcc(i, 10, -0.22, 0.6, 10) + end + end + t_wait(delay) + loopDir = loopDir * -1 + end + + THICKNESS = oldThickness; + t_wait(delay) +end diff --git a/_RELEASE/Packs/cube/Scripts/Levels/apeirogon.lua b/_RELEASE/Packs/cube/Scripts/Levels/apeirogon.lua index 94dce758..754d37da 100644 --- a/_RELEASE/Packs/cube/Scripts/Levels/apeirogon.lua +++ b/_RELEASE/Packs/cube/Scripts/Levels/apeirogon.lua @@ -20,7 +20,7 @@ function addPattern(mKey) mKey = 7 end - if mKey == 0 then pAltBarrage(u_rndInt(2, 3), 2) + if mKey == 0 then pAltBarrage(u_rndInt(3, 4), 2) elseif mKey == 1 then pBarrageSpiral(3, 0.6, 1) elseif mKey == 2 then pInverseBarrage(0) elseif mKey == 3 then pTunnel(u_rndInt(1, 3)) diff --git a/_RELEASE/Packs/cube/Scripts/Levels/commando.lua b/_RELEASE/Packs/cube/Scripts/Levels/commando.lua index bf3623fa..f9211b67 100644 --- a/_RELEASE/Packs/cube/Scripts/Levels/commando.lua +++ b/_RELEASE/Packs/cube/Scripts/Levels/commando.lua @@ -18,7 +18,7 @@ end -- onStep should contain your pattern spawning logic function onStep() rWallEx(getRandomSide(), extra) - t_wait(getPerfectDelayDM(THICKNESS) * 6) + t_wait(getPerfectDelay(THICKNESS) * 6) end -- onInit is an hardcoded function that is called when the level is first loaded diff --git a/_RELEASE/Packs/cube/Scripts/Levels/euclideanpc.lua b/_RELEASE/Packs/cube/Scripts/Levels/euclideanpc.lua index 44e7f134..35d9bfb0 100644 --- a/_RELEASE/Packs/cube/Scripts/Levels/euclideanpc.lua +++ b/_RELEASE/Packs/cube/Scripts/Levels/euclideanpc.lua @@ -5,7 +5,7 @@ u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "commonpatterns. -- this function adds a pattern to the timeline based on a key function addPattern(mKey) - if mKey == 0 then pAltBarrage(u_rndInt(2, 4), 2) + if mKey == 0 then pAltBarrage(u_rndInt(3, 5), 2) elseif mKey == 1 then pMirrorSpiral(u_rndInt(3, 6), 0) elseif mKey == 2 then pBarrageSpiral(u_rndInt(0, 3), 1, 1) elseif mKey == 3 then pBarrageSpiral(u_rndInt(0, 2), 1.2, 2) diff --git a/_RELEASE/Packs/cube/Scripts/Levels/flatteringshape.lua b/_RELEASE/Packs/cube/Scripts/Levels/flatteringshape.lua index 239d81fc..963d744b 100644 --- a/_RELEASE/Packs/cube/Scripts/Levels/flatteringshape.lua +++ b/_RELEASE/Packs/cube/Scripts/Levels/flatteringshape.lua @@ -29,7 +29,7 @@ function addPattern(mKey) mKey = 5 end - if mKey == 0 then pAltBarrage(u_rndInt(2, 4), 2) + if mKey == 0 then pAltBarrage(u_rndInt(3, 5), 2) elseif mKey == 1 then adjustThicknessForLowDM(2) pMirrorSpiral(u_rndInt(3, 6), 0) diff --git a/_RELEASE/Packs/cube/Scripts/Levels/goldenratio.lua b/_RELEASE/Packs/cube/Scripts/Levels/goldenratio.lua index ecd7f543..56935886 100644 --- a/_RELEASE/Packs/cube/Scripts/Levels/goldenratio.lua +++ b/_RELEASE/Packs/cube/Scripts/Levels/goldenratio.lua @@ -19,8 +19,8 @@ function addPattern(mKey) local ot = THICKNESS local rd = getRandomDir() THICKNESS = THICKNESS * 0.85 - pSpiralDir(rd, l_getSides(), 1) - pSpiralDir(rd * -1, l_getSides(), 1) + pSpiral(l_getSides(), 1, rd) + pSpiral(l_getSides(), 1, rd * -1) THICKNESS = ot end diff --git a/_RELEASE/Packs/cube/Scripts/Levels/labyrinth.lua b/_RELEASE/Packs/cube/Scripts/Levels/labyrinth.lua index 581efda2..cffab690 100644 --- a/_RELEASE/Packs/cube/Scripts/Levels/labyrinth.lua +++ b/_RELEASE/Packs/cube/Scripts/Levels/labyrinth.lua @@ -18,7 +18,7 @@ end -- onStep should contain your pattern spawning logic function onStep() cBarrage(getRandomSide()) - t_wait(getPerfectDelayDM(THICKNESS) * 6.55) + t_wait(getPerfectDelay(THICKNESS) * 6.55) end -- onInit is an hardcoded function that is called when the level is first loaded diff --git a/_RELEASE/Packs/cube/Scripts/Levels/pointless.lua b/_RELEASE/Packs/cube/Scripts/Levels/pointless.lua index c1fb409d..336199b8 100644 --- a/_RELEASE/Packs/cube/Scripts/Levels/pointless.lua +++ b/_RELEASE/Packs/cube/Scripts/Levels/pointless.lua @@ -24,7 +24,7 @@ function addPattern(mKey) mKey = 5 end - if mKey == 0 then pAltBarrage(u_rndInt(2, 4), 2) + if mKey == 0 then pAltBarrage(u_rndInt(3, 5), 2) elseif mKey == 1 then adjustThicknessForLowDM(3) pMirrorSpiral(u_rndInt(2, 5), getHalfSides() - 3) @@ -38,6 +38,11 @@ function addPattern(mKey) elseif mKey == 5 then adjustThicknessForLowDM(3) pSpiral(l_getSides() * u_rndInt(1, 2), 0) + + if l_getSpeedMult() >= 4.25 then + t_wait(getPerfectDelayDM(THICKNESS) * 0.5) + end + restoreThicknessForLowDM() end end @@ -124,7 +129,7 @@ end -- onIncrement is an hardcoded function that is called when the level difficulty is incremented function onIncrement() - enableSwapIfSpeedGEThan(4.5); + enableSwapIfSpeedGEThan(4.25); end -- onUnload is an hardcoded function that is called when the level is closed/restarted diff --git a/_RELEASE/Packs/cube/Scripts/Levels/seconddimension.lua b/_RELEASE/Packs/cube/Scripts/Levels/seconddimension.lua index 865b9770..e69b6615 100644 --- a/_RELEASE/Packs/cube/Scripts/Levels/seconddimension.lua +++ b/_RELEASE/Packs/cube/Scripts/Levels/seconddimension.lua @@ -15,7 +15,7 @@ function addPattern(mKey) mKey = 5 end - if mKey == 0 then pAltBarrage(u_rndInt(2, 4), 2) + if mKey == 0 then pAltBarrage(u_rndInt(3, 5), 2) elseif mKey == 1 then pMirrorSpiral(u_rndInt(3, 6), 0) elseif mKey == 2 then pBarrageSpiral(u_rndInt(0, 3), 1, 1) elseif mKey == 3 then pBarrageSpiral(u_rndInt(0, 2), 1.2, 2) diff --git a/_RELEASE/Packs/cube/Styles/labyrinth.json b/_RELEASE/Packs/cube/Styles/labyrinth.json index 06e654e3..db0a6f9b 100644 --- a/_RELEASE/Packs/cube/Styles/labyrinth.json +++ b/_RELEASE/Packs/cube/Styles/labyrinth.json @@ -26,9 +26,9 @@ // Background colors "colors": [ - { "dynamic": false, "dynamic_offset": false, "dynamic_darkness": 1.0, "value": [12, 24, 30, 255], "pulse": [0, 0, 0, 0] }, - { "dynamic": false, "dynamic_offset": false, "dynamic_darkness": 1.0, "value": [73, 31, 92, 255], "pulse": [0, 0, -30, 0] }, - { "dynamic": false, "dynamic_offset": false, "dynamic_darkness": 1.0, "value": [25, 25, 25, 255], "pulse": [0, 0, 0, 0] }, - { "dynamic": false, "dynamic_offset": false, "dynamic_darkness": 1.0, "value": [73, 31, 92, 255], "pulse": [0, 0, -30, 0] } + { "dynamic": false, "dynamic_offset": false, "dynamic_darkness": 1.0, "value": [12, 24, 30, 255], "pulse": [0, 0, 0, 0] }, + { "dynamic": false, "dynamic_offset": false, "dynamic_darkness": 1.0, "value": [73, 31, 92, 255], "pulse": [0, 0, -30, 0] }, + { "dynamic": false, "dynamic_offset": false, "dynamic_darkness": 1.0, "value": [25, 25, 25, 255], "pulse": [0, 0, 0, 0] }, + { "dynamic": false, "dynamic_offset": false, "dynamic_darkness": 1.0, "value": [73, 31, 92, 255], "pulse": [0, 0, -30, 0] } ] } diff --git a/_RELEASE/Packs/experimental/Levels/customtimeline.json b/_RELEASE/Packs/experimental/Levels/customtimeline.json new file mode 100644 index 00000000..ccc9e2f5 --- /dev/null +++ b/_RELEASE/Packs/experimental/Levels/customtimeline.json @@ -0,0 +1,12 @@ +{ + "id": "customtimeline", + "name": "customtimeline", + "description": "test", + "author": "vittorio romeo", + "menuPriority": 10, + "selectable": true, + "styleId": "cw", + "musicId": "jackRussel", + "luaFile": "Scripts/Levels/customtimeline.lua", + "difficultyMults": [] +} diff --git a/_RELEASE/Packs/experimental/Scripts/Levels/3D_required.lua b/_RELEASE/Packs/experimental/Scripts/Levels/3D_required.lua index b4e5427e..a61ba2be 100644 --- a/_RELEASE/Packs/experimental/Scripts/Levels/3D_required.lua +++ b/_RELEASE/Packs/experimental/Scripts/Levels/3D_required.lua @@ -5,7 +5,7 @@ u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "commonpatterns. -- this function adds a pattern to the timeline based on a key function addPattern(mKey) - if mKey == 0 then pAltBarrage(u_rndInt(2, 4), 2) + if mKey == 0 then pAltBarrage(u_rndInt(3, 5), 2) elseif mKey == 1 then pMirrorSpiral(u_rndInt(3, 6), 0) elseif mKey == 2 then pBarrageSpiral(u_rndInt(0, 3), 1, 1) elseif mKey == 3 then pBarrageSpiral(u_rndInt(0, 2), 1.2, 2) diff --git a/_RELEASE/Packs/experimental/Scripts/Levels/autotest0.lua b/_RELEASE/Packs/experimental/Scripts/Levels/autotest0.lua index e98c1dca..c74f130b 100644 --- a/_RELEASE/Packs/experimental/Scripts/Levels/autotest0.lua +++ b/_RELEASE/Packs/experimental/Scripts/Levels/autotest0.lua @@ -42,7 +42,7 @@ end -- onStep should contain your pattern spawning logic function onStep() hmcSimpleBarrageSNeigh(getRandomSide(), 3, 4) - t_wait(getPerfectDelayDM(THICKNESS) * 6) + t_wait(getPerfectDelay(THICKNESS) * 6) end -- onIncrement is an hardcoded function that is called when the level difficulty is incremented diff --git a/_RELEASE/Packs/experimental/Scripts/Levels/curvetest.lua b/_RELEASE/Packs/experimental/Scripts/Levels/curvetest.lua index 2fc4de86..017844b4 100644 --- a/_RELEASE/Packs/experimental/Scripts/Levels/curvetest.lua +++ b/_RELEASE/Packs/experimental/Scripts/Levels/curvetest.lua @@ -34,7 +34,7 @@ end function hmcDefSpinner() hmcSimpleSpinner(u_rndInt(10, 45) / 10.0 * getRandomDir()) - t_wait(getPerfectDelayDM(THICKNESS) * 1.2) + t_wait(getPerfectDelay(THICKNESS) * 1.2) end -- onInit is an hardcoded function that is called when the level is first loaded diff --git a/_RELEASE/Packs/experimental/Scripts/Levels/curvetest2.lua b/_RELEASE/Packs/experimental/Scripts/Levels/curvetest2.lua index 73106b8d..71322b0b 100644 --- a/_RELEASE/Packs/experimental/Scripts/Levels/curvetest2.lua +++ b/_RELEASE/Packs/experimental/Scripts/Levels/curvetest2.lua @@ -43,7 +43,7 @@ end -- onStep should contain your pattern spawning logic function onStep() hmcSimpleBarrageSNeigh(getRandomSide(), 3, 0) - t_wait(getPerfectDelayDM(THICKNESS) * 6) + t_wait(getPerfectDelay(THICKNESS) * 6) end diff --git a/_RELEASE/Packs/experimental/Scripts/Levels/customtimeline.lua b/_RELEASE/Packs/experimental/Scripts/Levels/customtimeline.lua new file mode 100644 index 00000000..8aff942e --- /dev/null +++ b/_RELEASE/Packs/experimental/Scripts/Levels/customtimeline.lua @@ -0,0 +1,80 @@ +-- include useful files +u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "utils.lua") +u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "common.lua") +u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "commonpatterns.lua") +u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "nextpatterns.lua") +u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "evolutionpatterns.lua") + +-- onInit is an hardcoded function that is called when the level is first loaded +function onInit() + l_setSpeedMult(2.7) + l_setSpeedInc(0.15) + l_setRotationSpeed(0.1) + l_setRotationSpeedMax(0.4) + l_setRotationSpeedInc(0.035) + l_setDelayMult(1.2) + l_setDelayInc(0.0) + l_setFastSpin(0.0) + l_setSides(6) + l_setSidesMin(6) + l_setSidesMax(6) + l_setIncTime(15) + + l_setPulseMin(77) + l_setPulseMax(95) + l_setPulseSpeed(1.95) + l_setPulseSpeedR(0.51) + l_setPulseDelayMax(13) + + l_setBeatPulseMax(17) + l_setBeatPulseDelayMax(27.8) + + l_setSwapEnabled(true) + l_setTutorialMode(true) +end + +-- onLoad is an hardcoded function that is called when the level is started/restarted +function onLoad() + ct0 = ct_create() + ct1 = ct_create() + ct2 = ct_create() + + ct_eval(ct0, [[u_log("ct0_A")]]) + ct_wait(ct0, 60) + ct_eval(ct0, [[u_log("ct0_B")]]) + + ct_eval(ct1, [[u_log("ct1_A")]]) + ct_wait(ct1, 60) + ct_eval(ct1, [[u_log("ct1_B")]]) + + ct_eval(ct2, [[u_log("ct2_A")]]) + ct_wait(ct2, 60) + ct_eval(ct2, [[u_log("ct2_B")]]) + + ct3 = ct_create() + + ct_eval(ct3, [[u_log("ct3_A")]]) + ct_wait(ct3, 20) + ct_eval(ct3, [[u_log("ct3_AX")]]) + ct_wait(ct3, 20) + ct_eval(ct3, [[u_log("ct3_AY")]]) + ct_wait(ct3, 20) + ct_eval(ct3, [[u_log("ct3_B")]]) +end + +-- onStep is an hardcoded function that is called when the level timeline is empty +-- onStep should contain your pattern spawning logic +function onStep() +end + +-- onIncrement is an hardcoded function that is called when the level difficulty is incremented +function onIncrement() +end + +-- onUnload is an hardcoded function that is called when the level is closed/restarted +function onUnload() +end + +-- onUpdate is an hardcoded function that is called every frame +function onUpdate(mFrameTime) +end diff --git a/_RELEASE/Packs/experimental/Scripts/Levels/cw.lua b/_RELEASE/Packs/experimental/Scripts/Levels/cw.lua index 5284c0d8..42fd83d1 100644 --- a/_RELEASE/Packs/experimental/Scripts/Levels/cw.lua +++ b/_RELEASE/Packs/experimental/Scripts/Levels/cw.lua @@ -7,7 +7,7 @@ u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "evolutionpatter -- this function adds a pattern to the timeline based on a key function addPattern(mKey) - if mKey == 0 then pAltBarrage(u_rndInt(1, 3), 2) + if mKey == 0 then pAltBarrage(u_rndInt(2, 4), 2) elseif mKey == 1 then pMirrorSpiral(u_rndInt(2, 4), 0) elseif mKey == 2 then pBarrageSpiral(u_rndInt(0, 3), 1, 1) elseif mKey == 3 then pBarrageSpiral(u_rndInt(0, 2), 1.2, 2) diff --git a/_RELEASE/Packs/experimental/Scripts/Levels/negative_pulse.lua b/_RELEASE/Packs/experimental/Scripts/Levels/negative_pulse.lua index 757cb324..60da3b48 100644 --- a/_RELEASE/Packs/experimental/Scripts/Levels/negative_pulse.lua +++ b/_RELEASE/Packs/experimental/Scripts/Levels/negative_pulse.lua @@ -5,7 +5,7 @@ u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "commonpatterns. -- this function adds a pattern to the timeline based on a key function addPattern(mKey) - if mKey == 0 then pAltBarrage(u_rndInt(2, 4), 2) + if mKey == 0 then pAltBarrage(u_rndInt(3, 5), 2) elseif mKey == 1 then pMirrorSpiral(u_rndInt(3, 6), 0) elseif mKey == 2 then pBarrageSpiral(u_rndInt(0, 3), 1, 1) elseif mKey == 3 then pBarrageSpiral(u_rndInt(0, 2), 1.2, 2) diff --git a/_RELEASE/Packs/experimental/Scripts/Levels/stress1.lua b/_RELEASE/Packs/experimental/Scripts/Levels/stress1.lua index c6b41307..902f7bb1 100644 --- a/_RELEASE/Packs/experimental/Scripts/Levels/stress1.lua +++ b/_RELEASE/Packs/experimental/Scripts/Levels/stress1.lua @@ -44,7 +44,7 @@ end -- onStep should contain your pattern spawning logic function onStep() hmcSimpleBarrageSNeigh(getRandomSide(), 3, 0) - t_wait(getPerfectDelayDM(THICKNESS) * 6) + t_wait(getPerfectDelay(THICKNESS) * 6) end diff --git a/_RELEASE/Packs/experimental/Styles/construct.json b/_RELEASE/Packs/experimental/Styles/construct.json index 2855ea87..18aeda91 100644 --- a/_RELEASE/Packs/experimental/Styles/construct.json +++ b/_RELEASE/Packs/experimental/Styles/construct.json @@ -26,7 +26,7 @@ "cap_color": {"legacy": false, "value": [255, 255, 255, 255], "pulse": [0, 0, 0, 0] }, "player_color": {"dynamic": false, "value": [0, 255, 0, 255], "pulse": [0, 0, 0, 0] }, "text_color": {"dynamic": false, "value": [255, 255, 255, 255], "pulse": [0, 0, 0, 0] }, - "wall_color": {"dynamic": false, "value": [0, 198, 254, 255], "pulse": [0, 0, 0, 0] }, + "wall_color": {"dynamic": false, "value": [0, 198, 254, 255], "pulse": [0, 0, 0, 0] }, // Background colors "colors": diff --git a/_RELEASE/Packs/hypercube/Levels/acceleradiant.json b/_RELEASE/Packs/hypercube/Levels/acceleradiant.json index f41e38a0..f730e7a8 100644 --- a/_RELEASE/Packs/hypercube/Levels/acceleradiant.json +++ b/_RELEASE/Packs/hypercube/Levels/acceleradiant.json @@ -3,10 +3,10 @@ "name": "acceleradiant", "description": "dunno lol", "author": "vittorio romeo", - "menuPriority": 20, + "menuPriority": 10, "selectable": true, "styleId": "acceleradiant", "musicId": "mrGawne", "luaFile": "Scripts/Levels/acceleradiant.lua", - "difficultyMults": [0.85, 1.25, 1.4, 1.8] + "difficultyMults": [0.85, 1.8, 2.4] } diff --git a/_RELEASE/Packs/hypercube/Levels/centrifugal.json b/_RELEASE/Packs/hypercube/Levels/centrifugal.json index 9d06ee27..7fec77f4 100644 --- a/_RELEASE/Packs/hypercube/Levels/centrifugal.json +++ b/_RELEASE/Packs/hypercube/Levels/centrifugal.json @@ -3,10 +3,10 @@ "name": "centrifugal force", "description": "insert cool description here", "author": "vittorio romeo", - "menuPriority": 71, + "menuPriority": 80, "selectable": true, "styleId": "centrifugal", "musicId": "tengil", "luaFile": "Scripts/Levels/centrifugal.lua", - "difficultyMults": [1.2, 1.4, 1.6, 0.6, 0.3] + "difficultyMults": [1.6, 0.6] } diff --git a/_RELEASE/Packs/hypercube/Levels/disc-o.json b/_RELEASE/Packs/hypercube/Levels/disc-o.json index 25617751..ee74c624 100644 --- a/_RELEASE/Packs/hypercube/Levels/disc-o.json +++ b/_RELEASE/Packs/hypercube/Levels/disc-o.json @@ -3,10 +3,10 @@ "name": "disc-o", "description": "dance!", "author": "vittorio romeo", - "menuPriority": 10, + "menuPriority": 40, "selectable": true, "styleId": "disc-o", "musicId": "dischipo", "luaFile": "Scripts/Levels/disc-o.lua", - "difficultyMults": [1.6, 2.2, 2.8, 0.6, 0.4] + "difficultyMults": [2, 2.8, 0.5] } diff --git a/_RELEASE/Packs/hypercube/Levels/g-force.json b/_RELEASE/Packs/hypercube/Levels/g-force.json index 722ae472..f74cd366 100644 --- a/_RELEASE/Packs/hypercube/Levels/g-force.json +++ b/_RELEASE/Packs/hypercube/Levels/g-force.json @@ -3,10 +3,10 @@ "name": "g-force", "description": "beware the acceleration", "author": "vittorio romeo", - "menuPriority": 30, + "menuPriority": 60, "selectable": true, "styleId": "g-force", "musicId": "cpumood", "luaFile": "Scripts/Levels/g-force.lua", - "difficultyMults": [1.6, 2.2, 2.8, 0.4, 0.6] + "difficultyMults": [1.6, 2.8, 0.6] } diff --git a/_RELEASE/Packs/hypercube/Levels/incongruence.json b/_RELEASE/Packs/hypercube/Levels/incongruence.json index bdc5755b..a57f5348 100644 --- a/_RELEASE/Packs/hypercube/Levels/incongruence.json +++ b/_RELEASE/Packs/hypercube/Levels/incongruence.json @@ -3,10 +3,10 @@ "name": "incongruence", "description": "choose your side...", "author": "vittorio romeo", - "menuPriority": 40, + "menuPriority": 20, "selectable": true, "styleId": "incongruence", "musicId": "flirtFlirt", "luaFile": "Scripts/Levels/incongruence.lua", - "difficultyMults": [0.75, 1.25, 1.5, 1.8] + "difficultyMults": [0.5, 1.8] } diff --git a/_RELEASE/Packs/hypercube/Levels/massacre.json b/_RELEASE/Packs/hypercube/Levels/massacre.json index 927c4a69..3f41ed82 100644 --- a/_RELEASE/Packs/hypercube/Levels/massacre.json +++ b/_RELEASE/Packs/hypercube/Levels/massacre.json @@ -3,10 +3,10 @@ "name": "massacre", "description": "you >will< die", "author": "vittorio romeo", - "menuPriority": 80, + "menuPriority": 70, "selectable": true, "styleId": "massacre", "musicId": "massacrev2", "luaFile": "Scripts/Levels/massacre.lua", - "difficultyMults": [1.3, 1.6, 0.3, 0.6] + "difficultyMults": [1.6, 0.5] } diff --git a/_RELEASE/Packs/hypercube/Levels/polyhedrug.json b/_RELEASE/Packs/hypercube/Levels/polyhedrug.json index 7af1ac93..4b3310ff 100644 --- a/_RELEASE/Packs/hypercube/Levels/polyhedrug.json +++ b/_RELEASE/Packs/hypercube/Levels/polyhedrug.json @@ -3,10 +3,10 @@ "name": "polyhedrug", "description": "ready to experience something different?", "author": "vittorio romeo", - "menuPriority": 60, + "menuPriority": 30, "selectable": true, "styleId": "polyhedrug", "musicId": "johnnyDerp", "luaFile": "Scripts/Levels/polyhedrug.lua", - "difficultyMults": [0.75, 1.25, 1.5, 1.8] + "difficultyMults": [0.75, 1.8] } diff --git a/_RELEASE/Packs/hypercube/Levels/reppaws.json b/_RELEASE/Packs/hypercube/Levels/reppaws.json index b8492d40..e30d193f 100644 --- a/_RELEASE/Packs/hypercube/Levels/reppaws.json +++ b/_RELEASE/Packs/hypercube/Levels/reppaws.json @@ -3,10 +3,10 @@ "name": "reppaws", "description": "180 degrees of awesomeness", "author": "vittorio romeo", - "menuPriority": 70, + "menuPriority": 90, "selectable": true, "styleId": "reppaws", "musicId": "minimal", "luaFile": "Scripts/Levels/reppaws.lua", - "difficultyMults": [1.2, 1.4, 1.6, 0.6, 0.3] + "difficultyMults": [1.8, 0.5] } diff --git a/_RELEASE/Packs/hypercube/Scripts/Levels/acceleradiant.lua b/_RELEASE/Packs/hypercube/Scripts/Levels/acceleradiant.lua index e7646e99..00823c0f 100644 --- a/_RELEASE/Packs/hypercube/Scripts/Levels/acceleradiant.lua +++ b/_RELEASE/Packs/hypercube/Scripts/Levels/acceleradiant.lua @@ -6,30 +6,35 @@ u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "nextpatterns.lu -- this function adds a pattern to the timeline based on a key function addPattern(mKey) - if mKey == 0 then pACBarrage() + if mKey == 0 then pACBarrageDecelerate() elseif mKey == 1 then pACBarrageMulti() - elseif mKey == 2 then pACBarrageMultiAltDir() + elseif mKey == 2 then pACSpiral() + elseif mKey == 3 and u_getDifficultyMult() >= 1 then pACBarrageDeception() + elseif mKey == 4 then pACAltBarrage(math.random(2, 3)) + elseif mKey == 5 then pACAltBarrageMulti() + elseif mKey == 6 then pACAltBarrageReveal(math.random(3, 4)) + elseif mKey == 7 then pACInverseBarrage(); + elseif mKey == 8 then pACTunnelReveal(math.random(2, 4)); end end -- shuffle the keys, and then call them to add all the patterns -- shuffling is better than randomizing - it guarantees all the patterns will be called -keys = { 0, 0, 1, 1, 2, 2, 0, 0, 0, 0, 0 } +keys = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 4, 5, 6, 7, 7, 8} shuffle(keys) index = 0 achievementUnlocked = false +hardAchievementUnlocked = false -- onInit is an hardcoded function that is called when the level is first loaded function onInit() - l_setSpeedMult(2.25) - l_setSpeedInc(0.045) - l_setSpeedMax(2.35); + l_setSpeedMult(2.2) + l_setSpeedInc(0.1) + l_setSpeedMax(4.5); l_setRotationSpeed(0.27) l_setRotationSpeedMax(0.45) l_setRotationSpeedInc(0.045) l_setDelayMult(1.1) - l_setDelayInc(-0.01) - l_setDelayMin(1.07) l_setFastSpin(71.0) l_setSides(6) l_setSidesMin(5) @@ -46,6 +51,7 @@ function onInit() l_setBeatPulseDelayMax(21.8) enableSwapIfDMGreaterThan(1.4) + l_setSwapCooldownMult(1 / u_getDifficultyMult()); end -- onLoad is an hardcoded function that is called when the level is started/restarted @@ -76,7 +82,7 @@ end dirChangeTime = 400 hueIMin = 0.0 hueIMax = 22.0 -hueIStep = 0.0065 +hueIStep = 6.5 -- onUpdate is an hardcoded function that is called every frame function onUpdate(mFrameTime) @@ -94,7 +100,12 @@ function onUpdate(mFrameTime) achievementUnlocked = true end - s_setHueInc(s_getHueInc() + hueIStep) + if not hardAchievementUnlocked and l_getLevelTime() > 30 and u_getDifficultyMult() > 2.2 then + steam_unlockAchievement("a39_acceleradiant_hard") + hardAchievementUnlocked = true + end + + s_setHueInc(s_getHueInc() + hueIStep * mFrameTime/FPS) if(s_getHueInc() > hueIMax) then hueIStep = hueIStep * -1 end if(s_getHueInc() < hueIMin) then hueIStep = hueIStep * -1 end end diff --git a/_RELEASE/Packs/hypercube/Scripts/Levels/centrifugal.lua b/_RELEASE/Packs/hypercube/Scripts/Levels/centrifugal.lua index 51b61b73..1fb6f9c5 100644 --- a/_RELEASE/Packs/hypercube/Scripts/Levels/centrifugal.lua +++ b/_RELEASE/Packs/hypercube/Scripts/Levels/centrifugal.lua @@ -5,12 +5,13 @@ u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "commonpatterns. u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "nextpatterns.lua") u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "evolutionpatterns.lua") -curveSpeed = 1 +curveSpeed = 15 achievementUnlocked = false +hardAchievementUnlocked = false -- onInit is an hardcoded function that is called when the level is first loaded function onInit() - l_setSpeedMult(2.91) + l_setSpeedMult(2.8) l_setSpeedInc(0.0) l_setRotationSpeed(0.0) l_setRotationSpeedMax(0.0) @@ -31,31 +32,39 @@ function onInit() l_setPulseSpeedR(0) l_setPulseDelayMax(6.8) - l_setBeatPulseMax(19) - l_setBeatPulseDelayMax(28.346) + l_setBeatPulseMax(20) + l_setBeatPulseDelayMax(27.2) l_setSwapEnabled(true) + l_setSwapCooldownMult(0.8/u_getDifficultyMult()) l_addTracked("curveSpeed", "curve speed") end -- onLoad is an hardcoded function that is called when the level is started/restarted function onLoad() + if (u_getDifficultyMult() > 1) then + e_messageAdd("Difficulty > 1\nWalls travel constantly!", 120) + end end -- onStep is an hardcoded function that is called when the level timeline is empty -- onStep should contain your pattern spawning logic function onStep() - hmcSimpleBarrageSNeigh(getRandomSide(), getRandomDir() * curveSpeed, 4) - t_wait(getPerfectDelayDM(THICKNESS) * 6.22) + if (u_getDifficultyMult() > 1) then + hmcSimpleBarrageSNeigh(getRandomSide(), getRandomDir() * curveSpeed / 1.5, 4) + t_wait(getPerfectDelay(THICKNESS) * 7.5) + else + hmcBarrageStop(getRandomSide(), getRandomDir() * curveSpeed, 4) + t_wait(getPerfectDelay(THICKNESS) * 7) + end + end -- onIncrement is an hardcoded function that is called when the level difficulty is incremented function onIncrement() - if curveSpeed < 3 then - curveSpeed = curveSpeed + 0.4 - e_messageAddImportant("Curve speed: "..curveSpeed, 120) - end + curveSpeed = curveSpeed + 5 + e_messageAddImportant("Curve speed: "..curveSpeed, 120) end -- onUnload is an hardcoded function that is called when the level is closed/restarted @@ -68,4 +77,9 @@ function onUpdate(mFrameTime) steam_unlockAchievement("a19_centrifugalforce") achievementUnlocked = true end + + if not hardAchievementUnlocked and l_getLevelTime() > 30 and u_getDifficultyMult() > 1.5 then + steam_unlockAchievement("a45_centrifugalforce_hard") + hardAchievementUnlocked = true + end end diff --git a/_RELEASE/Packs/hypercube/Scripts/Levels/disc-o.lua b/_RELEASE/Packs/hypercube/Scripts/Levels/disc-o.lua index 896e15f5..c5b945ae 100644 --- a/_RELEASE/Packs/hypercube/Scripts/Levels/disc-o.lua +++ b/_RELEASE/Packs/hypercube/Scripts/Levels/disc-o.lua @@ -7,39 +7,62 @@ u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "evolutionpatter -- this function adds a pattern to the timeline based on a key function addPattern(mKey) - if mKey == 0 then pAltBarrage(u_rndInt(1, 3), 2) - elseif mKey == 1 then pMirrorSpiral(u_rndInt(2, 4), 0) - elseif mKey == 2 then pBarrageSpiral(u_rndInt(0, 3), 1, 1) - elseif mKey == 3 then pBarrageSpiral(u_rndInt(0, 2), 1.2, 2) + -- Normal Palette + if mKey == 0 then pAltBarrage(math.random(2, 4), 2) + elseif mKey == 1 then pMirrorSpiral(math.random(2, 4), 0) + elseif mKey == 2 then pBarrageSpiral(math.random(0, 3), 1, 1) + elseif mKey == 3 then pBarrageSpiral(math.random(0, 2), 1.2, 2) elseif mKey == 4 then pBarrageSpiral(2, 0.7, 1) elseif mKey == 5 then pInverseBarrage(0) - elseif mKey == 6 then hmcDefBarrageSpiral() + elseif mKey == 6 then hmpBarrageSpiral(math.random(1, 3), 2, clamp(level + 1, 2, 6)) elseif mKey == 7 then pMirrorWallStrip(1, 0) - elseif mKey == 8 then hmcDefSpinner() - elseif mKey == 9 then hmcDefBarrage() + elseif mKey == 8 then hmpSpinner(1, clamp(level, 1, 6)) + elseif mKey == 9 then hmpBarrage(1, clamp(level, 1, 6)) elseif mKey == 10 then hmcDef2Cage() - elseif mKey == 11 then hmcDefBarrageSpiralSpin() + elseif mKey == 11 then hmpBarrageSpiralSpin(math.random(7, 14), 2, clamp(level + 1, 2, 6)) + elseif mKey == 12 then hmpGrowTunnel(math.random(2, 3)) + elseif mKey == 13 then + hmcGrowBarrage(getRandomSide(), getRandomSide()) + t_wait(getPerfectDelay(getPerfectThickness(THICKNESS)) * 4.5) + elseif mKey == 14 then pSwapBarrage(getRandomSide()) + elseif mKey == 15 then pSwapCorridor(math.random(2, 3)) + elseif mKey == 16 then hmpTwirl(math.random(2, 4), clamp(level + 1, 2, 5), 1) + -- Specials Palette + elseif mKey == 101 then + hmpBarrageStop(3, 12) + if (u_getDifficultyMult() > 1) then + t_wait(getPerfectDelay(THICKNESS) * 3) + end + elseif mKey == 102 then hmpSwarm(clamp(level, 1, 6), 3 * clamp(level, 1, 6), true, level > 10, false) end end +--globalHueModifier = 0 +level = 1; + -- shuffle the keys, and then call them to add all the patterns -- shuffling is better than randomizing - it guarantees all the patterns will be called -keys = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 10, 10, 8, 8, 9, 9, 9, 9, 6, 11, 11, 10, 10 } +keys = { 0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 11, 12, 13, 13, 14, 14, 15, 16, 16 } +specialKeys = {} -- For specials. shuffle(keys) -index = 0 +index = 1 +specialIndex = 1 achievementUnlocked = false +hardAchievementUnlocked = false -specials = { "cage", "spinner", "barrage" } +specials = { "spinner", "barrage", "grow", "swarm" } +shuffle(specials) +currSpecial = 1 special = "none" -- onInit is an hardcoded function that is called when the level is first loaded function onInit() l_setSpeedMult(1.7) - l_setSpeedInc(0.15) - l_setSpeedMax(2.9) + l_setSpeedInc(0) + l_setSpeedMax(3.5) l_setRotationSpeed(0.1) l_setRotationSpeedMax(0.415) - l_setRotationSpeedInc(0.035) + l_setRotationSpeedInc(0) l_setDelayMult(1.2) l_setDelayInc(0.0) l_setFastSpin(0.0) @@ -59,6 +82,7 @@ function onInit() l_setBeatPulseDelayMax(28.346) l_setSwapEnabled(true) + l_setSwapCooldownMult(1.4/u_getSpeedMultDM()); l_addTracked("special", "special") end @@ -69,34 +93,64 @@ end -- onStep is an hardcoded function that is called when the level timeline is empty -- onStep should contain your pattern spawning logic function onStep() - if special == "none" then + if (special == "none") then addPattern(keys[index]) index = index + 1 - if index - 1 == #keys then index = 1 shuffle(keys) end - elseif special == "cage" then - addPattern(10) - elseif special == "spinner" then - addPattern(8) - elseif special == "barrage" then - addPattern(9) + else + addPattern(specialKeys[specialIndex]) + if (#specialKeys > 1) then + specialIndex = specialIndex + 1; + + if specialIndex - 1 == #specialKeys then + specialIndex = 1 + shuffle(specialKeys) + end + end end end +local constantFlag = false -- onIncrement is an hardcoded function that is called when the level difficulty is incremented function onIncrement() - shuffle(specials) - if special == "none" then - special = specials[1] + special = specials[currSpecial] e_messageAddImportant("Special: "..special, 120) + currSpecial = currSpecial + 1 + if (currSpecial - 1 == #specials) then + currSpecial = 1 + shuffle(specials) + end + specialIndex = 1 + if (special == "cage") then + specialKeys = {10}; + elseif (special == "spinner") then + specialKeys = {8}; + elseif (special == "barrage") then + specialKeys = {101}; + elseif (special == "grow") then + specialKeys = {13}; + elseif (special == "swarm") then + specialKeys = {102}; + if (level > 10 and not constantFlag) then + e_messageAddImportant("Constant Speed Enabled!", 90) + constantFlag = true + end + end + shuffle(specialKeys) + l_setSpeedInc(0.1) + l_setRotationSpeedInc(0.035) else special = "none" + level = level + 1; + l_setSpeedInc(0) + l_setRotationSpeedInc(0) end + l_setSwapCooldownMult(1.7/u_getSpeedMultDM()); end -- onUnload is an hardcoded function that is called when the level is closed/restarted @@ -109,4 +163,9 @@ function onUpdate(mFrameTime) steam_unlockAchievement("a12_disco") achievementUnlocked = true end + + if not hardAchievementUnlocked and l_getLevelTime() > 40 and u_getDifficultyMult() > 2.5 then + steam_unlockAchievement("a38_disco_hard") + hardAchievementUnlocked = true + end end diff --git a/_RELEASE/Packs/hypercube/Scripts/Levels/evotutorial.lua b/_RELEASE/Packs/hypercube/Scripts/Levels/evotutorial.lua index b27b9b49..48602378 100644 --- a/_RELEASE/Packs/hypercube/Scripts/Levels/evotutorial.lua +++ b/_RELEASE/Packs/hypercube/Scripts/Levels/evotutorial.lua @@ -37,14 +37,14 @@ function onInit() l_setWallSkewLeft(18) - l_setPulseMin(68) - l_setPulseMax(82.93) - l_setPulseSpeed(1.521) - l_setPulseSpeedR(1.4) + l_setPulseMin(64) + l_setPulseMax(84) + l_setPulseSpeed(1.05) + l_setPulseSpeedR(1.35) l_setPulseDelayMax(7) l_setBeatPulseMax(15) - l_setBeatPulseDelayMax(27.48) + l_setBeatPulseDelayMax(110) l_setSwapEnabled(true) @@ -53,54 +53,85 @@ function onInit() end swappedOnce = false +swapTime = false -- onCursorSwap is executed whenever the player executes a successful 180° swap function onCursorSwap() - if swappedOnce == false then - u_log("swap detected!") + if (swapTime and not swappedOnce) then swappedOnce = true + e_clearMessages() end end -- onLoad is an hardcoded function that is called when the level is started/restarted function onLoad() - e_messageAddImportant("welcome to the evolution tutorial", 120) - e_messageAddImportant("today you'll be introduced to...", 120) - e_messageAddImportant("1. swapping!", 100) - e_messageAddImportant("2. curving walls!", 100) - e_messageAddImportant("", 120) - e_messageAddImportant("press space or middle mouse button\nto swap", 250) - e_messageAddImportant("it allows you to rotate 180 degrees!", 200) - e_messageAddImportant("", 120) - - e_messageAddImportant("now: curving walls", 120) + e_waitS(1) + e_messageAddImportant("Welcome to the evolution tutorial", 150) + e_messageAddImportant("Today you'll be introduced to...", 120) + e_messageAddImportant("swapping, accelerating walls, and curving walls!", 300) + e_wait(570 + 120) + e_messageAddImportant("Swapping allows you to instantly rotate 180 degrees!", 200) + e_messageAddImportant("You can only swap when your player\nblinks red and yellow!", 250) + e_wait(450) + + e_eval([[swapTime = true]]) + e_messageAddImportant("Try swapping now!\nPress space or middle mouse button to swap", 10000) +end + +function partTwo() + e_messageAddImportant("Awesome!", 120) + e_messageAddImportant("After swapping, you must wait before\nswapping again!", 250) + e_messageAddImportant("Let's introduce a swap pattern.", 180); + e_messageAddImportant("For this pattern, you must swap to survive", 180) + e_messageAddImportant("Swap at the right moment!", 180) + e_wait(180 * 3 + 120 + 250 + 120) + e_messageAddImportant("Excellent! You know how to swap!", 120) + t_wait(610) + pSwapBarrage(getRandomSide(), 2) + + e_messageAddImportant("Let's talk about accelerating walls", 180); + e_messageAddImportant("These walls can change speed over time", 180); + e_messageAddImportant("They can either accelerate...", 180); + e_messageAddImportant("Decelerate...", 180); + e_messageAddImportant("Or go completely backwards to trick you!", 180); + e_wait(180 * 5 + 120 + 120) + + t_wait(760); + pACBarrageAccelerate(); + t_wait(120); + pACBarrageDecelerate(); + t_wait(100); + pACBarrageDeception(3, 1); + + e_messageAddImportant("But wait, it gets crazier!", 120); + e_messageAddImportant("Let's focus on curving walls", 120) e_messageAddImportant("they can be simple...", 120) - e_messageAddImportant("", 120 * 3 + 80) + e_wait(300 + 410) - t_wait(135 * 8) - hmcSimpleBarrage(1) + t_wait(300) + hmcSimpleBarrage(0, 1) t_wait(100) - hmcSimpleBarrage(-1) + hmcSimpleBarrage(0, -1) t_wait(50) - hmcSimpleBarrage(1) + hmcSimpleBarrage(0, math.random(1, 6)) t_wait(100) - hmcSimpleBarrage(-2.5) + hmcSimpleBarrage(0, -math.random(1, 6)) t_wait(80) - hmcSimpleBarrage(2.5) + hmcSimpleBarrage(0, math.random(1, 6)) t_wait(80) - hmcSimpleBarrage(3) + hmcSimpleBarrage(0, math.random(-6, 6)) t_wait(50) e_messageAddImportant("...in various patterns...", 130) - e_messageAddImportant("", 120 * 5 + 80) + e_wait(100 + 120 * 5 + 80) t_wait(130) - hmcSimpleTwirl(5, 1, 0) + hmcSimpleTwirl(5, math.random(-3, 3), 0) t_wait(50) - hmcSimpleTwirl(5, -2.5, 0.3) + hmcSimpleTwirl(5, -2, 1) - e_messageAddImportant("...or can accellerate!", 130) - e_messageAddImportant("", 120 * 4 + 40) + e_messageAddImportant("...or can accelerate!", 130) + e_wait(130 + 340) t_wait(130) hmcBarrage(0, 0.05, -1.5, 3, true) @@ -112,42 +143,40 @@ function onLoad() hmcBarrage(0, 0.1, -3, 3, true) t_wait(200) - e_messageAddImportant("they can also do crazy stuff!", 130) - e_messageAddImportant("", 120 * 8 + 50) + e_messageAddImportant("they can also do crazy stuff!", 180) + e_wait(120 * 9) - hmcSimpleCage(2.5, 1) + hmcSimpleCage(2, 1) t_wait(80) - hmcSimpleCage(2.5, -1) + hmcSimpleCage(2, -1) t_wait(100) - hmcSimpleCage(2.5, 1) - hmcSimpleCage(2.5, 1) + hmcSimpleCage(2, 1) + hmcSimpleCage(2, 1) t_wait(100) - hmcSimpleCage(2.5, 1) - hmcSimpleCage(2.5, -1) + hmcSimpleCage(2, 1) + hmcSimpleCage(2, -1) t_wait(100) - hmcSimpleSpinner(1) + hmcSimpleSpinner(getRandomSide(), 1) t_wait(100) - hmcSimpleSpinner(-2) + hmcGrowBarrage(getRandomSide(), 0) t_wait(100) - hmcSimpleSpinner(3) + hmcGrowBarrage(getRandomSide(), 3) t_wait(100) - hmcSimpleCage(1.5, 1) - hmcSimpleCage(2.5, 1) + hmcGrowBarrage(getRandomSide(), 6) t_wait(100) - hmcSimpleCage(1.5, 1) - hmcSimpleCage(2.5, -1) + hmcAssembleBarrage(getRandomSide()) t_wait(100) - hmcSimpleSpinner(1) - hmcSimpleSpinner(1.2) + hmcAssembleBarrage(getRandomSide()) t_wait(100) - hmcSimpleSpinner(1) - hmcSimpleSpinner(-1.2) - t_wait(500) + hmcAssembleBarrage(getRandomSide()) + t_wait(700) - e_messageAddImportant("now play some real levels!", 138) - e_messageAddImportant("good luck!", 130) + e_messageAddImportant("Well done!", 130) + e_messageAddImportant("You should be prepared to take on Hypercube!", 300) + e_messageAddImportant("Have fun!", 1000) - t_kill() + e_wait(340) + e_kill() end -- onStep is an hardcoded function that is called when the level timeline is empty @@ -168,6 +197,11 @@ dirChangeTime = 600 -- onUpdate is an hardcoded function that is called every frame function onUpdate(mFrameTime) + --print(l_getPulse()); + if (swappedOnce and swapTime) then + partTwo() + swapTime = false + end dirChangeTime = dirChangeTime - mFrameTime; if dirChangeTime < 0 then -- do not change direction while fast spinning diff --git a/_RELEASE/Packs/hypercube/Scripts/Levels/g-force.lua b/_RELEASE/Packs/hypercube/Scripts/Levels/g-force.lua index 27cb7170..b5511bd4 100644 --- a/_RELEASE/Packs/hypercube/Scripts/Levels/g-force.lua +++ b/_RELEASE/Packs/hypercube/Scripts/Levels/g-force.lua @@ -7,18 +7,18 @@ u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "evolutionpatter function gforceBarrage() cBarrage(getRandomSide()) - t_wait(getPerfectDelayDM(THICKNESS) * 6.1) + t_wait(getPerfectDelay(THICKNESS) * 6.1) end function gforceBarrageAssault() cBarrage(getRandomSide()) - t_wait(getPerfectDelayDM(THICKNESS) * 3.1) + t_wait(getPerfectDelay(THICKNESS) * 3.5) end -- this function adds a pattern to the timeline based on a key function addPattern(mKey) - if mKey == 0 then hmcDefAccelBarrage() + if mKey == 0 then hmpDefAccelBarrage() elseif mKey == 1 then gforceBarrage() end end @@ -29,18 +29,21 @@ keys = { 0, 1 } shuffle(keys) index = 0 achievementUnlocked = false +hardAchievementUnlocked = false specials = { "double", "assault", "incongruence", "dizzy" } +shuffle(specials) +currSpecial = 1 special = "none" -- onInit is an hardcoded function that is called when the level is first loaded function onInit() l_setSpeedMult(2.1) - l_setSpeedInc(0.16) + l_setSpeedInc(0) -- Disable it for right now l_setSpeedMax(3.9) l_setRotationSpeed(0.12) - l_setRotationSpeedMax(0.6) - l_setRotationSpeedInc(0.035) + l_setRotationSpeedMax(0.65) + l_setRotationSpeedInc(0.0175) l_setDelayMult(1.9) l_setDelayInc(0.0) l_setFastSpin(0.0) @@ -62,6 +65,7 @@ function onInit() l_setBeatPulseDelayMax(13.953) l_setSwapEnabled(true) + l_setSwapCooldownMult(1.9/u_getSpeedMultDM()) l_addTracked("special", "special") end @@ -106,13 +110,23 @@ end -- onIncrement is an hardcoded function that is called when the level difficulty is incremented function onIncrement() - shuffle(specials) - if special == "none" then - special = specials[1] + special = specials[currSpecial] + currSpecial = currSpecial + 1 + if (currSpecial - 1 == #specials) then + currSpecial = 1 + shuffle(specials) + end e_messageAddImportant("Special: "..special, 120) + l_setSpeedInc(0.16) else special = "none" + l_setSpeedInc(0) + end + if (special == "assault") then + l_setSwapCooldownMult(1/u_getSpeedMultDM()) -- Assault has tighter spacing so we need lower swap cooldown. + else + l_setSwapCooldownMult(1.9/u_getSpeedMultDM()) end end @@ -126,4 +140,9 @@ function onUpdate(mFrameTime) steam_unlockAchievement("a14_gforce") achievementUnlocked = true end + + if not hardAchievementUnlocked and l_getLevelTime() > 40 and u_getDifficultyMult() > 2.5 then + steam_unlockAchievement("a40_gforce_hard") + hardAchievementUnlocked = true + end end diff --git a/_RELEASE/Packs/hypercube/Scripts/Levels/incongruence.lua b/_RELEASE/Packs/hypercube/Scripts/Levels/incongruence.lua index 1add3d7c..62d2b08d 100644 --- a/_RELEASE/Packs/hypercube/Scripts/Levels/incongruence.lua +++ b/_RELEASE/Packs/hypercube/Scripts/Levels/incongruence.lua @@ -6,43 +6,53 @@ u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "nextpatterns.lu -- this function adds a pattern to the timeline based on a key function addPattern(mKey) - if mKey == 0 then pRCBarrage() + if mKey == 0 then pBarrage() elseif mKey == 1 then pRCBarrageDouble() - elseif mKey == 2 then pRCBarrageSpin() + elseif mKey == 2 then pBarrageSpiral(math.random(1, 2), 0.8) + elseif mKey == 3 then + if (l_getSides() > 8) then + pAltBarrage(math.random(2, 3), 3, 0.75) + else + pAltBarrage(math.random(2, 3), 2, 0.75) + end + -- "Dynamic" Patterns + elseif mKey == 4 then pRCAscendBarrageRandom(lowerBound, upperBound) + elseif mKey == 5 then pRCAscendBarrage(getRandomSide(), lowerBound, upperBound) + elseif mKey == 6 then pRCDynamicAltBarrage(2, math.random(3, 4), lowerBound, upperBound) end end -- shuffle the keys, and then call them to add all the patterns -- shuffling is better than randomizing - it guarantees all the patterns will be called -keys = { 0, 0, 1, 1, 2 } +keys = { 0, 0, 0, 1, 1, 2, 3 } shuffle(keys) index = 0 lowerBound = 4 upperBound = 6 achievementUnlocked = false +hardAchievementUnlocked = false -- onInit is an hardcoded function that is called when the level is first loaded function onInit() l_setSpeedMult(2.7) - l_setSpeedInc(0.11) - l_setSpeedMax(3.14) -- A lot of the difficulty is coming from the changing sides. Speed isn't too important here. + l_setSpeedInc(0.1) + l_setSpeedMax(3.8) -- A lot of the difficulty is coming from the changing sides. Speed isn't too important here. l_setRotationSpeed(0.27) l_setRotationSpeedMax(0.5) l_setRotationSpeedInc(0.045) l_setDelayMult(1.1) - l_setDelayInc(-0.04) - l_setDelayMin(0.86) + l_setDelayInc(0) l_setFastSpin(71.0) l_setSides(6) l_setSidesMin(0) l_setSidesMax(0) l_setIncTime(15) - l_setPulseMin(62.27) - l_setPulseMax(100.22) - l_setPulseSpeed(1.971) - l_setPulseSpeedR(0.71) - l_setPulseDelayMax(13.01) + l_setPulseMin(64) + l_setPulseMax(84) + l_setPulseSpeed(1.05) + l_setPulseSpeedR(1.34) + l_setPulseDelayMax(1.74) l_setBeatPulseMax(15) l_setBeatPulseDelayMax(21.428) @@ -52,11 +62,22 @@ function onInit() l_enableRndSideChanges(false) enableSwapIfDMGreaterThan(1.5) + l_setSwapCooldownMult(1 / u_getDifficultyMult()) end -- onLoad is an hardcoded function that is called when the level is started/restarted function onLoad() - e_messageAddImportant("Sides ("..lowerBound.." / "..upperBound..")", 170) + if (u_getDifficultyMult() >= 1) then + keys[#keys + 1] = 4 + keys[#keys + 1] = 4 + if (u_getDifficultyMult() > 1) then + e_messageAdd("Difficulty > 1\n\"Dynamic\" patterns enabled!", 120) + keys[#keys + 1] = 5 + keys[#keys + 1] = 6 + end + shuffle(keys) + end + e_messageAddImportant("Sides: "..lowerBound.." - "..upperBound, 120) end -- onStep is an hardcoded function that is called when the level timeline is empty @@ -74,9 +95,10 @@ end -- onIncrement is an hardcoded function that is called when the level difficulty is incremented function onIncrement() - lowerBound = u_rndInt(4, 6) - upperBound = lowerBound + u_rndInt(1, 3) - e_messageAddImportant("Sides ("..lowerBound.." / "..upperBound..")", 170) + lowerBound = math.floor(u_rndInt(4, 6)) + upperBound = math.floor(lowerBound + u_rndInt(1, 4)) + e_messageAddImportant("Sides: "..lowerBound.." - "..upperBound, 120) + t_clear() end -- onUnload is an hardcoded function that is called when the level is closed/restarted @@ -86,7 +108,7 @@ end -- continuous direction change (even if not on level increment) dirChangeTime = 400 hueIMin = 0.0 -hueIMax = 22.0 +hueIMax = 15.0 hueIStep = 0.0065 -- onUpdate is an hardcoded function that is called every frame @@ -105,6 +127,11 @@ function onUpdate(mFrameTime) achievementUnlocked = true end + if not hardAchievementUnlocked and l_getLevelTime() > 45 and u_getDifficultyMult() > 1.5 then + steam_unlockAchievement("a41_incongruence_hard") + hardAchievementUnlocked = true + end + s_setHueInc(s_getHueInc() + hueIStep) if(s_getHueInc() > hueIMax) then hueIStep = hueIStep * -1 end if(s_getHueInc() < hueIMin) then hueIStep = hueIStep * -1 end diff --git a/_RELEASE/Packs/hypercube/Scripts/Levels/massacre.lua b/_RELEASE/Packs/hypercube/Scripts/Levels/massacre.lua index 31f2f65c..15264b73 100644 --- a/_RELEASE/Packs/hypercube/Scripts/Levels/massacre.lua +++ b/_RELEASE/Packs/hypercube/Scripts/Levels/massacre.lua @@ -5,52 +5,93 @@ u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "commonpatterns. u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "nextpatterns.lua") u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "evolutionpatterns.lua") +local side = 0 +local sideSpawn = 0; + -- this function adds a pattern to the timeline based on a key function addPattern(mKey) - if mKey == 0 then pAltBarrage(u_rndInt(1, 2), 2) + if mKey == 0 then pAltBarrage(math.random(2, 4), 2) elseif mKey == 1 then pBarrageSpiral(2, 0.6, 1) elseif mKey == 2 then pInverseBarrage(0) - elseif mKey == 3 then hmcDefBarrageSpiralFast() + elseif mKey == 3 then hmpTunnelDynamic(math.random(2, 3)) elseif mKey == 4 then pWallExVortex(0, 1, 1) - elseif mKey == 5 then pDMBarrageSpiral(u_rndInt(2, 4), 0.4, 1) - elseif mKey == 6 then pRandomBarrage(u_rndInt(1, 3), 2.25) + elseif mKey == 5 then pDMBarrageSpiral(math.random(2, 4), 0.4, 1) + elseif mKey == 6 then pRandomBarrage(math.random(1, 3), 2.25) elseif mKey == 7 then pInverseBarrage(0) elseif mKey == 8 then pMirrorWallStrip(1, 0) - elseif mKey == 9 then hmcDefSpinner() - elseif mKey == 10 then hmcDefBarrageSpiral() + elseif mKey == 9 then hmpSpinner(1, clamp(level, 1, 6)) + elseif mKey == 10 then hmpBarrageSpiral(math.random(1, 3), 2, clamp(level + 1, 2, 5)) elseif mKey == 11 then hmcDef2CageD() - elseif mKey == 12 then hmcDefBarrageSpiralSpin() - elseif mKey == 13 then hmcDefSpinnerSpiralAcc() - elseif mKey == 14 then hmcDefBarrageSpiralRnd() - elseif mKey == 15 then hmcDefBarrageInv() + elseif mKey == 12 then hmpBarrageSpiralSpin(math.random(4, 8), 2, clamp(level + 1, 2, 5)) + elseif mKey == 13 then hmpBarrageSpiralStop(math.random(2, 4), 2, 6); + elseif mKey == 14 then hmcBarrageInv(1, clamp(level + 1, 2, 5)) + elseif mKey == 15 then hmpStripeSnakeBarrage(getRandomSide(), math.random(3, 6)) + elseif mKey == 16 then hmpStripeSnakeAltBarrage(math.random(5, 8), 2) + elseif mKey == 17 then hmpGrowTunnel(math.random(2, 3)) + elseif mKey == 18 then hmpAssembleTunnel(math.random(2, 3)) + elseif mKey == 19 then pSwapBarrage(getRandomSide()) + elseif mKey == 20 then pSwapCorridor(math.random(2, 3)) + elseif mKey == 21 then hmpTwirl(math.random(2, 3), clamp(level + 1, 2, 5), 1) + + -- Special-exclusive patterns + -- Assemble Pattern + elseif mKey == 101 then + hmcAssembleBarrage(getRandomSide(), 1, 4) + t_wait(getPerfectDelay(getPerfectThickness(THICKNESS)) * 4.5) + -- Chaser Pattern + elseif mKey == 102 then + hmpChaserAltBarrage(math.random(1, 3), 2, math.random(getHalfSides(), l_getSides() - 1), true); + -- Turnaround pattern + elseif mKey == 103 then + sideSpawn = side + math.random(0, 1) * getHalfSides(); + local revealChance = math.random(1, 3) + hmcTurnaroundSector(sideSpawn, revealChance == 1) + -- Alternate pattern + elseif mKey == 104 then + hmpAlternate() + -- Tunnel exclusive pattern + elseif mKey == 105 then + hmpTunnelSpinner(math.random(1, 3), 2, clamp(level + 1, 2, 5)) + elseif mKey == 106 then + hmpSwarm(clamp(level, 1, 6), 3 * clamp(level, 1, 6), true, true, level > 14) + -- Consistency pattern + elseif mKey == 107 then + hmpTwirl(10, clamp(level, 1, 4), 1) end end +level = 1; + -- shuffle the keys, and then call them to add all the patterns -- shuffling is better than randomizing - it guarantees all the patterns will be called -keys = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } +keys = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 10, 11, 12, 13, 14, 15, 16, 16, 16, 17, 18, 19, 19, 19, 20, 20, 21, 21 } +specialKeys = {} -- For specials. shuffle(keys) -index = 0 +index = 1 +specialIndex = 1 achievementUnlocked = false +hardAchievementUnlocked = false -specials = { "cage", "spinner", "barrage", "spiral" } +specials = { "assemble", "turnaround", "alternate", "chaser", "tunnel", "swarm", "consistency"} +shuffle(specials) +currSpecial = 1; special = "none" -- onInit is an hardcoded function that is called when the level is first loaded function onInit() l_setSpeedMult(2.7) - l_setSpeedInc(0.04) - l_setSpeedMax(3) + l_setSpeedInc(0) + l_setSpeedMax(3.5) l_setRotationSpeed(0.25) - l_setRotationSpeedMax(0.4) - l_setRotationSpeedInc(0.015) + l_setRotationSpeedMax(0.7) + l_setRotationSpeedInc(0) l_setDelayMult(1.35) l_setDelayInc(0.0) l_setFastSpin(71.0) l_setSides(6) l_setSidesMin(6) l_setSidesMax(6) - l_setIncTime(10) + l_setIncTime(20) l_setPulseMin(61.01) l_setPulseMax(80.48) @@ -62,52 +103,84 @@ function onInit() l_setBeatPulseDelayMax(28.346) l_setSwapEnabled(true) + l_setSwapCooldownMult(0.6) l_addTracked("special", "special") end -- onLoad is an hardcoded function that is called when the level is started/restarted function onLoad() - setCurveMult(0.85) end -- onStep is an hardcoded function that is called when the level timeline is empty -- onStep should contain your pattern spawning logic function onStep() - if special == "none" then + if (special == "none") then addPattern(keys[index]) index = index + 1 - if index - 1 == #keys then index = 1 shuffle(keys) end - elseif special == "cage" then - addPattern(11) - addPattern(9) - elseif special == "spinner" then - addPattern(14) - addPattern(9) - elseif special == "barrage" then - addPattern(3) - addPattern(14) - addPattern(13) - addPattern(15) - elseif special == "spiral" then - addPattern(12) - addPattern(4) + else + addPattern(specialKeys[specialIndex]) + if (#specialKeys > 1) then + specialIndex = specialIndex + 1; + + if specialIndex - 1 == #specialKeys then + specialIndex = 1 + shuffle(specialKeys) + end + end end end +local accelFlag = false -- onIncrement is an hardcoded function that is called when the level difficulty is incremented function onIncrement() - shuffle(specials) + if (special == "none") then + special = specials[currSpecial] + currSpecial = currSpecial + 1 + if (currSpecial - 1 == #specials) then + currSpecial = 1 + shuffle(specials) + end + specialIndex = 1 + -- If branch to set up keys + if (special == "turnaround") then + side = getRandomSide(); + specialKeys = {103}; + elseif (special == "assemble") then + specialKeys = {101}; + elseif (special == "alternate") then + specialKeys = {104}; + elseif (special == "chaser") then + specialKeys = {102}; + elseif (special == "tunnel") then + specialKeys = {3, 17, 18, 105}; + elseif (special == "swarm") then + specialKeys = {106}; + if (level > 14 and not accelFlag) then + e_messageAddImportant("Accelerating Speed Enabled!", 90) + accelFlag = true + end + elseif (special == "consistency") then + specialKeys = {107} + end + shuffle(specialKeys); + l_setIncTime(10); + e_messageAddImportant("Special: "..special, 90); - if special == "none" then - special = specials[1] - e_messageAddImportant("Special: "..special, 120) + -- Enable the speed and rotation increment + l_setSpeedInc(0.05) + l_setRotationSpeedInc(0.015) else - special = "none" + level = level + 1; + special = "none"; + l_setIncTime(20); + -- Disable the speed and rotation increment + l_setSpeedInc(0) + l_setRotationSpeedInc(0) end end @@ -133,4 +206,9 @@ function onUpdate(mFrameTime) steam_unlockAchievement("a20_massacre") achievementUnlocked = true end + + if not hardAchievementUnlocked and l_getLevelTime() > 30 and u_getDifficultyMult() > 1.5 then + steam_unlockAchievement("a46_massacre_hard") + hardAchievementUnlocked = true + end end diff --git a/_RELEASE/Packs/hypercube/Scripts/Levels/polyhedrug.lua b/_RELEASE/Packs/hypercube/Scripts/Levels/polyhedrug.lua index f1bf65d3..b2a77dc5 100644 --- a/_RELEASE/Packs/hypercube/Scripts/Levels/polyhedrug.lua +++ b/_RELEASE/Packs/hypercube/Scripts/Levels/polyhedrug.lua @@ -23,6 +23,7 @@ keys = { 0, 0, 1, 1, 2, 2, 3, 3 } shuffle(keys) index = 0 achievementUnlocked = false +hardAchievementUnlocked = false -- onInit is an hardcoded function that is called when the level is first loaded function onInit() @@ -109,6 +110,11 @@ function onUpdate(mFrameTime) achievementUnlocked = true end + if not hardAchievementUnlocked and l_getLevelTime() > 45 and u_getDifficultyMult() > 1.5 then + steam_unlockAchievement("a43_polyhedrug_hard") + hardAchievementUnlocked = true + end + s_setHueInc(s_getHueInc() + hueIStep) if(s_getHueInc() > hueIMax) then hueIStep = hueIStep * -1 end if(s_getHueInc() < hueIMin) then hueIStep = hueIStep * -1 end diff --git a/_RELEASE/Packs/hypercube/Scripts/Levels/reppaws.lua b/_RELEASE/Packs/hypercube/Scripts/Levels/reppaws.lua index af1a1ed2..a92adcee 100644 --- a/_RELEASE/Packs/hypercube/Scripts/Levels/reppaws.lua +++ b/_RELEASE/Packs/hypercube/Scripts/Levels/reppaws.lua @@ -6,36 +6,44 @@ u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "nextpatterns.lu u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "evolutionpatterns.lua") gap = 7 -minGap = 3 +minGap = 4 -- this function adds a pattern to the timeline based on a key function addPattern(mKey) - if mKey == 0 then cBarrageN(getRandomSide(), gap) t_wait(getPerfectDelayDM(THICKNESS) * 6) - elseif mKey == 1 then hmcSimpleBarrageSNeigh(getRandomSide(), 0, gap) t_wait(getPerfectDelayDM(THICKNESS) * 6) + if mKey == 0 then cBarrageN(getRandomSide(), gap) t_wait(getPerfectDelay(THICKNESS) * 6) + elseif mKey == 1 then hmcSimpleBarrageSNeigh(getRandomSide(), 0, gap) t_wait(getPerfectDelay(THICKNESS) * 6) + elseif mKey == 2 then + if (u_getDifficultyMult() < 1) then + cSwapBarrageN(getRandomSide(), gap, 1.25) + else + cSwapBarrageN(getRandomSide(), gap, .75) + end + t_wait(getPerfectDelay(THICKNESS) * 6) end end -- shuffle the keys, and then call them to add all the patterns -- shuffling is better than randomizing - it guarantees all the patterns will be called -keys = { 0, 0, 0, 1, 1, 1 } +keys = { 0, 0, 0, 1, 1, 1, 2, 2 } shuffle(keys) index = 0 achievementUnlocked = false +hardAchievementUnlocked = false -- onInit is an hardcoded function that is called when the level is first loaded function onInit() l_setSpeedMult(3.0) l_setSpeedInc(0.0) l_setRotationSpeed(0.22) - l_setRotationSpeedMax(0.4) - l_setRotationSpeedInc(0.0) - l_setDelayMult(1.35) + l_setRotationSpeedMax(0.75) + l_setRotationSpeedInc(0.03) + l_setDelayMult(1.5) l_setDelayInc(0.0) l_setFastSpin(71.0) l_setSides(32) l_setSidesMin(32) l_setSidesMax(32) - l_setIncTime(10) + l_setIncTime(15) l_setWallSkewLeft(15) @@ -49,27 +57,28 @@ function onInit() l_setBeatPulseDelayMax(25.714) l_setSwapEnabled(true) + l_setSwapCooldownMult(0.7/u_getDifficultyMult()) l_addTracked("gap", "gap size") if(u_getDifficultyMult() >= 1.59) then gap = 9 minGap = 5 - l_setSwapCooldownMult(0.8) elseif(u_getDifficultyMult() >= 1.39) then gap = 8 minGap = 4 - l_setSwapCooldownMult(0.9) else gap = 7 minGap = 3 - l_setSwapCooldownMult(1.0) end end -- onLoad is an hardcoded function that is called when the level is started/restarted function onLoad() syncCurveWithRotationSpeed(0, 0) - e_messageAdd("remember, swap with spacebar!", 120) + e_messageAdd("Remember to swap!", 120) + if (u_getDifficultyMult() > 1) then + minGap = 5 + end end -- onStep is an hardcoded function that is called when the level timeline is empty @@ -104,4 +113,9 @@ function onUpdate(mFrameTime) steam_unlockAchievement("a18_reppaws") achievementUnlocked = true end + + if not hardAchievementUnlocked and l_getLevelTime() > 30 and u_getDifficultyMult() > 1.5 then + steam_unlockAchievement("a44_reppaws_hard") + hardAchievementUnlocked = true + end end diff --git a/_RELEASE/Packs/hypercube/Scripts/Levels/slither.lua b/_RELEASE/Packs/hypercube/Scripts/Levels/slither.lua index 9534573f..dc316ff3 100644 --- a/_RELEASE/Packs/hypercube/Scripts/Levels/slither.lua +++ b/_RELEASE/Packs/hypercube/Scripts/Levels/slither.lua @@ -7,13 +7,15 @@ u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "evolutionpatter -- shuffle the keys, and then call them to add all the patterns -- shuffling is better than randomizing - it guarantees all the patterns will be called -keys = { 0 } +keys = { 0, 0, 0, 1, 2 } shuffle(keys) index = 0 achievementUnlocked = false +hardAchievementUnlocked = false smin = 2 smax = 2 +completed = false level = 1 incrementTime = 10 @@ -21,32 +23,44 @@ incrementTime = 10 range = "("..(smin * 2).."/"..(smax * 2).."]" function slitherSpiralAcc() - t_wait(getPerfectDelayDM(THICKNESS) * 2.1) - t_wait(getPerfectDelayDM(THICKNESS) * 2.1) + t_wait(getPerfectDelay(THICKNESS) * 2.1) + t_wait(getPerfectDelay(THICKNESS) * 2.1) local side = getRandomSide() local acc = u_rndInt(50, 90) / 500.0 * getRandomDir() local minimum = u_rndInt(15, 21) / 10.0 * -1 local maximum = -minimum - t_wait(getPerfectDelayDM(THICKNESS) * 3.1) + t_wait(getPerfectDelay(THICKNESS) * 3.1) for i = 0, u_rndInt(6, 10) do hmcSimpleSpinnerSAcc(side, 0, acc, minimum, maximum, true) t_wait(getPerfectDelay(THICKNESS) * 0.55) end - t_wait(getPerfectDelayDM(THICKNESS) * 5.3) + t_wait(getPerfectDelay(THICKNESS) * 5.3) +end + +-- this function adds a pattern to the timeline based on a key +function addPattern(mKey) + if (mKey == 0) then slitherSpiralAcc() + elseif (mKey == 1) then + hmpStripeSnakeBarrage(getRandomSide(), u_rndInt(5, 10), getHalfSides(), l_getSides()) + t_wait(getPerfectDelay(THICKNESS) * 2) + elseif (mKey == 2) then + hmpStripeSnakeAltBarrage(u_rndInt(5, 10), 2, getHalfSides(), l_getSides()) + t_wait(getPerfectDelay(THICKNESS) * 2) + end end -- onInit is an hardcoded function that is called when the level is first loaded function onInit() - l_setSpeedMult(1.7) - l_setSpeedInc(0.1) + l_setSpeedMult(1.8) + l_setSpeedInc(0) -- This will be set later. l_setSpeedMax(2.9) l_setRotationSpeed(0.2) - l_setRotationSpeedMax(0.4) + l_setRotationSpeedMax(0.5) l_setRotationSpeedInc(0.035) l_setDelayMult(1.1) @@ -85,22 +99,35 @@ end -- onStep is an hardcoded function that is called when the level timeline is empty -- onStep should contain your pattern spawning logic function onStep() - l_setSides(u_rndInt(smin, smax) * 2) - slitherSpiralAcc() + l_setSides(math.random(smin, smax) * 2) + addPattern(keys[index]) + index = index + 1 + if index - 1 == #keys then + index = 1 + shuffle(keys) + end end - -- onIncrement is an hardcoded function that is called when the level difficulty is incremented function onIncrement() level = level + 1 - incrementTime = incrementTime + 5 - e_messageAddImportant("level: "..(level).." / time: "..incrementTime, 150) - - if smax < 4 then - smax = smax + 1; - else - smin = smin + 1; - smax = smin; + e_messageAddImportant("level: "..(level).." / time: "..incrementTime, 120) + if (not completed) then + incrementTime = incrementTime + 5 + smin = smin + 1 + if (smin > smax) then + smin = 2 + smax = smax + 1 + if (smax > 4) then + completed = true + l_setSpeedInc(0.1) -- Enable the speed increment + incrementTime = 30 + end + end + end + if (completed) then + smin = math.random(2, 3) + smax = clamp(smin + math.random(0, 3), smin, 5) end range = "("..(smin * 2).."/"..(smax * 2).."]" @@ -132,4 +159,9 @@ function onUpdate(mFrameTime) steam_unlockAchievement("a16_slither") achievementUnlocked = true end + + if not hardAchievementUnlocked and l_getLevelTime() > 30 and u_getDifficultyMult() > 1.8 then + steam_unlockAchievement("a42_slither_hard") + hardAchievementUnlocked = true + end end diff --git a/_RELEASE/Packs/orthoplex/Scripts/Levels/bipolarity.lua b/_RELEASE/Packs/orthoplex/Scripts/Levels/bipolarity.lua index 063cdf0a..099647f1 100644 --- a/_RELEASE/Packs/orthoplex/Scripts/Levels/bipolarity.lua +++ b/_RELEASE/Packs/orthoplex/Scripts/Levels/bipolarity.lua @@ -13,6 +13,8 @@ lastRotationDir = 0 swapped = false rotSpeed = 0.25 rotSpeedMax = 0.9 +achievementUnlocked = false +hardAchievementUnlocked = false FloatingWall = {} FloatingWall.__index = FloatingWall @@ -170,7 +172,7 @@ function addPattern(mKey) setDirection(direction + getRandomDir()) end - -- local delay = getPerfectDelayDM(THICKNESS) * 5.6 + -- local delay = getPerfectDelay(THICKNESS) * 5.6 -- t_wait(delay) beat = getBPMToBeatPulseDelay(180) / getMusicDMSyncFactor() @@ -297,6 +299,16 @@ function onUpdate(mFrameTime) end end end + + if not achievementUnlocked and l_getLevelTime() > 45 and u_getDifficultyMult() >= 1 then + steam_unlockAchievement("a36_bipolarity") + achievementUnlocked = true + end + + if not hardAchievementUnlocked and l_getLevelTime() > 30 and u_getDifficultyMult() > 1.5 then + steam_unlockAchievement("a37_bipolarity_hard") + hardAchievementUnlocked = true + end end function swapStyle() diff --git a/_RELEASE/Packs/tutorial/Scripts/Levels/babysteps.lua b/_RELEASE/Packs/tutorial/Scripts/Levels/babysteps.lua index 71f3cfc7..f56e51ef 100644 --- a/_RELEASE/Packs/tutorial/Scripts/Levels/babysteps.lua +++ b/_RELEASE/Packs/tutorial/Scripts/Levels/babysteps.lua @@ -7,7 +7,7 @@ u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "commonpatterns. function addPattern(mKey) if mKey == 0 then pBarrageSpiral(u_rndInt(1, 2), 1, 1) elseif mKey == 1 then pInverseBarrage(0) - elseif mKey == 2 then pAltBarrage(u_rndInt(1, 3), 2) + elseif mKey == 2 then pAltBarrage(u_rndInt(2, 4), 2) elseif mKey == 3 then pSpiral(12, 0) end end diff --git a/_RELEASE/Packs/workshopexample/Scripts/Levels/examplelevel.lua b/_RELEASE/Packs/workshopexample/Scripts/Levels/examplelevel.lua index f3774941..ce519070 100644 --- a/_RELEASE/Packs/workshopexample/Scripts/Levels/examplelevel.lua +++ b/_RELEASE/Packs/workshopexample/Scripts/Levels/examplelevel.lua @@ -6,7 +6,7 @@ u_execDependencyScript("ohvrvanilla", "base", "vittorio romeo", "commonpatterns. -- This function adds a pattern to the level "timeline" based on a numeric key. function addPattern(mKey) - if mKey == 0 then pAltBarrage(u_rndInt(2, 4), 2) + if mKey == 0 then pAltBarrage(u_rndInt(3, 5), 2) elseif mKey == 1 then pMirrorSpiral(u_rndInt(2, 5), getHalfSides() - 3) elseif mKey == 2 then pBarrageSpiral(u_rndInt(0, 3), 1, 1) elseif mKey == 3 then pInverseBarrage(0) diff --git a/_RELEASE/config.json b/_RELEASE/config.json index 7ee03e70..0a2c6165 100644 --- a/_RELEASE/config.json +++ b/_RELEASE/config.json @@ -32,6 +32,7 @@ "j_swap" : 5,
"joystick_deadzone" : 5.0,
"key_icons_scale" : 0.750,
+ "last_login_username" : "vee",
"limit_fps" : false,
"max_fps" : 200,
"music_speed_dm_sync" : true,
@@ -49,10 +50,11 @@ "player_speed" : 9.449999809265137,
"pulse_enabled" : true,
"rotate_to_start" : false,
+ "save_last_login_username" : true,
"save_local_best_replay_to_file" : true,
"server_control_port" : 50506,
- "server_ip" : "139.162.199.162",
- "server_level_whitelist" :
+ "server_ip" : "127.0.0.1",
+ "server_level_whitelist" :
[
"ohvrvanilla_vittorio_romeo_cube_1_apeirogon_m_0.35",
"ohvrvanilla_vittorio_romeo_cube_1_apeirogon_m_1",
@@ -86,7 +88,38 @@ "ohvrvanilla_vittorio_romeo_cube_1_seconddimension_m_2.2",
"ohvrvanilla_vittorio_romeo_orthoplex_1_bipolarity_m_0.5",
"ohvrvanilla_vittorio_romeo_orthoplex_1_bipolarity_m_1",
- "ohvrvanilla_vittorio_romeo_orthoplex_1_bipolarity_m_1.8"
+ "ohvrvanilla_vittorio_romeo_orthoplex_1_bipolarity_m_1.8",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_acceleradiant_m_0.85",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_acceleradiant_m_1",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_acceleradiant_m_1.8",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_acceleradiant_m_2.4",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_centrifugal_m_0.6",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_centrifugal_m_1",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_centrifugal_m_1.6",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_disc-o_m_0.5",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_disc-o_m_1",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_disc-o_m_2",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_disc-o_m_2.8",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_g-force_m_0.6",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_g-force_m_1",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_g-force_m_1.6",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_g-force_m_2.8",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_incongruence_m_0.5",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_incongruence_m_1",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_incongruence_m_1.8",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_massacre_m_0.5",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_massacre_m_1",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_massacre_m_1.6",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_polyhedrug_m_0.75",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_polyhedrug_m_1",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_polyhedrug_m_1.8",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_reppaws_m_0.5",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_reppaws_m_1",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_reppaws_m_1.8",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_slither_m_0.5",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_slither_m_1",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_slither_m_1.5",
+ "ohvrvanilla_vittorio_romeo_hypercube_1_slither_m_2"
],
"server_local" : true,
"server_port" : 50505,
@@ -94,117 +127,118 @@ "show_fps" : true,
"show_key_icons" : true,
"show_level_info" : true,
+ "show_login_at_startup" : false,
"show_messages" : true,
"show_status_text" : true,
"show_timer" : true,
"show_tracked_variables" : true,
"sound_volume" : 85.0,
- "t_down" :
+ "t_down" :
[
[ "" ],
[ "" ],
[ "" ],
[ "" ]
],
- "t_exit" :
+ "t_exit" :
[
[ "kT" ],
[ "bXButton2" ],
[ "" ],
[ "" ]
],
- "t_focus" :
+ "t_focus" :
[
[ "kLShift" ],
[ "bXButton1" ],
[ "" ],
[ "" ]
],
- "t_force_restart" :
+ "t_force_restart" :
[
[ "kUp" ],
[ "kR" ],
[ "" ],
[ "" ]
],
- "t_lua_console" :
+ "t_lua_console" :
[
[ "kF1" ],
[ "" ],
[ "" ],
[ "" ]
],
- "t_next" :
+ "t_next" :
[
[ "kPageDown" ],
[ "" ],
[ "" ],
[ "" ]
],
- "t_pause" :
+ "t_pause" :
[
[ "kF2" ],
[ "" ],
[ "" ],
[ "" ]
],
- "t_previous" :
+ "t_previous" :
[
[ "kPageUp" ],
[ "" ],
[ "" ],
[ "" ]
],
- "t_replay" :
+ "t_replay" :
[
[ "kY" ],
[ "" ],
[ "" ],
[ "" ]
],
- "t_restart" :
+ "t_restart" :
[
[ "bMiddle" ],
[ "kSpace" ],
[ "kReturn" ],
[ "" ]
],
- "t_rotate_ccw" :
+ "t_rotate_ccw" :
[
[ "kA" ],
[ "kLeft" ],
[ "bLeft" ],
[ "" ]
],
- "t_rotate_cw" :
+ "t_rotate_cw" :
[
[ "kD" ],
[ "kRight" ],
[ "bRight" ],
[ "" ]
],
- "t_screenshot" :
+ "t_screenshot" :
[
[ "kF12" ],
[ "" ],
[ "" ],
[ "" ]
],
- "t_select" :
+ "t_select" :
[
[ "kSpace" ],
[ "bMiddle" ],
[ "" ],
[ "" ]
],
- "t_swap" :
+ "t_swap" :
[
[ "bMiddle" ],
[ "kSpace" ],
[ "" ],
[ "" ]
],
- "t_up" :
+ "t_up" :
[
[ "" ],
[ "" ],
@@ -217,7 +251,7 @@ "timescale" : 1.0,
"vsync" : false,
"windowed_auto_resolution" : false,
- "windowed_height" : 580,
- "windowed_width" : 1019,
- "zoom_factor" : 1.324137926101685
+ "windowed_height" : 715,
+ "windowed_width" : 1275,
+ "zoom_factor" : 1.074125885963440
}
diff --git a/art/a38_disco_hard_l.png b/art/a38_disco_hard_l.png Binary files differnew file mode 100644 index 00000000..91ebfa45 --- /dev/null +++ b/art/a38_disco_hard_l.png diff --git a/art/a38_disco_hard_u.png b/art/a38_disco_hard_u.png Binary files differnew file mode 100644 index 00000000..b755e7d8 --- /dev/null +++ b/art/a38_disco_hard_u.png diff --git a/art/a39_acceleradiant_hard_l.png b/art/a39_acceleradiant_hard_l.png Binary files differnew file mode 100644 index 00000000..868ebe7c --- /dev/null +++ b/art/a39_acceleradiant_hard_l.png diff --git a/art/a39_acceleradiant_hard_u.png b/art/a39_acceleradiant_hard_u.png Binary files differnew file mode 100644 index 00000000..e2107c1e --- /dev/null +++ b/art/a39_acceleradiant_hard_u.png diff --git a/art/a40_gforce_hard_l.png b/art/a40_gforce_hard_l.png Binary files differnew file mode 100644 index 00000000..fbd84532 --- /dev/null +++ b/art/a40_gforce_hard_l.png diff --git a/art/a40_gforce_hard_u.png b/art/a40_gforce_hard_u.png Binary files differnew file mode 100644 index 00000000..6a17f1a4 --- /dev/null +++ b/art/a40_gforce_hard_u.png diff --git a/art/a41_incongruence_hard_l.png b/art/a41_incongruence_hard_l.png Binary files differnew file mode 100644 index 00000000..7964f934 --- /dev/null +++ b/art/a41_incongruence_hard_l.png diff --git a/art/a41_incongruence_hard_u.png b/art/a41_incongruence_hard_u.png Binary files differnew file mode 100644 index 00000000..c22555cd --- /dev/null +++ b/art/a41_incongruence_hard_u.png diff --git a/art/a42_slither_hard_l.png b/art/a42_slither_hard_l.png Binary files differnew file mode 100644 index 00000000..5677d110 --- /dev/null +++ b/art/a42_slither_hard_l.png diff --git a/art/a42_slither_hard_u.png b/art/a42_slither_hard_u.png Binary files differnew file mode 100644 index 00000000..304800b8 --- /dev/null +++ b/art/a42_slither_hard_u.png diff --git a/art/a43_polyhedrug_hard_l.png b/art/a43_polyhedrug_hard_l.png Binary files differnew file mode 100644 index 00000000..26f20816 --- /dev/null +++ b/art/a43_polyhedrug_hard_l.png diff --git a/art/a43_polyhedrug_hard_u.png b/art/a43_polyhedrug_hard_u.png Binary files differnew file mode 100644 index 00000000..727056ae --- /dev/null +++ b/art/a43_polyhedrug_hard_u.png diff --git a/art/a44_reppaws_hard_l.png b/art/a44_reppaws_hard_l.png Binary files differnew file mode 100644 index 00000000..a71b194d --- /dev/null +++ b/art/a44_reppaws_hard_l.png diff --git a/art/a44_reppaws_hard_u.png b/art/a44_reppaws_hard_u.png Binary files differnew file mode 100644 index 00000000..ddbcf913 --- /dev/null +++ b/art/a44_reppaws_hard_u.png diff --git a/art/a45_centrifugalforce_hard_l.png b/art/a45_centrifugalforce_hard_l.png Binary files differnew file mode 100644 index 00000000..9b3e16e7 --- /dev/null +++ b/art/a45_centrifugalforce_hard_l.png diff --git a/art/a45_centrifugalforce_hard_u.png b/art/a45_centrifugalforce_hard_u.png Binary files differnew file mode 100644 index 00000000..84870b07 --- /dev/null +++ b/art/a45_centrifugalforce_hard_u.png diff --git a/art/a46_massacre_hard_l.png b/art/a46_massacre_hard_l.png Binary files differnew file mode 100644 index 00000000..54225da2 --- /dev/null +++ b/art/a46_massacre_hard_l.png diff --git a/art/a46_massacre_hard_u.png b/art/a46_massacre_hard_u.png Binary files differnew file mode 100644 index 00000000..58cd673e --- /dev/null +++ b/art/a46_massacre_hard_u.png diff --git a/art/a47_hypercubegod_l.png b/art/a47_hypercubegod_l.png Binary files differnew file mode 100644 index 00000000..3557726d --- /dev/null +++ b/art/a47_hypercubegod_l.png diff --git a/art/a47_hypercubegod_u.png b/art/a47_hypercubegod_u.png Binary files differnew file mode 100644 index 00000000..5d32f34a --- /dev/null +++ b/art/a47_hypercubegod_u.png diff --git a/art/font/Open Square Bold.sfd b/art/font/Open Square Bold.sfd index 49e65fc6..350de155 100644 --- a/art/font/Open Square Bold.sfd +++ b/art/font/Open Square Bold.sfd @@ -3,9 +3,9 @@ FontName: OpenSquare-Bold FullName: Open Square Bold FamilyName: OpenSquare Weight: Bold -Copyright: Copyright (c) 2020, John Kline +Copyright: Copyright (c) 2021, John Kline UComments: "2020-10-14: Created with FontForge (http://fontforge.org)" -Version: 001.000 +Version: 001.010 ItalicAngle: 0 UnderlinePosition: -102 UnderlineWidth: 51 @@ -22,7 +22,7 @@ OS2Version: 0 OS2_WeightWidthSlopeOnly: 0 OS2_UseTypoMetrics: 1 CreationTime: 1602678841 -ModificationTime: 1623046305 +ModificationTime: 1634863760 PfmFamily: 17 TTFWeight: 700 TTFWidth: 5 @@ -58,6 +58,8 @@ BeginPrivate: 1 BlueValues 13 [0 0 655 655] EndPrivate Grid +-341 1331 m 0 + -341 -717 l 1024 -1024 655.360351562 m 0 2048 655.360351562 l 1024 Named: "Letter Top" @@ -1162,6 +1164,7 @@ SplineSet 426 500 l 1 426 379 l 1 EndSplineSet +Validated: 1 EndChar StartChar: g @@ -1226,8 +1229,8 @@ SplineSet 50 500 l 1 187 500 l 1 187 655 m 1 - 187 534 l 1 - 50 534 l 1 + 187 564 l 1 + 50 564 l 1 50 655 l 1 187 655 l 1 EndSplineSet @@ -1251,8 +1254,8 @@ SplineSet 50 -4 l 1 191 -4 l 1 424 655 m 1 - 424 534 l 1 - 287 534 l 1 + 424 554 l 1 + 287 554 l 1 287 655 l 1 424 655 l 1 EndSplineSet @@ -2015,7 +2018,7 @@ StartChar: exclam Encoding: 33 33 75 Width: 221 VWidth: 819 -Flags: W +Flags: HW HStem: -15 129<46 175> VStem: 46 129<-15 114 181 671> LayerCount: 2 @@ -2023,8 +2026,8 @@ Fore SplineSet 46 671 m 1 175 671 l 1 - 175 181 l 1 - 46 181 l 1 + 175 230 l 5 + 46 230 l 5 46 671 l 1 61 655 m 1025 46 114 m 1 @@ -2744,15 +2747,15 @@ StartChar: exclamdown Encoding: 161 161 95 Width: 200 VWidth: 565 -Flags: W +Flags: HW HStem: 233 110<46 155> VStem: 46 109<-212 185 233 343> LayerCount: 2 Fore SplineSet 46 -212 m 1 - 46 185 l 1 - 155 185 l 1 + 46 140 l 1 + 155 140 l 1 155 -212 l 1 46 -212 l 1 61 -197 m 1025 @@ -5193,11 +5196,11 @@ Flags: HW LayerCount: 2 Fore SplineSet -190 503 m 1 +190 533 m 5 50 655 l 1 166 655 l 1 - 308 503 l 1 - 190 503 l 1 + 308 533 l 5 + 190 533 l 5 76 655 m 1025 248 500 m 1 248 0 l 1 @@ -5216,12 +5219,11 @@ Flags: HW LayerCount: 2 Fore SplineSet -50 503 m 1 +50 533 m 1 189 655 l 1 306 655 l 1 - 166 503 l 1 - 50 503 l 1 -76 526 m 1025 + 166 533 l 1 + 50 533 l 1 247 500 m 1 247 0 l 1 110 0 l 1 @@ -5235,27 +5237,26 @@ StartChar: icircumflex Encoding: 238 238 172 Width: 291 VWidth: 818 -Flags: HW +Flags: HWO LayerCount: 2 Fore SplineSet -251 579 m 5 - 251 527 l 5 - 197 527 l 5 - 145 578 l 5 - 98 527 l 5 - 41 527 l 5 - 41 580 l 5 - 110 655 l 5 - 183 655 l 5 - 251 579 l 5 +251 589 m 1 + 251 537 l 1 + 197 537 l 1 + 145 583 l 1 + 98 537 l 1 + 41 537 l 1 + 41 590 l 1 + 110 655 l 1 + 183 655 l 1 + 251 589 l 1 215 500 m 1 215 0 l 1 78 0 l 1 78 500 l 1 215 500 l 1 EndSplineSet -Validated: 1 EndChar StartChar: idieresis @@ -5266,17 +5267,17 @@ Flags: HW LayerCount: 2 Fore SplineSet -230 542 m 1 +230 562 m 5 230 655 l 1 351 655 l 1 - 351 542 l 1 - 230 542 l 1 + 351 562 l 5 + 230 562 l 5 242 655 m 1025 -50 542 m 1 +50 562 m 5 50 655 l 1 171 655 l 1 - 171 542 l 1 - 50 542 l 1 + 171 562 l 5 + 50 562 l 5 62 655 m 1025 270 500 m 1 270 0 l 1 diff --git a/art/font/Open Square.sfd b/art/font/Open Square.sfd index 4f3f85a0..0f54b444 100644 --- a/art/font/Open Square.sfd +++ b/art/font/Open Square.sfd @@ -3,8 +3,8 @@ FontName: OpenSquare-Regular FullName: Open Square Regular FamilyName: OpenSquare Weight: Regular -Copyright: Copyright (c) 2020, John Kline -Version: 001.000 +Copyright: Copyright (c) 2021, John Kline +Version: 001.010 ItalicAngle: 0 UnderlinePosition: -102 UnderlineWidth: 51 @@ -22,7 +22,7 @@ OS2Version: 4 OS2_WeightWidthSlopeOnly: 0 OS2_UseTypoMetrics: 1 CreationTime: 1602678841 -ModificationTime: 1623045928 +ModificationTime: 1634863593 PfmFamily: 17 TTFWeight: 400 TTFWidth: 5 @@ -866,8 +866,8 @@ Fore SplineSet 61 655 m 1,0,-1 160 655 l 1,1,-1 - 160 197 l 1,2,-1 - 61 197 l 1,3,-1 + 160 246 l 1,2,-1 + 61 246 l 1,3,-1 61 655 l 1,0,-1 61 655 m 1024,4,-1 61 98 m 1,5,-1 @@ -2610,18 +2610,17 @@ Flags: W LayerCount: 2 Fore SplineSet -164 0 m 5,0,-1 - 50 0 l 5,1,-1 - 50 500 l 5,2,-1 - 164 500 l 5,3,-1 - 164 0 l 5,0,-1 -164 557 m 1,4,-1 - 50 557 l 1,5,-1 +164 0 m 1,0,-1 + 50 0 l 1,1,-1 + 50 500 l 1,2,-1 + 164 500 l 1,3,-1 + 164 0 l 1,0,-1 +164 577 m 5,4,-1 + 50 577 l 5,5,-1 50 655 l 1,6,-1 164 655 l 1,7,-1 - 164 557 l 1,4,-1 + 164 577 l 5,4,-1 EndSplineSet -Validated: 1 EndChar StartChar: j @@ -2631,22 +2630,21 @@ Flags: W LayerCount: 2 Fore SplineSet -264 -107 m 21,0,-1 +264 -107 m 17,0,-1 264 500 l 1,1,-1 378 500 l 1,2,-1 - 378 -205 l 5,3,-1 - 50 -205 l 5,4,-1 - 50 -27 l 5,5,-1 - 164 -27 l 5,6,-1 - 164 -107 l 13,7,-1 - 264 -107 l 21,0,-1 -378 557 m 1,8,-1 - 264 557 l 1,9,-1 + 378 -205 l 1,3,-1 + 50 -205 l 1,4,-1 + 50 -27 l 1,5,-1 + 164 -27 l 1,6,-1 + 164 -107 l 9,7,-1 + 264 -107 l 17,0,-1 +378 577 m 5,8,-1 + 264 577 l 5,9,-1 264 655 l 1,10,-1 378 655 l 1,11,-1 - 378 557 l 1,8,-1 + 378 577 l 5,8,-1 EndSplineSet -Validated: 1 EndChar StartChar: k @@ -2822,6 +2820,7 @@ SplineSet 164 402 l 25,5,-1 164 0 l 17,0,-1 EndSplineSet +Validated: 1 EndChar StartChar: s @@ -3108,8 +3107,8 @@ LayerCount: 2 Fore SplineSet 61 -197 m 1,0,-1 - 61 170 l 1,1,-1 - 139 170 l 1,2,-1 + 61 123 l 5,1,-1 + 139 123 l 5,2,-1 139 -197 l 1,3,-1 61 -197 l 1,0,-1 61 -197 m 1024,4,-1 @@ -5381,19 +5380,18 @@ Flags: W LayerCount: 2 Fore SplineSet -50 655 m 5,0,-1 - 135 655 l 5,1,-1 - 255 526 l 5,2,-1 - 169 526 l 5,3,-1 - 50 655 l 5,0,-1 -50 655 m 1028,4,-1 +50 655 m 1,0,-1 + 135 655 l 1,1,-1 + 255 556 l 5,2,-1 + 169 556 l 5,3,-1 + 50 655 l 1,0,-1 +50 655 m 1024,4,-1 210 0 m 1,5,-1 96 0 l 1,6,-1 96 500 l 1,7,-1 210 500 l 1,8,-1 210 0 l 1,5,-1 EndSplineSet -Validated: 1 EndChar StartChar: iacute @@ -5404,19 +5402,18 @@ Flags: W LayerCount: 2 Fore SplineSet -50 526 m 5,0,-1 - 168 655 l 5,1,-1 - 254 655 l 5,2,-1 - 135 526 l 5,3,-1 - 50 526 l 5,0,-1 -50 526 m 1028,4,-1 +50 556 m 5,0,-1 + 168 655 l 1,1,-1 + 254 655 l 1,2,-1 + 135 556 l 5,3,-1 + 50 556 l 5,0,-1 +50 556 m 1028,4,-1 209 0 m 1,5,-1 95 0 l 1,6,-1 95 500 l 1,7,-1 209 500 l 1,8,-1 209 0 l 1,5,-1 EndSplineSet -Validated: 1 EndChar StartChar: icircumflex @@ -5443,27 +5440,26 @@ SplineSet 192 500 l 1,12,-1 192 0 l 1,9,-1 EndSplineSet -Validated: 1 EndChar StartChar: idieresis Encoding: 239 239 176 Width: 378 VWidth: 818 -Flags: W +Flags: WO LayerCount: 2 Fore SplineSet 230 655 m 1,0,-1 328 655 l 1,1,-1 - 328 565 l 1,2,-1 - 230 565 l 1,3,-1 + 328 585 l 1,2,-1 + 230 585 l 1,3,-1 230 655 l 1,0,-1 230 655 m 1024,4,-1 50 655 m 1,5,-1 148 655 l 1,6,-1 - 148 565 l 1,7,-1 - 50 565 l 1,8,-1 + 148 585 l 1,7,-1 + 50 585 l 1,8,-1 50 655 l 1,5,-1 50 655 m 1024,9,-1 247 0 m 1,10,-1 @@ -5472,7 +5468,6 @@ SplineSet 247 500 l 1,13,-1 247 0 l 1,10,-1 EndSplineSet -Validated: 1 EndChar StartChar: eth diff --git a/art/font/OpenSquare-Bold.ttf b/art/font/OpenSquare-Bold.ttf Binary files differindex eef821f9..0ebf62e5 100644 --- a/art/font/OpenSquare-Bold.ttf +++ b/art/font/OpenSquare-Bold.ttf diff --git a/art/font/OpenSquare-Regular.ttf b/art/font/OpenSquare-Regular.ttf Binary files differindex aef149d6..b695c12c 100644 --- a/art/font/OpenSquare-Regular.ttf +++ b/art/font/OpenSquare-Regular.ttf diff --git a/art/font/OpenSquare.ttc b/art/font/OpenSquare.ttc Binary files differindex 4744b68a..1bb8b949 100644 --- a/art/font/OpenSquare.ttc +++ b/art/font/OpenSquare.ttc diff --git a/include/SSVOpenHexagon/Components/CCustomWallManager.hpp b/include/SSVOpenHexagon/Components/CCustomWallManager.hpp index 5c528a09..c9e66665 100644 --- a/include/SSVOpenHexagon/Components/CCustomWallManager.hpp +++ b/include/SSVOpenHexagon/Components/CCustomWallManager.hpp @@ -22,8 +22,6 @@ class CPlayer; class CCustomWallManager { - // TODO (P2): consider using a sparse integer set - std::vector<CCustomWall> _customWalls; std::vector<CCustomWallHandle> _freeHandles; std::vector<bool> _handleAvailable; @@ -91,8 +89,6 @@ public: [[nodiscard]] std::uint8_t getKillingSide(const CCustomWallHandle cwHandle); - [[nodiscard]] bool isOverlappingPlayer(const CCustomWallHandle cwHandle); - void clear(); void draw(Utils::FastVertexVectorQuads& wallQuads); diff --git a/include/SSVOpenHexagon/Core/CustomTimeline.hpp b/include/SSVOpenHexagon/Core/CustomTimeline.hpp new file mode 100644 index 00000000..c42738ed --- /dev/null +++ b/include/SSVOpenHexagon/Core/CustomTimeline.hpp @@ -0,0 +1,17 @@ +// Copyright (c) 2013-2020 Vittorio Romeo +// License: Academic Free License ("AFL") v. 3.0 +// AFL License page: https://opensource.org/licenses/AFL-3.0 + +#pragma once + +#include "SSVOpenHexagon/Utils/Timeline2.hpp" + +namespace hg { + +struct CustomTimeline +{ + Utils::timeline2 _timeline; + Utils::timeline2_runner _runner; +}; + +} // namespace hg diff --git a/include/SSVOpenHexagon/Core/CustomTimelineHandle.hpp b/include/SSVOpenHexagon/Core/CustomTimelineHandle.hpp new file mode 100644 index 00000000..d76be6b6 --- /dev/null +++ b/include/SSVOpenHexagon/Core/CustomTimelineHandle.hpp @@ -0,0 +1,11 @@ +// Copyright (c) 2013-2020 Vittorio Romeo +// License: Academic Free License ("AFL") v. 3.0 +// AFL License page: https://opensource.org/licenses/AFL-3.0 + +#pragma once + +namespace hg { + +using CustomTimelineHandle = int; + +} // namespace hg diff --git a/include/SSVOpenHexagon/Core/CustomTimelineManager.hpp b/include/SSVOpenHexagon/Core/CustomTimelineManager.hpp new file mode 100644 index 00000000..b0b5243d --- /dev/null +++ b/include/SSVOpenHexagon/Core/CustomTimelineManager.hpp @@ -0,0 +1,44 @@ +// Copyright (c) 2013-2020 Vittorio Romeo +// License: Academic Free License ("AFL") v. 3.0 +// AFL License page: https://opensource.org/licenses/AFL-3.0 + +#pragma once + +#include "SSVOpenHexagon/Core/CustomTimelineHandle.hpp" + +#include <chrono> +#include <vector> + +namespace hg { + +struct CustomTimeline; + +class CustomTimelineManager +{ +public: + using Clock = std::chrono::high_resolution_clock; + using TimePoint = std::chrono::time_point<Clock>; + +private: + std::vector<CustomTimeline> _timelines; + +public: + CustomTimelineManager(); + ~CustomTimelineManager(); + + [[nodiscard]] bool isHandleValid( + const CustomTimelineHandle h) const noexcept; + + void clear() noexcept; + + void updateAllTimelines(const TimePoint tp); + + [[nodiscard]] CustomTimelineHandle create(); + + [[nodiscard]] CustomTimeline& get(const CustomTimelineHandle h) noexcept; + + [[nodiscard]] const CustomTimeline& get( + const CustomTimelineHandle h) const noexcept; +}; + +} // namespace hg diff --git a/include/SSVOpenHexagon/Core/HGStatus.hpp b/include/SSVOpenHexagon/Core/HGStatus.hpp index ef225651..853022c9 100644 --- a/include/SSVOpenHexagon/Core/HGStatus.hpp +++ b/include/SSVOpenHexagon/Core/HGStatus.hpp @@ -55,22 +55,22 @@ public: void start() noexcept; // Number of seconds that have passed since last increment - [[nodiscard]] double getIncrementTimeSeconds() noexcept; + [[nodiscard]] double getIncrementTimeSeconds() const noexcept; // Game timer, in seconds - [[nodiscard]] double getTimeSeconds() noexcept; + [[nodiscard]] double getTimeSeconds() const noexcept; // Absolute time, as time point - [[nodiscard]] TimePoint getCurrentTP() noexcept; + [[nodiscard]] TimePoint getCurrentTP() const noexcept; // Game timer, as time point - [[nodiscard]] TimePoint getTimeTP() noexcept; + [[nodiscard]] TimePoint getTimeTP() const noexcept; // Level start, as time point - [[nodiscard]] TimePoint getLevelStartTP() noexcept; + [[nodiscard]] TimePoint getLevelStartTP() const noexcept; // `true` if we are currently paused - [[nodiscard]] bool isTimePaused() noexcept; + [[nodiscard]] bool isTimePaused() const noexcept; // Start a new pause or extend the current pause by `seconds` void pauseTime(const double seconds) noexcept; diff --git a/include/SSVOpenHexagon/Core/HexagonGame.hpp b/include/SSVOpenHexagon/Core/HexagonGame.hpp index 2769f5d0..211eec36 100644 --- a/include/SSVOpenHexagon/Core/HexagonGame.hpp +++ b/include/SSVOpenHexagon/Core/HexagonGame.hpp @@ -4,17 +4,22 @@ #pragma once +#include "SSVOpenHexagon/Core/CustomTimelineManager.hpp" #include "SSVOpenHexagon/Core/HGStatus.hpp" #include "SSVOpenHexagon/Core/RandomNumberGenerator.hpp" #include "SSVOpenHexagon/Core/Replay.hpp" + #include "SSVOpenHexagon/Data/LevelStatus.hpp" #include "SSVOpenHexagon/Data/MusicData.hpp" #include "SSVOpenHexagon/Data/StyleData.hpp" + #include "SSVOpenHexagon/Components/CPlayer.hpp" + #include "SSVOpenHexagon/Utils/Utils.hpp" #include "SSVOpenHexagon/Utils/LuaWrapper.hpp" #include "SSVOpenHexagon/Utils/FastVertexVector.hpp" #include "SSVOpenHexagon/Utils/Timeline2.hpp" + #include "SSVOpenHexagon/Components/CCustomWallManager.hpp" #include <SSVStart/GameSystem/GameSystem.hpp> @@ -146,6 +151,8 @@ private: Utils::timeline2 messageTimeline; Utils::timeline2_runner messageTimelineRunner; + CustomTimelineManager _customTimelineManager; + sf::Text messageText; sf::Text pbText; @@ -230,6 +237,7 @@ private: void initLua_AudioControl(); void initLua_MainTimeline(); void initLua_EventTimeline(); + void initLua_CustomTimelines(); void initLua_LevelControl(); void initLua_StyleControl(); void initLua_WallCreation(); @@ -245,19 +253,18 @@ private: const SpeedData& mCurve, float mHueMod); public: - // TODO (P2): For testing + // ------------------------------------------------------------------------ + // Testing-related utilities std::function<void(const replay_file&)> onDeathReplayCreated; - // TODO (P2): For testing - void setMustStart(const bool x) - { - mustStart = x; - } + void setMustStart(const bool x); - // TODO (P2): For testing bool executeRandomInputs{false}; bool alwaysSpinRight{false}; + // ------------------------------------------------------------------------ + // Lua stuff + void initLuaAndPrintDocs(); void luaExceptionLippincottHandler(const std::string& mName); @@ -290,6 +297,10 @@ private: std::optional<double> fastForwardTarget; void fastForwardTo(const double target); + // Advance by ticks + std::optional<int> advanceTickCount; + void advanceByTicks(const int nTicks); + // Update methods void update(ssvu::FT mFT, const float timescale); void updateInput(); @@ -301,6 +312,7 @@ private: void updateIncrement(); void updateEvents(ssvu::FT mFT); void updateLevel(ssvu::FT mFT); + void updateCustomTimelines(); void updateCustomWalls(ssvu::FT mFT); void updatePulse(ssvu::FT mFT); void updateBeatPulse(ssvu::FT mFT); diff --git a/include/SSVOpenHexagon/Core/MenuGame.hpp b/include/SSVOpenHexagon/Core/MenuGame.hpp index 375306af..cf720119 100644 --- a/include/SSVOpenHexagon/Core/MenuGame.hpp +++ b/include/SSVOpenHexagon/Core/MenuGame.hpp @@ -189,6 +189,7 @@ private: [[nodiscard]] ssvms::Menu* getCurrentMenu() noexcept; [[nodiscard]] bool isInMenu() noexcept; + void ignoreInputsAfterMenuExec(); //--------------------------------------- // State changes @@ -237,10 +238,10 @@ private: MenuFont txtProf; MenuFont txtLoadBig; MenuFont txtLoadSmall; + MenuFont txtRandomTip; MenuFont txtMenuBig; MenuFont txtMenuSmall; MenuFont txtProfile; - MenuFont txtRandomTip; MenuFont txtInstructionsBig; MenuFont txtInstructionsMedium; MenuFont txtInstructionsSmall; @@ -422,6 +423,10 @@ private: static inline constexpr float baseScrollSpeed{30.f}; float scrollSpeed{baseScrollSpeed}; + // Login at startup + bool mustShowLoginAtStartup{true}; + void openLoginDialogBoxAndStartLoginProcess(); + // First timer tips bool showFirstTimeTips{false}; bool mustShowFTTMainMenu{true}; @@ -558,6 +563,9 @@ private: void showInputDialogBox(const std::string& msg); void showInputDialogBoxNice(const std::string& title, const std::string& inputType, const std::string& extra = ""); + void showInputDialogBoxNiceWithDefault(const std::string& title, + const std::string& inputType, const std::string& def, + const std::string& extra = ""); public: MenuGame(Steam::steam_manager& mSteamManager, diff --git a/include/SSVOpenHexagon/Core/Replay.hpp b/include/SSVOpenHexagon/Core/Replay.hpp index 5198ee1d..d4628ba3 100644 --- a/include/SSVOpenHexagon/Core/Replay.hpp +++ b/include/SSVOpenHexagon/Core/Replay.hpp @@ -33,7 +33,6 @@ enum class input_bit : unsigned int k_count }; -// TODO (P2): optimize size - `sizeof` this is `4` using input_bitset = std::bitset<static_cast<unsigned int>(input_bit::k_count)>; struct serialization_result diff --git a/include/SSVOpenHexagon/Global/Assets.hpp b/include/SSVOpenHexagon/Global/Assets.hpp index 49689224..75815ab6 100644 --- a/include/SSVOpenHexagon/Global/Assets.hpp +++ b/include/SSVOpenHexagon/Global/Assets.hpp @@ -73,6 +73,7 @@ private: [[nodiscard]] bool loadAllPackDatas(); [[nodiscard]] bool loadAllPackAssets(); + [[nodiscard]] bool loadWorkshopPackDatasFromCache(); [[nodiscard]] bool verifyAllPackDependencies(); [[nodiscard]] bool loadAllLocalProfiles(); @@ -102,6 +103,10 @@ public: ~HGAssets(); + // When the Steam API can not be retrieved, this set holds pack ids + // retrieved from the cache to try and load the workshop packs installed + std::unordered_set<std::string> cachedWorkshopPackIds; + [[nodiscard]] LoadInfo& getLoadResults(); [[nodiscard]] sf::Texture& getTexture(const std::string& mId); diff --git a/include/SSVOpenHexagon/Global/Audio.hpp b/include/SSVOpenHexagon/Global/Audio.hpp index fee871fc..f7c8b7fa 100644 --- a/include/SSVOpenHexagon/Global/Audio.hpp +++ b/include/SSVOpenHexagon/Global/Audio.hpp @@ -45,6 +45,12 @@ public: void pauseMusic(); void stopMusic(); + void setMusicPlayingOffsetSeconds(const float seconds); + void setMusicPlayingOffsetMilliseconds(const int milliseconds); + + [[nodiscard]] float getMusicPlayingOffsetSeconds() const; + [[nodiscard]] int getMusicPlayingOffsetMilliseconds() const; + void stopSounds(); void playSoundOverride(const std::string& id); diff --git a/include/SSVOpenHexagon/Global/Config.hpp b/include/SSVOpenHexagon/Global/Config.hpp index aee80f00..7924c8ed 100644 --- a/include/SSVOpenHexagon/Global/Config.hpp +++ b/include/SSVOpenHexagon/Global/Config.hpp @@ -27,6 +27,7 @@ inline constexpr float TIME_STEP = 60.f / TICKS_PER_SECOND; inline constexpr float TIME_SLICE = 60.f / TICKS_PER_SECOND; void loadConfig(const std::vector<std::string>& mOverridesIds); +void reapplyResolution(); void resetConfigToDefaults(); void resetBindsToDefaults(); void saveConfig(); @@ -78,6 +79,9 @@ void setServerIp(const std::string& mX); void setServerPort(unsigned short mX); void setServerControlPort(unsigned short mX); void setServerLevelWhitelist(const std::vector<std::string>& levelValidators); +void setSaveLastLoginUsername(bool mX); +void setLastLoginUsername(const std::string& mX); +void setShowLoginAtStartup(bool mX); [[nodiscard]] bool getOfficial(); [[nodiscard]] const std::string& getUneligibilityReason(); @@ -142,6 +146,9 @@ void setServerLevelWhitelist(const std::vector<std::string>& levelValidators); [[nodiscard]] unsigned short getServerPort(); [[nodiscard]] unsigned short getServerControlPort(); [[nodiscard]] const std::vector<std::string> getServerLevelWhitelist(); +[[nodiscard]] bool getSaveLastLoginUsername(); +[[nodiscard]] const std::string& getLastLoginUsername(); +[[nodiscard]] bool getShowLoginAtStartup(); // keyboard binds diff --git a/include/SSVOpenHexagon/Global/Version.hpp b/include/SSVOpenHexagon/Global/Version.hpp index c78216e7..2d6af24e 100644 --- a/include/SSVOpenHexagon/Global/Version.hpp +++ b/include/SSVOpenHexagon/Global/Version.hpp @@ -43,7 +43,7 @@ struct GameVersion } }; -inline constexpr GameVersion GAME_VERSION{2, 0, 6}; -inline constexpr auto& GAME_VERSION_STR = "2.0.6"; +inline constexpr GameVersion GAME_VERSION{2, 0, 7}; +inline constexpr auto& GAME_VERSION_STR = "2.0.7"; } // namespace hg diff --git a/include/SSVOpenHexagon/Online/Shared.hpp b/include/SSVOpenHexagon/Online/Shared.hpp index be390e77..789d44bb 100644 --- a/include/SSVOpenHexagon/Online/Shared.hpp +++ b/include/SSVOpenHexagon/Online/Shared.hpp @@ -144,7 +144,7 @@ template <typename T> } // namespace hg -// TODO (P0): leaderboards article on /r/gamedev +// TODO (P1): leaderboards article on /r/gamedev // - fast math issue // - rng advance for graphical things // - check devlogs diff --git a/release_checklist.md b/release_checklist.md index 32ab3553..81706024 100644 --- a/release_checklist.md +++ b/release_checklist.md @@ -2,13 +2,15 @@ ## Server -1. `git push` from main development machine +1. `git push` from main development machine, check the branch 2. Start the `endeavouros64` VirtualBox virtual machine -3. Run `SSVOpenHexagon/vbox/build_and_upload_server.sh` +3. Check the repository's branch on the virtual machine -4. Verify that the server is working with `ssh vittorioromeo@139.162.199.162`, `sudo journalctl -u openhexagon-server -f` +4. Run `SSVOpenHexagon/vbox/build_and_upload_server.sh` + +5. Verify that the server is working with `ssh vittorioromeo@139.162.199.162`, `sudo journalctl -u openhexagon-server -f` ## Client diff --git a/src/SSVOpenHexagon/Core/Audio.cpp b/src/SSVOpenHexagon/Core/Audio.cpp index aefa5d7b..b8d88bc1 100644 --- a/src/SSVOpenHexagon/Core/Audio.cpp +++ b/src/SSVOpenHexagon/Core/Audio.cpp @@ -9,6 +9,7 @@ #include "SSVOpenHexagon/Utils/Concat.hpp" #include "SSVOpenHexagon/Utils/UniquePtr.hpp" +#include <SFML/System/Time.hpp> #include <SSVStart/SoundPlayer/SoundPlayer.hpp> #include <SSVUtils/Core/Log/Log.hpp> @@ -101,6 +102,42 @@ public: } } + void setMusicPlayingOffsetSeconds(const float seconds) + { + if(_music.has_value()) + { + _music->setPlayingOffset(sf::seconds(seconds)); + } + } + + void setMusicPlayingOffsetMilliseconds(const int milliseconds) + { + if(_music.has_value()) + { + _music->setPlayingOffset(sf::milliseconds(milliseconds)); + } + } + + [[nodiscard]] float getMusicPlayingOffsetSeconds() const + { + if(_music.has_value()) + { + return _music->getPlayingOffset().asSeconds(); + } + + return 0.f; + } + + [[nodiscard]] int getMusicPlayingOffsetMilliseconds() const + { + if(_music.has_value()) + { + return _music->getPlayingOffset().asMilliseconds(); + } + + return 0; + } + void stopSounds() { _soundPlayer.stop(); @@ -162,7 +199,7 @@ public: } _music->setLoop(true); - _music->setPlayingOffset(sf::seconds(playingOffsetSeconds)); + setMusicPlayingOffsetSeconds(playingOffsetSeconds); resumeMusic(); return true; @@ -221,6 +258,26 @@ void Audio::stopMusic() impl().stopMusic(); } +void Audio::setMusicPlayingOffsetSeconds(const float seconds) +{ + impl().setMusicPlayingOffsetSeconds(seconds); +} + +void Audio::setMusicPlayingOffsetMilliseconds(const int milliseconds) +{ + impl().setMusicPlayingOffsetMilliseconds(milliseconds); +} + +[[nodiscard]] float Audio::getMusicPlayingOffsetSeconds() const +{ + return impl().getMusicPlayingOffsetSeconds(); +} + +[[nodiscard]] int Audio::getMusicPlayingOffsetMilliseconds() const +{ + return impl().getMusicPlayingOffsetMilliseconds(); +} + void Audio::stopSounds() { impl().stopSounds(); diff --git a/src/SSVOpenHexagon/Core/CCustomWallManager.cpp b/src/SSVOpenHexagon/Core/CCustomWallManager.cpp index 33bbe6c9..4a1b39ed 100644 --- a/src/SSVOpenHexagon/Core/CCustomWallManager.cpp +++ b/src/SSVOpenHexagon/Core/CCustomWallManager.cpp @@ -308,18 +308,6 @@ void CCustomWallManager::setVertexColor4Same( _customWalls[cwHandle].setVertexColor(3, color); } -// TODO (P2): implement -[[nodiscard]] bool CCustomWallManager::isOverlappingPlayer( - const CCustomWallHandle cwHandle) -{ - if(!checkValidHandle(cwHandle, "Attempted to check player overlap")) - { - return false; - } - - return false; // _customWalls[cwHandle].isOverlappingPlayer(); -} - void CCustomWallManager::clear() { _freeHandles.clear(); diff --git a/src/SSVOpenHexagon/Core/CustomTimelineManager.cpp b/src/SSVOpenHexagon/Core/CustomTimelineManager.cpp new file mode 100644 index 00000000..05ab9c2c --- /dev/null +++ b/src/SSVOpenHexagon/Core/CustomTimelineManager.cpp @@ -0,0 +1,71 @@ +// Copyright (c) 2013-2020 Vittorio Romeo +// License: Academic Free License ("AFL") v. 3.0 +// AFL License page: https://opensource.org/licenses/AFL-3.0 + +#pragma once + +#include "SSVOpenHexagon/Core/CustomTimelineManager.hpp" + +#include "SSVOpenHexagon/Core/CustomTimeline.hpp" +#include "SSVOpenHexagon/Core/CustomTimelineHandle.hpp" + +#include "SSVOpenHexagon/Global/Assert.hpp" + +#include <chrono> +#include <cstddef> +#include <vector> + +namespace hg { + +CustomTimelineManager::CustomTimelineManager() = default; + +CustomTimelineManager::~CustomTimelineManager() = default; + +[[nodiscard]] bool CustomTimelineManager::isHandleValid( + const CustomTimelineHandle h) const noexcept +{ + return h >= 0 && h < static_cast<CustomTimelineHandle>(_timelines.size()); +} + +void CustomTimelineManager::clear() noexcept +{ + _timelines.clear(); +} + +void CustomTimelineManager::updateAllTimelines(const TimePoint tp) +{ + for(CustomTimeline& t : _timelines) + { + if(const auto o = t._runner.update(t._timeline, tp); + o == Utils::timeline2_runner::outcome::finished) + { + t._timeline.clear(); + t._runner = {}; + } + } +} + +[[nodiscard]] CustomTimelineHandle CustomTimelineManager::create() +{ + _timelines.emplace_back(); + const CustomTimelineHandle h = _timelines.size() - 1; + + SSVOH_ASSERT(isHandleValid(h)); + return h; +} + +[[nodiscard]] CustomTimeline& CustomTimelineManager::get( + const CustomTimelineHandle h) noexcept +{ + SSVOH_ASSERT(isHandleValid(h)); + return _timelines.at(static_cast<std::size_t>(h)); +} + +[[nodiscard]] const CustomTimeline& CustomTimelineManager::get( + const CustomTimelineHandle h) const noexcept +{ + SSVOH_ASSERT(isHandleValid(h)); + return _timelines.at(static_cast<std::size_t>(h)); +} + +} // namespace hg diff --git a/src/SSVOpenHexagon/Core/HGGraphics.cpp b/src/SSVOpenHexagon/Core/HGGraphics.cpp index 948daf03..7a085104 100644 --- a/src/SSVOpenHexagon/Core/HGGraphics.cpp +++ b/src/SSVOpenHexagon/Core/HGGraphics.cpp @@ -9,6 +9,7 @@ #include "SSVOpenHexagon/Utils/String.hpp" #include "SSVOpenHexagon/Utils/Color.hpp" #include "SSVOpenHexagon/Components/CWall.hpp" +#include "SSVStart/Utils/SFML.hpp" #include <imgui.h> #include <imgui-SFML.h> @@ -484,7 +485,6 @@ void HexagonGame::updateText(ssvu::FT mFT) }; timeText.setCharacterSize(getScaledCharacterSize(70.f)); - timeText.setOrigin(0, 0); // Set information text text.setString(os.str()); @@ -556,43 +556,45 @@ void HexagonGame::drawText_TimeAndStatus(const sf::Color& offsetColor) replayText.setOutlineThickness(0.f); } - const float padding = Config::getTextPadding() * Config::getTextScaling(); - const float offsetRatio = Config::getHeight() / 720.f; + const float padding = + (Config::getTextPadding() * Config::getTextScaling()) / + Config::getZoomFactor(); const sf::Color colorText = getColorText(); if(Config::getShowTimer()) { timeText.setFillColor(colorText); - timeText.setPosition(sf::Vector2f{ - padding, -16.f * offsetRatio * Config::getTextScaling()}); + timeText.setOrigin(ssvs::getLocalNW(timeText)); + timeText.setPosition(sf::Vector2f{padding, padding}); + render(timeText); } if(Config::getShowStatusText()) { text.setFillColor(colorText); + text.setOrigin(ssvs::getLocalNW(text)); text.setPosition( - sf::Vector2f{padding * 1.5f, ssvs::getGlobalBottom(timeText)}); + sf::Vector2f{padding, ssvs::getGlobalBottom(timeText) + padding}); + render(text); } if(Config::getShowFPS()) { fpsText.setFillColor(colorText); - fpsText.setOrigin(0, ssvs::getGlobalHeight(fpsText)); + fpsText.setOrigin(ssvs::getLocalSW(fpsText)); if(Config::getShowLevelInfo() || mustShowReplayUI()) { - fpsText.setPosition(sf::Vector2f{padding, - ssvs::getGlobalTop(levelInfoRectangle) - - ((7.f * (2.f * offsetRatio))) * Config::getTextScaling()}); + fpsText.setPosition(sf::Vector2f{ + padding, ssvs::getGlobalTop(levelInfoRectangle) - padding}); } else { - fpsText.setPosition(sf::Vector2f{ - padding, Config::getHeight() - ((7.f * (2.f * offsetRatio))) * - Config::getTextScaling()}); + fpsText.setPosition( + sf::Vector2f{padding, Config::getHeight() - padding}); } render(fpsText); @@ -602,6 +604,7 @@ void HexagonGame::drawText_TimeAndStatus(const sf::Color& offsetColor) { const float scaling = Config::getKeyIconsScale() / Config::getZoomFactor(); + const float replayPadding = 8.f * scaling; replayText.setFillColor(colorText); diff --git a/src/SSVOpenHexagon/Core/HGScripting.cpp b/src/SSVOpenHexagon/Core/HGScripting.cpp index 248171a8..4268294e 100644 --- a/src/SSVOpenHexagon/Core/HGScripting.cpp +++ b/src/SSVOpenHexagon/Core/HGScripting.cpp @@ -2,20 +2,28 @@ // License: Academic Free License ("AFL") v. 3.0 // AFL License page: https://opensource.org/licenses/AFL-3.0 +#include "SSVOpenHexagon/Core/HexagonGame.hpp" + +#include "SSVOpenHexagon/Components/CCustomWallHandle.hpp" #include "SSVOpenHexagon/Components/CWall.hpp" + +#include "SSVOpenHexagon/Core/CustomTimeline.hpp" +#include "SSVOpenHexagon/Core/CustomTimelineHandle.hpp" +#include "SSVOpenHexagon/Core/CustomTimelineManager.hpp" +#include "SSVOpenHexagon/Core/LuaScripting.hpp" +#include "SSVOpenHexagon/Core/Steam.hpp" + #include "SSVOpenHexagon/Global/Assets.hpp" -#include "SSVOpenHexagon/Global/Config.hpp" #include "SSVOpenHexagon/Global/Audio.hpp" -#include "SSVOpenHexagon/Utils/Utils.hpp" +#include "SSVOpenHexagon/Global/Config.hpp" + #include "SSVOpenHexagon/Utils/Concat.hpp" -#include "SSVOpenHexagon/Utils/ScopeGuard.hpp" #include "SSVOpenHexagon/Utils/LuaMetadata.hpp" #include "SSVOpenHexagon/Utils/LuaMetadataProxy.hpp" -#include "SSVOpenHexagon/Core/HexagonGame.hpp" -#include "SSVOpenHexagon/Components/CCustomWallHandle.hpp" -#include "SSVOpenHexagon/Core/LuaScripting.hpp" +#include "SSVOpenHexagon/Utils/ScopeGuard.hpp" +#include "SSVOpenHexagon/Utils/Timeline2.hpp" #include "SSVOpenHexagon/Utils/TypeWrapper.hpp" -#include "SSVOpenHexagon/Core/Steam.hpp" +#include "SSVOpenHexagon/Utils/Utils.hpp" #include <SSVUtils/Core/Log/Log.hpp> @@ -93,7 +101,10 @@ void HexagonGame::initLua_Utils() addLuaFn(lua, "u_isKeyPressed", [this](int mKey) { - // TODO (P2): this is not saved in replays. Deprecate? + raiseWarning("u_isKeyPressed", + "This function will be removed in a future version of Open " + "Hexagon. Please replace uses of this function with the " + "`onInput` callback."); return window != nullptr && window->getInputState()[ssvs::KKey(mKey)]; @@ -102,7 +113,8 @@ void HexagonGame::initLua_Utils() .doc( "Return `true` if the keyboard key with code `$0` is being " "pressed, `false` otherwise. The key code must match the " - "definition of the SFML `sf::Keyboard::Key` enumeration."); + "definition of the SFML `sf::Keyboard::Key` enumeration. **This " + "function is deprecated and will be removed in a future version."); addLuaFn(lua, "u_haltTime", // [this](double mDuration) @@ -126,7 +138,10 @@ void HexagonGame::initLua_Utils() addLuaFn(lua, "u_isMouseButtonPressed", [this](int mKey) { - // TODO (P2): this is not saved in replays. Deprecate? + raiseWarning("u_isMouseButtonPressed", + "This function will be removed in a future version of Open " + "Hexagon. Please replace uses of this function with the " + "`onInput` callback."); return window != nullptr && window->getInputState()[ssvs::MBtn(mKey)]; @@ -135,7 +150,9 @@ void HexagonGame::initLua_Utils() .doc( "Return `true` if the mouse button with code `$0` is being " "pressed, `false` otherwise. The button code must match the " - "definition of the SFML `sf::Mouse::Button` enumeration."); + "definition of the SFML `sf::Mouse::Button` enumeration. **This " + "function is deprecated and will be removed in a future " + "version."); addLuaFn(lua, "u_isFastSpinning", // [this] { return status.fastSpin > 0; }) @@ -293,6 +310,18 @@ void HexagonGame::initLua_AudioControl() "applies to the particular level where this function is called."); } +static void waitUntilSImpl(const double mDuration, + const HexagonGameStatus& status, Utils::timeline2& timeline) +{ + timeline.append_wait_until_fn( + [&status, mDuration] + { + return status.getLevelStartTP() + + std::chrono::milliseconds( + static_cast<int>(mDuration * 1000.0)); + }); +} + void HexagonGame::initLua_MainTimeline() { addLuaFn(lua, "t_eval", @@ -326,15 +355,7 @@ void HexagonGame::initLua_MainTimeline() addLuaFn(lua, "t_waitUntilS", // [this](double mDuration) - { - timeline.append_wait_until_fn( - [this, mDuration] - { - return status.getLevelStartTP() + - std::chrono::milliseconds( - static_cast<int>(mDuration * 1000.0)); - }); - }) + { waitUntilSImpl(mDuration, status, timeline); }) .arg("duration") .doc( "*Add to the main timeline*: wait until the timer reaches `$0` " @@ -393,15 +414,7 @@ void HexagonGame::initLua_EventTimeline() addLuaFn(lua, "e_waitUntilS", // [this](double mDuration) - { - eventTimeline.append_wait_until_fn( - [this, mDuration] - { - return status.getLevelStartTP() + - std::chrono::milliseconds( - static_cast<int>(mDuration * 1000.0)); - }); - }) + { waitUntilSImpl(mDuration, status, eventTimeline); }) .arg("duration") .doc( "*Add to the event timeline*: wait until the timer reaches `$0` " @@ -457,6 +470,148 @@ void HexagonGame::initLua_EventTimeline() .doc("Remove all previously scheduled messages."); } +void HexagonGame::initLua_CustomTimelines() +{ + addLuaFn(lua, "ct_create", // + [this]() -> CustomTimelineHandle + { return _customTimelineManager.create(); }) + .doc( + "Create a new custom timeline and return a integer handle " + "to it."); + + + const auto checkHandle = [this](CustomTimelineHandle cth, + const char* title) -> bool + { + if(_customTimelineManager.isHandleValid(cth)) + { + return true; + } + + ssvu::lo("CustomTimelineManager") + << "Invalid handle '" << cth << "' during '" << title << "'\n"; + + return false; + }; + + addLuaFn(lua, "ct_eval", + [checkHandle, this](CustomTimelineHandle cth, const std::string& mCode) + { + if(!checkHandle(cth, "ct_eval")) + { + return; + } + + _customTimelineManager.get(cth)._timeline.append_do( + [=, this] { Utils::runLuaCode(lua, mCode); }); + }) + .arg("handle") + .arg("code") + .doc( + "*Add to the custom timeline with handle `$0`*: evaluate the Lua " + "code specified in `$1`."); + + addLuaFn(lua, "ct_kill", // + [checkHandle, this](CustomTimelineHandle cth) + { + if(!checkHandle(cth, "ct_kill")) + { + return; + } + + _customTimelineManager.get(cth)._timeline.append_do( + [this] { death(true); }); + }) + .arg("handle") + .doc("*Add to the custom timeline with handle `$0`*: kill the player."); + + addLuaFn(lua, "ct_stopTime", // + [checkHandle, this](CustomTimelineHandle cth, double mDuration) + { + if(!checkHandle(cth, "ct_stopTime")) + { + return; + } + + _customTimelineManager.get(cth)._timeline.append_do([=, this] + { status.pauseTime(ssvu::getFTToSeconds(mDuration)); }); + }) + .arg("handle") + .arg("duration") + .doc( + "*Add to the custom timeline with handle `$0`*: pause the game " + "timer for `$1` frames (under the assumption of a 60 FPS frame " + "rate)."); + + addLuaFn(lua, "ct_stopTimeS", // + [checkHandle, this](CustomTimelineHandle cth, double mDuration) + { + if(!checkHandle(cth, "ct_stopTimeS")) + { + return; + } + + _customTimelineManager.get(cth)._timeline.append_do( + [=, this] { status.pauseTime(mDuration); }); + }) + .arg("handle") + .arg("duration") + .doc( + "*Add to the custom timeline with handle `$0`*: pause the game " + "timer for `$1` seconds."); + + addLuaFn(lua, "ct_wait", + [checkHandle, this](CustomTimelineHandle cth, double mDuration) + { + if(!checkHandle(cth, "ct_wait")) + { + return; + } + + _customTimelineManager.get(cth)._timeline.append_wait_for_sixths( + mDuration); + }) + .arg("handle") + .arg("duration") + .doc( + "*Add to the custom timeline with handle `$0`*: wait for `$1` " + "frames (under the assumption of a 60 FPS frame rate)."); + + addLuaFn(lua, "ct_waitS", // + [checkHandle, this](CustomTimelineHandle cth, double mDuration) + { + if(!checkHandle(cth, "ct_waitS")) + { + return; + } + + _customTimelineManager.get(cth)._timeline.append_wait_for_seconds( + mDuration); + }) + .arg("handle") + .arg("duration") + .doc( + "*Add to the custom timeline with handle `$0`*: wait for `$1` " + "seconds."); + + addLuaFn(lua, "ct_waitUntilS", // + [checkHandle, this](CustomTimelineHandle cth, double mDuration) + { + if(!checkHandle(cth, "ct_waitUntilS")) + { + return; + } + + waitUntilSImpl( + mDuration, status, _customTimelineManager.get(cth)._timeline); + }) + .arg("handle") + .arg("duration") + .doc( + "*Add to the custom timeline with handle `$0`*: wait until the " + "timer reaches `$1` seconds."); +} + template <typename T> auto HexagonGame::makeLuaAccessor(T& obj, const std::string& prefix) { @@ -517,7 +672,8 @@ void HexagonGame::initLua_LevelControl() addLuaFn(lua, "l_setRotation", // [this](float mValue) { - // TODO (P2): might break replays? + // TODO (P2): might break replays if someone uses this to control + // game logic if(backgroundCamera.has_value()) { backgroundCamera->setRotation(mValue); @@ -529,7 +685,8 @@ void HexagonGame::initLua_LevelControl() addLuaFn(lua, "l_getRotation", // [this] { - // TODO (P2): might break replays? + // TODO (P2): might break replays if someone uses this to control + // game logic return backgroundCamera.has_value() ? backgroundCamera->getRotation() : 0.f; @@ -948,6 +1105,7 @@ void HexagonGame::initLua() initLua_AudioControl(); initLua_MainTimeline(); initLua_EventTimeline(); + initLua_CustomTimelines(); initLua_LevelControl(); initLua_StyleControl(); initLua_WallCreation(); diff --git a/src/SSVOpenHexagon/Core/HGStatus.cpp b/src/SSVOpenHexagon/Core/HGStatus.cpp index 32fb87d6..6fd422a9 100644 --- a/src/SSVOpenHexagon/Core/HGStatus.cpp +++ b/src/SSVOpenHexagon/Core/HGStatus.cpp @@ -18,37 +18,37 @@ void HexagonGameStatus::start() noexcept started = true; } -[[nodiscard]] double HexagonGameStatus::getIncrementTimeSeconds() noexcept +[[nodiscard]] double HexagonGameStatus::getIncrementTimeSeconds() const noexcept { return currentIncrementTime / 60.0; } -[[nodiscard]] double HexagonGameStatus::getTimeSeconds() noexcept +[[nodiscard]] double HexagonGameStatus::getTimeSeconds() const noexcept { return getPlayedAccumulatedFrametimeInSeconds(); } [[nodiscard]] HexagonGameStatus::TimePoint -HexagonGameStatus::getCurrentTP() noexcept +HexagonGameStatus::getCurrentTP() const noexcept { return HexagonGameStatus::TimePoint{std::chrono::milliseconds{ (int64_t)(getTotalAccumulatedFrametimeInSeconds() * 1000.0)}}; } [[nodiscard]] HexagonGameStatus::TimePoint -HexagonGameStatus::getTimeTP() noexcept +HexagonGameStatus::getTimeTP() const noexcept { return HexagonGameStatus::TimePoint{std::chrono::milliseconds{ (int64_t)(getPlayedAccumulatedFrametimeInSeconds() * 1000.0)}}; } [[nodiscard]] HexagonGameStatus::TimePoint -HexagonGameStatus::getLevelStartTP() noexcept +HexagonGameStatus::getLevelStartTP() const noexcept { return HexagonGameStatus::TimePoint{}; } -[[nodiscard]] bool HexagonGameStatus::isTimePaused() noexcept +[[nodiscard]] bool HexagonGameStatus::isTimePaused() const noexcept { return currentPause > 0.0; } diff --git a/src/SSVOpenHexagon/Core/HGUpdate.cpp b/src/SSVOpenHexagon/Core/HGUpdate.cpp index c9f735af..6882b77c 100644 --- a/src/SSVOpenHexagon/Core/HGUpdate.cpp +++ b/src/SSVOpenHexagon/Core/HGUpdate.cpp @@ -60,6 +60,15 @@ void HexagonGame::fastForwardTo(const double target) } } +void HexagonGame::advanceByTicks(const int nTicks) +{ + for(int i = 0; i < nTicks; ++i) + { + update(Config::TIME_STEP, 1.0f /* timescale */); + postUpdate(); + } +} + void HexagonGame::update(ssvu::FT mFT, const float timescale) { // ------------------------------------------------------------------------ @@ -70,6 +79,30 @@ void HexagonGame::update(ssvu::FT mFT, const float timescale) fastForwardTarget.reset(); fastForwardTo(target); + + if(audio != nullptr) + { + audio->setMusicPlayingOffsetSeconds( + audio->getMusicPlayingOffsetSeconds() + + status.getTimeSeconds()); + } + + return; + } + + // ------------------------------------------------------------------------ + // Advance by ticks for level testing + if(advanceTickCount.has_value()) + { + const int nTicks = advanceTickCount.value(); + advanceTickCount.reset(); + + const bool wasPaused = debugPause; + + debugPause = false; + advanceByTicks(nTicks); + debugPause = wasPaused; + return; } @@ -133,6 +166,12 @@ void HexagonGame::update(ssvu::FT mFT, const float timescale) if(!status.started) { + if(window != nullptr && window->hasTimer()) + { + // This avoids initial speedup when viewing replays. + window->getTimerBase().reset(); + } + mustStart = true; } else @@ -229,6 +268,7 @@ void HexagonGame::update(ssvu::FT mFT, const float timescale) } updateLevel(mFT); + updateCustomTimelines(); if(Config::getBeatPulse()) { @@ -606,6 +646,11 @@ void HexagonGame::updateEvents(ssvu::FT) } } +void HexagonGame::updateCustomTimelines() +{ + _customTimelineManager.updateAllTimelines(status.getCurrentTP()); +} + void HexagonGame::updateIncrement() { if(!levelStatus.incEnabled) @@ -1150,6 +1195,7 @@ void HexagonGame::postUpdate_ImguiLuaConsole() !clear Clears the console !help Display this help !ff <seconds> Fast-forward simulation to specified time +!advt <ticks> Advance simulation by specified number of ticks ?fn Display Lua docs for function `fn` )"); } @@ -1175,6 +1221,28 @@ void HexagonGame::postUpdate_ImguiLuaConsole() ilcCmdLog.emplace_back("[error]: out of range for <seconds>\n"); } } + else if(cmdSplit.size() > 1 && cmdSplit.at(0) == "!advt") + { + try + { + const std::string& ticksStr = cmdSplit.at(1); + const int ticks = std::stoi(ticksStr); + + ilcCmdLog.emplace_back(Utils::concat( + "[advt]: advancing simulation by ", ticks, " ticks\n")); + + advanceTickCount = ticks >= 0 ? ticks : 0; + } + catch(const std::invalid_argument&) + { + ilcCmdLog.emplace_back( + "[error]: invalid argument for <seconds>\n"); + } + catch(const std::out_of_range&) + { + ilcCmdLog.emplace_back("[error]: out of range for <seconds>\n"); + } + } else if(cmdString[0] == '?') { const std::string rest = Utils::getRTrim(cmdString.substr(1)); diff --git a/src/SSVOpenHexagon/Core/HexagonDialogBox.cpp b/src/SSVOpenHexagon/Core/HexagonDialogBox.cpp index 12469983..851d3609 100644 --- a/src/SSVOpenHexagon/Core/HexagonDialogBox.cpp +++ b/src/SSVOpenHexagon/Core/HexagonDialogBox.cpp @@ -134,7 +134,7 @@ void HexagonDialogBox::drawText( txtDialog.setString(str); txtDialog.setPosition( {xOffset - ssvs::getGlobalWidth(txtDialog) / 2.f, - yOffset + heightOffset}); + yOffset + heightOffset + 5.f}); window.draw(txtDialog); } @@ -155,7 +155,7 @@ void HexagonDialogBox::drawText( } txtDialog.setPosition({xOffset - ssvs::getGlobalWidth(txtDialog) / 2.f, - yOffset + heightOffset}); + yOffset + heightOffset + 5.f}); window.draw(txtDialog); } } diff --git a/src/SSVOpenHexagon/Core/HexagonGame.cpp b/src/SSVOpenHexagon/Core/HexagonGame.cpp index 017fdc97..f964dc86 100644 --- a/src/SSVOpenHexagon/Core/HexagonGame.cpp +++ b/src/SSVOpenHexagon/Core/HexagonGame.cpp @@ -67,6 +67,11 @@ void HexagonGame::createWall(int mSide, float mThickness, levelStatus.wallSpawnDistance, mSpeed, mCurve, mHueMod); } +void HexagonGame::setMustStart(const bool x) +{ + mustStart = x; +} + void HexagonGame::initKeyIcons() { if(window == nullptr) @@ -638,6 +643,9 @@ void HexagonGame::newGame(const std::string& mPackId, const std::string& mId, messageTimeline.clear(); messageTimelineRunner = {}; + // Custom timeline manager cleanup + _customTimelineManager.clear(); + // Manager cleanup walls.clear(); cwManager.clear(); @@ -1324,6 +1332,8 @@ void HexagonGame::playLevelMusic() const MusicData::Segment segment = musicData.playRandomSegment(getPackId(), *audio); + // TODO (P0): can this actually de-sync replays? beatpulse affects + // gameplay // TODO (P1): problems with addHash in headless mode: status.beatPulseDelay += segment.beatPulseDelayOffset; } diff --git a/src/SSVOpenHexagon/Core/LuaScripting.cpp b/src/SSVOpenHexagon/Core/LuaScripting.cpp index 24fbf5bd..0d35f10c 100644 --- a/src/SSVOpenHexagon/Core/LuaScripting.cpp +++ b/src/SSVOpenHexagon/Core/LuaScripting.cpp @@ -130,8 +130,8 @@ static void redefineIoOpen(Lua::LuaContext& lua) try { lua.executeCode( - "local open = io.open; io.open = function(filename) return " - "open(filename, \"r\"); end"); + "local open = io.open; io.open = function(filename, mode) return " + "open(filename, mode == \"rb\" and mode or \"r\"); end"); } catch(...) { @@ -194,6 +194,16 @@ static void destroyMaliciousFunctions(Lua::LuaContext& lua) // properly. lua.clearVariable("package.loadlib"); lua.clearVariable("package.searchpath"); + + // This removes some other ways in which the "os" and "debug" libraries can + // be accessed. + lua.clearVariable("package.loaded.os"); + lua.clearVariable("package.loaded.debug"); + + // This removes some potentially dangerous packages that could be used to + // inject malicious code. + lua.clearVariable("package.loadlib"); + lua.clearVariable("package.loaders"); } static void initUtils(Lua::LuaContext& lua, const bool inMenu) @@ -470,18 +480,6 @@ static void initCustomWalls(Lua::LuaContext& lua, CCustomWallManager& cwManager) "Given the custom wall represented by `$0`, return the position of " "its vertics with indices `0`, `1`, `2`, and `3`, as a tuple."); - // TODO (P2): implement - /* - addLuaFn(lua, "cw_isOverlappingPlayer", // - [&cwManager](CCustomWallHandle cwHandle) -> bool { - return cwManager.isOverlappingPlayer(cwHandle); - }) - .arg("cwHandle") - .doc( - "Return `true` if the custom wall represented by `$0` is " - "overlapping the player, `false` otherwise."); - */ - addLuaFn(lua, "cw_clear", // [&cwManager] { cwManager.clear(); }) .doc("Remove all existing custom walls."); diff --git a/src/SSVOpenHexagon/Core/MenuGame.cpp b/src/SSVOpenHexagon/Core/MenuGame.cpp index d7efef35..705295d4 100644 --- a/src/SSVOpenHexagon/Core/MenuGame.cpp +++ b/src/SSVOpenHexagon/Core/MenuGame.cpp @@ -200,11 +200,11 @@ MenuGame::MenuGame(Steam::steam_manager& mSteamManager, txtVersion{.font{"", openSquare, 40}}, txtProf{.font{"", openSquare, 18}}, // For the loading screen - txtLoadBig{.font{"", openSquare, 72}}, + txtLoadBig{.font{"", openSquare}}, txtLoadSmall{.font{"", openSquareBold}}, - txtRandomTip{.font{"", openSquare, 32}}, + txtRandomTip{.font{"", openSquare}}, // For the Main Menu - txtMenuBig{.font{"", openSquare, 96}}, + txtMenuBig{.font{"", openSquare}}, txtMenuSmall{.font{"", openSquare}}, txtProfile{.font{"", openSquare, 32}}, txtInstructionsBig{.font{"", openSquare, 46}}, @@ -213,11 +213,11 @@ MenuGame::MenuGame(Steam::steam_manager& mSteamManager, // Manual Input txtEnteringText{.font{"", openSquare, 54}}, // For the Level Selection Screen - txtSelectionBig{.font{"", openSquareBold, 28}}, - txtSelectionMedium{.font{"", openSquareBold, 20}}, - txtSelectionSmall{.font{"", openSquare, 12}}, + txtSelectionBig{.font{"", openSquareBold}}, + txtSelectionMedium{.font{"", openSquareBold, 19}}, + txtSelectionSmall{.font{"", openSquare}}, txtSelectionScore{.font{"", openSquare, 28}}, - txtSelectionRanked{.font{"", openSquare, 10}}, + txtSelectionRanked{.font{"", openSquareBold}}, menuTextColor{}, menuQuadColor{}, menuSelectionColor{}, @@ -833,6 +833,16 @@ void MenuGame::changeStateTo(const States mState) return; } + if(state == States::SMain) + { + if(std::exchange(mustShowLoginAtStartup, false) && + Config::getShowLoginAtStartup()) + { + openLoginDialogBoxAndStartLoginProcess(); + setIgnoreAllInputs(2); + } + } + if(state == States::LevelSelection) { firstLevelSelection = false; @@ -1095,6 +1105,9 @@ void MenuGame::initLua() "e_waitS", "e_waitUntilS", "e_messageAdd", "e_messageAddImportant", "e_messageAddImportantSilent", "e_clearMessages", + "ct_create", "ct_eval", "ct_kill", "ct_stopTime", "ct_stopTimeS", + "ct_wait", "ct_waitS", "ct_waitUntilS", + "l_overrideScore", "l_setRotation", "l_getRotation", "l_getOfficial", @@ -1115,6 +1128,15 @@ void MenuGame::initLua() } } +void MenuGame::ignoreInputsAfterMenuExec() +{ + // We only want to ignore a single input when using the left mouse button, + // otherwise the user would have to press enter twice to accept in a dialog + // box. + + setIgnoreAllInputs(mustUseMenuItem.has_value() ? 1 : 2); +} + void MenuGame::initMenus() { namespace i = ssvms::Items; @@ -1170,6 +1192,10 @@ void MenuGame::initMenus() play.create<i::Slider>("timescale", &Config::getTimescale, &Config::setTimescale, 0.1f, 2.f, 0.05f) | whenNotOfficial; + play.create<i::Toggle>("save last username", + &Config::getSaveLastLoginUsername, &Config::setSaveLastLoginUsername); + play.create<i::Toggle>("show login at startup", + &Config::getShowLoginAtStartup, &Config::setShowLoginAtStartup); play.create<i::GoBack>("back"); //-------------------------------- @@ -1199,7 +1225,7 @@ void MenuGame::initMenus() "F3 - RELOAD LEVEL ASSETS (DEBUG MODE ONLY)\n" "F4 - RELOAD PACK ASSETS (DEBUG MODE ONLY)\n\n" "PRESS ANY KEY OR BUTTON TO CLOSE THIS MESSAGE\n"); - setIgnoreAllInputs(2); + ignoreInputsAfterMenuExec(); }); controls.create<i::GoBack>("back"); @@ -1544,10 +1570,8 @@ void MenuGame::initMenus() return; } - dialogInputState = DialogInputState::Login_EnteringUsername; - - showInputDialogBoxNice("LOGIN", "USERNAME"); - setIgnoreAllInputs(2); + openLoginDialogBoxAndStartLoginProcess(); + ignoreInputsAfterMenuExec(); }) | whenMustLogin; @@ -1562,7 +1586,7 @@ void MenuGame::initMenus() dialogInputState = DialogInputState::Registration_EnteringUsername; showInputDialogBoxNice("REGISTRATION", "USERNAME"); - setIgnoreAllInputs(2); + ignoreInputsAfterMenuExec(); }) | whenMustRegister; @@ -1587,7 +1611,7 @@ void MenuGame::initMenus() showInputDialogBoxNice("DELETE ACCOUNT", "PASSWORD", "WARNING: THIS WILL DELETE ALL YOUR SCORES"); dialogBox.setInputBoxPassword(true); - setIgnoreAllInputs(2); + ignoreInputsAfterMenuExec(); }) | whenMustDeleteAccount; @@ -2913,7 +2937,8 @@ void MenuGame::setIndex(const int mIdx) // Set the colors of the menus auto& colors{styleData.getColors()}; - menuQuadColor = Config::getBlackAndWhite() ? sf::Color(20, 20, 20, 255) : styleData.getTextColor(); + menuQuadColor = Config::getBlackAndWhite() ? sf::Color(20, 20, 20, 255) + : styleData.getTextColor(); if(ssvu::toInt(menuQuadColor.a) == 0 && !Config::getBlackAndWhite()) { for(auto& c : colors) @@ -2945,7 +2970,8 @@ void MenuGame::setIndex(const int mIdx) { for(auto& c : colors) { - if(ssvu::toInt(c.a) != 0 && c != menuQuadColor && !Config::getBlackAndWhite()) + if(ssvu::toInt(c.a) != 0 && c != menuQuadColor && + !Config::getBlackAndWhite()) { menuTextColor = c; break; @@ -2954,7 +2980,8 @@ void MenuGame::setIndex(const int mIdx) } // Same as above. - menuSelectionColor = Config::getBlackAndWhite() ? sf::Color::White : colors[1]; + menuSelectionColor = + Config::getBlackAndWhite() ? sf::Color::White : colors[1]; if(ssvu::toInt(menuSelectionColor.a) == 0 || menuSelectionColor == menuQuadColor || menuSelectionColor == menuTextColor) @@ -3132,22 +3159,28 @@ void MenuGame::refreshCamera() { txtMenuBig.font.setCharacterSize(26); txtMenuSmall.font.setCharacterSize(16); - txtSelectionSmall.font.setCharacterSize(16); - txtSelectionRanked.font.setCharacterSize(12); + + txtSelectionBig.font.setCharacterSize(24); + txtSelectionSmall.font.setCharacterSize(14); + txtSelectionRanked.font.setCharacterSize(10); txtLoadBig.font.setCharacterSize(56); txtLoadSmall.font.setCharacterSize(16); + txtRandomTip.font.setCharacterSize(24); } else { txtMenuBig.font.setCharacterSize(36); txtMenuSmall.font.setCharacterSize(24); - txtSelectionSmall.font.setCharacterSize(16); - txtSelectionRanked.font.setCharacterSize(12); + + txtSelectionBig.font.setCharacterSize(28); + txtSelectionSmall.font.setCharacterSize(14); + txtSelectionRanked.font.setCharacterSize(10); txtLoadBig.font.setCharacterSize(70); txtLoadSmall.font.setCharacterSize(24); + txtRandomTip.font.setCharacterSize(32); } @@ -4983,6 +5016,9 @@ void MenuGame::drawLevelSelectionRightSide( const std::string& levelValidator = levelData->getValidator(currentDiffMult); + renderText(tempString, txtSelectionBig.font, + {indent, height - txtSelectionBig.height * fontHeightOffset}, c0); + if(!levelData->unscored && hexagonClient.isLevelSupportedByServer(levelValidator)) { @@ -4995,20 +5031,17 @@ void MenuGame::drawLevelSelectionRightSide( createQuad(menuQuadColor, w - width - padding, w, height - textToQuadBorder, height - textToQuadBorder + txtSelectionRanked.height + - padding); + padding + 1.f); render(menuQuads); renderText("RANKED", txtSelectionRanked.font, {w - width, height - txtSelectionRanked.height * fontHeightOffset - - 5.5f}, + 3.f}, mouseOverlapColor(mouseOverlap, c)); } - renderText(tempString, txtSelectionBig.font, - {indent, height - txtSelectionBig.height * fontHeightOffset}, c0); - //------------------------------------- // Author height += txtSelectionBig.height + textToQuadBorder; @@ -5544,23 +5577,25 @@ void MenuGame::drawLevelSelectionLeftSide( std::string scoreStr = ssvu::toStr(score) + 's'; std::string playerStr = userName; - if(playerStr.size() > 20) + if(playerStr.size() > 19) { - playerStr.resize(17); + playerStr.resize(16); playerStr += "..."; } const float tx = textToQuadBorder - panelOffset; const float ty = height - txtSelectionMedium.height * fontHeightOffset + - txtSelectionSmall.height; + txtSelectionSmall.height - 9.f; + + constexpr float ySpacing = 11.f; renderText(timestampStr, txtSelectionSmall.font, {tx, ty}); - renderText(posStr, txtSelectionMedium.font, {tx, ty + 7.5f}); - renderText( - scoreStr, txtSelectionMedium.font, {tx + 54.f, ty + 7.5f}); + renderText(posStr, txtSelectionMedium.font, {tx, ty + ySpacing}); renderText( - playerStr, txtSelectionMedium.font, {tx + 150.f, ty + 7.5f}); + scoreStr, txtSelectionMedium.font, {tx + 58.f, ty + ySpacing}); + renderText(playerStr, txtSelectionMedium.font, + {tx + 185.f, ty + ySpacing}); height += txtSelectionMedium.height + txtSelectionSmall.height + txtSelectionSmall.height + 10.f; @@ -5711,13 +5746,13 @@ void MenuGame::draw() case States::LoadingScreen: drawLoadResults(); renderText("PRESS ANY KEY OR BUTTON TO CONTINUE", txtProf.font, - {txtProf.height, h - txtProf.height * 2.7f}); + {txtProf.height, h - txtProf.height * 2.7f + 5.f}); return; case States::EpilepsyWarning: render(epilepsyWarning); renderText("PRESS ANY KEY OR BUTTON TO CONTINUE", txtProf.font, - {txtProf.height, h - txtProf.height * 2.7f}); + {txtProf.height, h - txtProf.height * 2.7f + 5.f}); return; case States::ETLPNewBoot: @@ -5932,6 +5967,15 @@ void MenuGame::drawOnlineStatus() case HexagonClient::State::LoggedIn: [[fallthrough]]; case HexagonClient::State::LoggedIn_Ready: { + if(Config::getSaveLastLoginUsername() && + hexagonClient.getLoginName().has_value()) + { + // Save last login username for quicker login next time. + + Config::setLastLoginUsername( + hexagonClient.getLoginName().value()); + } + return { true, "LOGGED IN AS " + hexagonClient.getLoginName().value_or("UNKNOWN")}; @@ -5969,7 +6013,8 @@ void MenuGame::drawOnlineStatus() ssvs::getGlobalRight(sOnline) + padding, sOnline.getPosition().y); txtOnlineStatus.setOrigin(ssvs::getLocalCenterW(txtOnlineStatus)); - txtOnlineStatus.setPosition(ssvs::getGlobalLeft(rsOnlineStatus) + padding * 2.f, + txtOnlineStatus.setPosition( + ssvs::getGlobalLeft(rsOnlineStatus) + padding * 2.f, ssvs::getGlobalCenter(rsOnlineStatus).y); render(sOnline); @@ -5992,6 +6037,14 @@ void MenuGame::showInputDialogBox(const std::string& msg) void MenuGame::showInputDialogBoxNice(const std::string& title, const std::string& inputType, const std::string& extra) { + showInputDialogBoxNiceWithDefault( + title, inputType, "" /* default */, extra); +} + +void MenuGame::showInputDialogBoxNiceWithDefault(const std::string& title, + const std::string& inputType, const std::string& def, + const std::string& extra) +{ strBuf.clear(); if(extra.empty()) @@ -6006,6 +6059,21 @@ void MenuGame::showInputDialogBoxNice(const std::string& title, } showInputDialogBox(strBuf); + dialogBox.getInput() = def; +} + +void MenuGame::openLoginDialogBoxAndStartLoginProcess() +{ + SSVOH_ASSERT(dialogInputState == DialogInputState::Nothing); + + dialogInputState = DialogInputState::Login_EnteringUsername; + + const std::string defaultLoginUsername = + Config::getSaveLastLoginUsername() ? Config::getLastLoginUsername() + : ""; + + showInputDialogBoxNiceWithDefault( + "LOGIN", "USERNAME", defaultLoginUsername); } } // namespace hg diff --git a/src/SSVOpenHexagon/Core/Steam.cpp b/src/SSVOpenHexagon/Core/Steam.cpp index 6fca3dd9..2d77f9fb 100644 --- a/src/SSVOpenHexagon/Core/Steam.cpp +++ b/src/SSVOpenHexagon/Core/Steam.cpp @@ -104,6 +104,7 @@ private: bool update_hardcoded_achievement_cube_master(); bool update_hardcoded_achievement_hypercube_master(); bool update_hardcoded_achievement_cube_god(); + bool update_hardcoded_achievement_hypercube_god(); void load_workshop_data(); @@ -139,6 +140,7 @@ public: bool set_and_store_stat(std::string_view name, int data); [[nodiscard]] bool get_achievement(bool* out, std::string_view name); [[nodiscard]] bool get_stat(int* out, std::string_view name); + [[nodiscard]] std::optional<bool> is_achievement_unlocked(const char* name); bool update_hardcoded_achievements(); @@ -191,6 +193,8 @@ void steam_manager::steam_manager_impl::load_workshop_data() constexpr std::size_t folderBufSize = 512; char folderBuf[folderBufSize]; + ssvuj::Obj cacheArray; + for(PublishedFileId_t id : subscribedItemsIds) { ssvu::lo("Steam") << "Workshop subscribed item id: " << id << '\n'; @@ -203,14 +207,29 @@ void steam_manager::steam_manager_impl::load_workshop_data() if(installed) { + std::string folderBufStr{folderBuf}; + ssvu::lo("Steam") << "Workshop id " << id << " is installed, with size " - << itemDiskSize << " at folder " << std::string{folderBuf} - << '\n'; + << itemDiskSize << " at folder " << folderBufStr << '\n'; - _workshop_pack_folders.emplace(std::string{folderBuf}); + // Write the path to an element in a JSON array. + ssvuj::arch( + cacheArray, _workshop_pack_folders.size(), folderBufStr); + + _workshop_pack_folders.emplace(std::move(folderBufStr)); } } + + // Update the workshop cache with our loaded folders + if(_workshop_pack_folders.size() > 0) + { + ssvu::lo("Steam") << "Updating workshop cache\n"; + ssvuj::Obj cacheObj; + + ssvuj::arch(cacheObj, "cachedPacks", cacheArray); + ssvuj::writeToFile(cacheObj, "workshopCache.json"); + } } steam_manager::steam_manager_impl::steam_manager_impl() @@ -364,8 +383,8 @@ bool steam_manager::steam_manager_impl::set_and_store_stat( // Steam API seems to be bugged, and sometimes needs floats even for integer // stats. const float as_float = data; - if(!SteamUserStats()->SetStat(name.data(), as_float) && - !SteamUserStats()->SetStat(name.data(), data)) + if(!SteamUserStats()->SetStat(name.data(), as_float) && // Try with float. + !SteamUserStats()->SetStat(name.data(), data)) // Try with integer. { ssvu::lo("Steam") << "Error setting stat '" << name << "' to '" << as_float << "'\n"; @@ -379,12 +398,7 @@ bool steam_manager::steam_manager_impl::set_and_store_stat( [[nodiscard]] bool steam_manager::steam_manager_impl::get_achievement( bool* out, std::string_view name) { - if(!_initialized) - { - return false; - } - - if(!_got_stats) + if(!_initialized || !_got_stats) { return false; } @@ -401,12 +415,7 @@ bool steam_manager::steam_manager_impl::set_and_store_stat( [[nodiscard]] bool steam_manager::steam_manager_impl::get_stat( int* out, std::string_view name) { - if(!_initialized) - { - return false; - } - - if(!_got_stats) + if(!_initialized || !_got_stats) { return false; } @@ -414,47 +423,45 @@ bool steam_manager::steam_manager_impl::set_and_store_stat( // Steam API seems to be bugged, and sometimes needs floats even for integer // stats. float as_float; - if(SteamUserStats()->GetStat(name.data(), &as_float)) + if(SteamUserStats()->GetStat(name.data(), &as_float)) // Try with float. { *out = as_float; return true; } - else if(SteamUserStats()->GetStat(name.data(), out)) + + if(SteamUserStats()->GetStat(name.data(), out)) // Try with integer. { return true; } - else + + ssvu::lo("Steam") << "Error getting stat " << name.data() << '\n'; + return false; +} + +[[nodiscard]] std::optional<bool> +steam_manager::steam_manager_impl::is_achievement_unlocked(const char* name) +{ + bool res{false}; + const bool rc = get_achievement(&res, name); + + if(!rc) { - ssvu::lo("Steam") << "Error getting stat " << name.data() << '\n'; - return false; + return std::nullopt; } + + return res; } bool steam_manager::steam_manager_impl:: update_hardcoded_achievement_cube_master() { - if(!_initialized) - { - return false; - } - - if(!_got_stats) + if(!_initialized || !_got_stats) { return false; } const auto unlocked = [this](const char* name) -> int - { - bool res{false}; - const bool rc = get_achievement(&res, name); - - if(!rc) - { - return 0; - } - - return res ? 1 : 0; - }; + { return is_achievement_unlocked(name).value_or(false) ? 1 : 0; }; // "Cube Master" { @@ -492,28 +499,13 @@ bool steam_manager::steam_manager_impl:: bool steam_manager::steam_manager_impl:: update_hardcoded_achievement_hypercube_master() { - if(!_initialized) - { - return false; - } - - if(!_got_stats) + if(!_initialized || !_got_stats) { return false; } const auto unlocked = [this](const char* name) -> int - { - bool res{false}; - const bool rc = get_achievement(&res, name); - - if(!rc) - { - return 0; - } - - return res ? 1 : 0; - }; + { return is_achievement_unlocked(name).value_or(false) ? 1 : 0; }; // "Hypercube Master" { @@ -548,31 +540,15 @@ bool steam_manager::steam_manager_impl:: return true; } - bool steam_manager::steam_manager_impl::update_hardcoded_achievement_cube_god() { - if(!_initialized) - { - return false; - } - - if(!_got_stats) + if(!_initialized || !_got_stats) { return false; } const auto unlocked = [this](const char* name) -> int - { - bool res{false}; - const bool rc = get_achievement(&res, name); - - if(!rc) - { - return 0; - } - - return res ? 1 : 0; - }; + { return is_achievement_unlocked(name).value_or(false) ? 1 : 0; }; // "Cube God" { @@ -606,26 +582,74 @@ bool steam_manager::steam_manager_impl::update_hardcoded_achievement_cube_god() return true; } +bool steam_manager::steam_manager_impl:: + update_hardcoded_achievement_hypercube_god() +{ + if(!_initialized || !_got_stats) + { + return false; + } + + const auto unlocked = [this](const char* name) -> int + { return is_achievement_unlocked(name).value_or(false) ? 1 : 0; }; + + // "Hypercube Master" + { + int stat; + const bool rc = get_stat(&stat, "s3_packprogress_hypercubegod"); + + if(!rc) + { + return false; + } + + const int acc = unlocked("a38_disco_hard") + // + unlocked("a39_acceleradiant_hard") + // + unlocked("a40_gforce_hard") + // + unlocked("a41_incongruence_hard") + // + unlocked("a42_slither_hard") + // + unlocked("a43_polyhedrug_hard") + // + unlocked("a44_reppaws_hard") + // + unlocked("a45_centrifugalforce_hard") + // + unlocked("a46_massacre_hard"); + + if(acc > stat) + { + if(!set_and_store_stat("s3_packprogress_hypercubegod", acc)) + { + return false; + } + } + } + + return true; +} + bool steam_manager::steam_manager_impl::update_hardcoded_achievements() { - bool success = true; + bool any_failure = false; if(!update_hardcoded_achievement_cube_master()) { - success = false; + any_failure = true; } if(!update_hardcoded_achievement_hypercube_master()) { - success = false; + any_failure = true; } if(!update_hardcoded_achievement_cube_god()) { - success = false; + any_failure = true; + } + + if(!update_hardcoded_achievement_hypercube_god()) + { + any_failure = true; } - return success; + return !any_failure; } void steam_manager::steam_manager_impl::for_workshop_pack_folders( diff --git a/src/SSVOpenHexagon/Core/main.cpp b/src/SSVOpenHexagon/Core/main.cpp index 03d1b979..2600ca32 100644 --- a/src/SSVOpenHexagon/Core/main.cpp +++ b/src/SSVOpenHexagon/Core/main.cpp @@ -85,7 +85,7 @@ struct ParsedArgs bool server{false}; }; -ParsedArgs parseArgs(const int argc, char* argv[]) +[[nodiscard]] ParsedArgs parseArgs(const int argc, char* argv[]) { ParsedArgs result; @@ -134,14 +134,14 @@ ParsedArgs parseArgs(const int argc, char* argv[]) return result; } -std::string makeWindowTitle() +[[nodiscard]] std::string makeWindowTitle() { return hg::Utils::concat("Open Hexagon ", hg::GAME_VERSION_STR, " - by Vittorio Romeo - https://vittorioromeo.info"); } -std::optional<std::string> getFirstCompressedReplayFilenameFromArgs( - const std::vector<std::string>& args) +[[nodiscard]] std::optional<std::string> +getFirstCompressedReplayFilenameFromArgs(const std::vector<std::string>& args) { for(const std::string& arg : args) { @@ -196,8 +196,7 @@ std::optional<std::string> getFirstCompressedReplayFilenameFromArgs( { hg::Steam::steam_manager steamManager; - // TODO (P0): tries to use X11 server, fix. - // hg::Config::loadConfig({} /* overrideIds */); + hg::Config::loadConfig({} /* overrideIds */); hg::HGAssets assets{ &steamManager, // @@ -271,6 +270,8 @@ std::optional<std::string> getFirstCompressedReplayFilenameFromArgs( // ------------------------------------------------------------------------ // Load configuration (and overrides) hg::Config::loadConfig(args); + hg::Config::reapplyResolution(); + HG_SCOPE_GUARD( { ssvu::lo("::main") << "Saving config...\n"; @@ -445,7 +446,6 @@ std::optional<std::string> getFirstCompressedReplayFilenameFromArgs( const auto gotoMenu = [&] { mg->init(false /* mError */); - window->setGameState(mg->getGame()); }; diff --git a/src/SSVOpenHexagon/Data/StyleData.cpp b/src/SSVOpenHexagon/Data/StyleData.cpp index 78881c14..7ead052d 100644 --- a/src/SSVOpenHexagon/Data/StyleData.cpp +++ b/src/SSVOpenHexagon/Data/StyleData.cpp @@ -303,7 +303,7 @@ void StyleData::setCapColor(const CapColor& mCapColor) } [[nodiscard]] const sf::Color& StyleData::getWallColor() const noexcept -{ +{ return currentWallColor; } diff --git a/src/SSVOpenHexagon/Global/Assets.cpp b/src/SSVOpenHexagon/Global/Assets.cpp index 18d164b9..771d03ad 100644 --- a/src/SSVOpenHexagon/Global/Assets.cpp +++ b/src/SSVOpenHexagon/Global/Assets.cpp @@ -457,6 +457,60 @@ HGAssets::getSelectablePackInfos() const noexcept return nullptr; } +[[nodiscard]] bool HGAssets::loadWorkshopPackDatasFromCache() +{ + if(!ssvufs::Path{"workshopCache.json"}.exists<ssvufs::Type::File>()) + { + ssvu::lo("::loadAssets") << "Workshop cache file does not exist. No " + "workshop packs to load\n"; + return false; + } + auto [cacheObject, cacheError] = + ssvuj::getFromFileWithErrors("workshopCache.json"); + + ssvu::lo("::loadAssets") << "Loading workshop packs from cache\n"; + if(ssvuj::hasObj(cacheObject, "cachedPacks")) + { + // Null check + auto& packValue = ssvuj::getObj(cacheObject, "cachedPacks"); + if(packValue.type() == Json::ValueType::nullValue || + packValue.type() != Json::ValueType::arrayValue) + { + ssvu::lo("::loadAssets") + << "Cache array is null. No workshop packs to load\n"; + return false; + } + + // Empty check + const auto packArray = + ssvuj::getExtr<std::vector<std::string>>( + cacheObject, "cachedPacks"); + + if(packArray.size() <= 0) + { + ssvu::lo("::loadAssets") + << "Cache array is empty. No workshop packs to load\n"; + return false; + } + + for(const auto& f : packArray) + { + // Simply emplace them. We will check them later. + cachedWorkshopPackIds.emplace(f); + } + } + else + { + ssvu::lo("::loadAssets") + << "[ERROR]: Cannot locate cache array in workshop cache file\n"; + + return false; + } + + loadInfo.addFormattedError(cacheError); + return true; +} + [[nodiscard]] bool HGAssets::loadAllPackDatas() { if(!ssvufs::Path{"Packs/"}.exists<ssvufs::Type::Folder>()) @@ -494,7 +548,19 @@ HGAssets::getSelectablePackInfos() const noexcept // Load pack datas from Steam workshop. if(steamManager != nullptr) { - steamManager->for_workshop_pack_folders(tryLoadPackFromPath); + if(steamManager->is_initialized()) + { + steamManager->for_workshop_pack_folders(tryLoadPackFromPath); + } + else if(loadWorkshopPackDatasFromCache()) + { + // In the case the Steam API can't be retrieved, look for a cache + // that contains the paths we need to load + for(const auto& cachedPath : cachedWorkshopPackIds) + { + tryLoadPackFromPath(cachedPath); + } + } } return true; diff --git a/src/SSVOpenHexagon/Global/Config.cpp b/src/SSVOpenHexagon/Global/Config.cpp index a52ac843..c29f3141 100644 --- a/src/SSVOpenHexagon/Global/Config.cpp +++ b/src/SSVOpenHexagon/Global/Config.cpp @@ -58,7 +58,39 @@ defaultServerLevelWhitelist() "ohvrvanilla_vittorio_romeo_cube_1_seconddimension_m_2.2", "ohvrvanilla_vittorio_romeo_orthoplex_1_bipolarity_m_0.5", "ohvrvanilla_vittorio_romeo_orthoplex_1_bipolarity_m_1", - "ohvrvanilla_vittorio_romeo_orthoplex_1_bipolarity_m_1.8"}; + "ohvrvanilla_vittorio_romeo_orthoplex_1_bipolarity_m_1.8", + "ohvrvanilla_vittorio_romeo_hypercube_1_acceleradiant_m_0.85", + "ohvrvanilla_vittorio_romeo_hypercube_1_acceleradiant_m_1", + "ohvrvanilla_vittorio_romeo_hypercube_1_acceleradiant_m_1.8", + "ohvrvanilla_vittorio_romeo_hypercube_1_acceleradiant_m_2.4", + "ohvrvanilla_vittorio_romeo_hypercube_1_centrifugal_m_0.6", + "ohvrvanilla_vittorio_romeo_hypercube_1_centrifugal_m_1", + "ohvrvanilla_vittorio_romeo_hypercube_1_centrifugal_m_1.6", + "ohvrvanilla_vittorio_romeo_hypercube_1_disc-o_m_0.5", + "ohvrvanilla_vittorio_romeo_hypercube_1_disc-o_m_1", + "ohvrvanilla_vittorio_romeo_hypercube_1_disc-o_m_2", + "ohvrvanilla_vittorio_romeo_hypercube_1_disc-o_m_2.8", + "ohvrvanilla_vittorio_romeo_hypercube_1_g-force_m_0.6", + "ohvrvanilla_vittorio_romeo_hypercube_1_g-force_m_1", + "ohvrvanilla_vittorio_romeo_hypercube_1_g-force_m_1.6", + "ohvrvanilla_vittorio_romeo_hypercube_1_g-force_m_2.8", + "ohvrvanilla_vittorio_romeo_hypercube_1_incongruence_m_0.5", + "ohvrvanilla_vittorio_romeo_hypercube_1_incongruence_m_1", + "ohvrvanilla_vittorio_romeo_hypercube_1_incongruence_m_1.8", + "ohvrvanilla_vittorio_romeo_hypercube_1_massacre_m_0.5", + "ohvrvanilla_vittorio_romeo_hypercube_1_massacre_m_1", + "ohvrvanilla_vittorio_romeo_hypercube_1_massacre_m_1.6", + "ohvrvanilla_vittorio_romeo_hypercube_1_polyhedrug_m_0.75", + "ohvrvanilla_vittorio_romeo_hypercube_1_polyhedrug_m_1", + "ohvrvanilla_vittorio_romeo_hypercube_1_polyhedrug_m_1.8", + "ohvrvanilla_vittorio_romeo_hypercube_1_reppaws_m_0.5", + "ohvrvanilla_vittorio_romeo_hypercube_1_reppaws_m_1", + "ohvrvanilla_vittorio_romeo_hypercube_1_reppaws_m_1.8", + "ohvrvanilla_vittorio_romeo_hypercube_1_slither_m_0.5", + "ohvrvanilla_vittorio_romeo_hypercube_1_slither_m_1", + "ohvrvanilla_vittorio_romeo_hypercube_1_slither_m_1.5", + "ohvrvanilla_vittorio_romeo_hypercube_1_slither_m_2" // + }; return result; } @@ -67,11 +99,14 @@ using uint = unsigned int; using ushort = unsigned short; using trig = ssvs::Input::Trigger; + using k = ssvs::KKey; using m = ssvs::MBtn; -using c = ssvs::Input::Combo; +using cmb = ssvs::Input::Combo; + using kil = std::initializer_list<ssvs::KKey>; using mil = std::initializer_list<ssvs::MBtn>; +using cil = std::initializer_list<cmb>; #define X_LINKEDVALUES_BINDS_JOYSTICK \ X(joystickSelect, uint, "j_select", 1) \ @@ -87,38 +122,31 @@ using mil = std::initializer_list<ssvs::MBtn>; X(joystickAddToFavorites, uint, "j_add_favorite", 8) \ X(joystickFavoritesMenu, uint, "j_favorite_menu", 9) -#define X_LINKEDVALUES_BINDS_TRIGGERS \ - X(triggerRotateCCW, trig, "t_rotate_ccw", \ - std::initializer_list<c>{ \ - c{{k::A}}, c{{k::Left}}, c{kil{}, mil{m::Left}}}) \ - X(triggerRotateCW, trig, "t_rotate_cw", \ - std::initializer_list<c>{ \ - c{{k::D}}, c{{k::Right}}, c{kil{}, mil{m::Right}}}) \ - X(triggerFocus, trig, "t_focus", \ - std::initializer_list<c>{c{{k::LShift}}, c{kil{}, mil{m::XButton1}}}) \ - X(triggerSelect, trig, "t_select", \ - std::initializer_list<c>{c{{k::Space}}, c{kil{}, mil{m::Middle}}}) \ - X(triggerExit, trig, "t_exit", \ - std::initializer_list<c>{c{{k::T}}, c{kil{}, mil{m::XButton2}}}) \ - X(triggerForceRestart, trig, "t_force_restart", \ - std::initializer_list<c>{c{{k::Up}}, c{{k::R}}}) \ - X(triggerRestart, trig, "t_restart", \ - std::initializer_list<c>{ \ - c{{k::Space}}, c{{k::Return}}, c{kil{}, mil{m::Middle}}}) \ - X(triggerReplay, trig, "t_replay", std::initializer_list<c>{c{{k::Y}}}) \ - X(triggerScreenshot, trig, "t_screenshot", \ - std::initializer_list<c>{c{{k::F12}}}) \ - X(triggerSwap, trig, "t_swap", \ - std::initializer_list<c>{c{{k::Space}}, c{kil{}, mil{m::Middle}}}) \ - X(triggerUp, trig, "t_up", std::initializer_list<c>{c{{k::W}}}) \ - X(triggerDown, trig, "t_down", std::initializer_list<c>{c{{k::S}}}) \ - X(triggerNextPack, trig, "t_next", \ - std::initializer_list<c>{c{{k::PageDown}}}) \ - X(triggerPreviousPack, trig, "t_previous", \ - std::initializer_list<c>{c{{k::PageUp}}}) \ - X(triggerLuaConsole, trig, "t_lua_console", \ - std::initializer_list<c>{c{{k::F1}}}) \ - X(triggerPause, trig, "t_pause", std::initializer_list<c>{c{{k::F2}}}) +#define X_LINKEDVALUES_BINDS_TRIGGERS \ + X(triggerRotateCCW, trig, "t_rotate_ccw", \ + cil{cmb{{k::A}}, cmb{{k::Left}}, cmb{kil{}, mil{m::Left}}}) \ + X(triggerRotateCW, trig, "t_rotate_cw", \ + cil{cmb{{k::D}}, cmb{{k::Right}}, cmb{kil{}, mil{m::Right}}}) \ + X(triggerFocus, trig, "t_focus", \ + cil{cmb{{k::LShift}}, cmb{kil{}, mil{m::XButton1}}}) \ + X(triggerSelect, trig, "t_select", \ + cil{cmb{{k::Space}}, cmb{kil{}, mil{m::Middle}}}) \ + X(triggerExit, trig, "t_exit", \ + cil{cmb{{k::T}}, cmb{kil{}, mil{m::XButton2}}}) \ + X(triggerForceRestart, trig, "t_force_restart", \ + cil{cmb{{k::Up}}, cmb{{k::R}}}) \ + X(triggerRestart, trig, "t_restart", \ + cil{cmb{{k::Space}}, cmb{{k::Return}}, cmb{kil{}, mil{m::Middle}}}) \ + X(triggerReplay, trig, "t_replay", cil{cmb{{k::Y}}}) \ + X(triggerScreenshot, trig, "t_screenshot", cil{cmb{{k::F12}}}) \ + X(triggerSwap, trig, "t_swap", \ + cil{cmb{{k::Space}}, cmb{kil{}, mil{m::Middle}}}) \ + X(triggerUp, trig, "t_up", cil{cmb{{k::W}}}) \ + X(triggerDown, trig, "t_down", cil{cmb{{k::S}}}) \ + X(triggerNextPack, trig, "t_next", cil{cmb{{k::PageDown}}}) \ + X(triggerPreviousPack, trig, "t_previous", cil{cmb{{k::PageUp}}}) \ + X(triggerLuaConsole, trig, "t_lua_console", cil{cmb{{k::F1}}}) \ + X(triggerPause, trig, "t_pause", cil{cmb{{k::F2}}}) #define X_LINKEDVALUES_BINDS \ X_LINKEDVALUES_BINDS_JOYSTICK \ @@ -183,6 +211,9 @@ using mil = std::initializer_list<ssvs::MBtn>; X(serverControlPort, ushort, "server_control_port", 50506) \ X(serverLevelWhitelist, std::vector<std::string>, \ "server_level_whitelist", defaultServerLevelWhitelist()) \ + X(saveLastLoginUsername, bool, "save_last_login_username", true) \ + X(lastLoginUsername, std::string, "last_login_username", "") \ + X(showLoginAtStartup, bool, "show_login_at_startup", false) \ X_LINKEDVALUES_BINDS namespace hg::Config { @@ -327,6 +358,11 @@ void loadConfig(const std::vector<std::string>& mOverridesIds) } syncAllFromObj(); +} + +void reapplyResolution() +{ + ssvu::lo("::reapplyResolution") << "reapplying resolution\n"; if(getWindowedAutoResolution()) { @@ -346,18 +382,7 @@ void resetConfigToDefaults() ssvu::lo("::resetConfigToDefaults") << "resetting configs\n"; resetAllFromDefault(); - - if(getWindowedAutoResolution()) - { - applyAutoWindowedResolution(); - } - - if(getFullscreenAutoResolution()) - { - applyAutoFullscreenResolution(); - } - - recalculateSizes(); + reapplyResolution(); } void resetBindsToDefaults() @@ -690,6 +715,21 @@ void setServerLevelWhitelist(const std::vector<std::string>& levelValidators) serverLevelWhitelist() = levelValidators; } +void setSaveLastLoginUsername(bool mX) +{ + saveLastLoginUsername() = mX; +} + +void setLastLoginUsername(const std::string& mX) +{ + lastLoginUsername() = mX; +} + +void setShowLoginAtStartup(bool mX) +{ + showLoginAtStartup() = mX; +} + [[nodiscard]] bool getOfficial() { return official(); @@ -1000,6 +1040,21 @@ void setServerLevelWhitelist(const std::vector<std::string>& levelValidators) return serverLevelWhitelist(); } +[[nodiscard]] bool getSaveLastLoginUsername() +{ + return saveLastLoginUsername(); +} + +[[nodiscard]] const std::string& getLastLoginUsername() +{ + return lastLoginUsername(); +} + +[[nodiscard]] bool getShowLoginAtStartup() +{ + return showLoginAtStartup(); +} + //*********************************************************** // // KEYBOARD/MOUSE BINDS |