0% ont trouvé ce document utile (0 vote)
56 vues10 pages

SimpleModchartTemplate Lua

Ce document est un template de modchart en Lua pour le jeu FNF, permettant une personnalisation avancée des notes avec des effets comme le 'drunk', 'tipsy' et 'reverse'. Il inclut des fonctionnalités telles que le système de modificateurs, la gestion des axes Z pour des effets de profondeur, et des fonctions pour réinitialiser et animer les propriétés des notes. Le code est conçu pour être flexible et puissant, adapté aux utilisateurs qui souhaitent créer des modcharts uniques.

Transféré par

leivetd
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats TXT, PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
56 vues10 pages

SimpleModchartTemplate Lua

Ce document est un template de modchart en Lua pour le jeu FNF, permettant une personnalisation avancée des notes avec des effets comme le 'drunk', 'tipsy' et 'reverse'. Il inclut des fonctionnalités telles que le système de modificateurs, la gestion des axes Z pour des effets de profondeur, et des fonctions pour réinitialiser et animer les propriétés des notes. Le code est conçu pour être flexible et puissant, adapté aux utilisateurs qui souhaitent créer des modcharts uniques.

Transféré par

leivetd
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats TXT, PDF, TXT ou lisez en ligne sur Scribd

local modchart = true --easy toggle

--"simple" psych lua modchart template by TheZoroForce240


--insprired by other templates (and notitg/itg of course)
--simple for actual modcharts, complex for generic fnf modcharts, can be really
powerful if you know what youre doing
--made originally for final timeout new modchart (vs mart)

--features
--z axis on notes/strums (no layering sorry it causes a lot of lag, can be done in
source if you know what youre doing)
--modifier system which can affect each note indivdually, and allows for cool
looking wavy long notes
--basic modifiers built in (drunk/tipsy/reverse)
--modifiers can be stacked, tweened in/out and automatically work for both up and
downscroll

local keyCount = 4
local arrowSize = 112 --160 * 0.7, usually wanna change if using more keys
local scrollSwitch = 520 --height to move to when reverse
--local noteDist = 0.45 --used for flipping between up/downscroll smoothly

local defaultSusScaleY = -1 --store scaley for all sustains


local defaultSusEndScaleY = -1

function runModifiers(data, curPos)


--this is where mod math gets applied to strums/notes
local xOffset = 0
local yOffset = 0
local zOffset = 0
local angle = 0

xOffset = xOffset + getProperty('strumOffset'..data..'.x') --add strum


offsets
yOffset = yOffset + getProperty('strumOffset'..data..'.y')
zOffset = zOffset + getProperty('strumOffset'..data..'.angle') --using angle
because

--drunk
if getProperty('drunk.x') ~= 0 then
xOffset = xOffset + getProperty('drunk.x') *
(math.cos( ((getSongPosition()*0.001) + ((data%keyCount)*0.2) +
(curPos*0.45)*(10/screenHeight)) * (getProperty('drunkSpeed.x')*0.2)) *
arrowSize*0.5);
end
if getProperty('drunk.y') ~= 0 then
yOffset = yOffset + getProperty('drunk.y') *
(math.cos( ((getSongPosition()*0.001) + ((data%keyCount)*0.2) +
(curPos*0.45)*(10/screenHeight)) * (getProperty('drunkSpeed.y')*0.2)) *
arrowSize*0.5);
end
if getProperty('drunk.angle') ~= 0 then
zOffset = zOffset + getProperty('drunk.angle') *
(math.cos( ((getSongPosition()*0.001) + ((data%keyCount)*0.2) +
(curPos*0.45)*(10/screenHeight)) * (getProperty('drunkSpeed.angle')*0.2)) *
arrowSize*0.5);
end

--tipsy
if getProperty('tipsy.x') ~= 0 then
xOffset = xOffset + getProperty('tipsy.x') *
( math.cos( getSongPosition()*0.001 *(1.2) + (data%keyCount)*(2.0) +
getProperty('tipsySpeed.x')*(0.2) ) * arrowSize*0.4 );
end
if getProperty('tipsy.y') ~= 0 then
yOffset = yOffset + getProperty('tipsy.y') *
( math.cos( getSongPosition()*0.001 *(1.2) + (data%keyCount)*(2.0) +
getProperty('tipsySpeed.y')*(0.2) ) * arrowSize*0.4 );
end
if getProperty('tipsy.angle') ~= 0 then
zOffset = zOffset + getProperty('tipsy.angle') *
( math.cos( getSongPosition()*0.001 *(1.2) + (data%keyCount)*(2.0) +
getProperty('tipsySpeed.angle')*(0.2) ) * arrowSize*0.4 );
end

