-
Notifications
You must be signed in to change notification settings - Fork 6
Light Iterators
- You should learn about basic lighting events first.
Light iterators are designed to run LightEvents through a sequence of "conditions" and "processes".
It was initially designed to solve the problem of lighting custom environment lights. The issue being that custom environment lights would create new light IDs that aren't available in the editor. You could get around this by "allocating" existing light IDs and converting them in the script to your custom IDs.
rm.lightIterator()
.checkIDs([1, 2, 3])
.setIDs(4)
.run(map)- Note: this is equivalent to:
// IDs to allow
const checkIDs = [1, 2, 3]
map.lightEvents.forEach((light) => {
if (light.lightID === undefined) return
// normalize light IDs to number[] instead of number | number[]
const lightIDArray = typeof light.lightID === 'number'
? [light.lightID]
: light.lightID
// check that `lightIDArray` contains any number from `checkIDs`
const hasAtLeastOneID = lightIDArray.some((x) => checkIDs.some((y) => y === x))
if (hasAtLeastOneID) {
light.lightID = 4
}
})When the run function is called on a light iterator, it will run all of the conditions on every light event in the provided difficulty. Based on whatever lights pass all of the conditions, all of the processes will then be run on each one.
The built-in functions require a bit of explanation. Some functions are designed to work with existing light events incoming from the editor. These functions become unavailable after using setIDs because they're no longer relevant.
There are lots of provided conditions and processes but you can also add your own.
rm.lightIterator()
.addCondition(event => {
return event.beat < 20
})
.addProcess(event => {
event.beat += 1
})
.run(map)- The
addConditionfunction accepts a function parameter that takes in a light event and returns true or false. It adds this function to it's internalconditions. - The
addProcessfunction accepts a function parameter that takes in a light event and returns nothing. It adds this function to it's internalprocesses.
You can process an array of events through a light iterator with the processEvents function.
rm.lightIterator()
.setIDs(1)
.processEvents(myEvents)Some of the light ID functions on light iterators are a little confusing at first glance as to what they are trying to achieve. Mainly this is covering the following functions:
shiftIDsnormalizeLinearnormalizeWithChangesremapIDs
As talked about above, the purpose of light iterators focus on transferring "allocated" existing IDs to custom IDs.
Let's say you have events on IDs 1, 2, 3, and you want them to correspond to IDs 1001, 1002, 1003. In this case this is easy, you can just use the shiftIDs function with an offset of 1000.
rm.lightIterator()
.shiftIDs(1000)
.run(map)But what happens if you want the same output IDs, but your inputs are 1, 3, 5? This is where the concept of "normalization" comes in, we want this sequence back to 1, 2, 3 before we shift it. The first thing we can notice is the "step" (difference) between each ID is 2.

For this purpose, we can easily use normalizeLinear. It takes in parameters start and step. Our "start" is 1, since the sequence starts at 1, and our "step" is 2, since there is a difference of 2 between each light ID.
rm.lightIterator()
.normalizeLinear(1, 2)
.shiftIDs(1000)
.run(map)Typically light IDs are simple, with a fixed "step". However, sometimes there are cases where the "step" changes. For example, let's say we have our input IDs in the sequence 1, 2, 4. This time, our "step" is at 1 at the first number, and then changes to 2 at the second number.

We will use the normalizeWithChanges function in order to transform this into a "normalized" (1, 2, 3...) sequence again. It takes in parameters start and map. The start of our sequence is 1, and the map basically encodes positions in the sequence and what step they have. For example:
{
1: 1 // at the first number our step is 1
2: 2 // at the second number our step is 2
}So in order to fix our sequence, we can write our light iterator like so:
rm.lightIterator()
.normalizeWithChanges(1, {
1: 1,
2: 2,
})
.shiftIDs(1000)
.run(map)Let's say there's not really a pattern in the IDs we want to transfer, maybe the sequence isn't even chronological. For example, we want to change the sequence 3, 9, 5, 6 into the sequence 1000, 1001, 1002, 1003. We can see a direct correlation from one ID to another:
3 --> 1000
9 --> 1001
5 --> 1002
6 --> 1003
In order to achieve this, we can use the remapIDs function. It takes a dictionary that maps input IDs to output IDs, similarly to what was shown above:
rm.lightIterator()
.remapIDs({
3: 1000,
9: 1001,
5: 1002,
6: 1003
})
.run(map)- Info
- Difficulty
- Beatmap Objects
- Gameplay Objects
- Walls
- Basic Notemods
- Note Iterators
- Basic Events
- V3 Events
- Custom Events
- Heck Tracks and Animation
- Easings
- Point Types
- Point Utilities
- Heck Animation Baking
- Heck Animation Settings
Non-Vivify Models
Vivify