summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVittorio Romeo <vittorio.romeo@outlook.com>2021-10-29 01:22:29 +0100
committerGitHub <noreply@github.com>2021-10-29 01:22:29 +0100
commitf36a5e40d776a1c879b67307f3cf9e2147f59668 (patch)
treefb54029b355e4818aa9ffe13d74797d8d726c490
parent10eb8abbe7c124eca2d59bfeb6739bb447b5377f (diff)
parent909e57c84f1d0fffdc9b951ecf5b70bc58db463a (diff)
Merge pull request #358 from SuperV1234/2.0.7
Open Hexagon v2.0.7
-rwxr-xr-x.gitignorebin3324 -> 3353 bytes
-rw-r--r--_RELEASE/Assets/OpenSquare-Bold.ttfbin13692 -> 13792 bytes
-rw-r--r--_RELEASE/Assets/OpenSquare-Regular.ttfbin14344 -> 14504 bytes
-rw-r--r--_RELEASE/Assets/assets.json81
-rw-r--r--_RELEASE/ConfigOverrides/debug.json6
-rw-r--r--_RELEASE/ConfigOverrides/fraps.json12
-rw-r--r--_RELEASE/ConfigOverrides/fullscreen.json4
-rw-r--r--_RELEASE/ConfigOverrides/highfps.json16
-rw-r--r--_RELEASE/ConfigOverrides/localserver.json4
-rw-r--r--_RELEASE/ConfigOverrides/no3D.json4
-rw-r--r--_RELEASE/ConfigOverrides/noaudio.json6
-rw-r--r--_RELEASE/ConfigOverrides/nocosmeticscript.json8
-rw-r--r--_RELEASE/ConfigOverrides/remoteserver.json4
-rw-r--r--_RELEASE/ConfigOverrides/windowed.json4
-rw-r--r--_RELEASE/Packs/base/Scripts/common.lua89
-rw-r--r--_RELEASE/Packs/base/Scripts/commonpatterns.lua91
-rw-r--r--_RELEASE/Packs/base/Scripts/evolutionpatterns.lua626
-rw-r--r--_RELEASE/Packs/base/Scripts/nextpatterns.lua314
-rw-r--r--_RELEASE/Packs/cube/Scripts/Levels/apeirogon.lua2
-rw-r--r--_RELEASE/Packs/cube/Scripts/Levels/commando.lua2
-rw-r--r--_RELEASE/Packs/cube/Scripts/Levels/euclideanpc.lua2
-rw-r--r--_RELEASE/Packs/cube/Scripts/Levels/flatteringshape.lua2
-rw-r--r--_RELEASE/Packs/cube/Scripts/Levels/goldenratio.lua4
-rw-r--r--_RELEASE/Packs/cube/Scripts/Levels/labyrinth.lua2
-rw-r--r--_RELEASE/Packs/cube/Scripts/Levels/pointless.lua9
-rw-r--r--_RELEASE/Packs/cube/Scripts/Levels/seconddimension.lua2
-rw-r--r--_RELEASE/Packs/cube/Styles/labyrinth.json8
-rw-r--r--_RELEASE/Packs/experimental/Levels/customtimeline.json12
-rw-r--r--_RELEASE/Packs/experimental/Scripts/Levels/3D_required.lua2
-rw-r--r--_RELEASE/Packs/experimental/Scripts/Levels/autotest0.lua2
-rw-r--r--_RELEASE/Packs/experimental/Scripts/Levels/curvetest.lua2
-rw-r--r--_RELEASE/Packs/experimental/Scripts/Levels/curvetest2.lua2
-rw-r--r--_RELEASE/Packs/experimental/Scripts/Levels/customtimeline.lua80
-rw-r--r--_RELEASE/Packs/experimental/Scripts/Levels/cw.lua2
-rw-r--r--_RELEASE/Packs/experimental/Scripts/Levels/negative_pulse.lua2
-rw-r--r--_RELEASE/Packs/experimental/Scripts/Levels/stress1.lua2
-rw-r--r--_RELEASE/Packs/experimental/Styles/construct.json2
-rw-r--r--_RELEASE/Packs/hypercube/Levels/acceleradiant.json4
-rw-r--r--_RELEASE/Packs/hypercube/Levels/centrifugal.json4
-rw-r--r--_RELEASE/Packs/hypercube/Levels/disc-o.json4
-rw-r--r--_RELEASE/Packs/hypercube/Levels/g-force.json4
-rw-r--r--_RELEASE/Packs/hypercube/Levels/incongruence.json4
-rw-r--r--_RELEASE/Packs/hypercube/Levels/massacre.json4
-rw-r--r--_RELEASE/Packs/hypercube/Levels/polyhedrug.json4
-rw-r--r--_RELEASE/Packs/hypercube/Levels/reppaws.json4
-rw-r--r--_RELEASE/Packs/hypercube/Scripts/Levels/acceleradiant.lua31
-rw-r--r--_RELEASE/Packs/hypercube/Scripts/Levels/centrifugal.lua34
-rw-r--r--_RELEASE/Packs/hypercube/Scripts/Levels/disc-o.lua109
-rw-r--r--_RELEASE/Packs/hypercube/Scripts/Levels/evotutorial.lua140
-rw-r--r--_RELEASE/Packs/hypercube/Scripts/Levels/g-force.lua37
-rw-r--r--_RELEASE/Packs/hypercube/Scripts/Levels/incongruence.lua61
-rw-r--r--_RELEASE/Packs/hypercube/Scripts/Levels/massacre.lua158
-rw-r--r--_RELEASE/Packs/hypercube/Scripts/Levels/polyhedrug.lua6
-rw-r--r--_RELEASE/Packs/hypercube/Scripts/Levels/reppaws.lua38
-rw-r--r--_RELEASE/Packs/hypercube/Scripts/Levels/slither.lua70
-rw-r--r--_RELEASE/Packs/orthoplex/Scripts/Levels/bipolarity.lua14
-rw-r--r--_RELEASE/Packs/tutorial/Scripts/Levels/babysteps.lua2
-rw-r--r--_RELEASE/Packs/workshopexample/Scripts/Levels/examplelevel.lua2
-rw-r--r--_RELEASE/config.json78
-rw-r--r--art/a38_disco_hard_l.pngbin0 -> 3889 bytes
-rw-r--r--art/a38_disco_hard_u.pngbin0 -> 6171 bytes
-rw-r--r--art/a39_acceleradiant_hard_l.pngbin0 -> 4081 bytes
-rw-r--r--art/a39_acceleradiant_hard_u.pngbin0 -> 6484 bytes
-rw-r--r--art/a40_gforce_hard_l.pngbin0 -> 3873 bytes
-rw-r--r--art/a40_gforce_hard_u.pngbin0 -> 6088 bytes
-rw-r--r--art/a41_incongruence_hard_l.pngbin0 -> 4204 bytes
-rw-r--r--art/a41_incongruence_hard_u.pngbin0 -> 6666 bytes
-rw-r--r--art/a42_slither_hard_l.pngbin0 -> 4187 bytes
-rw-r--r--art/a42_slither_hard_u.pngbin0 -> 6856 bytes
-rw-r--r--art/a43_polyhedrug_hard_l.pngbin0 -> 4168 bytes
-rw-r--r--art/a43_polyhedrug_hard_u.pngbin0 -> 6653 bytes
-rw-r--r--art/a44_reppaws_hard_l.pngbin0 -> 4055 bytes
-rw-r--r--art/a44_reppaws_hard_u.pngbin0 -> 6403 bytes
-rw-r--r--art/a45_centrifugalforce_hard_l.pngbin0 -> 4061 bytes
-rw-r--r--art/a45_centrifugalforce_hard_u.pngbin0 -> 6432 bytes
-rw-r--r--art/a46_massacre_hard_l.pngbin0 -> 4090 bytes
-rw-r--r--art/a46_massacre_hard_u.pngbin0 -> 6580 bytes
-rw-r--r--art/a47_hypercubegod_l.pngbin0 -> 4873 bytes
-rw-r--r--art/a47_hypercubegod_u.pngbin0 -> 7891 bytes
-rw-r--r--art/font/Open Square Bold.sfd77
-rw-r--r--art/font/Open Square.sfd91
-rw-r--r--art/font/OpenSquare-Bold.ttfbin13680 -> 13792 bytes
-rw-r--r--art/font/OpenSquare-Regular.ttfbin14320 -> 14504 bytes
-rw-r--r--art/font/OpenSquare.ttcbin29476 -> 29660 bytes
-rw-r--r--include/SSVOpenHexagon/Components/CCustomWallManager.hpp4
-rw-r--r--include/SSVOpenHexagon/Core/CustomTimeline.hpp17
-rw-r--r--include/SSVOpenHexagon/Core/CustomTimelineHandle.hpp11
-rw-r--r--include/SSVOpenHexagon/Core/CustomTimelineManager.hpp44
-rw-r--r--include/SSVOpenHexagon/Core/HGStatus.hpp12
-rw-r--r--include/SSVOpenHexagon/Core/HexagonGame.hpp26
-rw-r--r--include/SSVOpenHexagon/Core/MenuGame.hpp10
-rw-r--r--include/SSVOpenHexagon/Core/Replay.hpp1
-rw-r--r--include/SSVOpenHexagon/Global/Assets.hpp5
-rw-r--r--include/SSVOpenHexagon/Global/Audio.hpp6
-rw-r--r--include/SSVOpenHexagon/Global/Config.hpp7
-rw-r--r--include/SSVOpenHexagon/Global/Version.hpp4
-rw-r--r--include/SSVOpenHexagon/Online/Shared.hpp2
-rw-r--r--release_checklist.md8
-rw-r--r--src/SSVOpenHexagon/Core/Audio.cpp59
-rw-r--r--src/SSVOpenHexagon/Core/CCustomWallManager.cpp12
-rw-r--r--src/SSVOpenHexagon/Core/CustomTimelineManager.cpp71
-rw-r--r--src/SSVOpenHexagon/Core/HGGraphics.cpp29
-rw-r--r--src/SSVOpenHexagon/Core/HGScripting.cpp220
-rw-r--r--src/SSVOpenHexagon/Core/HGStatus.cpp12
-rw-r--r--src/SSVOpenHexagon/Core/HGUpdate.cpp68
-rw-r--r--src/SSVOpenHexagon/Core/HexagonDialogBox.cpp4
-rw-r--r--src/SSVOpenHexagon/Core/HexagonGame.cpp10
-rw-r--r--src/SSVOpenHexagon/Core/LuaScripting.cpp26
-rw-r--r--src/SSVOpenHexagon/Core/MenuGame.cpp140
-rw-r--r--src/SSVOpenHexagon/Core/Steam.cpp182
-rw-r--r--src/SSVOpenHexagon/Core/main.cpp14
-rw-r--r--src/SSVOpenHexagon/Data/StyleData.cpp2
-rw-r--r--src/SSVOpenHexagon/Global/Assets.cpp68
-rw-r--r--src/SSVOpenHexagon/Global/Config.cpp147
114 files changed, 2660 insertions, 951 deletions
diff --git a/.gitignore b/.gitignore
index fa76b2f7..6638cbcf 100755
--- a/.gitignore
+++ b/.gitignore
Binary files differ
diff --git a/_RELEASE/Assets/OpenSquare-Bold.ttf b/_RELEASE/Assets/OpenSquare-Bold.ttf
index 4a2defa5..0ebf62e5 100644
--- a/_RELEASE/Assets/OpenSquare-Bold.ttf
+++ b/_RELEASE/Assets/OpenSquare-Bold.ttf
Binary files differ
diff --git a/_RELEASE/Assets/OpenSquare-Regular.ttf b/_RELEASE/Assets/OpenSquare-Regular.ttf
index 1198160b..b695c12c 100644
--- a/_RELEASE/Assets/OpenSquare-Regular.ttf
+++ b/_RELEASE/Assets/OpenSquare-Regular.ttf
Binary files differ
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
new file mode 100644
index 00000000..91ebfa45
--- /dev/null
+++ b/art/a38_disco_hard_l.png
Binary files differ
diff --git a/art/a38_disco_hard_u.png b/art/a38_disco_hard_u.png
new file mode 100644
index 00000000..b755e7d8
--- /dev/null
+++ b/art/a38_disco_hard_u.png
Binary files differ
diff --git a/art/a39_acceleradiant_hard_l.png b/art/a39_acceleradiant_hard_l.png
new file mode 100644
index 00000000..868ebe7c
--- /dev/null
+++ b/art/a39_acceleradiant_hard_l.png
Binary files differ
diff --git a/art/a39_acceleradiant_hard_u.png b/art/a39_acceleradiant_hard_u.png
new file mode 100644
index 00000000..e2107c1e
--- /dev/null
+++ b/art/a39_acceleradiant_hard_u.png
Binary files differ
diff --git a/art/a40_gforce_hard_l.png b/art/a40_gforce_hard_l.png
new file mode 100644
index 00000000..fbd84532
--- /dev/null
+++ b/art/a40_gforce_hard_l.png
Binary files differ
diff --git a/art/a40_gforce_hard_u.png b/art/a40_gforce_hard_u.png
new file mode 100644
index 00000000..6a17f1a4
--- /dev/null
+++ b/art/a40_gforce_hard_u.png
Binary files differ
diff --git a/art/a41_incongruence_hard_l.png b/art/a41_incongruence_hard_l.png
new file mode 100644
index 00000000..7964f934
--- /dev/null
+++ b/art/a41_incongruence_hard_l.png
Binary files differ
diff --git a/art/a41_incongruence_hard_u.png b/art/a41_incongruence_hard_u.png
new file mode 100644
index 00000000..c22555cd
--- /dev/null
+++ b/art/a41_incongruence_hard_u.png
Binary files differ
diff --git a/art/a42_slither_hard_l.png b/art/a42_slither_hard_l.png
new file mode 100644
index 00000000..5677d110
--- /dev/null
+++ b/art/a42_slither_hard_l.png
Binary files differ
diff --git a/art/a42_slither_hard_u.png b/art/a42_slither_hard_u.png
new file mode 100644
index 00000000..304800b8
--- /dev/null
+++ b/art/a42_slither_hard_u.png
Binary files differ
diff --git a/art/a43_polyhedrug_hard_l.png b/art/a43_polyhedrug_hard_l.png
new file mode 100644
index 00000000..26f20816
--- /dev/null
+++ b/art/a43_polyhedrug_hard_l.png
Binary files differ
diff --git a/art/a43_polyhedrug_hard_u.png b/art/a43_polyhedrug_hard_u.png
new file mode 100644
index 00000000..727056ae
--- /dev/null
+++ b/art/a43_polyhedrug_hard_u.png
Binary files differ
diff --git a/art/a44_reppaws_hard_l.png b/art/a44_reppaws_hard_l.png
new file mode 100644
index 00000000..a71b194d
--- /dev/null
+++ b/art/a44_reppaws_hard_l.png
Binary files differ
diff --git a/art/a44_reppaws_hard_u.png b/art/a44_reppaws_hard_u.png
new file mode 100644
index 00000000..ddbcf913
--- /dev/null
+++ b/art/a44_reppaws_hard_u.png
Binary files differ
diff --git a/art/a45_centrifugalforce_hard_l.png b/art/a45_centrifugalforce_hard_l.png
new file mode 100644
index 00000000..9b3e16e7
--- /dev/null
+++ b/art/a45_centrifugalforce_hard_l.png
Binary files differ
diff --git a/art/a45_centrifugalforce_hard_u.png b/art/a45_centrifugalforce_hard_u.png
new file mode 100644
index 00000000..84870b07
--- /dev/null
+++ b/art/a45_centrifugalforce_hard_u.png
Binary files differ
diff --git a/art/a46_massacre_hard_l.png b/art/a46_massacre_hard_l.png
new file mode 100644
index 00000000..54225da2
--- /dev/null
+++ b/art/a46_massacre_hard_l.png
Binary files differ
diff --git a/art/a46_massacre_hard_u.png b/art/a46_massacre_hard_u.png
new file mode 100644
index 00000000..58cd673e
--- /dev/null
+++ b/art/a46_massacre_hard_u.png
Binary files differ
diff --git a/art/a47_hypercubegod_l.png b/art/a47_hypercubegod_l.png
new file mode 100644
index 00000000..3557726d
--- /dev/null
+++ b/art/a47_hypercubegod_l.png
Binary files differ
diff --git a/art/a47_hypercubegod_u.png b/art/a47_hypercubegod_u.png
new file mode 100644
index 00000000..5d32f34a
--- /dev/null
+++ b/art/a47_hypercubegod_u.png
Binary files differ
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
index eef821f9..0ebf62e5 100644
--- a/art/font/OpenSquare-Bold.ttf
+++ b/art/font/OpenSquare-Bold.ttf
Binary files differ
diff --git a/art/font/OpenSquare-Regular.ttf b/art/font/OpenSquare-Regular.ttf
index aef149d6..b695c12c 100644
--- a/art/font/OpenSquare-Regular.ttf
+++ b/art/font/OpenSquare-Regular.ttf
Binary files differ
diff --git a/art/font/OpenSquare.ttc b/art/font/OpenSquare.ttc
index 4744b68a..1bb8b949 100644
--- a/art/font/OpenSquare.ttc
+++ b/art/font/OpenSquare.ttc
Binary files differ
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