--reverse (scroll flip)


if getProperty('reverse.y') ~= 0 or getProperty('reverse'..data..'.y') ~= 0
then
yOffset = yOffset + scrollSwitch * (getProperty('reverse.y') +
getProperty('reverse'..data..'.y'))
end

--confusion (note angle)


if getProperty('confusion.angle') ~= 0 or
getProperty('confusion'..data..'.angle') ~= 0 then
angle = angle + getProperty('confusion.angle') +
getProperty('confusion'..data..'.angle')
end

--add any custom modifiers here lol

return {xOffset, yOffset, zOffset/1000, angle}


--do divide 1000 on z so it matches more closely to the other axis
end

function onCreatePost()
for i = 0,(keyCount*2)-1 do
makeLuaSprite('strumOffset'..i, '', 0, 0)
makeLuaSprite('scaleMulti'..i, '', 1, 1)
makeLuaSprite('reverse'..i, '', 0, 0)
makeLuaSprite('confusion'..i, '', 0, 0)
end

--basic modifiers
--using lua sprites so you can tween lol
--changing angle changes the z, not actual angle, to change note angle use
confusion
makeLuaSprite('tipsy', '', 0, 0)
makeLuaSprite('drunk', '', 0, 0)
makeLuaSprite('tipsySpeed', '', 1, 1)
makeLuaSprite('drunkSpeed', '', 1, 1)

makeLuaSprite('scale', '', 0.7, 0.7) --0.7 = default scale


makeLuaSprite('confusion', '', 0, 0) --angle
makeLuaSprite('reverse', '', 0, 0) --only y does stuff

--setProperty('drunk.x', 0.5)
--setProperty('tipsySpeed.angle', 1)

if downscroll then
scrollSwitch = -520
end

modchart = true
if not modchart then
return
end
end

function onEvent(tag, val1, val2)

if tag == 'resetX' then


resetX(tonumber(val1), val2)
elseif tag == 'resetY' then
resetY(tonumber(val1), val2)
elseif tag == 'resetZ' then
resetZ(tonumber(val1), val2)
elseif tag == 'invert' then
invert(tonumber(val1), val2)
elseif tag == 'flip' then
flip(tonumber(val1), val2)
elseif tag == 'easeModifierX' then
local nameAndVal = val1:split(",")
local timeAndEase = val2:split(",")
doTweenX(nameAndVal[1]..'.x', nameAndVal[1], tonumber(nameAndVal[2]),
tonumber(timeAndEase[1]), timeAndEase[2])
elseif tag == 'easeModifierY' then
local nameAndVal = val1:split(",")
local timeAndEase = val2:split(",")
doTweenY(nameAndVal[1]..'.y', nameAndVal[1], tonumber(nameAndVal[2]),
tonumber(timeAndEase[1]), timeAndEase[2])
elseif tag == 'easeModifierZ' then
local nameAndVal = val1:split(",")
local timeAndEase = val2:split(",")
doTweenAngle(nameAndVal[1]..'.z', nameAndVal[1],
tonumber(nameAndVal[2]), tonumber(timeAndEase[1]), timeAndEase[2])
elseif tag == 'setModifierX' then
setProperty(val1..'.x', tonumber(val2))
elseif tag == 'setModifierY' then
setProperty(val1..'.y', tonumber(val2))
elseif tag == 'setModifierZ' then
setProperty(val1..'.angle', tonumber(val2))
end
end

function string:split( inSplitPattern, outResults ) -- from here, code isnt mine,


https://stackoverflow.com/questions/19262761/lua-need-to-split-at-comma
if not outResults then
outResults = { }
end
local theStart = 1
local theSplitStart, theSplitEnd = string.find( self, inSplitPattern,
theStart )
while theSplitStart do
table.insert( outResults, string.sub( self, theStart, theSplitStart-1 ) )
theStart = theSplitEnd + 1
theSplitStart, theSplitEnd = string.find( self, inSplitPattern, theStart )
end
table.insert( outResults, string.sub( self, theStart ) )
return outResults
end

function resetX(time, ease)


