Curse mechanism
In the following content, midspins will be considered as 0° angle.
What is curse?
Curse is a type of object that can be applied on ADOFAI Levels which performs the corrsponding transform action for parts that meets certain conditions.
A curse is usually binded with a certain ADOFAI level, and use the (slightly modified) ID of the level as the ID of the curse.
Curse effects that don't binds to levels also exists, for example, Color Curse Series by 小绿君.
To describe a curse: But statement
A brief introduction to describe a curse effect is called a but statement.
The actual effect might be a little different between what the but statement describes.
But statements are usually written after the title of the level that the curse effect belongs to, for example:
This website recommends using the following level title format:
To describe a cursed level: Curse hints
The curses the player experiences, including the current and the next, could be tipped by using text decorations on the bottom of the interface called curse hints or curse prompts.
The tips shouldn't move or rotate with the camera, or they will not have a prompt effect.
Although it's already not understandable for most of the viewers...
Countdown
Countdown is the most commonly used method to inform players when the curse effect will be changed.
The countdown starts with 3, decreases by 1 in equal interval time.
The curse effects will be updated at the exact same time when the number should be counted down to -1.
Each number should poduce a noticable hitsound without ambiguity, usually the same sound for paused beat countdowns.
The countdowns are usually located at the end of the line before the colon, surrounded by brackets, with a extra space before the left bracket only:
Functions like functions
Function:
- get a number
- modify that number
- return a modified number
Curse:
- get a level
- curse that level
- return a cursed level
They are just so alike, that it's actually possible to treat curses like functions, for example:
is equivalent to:
However, this cannot explain that some curses may return different results, even if the inputs are the same.
For example, 2-X?
could also repeats the near-ending section of B-X? Please Stop Playing My Levels
by 2 more times, instead of just 1 more time that we have.
This results in:
is NOT equal to:
To solve this problem, one way is to change the output of curses, from a single level, to a set of levels:
And the cursed level that we have is one of them inside.
This also solves some other problems below:
- Some curses weren't well-defined, so there's thousands of results in thousands of cursed ones' perspective
-
sadly there's no thousands of cursed ones for now :(
All of them are in the output set. You are free to ignore results you don't agree. - Some curses weren't well-defined, so they just cannot produce any valid results for certain situations
-
The output should be the empty set
∅.
We'll also change the input to a set of levels via a few definitions:
Cursing a set which only contains a single level is the same as cursing that level.
For example,
2-X? ( B-X )
is equal to
2-X? ( { B-X } ) .
Cursing a set which contains multiple levels is the same as
cursing each level individually then merge the results into one set.
For example:
is equal to
Cursing the empty set outputs the complement set of the output set with the universe set as the input set.
Corollary: Curse evaluation distributes over union operation, as long as none of the input sets are empty.
This isn't only for consistency, but also because that it would be really helpful to define concepts, for example:
Applying multiple curses at once
Multiplication signs * are used to present that
multiple curses takes effect in order from left to right.
Function composition
signs ∘ are used to present that
multiple curses takes effect in order from right to left.
Here's an example that shows the importance of the curse order:
11-X? * XC-X?
will first add twirls onto every tile, then remove all the twirls,
so the result is that all the twirls are missing;
11-X? ∘ XC-X?
will first remove all the twirls, then add twirls onto every tile,
so the result is that there's a twirl on every tile.
Multiplication signs can be omitted but function composition signs cannot be omitted:
Nesting curse functions is another way to specify the curse order:
However, abusing nesting would cause a lot of brackets to stack, therefore the hints would be even harder to parse.
Keywords
- None
-
The identity curse, or in other words,
None(x) = x.Current curse: None - ...
-
Replace curses that you don't want to hint for some reason, for example, you don't want to reveal the next curse too early.
Next curse: ...Or, just use it for actual ellipsis purposes:
Also known as Ellipsis.
- StopIteration
-
The curse effect won't change before level ends, so the player can now finally ignore the hint and fully focuses on the chart.
Next curse: StopIteration
Multiple instances of the same curse effect
Applying a curse is actually applying an instance of the curse.
Subscripts are used to explicitly indicate that if multiple curses are the same instance or not.
(Thanks Regularly for the format method!)
Curse instance are initialized at its first appearance.
Curse instance are still able to be reused after its first disappearance, however the default behavior is that,
if a curse instance without subscript stops appearing inside current curse, the next curse wouldn't use that instance of that curse.
Next curse: 12-X? * XR-X? # instance a * instance x
Next curse: XR-X? # instance x
Next curse: 12-X? # instance b
Next curse: 12-X?1 # instance 1
Next curse: 12-X? # instance c
Next curse: 12-X?1 * 12-X? # instance 1 * instance c
Next curse: 12-X? * 12-X? # instance c * instance d
Some curse may have needs to store some temporary data used for calculations, for example,
12-X? store all unique loop has been played since the effect starts.
Temporary data are stored inside each curse instance seperately.
Iterating curses
Self-explanatory.
Anticurses
An anti for a curse is created by replacing a concept with its opposite concept inside that curses' but statement.
Anticurses should be represented changing the - sign
to the + sign in the ID of the corresponding curse, if possible:
In the deprecated sequential format, cures could also be represented by inserting the
- sign at the beginning of the ID:
Here's a way to abbreviate 2 opposite curses which are next to each other that's not deprecated since it's indeed convenient:
( Thanks DragonFire28 for the suggestion of the color! )
The anti of an anticurse should be the original curse.
The result of anti may be not unique. For example:
Next curse: 2+X? # Offbeats but it's significantly longer
Next curse: 2+X? # Offbeats but it's a little shorter
It would be better to reverse curses in a deterministic way...
Cures
The cure for a curse assumes the level is cursed by that curse,
and reverts the cursed level back to its not-cursed-yet state.
This concept is similar yet different to the
inverse for functions.
For each curse C,
its cure C-1 exists,
that for each level L and LC,
LC ∈ C ( L ) is equivalent to
L ∈ C-1 ( LC ).
The difference between this above and inverse for functions is that this uses ∈ instead of =.
Due to the difference above, if multiple levels can be cursed into the same cursed level, cure the cursed level back returns all those levels in the set, so the result might not be the original level.
Cures should be represented via the power operation of functions:
The cure of an cure is the original curse.
Some curses have themselves as the cure, in other words,
LC ∈ C ( L ) is equivalent to
L ∈ C ( LC ).
They acts like a coin flipper which always flip the coin into the otherside perfectly, so using them twice acts exactly like nothing happens.
Curing a level which is impossible to get from the corresponding curse would produce the empty set ∅.
To avoid these situations in actual cursed level making progress,
you can overwrite the output using the input, like nothing happens.
( Thanks PyrotechnicTriforce for the idea! )
( TODO: there's a way to define it but it requires concepts woould introduce inside the metacurse article )
Using an cure often produces massive amount of possible results
(
O(2n),
O(n!),
even O(∞) theoredically
) .
Metacurse
Say there exists a levelL and 2 curses C和M,
which curse C is to curse level L,
while curse M is to modify C.
Methods to define metacurses that currently get used by the community for now are shown below:
-
Apply
LwithCcursed byM
(Current curse: M ( C )L → M ( C ) ( L )) -
This is the method this site recommands.
Since the input of a curse should be the thing that gets modified, curseMthat modifies curses should use the curseCthat will get modified as input.The downside is, as a curse, metacurse also output a set of cursed curses, so there's need to define the behavior that curse things with a set of curses:
Cursing a level using a set which contains multiple curses is the same as using each curse to curse that level individually then merge the results into one set. For example:
is equivalent to
Using the empty set as the curse set outputs the empty set, no matter if the set gets cursed is empty or not[Needs Proof].
This site assumes metacurses all works in this way.
-
Apply
LwithCbeforeM
(Current curse: C * ML → M ( C ( L ) )) -
This method avoids the inconvenient of reading the curse hint and defining to curse things using a curse set, as the cost of the uniqueness and reasonableness for metacurses.
Since the input
Mgets isn't theCitself, it's its output set instead,
Mcan't directly know what curseCis, and the contents before getting cursed.Although
Mtechincally can get the curse by reading the curse hint, and get the superset of contents by applyingC-1to its input,
but this is indeed more complicated than just throwingCtoMto deal with, not to mention that cure also has side-effects.Depends on
M's definition, usingMbefore using other curses first may also cause undefined behavior. -
Apply
LwithCafterM
(Current curse: M * CL → C ( M ( L ) )) -
This is the most unreasonable one.
Citself cannot gets modified whenMis taking effect,
Because at this time,Cdoes not have any result, nor it does gets any input fromM, nor it gets inputed intoM, it even might not existed yet, or even at all.Citself cannot gets modified when itself s taking effect,
Because at this time,Mhas already done with its curse progress, and cannot actively do anything to anything else.Therefore,
Mis impossible to impactCitself. This conclusion works the same as the output ofCusing similar steps to proof.Only 1 way that how
Mmight work left: its own output works as intended after being cursed byC. However:- Cannot ensure the curse process exists for every
MandC; - Cannot ensure an reasonable result exists when
Mis the final curse; - Cannot ensure that
Mcan get what curseCis when it takes effect.
TL;DR: Don't.
- Cannot ensure the curse process exists for every