Repository navigation
MilyMC ITM (Modcharts)
Uma ferramenta de modcharting 100% em Lua, desenvolvida especialmente para esta engine. Um sistema livre, poderoso e de código aberto, criado para facilitar o modcharting em músicas de Friday Night Funkin'.
Important
THIS IS REAAAAAAAAAALLY OUTDATED DO NOT USE FOR NOW!!! MILY 4.0.1 IS PRETTY DIFFERENT x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
x
- Mily MC ITM (abreviação de Mily Modcharting: Into the Music) é um sistema de modcharting para Friday Night Funkin', desenvolvido utilizando a API de Lua da Psych Engine e projetado especificamente para a engine ViroViroIce.
- Facilidade e agilidade
- API própria
- Lésbica
- Inúmeras animações matemáticas iradas
- Customização praticamente infinita
- Suporte a modifiers customizados[1]
Os modcharts de música ficam em:
mods/<seu mod>/songs/<nome-da-musica>/modchart.luaSe um modchart.lua tiver erro de sintaxe, a engine tenta avisar no debug em vez de quebrar o backend inteiro do MilyMC.
Esses são os targets principais:
| Target | O que controla |
|---|---|
BF_Strum / "bf" / "player"
|
Lado do player |
DAD_Strum / "dad" / "opponent"
|
Lado do opponent |
Strum_Gen / "gen" / "all" / "both"
|
Os dois lados |
0 até 7
|
Uma lane específica |
{0, 1, 2, 3} |
Várias lanes, apenas nos helpers por lane como setStrumX/Y/Z
|
setModchart('drunk', 1, BF_Strum)
setModchart('flip', 1, DAD_Strum)
setModchart('confusion', 90, 4)
setStrumAngle({0, 3, 4, 7}, 15)Em HScript/Haxe, use strings para os lados:
game.setModchart("drunk", 1, "bf");
game.setModchart("flip", 1, "dad");
game.setModchart("confusion", 90, 4);Se o target não for passado, a maioria das funções usa
Strum_Gen.
Os callbacks do modchart usam nomes próprios para não bater com scripts normais.
| Original | Modchart |
|---|---|
function onCreate() |
function modChartCreate() |
function onCreatePost() |
function modChartCreatePost() |
function onUpdate(elapsed) |
function modChartUpdate(elapsed) |
function onUpdatePost(elapsed) |
function modChartUpdatePost(elapsed) |
function onBeatHit() |
function modChartBeatHit() |
function onStepHit() |
function modChartStepHit() |
function onSectionHit() |
function modChartSectionHit() |
function onMoveCamera(focus) |
function modChartFocus(focus) |
function goodNoteHit(id, nd, nt, sus) |
function modChartBFNote(id, nd, nt, sus) |
function opponentNoteHit(id, nd, nt, sus) |
function modChartDADNote(id, nd, nt, sus) |
function goodNoteHitPre(id, nd, nt, sus) |
function modChartBFNotePre(id, nd, nt, sus) |
function opponentNoteHitPre(id, nd, nt, sus) |
function modChartDADNotePre(id, nd, nt, sus) |
function noteMiss(id, nd, nt, sus) |
function modChartMiss(id, nd, nt, sus) |
function noteMissPress(id, nd, nt, sus) |
function modChartMissPress(id, nd, nt, sus) |
function onSongStart() |
function modChartSongStart() |
function modChartCreatePost()
setModchart('drunk', 0.6, BF_Strum)
end
function modChartStepHit()
if curStep == 128 then
easeModchart('dadFlipIn', 'flip', 1, 1, 'expoOut', DAD_Strum)
end
end
function modChartFocus(focus)
if focus == 'boyfriend' then
kickPulse('focusBF', BF_Strum, 0.12, 0.45, 'expoOut')
elseif focus == 'dad' then
kickPulse('focusDAD', DAD_Strum, 0.12, 0.45, 'expoOut')
end
endaddModchart inicializa um modifier. Muita coisa inicializa sozinha quando você usa setModchart/easeModchart, mas registrar deixa o debug mais claro.
| Ação | Lua | HScript/Haxe |
|---|---|---|
| Adicionar | addModchart('drunk') |
game.addModchart("drunk"); |
| Remover | clearModchart('drunk') |
game.clearModchart("drunk"); |
function modChartCreatePost()
addModchart('drunk')
addModchart('beat')
addModchart('receptorScroll')
endfunction onCreatePost() {
game.addModchart("drunk");
game.addModchart("beat");
game.addModchart("receptorScroll");
}setModchart muda um modifier imediatamente.
| Lua | HScript/Haxe |
|---|---|
setModchart('drunk', 1.5, BF_Strum) |
game.setModchart("drunk", 1.5, "bf"); |
setModchart('confusion', 90, 4) |
game.setModchart("confusion", 90, 4); |
function modChartStepHit()
if curStep == 64 then
setModchart('drunk', 1.5, BF_Strum)
setModchart('invert', 1, DAD_Strum)
end
endeaseModchart faz tween de um modifier.
easeModchart('tagDoTween', 'drunk', 1.5, 4, 'linear', BF_Strum)O formato antigo ainda funciona:
easeModchart('drunk', 1.5, 4, 'linear', BF_Strum)Em HScript/Haxe, o wrapper atual usa o formato antigo:
game.easeModchart("drunk", 1.5, 4, "linear", "bf");Para saber quando um tween com tag terminou:
function modChartTweenFinished(tag)
if tag == 'tagDoTween' then
debugPrint('Tween terminou!')
end
endUse queueSet e queueEase quando a mudança já tem step certo.
| Ação | Lua | HScript/Haxe |
|---|---|---|
| Set agendado | queueSet(64, 'flip', 1, BF_Strum) |
game.queueSetModchart(64, "flip", 1, "bf"); |
| Ease agendado | queueEase(128, 160, 'drunk', 1.4, 'expoOut', Strum_Gen) |
game.queueEaseModchart(128, 160, "drunk", 1.4, "expoOut", "gen"); |
function modChartCreatePost()
queueSet(64, 'flip', 1, BF_Strum)
queueEase(128, 160, 'drunk', 1.4, 'expoOut', Strum_Gen)
queueEase(192, 224, 'drunk', 0, 'expoInOut', Strum_Gen)
endTambém existem versões em porcentagem:
queueSetP(64, 'stealth', 50, BF_Strum)
queueEaseP(96, 128, 'stealth', 0, 'quadOut', BF_Strum)getMod lê o valor atual. getModDef lê o valor atual ou retorna um padrão.
local curDrunk = getMod('drunk', true, 4)
local curScale = getModDef('receptorScaleX', true, 1, 4)O segundo argumento é isPlayer: true para BF, false para DAD.
Esses helpers mexem diretamente em receptors/lanes. Eles são mais específicos que transformX/Y/Z.
| Função | O que faz | Exemplo |
|---|---|---|
setStrumX(target, value) |
Offset X instantâneo | setStrumX(4, 30) |
setStrumY(target, value) |
Offset Y instantâneo | setStrumY(BF_Strum, -20) |
setStrumZ(target, value) |
Offset Z instantâneo | setStrumZ(Strum_Gen, 0.2) |
setStrumAngle(target, value) |
Ângulo instantâneo | setStrumAngle({0, 1, 2, 3}, 15) |
setStrumScale(target, value) |
Escala instantânea | setStrumScale(BF_Strum, 0.85) |
setStrumAlpha(target, value) |
Alpha instantâneo | setStrumAlpha(DAD_Strum, 0.4) |
| Tween | Exemplo |
|---|---|
easeStrumX / tweenStrumX
|
easeStrumX('bfX', BF_Strum, 80, 1, 'expoOut') |
easeStrumY / tweenStrumY
|
easeStrumY('dadY', DAD_Strum, -60, 1, 'quadOut') |
easeStrumZ / tweenStrumZ
|
easeStrumZ('zoomLane', 4, 0.2, 0.8, 'expoOut') |
easeStrumAngle / tweenStrumAngle
|
easeStrumAngle('spinLane', 7, 360, 1.2, 'cubeOut') |
easeStrumScale / tweenStrumScale
|
easeStrumScale('tinyBF', BF_Strum, 0.7, 0.6, 'quadOut') |
easeStrumAlpha / strumTweenAlpha
|
easeStrumAlpha('hideDad', DAD_Strum, 0.2, 0.4, 'linear') |
Para voltar ao valor original:
setStrumX(BF_Strum, 'og')
setStrumAlpha(DAD_Strum, 'og')Em HScript/Haxe não existe helper direto para todos eles ainda, mas você pode mexer nos modifiers internos:
game.setModchart("strumX4", 30, 4);
game.easeModchart("strumAngle7", 360, 1.2, "cubeOut", 7);transformX/Y/Z move o playfield inteiro. Use isso para deslocamentos grandes de lado/cena. Use setStrumX/Y/Z quando quiser mexer em lanes/receptors específicos.
| Função | O que faz | Exemplo |
|---|---|---|
setTransformX(target, value) |
Move o playfield no X | setTransformX(BF_Strum, 120) |
setTransformY(target, value) |
Move o playfield no Y | setTransformY(DAD_Strum, -60) |
setTransformZ(target, value) |
Move o playfield no Z | setTransformZ(Strum_Gen, 0.15) |
easeTransformX / tweenTransformX
|
Tween no X | easeTransformX('bfSlide', BF_Strum, 0, 1, 'expoOut') |
easeTransformY / tweenTransformY
|
Tween no Y | easeTransformY('dadDrop', DAD_Strum, 80, 0.8, 'bounceOut') |
easeTransformZ / tweenTransformZ
|
Tween no Z | easeTransformZ('allDepth', Strum_Gen, 0, 1, 'quadOut') |
Variação em HScript/Haxe:
game.setModchart("transformX", 120, "bf");
game.easeModchart("transformY", 80, 0.8, "bounceOut", "dad");
game.easeModchart("transformZ", 0, 1, "quadOut", "gen");Essas funções são só atalhos para modifiers de scroll/layout. Elas não são modifiers separados.
Alterna entre 0 e 1 automaticamente:
easeScroll(BF_Strum, 'opposite', 0.75, 'expoOut', 'bfReverseToggle')Ou define um valor exato:
easeScroll(DAD_Strum, 'opposite', 1, 0.75, 'expoOut', 'dadReverseOn')Define/tweeta direto:
setScroll(BF_Strum, 'upscroll', 1, 0.5, 'quadOut', 'bfUp')
setScroll(BF_Strum, 'downscroll', 0, 0.5, 'quadOut', 'bfDown')
setScroll(Strum_Gen, 'cross', 1, 0.6, 'expoOut', 'crossOn')
setScroll(Strum_Gen, 'receptorScroll', 1, 1, 'expoOut', 'recScroll')Nomes comuns: opposite, reverse, scroll, upscroll, downscroll, cross, alternate, receptorScroll, oppSwap.
Kick shots são impactos curtos: setam um modifier para um valor forte e fazem tween de volta.
kickShot(tag, target, modName, startVal, endVal, duration, ease)Exemplos:
function modChartBeatHit()
if curBeat % 4 == 0 then
kickShot('beat_' .. curBeat, Strum_Gen, 'beat', 0.22, 0, 0.55, 'expoOut')
end
if curBeat % 8 == 4 then
kickOut('scale_' .. curBeat, BF_Strum, 0.18, 0.45, 'cubeOut')
end
endAtalhos:
| Função | O que faz |
|---|---|
kickIn(tag, target, intensity, duration, ease) |
Diminui e volta |
kickOut(tag, target, intensity, duration, ease) |
Aumenta e volta |
kickPulse(tag, target, intensity, duration, ease) |
Pulso de escala |
kickEarthquake(tag, target, intensity, duration, ease) |
Tremor curto |
kickSusPulse(tag, target, intensity, duration, ease) |
Pulso nas sustain notes |
Quando o modName é beat, o kickShot também mexe internamente no beatKick, então o impacto aparece mesmo quando a onda normal do beat estaria em uma parte fraca.
Essa seção é só a lista dos modifiers. Os helpers e atalhos ficam nas categorias de cima.
| Categoria | Modifier | O que faz | Visual |
|---|---|---|---|
| Ondas | drunk |
Move notes no X em onda. Ajustes: drunkSpeed, drunkPeriod, drunkOffset. |
![]() |
| Ondas | tipsy |
Move notes no Y em onda. Ajustes: tipsySpeed, tipsyPeriod, tipsyOffset. |
![]() |
| Ondas | tipZ |
Move notes no Z em onda. Ajustes: tipZSpeed, tipZPeriod, tipZOffset. |
![]() |
| Ondas | float |
Efeito flutuante nos receptors. | ![]() |
| Ondas | zigzag |
Faz as notes andarem em zigzag no caminho. | ![]() |
| Layout | flip |
Espelha as colunas do lado escolhido. | ![]() |
| Layout | invert |
Troca colunas vizinhas: esquerda com baixo, cima com direita. | ![]() |
| Layout |
opposite / reverse
|
Inverte o sentido do scroll do alvo. | ![]() |
| Layout | cross |
Inverte apenas as colunas do meio. | ![]() |
| Layout | alternate |
Inverte colunas alternadas. | ![]() |
| Layout |
middle / centered
|
Centraliza o playfield no X. | ![]() |
| Layout | middle2 |
Move colunas para posições mais extremas. | ![]() |
| Layout | oppSwap |
Troca visualmente o lado do opponent e do player. | ![]() |
| Layout | receptorScroll |
Faz receptors rolarem e deixa notes mais estáticas. | ![]() |
| Posição |
transformX, transformY, transformZ
|
Move o playfield inteiro. | ![]() |
| Posição |
strumX, strumY, strumZ
|
Move receptors/lanes específicas. | ![]() |
| Posição | bumpX |
Offset extra no X da lane. | ![]() |
| Posição | z |
Profundidade extra para notes/receptors. | ![]() |
| Rotação |
rX, rY, rZ
|
Rotaciona usando a strumline como referência. | ![]() |
| Rotação |
centerRX, centerRY, centerRZ
|
Rotaciona usando o centro da tela. Aliases: centerRotateX/Y/Z, crx/cry/crz. |
![]() |
| Rotação |
rotate0X até rotate7Z
|
Rotação por lane. Também funciona com centerRotate0X etc. |
![]() |
| Rotação | confusion |
Gira notes e receptors juntos por graus. | ![]() |
| Rotação | noteAngle |
Gira só as notes. | ![]() |
| Rotação | receptorAngle |
Gira só os receptors. | ![]() |
| Rotação | spin |
Gira notes que não são sustain. | ![]() |
| Ritmo | beat |
Move o campo no ritmo da música. | ![]() |
| Ritmo | beatZ |
Versão em profundidade do beat. | ![]() |
| Ritmo |
earthquake, earthquakeX, earthquakeY
|
Treme o playfield. | ![]() |
| Ritmo | susPulse |
Pulso visual nas sustain notes. | ![]() |
| Ritmo | temna |
Efeito especial de movimento/rotação. | ![]() |
| Velocidade | boost |
Desacelera antes e acelera perto do receptor. | ![]() |
| Velocidade | wave |
Alterna velocidade no caminho, criando efeito de onda. | ![]() |
| Velocidade | brake |
Acelera no começo e freia perto do receptor. | ![]() |
| Visibilidade | stealth |
Primeiro clareia a note, depois começa a apagar. | ![]() |
| Visibilidade |
hidden / hiddenOffset
|
Esconde notes conforme elas chegam perto do receptor. | ![]() |
| Visibilidade |
sudden / suddenOffset
|
Faz notes aparecerem perto do receptor. | ![]() |
| Visibilidade | blink |
Pisca a visibilidade das notes. | ![]() |
| Visibilidade | dark |
Apaga os receptors. | ![]() |
| Escala | globalScale |
Aumenta ou diminui tudo. 0 é normal. |
![]() |
| Escala |
mini, miniX, miniY
|
Diminui notes e receptors. | ![]() |
| Escala |
squish, stretch
|
Espreme ou estica. | ![]() |
| Escala |
receptorScaleX, receptorScaleY
|
Escala só os receptors. | ![]() |
| Escala |
noteScaleX, noteScaleY
|
Escala só as notes. | ![]() |
Exemplo:
function modChartCreatePost()
setModchart('drunkSpeed', 1.2, Strum_Gen)
setModchart('drunkPeriod', 0.9, Strum_Gen)
setModchart('hiddenOffset', 0.35, BF_Strum)
queueEase(64, 96, 'drunk', 1, 'expoOut', BF_Strum)
queueEase(96, 128, 'tipsy', 0.8, 'expoOut', DAD_Strum)
queueEase(128, 160, 'receptorScroll', 1, 'quadInOut', BF_Strum)
queueEase(160, 192, 'centerRotateZ', 0.12, 'expoOut', Strum_Gen)
endCustom modifiers são criados com defineModifier. A ideia é: você cria um nome, recebe um ctx, muda os campos dele, e depois controla esse modifier com setModchart, easeModchart, queueEase e tudo mais.
defineModifier('nomeDoModifier', {
note = function(ctx, value)
-- roda apenas em notes
end,
sustain = function(ctx, value)
-- roda em sustain notes
end,
strum = function(ctx, value)
-- roda em receptors
end
})Callbacks possíveis:
| Callback | Quando roda |
|---|---|
apply(ctx, value) |
Notes e receptors |
note(ctx, value) |
Só notes |
sustain(ctx, value) |
Só sustain notes |
strum(ctx, value) |
Só receptors |
receptor(ctx, value) |
Alias de strum
|
Campos úteis do ctx:
| Campo | O que é |
|---|---|
ctx.x, ctx.y, ctx.z
|
Posição final |
ctx.angle |
Ângulo final |
ctx.scaleX, ctx.scaleY
|
Escala final |
ctx.alpha |
Transparência |
ctx.brightness |
Brilho usado por efeitos tipo stealth
|
ctx.distance |
Distância da note até o receptor |
ctx.beat, ctx.songPos
|
Tempo da música |
ctx.strumID, ctx.col
|
Lane absoluta e coluna local |
ctx.isPlayer |
true se for player |
ctx.strumE |
true se for receptor |
ctx.isSustainNote |
true se for sustain |
Exemplo simples:
function modChartCreatePost()
defineModifier('myWave', {
note = function(ctx, value)
ctx.x = ctx.x + math.sin(ctx.distance * 0.01 + ctx.beat) * 80 * value
end,
strum = function(ctx, value)
ctx.angle = ctx.angle + math.sin(ctx.beat) * 15 * value
end
})
easeModchart('myWaveIn', 'myWave', 1, 2, 'expoOut', BF_Strum)
endExemplo com sustain separado:
function modChartCreatePost()
defineModifier('softLongs', {
sustain = function(ctx, value)
ctx.alpha = ctx.alpha * (1 - value * 0.35)
ctx.brightness = math.max(ctx.brightness, value * 0.3)
end
})
setModchart('softLongs', 1, Strum_Gen)
endaddCustomModifier também existe como alias de defineModifier.
Custom modifiers são Lua por enquanto. Em HScript/Haxe você pode pilotar modifiers já criados com game.setModchart, game.easeModchart, game.queueSetModchart e game.queueEaseModchart.
- Creating new characters
- Creating new songs
- Creating new stages
- Creating new levels
- Creating new stickers
- Creating new freeplay tabs
- Creating new mixes
- Creating new states
- on Songs
- on Characters
- on Notetypes
- on Events
- on Dialogues (DialoguePlus)
- on GameOver
- on Dialogue Portraits
- on Icons
- on StoryMenu
- on Pause Screen
- Globally