for i = 0,(keyCount*2)-1 do
doTweenX(i..'x', 'strumOffset'..i, 0, time, ease)
end
end
function resetY(time, ease)
for i = 0,(keyCount*2)-1 do
doTweenY(i..'y', 'strumOffset'..i, 0, time, ease)
end
end
function resetZ(time, ease)
for i = 0,(keyCount*2)-1 do
doTweenAngle(i..'z', 'strumOffset'..i, 0, time, ease)
end
end

function invert(time, ease)


for i = 0,(keyCount*2)-1 do
local invert = -1
if i % 2 == 0 then
invert = 1
end
doTweenX(i..'x', 'strumOffset'..i, arrowSize*invert, time, ease)
end
end
function flip(time, ease)
--for i = 0,7 do
-- doTweenX(i..'x', 'strumOffset'..i, arrowSize * 2 * (1.5 - (i%4)), time,
ease)
--end
doTweenX('0x', 'strumOffset0', arrowSize * 3, time, ease)
doTweenX('1x', 'strumOffset1', arrowSize * 1, time, ease)
doTweenX('2x', 'strumOffset2', arrowSize * -1, time, ease)
doTweenX('3x', 'strumOffset3', arrowSize * -3, time, ease)

doTweenX('4x', 'strumOffset4', arrowSize * 3, time, ease)


doTweenX('5x', 'strumOffset5', arrowSize * 1, time, ease)
doTweenX('6x', 'strumOffset6', arrowSize * -1, time, ease)
doTweenX('7x', 'strumOffset7', arrowSize * -3, time, ease)

end

local copyZ = true

function onUpdatePost(elapsed)

if not modchart then


return
end

local xPos = {}
local yPos = {}
local zPos = {}

--runHaxeCode('game.variables["strumZOrder"] = [];')

for i = 0,(keyCount*2)-1 do
table.insert(xPos, 0) --fill up for how many keys there are
table.insert(yPos, 0)
table.insert(zPos, 0)

--xPos[i+1] = xPos[i+1] + xOffset[i+1]


--yPos[i+1] = yPos[i+1] + yOffset[i+1]
--zPos[i+1] = zPos[i+1] + zOffset[i+1]

--runHaxeCode('game.variables["strumZOrder"].push('..zPos[i+1]..');')

end

local zNear = 0
local zFar = 100
local zRange = zNear - zFar
local tanHalfFOV = math.tan(math.pi/4)

if copyZ then

for i = 0,(keyCount*2)-1 do

--Z Axis on strums

local notePosZ = -1

local strum = 'PlayerStrum'


if i < keyCount then
strum = 'OpponentStrum'
end
local notePosX = _G['default'..strum..'X'..i%keyCount] -
(screenWidth/2)
local notePosY = _G['default'..strum..'Y'..i%keyCount] -
(screenHeight/2)
local noteAngle = 0

local offsets = runModifiers(i, 0) --calc offsets


notePosX = notePosX + offsets[1]
notePosY = notePosY + offsets[2]
notePosZ = notePosZ + offsets[3]
noteAngle = noteAngle + offsets[4]

local zPerspectiveOffset = (notePosZ+(2 * zFar * zNear /


zRange));
local xPerspective = notePosX*(1/tanHalfFOV);
local yPerspective = notePosY/(1/tanHalfFOV);
xPerspective = xPerspective/-zPerspectiveOffset;
yPerspective = yPerspective/-zPerspectiveOffset;
setPropertyFromGroup('strumLineNotes', i, 'x', xPerspective +
(screenWidth/2))
setPropertyFromGroup('strumLineNotes', i, 'y', yPerspective +
(screenHeight/2))
setPropertyFromGroup('strumLineNotes', i, 'angle', noteAngle)

setPropertyFromGroup('strumLineNotes', i, 'scale.x',
getProperty('scale.x') * (1/-zPerspectiveOffset) *
getProperty('scaleMulti'..i..'.x'))
setPropertyFromGroup('strumLineNotes', i, 'scale.y',
getProperty('scale.y') * (1/-zPerspectiveOffset) *
getProperty('scaleMulti'..i..'.y'))

--explaination on perspective math

