ADOFAI but Cursed

Curse mechanism

Special thanks to DongtDw for contributions to complete and correcting a lot of concepts in this page.

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, Coloer 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:

1-X? A Dance of Fire and Ice but the music is cursed
2-X? Offbeats but it's a little longer

This website recommends using the following level title format:

{level ID} {level title} but {curse effect introduction}

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.

Current curse: 8-X?
Next curse: 10-X?

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:

Current curse: 8-X?
Next curse (3): 10-X?

Current curse: 8-X?
Next curse (2): 10-X?

Current curse: 8-X?
Next curse (1): 10-X?

Current curse: 8-X?
Next curse (0): 10-X?

Current curse: 10-X?
Next curse: XC-X?
Notes that the time order of curse effects IS NOT the affect order of curse effects, and there's no direct relationship between them.

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:

Current level: 1-X? ( B-X )

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:

Current level: 2-X? ( B-X )

is NOT equal to:

Current level: 2-X? ( B-X )

To solve this problem, one way is to treat curses as mappings from levels to cursed levels, and change the output of curses from a single level to a set of levels:

2-X? ( B-X ) → { B-X extended by 1 more times, B-X extended by 2 more times, B-X extended by 3 more times, etc. }

And the result that we have is one of them inside.


Not only that, the input can also be expanded to a set of levels, via a few definitions as shown below:

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 } ) .

Not to be confused with KirbyCreep's curses, which is noted as {N-X} due to my mistake.

Cursing a set which contains multiple levels is the same as cursing each level individually then merge the results into one set.
For example:

5-X? ( { 5-X, 11-X, XC-X } )

is equal to

5-X? ( 5-X ) ∪ 5-X? ( 11-X ) ∪ 5-X? ( XC-X )

It should be noted that curse doesn't required to map every level of its domain to atleast 1 output of its preimage,
similar to that the image (set of every defined output) doesn't required to be equal to codomain (set of possible outputs if ignoring the mapping itself).

If an input belongs to the domain, but cannot be found in mappings (aka doesn't belongs to the preimage), the output set of cursing this input is empty.

Similarly, if an output belongs to the codomain, but cannot be found in mappings (aka doesn't belongs to the image), it belongs to the the output set of cursing empty set (or, the output of cursing the empty set is the difference of the codomain and the image).

Applying multiple curses at once

The notation method that use plus signs + to represent that multiple curses takes effect in order from left to right has been DEPRECATED!

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:

Current curse: 11-X? * XC-X?
Next curse: 11-X?XC-X?

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:

Current curse: 11-X? XC-X?
Next curse: 11-X?XC-X?

Nesting curse functions is another way to specify the curse order:

Current level: XC-X?( 11-X?( 5-X ) )
Next level: 11-X?( XC-X?( 5-X ) )

However, abusing nesting would cause a lot of brackets to stack, therefore the hints would be even harder to parse.

Keywords

None

The identity mapping, 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:

Current curse: 8-X? * 10-X? * 5-X? * 11-X?
Next curse: ... * 2-X?

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.

Current curse: 12-X?1
Next curse: 12-X?2
Current curse: 12-X?_1
Next curse: 12-X?_2

(Thanks Regularly for the format method!)

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.

Anticurses

Not to be confused with Cure, another way to to create paired curses.

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:

Current curse: XC-X?
Next curse: XC+X?

In the deprecated sequential format, cures could also be represented by inserting the - sign at the beginning of the ID:

Current curse: - XC-X?
Next curse: XC-X? - XC-X?

Here's a way to abbreviate 2 opposite curses which are next to each other that's not deprecated since it's indeed convenient:

Current curse: XC-X? - XC-X?
Current curse: ± XC-X?
Current curse: - XC-X? + XC-X?
Current curse: XC-X?

( 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:

Current curse: 2-X? # Offbeats but it's a little longer
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

Not to be confused with Anti, another way to to create paired curses.

The cure for a curse works like to assume the level is cursed by that curse, then recover the cursed level back to its not-cursed-yet state,
therefore can be used to cancel (but cannot guarantee to cancel, reason see below) the changes caused by its corresponding curse.

The cure is defined as a new curse formed by swapping each pair of input and output in the mapping of its corresponding curse.
The domain and codomain also swaps, as same as the preimage and the image.


Cures should be represented via the power operation of functions:

Current curse: XH-X?-1
Current curse: XH-X?^-1
Current curse: XH-X?-1 * XH-X?-1 * XH-X?-1
Current curse: XH-X?-3

Here's some inference below:

An cure of an cure of an curse is equivalent to that curse itself.

(8-X?-1)-1 = 8-X?

If and only if, for all X→Y, exists Y→X in the mapping of an curse, the cure of that curse is equivalent to the curse itself.

An arbitrary level set must be the subset of the cursed level set by applying an arbitrary curse and then its cure.

{ 11-X } ⊆ 11-X?-1 ( 11-X? ( { 11-X } ) )

Curing a level which is impossible to get from the corresponding curse would cause the output set to be empty.


In practice, if a level is impossible to get from the corresponding curse,
you can remove the parts that causes the level being uncureable first,
or just directly use the input as the output, 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 )

Metacurse

Say there exists a level L and 2 curses C and 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 L with C cursed by M
(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, curse M that modifies curses should use the curse C that will get modified as input.
This site assumes metacurses all works in this way. Most of the curses that don't are able to be rewritten into this form.

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:
When using a set of curses as the curse, its behavior is the same as the curse which its mapping is the union of the mappings of each curse in that curse set.

Here's some inference below:

The result of using a set of curses is the same as the union of the results of using each curse in that curse set.

{ 5-X?, 11-X?, XC-X? } ( 5-X ) = 5-X? ( 5-X ) ∪ 11-X? ( 5-X ) ∪ XC-X? ( 5-X )

The cure of a curse set works the same as the set of cures for each curse.

{ 5-X?, 11-X?, XC-X? }-1 = { 5-X?-1, 11-X?-1, XC-X?-1 }

When using the empty set as the curse set, if the input set is not empty, the output set is empty:

∅ ( { 1-X } ) = ∅
∅ ( { 1-X, 2-X } ) = ∅

Otherwise, the output set is the codomain.

The cure of the empty curse set is still the empty curse set.

-1 = ∅

Apply L with C before M
(Current curse: C * M L → 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 M gets isn't the C itself, it's its output set instead,
M can't directly know what curse C is, and the contents before getting cursed.

Although M techincally can get the curse by reading the curse hint, and get the superset of contents by applying C-1 to its input,
but this is indeed more complicated than just throwing C to M to deal with, not to mention that cure also has side-effects.

Depends on M's definition, using M before using other curses first may also cause undefined behavior.


Apply L with C after M
(Current curse: M * C L → C ( M ( L ) ))

This is the most unreasonable one.

C itself cannot gets modified when M is taking effect,
Because at this time, C does not have any result, nor it does gets any input from M, nor it gets inputed into M, it even might not existed yet, or even at all.

C itself cannot gets modified when itself s taking effect,
Because at this time, M has already done with its curse progress, and cannot actively do anything to anything else.

Therefore, M is impossible to impact C itself. This conclusion works the same as the output of C using similar steps to proof.

Only 1 way that how M might work left: its own output works as intended after being cursed by C. However:

  • Cannot ensure the curse process exists for every M and C;
  • Cannot ensure an reasonable result exists when M is the final curse;
  • Cannot ensure that M can get what curse C is when it takes effect.

TL;DR: Don't.