--to start you need some basic variables for far, near, and range
and the fov (tan halfed), ive skipped the actual fov since its not really needed
(its just math.pi/2, which is 90 deg)
--[[
local zNear = 0
local zFar = 100
local zRange = zNear - zFar
local tanHalfFOV = math.tan(math.pi/4)
]]
--next, you need the position of your object without any
perspective applied, so for this, it gets the default strum pos
--you then take away that position by the screen width/height / 2
(it will be added back later so down worry about it),
--this is so fake "3D" camera focuses on the center of the screen

--you then do the main calculation for the perspective which is


--[[
(z+(2 * zFar * zNear / zRange));
]]--
--there are more but its not nessessary for this

--now you can transform the x and y using the halftanfov and the
calculated perspective,
--[[
local xPerspective = notePosX*(1/tanHalfFOV);
local yPerspective = notePosY/(1/tanHalfFOV);
xPerspective = xPerspective/-zPerspectiveOffset;
yPerspective = yPerspective/-zPerspectiveOffset;
]]--
--now you just add the screen width/height / 2 back onto the x/y
and thats it for the main positioning

--the only thing left is to do the scaling


--which is just multiplying the scale by (1/-zPerspectiveOffset)
--you need to remember the default scale though, for notes its
just 0.7
--[[
getProperty('scale.x') * (1/-zPerspectiveOffset)
getProperty('scale.y') * (1/-zPerspectiveOffset)
]]
--for most sprites, you also should updatehitbox right after
scaling, its not done for strums since its done automatically and doing it here
breaks the offsets

--good place to go if you wanna learn more about this kinda stuff
https://ogldev.org/www/tutorial12/tutorial12.html

--updateHitboxFromGroup('strumLineNotes', i)

end

local noteCount = getProperty('notes.length');


local fakeCrochet = (60 / bpm) * 1000

--runHaxeCode('game.variables["noteZOrder"] = [];')

for i = 0, noteCount-1 do
--setProperty('notes', i, 'copyX', false)
--setProperty('notes', i, 'copyY', false)
local mustPress = getPropertyFromGroup('notes', i, 'mustPress');
local isSustainNote = getPropertyFromGroup('notes', i,
'isSustainNote');
local noteData = getPropertyFromGroup('notes', i, 'noteData');
local isSusEnd =
string.find(string.lower(getPropertyFromGroup('notes', i,
'animation.curAnim.name')), 'end') or
string.find(string.lower(getPropertyFromGroup('notes', i,
'animation.curAnim.name')), 'tail') --check tail in case playing on engine with
extra keys

--Z Axis on notes

local notePosZ = -1

local strumPos = noteData+keyCount


local noteAngle = 0

local strum = 'PlayerStrum'


if not mustPress then
strum = 'OpponentStrum'
strumPos = noteData
end
local notePosX = _G['default'..strum..'X'..noteData%keyCount] -
(screenWidth/2)

--local notePosY = getPropertyFromGroup('notes', i, 'y') -


(screenHeight/2)
local notePosY = _G['default'..strum..'Y'..noteData%keyCount] -
(screenHeight/2)

local curPos = (getSongPosition()-getPropertyFromGroup('notes',


i, 'strumTime')) * getProperty('songSpeed')

local noteDist = 0.45


if getProperty('reverse.y') ~= 0 then
noteDist = noteDist * (1-(getProperty('reverse.y')*2))

end
if getProperty('reverse'..strumPos..'.y') ~= 0 then
noteDist = noteDist * (1-
(getProperty('reverse'..strumPos..'.y')*2))
end

local fixedDist = noteDist


if downscroll then
fixedDist = noteDist * -1 --flip for downscroll
end

notePosY = notePosY - (curPos*fixedDist) --calc note y pos


--setProperty('notes', i, 'flipY', fixedDist < 0)

if isSustainNote and fixedDist < 0 then --fix downscroll


sustains, just psych engine code put into lua
setPropertyFromGroup('notes', i, 'flipY', true)

if isSusEnd then
notePosY = notePosY + 10.5 * (fakeCrochet / 400) *
1.5 * scrollSpeed + (46 * (scrollSpeed - 1));
notePosY = notePosY - (46 * (1 - (fakeCrochet / 600))
* scrollSpeed);
notePosY = notePosY - 19;
end
notePosY = notePosY + ((arrowSize) / 2) - (60.5 *
(scrollSpeed - 1));
notePosY = notePosY + 27.5 * ((bpm / 100) - 1) *
(scrollSpeed - 1);

else
setPropertyFromGroup('notes', i, 'flipY', false)
end

local offsets = runModifiers(strumPos, curPos) --calc offsets


notePosX = notePosX + offsets[1]
notePosY = notePosY + offsets[2]
notePosZ = notePosZ + offsets[3]
noteAngle = noteAngle + offsets[4]

local zPerspectiveOffset = (notePosZ+(2 * zFar * zNear /


zRange));

notePosX = notePosX + getPropertyFromGroup('notes', i, 'offsetX')


/ (1/-zPerspectiveOffset)

local xPerspective = notePosX*(1/tanHalfFOV);


local yPerspective = notePosY/(1/tanHalfFOV);
xPerspective = xPerspective/-zPerspectiveOffset;
yPerspective = yPerspective/-zPerspectiveOffset;

setPropertyFromGroup('notes', i, 'x', xPerspective +


(screenWidth/2))
setPropertyFromGroup('notes', i, 'y', yPerspective +
(screenHeight/2))
setPropertyFromGroup('notes', i, 'scale.x',
getProperty('scale.x') * (1/-zPerspectiveOffset) *
getProperty('scaleMulti'..strumPos..'.x'))
if not isSustainNote then
setPropertyFromGroup('notes', i, 'scale.y',
getProperty('scale.y') * (1/-zPerspectiveOffset) *
getProperty('scaleMulti'..strumPos..'.y'))
setPropertyFromGroup('notes', i, 'angle', noteAngle)
else
if defaultSusEndScaleY == -1 and isSusEnd then
defaultSusEndScaleY = getPropertyFromGroup('notes',
i, 'scale.y') --get sustain scaley

elseif defaultSusScaleY == -1 and not isSusEnd then


defaultSusScaleY = getPropertyFromGroup('notes', i,
'scale.y') --get sustain scaley
end

if isSusEnd then --do sustains scale shit


setPropertyFromGroup('notes', i, 'scale.y',
defaultSusEndScaleY * (1/-zPerspectiveOffset) *
(scrollSpeed/getProperty('songSpeed')))
else
setPropertyFromGroup('notes', i, 'scale.y',
defaultSusScaleY * (1/-zPerspectiveOffset) *
(scrollSpeed/getProperty('songSpeed')))
end

--work out angle by calculating where the next sustain note


would be
local nextPos = curPos + (stepCrochet +
(stepCrochet/getProperty('songSpeed')))
local nextNoteY = _G['default'..strum..'Y'..noteData
%keyCount] - (screenHeight/2)
--nextNoteY = nextNoteY + ((stepCrochet +
(stepCrochet/scrollSpeed))*fixedDist*scrollSpeed)
nextNoteY = nextNoteY - (nextPos*fixedDist)
local thisNoteY = notePosY
local nextNoteX = _G['default'..strum..'X'..noteData
%keyCount] - (screenWidth/2)
nextNoteX = nextNoteX + getPropertyFromGroup('notes', i,
'offsetX') / (1/-zPerspectiveOffset)
local thisNoteX = notePosX

local susOffsets = runModifiers(noteData, nextPos) --do


mods for fake sustain
nextNoteX = nextNoteX + susOffsets[1]
nextNoteY = nextNoteY + susOffsets[2]
--notePosZ = notePosZ + offsets[3] --no doing z because
cant angle towards z
local ang = 0
if fixedDist < 0 then
ang = math.deg(math.atan2( (nextNoteY-thisNoteY),
(nextNoteX-thisNoteX) ) - math.pi/2) --calc angle by comparing difference in
position
--debugPrint(ang)
else
ang = math.deg(math.atan2( (nextNoteY-thisNoteY),
(nextNoteX-thisNoteX) ) + math.pi/2)
end

setPropertyFromGroup('notes', i, 'angle', ang)

end

updateHitboxFromGroup('notes', i) --update hitbox on notes lol

--runHaxeCode('game.variables["noteZOrder"].push('..za..');')

end

--pain
--dont even know if this works
--runHaxeCode('game.notes.sort(function(a,b) { if
(game.variables["noteZOrder"][game.notes.members.indexOf(a)] <
game.variables["noteZOrder"][game.notes.members.indexOf(b)]) \nreturn -1; else if \
n(game.variables["noteZOrder"][game.notes.members.indexOf(a)] >
game.variables["noteZOrder"][game.notes.members.indexOf(b)]) return 1; else \n
return 0;});')

end

end

Vous aimerez peut-être aussi