diff --git a/.gitignore b/.gitignore index 7ac23a28..77035890 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ latest/* pages/ docs/doc.xml mods/* +addons/* !mods/readme.txt addons/* !addons/readme.txt diff --git a/assets/data/characters/bf-holding-gf-dead.xml b/assets/data/characters/bf-holding-gf-dead.xml index 52fa52eb..3a2981ac 100644 --- a/assets/data/characters/bf-holding-gf-dead.xml +++ b/assets/data/characters/bf-holding-gf-dead.xml @@ -1,5 +1,5 @@ - + diff --git a/assets/data/characters/bf-pixel-dead.xml b/assets/data/characters/bf-pixel-dead.xml index 52aedd58..8b205eb9 100644 --- a/assets/data/characters/bf-pixel-dead.xml +++ b/assets/data/characters/bf-pixel-dead.xml @@ -1,5 +1,5 @@ - + diff --git a/assets/data/characters/gf-car.xml b/assets/data/characters/gf-car.xml index 02fe7e1b..359a8eea 100644 --- a/assets/data/characters/gf-car.xml +++ b/assets/data/characters/gf-car.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/assets/data/characters/gf-christmas.xml b/assets/data/characters/gf-christmas.xml index d9a05715..f27cdcbd 100644 --- a/assets/data/characters/gf-christmas.xml +++ b/assets/data/characters/gf-christmas.xml @@ -1,5 +1,5 @@ - + diff --git a/assets/data/characters/gf-tankmen.xml b/assets/data/characters/gf-tankmen.xml index a332b124..c498cb57 100644 --- a/assets/data/characters/gf-tankmen.xml +++ b/assets/data/characters/gf-tankmen.xml @@ -1,5 +1,5 @@ - + diff --git a/assets/data/characters/gf.xml b/assets/data/characters/gf.xml index 54a8ad59..5981b647 100644 --- a/assets/data/characters/gf.xml +++ b/assets/data/characters/gf.xml @@ -1,5 +1,5 @@ - + diff --git a/assets/data/characters/senpai-angry.xml b/assets/data/characters/senpai-angry.xml index b9450a24..a0fab601 100644 --- a/assets/data/characters/senpai-angry.xml +++ b/assets/data/characters/senpai-angry.xml @@ -1,5 +1,5 @@ - + diff --git a/assets/data/characters/tankman.xml b/assets/data/characters/tankman.xml index c06aa6c4..d02f5a41 100644 --- a/assets/data/characters/tankman.xml +++ b/assets/data/characters/tankman.xml @@ -1,5 +1,5 @@ - + diff --git a/assets/data/discord.hx b/assets/data/discord.hx index 9d5f0f6f..8c9e3a48 100644 --- a/assets/data/discord.hx +++ b/assets/data/discord.hx @@ -8,7 +8,7 @@ function onDiscordPresenceUpdate(e) { var data = e.presence; if(data.button1Label == null) - data.button1Label = "Codename Engine Discord"; + data.button1Label = "meow"; // "Codename Engine Discord" if(data.button1Url == null) data.button1Url = "https://discord.gg/2NTCdsQvx4"; } @@ -33,8 +33,8 @@ function onEditorTreeLoaded(name:String) { DiscordUtil.changePresenceSince("Choosing a Character", null); case "Chart Editor": DiscordUtil.changePresenceSince("Choosing a Chart", null); - case "Stage Editor": - DiscordUtil.changePresenceSince("Choosing a Stage", null); + //case "Stage Editor": // secret for now + // DiscordUtil.changePresenceSince("Choosing a Stage", null); } } @@ -44,7 +44,7 @@ function onEditorLoaded(name:String, editingThing:String) { DiscordUtil.changePresenceSince("Editing a Character", editingThing); case "Chart Editor": DiscordUtil.changePresenceSince("Editing a Chart", editingThing); - case "Stage Editor": - DiscordUtil.changePresenceSince("Editing a Stage", editingThing); + //case "Stage Editor": + // DiscordUtil.changePresenceSince("Editing a Stage", editingThing); } } \ No newline at end of file diff --git a/assets/data/editors/layouts/stage/characterEditScreen.xml b/assets/data/editors/layouts/stage/characterEditScreen.xml new file mode 100644 index 00000000..2658f4ac --- /dev/null +++ b/assets/data/editors/layouts/stage/characterEditScreen.xml @@ -0,0 +1,126 @@ + + + + + + + function col(c:Int) { + return 20 + (150 + XOFFSET) * c; + } + previewSprite = null; + if(char.animateAtlas == null) { + previewSprite = new FunkinSprite(0, 0); + previewSprite.frame = char.frame; + + self.add(previewSprite); + } + + + + + <section> + <set name="curY" value="title.y + TO" /> + + <stepper name="xStepper" label="X" value="char.x" x="col(0)" y="curY" step="1" width="100" /> + <stepper name="yStepper" label="Y" value="char.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" step="1" width="100" /> + + <stepper name="scrollXStepper" label="Scroll X" value="char.scrollFactor.x" x="col(1)" y="curY" precision="2" step="0.01" width="100" /> + <stepper name="scrollYStepper" label="Scroll Y" value="char.scrollFactor.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" /> + + <stepper name="scaleXStepper" label="Scale X" value="char.scale.x / charScale" x="col(2)" y="curY" precision="2" step="0.01" width="100" /> + <stepper name="scaleYStepper" label="Scale Y" value="char.scale.y / charScale" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" /> + </section> + + <section> + <set name="curY" value="xStepper.y + xStepper.bHeight + YOFFSET" /> + + <stepper name="camxStepper" label="Cam X" value="getEx('camX')" x="col(0)" y="curY" step="1" width="100" /> + <stepper name="camyStepper" label="Cam Y" value="getEx('camY')" x="last.x + last.bWidth + XOFFSET - 20" y="curY" step="1" width="100" /> + + <label name="spacingLabel" text="Spacing (X, Y)" x="col(1)" y="curY" size="15" /> + <stepper name="spacingXStepper" value="getEx('spacingX')" x="col(1)" y="curY" precision="2" step="0.01" width="100" /> + <stepper name="spacingYStepper" value="getEx('spacingY')" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" /> + + <stepper name="skewXStepper" label="Skew X" value="char.skew.x" x="col(2)" y="curY" precision="2" step="0.01" width="100" /> + <stepper name="skewYStepper" label="Skew Y" value="char.skew.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" /> + </section> + + <section> + <set name="curY" value="camxStepper.y + camxStepper.bHeight + YOFFSET" /> + + <stepper name="alphaStepper" label="Alpha" value="char.alpha / 0.75" x="col(0)" y="curY" precision="2" step="0.01" width="100" /> + <stepper name="angleStepper" label="Angle" value="char.angle" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" /> + + <stepper name="zoomFactorStepper" label="Zoom Factor" value="char.zoomFactor" x="col(1)" y="curY" precision="2" step="0.01" width="100" /> + + <section if="!StringTools.startsWith(char.name, 'NO_DELETE_')"> + <textbox name="charName" label="Position Name" value="char.name" x="col(2)" y="curY" width="100"/> + </section> + </section> + + <section> + <set name="curY" value="alphaStepper.y + alphaStepper.bHeight + YOFFSET - 20" /> + + <checkbox name="flipXCheckbox" text="Flip X" value="char.isPlayer" x="col(0)" y="curY" /> + + <radio for="memoryCheck" name="highMemoryRadio" text="High Memory Only" value="getEx('highMemory')" x="last.field.x + last.field.width + XOFFSET * 2" y="curY" /> + <radio for="memoryCheck" name="lowMemoryRadio" text="Low Memory Only" value="getEx('lowMemory')" x="last.field.x + last.field.width + XOFFSET * 2" y="curY" /> + </section> + + <section if="previewSprite != null"> + <title name="previewTitle" x="20" y="self.winHeight - 220" text="Preview Transform:" size="15" /> + </section> + <section if="previewSprite == null"> + <exec> + self.windowSpr.bHeight -= 170; + </exec> + </section> + + <exec> + function onUpdate() { + if(previewSprite == null) return; + previewSprite.skew.x = skewXStepper.value; + previewSprite.skew.y = skewYStepper.value; + previewSprite.angle = angleStepper.value; + previewSprite.antialiasing = char.antialiasing; + var ratio = char.frameWidth / char.frameHeight; + previewSprite.setGraphicSize(145 * scaleXStepper.value * ratio, 145 * scaleYStepper.value); + previewSprite.updateHitbox(); + if(flipXCheckbox.checked != char.playerOffsets) + previewSprite.scale.x *= -1; + + previewSprite.x = 30; + previewSprite.y = self.winHeight - previewSprite.height - 30; + } + + function onSave() { + setEx("highMemory", highMemoryRadio.checked); + setEx("lowMemory", lowMemoryRadio.checked); + + if (!StringTools.startsWith(char.name, 'NO_DELETE_')) + char.name = charName.field.text; + + char.x = xStepper.value; + char.y = yStepper.value; + char.scrollFactor.set(scrollXStepper.value, scrollYStepper.value); + char.scale.set(scaleXStepper.value * charScale, scaleYStepper.value * charScale); + char.cameraOffset.x -= getEx("camX"); + char.cameraOffset.x += camxStepper.value; + setEx("camX", camxStepper.value); + char.cameraOffset.y -= getEx("camY"); + char.cameraOffset.y += camyStepper.value; + setEx("camY", camyStepper.value); + char.skew.x = skewXStepper.value; + char.skew.y = skewYStepper.value; + char.alpha = alphaStepper.value * 0.75; + setEx("spacingX", spacingXStepper.value); + setEx("spacingY", spacingYStepper.value); + if (flipXCheckbox.checked != char.isPlayer) { + char.isPlayer = flipXCheckbox.checked; + char.swapLeftRightAnimations(); + } + char.zoomFactor = zoomFactorStepper.value; + char.angle = angleStepper.value; + } + </exec> +</window> \ No newline at end of file diff --git a/assets/data/editors/layouts/stage/spriteEditScreen.xml b/assets/data/editors/layouts/stage/spriteEditScreen.xml new file mode 100644 index 00000000..651756a0 --- /dev/null +++ b/assets/data/editors/layouts/stage/spriteEditScreen.xml @@ -0,0 +1,185 @@ +<window width="1056" height="520" title="Editing Sprite ${button.getName()}"> + <set name="MARGIN" value="20" /> + <set name="XOFFSET" value="50" /> + <set name="YOFFSET" value="40" /> + <set name="TO" value="64" /> + + <exec> + function col(c:Int) { + return 20 + (200 + XOFFSET) * c; + } + + if(sprite.animateAtlas == null) { + previewSprite = new FunkinSprite(50, self.winHeight - 100); + previewSprite.frame = sprite.frame; + + self.add(previewSprite); + } + </exec> + + <title name="title" x="20" y="30 + 16" text="Sprite Data" /> + + <section> <!-- ROW 1 --> + <set name="curY" value="title.y + TO" /> + + <textbox name="nameTextBox" label="Name Identifier" value="button.getName()" x="col(0)" y="curY" width="200" height="32" /> + <textbox name="spriteTextBox" value="getEx('imageFile')" x="last.x + last.bWidth + XOFFSET" y="curY" width="200" height="32" /> + <stepper name="alphaStepper" label="Alpha" value="sprite.alpha" x="spriteTextBox.x + spriteTextBox.bWidth + XOFFSET" y="curY" precision="2" step="0.01" width="100" /> + <label name="imageFile" text="Image File (WIP: needs reload) In (${stage.spritesParentFolder})" x="spriteTextBox.x" y="spriteTextBox.y - 14" size="15" /> + </section> + + <section> <!-- ROW 2 --> + <set name="curY" value="nameTextBox.y + nameTextBox.bHeight + YOFFSET" /> + + <stepper name="xStepper" label="X" value="sprite.x" x="col(0)" y="curY" step="1" width="100" /> + <stepper name="yStepper" label="Y" value="sprite.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" step="1" width="100" /> + + <stepper name="scrollXStepper" label="Scroll X" value="sprite.scrollFactor.x" x="spriteTextBox.x" y="curY" precision="2" step="0.01" width="100" /> + <stepper name="scrollYStepper" label="Scroll Y" value="sprite.scrollFactor.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" /> + + <stepper name="scaleXStepper" label="Scale X" value="sprite.scale.x" x="col(2)" y="curY" precision="2" step="0.01" width="100" /> + <stepper name="scaleYStepper" label="Scale Y" value="sprite.scale.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" /> + </section> + + <section> <!-- ROW 3 --> + <set name="curY" value="xStepper.y + xStepper.bHeight + YOFFSET" /> + + <stepper name="angleStepper" label="Angle" value="sprite.angle" x="col(0)" y="curY" precision="2" step="0.01" width="100" /> + <stepper name="zoomFactorStepper" label="Zoom Factor" value="sprite.zoomFactor" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" /> + + <!-- TODO: Simplify this --> + <dropdown name="animType" label="Animation Type" value="sprite.spriteAnimType" x="col(1)" y="curY" width="150" height="32"> + <item label="NONE" value="0" /> + <item label="BEAT" value="1" /> + <item label="LOOP" value="2" /> + </dropdown> + + <stepper name="skewXStepper" label="Skew X" value="sprite.skew.x" x="col(2)" y="curY" precision="2" step="0.01" width="100" /> + <stepper name="skewYStepper" label="Skew Y" value="sprite.skew.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" /> + </section> + + <section> <!-- ROW 4 --> + <set name="curY" value="angleStepper.y + angleStepper.bHeight + YOFFSET" /> + + <checkbox name="antialiasingCheckbox" text="Antialiasing" value="sprite.antialiasing" x="col(0)" y="curY" /> + <radio for="memoryCheck" name="highMemoryRadio" text="High Memory Only" value="getEx('highMemory')" x="col(1)" y="curY" /> + <radio for="memoryCheck" name="lowMemoryRadio" text="Low Memory Only" value="getEx('lowMemory')" x="col(2)" y="curY" /> + + <!-- TODO: Animation list --> + </section> + + <section> <!-- RIGHT SIDE --> + <set name="curY" value="title.y + TO" /> + <set name="listWidth" value="250" /> + + <buttonlist name="animations" label="Animations (TODO)" x="col(3)" y="curY" width="listWidth" height="330" buttonSizeX="listWidth-10" buttonSizeY="35" buttonSpacing="5"> + <texture path="editors/ui/inputbox" /> + <camSpacing value="0" /> + + <code> + buttonlist.addButton.exists = false; + </code> + + <!-- <code> + import funkin.editors.extra.PropertyButton; + + var buttonList = buttonlist; // important for now, since it gets reset after the code is run + + var width = buttonList.buttonSize.x; + var height = buttonList.buttonSize.y; + + buttonList.addButton.callback = function() { + buttonList.add(new PropertyButton("newProperty", "valueHere", buttonList, width, height, 150, 220, 25)); + } + + //var defaultAttribs = Stage.DEFAULT_ATTRIBUTES; + //for (i in stage.stageXML.attributes()) { + // trace(i, defaultAttribs); + // if (!defaultAttribs.contains(i)) + // buttonList.add(new PropertyButton(i, stage.stageXML.get(i), buttonList, width, height, 150, 220, 25)); + //} + for(key => val in stage.extra) + buttonList.add(new PropertyButton(key, val, buttonList, width, height, 150, 220, 25)); + </code> --> + </buttonlist> + + <text text="Tip: To access custom properties, hold shift when clicking edit" x="col(2) - 60" y="self.winHeight - 50" size="15" /> + </section> + + + <!-- Preview Transform --> + + <section if="previewSprite != null"> + <title name="previewTitle" x="20" y="self.winHeight - 150" text="Preview Transform:" size="15" /> + </section> + + <exec> + function onUpdate() { + if(previewSprite == null) return; + previewSprite.color = 0xFFFFFFFF; // TODO: Color Wheel + previewSprite.skew.x = skewXStepper.value; + previewSprite.skew.y = skewYStepper.value; + previewSprite.angle = angleStepper.value; + previewSprite.alpha = alphaStepper.value; + previewSprite.antialiasing = antialiasingCheckbox.checked; + var ratio = sprite.width / sprite.height; + previewSprite.setGraphicSize(50 * scaleXStepper.value * ratio, 50 * scaleYStepper.value); + previewSprite.updateHitbox(); + } + + function onSave() { + sprite.name = nameTextBox.label.text; + setEx("imageFile", spriteTextBox.label.text); + + setEx("highMemory", highMemoryRadio.checked); + setEx("lowMemory", lowMemoryRadio.checked); + + //sprite.x = xStepper.value; + //sprite.y = yStepper.value; + //sprite.zoomFactor = zoomFactorStepper.value; + //sprite.antialiasing = antialiasingCheckbox.checked; + //sprite.color = colorWheel.color; + //sprite.spriteAnimType = animType.value; + sprite.angle = angleStepper.value; + sprite.alpha = alphaStepper.value; + + function saveXY(obj:Dynamic, name:String, x:UINumericStepper, y:UINumericStepper) { + button.xml.remove(name + ""); + button.xml.remove(name + "x"); + button.xml.remove(name + "y"); + + obj.x = x.value; + obj.y = y.value; + + if(x.value == y.value) + button.xml.set(name, x.value); + else { + button.xml.set(name + "x", x.value); + button.xml.set(name + "y", y.value); + } + } + function save(name:String, value:Dynamic) { + button.xml.remove(name); + button.xml.set(name, value); + } + + // TODO: remove old attributes to prevent possible errors + // button.xml.remove(); + + button.xml.set("name", nameTextBox.label.text); + button.xml.set("sprite", spriteTextBox.label.text); + button.xml.set("x", xStepper.value); + button.xml.set("y", yStepper.value); + button.xml.set("zoom", zoomFactorStepper.value); + button.xml.set("type", animType.key); + button.xml.set("antialiasing", antialiasingCheckbox.checked); + button.xml.set("angle", angleStepper.value); + button.xml.set("alpha", alphaStepper.value); + + saveXY(sprite.scrollFactor, "scroll", scrollXStepper, scrollYStepper); + saveXY(sprite.scale, "scale", scaleXStepper, scaleYStepper); + saveXY(sprite.skew, "skew", skewXStepper, skewYStepper); + XMLUtil.loadSpriteFromXML(sprite, button.xml, stage.spritesParentFolder); + } + </exec> +</window> \ No newline at end of file diff --git a/assets/data/editors/layouts/stage/stageInfoScreen.xml b/assets/data/editors/layouts/stage/stageInfoScreen.xml new file mode 100644 index 00000000..1a477da8 --- /dev/null +++ b/assets/data/editors/layouts/stage/stageInfoScreen.xml @@ -0,0 +1,110 @@ +<window width="470" height="620" title="Editing Stage Info '${stage.stageName}'"> + <set name="MARGIN" value="20" /> + <set name="XOFFSET" value="50" /> + <set name="YOFFSET" value="40" /> + <set name="TO" value="64" /> + <set name="storedProps" value="[]" /> + + <exec> + import haxe.ds.StringMap; + var oldInfo = { + name: stage.stageName, + folder: stage.spritesParentFolder, + startCamX: stage.startCam.x, + startCamY: stage.startCam.y, + zoom: stage.defaultZoom, + attrib: new StringMap() + }; + + function col(c:Int) { + return MARGIN + (170 + XOFFSET) * c; + } + + for(prop => val in stage.extra) { + if(!StringTools.startsWith(prop, "stageEditor.")) { + storedProps.push(prop); + oldInfo.attrib.set(prop, val); + } + } + </exec> + + <title name="title" x="MARGIN" y="30 + 16" text="Stage Info" /> + + <section> <!-- ROW 1 --> + <set name="curY" value="title.y + TO" /> + + <textbox name="nameTextBox" label="Stage Name" value="stage.stageName" x="col(0)" y="curY" width="200" height="32" /> + <textbox name="spritePathTextBox" value="stage.spritesParentFolder" x="col(1)" y="curY" width="200" height="32" /> + <label text="Sprite Path (Needs Refresh)" x="spritePathTextBox.x" y="spritePathTextBox.y - 14" size="15" /> + </section> + + <section> <!-- ROW 2 --> + <set name="curY" value="nameTextBox.y + nameTextBox.bHeight + YOFFSET" /> + + <label text="Start Cam Pos (X, Y)" x="col(0)" y="curY" /> + <stepper name="camxStepper" value="stage.startCam.x" x="col(0)" y="curY" step="1" width="120" /> + <stepper name="camyStepper" value="stage.startCam.y" x="last.x + last.bWidth + XOFFSET - 30" y="curY" step="1" width="120" /> + + <stepper name="zoomStepper" label="Zoom" value="stage.defaultZoom" x="col(1)" y="curY" precision="2" step="0.01" width="100" /> + </section> + + <section> + <set name="curY" value="camxStepper.y + camxStepper.bHeight + YOFFSET" /> + <set name="listWidth" value="self.winWidth - MARGIN * 2" /> + + <buttonlist name="attributesList" label="Custom Attributes" x="col(0)" y="curY" width="listWidth" height="290" buttonSizeX="listWidth-10" buttonSizeY="35" buttonSpacing="5"> + <texture path="editors/ui/inputbox" /> + <camSpacing value="0" /> + + <code> + import funkin.editors.extra.PropertyButton; + + var buttonList = buttonlist; // important for now, since it gets reset after the code is run + + var width = buttonList.buttonSize.x; + var height = buttonList.buttonSize.y; + + buttonList.addButton.callback = function() { + buttonList.add(new PropertyButton("newProperty", "valueHere", buttonList, width, height, 150, 220, 25)); + } + + //var defaultAttribs = Stage.DEFAULT_ATTRIBUTES; + //for (i in stage.stageXML.attributes()) { + // trace(i, defaultAttribs); + // if (!defaultAttribs.contains(i)) + // buttonList.add(new PropertyButton(i, stage.stageXML.get(i), buttonList, width, height, 150, 220, 25)); + //} + for(key => val in stage.extra) + buttonList.add(new PropertyButton(key, val, buttonList, width, height, 150, 220, 25)); + </code> + </buttonlist> + </section> + + <exec> + function onSave() { + stage.startCam.x = camxStepper.value; + stage.startCam.y = camyStepper.value; + stage.defaultZoom = zoomStepper.value; + stage.stageName = nameTextBox.label.text; + stage.spritesParentFolder = spritePathTextBox.label.text; + + for(prop in storedProps) + stage.extra.remove(prop); + + var newInfo = { + name: stage.stageName, + folder: stage.spritesParentFolder, + startCamX: stage.startCam.x, + startCamY: stage.startCam.y, + zoom: stage.defaultZoom, + attrib: new StringMap() + }; + for(prop in attributesList.buttons.members) { + stage.extra.set(prop.propertyText.label.text, prop.valueText.label.text); + newInfo.attrib.set(prop.propertyText.label.text, prop.valueText.label.text); + } + + FlxG.state.undos.addToUndo(CEditInfo(oldInfo, newInfo)); + } + </exec> +</window> \ No newline at end of file diff --git a/assets/data/editors/layouts/stage/unknownEditScreen.xml b/assets/data/editors/layouts/stage/unknownEditScreen.xml new file mode 100644 index 00000000..ec6f3d71 --- /dev/null +++ b/assets/data/editors/layouts/stage/unknownEditScreen.xml @@ -0,0 +1,66 @@ +<window width="350" height="570" title="Editing XML <${button.getName()} />"> + <set name="MARGIN" value="20" /> + <set name="XOFFSET" value="30" /> + <set name="YOFFSET" value="40" /> + <set name="TO" value="64" /> + + <exec> + function col(c:Int) { + return MARGIN + (150 + XOFFSET) * c; + } + </exec> + + <title name="title" x="MARGIN" y="30 + 16" text="Xml Data" /> + + <section> + <set name="curY" value="title.y + TO" /> + + <textbox name="nodeNameTextBox" label="Element Name" value="button.getName()" x="col(0)" y="curY" width="280" height="32" /> + </section> + <section> + <set name="curY" value="nodeNameTextBox.y + nodeNameTextBox.bHeight + YOFFSET - 30" /> + + <radio for="memoryCheck" name="highMemoryRadio" text="High Memory Only" value="button.highMemory" x="col(0)" y="curY" /> + <radio for="memoryCheck" name="lowMemoryRadio" text="Low Memory Only" value="button.lowMemory" x="col(0)" y="last.y + last.check.height + YOFFSET - 30" /> + </section> + + <section> + <set name="curY" value="lowMemoryRadio.y + lowMemoryRadio.check.height + YOFFSET" /> + <set name="listWidth" value="self.winWidth - MARGIN * 2" /> + + <buttonlist name="attributesList" label="Attributes" x="col(0)" y="curY" width="listWidth" height="260" buttonSizeX="listWidth-10" buttonSizeY="35" buttonSpacing="5"> + <texture path="editors/ui/inputbox" /> + <camSpacing value="0" /> + + <code> + import funkin.editors.extra.PropertyButton; + + var buttonList = buttonlist; // important for now, since it gets reset after the code is run + + var width = buttonList.buttonSize.x; + var height = buttonList.buttonSize.y; + + buttonList.addButton.callback = function() { + buttonList.add(new PropertyButton("newProperty", "valueHere", buttonList, width, height, 110, 145, 25)); + } + + for (i in xml.attributes()) + buttonList.add(new PropertyButton(i, xml.get(i), buttonList, width, height, 110, 145, 25)); + </code> + </buttonlist> + </section> + + <exec> + function onSave() { + button.xml.nodeName = nodeNameTextBox.label.text; + button.highMemory = highMemoryRadio.checked; + button.lowMemory = lowMemoryRadio.checked; + + for (i in xml.attributes()) + xml.remove(i); + + for (val in attributesList.buttons.members) + xml.set(val.propertyText.label.text, val.valueText.label.text); + } + </exec> +</window> \ No newline at end of file diff --git a/assets/data/editors/layouts/stage/xmlEditScreen.xml b/assets/data/editors/layouts/stage/xmlEditScreen.xml new file mode 100644 index 00000000..7a7ac956 --- /dev/null +++ b/assets/data/editors/layouts/stage/xmlEditScreen.xml @@ -0,0 +1,51 @@ +<window width="450" height="570" title="Editing XML ${type}"> + <!-- BASED ON unknownEditScreen.xml --> + <set name="MARGIN" value="20" /> + <set name="XOFFSET" value="30" /> + <set name="YOFFSET" value="40" /> + <set name="TO" value="64" /> + + <exec> + function col(c:Int) { + return MARGIN + (150 + XOFFSET) * c; + } + </exec> + + <title name="title" x="MARGIN" y="30 + 16" text="Xml Data" /> + + <section> + <set name="curY" value="title.y + TO" /> + <set name="listWidth" value="self.winWidth - MARGIN * 2" /> + + <buttonlist name="attributesList" label="Attributes" x="col(0)" y="curY" width="listWidth" height="400" buttonSizeX="listWidth-10" buttonSizeY="35" buttonSpacing="5"> + <texture path="editors/ui/inputbox" /> + <camSpacing value="0" /> + + <code> + import funkin.editors.extra.PropertyButton; + + var buttonList = buttonlist; // important for now, since it gets reset after the code is run + + var width = buttonList.buttonSize.x; + var height = buttonList.buttonSize.y; + + buttonList.addButton.callback = function() { + buttonList.add(new PropertyButton("newProperty", "valueHere", buttonList, width, height, 160, 195, 25)); + } + + for (i in xml.attributes()) + buttonList.add(new PropertyButton(i, xml.get(i), buttonList, width, height, 160, 195, 25)); + </code> + </buttonlist> + </section> + + <exec> + function onSave() { + for (i in xml.attributes()) + xml.remove(i); + + for (val in attributesList.buttons.members) + xml.set(val.propertyText.label.text, val.valueText.label.text); + } + </exec> +</window> \ No newline at end of file diff --git a/assets/data/scripts/pixel.hx b/assets/data/scripts/pixel.hx index aaa7a6c8..7031838e 100644 --- a/assets/data/scripts/pixel.hx +++ b/assets/data/scripts/pixel.hx @@ -22,15 +22,19 @@ function onNoteCreation(event) { event.cancel(); var note = event.note; + var strumID = event.strumID; if (event.note.isSustainNote) { note.loadGraphic(Paths.image('stages/school/ui/arrowEnds'), true, 7, 6); - note.animation.add("hold", [event.strumID]); - note.animation.add("holdend", [4 + event.strumID]); + var maxCol = Math.floor(note.graphic.width / 7); + note.animation.add("hold", [strumID%maxCol]); + note.animation.add("holdend", [maxCol + strumID%maxCol]); } else { note.loadGraphic(Paths.image('stages/school/ui/arrows-pixels'), true, 17, 17); - note.animation.add("scroll", [4 + event.strumID]); + var maxCol = Math.floor(note.graphic.width / 17); + note.animation.add("scroll", [maxCol + strumID%maxCol]); } - note.scale.set(daPixelZoom, daPixelZoom); + var strumScale = event.note.strumLine.strumScale; + note.scale.set(daPixelZoom*strumScale, daPixelZoom*strumScale); note.updateHitbox(); note.antialiasing = false; } @@ -44,11 +48,15 @@ function onStrumCreation(event) { var strum = event.strum; strum.loadGraphic(Paths.image('stages/school/ui/arrows-pixels'), true, 17, 17); - strum.animation.add("static", [event.strumID]); - strum.animation.add("pressed", [4 + event.strumID, 8 + event.strumID], 12, false); - strum.animation.add("confirm", [12 + event.strumID, 16 + event.strumID], 24, false); + var maxCol = Math.floor(strum.graphic.width / 17); + var strumID = event.strumID % maxCol; - strum.scale.set(daPixelZoom, daPixelZoom); + strum.animation.add("static", [strumID]); + strum.animation.add("pressed", [maxCol + strumID, (maxCol*2) + strumID], 12, false); + strum.animation.add("confirm", [(maxCol*3) + strumID, (maxCol*4) + strumID], 24, false); + + var strumScale = strumLines.members[event.player].strumScale; + strum.scale.set(daPixelZoom*strumScale, daPixelZoom*strumScale); strum.updateHitbox(); strum.antialiasing = false; } diff --git a/assets/data/stages/test.xml b/assets/data/stages/test.xml new file mode 100644 index 00000000..697711bb --- /dev/null +++ b/assets/data/stages/test.xml @@ -0,0 +1,12 @@ +<!-- praying to god we remove this file later on --> + +<stage zoom="0.9" name="stage" folder="stages/default/" startCamPosY="600" startCamPosX="1000" example="value"> + <sprite name="normBG" sprite="stagefront" x="-500" y="100" scale="0.75" updateHitbox="true" /> + <sprite name="noUpdateHitbox" sprite="stagefront" x="-500" y="300" scale="0.75" /> + <sprite name="ro-tat-ay" sprite="stagefront" x="-500" y="700" scale="0.75" updateHitbox="true" angle="37" /> + <box name="box" x="0" y="0" width="200" height="200" color="0xFF0000" /> + <solid name="box2" x="0" y="300" width="200" height="200" color="0x00FF00" /> + <unknown-sprite /> + + <unknown-sprite-1-attr name="test" /> +</stage> \ No newline at end of file diff --git a/assets/data/states/StageEditor.hx b/assets/data/states/StageEditor.hx new file mode 100644 index 00000000..af9f6cbe --- /dev/null +++ b/assets/data/states/StageEditor.hx @@ -0,0 +1,328 @@ +/** +* THIS FILE IS TEMPORARY UNTIL TRANSFORMS ARE AT A USEABLE STATE. +* TODO: +* - !!!! MOVE BACK TO SOURCE !!!! +* - Rotation +* - Skewing +* - Angle + Scale (like already at a angle) +*/ + +import funkin.editors.stage.StageEditor; +import funkin.editors.ui.UIWarningSubstate; +import funkin.backend.utils.WindowUtils; +import funkin.backend.utils.MatrixUtil; +import flixel.math.FlxAngle; +import openfl.Lib; + +var exID = StageEditor.exID; + +function tryUpdateHitbox(sprite) { + var spriteNode = sprite.extra.get(exID("node")); + if (spriteNode.exists("updateHitbox") && spriteNode.get("updateHitbox") == "true") { + sprite.updateHitbox(); + return true; + } + + if (!FlxG.keys.pressed.ALT) { + sprite.x = storedPos.x - (sprite.frameWidth * (storedScale.x - sprite.scale.x) * 0.5); + sprite.y = storedPos.y - (sprite.frameHeight * (storedScale.y - sprite.scale.y) * 0.5); + } + return false; +} + +var allowClose = false; +function create() { + WindowUtils.preventClosing = true; + WindowUtils.resetClosing(); + WindowUtils.onClosing = function() { + if(!allowClose) + Lib.application.window.onClose.cancel(); + + var substate; + substate = new UIWarningSubstate("nuh uh", "you dont need to close goofus doofus", [ + { + label: "Actually Exit", + onClick: function(t) { + allowClose = true; + Lib.application.window.close(); + } + }, + { + label: "Reload State", + onClick: function(t) { + FlxG.switchState(new StageEditor(StageEditor.__stage)); + } + }, + { + label: "oh whoops", + onClick: function(t) { + substate.close(); + } + } + ]); + + openSubState(substate); + } +} + +function destroy() { + WindowUtils.preventClosing = false; + WindowUtils.resetClosing(); + WindowUtils.onClosing = null; +} + +function update() { + if (FlxG.mouse.justPressed) + lastRelative.set(); + + if(FlxG.keys.justPressed.R) { + trace("reloading"); + FlxG.switchState(new StageEditor(StageEditor.__stage)); + } +} + +function genericScale(sprite, relative, doX, doY) { + + var relativeMult = 1 / (FlxMath.lerp(1, stageCamera.zoom, sprite.zoomFactor) / stageCamera.zoom) * (FlxG.keys.pressed.ALT ? 2 : 1); + relative.x *= relativeMult; + relative.y *= relativeMult; + + var width = sprite.frameWidth * storedScale.x; + var height = sprite.frameHeight * storedScale.y; + if(doX) width -= relative.x; + if(doY) height -= relative.y; + CoolUtil.setGraphicSizeFloat(sprite, width, height); + + if(FlxG.keys.pressed.SHIFT) { + var nscale = Math.max(sprite.scale.x, sprite.scale.y); + sprite.scale.set(nscale, nscale); + } + + var updatedHitbox = tryUpdateHitbox(sprite); + if (FlxG.keys.pressed.ALT) { + sprite.x = storedPos.x; + sprite.y = storedPos.y; + if(updatedHitbox) { + sprite.x += (sprite.frameWidth * (storedScale.x - sprite.scale.x) * 0.5); + sprite.y += (sprite.frameHeight * (storedScale.y - sprite.scale.y) * 0.5); + } + return true; + } + return !updatedHitbox; +} + +function genericOppositeScale(sprite, relative, scaleX, scaleY, repositionX, repositionY) { + if(repositionX) relative.x *= -1; + if(repositionY) relative.y *= -1; + var repositioned = genericScale(sprite, relative, scaleX, scaleY); + if (!repositioned) { + if(repositionX) sprite.x = storedPos.x + (sprite.frameWidth * (storedScale.x - sprite.scale.x)); + if(repositionY) sprite.y = storedPos.y + (sprite.frameHeight * (storedScale.y - sprite.scale.y)); + } else if (!FlxG.keys.pressed.ALT) { + if(repositionX) sprite.x += (sprite.frameWidth * (storedScale.x - sprite.scale.x)); + if(repositionY) sprite.y += (sprite.frameHeight * (storedScale.y - sprite.scale.y)); + } +} + +var oldSpritePos = FlxPoint.get(); +function SCALE_BOTTOM_RIGHT(sprite, relative) { + preRotBullshit(sprite, relative); + genericScale(sprite, relative, true, true); + postRotBullshit(sprite, relative); +} + +/** + ROTATION +**/ +function preRotBullshit(sprite, relative) { + if (sprite.angle != 0) + relative = rotateByDegrees(relative, -sprite.angle); + + if (FlxG.mouse.justPressed) { + oldSpritePos.x = sprite.x; + oldSpritePos.y = sprite.y; + } +} + +function postRotBullshit(sprite, relative) { + if (sprite.angle != 0) { + var p = rotateAround(FlxPoint.get(sprite.x, sprite.y), oldSpritePos, sprite.angle); + sprite.x = p.x; + sprite.y = p.y; + } +} + +function rotateAround(p, origin, angle) { + var rel = FlxPoint.get(p.x - origin.x, p.y - origin.y); + rotateByDegrees(rel, angle); + p.x = origin.x + rel.x; + p.y = origin.y + rel.y; + rel.put(); + return p; +} +function rotateByDegrees(p, angle) { + var rads = angle * FlxAngle.TO_RAD; + var s:Float = Math.sin(rads); + var c:Float = Math.cos(rads); + var tempX:Float = p.x; + + p.x = tempX * c - p.y * s; + p.y = tempX * s + p.y * c; +} + /** + END OF ROTATION + **/ + + +function SCALE_TOP_RIGHT(sprite, relative) { + preRotBullshit(sprite, relative); + genericOppositeScale(sprite, relative, true, true, false, true); + postRotBullshit(sprite, relative); +} + +function MOVE_CENTER(sprite, relative) { + sprite.x = storedPos.x-relative.x; + sprite.y = storedPos.y-relative.y; +} + +function SCALE_TOP_LEFT(sprite, relative) { + preRotBullshit(sprite, relative); + genericOppositeScale(sprite, relative, true, true, true, true); + postRotBullshit(sprite, relative); +} + +function SCALE_BOTTOM_LEFT(sprite, relative) { + preRotBullshit(sprite, relative); + genericOppositeScale(sprite, relative, true, true, true, false); + postRotBullshit(sprite, relative); +} + +function SCALE_LEFT(sprite, relative) { + preRotBullshit(sprite, relative); + genericOppositeScale(sprite, relative, true, false, true, false); + postRotBullshit(sprite, relative); +} + +function SCALE_RIGHT(sprite, relative) { + preRotBullshit(sprite, relative); + genericScale(sprite, relative, true, false); + postRotBullshit(sprite, relative); +} + +function SCALE_TOP(sprite, relative) { + preRotBullshit(sprite, relative); + genericOppositeScale(sprite, relative, false, true, false, true); + postRotBullshit(sprite, relative); +} + +function SCALE_BOTTOM(sprite, relative) { + preRotBullshit(sprite, relative); + genericScale(sprite, relative, false, true); + postRotBullshit(sprite, relative); +} + +var lastRelative = FlxPoint.get(); +var skewPoint1 = FlxPoint.get(); +var skewPoint2 = FlxPoint.get(); + +function SKEW_LEFT(sprite, relative) { + preRotBullshit(sprite, relative); + if (!FlxG.keys.pressed.SHIFT) { + genericOppositeScale(sprite, relative, true, false, true, false); + postRotBullshit(sprite, relative); + } + + skewPoint1.set(0, 0); + skewPoint2.set(1, 0); + gimmeSkewCorners(sprite, 0, 1); + + sprite.y = storedPos.y - relative.y * 0.5; + sprite.skew.y += ((relative.y - lastRelative.y) * 0.02); + + lastRelative.set((FlxG.keys.pressed.SHIFT ? lastRelative.x : relative.x), relative.y); +} + +function SKEW_BOTTOM(sprite, relative) { + preRotBullshit(sprite, relative); + if (!FlxG.keys.pressed.SHIFT) { + genericScale(sprite, relative, false, true); + postRotBullshit(sprite, relative); + } + + skewPoint1.set(0, 0); + skewPoint2.set(0, 1); + gimmeSkewCorners(sprite, 0, 2); + + sprite.x = storedPos.x - relative.x * 0.4; + sprite.skew.x -= ((relative.x - lastRelative.x) * 0.03); + + lastRelative.set(relative.x, (FlxG.keys.pressed.SHIFT ? lastRelative.y : relative.y)); +} + +function SKEW_TOP(sprite, relative) { + preRotBullshit(sprite, relative); + if (!FlxG.keys.pressed.SHIFT) { + genericOppositeScale(sprite, relative, false, true, false, true); + postRotBullshit(sprite, relative); + } + + skewPoint1.set(0, 0); + skewPoint2.set(0, 1); + gimmeSkewCorners(sprite, 0, 2); + + sprite.x = storedPos.x - relative.x * 0.4; + sprite.skew.x += ((relative.x - lastRelative.x) * 0.03); + + lastRelative.set(relative.x, (FlxG.keys.pressed.SHIFT ? lastRelative.y : relative.y)); +} + +function SKEW_RIGHT(sprite, relative) { + preRotBullshit(sprite, relative); + if (!FlxG.keys.pressed.SHIFT) { + genericScale(sprite, relative, true, false); + postRotBullshit(sprite, relative); + } + + skewPoint1.set(0, 0); + skewPoint2.set(1, 0); + gimmeSkewCorners(sprite, 0, 1); + + sprite.y = storedPos.y - relative.y * 0.5; + sprite.skew.y -= ((relative.y - lastRelative.y) * 0.02); + + lastRelative.set((FlxG.keys.pressed.SHIFT ? lastRelative.x : relative.x), relative.y); +} + +function ROTATE(sprite, relative) { + var buttonBoxes:Array<FlxPoint> = sprite.extra.get(exID("buttonBoxes")); + var p:FlxPoint = buttonBoxes[8]; + + FlxG.mouse.getWorldPosition(stageCamera, _point); + + var dx:Float = _point.x - p.x; + var dy:Float = _point.y - p.y; + var angle = FlxAngle.angleFromOrigin(dx, dy, true) + angleOffset; + if(FlxG.keys.pressed.SHIFT) angle = Std.int(angle / 45) * 45; + sprite.angle = angle; +} + + +/*function postDraw() { + if(storedCenter.x != 0 || storedCenter.y != 0) { + drawLine(storedCenter, storedRelative, 0.3); + } +}*/ + +function gimmeSkewCorners(sprite, index1, index2) { + if (sprite.angle == 0) { + var corners = sprite.extra.get(exID("buttonBoxes")); + skewPoint1.set(corners[index1].x, corners[index1].y); + skewPoint2.set(corners[index2].x, corners[index2].y); + return; + } + + var ogAngle = sprite.angle; + sprite.angle = 0; + MatrixUtil.getMatrixPosition(sprite, [skewPoint1, skewPoint2], sprite.camera, sprite.frameWidth, sprite.frameHeight); + sprite.angle = ogAngle; +} \ No newline at end of file diff --git a/assets/images/editors/character/anim-icons.png b/assets/images/editors/character/anim-icons.png new file mode 100644 index 00000000..765e3725 Binary files /dev/null and b/assets/images/editors/character/anim-icons.png differ diff --git a/assets/images/editors/character/edit-button.png b/assets/images/editors/character/edit-button.png index 66c49aa8..8b439a63 100644 Binary files a/assets/images/editors/character/edit-button.png and b/assets/images/editors/character/edit-button.png differ diff --git a/assets/images/editors/character/ghost-button.png b/assets/images/editors/character/ghost-button.png deleted file mode 100644 index a383bffd..00000000 Binary files a/assets/images/editors/character/ghost-button.png and /dev/null differ diff --git a/assets/images/editors/character/ghost.png b/assets/images/editors/character/ghost.png new file mode 100644 index 00000000..4906cd32 Binary files /dev/null and b/assets/images/editors/character/ghost.png differ diff --git a/assets/images/editors/charter/event-icons/BPM Change Start.png b/assets/images/editors/charter/event-icons/BPM Change Start.png new file mode 100644 index 00000000..832a0235 Binary files /dev/null and b/assets/images/editors/charter/event-icons/BPM Change Start.png differ diff --git a/assets/images/editors/charter/event-icons/BPM Change.png b/assets/images/editors/charter/event-icons/BPM Change.png index 40dba09a..4eefebc3 100644 Binary files a/assets/images/editors/charter/event-icons/BPM Change.png and b/assets/images/editors/charter/event-icons/BPM Change.png differ diff --git a/assets/images/editors/charter/event-icons/Time Signature Change.png b/assets/images/editors/charter/event-icons/Time Signature Change.png new file mode 100644 index 00000000..96714600 Binary files /dev/null and b/assets/images/editors/charter/event-icons/Time Signature Change.png differ diff --git a/assets/images/editors/charter/event-icons/components/arrow-down.png b/assets/images/editors/charter/event-icons/components/arrow-down.png new file mode 100644 index 00000000..f44a8711 Binary files /dev/null and b/assets/images/editors/charter/event-icons/components/arrow-down.png differ diff --git a/assets/images/editors/charter/event-icons/components/arrow-down.xml b/assets/images/editors/charter/event-icons/components/arrow-down.xml new file mode 100644 index 00000000..1e58cc38 --- /dev/null +++ b/assets/images/editors/charter/event-icons/components/arrow-down.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<TextureAtlas imagePath="arrow-down.png"> + <SubTexture name="tail" x="0" y="0" width="8" height="3"/> + <SubTexture name="segment" x="0" y="3" width="8" height="1"/> + <SubTexture name="arrow" x="0" y="6" width="8" height="6"/> +</TextureAtlas> \ No newline at end of file diff --git a/assets/images/editors/charter/event-icons/components/arrow-right.png b/assets/images/editors/charter/event-icons/components/arrow-right.png new file mode 100644 index 00000000..a5da3a70 Binary files /dev/null and b/assets/images/editors/charter/event-icons/components/arrow-right.png differ diff --git a/assets/images/editors/charter/event-icons/components/cross.png b/assets/images/editors/charter/event-icons/components/cross.png new file mode 100644 index 00000000..a3dee356 Binary files /dev/null and b/assets/images/editors/charter/event-icons/components/cross.png differ diff --git a/assets/images/editors/charter/event-icons/components/end-plus.png b/assets/images/editors/charter/event-icons/components/end-plus.png new file mode 100644 index 00000000..7629e754 Binary files /dev/null and b/assets/images/editors/charter/event-icons/components/end-plus.png differ diff --git a/assets/images/editors/charter/event-icons/components/eventNums.png b/assets/images/editors/charter/event-icons/components/eventNums.png new file mode 100644 index 00000000..8ae1513c Binary files /dev/null and b/assets/images/editors/charter/event-icons/components/eventNums.png differ diff --git a/assets/images/editors/charter/event-icons/components/flash.png b/assets/images/editors/charter/event-icons/components/flash.png new file mode 100644 index 00000000..2ef5f949 Binary files /dev/null and b/assets/images/editors/charter/event-icons/components/flash.png differ diff --git a/assets/images/editors/charter/event-icons/components/note.png b/assets/images/editors/charter/event-icons/components/note.png new file mode 100644 index 00000000..4b08e4f5 Binary files /dev/null and b/assets/images/editors/charter/event-icons/components/note.png differ diff --git a/assets/images/editors/charter/event-icons/components/plus.png b/assets/images/editors/charter/event-icons/components/plus.png new file mode 100644 index 00000000..939d4c46 Binary files /dev/null and b/assets/images/editors/charter/event-icons/components/plus.png differ diff --git a/assets/images/editors/charter/event-icons/components/start-plus.png b/assets/images/editors/charter/event-icons/components/start-plus.png new file mode 100644 index 00000000..84b42f13 Binary files /dev/null and b/assets/images/editors/charter/event-icons/components/start-plus.png differ diff --git a/assets/images/editors/charter/event-icons/components/warning.png b/assets/images/editors/charter/event-icons/components/warning.png new file mode 100644 index 00000000..03a423db Binary files /dev/null and b/assets/images/editors/charter/event-icons/components/warning.png differ diff --git a/assets/images/editors/stage/edit-button.png b/assets/images/editors/stage/edit-button.png new file mode 100644 index 00000000..47991177 Binary files /dev/null and b/assets/images/editors/stage/edit-button.png differ diff --git a/assets/images/editors/stage/selectionDot.png b/assets/images/editors/stage/selectionDot.png new file mode 100644 index 00000000..d5aa449e Binary files /dev/null and b/assets/images/editors/stage/selectionDot.png differ diff --git a/assets/images/editors/stage/tarr.png b/assets/images/editors/stage/tarr.png new file mode 100644 index 00000000..d989b82b Binary files /dev/null and b/assets/images/editors/stage/tarr.png differ diff --git a/assets/images/editors/stage/trash-icon.png b/assets/images/editors/stage/trash-icon.png new file mode 100644 index 00000000..37041078 Binary files /dev/null and b/assets/images/editors/stage/trash-icon.png differ diff --git a/assets/images/editors/stage/visible-icon.png b/assets/images/editors/stage/visible-icon.png new file mode 100644 index 00000000..2e603333 Binary files /dev/null and b/assets/images/editors/stage/visible-icon.png differ diff --git a/assets/images/editors/ui/button small.png b/assets/images/editors/ui/button small.png new file mode 100644 index 00000000..9b0a33e4 Binary files /dev/null and b/assets/images/editors/ui/button small.png differ diff --git a/assets/images/editors/ui/button small.xml b/assets/images/editors/ui/button small.xml new file mode 100644 index 00000000..5f1cae61 --- /dev/null +++ b/assets/images/editors/ui/button small.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<TextureAtlas imagePath="button.png"> + <!-- Created without animate lol i wrote this--> + <SubTexture name="normal-topleft" x="0" y="0" width="2" height="2"/> + <SubTexture name="normal-top" x="2" y="0" width="6" height="2"/> + <SubTexture name="normal-topright" x="8" y="0" width="2" height="2"/> + <SubTexture name="normal-middleleft" x="0" y="2" width="2" height="7.5"/> + <SubTexture name="normal-middle" x="2" y="2" width="6" height="7.5"/> + <SubTexture name="normal-middleright" x="8" y="2" width="2" height="7.5"/> + <SubTexture name="normal-bottomleft" x="0" y="9.5" width="2" height="2"/> + <SubTexture name="normal-bottom" x="2" y="9.5" width="6" height="2"/> + <SubTexture name="normal-bottomright" x="8" y="9.5" width="2" height="2"/> + + <SubTexture name="hover-topleft" x="10" y="0" width="2" height="2"/> + <SubTexture name="hover-top" x="12" y="0" width="6" height="2"/> + <SubTexture name="hover-topright" x="18" y="0" width="2" height="2"/> + <SubTexture name="hover-middleleft" x="10" y="2" width="2" height="7.5"/> + <SubTexture name="hover-middle" x="12" y="2" width="6" height="7.5"/> + <SubTexture name="hover-middleright" x="18" y="2" width="2" height="7.5"/> + <SubTexture name="hover-bottomleft" x="10" y="9.5" width="2" height="2"/> + <SubTexture name="hover-bottom" x="12" y="9.5" width="6" height="2"/> + <SubTexture name="hover-bottomright" x="18" y="9.5" width="2" height="2"/> + + <SubTexture name="pressed-topleft" x="20" y="0" width="2" height="2"/> + <SubTexture name="pressed-top" x="22" y="0" width="6" height="2"/> + <SubTexture name="pressed-topright" x="28" y="0" width="2" height="2"/> + <SubTexture name="pressed-middleleft" x="20" y="2" width="2" height="7.5"/> + <SubTexture name="pressed-middle" x="22" y="2" width="6" height="7.5"/> + <SubTexture name="pressed-middleright" x="28" y="2" width="2" height="7.5"/> + <SubTexture name="pressed-bottomleft" x="20" y="9.5" width="2" height="2"/> + <SubTexture name="pressed-bottom" x="22" y="9.5" width="6" height="2"/> + <SubTexture name="pressed-bottomright" x="28" y="9.5" width="2" height="2"/> +</TextureAtlas> diff --git a/assets/images/editors/ui/checkbox-small.png b/assets/images/editors/ui/checkbox-small.png new file mode 100644 index 00000000..bd6472a2 Binary files /dev/null and b/assets/images/editors/ui/checkbox-small.png differ diff --git a/assets/images/editors/ui/context-bg-debug.png b/assets/images/editors/ui/context-bg-debug.png new file mode 100644 index 00000000..aa83123f Binary files /dev/null and b/assets/images/editors/ui/context-bg-debug.png differ diff --git a/assets/images/editors/ui/context-bg-debug.xml b/assets/images/editors/ui/context-bg-debug.xml new file mode 100644 index 00000000..6eb3c21a --- /dev/null +++ b/assets/images/editors/ui/context-bg-debug.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<TextureAtlas imagePath="context-bg.png"> + <!-- Created without animate lol i wrote this--> + <SubTexture name="topleft" x="0" y="0" width="10" height="10"/> + <SubTexture name="top" x="10" y="0" width="44" height="10"/> + <SubTexture name="topright" x="54" y="0" width="10" height="10"/> + + <SubTexture name="middleleft" x="0" y="10" width="10" height="44"/> + <SubTexture name="middle" x="10" y="10" width="44" height="44"/> + <SubTexture name="middleright" x="54" y="10" width="10" height="44"/> + + <SubTexture name="bottomleft" x="0" y="54" width="10" height="10"/> + <SubTexture name="bottom" x="10" y="54" width="44" height="10"/> + <SubTexture name="bottomright" x="54" y="54" width="10" height="10"/> +</TextureAtlas> \ No newline at end of file diff --git a/assets/images/editors/ui/dropdown-button.png b/assets/images/editors/ui/dropdown-button.png new file mode 100644 index 00000000..13a96bad Binary files /dev/null and b/assets/images/editors/ui/dropdown-button.png differ diff --git a/assets/images/editors/ui/inputbox-small.png b/assets/images/editors/ui/inputbox-small.png new file mode 100644 index 00000000..30b76224 Binary files /dev/null and b/assets/images/editors/ui/inputbox-small.png differ diff --git a/assets/images/editors/ui/inputbox-small.xml b/assets/images/editors/ui/inputbox-small.xml new file mode 100644 index 00000000..de0e833b --- /dev/null +++ b/assets/images/editors/ui/inputbox-small.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<TextureAtlas imagePath="button.png"> + <!-- Created without animate lol i wrote this--> + + <!-- Thanks yosh, and i wrote this script incase anyone ever needs to resize these xmls --> + <!-- Here you guys go :D https://try.haxe.org/#7C373001 -lunar --> + + <SubTexture name="normal-topleft" x="0" y="0" width="4" height="4"/> + <SubTexture name="normal-top" x="4" y="0" width="8" height="4"/> + <SubTexture name="normal-topright" x="12" y="0" width="4" height="4"/> + <SubTexture name="normal-middleleft" x="0" y="4" width="4" height="8"/> + <SubTexture name="normal-middle" x="4" y="4" width="8" height="8"/> + <SubTexture name="normal-middleright" x="12" y="4" width="4" height="8"/> + <SubTexture name="normal-bottomleft" x="0" y="12" width="4" height="4"/> + <SubTexture name="normal-bottom" x="4" y="12" width="8" height="4"/> + <SubTexture name="normal-bottomright" x="12" y="12" width="4" height="4"/> + + <SubTexture name="hover-topleft" x="16" y="0" width="4" height="4"/> + <SubTexture name="hover-top" x="20" y="0" width="8" height="4"/> + <SubTexture name="hover-topright" x="28" y="0" width="4" height="4"/> + <SubTexture name="hover-middleleft" x="16" y="4" width="4" height="8"/> + <SubTexture name="hover-middle" x="20" y="4" width="8" height="8"/> + <SubTexture name="hover-middleright" x="28" y="4" width="4" height="8"/> + <SubTexture name="hover-bottomleft" x="16" y="12" width="4" height="4"/> + <SubTexture name="hover-bottom" x="20" y="12" width="8" height="4"/> + <SubTexture name="hover-bottomright" x="28" y="12" width="4" height="4"/> + + <SubTexture name="pressed-topleft" x="32" y="0" width="4" height="4"/> + <SubTexture name="pressed-top" x="36" y="0" width="8" height="4"/> + <SubTexture name="pressed-topright" x="44" y="0" width="4" height="4"/> + <SubTexture name="pressed-middleleft" x="32" y="4" width="4" height="8"/> + <SubTexture name="pressed-middle" x="36" y="4" width="8" height="8"/> + <SubTexture name="pressed-middleright" x="44" y="4" width="4" height="8"/> + <SubTexture name="pressed-bottomleft" x="32" y="12" width="4" height="4"/> + <SubTexture name="pressed-bottom" x="36" y="12" width="8" height="4"/> + <SubTexture name="pressed-bottomright" x="44" y="12" width="4" height="4"/> +</TextureAtlas> diff --git a/assets/images/editors/ui/picker.png b/assets/images/editors/ui/picker.png new file mode 100644 index 00000000..12fb58a1 Binary files /dev/null and b/assets/images/editors/ui/picker.png differ diff --git a/assets/images/editors/ui/radiobutton.png b/assets/images/editors/ui/radiobutton.png new file mode 100644 index 00000000..56c26c15 Binary files /dev/null and b/assets/images/editors/ui/radiobutton.png differ diff --git a/assets/shaders/engine/charterGrid.frag b/assets/shaders/engine/charterGrid.frag new file mode 100644 index 00000000..cddafa2a --- /dev/null +++ b/assets/shaders/engine/charterGrid.frag @@ -0,0 +1,35 @@ +#pragma header + +#define GRID_SIZE 40.0 +#define GRID_HEIGHT 4.0 + +uniform int segments; + +vec4 colorA = vec4(0.329, 0.329, 0.329, 1.0); +vec4 colorB = vec4(0.153, 0.153, 0.153, 1.0); +vec4 colorOutline = vec4(0.867, 0.867, 0.867, 1.0); + +void main() +{ + vec2 pixelSize = (1.0 / openfl_TextureSize) / (GRID_SIZE * float(segments)); + vec2 uv = openfl_TextureCoordv.xy; + vec2 pixelUV = vec2(uv.x * GRID_SIZE * float(segments), uv.y * GRID_SIZE * GRID_HEIGHT); + if (uv.x < pixelSize.x || uv.x > 1.0 - pixelSize.x) { //edges + gl_FragColor = colorOutline; + return; + } + vec4 col = colorA; + bool flip = false; + + //horizontal + if (mod(pixelUV.x, GRID_SIZE * 2.0) < GRID_SIZE) + flip = !flip; + //vertical + if (mod(pixelUV.y, GRID_SIZE * 2.0) < GRID_SIZE) + flip = !flip; + + if (flip) + col = colorB; + + gl_FragColor = col * flixel_texture2D(bitmap, uv); +} \ No newline at end of file diff --git a/assets/shaders/engine/editorWaveforms.frag b/assets/shaders/engine/editorWaveforms.frag index 490d6698..c65452dc 100644 --- a/assets/shaders/engine/editorWaveforms.frag +++ b/assets/shaders/engine/editorWaveforms.frag @@ -21,8 +21,9 @@ uniform bool lowDetail; float getAmplitude(vec2 pixel) { float pixelID = floor((pixel.y+pixelOffset)/3.0); - // TODO: INVESTIGATE THE 1.+ AND WHY IT WORKS (SERIOUSLY I GOT NO CLUE) -lunar - vec2 wavePixel = vec2(mod(pixelID, waveformSize.x), 1.0+floor(pixelID/waveformSize.x)); + // TODO: INVESTIGATE THE 1.+ AND WHY IT WORKS (SRSLY I GOT NO CLUE) -lunar + // Somehow using 0.999 instead of 1.0 fixes weird glitches on some systems -Ne_Eo + vec2 wavePixel = vec2(mod(pixelID, waveformSize.x), 0.999+floor(pixelID/waveformSize.x)); vec4 waveData = texture2D(waveformTexture, wavePixel / waveformSize); int id = int(mod(wavePixel.x, 3.0)); diff --git a/assets/shaders/engine/editorWaveformsRainbow.frag b/assets/shaders/engine/editorWaveformsRainbow.frag new file mode 100644 index 00000000..7ac0df32 --- /dev/null +++ b/assets/shaders/engine/editorWaveformsRainbow.frag @@ -0,0 +1,88 @@ +#pragma header + +// Used in charter by waveforms + +#define HEIGHT 0.05 // Height of the Bar +#define BACKLIGHT 0.6 // Backlight +#define BRIGHTNESS 0.33 // overall Brightness +#define GLOW 1.25 // Glow intensity of the Bar + +const vec3 gradient1 = vec3(114.0/255.0, 81.0/255.0, 135.0/255.0); +const vec3 gradient2 = vec3(144.0/255.0, 80.0/255.0, 186.0/255.0); + +const vec4 outlinecolor = vec4(0.1); +const vec3 lowdetailcol = vec3(0.0, 0.0, 1.0); + +uniform vec2 textureRes; +uniform float pixelOffset; + +uniform float playerPosition; + +uniform sampler2D waveformTexture; +uniform ivec2 waveformSize; + +uniform bool lowDetail; + +uniform float time; + +float getAmplitude(vec2 pixel) { + float pixelID = floor((pixel.y+pixelOffset)/3.0); + + // TODO: INVESTIGATE THE 1.+ AND WHY IT WORKS (SRSLY I GOT NO CLUE) -lunar + vec2 wavePixel = vec2(mod(pixelID,waveformSize.x), 1.0+floor(pixelID/waveformSize.x)); + vec4 waveData = texture2D(waveformTexture, wavePixel / waveformSize); + + int id = int(mod(wavePixel.x, 3.0)); + if(id == 0) return waveData.r; + if(id == 1) return waveData.g; + if(id == 2) return waveData.b; + return 0.0; +} + +float getAmpWidth(float amplitude) { + return (1.0-amplitude) * textureRes.x; +} + +bool inWaveForm(vec2 pixel, float width) { + float widthdiv2 = width/2.0; + return pixel.x > widthdiv2 && pixel.x < textureRes.x-widthdiv2; +} + +float getHightlight(float ampwidth) { + float remapCoord = (ampwidth/textureRes.x)/2.0; + float mappedCoord = map(openfl_TextureCoordv.x, remapCoord, 1.0-remapCoord, 0.0, 1.0); + return 1.0-(abs(mappedCoord-0.5)*2.0); +} + +void outlineWaveform(vec2 pixel, vec2 offset) { + float amplitude = getAmplitude(pixel - offset); + float ampwidth = (1.0-amplitude) * textureRes.x; + + if (!inWaveForm(pixel, ampwidth)) + gl_FragColor = vec4(vec3(0.), 1.); +} + +void main() +{ + vec2 pixel = openfl_TextureCoordv * textureRes; + float amplitude = getAmplitude(pixel); + float ampwidth = getAmpWidth(amplitude); + + gl_FragColor = vec4(vec3(0.0), .1); + + vec2 uv = openfl_TextureCoordv.xy - 0.5; + + float c = BACKLIGHT; + float a = abs(uv.x); + float s = 1.0 - smoothstep(0.0, ampwidth/textureRes.x, a); + c *= 1.33 - smoothstep(0.0, 0.5, a); + c*=c*c; + c += s*.5; + + if (inWaveForm(pixel, ampwidth)) + c += s; + + gl_FragColor = vec4((cos(6.283 * (uv.y + time + vec3(0.0,.33,0.66)))*map(getHightlight(amplitude), 0.0, 1.0, 0.5, 1.0)) + GLOW, 1.0) * c * BRIGHTNESS; + gl_FragColor.xyz *= map(getHightlight(amplitude), 0.0, 1.0, 0.5, 1.0); + gl_FragColor.xyz *= pixel.y+pixelOffset>playerPosition ? 0.6 : 1.0; +} \ No newline at end of file diff --git a/assets/songs/bopeebo/charts/easy.json b/assets/songs/bopeebo/charts/easy.json index f32e1dd2..dbad2465 100644 --- a/assets/songs/bopeebo/charts/easy.json +++ b/assets/songs/bopeebo/charts/easy.json @@ -1 +1 @@ -{"song":{"song":"Bopeebo","bpm":100.0,"needsVoices":true,"player1":"bf","player2":"dad","speed":1.0,"notes":[{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[0.0,2,0.0],[600.0,3,450.0],[1200.0,3,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[2400.0,2,0.0],[3000.0,3,450.0],[3600.0,3,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[4800.0,1,300.0],[5400.0,0,300.0],[6000.0,3,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[7200.0,1,300.0],[7800.0,0,300.0],[8400.0,3,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[9600.0,1,0.0],[10200.0,3,0.0],[10500.0,0,0.0],[10800.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[12000.0,1,0.0],[12600.0,3,0.0],[12900.0,0,0.0],[13200.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[14400.0,3,0.0],[14700.0,1,0.0],[15300.0,0,0.0],[15600.0,2,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[16800.0,3,0.0],[17100.0,1,0.0],[17700.0,0,0.0],[18000.0,2,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[19200.0,0,0.0],[19500.0,3,0.0],[19800.0,1,900.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[21600.0,0,0.0],[21900.0,3,0.0],[22200.0,1,900.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[24000.0,1,0.0],[24300.0,3,0.0],[24600.0,0,900.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[26400.0,1,0.0],[26700.0,3,0.0],[27000.0,0,900.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[28800.0,2,0.0],[29100.0,3,0.0],[29400.0,0,1200.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[31200.0,2,0.0],[31500.0,3,0.0],[31800.0,0,1200.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[33600.0,0,0.0],[33900.0,3,0.0],[34500.0,2,0.0],[34800.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[36000.0,0,0.0],[36300.0,3,0.0],[36900.0,2,0.0],[37200.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[38400.0,2,450.0],[39000.0,3,300.0],[39600.0,0,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[40800.0,2,450.0],[41400.0,3,300.0],[42000.0,0,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[43200.0,1,0.0],[43800.0,2,0.0],[44400.0,1,0.0],[44700.0,1,0.0],[45000.0,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[45600.0,1,0.0],[46200.0,2,0.0],[46800.0,1,0.0],[47100.0,1,0.0],[47400.0,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[48000.0,2,450.0],[48600.0,3,300.0],[49200.0,0,450.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[50400.0,2,450.0],[51000.0,3,300.0],[51600.0,0,450.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[52800.0,3,1800.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[55200.0,3,1800.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[57600.0,2,0.0],[57900.0,3,0.0],[58200.0,0,1200.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[60000.0,2,0.0],[60300.0,3,0.0],[60600.0,0,1200.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[62400.0,0,0.0],[62700.0,3,0.0],[63300.0,2,0.0],[63600.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[64800.0,0,0.0],[65100.0,3,0.0],[65700.0,2,0.0],[66000.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[67200.0,2,0.0],[67500.0,3,0.0],[67800.0,0,0.0],[68100.0,2,0.0],[68400.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[69600.0,2,0.0],[69900.0,3,0.0],[70200.0,0,0.0],[70500.0,2,0.0],[70800.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[72000.0,0,0.0],[72300.0,3,0.0],[72900.0,2,0.0],[73200.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[74400.0,0,0.0],[74700.0,3,0.0],[75300.0,2,0.0],[75600.0,1,600.0]]}]},"generatedBy":"SNIFF ver.6"} \ No newline at end of file +{"events":[{"params":[1],"time":2400,"name":"Camera Movement"},{"params":[0],"time":4800,"name":"Camera Movement"},{"params":[1],"time":7200,"name":"Camera Movement"},{"params":[0],"time":9600,"name":"Camera Movement"},{"params":[1],"time":12000,"name":"Camera Movement"},{"params":[0],"time":14400,"name":"Camera Movement"},{"params":[1],"time":16800,"name":"Camera Movement"},{"params":[0],"time":19200,"name":"Camera Movement"},{"params":[1],"time":21600,"name":"Camera Movement"},{"params":[0],"time":24000,"name":"Camera Movement"},{"params":[1],"time":26400,"name":"Camera Movement"},{"params":[0],"time":28800,"name":"Camera Movement"},{"params":[1],"time":31200,"name":"Camera Movement"},{"params":[0],"time":33600,"name":"Camera Movement"},{"params":[1],"time":36000,"name":"Camera Movement"},{"params":[0],"time":38400,"name":"Camera Movement"},{"params":[1],"time":40800,"name":"Camera Movement"},{"params":[0],"time":43200,"name":"Camera Movement"},{"params":[1],"time":45600,"name":"Camera Movement"},{"params":[0],"time":48000,"name":"Camera Movement"},{"params":[1],"time":50400,"name":"Camera Movement"},{"params":[0],"time":52800,"name":"Camera Movement"},{"params":[1],"time":55200,"name":"Camera Movement"},{"params":[0],"time":57600,"name":"Camera Movement"},{"params":[1],"time":60000,"name":"Camera Movement"},{"params":[0],"time":62400,"name":"Camera Movement"},{"params":[1],"time":64800,"name":"Camera Movement"},{"params":[0],"time":67200,"name":"Camera Movement"},{"params":[1],"time":69600,"name":"Camera Movement"},{"params":[0],"time":72000,"name":"Camera Movement"},{"params":[1],"time":74400,"name":"Camera Movement"}],"strumLines":[{"visible":true,"keyCount":4,"notes":[{"id":2,"sLen":0,"time":0,"type":0},{"id":3,"sLen":450,"time":600,"type":0},{"id":3,"sLen":600,"time":1200,"type":0},{"id":1,"sLen":300,"time":4800,"type":0},{"id":0,"sLen":300,"time":5400,"type":0},{"id":3,"sLen":600,"time":6000,"type":0},{"id":1,"sLen":0,"time":9600,"type":0},{"id":3,"sLen":0,"time":10200,"type":0},{"id":0,"sLen":0,"time":10500,"type":0},{"id":1,"sLen":600,"time":10800,"type":0},{"id":3,"sLen":0,"time":14400,"type":0},{"id":1,"sLen":0,"time":14700,"type":0},{"id":0,"sLen":0,"time":15300,"type":0},{"id":2,"sLen":600,"time":15600,"type":0},{"id":0,"sLen":0,"time":19200,"type":0},{"id":3,"sLen":0,"time":19500,"type":0},{"id":1,"sLen":900,"time":19800,"type":0},{"id":1,"sLen":0,"time":24000,"type":0},{"id":3,"sLen":0,"time":24300,"type":0},{"id":0,"sLen":900,"time":24600,"type":0},{"id":2,"sLen":0,"time":28800,"type":0},{"id":3,"sLen":0,"time":29100,"type":0},{"id":0,"sLen":1200,"time":29400,"type":0},{"id":0,"sLen":0,"time":33600,"type":0},{"id":3,"sLen":0,"time":33900,"type":0},{"id":2,"sLen":0,"time":34500,"type":0},{"id":1,"sLen":600,"time":34800,"type":0},{"id":2,"sLen":450,"time":38400,"type":0},{"id":3,"sLen":300,"time":39000,"type":0},{"id":0,"sLen":600,"time":39600,"type":0},{"id":1,"sLen":0,"time":43200,"type":0},{"id":2,"sLen":0,"time":43800,"type":0},{"id":1,"sLen":0,"time":44400,"type":0},{"id":1,"sLen":0,"time":44700,"type":0},{"id":2,"sLen":0,"time":45000,"type":0},{"id":2,"sLen":450,"time":48000,"type":0},{"id":3,"sLen":300,"time":48600,"type":0},{"id":0,"sLen":450,"time":49200,"type":0},{"id":3,"sLen":1800,"time":52800,"type":0},{"id":2,"sLen":0,"time":57600,"type":0},{"id":3,"sLen":0,"time":57900,"type":0},{"id":0,"sLen":1200,"time":58200,"type":0},{"id":0,"sLen":0,"time":62400,"type":0},{"id":3,"sLen":0,"time":62700,"type":0},{"id":2,"sLen":0,"time":63300,"type":0},{"id":1,"sLen":600,"time":63600,"type":0},{"id":2,"sLen":0,"time":67200,"type":0},{"id":3,"sLen":0,"time":67500,"type":0},{"id":0,"sLen":0,"time":67800,"type":0},{"id":2,"sLen":0,"time":68100,"type":0},{"id":1,"sLen":600,"time":68400,"type":0},{"id":0,"sLen":0,"time":72000,"type":0},{"id":3,"sLen":0,"time":72300,"type":0},{"id":2,"sLen":0,"time":72900,"type":0},{"id":1,"sLen":600,"time":73200,"type":0}],"position":"dad","type":0,"characters":["dad"]},{"visible":true,"keyCount":4,"notes":[{"id":2,"sLen":0,"time":2400,"type":0},{"id":3,"sLen":450,"time":3000,"type":0},{"id":3,"sLen":600,"time":3600,"type":0},{"id":1,"sLen":300,"time":7200,"type":0},{"id":0,"sLen":300,"time":7800,"type":0},{"id":3,"sLen":600,"time":8400,"type":0},{"id":1,"sLen":0,"time":12000,"type":0},{"id":3,"sLen":0,"time":12600,"type":0},{"id":0,"sLen":0,"time":12900,"type":0},{"id":1,"sLen":600,"time":13200,"type":0},{"id":3,"sLen":0,"time":16800,"type":0},{"id":1,"sLen":0,"time":17100,"type":0},{"id":0,"sLen":0,"time":17700,"type":0},{"id":2,"sLen":600,"time":18000,"type":0},{"id":0,"sLen":0,"time":21600,"type":0},{"id":3,"sLen":0,"time":21900,"type":0},{"id":1,"sLen":900,"time":22200,"type":0},{"id":1,"sLen":0,"time":26400,"type":0},{"id":3,"sLen":0,"time":26700,"type":0},{"id":0,"sLen":900,"time":27000,"type":0},{"id":2,"sLen":0,"time":31200,"type":0},{"id":3,"sLen":0,"time":31500,"type":0},{"id":0,"sLen":1200,"time":31800,"type":0},{"id":0,"sLen":0,"time":36000,"type":0},{"id":3,"sLen":0,"time":36300,"type":0},{"id":2,"sLen":0,"time":36900,"type":0},{"id":1,"sLen":600,"time":37200,"type":0},{"id":2,"sLen":450,"time":40800,"type":0},{"id":3,"sLen":300,"time":41400,"type":0},{"id":0,"sLen":600,"time":42000,"type":0},{"id":1,"sLen":0,"time":45600,"type":0},{"id":2,"sLen":0,"time":46200,"type":0},{"id":1,"sLen":0,"time":46800,"type":0},{"id":1,"sLen":0,"time":47100,"type":0},{"id":2,"sLen":0,"time":47400,"type":1},{"id":2,"sLen":450,"time":50400,"type":0},{"id":3,"sLen":300,"time":51000,"type":0},{"id":0,"sLen":450,"time":51600,"type":0},{"id":3,"sLen":1800,"time":55200,"type":0},{"id":2,"sLen":0,"time":60000,"type":0},{"id":3,"sLen":0,"time":60300,"type":0},{"id":0,"sLen":1200,"time":60600,"type":0},{"id":0,"sLen":0,"time":64800,"type":0},{"id":3,"sLen":0,"time":65100,"type":0},{"id":2,"sLen":0,"time":65700,"type":0},{"id":1,"sLen":600,"time":66000,"type":0},{"id":2,"sLen":0,"time":69600,"type":0},{"id":3,"sLen":0,"time":69900,"type":0},{"id":0,"sLen":0,"time":70200,"type":0},{"id":2,"sLen":0,"time":70500,"type":0},{"id":1,"sLen":600,"time":70800,"type":0},{"id":0,"sLen":0,"time":74400,"type":0},{"id":3,"sLen":0,"time":74700,"type":0},{"id":2,"sLen":0,"time":75300,"type":0},{"id":1,"sLen":600,"time":75600,"type":0}],"position":"boyfriend","type":1,"characters":["bf"]},{"keyCount":4,"notes":[],"visible":false,"position":"girlfriend","type":2,"characters":["gf"]}],"codenameChart":true,"scrollSpeed":1,"chartVersion":"1.6.0","stage":"stage","noteTypes":["No Anim Note"]} \ No newline at end of file diff --git a/assets/songs/bopeebo/charts/hard.json b/assets/songs/bopeebo/charts/hard.json index 09e58004..06009383 100644 --- a/assets/songs/bopeebo/charts/hard.json +++ b/assets/songs/bopeebo/charts/hard.json @@ -1 +1 @@ -{"song":{"song":"Bopeebo","bpm":100.0,"needsVoices":true,"player1":"bf","player2":"dad","speed":1.3,"notes":[{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[0.0,2,0.0],[600.0,3,600.0],[1200.0,3,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[2400.0,2,0.0],[3000.0,3,600.0],[3600.0,3,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[4800.0,1,300.0],[5400.0,0,300.0],[6000.0,3,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[7200.0,1,300.0],[7800.0,0,300.0],[8400.0,3,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[9600.0,1,300.0],[10200.0,3,0.0],[10500.0,0,0.0],[10800.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[12000.0,1,300.0],[12600.0,3,0.0],[12900.0,0,0.0],[13200.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[14400.0,3,0.0],[14700.0,1,0.0],[15300.0,0,0.0],[15600.0,2,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[16800.0,3,0.0],[17100.0,1,0.0],[17700.0,0,0.0],[18000.0,2,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[19200.0,0,0.0],[19500.0,3,0.0],[19800.0,1,900.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[21600.0,0,0.0],[21900.0,3,0.0],[22200.0,1,900.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[24000.0,1,0.0],[24300.0,3,0.0],[24600.0,0,900.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[26400.0,1,0.0],[26700.0,3,0.0],[27000.0,0,900.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[28800.0,2,0.0],[29100.0,3,0.0],[29400.0,0,1200.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[31200.0,2,0.0],[31500.0,3,0.0],[31800.0,0,1200.0],[33300.0,6,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[33600.0,0,0.0],[33900.0,3,0.0],[34500.0,2,0.0],[34575.0,0,0.0],[34800.0,1,600.0],[35700.0,6,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[36000.0,0,0.0],[36300.0,3,0.0],[36900.0,2,0.0],[36975.0,0,0.0],[37200.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[38400.0,2,450.0],[39000.0,3,300.0],[39600.0,0,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[40800.0,2,450.0],[41400.0,3,300.0],[42000.0,0,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[43200.0,1,0.0],[43800.0,2,0.0],[44400.0,1,0.0],[44550.0,1,0.0],[44700.0,1,0.0],[45000.0,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[45600.0,1,0.0],[46200.0,2,0.0],[46800.0,1,0.0],[46950.0,1,0.0],[47100.0,1,0.0],[47400.0,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[48000.0,2,450.0],[48600.0,3,300.0],[49200.0,0,450.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[50400.0,2,450.0],[51000.0,3,300.0],[51600.0,0,450.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[52800.0,3,1800.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[55200.0,3,1800.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[57600.0,2,0.0],[57900.0,3,0.0],[58200.0,0,1200.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[60000.0,2,0.0],[60300.0,3,0.0],[60600.0,0,1200.0],[62100.0,6,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[62400.0,0,0.0],[62700.0,3,0.0],[63300.0,2,0.0],[63375.0,0,0.0],[63600.0,1,600.0],[64500.0,6,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[64800.0,0,0.0],[65100.0,3,0.0],[65700.0,2,0.0],[65775.0,0,0.0],[66000.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[67200.0,2,0.0],[67500.0,3,0.0],[67800.0,0,0.0],[68100.0,2,0.0],[68400.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[69600.0,2,0.0],[69900.0,3,0.0],[70200.0,0,0.0],[70500.0,2,0.0],[70800.0,1,600.0],[71700.0,6,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[72000.0,0,0.0],[72300.0,3,0.0],[72900.0,2,0.0],[72975.0,0,0.0],[73200.0,1,600.0],[74100.0,6,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[74400.0,0,0.0],[74700.0,3,0.0],[75300.0,2,0.0],[75375.0,0,0.0],[75600.0,1,600.0]]}]},"generatedBy":"SNIFF ver.6"} \ No newline at end of file +{"events":[{"params":[1],"time":2400,"name":"Camera Movement"},{"params":[0],"time":4800,"name":"Camera Movement"},{"params":[1],"time":7200,"name":"Camera Movement"},{"params":[0],"time":9600,"name":"Camera Movement"},{"params":[1],"time":12000,"name":"Camera Movement"},{"params":[0],"time":14400,"name":"Camera Movement"},{"params":[1],"time":16800,"name":"Camera Movement"},{"params":[0],"time":19200,"name":"Camera Movement"},{"params":[1],"time":21600,"name":"Camera Movement"},{"params":[0],"time":24000,"name":"Camera Movement"},{"params":[1],"time":26400,"name":"Camera Movement"},{"params":[0],"time":28800,"name":"Camera Movement"},{"params":[1],"time":31200,"name":"Camera Movement"},{"params":[0],"time":33600,"name":"Camera Movement"},{"params":[1],"time":36000,"name":"Camera Movement"},{"params":[0],"time":38400,"name":"Camera Movement"},{"params":[1],"time":40800,"name":"Camera Movement"},{"params":[0],"time":43200,"name":"Camera Movement"},{"params":[1],"time":45600,"name":"Camera Movement"},{"params":[0],"time":48000,"name":"Camera Movement"},{"params":[1],"time":50400,"name":"Camera Movement"},{"params":[0],"time":52800,"name":"Camera Movement"},{"params":[1],"time":55200,"name":"Camera Movement"},{"params":[0],"time":57600,"name":"Camera Movement"},{"params":[1],"time":60000,"name":"Camera Movement"},{"params":[0],"time":62400,"name":"Camera Movement"},{"params":[1],"time":64800,"name":"Camera Movement"},{"params":[0],"time":67200,"name":"Camera Movement"},{"params":[1],"time":69600,"name":"Camera Movement"},{"params":[0],"time":72000,"name":"Camera Movement"},{"params":[1],"time":74400,"name":"Camera Movement"}],"codenameChart":true,"strumLines":[{"keyCount":4,"position":"dad","notes":[{"id":2,"sLen":0,"time":0,"type":0},{"id":3,"sLen":600,"time":600,"type":0},{"id":3,"sLen":600,"time":1200,"type":0},{"id":1,"sLen":300,"time":4800,"type":0},{"id":0,"sLen":300,"time":5400,"type":0},{"id":3,"sLen":600,"time":6000,"type":0},{"id":1,"sLen":300,"time":9600,"type":0},{"id":3,"sLen":0,"time":10200,"type":0},{"id":0,"sLen":0,"time":10500,"type":0},{"id":1,"sLen":600,"time":10800,"type":0},{"id":3,"sLen":0,"time":14400,"type":0},{"id":1,"sLen":0,"time":14700,"type":0},{"id":0,"sLen":0,"time":15300,"type":0},{"id":2,"sLen":600,"time":15600,"type":0},{"id":0,"sLen":0,"time":19200,"type":0},{"id":3,"sLen":0,"time":19500,"type":0},{"id":1,"sLen":900,"time":19800,"type":0},{"id":1,"sLen":0,"time":24000,"type":0},{"id":3,"sLen":0,"time":24300,"type":0},{"id":0,"sLen":900,"time":24600,"type":0},{"id":2,"sLen":0,"time":28800,"type":0},{"id":3,"sLen":0,"time":29100,"type":0},{"id":0,"sLen":1200,"time":29400,"type":0},{"id":2,"sLen":0,"time":33300,"type":0},{"id":0,"sLen":0,"time":33600,"type":0},{"id":3,"sLen":0,"time":33900,"type":0},{"id":2,"sLen":0,"time":34500,"type":0},{"id":0,"sLen":0,"time":34575,"type":0},{"id":1,"sLen":600,"time":34800,"type":0},{"id":2,"sLen":450,"time":38400,"type":0},{"id":3,"sLen":300,"time":39000,"type":0},{"id":0,"sLen":600,"time":39600,"type":0},{"id":1,"sLen":0,"time":43200,"type":0},{"id":2,"sLen":0,"time":43800,"type":0},{"id":1,"sLen":0,"time":44400,"type":0},{"id":1,"sLen":0,"time":44550,"type":0},{"id":1,"sLen":0,"time":44700,"type":0},{"id":2,"sLen":0,"time":45000,"type":0},{"id":2,"sLen":450,"time":48000,"type":0},{"id":3,"sLen":300,"time":48600,"type":0},{"id":0,"sLen":450,"time":49200,"type":0},{"id":3,"sLen":1800,"time":52800,"type":0},{"id":2,"sLen":0,"time":57600,"type":0},{"id":3,"sLen":0,"time":57900,"type":0},{"id":0,"sLen":1200,"time":58200,"type":0},{"id":2,"sLen":0,"time":62100,"type":0},{"id":0,"sLen":0,"time":62400,"type":0},{"id":3,"sLen":0,"time":62700,"type":0},{"id":2,"sLen":0,"time":63300,"type":0},{"id":0,"sLen":0,"time":63375,"type":0},{"id":1,"sLen":600,"time":63600,"type":0},{"id":2,"sLen":0,"time":67200,"type":0},{"id":3,"sLen":0,"time":67500,"type":0},{"id":0,"sLen":0,"time":67800,"type":0},{"id":2,"sLen":0,"time":68100,"type":0},{"id":1,"sLen":600,"time":68400,"type":0},{"id":2,"sLen":0,"time":71700,"type":0},{"id":0,"sLen":0,"time":72000,"type":0},{"id":3,"sLen":0,"time":72300,"type":0},{"id":2,"sLen":0,"time":72900,"type":0},{"id":0,"sLen":0,"time":72975,"type":0},{"id":1,"sLen":600,"time":73200,"type":0}],"characters":["dad"],"type":0,"visible":true},{"keyCount":4,"position":"boyfriend","notes":[{"id":2,"sLen":0,"time":2400,"type":0},{"id":3,"sLen":600,"time":3000,"type":0},{"id":3,"sLen":600,"time":3600,"type":0},{"id":1,"sLen":300,"time":7200,"type":0},{"id":0,"sLen":300,"time":7800,"type":0},{"id":3,"sLen":600,"time":8400,"type":0},{"id":1,"sLen":300,"time":12000,"type":0},{"id":3,"sLen":0,"time":12600,"type":0},{"id":0,"sLen":0,"time":12900,"type":0},{"id":1,"sLen":600,"time":13200,"type":0},{"id":3,"sLen":0,"time":16800,"type":0},{"id":1,"sLen":0,"time":17100,"type":0},{"id":0,"sLen":0,"time":17700,"type":0},{"id":2,"sLen":600,"time":18000,"type":0},{"id":0,"sLen":0,"time":21600,"type":0},{"id":3,"sLen":0,"time":21900,"type":0},{"id":1,"sLen":900,"time":22200,"type":0},{"id":1,"sLen":0,"time":26400,"type":0},{"id":3,"sLen":0,"time":26700,"type":0},{"id":0,"sLen":900,"time":27000,"type":0},{"id":2,"sLen":0,"time":31200,"type":0},{"id":3,"sLen":0,"time":31500,"type":0},{"id":0,"sLen":1200,"time":31800,"type":0},{"id":2,"sLen":0,"time":35700,"type":0},{"id":0,"sLen":0,"time":36000,"type":0},{"id":3,"sLen":0,"time":36300,"type":0},{"id":2,"sLen":0,"time":36900,"type":0},{"id":0,"sLen":0,"time":36975,"type":0},{"id":1,"sLen":600,"time":37200,"type":0},{"id":2,"sLen":450,"time":40800,"type":0},{"id":3,"sLen":300,"time":41400,"type":0},{"id":0,"sLen":600,"time":42000,"type":0},{"id":1,"sLen":0,"time":45600,"type":0},{"id":2,"sLen":0,"time":46200,"type":0},{"id":1,"sLen":0,"time":46800,"type":0},{"id":1,"sLen":0,"time":46950,"type":0},{"id":1,"sLen":0,"time":47100,"type":0},{"id":2,"sLen":0,"time":47400,"type":1},{"id":2,"sLen":450,"time":50400,"type":0},{"id":3,"sLen":300,"time":51000,"type":0},{"id":0,"sLen":450,"time":51600,"type":0},{"id":3,"sLen":1800,"time":55200,"type":0},{"id":2,"sLen":0,"time":60000,"type":0},{"id":3,"sLen":0,"time":60300,"type":0},{"id":0,"sLen":1200,"time":60600,"type":0},{"id":2,"sLen":0,"time":64500,"type":0},{"id":0,"sLen":0,"time":64800,"type":0},{"id":3,"sLen":0,"time":65100,"type":0},{"id":2,"sLen":0,"time":65700,"type":0},{"id":0,"sLen":0,"time":65775,"type":0},{"id":1,"sLen":600,"time":66000,"type":0},{"id":2,"sLen":0,"time":69600,"type":0},{"id":3,"sLen":0,"time":69900,"type":0},{"id":0,"sLen":0,"time":70200,"type":0},{"id":2,"sLen":0,"time":70500,"type":0},{"id":1,"sLen":600,"time":70800,"type":0},{"id":2,"sLen":0,"time":74100,"type":0},{"id":0,"sLen":0,"time":74400,"type":0},{"id":3,"sLen":0,"time":74700,"type":0},{"id":2,"sLen":0,"time":75300,"type":0},{"id":0,"sLen":0,"time":75375,"type":0},{"id":1,"sLen":600,"time":75600,"type":0}],"characters":["bf"],"type":1,"visible":true},{"keyCount":4,"position":"girlfriend","notes":[],"characters":["gf"],"type":2,"visible":false}],"scrollSpeed":1.3,"chartVersion":"1.6.0","stage":"stage","noteTypes":["No Anim Note"]} \ No newline at end of file diff --git a/assets/songs/bopeebo/charts/normal.json b/assets/songs/bopeebo/charts/normal.json index 3567ce5e..c70f6291 100644 --- a/assets/songs/bopeebo/charts/normal.json +++ b/assets/songs/bopeebo/charts/normal.json @@ -1 +1 @@ -{"song":{"song":"Bopeebo","bpm":100.0,"needsVoices":true,"player1":"bf","player2":"dad","speed":1.0,"notes":[{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[0.0,2,0.0],[600.0,3,600.0],[1200.0,3,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[2400.0,2,0.0],[3000.0,3,600.0],[3600.0,3,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[4800.0,1,300.0],[5400.0,0,300.0],[6000.0,3,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[7200.0,1,300.0],[7800.0,0,300.0],[8400.0,3,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[9600.0,1,300.0],[10200.0,3,0.0],[10500.0,0,0.0],[10800.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[12000.0,1,300.0],[12600.0,3,0.0],[12900.0,0,0.0],[13200.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[14400.0,3,0.0],[14700.0,1,0.0],[15300.0,0,0.0],[15600.0,2,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[16800.0,3,0.0],[17100.0,1,0.0],[17700.0,0,0.0],[18000.0,2,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[19200.0,0,0.0],[19500.0,3,0.0],[19800.0,1,900.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[21600.0,0,0.0],[21900.0,3,0.0],[22200.0,1,900.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[24000.0,1,0.0],[24300.0,3,0.0],[24600.0,0,900.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[26400.0,1,0.0],[26700.0,3,0.0],[27000.0,0,900.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[28800.0,2,0.0],[29100.0,3,0.0],[29400.0,0,1200.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[31200.0,2,0.0],[31500.0,3,0.0],[31800.0,0,1200.0],[33300.0,6,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[33600.0,0,0.0],[33900.0,3,0.0],[34500.0,2,0.0],[34800.0,1,600.0],[35700.0,6,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[36000.0,0,0.0],[36300.0,3,0.0],[36900.0,2,0.0],[37200.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[38400.0,2,450.0],[39000.0,3,300.0],[39600.0,0,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[40800.0,2,450.0],[41400.0,3,300.0],[42000.0,0,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[43200.0,1,0.0],[43800.0,2,0.0],[44400.0,1,0.0],[44550.0,1,0.0],[44700.0,1,0.0],[45000.0,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[45600.0,1,0.0],[46200.0,2,0.0],[46800.0,1,0.0],[46950.0,1,0.0],[47100.0,1,0.0],[47400.0,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[48000.0,2,450.0],[48600.0,3,300.0],[49200.0,0,450.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[50400.0,2,450.0],[51000.0,3,300.0],[51600.0,0,450.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[52800.0,3,1800.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[55200.0,3,1800.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[57600.0,2,0.0],[57900.0,3,0.0],[58200.0,0,1200.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[60000.0,2,0.0],[60300.0,3,0.0],[60600.0,0,1200.0],[62100.0,6,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[62400.0,0,0.0],[62700.0,3,0.0],[63300.0,2,0.0],[63600.0,1,600.0],[64500.0,6,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[64800.0,0,0.0],[65100.0,3,0.0],[65700.0,2,0.0],[66000.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[67200.0,2,0.0],[67500.0,3,0.0],[67800.0,0,0.0],[68100.0,2,0.0],[68400.0,1,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[69600.0,2,0.0],[69900.0,3,0.0],[70200.0,0,0.0],[70500.0,2,0.0],[70800.0,1,600.0],[71700.0,6,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[72000.0,0,0.0],[72300.0,3,0.0],[72900.0,2,0.0],[73200.0,1,600.0],[74100.0,6,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[74400.0,0,0.0],[74700.0,3,0.0],[75300.0,2,0.0],[75600.0,1,600.0]]}]},"generatedBy":"SNIFF ver.6"} \ No newline at end of file +{"events":[{"params":[1],"time":2400,"name":"Camera Movement"},{"params":[0],"time":4800,"name":"Camera Movement"},{"params":[1],"time":7200,"name":"Camera Movement"},{"params":[0],"time":9600,"name":"Camera Movement"},{"params":[1],"time":12000,"name":"Camera Movement"},{"params":[0],"time":14400,"name":"Camera Movement"},{"params":[1],"time":16800,"name":"Camera Movement"},{"params":[0],"time":19200,"name":"Camera Movement"},{"params":[1],"time":21600,"name":"Camera Movement"},{"params":[0],"time":24000,"name":"Camera Movement"},{"params":[1],"time":26400,"name":"Camera Movement"},{"params":[0],"time":28800,"name":"Camera Movement"},{"params":[1],"time":31200,"name":"Camera Movement"},{"params":[0],"time":33600,"name":"Camera Movement"},{"params":[1],"time":36000,"name":"Camera Movement"},{"params":[0],"time":38400,"name":"Camera Movement"},{"params":[1],"time":40800,"name":"Camera Movement"},{"params":[0],"time":43200,"name":"Camera Movement"},{"params":[1],"time":45600,"name":"Camera Movement"},{"params":[0],"time":48000,"name":"Camera Movement"},{"params":[1],"time":50400,"name":"Camera Movement"},{"params":[0],"time":52800,"name":"Camera Movement"},{"params":[1],"time":55200,"name":"Camera Movement"},{"params":[0],"time":57600,"name":"Camera Movement"},{"params":[1],"time":60000,"name":"Camera Movement"},{"params":[0],"time":62400,"name":"Camera Movement"},{"params":[1],"time":64800,"name":"Camera Movement"},{"params":[0],"time":67200,"name":"Camera Movement"},{"params":[1],"time":69600,"name":"Camera Movement"},{"params":[0],"time":72000,"name":"Camera Movement"},{"params":[1],"time":74400,"name":"Camera Movement"}],"strumLines":[{"visible":true,"keyCount":4,"notes":[{"id":2,"sLen":0,"time":0,"type":0},{"id":3,"sLen":600,"time":600,"type":0},{"id":3,"sLen":600,"time":1200,"type":0},{"id":1,"sLen":300,"time":4800,"type":0},{"id":0,"sLen":300,"time":5400,"type":0},{"id":3,"sLen":600,"time":6000,"type":0},{"id":1,"sLen":300,"time":9600,"type":0},{"id":3,"sLen":0,"time":10200,"type":0},{"id":0,"sLen":0,"time":10500,"type":0},{"id":1,"sLen":600,"time":10800,"type":0},{"id":3,"sLen":0,"time":14400,"type":0},{"id":1,"sLen":0,"time":14700,"type":0},{"id":0,"sLen":0,"time":15300,"type":0},{"id":2,"sLen":600,"time":15600,"type":0},{"id":0,"sLen":0,"time":19200,"type":0},{"id":3,"sLen":0,"time":19500,"type":0},{"id":1,"sLen":900,"time":19800,"type":0},{"id":1,"sLen":0,"time":24000,"type":0},{"id":3,"sLen":0,"time":24300,"type":0},{"id":0,"sLen":900,"time":24600,"type":0},{"id":2,"sLen":0,"time":28800,"type":0},{"id":3,"sLen":0,"time":29100,"type":0},{"id":0,"sLen":1200,"time":29400,"type":0},{"id":2,"sLen":0,"time":33300,"type":0},{"id":0,"sLen":0,"time":33600,"type":0},{"id":3,"sLen":0,"time":33900,"type":0},{"id":2,"sLen":0,"time":34500,"type":0},{"id":1,"sLen":600,"time":34800,"type":0},{"id":2,"sLen":450,"time":38400,"type":0},{"id":3,"sLen":300,"time":39000,"type":0},{"id":0,"sLen":600,"time":39600,"type":0},{"id":1,"sLen":0,"time":43200,"type":0},{"id":2,"sLen":0,"time":43800,"type":0},{"id":1,"sLen":0,"time":44400,"type":0},{"id":1,"sLen":0,"time":44550,"type":0},{"id":1,"sLen":0,"time":44700,"type":0},{"id":2,"sLen":0,"time":45000,"type":0},{"id":2,"sLen":450,"time":48000,"type":0},{"id":3,"sLen":300,"time":48600,"type":0},{"id":0,"sLen":450,"time":49200,"type":0},{"id":3,"sLen":1800,"time":52800,"type":0},{"id":2,"sLen":0,"time":57600,"type":0},{"id":3,"sLen":0,"time":57900,"type":0},{"id":0,"sLen":1200,"time":58200,"type":0},{"id":2,"sLen":0,"time":62100,"type":0},{"id":0,"sLen":0,"time":62400,"type":0},{"id":3,"sLen":0,"time":62700,"type":0},{"id":2,"sLen":0,"time":63300,"type":0},{"id":1,"sLen":600,"time":63600,"type":0},{"id":2,"sLen":0,"time":67200,"type":0},{"id":3,"sLen":0,"time":67500,"type":0},{"id":0,"sLen":0,"time":67800,"type":0},{"id":2,"sLen":0,"time":68100,"type":0},{"id":1,"sLen":600,"time":68400,"type":0},{"id":2,"sLen":0,"time":71700,"type":0},{"id":0,"sLen":0,"time":72000,"type":0},{"id":3,"sLen":0,"time":72300,"type":0},{"id":2,"sLen":0,"time":72900,"type":0},{"id":1,"sLen":600,"time":73200,"type":0}],"position":"dad","type":0,"characters":["dad"]},{"visible":true,"keyCount":4,"notes":[{"id":2,"sLen":0,"time":2400,"type":0},{"id":3,"sLen":600,"time":3000,"type":0},{"id":3,"sLen":600,"time":3600,"type":0},{"id":1,"sLen":300,"time":7200,"type":0},{"id":0,"sLen":300,"time":7800,"type":0},{"id":3,"sLen":600,"time":8400,"type":0},{"id":1,"sLen":300,"time":12000,"type":0},{"id":3,"sLen":0,"time":12600,"type":0},{"id":0,"sLen":0,"time":12900,"type":0},{"id":1,"sLen":600,"time":13200,"type":0},{"id":3,"sLen":0,"time":16800,"type":0},{"id":1,"sLen":0,"time":17100,"type":0},{"id":0,"sLen":0,"time":17700,"type":0},{"id":2,"sLen":600,"time":18000,"type":0},{"id":0,"sLen":0,"time":21600,"type":0},{"id":3,"sLen":0,"time":21900,"type":0},{"id":1,"sLen":900,"time":22200,"type":0},{"id":1,"sLen":0,"time":26400,"type":0},{"id":3,"sLen":0,"time":26700,"type":0},{"id":0,"sLen":900,"time":27000,"type":0},{"id":2,"sLen":0,"time":31200,"type":0},{"id":3,"sLen":0,"time":31500,"type":0},{"id":0,"sLen":1200,"time":31800,"type":0},{"id":2,"sLen":0,"time":35700,"type":0},{"id":0,"sLen":0,"time":36000,"type":0},{"id":3,"sLen":0,"time":36300,"type":0},{"id":2,"sLen":0,"time":36900,"type":0},{"id":1,"sLen":600,"time":37200,"type":0},{"id":2,"sLen":450,"time":40800,"type":0},{"id":3,"sLen":300,"time":41400,"type":0},{"id":0,"sLen":600,"time":42000,"type":0},{"id":1,"sLen":0,"time":45600,"type":0},{"id":2,"sLen":0,"time":46200,"type":0},{"id":1,"sLen":0,"time":46800,"type":0},{"id":1,"sLen":0,"time":46950,"type":0},{"id":1,"sLen":0,"time":47100,"type":0},{"id":2,"sLen":0,"time":47400,"type":1},{"id":2,"sLen":450,"time":50400,"type":0},{"id":3,"sLen":300,"time":51000,"type":0},{"id":0,"sLen":450,"time":51600,"type":0},{"id":3,"sLen":1800,"time":55200,"type":0},{"id":2,"sLen":0,"time":60000,"type":0},{"id":3,"sLen":0,"time":60300,"type":0},{"id":0,"sLen":1200,"time":60600,"type":0},{"id":2,"sLen":0,"time":64500,"type":0},{"id":0,"sLen":0,"time":64800,"type":0},{"id":3,"sLen":0,"time":65100,"type":0},{"id":2,"sLen":0,"time":65700,"type":0},{"id":1,"sLen":600,"time":66000,"type":0},{"id":2,"sLen":0,"time":69600,"type":0},{"id":3,"sLen":0,"time":69900,"type":0},{"id":0,"sLen":0,"time":70200,"type":0},{"id":2,"sLen":0,"time":70500,"type":0},{"id":1,"sLen":600,"time":70800,"type":0},{"id":2,"sLen":0,"time":74100,"type":0},{"id":0,"sLen":0,"time":74400,"type":0},{"id":3,"sLen":0,"time":74700,"type":0},{"id":2,"sLen":0,"time":75300,"type":0},{"id":1,"sLen":600,"time":75600,"type":0}],"position":"boyfriend","type":1,"characters":["bf"]},{"keyCount":4,"notes":[],"visible":false,"position":"girlfriend","type":2,"characters":["gf"]}],"codenameChart":true,"scrollSpeed":1,"chartVersion":"1.6.0","stage":"stage","noteTypes":["No Anim Note"]} \ No newline at end of file diff --git a/assets/songs/bopeebo/events.json b/assets/songs/bopeebo/events.json new file mode 100644 index 00000000..f7fcd2cf --- /dev/null +++ b/assets/songs/bopeebo/events.json @@ -0,0 +1 @@ +{"events":[{"params":[1,"hey",true],"time":4200,"name":"Play Animation"},{"params":[1,"hey",true],"name":"Play Animation","time":9000},{"params":[1,"hey",true],"time":13800,"name":"Play Animation"},{"params":[1,"hey",true],"name":"Play Animation","time":18600},{"params":[1,"hey",true],"time":23400,"name":"Play Animation"},{"params":[1,"hey",true],"name":"Play Animation","time":28200},{"params":[1,"hey",true],"time":28500,"name":"Play Animation"},{"params":[1,"hey",true],"name":"Play Animation","time":33000},{"params":[1,"hey",true],"time":37800,"name":"Play Animation"},{"params":[1,"hey",true],"name":"Play Animation","time":42600},{"params":[1,"hey",true],"time":47400,"name":"Play Animation"},{"params":[1,"hey",true],"name":"Play Animation","time":52200},{"params":[1,"hey",true],"time":57000,"name":"Play Animation"},{"params":[1,"hey",true],"name":"Play Animation","time":61800},{"params":[1,"hey",true],"time":66600,"name":"Play Animation"},{"params":[1,"hey",true],"name":"Play Animation","time":66900},{"params":[1,"hey",true],"time":71400,"name":"Play Animation"},{"params":[1,"hey",true],"name":"Play Animation","time":76200}]} \ No newline at end of file diff --git a/assets/songs/bopeebo/scripts/hey.hx b/assets/songs/bopeebo/scripts/hey.hx deleted file mode 100644 index eb3a7d04..00000000 --- a/assets/songs/bopeebo/scripts/hey.hx +++ /dev/null @@ -1,4 +0,0 @@ -function beatHit(curBeat) { - if (curBeat != 79 && curBeat % 8 == 7) - boyfriend.playAnim("hey"); -} \ No newline at end of file diff --git a/assets/songs/cocoa/charts/easy.json b/assets/songs/cocoa/charts/easy.json index 19c14a3c..1d815f45 100644 --- a/assets/songs/cocoa/charts/easy.json +++ b/assets/songs/cocoa/charts/easy.json @@ -1 +1 @@ -{"song":{"gf":"gf-christmas","stage":"mall","song":"Cocoa","bpm":100.0,"needsVoices":true,"player1":"bf-christmas","player2":"parents-christmas","speed":1.3,"notes":[{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[0.0,0,0.0],[600.0,2,0.0],[1350.0,1,0.0],[1650.0,3,0.0],[2100.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[2400.0,0,0.0],[3000.0,2,0.0],[3750.0,1,0.0],[4050.0,3,0.0],[4500.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[4800.0,0,0.0],[5400.0,2,0.0],[5700.0,0,0.0],[6300.0,2,0.0],[6600.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[7200.0,2,0.0],[7500.0,0,0.0],[8100.0,2,0.0],[8400.0,0,0.0],[9000.0,2,0.0],[9300.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[9600.0,0,0.0],[10200.0,2,0.0],[10950.0,1,0.0],[11250.0,3,0.0],[11700.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[12000.0,0,0.0],[12600.0,2,0.0],[13350.0,1,0.0],[13650.0,3,0.0],[14100.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[14400.0,0,0.0],[15000.0,2,0.0],[15300.0,0,0.0],[15900.0,2,0.0],[16200.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[16800.0,2,0.0],[17100.0,0,0.0],[17700.0,2,0.0],[18000.0,0,0.0],[18600.0,2,0.0],[18900.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[19500.0,3,150.0],[19800.0,1,450.0],[20400.0,2,1050.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[22500.0,1,0.0],[23100.0,1,0.0],[23400.0,3,0.0],[23700.0,1,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[24000.0,0,0.0],[24600.0,2,0.0],[24900.0,0,0.0],[25500.0,2,0.0],[25800.0,0,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[26400.0,2,0.0],[26700.0,0,0.0],[27300.0,2,0.0],[27600.0,0,0.0],[28200.0,2,0.0],[28500.0,1,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[28800.0,6,600.0],[29100.0,3,150.0],[29400.0,1,450.0],[30000.0,2,1050.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[32100.0,1,0.0],[32700.0,1,0.0],[33000.0,3,0.0],[33300.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[33600.0,0,0.0],[34200.0,2,0.0],[34500.0,0,0.0],[35100.0,2,0.0],[35400.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[36000.0,2,0.0],[36300.0,0,0.0],[36900.0,2,0.0],[37200.0,0,0.0],[37800.0,2,0.0],[38100.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[39000.0,0,0.0],[39450.0,3,0.0],[40050.0,2,0.0],[40500.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[40800.0,1,0.0],[41400.0,0,0.0],[41850.0,3,0.0],[42450.0,2,0.0],[42900.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[43200.0,1,0.0],[43800.0,0,0.0],[44250.0,3,0.0],[44850.0,2,0.0],[45300.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[45600.0,0,0.0],[46050.0,0,0.0],[46200.0,3,0.0],[46950.0,0,0.0],[47100.0,3,0.0],[47400.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[48600.0,0,0.0],[49050.0,3,0.0],[49650.0,2,0.0],[50100.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[50400.0,1,0.0],[51000.0,0,0.0],[51450.0,3,0.0],[52050.0,2,0.0],[52500.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[52800.0,1,0.0],[53400.0,0,0.0],[53850.0,3,0.0],[54450.0,2,0.0],[54900.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[55200.0,0,0.0],[55650.0,0,0.0],[55800.0,3,0.0],[56550.0,0,0.0],[56700.0,3,0.0],[57000.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[57750.0,0,0.0],[58200.0,2,450.0],[58800.0,3,450.0],[59400.0,2,150.0],[59700.0,0,750.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[60600.0,2,300.0],[61200.0,3,450.0],[61800.0,2,450.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[62400.0,0,0.0],[63000.0,2,0.0],[63300.0,0,0.0],[63900.0,2,0.0],[64200.0,0,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[64800.0,2,0.0],[65100.0,0,0.0],[65700.0,2,0.0],[66000.0,0,0.0],[66600.0,2,0.0],[66900.0,1,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[67350.0,0,0.0],[67800.0,2,450.0],[68400.0,3,450.0],[69000.0,2,150.0],[69300.0,0,750.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[70200.0,2,300.0],[70800.0,3,450.0],[71400.0,2,450.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[72000.0,0,0.0],[72600.0,2,0.0],[72900.0,0,0.0],[73500.0,2,0.0],[73800.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[74400.0,2,0.0],[74700.0,0,0.0],[75300.0,2,0.0],[75600.0,0,0.0],[76200.0,2,0.0],[76500.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[76800.0,0,0.0],[77400.0,2,0.0],[78150.0,1,0.0],[78450.0,3,0.0],[78900.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[79200.0,0,0.0],[79800.0,2,0.0],[80550.0,1,0.0],[80850.0,3,0.0],[81300.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[81600.0,0,0.0],[82200.0,2,0.0],[82950.0,1,0.0],[83250.0,3,0.0],[83700.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[84000.0,0,0.0],[84600.0,2,0.0],[85350.0,1,0.0],[85650.0,3,0.0],[86100.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[86400.0,0,0.0],[86700.0,3,0.0],[87300.0,3,0.0],[87600.0,0,0.0],[87900.0,3,0.0],[88200.0,3,0.0],[88500.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[88800.0,0,0.0],[89100.0,3,0.0],[89700.0,3,0.0],[90000.0,3,0.0],[90600.0,3,0.0],[90900.0,2,0.0],[91050.0,0,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[91200.0,0,0.0],[91500.0,3,0.0],[92100.0,3,0.0],[92400.0,0,0.0],[92700.0,3,0.0],[93000.0,3,0.0],[93300.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[93600.0,0,0.0],[93900.0,3,0.0],[94500.0,3,0.0],[94800.0,3,0.0],[95400.0,3,0.0],[95700.0,2,0.0],[95850.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[96000.0,0,0.0],[96300.0,3,0.0],[96900.0,3,0.0],[97200.0,0,0.0],[97500.0,3,0.0],[97800.0,3,0.0],[98100.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[98400.0,0,0.0],[98700.0,3,0.0],[99300.0,3,0.0],[99600.0,3,0.0],[100200.0,3,0.0],[100500.0,2,0.0],[100650.0,0,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[100800.0,7,600.0],[100800.0,0,0.0],[101100.0,3,0.0],[101700.0,3,0.0],[102000.0,0,0.0],[102300.0,3,0.0],[102600.0,3,0.0],[102900.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[103200.0,0,0.0],[103500.0,3,0.0],[104100.0,3,0.0],[104400.0,3,0.0],[105000.0,3,0.0],[105300.0,2,0.0],[105450.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[105600.0,3,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]}]},"generatedBy":"SNIFF ver.6"} \ No newline at end of file +{"events":[{"params":[1],"time":9600,"name":"Camera Movement"},{"params":[0],"name":"Camera Movement","time":19200},{"params":[1],"time":28800,"name":"Camera Movement"},{"params":[0],"time":38400,"name":"Camera Movement"},{"params":[1],"time":48000,"name":"Camera Movement"},{"params":[0],"time":57600,"name":"Camera Movement"},{"params":[1],"time":67200,"name":"Camera Movement"},{"params":[0],"time":76800,"name":"Camera Movement"},{"params":[1],"time":81600,"name":"Camera Movement"},{"params":[0],"time":86400,"name":"Camera Movement"},{"params":[1],"time":91200,"name":"Camera Movement"},{"params":[0],"time":96000,"name":"Camera Movement"},{"params":[1],"time":100800,"name":"Camera Movement"}],"strumLines":[{"visible":true,"keyCount":4,"notes":[{"id":0,"sLen":0,"time":0,"type":0},{"id":2,"sLen":0,"time":600,"type":0},{"id":1,"sLen":0,"time":1350,"type":0},{"id":3,"sLen":0,"time":1650,"type":0},{"id":0,"sLen":0,"time":2100,"type":0},{"id":0,"sLen":0,"time":2400,"type":0},{"id":2,"sLen":0,"time":3000,"type":0},{"id":1,"sLen":0,"time":3750,"type":0},{"id":3,"sLen":0,"time":4050,"type":0},{"id":0,"sLen":0,"time":4500,"type":0},{"id":0,"sLen":0,"time":4800,"type":0},{"id":2,"sLen":0,"time":5400,"type":0},{"id":0,"sLen":0,"time":5700,"type":0},{"id":2,"sLen":0,"time":6300,"type":0},{"id":0,"sLen":0,"time":6600,"type":0},{"id":2,"sLen":0,"time":7200,"type":0},{"id":0,"sLen":0,"time":7500,"type":0},{"id":2,"sLen":0,"time":8100,"type":0},{"id":0,"sLen":0,"time":8400,"type":0},{"id":2,"sLen":0,"time":9000,"type":0},{"id":1,"sLen":0,"time":9300,"type":0},{"id":3,"sLen":150,"time":19500,"type":0},{"id":1,"sLen":450,"time":19800,"type":0},{"id":2,"sLen":1050,"time":20400,"type":0},{"id":1,"sLen":0,"time":22500,"type":0},{"id":1,"sLen":0,"time":23100,"type":0},{"id":3,"sLen":0,"time":23400,"type":0},{"id":1,"sLen":0,"time":23700,"type":0},{"id":0,"sLen":0,"time":24000,"type":0},{"id":2,"sLen":0,"time":24600,"type":0},{"id":0,"sLen":0,"time":24900,"type":0},{"id":2,"sLen":0,"time":25500,"type":0},{"id":0,"sLen":0,"time":25800,"type":0},{"id":2,"sLen":0,"time":26400,"type":0},{"id":0,"sLen":0,"time":26700,"type":0},{"id":2,"sLen":0,"time":27300,"type":0},{"id":0,"sLen":0,"time":27600,"type":0},{"id":2,"sLen":0,"time":28200,"type":0},{"id":1,"sLen":0,"time":28500,"type":0},{"id":2,"sLen":600,"time":28800,"type":0},{"id":0,"sLen":0,"time":39000,"type":0},{"id":3,"sLen":0,"time":39450,"type":0},{"id":2,"sLen":0,"time":40050,"type":0},{"id":0,"sLen":0,"time":40500,"type":0},{"id":1,"sLen":0,"time":40800,"type":0},{"id":0,"sLen":0,"time":41400,"type":0},{"id":3,"sLen":0,"time":41850,"type":0},{"id":2,"sLen":0,"time":42450,"type":0},{"id":0,"sLen":0,"time":42900,"type":0},{"id":1,"sLen":0,"time":43200,"type":0},{"id":0,"sLen":0,"time":43800,"type":0},{"id":3,"sLen":0,"time":44250,"type":0},{"id":2,"sLen":0,"time":44850,"type":0},{"id":0,"sLen":0,"time":45300,"type":0},{"id":0,"sLen":0,"time":45600,"type":0},{"id":0,"sLen":0,"time":46050,"type":0},{"id":3,"sLen":0,"time":46200,"type":0},{"id":0,"sLen":0,"time":46950,"type":0},{"id":3,"sLen":0,"time":47100,"type":0},{"id":0,"sLen":0,"time":47400,"type":0},{"id":0,"sLen":0,"time":57750,"type":0},{"id":2,"sLen":450,"time":58200,"type":0},{"id":3,"sLen":450,"time":58800,"type":0},{"id":2,"sLen":150,"time":59400,"type":0},{"id":0,"sLen":750,"time":59700,"type":0},{"id":2,"sLen":300,"time":60600,"type":0},{"id":3,"sLen":450,"time":61200,"type":0},{"id":2,"sLen":450,"time":61800,"type":0},{"id":0,"sLen":0,"time":62400,"type":0},{"id":2,"sLen":0,"time":63000,"type":0},{"id":0,"sLen":0,"time":63300,"type":0},{"id":2,"sLen":0,"time":63900,"type":0},{"id":0,"sLen":0,"time":64200,"type":0},{"id":2,"sLen":0,"time":64800,"type":0},{"id":0,"sLen":0,"time":65100,"type":0},{"id":2,"sLen":0,"time":65700,"type":0},{"id":0,"sLen":0,"time":66000,"type":0},{"id":2,"sLen":0,"time":66600,"type":0},{"id":1,"sLen":0,"time":66900,"type":0},{"id":0,"sLen":0,"time":76800,"type":0},{"id":2,"sLen":0,"time":77400,"type":0},{"id":1,"sLen":0,"time":78150,"type":0},{"id":3,"sLen":0,"time":78450,"type":0},{"id":0,"sLen":0,"time":78900,"type":0},{"id":0,"sLen":0,"time":79200,"type":0},{"id":2,"sLen":0,"time":79800,"type":0},{"id":1,"sLen":0,"time":80550,"type":0},{"id":3,"sLen":0,"time":80850,"type":0},{"id":0,"sLen":0,"time":81300,"type":0},{"id":0,"sLen":0,"time":86400,"type":0},{"id":3,"sLen":0,"time":86700,"type":0},{"id":3,"sLen":0,"time":87300,"type":0},{"id":0,"sLen":0,"time":87600,"type":0},{"id":3,"sLen":0,"time":87900,"type":0},{"id":3,"sLen":0,"time":88200,"type":0},{"id":3,"sLen":0,"time":88500,"type":0},{"id":0,"sLen":0,"time":88800,"type":0},{"id":3,"sLen":0,"time":89100,"type":0},{"id":3,"sLen":0,"time":89700,"type":0},{"id":3,"sLen":0,"time":90000,"type":0},{"id":3,"sLen":0,"time":90600,"type":0},{"id":2,"sLen":0,"time":90900,"type":0},{"id":0,"sLen":0,"time":91050,"type":0},{"id":0,"sLen":0,"time":96000,"type":0},{"id":3,"sLen":0,"time":96300,"type":0},{"id":3,"sLen":0,"time":96900,"type":0},{"id":0,"sLen":0,"time":97200,"type":0},{"id":3,"sLen":0,"time":97500,"type":0},{"id":3,"sLen":0,"time":97800,"type":0},{"id":3,"sLen":0,"time":98100,"type":0},{"id":0,"sLen":0,"time":98400,"type":0},{"id":3,"sLen":0,"time":98700,"type":0},{"id":3,"sLen":0,"time":99300,"type":0},{"id":3,"sLen":0,"time":99600,"type":0},{"id":3,"sLen":0,"time":100200,"type":0},{"id":2,"sLen":0,"time":100500,"type":0},{"id":0,"sLen":0,"time":100650,"type":0},{"id":3,"sLen":600,"time":100800,"type":0}],"position":"dad","type":0,"characters":["parents-christmas"]},{"visible":true,"keyCount":4,"notes":[{"id":0,"sLen":0,"time":9600,"type":0},{"id":2,"sLen":0,"time":10200,"type":0},{"id":1,"sLen":0,"time":10950,"type":0},{"id":3,"sLen":0,"time":11250,"type":0},{"id":0,"sLen":0,"time":11700,"type":0},{"id":0,"sLen":0,"time":12000,"type":0},{"id":2,"sLen":0,"time":12600,"type":0},{"id":1,"sLen":0,"time":13350,"type":0},{"id":3,"sLen":0,"time":13650,"type":0},{"id":0,"sLen":0,"time":14100,"type":0},{"id":0,"sLen":0,"time":14400,"type":0},{"id":2,"sLen":0,"time":15000,"type":0},{"id":0,"sLen":0,"time":15300,"type":0},{"id":2,"sLen":0,"time":15900,"type":0},{"id":0,"sLen":0,"time":16200,"type":0},{"id":2,"sLen":0,"time":16800,"type":0},{"id":0,"sLen":0,"time":17100,"type":0},{"id":2,"sLen":0,"time":17700,"type":0},{"id":0,"sLen":0,"time":18000,"type":0},{"id":2,"sLen":0,"time":18600,"type":0},{"id":1,"sLen":0,"time":18900,"type":0},{"id":3,"sLen":150,"time":29100,"type":0},{"id":1,"sLen":450,"time":29400,"type":0},{"id":2,"sLen":1050,"time":30000,"type":0},{"id":1,"sLen":0,"time":32100,"type":0},{"id":1,"sLen":0,"time":32700,"type":0},{"id":3,"sLen":0,"time":33000,"type":0},{"id":1,"sLen":0,"time":33300,"type":0},{"id":0,"sLen":0,"time":33600,"type":0},{"id":2,"sLen":0,"time":34200,"type":0},{"id":0,"sLen":0,"time":34500,"type":0},{"id":2,"sLen":0,"time":35100,"type":0},{"id":0,"sLen":0,"time":35400,"type":0},{"id":2,"sLen":0,"time":36000,"type":0},{"id":0,"sLen":0,"time":36300,"type":0},{"id":2,"sLen":0,"time":36900,"type":0},{"id":0,"sLen":0,"time":37200,"type":0},{"id":2,"sLen":0,"time":37800,"type":0},{"id":1,"sLen":0,"time":38100,"type":0},{"id":0,"sLen":0,"time":48600,"type":0},{"id":3,"sLen":0,"time":49050,"type":0},{"id":2,"sLen":0,"time":49650,"type":0},{"id":0,"sLen":0,"time":50100,"type":0},{"id":1,"sLen":0,"time":50400,"type":0},{"id":0,"sLen":0,"time":51000,"type":0},{"id":3,"sLen":0,"time":51450,"type":0},{"id":2,"sLen":0,"time":52050,"type":0},{"id":0,"sLen":0,"time":52500,"type":0},{"id":1,"sLen":0,"time":52800,"type":0},{"id":0,"sLen":0,"time":53400,"type":0},{"id":3,"sLen":0,"time":53850,"type":0},{"id":2,"sLen":0,"time":54450,"type":0},{"id":0,"sLen":0,"time":54900,"type":0},{"id":0,"sLen":0,"time":55200,"type":0},{"id":0,"sLen":0,"time":55650,"type":0},{"id":3,"sLen":0,"time":55800,"type":0},{"id":0,"sLen":0,"time":56550,"type":0},{"id":3,"sLen":0,"time":56700,"type":0},{"id":0,"sLen":0,"time":57000,"type":0},{"id":0,"sLen":0,"time":67350,"type":0},{"id":2,"sLen":450,"time":67800,"type":0},{"id":3,"sLen":450,"time":68400,"type":0},{"id":2,"sLen":150,"time":69000,"type":0},{"id":0,"sLen":750,"time":69300,"type":0},{"id":2,"sLen":300,"time":70200,"type":0},{"id":3,"sLen":450,"time":70800,"type":0},{"id":2,"sLen":450,"time":71400,"type":0},{"id":0,"sLen":0,"time":72000,"type":0},{"id":2,"sLen":0,"time":72600,"type":0},{"id":0,"sLen":0,"time":72900,"type":0},{"id":2,"sLen":0,"time":73500,"type":0},{"id":0,"sLen":0,"time":73800,"type":0},{"id":2,"sLen":0,"time":74400,"type":0},{"id":0,"sLen":0,"time":74700,"type":0},{"id":2,"sLen":0,"time":75300,"type":0},{"id":0,"sLen":0,"time":75600,"type":0},{"id":2,"sLen":0,"time":76200,"type":0},{"id":1,"sLen":0,"time":76500,"type":0},{"id":0,"sLen":0,"time":81600,"type":0},{"id":2,"sLen":0,"time":82200,"type":0},{"id":1,"sLen":0,"time":82950,"type":0},{"id":3,"sLen":0,"time":83250,"type":0},{"id":0,"sLen":0,"time":83700,"type":0},{"id":0,"sLen":0,"time":84000,"type":0},{"id":2,"sLen":0,"time":84600,"type":0},{"id":1,"sLen":0,"time":85350,"type":0},{"id":3,"sLen":0,"time":85650,"type":0},{"id":0,"sLen":0,"time":86100,"type":0},{"id":0,"sLen":0,"time":91200,"type":0},{"id":3,"sLen":0,"time":91500,"type":0},{"id":3,"sLen":0,"time":92100,"type":0},{"id":0,"sLen":0,"time":92400,"type":0},{"id":3,"sLen":0,"time":92700,"type":0},{"id":3,"sLen":0,"time":93000,"type":0},{"id":3,"sLen":0,"time":93300,"type":0},{"id":0,"sLen":0,"time":93600,"type":0},{"id":3,"sLen":0,"time":93900,"type":0},{"id":3,"sLen":0,"time":94500,"type":0},{"id":3,"sLen":0,"time":94800,"type":0},{"id":3,"sLen":0,"time":95400,"type":0},{"id":2,"sLen":0,"time":95700,"type":0},{"id":0,"sLen":0,"time":95850,"type":0},{"id":0,"sLen":0,"time":100800,"type":0},{"id":3,"sLen":0,"time":101100,"type":0},{"id":3,"sLen":0,"time":101700,"type":0},{"id":0,"sLen":0,"time":102000,"type":0},{"id":3,"sLen":0,"time":102300,"type":0},{"id":3,"sLen":0,"time":102600,"type":0},{"id":3,"sLen":0,"time":102900,"type":0},{"id":0,"sLen":0,"time":103200,"type":0},{"id":3,"sLen":0,"time":103500,"type":0},{"id":3,"sLen":0,"time":104100,"type":0},{"id":3,"sLen":0,"time":104400,"type":0},{"id":3,"sLen":0,"time":105000,"type":0},{"id":2,"sLen":0,"time":105300,"type":0},{"id":0,"sLen":0,"time":105450,"type":0},{"id":3,"sLen":600,"time":105600,"type":0}],"position":"boyfriend","type":1,"characters":["bf-christmas"]},{"keyCount":4,"notes":[],"visible":false,"position":"girlfriend","type":2,"characters":["gf-christmas"]}],"scrollSpeed":1.3,"chartVersion":"1.6.0","stage":"mall","codenameChart":true,"noteTypes":[]} \ No newline at end of file diff --git a/assets/songs/cocoa/charts/hard.json b/assets/songs/cocoa/charts/hard.json index 2f20d63f..c085a578 100644 --- a/assets/songs/cocoa/charts/hard.json +++ b/assets/songs/cocoa/charts/hard.json @@ -1 +1 @@ -{"song":{"gf":"gf-christmas","stage":"mall","song":"Cocoa","bpm":100.0,"needsVoices":true,"player1":"bf-christmas","player2":"parents-christmas","speed":1.5,"notes":[{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[0.0,0,0.0],[600.0,2,0.0],[900.0,0,0.0],[1350.0,1,0.0],[1650.0,3,0.0],[1950.0,1,0.0],[2100.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[2400.0,0,0.0],[3000.0,2,0.0],[3300.0,0,0.0],[3750.0,1,0.0],[4050.0,3,0.0],[4350.0,1,0.0],[4500.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[4800.0,0,0.0],[4950.0,3,0.0],[5100.0,0,0.0],[5250.0,3,0.0],[5400.0,2,0.0],[5700.0,0,0.0],[5850.0,3,0.0],[6000.0,0,0.0],[6150.0,3,0.0],[6300.0,2,0.0],[6600.0,0,0.0],[6750.0,3,0.0],[6900.0,0,0.0],[7050.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[7200.0,2,0.0],[7500.0,0,0.0],[7650.0,3,0.0],[7800.0,0,0.0],[7950.0,3,0.0],[8100.0,2,0.0],[8400.0,0,0.0],[8550.0,3,0.0],[8700.0,0,0.0],[8850.0,3,0.0],[9000.0,2,0.0],[9300.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[9600.0,0,0.0],[10200.0,2,0.0],[10500.0,0,0.0],[10950.0,1,0.0],[11250.0,3,0.0],[11550.0,1,0.0],[11700.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[12000.0,0,0.0],[12600.0,2,0.0],[12900.0,0,0.0],[13350.0,1,0.0],[13650.0,3,0.0],[13950.0,1,0.0],[14100.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[14400.0,0,0.0],[14550.0,3,0.0],[14700.0,0,0.0],[14850.0,3,0.0],[15000.0,2,0.0],[15300.0,0,0.0],[15450.0,3,0.0],[15600.0,0,0.0],[15750.0,3,0.0],[15900.0,2,0.0],[16200.0,0,0.0],[16350.0,3,0.0],[16500.0,0,0.0],[16650.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[16800.0,2,0.0],[17100.0,0,0.0],[17250.0,3,0.0],[17400.0,0,0.0],[17550.0,3,0.0],[17700.0,2,0.0],[18000.0,0,0.0],[18150.0,3,0.0],[18300.0,0,0.0],[18450.0,3,0.0],[18600.0,2,0.0],[18900.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[19500.0,3,150.0],[19800.0,1,450.0],[20400.0,2,1050.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[22500.0,1,0.0],[22650.0,3,0.0],[22800.0,0,150.0],[23100.0,1,0.0],[23400.0,3,0.0],[23700.0,1,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[24000.0,0,0.0],[24150.0,3,0.0],[24300.0,0,0.0],[24450.0,3,0.0],[24600.0,2,0.0],[24900.0,0,0.0],[25050.0,3,0.0],[25200.0,0,0.0],[25350.0,3,0.0],[25500.0,2,0.0],[25800.0,0,0.0],[25950.0,3,0.0],[26100.0,0,0.0],[26250.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[26400.0,2,0.0],[26700.0,0,0.0],[26850.0,3,0.0],[27000.0,0,0.0],[27150.0,3,0.0],[27300.0,2,0.0],[27600.0,0,0.0],[27750.0,3,0.0],[27900.0,0,0.0],[28050.0,3,0.0],[28200.0,2,0.0],[28500.0,1,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[28800.0,6,600.0],[29100.0,3,150.0],[29400.0,1,450.0],[30000.0,2,1050.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[32100.0,1,0.0],[32250.0,3,0.0],[32400.0,0,150.0],[32700.0,1,0.0],[33000.0,3,0.0],[33300.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[33600.0,0,0.0],[33750.0,3,0.0],[33900.0,0,0.0],[34050.0,3,0.0],[34200.0,2,0.0],[34500.0,0,0.0],[34650.0,3,0.0],[34800.0,0,0.0],[34950.0,3,0.0],[35100.0,2,0.0],[35400.0,0,0.0],[35550.0,3,0.0],[35700.0,0,0.0],[35850.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[36000.0,2,0.0],[36300.0,0,0.0],[36450.0,3,0.0],[36600.0,0,0.0],[36750.0,3,0.0],[36900.0,2,0.0],[37200.0,0,0.0],[37350.0,3,0.0],[37500.0,0,0.0],[37650.0,3,0.0],[37800.0,2,0.0],[38100.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[39000.0,0,0.0],[39150.0,1,0.0],[39450.0,3,0.0],[39750.0,3,0.0],[40050.0,2,0.0],[40200.0,3,0.0],[40500.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[40800.0,1,0.0],[41400.0,0,0.0],[41550.0,1,0.0],[41850.0,3,0.0],[42150.0,3,0.0],[42450.0,2,0.0],[42600.0,3,0.0],[42900.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[43200.0,1,0.0],[43800.0,0,0.0],[43950.0,1,0.0],[44250.0,3,0.0],[44550.0,3,0.0],[44850.0,2,0.0],[45000.0,3,0.0],[45300.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[45600.0,0,0.0],[45750.0,3,0.0],[46050.0,0,0.0],[46200.0,3,0.0],[46500.0,0,0.0],[46650.0,3,0.0],[46950.0,0,0.0],[47100.0,3,0.0],[47400.0,0,0.0],[47550.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[48600.0,0,0.0],[48750.0,1,0.0],[49050.0,3,0.0],[49350.0,3,0.0],[49650.0,2,0.0],[49800.0,3,0.0],[50100.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[50400.0,1,0.0],[51000.0,0,0.0],[51150.0,1,0.0],[51450.0,3,0.0],[51750.0,3,0.0],[52050.0,2,0.0],[52200.0,3,0.0],[52500.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[52800.0,1,0.0],[53400.0,0,0.0],[53550.0,1,0.0],[53850.0,3,0.0],[54150.0,3,0.0],[54450.0,2,0.0],[54600.0,3,0.0],[54900.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[55200.0,0,0.0],[55350.0,3,0.0],[55650.0,0,0.0],[55800.0,3,0.0],[56100.0,0,0.0],[56250.0,3,0.0],[56550.0,0,0.0],[56700.0,3,0.0],[57000.0,0,0.0],[57150.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[57750.0,0,0.0],[57900.0,1,0.0],[58050.0,0,0.0],[58200.0,2,450.0],[58800.0,3,450.0],[59400.0,2,150.0],[59700.0,0,750.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[60600.0,2,300.0],[61200.0,3,450.0],[61800.0,2,450.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[62400.0,0,0.0],[62550.0,3,0.0],[62700.0,0,0.0],[62850.0,3,0.0],[63000.0,2,0.0],[63300.0,0,0.0],[63450.0,3,0.0],[63600.0,0,0.0],[63750.0,3,0.0],[63900.0,2,0.0],[64200.0,0,0.0],[64350.0,3,0.0],[64500.0,0,0.0],[64650.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[64800.0,2,0.0],[65100.0,0,0.0],[65250.0,3,0.0],[65400.0,0,0.0],[65550.0,3,0.0],[65700.0,2,0.0],[66000.0,0,0.0],[66150.0,3,0.0],[66300.0,0,0.0],[66450.0,3,0.0],[66600.0,2,0.0],[66900.0,1,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[67350.0,0,0.0],[67500.0,1,0.0],[67650.0,0,0.0],[67800.0,2,450.0],[68400.0,3,450.0],[69000.0,2,150.0],[69300.0,0,750.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[70200.0,2,300.0],[70800.0,3,450.0],[71400.0,2,450.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[72000.0,0,0.0],[72150.0,3,0.0],[72300.0,0,0.0],[72450.0,3,0.0],[72600.0,2,0.0],[72900.0,0,0.0],[73050.0,3,0.0],[73200.0,0,0.0],[73350.0,3,0.0],[73500.0,2,0.0],[73800.0,0,0.0],[73950.0,3,0.0],[74100.0,0,0.0],[74250.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[74400.0,2,0.0],[74700.0,0,0.0],[74850.0,3,0.0],[75000.0,0,0.0],[75150.0,3,0.0],[75300.0,2,0.0],[75600.0,0,0.0],[75750.0,3,0.0],[75900.0,0,0.0],[76050.0,3,0.0],[76200.0,2,0.0],[76500.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[76800.0,0,0.0],[77400.0,2,0.0],[77700.0,0,0.0],[78150.0,1,0.0],[78450.0,3,0.0],[78750.0,1,0.0],[78900.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[79200.0,0,0.0],[79800.0,2,0.0],[80100.0,0,0.0],[80550.0,1,0.0],[80850.0,3,0.0],[81150.0,1,0.0],[81300.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[81600.0,0,0.0],[82200.0,2,0.0],[82500.0,0,0.0],[82950.0,1,0.0],[83250.0,3,0.0],[83550.0,1,0.0],[83700.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[84000.0,0,0.0],[84600.0,2,0.0],[84900.0,0,0.0],[85350.0,1,0.0],[85650.0,3,0.0],[85950.0,1,0.0],[86100.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[86400.0,0,0.0],[86550.0,1,0.0],[86700.0,3,0.0],[86850.0,1,0.0],[87000.0,0,0.0],[87150.0,1,0.0],[87300.0,3,0.0],[87450.0,2,0.0],[87600.0,0,0.0],[87750.0,1,0.0],[87900.0,3,0.0],[88050.0,1,0.0],[88200.0,0,0.0],[88350.0,1,0.0],[88500.0,3,0.0],[88650.0,2,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[88800.0,0,0.0],[88950.0,1,0.0],[89100.0,3,0.0],[89250.0,1,0.0],[89400.0,0,0.0],[89550.0,1,0.0],[89700.0,3,0.0],[89850.0,2,0.0],[90000.0,3,0.0],[90150.0,0,0.0],[90300.0,3,0.0],[90450.0,1,0.0],[90600.0,3,0.0],[90750.0,1,0.0],[90900.0,2,0.0],[91050.0,0,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[91200.0,0,0.0],[91350.0,1,0.0],[91500.0,3,0.0],[91650.0,1,0.0],[91800.0,0,0.0],[91950.0,1,0.0],[92100.0,3,0.0],[92250.0,2,0.0],[92400.0,0,0.0],[92550.0,1,0.0],[92700.0,3,0.0],[92850.0,1,0.0],[93000.0,0,0.0],[93150.0,1,0.0],[93300.0,3,0.0],[93450.0,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[93600.0,0,0.0],[93750.0,1,0.0],[93900.0,3,0.0],[94050.0,1,0.0],[94200.0,0,0.0],[94350.0,1,0.0],[94500.0,3,0.0],[94650.0,2,0.0],[94800.0,3,0.0],[94950.0,0,0.0],[95100.0,3,0.0],[95250.0,1,0.0],[95400.0,3,0.0],[95550.0,1,0.0],[95700.0,2,0.0],[95850.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[96000.0,0,0.0],[96150.0,1,0.0],[96300.0,3,0.0],[96450.0,1,0.0],[96600.0,0,0.0],[96750.0,1,0.0],[96900.0,3,0.0],[97050.0,2,0.0],[97200.0,0,0.0],[97350.0,1,0.0],[97500.0,3,0.0],[97650.0,1,0.0],[97800.0,0,0.0],[97950.0,1,0.0],[98100.0,3,0.0],[98250.0,2,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[98400.0,0,0.0],[98550.0,1,0.0],[98700.0,3,0.0],[98850.0,1,0.0],[99000.0,0,0.0],[99150.0,1,0.0],[99300.0,3,0.0],[99450.0,2,0.0],[99600.0,3,0.0],[99750.0,0,0.0],[99900.0,3,0.0],[100050.0,1,0.0],[100200.0,3,0.0],[100350.0,1,0.0],[100500.0,2,0.0],[100650.0,0,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[100800.0,7,600.0],[100800.0,0,0.0],[100950.0,1,0.0],[101100.0,3,0.0],[101250.0,1,0.0],[101400.0,0,0.0],[101550.0,1,0.0],[101700.0,3,0.0],[101850.0,2,0.0],[102000.0,0,0.0],[102150.0,1,0.0],[102300.0,3,0.0],[102450.0,1,0.0],[102600.0,0,0.0],[102750.0,1,0.0],[102900.0,3,0.0],[103050.0,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[103200.0,0,0.0],[103350.0,1,0.0],[103500.0,3,0.0],[103650.0,1,0.0],[103800.0,0,0.0],[103950.0,1,0.0],[104100.0,3,0.0],[104250.0,2,0.0],[104400.0,3,0.0],[104550.0,0,0.0],[104700.0,3,0.0],[104850.0,1,0.0],[105000.0,3,0.0],[105150.0,1,0.0],[105300.0,2,0.0],[105450.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[105600.0,3,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]}]},"generatedBy":"SNIFF ver.6"} \ No newline at end of file +{"events":[{"params":[1],"time":9600,"name":"Camera Movement"},{"params":[0],"name":"Camera Movement","time":19200},{"params":[1],"time":28800,"name":"Camera Movement"},{"params":[0],"time":38400,"name":"Camera Movement"},{"params":[1],"time":48000,"name":"Camera Movement"},{"params":[0],"time":57600,"name":"Camera Movement"},{"params":[1],"time":67200,"name":"Camera Movement"},{"params":[0],"time":76800,"name":"Camera Movement"},{"params":[1],"time":81600,"name":"Camera Movement"},{"params":[0],"time":86400,"name":"Camera Movement"},{"params":[1],"time":91200,"name":"Camera Movement"},{"params":[0],"time":96000,"name":"Camera Movement"},{"params":[1],"time":100800,"name":"Camera Movement"}],"strumLines":[{"visible":true,"keyCount":4,"notes":[{"id":0,"sLen":0,"time":0,"type":0},{"id":2,"sLen":0,"time":600,"type":0},{"id":0,"sLen":0,"time":900,"type":0},{"id":1,"sLen":0,"time":1350,"type":0},{"id":3,"sLen":0,"time":1650,"type":0},{"id":1,"sLen":0,"time":1950,"type":0},{"id":0,"sLen":0,"time":2100,"type":0},{"id":0,"sLen":0,"time":2400,"type":0},{"id":2,"sLen":0,"time":3000,"type":0},{"id":0,"sLen":0,"time":3300,"type":0},{"id":1,"sLen":0,"time":3750,"type":0},{"id":3,"sLen":0,"time":4050,"type":0},{"id":1,"sLen":0,"time":4350,"type":0},{"id":0,"sLen":0,"time":4500,"type":0},{"id":0,"sLen":0,"time":4800,"type":0},{"id":3,"sLen":0,"time":4950,"type":0},{"id":0,"sLen":0,"time":5100,"type":0},{"id":3,"sLen":0,"time":5250,"type":0},{"id":2,"sLen":0,"time":5400,"type":0},{"id":0,"sLen":0,"time":5700,"type":0},{"id":3,"sLen":0,"time":5850,"type":0},{"id":0,"sLen":0,"time":6000,"type":0},{"id":3,"sLen":0,"time":6150,"type":0},{"id":2,"sLen":0,"time":6300,"type":0},{"id":0,"sLen":0,"time":6600,"type":0},{"id":3,"sLen":0,"time":6750,"type":0},{"id":0,"sLen":0,"time":6900,"type":0},{"id":3,"sLen":0,"time":7050,"type":0},{"id":2,"sLen":0,"time":7200,"type":0},{"id":0,"sLen":0,"time":7500,"type":0},{"id":3,"sLen":0,"time":7650,"type":0},{"id":0,"sLen":0,"time":7800,"type":0},{"id":3,"sLen":0,"time":7950,"type":0},{"id":2,"sLen":0,"time":8100,"type":0},{"id":0,"sLen":0,"time":8400,"type":0},{"id":3,"sLen":0,"time":8550,"type":0},{"id":0,"sLen":0,"time":8700,"type":0},{"id":3,"sLen":0,"time":8850,"type":0},{"id":2,"sLen":0,"time":9000,"type":0},{"id":1,"sLen":0,"time":9300,"type":0},{"id":3,"sLen":150,"time":19500,"type":0},{"id":1,"sLen":450,"time":19800,"type":0},{"id":2,"sLen":1050,"time":20400,"type":0},{"id":1,"sLen":0,"time":22500,"type":0},{"id":3,"sLen":0,"time":22650,"type":0},{"id":0,"sLen":150,"time":22800,"type":0},{"id":1,"sLen":0,"time":23100,"type":0},{"id":3,"sLen":0,"time":23400,"type":0},{"id":1,"sLen":0,"time":23700,"type":0},{"id":0,"sLen":0,"time":24000,"type":0},{"id":3,"sLen":0,"time":24150,"type":0},{"id":0,"sLen":0,"time":24300,"type":0},{"id":3,"sLen":0,"time":24450,"type":0},{"id":2,"sLen":0,"time":24600,"type":0},{"id":0,"sLen":0,"time":24900,"type":0},{"id":3,"sLen":0,"time":25050,"type":0},{"id":0,"sLen":0,"time":25200,"type":0},{"id":3,"sLen":0,"time":25350,"type":0},{"id":2,"sLen":0,"time":25500,"type":0},{"id":0,"sLen":0,"time":25800,"type":0},{"id":3,"sLen":0,"time":25950,"type":0},{"id":0,"sLen":0,"time":26100,"type":0},{"id":3,"sLen":0,"time":26250,"type":0},{"id":2,"sLen":0,"time":26400,"type":0},{"id":0,"sLen":0,"time":26700,"type":0},{"id":3,"sLen":0,"time":26850,"type":0},{"id":0,"sLen":0,"time":27000,"type":0},{"id":3,"sLen":0,"time":27150,"type":0},{"id":2,"sLen":0,"time":27300,"type":0},{"id":0,"sLen":0,"time":27600,"type":0},{"id":3,"sLen":0,"time":27750,"type":0},{"id":0,"sLen":0,"time":27900,"type":0},{"id":3,"sLen":0,"time":28050,"type":0},{"id":2,"sLen":0,"time":28200,"type":0},{"id":1,"sLen":0,"time":28500,"type":0},{"id":2,"sLen":600,"time":28800,"type":0},{"id":0,"sLen":0,"time":39000,"type":0},{"id":1,"sLen":0,"time":39150,"type":0},{"id":3,"sLen":0,"time":39450,"type":0},{"id":3,"sLen":0,"time":39750,"type":0},{"id":2,"sLen":0,"time":40050,"type":0},{"id":3,"sLen":0,"time":40200,"type":0},{"id":0,"sLen":0,"time":40500,"type":0},{"id":1,"sLen":0,"time":40800,"type":0},{"id":0,"sLen":0,"time":41400,"type":0},{"id":1,"sLen":0,"time":41550,"type":0},{"id":3,"sLen":0,"time":41850,"type":0},{"id":3,"sLen":0,"time":42150,"type":0},{"id":2,"sLen":0,"time":42450,"type":0},{"id":3,"sLen":0,"time":42600,"type":0},{"id":0,"sLen":0,"time":42900,"type":0},{"id":1,"sLen":0,"time":43200,"type":0},{"id":0,"sLen":0,"time":43800,"type":0},{"id":1,"sLen":0,"time":43950,"type":0},{"id":3,"sLen":0,"time":44250,"type":0},{"id":3,"sLen":0,"time":44550,"type":0},{"id":2,"sLen":0,"time":44850,"type":0},{"id":3,"sLen":0,"time":45000,"type":0},{"id":0,"sLen":0,"time":45300,"type":0},{"id":0,"sLen":0,"time":45600,"type":0},{"id":3,"sLen":0,"time":45750,"type":0},{"id":0,"sLen":0,"time":46050,"type":0},{"id":3,"sLen":0,"time":46200,"type":0},{"id":0,"sLen":0,"time":46500,"type":0},{"id":3,"sLen":0,"time":46650,"type":0},{"id":0,"sLen":0,"time":46950,"type":0},{"id":3,"sLen":0,"time":47100,"type":0},{"id":0,"sLen":0,"time":47400,"type":0},{"id":3,"sLen":0,"time":47550,"type":0},{"id":0,"sLen":0,"time":57750,"type":0},{"id":1,"sLen":0,"time":57900,"type":0},{"id":0,"sLen":0,"time":58050,"type":0},{"id":2,"sLen":450,"time":58200,"type":0},{"id":3,"sLen":450,"time":58800,"type":0},{"id":2,"sLen":150,"time":59400,"type":0},{"id":0,"sLen":750,"time":59700,"type":0},{"id":2,"sLen":300,"time":60600,"type":0},{"id":3,"sLen":450,"time":61200,"type":0},{"id":2,"sLen":450,"time":61800,"type":0},{"id":0,"sLen":0,"time":62400,"type":0},{"id":3,"sLen":0,"time":62550,"type":0},{"id":0,"sLen":0,"time":62700,"type":0},{"id":3,"sLen":0,"time":62850,"type":0},{"id":2,"sLen":0,"time":63000,"type":0},{"id":0,"sLen":0,"time":63300,"type":0},{"id":3,"sLen":0,"time":63450,"type":0},{"id":0,"sLen":0,"time":63600,"type":0},{"id":3,"sLen":0,"time":63750,"type":0},{"id":2,"sLen":0,"time":63900,"type":0},{"id":0,"sLen":0,"time":64200,"type":0},{"id":3,"sLen":0,"time":64350,"type":0},{"id":0,"sLen":0,"time":64500,"type":0},{"id":3,"sLen":0,"time":64650,"type":0},{"id":2,"sLen":0,"time":64800,"type":0},{"id":0,"sLen":0,"time":65100,"type":0},{"id":3,"sLen":0,"time":65250,"type":0},{"id":0,"sLen":0,"time":65400,"type":0},{"id":3,"sLen":0,"time":65550,"type":0},{"id":2,"sLen":0,"time":65700,"type":0},{"id":0,"sLen":0,"time":66000,"type":0},{"id":3,"sLen":0,"time":66150,"type":0},{"id":0,"sLen":0,"time":66300,"type":0},{"id":3,"sLen":0,"time":66450,"type":0},{"id":2,"sLen":0,"time":66600,"type":0},{"id":1,"sLen":0,"time":66900,"type":0},{"id":0,"sLen":0,"time":76800,"type":0},{"id":2,"sLen":0,"time":77400,"type":0},{"id":0,"sLen":0,"time":77700,"type":0},{"id":1,"sLen":0,"time":78150,"type":0},{"id":3,"sLen":0,"time":78450,"type":0},{"id":1,"sLen":0,"time":78750,"type":0},{"id":0,"sLen":0,"time":78900,"type":0},{"id":0,"sLen":0,"time":79200,"type":0},{"id":2,"sLen":0,"time":79800,"type":0},{"id":0,"sLen":0,"time":80100,"type":0},{"id":1,"sLen":0,"time":80550,"type":0},{"id":3,"sLen":0,"time":80850,"type":0},{"id":1,"sLen":0,"time":81150,"type":0},{"id":0,"sLen":0,"time":81300,"type":0},{"id":0,"sLen":0,"time":86400,"type":0},{"id":1,"sLen":0,"time":86550,"type":0},{"id":3,"sLen":0,"time":86700,"type":0},{"id":1,"sLen":0,"time":86850,"type":0},{"id":0,"sLen":0,"time":87000,"type":0},{"id":1,"sLen":0,"time":87150,"type":0},{"id":3,"sLen":0,"time":87300,"type":0},{"id":2,"sLen":0,"time":87450,"type":0},{"id":0,"sLen":0,"time":87600,"type":0},{"id":1,"sLen":0,"time":87750,"type":0},{"id":3,"sLen":0,"time":87900,"type":0},{"id":1,"sLen":0,"time":88050,"type":0},{"id":0,"sLen":0,"time":88200,"type":0},{"id":1,"sLen":0,"time":88350,"type":0},{"id":3,"sLen":0,"time":88500,"type":0},{"id":2,"sLen":0,"time":88650,"type":0},{"id":0,"sLen":0,"time":88800,"type":0},{"id":1,"sLen":0,"time":88950,"type":0},{"id":3,"sLen":0,"time":89100,"type":0},{"id":1,"sLen":0,"time":89250,"type":0},{"id":0,"sLen":0,"time":89400,"type":0},{"id":1,"sLen":0,"time":89550,"type":0},{"id":3,"sLen":0,"time":89700,"type":0},{"id":2,"sLen":0,"time":89850,"type":0},{"id":3,"sLen":0,"time":90000,"type":0},{"id":0,"sLen":0,"time":90150,"type":0},{"id":3,"sLen":0,"time":90300,"type":0},{"id":1,"sLen":0,"time":90450,"type":0},{"id":3,"sLen":0,"time":90600,"type":0},{"id":1,"sLen":0,"time":90750,"type":0},{"id":2,"sLen":0,"time":90900,"type":0},{"id":0,"sLen":0,"time":91050,"type":0},{"id":0,"sLen":0,"time":96000,"type":0},{"id":1,"sLen":0,"time":96150,"type":0},{"id":3,"sLen":0,"time":96300,"type":0},{"id":1,"sLen":0,"time":96450,"type":0},{"id":0,"sLen":0,"time":96600,"type":0},{"id":1,"sLen":0,"time":96750,"type":0},{"id":3,"sLen":0,"time":96900,"type":0},{"id":2,"sLen":0,"time":97050,"type":0},{"id":0,"sLen":0,"time":97200,"type":0},{"id":1,"sLen":0,"time":97350,"type":0},{"id":3,"sLen":0,"time":97500,"type":0},{"id":1,"sLen":0,"time":97650,"type":0},{"id":0,"sLen":0,"time":97800,"type":0},{"id":1,"sLen":0,"time":97950,"type":0},{"id":3,"sLen":0,"time":98100,"type":0},{"id":2,"sLen":0,"time":98250,"type":0},{"id":0,"sLen":0,"time":98400,"type":0},{"id":1,"sLen":0,"time":98550,"type":0},{"id":3,"sLen":0,"time":98700,"type":0},{"id":1,"sLen":0,"time":98850,"type":0},{"id":0,"sLen":0,"time":99000,"type":0},{"id":1,"sLen":0,"time":99150,"type":0},{"id":3,"sLen":0,"time":99300,"type":0},{"id":2,"sLen":0,"time":99450,"type":0},{"id":3,"sLen":0,"time":99600,"type":0},{"id":0,"sLen":0,"time":99750,"type":0},{"id":3,"sLen":0,"time":99900,"type":0},{"id":1,"sLen":0,"time":100050,"type":0},{"id":3,"sLen":0,"time":100200,"type":0},{"id":1,"sLen":0,"time":100350,"type":0},{"id":2,"sLen":0,"time":100500,"type":0},{"id":0,"sLen":0,"time":100650,"type":0},{"id":3,"sLen":600,"time":100800,"type":0}],"position":"dad","type":0,"characters":["parents-christmas"]},{"visible":true,"keyCount":4,"notes":[{"id":0,"sLen":0,"time":9600,"type":0},{"id":2,"sLen":0,"time":10200,"type":0},{"id":0,"sLen":0,"time":10500,"type":0},{"id":1,"sLen":0,"time":10950,"type":0},{"id":3,"sLen":0,"time":11250,"type":0},{"id":1,"sLen":0,"time":11550,"type":0},{"id":0,"sLen":0,"time":11700,"type":0},{"id":0,"sLen":0,"time":12000,"type":0},{"id":2,"sLen":0,"time":12600,"type":0},{"id":0,"sLen":0,"time":12900,"type":0},{"id":1,"sLen":0,"time":13350,"type":0},{"id":3,"sLen":0,"time":13650,"type":0},{"id":1,"sLen":0,"time":13950,"type":0},{"id":0,"sLen":0,"time":14100,"type":0},{"id":0,"sLen":0,"time":14400,"type":0},{"id":3,"sLen":0,"time":14550,"type":0},{"id":0,"sLen":0,"time":14700,"type":0},{"id":3,"sLen":0,"time":14850,"type":0},{"id":2,"sLen":0,"time":15000,"type":0},{"id":0,"sLen":0,"time":15300,"type":0},{"id":3,"sLen":0,"time":15450,"type":0},{"id":0,"sLen":0,"time":15600,"type":0},{"id":3,"sLen":0,"time":15750,"type":0},{"id":2,"sLen":0,"time":15900,"type":0},{"id":0,"sLen":0,"time":16200,"type":0},{"id":3,"sLen":0,"time":16350,"type":0},{"id":0,"sLen":0,"time":16500,"type":0},{"id":3,"sLen":0,"time":16650,"type":0},{"id":2,"sLen":0,"time":16800,"type":0},{"id":0,"sLen":0,"time":17100,"type":0},{"id":3,"sLen":0,"time":17250,"type":0},{"id":0,"sLen":0,"time":17400,"type":0},{"id":3,"sLen":0,"time":17550,"type":0},{"id":2,"sLen":0,"time":17700,"type":0},{"id":0,"sLen":0,"time":18000,"type":0},{"id":3,"sLen":0,"time":18150,"type":0},{"id":0,"sLen":0,"time":18300,"type":0},{"id":3,"sLen":0,"time":18450,"type":0},{"id":2,"sLen":0,"time":18600,"type":0},{"id":1,"sLen":0,"time":18900,"type":0},{"id":3,"sLen":150,"time":29100,"type":0},{"id":1,"sLen":450,"time":29400,"type":0},{"id":2,"sLen":1050,"time":30000,"type":0},{"id":1,"sLen":0,"time":32100,"type":0},{"id":3,"sLen":0,"time":32250,"type":0},{"id":0,"sLen":150,"time":32400,"type":0},{"id":1,"sLen":0,"time":32700,"type":0},{"id":3,"sLen":0,"time":33000,"type":0},{"id":1,"sLen":0,"time":33300,"type":0},{"id":0,"sLen":0,"time":33600,"type":0},{"id":3,"sLen":0,"time":33750,"type":0},{"id":0,"sLen":0,"time":33900,"type":0},{"id":3,"sLen":0,"time":34050,"type":0},{"id":2,"sLen":0,"time":34200,"type":0},{"id":0,"sLen":0,"time":34500,"type":0},{"id":3,"sLen":0,"time":34650,"type":0},{"id":0,"sLen":0,"time":34800,"type":0},{"id":3,"sLen":0,"time":34950,"type":0},{"id":2,"sLen":0,"time":35100,"type":0},{"id":0,"sLen":0,"time":35400,"type":0},{"id":3,"sLen":0,"time":35550,"type":0},{"id":0,"sLen":0,"time":35700,"type":0},{"id":3,"sLen":0,"time":35850,"type":0},{"id":2,"sLen":0,"time":36000,"type":0},{"id":0,"sLen":0,"time":36300,"type":0},{"id":3,"sLen":0,"time":36450,"type":0},{"id":0,"sLen":0,"time":36600,"type":0},{"id":3,"sLen":0,"time":36750,"type":0},{"id":2,"sLen":0,"time":36900,"type":0},{"id":0,"sLen":0,"time":37200,"type":0},{"id":3,"sLen":0,"time":37350,"type":0},{"id":0,"sLen":0,"time":37500,"type":0},{"id":3,"sLen":0,"time":37650,"type":0},{"id":2,"sLen":0,"time":37800,"type":0},{"id":1,"sLen":0,"time":38100,"type":0},{"id":0,"sLen":0,"time":48600,"type":0},{"id":1,"sLen":0,"time":48750,"type":0},{"id":3,"sLen":0,"time":49050,"type":0},{"id":3,"sLen":0,"time":49350,"type":0},{"id":2,"sLen":0,"time":49650,"type":0},{"id":3,"sLen":0,"time":49800,"type":0},{"id":0,"sLen":0,"time":50100,"type":0},{"id":1,"sLen":0,"time":50400,"type":0},{"id":0,"sLen":0,"time":51000,"type":0},{"id":1,"sLen":0,"time":51150,"type":0},{"id":3,"sLen":0,"time":51450,"type":0},{"id":3,"sLen":0,"time":51750,"type":0},{"id":2,"sLen":0,"time":52050,"type":0},{"id":3,"sLen":0,"time":52200,"type":0},{"id":0,"sLen":0,"time":52500,"type":0},{"id":1,"sLen":0,"time":52800,"type":0},{"id":0,"sLen":0,"time":53400,"type":0},{"id":1,"sLen":0,"time":53550,"type":0},{"id":3,"sLen":0,"time":53850,"type":0},{"id":3,"sLen":0,"time":54150,"type":0},{"id":2,"sLen":0,"time":54450,"type":0},{"id":3,"sLen":0,"time":54600,"type":0},{"id":0,"sLen":0,"time":54900,"type":0},{"id":0,"sLen":0,"time":55200,"type":0},{"id":3,"sLen":0,"time":55350,"type":0},{"id":0,"sLen":0,"time":55650,"type":0},{"id":3,"sLen":0,"time":55800,"type":0},{"id":0,"sLen":0,"time":56100,"type":0},{"id":3,"sLen":0,"time":56250,"type":0},{"id":0,"sLen":0,"time":56550,"type":0},{"id":3,"sLen":0,"time":56700,"type":0},{"id":0,"sLen":0,"time":57000,"type":0},{"id":3,"sLen":0,"time":57150,"type":0},{"id":0,"sLen":0,"time":67350,"type":0},{"id":1,"sLen":0,"time":67500,"type":0},{"id":0,"sLen":0,"time":67650,"type":0},{"id":2,"sLen":450,"time":67800,"type":0},{"id":3,"sLen":450,"time":68400,"type":0},{"id":2,"sLen":150,"time":69000,"type":0},{"id":0,"sLen":750,"time":69300,"type":0},{"id":2,"sLen":300,"time":70200,"type":0},{"id":3,"sLen":450,"time":70800,"type":0},{"id":2,"sLen":450,"time":71400,"type":0},{"id":0,"sLen":0,"time":72000,"type":0},{"id":3,"sLen":0,"time":72150,"type":0},{"id":0,"sLen":0,"time":72300,"type":0},{"id":3,"sLen":0,"time":72450,"type":0},{"id":2,"sLen":0,"time":72600,"type":0},{"id":0,"sLen":0,"time":72900,"type":0},{"id":3,"sLen":0,"time":73050,"type":0},{"id":0,"sLen":0,"time":73200,"type":0},{"id":3,"sLen":0,"time":73350,"type":0},{"id":2,"sLen":0,"time":73500,"type":0},{"id":0,"sLen":0,"time":73800,"type":0},{"id":3,"sLen":0,"time":73950,"type":0},{"id":0,"sLen":0,"time":74100,"type":0},{"id":3,"sLen":0,"time":74250,"type":0},{"id":2,"sLen":0,"time":74400,"type":0},{"id":0,"sLen":0,"time":74700,"type":0},{"id":3,"sLen":0,"time":74850,"type":0},{"id":0,"sLen":0,"time":75000,"type":0},{"id":3,"sLen":0,"time":75150,"type":0},{"id":2,"sLen":0,"time":75300,"type":0},{"id":0,"sLen":0,"time":75600,"type":0},{"id":3,"sLen":0,"time":75750,"type":0},{"id":0,"sLen":0,"time":75900,"type":0},{"id":3,"sLen":0,"time":76050,"type":0},{"id":2,"sLen":0,"time":76200,"type":0},{"id":1,"sLen":0,"time":76500,"type":0},{"id":0,"sLen":0,"time":81600,"type":0},{"id":2,"sLen":0,"time":82200,"type":0},{"id":0,"sLen":0,"time":82500,"type":0},{"id":1,"sLen":0,"time":82950,"type":0},{"id":3,"sLen":0,"time":83250,"type":0},{"id":1,"sLen":0,"time":83550,"type":0},{"id":0,"sLen":0,"time":83700,"type":0},{"id":0,"sLen":0,"time":84000,"type":0},{"id":2,"sLen":0,"time":84600,"type":0},{"id":0,"sLen":0,"time":84900,"type":0},{"id":1,"sLen":0,"time":85350,"type":0},{"id":3,"sLen":0,"time":85650,"type":0},{"id":1,"sLen":0,"time":85950,"type":0},{"id":0,"sLen":0,"time":86100,"type":0},{"id":0,"sLen":0,"time":91200,"type":0},{"id":1,"sLen":0,"time":91350,"type":0},{"id":3,"sLen":0,"time":91500,"type":0},{"id":1,"sLen":0,"time":91650,"type":0},{"id":0,"sLen":0,"time":91800,"type":0},{"id":1,"sLen":0,"time":91950,"type":0},{"id":3,"sLen":0,"time":92100,"type":0},{"id":2,"sLen":0,"time":92250,"type":0},{"id":0,"sLen":0,"time":92400,"type":0},{"id":1,"sLen":0,"time":92550,"type":0},{"id":3,"sLen":0,"time":92700,"type":0},{"id":1,"sLen":0,"time":92850,"type":0},{"id":0,"sLen":0,"time":93000,"type":0},{"id":1,"sLen":0,"time":93150,"type":0},{"id":3,"sLen":0,"time":93300,"type":0},{"id":2,"sLen":0,"time":93450,"type":0},{"id":0,"sLen":0,"time":93600,"type":0},{"id":1,"sLen":0,"time":93750,"type":0},{"id":3,"sLen":0,"time":93900,"type":0},{"id":1,"sLen":0,"time":94050,"type":0},{"id":0,"sLen":0,"time":94200,"type":0},{"id":1,"sLen":0,"time":94350,"type":0},{"id":3,"sLen":0,"time":94500,"type":0},{"id":2,"sLen":0,"time":94650,"type":0},{"id":3,"sLen":0,"time":94800,"type":0},{"id":0,"sLen":0,"time":94950,"type":0},{"id":3,"sLen":0,"time":95100,"type":0},{"id":1,"sLen":0,"time":95250,"type":0},{"id":3,"sLen":0,"time":95400,"type":0},{"id":1,"sLen":0,"time":95550,"type":0},{"id":2,"sLen":0,"time":95700,"type":0},{"id":0,"sLen":0,"time":95850,"type":0},{"id":0,"sLen":0,"time":100800,"type":0},{"id":1,"sLen":0,"time":100950,"type":0},{"id":3,"sLen":0,"time":101100,"type":0},{"id":1,"sLen":0,"time":101250,"type":0},{"id":0,"sLen":0,"time":101400,"type":0},{"id":1,"sLen":0,"time":101550,"type":0},{"id":3,"sLen":0,"time":101700,"type":0},{"id":2,"sLen":0,"time":101850,"type":0},{"id":0,"sLen":0,"time":102000,"type":0},{"id":1,"sLen":0,"time":102150,"type":0},{"id":3,"sLen":0,"time":102300,"type":0},{"id":1,"sLen":0,"time":102450,"type":0},{"id":0,"sLen":0,"time":102600,"type":0},{"id":1,"sLen":0,"time":102750,"type":0},{"id":3,"sLen":0,"time":102900,"type":0},{"id":2,"sLen":0,"time":103050,"type":0},{"id":0,"sLen":0,"time":103200,"type":0},{"id":1,"sLen":0,"time":103350,"type":0},{"id":3,"sLen":0,"time":103500,"type":0},{"id":1,"sLen":0,"time":103650,"type":0},{"id":0,"sLen":0,"time":103800,"type":0},{"id":1,"sLen":0,"time":103950,"type":0},{"id":3,"sLen":0,"time":104100,"type":0},{"id":2,"sLen":0,"time":104250,"type":0},{"id":3,"sLen":0,"time":104400,"type":0},{"id":0,"sLen":0,"time":104550,"type":0},{"id":3,"sLen":0,"time":104700,"type":0},{"id":1,"sLen":0,"time":104850,"type":0},{"id":3,"sLen":0,"time":105000,"type":0},{"id":1,"sLen":0,"time":105150,"type":0},{"id":2,"sLen":0,"time":105300,"type":0},{"id":0,"sLen":0,"time":105450,"type":0},{"id":3,"sLen":600,"time":105600,"type":0}],"position":"boyfriend","type":1,"characters":["bf-christmas"]},{"keyCount":4,"notes":[],"visible":false,"position":"girlfriend","type":2,"characters":["gf-christmas"]}],"codenameChart":true,"scrollSpeed":1.5,"chartVersion":"1.6.0","stage":"mall","noteTypes":[]} \ No newline at end of file diff --git a/assets/songs/cocoa/charts/normal.json b/assets/songs/cocoa/charts/normal.json index 622b90e5..292f47a7 100644 --- a/assets/songs/cocoa/charts/normal.json +++ b/assets/songs/cocoa/charts/normal.json @@ -1 +1 @@ -{"song":{"gf":"gf-christmas","stage":"mall","song":"Cocoa","bpm":100.0,"needsVoices":true,"player1":"bf-christmas","player2":"parents-christmas","speed":1.3,"notes":[{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[0.0,0,0.0],[600.0,2,0.0],[900.0,0,0.0],[1350.0,1,0.0],[1650.0,3,0.0],[2100.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[2400.0,0,0.0],[3000.0,2,0.0],[3300.0,0,0.0],[3750.0,1,0.0],[4050.0,3,0.0],[4500.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[4800.0,0,0.0],[5100.0,0,0.0],[5400.0,2,0.0],[5700.0,0,0.0],[6000.0,0,0.0],[6300.0,2,0.0],[6600.0,0,0.0],[6750.0,3,0.0],[6900.0,0,0.0],[7050.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[7200.0,2,0.0],[7500.0,0,0.0],[7800.0,0,0.0],[8100.0,2,0.0],[8400.0,0,0.0],[8700.0,0,0.0],[9000.0,2,0.0],[9300.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[9600.0,0,0.0],[10200.0,2,0.0],[10500.0,0,0.0],[10950.0,1,0.0],[11250.0,3,0.0],[11700.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[12000.0,0,0.0],[12600.0,2,0.0],[12900.0,0,0.0],[13350.0,1,0.0],[13650.0,3,0.0],[14100.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[14400.0,0,0.0],[14700.0,0,0.0],[15000.0,2,0.0],[15300.0,0,0.0],[15600.0,0,0.0],[15900.0,2,0.0],[16200.0,0,0.0],[16350.0,3,0.0],[16500.0,0,0.0],[16650.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[16800.0,2,0.0],[17100.0,0,0.0],[17400.0,0,0.0],[17700.0,2,0.0],[18000.0,0,0.0],[18300.0,0,0.0],[18600.0,2,0.0],[18900.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[19500.0,3,150.0],[19800.0,1,450.0],[20400.0,2,1050.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[22500.0,1,0.0],[22800.0,0,150.0],[23100.0,1,0.0],[23400.0,3,0.0],[23700.0,1,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[24000.0,0,0.0],[24300.0,0,0.0],[24600.0,2,0.0],[24900.0,0,0.0],[25200.0,0,0.0],[25500.0,2,0.0],[25800.0,0,0.0],[26100.0,0,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[26400.0,2,0.0],[26700.0,0,0.0],[27000.0,0,0.0],[27300.0,2,0.0],[27600.0,0,0.0],[27900.0,0,0.0],[28200.0,2,0.0],[28500.0,1,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[28800.0,6,600.0],[29100.0,3,150.0],[29400.0,1,450.0],[30000.0,2,1050.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[32100.0,1,0.0],[32400.0,0,150.0],[32700.0,1,0.0],[33000.0,3,0.0],[33300.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[33600.0,0,0.0],[33900.0,0,0.0],[34200.0,2,0.0],[34500.0,0,0.0],[34800.0,0,0.0],[35100.0,2,0.0],[35400.0,0,0.0],[35700.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[36000.0,2,0.0],[36300.0,0,0.0],[36600.0,0,0.0],[36900.0,2,0.0],[37200.0,0,0.0],[37500.0,0,0.0],[37800.0,2,0.0],[38100.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[39000.0,0,0.0],[39450.0,3,0.0],[39750.0,3,0.0],[40050.0,2,0.0],[40500.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[40800.0,1,0.0],[41400.0,0,0.0],[41850.0,3,0.0],[42150.0,3,0.0],[42450.0,2,0.0],[42900.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[43200.0,1,0.0],[43800.0,0,0.0],[44250.0,3,0.0],[44550.0,3,0.0],[44850.0,2,0.0],[45300.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[45600.0,0,0.0],[46050.0,0,0.0],[46200.0,3,0.0],[46500.0,0,0.0],[46950.0,0,0.0],[47100.0,3,0.0],[47400.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[48600.0,0,0.0],[49050.0,3,0.0],[49350.0,3,0.0],[49650.0,2,0.0],[50100.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[50400.0,1,0.0],[51000.0,0,0.0],[51450.0,3,0.0],[51750.0,3,0.0],[52050.0,2,0.0],[52500.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[52800.0,1,0.0],[53400.0,0,0.0],[53850.0,3,0.0],[54150.0,3,0.0],[54450.0,2,0.0],[54900.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[55200.0,0,0.0],[55650.0,0,0.0],[55800.0,3,0.0],[56100.0,0,0.0],[56550.0,0,0.0],[56700.0,3,0.0],[57000.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[57750.0,0,0.0],[58200.0,2,450.0],[58800.0,3,450.0],[59400.0,2,150.0],[59700.0,0,750.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[60600.0,2,300.0],[61200.0,3,450.0],[61800.0,2,450.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[62400.0,0,0.0],[62700.0,0,0.0],[63000.0,2,0.0],[63300.0,0,0.0],[63600.0,0,0.0],[63900.0,2,0.0],[64200.0,0,0.0],[64500.0,0,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[64800.0,2,0.0],[65100.0,0,0.0],[65400.0,0,0.0],[65700.0,2,0.0],[66000.0,0,0.0],[66300.0,0,0.0],[66600.0,2,0.0],[66900.0,1,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[67350.0,0,0.0],[67800.0,2,450.0],[68400.0,3,450.0],[69000.0,2,150.0],[69300.0,0,750.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[70200.0,2,300.0],[70800.0,3,450.0],[71400.0,2,450.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[72000.0,0,0.0],[72300.0,0,0.0],[72600.0,2,0.0],[72900.0,0,0.0],[73200.0,0,0.0],[73500.0,2,0.0],[73800.0,0,0.0],[74100.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[74400.0,2,0.0],[74700.0,0,0.0],[75000.0,0,0.0],[75300.0,2,0.0],[75600.0,0,0.0],[75900.0,0,0.0],[76200.0,2,0.0],[76500.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[76800.0,0,0.0],[77400.0,2,0.0],[77700.0,0,0.0],[78150.0,1,0.0],[78450.0,3,0.0],[78900.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[79200.0,0,0.0],[79800.0,2,0.0],[80100.0,0,0.0],[80550.0,1,0.0],[80850.0,3,0.0],[81300.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[81600.0,0,0.0],[82200.0,2,0.0],[82500.0,0,0.0],[82950.0,1,0.0],[83250.0,3,0.0],[83700.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[84000.0,0,0.0],[84600.0,2,0.0],[84900.0,0,0.0],[85350.0,1,0.0],[85650.0,3,0.0],[86100.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[86400.0,0,0.0],[86700.0,3,0.0],[87000.0,3,0.0],[87300.0,3,0.0],[87450.0,2,0.0],[87600.0,0,0.0],[87900.0,3,0.0],[88200.0,3,0.0],[88500.0,3,0.0],[88650.0,2,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[88800.0,0,0.0],[89100.0,3,0.0],[89400.0,3,0.0],[89700.0,3,0.0],[89850.0,2,0.0],[90000.0,3,0.0],[90300.0,3,0.0],[90600.0,3,0.0],[90900.0,2,0.0],[91050.0,0,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[91200.0,0,0.0],[91500.0,3,0.0],[91800.0,3,0.0],[92100.0,3,0.0],[92250.0,2,0.0],[92400.0,0,0.0],[92700.0,3,0.0],[93000.0,3,0.0],[93300.0,3,0.0],[93450.0,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[93600.0,0,0.0],[93900.0,3,0.0],[94200.0,3,0.0],[94500.0,3,0.0],[94650.0,2,0.0],[94800.0,3,0.0],[95100.0,3,0.0],[95400.0,3,0.0],[95700.0,2,0.0],[95850.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[96000.0,0,0.0],[96300.0,3,0.0],[96600.0,3,0.0],[96900.0,3,0.0],[97050.0,2,0.0],[97200.0,0,0.0],[97500.0,3,0.0],[97800.0,3,0.0],[98100.0,3,0.0],[98250.0,2,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[98400.0,0,0.0],[98700.0,3,0.0],[99000.0,3,0.0],[99300.0,3,0.0],[99450.0,2,0.0],[99600.0,3,0.0],[99900.0,3,0.0],[100200.0,3,0.0],[100500.0,2,0.0],[100650.0,0,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[100800.0,7,600.0],[100800.0,0,0.0],[101100.0,3,0.0],[101400.0,3,0.0],[101700.0,3,0.0],[101850.0,2,0.0],[102000.0,0,0.0],[102300.0,3,0.0],[102600.0,3,0.0],[102900.0,3,0.0],[103050.0,2,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[103200.0,0,0.0],[103500.0,3,0.0],[103800.0,3,0.0],[104100.0,3,0.0],[104250.0,2,0.0],[104400.0,3,0.0],[104700.0,3,0.0],[105000.0,3,0.0],[105300.0,2,0.0],[105450.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[105600.0,3,600.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]}]},"generatedBy":"SNIFF ver.6"} \ No newline at end of file +{"events":[{"params":[1],"time":9600,"name":"Camera Movement"},{"params":[0],"name":"Camera Movement","time":19200},{"params":[1],"time":28800,"name":"Camera Movement"},{"params":[0],"time":38400,"name":"Camera Movement"},{"params":[1],"time":48000,"name":"Camera Movement"},{"params":[0],"time":57600,"name":"Camera Movement"},{"params":[1],"time":67200,"name":"Camera Movement"},{"params":[0],"time":76800,"name":"Camera Movement"},{"params":[1],"time":81600,"name":"Camera Movement"},{"params":[0],"time":86400,"name":"Camera Movement"},{"params":[1],"time":91200,"name":"Camera Movement"},{"params":[0],"time":96000,"name":"Camera Movement"},{"params":[1],"time":100800,"name":"Camera Movement"}],"strumLines":[{"visible":true,"keyCount":4,"notes":[{"id":0,"sLen":0,"time":0,"type":0},{"id":2,"sLen":0,"time":600,"type":0},{"id":0,"sLen":0,"time":900,"type":0},{"id":1,"sLen":0,"time":1350,"type":0},{"id":3,"sLen":0,"time":1650,"type":0},{"id":0,"sLen":0,"time":2100,"type":0},{"id":0,"sLen":0,"time":2400,"type":0},{"id":2,"sLen":0,"time":3000,"type":0},{"id":0,"sLen":0,"time":3300,"type":0},{"id":1,"sLen":0,"time":3750,"type":0},{"id":3,"sLen":0,"time":4050,"type":0},{"id":0,"sLen":0,"time":4500,"type":0},{"id":0,"sLen":0,"time":4800,"type":0},{"id":0,"sLen":0,"time":5100,"type":0},{"id":2,"sLen":0,"time":5400,"type":0},{"id":0,"sLen":0,"time":5700,"type":0},{"id":0,"sLen":0,"time":6000,"type":0},{"id":2,"sLen":0,"time":6300,"type":0},{"id":0,"sLen":0,"time":6600,"type":0},{"id":3,"sLen":0,"time":6750,"type":0},{"id":0,"sLen":0,"time":6900,"type":0},{"id":3,"sLen":0,"time":7050,"type":0},{"id":2,"sLen":0,"time":7200,"type":0},{"id":0,"sLen":0,"time":7500,"type":0},{"id":0,"sLen":0,"time":7800,"type":0},{"id":2,"sLen":0,"time":8100,"type":0},{"id":0,"sLen":0,"time":8400,"type":0},{"id":0,"sLen":0,"time":8700,"type":0},{"id":2,"sLen":0,"time":9000,"type":0},{"id":1,"sLen":0,"time":9300,"type":0},{"id":3,"sLen":150,"time":19500,"type":0},{"id":1,"sLen":450,"time":19800,"type":0},{"id":2,"sLen":1050,"time":20400,"type":0},{"id":1,"sLen":0,"time":22500,"type":0},{"id":0,"sLen":150,"time":22800,"type":0},{"id":1,"sLen":0,"time":23100,"type":0},{"id":3,"sLen":0,"time":23400,"type":0},{"id":1,"sLen":0,"time":23700,"type":0},{"id":0,"sLen":0,"time":24000,"type":0},{"id":0,"sLen":0,"time":24300,"type":0},{"id":2,"sLen":0,"time":24600,"type":0},{"id":0,"sLen":0,"time":24900,"type":0},{"id":0,"sLen":0,"time":25200,"type":0},{"id":2,"sLen":0,"time":25500,"type":0},{"id":0,"sLen":0,"time":25800,"type":0},{"id":0,"sLen":0,"time":26100,"type":0},{"id":2,"sLen":0,"time":26400,"type":0},{"id":0,"sLen":0,"time":26700,"type":0},{"id":0,"sLen":0,"time":27000,"type":0},{"id":2,"sLen":0,"time":27300,"type":0},{"id":0,"sLen":0,"time":27600,"type":0},{"id":0,"sLen":0,"time":27900,"type":0},{"id":2,"sLen":0,"time":28200,"type":0},{"id":1,"sLen":0,"time":28500,"type":0},{"id":2,"sLen":600,"time":28800,"type":0},{"id":0,"sLen":0,"time":39000,"type":0},{"id":3,"sLen":0,"time":39450,"type":0},{"id":3,"sLen":0,"time":39750,"type":0},{"id":2,"sLen":0,"time":40050,"type":0},{"id":0,"sLen":0,"time":40500,"type":0},{"id":1,"sLen":0,"time":40800,"type":0},{"id":0,"sLen":0,"time":41400,"type":0},{"id":3,"sLen":0,"time":41850,"type":0},{"id":3,"sLen":0,"time":42150,"type":0},{"id":2,"sLen":0,"time":42450,"type":0},{"id":0,"sLen":0,"time":42900,"type":0},{"id":1,"sLen":0,"time":43200,"type":0},{"id":0,"sLen":0,"time":43800,"type":0},{"id":3,"sLen":0,"time":44250,"type":0},{"id":3,"sLen":0,"time":44550,"type":0},{"id":2,"sLen":0,"time":44850,"type":0},{"id":0,"sLen":0,"time":45300,"type":0},{"id":0,"sLen":0,"time":45600,"type":0},{"id":0,"sLen":0,"time":46050,"type":0},{"id":3,"sLen":0,"time":46200,"type":0},{"id":0,"sLen":0,"time":46500,"type":0},{"id":0,"sLen":0,"time":46950,"type":0},{"id":3,"sLen":0,"time":47100,"type":0},{"id":0,"sLen":0,"time":47400,"type":0},{"id":0,"sLen":0,"time":57750,"type":0},{"id":2,"sLen":450,"time":58200,"type":0},{"id":3,"sLen":450,"time":58800,"type":0},{"id":2,"sLen":150,"time":59400,"type":0},{"id":0,"sLen":750,"time":59700,"type":0},{"id":2,"sLen":300,"time":60600,"type":0},{"id":3,"sLen":450,"time":61200,"type":0},{"id":2,"sLen":450,"time":61800,"type":0},{"id":0,"sLen":0,"time":62400,"type":0},{"id":0,"sLen":0,"time":62700,"type":0},{"id":2,"sLen":0,"time":63000,"type":0},{"id":0,"sLen":0,"time":63300,"type":0},{"id":0,"sLen":0,"time":63600,"type":0},{"id":2,"sLen":0,"time":63900,"type":0},{"id":0,"sLen":0,"time":64200,"type":0},{"id":0,"sLen":0,"time":64500,"type":0},{"id":2,"sLen":0,"time":64800,"type":0},{"id":0,"sLen":0,"time":65100,"type":0},{"id":0,"sLen":0,"time":65400,"type":0},{"id":2,"sLen":0,"time":65700,"type":0},{"id":0,"sLen":0,"time":66000,"type":0},{"id":0,"sLen":0,"time":66300,"type":0},{"id":2,"sLen":0,"time":66600,"type":0},{"id":1,"sLen":0,"time":66900,"type":0},{"id":0,"sLen":0,"time":76800,"type":0},{"id":2,"sLen":0,"time":77400,"type":0},{"id":0,"sLen":0,"time":77700,"type":0},{"id":1,"sLen":0,"time":78150,"type":0},{"id":3,"sLen":0,"time":78450,"type":0},{"id":0,"sLen":0,"time":78900,"type":0},{"id":0,"sLen":0,"time":79200,"type":0},{"id":2,"sLen":0,"time":79800,"type":0},{"id":0,"sLen":0,"time":80100,"type":0},{"id":1,"sLen":0,"time":80550,"type":0},{"id":3,"sLen":0,"time":80850,"type":0},{"id":0,"sLen":0,"time":81300,"type":0},{"id":0,"sLen":0,"time":86400,"type":0},{"id":3,"sLen":0,"time":86700,"type":0},{"id":3,"sLen":0,"time":87000,"type":0},{"id":3,"sLen":0,"time":87300,"type":0},{"id":2,"sLen":0,"time":87450,"type":0},{"id":0,"sLen":0,"time":87600,"type":0},{"id":3,"sLen":0,"time":87900,"type":0},{"id":3,"sLen":0,"time":88200,"type":0},{"id":3,"sLen":0,"time":88500,"type":0},{"id":2,"sLen":0,"time":88650,"type":0},{"id":0,"sLen":0,"time":88800,"type":0},{"id":3,"sLen":0,"time":89100,"type":0},{"id":3,"sLen":0,"time":89400,"type":0},{"id":3,"sLen":0,"time":89700,"type":0},{"id":2,"sLen":0,"time":89850,"type":0},{"id":3,"sLen":0,"time":90000,"type":0},{"id":3,"sLen":0,"time":90300,"type":0},{"id":3,"sLen":0,"time":90600,"type":0},{"id":2,"sLen":0,"time":90900,"type":0},{"id":0,"sLen":0,"time":91050,"type":0},{"id":0,"sLen":0,"time":96000,"type":0},{"id":3,"sLen":0,"time":96300,"type":0},{"id":3,"sLen":0,"time":96600,"type":0},{"id":3,"sLen":0,"time":96900,"type":0},{"id":2,"sLen":0,"time":97050,"type":0},{"id":0,"sLen":0,"time":97200,"type":0},{"id":3,"sLen":0,"time":97500,"type":0},{"id":3,"sLen":0,"time":97800,"type":0},{"id":3,"sLen":0,"time":98100,"type":0},{"id":2,"sLen":0,"time":98250,"type":0},{"id":0,"sLen":0,"time":98400,"type":0},{"id":3,"sLen":0,"time":98700,"type":0},{"id":3,"sLen":0,"time":99000,"type":0},{"id":3,"sLen":0,"time":99300,"type":0},{"id":2,"sLen":0,"time":99450,"type":0},{"id":3,"sLen":0,"time":99600,"type":0},{"id":3,"sLen":0,"time":99900,"type":0},{"id":3,"sLen":0,"time":100200,"type":0},{"id":2,"sLen":0,"time":100500,"type":0},{"id":0,"sLen":0,"time":100650,"type":0},{"id":3,"sLen":600,"time":100800,"type":0}],"position":"dad","type":0,"characters":["parents-christmas"]},{"visible":true,"keyCount":4,"notes":[{"id":0,"sLen":0,"time":9600,"type":0},{"id":2,"sLen":0,"time":10200,"type":0},{"id":0,"sLen":0,"time":10500,"type":0},{"id":1,"sLen":0,"time":10950,"type":0},{"id":3,"sLen":0,"time":11250,"type":0},{"id":0,"sLen":0,"time":11700,"type":0},{"id":0,"sLen":0,"time":12000,"type":0},{"id":2,"sLen":0,"time":12600,"type":0},{"id":0,"sLen":0,"time":12900,"type":0},{"id":1,"sLen":0,"time":13350,"type":0},{"id":3,"sLen":0,"time":13650,"type":0},{"id":0,"sLen":0,"time":14100,"type":0},{"id":0,"sLen":0,"time":14400,"type":0},{"id":0,"sLen":0,"time":14700,"type":0},{"id":2,"sLen":0,"time":15000,"type":0},{"id":0,"sLen":0,"time":15300,"type":0},{"id":0,"sLen":0,"time":15600,"type":0},{"id":2,"sLen":0,"time":15900,"type":0},{"id":0,"sLen":0,"time":16200,"type":0},{"id":3,"sLen":0,"time":16350,"type":0},{"id":0,"sLen":0,"time":16500,"type":0},{"id":3,"sLen":0,"time":16650,"type":0},{"id":2,"sLen":0,"time":16800,"type":0},{"id":0,"sLen":0,"time":17100,"type":0},{"id":0,"sLen":0,"time":17400,"type":0},{"id":2,"sLen":0,"time":17700,"type":0},{"id":0,"sLen":0,"time":18000,"type":0},{"id":0,"sLen":0,"time":18300,"type":0},{"id":2,"sLen":0,"time":18600,"type":0},{"id":1,"sLen":0,"time":18900,"type":0},{"id":3,"sLen":150,"time":29100,"type":0},{"id":1,"sLen":450,"time":29400,"type":0},{"id":2,"sLen":1050,"time":30000,"type":0},{"id":1,"sLen":0,"time":32100,"type":0},{"id":0,"sLen":150,"time":32400,"type":0},{"id":1,"sLen":0,"time":32700,"type":0},{"id":3,"sLen":0,"time":33000,"type":0},{"id":1,"sLen":0,"time":33300,"type":0},{"id":0,"sLen":0,"time":33600,"type":0},{"id":0,"sLen":0,"time":33900,"type":0},{"id":2,"sLen":0,"time":34200,"type":0},{"id":0,"sLen":0,"time":34500,"type":0},{"id":0,"sLen":0,"time":34800,"type":0},{"id":2,"sLen":0,"time":35100,"type":0},{"id":0,"sLen":0,"time":35400,"type":0},{"id":0,"sLen":0,"time":35700,"type":0},{"id":2,"sLen":0,"time":36000,"type":0},{"id":0,"sLen":0,"time":36300,"type":0},{"id":0,"sLen":0,"time":36600,"type":0},{"id":2,"sLen":0,"time":36900,"type":0},{"id":0,"sLen":0,"time":37200,"type":0},{"id":0,"sLen":0,"time":37500,"type":0},{"id":2,"sLen":0,"time":37800,"type":0},{"id":1,"sLen":0,"time":38100,"type":0},{"id":0,"sLen":0,"time":48600,"type":0},{"id":3,"sLen":0,"time":49050,"type":0},{"id":3,"sLen":0,"time":49350,"type":0},{"id":2,"sLen":0,"time":49650,"type":0},{"id":0,"sLen":0,"time":50100,"type":0},{"id":1,"sLen":0,"time":50400,"type":0},{"id":0,"sLen":0,"time":51000,"type":0},{"id":3,"sLen":0,"time":51450,"type":0},{"id":3,"sLen":0,"time":51750,"type":0},{"id":2,"sLen":0,"time":52050,"type":0},{"id":0,"sLen":0,"time":52500,"type":0},{"id":1,"sLen":0,"time":52800,"type":0},{"id":0,"sLen":0,"time":53400,"type":0},{"id":3,"sLen":0,"time":53850,"type":0},{"id":3,"sLen":0,"time":54150,"type":0},{"id":2,"sLen":0,"time":54450,"type":0},{"id":0,"sLen":0,"time":54900,"type":0},{"id":0,"sLen":0,"time":55200,"type":0},{"id":0,"sLen":0,"time":55650,"type":0},{"id":3,"sLen":0,"time":55800,"type":0},{"id":0,"sLen":0,"time":56100,"type":0},{"id":0,"sLen":0,"time":56550,"type":0},{"id":3,"sLen":0,"time":56700,"type":0},{"id":0,"sLen":0,"time":57000,"type":0},{"id":0,"sLen":0,"time":67350,"type":0},{"id":2,"sLen":450,"time":67800,"type":0},{"id":3,"sLen":450,"time":68400,"type":0},{"id":2,"sLen":150,"time":69000,"type":0},{"id":0,"sLen":750,"time":69300,"type":0},{"id":2,"sLen":300,"time":70200,"type":0},{"id":3,"sLen":450,"time":70800,"type":0},{"id":2,"sLen":450,"time":71400,"type":0},{"id":0,"sLen":0,"time":72000,"type":0},{"id":0,"sLen":0,"time":72300,"type":0},{"id":2,"sLen":0,"time":72600,"type":0},{"id":0,"sLen":0,"time":72900,"type":0},{"id":0,"sLen":0,"time":73200,"type":0},{"id":2,"sLen":0,"time":73500,"type":0},{"id":0,"sLen":0,"time":73800,"type":0},{"id":0,"sLen":0,"time":74100,"type":0},{"id":2,"sLen":0,"time":74400,"type":0},{"id":0,"sLen":0,"time":74700,"type":0},{"id":0,"sLen":0,"time":75000,"type":0},{"id":2,"sLen":0,"time":75300,"type":0},{"id":0,"sLen":0,"time":75600,"type":0},{"id":0,"sLen":0,"time":75900,"type":0},{"id":2,"sLen":0,"time":76200,"type":0},{"id":1,"sLen":0,"time":76500,"type":0},{"id":0,"sLen":0,"time":81600,"type":0},{"id":2,"sLen":0,"time":82200,"type":0},{"id":0,"sLen":0,"time":82500,"type":0},{"id":1,"sLen":0,"time":82950,"type":0},{"id":3,"sLen":0,"time":83250,"type":0},{"id":0,"sLen":0,"time":83700,"type":0},{"id":0,"sLen":0,"time":84000,"type":0},{"id":2,"sLen":0,"time":84600,"type":0},{"id":0,"sLen":0,"time":84900,"type":0},{"id":1,"sLen":0,"time":85350,"type":0},{"id":3,"sLen":0,"time":85650,"type":0},{"id":0,"sLen":0,"time":86100,"type":0},{"id":0,"sLen":0,"time":91200,"type":0},{"id":3,"sLen":0,"time":91500,"type":0},{"id":3,"sLen":0,"time":91800,"type":0},{"id":3,"sLen":0,"time":92100,"type":0},{"id":2,"sLen":0,"time":92250,"type":0},{"id":0,"sLen":0,"time":92400,"type":0},{"id":3,"sLen":0,"time":92700,"type":0},{"id":3,"sLen":0,"time":93000,"type":0},{"id":3,"sLen":0,"time":93300,"type":0},{"id":2,"sLen":0,"time":93450,"type":0},{"id":0,"sLen":0,"time":93600,"type":0},{"id":3,"sLen":0,"time":93900,"type":0},{"id":3,"sLen":0,"time":94200,"type":0},{"id":3,"sLen":0,"time":94500,"type":0},{"id":2,"sLen":0,"time":94650,"type":0},{"id":3,"sLen":0,"time":94800,"type":0},{"id":3,"sLen":0,"time":95100,"type":0},{"id":3,"sLen":0,"time":95400,"type":0},{"id":2,"sLen":0,"time":95700,"type":0},{"id":0,"sLen":0,"time":95850,"type":0},{"id":0,"sLen":0,"time":100800,"type":0},{"id":3,"sLen":0,"time":101100,"type":0},{"id":3,"sLen":0,"time":101400,"type":0},{"id":3,"sLen":0,"time":101700,"type":0},{"id":2,"sLen":0,"time":101850,"type":0},{"id":0,"sLen":0,"time":102000,"type":0},{"id":3,"sLen":0,"time":102300,"type":0},{"id":3,"sLen":0,"time":102600,"type":0},{"id":3,"sLen":0,"time":102900,"type":0},{"id":2,"sLen":0,"time":103050,"type":0},{"id":0,"sLen":0,"time":103200,"type":0},{"id":3,"sLen":0,"time":103500,"type":0},{"id":3,"sLen":0,"time":103800,"type":0},{"id":3,"sLen":0,"time":104100,"type":0},{"id":2,"sLen":0,"time":104250,"type":0},{"id":3,"sLen":0,"time":104400,"type":0},{"id":3,"sLen":0,"time":104700,"type":0},{"id":3,"sLen":0,"time":105000,"type":0},{"id":2,"sLen":0,"time":105300,"type":0},{"id":0,"sLen":0,"time":105450,"type":0},{"id":3,"sLen":600,"time":105600,"type":0}],"position":"boyfriend","type":1,"characters":["bf-christmas"]},{"keyCount":4,"notes":[],"visible":false,"position":"girlfriend","type":2,"characters":["gf-christmas"]}],"scrollSpeed":1.3,"chartVersion":"1.6.0","stage":"mall","codenameChart":true,"noteTypes":[]} \ No newline at end of file diff --git a/assets/songs/cocoa/events.json b/assets/songs/cocoa/events.json new file mode 100644 index 00000000..5a785db8 --- /dev/null +++ b/assets/songs/cocoa/events.json @@ -0,0 +1 @@ +{"events":[{"params":[true,false,0],"time":19200,"name":"Alt Animation Toggle"},{"params":[false,false,0],"time":38400,"name":"Alt Animation Toggle"},{"params":[true,false,0],"time":57600,"name":"Alt Animation Toggle"},{"params":[false,false,0],"time":76800,"name":"Alt Animation Toggle"},{"params":[true,false,0],"time":86400,"name":"Alt Animation Toggle"}]} \ No newline at end of file diff --git a/assets/songs/cocoa/scripts/week5.hx b/assets/songs/cocoa/scripts/week5.hx deleted file mode 100644 index 8c2f4fb3..00000000 --- a/assets/songs/cocoa/scripts/week5.hx +++ /dev/null @@ -1,4 +0,0 @@ -function onNoteCreation(event) { - if (event.mustHit) - event.animSuffix = ""; -} \ No newline at end of file diff --git a/assets/songs/eggnog/charts/easy.json b/assets/songs/eggnog/charts/easy.json index 8c929650..674d25be 100644 --- a/assets/songs/eggnog/charts/easy.json +++ b/assets/songs/eggnog/charts/easy.json @@ -1 +1 @@ -{"song":{"gf":"gf-christmas","stage":"mall","song":"Eggnog","bpm":150.0,"needsVoices":true,"player1":"bf-christmas","player2":"parents-christmas","speed":1.4,"notes":[{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[3399.99976,1,0.0],[3799.99976,1,0.0],[4200.0,1,0.0],[4400.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[5000.0,1,0.0],[5400.0,1,0.0],[5600.0,3,0.0],[6000.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[6599.99951,1,0.0],[6999.99951,1,0.0],[7399.99951,1,0.0],[7599.99951,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[8200.0,1,0.0],[8600.0,1,0.0],[8800.0,3,0.0],[9000.0,2,0.0],[9400.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[9800.0,1,0.0],[10200.0,1,0.0],[10600.0,1,0.0],[10800.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[11400.0,1,0.0],[11800.0,1,0.0],[12000.0,3,0.0],[12400.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[12999.999,1,0.0],[13399.999,1,0.0],[13799.999,1,0.0],[13999.999,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[14599.999,1,0.0],[14999.999,1,0.0],[15199.999,3,0.0],[15399.999,2,0.0],[15799.999,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[15999.999,2,0.0],[16400.0,1,0.0],[16800.0,3,100.0],[17200.0,0,0.0],[17400.0,2,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[17800.0,0,0.0],[18200.0,0,0.0],[18600.0,0,0.0],[19000.0,3,400.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[19600.0,1,100.0],[20000.0,3,100.0],[20400.0,1,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[21000.0,0,0.0],[21400.0,1,0.0],[22000.0,2,400.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[22400.0,4,400.0],[22400.0,2,0.0],[22800.0,1,0.0],[23200.0,3,100.0],[23600.0,0,0.0],[23800.0,2,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[24200.0,0,0.0],[24600.0,0,0.0],[25000.0,0,0.0],[25400.0,3,400.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[25999.998,1,100.0],[26399.998,3,100.0],[26799.998,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[27399.998,0,0.0],[27799.998,1,0.0],[28399.998,2,300.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[28799.998,4,400.0],[28999.998,3,0.0],[29199.998,1,0.0],[29399.998,0,0.0],[29799.998,3,0.0],[30199.998,0,0.0],[30299.998,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[30599.998,3,0.0],[30799.998,1,0.0],[30999.998,0,0.0],[31099.998,3,0.0],[31599.998,3,0.0],[31799.998,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[31999.998,3,0.0],[32399.998,1,0.0],[32599.998,0,0.0],[32699.998,3,0.0],[33000.0,3,0.0],[33400.0,0,0.0],[33500.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[33800.0,3,0.0],[34000.0,1,0.0],[34200.0,0,0.0],[34400.0,1,0.0],[34800.0,3,0.0],[35000.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[35200.0,7,400.0],[35400.0,3,0.0],[35600.0,1,0.0],[35800.0,0,0.0],[36200.0,3,0.0],[36600.0,0,0.0],[36700.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[37000.0,3,0.0],[37200.0,1,0.0],[37400.0,0,0.0],[37500.0,3,0.0],[38000.0,3,0.0],[38200.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[38400.0,3,0.0],[38800.0,1,0.0],[39000.0,0,0.0],[39100.0,3,0.0],[39400.0,3,0.0],[39800.0,0,0.0],[39900.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[40200.0,3,0.0],[40400.0,1,0.0],[40600.0,0,0.0],[40800.0,1,0.0],[41200.0,3,0.0],[41400.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[41600.0,7,400.0],[41600.0,0,0.0],[42000.0,3,0.0],[42200.0,0,0.0],[42600.0,0,0.0],[43000.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[43200.0,7,400.0],[43200.0,0,0.0],[43600.0,3,0.0],[43800.0,0,0.0],[44200.0,0,0.0],[44600.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[44800.0,7,400.0],[44800.0,0,0.0],[45200.0,3,0.0],[45400.0,0,0.0],[45800.0,0,0.0],[46200.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[46400.0,7,400.0],[46400.0,0,0.0],[46800.0,3,0.0],[47000.0,0,0.0],[47400.0,0,0.0],[47800.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[48000.0,7,400.0],[48200.0,1,0.0],[48600.0,1,0.0],[49000.0,1,0.0],[49400.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[49800.0,1,0.0],[50200.0,1,0.0],[50600.0,1,0.0],[50800.0,3,0.0],[51000.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[51399.9961,1,0.0],[51799.9961,1,0.0],[52199.9961,1,0.0],[52599.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[52999.9961,1,0.0],[53399.9961,1,0.0],[53799.9961,1,0.0],[53999.9961,3,0.0],[54199.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[54599.9961,1,0.0],[54999.9961,1,0.0],[55399.9961,1,0.0],[55799.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[56199.9961,1,0.0],[56599.9961,1,0.0],[56999.9961,1,0.0],[57199.9961,3,0.0],[57399.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[57799.9961,1,0.0],[58199.9961,1,0.0],[58599.9961,1,0.0],[58999.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[59399.9961,1,0.0],[59799.9961,1,0.0],[60199.9961,1,0.0],[60399.9961,3,0.0],[60599.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[60799.9961,2,0.0],[60999.9961,3,0.0],[61199.9961,0,0.0],[61599.9961,1,0.0],[61999.9961,1,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[62399.9961,0,0.0],[62799.9961,0,0.0],[62999.9961,3,100.0],[63199.9961,1,0.0],[63599.9961,3,200.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[63999.9961,2,0.0],[64199.9961,3,0.0],[64399.9961,0,0.0],[64799.9961,1,0.0],[65199.9961,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[65600.0,0,0.0],[66000.0,0,0.0],[66200.0,3,100.0],[66400.0,1,0.0],[66800.0,3,200.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[67200.0,1,0.0],[67400.0,3,0.0],[67600.0,0,200.0],[68000.0,1,0.0],[68400.0,1,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[68800.0,3,0.0],[68900.0,0,100.0],[69200.0,1,0.0],[69600.0,2,300.0],[70000.0,2,300.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[70400.0,1,0.0],[70600.0,3,0.0],[70800.0,0,200.0],[71200.0,1,0.0],[71600.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[72000.0,3,0.0],[72100.0,0,100.0],[72400.0,1,0.0],[72800.0,2,300.0],[73200.0,2,300.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[73600.0,2,0.0],[73800.0,7,0.0],[74000.0,5,0.0],[74000.0,1,0.0],[74200.0,4,0.0],[74400.0,3,100.0],[74600.0,7,0.0],[74800.0,0,0.0],[75000.0,4,0.0],[75000.0,2,0.0],[75100.0,7,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[75400.0,7,0.0],[75400.0,0,0.0],[75600.0,5,0.0],[75800.0,4,0.0],[75800.0,0,0.0],[75900.0,7,0.0],[76200.0,0,0.0],[76400.0,7,0.0],[76600.0,4,0.0],[76600.0,3,400.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[76800.0,7,0.0],[77200.0,5,0.0],[77200.0,1,100.0],[77400.0,4,0.0],[77500.0,7,0.0],[77600.0,3,100.0],[77800.0,7,0.0],[78000.0,1,0.0],[78200.0,4,0.0],[78200.0,0,0.0],[78300.0,7,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[78600.0,7,0.0],[78600.0,0,0.0],[78800.0,5,0.0],[79000.0,4,0.0],[79000.0,1,0.0],[79200.0,5,0.0],[79400.0,1,0.0],[79600.0,7,0.0],[79600.0,2,300.0],[79800.0,5,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[80000.0,7,400.0],[80200.0,3,0.0],[80400.0,1,0.0],[80600.0,0,0.0],[81000.0,3,0.0],[81400.0,0,0.0],[81500.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[81800.0,3,0.0],[82000.0,1,0.0],[82200.0,0,0.0],[82300.0,3,0.0],[82800.0,3,0.0],[83000.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[83200.0,3,0.0],[83600.0,1,0.0],[83800.0,0,0.0],[83900.0,3,0.0],[84200.0,3,0.0],[84600.0,0,0.0],[84700.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[85000.0,3,0.0],[85200.0,1,0.0],[85400.0,0,0.0],[85600.0,1,0.0],[86000.0,3,0.0],[86200.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[86400.0,0,0.0],[86400.0,7,400.0],[86800.0,3,0.0],[87000.0,0,0.0],[87400.0,0,0.0],[87800.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[88000.0,7,400.0],[88000.0,0,0.0],[88400.0,3,0.0],[88600.0,0,0.0],[89000.0,0,0.0],[89400.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[89600.0,7,400.0],[89600.0,0,0.0],[90000.0,3,0.0],[90200.0,0,0.0],[90600.0,0,0.0],[91000.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[91200.0,7,400.0],[91200.0,0,0.0],[91600.0,3,0.0],[91800.0,0,0.0],[92200.0,0,0.0],[92600.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[92800.0,3,800.0]]}]},"generatedBy":"SNIFF ver.6"} \ No newline at end of file +{"events":[{"params":[1],"time":0,"name":"Camera Movement"},{"params":[0],"time":3200,"name":"Camera Movement"},{"params":[1],"time":9600,"name":"Camera Movement"},{"params":[0],"time":16000,"name":"Camera Movement"},{"params":[1],"time":22400,"name":"Camera Movement"},{"params":[0],"time":28800,"name":"Camera Movement"},{"params":[1],"time":35200,"name":"Camera Movement"},{"params":[0],"time":41600,"name":"Camera Movement"},{"params":[1],"time":43200,"name":"Camera Movement"},{"params":[0],"time":44800,"name":"Camera Movement"},{"params":[1],"time":46400,"name":"Camera Movement"},{"params":[0],"time":48000,"name":"Camera Movement"},{"params":[1],"time":54400,"name":"Camera Movement"},{"params":[0],"time":60800,"name":"Camera Movement"},{"params":[1],"time":64000,"name":"Camera Movement"},{"params":[0],"time":67200,"name":"Camera Movement"},{"params":[1],"time":70400,"name":"Camera Movement"},{"params":[0],"time":86400,"name":"Camera Movement"},{"params":[1],"time":88000,"name":"Camera Movement"},{"params":[0],"time":89600,"name":"Camera Movement"},{"params":[1],"time":91200,"name":"Camera Movement"}],"strumLines":[{"visible":true,"keyCount":4,"notes":[{"id":1,"sLen":0,"time":3399.99976,"type":0},{"id":1,"sLen":0,"time":3799.99976,"type":0},{"id":1,"sLen":0,"time":4200,"type":0},{"id":3,"sLen":0,"time":4400,"type":0},{"id":1,"sLen":0,"time":5000,"type":0},{"id":1,"sLen":0,"time":5400,"type":0},{"id":3,"sLen":0,"time":5600,"type":0},{"id":3,"sLen":0,"time":6000,"type":0},{"id":1,"sLen":0,"time":6599.99951,"type":0},{"id":1,"sLen":0,"time":6999.99951,"type":0},{"id":1,"sLen":0,"time":7399.99951,"type":0},{"id":3,"sLen":0,"time":7599.99951,"type":0},{"id":1,"sLen":0,"time":8200,"type":0},{"id":1,"sLen":0,"time":8600,"type":0},{"id":3,"sLen":0,"time":8800,"type":0},{"id":2,"sLen":0,"time":9000,"type":0},{"id":1,"sLen":0,"time":9400,"type":0},{"id":2,"sLen":0,"time":15999.999,"type":0},{"id":1,"sLen":0,"time":16400,"type":0},{"id":3,"sLen":100,"time":16800,"type":0},{"id":0,"sLen":0,"time":17200,"type":0},{"id":2,"sLen":0,"time":17400,"type":0},{"id":0,"sLen":0,"time":17800,"type":0},{"id":0,"sLen":0,"time":18200,"type":0},{"id":0,"sLen":0,"time":18600,"type":0},{"id":3,"sLen":400,"time":19000,"type":0},{"id":1,"sLen":100,"time":19600,"type":0},{"id":3,"sLen":100,"time":20000,"type":0},{"id":1,"sLen":0,"time":20400,"type":0},{"id":0,"sLen":0,"time":21000,"type":0},{"id":1,"sLen":0,"time":21400,"type":0},{"id":2,"sLen":400,"time":22000,"type":0},{"id":0,"sLen":400,"time":22400,"type":0},{"id":3,"sLen":0,"time":28999.998,"type":0},{"id":1,"sLen":0,"time":29199.998,"type":0},{"id":0,"sLen":0,"time":29399.998,"type":0},{"id":3,"sLen":0,"time":29799.998,"type":0},{"id":0,"sLen":0,"time":30199.998,"type":0},{"id":3,"sLen":0,"time":30299.998,"type":0},{"id":3,"sLen":0,"time":30599.998,"type":0},{"id":1,"sLen":0,"time":30799.998,"type":0},{"id":0,"sLen":0,"time":30999.998,"type":0},{"id":3,"sLen":0,"time":31099.998,"type":0},{"id":3,"sLen":0,"time":31599.998,"type":0},{"id":0,"sLen":0,"time":31799.998,"type":0},{"id":3,"sLen":0,"time":31999.998,"type":0},{"id":1,"sLen":0,"time":32399.998,"type":0},{"id":0,"sLen":0,"time":32599.998,"type":0},{"id":3,"sLen":0,"time":32699.998,"type":0},{"id":3,"sLen":0,"time":33000,"type":0},{"id":0,"sLen":0,"time":33400,"type":0},{"id":3,"sLen":0,"time":33500,"type":0},{"id":3,"sLen":0,"time":33800,"type":0},{"id":1,"sLen":0,"time":34000,"type":0},{"id":0,"sLen":0,"time":34200,"type":0},{"id":1,"sLen":0,"time":34400,"type":0},{"id":3,"sLen":0,"time":34800,"type":0},{"id":1,"sLen":0,"time":35000,"type":0},{"id":3,"sLen":400,"time":35200,"type":0},{"id":0,"sLen":0,"time":41600,"type":0},{"id":3,"sLen":0,"time":42000,"type":0},{"id":0,"sLen":0,"time":42200,"type":0},{"id":0,"sLen":0,"time":42600,"type":0},{"id":3,"sLen":0,"time":43000,"type":0},{"id":3,"sLen":400,"time":43200,"type":0},{"id":0,"sLen":0,"time":44800,"type":0},{"id":3,"sLen":0,"time":45200,"type":0},{"id":0,"sLen":0,"time":45400,"type":0},{"id":0,"sLen":0,"time":45800,"type":0},{"id":3,"sLen":0,"time":46200,"type":0},{"id":3,"sLen":400,"time":46400,"type":0},{"id":1,"sLen":0,"time":48200,"type":0},{"id":1,"sLen":0,"time":48600,"type":0},{"id":1,"sLen":0,"time":49000,"type":0},{"id":3,"sLen":0,"time":49400,"type":0},{"id":1,"sLen":0,"time":49800,"type":0},{"id":1,"sLen":0,"time":50200,"type":0},{"id":1,"sLen":0,"time":50600,"type":0},{"id":3,"sLen":0,"time":50800,"type":0},{"id":3,"sLen":0,"time":51000,"type":0},{"id":1,"sLen":0,"time":51399.9961,"type":0},{"id":1,"sLen":0,"time":51799.9961,"type":0},{"id":1,"sLen":0,"time":52199.9961,"type":0},{"id":3,"sLen":0,"time":52599.9961,"type":0},{"id":1,"sLen":0,"time":52999.9961,"type":0},{"id":1,"sLen":0,"time":53399.9961,"type":0},{"id":1,"sLen":0,"time":53799.9961,"type":0},{"id":3,"sLen":0,"time":53999.9961,"type":0},{"id":3,"sLen":0,"time":54199.9961,"type":0},{"id":2,"sLen":0,"time":60799.9961,"type":0},{"id":3,"sLen":0,"time":60999.9961,"type":0},{"id":0,"sLen":0,"time":61199.9961,"type":0},{"id":1,"sLen":0,"time":61599.9961,"type":0},{"id":1,"sLen":0,"time":61999.9961,"type":0},{"id":0,"sLen":0,"time":62399.9961,"type":0},{"id":0,"sLen":0,"time":62799.9961,"type":0},{"id":3,"sLen":100,"time":62999.9961,"type":0},{"id":1,"sLen":0,"time":63199.9961,"type":0},{"id":3,"sLen":200,"time":63599.9961,"type":0},{"id":1,"sLen":0,"time":67200,"type":0},{"id":3,"sLen":0,"time":67400,"type":0},{"id":0,"sLen":200,"time":67600,"type":0},{"id":1,"sLen":0,"time":68000,"type":0},{"id":1,"sLen":0,"time":68400,"type":0},{"id":3,"sLen":0,"time":68800,"type":0},{"id":0,"sLen":100,"time":68900,"type":0},{"id":1,"sLen":0,"time":69200,"type":0},{"id":2,"sLen":300,"time":69600,"type":0},{"id":2,"sLen":300,"time":70000,"type":0},{"id":3,"sLen":0,"time":73800,"type":0},{"id":1,"sLen":0,"time":74000,"type":0},{"id":0,"sLen":0,"time":74200,"type":0},{"id":3,"sLen":0,"time":74600,"type":0},{"id":0,"sLen":0,"time":75000,"type":0},{"id":3,"sLen":0,"time":75100,"type":0},{"id":3,"sLen":0,"time":75400,"type":0},{"id":1,"sLen":0,"time":75600,"type":0},{"id":0,"sLen":0,"time":75800,"type":0},{"id":3,"sLen":0,"time":75900,"type":0},{"id":3,"sLen":0,"time":76400,"type":0},{"id":0,"sLen":0,"time":76600,"type":0},{"id":3,"sLen":0,"time":76800,"type":0},{"id":1,"sLen":0,"time":77200,"type":0},{"id":0,"sLen":0,"time":77400,"type":0},{"id":3,"sLen":0,"time":77500,"type":0},{"id":3,"sLen":0,"time":77800,"type":0},{"id":0,"sLen":0,"time":78200,"type":0},{"id":3,"sLen":0,"time":78300,"type":0},{"id":3,"sLen":0,"time":78600,"type":0},{"id":1,"sLen":0,"time":78800,"type":0},{"id":0,"sLen":0,"time":79000,"type":0},{"id":1,"sLen":0,"time":79200,"type":0},{"id":3,"sLen":0,"time":79600,"type":0},{"id":1,"sLen":0,"time":79800,"type":0},{"id":3,"sLen":400,"time":80000,"type":0},{"id":0,"sLen":0,"time":86400,"type":0},{"id":3,"sLen":0,"time":86800,"type":0},{"id":0,"sLen":0,"time":87000,"type":0},{"id":0,"sLen":0,"time":87400,"type":0},{"id":3,"sLen":0,"time":87800,"type":0},{"id":3,"sLen":400,"time":88000,"type":0},{"id":0,"sLen":0,"time":89600,"type":0},{"id":3,"sLen":0,"time":90000,"type":0},{"id":0,"sLen":0,"time":90200,"type":0},{"id":0,"sLen":0,"time":90600,"type":0},{"id":3,"sLen":0,"time":91000,"type":0},{"id":3,"sLen":400,"time":91200,"type":0}],"position":"dad","type":0,"characters":["parents-christmas"]},{"visible":true,"keyCount":4,"notes":[{"id":1,"sLen":0,"time":9800,"type":0},{"id":1,"sLen":0,"time":10200,"type":0},{"id":1,"sLen":0,"time":10600,"type":0},{"id":3,"sLen":0,"time":10800,"type":0},{"id":1,"sLen":0,"time":11400,"type":0},{"id":1,"sLen":0,"time":11800,"type":0},{"id":3,"sLen":0,"time":12000,"type":0},{"id":3,"sLen":0,"time":12400,"type":0},{"id":1,"sLen":0,"time":12999.999,"type":0},{"id":1,"sLen":0,"time":13399.999,"type":0},{"id":1,"sLen":0,"time":13799.999,"type":0},{"id":3,"sLen":0,"time":13999.999,"type":0},{"id":1,"sLen":0,"time":14599.999,"type":0},{"id":1,"sLen":0,"time":14999.999,"type":0},{"id":3,"sLen":0,"time":15199.999,"type":0},{"id":2,"sLen":0,"time":15399.999,"type":0},{"id":1,"sLen":0,"time":15799.999,"type":0},{"id":2,"sLen":0,"time":22400,"type":0},{"id":1,"sLen":0,"time":22800,"type":0},{"id":3,"sLen":100,"time":23200,"type":0},{"id":0,"sLen":0,"time":23600,"type":0},{"id":2,"sLen":0,"time":23800,"type":0},{"id":0,"sLen":0,"time":24200,"type":0},{"id":0,"sLen":0,"time":24600,"type":0},{"id":0,"sLen":0,"time":25000,"type":0},{"id":3,"sLen":400,"time":25400,"type":0},{"id":1,"sLen":100,"time":25999.998,"type":0},{"id":3,"sLen":100,"time":26399.998,"type":0},{"id":1,"sLen":0,"time":26799.998,"type":0},{"id":0,"sLen":0,"time":27399.998,"type":0},{"id":1,"sLen":0,"time":27799.998,"type":0},{"id":2,"sLen":300,"time":28399.998,"type":0},{"id":0,"sLen":400,"time":28799.998,"type":0},{"id":3,"sLen":0,"time":35400,"type":0},{"id":1,"sLen":0,"time":35600,"type":0},{"id":0,"sLen":0,"time":35800,"type":0},{"id":3,"sLen":0,"time":36200,"type":0},{"id":0,"sLen":0,"time":36600,"type":0},{"id":3,"sLen":0,"time":36700,"type":0},{"id":3,"sLen":0,"time":37000,"type":0},{"id":1,"sLen":0,"time":37200,"type":0},{"id":0,"sLen":0,"time":37400,"type":0},{"id":3,"sLen":0,"time":37500,"type":0},{"id":3,"sLen":0,"time":38000,"type":0},{"id":0,"sLen":0,"time":38200,"type":0},{"id":3,"sLen":0,"time":38400,"type":0},{"id":1,"sLen":0,"time":38800,"type":0},{"id":0,"sLen":0,"time":39000,"type":0},{"id":3,"sLen":0,"time":39100,"type":0},{"id":3,"sLen":0,"time":39400,"type":0},{"id":0,"sLen":0,"time":39800,"type":0},{"id":3,"sLen":0,"time":39900,"type":0},{"id":3,"sLen":0,"time":40200,"type":0},{"id":1,"sLen":0,"time":40400,"type":0},{"id":0,"sLen":0,"time":40600,"type":0},{"id":1,"sLen":0,"time":40800,"type":0},{"id":3,"sLen":0,"time":41200,"type":0},{"id":1,"sLen":0,"time":41400,"type":0},{"id":3,"sLen":400,"time":41600,"type":0},{"id":0,"sLen":0,"time":43200,"type":0},{"id":3,"sLen":0,"time":43600,"type":0},{"id":0,"sLen":0,"time":43800,"type":0},{"id":0,"sLen":0,"time":44200,"type":0},{"id":3,"sLen":0,"time":44600,"type":0},{"id":3,"sLen":400,"time":44800,"type":0},{"id":0,"sLen":0,"time":46400,"type":0},{"id":3,"sLen":0,"time":46800,"type":0},{"id":0,"sLen":0,"time":47000,"type":0},{"id":0,"sLen":0,"time":47400,"type":0},{"id":3,"sLen":0,"time":47800,"type":0},{"id":3,"sLen":400,"time":48000,"type":0},{"id":1,"sLen":0,"time":54599.9961,"type":0},{"id":1,"sLen":0,"time":54999.9961,"type":0},{"id":1,"sLen":0,"time":55399.9961,"type":0},{"id":3,"sLen":0,"time":55799.9961,"type":0},{"id":1,"sLen":0,"time":56199.9961,"type":0},{"id":1,"sLen":0,"time":56599.9961,"type":0},{"id":1,"sLen":0,"time":56999.9961,"type":0},{"id":3,"sLen":0,"time":57199.9961,"type":0},{"id":3,"sLen":0,"time":57399.9961,"type":0},{"id":1,"sLen":0,"time":57799.9961,"type":0},{"id":1,"sLen":0,"time":58199.9961,"type":0},{"id":1,"sLen":0,"time":58599.9961,"type":0},{"id":3,"sLen":0,"time":58999.9961,"type":0},{"id":1,"sLen":0,"time":59399.9961,"type":0},{"id":1,"sLen":0,"time":59799.9961,"type":0},{"id":1,"sLen":0,"time":60199.9961,"type":0},{"id":3,"sLen":0,"time":60399.9961,"type":0},{"id":3,"sLen":0,"time":60599.9961,"type":0},{"id":2,"sLen":0,"time":63999.9961,"type":0},{"id":3,"sLen":0,"time":64199.9961,"type":0},{"id":0,"sLen":0,"time":64399.9961,"type":0},{"id":1,"sLen":0,"time":64799.9961,"type":0},{"id":1,"sLen":0,"time":65199.9961,"type":0},{"id":0,"sLen":0,"time":65600,"type":0},{"id":0,"sLen":0,"time":66000,"type":0},{"id":3,"sLen":100,"time":66200,"type":0},{"id":1,"sLen":0,"time":66400,"type":0},{"id":3,"sLen":200,"time":66800,"type":0},{"id":1,"sLen":0,"time":70400,"type":0},{"id":3,"sLen":0,"time":70600,"type":0},{"id":0,"sLen":200,"time":70800,"type":0},{"id":1,"sLen":0,"time":71200,"type":0},{"id":1,"sLen":0,"time":71600,"type":0},{"id":3,"sLen":0,"time":72000,"type":0},{"id":0,"sLen":100,"time":72100,"type":0},{"id":1,"sLen":0,"time":72400,"type":0},{"id":2,"sLen":300,"time":72800,"type":0},{"id":2,"sLen":300,"time":73200,"type":0},{"id":2,"sLen":0,"time":73600,"type":0},{"id":1,"sLen":0,"time":74000,"type":0},{"id":3,"sLen":100,"time":74400,"type":0},{"id":0,"sLen":0,"time":74800,"type":0},{"id":2,"sLen":0,"time":75000,"type":0},{"id":0,"sLen":0,"time":75400,"type":0},{"id":0,"sLen":0,"time":75800,"type":0},{"id":0,"sLen":0,"time":76200,"type":0},{"id":3,"sLen":400,"time":76600,"type":0},{"id":1,"sLen":100,"time":77200,"type":0},{"id":3,"sLen":100,"time":77600,"type":0},{"id":1,"sLen":0,"time":78000,"type":0},{"id":0,"sLen":0,"time":78200,"type":0},{"id":0,"sLen":0,"time":78600,"type":0},{"id":1,"sLen":0,"time":79000,"type":0},{"id":1,"sLen":0,"time":79400,"type":0},{"id":2,"sLen":300,"time":79600,"type":0},{"id":3,"sLen":0,"time":80200,"type":0},{"id":1,"sLen":0,"time":80400,"type":0},{"id":0,"sLen":0,"time":80600,"type":0},{"id":3,"sLen":0,"time":81000,"type":0},{"id":0,"sLen":0,"time":81400,"type":0},{"id":3,"sLen":0,"time":81500,"type":0},{"id":3,"sLen":0,"time":81800,"type":0},{"id":1,"sLen":0,"time":82000,"type":0},{"id":0,"sLen":0,"time":82200,"type":0},{"id":3,"sLen":0,"time":82300,"type":0},{"id":3,"sLen":0,"time":82800,"type":0},{"id":0,"sLen":0,"time":83000,"type":0},{"id":3,"sLen":0,"time":83200,"type":0},{"id":1,"sLen":0,"time":83600,"type":0},{"id":0,"sLen":0,"time":83800,"type":0},{"id":3,"sLen":0,"time":83900,"type":0},{"id":3,"sLen":0,"time":84200,"type":0},{"id":0,"sLen":0,"time":84600,"type":0},{"id":3,"sLen":0,"time":84700,"type":0},{"id":3,"sLen":0,"time":85000,"type":0},{"id":1,"sLen":0,"time":85200,"type":0},{"id":0,"sLen":0,"time":85400,"type":0},{"id":1,"sLen":0,"time":85600,"type":0},{"id":3,"sLen":0,"time":86000,"type":0},{"id":1,"sLen":0,"time":86200,"type":0},{"id":3,"sLen":400,"time":86400,"type":0},{"id":0,"sLen":0,"time":88000,"type":0},{"id":3,"sLen":0,"time":88400,"type":0},{"id":0,"sLen":0,"time":88600,"type":0},{"id":0,"sLen":0,"time":89000,"type":0},{"id":3,"sLen":0,"time":89400,"type":0},{"id":3,"sLen":400,"time":89600,"type":0},{"id":0,"sLen":0,"time":91200,"type":0},{"id":3,"sLen":0,"time":91600,"type":0},{"id":0,"sLen":0,"time":91800,"type":0},{"id":0,"sLen":0,"time":92200,"type":0},{"id":3,"sLen":0,"time":92600,"type":0},{"id":3,"sLen":800,"time":92800,"type":0}],"position":"boyfriend","type":1,"characters":["bf-christmas"]},{"keyCount":4,"notes":[],"visible":false,"position":"girlfriend","type":2,"characters":["gf-christmas"]}],"scrollSpeed":1.4,"chartVersion":"1.6.0","stage":"mall","codenameChart":true,"noteTypes":[]} \ No newline at end of file diff --git a/assets/songs/eggnog/charts/hard.json b/assets/songs/eggnog/charts/hard.json index 282cf3ab..eedf0024 100644 --- a/assets/songs/eggnog/charts/hard.json +++ b/assets/songs/eggnog/charts/hard.json @@ -1 +1 @@ -{"song":{"gf":"gf-christmas","stage":"mall","song":"Eggnog","bpm":150.0,"needsVoices":true,"player1":"bf-christmas","player2":"parents-christmas","speed":1.9,"notes":[{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[3399.99976,1,0.0],[3799.99976,1,0.0],[4200.0,1,0.0],[4400.0,3,0.0],[4600.0,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[5000.0,1,0.0],[5400.0,1,0.0],[5600.0,3,0.0],[5800.0,2,0.0],[6000.0,3,0.0],[6200.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[6599.99951,1,0.0],[6999.99951,1,0.0],[7399.99951,1,0.0],[7599.99951,3,0.0],[7799.99951,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[8200.0,1,0.0],[8600.0,1,0.0],[8800.0,3,0.0],[9000.0,2,0.0],[9200.0,0,0.0],[9400.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[9800.0,1,0.0],[10200.0,1,0.0],[10600.0,1,0.0],[10800.0,3,0.0],[11000.0,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[11400.0,1,0.0],[11800.0,1,0.0],[12000.0,3,0.0],[12200.0,2,0.0],[12400.0,3,0.0],[12600.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[12999.999,1,0.0],[13399.999,1,0.0],[13799.999,1,0.0],[13999.999,3,0.0],[14199.999,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[14599.999,1,0.0],[14999.999,1,0.0],[15199.999,3,0.0],[15399.999,2,0.0],[15599.999,0,0.0],[15799.999,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[15999.999,2,0.0],[16099.999,0,0.0],[16199.999,3,100.0],[16400.0,1,0.0],[16500.0,2,200.0],[16800.0,3,100.0],[17200.0,0,0.0],[17400.0,2,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[17800.0,0,0.0],[18200.0,0,0.0],[18600.0,0,0.0],[18700.0,1,0.0],[18800.0,2,0.0],[19000.0,3,400.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[19600.0,1,100.0],[20000.0,3,100.0],[20400.0,1,0.0],[20600.0,0,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[21000.0,0,0.0],[21400.0,1,0.0],[21800.0,1,0.0],[22000.0,2,300.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[22400.0,4,400.0],[22400.0,2,0.0],[22500.0,0,0.0],[22600.0,3,100.0],[22800.0,1,0.0],[22900.0,2,200.0],[23200.0,3,100.0],[23600.0,0,0.0],[23800.0,2,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[24200.0,0,0.0],[24600.0,0,0.0],[25000.0,0,0.0],[25100.0,1,0.0],[25200.0,2,0.0],[25400.0,3,400.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[25999.998,1,100.0],[26399.998,3,100.0],[26799.998,1,0.0],[26999.998,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[27399.998,0,0.0],[27799.998,1,0.0],[28199.998,1,0.0],[28399.998,2,300.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[28799.998,4,400.0],[28999.998,3,0.0],[29199.998,1,0.0],[29399.998,0,0.0],[29499.998,3,0.0],[29599.998,2,0.0],[29799.998,3,0.0],[29999.998,1,0.0],[30199.998,0,0.0],[30299.998,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[30399.998,2,0.0],[30599.998,3,0.0],[30799.998,1,0.0],[30999.998,0,0.0],[31099.998,3,0.0],[31199.998,2,0.0],[31399.998,3,0.0],[31599.998,3,0.0],[31799.998,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[31999.998,3,0.0],[32199.998,3,0.0],[32399.998,1,0.0],[32599.998,0,0.0],[32699.998,3,0.0],[32800.0,2,0.0],[33000.0,3,0.0],[33200.0,1,0.0],[33400.0,0,0.0],[33500.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[33600.0,2,0.0],[33800.0,3,0.0],[34000.0,1,0.0],[34200.0,0,0.0],[34400.0,1,0.0],[34500.0,0,0.0],[34600.0,2,100.0],[34800.0,3,0.0],[35000.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[35200.0,7,400.0],[35400.0,3,0.0],[35600.0,1,0.0],[35800.0,0,0.0],[35900.0,3,0.0],[36000.0,2,0.0],[36200.0,3,0.0],[36400.0,1,0.0],[36600.0,0,0.0],[36700.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[36800.0,2,0.0],[37000.0,3,0.0],[37200.0,1,0.0],[37400.0,0,0.0],[37500.0,3,0.0],[37600.0,2,0.0],[37800.0,3,0.0],[38000.0,3,0.0],[38200.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[38400.0,3,0.0],[38600.0,3,0.0],[38800.0,1,0.0],[39000.0,0,0.0],[39100.0,3,0.0],[39200.0,2,0.0],[39400.0,3,0.0],[39600.0,1,0.0],[39800.0,0,0.0],[39900.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[40000.0,2,0.0],[40200.0,3,0.0],[40400.0,1,0.0],[40600.0,0,0.0],[40800.0,1,0.0],[40900.0,0,0.0],[41000.0,2,100.0],[41200.0,3,0.0],[41400.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[41600.0,0,0.0],[41600.0,7,400.0],[41700.0,2,0.0],[41800.0,3,0.0],[42000.0,2,0.0],[42200.0,0,0.0],[42400.0,1,0.0],[42600.0,0,0.0],[42800.0,1,0.0],[43000.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[43200.0,7,400.0],[43200.0,0,0.0],[43300.0,2,0.0],[43400.0,3,0.0],[43600.0,2,0.0],[43800.0,0,0.0],[44000.0,1,0.0],[44200.0,0,0.0],[44400.0,1,0.0],[44600.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[44800.0,7,400.0],[44800.0,0,0.0],[44900.0,2,0.0],[45000.0,3,0.0],[45200.0,2,0.0],[45400.0,0,0.0],[45600.0,1,0.0],[45800.0,0,0.0],[46000.0,1,0.0],[46200.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[46400.0,7,400.0],[46400.0,0,0.0],[46500.0,2,0.0],[46600.0,3,0.0],[46800.0,2,0.0],[47000.0,0,0.0],[47200.0,1,0.0],[47400.0,0,0.0],[47600.0,1,0.0],[47800.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[48000.0,7,400.0],[48200.0,1,0.0],[48600.0,1,0.0],[49000.0,1,0.0],[49200.0,2,0.0],[49300.0,0,0.0],[49400.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[49800.0,1,0.0],[50200.0,1,0.0],[50600.0,1,0.0],[50800.0,3,0.0],[50900.0,0,0.0],[51000.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[51399.9961,1,0.0],[51799.9961,1,0.0],[52199.9961,1,0.0],[52399.9961,2,0.0],[52499.9961,0,0.0],[52599.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[52999.9961,1,0.0],[53399.9961,1,0.0],[53799.9961,1,0.0],[53999.9961,3,0.0],[54099.9961,0,0.0],[54199.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[54599.9961,1,0.0],[54999.9961,1,0.0],[55399.9961,1,0.0],[55599.9961,2,0.0],[55699.9961,0,0.0],[55799.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[56199.9961,1,0.0],[56599.9961,1,0.0],[56999.9961,1,0.0],[57199.9961,3,0.0],[57299.9961,0,0.0],[57399.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[57799.9961,1,0.0],[58199.9961,1,0.0],[58599.9961,1,0.0],[58799.9961,2,0.0],[58899.9961,0,0.0],[58999.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[59399.9961,1,0.0],[59799.9961,1,0.0],[60199.9961,1,0.0],[60399.9961,3,0.0],[60499.9961,0,0.0],[60599.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[60799.9961,2,0.0],[60999.9961,3,0.0],[61199.9961,0,0.0],[61399.9961,3,0.0],[61599.9961,1,0.0],[61799.9961,0,0.0],[61999.9961,1,0.0],[62199.9961,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[62399.9961,0,0.0],[62499.9961,3,0.0],[62599.9961,1,0.0],[62799.9961,0,0.0],[62899.9961,1,0.0],[62999.9961,3,100.0],[63199.9961,1,0.0],[63399.9961,2,0.0],[63599.9961,3,200.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[63999.9961,2,0.0],[64199.9961,3,0.0],[64399.9961,0,0.0],[64599.9961,3,0.0],[64799.9961,1,0.0],[64999.9961,0,0.0],[65199.9961,1,0.0],[65399.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[65600.0,0,0.0],[65700.0,3,0.0],[65800.0,1,0.0],[66000.0,0,0.0],[66100.0,1,0.0],[66200.0,3,100.0],[66400.0,1,0.0],[66600.0,2,0.0],[66800.0,3,200.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[67200.0,1,0.0],[67400.0,3,0.0],[67600.0,0,200.0],[68000.0,1,0.0],[68200.0,1,0.0],[68400.0,1,0.0],[68600.0,2,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[68800.0,3,0.0],[68900.0,0,0.0],[69000.0,3,0.0],[69200.0,1,0.0],[69300.0,0,0.0],[69400.0,1,0.0],[69500.0,0,0.0],[69600.0,2,300.0],[70000.0,2,300.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[70400.0,1,0.0],[70600.0,3,0.0],[70800.0,0,200.0],[71200.0,1,0.0],[71400.0,0,0.0],[71600.0,1,0.0],[71800.0,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[72000.0,3,0.0],[72100.0,0,0.0],[72200.0,3,0.0],[72400.0,1,0.0],[72500.0,0,0.0],[72600.0,1,0.0],[72700.0,0,0.0],[72800.0,2,300.0],[73200.0,2,300.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[73600.0,2,0.0],[73700.0,0,0.0],[73800.0,7,0.0],[73800.0,3,100.0],[74000.0,5,0.0],[74000.0,1,0.0],[74100.0,2,200.0],[74200.0,4,0.0],[74300.0,7,0.0],[74400.0,6,0.0],[74400.0,3,100.0],[74600.0,7,0.0],[74800.0,5,0.0],[74800.0,0,0.0],[75000.0,4,0.0],[75000.0,2,0.0],[75100.0,7,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[75200.0,6,0.0],[75400.0,7,0.0],[75400.0,0,0.0],[75600.0,5,0.0],[75800.0,4,0.0],[75800.0,0,0.0],[75900.0,7,0.0],[76000.0,6,0.0],[76200.0,7,0.0],[76200.0,0,0.0],[76300.0,1,0.0],[76400.0,7,0.0],[76400.0,2,0.0],[76600.0,4,0.0],[76600.0,3,400.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[76800.0,7,0.0],[77000.0,7,0.0],[77200.0,5,0.0],[77200.0,1,100.0],[77400.0,4,0.0],[77500.0,7,0.0],[77600.0,6,0.0],[77600.0,3,100.0],[77800.0,7,0.0],[78000.0,5,0.0],[78000.0,1,0.0],[78200.0,4,0.0],[78200.0,0,0.0],[78300.0,7,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[78400.0,6,0.0],[78600.0,7,0.0],[78600.0,0,0.0],[78800.0,5,0.0],[79000.0,4,0.0],[79000.0,1,0.0],[79200.0,5,0.0],[79300.0,4,0.0],[79400.0,6,100.0],[79400.0,1,0.0],[79600.0,7,0.0],[79600.0,2,300.0],[79800.0,5,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[80000.0,7,400.0],[80200.0,3,0.0],[80400.0,1,0.0],[80600.0,0,0.0],[80700.0,3,0.0],[80800.0,2,0.0],[81000.0,3,0.0],[81200.0,1,0.0],[81400.0,0,0.0],[81500.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[81600.0,2,0.0],[81800.0,3,0.0],[82000.0,1,0.0],[82200.0,0,0.0],[82300.0,3,0.0],[82400.0,2,0.0],[82600.0,3,0.0],[82800.0,3,0.0],[83000.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[83200.0,3,0.0],[83400.0,3,0.0],[83600.0,1,0.0],[83800.0,0,0.0],[83900.0,3,0.0],[84000.0,2,0.0],[84200.0,3,0.0],[84400.0,1,0.0],[84600.0,0,0.0],[84700.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[84800.0,2,0.0],[85000.0,3,0.0],[85200.0,1,0.0],[85400.0,0,0.0],[85600.0,1,0.0],[85700.0,0,0.0],[85800.0,2,100.0],[86000.0,3,0.0],[86200.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[86400.0,0,0.0],[86400.0,7,400.0],[86500.0,2,0.0],[86600.0,3,0.0],[86800.0,2,0.0],[87000.0,0,0.0],[87200.0,1,0.0],[87400.0,0,0.0],[87600.0,1,0.0],[87800.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[88000.0,7,400.0],[88000.0,0,0.0],[88100.0,2,0.0],[88200.0,3,0.0],[88400.0,2,0.0],[88600.0,0,0.0],[88800.0,1,0.0],[89000.0,0,0.0],[89200.0,1,0.0],[89400.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[89600.0,7,400.0],[89600.0,0,0.0],[89700.0,2,0.0],[89800.0,3,0.0],[90000.0,2,0.0],[90200.0,0,0.0],[90400.0,1,0.0],[90600.0,0,0.0],[90800.0,1,0.0],[91000.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[91200.0,7,400.0],[91200.0,0,0.0],[91300.0,2,0.0],[91400.0,3,0.0],[91600.0,2,0.0],[91800.0,0,0.0],[92000.0,1,0.0],[92200.0,0,0.0],[92400.0,1,0.0],[92600.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[92800.0,3,800.0]]}]},"generatedBy":"SNIFF ver.6"} \ No newline at end of file +{"events":[{"params":[1],"time":0,"name":"Camera Movement"},{"params":[0],"time":3200,"name":"Camera Movement"},{"params":[1],"time":9600,"name":"Camera Movement"},{"params":[0],"time":16000,"name":"Camera Movement"},{"params":[1],"time":22400,"name":"Camera Movement"},{"params":[0],"time":28800,"name":"Camera Movement"},{"params":[1],"time":35200,"name":"Camera Movement"},{"params":[0],"time":41600,"name":"Camera Movement"},{"params":[1],"time":43200,"name":"Camera Movement"},{"params":[0],"time":44800,"name":"Camera Movement"},{"params":[1],"time":46400,"name":"Camera Movement"},{"params":[0],"time":48000,"name":"Camera Movement"},{"params":[1],"time":54400,"name":"Camera Movement"},{"params":[0],"time":60800,"name":"Camera Movement"},{"params":[1],"time":64000,"name":"Camera Movement"},{"params":[0],"time":67200,"name":"Camera Movement"},{"params":[1],"time":70400,"name":"Camera Movement"},{"params":[0],"time":86400,"name":"Camera Movement"},{"params":[1],"time":88000,"name":"Camera Movement"},{"params":[0],"time":89600,"name":"Camera Movement"},{"params":[1],"time":91200,"name":"Camera Movement"}],"strumLines":[{"visible":true,"keyCount":4,"notes":[{"id":1,"sLen":0,"time":3399.99976,"type":0},{"id":1,"sLen":0,"time":3799.99976,"type":0},{"id":1,"sLen":0,"time":4200,"type":0},{"id":3,"sLen":0,"time":4400,"type":0},{"id":2,"sLen":0,"time":4600,"type":0},{"id":1,"sLen":0,"time":5000,"type":0},{"id":1,"sLen":0,"time":5400,"type":0},{"id":3,"sLen":0,"time":5600,"type":0},{"id":2,"sLen":0,"time":5800,"type":0},{"id":3,"sLen":0,"time":6000,"type":0},{"id":1,"sLen":0,"time":6200,"type":0},{"id":1,"sLen":0,"time":6599.99951,"type":0},{"id":1,"sLen":0,"time":6999.99951,"type":0},{"id":1,"sLen":0,"time":7399.99951,"type":0},{"id":3,"sLen":0,"time":7599.99951,"type":0},{"id":2,"sLen":0,"time":7799.99951,"type":0},{"id":1,"sLen":0,"time":8200,"type":0},{"id":1,"sLen":0,"time":8600,"type":0},{"id":3,"sLen":0,"time":8800,"type":0},{"id":2,"sLen":0,"time":9000,"type":0},{"id":0,"sLen":0,"time":9200,"type":0},{"id":1,"sLen":0,"time":9400,"type":0},{"id":2,"sLen":0,"time":15999.999,"type":0},{"id":0,"sLen":0,"time":16099.999,"type":0},{"id":3,"sLen":100,"time":16199.999,"type":0},{"id":1,"sLen":0,"time":16400,"type":0},{"id":2,"sLen":200,"time":16500,"type":0},{"id":3,"sLen":100,"time":16800,"type":0},{"id":0,"sLen":0,"time":17200,"type":0},{"id":2,"sLen":0,"time":17400,"type":0},{"id":0,"sLen":0,"time":17800,"type":0},{"id":0,"sLen":0,"time":18200,"type":0},{"id":0,"sLen":0,"time":18600,"type":0},{"id":1,"sLen":0,"time":18700,"type":0},{"id":2,"sLen":0,"time":18800,"type":0},{"id":3,"sLen":400,"time":19000,"type":0},{"id":1,"sLen":100,"time":19600,"type":0},{"id":3,"sLen":100,"time":20000,"type":0},{"id":1,"sLen":0,"time":20400,"type":0},{"id":0,"sLen":0,"time":20600,"type":0},{"id":0,"sLen":0,"time":21000,"type":0},{"id":1,"sLen":0,"time":21400,"type":0},{"id":1,"sLen":0,"time":21800,"type":0},{"id":2,"sLen":300,"time":22000,"type":0},{"id":0,"sLen":400,"time":22400,"type":0},{"id":3,"sLen":0,"time":28999.998,"type":0},{"id":1,"sLen":0,"time":29199.998,"type":0},{"id":0,"sLen":0,"time":29399.998,"type":0},{"id":3,"sLen":0,"time":29499.998,"type":0},{"id":2,"sLen":0,"time":29599.998,"type":0},{"id":3,"sLen":0,"time":29799.998,"type":0},{"id":1,"sLen":0,"time":29999.998,"type":0},{"id":0,"sLen":0,"time":30199.998,"type":0},{"id":3,"sLen":0,"time":30299.998,"type":0},{"id":2,"sLen":0,"time":30399.998,"type":0},{"id":3,"sLen":0,"time":30599.998,"type":0},{"id":1,"sLen":0,"time":30799.998,"type":0},{"id":0,"sLen":0,"time":30999.998,"type":0},{"id":3,"sLen":0,"time":31099.998,"type":0},{"id":2,"sLen":0,"time":31199.998,"type":0},{"id":3,"sLen":0,"time":31399.998,"type":0},{"id":3,"sLen":0,"time":31599.998,"type":0},{"id":0,"sLen":0,"time":31799.998,"type":0},{"id":3,"sLen":0,"time":31999.998,"type":0},{"id":3,"sLen":0,"time":32199.998,"type":0},{"id":1,"sLen":0,"time":32399.998,"type":0},{"id":0,"sLen":0,"time":32599.998,"type":0},{"id":3,"sLen":0,"time":32699.998,"type":0},{"id":2,"sLen":0,"time":32800,"type":0},{"id":3,"sLen":0,"time":33000,"type":0},{"id":1,"sLen":0,"time":33200,"type":0},{"id":0,"sLen":0,"time":33400,"type":0},{"id":3,"sLen":0,"time":33500,"type":0},{"id":2,"sLen":0,"time":33600,"type":0},{"id":3,"sLen":0,"time":33800,"type":0},{"id":1,"sLen":0,"time":34000,"type":0},{"id":0,"sLen":0,"time":34200,"type":0},{"id":1,"sLen":0,"time":34400,"type":0},{"id":0,"sLen":0,"time":34500,"type":0},{"id":2,"sLen":100,"time":34600,"type":0},{"id":3,"sLen":0,"time":34800,"type":0},{"id":1,"sLen":0,"time":35000,"type":0},{"id":3,"sLen":400,"time":35200,"type":0},{"id":0,"sLen":0,"time":41600,"type":0},{"id":2,"sLen":0,"time":41700,"type":0},{"id":3,"sLen":0,"time":41800,"type":0},{"id":2,"sLen":0,"time":42000,"type":0},{"id":0,"sLen":0,"time":42200,"type":0},{"id":1,"sLen":0,"time":42400,"type":0},{"id":0,"sLen":0,"time":42600,"type":0},{"id":1,"sLen":0,"time":42800,"type":0},{"id":3,"sLen":0,"time":43000,"type":0},{"id":3,"sLen":400,"time":43200,"type":0},{"id":0,"sLen":0,"time":44800,"type":0},{"id":2,"sLen":0,"time":44900,"type":0},{"id":3,"sLen":0,"time":45000,"type":0},{"id":2,"sLen":0,"time":45200,"type":0},{"id":0,"sLen":0,"time":45400,"type":0},{"id":1,"sLen":0,"time":45600,"type":0},{"id":0,"sLen":0,"time":45800,"type":0},{"id":1,"sLen":0,"time":46000,"type":0},{"id":3,"sLen":0,"time":46200,"type":0},{"id":3,"sLen":400,"time":46400,"type":0},{"id":1,"sLen":0,"time":48200,"type":0},{"id":1,"sLen":0,"time":48600,"type":0},{"id":1,"sLen":0,"time":49000,"type":0},{"id":2,"sLen":0,"time":49200,"type":0},{"id":0,"sLen":0,"time":49300,"type":0},{"id":3,"sLen":0,"time":49400,"type":0},{"id":1,"sLen":0,"time":49800,"type":0},{"id":1,"sLen":0,"time":50200,"type":0},{"id":1,"sLen":0,"time":50600,"type":0},{"id":3,"sLen":0,"time":50800,"type":0},{"id":0,"sLen":0,"time":50900,"type":0},{"id":3,"sLen":0,"time":51000,"type":0},{"id":1,"sLen":0,"time":51399.9961,"type":0},{"id":1,"sLen":0,"time":51799.9961,"type":0},{"id":1,"sLen":0,"time":52199.9961,"type":0},{"id":2,"sLen":0,"time":52399.9961,"type":0},{"id":0,"sLen":0,"time":52499.9961,"type":0},{"id":3,"sLen":0,"time":52599.9961,"type":0},{"id":1,"sLen":0,"time":52999.9961,"type":0},{"id":1,"sLen":0,"time":53399.9961,"type":0},{"id":1,"sLen":0,"time":53799.9961,"type":0},{"id":3,"sLen":0,"time":53999.9961,"type":0},{"id":0,"sLen":0,"time":54099.9961,"type":0},{"id":3,"sLen":0,"time":54199.9961,"type":0},{"id":2,"sLen":0,"time":60799.9961,"type":0},{"id":3,"sLen":0,"time":60999.9961,"type":0},{"id":0,"sLen":0,"time":61199.9961,"type":0},{"id":3,"sLen":0,"time":61399.9961,"type":0},{"id":1,"sLen":0,"time":61599.9961,"type":0},{"id":0,"sLen":0,"time":61799.9961,"type":0},{"id":1,"sLen":0,"time":61999.9961,"type":0},{"id":3,"sLen":0,"time":62199.9961,"type":0},{"id":0,"sLen":0,"time":62399.9961,"type":0},{"id":3,"sLen":0,"time":62499.9961,"type":0},{"id":1,"sLen":0,"time":62599.9961,"type":0},{"id":0,"sLen":0,"time":62799.9961,"type":0},{"id":1,"sLen":0,"time":62899.9961,"type":0},{"id":3,"sLen":100,"time":62999.9961,"type":0},{"id":1,"sLen":0,"time":63199.9961,"type":0},{"id":2,"sLen":0,"time":63399.9961,"type":0},{"id":3,"sLen":200,"time":63599.9961,"type":0},{"id":1,"sLen":0,"time":67200,"type":0},{"id":3,"sLen":0,"time":67400,"type":0},{"id":0,"sLen":200,"time":67600,"type":0},{"id":1,"sLen":0,"time":68000,"type":0},{"id":1,"sLen":0,"time":68200,"type":0},{"id":1,"sLen":0,"time":68400,"type":0},{"id":2,"sLen":0,"time":68600,"type":0},{"id":3,"sLen":0,"time":68800,"type":0},{"id":0,"sLen":0,"time":68900,"type":0},{"id":3,"sLen":0,"time":69000,"type":0},{"id":1,"sLen":0,"time":69200,"type":0},{"id":0,"sLen":0,"time":69300,"type":0},{"id":1,"sLen":0,"time":69400,"type":0},{"id":0,"sLen":0,"time":69500,"type":0},{"id":2,"sLen":300,"time":69600,"type":0},{"id":2,"sLen":300,"time":70000,"type":0},{"id":3,"sLen":0,"time":73800,"type":0},{"id":1,"sLen":0,"time":74000,"type":0},{"id":0,"sLen":0,"time":74200,"type":0},{"id":3,"sLen":0,"time":74300,"type":0},{"id":2,"sLen":0,"time":74400,"type":0},{"id":3,"sLen":0,"time":74600,"type":0},{"id":1,"sLen":0,"time":74800,"type":0},{"id":0,"sLen":0,"time":75000,"type":0},{"id":3,"sLen":0,"time":75100,"type":0},{"id":2,"sLen":0,"time":75200,"type":0},{"id":3,"sLen":0,"time":75400,"type":0},{"id":1,"sLen":0,"time":75600,"type":0},{"id":0,"sLen":0,"time":75800,"type":0},{"id":3,"sLen":0,"time":75900,"type":0},{"id":2,"sLen":0,"time":76000,"type":0},{"id":3,"sLen":0,"time":76200,"type":0},{"id":3,"sLen":0,"time":76400,"type":0},{"id":0,"sLen":0,"time":76600,"type":0},{"id":3,"sLen":0,"time":76800,"type":0},{"id":3,"sLen":0,"time":77000,"type":0},{"id":1,"sLen":0,"time":77200,"type":0},{"id":0,"sLen":0,"time":77400,"type":0},{"id":3,"sLen":0,"time":77500,"type":0},{"id":2,"sLen":0,"time":77600,"type":0},{"id":3,"sLen":0,"time":77800,"type":0},{"id":1,"sLen":0,"time":78000,"type":0},{"id":0,"sLen":0,"time":78200,"type":0},{"id":3,"sLen":0,"time":78300,"type":0},{"id":2,"sLen":0,"time":78400,"type":0},{"id":3,"sLen":0,"time":78600,"type":0},{"id":1,"sLen":0,"time":78800,"type":0},{"id":0,"sLen":0,"time":79000,"type":0},{"id":1,"sLen":0,"time":79200,"type":0},{"id":0,"sLen":0,"time":79300,"type":0},{"id":2,"sLen":100,"time":79400,"type":0},{"id":3,"sLen":0,"time":79600,"type":0},{"id":1,"sLen":0,"time":79800,"type":0},{"id":3,"sLen":400,"time":80000,"type":0},{"id":0,"sLen":0,"time":86400,"type":0},{"id":2,"sLen":0,"time":86500,"type":0},{"id":3,"sLen":0,"time":86600,"type":0},{"id":2,"sLen":0,"time":86800,"type":0},{"id":0,"sLen":0,"time":87000,"type":0},{"id":1,"sLen":0,"time":87200,"type":0},{"id":0,"sLen":0,"time":87400,"type":0},{"id":1,"sLen":0,"time":87600,"type":0},{"id":3,"sLen":0,"time":87800,"type":0},{"id":3,"sLen":400,"time":88000,"type":0},{"id":0,"sLen":0,"time":89600,"type":0},{"id":2,"sLen":0,"time":89700,"type":0},{"id":3,"sLen":0,"time":89800,"type":0},{"id":2,"sLen":0,"time":90000,"type":0},{"id":0,"sLen":0,"time":90200,"type":0},{"id":1,"sLen":0,"time":90400,"type":0},{"id":0,"sLen":0,"time":90600,"type":0},{"id":1,"sLen":0,"time":90800,"type":0},{"id":3,"sLen":0,"time":91000,"type":0},{"id":3,"sLen":400,"time":91200,"type":0}],"position":"dad","type":0,"characters":["parents-christmas"]},{"visible":true,"keyCount":4,"notes":[{"id":1,"sLen":0,"time":9800,"type":0},{"id":1,"sLen":0,"time":10200,"type":0},{"id":1,"sLen":0,"time":10600,"type":0},{"id":3,"sLen":0,"time":10800,"type":0},{"id":2,"sLen":0,"time":11000,"type":0},{"id":1,"sLen":0,"time":11400,"type":0},{"id":1,"sLen":0,"time":11800,"type":0},{"id":3,"sLen":0,"time":12000,"type":0},{"id":2,"sLen":0,"time":12200,"type":0},{"id":3,"sLen":0,"time":12400,"type":0},{"id":1,"sLen":0,"time":12600,"type":0},{"id":1,"sLen":0,"time":12999.999,"type":0},{"id":1,"sLen":0,"time":13399.999,"type":0},{"id":1,"sLen":0,"time":13799.999,"type":0},{"id":3,"sLen":0,"time":13999.999,"type":0},{"id":2,"sLen":0,"time":14199.999,"type":0},{"id":1,"sLen":0,"time":14599.999,"type":0},{"id":1,"sLen":0,"time":14999.999,"type":0},{"id":3,"sLen":0,"time":15199.999,"type":0},{"id":2,"sLen":0,"time":15399.999,"type":0},{"id":0,"sLen":0,"time":15599.999,"type":0},{"id":1,"sLen":0,"time":15799.999,"type":0},{"id":2,"sLen":0,"time":22400,"type":0},{"id":0,"sLen":0,"time":22500,"type":0},{"id":3,"sLen":100,"time":22600,"type":0},{"id":1,"sLen":0,"time":22800,"type":0},{"id":2,"sLen":200,"time":22900,"type":0},{"id":3,"sLen":100,"time":23200,"type":0},{"id":0,"sLen":0,"time":23600,"type":0},{"id":2,"sLen":0,"time":23800,"type":0},{"id":0,"sLen":0,"time":24200,"type":0},{"id":0,"sLen":0,"time":24600,"type":0},{"id":0,"sLen":0,"time":25000,"type":0},{"id":1,"sLen":0,"time":25100,"type":0},{"id":2,"sLen":0,"time":25200,"type":0},{"id":3,"sLen":400,"time":25400,"type":0},{"id":1,"sLen":100,"time":25999.998,"type":0},{"id":3,"sLen":100,"time":26399.998,"type":0},{"id":1,"sLen":0,"time":26799.998,"type":0},{"id":0,"sLen":0,"time":26999.998,"type":0},{"id":0,"sLen":0,"time":27399.998,"type":0},{"id":1,"sLen":0,"time":27799.998,"type":0},{"id":1,"sLen":0,"time":28199.998,"type":0},{"id":2,"sLen":300,"time":28399.998,"type":0},{"id":0,"sLen":400,"time":28799.998,"type":0},{"id":3,"sLen":0,"time":35400,"type":0},{"id":1,"sLen":0,"time":35600,"type":0},{"id":0,"sLen":0,"time":35800,"type":0},{"id":3,"sLen":0,"time":35900,"type":0},{"id":2,"sLen":0,"time":36000,"type":0},{"id":3,"sLen":0,"time":36200,"type":0},{"id":1,"sLen":0,"time":36400,"type":0},{"id":0,"sLen":0,"time":36600,"type":0},{"id":3,"sLen":0,"time":36700,"type":0},{"id":2,"sLen":0,"time":36800,"type":0},{"id":3,"sLen":0,"time":37000,"type":0},{"id":1,"sLen":0,"time":37200,"type":0},{"id":0,"sLen":0,"time":37400,"type":0},{"id":3,"sLen":0,"time":37500,"type":0},{"id":2,"sLen":0,"time":37600,"type":0},{"id":3,"sLen":0,"time":37800,"type":0},{"id":3,"sLen":0,"time":38000,"type":0},{"id":0,"sLen":0,"time":38200,"type":0},{"id":3,"sLen":0,"time":38400,"type":0},{"id":3,"sLen":0,"time":38600,"type":0},{"id":1,"sLen":0,"time":38800,"type":0},{"id":0,"sLen":0,"time":39000,"type":0},{"id":3,"sLen":0,"time":39100,"type":0},{"id":2,"sLen":0,"time":39200,"type":0},{"id":3,"sLen":0,"time":39400,"type":0},{"id":1,"sLen":0,"time":39600,"type":0},{"id":0,"sLen":0,"time":39800,"type":0},{"id":3,"sLen":0,"time":39900,"type":0},{"id":2,"sLen":0,"time":40000,"type":0},{"id":3,"sLen":0,"time":40200,"type":0},{"id":1,"sLen":0,"time":40400,"type":0},{"id":0,"sLen":0,"time":40600,"type":0},{"id":1,"sLen":0,"time":40800,"type":0},{"id":0,"sLen":0,"time":40900,"type":0},{"id":2,"sLen":100,"time":41000,"type":0},{"id":3,"sLen":0,"time":41200,"type":0},{"id":1,"sLen":0,"time":41400,"type":0},{"id":3,"sLen":400,"time":41600,"type":0},{"id":0,"sLen":0,"time":43200,"type":0},{"id":2,"sLen":0,"time":43300,"type":0},{"id":3,"sLen":0,"time":43400,"type":0},{"id":2,"sLen":0,"time":43600,"type":0},{"id":0,"sLen":0,"time":43800,"type":0},{"id":1,"sLen":0,"time":44000,"type":0},{"id":0,"sLen":0,"time":44200,"type":0},{"id":1,"sLen":0,"time":44400,"type":0},{"id":3,"sLen":0,"time":44600,"type":0},{"id":3,"sLen":400,"time":44800,"type":0},{"id":0,"sLen":0,"time":46400,"type":0},{"id":2,"sLen":0,"time":46500,"type":0},{"id":3,"sLen":0,"time":46600,"type":0},{"id":2,"sLen":0,"time":46800,"type":0},{"id":0,"sLen":0,"time":47000,"type":0},{"id":1,"sLen":0,"time":47200,"type":0},{"id":0,"sLen":0,"time":47400,"type":0},{"id":1,"sLen":0,"time":47600,"type":0},{"id":3,"sLen":0,"time":47800,"type":0},{"id":3,"sLen":400,"time":48000,"type":0},{"id":1,"sLen":0,"time":54599.9961,"type":0},{"id":1,"sLen":0,"time":54999.9961,"type":0},{"id":1,"sLen":0,"time":55399.9961,"type":0},{"id":2,"sLen":0,"time":55599.9961,"type":0},{"id":0,"sLen":0,"time":55699.9961,"type":0},{"id":3,"sLen":0,"time":55799.9961,"type":0},{"id":1,"sLen":0,"time":56199.9961,"type":0},{"id":1,"sLen":0,"time":56599.9961,"type":0},{"id":1,"sLen":0,"time":56999.9961,"type":0},{"id":3,"sLen":0,"time":57199.9961,"type":0},{"id":0,"sLen":0,"time":57299.9961,"type":0},{"id":3,"sLen":0,"time":57399.9961,"type":0},{"id":1,"sLen":0,"time":57799.9961,"type":0},{"id":1,"sLen":0,"time":58199.9961,"type":0},{"id":1,"sLen":0,"time":58599.9961,"type":0},{"id":2,"sLen":0,"time":58799.9961,"type":0},{"id":0,"sLen":0,"time":58899.9961,"type":0},{"id":3,"sLen":0,"time":58999.9961,"type":0},{"id":1,"sLen":0,"time":59399.9961,"type":0},{"id":1,"sLen":0,"time":59799.9961,"type":0},{"id":1,"sLen":0,"time":60199.9961,"type":0},{"id":3,"sLen":0,"time":60399.9961,"type":0},{"id":0,"sLen":0,"time":60499.9961,"type":0},{"id":3,"sLen":0,"time":60599.9961,"type":0},{"id":2,"sLen":0,"time":63999.9961,"type":0},{"id":3,"sLen":0,"time":64199.9961,"type":0},{"id":0,"sLen":0,"time":64399.9961,"type":0},{"id":3,"sLen":0,"time":64599.9961,"type":0},{"id":1,"sLen":0,"time":64799.9961,"type":0},{"id":0,"sLen":0,"time":64999.9961,"type":0},{"id":1,"sLen":0,"time":65199.9961,"type":0},{"id":3,"sLen":0,"time":65399.9961,"type":0},{"id":0,"sLen":0,"time":65600,"type":0},{"id":3,"sLen":0,"time":65700,"type":0},{"id":1,"sLen":0,"time":65800,"type":0},{"id":0,"sLen":0,"time":66000,"type":0},{"id":1,"sLen":0,"time":66100,"type":0},{"id":3,"sLen":100,"time":66200,"type":0},{"id":1,"sLen":0,"time":66400,"type":0},{"id":2,"sLen":0,"time":66600,"type":0},{"id":3,"sLen":200,"time":66800,"type":0},{"id":1,"sLen":0,"time":70400,"type":0},{"id":3,"sLen":0,"time":70600,"type":0},{"id":0,"sLen":200,"time":70800,"type":0},{"id":1,"sLen":0,"time":71200,"type":0},{"id":0,"sLen":0,"time":71400,"type":0},{"id":1,"sLen":0,"time":71600,"type":0},{"id":2,"sLen":0,"time":71800,"type":0},{"id":3,"sLen":0,"time":72000,"type":0},{"id":0,"sLen":0,"time":72100,"type":0},{"id":3,"sLen":0,"time":72200,"type":0},{"id":1,"sLen":0,"time":72400,"type":0},{"id":0,"sLen":0,"time":72500,"type":0},{"id":1,"sLen":0,"time":72600,"type":0},{"id":0,"sLen":0,"time":72700,"type":0},{"id":2,"sLen":300,"time":72800,"type":0},{"id":2,"sLen":300,"time":73200,"type":0},{"id":2,"sLen":0,"time":73600,"type":0},{"id":0,"sLen":0,"time":73700,"type":0},{"id":3,"sLen":100,"time":73800,"type":0},{"id":1,"sLen":0,"time":74000,"type":0},{"id":2,"sLen":200,"time":74100,"type":0},{"id":3,"sLen":100,"time":74400,"type":0},{"id":0,"sLen":0,"time":74800,"type":0},{"id":2,"sLen":0,"time":75000,"type":0},{"id":0,"sLen":0,"time":75400,"type":0},{"id":0,"sLen":0,"time":75800,"type":0},{"id":0,"sLen":0,"time":76200,"type":0},{"id":1,"sLen":0,"time":76300,"type":0},{"id":2,"sLen":0,"time":76400,"type":0},{"id":3,"sLen":400,"time":76600,"type":0},{"id":1,"sLen":100,"time":77200,"type":0},{"id":3,"sLen":100,"time":77600,"type":0},{"id":1,"sLen":0,"time":78000,"type":0},{"id":0,"sLen":0,"time":78200,"type":0},{"id":0,"sLen":0,"time":78600,"type":0},{"id":1,"sLen":0,"time":79000,"type":0},{"id":1,"sLen":0,"time":79400,"type":0},{"id":2,"sLen":300,"time":79600,"type":0},{"id":3,"sLen":0,"time":80200,"type":0},{"id":1,"sLen":0,"time":80400,"type":0},{"id":0,"sLen":0,"time":80600,"type":0},{"id":3,"sLen":0,"time":80700,"type":0},{"id":2,"sLen":0,"time":80800,"type":0},{"id":3,"sLen":0,"time":81000,"type":0},{"id":1,"sLen":0,"time":81200,"type":0},{"id":0,"sLen":0,"time":81400,"type":0},{"id":3,"sLen":0,"time":81500,"type":0},{"id":2,"sLen":0,"time":81600,"type":0},{"id":3,"sLen":0,"time":81800,"type":0},{"id":1,"sLen":0,"time":82000,"type":0},{"id":0,"sLen":0,"time":82200,"type":0},{"id":3,"sLen":0,"time":82300,"type":0},{"id":2,"sLen":0,"time":82400,"type":0},{"id":3,"sLen":0,"time":82600,"type":0},{"id":3,"sLen":0,"time":82800,"type":0},{"id":0,"sLen":0,"time":83000,"type":0},{"id":3,"sLen":0,"time":83200,"type":0},{"id":3,"sLen":0,"time":83400,"type":0},{"id":1,"sLen":0,"time":83600,"type":0},{"id":0,"sLen":0,"time":83800,"type":0},{"id":3,"sLen":0,"time":83900,"type":0},{"id":2,"sLen":0,"time":84000,"type":0},{"id":3,"sLen":0,"time":84200,"type":0},{"id":1,"sLen":0,"time":84400,"type":0},{"id":0,"sLen":0,"time":84600,"type":0},{"id":3,"sLen":0,"time":84700,"type":0},{"id":2,"sLen":0,"time":84800,"type":0},{"id":3,"sLen":0,"time":85000,"type":0},{"id":1,"sLen":0,"time":85200,"type":0},{"id":0,"sLen":0,"time":85400,"type":0},{"id":1,"sLen":0,"time":85600,"type":0},{"id":0,"sLen":0,"time":85700,"type":0},{"id":2,"sLen":100,"time":85800,"type":0},{"id":3,"sLen":0,"time":86000,"type":0},{"id":1,"sLen":0,"time":86200,"type":0},{"id":3,"sLen":400,"time":86400,"type":0},{"id":0,"sLen":0,"time":88000,"type":0},{"id":2,"sLen":0,"time":88100,"type":0},{"id":3,"sLen":0,"time":88200,"type":0},{"id":2,"sLen":0,"time":88400,"type":0},{"id":0,"sLen":0,"time":88600,"type":0},{"id":1,"sLen":0,"time":88800,"type":0},{"id":0,"sLen":0,"time":89000,"type":0},{"id":1,"sLen":0,"time":89200,"type":0},{"id":3,"sLen":0,"time":89400,"type":0},{"id":3,"sLen":400,"time":89600,"type":0},{"id":0,"sLen":0,"time":91200,"type":0},{"id":2,"sLen":0,"time":91300,"type":0},{"id":3,"sLen":0,"time":91400,"type":0},{"id":2,"sLen":0,"time":91600,"type":0},{"id":0,"sLen":0,"time":91800,"type":0},{"id":1,"sLen":0,"time":92000,"type":0},{"id":0,"sLen":0,"time":92200,"type":0},{"id":1,"sLen":0,"time":92400,"type":0},{"id":3,"sLen":0,"time":92600,"type":0},{"id":3,"sLen":800,"time":92800,"type":0}],"position":"boyfriend","type":1,"characters":["bf-christmas"]},{"keyCount":4,"notes":[],"visible":false,"position":"girlfriend","type":2,"characters":["gf-christmas"]}],"codenameChart":true,"scrollSpeed":1.9,"chartVersion":"1.6.0","stage":"mall","noteTypes":[]} \ No newline at end of file diff --git a/assets/songs/eggnog/charts/normal.json b/assets/songs/eggnog/charts/normal.json index 12973852..609cf537 100644 --- a/assets/songs/eggnog/charts/normal.json +++ b/assets/songs/eggnog/charts/normal.json @@ -1 +1 @@ -{"song":{"gf":"gf-christmas","stage":"mall","song":"Eggnog","bpm":150.0,"needsVoices":true,"player1":"bf-christmas","player2":"parents-christmas","speed":1.6,"notes":[{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[3399.99976,1,0.0],[3799.99976,1,0.0],[4200.0,1,0.0],[4400.0,3,0.0],[4600.0,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[5000.0,1,0.0],[5400.0,1,0.0],[5600.0,3,0.0],[5800.0,2,0.0],[6000.0,3,0.0],[6200.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[6599.99951,1,0.0],[6999.99951,1,0.0],[7399.99951,1,0.0],[7599.99951,3,0.0],[7799.99951,2,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[8200.0,1,0.0],[8600.0,1,0.0],[8800.0,3,0.0],[9000.0,2,0.0],[9400.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[9800.0,1,0.0],[10200.0,1,0.0],[10600.0,1,0.0],[10800.0,3,0.0],[11000.0,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[11400.0,1,0.0],[11800.0,1,0.0],[12000.0,3,0.0],[12200.0,2,0.0],[12400.0,3,0.0],[12600.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[12999.999,1,0.0],[13399.999,1,0.0],[13799.999,1,0.0],[13999.999,3,0.0],[14199.999,2,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[14599.999,1,0.0],[14999.999,1,0.0],[15199.999,3,0.0],[15399.999,2,0.0],[15799.999,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[15999.999,2,0.0],[16199.999,3,100.0],[16400.0,1,0.0],[16800.0,3,100.0],[17200.0,0,0.0],[17400.0,2,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[17800.0,0,0.0],[18200.0,0,0.0],[18600.0,0,0.0],[19000.0,3,400.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[19600.0,1,100.0],[20000.0,3,100.0],[20400.0,1,0.0],[20600.0,0,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[21000.0,0,0.0],[21400.0,1,0.0],[21800.0,1,0.0],[22000.0,2,300.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[22400.0,4,400.0],[22400.0,2,0.0],[22600.0,3,100.0],[22800.0,1,0.0],[23200.0,3,100.0],[23600.0,0,0.0],[23800.0,2,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[24200.0,0,0.0],[24600.0,0,0.0],[25000.0,0,0.0],[25400.0,3,400.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[25999.998,1,100.0],[26399.998,3,100.0],[26799.998,1,0.0],[26999.998,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[27399.998,0,0.0],[27799.998,1,0.0],[28199.998,1,0.0],[28399.998,2,300.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[28799.998,4,400.0],[28999.998,3,0.0],[29199.998,1,0.0],[29399.998,0,0.0],[29499.998,3,0.0],[29799.998,3,0.0],[29999.998,1,0.0],[30199.998,0,0.0],[30299.998,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[30599.998,3,0.0],[30799.998,1,0.0],[30999.998,0,0.0],[31099.998,3,0.0],[31399.998,3,0.0],[31599.998,3,0.0],[31799.998,0,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[31999.998,3,0.0],[32199.998,3,0.0],[32399.998,1,0.0],[32599.998,0,0.0],[32699.998,3,0.0],[33000.0,3,0.0],[33200.0,1,0.0],[33400.0,0,0.0],[33500.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[33800.0,3,0.0],[34000.0,1,0.0],[34200.0,0,0.0],[34400.0,1,0.0],[34500.0,0,0.0],[34800.0,3,0.0],[35000.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[35200.0,7,400.0],[35400.0,3,0.0],[35600.0,1,0.0],[35800.0,0,0.0],[35900.0,3,0.0],[36200.0,3,0.0],[36400.0,1,0.0],[36600.0,0,0.0],[36700.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[37000.0,3,0.0],[37200.0,1,0.0],[37400.0,0,0.0],[37500.0,3,0.0],[37800.0,3,0.0],[38000.0,3,0.0],[38200.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[38400.0,3,0.0],[38600.0,3,0.0],[38800.0,1,0.0],[39000.0,0,0.0],[39100.0,3,0.0],[39400.0,3,0.0],[39600.0,1,0.0],[39800.0,0,0.0],[39900.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[40200.0,3,0.0],[40400.0,1,0.0],[40600.0,0,0.0],[40800.0,1,0.0],[41000.0,2,100.0],[41200.0,3,0.0],[41400.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[41600.0,0,0.0],[41600.0,7,400.0],[41800.0,3,0.0],[42000.0,3,0.0],[42200.0,0,0.0],[42600.0,0,0.0],[43000.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[43200.0,7,400.0],[43200.0,0,0.0],[43400.0,3,0.0],[43600.0,3,0.0],[43800.0,0,0.0],[44200.0,0,0.0],[44600.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[44800.0,7,400.0],[44800.0,0,0.0],[45000.0,3,0.0],[45200.0,3,0.0],[45400.0,0,0.0],[45800.0,0,0.0],[46200.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[46400.0,7,400.0],[46400.0,0,0.0],[46600.0,3,0.0],[46800.0,3,0.0],[47000.0,0,0.0],[47400.0,0,0.0],[47800.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[48000.0,7,400.0],[48200.0,1,0.0],[48600.0,1,0.0],[49000.0,1,0.0],[49200.0,2,0.0],[49400.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[49800.0,1,0.0],[50200.0,1,0.0],[50600.0,1,0.0],[50800.0,3,0.0],[50900.0,0,0.0],[51000.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[51399.9961,1,0.0],[51799.9961,1,0.0],[52199.9961,1,0.0],[52399.9961,2,0.0],[52599.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[52999.9961,1,0.0],[53399.9961,1,0.0],[53799.9961,1,0.0],[53999.9961,3,0.0],[54099.9961,0,0.0],[54199.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[54599.9961,1,0.0],[54999.9961,1,0.0],[55399.9961,1,0.0],[55599.9961,2,0.0],[55799.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[56199.9961,1,0.0],[56599.9961,1,0.0],[56999.9961,1,0.0],[57199.9961,3,0.0],[57299.9961,0,0.0],[57399.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[57799.9961,1,0.0],[58199.9961,1,0.0],[58599.9961,1,0.0],[58799.9961,2,0.0],[58999.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[59399.9961,1,0.0],[59799.9961,1,0.0],[60199.9961,1,0.0],[60399.9961,3,0.0],[60499.9961,0,0.0],[60599.9961,3,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[60799.9961,2,0.0],[60999.9961,3,0.0],[61199.9961,0,0.0],[61599.9961,1,0.0],[61799.9961,0,0.0],[61999.9961,1,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[62399.9961,0,0.0],[62599.9961,1,0.0],[62799.9961,0,0.0],[62999.9961,3,100.0],[63199.9961,1,0.0],[63399.9961,2,0.0],[63599.9961,3,200.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[63999.9961,2,0.0],[64199.9961,3,0.0],[64399.9961,0,0.0],[64799.9961,1,0.0],[64999.9961,0,0.0],[65199.9961,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[65600.0,0,0.0],[65800.0,1,0.0],[66000.0,0,0.0],[66200.0,3,100.0],[66400.0,1,0.0],[66600.0,2,0.0],[66800.0,3,200.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[67200.0,1,0.0],[67400.0,3,0.0],[67600.0,0,200.0],[68000.0,1,0.0],[68200.0,1,0.0],[68400.0,1,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[68800.0,3,0.0],[68900.0,0,100.0],[69200.0,1,0.0],[69400.0,1,0.0],[69600.0,2,300.0],[70000.0,2,300.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[70400.0,1,0.0],[70600.0,3,0.0],[70800.0,0,200.0],[71200.0,1,0.0],[71400.0,0,0.0],[71600.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[72000.0,3,0.0],[72100.0,0,100.0],[72400.0,1,0.0],[72600.0,1,0.0],[72800.0,2,300.0],[73200.0,2,300.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[73600.0,2,0.0],[73800.0,7,0.0],[73800.0,3,100.0],[74000.0,5,0.0],[74000.0,1,0.0],[74200.0,4,0.0],[74300.0,7,0.0],[74400.0,3,100.0],[74600.0,7,0.0],[74800.0,5,0.0],[74800.0,0,0.0],[75000.0,4,0.0],[75000.0,2,0.0],[75100.0,7,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[75400.0,7,0.0],[75400.0,0,0.0],[75600.0,5,0.0],[75800.0,4,0.0],[75800.0,0,0.0],[75900.0,7,0.0],[76200.0,7,0.0],[76200.0,0,0.0],[76400.0,7,0.0],[76600.0,4,0.0],[76600.0,3,400.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[76800.0,7,0.0],[77000.0,7,0.0],[77200.0,5,0.0],[77200.0,1,100.0],[77400.0,4,0.0],[77500.0,7,0.0],[77600.0,3,100.0],[77800.0,7,0.0],[78000.0,5,0.0],[78000.0,1,0.0],[78200.0,4,0.0],[78200.0,0,0.0],[78300.0,7,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[78600.0,7,0.0],[78600.0,0,0.0],[78800.0,5,0.0],[79000.0,4,0.0],[79000.0,1,0.0],[79200.0,5,0.0],[79300.0,4,0.0],[79400.0,1,0.0],[79600.0,7,0.0],[79600.0,2,300.0],[79800.0,5,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[80000.0,7,400.0],[80200.0,3,0.0],[80400.0,1,0.0],[80600.0,0,0.0],[80700.0,3,0.0],[81000.0,3,0.0],[81200.0,1,0.0],[81400.0,0,0.0],[81500.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[81800.0,3,0.0],[82000.0,1,0.0],[82200.0,0,0.0],[82300.0,3,0.0],[82600.0,3,0.0],[82800.0,3,0.0],[83000.0,0,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[83200.0,3,0.0],[83400.0,3,0.0],[83600.0,1,0.0],[83800.0,0,0.0],[83900.0,3,0.0],[84200.0,3,0.0],[84400.0,1,0.0],[84600.0,0,0.0],[84700.0,3,0.0]]},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[85000.0,3,0.0],[85200.0,1,0.0],[85400.0,0,0.0],[85600.0,1,0.0],[85700.0,0,0.0],[86000.0,3,0.0],[86200.0,1,0.0]]},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[86400.0,0,0.0],[86400.0,7,400.0],[86600.0,3,0.0],[86800.0,3,0.0],[87000.0,0,0.0],[87400.0,0,0.0],[87800.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[88000.0,7,400.0],[88000.0,0,0.0],[88200.0,3,0.0],[88400.0,3,0.0],[88600.0,0,0.0],[89000.0,0,0.0],[89400.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":false,"sectionNotes":[[89600.0,7,400.0],[89600.0,0,0.0],[89800.0,3,0.0],[90000.0,3,0.0],[90200.0,0,0.0],[90600.0,0,0.0],[91000.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[91200.0,7,400.0],[91200.0,0,0.0],[91400.0,3,0.0],[91600.0,3,0.0],[91800.0,0,0.0],[92200.0,0,0.0],[92600.0,3,0.0]],"altAnim":true},{"lengthInSteps":16,"mustHitSection":true,"sectionNotes":[[92800.0,3,800.0]]}]},"generatedBy":"SNIFF ver.6"} \ No newline at end of file +{"events":[{"params":[1],"time":0,"name":"Camera Movement"},{"params":[0],"time":3200,"name":"Camera Movement"},{"params":[1],"time":9600,"name":"Camera Movement"},{"params":[0],"time":16000,"name":"Camera Movement"},{"params":[1],"time":22400,"name":"Camera Movement"},{"params":[0],"time":28800,"name":"Camera Movement"},{"params":[1],"time":35200,"name":"Camera Movement"},{"params":[0],"time":41600,"name":"Camera Movement"},{"params":[1],"time":43200,"name":"Camera Movement"},{"params":[0],"time":44800,"name":"Camera Movement"},{"params":[1],"time":46400,"name":"Camera Movement"},{"params":[0],"time":48000,"name":"Camera Movement"},{"params":[1],"time":54400,"name":"Camera Movement"},{"params":[0],"time":60800,"name":"Camera Movement"},{"params":[1],"time":64000,"name":"Camera Movement"},{"params":[0],"time":67200,"name":"Camera Movement"},{"params":[1],"time":70400,"name":"Camera Movement"},{"params":[0],"time":86400,"name":"Camera Movement"},{"params":[1],"time":88000,"name":"Camera Movement"},{"params":[0],"time":89600,"name":"Camera Movement"},{"params":[1],"time":91200,"name":"Camera Movement"}],"strumLines":[{"visible":true,"keyCount":4,"notes":[{"id":1,"sLen":0,"time":3399.99976,"type":0},{"id":1,"sLen":0,"time":3799.99976,"type":0},{"id":1,"sLen":0,"time":4200,"type":0},{"id":3,"sLen":0,"time":4400,"type":0},{"id":2,"sLen":0,"time":4600,"type":0},{"id":1,"sLen":0,"time":5000,"type":0},{"id":1,"sLen":0,"time":5400,"type":0},{"id":3,"sLen":0,"time":5600,"type":0},{"id":2,"sLen":0,"time":5800,"type":0},{"id":3,"sLen":0,"time":6000,"type":0},{"id":1,"sLen":0,"time":6200,"type":0},{"id":1,"sLen":0,"time":6599.99951,"type":0},{"id":1,"sLen":0,"time":6999.99951,"type":0},{"id":1,"sLen":0,"time":7399.99951,"type":0},{"id":3,"sLen":0,"time":7599.99951,"type":0},{"id":2,"sLen":0,"time":7799.99951,"type":0},{"id":1,"sLen":0,"time":8200,"type":0},{"id":1,"sLen":0,"time":8600,"type":0},{"id":3,"sLen":0,"time":8800,"type":0},{"id":2,"sLen":0,"time":9000,"type":0},{"id":1,"sLen":0,"time":9400,"type":0},{"id":2,"sLen":0,"time":15999.999,"type":0},{"id":3,"sLen":100,"time":16199.999,"type":0},{"id":1,"sLen":0,"time":16400,"type":0},{"id":3,"sLen":100,"time":16800,"type":0},{"id":0,"sLen":0,"time":17200,"type":0},{"id":2,"sLen":0,"time":17400,"type":0},{"id":0,"sLen":0,"time":17800,"type":0},{"id":0,"sLen":0,"time":18200,"type":0},{"id":0,"sLen":0,"time":18600,"type":0},{"id":3,"sLen":400,"time":19000,"type":0},{"id":1,"sLen":100,"time":19600,"type":0},{"id":3,"sLen":100,"time":20000,"type":0},{"id":1,"sLen":0,"time":20400,"type":0},{"id":0,"sLen":0,"time":20600,"type":0},{"id":0,"sLen":0,"time":21000,"type":0},{"id":1,"sLen":0,"time":21400,"type":0},{"id":1,"sLen":0,"time":21800,"type":0},{"id":2,"sLen":300,"time":22000,"type":0},{"id":0,"sLen":400,"time":22400,"type":0},{"id":3,"sLen":0,"time":28999.998,"type":0},{"id":1,"sLen":0,"time":29199.998,"type":0},{"id":0,"sLen":0,"time":29399.998,"type":0},{"id":3,"sLen":0,"time":29499.998,"type":0},{"id":3,"sLen":0,"time":29799.998,"type":0},{"id":1,"sLen":0,"time":29999.998,"type":0},{"id":0,"sLen":0,"time":30199.998,"type":0},{"id":3,"sLen":0,"time":30299.998,"type":0},{"id":3,"sLen":0,"time":30599.998,"type":0},{"id":1,"sLen":0,"time":30799.998,"type":0},{"id":0,"sLen":0,"time":30999.998,"type":0},{"id":3,"sLen":0,"time":31099.998,"type":0},{"id":3,"sLen":0,"time":31399.998,"type":0},{"id":3,"sLen":0,"time":31599.998,"type":0},{"id":0,"sLen":0,"time":31799.998,"type":0},{"id":3,"sLen":0,"time":31999.998,"type":0},{"id":3,"sLen":0,"time":32199.998,"type":0},{"id":1,"sLen":0,"time":32399.998,"type":0},{"id":0,"sLen":0,"time":32599.998,"type":0},{"id":3,"sLen":0,"time":32699.998,"type":0},{"id":3,"sLen":0,"time":33000,"type":0},{"id":1,"sLen":0,"time":33200,"type":0},{"id":0,"sLen":0,"time":33400,"type":0},{"id":3,"sLen":0,"time":33500,"type":0},{"id":3,"sLen":0,"time":33800,"type":0},{"id":1,"sLen":0,"time":34000,"type":0},{"id":0,"sLen":0,"time":34200,"type":0},{"id":1,"sLen":0,"time":34400,"type":0},{"id":0,"sLen":0,"time":34500,"type":0},{"id":3,"sLen":0,"time":34800,"type":0},{"id":1,"sLen":0,"time":35000,"type":0},{"id":3,"sLen":400,"time":35200,"type":0},{"id":0,"sLen":0,"time":41600,"type":0},{"id":3,"sLen":0,"time":41800,"type":0},{"id":3,"sLen":0,"time":42000,"type":0},{"id":0,"sLen":0,"time":42200,"type":0},{"id":0,"sLen":0,"time":42600,"type":0},{"id":3,"sLen":0,"time":43000,"type":0},{"id":3,"sLen":400,"time":43200,"type":0},{"id":0,"sLen":0,"time":44800,"type":0},{"id":3,"sLen":0,"time":45000,"type":0},{"id":3,"sLen":0,"time":45200,"type":0},{"id":0,"sLen":0,"time":45400,"type":0},{"id":0,"sLen":0,"time":45800,"type":0},{"id":3,"sLen":0,"time":46200,"type":0},{"id":3,"sLen":400,"time":46400,"type":0},{"id":1,"sLen":0,"time":48200,"type":0},{"id":1,"sLen":0,"time":48600,"type":0},{"id":1,"sLen":0,"time":49000,"type":0},{"id":2,"sLen":0,"time":49200,"type":0},{"id":3,"sLen":0,"time":49400,"type":0},{"id":1,"sLen":0,"time":49800,"type":0},{"id":1,"sLen":0,"time":50200,"type":0},{"id":1,"sLen":0,"time":50600,"type":0},{"id":3,"sLen":0,"time":50800,"type":0},{"id":0,"sLen":0,"time":50900,"type":0},{"id":3,"sLen":0,"time":51000,"type":0},{"id":1,"sLen":0,"time":51399.9961,"type":0},{"id":1,"sLen":0,"time":51799.9961,"type":0},{"id":1,"sLen":0,"time":52199.9961,"type":0},{"id":2,"sLen":0,"time":52399.9961,"type":0},{"id":3,"sLen":0,"time":52599.9961,"type":0},{"id":1,"sLen":0,"time":52999.9961,"type":0},{"id":1,"sLen":0,"time":53399.9961,"type":0},{"id":1,"sLen":0,"time":53799.9961,"type":0},{"id":3,"sLen":0,"time":53999.9961,"type":0},{"id":0,"sLen":0,"time":54099.9961,"type":0},{"id":3,"sLen":0,"time":54199.9961,"type":0},{"id":2,"sLen":0,"time":60799.9961,"type":0},{"id":3,"sLen":0,"time":60999.9961,"type":0},{"id":0,"sLen":0,"time":61199.9961,"type":0},{"id":1,"sLen":0,"time":61599.9961,"type":0},{"id":0,"sLen":0,"time":61799.9961,"type":0},{"id":1,"sLen":0,"time":61999.9961,"type":0},{"id":0,"sLen":0,"time":62399.9961,"type":0},{"id":1,"sLen":0,"time":62599.9961,"type":0},{"id":0,"sLen":0,"time":62799.9961,"type":0},{"id":3,"sLen":100,"time":62999.9961,"type":0},{"id":1,"sLen":0,"time":63199.9961,"type":0},{"id":2,"sLen":0,"time":63399.9961,"type":0},{"id":3,"sLen":200,"time":63599.9961,"type":0},{"id":1,"sLen":0,"time":67200,"type":0},{"id":3,"sLen":0,"time":67400,"type":0},{"id":0,"sLen":200,"time":67600,"type":0},{"id":1,"sLen":0,"time":68000,"type":0},{"id":1,"sLen":0,"time":68200,"type":0},{"id":1,"sLen":0,"time":68400,"type":0},{"id":3,"sLen":0,"time":68800,"type":0},{"id":0,"sLen":100,"time":68900,"type":0},{"id":1,"sLen":0,"time":69200,"type":0},{"id":1,"sLen":0,"time":69400,"type":0},{"id":2,"sLen":300,"time":69600,"type":0},{"id":2,"sLen":300,"time":70000,"type":0},{"id":3,"sLen":0,"time":73800,"type":0},{"id":1,"sLen":0,"time":74000,"type":0},{"id":0,"sLen":0,"time":74200,"type":0},{"id":3,"sLen":0,"time":74300,"type":0},{"id":3,"sLen":0,"time":74600,"type":0},{"id":1,"sLen":0,"time":74800,"type":0},{"id":0,"sLen":0,"time":75000,"type":0},{"id":3,"sLen":0,"time":75100,"type":0},{"id":3,"sLen":0,"time":75400,"type":0},{"id":1,"sLen":0,"time":75600,"type":0},{"id":0,"sLen":0,"time":75800,"type":0},{"id":3,"sLen":0,"time":75900,"type":0},{"id":3,"sLen":0,"time":76200,"type":0},{"id":3,"sLen":0,"time":76400,"type":0},{"id":0,"sLen":0,"time":76600,"type":0},{"id":3,"sLen":0,"time":76800,"type":0},{"id":3,"sLen":0,"time":77000,"type":0},{"id":1,"sLen":0,"time":77200,"type":0},{"id":0,"sLen":0,"time":77400,"type":0},{"id":3,"sLen":0,"time":77500,"type":0},{"id":3,"sLen":0,"time":77800,"type":0},{"id":1,"sLen":0,"time":78000,"type":0},{"id":0,"sLen":0,"time":78200,"type":0},{"id":3,"sLen":0,"time":78300,"type":0},{"id":3,"sLen":0,"time":78600,"type":0},{"id":1,"sLen":0,"time":78800,"type":0},{"id":0,"sLen":0,"time":79000,"type":0},{"id":1,"sLen":0,"time":79200,"type":0},{"id":0,"sLen":0,"time":79300,"type":0},{"id":3,"sLen":0,"time":79600,"type":0},{"id":1,"sLen":0,"time":79800,"type":0},{"id":3,"sLen":400,"time":80000,"type":0},{"id":0,"sLen":0,"time":86400,"type":0},{"id":3,"sLen":0,"time":86600,"type":0},{"id":3,"sLen":0,"time":86800,"type":0},{"id":0,"sLen":0,"time":87000,"type":0},{"id":0,"sLen":0,"time":87400,"type":0},{"id":3,"sLen":0,"time":87800,"type":0},{"id":3,"sLen":400,"time":88000,"type":0},{"id":0,"sLen":0,"time":89600,"type":0},{"id":3,"sLen":0,"time":89800,"type":0},{"id":3,"sLen":0,"time":90000,"type":0},{"id":0,"sLen":0,"time":90200,"type":0},{"id":0,"sLen":0,"time":90600,"type":0},{"id":3,"sLen":0,"time":91000,"type":0},{"id":3,"sLen":400,"time":91200,"type":0}],"position":"dad","type":0,"characters":["parents-christmas"]},{"visible":true,"keyCount":4,"notes":[{"id":1,"sLen":0,"time":9800,"type":0},{"id":1,"sLen":0,"time":10200,"type":0},{"id":1,"sLen":0,"time":10600,"type":0},{"id":3,"sLen":0,"time":10800,"type":0},{"id":2,"sLen":0,"time":11000,"type":0},{"id":1,"sLen":0,"time":11400,"type":0},{"id":1,"sLen":0,"time":11800,"type":0},{"id":3,"sLen":0,"time":12000,"type":0},{"id":2,"sLen":0,"time":12200,"type":0},{"id":3,"sLen":0,"time":12400,"type":0},{"id":1,"sLen":0,"time":12600,"type":0},{"id":1,"sLen":0,"time":12999.999,"type":0},{"id":1,"sLen":0,"time":13399.999,"type":0},{"id":1,"sLen":0,"time":13799.999,"type":0},{"id":3,"sLen":0,"time":13999.999,"type":0},{"id":2,"sLen":0,"time":14199.999,"type":0},{"id":1,"sLen":0,"time":14599.999,"type":0},{"id":1,"sLen":0,"time":14999.999,"type":0},{"id":3,"sLen":0,"time":15199.999,"type":0},{"id":2,"sLen":0,"time":15399.999,"type":0},{"id":1,"sLen":0,"time":15799.999,"type":0},{"id":2,"sLen":0,"time":22400,"type":0},{"id":3,"sLen":100,"time":22600,"type":0},{"id":1,"sLen":0,"time":22800,"type":0},{"id":3,"sLen":100,"time":23200,"type":0},{"id":0,"sLen":0,"time":23600,"type":0},{"id":2,"sLen":0,"time":23800,"type":0},{"id":0,"sLen":0,"time":24200,"type":0},{"id":0,"sLen":0,"time":24600,"type":0},{"id":0,"sLen":0,"time":25000,"type":0},{"id":3,"sLen":400,"time":25400,"type":0},{"id":1,"sLen":100,"time":25999.998,"type":0},{"id":3,"sLen":100,"time":26399.998,"type":0},{"id":1,"sLen":0,"time":26799.998,"type":0},{"id":0,"sLen":0,"time":26999.998,"type":0},{"id":0,"sLen":0,"time":27399.998,"type":0},{"id":1,"sLen":0,"time":27799.998,"type":0},{"id":1,"sLen":0,"time":28199.998,"type":0},{"id":2,"sLen":300,"time":28399.998,"type":0},{"id":0,"sLen":400,"time":28799.998,"type":0},{"id":3,"sLen":0,"time":35400,"type":0},{"id":1,"sLen":0,"time":35600,"type":0},{"id":0,"sLen":0,"time":35800,"type":0},{"id":3,"sLen":0,"time":35900,"type":0},{"id":3,"sLen":0,"time":36200,"type":0},{"id":1,"sLen":0,"time":36400,"type":0},{"id":0,"sLen":0,"time":36600,"type":0},{"id":3,"sLen":0,"time":36700,"type":0},{"id":3,"sLen":0,"time":37000,"type":0},{"id":1,"sLen":0,"time":37200,"type":0},{"id":0,"sLen":0,"time":37400,"type":0},{"id":3,"sLen":0,"time":37500,"type":0},{"id":3,"sLen":0,"time":37800,"type":0},{"id":3,"sLen":0,"time":38000,"type":0},{"id":0,"sLen":0,"time":38200,"type":0},{"id":3,"sLen":0,"time":38400,"type":0},{"id":3,"sLen":0,"time":38600,"type":0},{"id":1,"sLen":0,"time":38800,"type":0},{"id":0,"sLen":0,"time":39000,"type":0},{"id":3,"sLen":0,"time":39100,"type":0},{"id":3,"sLen":0,"time":39400,"type":0},{"id":1,"sLen":0,"time":39600,"type":0},{"id":0,"sLen":0,"time":39800,"type":0},{"id":3,"sLen":0,"time":39900,"type":0},{"id":3,"sLen":0,"time":40200,"type":0},{"id":1,"sLen":0,"time":40400,"type":0},{"id":0,"sLen":0,"time":40600,"type":0},{"id":1,"sLen":0,"time":40800,"type":0},{"id":2,"sLen":100,"time":41000,"type":0},{"id":3,"sLen":0,"time":41200,"type":0},{"id":1,"sLen":0,"time":41400,"type":0},{"id":3,"sLen":400,"time":41600,"type":0},{"id":0,"sLen":0,"time":43200,"type":0},{"id":3,"sLen":0,"time":43400,"type":0},{"id":3,"sLen":0,"time":43600,"type":0},{"id":0,"sLen":0,"time":43800,"type":0},{"id":0,"sLen":0,"time":44200,"type":0},{"id":3,"sLen":0,"time":44600,"type":0},{"id":3,"sLen":400,"time":44800,"type":0},{"id":0,"sLen":0,"time":46400,"type":0},{"id":3,"sLen":0,"time":46600,"type":0},{"id":3,"sLen":0,"time":46800,"type":0},{"id":0,"sLen":0,"time":47000,"type":0},{"id":0,"sLen":0,"time":47400,"type":0},{"id":3,"sLen":0,"time":47800,"type":0},{"id":3,"sLen":400,"time":48000,"type":0},{"id":1,"sLen":0,"time":54599.9961,"type":0},{"id":1,"sLen":0,"time":54999.9961,"type":0},{"id":1,"sLen":0,"time":55399.9961,"type":0},{"id":2,"sLen":0,"time":55599.9961,"type":0},{"id":3,"sLen":0,"time":55799.9961,"type":0},{"id":1,"sLen":0,"time":56199.9961,"type":0},{"id":1,"sLen":0,"time":56599.9961,"type":0},{"id":1,"sLen":0,"time":56999.9961,"type":0},{"id":3,"sLen":0,"time":57199.9961,"type":0},{"id":0,"sLen":0,"time":57299.9961,"type":0},{"id":3,"sLen":0,"time":57399.9961,"type":0},{"id":1,"sLen":0,"time":57799.9961,"type":0},{"id":1,"sLen":0,"time":58199.9961,"type":0},{"id":1,"sLen":0,"time":58599.9961,"type":0},{"id":2,"sLen":0,"time":58799.9961,"type":0},{"id":3,"sLen":0,"time":58999.9961,"type":0},{"id":1,"sLen":0,"time":59399.9961,"type":0},{"id":1,"sLen":0,"time":59799.9961,"type":0},{"id":1,"sLen":0,"time":60199.9961,"type":0},{"id":3,"sLen":0,"time":60399.9961,"type":0},{"id":0,"sLen":0,"time":60499.9961,"type":0},{"id":3,"sLen":0,"time":60599.9961,"type":0},{"id":2,"sLen":0,"time":63999.9961,"type":0},{"id":3,"sLen":0,"time":64199.9961,"type":0},{"id":0,"sLen":0,"time":64399.9961,"type":0},{"id":1,"sLen":0,"time":64799.9961,"type":0},{"id":0,"sLen":0,"time":64999.9961,"type":0},{"id":1,"sLen":0,"time":65199.9961,"type":0},{"id":0,"sLen":0,"time":65600,"type":0},{"id":1,"sLen":0,"time":65800,"type":0},{"id":0,"sLen":0,"time":66000,"type":0},{"id":3,"sLen":100,"time":66200,"type":0},{"id":1,"sLen":0,"time":66400,"type":0},{"id":2,"sLen":0,"time":66600,"type":0},{"id":3,"sLen":200,"time":66800,"type":0},{"id":1,"sLen":0,"time":70400,"type":0},{"id":3,"sLen":0,"time":70600,"type":0},{"id":0,"sLen":200,"time":70800,"type":0},{"id":1,"sLen":0,"time":71200,"type":0},{"id":0,"sLen":0,"time":71400,"type":0},{"id":1,"sLen":0,"time":71600,"type":0},{"id":3,"sLen":0,"time":72000,"type":0},{"id":0,"sLen":100,"time":72100,"type":0},{"id":1,"sLen":0,"time":72400,"type":0},{"id":1,"sLen":0,"time":72600,"type":0},{"id":2,"sLen":300,"time":72800,"type":0},{"id":2,"sLen":300,"time":73200,"type":0},{"id":2,"sLen":0,"time":73600,"type":0},{"id":3,"sLen":100,"time":73800,"type":0},{"id":1,"sLen":0,"time":74000,"type":0},{"id":3,"sLen":100,"time":74400,"type":0},{"id":0,"sLen":0,"time":74800,"type":0},{"id":2,"sLen":0,"time":75000,"type":0},{"id":0,"sLen":0,"time":75400,"type":0},{"id":0,"sLen":0,"time":75800,"type":0},{"id":0,"sLen":0,"time":76200,"type":0},{"id":3,"sLen":400,"time":76600,"type":0},{"id":1,"sLen":100,"time":77200,"type":0},{"id":3,"sLen":100,"time":77600,"type":0},{"id":1,"sLen":0,"time":78000,"type":0},{"id":0,"sLen":0,"time":78200,"type":0},{"id":0,"sLen":0,"time":78600,"type":0},{"id":1,"sLen":0,"time":79000,"type":0},{"id":1,"sLen":0,"time":79400,"type":0},{"id":2,"sLen":300,"time":79600,"type":0},{"id":3,"sLen":0,"time":80200,"type":0},{"id":1,"sLen":0,"time":80400,"type":0},{"id":0,"sLen":0,"time":80600,"type":0},{"id":3,"sLen":0,"time":80700,"type":0},{"id":3,"sLen":0,"time":81000,"type":0},{"id":1,"sLen":0,"time":81200,"type":0},{"id":0,"sLen":0,"time":81400,"type":0},{"id":3,"sLen":0,"time":81500,"type":0},{"id":3,"sLen":0,"time":81800,"type":0},{"id":1,"sLen":0,"time":82000,"type":0},{"id":0,"sLen":0,"time":82200,"type":0},{"id":3,"sLen":0,"time":82300,"type":0},{"id":3,"sLen":0,"time":82600,"type":0},{"id":3,"sLen":0,"time":82800,"type":0},{"id":0,"sLen":0,"time":83000,"type":0},{"id":3,"sLen":0,"time":83200,"type":0},{"id":3,"sLen":0,"time":83400,"type":0},{"id":1,"sLen":0,"time":83600,"type":0},{"id":0,"sLen":0,"time":83800,"type":0},{"id":3,"sLen":0,"time":83900,"type":0},{"id":3,"sLen":0,"time":84200,"type":0},{"id":1,"sLen":0,"time":84400,"type":0},{"id":0,"sLen":0,"time":84600,"type":0},{"id":3,"sLen":0,"time":84700,"type":0},{"id":3,"sLen":0,"time":85000,"type":0},{"id":1,"sLen":0,"time":85200,"type":0},{"id":0,"sLen":0,"time":85400,"type":0},{"id":1,"sLen":0,"time":85600,"type":0},{"id":0,"sLen":0,"time":85700,"type":0},{"id":3,"sLen":0,"time":86000,"type":0},{"id":1,"sLen":0,"time":86200,"type":0},{"id":3,"sLen":400,"time":86400,"type":0},{"id":0,"sLen":0,"time":88000,"type":0},{"id":3,"sLen":0,"time":88200,"type":0},{"id":3,"sLen":0,"time":88400,"type":0},{"id":0,"sLen":0,"time":88600,"type":0},{"id":0,"sLen":0,"time":89000,"type":0},{"id":3,"sLen":0,"time":89400,"type":0},{"id":3,"sLen":400,"time":89600,"type":0},{"id":0,"sLen":0,"time":91200,"type":0},{"id":3,"sLen":0,"time":91400,"type":0},{"id":3,"sLen":0,"time":91600,"type":0},{"id":0,"sLen":0,"time":91800,"type":0},{"id":0,"sLen":0,"time":92200,"type":0},{"id":3,"sLen":0,"time":92600,"type":0},{"id":3,"sLen":800,"time":92800,"type":0}],"position":"boyfriend","type":1,"characters":["bf-christmas"]},{"keyCount":4,"notes":[],"visible":false,"position":"girlfriend","type":2,"characters":["gf-christmas"]}],"scrollSpeed":1.6,"chartVersion":"1.6.0","stage":"mall","codenameChart":true,"noteTypes":[]} \ No newline at end of file diff --git a/assets/songs/eggnog/events.json b/assets/songs/eggnog/events.json new file mode 100644 index 00000000..8d13b58b --- /dev/null +++ b/assets/songs/eggnog/events.json @@ -0,0 +1 @@ +{"events":[{"params":[true,false,0],"time":16000,"name":"Alt Animation Toggle"},{"params":[false,false,0],"time":28800,"name":"Alt Animation Toggle"},{"params":[true,false,0],"time":41600,"name":"Alt Animation Toggle"},{"params":[false,false,0],"time":48000,"name":"Alt Animation Toggle"},{"params":[true,false,0],"time":60800,"name":"Alt Animation Toggle"},{"params":[false,false,0],"time":73600,"name":"Alt Animation Toggle"},{"params":[true,false,0],"time":86400,"name":"Alt Animation Toggle"}]} \ No newline at end of file diff --git a/assets/songs/eggnog/scripts/week5.hx b/assets/songs/eggnog/scripts/week5.hx deleted file mode 100644 index 8c2f4fb3..00000000 --- a/assets/songs/eggnog/scripts/week5.hx +++ /dev/null @@ -1,4 +0,0 @@ -function onNoteCreation(event) { - if (event.mustHit) - event.animSuffix = ""; -} \ No newline at end of file diff --git a/assets/songs/milf/events.json b/assets/songs/milf/events.json new file mode 100644 index 00000000..67952fcd --- /dev/null +++ b/assets/songs/milf/events.json @@ -0,0 +1 @@ +{"events":[{"params":[1,1],"time":56000,"name":"Camera Modulo Change"},{"params":[4,1],"time":66666.6666666667,"name":"Camera Modulo Change"}]} \ No newline at end of file diff --git a/assets/songs/milf/scripts/bump.hx b/assets/songs/milf/scripts/bump.hx deleted file mode 100644 index 51c4a158..00000000 --- a/assets/songs/milf/scripts/bump.hx +++ /dev/null @@ -1,8 +0,0 @@ -function beatHit(curBeat:Int) { - switch(curBeat) { - case 168: - camZoomingInterval = 1; - case 200: - camZoomingInterval = 4; - } -} \ No newline at end of file diff --git a/assets/songs/stress/charts/easy.json b/assets/songs/stress/charts/easy.json index fcd5b581..9b9183f1 100644 --- a/assets/songs/stress/charts/easy.json +++ b/assets/songs/stress/charts/easy.json @@ -1 +1 @@ -{"events":[{"params":[1],"time":5393.25842696629,"name":"Camera Movement"},{"params":[0],"time":10786.5168539326,"name":"Camera Movement"},{"params":[1],"time":14831.4606741573,"name":"Camera Movement"},{"params":[0],"time":21573.0337078652,"name":"Camera Movement"},{"params":[1],"time":24269.6629213483,"name":"Camera Movement"},{"params":[0],"time":26966.2921348315,"name":"Camera Movement"},{"params":[1],"time":28314.606741573,"name":"Camera Movement"},{"params":[0],"time":31011.2359550562,"name":"Camera Movement"},{"params":[1],"time":37752.808988764,"name":"Camera Movement"},{"params":[0],"time":43146.0674157303,"name":"Camera Movement"},{"params":[1],"time":48539.3258426966,"name":"Camera Movement"},{"params":[0],"time":53932.5842696629,"name":"Camera Movement"},{"params":[1],"time":59325.8426966292,"name":"Camera Movement"},{"params":[0],"time":62022.4719101124,"name":"Camera Movement"},{"params":[0,"good!"],"time":62022.4719101124,"name":"Play Animation"},{"params":[1],"time":67415.7303370787,"name":"Camera Movement"},{"params":[0],"time":75505.6179775281,"name":"Camera Movement"},{"params":[1],"time":80898.8764044945,"name":"Camera Movement"},{"params":[0],"time":86292.1348314608,"name":"Camera Movement"},{"params":[1],"time":91685.3932584271,"name":"Camera Movement"},{"params":[0],"time":97078.6516853934,"name":"Camera Movement"},{"params":[1],"time":102471.91011236,"name":"Camera Movement"},{"params":[0],"time":107865.168539326,"name":"Camera Movement"},{"params":[1],"time":113258.426966292,"name":"Camera Movement"}],"scrollSpeed":1.75,"strumLines":[{"notes":[{"id":2,"sLen":0,"time":0,"type":0},{"id":0,"sLen":0,"time":337.078644,"type":0},{"id":0,"sLen":0,"time":674.1573,"type":0},{"id":2,"sLen":0,"time":1011.23596,"type":0},{"id":1,"sLen":0,"time":1348.31458,"type":0},{"id":3,"sLen":0,"time":1685.39331,"type":0},{"id":1,"sLen":0,"time":2022.47192,"type":0},{"id":1,"sLen":0,"time":2359.55054,"type":0},{"id":0,"sLen":0,"time":2528.08984,"type":0},{"id":2,"sLen":0,"time":2696.62915,"type":0},{"id":0,"sLen":0,"time":3033.70776,"type":0},{"id":0,"sLen":0,"time":3370.78662,"type":0},{"id":2,"sLen":0,"time":3707.86523,"type":0},{"id":1,"sLen":0,"time":4044.94385,"type":0},{"id":3,"sLen":0,"time":4382.02246,"type":0},{"id":1,"sLen":0,"time":4719.101,"type":0},{"id":1,"sLen":0,"time":5056.17969,"type":0},{"id":0,"sLen":0,"time":5224.719,"type":0},{"id":0,"sLen":0,"time":11123.5957,"type":0},{"id":1,"sLen":0,"time":11292.1348,"type":0},{"id":3,"sLen":252.808989999996,"time":11460.6738,"type":0},{"id":1,"sLen":252.80899,"time":11797.7529,"type":0},{"id":0,"sLen":0,"time":12134.8311,"type":0},{"id":0,"sLen":0,"time":12471.91,"type":0},{"id":0,"sLen":0,"time":12808.9883,"type":0},{"id":3,"sLen":0,"time":13146.0674,"type":0},{"id":1,"sLen":0,"time":13483.1465,"type":0},{"id":3,"sLen":0,"time":13820.2246,"type":0},{"id":1,"sLen":0,"time":13988.7637,"type":0},{"id":3,"sLen":0,"time":14325.8428,"type":0},{"id":1,"sLen":0,"time":14494.3818,"type":0},{"id":1,"sLen":0,"time":14831.4609,"type":0},{"id":3,"sLen":0,"time":15168.5391,"type":0},{"id":1,"sLen":0,"time":15505.6182,"type":0},{"id":1,"sLen":0,"time":15842.6963,"type":0},{"id":0,"sLen":0,"time":16011.2363,"type":0},{"id":3,"sLen":0,"time":21573.0332,"type":0},{"id":0,"sLen":0,"time":21910.1133,"type":0},{"id":3,"sLen":0,"time":22247.1914,"type":0},{"id":0,"sLen":0,"time":22584.27,"type":0},{"id":3,"sLen":0,"time":22921.3477,"type":0},{"id":2,"sLen":0,"time":23258.4277,"type":0},{"id":3,"sLen":0,"time":23595.5059,"type":0},{"id":1,"sLen":0,"time":23932.584,"type":0},{"id":0,"sLen":0,"time":24269.6621,"type":0},{"id":3,"sLen":0,"time":26966.293,"type":0},{"id":0,"sLen":0,"time":27303.3711,"type":0},{"id":3,"sLen":0,"time":27640.45,"type":0},{"id":0,"sLen":0,"time":27977.5273,"type":0},{"id":3,"sLen":0,"time":28314.6074,"type":0},{"id":2,"sLen":0,"time":28651.6855,"type":0},{"id":3,"sLen":0,"time":28988.7637,"type":0},{"id":1,"sLen":0,"time":29325.8418,"type":0},{"id":0,"sLen":0,"time":29662.9219,"type":0},{"id":0,"sLen":0,"time":30000,"type":0},{"id":0,"sLen":0,"time":30337.0781,"type":0},{"id":0,"sLen":0,"time":30674.1582,"type":0},{"id":0,"sLen":0,"time":31011.2363,"type":0},{"id":0,"sLen":0,"time":31348.3145,"type":0},{"id":0,"sLen":0,"time":31685.3926,"type":0},{"id":0,"sLen":0,"time":31853.9316,"type":0},{"id":0,"sLen":0,"time":32022.4727,"type":0},{"id":0,"sLen":0,"time":32191.0117,"type":0},{"id":0,"sLen":0,"time":32359.55,"type":0},{"id":2,"sLen":0,"time":32696.6289,"type":0},{"id":0,"sLen":0,"time":33033.707,"type":0},{"id":2,"sLen":0,"time":33370.7852,"type":0},{"id":3,"sLen":0,"time":33539.3242,"type":0},{"id":1,"sLen":0,"time":33707.8633,"type":0},{"id":0,"sLen":0,"time":34044.9453,"type":0},{"id":1,"sLen":0,"time":34382.0234,"type":0},{"id":1,"sLen":0,"time":34719.1,"type":0},{"id":0,"sLen":0,"time":34887.64,"type":0},{"id":3,"sLen":0,"time":35056.18,"type":0},{"id":2,"sLen":0,"time":35393.2578,"type":0},{"id":0,"sLen":0,"time":35730.3359,"type":0},{"id":2,"sLen":0,"time":36067.4141,"type":0},{"id":3,"sLen":0,"time":36404.4961,"type":0},{"id":0,"sLen":0,"time":36741.5742,"type":0},{"id":0,"sLen":0,"time":37078.6523,"type":0},{"id":1,"sLen":0,"time":37247.19,"type":0},{"id":1,"sLen":0,"time":37415.73,"type":0},{"id":0,"sLen":0,"time":37584.27,"type":0},{"id":3,"sLen":0,"time":43146.0664,"type":0},{"id":1,"sLen":0,"time":43314.6055,"type":0},{"id":0,"sLen":0,"time":43483.1445,"type":0},{"id":1,"sLen":0,"time":43820.2266,"type":0},{"id":0,"sLen":0,"time":44157.3047,"type":0},{"id":2,"sLen":0,"time":44494.3828,"type":0},{"id":3,"sLen":0,"time":44662.92,"type":0},{"id":2,"sLen":0,"time":44831.46,"type":0},{"id":3,"sLen":0,"time":45168.54,"type":0},{"id":2,"sLen":0,"time":45505.6172,"type":0},{"id":3,"sLen":0,"time":45842.6953,"type":0},{"id":1,"sLen":0,"time":46179.7734,"type":0},{"id":0,"sLen":0,"time":46516.8555,"type":0},{"id":1,"sLen":0,"time":46853.9336,"type":0},{"id":2,"sLen":0,"time":47191.01,"type":0},{"id":1,"sLen":0,"time":47528.09,"type":0},{"id":1,"sLen":0,"time":47865.168,"type":0},{"id":1,"sLen":0,"time":48202.2461,"type":0},{"id":2,"sLen":0,"time":53932.5859,"type":0},{"id":0,"sLen":0,"time":54269.6641,"type":0},{"id":0,"sLen":0,"time":54606.7422,"type":0},{"id":2,"sLen":0,"time":54943.82,"type":0},{"id":1,"sLen":0,"time":55280.9,"type":0},{"id":3,"sLen":0,"time":55617.9766,"type":0},{"id":1,"sLen":0,"time":55955.0547,"type":0},{"id":1,"sLen":0,"time":56292.1367,"type":0},{"id":0,"sLen":0,"time":56460.6758,"type":0},{"id":2,"sLen":0,"time":56629.2148,"type":0},{"id":0,"sLen":0,"time":56966.293,"type":0},{"id":0,"sLen":0,"time":57303.37,"type":0},{"id":2,"sLen":0,"time":57640.45,"type":0},{"id":1,"sLen":0,"time":57977.5273,"type":0},{"id":3,"sLen":0,"time":58314.6055,"type":0},{"id":1,"sLen":0,"time":58651.6836,"type":0},{"id":1,"sLen":0,"time":58988.7656,"type":0},{"id":0,"sLen":0,"time":59157.3047,"type":0},{"id":0,"sLen":168.539321999997,"time":59325.8438,"type":0},{"id":0,"sLen":168.539321999997,"time":60000,"type":0},{"id":0,"sLen":168.539322000004,"time":60674.1563,"type":0},{"id":0,"sLen":168.539321999997,"time":61348.3164,"type":0},{"id":1,"sLen":0,"time":62022.4719101124,"type":1},{"id":1,"sLen":0,"time":64719.1,"type":0},{"id":0,"sLen":0,"time":65056.18,"type":0},{"id":3,"sLen":0,"time":65393.2578,"type":0},{"id":1,"sLen":0,"time":65730.3359,"type":0},{"id":0,"sLen":0,"time":66067.4141,"type":0},{"id":0,"sLen":0,"time":66404.49,"type":0},{"id":0,"sLen":0,"time":66741.57,"type":0},{"id":0,"sLen":0,"time":66910.11,"type":0},{"id":0,"sLen":0,"time":67078.65,"type":0},{"id":1,"sLen":0,"time":67415.73,"type":0},{"id":3,"sLen":0,"time":67752.81,"type":0},{"id":1,"sLen":0,"time":67921.35,"type":0},{"id":3,"sLen":0,"time":68258.43,"type":0},{"id":1,"sLen":0,"time":68426.97,"type":0},{"id":1,"sLen":0,"time":68764.05,"type":0},{"id":3,"sLen":0,"time":69101.125,"type":0},{"id":1,"sLen":0,"time":69438.2,"type":0},{"id":1,"sLen":0,"time":69775.28,"type":0},{"id":0,"sLen":0,"time":69943.82,"type":0},{"id":0,"sLen":0,"time":75505.62,"type":0},{"id":0,"sLen":0,"time":75842.6953,"type":0},{"id":3,"sLen":0,"time":76179.77,"type":0},{"id":0,"sLen":0,"time":76516.85,"type":0},{"id":3,"sLen":0,"time":76853.93,"type":0},{"id":3,"sLen":0,"time":77191.01,"type":0},{"id":0,"sLen":0,"time":77359.55,"type":0},{"id":1,"sLen":252.808990000005,"time":77528.0859,"type":0},{"id":0,"sLen":0,"time":77865.17,"type":0},{"id":3,"sLen":0,"time":78202.25,"type":0},{"id":1,"sLen":0,"time":78539.33,"type":0},{"id":0,"sLen":0,"time":78876.41,"type":0},{"id":3,"sLen":0,"time":79213.4844,"type":0},{"id":1,"sLen":0,"time":79550.56,"type":0},{"id":0,"sLen":0,"time":79803.37,"type":0},{"id":1,"sLen":0,"time":80056.18,"type":0},{"id":0,"sLen":0,"time":80224.72,"type":0},{"id":2,"sLen":0,"time":80561.8,"type":0},{"id":0,"sLen":0,"time":86292.13,"type":0},{"id":1,"sLen":0,"time":86629.21,"type":0},{"id":3,"sLen":0,"time":86966.29,"type":0},{"id":0,"sLen":0,"time":87303.37,"type":0},{"id":0,"sLen":0,"time":87640.45,"type":0},{"id":1,"sLen":0,"time":87977.53,"type":0},{"id":3,"sLen":0,"time":88314.61,"type":0},{"id":0,"sLen":0,"time":88651.69,"type":0},{"id":2,"sLen":0,"time":88988.7656,"type":0},{"id":3,"sLen":0,"time":89325.84,"type":0},{"id":0,"sLen":0,"time":89662.92,"type":0},{"id":3,"sLen":0,"time":90000,"type":0},{"id":0,"sLen":0,"time":90337.08,"type":0},{"id":3,"sLen":0,"time":90674.16,"type":0},{"id":0,"sLen":0,"time":91011.2344,"type":0},{"id":3,"sLen":337.078643999994,"time":91348.31,"type":0},{"id":2,"sLen":0,"time":97078.65,"type":0},{"id":0,"sLen":0,"time":97415.73,"type":0},{"id":2,"sLen":0,"time":97584.27,"type":0},{"id":0,"sLen":0,"time":97921.35,"type":0},{"id":2,"sLen":0,"time":98089.89,"type":0},{"id":0,"sLen":0,"time":98426.97,"type":0},{"id":2,"sLen":0,"time":98595.51,"type":0},{"id":0,"sLen":0,"time":98932.5859,"type":0},{"id":1,"sLen":0,"time":99101.125,"type":0},{"id":1,"sLen":0,"time":99438.2,"type":0},{"id":2,"sLen":0,"time":99775.28,"type":0},{"id":0,"sLen":0,"time":100112.359,"type":0},{"id":2,"sLen":0,"time":100280.9,"type":0},{"id":0,"sLen":0,"time":100617.977,"type":0},{"id":2,"sLen":0,"time":100786.516,"type":0},{"id":0,"sLen":0,"time":101123.594,"type":0},{"id":2,"sLen":0,"time":101292.133,"type":0},{"id":0,"sLen":0,"time":101629.211,"type":0},{"id":1,"sLen":0,"time":101797.75,"type":0},{"id":1,"sLen":0,"time":102134.828,"type":0},{"id":2,"sLen":0,"time":102471.906,"type":0},{"id":0,"sLen":0,"time":102808.992,"type":0},{"id":2,"sLen":0,"time":102977.531,"type":0},{"id":0,"sLen":0,"time":103314.609,"type":0},{"id":2,"sLen":0,"time":103483.148,"type":0},{"id":0,"sLen":0,"time":103820.227,"type":0},{"id":2,"sLen":0,"time":103988.766,"type":0},{"id":0,"sLen":0,"time":104325.844,"type":0},{"id":1,"sLen":0,"time":104494.383,"type":0},{"id":1,"sLen":0,"time":104831.461,"type":0},{"id":2,"sLen":0,"time":105168.539,"type":0},{"id":0,"sLen":0,"time":105505.617,"type":0},{"id":2,"sLen":0,"time":105674.156,"type":0},{"id":0,"sLen":0,"time":106011.234,"type":0},{"id":2,"sLen":0,"time":106179.773,"type":0},{"id":0,"sLen":0,"time":106516.852,"type":0},{"id":2,"sLen":0,"time":106685.391,"type":0},{"id":0,"sLen":0,"time":107022.469,"type":0},{"id":1,"sLen":0,"time":107191.008,"type":0},{"id":1,"sLen":0,"time":107528.086,"type":0},{"id":1,"sLen":0,"time":107865.172,"type":0},{"id":1,"sLen":0,"time":108202.25,"type":0},{"id":0,"sLen":0,"time":108370.789,"type":0},{"id":1,"sLen":0,"time":108539.328,"type":0},{"id":1,"sLen":0,"time":108876.406,"type":0},{"id":0,"sLen":0,"time":109044.945,"type":0},{"id":1,"sLen":0,"time":109213.484,"type":0},{"id":1,"sLen":0,"time":109550.563,"type":0},{"id":0,"sLen":0,"time":109719.1,"type":0},{"id":1,"sLen":0,"time":109887.641,"type":0},{"id":1,"sLen":0,"time":110224.719,"type":0},{"id":0,"sLen":0,"time":110393.258,"type":0},{"id":2,"sLen":0,"time":113258.43,"type":0},{"id":3,"sLen":0,"time":113595.508,"type":0},{"id":0,"sLen":0,"time":113932.586,"type":0},{"id":1,"sLen":0,"time":114269.664,"type":0},{"id":1,"sLen":0,"time":114606.742,"type":0},{"id":3,"sLen":0,"time":114943.82,"type":0},{"id":1,"sLen":0,"time":115280.9,"type":0},{"id":1,"sLen":0,"time":115617.977,"type":0},{"id":0,"sLen":0,"time":115786.516,"type":0},{"id":2,"sLen":589.887633999984,"time":115955.055,"type":0}],"position":"dad","characters":["tankman"],"visible":true,"type":0},{"notes":[{"id":2,"sLen":0,"time":5393.2583,"type":0},{"id":0,"sLen":0,"time":5730.337,"type":0},{"id":0,"sLen":0,"time":6067.41553,"type":0},{"id":2,"sLen":0,"time":6404.494,"type":0},{"id":1,"sLen":0,"time":6741.573,"type":0},{"id":3,"sLen":0,"time":7078.652,"type":0},{"id":1,"sLen":0,"time":7415.73047,"type":0},{"id":1,"sLen":0,"time":7752.809,"type":0},{"id":0,"sLen":0,"time":7921.348,"type":0},{"id":2,"sLen":0,"time":8089.8877,"type":0},{"id":0,"sLen":0,"time":8426.966,"type":0},{"id":0,"sLen":0,"time":8764.045,"type":0},{"id":2,"sLen":0,"time":9101.124,"type":0},{"id":1,"sLen":0,"time":9438.202,"type":0},{"id":3,"sLen":0,"time":9775.281,"type":0},{"id":1,"sLen":0,"time":10112.3594,"type":0},{"id":1,"sLen":0,"time":10449.4385,"type":0},{"id":0,"sLen":0,"time":10617.9775,"type":0},{"id":0,"sLen":252.808990000001,"time":14831.4609,"type":0},{"id":1,"sLen":252.80899,"time":15168.5391,"type":0},{"id":3,"sLen":252.80899,"time":15505.6182,"type":0},{"id":1,"sLen":252.808990000001,"time":15842.6963,"type":0},{"id":3,"sLen":0,"time":16179.7754,"type":0},{"id":0,"sLen":0,"time":16516.8535,"type":0},{"id":1,"sLen":0,"time":16685.3926,"type":0},{"id":3,"sLen":252.808990000001,"time":16853.9316,"type":0},{"id":1,"sLen":252.808990000001,"time":17191.0117,"type":0},{"id":0,"sLen":0,"time":17528.09,"type":0},{"id":0,"sLen":0,"time":17865.168,"type":0},{"id":0,"sLen":0,"time":18202.248,"type":0},{"id":3,"sLen":0,"time":18539.3262,"type":0},{"id":1,"sLen":0,"time":18876.4043,"type":0},{"id":3,"sLen":0,"time":19213.4824,"type":0},{"id":1,"sLen":0,"time":19382.0215,"type":0},{"id":3,"sLen":0,"time":19719.1016,"type":0},{"id":1,"sLen":0,"time":19887.64,"type":0},{"id":1,"sLen":0,"time":20224.7188,"type":0},{"id":3,"sLen":0,"time":20561.7969,"type":0},{"id":1,"sLen":0,"time":20898.877,"type":0},{"id":1,"sLen":0,"time":21235.9551,"type":0},{"id":0,"sLen":0,"time":21404.4941,"type":0},{"id":3,"sLen":0,"time":21573.0332,"type":0},{"id":3,"sLen":0,"time":21910.1133,"type":0},{"id":3,"sLen":0,"time":22247.1914,"type":0},{"id":3,"sLen":0,"time":22584.27,"type":0},{"id":1,"sLen":0,"time":22921.3477,"type":0},{"id":1,"sLen":0,"time":23258.4277,"type":0},{"id":3,"sLen":252.808990000001,"time":23595.5059,"type":0},{"id":0,"sLen":252.808990000012,"time":23932.584,"type":0},{"id":3,"sLen":0,"time":24269.6621,"type":0},{"id":0,"sLen":0,"time":24606.7422,"type":0},{"id":3,"sLen":0,"time":24943.82,"type":0},{"id":0,"sLen":0,"time":25280.8984,"type":0},{"id":3,"sLen":0,"time":25617.9766,"type":0},{"id":2,"sLen":0,"time":25955.0566,"type":0},{"id":3,"sLen":0,"time":26292.1348,"type":0},{"id":1,"sLen":0,"time":26629.2129,"type":0},{"id":2,"sLen":589.88763400001,"time":26966.293,"type":0},{"id":3,"sLen":589.887633999992,"time":27640.45,"type":0},{"id":1,"sLen":589.887633999999,"time":28314.6074,"type":0},{"id":0,"sLen":337.078644000008,"time":28988.7637,"type":0},{"id":3,"sLen":0,"time":29662.9219,"type":0},{"id":0,"sLen":0,"time":30000,"type":0},{"id":3,"sLen":0,"time":30337.0781,"type":0},{"id":0,"sLen":0,"time":30674.1582,"type":0},{"id":3,"sLen":0,"time":31011.2363,"type":0},{"id":2,"sLen":0,"time":31348.3145,"type":0},{"id":3,"sLen":0,"time":31685.3926,"type":0},{"id":1,"sLen":0,"time":32022.4727,"type":0},{"id":1,"sLen":674.157300000003,"time":32359.55,"type":0},{"id":0,"sLen":0,"time":37752.81,"type":0},{"id":2,"sLen":0,"time":38089.8867,"type":0},{"id":0,"sLen":0,"time":38426.9648,"type":0},{"id":2,"sLen":0,"time":38764.043,"type":0},{"id":3,"sLen":0,"time":38932.5859,"type":0},{"id":1,"sLen":0,"time":39101.125,"type":0},{"id":0,"sLen":0,"time":39438.2031,"type":0},{"id":1,"sLen":0,"time":39775.28,"type":0},{"id":1,"sLen":0,"time":40112.36,"type":0},{"id":0,"sLen":0,"time":40280.9,"type":0},{"id":3,"sLen":0,"time":40449.4375,"type":0},{"id":2,"sLen":0,"time":40786.5156,"type":0},{"id":0,"sLen":0,"time":41123.5938,"type":0},{"id":2,"sLen":0,"time":41460.6758,"type":0},{"id":3,"sLen":0,"time":41797.7539,"type":0},{"id":0,"sLen":0,"time":42134.832,"type":0},{"id":0,"sLen":0,"time":42471.91,"type":0},{"id":1,"sLen":0,"time":42640.45,"type":0},{"id":1,"sLen":0,"time":42808.99,"type":0},{"id":0,"sLen":0,"time":42977.5273,"type":0},{"id":3,"sLen":0,"time":48539.3242,"type":0},{"id":1,"sLen":0,"time":48707.8633,"type":0},{"id":0,"sLen":0,"time":48876.4063,"type":0},{"id":1,"sLen":0,"time":49213.4844,"type":0},{"id":0,"sLen":0,"time":49550.5625,"type":0},{"id":2,"sLen":0,"time":49887.64,"type":0},{"id":3,"sLen":0,"time":50056.18,"type":0},{"id":2,"sLen":0,"time":50224.72,"type":0},{"id":3,"sLen":0,"time":50561.7969,"type":0},{"id":2,"sLen":0,"time":50898.875,"type":0},{"id":3,"sLen":0,"time":51235.9531,"type":0},{"id":1,"sLen":0,"time":51573.0352,"type":0},{"id":0,"sLen":0,"time":51910.1133,"type":0},{"id":1,"sLen":0,"time":52247.19,"type":0},{"id":2,"sLen":0,"time":52584.27,"type":0},{"id":1,"sLen":0,"time":52921.3477,"type":0},{"id":1,"sLen":0,"time":53258.4258,"type":0},{"id":1,"sLen":0,"time":53595.5039,"type":0},{"id":1,"sLen":1011.23595999998,"time":53932.5859,"type":0},{"id":2,"sLen":505.617981000003,"time":55955.0547,"type":0},{"id":1,"sLen":674.157299999999,"time":56629.2148,"type":0},{"id":0,"sLen":252.808989999998,"time":58651.6836,"type":0},{"id":2,"sLen":0,"time":58988.7656,"type":0},{"id":2,"sLen":0,"time":59325.8438,"type":0},{"id":0,"sLen":0,"time":59662.92,"type":0},{"id":0,"sLen":0,"time":60000,"type":0},{"id":2,"sLen":0,"time":60337.08,"type":0},{"id":1,"sLen":0,"time":60674.1563,"type":0},{"id":3,"sLen":0,"time":61011.2344,"type":0},{"id":1,"sLen":0,"time":61348.3164,"type":0},{"id":1,"sLen":0,"time":61685.3945,"type":0},{"id":0,"sLen":0,"time":61853.9336,"type":0},{"id":2,"sLen":0,"time":62022.4727,"type":0},{"id":0,"sLen":0,"time":62359.55,"type":0},{"id":0,"sLen":0,"time":62696.63,"type":0},{"id":2,"sLen":0,"time":63033.707,"type":0},{"id":1,"sLen":0,"time":63370.7852,"type":0},{"id":3,"sLen":0,"time":63707.8633,"type":0},{"id":1,"sLen":0,"time":64044.9453,"type":0},{"id":1,"sLen":0,"time":64382.0234,"type":0},{"id":0,"sLen":0,"time":64550.5625,"type":0},{"id":1,"sLen":0,"time":64719.1,"type":0},{"id":0,"sLen":0,"time":65056.18,"type":0},{"id":3,"sLen":0,"time":65393.2578,"type":0},{"id":1,"sLen":0,"time":65730.3359,"type":0},{"id":3,"sLen":505.617981000003,"time":66067.4141,"type":0},{"id":1,"sLen":505.617981000003,"time":66741.57,"type":0},{"id":0,"sLen":505.617981000003,"time":67415.73,"type":0},{"id":1,"sLen":0,"time":68089.89,"type":0},{"id":0,"sLen":0,"time":68426.97,"type":0},{"id":3,"sLen":589.887633999999,"time":68764.05,"type":0},{"id":0,"sLen":337.078643999994,"time":69438.2,"type":0},{"id":1,"sLen":0,"time":70112.36,"type":0},{"id":0,"sLen":0,"time":70449.44,"type":0},{"id":3,"sLen":0,"time":70786.5156,"type":0},{"id":1,"sLen":0,"time":71123.59,"type":0},{"id":0,"sLen":0,"time":71460.67,"type":0},{"id":0,"sLen":0,"time":71797.75,"type":0},{"id":0,"sLen":0,"time":72134.83,"type":0},{"id":0,"sLen":0,"time":72303.37,"type":0},{"id":0,"sLen":0,"time":72471.91,"type":0},{"id":1,"sLen":0,"time":72808.99,"type":0},{"id":3,"sLen":0,"time":73146.07,"type":0},{"id":1,"sLen":0,"time":73314.61,"type":0},{"id":3,"sLen":0,"time":73651.69,"type":0},{"id":1,"sLen":0,"time":73820.23,"type":0},{"id":1,"sLen":0,"time":74157.3047,"type":0},{"id":3,"sLen":0,"time":74494.38,"type":0},{"id":1,"sLen":0,"time":74831.46,"type":0},{"id":1,"sLen":0,"time":75168.54,"type":0},{"id":0,"sLen":0,"time":75337.08,"type":0},{"id":1,"sLen":1264.04492,"time":75505.62,"type":0},{"id":0,"sLen":1264.04492,"time":76853.93,"type":0},{"id":3,"sLen":1264.04492,"time":78202.25,"type":0},{"id":0,"sLen":1011.23596000001,"time":79550.56,"type":0},{"id":0,"sLen":0,"time":80898.875,"type":0},{"id":0,"sLen":0,"time":81235.95,"type":0},{"id":3,"sLen":0,"time":81573.03,"type":0},{"id":0,"sLen":0,"time":81910.11,"type":0},{"id":3,"sLen":0,"time":82247.19,"type":0},{"id":3,"sLen":0,"time":82584.27,"type":0},{"id":0,"sLen":0,"time":82752.81,"type":0},{"id":1,"sLen":252.808990000005,"time":82921.35,"type":0},{"id":0,"sLen":0,"time":83258.43,"type":0},{"id":3,"sLen":0,"time":83595.51,"type":0},{"id":1,"sLen":0,"time":83932.5859,"type":0},{"id":0,"sLen":0,"time":84269.6641,"type":0},{"id":3,"sLen":0,"time":84606.74,"type":0},{"id":1,"sLen":0,"time":84943.82,"type":0},{"id":0,"sLen":0,"time":85196.63,"type":0},{"id":1,"sLen":0,"time":85449.44,"type":0},{"id":0,"sLen":0,"time":85617.98,"type":0},{"id":2,"sLen":0,"time":85955.0547,"type":0},{"id":0,"sLen":0,"time":91685.39,"type":0},{"id":1,"sLen":0,"time":92022.47,"type":0},{"id":3,"sLen":0,"time":92359.55,"type":0},{"id":0,"sLen":0,"time":92696.63,"type":0},{"id":0,"sLen":0,"time":93033.71,"type":0},{"id":1,"sLen":0,"time":93370.79,"type":0},{"id":3,"sLen":0,"time":93707.87,"type":0},{"id":0,"sLen":0,"time":94044.9453,"type":0},{"id":2,"sLen":0,"time":94382.02,"type":0},{"id":3,"sLen":0,"time":94719.1,"type":0},{"id":0,"sLen":0,"time":95056.18,"type":0},{"id":3,"sLen":0,"time":95393.26,"type":0},{"id":0,"sLen":0,"time":95730.3359,"type":0},{"id":3,"sLen":0,"time":96067.4141,"type":0},{"id":0,"sLen":0,"time":96404.49,"type":0},{"id":3,"sLen":337.078643999994,"time":96741.57,"type":0},{"id":0,"sLen":589.887633999955,"time":98426.97,"type":0},{"id":2,"sLen":589.887633999999,"time":99101.125,"type":0},{"id":0,"sLen":589.887633999999,"time":101123.594,"type":0},{"id":2,"sLen":337.078644000008,"time":101797.75,"type":0},{"id":2,"sLen":0,"time":102471.906,"type":0},{"id":3,"sLen":0,"time":102808.992,"type":0},{"id":2,"sLen":0,"time":102977.531,"type":0},{"id":3,"sLen":0,"time":103314.609,"type":0},{"id":2,"sLen":0,"time":103483.148,"type":0},{"id":3,"sLen":0,"time":103820.227,"type":0},{"id":2,"sLen":0,"time":103988.766,"type":0},{"id":3,"sLen":0,"time":104325.844,"type":0},{"id":1,"sLen":0,"time":104494.383,"type":0},{"id":1,"sLen":0,"time":104831.461,"type":0},{"id":2,"sLen":0,"time":105168.539,"type":0},{"id":3,"sLen":0,"time":105505.617,"type":0},{"id":2,"sLen":0,"time":105674.156,"type":0},{"id":3,"sLen":0,"time":106011.234,"type":0},{"id":2,"sLen":0,"time":106179.773,"type":0},{"id":3,"sLen":0,"time":106516.852,"type":0},{"id":2,"sLen":0,"time":106685.391,"type":0},{"id":3,"sLen":0,"time":107022.469,"type":0},{"id":1,"sLen":0,"time":107191.008,"type":0},{"id":1,"sLen":0,"time":107528.086,"type":0},{"id":3,"sLen":589.887634000028,"time":107865.172,"type":0},{"id":1,"sLen":589.887634000028,"time":108539.328,"type":0},{"id":0,"sLen":589.887633999999,"time":109213.484,"type":0},{"id":1,"sLen":589.887633999999,"time":109887.641,"type":0},{"id":1,"sLen":0,"time":110561.8,"type":0},{"id":1,"sLen":0,"time":110898.875,"type":0},{"id":0,"sLen":0,"time":111067.414,"type":0},{"id":1,"sLen":0,"time":111235.953,"type":0},{"id":1,"sLen":0,"time":111573.031,"type":0},{"id":0,"sLen":0,"time":111741.57,"type":0},{"id":1,"sLen":0,"time":111910.109,"type":0},{"id":1,"sLen":0,"time":112247.188,"type":0},{"id":0,"sLen":0,"time":112415.727,"type":0},{"id":1,"sLen":0,"time":112584.273,"type":0},{"id":1,"sLen":0,"time":112921.352,"type":0},{"id":0,"sLen":0,"time":113089.891,"type":0},{"id":3,"sLen":589.887633999999,"time":113258.43,"type":0},{"id":1,"sLen":589.887633999999,"time":113932.586,"type":0},{"id":0,"sLen":589.887633999984,"time":114606.742,"type":0},{"id":1,"sLen":337.078643999994,"time":115280.9,"type":0},{"id":2,"sLen":0,"time":115955.055,"type":0},{"id":3,"sLen":0,"time":116292.133,"type":0},{"id":0,"sLen":0,"time":116629.211,"type":0},{"id":1,"sLen":0,"time":116966.289,"type":0},{"id":1,"sLen":0,"time":117303.367,"type":0},{"id":3,"sLen":0,"time":117640.453,"type":0},{"id":1,"sLen":0,"time":117977.531,"type":0},{"id":1,"sLen":0,"time":118314.609,"type":0},{"id":0,"sLen":0,"time":118483.148,"type":0}],"position":"boyfriend","characters":["bf-holding-gf"],"visible":true,"type":1},{"notes":[{"id":0,"sLen":0,"time":0,"type":0},{"id":3,"sLen":0,"time":168.539325842697,"type":0},{"id":3,"sLen":0,"time":252.808988764045,"type":0},{"id":3,"sLen":0,"time":421.348314606742,"type":0},{"id":0,"sLen":0,"time":589.887640449438,"type":0},{"id":3,"sLen":0,"time":758.426966292135,"type":0},{"id":3,"sLen":0,"time":842.696629213483,"type":0},{"id":0,"sLen":0,"time":1011.23595505618,"type":0},{"id":0,"sLen":0,"time":1179.77528089888,"type":0},{"id":0,"sLen":0,"time":1264.04494382022,"type":0},{"id":3,"sLen":0,"time":1348.31460674157,"type":0},{"id":0,"sLen":0,"time":1516.85393258427,"type":0},{"id":0,"sLen":0,"time":1601.12359550562,"type":0},{"id":3,"sLen":0,"time":1853.93258426966,"type":0},{"id":0,"sLen":0,"time":2022.47191011236,"type":0},{"id":3,"sLen":0,"time":2106.74157303371,"type":0},{"id":3,"sLen":0,"time":2191.01123595506,"type":0},{"id":0,"sLen":0,"time":2359.55056179775,"type":0},{"id":0,"sLen":0,"time":2696.62921348315,"type":0},{"id":3,"sLen":0,"time":2865.16853932584,"type":0},{"id":3,"sLen":0,"time":2949.43820224719,"type":0},{"id":3,"sLen":0,"time":3117.97752808989,"type":0},{"id":0,"sLen":0,"time":3286.51685393258,"type":0},{"id":3,"sLen":0,"time":3455.05617977528,"type":0},{"id":3,"sLen":0,"time":3539.32584269663,"type":0},{"id":0,"sLen":0,"time":3707.86516853933,"type":0},{"id":0,"sLen":0,"time":3876.40449438202,"type":0},{"id":0,"sLen":0,"time":3960.67415730337,"type":0},{"id":3,"sLen":0,"time":4044.94382022472,"type":0},{"id":0,"sLen":0,"time":4213.48314606742,"type":0},{"id":0,"sLen":0,"time":4297.75280898876,"type":0},{"id":3,"sLen":0,"time":4550.56179775281,"type":0},{"id":0,"sLen":0,"time":4719.10112359551,"type":0},{"id":3,"sLen":0,"time":4803.37078651685,"type":0},{"id":3,"sLen":0,"time":4887.6404494382,"type":0},{"id":0,"sLen":0,"time":5056.1797752809,"type":0},{"id":0,"sLen":0,"time":5140.44943820225,"type":0},{"id":0,"sLen":0,"time":5224.7191011236,"type":0},{"id":0,"sLen":0,"time":5393.25842696629,"type":0},{"id":3,"sLen":0,"time":5561.79775280899,"type":0},{"id":3,"sLen":0,"time":5646.06741573034,"type":0},{"id":3,"sLen":0,"time":5814.60674157303,"type":0},{"id":0,"sLen":0,"time":5983.14606741573,"type":0},{"id":3,"sLen":0,"time":6151.68539325843,"type":0},{"id":3,"sLen":0,"time":6235.95505617978,"type":0},{"id":0,"sLen":0,"time":6404.49438202247,"type":0},{"id":0,"sLen":0,"time":6573.03370786517,"type":0},{"id":0,"sLen":0,"time":6657.30337078652,"type":0},{"id":3,"sLen":0,"time":6741.57303370786,"type":0},{"id":0,"sLen":0,"time":6910.11235955056,"type":0},{"id":0,"sLen":0,"time":6994.38202247191,"type":0},{"id":3,"sLen":0,"time":7247.19101123595,"type":0},{"id":0,"sLen":0,"time":7415.73033707865,"type":0},{"id":3,"sLen":0,"time":7500,"type":0},{"id":3,"sLen":0,"time":7584.26966292135,"type":0},{"id":0,"sLen":0,"time":7752.80898876405,"type":0},{"id":0,"sLen":0,"time":8089.88764044944,"type":0},{"id":3,"sLen":0,"time":8258.42696629213,"type":0},{"id":3,"sLen":0,"time":8342.69662921348,"type":0},{"id":3,"sLen":0,"time":8511.23595505618,"type":0},{"id":0,"sLen":0,"time":8679.77528089888,"type":0},{"id":3,"sLen":0,"time":8848.31460674157,"type":0},{"id":3,"sLen":0,"time":8932.58426966292,"type":0},{"id":0,"sLen":0,"time":9101.12359550562,"type":0},{"id":0,"sLen":0,"time":9269.66292134831,"type":0},{"id":0,"sLen":0,"time":9353.93258426966,"type":0},{"id":3,"sLen":0,"time":9438.20224719101,"type":0},{"id":0,"sLen":0,"time":9606.74157303371,"type":0},{"id":0,"sLen":0,"time":9691.01123595505,"type":0},{"id":3,"sLen":0,"time":9943.8202247191,"type":0},{"id":0,"sLen":0,"time":10112.3595505618,"type":0},{"id":3,"sLen":0,"time":10196.6292134831,"type":0},{"id":3,"sLen":0,"time":10280.8988764045,"type":0},{"id":0,"sLen":0,"time":10449.4382022472,"type":0},{"id":0,"sLen":0,"time":21235.9550561798,"type":0},{"id":3,"sLen":0,"time":21320.2247191011,"type":0},{"id":0,"sLen":0,"time":21404.4943820225,"type":0},{"id":0,"sLen":0,"time":21573.0337078652,"type":0},{"id":3,"sLen":0,"time":21910.1123595506,"type":0},{"id":0,"sLen":0,"time":22247.191011236,"type":0},{"id":3,"sLen":0,"time":22584.2696629213,"type":0},{"id":0,"sLen":0,"time":22921.3483146067,"type":0},{"id":0,"sLen":0,"time":23258.4269662921,"type":0},{"id":3,"sLen":0,"time":23595.5056179775,"type":0},{"id":3,"sLen":0,"time":23932.5842696629,"type":0},{"id":0,"sLen":0,"time":24269.6629213483,"type":0},{"id":3,"sLen":0,"time":24606.7415730337,"type":0},{"id":0,"sLen":0,"time":24943.8202247191,"type":0},{"id":3,"sLen":0,"time":25280.8988764045,"type":0},{"id":0,"sLen":0,"time":25617.9775280899,"type":0},{"id":0,"sLen":0,"time":25955.0561797753,"type":0},{"id":3,"sLen":0,"time":26292.1348314607,"type":0},{"id":3,"sLen":0,"time":26629.2134831461,"type":0},{"id":3,"sLen":0,"time":26713.4831460674,"type":0},{"id":3,"sLen":0,"time":26797.7528089888,"type":0},{"id":3,"sLen":0,"time":26966.2921348315,"type":0},{"id":0,"sLen":0,"time":27303.3707865169,"type":0},{"id":0,"sLen":0,"time":27640.4494382022,"type":0},{"id":3,"sLen":0,"time":27977.5280898876,"type":0},{"id":3,"sLen":0,"time":28314.606741573,"type":0},{"id":0,"sLen":0,"time":28651.6853932584,"type":0},{"id":3,"sLen":0,"time":28988.7640449438,"type":0},{"id":0,"sLen":0,"time":29325.8426966292,"type":0},{"id":0,"sLen":0,"time":29662.9213483146,"type":0},{"id":0,"sLen":0,"time":29831.4606741573,"type":0},{"id":0,"sLen":0,"time":30000,"type":0},{"id":3,"sLen":0,"time":30168.5393258427,"type":0},{"id":3,"sLen":0,"time":30337.0786516854,"type":0},{"id":3,"sLen":0,"time":30505.6179775281,"type":0},{"id":3,"sLen":0,"time":30674.1573033708,"type":0},{"id":0,"sLen":0,"time":30842.6966292135,"type":0},{"id":0,"sLen":0,"time":31011.2359550562,"type":0},{"id":3,"sLen":0,"time":31348.3146067416,"type":0},{"id":3,"sLen":0,"time":31685.393258427,"type":0},{"id":0,"sLen":0,"time":31853.9325842697,"type":0},{"id":0,"sLen":0,"time":32359.5505617978,"type":0},{"id":3,"sLen":0,"time":32696.6292134831,"type":0},{"id":0,"sLen":0,"time":33033.7078651685,"type":0},{"id":0,"sLen":0,"time":33202.2471910112,"type":0},{"id":3,"sLen":0,"time":33370.7865168539,"type":0},{"id":0,"sLen":0,"time":33707.8651685393,"type":0},{"id":3,"sLen":0,"time":34044.9438202247,"type":0},{"id":3,"sLen":0,"time":34382.0224719101,"type":0},{"id":0,"sLen":0,"time":34550.5617977528,"type":0},{"id":3,"sLen":0,"time":34719.1011235955,"type":0},{"id":0,"sLen":0,"time":35056.1797752809,"type":0},{"id":3,"sLen":0,"time":35393.2584269663,"type":0},{"id":0,"sLen":0,"time":35730.3370786517,"type":0},{"id":0,"sLen":0,"time":35898.8764044944,"type":0},{"id":3,"sLen":0,"time":36067.4157303371,"type":0},{"id":0,"sLen":0,"time":36404.4943820225,"type":0},{"id":3,"sLen":0,"time":36741.5730337079,"type":0},{"id":3,"sLen":0,"time":37078.6516853933,"type":0},{"id":0,"sLen":0,"time":37247.191011236,"type":0},{"id":3,"sLen":0,"time":37415.7303370787,"type":0},{"id":0,"sLen":0,"time":37752.808988764,"type":0},{"id":3,"sLen":0,"time":38089.8876404494,"type":0},{"id":3,"sLen":0,"time":38426.9662921348,"type":0},{"id":0,"sLen":0,"time":38595.5056179775,"type":0},{"id":3,"sLen":0,"time":38764.0449438202,"type":0},{"id":0,"sLen":0,"time":39101.1235955056,"type":0},{"id":3,"sLen":0,"time":39438.202247191,"type":0},{"id":3,"sLen":0,"time":39775.2808988764,"type":0},{"id":0,"sLen":0,"time":39943.8202247191,"type":0},{"id":3,"sLen":0,"time":40112.3595505618,"type":0},{"id":0,"sLen":0,"time":40449.4382022472,"type":0},{"id":3,"sLen":0,"time":40786.5168539326,"type":0},{"id":3,"sLen":0,"time":41123.595505618,"type":0},{"id":0,"sLen":0,"time":41292.1348314607,"type":0},{"id":3,"sLen":0,"time":41460.6741573034,"type":0},{"id":0,"sLen":0,"time":41797.7528089888,"type":0},{"id":0,"sLen":0,"time":42134.8314606742,"type":0},{"id":0,"sLen":0,"time":42471.9101123596,"type":0},{"id":0,"sLen":0,"time":42640.4494382022,"type":0},{"id":3,"sLen":0,"time":42808.9887640449,"type":0},{"id":3,"sLen":0,"time":42893.2584269663,"type":0},{"id":3,"sLen":0,"time":42977.5280898876,"type":0},{"id":0,"sLen":0,"time":43146.0674157303,"type":0},{"id":3,"sLen":0,"time":43483.1460674157,"type":0},{"id":3,"sLen":0,"time":43820.2247191011,"type":0},{"id":0,"sLen":0,"time":43988.7640449438,"type":0},{"id":3,"sLen":0,"time":44157.3033707865,"type":0},{"id":0,"sLen":0,"time":44494.3820224719,"type":0},{"id":3,"sLen":0,"time":44831.4606741573,"type":0},{"id":0,"sLen":0,"time":45168.5393258427,"type":0},{"id":0,"sLen":0,"time":45337.0786516854,"type":0},{"id":3,"sLen":0,"time":45505.6179775281,"type":0},{"id":0,"sLen":0,"time":45842.6966292135,"type":0},{"id":3,"sLen":0,"time":46179.7752808989,"type":0},{"id":3,"sLen":0,"time":46516.8539325843,"type":0},{"id":0,"sLen":0,"time":46685.393258427,"type":0},{"id":3,"sLen":0,"time":46853.9325842697,"type":0},{"id":0,"sLen":0,"time":47191.0112359551,"type":0},{"id":3,"sLen":0,"time":47528.0898876404,"type":0},{"id":3,"sLen":0,"time":47865.1685393258,"type":0},{"id":0,"sLen":0,"time":48033.7078651685,"type":0},{"id":3,"sLen":0,"time":48202.2471910112,"type":0},{"id":0,"sLen":0,"time":48539.3258426966,"type":0},{"id":3,"sLen":0,"time":48876.404494382,"type":0},{"id":3,"sLen":0,"time":49213.4831460674,"type":0},{"id":0,"sLen":0,"time":49382.0224719101,"type":0},{"id":3,"sLen":0,"time":49550.5617977528,"type":0},{"id":0,"sLen":0,"time":49887.6404494382,"type":0},{"id":3,"sLen":0,"time":50224.7191011236,"type":0},{"id":0,"sLen":0,"time":50561.797752809,"type":0},{"id":0,"sLen":0,"time":50730.3370786517,"type":0},{"id":3,"sLen":0,"time":50898.8764044944,"type":0},{"id":0,"sLen":0,"time":51235.9550561798,"type":0},{"id":3,"sLen":0,"time":51573.0337078652,"type":0},{"id":3,"sLen":0,"time":51910.1123595506,"type":0},{"id":0,"sLen":0,"time":52078.6516853933,"type":0},{"id":3,"sLen":0,"time":52247.191011236,"type":0},{"id":0,"sLen":0,"time":52584.2696629213,"type":0},{"id":0,"sLen":0,"time":52921.3483146067,"type":0},{"id":0,"sLen":0,"time":53258.4269662921,"type":0},{"id":0,"sLen":0,"time":53426.9662921348,"type":0},{"id":3,"sLen":0,"time":53595.5056179775,"type":0},{"id":3,"sLen":0,"time":53679.7752808989,"type":0},{"id":3,"sLen":0,"time":53764.0449438202,"type":0},{"id":0,"sLen":0,"time":53932.5842696629,"type":0},{"id":3,"sLen":0,"time":54101.1235955056,"type":0},{"id":3,"sLen":0,"time":54185.393258427,"type":0},{"id":3,"sLen":0,"time":54353.9325842697,"type":0},{"id":0,"sLen":0,"time":54522.4719101124,"type":0},{"id":3,"sLen":0,"time":54691.0112359551,"type":0},{"id":3,"sLen":0,"time":54775.2808988764,"type":0},{"id":0,"sLen":0,"time":54943.8202247191,"type":0},{"id":0,"sLen":0,"time":55112.3595505618,"type":0},{"id":0,"sLen":0,"time":55196.6292134831,"type":0},{"id":3,"sLen":0,"time":55280.8988764045,"type":0},{"id":0,"sLen":0,"time":55449.4382022472,"type":0},{"id":0,"sLen":0,"time":55533.7078651685,"type":0},{"id":3,"sLen":0,"time":55786.5168539326,"type":0},{"id":0,"sLen":0,"time":55955.0561797753,"type":0},{"id":3,"sLen":0,"time":56039.3258426966,"type":0},{"id":3,"sLen":0,"time":56123.595505618,"type":0},{"id":0,"sLen":0,"time":56292.1348314607,"type":0},{"id":0,"sLen":0,"time":56629.2134831461,"type":0},{"id":3,"sLen":0,"time":56797.7528089888,"type":0},{"id":3,"sLen":0,"time":56882.0224719101,"type":0},{"id":3,"sLen":0,"time":57050.5617977528,"type":0},{"id":0,"sLen":0,"time":57219.1011235955,"type":0},{"id":3,"sLen":0,"time":57387.6404494382,"type":0},{"id":3,"sLen":0,"time":57471.9101123595,"type":0},{"id":0,"sLen":0,"time":57640.4494382022,"type":0},{"id":0,"sLen":0,"time":57808.9887640449,"type":0},{"id":0,"sLen":0,"time":57893.2584269663,"type":0},{"id":3,"sLen":0,"time":57977.5280898876,"type":0},{"id":0,"sLen":0,"time":58146.0674157303,"type":0},{"id":0,"sLen":0,"time":58230.3370786517,"type":0},{"id":3,"sLen":0,"time":58483.1460674157,"type":0},{"id":0,"sLen":0,"time":58651.6853932584,"type":0},{"id":3,"sLen":0,"time":58735.9550561798,"type":0},{"id":3,"sLen":0,"time":58820.2247191011,"type":0},{"id":0,"sLen":0,"time":58988.7640449438,"type":0},{"id":0,"sLen":0,"time":59073.0337078652,"type":0},{"id":0,"sLen":0,"time":59157.3033707865,"type":0},{"id":0,"sLen":0,"time":59325.8426966292,"type":0},{"id":3,"sLen":0,"time":59494.3820224719,"type":0},{"id":3,"sLen":0,"time":59578.6516853933,"type":0},{"id":3,"sLen":0,"time":59747.191011236,"type":0},{"id":0,"sLen":0,"time":59915.7303370786,"type":0},{"id":3,"sLen":0,"time":60084.2696629213,"type":0},{"id":3,"sLen":0,"time":60168.5393258427,"type":0},{"id":0,"sLen":0,"time":60337.0786516854,"type":0},{"id":0,"sLen":0,"time":60505.6179775281,"type":0},{"id":0,"sLen":0,"time":60589.8876404494,"type":0},{"id":3,"sLen":0,"time":60674.1573033708,"type":0},{"id":0,"sLen":0,"time":60842.6966292135,"type":0},{"id":0,"sLen":0,"time":60926.9662921348,"type":0},{"id":3,"sLen":0,"time":61179.7752808989,"type":0},{"id":0,"sLen":0,"time":61348.3146067416,"type":0},{"id":3,"sLen":0,"time":61432.5842696629,"type":0},{"id":3,"sLen":0,"time":61516.8539325843,"type":0},{"id":0,"sLen":0,"time":61685.393258427,"type":0},{"id":0,"sLen":0,"time":62022.4719101124,"type":0},{"id":3,"sLen":0,"time":62191.0112359551,"type":0},{"id":3,"sLen":0,"time":62275.2808988764,"type":0},{"id":3,"sLen":0,"time":62443.8202247191,"type":0},{"id":0,"sLen":0,"time":62612.3595505618,"type":0},{"id":3,"sLen":0,"time":62780.8988764045,"type":0},{"id":3,"sLen":0,"time":62865.1685393258,"type":0},{"id":0,"sLen":0,"time":63033.7078651685,"type":0},{"id":0,"sLen":0,"time":63202.2471910112,"type":0},{"id":0,"sLen":0,"time":63286.5168539326,"type":0},{"id":3,"sLen":0,"time":63370.7865168539,"type":0},{"id":0,"sLen":0,"time":63539.3258426966,"type":0},{"id":0,"sLen":0,"time":63623.595505618,"type":0},{"id":3,"sLen":0,"time":63876.404494382,"type":0},{"id":0,"sLen":0,"time":64044.9438202247,"type":0},{"id":3,"sLen":0,"time":64129.2134831461,"type":0},{"id":3,"sLen":0,"time":64213.4831460674,"type":0},{"id":0,"sLen":0,"time":64382.0224719101,"type":0},{"id":3,"sLen":0,"time":64719.1011235955,"type":0},{"id":0,"sLen":0,"time":65056.1797752809,"type":0},{"id":0,"sLen":0,"time":65393.2584269663,"type":0},{"id":0,"sLen":0,"time":65730.3370786517,"type":0},{"id":0,"sLen":0,"time":66067.4157303371,"type":0},{"id":3,"sLen":0,"time":66404.4943820225,"type":0},{"id":3,"sLen":0,"time":66741.5730337079,"type":0},{"id":3,"sLen":0,"time":67078.6516853933,"type":0},{"id":3,"sLen":0,"time":67415.7303370787,"type":0},{"id":0,"sLen":0,"time":67752.808988764,"type":0},{"id":0,"sLen":0,"time":68089.8876404494,"type":0},{"id":0,"sLen":0,"time":68426.9662921348,"type":0},{"id":0,"sLen":0,"time":68764.0449438202,"type":0},{"id":3,"sLen":0,"time":69101.1235955056,"type":0},{"id":3,"sLen":0,"time":69438.202247191,"type":0},{"id":3,"sLen":0,"time":69775.2808988764,"type":0},{"id":3,"sLen":0,"time":69859.5505617978,"type":0},{"id":3,"sLen":0,"time":69943.8202247191,"type":0},{"id":3,"sLen":0,"time":70112.3595505618,"type":0},{"id":0,"sLen":0,"time":70449.4382022472,"type":0},{"id":0,"sLen":0,"time":70786.5168539326,"type":0},{"id":0,"sLen":0,"time":71123.595505618,"type":0},{"id":0,"sLen":0,"time":71460.6741573034,"type":0},{"id":3,"sLen":0,"time":71797.7528089888,"type":0},{"id":3,"sLen":0,"time":72134.8314606741,"type":0},{"id":3,"sLen":0,"time":72471.9101123595,"type":0},{"id":3,"sLen":0,"time":72556.1797752809,"type":0},{"id":3,"sLen":0,"time":72640.4494382023,"type":0},{"id":3,"sLen":0,"time":72808.9887640449,"type":0},{"id":3,"sLen":0,"time":72893.2584269663,"type":0},{"id":3,"sLen":0,"time":72977.5280898877,"type":0},{"id":0,"sLen":0,"time":73146.0674157303,"type":0},{"id":0,"sLen":0,"time":73230.3370786517,"type":0},{"id":0,"sLen":0,"time":73314.6067415731,"type":0},{"id":0,"sLen":0,"time":73483.1460674157,"type":0},{"id":0,"sLen":0,"time":73567.4157303371,"type":0},{"id":0,"sLen":0,"time":73651.6853932585,"type":0},{"id":0,"sLen":0,"time":73820.2247191011,"type":0},{"id":0,"sLen":0,"time":73904.4943820225,"type":0},{"id":0,"sLen":0,"time":73988.7640449438,"type":0},{"id":0,"sLen":0,"time":74157.3033707866,"type":0},{"id":0,"sLen":0,"time":74241.5730337079,"type":0},{"id":0,"sLen":0,"time":74325.8426966292,"type":0},{"id":0,"sLen":0,"time":74410.1123595506,"type":0},{"id":3,"sLen":0,"time":74494.382022472,"type":0},{"id":3,"sLen":0,"time":74578.6516853933,"type":0},{"id":3,"sLen":0,"time":74662.9213483146,"type":0},{"id":3,"sLen":0,"time":74747.191011236,"type":0},{"id":0,"sLen":0,"time":74831.4606741573,"type":0},{"id":0,"sLen":0,"time":74915.7303370787,"type":0},{"id":0,"sLen":0,"time":75000,"type":0},{"id":0,"sLen":0,"time":75084.2696629214,"type":0},{"id":3,"sLen":0,"time":75168.5393258427,"type":0},{"id":0,"sLen":0,"time":75505.6179775281,"type":0},{"id":3,"sLen":0,"time":75842.6966292135,"type":0},{"id":0,"sLen":0,"time":76179.7752808989,"type":0},{"id":0,"sLen":0,"time":76516.8539325843,"type":0},{"id":3,"sLen":0,"time":76853.9325842697,"type":0},{"id":3,"sLen":0,"time":77191.0112359551,"type":0},{"id":0,"sLen":0,"time":77528.0898876405,"type":0},{"id":3,"sLen":0,"time":77865.1685393259,"type":0},{"id":3,"sLen":0,"time":78033.7078651686,"type":0},{"id":0,"sLen":0,"time":78202.2471910113,"type":0},{"id":0,"sLen":0,"time":78539.3258426967,"type":0},{"id":3,"sLen":0,"time":78876.4044943821,"type":0},{"id":0,"sLen":0,"time":79213.4831460675,"type":0},{"id":0,"sLen":0,"time":79550.5617977529,"type":0},{"id":3,"sLen":0,"time":79887.6404494383,"type":0},{"id":0,"sLen":0,"time":80140.4494382023,"type":0},{"id":0,"sLen":0,"time":80224.7191011237,"type":0},{"id":0,"sLen":0,"time":80308.988764045,"type":0},{"id":0,"sLen":0,"time":80477.5280898877,"type":0},{"id":3,"sLen":84.269662921346,"time":80730.3370786517,"type":0},{"id":0,"sLen":0,"time":80898.8764044945,"type":0},{"id":3,"sLen":0,"time":81067.4157303371,"type":0},{"id":0,"sLen":0,"time":81235.9550561799,"type":0},{"id":3,"sLen":0,"time":81404.4943820225,"type":0},{"id":0,"sLen":0,"time":81573.0337078652,"type":0},{"id":3,"sLen":0,"time":81741.5730337079,"type":0},{"id":0,"sLen":0,"time":81910.1123595506,"type":0},{"id":3,"sLen":0,"time":82078.6516853933,"type":0},{"id":3,"sLen":0,"time":82247.191011236,"type":0},{"id":0,"sLen":0,"time":82415.7303370787,"type":0},{"id":3,"sLen":0,"time":82584.2696629214,"type":0},{"id":0,"sLen":0,"time":82752.8089887641,"type":0},{"id":3,"sLen":0,"time":82921.3483146068,"type":0},{"id":0,"sLen":0,"time":83089.8876404495,"type":0},{"id":3,"sLen":0,"time":83258.4269662922,"type":0},{"id":3,"sLen":0,"time":83426.9662921349,"type":0},{"id":0,"sLen":0,"time":83595.5056179776,"type":0},{"id":0,"sLen":0,"time":83764.0449438203,"type":0},{"id":0,"sLen":0,"time":83932.584269663,"type":0},{"id":3,"sLen":0,"time":84269.6629213484,"type":0},{"id":3,"sLen":0,"time":84606.7415730338,"type":0},{"id":3,"sLen":0,"time":84775.2808988765,"type":0},{"id":3,"sLen":0,"time":84943.8202247192,"type":0},{"id":3,"sLen":0,"time":85280.8988764046,"type":0},{"id":0,"sLen":0,"time":85617.97752809,"type":0},{"id":0,"sLen":0,"time":85702.2471910113,"type":0},{"id":3,"sLen":0,"time":85870.786516854,"type":0},{"id":0,"sLen":0,"time":86039.3258426967,"type":0},{"id":0,"sLen":0,"time":86292.1348314607,"type":0},{"id":3,"sLen":0,"time":86629.2134831461,"type":0},{"id":0,"sLen":0,"time":87134.8314606741,"type":0},{"id":3,"sLen":0,"time":87303.3707865169,"type":0},{"id":0,"sLen":0,"time":87640.4494382023,"type":0},{"id":3,"sLen":0,"time":87977.5280898877,"type":0},{"id":0,"sLen":0,"time":88483.1460674157,"type":0},{"id":3,"sLen":0,"time":88651.6853932584,"type":0},{"id":0,"sLen":0,"time":88988.7640449438,"type":0},{"id":3,"sLen":0,"time":89325.8426966292,"type":0},{"id":0,"sLen":0,"time":89831.4606741573,"type":0},{"id":3,"sLen":0,"time":90000,"type":0},{"id":0,"sLen":0,"time":90337.0786516854,"type":0},{"id":3,"sLen":0,"time":90674.1573033708,"type":0},{"id":0,"sLen":0,"time":91179.7752808989,"type":0},{"id":3,"sLen":0,"time":91348.3146067416,"type":0},{"id":0,"sLen":0,"time":91685.393258427,"type":0},{"id":3,"sLen":0,"time":92022.4719101124,"type":0},{"id":0,"sLen":0,"time":92528.0898876405,"type":0},{"id":3,"sLen":0,"time":92696.6292134832,"type":0},{"id":0,"sLen":0,"time":93033.7078651686,"type":0},{"id":3,"sLen":0,"time":93370.786516854,"type":0},{"id":0,"sLen":0,"time":93876.404494382,"type":0},{"id":3,"sLen":0,"time":94044.9438202248,"type":0},{"id":0,"sLen":0,"time":94382.0224719102,"type":0},{"id":3,"sLen":0,"time":94719.1011235956,"type":0},{"id":0,"sLen":0,"time":95224.7191011236,"type":0},{"id":3,"sLen":0,"time":95393.2584269663,"type":0},{"id":0,"sLen":0,"time":95730.3370786517,"type":0},{"id":0,"sLen":0,"time":96067.4157303371,"type":0},{"id":0,"sLen":0,"time":96404.4943820225,"type":0},{"id":0,"sLen":0,"time":96573.0337078652,"type":0},{"id":3,"sLen":0,"time":96741.5730337079,"type":0},{"id":3,"sLen":0,"time":96825.8426966292,"type":0},{"id":3,"sLen":0,"time":96910.1123595505,"type":0},{"id":0,"sLen":0,"time":97078.6516853933,"type":0},{"id":3,"sLen":0,"time":97415.7303370787,"type":0},{"id":3,"sLen":0,"time":97752.808988764,"type":0},{"id":0,"sLen":0,"time":97921.3483146067,"type":0},{"id":3,"sLen":0,"time":98089.8876404494,"type":0},{"id":0,"sLen":0,"time":98426.9662921348,"type":0},{"id":3,"sLen":0,"time":98764.0449438202,"type":0},{"id":0,"sLen":0,"time":99101.1235955056,"type":0},{"id":0,"sLen":0,"time":99269.6629213483,"type":0},{"id":3,"sLen":0,"time":99438.202247191,"type":0},{"id":0,"sLen":0,"time":99775.2808988764,"type":0},{"id":3,"sLen":0,"time":100112.359550562,"type":0},{"id":3,"sLen":0,"time":100449.438202247,"type":0},{"id":0,"sLen":0,"time":100617.97752809,"type":0},{"id":3,"sLen":0,"time":100786.516853933,"type":0},{"id":0,"sLen":0,"time":101123.595505618,"type":0},{"id":3,"sLen":0,"time":101460.674157303,"type":0},{"id":3,"sLen":0,"time":101797.752808989,"type":0},{"id":0,"sLen":0,"time":101966.292134831,"type":0},{"id":3,"sLen":0,"time":102134.831460674,"type":0},{"id":0,"sLen":0,"time":102471.91011236,"type":0},{"id":3,"sLen":0,"time":102808.988764045,"type":0},{"id":3,"sLen":0,"time":103146.06741573,"type":0},{"id":0,"sLen":0,"time":103314.606741573,"type":0},{"id":3,"sLen":0,"time":103483.146067416,"type":0},{"id":0,"sLen":0,"time":103820.224719101,"type":0},{"id":3,"sLen":0,"time":104157.303370787,"type":0},{"id":0,"sLen":0,"time":104494.382022472,"type":0},{"id":0,"sLen":0,"time":104662.921348315,"type":0},{"id":3,"sLen":0,"time":104831.460674157,"type":0},{"id":0,"sLen":0,"time":105168.539325843,"type":0},{"id":3,"sLen":0,"time":105505.617977528,"type":0},{"id":3,"sLen":0,"time":105842.696629213,"type":0},{"id":0,"sLen":0,"time":106011.235955056,"type":0},{"id":3,"sLen":0,"time":106179.775280899,"type":0},{"id":0,"sLen":0,"time":106516.853932584,"type":0},{"id":0,"sLen":0,"time":106853.93258427,"type":0},{"id":0,"sLen":0,"time":107191.011235955,"type":0},{"id":3,"sLen":0,"time":107528.08988764,"type":0},{"id":0,"sLen":0,"time":107865.168539326,"type":0},{"id":0,"sLen":0,"time":108202.247191011,"type":0},{"id":0,"sLen":0,"time":108539.325842697,"type":0},{"id":0,"sLen":0,"time":108876.404494382,"type":0},{"id":3,"sLen":0,"time":109213.483146068,"type":0},{"id":3,"sLen":0,"time":109550.561797753,"type":0},{"id":3,"sLen":0,"time":109887.640449438,"type":0},{"id":3,"sLen":0,"time":110224.719101124,"type":0},{"id":0,"sLen":0,"time":110561.797752809,"type":0},{"id":0,"sLen":0,"time":110730.337078652,"type":0},{"id":0,"sLen":0,"time":110898.876404495,"type":0},{"id":3,"sLen":0,"time":111235.95505618,"type":0},{"id":3,"sLen":0,"time":111573.033707865,"type":0},{"id":3,"sLen":0,"time":111910.112359551,"type":0},{"id":3,"sLen":0,"time":112247.191011236,"type":0},{"id":0,"sLen":0,"time":112584.269662922,"type":0},{"id":3,"sLen":0,"time":112921.348314607,"type":0},{"id":0,"sLen":0,"time":113258.426966292,"type":0},{"id":3,"sLen":0,"time":113932.584269663,"type":0},{"id":0,"sLen":0,"time":114269.662921349,"type":0},{"id":3,"sLen":0,"time":114438.202247191,"type":0},{"id":0,"sLen":0,"time":114606.741573034,"type":0},{"id":3,"sLen":0,"time":114943.820224719,"type":0},{"id":0,"sLen":0,"time":115280.898876405,"type":0},{"id":3,"sLen":0,"time":115617.97752809,"type":0},{"id":3,"sLen":0,"time":115786.516853933,"type":0},{"id":0,"sLen":0,"time":115955.056179776,"type":0},{"id":3,"sLen":0,"time":116123.595505618,"type":0},{"id":0,"sLen":0,"time":116292.134831461,"type":0},{"id":0,"sLen":0,"time":116629.213483146,"type":0},{"id":3,"sLen":0,"time":116966.292134832,"type":0},{"id":3,"sLen":0,"time":117303.370786517,"type":0},{"id":0,"sLen":0,"time":117640.449438203,"type":0},{"id":3,"sLen":0,"time":117977.528089888,"type":0},{"id":3,"sLen":0,"time":118061.797752809,"type":0},{"id":0,"sLen":0,"time":118314.606741573,"type":0},{"id":3,"sLen":0,"time":118398.876404495,"type":0},{"id":0,"sLen":0,"time":118651.685393258,"type":0},{"id":3,"sLen":0,"time":118820.224719101,"type":0},{"id":3,"sLen":0,"time":118904.494382022,"type":0},{"id":3,"sLen":0,"time":119073.033707865,"type":0},{"id":0,"sLen":0,"time":119241.573033708,"type":0},{"id":3,"sLen":0,"time":119410.112359551,"type":0},{"id":3,"sLen":0,"time":119494.382022472,"type":0},{"id":0,"sLen":0,"time":119662.921348315,"type":0},{"id":0,"sLen":0,"time":119831.460674157,"type":0},{"id":0,"sLen":0,"time":119915.730337079,"type":0},{"id":3,"sLen":0,"time":120000,"type":0},{"id":0,"sLen":0,"time":120168.539325843,"type":0},{"id":0,"sLen":0,"time":120252.808988764,"type":0},{"id":3,"sLen":0,"time":120505.617977528,"type":0},{"id":0,"sLen":0,"time":120674.157303371,"type":0},{"id":3,"sLen":0,"time":120758.426966292,"type":0},{"id":3,"sLen":0,"time":120842.696629213,"type":0},{"id":0,"sLen":0,"time":121011.235955056,"type":0},{"id":0,"sLen":0,"time":121095.505617978,"type":0},{"id":0,"sLen":0,"time":121179.775280899,"type":0}],"position":"girlfriend","type":2,"characters":["pico-speakers"],"visible":false}],"codenameChart":true,"stage":"tank","noteTypes":["No Anim Note"]} \ No newline at end of file +{"events":[{"params":[1],"time":5393.25842696629,"name":"Camera Movement"},{"params":[0],"time":10786.5168539326,"name":"Camera Movement"},{"params":[1],"time":14831.4606741573,"name":"Camera Movement"},{"params":[0],"time":21573.0337078652,"name":"Camera Movement"},{"params":[1],"time":24269.6629213483,"name":"Camera Movement"},{"params":[0],"time":26966.2921348315,"name":"Camera Movement"},{"params":[1],"time":28314.606741573,"name":"Camera Movement"},{"params":[0],"time":31011.2359550562,"name":"Camera Movement"},{"params":[1],"time":37752.808988764,"name":"Camera Movement"},{"params":[0],"time":43146.0674157303,"name":"Camera Movement"},{"params":[1],"time":48539.3258426966,"name":"Camera Movement"},{"params":[0],"time":53932.5842696629,"name":"Camera Movement"},{"params":[1],"time":59325.8426966292,"name":"Camera Movement"},{"params":[0],"time":62022.4719101124,"name":"Camera Movement"},{"params":[1],"time":67415.7303370787,"name":"Camera Movement"},{"params":[0],"time":75505.6179775281,"name":"Camera Movement"},{"params":[1],"time":80898.8764044945,"name":"Camera Movement"},{"params":[0],"time":86292.1348314608,"name":"Camera Movement"},{"params":[1],"time":91685.3932584271,"name":"Camera Movement"},{"params":[0],"time":97078.6516853934,"name":"Camera Movement"},{"params":[1],"time":102471.91011236,"name":"Camera Movement"},{"params":[0],"time":107865.168539326,"name":"Camera Movement"},{"params":[1],"time":113258.426966292,"name":"Camera Movement"}],"scrollSpeed":1.75,"strumLines":[{"notes":[{"id":2,"sLen":0,"time":0,"type":0},{"id":0,"sLen":0,"time":337.078644,"type":0},{"id":0,"sLen":0,"time":674.1573,"type":0},{"id":2,"sLen":0,"time":1011.23596,"type":0},{"id":1,"sLen":0,"time":1348.31458,"type":0},{"id":3,"sLen":0,"time":1685.39331,"type":0},{"id":1,"sLen":0,"time":2022.47192,"type":0},{"id":1,"sLen":0,"time":2359.55054,"type":0},{"id":0,"sLen":0,"time":2528.08984,"type":0},{"id":2,"sLen":0,"time":2696.62915,"type":0},{"id":0,"sLen":0,"time":3033.70776,"type":0},{"id":0,"sLen":0,"time":3370.78662,"type":0},{"id":2,"sLen":0,"time":3707.86523,"type":0},{"id":1,"sLen":0,"time":4044.94385,"type":0},{"id":3,"sLen":0,"time":4382.02246,"type":0},{"id":1,"sLen":0,"time":4719.101,"type":0},{"id":1,"sLen":0,"time":5056.17969,"type":0},{"id":0,"sLen":0,"time":5224.719,"type":0},{"id":0,"sLen":0,"time":11123.5957,"type":0},{"id":1,"sLen":0,"time":11292.1348,"type":0},{"id":3,"sLen":252.808989999996,"time":11460.6738,"type":0},{"id":1,"sLen":252.80899,"time":11797.7529,"type":0},{"id":0,"sLen":0,"time":12134.8311,"type":0},{"id":0,"sLen":0,"time":12471.91,"type":0},{"id":0,"sLen":0,"time":12808.9883,"type":0},{"id":3,"sLen":0,"time":13146.0674,"type":0},{"id":1,"sLen":0,"time":13483.1465,"type":0},{"id":3,"sLen":0,"time":13820.2246,"type":0},{"id":1,"sLen":0,"time":13988.7637,"type":0},{"id":3,"sLen":0,"time":14325.8428,"type":0},{"id":1,"sLen":0,"time":14494.3818,"type":0},{"id":1,"sLen":0,"time":14831.4609,"type":0},{"id":3,"sLen":0,"time":15168.5391,"type":0},{"id":1,"sLen":0,"time":15505.6182,"type":0},{"id":1,"sLen":0,"time":15842.6963,"type":0},{"id":0,"sLen":0,"time":16011.2363,"type":0},{"id":3,"sLen":0,"time":21573.0332,"type":0},{"id":0,"sLen":0,"time":21910.1133,"type":0},{"id":3,"sLen":0,"time":22247.1914,"type":0},{"id":0,"sLen":0,"time":22584.27,"type":0},{"id":3,"sLen":0,"time":22921.3477,"type":0},{"id":2,"sLen":0,"time":23258.4277,"type":0},{"id":3,"sLen":0,"time":23595.5059,"type":0},{"id":1,"sLen":0,"time":23932.584,"type":0},{"id":0,"sLen":0,"time":24269.6621,"type":0},{"id":3,"sLen":0,"time":26966.293,"type":0},{"id":0,"sLen":0,"time":27303.3711,"type":0},{"id":3,"sLen":0,"time":27640.45,"type":0},{"id":0,"sLen":0,"time":27977.5273,"type":0},{"id":3,"sLen":0,"time":28314.6074,"type":0},{"id":2,"sLen":0,"time":28651.6855,"type":0},{"id":3,"sLen":0,"time":28988.7637,"type":0},{"id":1,"sLen":0,"time":29325.8418,"type":0},{"id":0,"sLen":0,"time":29662.9219,"type":0},{"id":0,"sLen":0,"time":30000,"type":0},{"id":0,"sLen":0,"time":30337.0781,"type":0},{"id":0,"sLen":0,"time":30674.1582,"type":0},{"id":0,"sLen":0,"time":31011.2363,"type":0},{"id":0,"sLen":0,"time":31348.3145,"type":0},{"id":0,"sLen":0,"time":31685.3926,"type":0},{"id":0,"sLen":0,"time":31853.9316,"type":0},{"id":0,"sLen":0,"time":32022.4727,"type":0},{"id":0,"sLen":0,"time":32191.0117,"type":0},{"id":0,"sLen":0,"time":32359.55,"type":0},{"id":2,"sLen":0,"time":32696.6289,"type":0},{"id":0,"sLen":0,"time":33033.707,"type":0},{"id":2,"sLen":0,"time":33370.7852,"type":0},{"id":3,"sLen":0,"time":33539.3242,"type":0},{"id":1,"sLen":0,"time":33707.8633,"type":0},{"id":0,"sLen":0,"time":34044.9453,"type":0},{"id":1,"sLen":0,"time":34382.0234,"type":0},{"id":1,"sLen":0,"time":34719.1,"type":0},{"id":0,"sLen":0,"time":34887.64,"type":0},{"id":3,"sLen":0,"time":35056.18,"type":0},{"id":2,"sLen":0,"time":35393.2578,"type":0},{"id":0,"sLen":0,"time":35730.3359,"type":0},{"id":2,"sLen":0,"time":36067.4141,"type":0},{"id":3,"sLen":0,"time":36404.4961,"type":0},{"id":0,"sLen":0,"time":36741.5742,"type":0},{"id":0,"sLen":0,"time":37078.6523,"type":0},{"id":1,"sLen":0,"time":37247.19,"type":0},{"id":1,"sLen":0,"time":37415.73,"type":0},{"id":0,"sLen":0,"time":37584.27,"type":0},{"id":3,"sLen":0,"time":43146.0664,"type":0},{"id":1,"sLen":0,"time":43314.6055,"type":0},{"id":0,"sLen":0,"time":43483.1445,"type":0},{"id":1,"sLen":0,"time":43820.2266,"type":0},{"id":0,"sLen":0,"time":44157.3047,"type":0},{"id":2,"sLen":0,"time":44494.3828,"type":0},{"id":3,"sLen":0,"time":44662.92,"type":0},{"id":2,"sLen":0,"time":44831.46,"type":0},{"id":3,"sLen":0,"time":45168.54,"type":0},{"id":2,"sLen":0,"time":45505.6172,"type":0},{"id":3,"sLen":0,"time":45842.6953,"type":0},{"id":1,"sLen":0,"time":46179.7734,"type":0},{"id":0,"sLen":0,"time":46516.8555,"type":0},{"id":1,"sLen":0,"time":46853.9336,"type":0},{"id":2,"sLen":0,"time":47191.01,"type":0},{"id":1,"sLen":0,"time":47528.09,"type":0},{"id":1,"sLen":0,"time":47865.168,"type":0},{"id":1,"sLen":0,"time":48202.2461,"type":0},{"id":2,"sLen":0,"time":53932.5859,"type":0},{"id":0,"sLen":0,"time":54269.6641,"type":0},{"id":0,"sLen":0,"time":54606.7422,"type":0},{"id":2,"sLen":0,"time":54943.82,"type":0},{"id":1,"sLen":0,"time":55280.9,"type":0},{"id":3,"sLen":0,"time":55617.9766,"type":0},{"id":1,"sLen":0,"time":55955.0547,"type":0},{"id":1,"sLen":0,"time":56292.1367,"type":0},{"id":0,"sLen":0,"time":56460.6758,"type":0},{"id":2,"sLen":0,"time":56629.2148,"type":0},{"id":0,"sLen":0,"time":56966.293,"type":0},{"id":0,"sLen":0,"time":57303.37,"type":0},{"id":2,"sLen":0,"time":57640.45,"type":0},{"id":1,"sLen":0,"time":57977.5273,"type":0},{"id":3,"sLen":0,"time":58314.6055,"type":0},{"id":1,"sLen":0,"time":58651.6836,"type":0},{"id":1,"sLen":0,"time":58988.7656,"type":0},{"id":0,"sLen":0,"time":59157.3047,"type":0},{"id":0,"sLen":168.539321999997,"time":59325.8438,"type":0},{"id":0,"sLen":168.539321999997,"time":60000,"type":0},{"id":0,"sLen":168.539322000004,"time":60674.1563,"type":0},{"id":0,"sLen":168.539321999997,"time":61348.3164,"type":0},{"id":1,"sLen":0,"time":62022.4719101124,"type":1},{"id":1,"sLen":0,"time":64719.1,"type":0},{"id":0,"sLen":0,"time":65056.18,"type":0},{"id":3,"sLen":0,"time":65393.2578,"type":0},{"id":1,"sLen":0,"time":65730.3359,"type":0},{"id":0,"sLen":0,"time":66067.4141,"type":0},{"id":0,"sLen":0,"time":66404.49,"type":0},{"id":0,"sLen":0,"time":66741.57,"type":0},{"id":0,"sLen":0,"time":66910.11,"type":0},{"id":0,"sLen":0,"time":67078.65,"type":0},{"id":1,"sLen":0,"time":67415.73,"type":0},{"id":3,"sLen":0,"time":67752.81,"type":0},{"id":1,"sLen":0,"time":67921.35,"type":0},{"id":3,"sLen":0,"time":68258.43,"type":0},{"id":1,"sLen":0,"time":68426.97,"type":0},{"id":1,"sLen":0,"time":68764.05,"type":0},{"id":3,"sLen":0,"time":69101.125,"type":0},{"id":1,"sLen":0,"time":69438.2,"type":0},{"id":1,"sLen":0,"time":69775.28,"type":0},{"id":0,"sLen":0,"time":69943.82,"type":0},{"id":0,"sLen":0,"time":75505.62,"type":0},{"id":0,"sLen":0,"time":75842.6953,"type":0},{"id":3,"sLen":0,"time":76179.77,"type":0},{"id":0,"sLen":0,"time":76516.85,"type":0},{"id":3,"sLen":0,"time":76853.93,"type":0},{"id":3,"sLen":0,"time":77191.01,"type":0},{"id":0,"sLen":0,"time":77359.55,"type":0},{"id":1,"sLen":252.808990000005,"time":77528.0859,"type":0},{"id":0,"sLen":0,"time":77865.17,"type":0},{"id":3,"sLen":0,"time":78202.25,"type":0},{"id":1,"sLen":0,"time":78539.33,"type":0},{"id":0,"sLen":0,"time":78876.41,"type":0},{"id":3,"sLen":0,"time":79213.4844,"type":0},{"id":1,"sLen":0,"time":79550.56,"type":0},{"id":0,"sLen":0,"time":79803.37,"type":0},{"id":1,"sLen":0,"time":80056.18,"type":0},{"id":0,"sLen":0,"time":80224.72,"type":0},{"id":2,"sLen":0,"time":80561.8,"type":0},{"id":0,"sLen":0,"time":86292.13,"type":0},{"id":1,"sLen":0,"time":86629.21,"type":0},{"id":3,"sLen":0,"time":86966.29,"type":0},{"id":0,"sLen":0,"time":87303.37,"type":0},{"id":0,"sLen":0,"time":87640.45,"type":0},{"id":1,"sLen":0,"time":87977.53,"type":0},{"id":3,"sLen":0,"time":88314.61,"type":0},{"id":0,"sLen":0,"time":88651.69,"type":0},{"id":2,"sLen":0,"time":88988.7656,"type":0},{"id":3,"sLen":0,"time":89325.84,"type":0},{"id":0,"sLen":0,"time":89662.92,"type":0},{"id":3,"sLen":0,"time":90000,"type":0},{"id":0,"sLen":0,"time":90337.08,"type":0},{"id":3,"sLen":0,"time":90674.16,"type":0},{"id":0,"sLen":0,"time":91011.2344,"type":0},{"id":3,"sLen":337.078643999994,"time":91348.31,"type":0},{"id":2,"sLen":0,"time":97078.65,"type":0},{"id":0,"sLen":0,"time":97415.73,"type":0},{"id":2,"sLen":0,"time":97584.27,"type":0},{"id":0,"sLen":0,"time":97921.35,"type":0},{"id":2,"sLen":0,"time":98089.89,"type":0},{"id":0,"sLen":0,"time":98426.97,"type":0},{"id":2,"sLen":0,"time":98595.51,"type":0},{"id":0,"sLen":0,"time":98932.5859,"type":0},{"id":1,"sLen":0,"time":99101.125,"type":0},{"id":1,"sLen":0,"time":99438.2,"type":0},{"id":2,"sLen":0,"time":99775.28,"type":0},{"id":0,"sLen":0,"time":100112.359,"type":0},{"id":2,"sLen":0,"time":100280.9,"type":0},{"id":0,"sLen":0,"time":100617.977,"type":0},{"id":2,"sLen":0,"time":100786.516,"type":0},{"id":0,"sLen":0,"time":101123.594,"type":0},{"id":2,"sLen":0,"time":101292.133,"type":0},{"id":0,"sLen":0,"time":101629.211,"type":0},{"id":1,"sLen":0,"time":101797.75,"type":0},{"id":1,"sLen":0,"time":102134.828,"type":0},{"id":2,"sLen":0,"time":102471.906,"type":0},{"id":0,"sLen":0,"time":102808.992,"type":0},{"id":2,"sLen":0,"time":102977.531,"type":0},{"id":0,"sLen":0,"time":103314.609,"type":0},{"id":2,"sLen":0,"time":103483.148,"type":0},{"id":0,"sLen":0,"time":103820.227,"type":0},{"id":2,"sLen":0,"time":103988.766,"type":0},{"id":0,"sLen":0,"time":104325.844,"type":0},{"id":1,"sLen":0,"time":104494.383,"type":0},{"id":1,"sLen":0,"time":104831.461,"type":0},{"id":2,"sLen":0,"time":105168.539,"type":0},{"id":0,"sLen":0,"time":105505.617,"type":0},{"id":2,"sLen":0,"time":105674.156,"type":0},{"id":0,"sLen":0,"time":106011.234,"type":0},{"id":2,"sLen":0,"time":106179.773,"type":0},{"id":0,"sLen":0,"time":106516.852,"type":0},{"id":2,"sLen":0,"time":106685.391,"type":0},{"id":0,"sLen":0,"time":107022.469,"type":0},{"id":1,"sLen":0,"time":107191.008,"type":0},{"id":1,"sLen":0,"time":107528.086,"type":0},{"id":1,"sLen":0,"time":107865.172,"type":0},{"id":1,"sLen":0,"time":108202.25,"type":0},{"id":0,"sLen":0,"time":108370.789,"type":0},{"id":1,"sLen":0,"time":108539.328,"type":0},{"id":1,"sLen":0,"time":108876.406,"type":0},{"id":0,"sLen":0,"time":109044.945,"type":0},{"id":1,"sLen":0,"time":109213.484,"type":0},{"id":1,"sLen":0,"time":109550.563,"type":0},{"id":0,"sLen":0,"time":109719.1,"type":0},{"id":1,"sLen":0,"time":109887.641,"type":0},{"id":1,"sLen":0,"time":110224.719,"type":0},{"id":0,"sLen":0,"time":110393.258,"type":0},{"id":2,"sLen":0,"time":113258.43,"type":0},{"id":3,"sLen":0,"time":113595.508,"type":0},{"id":0,"sLen":0,"time":113932.586,"type":0},{"id":1,"sLen":0,"time":114269.664,"type":0},{"id":1,"sLen":0,"time":114606.742,"type":0},{"id":3,"sLen":0,"time":114943.82,"type":0},{"id":1,"sLen":0,"time":115280.9,"type":0},{"id":1,"sLen":0,"time":115617.977,"type":0},{"id":0,"sLen":0,"time":115786.516,"type":0},{"id":2,"sLen":589.887633999984,"time":115955.055,"type":0}],"position":"dad","characters":["tankman"],"visible":true,"type":0},{"notes":[{"id":2,"sLen":0,"time":5393.2583,"type":0},{"id":0,"sLen":0,"time":5730.337,"type":0},{"id":0,"sLen":0,"time":6067.41553,"type":0},{"id":2,"sLen":0,"time":6404.494,"type":0},{"id":1,"sLen":0,"time":6741.573,"type":0},{"id":3,"sLen":0,"time":7078.652,"type":0},{"id":1,"sLen":0,"time":7415.73047,"type":0},{"id":1,"sLen":0,"time":7752.809,"type":0},{"id":0,"sLen":0,"time":7921.348,"type":0},{"id":2,"sLen":0,"time":8089.8877,"type":0},{"id":0,"sLen":0,"time":8426.966,"type":0},{"id":0,"sLen":0,"time":8764.045,"type":0},{"id":2,"sLen":0,"time":9101.124,"type":0},{"id":1,"sLen":0,"time":9438.202,"type":0},{"id":3,"sLen":0,"time":9775.281,"type":0},{"id":1,"sLen":0,"time":10112.3594,"type":0},{"id":1,"sLen":0,"time":10449.4385,"type":0},{"id":0,"sLen":0,"time":10617.9775,"type":0},{"id":0,"sLen":252.808990000001,"time":14831.4609,"type":0},{"id":1,"sLen":252.80899,"time":15168.5391,"type":0},{"id":3,"sLen":252.80899,"time":15505.6182,"type":0},{"id":1,"sLen":252.808990000001,"time":15842.6963,"type":0},{"id":3,"sLen":0,"time":16179.7754,"type":0},{"id":0,"sLen":0,"time":16516.8535,"type":0},{"id":1,"sLen":0,"time":16685.3926,"type":0},{"id":3,"sLen":252.808990000001,"time":16853.9316,"type":0},{"id":1,"sLen":252.808990000001,"time":17191.0117,"type":0},{"id":0,"sLen":0,"time":17528.09,"type":0},{"id":0,"sLen":0,"time":17865.168,"type":0},{"id":0,"sLen":0,"time":18202.248,"type":0},{"id":3,"sLen":0,"time":18539.3262,"type":0},{"id":1,"sLen":0,"time":18876.4043,"type":0},{"id":3,"sLen":0,"time":19213.4824,"type":0},{"id":1,"sLen":0,"time":19382.0215,"type":0},{"id":3,"sLen":0,"time":19719.1016,"type":0},{"id":1,"sLen":0,"time":19887.64,"type":0},{"id":1,"sLen":0,"time":20224.7188,"type":0},{"id":3,"sLen":0,"time":20561.7969,"type":0},{"id":1,"sLen":0,"time":20898.877,"type":0},{"id":1,"sLen":0,"time":21235.9551,"type":0},{"id":0,"sLen":0,"time":21404.4941,"type":0},{"id":3,"sLen":0,"time":21573.0332,"type":0},{"id":3,"sLen":0,"time":21910.1133,"type":0},{"id":3,"sLen":0,"time":22247.1914,"type":0},{"id":3,"sLen":0,"time":22584.27,"type":0},{"id":1,"sLen":0,"time":22921.3477,"type":0},{"id":1,"sLen":0,"time":23258.4277,"type":0},{"id":3,"sLen":252.808990000001,"time":23595.5059,"type":0},{"id":0,"sLen":252.808990000012,"time":23932.584,"type":0},{"id":3,"sLen":0,"time":24269.6621,"type":0},{"id":0,"sLen":0,"time":24606.7422,"type":0},{"id":3,"sLen":0,"time":24943.82,"type":0},{"id":0,"sLen":0,"time":25280.8984,"type":0},{"id":3,"sLen":0,"time":25617.9766,"type":0},{"id":2,"sLen":0,"time":25955.0566,"type":0},{"id":3,"sLen":0,"time":26292.1348,"type":0},{"id":1,"sLen":0,"time":26629.2129,"type":0},{"id":2,"sLen":589.88763400001,"time":26966.293,"type":0},{"id":3,"sLen":589.887633999992,"time":27640.45,"type":0},{"id":1,"sLen":589.887633999999,"time":28314.6074,"type":0},{"id":0,"sLen":337.078644000008,"time":28988.7637,"type":0},{"id":3,"sLen":0,"time":29662.9219,"type":0},{"id":0,"sLen":0,"time":30000,"type":0},{"id":3,"sLen":0,"time":30337.0781,"type":0},{"id":0,"sLen":0,"time":30674.1582,"type":0},{"id":3,"sLen":0,"time":31011.2363,"type":0},{"id":2,"sLen":0,"time":31348.3145,"type":0},{"id":3,"sLen":0,"time":31685.3926,"type":0},{"id":1,"sLen":0,"time":32022.4727,"type":0},{"id":1,"sLen":674.157300000003,"time":32359.55,"type":0},{"id":0,"sLen":0,"time":37752.81,"type":0},{"id":2,"sLen":0,"time":38089.8867,"type":0},{"id":0,"sLen":0,"time":38426.9648,"type":0},{"id":2,"sLen":0,"time":38764.043,"type":0},{"id":3,"sLen":0,"time":38932.5859,"type":0},{"id":1,"sLen":0,"time":39101.125,"type":0},{"id":0,"sLen":0,"time":39438.2031,"type":0},{"id":1,"sLen":0,"time":39775.28,"type":0},{"id":1,"sLen":0,"time":40112.36,"type":0},{"id":0,"sLen":0,"time":40280.9,"type":0},{"id":3,"sLen":0,"time":40449.4375,"type":0},{"id":2,"sLen":0,"time":40786.5156,"type":0},{"id":0,"sLen":0,"time":41123.5938,"type":0},{"id":2,"sLen":0,"time":41460.6758,"type":0},{"id":3,"sLen":0,"time":41797.7539,"type":0},{"id":0,"sLen":0,"time":42134.832,"type":0},{"id":0,"sLen":0,"time":42471.91,"type":0},{"id":1,"sLen":0,"time":42640.45,"type":0},{"id":1,"sLen":0,"time":42808.99,"type":0},{"id":0,"sLen":0,"time":42977.5273,"type":0},{"id":3,"sLen":0,"time":48539.3242,"type":0},{"id":1,"sLen":0,"time":48707.8633,"type":0},{"id":0,"sLen":0,"time":48876.4063,"type":0},{"id":1,"sLen":0,"time":49213.4844,"type":0},{"id":0,"sLen":0,"time":49550.5625,"type":0},{"id":2,"sLen":0,"time":49887.64,"type":0},{"id":3,"sLen":0,"time":50056.18,"type":0},{"id":2,"sLen":0,"time":50224.72,"type":0},{"id":3,"sLen":0,"time":50561.7969,"type":0},{"id":2,"sLen":0,"time":50898.875,"type":0},{"id":3,"sLen":0,"time":51235.9531,"type":0},{"id":1,"sLen":0,"time":51573.0352,"type":0},{"id":0,"sLen":0,"time":51910.1133,"type":0},{"id":1,"sLen":0,"time":52247.19,"type":0},{"id":2,"sLen":0,"time":52584.27,"type":0},{"id":1,"sLen":0,"time":52921.3477,"type":0},{"id":1,"sLen":0,"time":53258.4258,"type":0},{"id":1,"sLen":0,"time":53595.5039,"type":0},{"id":1,"sLen":1011.23595999998,"time":53932.5859,"type":0},{"id":2,"sLen":505.617981000003,"time":55955.0547,"type":0},{"id":1,"sLen":674.157299999999,"time":56629.2148,"type":0},{"id":0,"sLen":252.808989999998,"time":58651.6836,"type":0},{"id":2,"sLen":0,"time":58988.7656,"type":0},{"id":2,"sLen":0,"time":59325.8438,"type":0},{"id":0,"sLen":0,"time":59662.92,"type":0},{"id":0,"sLen":0,"time":60000,"type":0},{"id":2,"sLen":0,"time":60337.08,"type":0},{"id":1,"sLen":0,"time":60674.1563,"type":0},{"id":3,"sLen":0,"time":61011.2344,"type":0},{"id":1,"sLen":0,"time":61348.3164,"type":0},{"id":1,"sLen":0,"time":61685.3945,"type":0},{"id":0,"sLen":0,"time":61853.9336,"type":0},{"id":2,"sLen":0,"time":62022.4727,"type":0},{"id":0,"sLen":0,"time":62359.55,"type":0},{"id":0,"sLen":0,"time":62696.63,"type":0},{"id":2,"sLen":0,"time":63033.707,"type":0},{"id":1,"sLen":0,"time":63370.7852,"type":0},{"id":3,"sLen":0,"time":63707.8633,"type":0},{"id":1,"sLen":0,"time":64044.9453,"type":0},{"id":1,"sLen":0,"time":64382.0234,"type":0},{"id":0,"sLen":0,"time":64550.5625,"type":0},{"id":1,"sLen":0,"time":64719.1,"type":0},{"id":0,"sLen":0,"time":65056.18,"type":0},{"id":3,"sLen":0,"time":65393.2578,"type":0},{"id":1,"sLen":0,"time":65730.3359,"type":0},{"id":3,"sLen":505.617981000003,"time":66067.4141,"type":0},{"id":1,"sLen":505.617981000003,"time":66741.57,"type":0},{"id":0,"sLen":505.617981000003,"time":67415.73,"type":0},{"id":1,"sLen":0,"time":68089.89,"type":0},{"id":0,"sLen":0,"time":68426.97,"type":0},{"id":3,"sLen":589.887633999999,"time":68764.05,"type":0},{"id":0,"sLen":337.078643999994,"time":69438.2,"type":0},{"id":1,"sLen":0,"time":70112.36,"type":0},{"id":0,"sLen":0,"time":70449.44,"type":0},{"id":3,"sLen":0,"time":70786.5156,"type":0},{"id":1,"sLen":0,"time":71123.59,"type":0},{"id":0,"sLen":0,"time":71460.67,"type":0},{"id":0,"sLen":0,"time":71797.75,"type":0},{"id":0,"sLen":0,"time":72134.83,"type":0},{"id":0,"sLen":0,"time":72303.37,"type":0},{"id":0,"sLen":0,"time":72471.91,"type":0},{"id":1,"sLen":0,"time":72808.99,"type":0},{"id":3,"sLen":0,"time":73146.07,"type":0},{"id":1,"sLen":0,"time":73314.61,"type":0},{"id":3,"sLen":0,"time":73651.69,"type":0},{"id":1,"sLen":0,"time":73820.23,"type":0},{"id":1,"sLen":0,"time":74157.3047,"type":0},{"id":3,"sLen":0,"time":74494.38,"type":0},{"id":1,"sLen":0,"time":74831.46,"type":0},{"id":1,"sLen":0,"time":75168.54,"type":0},{"id":0,"sLen":0,"time":75337.08,"type":0},{"id":1,"sLen":1264.04492,"time":75505.62,"type":0},{"id":0,"sLen":1264.04492,"time":76853.93,"type":0},{"id":3,"sLen":1264.04492,"time":78202.25,"type":0},{"id":0,"sLen":1011.23596000001,"time":79550.56,"type":0},{"id":0,"sLen":0,"time":80898.875,"type":0},{"id":0,"sLen":0,"time":81235.95,"type":0},{"id":3,"sLen":0,"time":81573.03,"type":0},{"id":0,"sLen":0,"time":81910.11,"type":0},{"id":3,"sLen":0,"time":82247.19,"type":0},{"id":3,"sLen":0,"time":82584.27,"type":0},{"id":0,"sLen":0,"time":82752.81,"type":0},{"id":1,"sLen":252.808990000005,"time":82921.35,"type":0},{"id":0,"sLen":0,"time":83258.43,"type":0},{"id":3,"sLen":0,"time":83595.51,"type":0},{"id":1,"sLen":0,"time":83932.5859,"type":0},{"id":0,"sLen":0,"time":84269.6641,"type":0},{"id":3,"sLen":0,"time":84606.74,"type":0},{"id":1,"sLen":0,"time":84943.82,"type":0},{"id":0,"sLen":0,"time":85196.63,"type":0},{"id":1,"sLen":0,"time":85449.44,"type":0},{"id":0,"sLen":0,"time":85617.98,"type":0},{"id":2,"sLen":0,"time":85955.0547,"type":0},{"id":0,"sLen":0,"time":91685.39,"type":0},{"id":1,"sLen":0,"time":92022.47,"type":0},{"id":3,"sLen":0,"time":92359.55,"type":0},{"id":0,"sLen":0,"time":92696.63,"type":0},{"id":0,"sLen":0,"time":93033.71,"type":0},{"id":1,"sLen":0,"time":93370.79,"type":0},{"id":3,"sLen":0,"time":93707.87,"type":0},{"id":0,"sLen":0,"time":94044.9453,"type":0},{"id":2,"sLen":0,"time":94382.02,"type":0},{"id":3,"sLen":0,"time":94719.1,"type":0},{"id":0,"sLen":0,"time":95056.18,"type":0},{"id":3,"sLen":0,"time":95393.26,"type":0},{"id":0,"sLen":0,"time":95730.3359,"type":0},{"id":3,"sLen":0,"time":96067.4141,"type":0},{"id":0,"sLen":0,"time":96404.49,"type":0},{"id":3,"sLen":337.078643999994,"time":96741.57,"type":0},{"id":0,"sLen":589.887633999955,"time":98426.97,"type":0},{"id":2,"sLen":589.887633999999,"time":99101.125,"type":0},{"id":0,"sLen":589.887633999999,"time":101123.594,"type":0},{"id":2,"sLen":337.078644000008,"time":101797.75,"type":0},{"id":2,"sLen":0,"time":102471.906,"type":0},{"id":3,"sLen":0,"time":102808.992,"type":0},{"id":2,"sLen":0,"time":102977.531,"type":0},{"id":3,"sLen":0,"time":103314.609,"type":0},{"id":2,"sLen":0,"time":103483.148,"type":0},{"id":3,"sLen":0,"time":103820.227,"type":0},{"id":2,"sLen":0,"time":103988.766,"type":0},{"id":3,"sLen":0,"time":104325.844,"type":0},{"id":1,"sLen":0,"time":104494.383,"type":0},{"id":1,"sLen":0,"time":104831.461,"type":0},{"id":2,"sLen":0,"time":105168.539,"type":0},{"id":3,"sLen":0,"time":105505.617,"type":0},{"id":2,"sLen":0,"time":105674.156,"type":0},{"id":3,"sLen":0,"time":106011.234,"type":0},{"id":2,"sLen":0,"time":106179.773,"type":0},{"id":3,"sLen":0,"time":106516.852,"type":0},{"id":2,"sLen":0,"time":106685.391,"type":0},{"id":3,"sLen":0,"time":107022.469,"type":0},{"id":1,"sLen":0,"time":107191.008,"type":0},{"id":1,"sLen":0,"time":107528.086,"type":0},{"id":3,"sLen":589.887634000028,"time":107865.172,"type":0},{"id":1,"sLen":589.887634000028,"time":108539.328,"type":0},{"id":0,"sLen":589.887633999999,"time":109213.484,"type":0},{"id":1,"sLen":589.887633999999,"time":109887.641,"type":0},{"id":1,"sLen":0,"time":110561.8,"type":0},{"id":1,"sLen":0,"time":110898.875,"type":0},{"id":0,"sLen":0,"time":111067.414,"type":0},{"id":1,"sLen":0,"time":111235.953,"type":0},{"id":1,"sLen":0,"time":111573.031,"type":0},{"id":0,"sLen":0,"time":111741.57,"type":0},{"id":1,"sLen":0,"time":111910.109,"type":0},{"id":1,"sLen":0,"time":112247.188,"type":0},{"id":0,"sLen":0,"time":112415.727,"type":0},{"id":1,"sLen":0,"time":112584.273,"type":0},{"id":1,"sLen":0,"time":112921.352,"type":0},{"id":0,"sLen":0,"time":113089.891,"type":0},{"id":3,"sLen":589.887633999999,"time":113258.43,"type":0},{"id":1,"sLen":589.887633999999,"time":113932.586,"type":0},{"id":0,"sLen":589.887633999984,"time":114606.742,"type":0},{"id":1,"sLen":337.078643999994,"time":115280.9,"type":0},{"id":2,"sLen":0,"time":115955.055,"type":0},{"id":3,"sLen":0,"time":116292.133,"type":0},{"id":0,"sLen":0,"time":116629.211,"type":0},{"id":1,"sLen":0,"time":116966.289,"type":0},{"id":1,"sLen":0,"time":117303.367,"type":0},{"id":3,"sLen":0,"time":117640.453,"type":0},{"id":1,"sLen":0,"time":117977.531,"type":0},{"id":1,"sLen":0,"time":118314.609,"type":0},{"id":0,"sLen":0,"time":118483.148,"type":0}],"position":"boyfriend","characters":["bf-holding-gf"],"visible":true,"type":1},{"notes":[{"id":0,"sLen":0,"time":0,"type":0},{"id":3,"sLen":0,"time":168.539325842697,"type":0},{"id":3,"sLen":0,"time":252.808988764045,"type":0},{"id":3,"sLen":0,"time":421.348314606742,"type":0},{"id":0,"sLen":0,"time":589.887640449438,"type":0},{"id":3,"sLen":0,"time":758.426966292135,"type":0},{"id":3,"sLen":0,"time":842.696629213483,"type":0},{"id":0,"sLen":0,"time":1011.23595505618,"type":0},{"id":0,"sLen":0,"time":1179.77528089888,"type":0},{"id":0,"sLen":0,"time":1264.04494382022,"type":0},{"id":3,"sLen":0,"time":1348.31460674157,"type":0},{"id":0,"sLen":0,"time":1516.85393258427,"type":0},{"id":0,"sLen":0,"time":1601.12359550562,"type":0},{"id":3,"sLen":0,"time":1853.93258426966,"type":0},{"id":0,"sLen":0,"time":2022.47191011236,"type":0},{"id":3,"sLen":0,"time":2106.74157303371,"type":0},{"id":3,"sLen":0,"time":2191.01123595506,"type":0},{"id":0,"sLen":0,"time":2359.55056179775,"type":0},{"id":0,"sLen":0,"time":2696.62921348315,"type":0},{"id":3,"sLen":0,"time":2865.16853932584,"type":0},{"id":3,"sLen":0,"time":2949.43820224719,"type":0},{"id":3,"sLen":0,"time":3117.97752808989,"type":0},{"id":0,"sLen":0,"time":3286.51685393258,"type":0},{"id":3,"sLen":0,"time":3455.05617977528,"type":0},{"id":3,"sLen":0,"time":3539.32584269663,"type":0},{"id":0,"sLen":0,"time":3707.86516853933,"type":0},{"id":0,"sLen":0,"time":3876.40449438202,"type":0},{"id":0,"sLen":0,"time":3960.67415730337,"type":0},{"id":3,"sLen":0,"time":4044.94382022472,"type":0},{"id":0,"sLen":0,"time":4213.48314606742,"type":0},{"id":0,"sLen":0,"time":4297.75280898876,"type":0},{"id":3,"sLen":0,"time":4550.56179775281,"type":0},{"id":0,"sLen":0,"time":4719.10112359551,"type":0},{"id":3,"sLen":0,"time":4803.37078651685,"type":0},{"id":3,"sLen":0,"time":4887.6404494382,"type":0},{"id":0,"sLen":0,"time":5056.1797752809,"type":0},{"id":0,"sLen":0,"time":5140.44943820225,"type":0},{"id":0,"sLen":0,"time":5224.7191011236,"type":0},{"id":0,"sLen":0,"time":5393.25842696629,"type":0},{"id":3,"sLen":0,"time":5561.79775280899,"type":0},{"id":3,"sLen":0,"time":5646.06741573034,"type":0},{"id":3,"sLen":0,"time":5814.60674157303,"type":0},{"id":0,"sLen":0,"time":5983.14606741573,"type":0},{"id":3,"sLen":0,"time":6151.68539325843,"type":0},{"id":3,"sLen":0,"time":6235.95505617978,"type":0},{"id":0,"sLen":0,"time":6404.49438202247,"type":0},{"id":0,"sLen":0,"time":6573.03370786517,"type":0},{"id":0,"sLen":0,"time":6657.30337078652,"type":0},{"id":3,"sLen":0,"time":6741.57303370786,"type":0},{"id":0,"sLen":0,"time":6910.11235955056,"type":0},{"id":0,"sLen":0,"time":6994.38202247191,"type":0},{"id":3,"sLen":0,"time":7247.19101123595,"type":0},{"id":0,"sLen":0,"time":7415.73033707865,"type":0},{"id":3,"sLen":0,"time":7500,"type":0},{"id":3,"sLen":0,"time":7584.26966292135,"type":0},{"id":0,"sLen":0,"time":7752.80898876405,"type":0},{"id":0,"sLen":0,"time":8089.88764044944,"type":0},{"id":3,"sLen":0,"time":8258.42696629213,"type":0},{"id":3,"sLen":0,"time":8342.69662921348,"type":0},{"id":3,"sLen":0,"time":8511.23595505618,"type":0},{"id":0,"sLen":0,"time":8679.77528089888,"type":0},{"id":3,"sLen":0,"time":8848.31460674157,"type":0},{"id":3,"sLen":0,"time":8932.58426966292,"type":0},{"id":0,"sLen":0,"time":9101.12359550562,"type":0},{"id":0,"sLen":0,"time":9269.66292134831,"type":0},{"id":0,"sLen":0,"time":9353.93258426966,"type":0},{"id":3,"sLen":0,"time":9438.20224719101,"type":0},{"id":0,"sLen":0,"time":9606.74157303371,"type":0},{"id":0,"sLen":0,"time":9691.01123595505,"type":0},{"id":3,"sLen":0,"time":9943.8202247191,"type":0},{"id":0,"sLen":0,"time":10112.3595505618,"type":0},{"id":3,"sLen":0,"time":10196.6292134831,"type":0},{"id":3,"sLen":0,"time":10280.8988764045,"type":0},{"id":0,"sLen":0,"time":10449.4382022472,"type":0},{"id":0,"sLen":0,"time":21235.9550561798,"type":0},{"id":3,"sLen":0,"time":21320.2247191011,"type":0},{"id":0,"sLen":0,"time":21404.4943820225,"type":0},{"id":0,"sLen":0,"time":21573.0337078652,"type":0},{"id":3,"sLen":0,"time":21910.1123595506,"type":0},{"id":0,"sLen":0,"time":22247.191011236,"type":0},{"id":3,"sLen":0,"time":22584.2696629213,"type":0},{"id":0,"sLen":0,"time":22921.3483146067,"type":0},{"id":0,"sLen":0,"time":23258.4269662921,"type":0},{"id":3,"sLen":0,"time":23595.5056179775,"type":0},{"id":3,"sLen":0,"time":23932.5842696629,"type":0},{"id":0,"sLen":0,"time":24269.6629213483,"type":0},{"id":3,"sLen":0,"time":24606.7415730337,"type":0},{"id":0,"sLen":0,"time":24943.8202247191,"type":0},{"id":3,"sLen":0,"time":25280.8988764045,"type":0},{"id":0,"sLen":0,"time":25617.9775280899,"type":0},{"id":0,"sLen":0,"time":25955.0561797753,"type":0},{"id":3,"sLen":0,"time":26292.1348314607,"type":0},{"id":3,"sLen":0,"time":26629.2134831461,"type":0},{"id":3,"sLen":0,"time":26713.4831460674,"type":0},{"id":3,"sLen":0,"time":26797.7528089888,"type":0},{"id":3,"sLen":0,"time":26966.2921348315,"type":0},{"id":0,"sLen":0,"time":27303.3707865169,"type":0},{"id":0,"sLen":0,"time":27640.4494382022,"type":0},{"id":3,"sLen":0,"time":27977.5280898876,"type":0},{"id":3,"sLen":0,"time":28314.606741573,"type":0},{"id":0,"sLen":0,"time":28651.6853932584,"type":0},{"id":3,"sLen":0,"time":28988.7640449438,"type":0},{"id":0,"sLen":0,"time":29325.8426966292,"type":0},{"id":0,"sLen":0,"time":29662.9213483146,"type":0},{"id":0,"sLen":0,"time":29831.4606741573,"type":0},{"id":0,"sLen":0,"time":30000,"type":0},{"id":3,"sLen":0,"time":30168.5393258427,"type":0},{"id":3,"sLen":0,"time":30337.0786516854,"type":0},{"id":3,"sLen":0,"time":30505.6179775281,"type":0},{"id":3,"sLen":0,"time":30674.1573033708,"type":0},{"id":0,"sLen":0,"time":30842.6966292135,"type":0},{"id":0,"sLen":0,"time":31011.2359550562,"type":0},{"id":3,"sLen":0,"time":31348.3146067416,"type":0},{"id":3,"sLen":0,"time":31685.393258427,"type":0},{"id":0,"sLen":0,"time":31853.9325842697,"type":0},{"id":0,"sLen":0,"time":32359.5505617978,"type":0},{"id":3,"sLen":0,"time":32696.6292134831,"type":0},{"id":0,"sLen":0,"time":33033.7078651685,"type":0},{"id":0,"sLen":0,"time":33202.2471910112,"type":0},{"id":3,"sLen":0,"time":33370.7865168539,"type":0},{"id":0,"sLen":0,"time":33707.8651685393,"type":0},{"id":3,"sLen":0,"time":34044.9438202247,"type":0},{"id":3,"sLen":0,"time":34382.0224719101,"type":0},{"id":0,"sLen":0,"time":34550.5617977528,"type":0},{"id":3,"sLen":0,"time":34719.1011235955,"type":0},{"id":0,"sLen":0,"time":35056.1797752809,"type":0},{"id":3,"sLen":0,"time":35393.2584269663,"type":0},{"id":0,"sLen":0,"time":35730.3370786517,"type":0},{"id":0,"sLen":0,"time":35898.8764044944,"type":0},{"id":3,"sLen":0,"time":36067.4157303371,"type":0},{"id":0,"sLen":0,"time":36404.4943820225,"type":0},{"id":3,"sLen":0,"time":36741.5730337079,"type":0},{"id":3,"sLen":0,"time":37078.6516853933,"type":0},{"id":0,"sLen":0,"time":37247.191011236,"type":0},{"id":3,"sLen":0,"time":37415.7303370787,"type":0},{"id":0,"sLen":0,"time":37752.808988764,"type":0},{"id":3,"sLen":0,"time":38089.8876404494,"type":0},{"id":3,"sLen":0,"time":38426.9662921348,"type":0},{"id":0,"sLen":0,"time":38595.5056179775,"type":0},{"id":3,"sLen":0,"time":38764.0449438202,"type":0},{"id":0,"sLen":0,"time":39101.1235955056,"type":0},{"id":3,"sLen":0,"time":39438.202247191,"type":0},{"id":3,"sLen":0,"time":39775.2808988764,"type":0},{"id":0,"sLen":0,"time":39943.8202247191,"type":0},{"id":3,"sLen":0,"time":40112.3595505618,"type":0},{"id":0,"sLen":0,"time":40449.4382022472,"type":0},{"id":3,"sLen":0,"time":40786.5168539326,"type":0},{"id":3,"sLen":0,"time":41123.595505618,"type":0},{"id":0,"sLen":0,"time":41292.1348314607,"type":0},{"id":3,"sLen":0,"time":41460.6741573034,"type":0},{"id":0,"sLen":0,"time":41797.7528089888,"type":0},{"id":0,"sLen":0,"time":42134.8314606742,"type":0},{"id":0,"sLen":0,"time":42471.9101123596,"type":0},{"id":0,"sLen":0,"time":42640.4494382022,"type":0},{"id":3,"sLen":0,"time":42808.9887640449,"type":0},{"id":3,"sLen":0,"time":42893.2584269663,"type":0},{"id":3,"sLen":0,"time":42977.5280898876,"type":0},{"id":0,"sLen":0,"time":43146.0674157303,"type":0},{"id":3,"sLen":0,"time":43483.1460674157,"type":0},{"id":3,"sLen":0,"time":43820.2247191011,"type":0},{"id":0,"sLen":0,"time":43988.7640449438,"type":0},{"id":3,"sLen":0,"time":44157.3033707865,"type":0},{"id":0,"sLen":0,"time":44494.3820224719,"type":0},{"id":3,"sLen":0,"time":44831.4606741573,"type":0},{"id":0,"sLen":0,"time":45168.5393258427,"type":0},{"id":0,"sLen":0,"time":45337.0786516854,"type":0},{"id":3,"sLen":0,"time":45505.6179775281,"type":0},{"id":0,"sLen":0,"time":45842.6966292135,"type":0},{"id":3,"sLen":0,"time":46179.7752808989,"type":0},{"id":3,"sLen":0,"time":46516.8539325843,"type":0},{"id":0,"sLen":0,"time":46685.393258427,"type":0},{"id":3,"sLen":0,"time":46853.9325842697,"type":0},{"id":0,"sLen":0,"time":47191.0112359551,"type":0},{"id":3,"sLen":0,"time":47528.0898876404,"type":0},{"id":3,"sLen":0,"time":47865.1685393258,"type":0},{"id":0,"sLen":0,"time":48033.7078651685,"type":0},{"id":3,"sLen":0,"time":48202.2471910112,"type":0},{"id":0,"sLen":0,"time":48539.3258426966,"type":0},{"id":3,"sLen":0,"time":48876.404494382,"type":0},{"id":3,"sLen":0,"time":49213.4831460674,"type":0},{"id":0,"sLen":0,"time":49382.0224719101,"type":0},{"id":3,"sLen":0,"time":49550.5617977528,"type":0},{"id":0,"sLen":0,"time":49887.6404494382,"type":0},{"id":3,"sLen":0,"time":50224.7191011236,"type":0},{"id":0,"sLen":0,"time":50561.797752809,"type":0},{"id":0,"sLen":0,"time":50730.3370786517,"type":0},{"id":3,"sLen":0,"time":50898.8764044944,"type":0},{"id":0,"sLen":0,"time":51235.9550561798,"type":0},{"id":3,"sLen":0,"time":51573.0337078652,"type":0},{"id":3,"sLen":0,"time":51910.1123595506,"type":0},{"id":0,"sLen":0,"time":52078.6516853933,"type":0},{"id":3,"sLen":0,"time":52247.191011236,"type":0},{"id":0,"sLen":0,"time":52584.2696629213,"type":0},{"id":0,"sLen":0,"time":52921.3483146067,"type":0},{"id":0,"sLen":0,"time":53258.4269662921,"type":0},{"id":0,"sLen":0,"time":53426.9662921348,"type":0},{"id":3,"sLen":0,"time":53595.5056179775,"type":0},{"id":3,"sLen":0,"time":53679.7752808989,"type":0},{"id":3,"sLen":0,"time":53764.0449438202,"type":0},{"id":0,"sLen":0,"time":53932.5842696629,"type":0},{"id":3,"sLen":0,"time":54101.1235955056,"type":0},{"id":3,"sLen":0,"time":54185.393258427,"type":0},{"id":3,"sLen":0,"time":54353.9325842697,"type":0},{"id":0,"sLen":0,"time":54522.4719101124,"type":0},{"id":3,"sLen":0,"time":54691.0112359551,"type":0},{"id":3,"sLen":0,"time":54775.2808988764,"type":0},{"id":0,"sLen":0,"time":54943.8202247191,"type":0},{"id":0,"sLen":0,"time":55112.3595505618,"type":0},{"id":0,"sLen":0,"time":55196.6292134831,"type":0},{"id":3,"sLen":0,"time":55280.8988764045,"type":0},{"id":0,"sLen":0,"time":55449.4382022472,"type":0},{"id":0,"sLen":0,"time":55533.7078651685,"type":0},{"id":3,"sLen":0,"time":55786.5168539326,"type":0},{"id":0,"sLen":0,"time":55955.0561797753,"type":0},{"id":3,"sLen":0,"time":56039.3258426966,"type":0},{"id":3,"sLen":0,"time":56123.595505618,"type":0},{"id":0,"sLen":0,"time":56292.1348314607,"type":0},{"id":0,"sLen":0,"time":56629.2134831461,"type":0},{"id":3,"sLen":0,"time":56797.7528089888,"type":0},{"id":3,"sLen":0,"time":56882.0224719101,"type":0},{"id":3,"sLen":0,"time":57050.5617977528,"type":0},{"id":0,"sLen":0,"time":57219.1011235955,"type":0},{"id":3,"sLen":0,"time":57387.6404494382,"type":0},{"id":3,"sLen":0,"time":57471.9101123595,"type":0},{"id":0,"sLen":0,"time":57640.4494382022,"type":0},{"id":0,"sLen":0,"time":57808.9887640449,"type":0},{"id":0,"sLen":0,"time":57893.2584269663,"type":0},{"id":3,"sLen":0,"time":57977.5280898876,"type":0},{"id":0,"sLen":0,"time":58146.0674157303,"type":0},{"id":0,"sLen":0,"time":58230.3370786517,"type":0},{"id":3,"sLen":0,"time":58483.1460674157,"type":0},{"id":0,"sLen":0,"time":58651.6853932584,"type":0},{"id":3,"sLen":0,"time":58735.9550561798,"type":0},{"id":3,"sLen":0,"time":58820.2247191011,"type":0},{"id":0,"sLen":0,"time":58988.7640449438,"type":0},{"id":0,"sLen":0,"time":59073.0337078652,"type":0},{"id":0,"sLen":0,"time":59157.3033707865,"type":0},{"id":0,"sLen":0,"time":59325.8426966292,"type":0},{"id":3,"sLen":0,"time":59494.3820224719,"type":0},{"id":3,"sLen":0,"time":59578.6516853933,"type":0},{"id":3,"sLen":0,"time":59747.191011236,"type":0},{"id":0,"sLen":0,"time":59915.7303370786,"type":0},{"id":3,"sLen":0,"time":60084.2696629213,"type":0},{"id":3,"sLen":0,"time":60168.5393258427,"type":0},{"id":0,"sLen":0,"time":60337.0786516854,"type":0},{"id":0,"sLen":0,"time":60505.6179775281,"type":0},{"id":0,"sLen":0,"time":60589.8876404494,"type":0},{"id":3,"sLen":0,"time":60674.1573033708,"type":0},{"id":0,"sLen":0,"time":60842.6966292135,"type":0},{"id":0,"sLen":0,"time":60926.9662921348,"type":0},{"id":3,"sLen":0,"time":61179.7752808989,"type":0},{"id":0,"sLen":0,"time":61348.3146067416,"type":0},{"id":3,"sLen":0,"time":61432.5842696629,"type":0},{"id":3,"sLen":0,"time":61516.8539325843,"type":0},{"id":0,"sLen":0,"time":61685.393258427,"type":0},{"id":0,"sLen":0,"time":62022.4719101124,"type":0},{"id":3,"sLen":0,"time":62191.0112359551,"type":0},{"id":3,"sLen":0,"time":62275.2808988764,"type":0},{"id":3,"sLen":0,"time":62443.8202247191,"type":0},{"id":0,"sLen":0,"time":62612.3595505618,"type":0},{"id":3,"sLen":0,"time":62780.8988764045,"type":0},{"id":3,"sLen":0,"time":62865.1685393258,"type":0},{"id":0,"sLen":0,"time":63033.7078651685,"type":0},{"id":0,"sLen":0,"time":63202.2471910112,"type":0},{"id":0,"sLen":0,"time":63286.5168539326,"type":0},{"id":3,"sLen":0,"time":63370.7865168539,"type":0},{"id":0,"sLen":0,"time":63539.3258426966,"type":0},{"id":0,"sLen":0,"time":63623.595505618,"type":0},{"id":3,"sLen":0,"time":63876.404494382,"type":0},{"id":0,"sLen":0,"time":64044.9438202247,"type":0},{"id":3,"sLen":0,"time":64129.2134831461,"type":0},{"id":3,"sLen":0,"time":64213.4831460674,"type":0},{"id":0,"sLen":0,"time":64382.0224719101,"type":0},{"id":3,"sLen":0,"time":64719.1011235955,"type":0},{"id":0,"sLen":0,"time":65056.1797752809,"type":0},{"id":0,"sLen":0,"time":65393.2584269663,"type":0},{"id":0,"sLen":0,"time":65730.3370786517,"type":0},{"id":0,"sLen":0,"time":66067.4157303371,"type":0},{"id":3,"sLen":0,"time":66404.4943820225,"type":0},{"id":3,"sLen":0,"time":66741.5730337079,"type":0},{"id":3,"sLen":0,"time":67078.6516853933,"type":0},{"id":3,"sLen":0,"time":67415.7303370787,"type":0},{"id":0,"sLen":0,"time":67752.808988764,"type":0},{"id":0,"sLen":0,"time":68089.8876404494,"type":0},{"id":0,"sLen":0,"time":68426.9662921348,"type":0},{"id":0,"sLen":0,"time":68764.0449438202,"type":0},{"id":3,"sLen":0,"time":69101.1235955056,"type":0},{"id":3,"sLen":0,"time":69438.202247191,"type":0},{"id":3,"sLen":0,"time":69775.2808988764,"type":0},{"id":3,"sLen":0,"time":69859.5505617978,"type":0},{"id":3,"sLen":0,"time":69943.8202247191,"type":0},{"id":3,"sLen":0,"time":70112.3595505618,"type":0},{"id":0,"sLen":0,"time":70449.4382022472,"type":0},{"id":0,"sLen":0,"time":70786.5168539326,"type":0},{"id":0,"sLen":0,"time":71123.595505618,"type":0},{"id":0,"sLen":0,"time":71460.6741573034,"type":0},{"id":3,"sLen":0,"time":71797.7528089888,"type":0},{"id":3,"sLen":0,"time":72134.8314606741,"type":0},{"id":3,"sLen":0,"time":72471.9101123595,"type":0},{"id":3,"sLen":0,"time":72556.1797752809,"type":0},{"id":3,"sLen":0,"time":72640.4494382023,"type":0},{"id":3,"sLen":0,"time":72808.9887640449,"type":0},{"id":3,"sLen":0,"time":72893.2584269663,"type":0},{"id":3,"sLen":0,"time":72977.5280898877,"type":0},{"id":0,"sLen":0,"time":73146.0674157303,"type":0},{"id":0,"sLen":0,"time":73230.3370786517,"type":0},{"id":0,"sLen":0,"time":73314.6067415731,"type":0},{"id":0,"sLen":0,"time":73483.1460674157,"type":0},{"id":0,"sLen":0,"time":73567.4157303371,"type":0},{"id":0,"sLen":0,"time":73651.6853932585,"type":0},{"id":0,"sLen":0,"time":73820.2247191011,"type":0},{"id":0,"sLen":0,"time":73904.4943820225,"type":0},{"id":0,"sLen":0,"time":73988.7640449438,"type":0},{"id":0,"sLen":0,"time":74157.3033707866,"type":0},{"id":0,"sLen":0,"time":74241.5730337079,"type":0},{"id":0,"sLen":0,"time":74325.8426966292,"type":0},{"id":0,"sLen":0,"time":74410.1123595506,"type":0},{"id":3,"sLen":0,"time":74494.382022472,"type":0},{"id":3,"sLen":0,"time":74578.6516853933,"type":0},{"id":3,"sLen":0,"time":74662.9213483146,"type":0},{"id":3,"sLen":0,"time":74747.191011236,"type":0},{"id":0,"sLen":0,"time":74831.4606741573,"type":0},{"id":0,"sLen":0,"time":74915.7303370787,"type":0},{"id":0,"sLen":0,"time":75000,"type":0},{"id":0,"sLen":0,"time":75084.2696629214,"type":0},{"id":3,"sLen":0,"time":75168.5393258427,"type":0},{"id":0,"sLen":0,"time":75505.6179775281,"type":0},{"id":3,"sLen":0,"time":75842.6966292135,"type":0},{"id":0,"sLen":0,"time":76179.7752808989,"type":0},{"id":0,"sLen":0,"time":76516.8539325843,"type":0},{"id":3,"sLen":0,"time":76853.9325842697,"type":0},{"id":3,"sLen":0,"time":77191.0112359551,"type":0},{"id":0,"sLen":0,"time":77528.0898876405,"type":0},{"id":3,"sLen":0,"time":77865.1685393259,"type":0},{"id":3,"sLen":0,"time":78033.7078651686,"type":0},{"id":0,"sLen":0,"time":78202.2471910113,"type":0},{"id":0,"sLen":0,"time":78539.3258426967,"type":0},{"id":3,"sLen":0,"time":78876.4044943821,"type":0},{"id":0,"sLen":0,"time":79213.4831460675,"type":0},{"id":0,"sLen":0,"time":79550.5617977529,"type":0},{"id":3,"sLen":0,"time":79887.6404494383,"type":0},{"id":0,"sLen":0,"time":80140.4494382023,"type":0},{"id":0,"sLen":0,"time":80224.7191011237,"type":0},{"id":0,"sLen":0,"time":80308.988764045,"type":0},{"id":0,"sLen":0,"time":80477.5280898877,"type":0},{"id":3,"sLen":84.269662921346,"time":80730.3370786517,"type":0},{"id":0,"sLen":0,"time":80898.8764044945,"type":0},{"id":3,"sLen":0,"time":81067.4157303371,"type":0},{"id":0,"sLen":0,"time":81235.9550561799,"type":0},{"id":3,"sLen":0,"time":81404.4943820225,"type":0},{"id":0,"sLen":0,"time":81573.0337078652,"type":0},{"id":3,"sLen":0,"time":81741.5730337079,"type":0},{"id":0,"sLen":0,"time":81910.1123595506,"type":0},{"id":3,"sLen":0,"time":82078.6516853933,"type":0},{"id":3,"sLen":0,"time":82247.191011236,"type":0},{"id":0,"sLen":0,"time":82415.7303370787,"type":0},{"id":3,"sLen":0,"time":82584.2696629214,"type":0},{"id":0,"sLen":0,"time":82752.8089887641,"type":0},{"id":3,"sLen":0,"time":82921.3483146068,"type":0},{"id":0,"sLen":0,"time":83089.8876404495,"type":0},{"id":3,"sLen":0,"time":83258.4269662922,"type":0},{"id":3,"sLen":0,"time":83426.9662921349,"type":0},{"id":0,"sLen":0,"time":83595.5056179776,"type":0},{"id":0,"sLen":0,"time":83764.0449438203,"type":0},{"id":0,"sLen":0,"time":83932.584269663,"type":0},{"id":3,"sLen":0,"time":84269.6629213484,"type":0},{"id":3,"sLen":0,"time":84606.7415730338,"type":0},{"id":3,"sLen":0,"time":84775.2808988765,"type":0},{"id":3,"sLen":0,"time":84943.8202247192,"type":0},{"id":3,"sLen":0,"time":85280.8988764046,"type":0},{"id":0,"sLen":0,"time":85617.97752809,"type":0},{"id":0,"sLen":0,"time":85702.2471910113,"type":0},{"id":3,"sLen":0,"time":85870.786516854,"type":0},{"id":0,"sLen":0,"time":86039.3258426967,"type":0},{"id":0,"sLen":0,"time":86292.1348314607,"type":0},{"id":3,"sLen":0,"time":86629.2134831461,"type":0},{"id":0,"sLen":0,"time":87134.8314606741,"type":0},{"id":3,"sLen":0,"time":87303.3707865169,"type":0},{"id":0,"sLen":0,"time":87640.4494382023,"type":0},{"id":3,"sLen":0,"time":87977.5280898877,"type":0},{"id":0,"sLen":0,"time":88483.1460674157,"type":0},{"id":3,"sLen":0,"time":88651.6853932584,"type":0},{"id":0,"sLen":0,"time":88988.7640449438,"type":0},{"id":3,"sLen":0,"time":89325.8426966292,"type":0},{"id":0,"sLen":0,"time":89831.4606741573,"type":0},{"id":3,"sLen":0,"time":90000,"type":0},{"id":0,"sLen":0,"time":90337.0786516854,"type":0},{"id":3,"sLen":0,"time":90674.1573033708,"type":0},{"id":0,"sLen":0,"time":91179.7752808989,"type":0},{"id":3,"sLen":0,"time":91348.3146067416,"type":0},{"id":0,"sLen":0,"time":91685.393258427,"type":0},{"id":3,"sLen":0,"time":92022.4719101124,"type":0},{"id":0,"sLen":0,"time":92528.0898876405,"type":0},{"id":3,"sLen":0,"time":92696.6292134832,"type":0},{"id":0,"sLen":0,"time":93033.7078651686,"type":0},{"id":3,"sLen":0,"time":93370.786516854,"type":0},{"id":0,"sLen":0,"time":93876.404494382,"type":0},{"id":3,"sLen":0,"time":94044.9438202248,"type":0},{"id":0,"sLen":0,"time":94382.0224719102,"type":0},{"id":3,"sLen":0,"time":94719.1011235956,"type":0},{"id":0,"sLen":0,"time":95224.7191011236,"type":0},{"id":3,"sLen":0,"time":95393.2584269663,"type":0},{"id":0,"sLen":0,"time":95730.3370786517,"type":0},{"id":0,"sLen":0,"time":96067.4157303371,"type":0},{"id":0,"sLen":0,"time":96404.4943820225,"type":0},{"id":0,"sLen":0,"time":96573.0337078652,"type":0},{"id":3,"sLen":0,"time":96741.5730337079,"type":0},{"id":3,"sLen":0,"time":96825.8426966292,"type":0},{"id":3,"sLen":0,"time":96910.1123595505,"type":0},{"id":0,"sLen":0,"time":97078.6516853933,"type":0},{"id":3,"sLen":0,"time":97415.7303370787,"type":0},{"id":3,"sLen":0,"time":97752.808988764,"type":0},{"id":0,"sLen":0,"time":97921.3483146067,"type":0},{"id":3,"sLen":0,"time":98089.8876404494,"type":0},{"id":0,"sLen":0,"time":98426.9662921348,"type":0},{"id":3,"sLen":0,"time":98764.0449438202,"type":0},{"id":0,"sLen":0,"time":99101.1235955056,"type":0},{"id":0,"sLen":0,"time":99269.6629213483,"type":0},{"id":3,"sLen":0,"time":99438.202247191,"type":0},{"id":0,"sLen":0,"time":99775.2808988764,"type":0},{"id":3,"sLen":0,"time":100112.359550562,"type":0},{"id":3,"sLen":0,"time":100449.438202247,"type":0},{"id":0,"sLen":0,"time":100617.97752809,"type":0},{"id":3,"sLen":0,"time":100786.516853933,"type":0},{"id":0,"sLen":0,"time":101123.595505618,"type":0},{"id":3,"sLen":0,"time":101460.674157303,"type":0},{"id":3,"sLen":0,"time":101797.752808989,"type":0},{"id":0,"sLen":0,"time":101966.292134831,"type":0},{"id":3,"sLen":0,"time":102134.831460674,"type":0},{"id":0,"sLen":0,"time":102471.91011236,"type":0},{"id":3,"sLen":0,"time":102808.988764045,"type":0},{"id":3,"sLen":0,"time":103146.06741573,"type":0},{"id":0,"sLen":0,"time":103314.606741573,"type":0},{"id":3,"sLen":0,"time":103483.146067416,"type":0},{"id":0,"sLen":0,"time":103820.224719101,"type":0},{"id":3,"sLen":0,"time":104157.303370787,"type":0},{"id":0,"sLen":0,"time":104494.382022472,"type":0},{"id":0,"sLen":0,"time":104662.921348315,"type":0},{"id":3,"sLen":0,"time":104831.460674157,"type":0},{"id":0,"sLen":0,"time":105168.539325843,"type":0},{"id":3,"sLen":0,"time":105505.617977528,"type":0},{"id":3,"sLen":0,"time":105842.696629213,"type":0},{"id":0,"sLen":0,"time":106011.235955056,"type":0},{"id":3,"sLen":0,"time":106179.775280899,"type":0},{"id":0,"sLen":0,"time":106516.853932584,"type":0},{"id":0,"sLen":0,"time":106853.93258427,"type":0},{"id":0,"sLen":0,"time":107191.011235955,"type":0},{"id":3,"sLen":0,"time":107528.08988764,"type":0},{"id":0,"sLen":0,"time":107865.168539326,"type":0},{"id":0,"sLen":0,"time":108202.247191011,"type":0},{"id":0,"sLen":0,"time":108539.325842697,"type":0},{"id":0,"sLen":0,"time":108876.404494382,"type":0},{"id":3,"sLen":0,"time":109213.483146068,"type":0},{"id":3,"sLen":0,"time":109550.561797753,"type":0},{"id":3,"sLen":0,"time":109887.640449438,"type":0},{"id":3,"sLen":0,"time":110224.719101124,"type":0},{"id":0,"sLen":0,"time":110561.797752809,"type":0},{"id":0,"sLen":0,"time":110730.337078652,"type":0},{"id":0,"sLen":0,"time":110898.876404495,"type":0},{"id":3,"sLen":0,"time":111235.95505618,"type":0},{"id":3,"sLen":0,"time":111573.033707865,"type":0},{"id":3,"sLen":0,"time":111910.112359551,"type":0},{"id":3,"sLen":0,"time":112247.191011236,"type":0},{"id":0,"sLen":0,"time":112584.269662922,"type":0},{"id":3,"sLen":0,"time":112921.348314607,"type":0},{"id":0,"sLen":0,"time":113258.426966292,"type":0},{"id":3,"sLen":0,"time":113932.584269663,"type":0},{"id":0,"sLen":0,"time":114269.662921349,"type":0},{"id":3,"sLen":0,"time":114438.202247191,"type":0},{"id":0,"sLen":0,"time":114606.741573034,"type":0},{"id":3,"sLen":0,"time":114943.820224719,"type":0},{"id":0,"sLen":0,"time":115280.898876405,"type":0},{"id":3,"sLen":0,"time":115617.97752809,"type":0},{"id":3,"sLen":0,"time":115786.516853933,"type":0},{"id":0,"sLen":0,"time":115955.056179776,"type":0},{"id":3,"sLen":0,"time":116123.595505618,"type":0},{"id":0,"sLen":0,"time":116292.134831461,"type":0},{"id":0,"sLen":0,"time":116629.213483146,"type":0},{"id":3,"sLen":0,"time":116966.292134832,"type":0},{"id":3,"sLen":0,"time":117303.370786517,"type":0},{"id":0,"sLen":0,"time":117640.449438203,"type":0},{"id":3,"sLen":0,"time":117977.528089888,"type":0},{"id":3,"sLen":0,"time":118061.797752809,"type":0},{"id":0,"sLen":0,"time":118314.606741573,"type":0},{"id":3,"sLen":0,"time":118398.876404495,"type":0},{"id":0,"sLen":0,"time":118651.685393258,"type":0},{"id":3,"sLen":0,"time":118820.224719101,"type":0},{"id":3,"sLen":0,"time":118904.494382022,"type":0},{"id":3,"sLen":0,"time":119073.033707865,"type":0},{"id":0,"sLen":0,"time":119241.573033708,"type":0},{"id":3,"sLen":0,"time":119410.112359551,"type":0},{"id":3,"sLen":0,"time":119494.382022472,"type":0},{"id":0,"sLen":0,"time":119662.921348315,"type":0},{"id":0,"sLen":0,"time":119831.460674157,"type":0},{"id":0,"sLen":0,"time":119915.730337079,"type":0},{"id":3,"sLen":0,"time":120000,"type":0},{"id":0,"sLen":0,"time":120168.539325843,"type":0},{"id":0,"sLen":0,"time":120252.808988764,"type":0},{"id":3,"sLen":0,"time":120505.617977528,"type":0},{"id":0,"sLen":0,"time":120674.157303371,"type":0},{"id":3,"sLen":0,"time":120758.426966292,"type":0},{"id":3,"sLen":0,"time":120842.696629213,"type":0},{"id":0,"sLen":0,"time":121011.235955056,"type":0},{"id":0,"sLen":0,"time":121095.505617978,"type":0},{"id":0,"sLen":0,"time":121179.775280899,"type":0}],"position":"girlfriend","type":2,"characters":["pico-speakers"],"visible":false}],"codenameChart":true,"stage":"tank","noteTypes":["No Anim Note"]} \ No newline at end of file diff --git a/assets/songs/stress/charts/hard.json b/assets/songs/stress/charts/hard.json index ad29ed8c..12b34d42 100644 --- a/assets/songs/stress/charts/hard.json +++ b/assets/songs/stress/charts/hard.json @@ -1 +1 @@ -{"events":[{"params":[1],"time":5393.25842696629,"name":"Camera Movement"},{"params":[0],"time":10786.5168539326,"name":"Camera Movement"},{"params":[1],"time":14831.4606741573,"name":"Camera Movement"},{"params":[0],"time":21573.0337078652,"name":"Camera Movement"},{"params":[1],"time":24269.6629213483,"name":"Camera Movement"},{"params":[0],"time":26966.2921348315,"name":"Camera Movement"},{"params":[1],"time":28314.606741573,"name":"Camera Movement"},{"params":[0],"time":31011.2359550562,"name":"Camera Movement"},{"params":[1],"time":37752.808988764,"name":"Camera Movement"},{"params":[0],"time":43146.0674157303,"name":"Camera Movement"},{"params":[1],"time":48539.3258426966,"name":"Camera Movement"},{"params":[0],"time":53932.5842696629,"name":"Camera Movement"},{"params":[1],"time":59325.8426966292,"name":"Camera Movement"},{"params":[0],"time":62022.4719101124,"name":"Camera Movement"},{"params":[0,"good!"],"time":62022.4719101124,"name":"Play Animation"},{"params":[1],"time":67415.7303370787,"name":"Camera Movement"},{"params":[0],"time":75505.6179775281,"name":"Camera Movement"},{"params":[1],"time":80898.8764044945,"name":"Camera Movement"},{"params":[0],"time":86292.1348314608,"name":"Camera Movement"},{"params":[1],"time":91685.3932584271,"name":"Camera Movement"},{"params":[0],"time":97078.6516853934,"name":"Camera Movement"},{"params":[1],"time":102471.91011236,"name":"Camera Movement"},{"params":[0],"time":107865.168539326,"name":"Camera Movement"},{"params":[1],"time":113258.426966292,"name":"Camera Movement"}],"scrollSpeed":2.6,"strumLines":[{"notes":[{"id":1,"sLen":0,"time":0,"type":0},{"id":2,"sLen":0,"time":168.539322,"type":0},{"id":0,"sLen":0,"time":337.078644,"type":0},{"id":0,"sLen":0,"time":505.617981,"type":0},{"id":2,"sLen":0,"time":674.1573,"type":0},{"id":3,"sLen":0,"time":842.696655,"type":0},{"id":1,"sLen":0,"time":1011.23596,"type":0},{"id":3,"sLen":0,"time":1179.77527,"type":0},{"id":0,"sLen":0,"time":1348.31458,"type":0},{"id":3,"sLen":0,"time":1516.85388,"type":0},{"id":2,"sLen":0,"time":1685.39331,"type":0},{"id":3,"sLen":0,"time":1853.93262,"type":0},{"id":1,"sLen":0,"time":2022.47192,"type":0},{"id":3,"sLen":0,"time":2191.01123,"type":0},{"id":0,"sLen":0,"time":2359.55054,"type":0},{"id":3,"sLen":0,"time":2528.08984,"type":0},{"id":1,"sLen":0,"time":2696.62915,"type":0},{"id":2,"sLen":0,"time":2865.16846,"type":0},{"id":0,"sLen":0,"time":3033.70776,"type":0},{"id":0,"sLen":0,"time":3202.247,"type":0},{"id":2,"sLen":0,"time":3370.78662,"type":0},{"id":3,"sLen":0,"time":3539.326,"type":0},{"id":1,"sLen":0,"time":3707.86523,"type":0},{"id":3,"sLen":0,"time":3876.40454,"type":0},{"id":0,"sLen":0,"time":4044.94385,"type":0},{"id":3,"sLen":0,"time":4213.483,"type":0},{"id":2,"sLen":0,"time":4382.02246,"type":0},{"id":3,"sLen":0,"time":4550.562,"type":0},{"id":1,"sLen":0,"time":4719.101,"type":0},{"id":3,"sLen":0,"time":4887.64063,"type":0},{"id":0,"sLen":0,"time":5056.17969,"type":0},{"id":3,"sLen":0,"time":5224.719,"type":0},{"id":0,"sLen":0,"time":11123.5957,"type":0},{"id":1,"sLen":0,"time":11292.1348,"type":0},{"id":3,"sLen":252.808989999996,"time":11460.6738,"type":0},{"id":1,"sLen":0,"time":11797.7529,"type":0},{"id":0,"sLen":0,"time":12134.8311,"type":0},{"id":0,"sLen":0,"time":12471.91,"type":0},{"id":0,"sLen":0,"time":12808.9883,"type":0},{"id":0,"sLen":0,"time":12977.5283,"type":0},{"id":2,"sLen":0,"time":13146.0674,"type":0},{"id":3,"sLen":0,"time":13230.3369,"type":0},{"id":2,"sLen":0,"time":13314.6064,"type":0},{"id":1,"sLen":0,"time":13483.1465,"type":0},{"id":3,"sLen":0,"time":13651.6855,"type":0},{"id":2,"sLen":0,"time":13820.2246,"type":0},{"id":1,"sLen":0,"time":13988.7637,"type":0},{"id":3,"sLen":0,"time":14157.3037,"type":0},{"id":2,"sLen":0,"time":14325.8428,"type":0},{"id":1,"sLen":0,"time":14494.3818,"type":0},{"id":3,"sLen":0,"time":14578.6514,"type":0},{"id":2,"sLen":0,"time":14662.9209,"type":0},{"id":0,"sLen":0,"time":14831.4609,"type":0},{"id":3,"sLen":0,"time":15000,"type":0},{"id":2,"sLen":0,"time":15168.5391,"type":0},{"id":3,"sLen":0,"time":15337.0791,"type":0},{"id":1,"sLen":0,"time":15505.6182,"type":0},{"id":3,"sLen":0,"time":15674.1572,"type":0},{"id":0,"sLen":0,"time":15842.6963,"type":0},{"id":3,"sLen":0,"time":16011.2363,"type":0},{"id":3,"sLen":0,"time":21573.0332,"type":0},{"id":2,"sLen":0,"time":21741.5723,"type":0},{"id":0,"sLen":0,"time":21910.1133,"type":0},{"id":2,"sLen":0,"time":22078.6523,"type":0},{"id":3,"sLen":0,"time":22247.1914,"type":0},{"id":2,"sLen":0,"time":22415.73,"type":0},{"id":0,"sLen":0,"time":22584.27,"type":0},{"id":2,"sLen":0,"time":22752.8086,"type":0},{"id":3,"sLen":0,"time":22921.3477,"type":0},{"id":2,"sLen":0,"time":23005.6172,"type":0},{"id":3,"sLen":0,"time":23089.8867,"type":0},{"id":3,"sLen":0,"time":23258.4277,"type":0},{"id":2,"sLen":0,"time":23426.9668,"type":0},{"id":3,"sLen":0,"time":23595.5059,"type":0},{"id":0,"sLen":0,"time":23764.0449,"type":0},{"id":1,"sLen":0,"time":23932.584,"type":0},{"id":0,"sLen":0,"time":24101.123,"type":0},{"id":2,"sLen":0,"time":24269.6621,"type":0},{"id":3,"sLen":0,"time":26966.293,"type":0},{"id":2,"sLen":0,"time":27134.832,"type":0},{"id":3,"sLen":0,"time":27303.3711,"type":0},{"id":0,"sLen":0,"time":27471.91,"type":0},{"id":3,"sLen":0,"time":27640.45,"type":0},{"id":2,"sLen":0,"time":27808.9883,"type":0},{"id":3,"sLen":0,"time":27977.5273,"type":0},{"id":0,"sLen":0,"time":28146.0684,"type":0},{"id":3,"sLen":0,"time":28314.6074,"type":0},{"id":2,"sLen":0,"time":28398.877,"type":0},{"id":3,"sLen":0,"time":28483.1465,"type":0},{"id":0,"sLen":0,"time":28651.6855,"type":0},{"id":3,"sLen":0,"time":28820.2246,"type":0},{"id":2,"sLen":0,"time":28988.7637,"type":0},{"id":0,"sLen":0,"time":29157.3027,"type":0},{"id":3,"sLen":0,"time":29325.8418,"type":0},{"id":1,"sLen":0,"time":29494.3828,"type":0},{"id":0,"sLen":0,"time":29662.9219,"type":0},{"id":0,"sLen":0,"time":30000,"type":0},{"id":0,"sLen":0,"time":30337.0781,"type":0},{"id":0,"sLen":0,"time":30674.1582,"type":0},{"id":0,"sLen":0,"time":31011.2363,"type":0},{"id":0,"sLen":0,"time":31348.3145,"type":0},{"id":0,"sLen":0,"time":31685.3926,"type":0},{"id":0,"sLen":0,"time":31853.9316,"type":0},{"id":0,"sLen":0,"time":32022.4727,"type":0},{"id":0,"sLen":0,"time":32191.0117,"type":0},{"id":2,"sLen":0,"time":32359.55,"type":0},{"id":3,"sLen":0,"time":32528.09,"type":0},{"id":1,"sLen":0,"time":32696.6289,"type":0},{"id":3,"sLen":0,"time":32865.168,"type":0},{"id":0,"sLen":0,"time":33033.707,"type":0},{"id":3,"sLen":0,"time":33202.2461,"type":0},{"id":1,"sLen":0,"time":33370.7852,"type":0},{"id":3,"sLen":0,"time":33539.3242,"type":0},{"id":3,"sLen":0,"time":33623.5938,"type":0},{"id":1,"sLen":0,"time":33707.8633,"type":0},{"id":3,"sLen":0,"time":33876.4063,"type":0},{"id":1,"sLen":0,"time":34044.9453,"type":0},{"id":2,"sLen":0,"time":34213.4844,"type":0},{"id":0,"sLen":0,"time":34382.0234,"type":0},{"id":2,"sLen":0,"time":34550.5625,"type":0},{"id":0,"sLen":0,"time":34719.1,"type":0},{"id":3,"sLen":0,"time":34887.64,"type":0},{"id":1,"sLen":0,"time":34971.91,"type":0},{"id":2,"sLen":0,"time":35056.18,"type":0},{"id":3,"sLen":0,"time":35224.72,"type":0},{"id":2,"sLen":0,"time":35393.2578,"type":0},{"id":3,"sLen":0,"time":35561.7969,"type":0},{"id":0,"sLen":0,"time":35730.3359,"type":0},{"id":1,"sLen":0,"time":35898.875,"type":0},{"id":0,"sLen":0,"time":36067.4141,"type":0},{"id":2,"sLen":0,"time":36151.6836,"type":0},{"id":0,"sLen":0,"time":36235.9531,"type":0},{"id":3,"sLen":0,"time":36404.4961,"type":0},{"id":1,"sLen":0,"time":36573.0352,"type":0},{"id":0,"sLen":0,"time":36741.5742,"type":0},{"id":1,"sLen":0,"time":36910.1133,"type":0},{"id":0,"sLen":0,"time":37078.6523,"type":0},{"id":1,"sLen":0,"time":37247.19,"type":0},{"id":1,"sLen":0,"time":37415.73,"type":0},{"id":3,"sLen":0,"time":37584.27,"type":0},{"id":3,"sLen":0,"time":37668.54,"type":0},{"id":3,"sLen":0,"time":43146.0664,"type":0},{"id":1,"sLen":0,"time":43230.3359,"type":0},{"id":3,"sLen":0,"time":43314.6055,"type":0},{"id":0,"sLen":0,"time":43483.1445,"type":0},{"id":3,"sLen":0,"time":43651.6836,"type":0},{"id":1,"sLen":0,"time":43735.9531,"type":0},{"id":2,"sLen":0,"time":43820.2266,"type":0},{"id":0,"sLen":0,"time":43988.7656,"type":0},{"id":1,"sLen":0,"time":44157.3047,"type":0},{"id":3,"sLen":0,"time":44325.8438,"type":0},{"id":3,"sLen":0,"time":44410.1133,"type":0},{"id":2,"sLen":0,"time":44494.3828,"type":0},{"id":3,"sLen":0,"time":44662.92,"type":0},{"id":0,"sLen":0,"time":44747.19,"type":0},{"id":1,"sLen":0,"time":44831.46,"type":0},{"id":0,"sLen":0,"time":45000,"type":0},{"id":2,"sLen":0,"time":45168.54,"type":0},{"id":3,"sLen":0,"time":45337.08,"type":0},{"id":1,"sLen":0,"time":45505.6172,"type":0},{"id":3,"sLen":0,"time":45674.1563,"type":0},{"id":2,"sLen":0,"time":45758.4258,"type":0},{"id":0,"sLen":0,"time":45842.6953,"type":0},{"id":3,"sLen":0,"time":46011.2344,"type":0},{"id":1,"sLen":0,"time":46179.7734,"type":0},{"id":3,"sLen":0,"time":46348.3164,"type":0},{"id":0,"sLen":0,"time":46516.8555,"type":0},{"id":1,"sLen":0,"time":46685.3945,"type":0},{"id":3,"sLen":0,"time":46853.9336,"type":0},{"id":2,"sLen":0,"time":46938.2031,"type":0},{"id":3,"sLen":0,"time":47022.4727,"type":0},{"id":1,"sLen":0,"time":47191.01,"type":0},{"id":3,"sLen":0,"time":47359.55,"type":0},{"id":0,"sLen":0,"time":47528.09,"type":0},{"id":3,"sLen":0,"time":47696.63,"type":0},{"id":1,"sLen":0,"time":47865.168,"type":0},{"id":3,"sLen":0,"time":48033.707,"type":0},{"id":2,"sLen":0,"time":48202.2461,"type":0},{"id":2,"sLen":0,"time":48370.7852,"type":0},{"id":1,"sLen":0,"time":53932.5859,"type":0},{"id":2,"sLen":0,"time":54101.125,"type":0},{"id":0,"sLen":0,"time":54269.6641,"type":0},{"id":0,"sLen":0,"time":54438.2031,"type":0},{"id":2,"sLen":0,"time":54606.7422,"type":0},{"id":3,"sLen":0,"time":54775.28,"type":0},{"id":1,"sLen":0,"time":54943.82,"type":0},{"id":3,"sLen":0,"time":55112.36,"type":0},{"id":0,"sLen":0,"time":55280.9,"type":0},{"id":1,"sLen":0,"time":55449.4375,"type":0},{"id":2,"sLen":0,"time":55617.9766,"type":0},{"id":3,"sLen":0,"time":55786.5156,"type":0},{"id":1,"sLen":0,"time":55955.0547,"type":0},{"id":3,"sLen":0,"time":56123.5938,"type":0},{"id":0,"sLen":0,"time":56292.1367,"type":0},{"id":3,"sLen":0,"time":56460.6758,"type":0},{"id":1,"sLen":0,"time":56629.2148,"type":0},{"id":2,"sLen":0,"time":56797.7539,"type":0},{"id":0,"sLen":0,"time":56966.293,"type":0},{"id":0,"sLen":0,"time":57134.832,"type":0},{"id":2,"sLen":0,"time":57303.37,"type":0},{"id":3,"sLen":0,"time":57471.91,"type":0},{"id":1,"sLen":0,"time":57640.45,"type":0},{"id":3,"sLen":0,"time":57808.99,"type":0},{"id":0,"sLen":0,"time":57977.5273,"type":0},{"id":2,"sLen":0,"time":58146.0664,"type":0},{"id":3,"sLen":0,"time":58230.3359,"type":0},{"id":1,"sLen":0,"time":58314.6055,"type":0},{"id":3,"sLen":0,"time":58483.1445,"type":0},{"id":1,"sLen":0,"time":58651.6836,"type":0},{"id":3,"sLen":0,"time":58820.2266,"type":0},{"id":0,"sLen":0,"time":58988.7656,"type":0},{"id":3,"sLen":0,"time":59157.3047,"type":0},{"id":0,"sLen":168.539321999997,"time":59325.8438,"type":0},{"id":0,"sLen":168.539321999997,"time":60000,"type":0},{"id":0,"sLen":168.539322000004,"time":60674.1563,"type":0},{"id":0,"sLen":168.539321999997,"time":61348.3164,"type":0},{"id":1,"sLen":0,"time":62022.4719101124,"type":1},{"id":1,"sLen":252.808989999998,"time":64719.1,"type":0},{"id":0,"sLen":0,"time":65056.18,"type":0},{"id":1,"sLen":0,"time":65224.72,"type":0},{"id":3,"sLen":252.808989999998,"time":65393.2578,"type":0},{"id":1,"sLen":0,"time":65730.3359,"type":0},{"id":0,"sLen":0,"time":66067.4141,"type":0},{"id":0,"sLen":0,"time":66404.49,"type":0},{"id":0,"sLen":0,"time":66741.57,"type":0},{"id":0,"sLen":0,"time":66910.11,"type":0},{"id":2,"sLen":0,"time":67078.65,"type":0},{"id":3,"sLen":0,"time":67162.92,"type":0},{"id":2,"sLen":0,"time":67247.19,"type":0},{"id":1,"sLen":0,"time":67415.73,"type":0},{"id":3,"sLen":0,"time":67584.27,"type":0},{"id":2,"sLen":0,"time":67752.81,"type":0},{"id":1,"sLen":0,"time":67921.35,"type":0},{"id":3,"sLen":0,"time":68089.89,"type":0},{"id":2,"sLen":0,"time":68258.43,"type":0},{"id":1,"sLen":0,"time":68426.97,"type":0},{"id":3,"sLen":0,"time":68511.2344,"type":0},{"id":2,"sLen":0,"time":68595.51,"type":0},{"id":0,"sLen":0,"time":68764.05,"type":0},{"id":3,"sLen":0,"time":68932.5859,"type":0},{"id":2,"sLen":0,"time":69101.125,"type":0},{"id":3,"sLen":0,"time":69269.6641,"type":0},{"id":1,"sLen":0,"time":69438.2,"type":0},{"id":3,"sLen":0,"time":69606.74,"type":0},{"id":0,"sLen":0,"time":69775.28,"type":0},{"id":3,"sLen":0,"time":69943.82,"type":0},{"id":0,"sLen":0,"time":75505.62,"type":0},{"id":0,"sLen":0,"time":75674.16,"type":0},{"id":1,"sLen":0,"time":75842.6953,"type":0},{"id":0,"sLen":0,"time":76011.2344,"type":0},{"id":2,"sLen":0,"time":76095.51,"type":0},{"id":0,"sLen":0,"time":76179.77,"type":0},{"id":1,"sLen":0,"time":76348.31,"type":0},{"id":3,"sLen":0,"time":76516.85,"type":0},{"id":0,"sLen":0,"time":76685.39,"type":0},{"id":1,"sLen":0,"time":76853.93,"type":0},{"id":3,"sLen":0,"time":76938.2,"type":0},{"id":1,"sLen":0,"time":77022.47,"type":0},{"id":0,"sLen":0,"time":77191.01,"type":0},{"id":1,"sLen":0,"time":77359.55,"type":0},{"id":2,"sLen":252.808990000005,"time":77528.0859,"type":0},{"id":0,"sLen":0,"time":77865.17,"type":0},{"id":0,"sLen":0,"time":78033.71,"type":0},{"id":1,"sLen":0,"time":78202.25,"type":0},{"id":0,"sLen":0,"time":78370.79,"type":0},{"id":3,"sLen":0,"time":78455.0547,"type":0},{"id":3,"sLen":0,"time":78539.33,"type":0},{"id":3,"sLen":0,"time":78707.87,"type":0},{"id":0,"sLen":0,"time":78792.13,"type":0},{"id":1,"sLen":0,"time":78876.41,"type":0},{"id":1,"sLen":0,"time":79044.9453,"type":0},{"id":3,"sLen":0,"time":79129.21,"type":0},{"id":2,"sLen":0,"time":79213.4844,"type":0},{"id":0,"sLen":0,"time":79382.02,"type":0},{"id":1,"sLen":0,"time":79466.29,"type":0},{"id":2,"sLen":0,"time":79550.56,"type":0},{"id":3,"sLen":0,"time":79634.83,"type":0},{"id":2,"sLen":0,"time":79719.1,"type":0},{"id":0,"sLen":84.2696600000054,"time":79803.37,"type":0},{"id":3,"sLen":0,"time":79971.91,"type":0},{"id":2,"sLen":0,"time":80056.18,"type":0},{"id":0,"sLen":0,"time":80224.72,"type":0},{"id":3,"sLen":0,"time":80393.26,"type":0},{"id":0,"sLen":0,"time":80561.8,"type":0},{"id":2,"sLen":0,"time":80730.3359,"type":0},{"id":1,"sLen":0,"time":86292.13,"type":0},{"id":0,"sLen":0,"time":86376.41,"type":0},{"id":3,"sLen":0,"time":86460.67,"type":0},{"id":2,"sLen":0,"time":86629.21,"type":0},{"id":1,"sLen":0,"time":86713.4844,"type":0},{"id":3,"sLen":0,"time":86797.75,"type":0},{"id":2,"sLen":0,"time":86966.29,"type":0},{"id":3,"sLen":0,"time":87134.83,"type":0},{"id":2,"sLen":0,"time":87303.37,"type":0},{"id":0,"sLen":0,"time":87471.91,"type":0},{"id":1,"sLen":0,"time":87640.45,"type":0},{"id":0,"sLen":0,"time":87724.72,"type":0},{"id":3,"sLen":0,"time":87808.99,"type":0},{"id":2,"sLen":0,"time":87977.53,"type":0},{"id":1,"sLen":0,"time":88061.8,"type":0},{"id":3,"sLen":0,"time":88146.07,"type":0},{"id":2,"sLen":0,"time":88314.61,"type":0},{"id":3,"sLen":0,"time":88483.15,"type":0},{"id":1,"sLen":0,"time":88651.69,"type":0},{"id":3,"sLen":0,"time":88820.23,"type":0},{"id":2,"sLen":0,"time":88988.7656,"type":0},{"id":3,"sLen":0,"time":89073.03,"type":0},{"id":2,"sLen":0,"time":89157.3047,"type":0},{"id":1,"sLen":0,"time":89325.84,"type":0},{"id":0,"sLen":0,"time":89410.11,"type":0},{"id":1,"sLen":0,"time":89494.38,"type":0},{"id":3,"sLen":0,"time":89662.92,"type":0},{"id":3,"sLen":0,"time":89831.46,"type":0},{"id":1,"sLen":0,"time":89915.73,"type":0},{"id":3,"sLen":0,"time":90000,"type":0},{"id":1,"sLen":0,"time":90168.54,"type":0},{"id":0,"sLen":0,"time":90337.08,"type":0},{"id":3,"sLen":0,"time":90421.35,"type":0},{"id":1,"sLen":0,"time":90505.62,"type":0},{"id":0,"sLen":0,"time":90589.89,"type":0},{"id":1,"sLen":0,"time":90674.16,"type":0},{"id":3,"sLen":0,"time":90842.6953,"type":0},{"id":0,"sLen":0,"time":91011.2344,"type":0},{"id":3,"sLen":0,"time":91095.51,"type":0},{"id":1,"sLen":0,"time":91179.77,"type":0},{"id":0,"sLen":0,"time":91264.05,"type":0},{"id":1,"sLen":168.539321999997,"time":91348.31,"type":0},{"id":0,"sLen":0,"time":97078.65,"type":0},{"id":3,"sLen":0,"time":97162.92,"type":0},{"id":2,"sLen":0,"time":97247.19,"type":0},{"id":3,"sLen":0,"time":97415.73,"type":0},{"id":0,"sLen":0,"time":97584.27,"type":0},{"id":3,"sLen":0,"time":97668.54,"type":0},{"id":2,"sLen":0,"time":97752.81,"type":0},{"id":3,"sLen":0,"time":97921.35,"type":0},{"id":0,"sLen":0,"time":98089.89,"type":0},{"id":3,"sLen":0,"time":98174.16,"type":0},{"id":2,"sLen":0,"time":98258.43,"type":0},{"id":3,"sLen":0,"time":98426.97,"type":0},{"id":0,"sLen":0,"time":98595.51,"type":0},{"id":3,"sLen":0,"time":98679.77,"type":0},{"id":2,"sLen":0,"time":98764.05,"type":0},{"id":3,"sLen":0,"time":98932.5859,"type":0},{"id":2,"sLen":0,"time":99101.125,"type":0},{"id":1,"sLen":0,"time":99269.6641,"type":0},{"id":0,"sLen":0,"time":99438.2,"type":0},{"id":3,"sLen":0,"time":99606.74,"type":0},{"id":0,"sLen":0,"time":99775.28,"type":0},{"id":3,"sLen":0,"time":99859.55,"type":0},{"id":2,"sLen":0,"time":99943.82,"type":0},{"id":3,"sLen":0,"time":100112.359,"type":0},{"id":0,"sLen":0,"time":100280.9,"type":0},{"id":3,"sLen":0,"time":100365.172,"type":0},{"id":2,"sLen":0,"time":100449.438,"type":0},{"id":3,"sLen":0,"time":100617.977,"type":0},{"id":0,"sLen":0,"time":100786.516,"type":0},{"id":3,"sLen":0,"time":100870.789,"type":0},{"id":2,"sLen":0,"time":100955.055,"type":0},{"id":3,"sLen":0,"time":101123.594,"type":0},{"id":0,"sLen":0,"time":101292.133,"type":0},{"id":3,"sLen":0,"time":101376.406,"type":0},{"id":2,"sLen":0,"time":101460.672,"type":0},{"id":3,"sLen":0,"time":101629.211,"type":0},{"id":2,"sLen":0,"time":101797.75,"type":0},{"id":1,"sLen":0,"time":101966.289,"type":0},{"id":0,"sLen":0,"time":102134.828,"type":0},{"id":3,"sLen":0,"time":102303.367,"type":0},{"id":0,"sLen":0,"time":102471.906,"type":0},{"id":3,"sLen":0,"time":102556.18,"type":0},{"id":2,"sLen":0,"time":102640.453,"type":0},{"id":3,"sLen":0,"time":102808.992,"type":0},{"id":0,"sLen":0,"time":102977.531,"type":0},{"id":3,"sLen":0,"time":103061.8,"type":0},{"id":2,"sLen":0,"time":103146.07,"type":0},{"id":3,"sLen":0,"time":103314.609,"type":0},{"id":0,"sLen":0,"time":103483.148,"type":0},{"id":3,"sLen":0,"time":103567.414,"type":0},{"id":2,"sLen":0,"time":103651.688,"type":0},{"id":3,"sLen":0,"time":103820.227,"type":0},{"id":0,"sLen":0,"time":103988.766,"type":0},{"id":3,"sLen":0,"time":104073.031,"type":0},{"id":2,"sLen":0,"time":104157.305,"type":0},{"id":3,"sLen":0,"time":104325.844,"type":0},{"id":2,"sLen":0,"time":104494.383,"type":0},{"id":1,"sLen":0,"time":104662.922,"type":0},{"id":0,"sLen":0,"time":104831.461,"type":0},{"id":3,"sLen":0,"time":105000,"type":0},{"id":0,"sLen":0,"time":105168.539,"type":0},{"id":3,"sLen":0,"time":105252.813,"type":0},{"id":2,"sLen":0,"time":105337.078,"type":0},{"id":3,"sLen":0,"time":105505.617,"type":0},{"id":0,"sLen":0,"time":105674.156,"type":0},{"id":3,"sLen":0,"time":105758.43,"type":0},{"id":2,"sLen":0,"time":105842.695,"type":0},{"id":3,"sLen":0,"time":106011.234,"type":0},{"id":0,"sLen":0,"time":106179.773,"type":0},{"id":3,"sLen":0,"time":106264.047,"type":0},{"id":2,"sLen":0,"time":106348.313,"type":0},{"id":3,"sLen":0,"time":106516.852,"type":0},{"id":0,"sLen":0,"time":106685.391,"type":0},{"id":3,"sLen":0,"time":106769.664,"type":0},{"id":2,"sLen":0,"time":106853.93,"type":0},{"id":3,"sLen":0,"time":107022.469,"type":0},{"id":2,"sLen":0,"time":107191.008,"type":0},{"id":1,"sLen":0,"time":107359.547,"type":0},{"id":0,"sLen":0,"time":107528.086,"type":0},{"id":3,"sLen":0,"time":107696.633,"type":0},{"id":3,"sLen":0,"time":107865.172,"type":0},{"id":2,"sLen":0,"time":107949.438,"type":0},{"id":3,"sLen":0,"time":108033.711,"type":0},{"id":2,"sLen":0,"time":108117.977,"type":0},{"id":0,"sLen":0,"time":108202.25,"type":0},{"id":1,"sLen":0,"time":108370.789,"type":0},{"id":3,"sLen":0,"time":108539.328,"type":0},{"id":2,"sLen":0,"time":108623.594,"type":0},{"id":3,"sLen":0,"time":108707.867,"type":0},{"id":2,"sLen":0,"time":108792.133,"type":0},{"id":0,"sLen":0,"time":108876.406,"type":0},{"id":1,"sLen":0,"time":109044.945,"type":0},{"id":3,"sLen":0,"time":109213.484,"type":0},{"id":0,"sLen":0,"time":109297.75,"type":0},{"id":2,"sLen":0,"time":109382.023,"type":0},{"id":3,"sLen":0,"time":109466.289,"type":0},{"id":0,"sLen":0,"time":109550.563,"type":0},{"id":1,"sLen":0,"time":109719.1,"type":0},{"id":3,"sLen":0,"time":109887.641,"type":0},{"id":2,"sLen":0,"time":109971.906,"type":0},{"id":3,"sLen":0,"time":110056.18,"type":0},{"id":2,"sLen":0,"time":110140.453,"type":0},{"id":0,"sLen":0,"time":110224.719,"type":0},{"id":1,"sLen":0,"time":110393.258,"type":0},{"id":2,"sLen":0,"time":113258.43,"type":0},{"id":3,"sLen":0,"time":113426.969,"type":0},{"id":1,"sLen":0,"time":113595.508,"type":0},{"id":3,"sLen":0,"time":113764.047,"type":0},{"id":0,"sLen":0,"time":113932.586,"type":0},{"id":1,"sLen":0,"time":114101.125,"type":0},{"id":0,"sLen":0,"time":114269.664,"type":0},{"id":3,"sLen":0,"time":114353.93,"type":0},{"id":1,"sLen":0,"time":114438.2,"type":0},{"id":0,"sLen":0,"time":114522.469,"type":0},{"id":2,"sLen":0,"time":114606.742,"type":0},{"id":3,"sLen":0,"time":114775.281,"type":0},{"id":1,"sLen":0,"time":114943.82,"type":0},{"id":3,"sLen":0,"time":115112.359,"type":0},{"id":0,"sLen":0,"time":115280.9,"type":0},{"id":3,"sLen":0,"time":115365.172,"type":0},{"id":1,"sLen":0,"time":115449.438,"type":0},{"id":0,"sLen":0,"time":115533.711,"type":0},{"id":3,"sLen":0,"time":115617.977,"type":0},{"id":1,"sLen":0,"time":115786.516,"type":0},{"id":2,"sLen":589.887633999984,"time":115955.055,"type":0}],"position":"dad","characters":["tankman"],"visible":true,"type":0},{"notes":[{"id":1,"sLen":0,"time":5393.2583,"type":0},{"id":2,"sLen":0,"time":5561.798,"type":0},{"id":0,"sLen":0,"time":5730.337,"type":0},{"id":0,"sLen":0,"time":5898.87646,"type":0},{"id":2,"sLen":0,"time":6067.41553,"type":0},{"id":3,"sLen":0,"time":6235.955,"type":0},{"id":1,"sLen":0,"time":6404.494,"type":0},{"id":3,"sLen":0,"time":6573.03369,"type":0},{"id":0,"sLen":0,"time":6741.573,"type":0},{"id":3,"sLen":0,"time":6910.1123,"type":0},{"id":2,"sLen":0,"time":7078.652,"type":0},{"id":3,"sLen":0,"time":7247.191,"type":0},{"id":1,"sLen":0,"time":7415.73047,"type":0},{"id":3,"sLen":0,"time":7584.26953,"type":0},{"id":0,"sLen":0,"time":7752.809,"type":0},{"id":3,"sLen":0,"time":7921.348,"type":0},{"id":1,"sLen":0,"time":8089.8877,"type":0},{"id":2,"sLen":0,"time":8258.427,"type":0},{"id":0,"sLen":0,"time":8426.966,"type":0},{"id":0,"sLen":0,"time":8595.506,"type":0},{"id":2,"sLen":0,"time":8764.045,"type":0},{"id":3,"sLen":0,"time":8932.584,"type":0},{"id":1,"sLen":0,"time":9101.124,"type":0},{"id":3,"sLen":0,"time":9269.663,"type":0},{"id":0,"sLen":0,"time":9438.202,"type":0},{"id":3,"sLen":0,"time":9606.741,"type":0},{"id":2,"sLen":0,"time":9775.281,"type":0},{"id":3,"sLen":0,"time":9943.82,"type":0},{"id":1,"sLen":0,"time":10112.3594,"type":0},{"id":3,"sLen":0,"time":10280.8984,"type":0},{"id":0,"sLen":0,"time":10449.4385,"type":0},{"id":3,"sLen":0,"time":10617.9775,"type":0},{"id":1,"sLen":252.808990000001,"time":14831.4609,"type":0},{"id":2,"sLen":252.80899,"time":15168.5391,"type":0},{"id":3,"sLen":252.80899,"time":15505.6182,"type":0},{"id":0,"sLen":252.808990000001,"time":15842.6963,"type":0},{"id":1,"sLen":252.80899,"time":16179.7754,"type":0},{"id":0,"sLen":0,"time":16516.8535,"type":0},{"id":1,"sLen":0,"time":16685.3926,"type":0},{"id":3,"sLen":252.808990000001,"time":16853.9316,"type":0},{"id":1,"sLen":0,"time":17191.0117,"type":0},{"id":0,"sLen":0,"time":17528.09,"type":0},{"id":0,"sLen":0,"time":17865.168,"type":0},{"id":0,"sLen":0,"time":18202.248,"type":0},{"id":0,"sLen":0,"time":18370.7871,"type":0},{"id":2,"sLen":0,"time":18539.3262,"type":0},{"id":3,"sLen":0,"time":18623.5957,"type":0},{"id":2,"sLen":0,"time":18707.8652,"type":0},{"id":1,"sLen":0,"time":18876.4043,"type":0},{"id":3,"sLen":0,"time":19044.9434,"type":0},{"id":2,"sLen":0,"time":19213.4824,"type":0},{"id":1,"sLen":0,"time":19382.0215,"type":0},{"id":3,"sLen":0,"time":19550.5625,"type":0},{"id":2,"sLen":0,"time":19719.1016,"type":0},{"id":1,"sLen":0,"time":19887.64,"type":0},{"id":3,"sLen":0,"time":19971.91,"type":0},{"id":2,"sLen":0,"time":20056.18,"type":0},{"id":0,"sLen":0,"time":20224.7188,"type":0},{"id":3,"sLen":0,"time":20393.2578,"type":0},{"id":2,"sLen":0,"time":20561.7969,"type":0},{"id":3,"sLen":0,"time":20730.3379,"type":0},{"id":1,"sLen":0,"time":20898.877,"type":0},{"id":3,"sLen":0,"time":21067.416,"type":0},{"id":0,"sLen":0,"time":21235.9551,"type":0},{"id":3,"sLen":0,"time":21404.4941,"type":0},{"id":1,"sLen":0,"time":21573.0332,"type":0},{"id":1,"sLen":0,"time":21910.1133,"type":0},{"id":1,"sLen":0,"time":22247.1914,"type":0},{"id":1,"sLen":0,"time":22584.27,"type":0},{"id":2,"sLen":0,"time":22921.3477,"type":0},{"id":2,"sLen":0,"time":23258.4277,"type":0},{"id":3,"sLen":252.808990000001,"time":23595.5059,"type":0},{"id":0,"sLen":252.808990000012,"time":23932.584,"type":0},{"id":3,"sLen":0,"time":24269.6621,"type":0},{"id":2,"sLen":0,"time":24438.2031,"type":0},{"id":0,"sLen":0,"time":24606.7422,"type":0},{"id":2,"sLen":0,"time":24775.2813,"type":0},{"id":3,"sLen":0,"time":24943.82,"type":0},{"id":2,"sLen":0,"time":25112.36,"type":0},{"id":0,"sLen":0,"time":25280.8984,"type":0},{"id":2,"sLen":0,"time":25449.4375,"type":0},{"id":3,"sLen":0,"time":25617.9766,"type":0},{"id":2,"sLen":0,"time":25702.248,"type":0},{"id":3,"sLen":0,"time":25786.5176,"type":0},{"id":3,"sLen":0,"time":25955.0566,"type":0},{"id":2,"sLen":0,"time":26123.5957,"type":0},{"id":3,"sLen":0,"time":26292.1348,"type":0},{"id":0,"sLen":0,"time":26460.6738,"type":0},{"id":1,"sLen":0,"time":26629.2129,"type":0},{"id":0,"sLen":0,"time":26797.752,"type":0},{"id":2,"sLen":505.617980999999,"time":26966.293,"type":0},{"id":3,"sLen":505.617980999989,"time":27640.45,"type":0},{"id":1,"sLen":505.617980999999,"time":28314.6074,"type":0},{"id":2,"sLen":505.617980999999,"time":28988.7637,"type":0},{"id":3,"sLen":0,"time":29662.9219,"type":0},{"id":2,"sLen":0,"time":29831.46,"type":0},{"id":3,"sLen":0,"time":30000,"type":0},{"id":0,"sLen":0,"time":30168.54,"type":0},{"id":3,"sLen":0,"time":30337.0781,"type":0},{"id":2,"sLen":0,"time":30505.6172,"type":0},{"id":3,"sLen":0,"time":30674.1582,"type":0},{"id":0,"sLen":0,"time":30842.6973,"type":0},{"id":3,"sLen":0,"time":31011.2363,"type":0},{"id":2,"sLen":0,"time":31095.5059,"type":0},{"id":3,"sLen":0,"time":31179.7754,"type":0},{"id":0,"sLen":0,"time":31348.3145,"type":0},{"id":3,"sLen":0,"time":31516.8535,"type":0},{"id":2,"sLen":0,"time":31685.3926,"type":0},{"id":0,"sLen":0,"time":31853.9316,"type":0},{"id":3,"sLen":0,"time":32022.4727,"type":0},{"id":1,"sLen":0,"time":32191.0117,"type":0},{"id":1,"sLen":674.157300000003,"time":32359.55,"type":0},{"id":2,"sLen":0,"time":37752.81,"type":0},{"id":3,"sLen":0,"time":37921.3477,"type":0},{"id":1,"sLen":0,"time":38089.8867,"type":0},{"id":3,"sLen":0,"time":38258.4258,"type":0},{"id":0,"sLen":0,"time":38426.9648,"type":0},{"id":3,"sLen":0,"time":38595.5039,"type":0},{"id":1,"sLen":0,"time":38764.043,"type":0},{"id":3,"sLen":0,"time":38932.5859,"type":0},{"id":3,"sLen":0,"time":39016.8555,"type":0},{"id":1,"sLen":0,"time":39101.125,"type":0},{"id":3,"sLen":0,"time":39269.6641,"type":0},{"id":1,"sLen":0,"time":39438.2031,"type":0},{"id":2,"sLen":0,"time":39606.7422,"type":0},{"id":0,"sLen":0,"time":39775.28,"type":0},{"id":2,"sLen":0,"time":39943.82,"type":0},{"id":0,"sLen":0,"time":40112.36,"type":0},{"id":3,"sLen":0,"time":40280.9,"type":0},{"id":1,"sLen":0,"time":40365.168,"type":0},{"id":2,"sLen":0,"time":40449.4375,"type":0},{"id":3,"sLen":0,"time":40617.9766,"type":0},{"id":2,"sLen":0,"time":40786.5156,"type":0},{"id":3,"sLen":0,"time":40955.0547,"type":0},{"id":0,"sLen":0,"time":41123.5938,"type":0},{"id":1,"sLen":0,"time":41292.1367,"type":0},{"id":0,"sLen":0,"time":41460.6758,"type":0},{"id":2,"sLen":0,"time":41544.9453,"type":0},{"id":0,"sLen":0,"time":41629.2148,"type":0},{"id":3,"sLen":0,"time":41797.7539,"type":0},{"id":1,"sLen":0,"time":41966.293,"type":0},{"id":0,"sLen":0,"time":42134.832,"type":0},{"id":1,"sLen":0,"time":42303.37,"type":0},{"id":0,"sLen":0,"time":42471.91,"type":0},{"id":1,"sLen":0,"time":42640.45,"type":0},{"id":1,"sLen":0,"time":42808.99,"type":0},{"id":3,"sLen":0,"time":42977.5273,"type":0},{"id":3,"sLen":0,"time":43061.7969,"type":0},{"id":3,"sLen":0,"time":48539.3242,"type":0},{"id":1,"sLen":0,"time":48623.5938,"type":0},{"id":3,"sLen":0,"time":48707.8633,"type":0},{"id":0,"sLen":0,"time":48876.4063,"type":0},{"id":3,"sLen":0,"time":49044.9453,"type":0},{"id":1,"sLen":0,"time":49129.2148,"type":0},{"id":2,"sLen":0,"time":49213.4844,"type":0},{"id":0,"sLen":0,"time":49382.0234,"type":0},{"id":1,"sLen":0,"time":49550.5625,"type":0},{"id":3,"sLen":0,"time":49719.1,"type":0},{"id":3,"sLen":0,"time":49803.37,"type":0},{"id":2,"sLen":0,"time":49887.64,"type":0},{"id":3,"sLen":0,"time":50056.18,"type":0},{"id":0,"sLen":0,"time":50140.45,"type":0},{"id":1,"sLen":0,"time":50224.72,"type":0},{"id":0,"sLen":0,"time":50393.2578,"type":0},{"id":2,"sLen":0,"time":50561.7969,"type":0},{"id":3,"sLen":0,"time":50730.3359,"type":0},{"id":1,"sLen":0,"time":50898.875,"type":0},{"id":3,"sLen":0,"time":51067.4141,"type":0},{"id":2,"sLen":0,"time":51151.6836,"type":0},{"id":0,"sLen":0,"time":51235.9531,"type":0},{"id":3,"sLen":0,"time":51404.4961,"type":0},{"id":1,"sLen":0,"time":51573.0352,"type":0},{"id":3,"sLen":0,"time":51741.5742,"type":0},{"id":0,"sLen":0,"time":51910.1133,"type":0},{"id":1,"sLen":0,"time":52078.6523,"type":0},{"id":3,"sLen":0,"time":52247.19,"type":0},{"id":2,"sLen":0,"time":52331.46,"type":0},{"id":3,"sLen":0,"time":52415.73,"type":0},{"id":1,"sLen":0,"time":52584.27,"type":0},{"id":3,"sLen":0,"time":52752.81,"type":0},{"id":0,"sLen":0,"time":52921.3477,"type":0},{"id":3,"sLen":0,"time":53089.8867,"type":0},{"id":1,"sLen":0,"time":53258.4258,"type":0},{"id":3,"sLen":0,"time":53426.9648,"type":0},{"id":2,"sLen":0,"time":53595.5039,"type":0},{"id":2,"sLen":0,"time":53764.043,"type":0},{"id":1,"sLen":842.696654999985,"time":53932.5859,"type":0},{"id":2,"sLen":505.617981000003,"time":55955.0547,"type":0},{"id":1,"sLen":674.157299999999,"time":56629.2148,"type":0},{"id":0,"sLen":252.808989999998,"time":58651.6836,"type":0},{"id":2,"sLen":84.2696599999836,"time":58988.7656,"type":0},{"id":1,"sLen":0,"time":59157.3047,"type":0},{"id":1,"sLen":0,"time":59325.8438,"type":0},{"id":2,"sLen":0,"time":59494.3828,"type":0},{"id":0,"sLen":0,"time":59662.92,"type":0},{"id":0,"sLen":0,"time":59831.46,"type":0},{"id":2,"sLen":0,"time":60000,"type":0},{"id":3,"sLen":0,"time":60168.54,"type":0},{"id":1,"sLen":0,"time":60337.08,"type":0},{"id":3,"sLen":0,"time":60505.6172,"type":0},{"id":0,"sLen":0,"time":60674.1563,"type":0},{"id":1,"sLen":0,"time":60842.6953,"type":0},{"id":2,"sLen":0,"time":61011.2344,"type":0},{"id":3,"sLen":0,"time":61179.7734,"type":0},{"id":1,"sLen":0,"time":61348.3164,"type":0},{"id":3,"sLen":0,"time":61516.8555,"type":0},{"id":0,"sLen":0,"time":61685.3945,"type":0},{"id":3,"sLen":0,"time":61853.9336,"type":0},{"id":1,"sLen":0,"time":62022.4727,"type":0},{"id":2,"sLen":0,"time":62191.01,"type":0},{"id":0,"sLen":0,"time":62359.55,"type":0},{"id":0,"sLen":0,"time":62528.09,"type":0},{"id":2,"sLen":0,"time":62696.63,"type":0},{"id":3,"sLen":0,"time":62865.168,"type":0},{"id":1,"sLen":0,"time":63033.707,"type":0},{"id":3,"sLen":0,"time":63202.2461,"type":0},{"id":0,"sLen":0,"time":63370.7852,"type":0},{"id":2,"sLen":0,"time":63539.3242,"type":0},{"id":3,"sLen":0,"time":63623.5938,"type":0},{"id":1,"sLen":0,"time":63707.8633,"type":0},{"id":3,"sLen":0,"time":63876.4063,"type":0},{"id":1,"sLen":0,"time":64044.9453,"type":0},{"id":3,"sLen":0,"time":64213.4844,"type":0},{"id":0,"sLen":0,"time":64382.0234,"type":0},{"id":3,"sLen":0,"time":64550.5625,"type":0},{"id":1,"sLen":252.808989999998,"time":64719.1,"type":0},{"id":0,"sLen":0,"time":65056.18,"type":0},{"id":1,"sLen":0,"time":65224.72,"type":0},{"id":3,"sLen":252.808989999998,"time":65393.2578,"type":0},{"id":1,"sLen":168.539321999997,"time":65730.3359,"type":0},{"id":3,"sLen":421.348327999993,"time":66067.4141,"type":0},{"id":0,"sLen":84.2696600000054,"time":66573.03,"type":0},{"id":1,"sLen":421.348327999993,"time":66741.57,"type":0},{"id":0,"sLen":0,"time":67247.19,"type":0},{"id":2,"sLen":589.887633999999,"time":67415.73,"type":0},{"id":1,"sLen":252.808990000005,"time":68089.89,"type":0},{"id":2,"sLen":252.808990000005,"time":68426.97,"type":0},{"id":3,"sLen":589.887633999999,"time":68764.05,"type":0},{"id":0,"sLen":589.887633999999,"time":69438.2,"type":0},{"id":1,"sLen":252.808990000005,"time":70112.36,"type":0},{"id":0,"sLen":0,"time":70449.44,"type":0},{"id":1,"sLen":0,"time":70617.98,"type":0},{"id":3,"sLen":252.808990000005,"time":70786.5156,"type":0},{"id":1,"sLen":0,"time":71123.59,"type":0},{"id":0,"sLen":0,"time":71460.67,"type":0},{"id":0,"sLen":0,"time":71797.75,"type":0},{"id":0,"sLen":0,"time":72134.83,"type":0},{"id":0,"sLen":0,"time":72303.37,"type":0},{"id":2,"sLen":0,"time":72471.91,"type":0},{"id":3,"sLen":0,"time":72556.18,"type":0},{"id":2,"sLen":0,"time":72640.45,"type":0},{"id":1,"sLen":0,"time":72808.99,"type":0},{"id":3,"sLen":0,"time":72977.53,"type":0},{"id":2,"sLen":0,"time":73146.07,"type":0},{"id":1,"sLen":0,"time":73314.61,"type":0},{"id":3,"sLen":0,"time":73483.15,"type":0},{"id":2,"sLen":0,"time":73651.69,"type":0},{"id":1,"sLen":0,"time":73820.23,"type":0},{"id":3,"sLen":0,"time":73904.49,"type":0},{"id":2,"sLen":0,"time":73988.7656,"type":0},{"id":0,"sLen":0,"time":74157.3047,"type":0},{"id":3,"sLen":0,"time":74325.84,"type":0},{"id":2,"sLen":0,"time":74494.38,"type":0},{"id":3,"sLen":0,"time":74662.92,"type":0},{"id":1,"sLen":0,"time":74831.46,"type":0},{"id":3,"sLen":0,"time":75000,"type":0},{"id":0,"sLen":0,"time":75168.54,"type":0},{"id":3,"sLen":0,"time":75337.08,"type":0},{"id":1,"sLen":1264.04492,"time":75505.62,"type":0},{"id":0,"sLen":1264.04492,"time":76853.93,"type":0},{"id":3,"sLen":1264.04492,"time":78202.25,"type":0},{"id":0,"sLen":1264.04492,"time":79550.56,"type":0},{"id":0,"sLen":0,"time":80898.875,"type":0},{"id":0,"sLen":0,"time":81067.4141,"type":0},{"id":1,"sLen":0,"time":81235.95,"type":0},{"id":0,"sLen":0,"time":81404.49,"type":0},{"id":2,"sLen":0,"time":81488.7656,"type":0},{"id":0,"sLen":0,"time":81573.03,"type":0},{"id":1,"sLen":0,"time":81741.57,"type":0},{"id":3,"sLen":0,"time":81910.11,"type":0},{"id":0,"sLen":0,"time":82078.65,"type":0},{"id":1,"sLen":0,"time":82247.19,"type":0},{"id":3,"sLen":0,"time":82331.46,"type":0},{"id":1,"sLen":0,"time":82415.73,"type":0},{"id":0,"sLen":0,"time":82584.27,"type":0},{"id":1,"sLen":0,"time":82752.81,"type":0},{"id":2,"sLen":252.808990000005,"time":82921.35,"type":0},{"id":0,"sLen":0,"time":83258.43,"type":0},{"id":0,"sLen":0,"time":83426.97,"type":0},{"id":1,"sLen":0,"time":83595.51,"type":0},{"id":0,"sLen":0,"time":83764.05,"type":0},{"id":3,"sLen":0,"time":83848.31,"type":0},{"id":3,"sLen":0,"time":83932.5859,"type":0},{"id":3,"sLen":0,"time":84101.125,"type":0},{"id":0,"sLen":0,"time":84185.39,"type":0},{"id":1,"sLen":0,"time":84269.6641,"type":0},{"id":1,"sLen":0,"time":84438.2,"type":0},{"id":3,"sLen":0,"time":84522.47,"type":0},{"id":2,"sLen":0,"time":84606.74,"type":0},{"id":0,"sLen":0,"time":84775.28,"type":0},{"id":1,"sLen":0,"time":84859.55,"type":0},{"id":2,"sLen":0,"time":84943.82,"type":0},{"id":3,"sLen":0,"time":85028.0859,"type":0},{"id":2,"sLen":0,"time":85112.36,"type":0},{"id":0,"sLen":84.2696600000054,"time":85196.63,"type":0},{"id":3,"sLen":0,"time":85365.17,"type":0},{"id":2,"sLen":0,"time":85449.44,"type":0},{"id":0,"sLen":0,"time":85617.98,"type":0},{"id":3,"sLen":0,"time":85786.5156,"type":0},{"id":0,"sLen":0,"time":85955.0547,"type":0},{"id":2,"sLen":0,"time":86123.59,"type":0},{"id":1,"sLen":0,"time":91685.39,"type":0},{"id":0,"sLen":0,"time":91769.6641,"type":0},{"id":3,"sLen":0,"time":91853.93,"type":0},{"id":2,"sLen":0,"time":92022.47,"type":0},{"id":1,"sLen":0,"time":92106.74,"type":0},{"id":3,"sLen":0,"time":92191.01,"type":0},{"id":2,"sLen":0,"time":92359.55,"type":0},{"id":3,"sLen":0,"time":92528.0859,"type":0},{"id":2,"sLen":0,"time":92696.63,"type":0},{"id":0,"sLen":0,"time":92865.17,"type":0},{"id":1,"sLen":0,"time":93033.71,"type":0},{"id":0,"sLen":0,"time":93117.98,"type":0},{"id":3,"sLen":0,"time":93202.25,"type":0},{"id":2,"sLen":0,"time":93370.79,"type":0},{"id":1,"sLen":0,"time":93455.0547,"type":0},{"id":3,"sLen":0,"time":93539.33,"type":0},{"id":2,"sLen":0,"time":93707.87,"type":0},{"id":3,"sLen":0,"time":93876.41,"type":0},{"id":1,"sLen":0,"time":94044.9453,"type":0},{"id":3,"sLen":0,"time":94213.4844,"type":0},{"id":2,"sLen":0,"time":94382.02,"type":0},{"id":3,"sLen":0,"time":94466.29,"type":0},{"id":2,"sLen":0,"time":94550.56,"type":0},{"id":1,"sLen":0,"time":94719.1,"type":0},{"id":0,"sLen":0,"time":94803.37,"type":0},{"id":1,"sLen":0,"time":94887.64,"type":0},{"id":3,"sLen":0,"time":95056.18,"type":0},{"id":3,"sLen":0,"time":95224.72,"type":0},{"id":1,"sLen":0,"time":95308.99,"type":0},{"id":3,"sLen":0,"time":95393.26,"type":0},{"id":1,"sLen":0,"time":95561.8,"type":0},{"id":0,"sLen":0,"time":95730.3359,"type":0},{"id":3,"sLen":0,"time":95814.61,"type":0},{"id":1,"sLen":0,"time":95898.875,"type":0},{"id":0,"sLen":0,"time":95983.15,"type":0},{"id":1,"sLen":0,"time":96067.4141,"type":0},{"id":3,"sLen":0,"time":96235.95,"type":0},{"id":0,"sLen":0,"time":96404.49,"type":0},{"id":3,"sLen":0,"time":96488.7656,"type":0},{"id":1,"sLen":0,"time":96573.03,"type":0},{"id":0,"sLen":0,"time":96657.3047,"type":0},{"id":1,"sLen":337.078643999994,"time":96741.57,"type":0},{"id":0,"sLen":589.887633999955,"time":98426.97,"type":0},{"id":2,"sLen":589.887633999999,"time":99101.125,"type":0},{"id":0,"sLen":589.887633999999,"time":101123.594,"type":0},{"id":2,"sLen":589.887633999984,"time":101797.75,"type":0},{"id":3,"sLen":0,"time":102471.906,"type":0},{"id":0,"sLen":0,"time":102556.18,"type":0},{"id":2,"sLen":0,"time":102640.453,"type":0},{"id":0,"sLen":0,"time":102808.992,"type":0},{"id":3,"sLen":0,"time":102977.531,"type":0},{"id":0,"sLen":0,"time":103061.8,"type":0},{"id":2,"sLen":0,"time":103146.07,"type":0},{"id":0,"sLen":0,"time":103314.609,"type":0},{"id":3,"sLen":0,"time":103483.148,"type":0},{"id":0,"sLen":0,"time":103567.414,"type":0},{"id":2,"sLen":0,"time":103651.688,"type":0},{"id":0,"sLen":0,"time":103820.227,"type":0},{"id":3,"sLen":0,"time":103988.766,"type":0},{"id":0,"sLen":0,"time":104073.031,"type":0},{"id":2,"sLen":0,"time":104157.305,"type":0},{"id":0,"sLen":0,"time":104325.844,"type":0},{"id":2,"sLen":0,"time":104494.383,"type":0},{"id":1,"sLen":0,"time":104662.922,"type":0},{"id":3,"sLen":0,"time":104831.461,"type":0},{"id":0,"sLen":0,"time":105000,"type":0},{"id":3,"sLen":0,"time":105168.539,"type":0},{"id":0,"sLen":0,"time":105252.813,"type":0},{"id":2,"sLen":0,"time":105337.078,"type":0},{"id":0,"sLen":0,"time":105505.617,"type":0},{"id":3,"sLen":0,"time":105674.156,"type":0},{"id":0,"sLen":0,"time":105758.43,"type":0},{"id":2,"sLen":0,"time":105842.695,"type":0},{"id":0,"sLen":0,"time":106011.234,"type":0},{"id":3,"sLen":0,"time":106179.773,"type":0},{"id":0,"sLen":0,"time":106264.047,"type":0},{"id":2,"sLen":0,"time":106348.313,"type":0},{"id":0,"sLen":0,"time":106516.852,"type":0},{"id":3,"sLen":0,"time":106685.391,"type":0},{"id":0,"sLen":0,"time":106769.664,"type":0},{"id":2,"sLen":0,"time":106853.93,"type":0},{"id":0,"sLen":0,"time":107022.469,"type":0},{"id":2,"sLen":0,"time":107191.008,"type":0},{"id":1,"sLen":0,"time":107359.547,"type":0},{"id":3,"sLen":0,"time":107528.086,"type":0},{"id":0,"sLen":0,"time":107696.633,"type":0},{"id":3,"sLen":589.887634000028,"time":107865.172,"type":0},{"id":1,"sLen":589.887634000028,"time":108539.328,"type":0},{"id":2,"sLen":589.887633999999,"time":109213.484,"type":0},{"id":1,"sLen":589.887633999999,"time":109887.641,"type":0},{"id":3,"sLen":0,"time":110561.8,"type":0},{"id":2,"sLen":0,"time":110646.07,"type":0},{"id":3,"sLen":0,"time":110730.336,"type":0},{"id":2,"sLen":0,"time":110814.609,"type":0},{"id":0,"sLen":0,"time":110898.875,"type":0},{"id":1,"sLen":0,"time":111067.414,"type":0},{"id":3,"sLen":0,"time":111235.953,"type":0},{"id":2,"sLen":0,"time":111320.227,"type":0},{"id":3,"sLen":0,"time":111404.492,"type":0},{"id":2,"sLen":0,"time":111488.766,"type":0},{"id":0,"sLen":0,"time":111573.031,"type":0},{"id":1,"sLen":0,"time":111741.57,"type":0},{"id":3,"sLen":0,"time":111910.109,"type":0},{"id":0,"sLen":0,"time":111994.383,"type":0},{"id":2,"sLen":0,"time":112078.648,"type":0},{"id":3,"sLen":0,"time":112162.922,"type":0},{"id":0,"sLen":0,"time":112247.188,"type":0},{"id":1,"sLen":0,"time":112415.727,"type":0},{"id":3,"sLen":0,"time":112584.273,"type":0},{"id":2,"sLen":0,"time":112668.539,"type":0},{"id":3,"sLen":0,"time":112752.813,"type":0},{"id":2,"sLen":0,"time":112837.078,"type":0},{"id":0,"sLen":0,"time":112921.352,"type":0},{"id":1,"sLen":0,"time":113089.891,"type":0},{"id":3,"sLen":589.887633999999,"time":113258.43,"type":0},{"id":1,"sLen":589.887633999999,"time":113932.586,"type":0},{"id":0,"sLen":589.887633999984,"time":114606.742,"type":0},{"id":1,"sLen":589.887633999999,"time":115280.9,"type":0},{"id":2,"sLen":0,"time":115955.055,"type":0},{"id":3,"sLen":0,"time":116123.594,"type":0},{"id":1,"sLen":0,"time":116292.133,"type":0},{"id":3,"sLen":0,"time":116460.672,"type":0},{"id":0,"sLen":0,"time":116629.211,"type":0},{"id":1,"sLen":0,"time":116797.75,"type":0},{"id":0,"sLen":0,"time":116966.289,"type":0},{"id":3,"sLen":0,"time":117050.563,"type":0},{"id":1,"sLen":0,"time":117134.828,"type":0},{"id":0,"sLen":0,"time":117219.1,"type":0},{"id":2,"sLen":0,"time":117303.367,"type":0},{"id":3,"sLen":0,"time":117471.906,"type":0},{"id":1,"sLen":0,"time":117640.453,"type":0},{"id":3,"sLen":0,"time":117808.992,"type":0},{"id":0,"sLen":0,"time":117977.531,"type":0},{"id":3,"sLen":0,"time":118061.8,"type":0},{"id":1,"sLen":0,"time":118146.07,"type":0},{"id":0,"sLen":0,"time":118230.336,"type":0},{"id":3,"sLen":0,"time":118314.609,"type":0},{"id":1,"sLen":0,"time":118483.148,"type":0}],"position":"boyfriend","characters":["bf-holding-gf"],"visible":true,"type":1},{"notes":[{"id":0,"sLen":0,"time":0,"type":0},{"id":3,"sLen":0,"time":168.539325842697,"type":0},{"id":3,"sLen":0,"time":252.808988764045,"type":0},{"id":3,"sLen":0,"time":421.348314606742,"type":0},{"id":0,"sLen":0,"time":589.887640449438,"type":0},{"id":3,"sLen":0,"time":758.426966292135,"type":0},{"id":3,"sLen":0,"time":842.696629213483,"type":0},{"id":0,"sLen":0,"time":1011.23595505618,"type":0},{"id":0,"sLen":0,"time":1179.77528089888,"type":0},{"id":0,"sLen":0,"time":1264.04494382022,"type":0},{"id":3,"sLen":0,"time":1348.31460674157,"type":0},{"id":0,"sLen":0,"time":1516.85393258427,"type":0},{"id":0,"sLen":0,"time":1601.12359550562,"type":0},{"id":3,"sLen":0,"time":1853.93258426966,"type":0},{"id":0,"sLen":0,"time":2022.47191011236,"type":0},{"id":3,"sLen":0,"time":2106.74157303371,"type":0},{"id":3,"sLen":0,"time":2191.01123595506,"type":0},{"id":0,"sLen":0,"time":2359.55056179775,"type":0},{"id":0,"sLen":0,"time":2696.62921348315,"type":0},{"id":3,"sLen":0,"time":2865.16853932584,"type":0},{"id":3,"sLen":0,"time":2949.43820224719,"type":0},{"id":3,"sLen":0,"time":3117.97752808989,"type":0},{"id":0,"sLen":0,"time":3286.51685393258,"type":0},{"id":3,"sLen":0,"time":3455.05617977528,"type":0},{"id":3,"sLen":0,"time":3539.32584269663,"type":0},{"id":0,"sLen":0,"time":3707.86516853933,"type":0},{"id":0,"sLen":0,"time":3876.40449438202,"type":0},{"id":0,"sLen":0,"time":3960.67415730337,"type":0},{"id":3,"sLen":0,"time":4044.94382022472,"type":0},{"id":0,"sLen":0,"time":4213.48314606742,"type":0},{"id":0,"sLen":0,"time":4297.75280898876,"type":0},{"id":3,"sLen":0,"time":4550.56179775281,"type":0},{"id":0,"sLen":0,"time":4719.10112359551,"type":0},{"id":3,"sLen":0,"time":4803.37078651685,"type":0},{"id":3,"sLen":0,"time":4887.6404494382,"type":0},{"id":0,"sLen":0,"time":5056.1797752809,"type":0},{"id":0,"sLen":0,"time":5140.44943820225,"type":0},{"id":0,"sLen":0,"time":5224.7191011236,"type":0},{"id":0,"sLen":0,"time":5393.25842696629,"type":0},{"id":3,"sLen":0,"time":5561.79775280899,"type":0},{"id":3,"sLen":0,"time":5646.06741573034,"type":0},{"id":3,"sLen":0,"time":5814.60674157303,"type":0},{"id":0,"sLen":0,"time":5983.14606741573,"type":0},{"id":3,"sLen":0,"time":6151.68539325843,"type":0},{"id":3,"sLen":0,"time":6235.95505617978,"type":0},{"id":0,"sLen":0,"time":6404.49438202247,"type":0},{"id":0,"sLen":0,"time":6573.03370786517,"type":0},{"id":0,"sLen":0,"time":6657.30337078652,"type":0},{"id":3,"sLen":0,"time":6741.57303370786,"type":0},{"id":0,"sLen":0,"time":6910.11235955056,"type":0},{"id":0,"sLen":0,"time":6994.38202247191,"type":0},{"id":3,"sLen":0,"time":7247.19101123595,"type":0},{"id":0,"sLen":0,"time":7415.73033707865,"type":0},{"id":3,"sLen":0,"time":7500,"type":0},{"id":3,"sLen":0,"time":7584.26966292135,"type":0},{"id":0,"sLen":0,"time":7752.80898876405,"type":0},{"id":0,"sLen":0,"time":8089.88764044944,"type":0},{"id":3,"sLen":0,"time":8258.42696629213,"type":0},{"id":3,"sLen":0,"time":8342.69662921348,"type":0},{"id":3,"sLen":0,"time":8511.23595505618,"type":0},{"id":0,"sLen":0,"time":8679.77528089888,"type":0},{"id":3,"sLen":0,"time":8848.31460674157,"type":0},{"id":3,"sLen":0,"time":8932.58426966292,"type":0},{"id":0,"sLen":0,"time":9101.12359550562,"type":0},{"id":0,"sLen":0,"time":9269.66292134831,"type":0},{"id":0,"sLen":0,"time":9353.93258426966,"type":0},{"id":3,"sLen":0,"time":9438.20224719101,"type":0},{"id":0,"sLen":0,"time":9606.74157303371,"type":0},{"id":0,"sLen":0,"time":9691.01123595505,"type":0},{"id":3,"sLen":0,"time":9943.8202247191,"type":0},{"id":0,"sLen":0,"time":10112.3595505618,"type":0},{"id":3,"sLen":0,"time":10196.6292134831,"type":0},{"id":3,"sLen":0,"time":10280.8988764045,"type":0},{"id":0,"sLen":0,"time":10449.4382022472,"type":0},{"id":0,"sLen":0,"time":21235.9550561798,"type":0},{"id":3,"sLen":0,"time":21320.2247191011,"type":0},{"id":0,"sLen":0,"time":21404.4943820225,"type":0},{"id":0,"sLen":0,"time":21573.0337078652,"type":0},{"id":3,"sLen":0,"time":21910.1123595506,"type":0},{"id":0,"sLen":0,"time":22247.191011236,"type":0},{"id":3,"sLen":0,"time":22584.2696629213,"type":0},{"id":0,"sLen":0,"time":22921.3483146067,"type":0},{"id":0,"sLen":0,"time":23258.4269662921,"type":0},{"id":3,"sLen":0,"time":23595.5056179775,"type":0},{"id":3,"sLen":0,"time":23932.5842696629,"type":0},{"id":0,"sLen":0,"time":24269.6629213483,"type":0},{"id":3,"sLen":0,"time":24606.7415730337,"type":0},{"id":0,"sLen":0,"time":24943.8202247191,"type":0},{"id":3,"sLen":0,"time":25280.8988764045,"type":0},{"id":0,"sLen":0,"time":25617.9775280899,"type":0},{"id":0,"sLen":0,"time":25955.0561797753,"type":0},{"id":3,"sLen":0,"time":26292.1348314607,"type":0},{"id":3,"sLen":0,"time":26629.2134831461,"type":0},{"id":3,"sLen":0,"time":26713.4831460674,"type":0},{"id":3,"sLen":0,"time":26797.7528089888,"type":0},{"id":3,"sLen":0,"time":26966.2921348315,"type":0},{"id":0,"sLen":0,"time":27303.3707865169,"type":0},{"id":0,"sLen":0,"time":27640.4494382022,"type":0},{"id":3,"sLen":0,"time":27977.5280898876,"type":0},{"id":3,"sLen":0,"time":28314.606741573,"type":0},{"id":0,"sLen":0,"time":28651.6853932584,"type":0},{"id":3,"sLen":0,"time":28988.7640449438,"type":0},{"id":0,"sLen":0,"time":29325.8426966292,"type":0},{"id":0,"sLen":0,"time":29662.9213483146,"type":0},{"id":0,"sLen":0,"time":29831.4606741573,"type":0},{"id":0,"sLen":0,"time":30000,"type":0},{"id":3,"sLen":0,"time":30168.5393258427,"type":0},{"id":3,"sLen":0,"time":30337.0786516854,"type":0},{"id":3,"sLen":0,"time":30505.6179775281,"type":0},{"id":3,"sLen":0,"time":30674.1573033708,"type":0},{"id":0,"sLen":0,"time":30842.6966292135,"type":0},{"id":0,"sLen":0,"time":31011.2359550562,"type":0},{"id":3,"sLen":0,"time":31348.3146067416,"type":0},{"id":3,"sLen":0,"time":31685.393258427,"type":0},{"id":0,"sLen":0,"time":31853.9325842697,"type":0},{"id":0,"sLen":0,"time":32359.5505617978,"type":0},{"id":3,"sLen":0,"time":32696.6292134831,"type":0},{"id":0,"sLen":0,"time":33033.7078651685,"type":0},{"id":0,"sLen":0,"time":33202.2471910112,"type":0},{"id":3,"sLen":0,"time":33370.7865168539,"type":0},{"id":0,"sLen":0,"time":33707.8651685393,"type":0},{"id":3,"sLen":0,"time":34044.9438202247,"type":0},{"id":3,"sLen":0,"time":34382.0224719101,"type":0},{"id":0,"sLen":0,"time":34550.5617977528,"type":0},{"id":3,"sLen":0,"time":34719.1011235955,"type":0},{"id":0,"sLen":0,"time":35056.1797752809,"type":0},{"id":3,"sLen":0,"time":35393.2584269663,"type":0},{"id":0,"sLen":0,"time":35730.3370786517,"type":0},{"id":0,"sLen":0,"time":35898.8764044944,"type":0},{"id":3,"sLen":0,"time":36067.4157303371,"type":0},{"id":0,"sLen":0,"time":36404.4943820225,"type":0},{"id":3,"sLen":0,"time":36741.5730337079,"type":0},{"id":3,"sLen":0,"time":37078.6516853933,"type":0},{"id":0,"sLen":0,"time":37247.191011236,"type":0},{"id":3,"sLen":0,"time":37415.7303370787,"type":0},{"id":0,"sLen":0,"time":37752.808988764,"type":0},{"id":3,"sLen":0,"time":38089.8876404494,"type":0},{"id":3,"sLen":0,"time":38426.9662921348,"type":0},{"id":0,"sLen":0,"time":38595.5056179775,"type":0},{"id":3,"sLen":0,"time":38764.0449438202,"type":0},{"id":0,"sLen":0,"time":39101.1235955056,"type":0},{"id":3,"sLen":0,"time":39438.202247191,"type":0},{"id":3,"sLen":0,"time":39775.2808988764,"type":0},{"id":0,"sLen":0,"time":39943.8202247191,"type":0},{"id":3,"sLen":0,"time":40112.3595505618,"type":0},{"id":0,"sLen":0,"time":40449.4382022472,"type":0},{"id":3,"sLen":0,"time":40786.5168539326,"type":0},{"id":3,"sLen":0,"time":41123.595505618,"type":0},{"id":0,"sLen":0,"time":41292.1348314607,"type":0},{"id":3,"sLen":0,"time":41460.6741573034,"type":0},{"id":0,"sLen":0,"time":41797.7528089888,"type":0},{"id":0,"sLen":0,"time":42134.8314606742,"type":0},{"id":0,"sLen":0,"time":42471.9101123596,"type":0},{"id":0,"sLen":0,"time":42640.4494382022,"type":0},{"id":3,"sLen":0,"time":42808.9887640449,"type":0},{"id":3,"sLen":0,"time":42893.2584269663,"type":0},{"id":3,"sLen":0,"time":42977.5280898876,"type":0},{"id":0,"sLen":0,"time":43146.0674157303,"type":0},{"id":3,"sLen":0,"time":43483.1460674157,"type":0},{"id":3,"sLen":0,"time":43820.2247191011,"type":0},{"id":0,"sLen":0,"time":43988.7640449438,"type":0},{"id":3,"sLen":0,"time":44157.3033707865,"type":0},{"id":0,"sLen":0,"time":44494.3820224719,"type":0},{"id":3,"sLen":0,"time":44831.4606741573,"type":0},{"id":0,"sLen":0,"time":45168.5393258427,"type":0},{"id":0,"sLen":0,"time":45337.0786516854,"type":0},{"id":3,"sLen":0,"time":45505.6179775281,"type":0},{"id":0,"sLen":0,"time":45842.6966292135,"type":0},{"id":3,"sLen":0,"time":46179.7752808989,"type":0},{"id":3,"sLen":0,"time":46516.8539325843,"type":0},{"id":0,"sLen":0,"time":46685.393258427,"type":0},{"id":3,"sLen":0,"time":46853.9325842697,"type":0},{"id":0,"sLen":0,"time":47191.0112359551,"type":0},{"id":3,"sLen":0,"time":47528.0898876404,"type":0},{"id":3,"sLen":0,"time":47865.1685393258,"type":0},{"id":0,"sLen":0,"time":48033.7078651685,"type":0},{"id":3,"sLen":0,"time":48202.2471910112,"type":0},{"id":0,"sLen":0,"time":48539.3258426966,"type":0},{"id":3,"sLen":0,"time":48876.404494382,"type":0},{"id":3,"sLen":0,"time":49213.4831460674,"type":0},{"id":0,"sLen":0,"time":49382.0224719101,"type":0},{"id":3,"sLen":0,"time":49550.5617977528,"type":0},{"id":0,"sLen":0,"time":49887.6404494382,"type":0},{"id":3,"sLen":0,"time":50224.7191011236,"type":0},{"id":0,"sLen":0,"time":50561.797752809,"type":0},{"id":0,"sLen":0,"time":50730.3370786517,"type":0},{"id":3,"sLen":0,"time":50898.8764044944,"type":0},{"id":0,"sLen":0,"time":51235.9550561798,"type":0},{"id":3,"sLen":0,"time":51573.0337078652,"type":0},{"id":3,"sLen":0,"time":51910.1123595506,"type":0},{"id":0,"sLen":0,"time":52078.6516853933,"type":0},{"id":3,"sLen":0,"time":52247.191011236,"type":0},{"id":0,"sLen":0,"time":52584.2696629213,"type":0},{"id":0,"sLen":0,"time":52921.3483146067,"type":0},{"id":0,"sLen":0,"time":53258.4269662921,"type":0},{"id":0,"sLen":0,"time":53426.9662921348,"type":0},{"id":3,"sLen":0,"time":53595.5056179775,"type":0},{"id":3,"sLen":0,"time":53679.7752808989,"type":0},{"id":3,"sLen":0,"time":53764.0449438202,"type":0},{"id":0,"sLen":0,"time":53932.5842696629,"type":0},{"id":3,"sLen":0,"time":54101.1235955056,"type":0},{"id":3,"sLen":0,"time":54185.393258427,"type":0},{"id":3,"sLen":0,"time":54353.9325842697,"type":0},{"id":0,"sLen":0,"time":54522.4719101124,"type":0},{"id":3,"sLen":0,"time":54691.0112359551,"type":0},{"id":3,"sLen":0,"time":54775.2808988764,"type":0},{"id":0,"sLen":0,"time":54943.8202247191,"type":0},{"id":0,"sLen":0,"time":55112.3595505618,"type":0},{"id":0,"sLen":0,"time":55196.6292134831,"type":0},{"id":3,"sLen":0,"time":55280.8988764045,"type":0},{"id":0,"sLen":0,"time":55449.4382022472,"type":0},{"id":0,"sLen":0,"time":55533.7078651685,"type":0},{"id":3,"sLen":0,"time":55786.5168539326,"type":0},{"id":0,"sLen":0,"time":55955.0561797753,"type":0},{"id":3,"sLen":0,"time":56039.3258426966,"type":0},{"id":3,"sLen":0,"time":56123.595505618,"type":0},{"id":0,"sLen":0,"time":56292.1348314607,"type":0},{"id":0,"sLen":0,"time":56629.2134831461,"type":0},{"id":3,"sLen":0,"time":56797.7528089888,"type":0},{"id":3,"sLen":0,"time":56882.0224719101,"type":0},{"id":3,"sLen":0,"time":57050.5617977528,"type":0},{"id":0,"sLen":0,"time":57219.1011235955,"type":0},{"id":3,"sLen":0,"time":57387.6404494382,"type":0},{"id":3,"sLen":0,"time":57471.9101123595,"type":0},{"id":0,"sLen":0,"time":57640.4494382022,"type":0},{"id":0,"sLen":0,"time":57808.9887640449,"type":0},{"id":0,"sLen":0,"time":57893.2584269663,"type":0},{"id":3,"sLen":0,"time":57977.5280898876,"type":0},{"id":0,"sLen":0,"time":58146.0674157303,"type":0},{"id":0,"sLen":0,"time":58230.3370786517,"type":0},{"id":3,"sLen":0,"time":58483.1460674157,"type":0},{"id":0,"sLen":0,"time":58651.6853932584,"type":0},{"id":3,"sLen":0,"time":58735.9550561798,"type":0},{"id":3,"sLen":0,"time":58820.2247191011,"type":0},{"id":0,"sLen":0,"time":58988.7640449438,"type":0},{"id":0,"sLen":0,"time":59073.0337078652,"type":0},{"id":0,"sLen":0,"time":59157.3033707865,"type":0},{"id":0,"sLen":0,"time":59325.8426966292,"type":0},{"id":3,"sLen":0,"time":59494.3820224719,"type":0},{"id":3,"sLen":0,"time":59578.6516853933,"type":0},{"id":3,"sLen":0,"time":59747.191011236,"type":0},{"id":0,"sLen":0,"time":59915.7303370786,"type":0},{"id":3,"sLen":0,"time":60084.2696629213,"type":0},{"id":3,"sLen":0,"time":60168.5393258427,"type":0},{"id":0,"sLen":0,"time":60337.0786516854,"type":0},{"id":0,"sLen":0,"time":60505.6179775281,"type":0},{"id":0,"sLen":0,"time":60589.8876404494,"type":0},{"id":3,"sLen":0,"time":60674.1573033708,"type":0},{"id":0,"sLen":0,"time":60842.6966292135,"type":0},{"id":0,"sLen":0,"time":60926.9662921348,"type":0},{"id":3,"sLen":0,"time":61179.7752808989,"type":0},{"id":0,"sLen":0,"time":61348.3146067416,"type":0},{"id":3,"sLen":0,"time":61432.5842696629,"type":0},{"id":3,"sLen":0,"time":61516.8539325843,"type":0},{"id":0,"sLen":0,"time":61685.393258427,"type":0},{"id":0,"sLen":0,"time":62022.4719101124,"type":0},{"id":3,"sLen":0,"time":62191.0112359551,"type":0},{"id":3,"sLen":0,"time":62275.2808988764,"type":0},{"id":3,"sLen":0,"time":62443.8202247191,"type":0},{"id":0,"sLen":0,"time":62612.3595505618,"type":0},{"id":3,"sLen":0,"time":62780.8988764045,"type":0},{"id":3,"sLen":0,"time":62865.1685393258,"type":0},{"id":0,"sLen":0,"time":63033.7078651685,"type":0},{"id":0,"sLen":0,"time":63202.2471910112,"type":0},{"id":0,"sLen":0,"time":63286.5168539326,"type":0},{"id":3,"sLen":0,"time":63370.7865168539,"type":0},{"id":0,"sLen":0,"time":63539.3258426966,"type":0},{"id":0,"sLen":0,"time":63623.595505618,"type":0},{"id":3,"sLen":0,"time":63876.404494382,"type":0},{"id":0,"sLen":0,"time":64044.9438202247,"type":0},{"id":3,"sLen":0,"time":64129.2134831461,"type":0},{"id":3,"sLen":0,"time":64213.4831460674,"type":0},{"id":0,"sLen":0,"time":64382.0224719101,"type":0},{"id":3,"sLen":0,"time":64719.1011235955,"type":0},{"id":0,"sLen":0,"time":65056.1797752809,"type":0},{"id":0,"sLen":0,"time":65393.2584269663,"type":0},{"id":0,"sLen":0,"time":65730.3370786517,"type":0},{"id":0,"sLen":0,"time":66067.4157303371,"type":0},{"id":3,"sLen":0,"time":66404.4943820225,"type":0},{"id":3,"sLen":0,"time":66741.5730337079,"type":0},{"id":3,"sLen":0,"time":67078.6516853933,"type":0},{"id":3,"sLen":0,"time":67415.7303370787,"type":0},{"id":0,"sLen":0,"time":67752.808988764,"type":0},{"id":0,"sLen":0,"time":68089.8876404494,"type":0},{"id":0,"sLen":0,"time":68426.9662921348,"type":0},{"id":0,"sLen":0,"time":68764.0449438202,"type":0},{"id":3,"sLen":0,"time":69101.1235955056,"type":0},{"id":3,"sLen":0,"time":69438.202247191,"type":0},{"id":3,"sLen":0,"time":69775.2808988764,"type":0},{"id":3,"sLen":0,"time":69859.5505617978,"type":0},{"id":3,"sLen":0,"time":69943.8202247191,"type":0},{"id":3,"sLen":0,"time":70112.3595505618,"type":0},{"id":0,"sLen":0,"time":70449.4382022472,"type":0},{"id":0,"sLen":0,"time":70786.5168539326,"type":0},{"id":0,"sLen":0,"time":71123.595505618,"type":0},{"id":0,"sLen":0,"time":71460.6741573034,"type":0},{"id":3,"sLen":0,"time":71797.7528089888,"type":0},{"id":3,"sLen":0,"time":72134.8314606741,"type":0},{"id":3,"sLen":0,"time":72471.9101123595,"type":0},{"id":3,"sLen":0,"time":72556.1797752809,"type":0},{"id":3,"sLen":0,"time":72640.4494382023,"type":0},{"id":3,"sLen":0,"time":72808.9887640449,"type":0},{"id":3,"sLen":0,"time":72893.2584269663,"type":0},{"id":3,"sLen":0,"time":72977.5280898877,"type":0},{"id":0,"sLen":0,"time":73146.0674157303,"type":0},{"id":0,"sLen":0,"time":73230.3370786517,"type":0},{"id":0,"sLen":0,"time":73314.6067415731,"type":0},{"id":0,"sLen":0,"time":73483.1460674157,"type":0},{"id":0,"sLen":0,"time":73567.4157303371,"type":0},{"id":0,"sLen":0,"time":73651.6853932585,"type":0},{"id":0,"sLen":0,"time":73820.2247191011,"type":0},{"id":0,"sLen":0,"time":73904.4943820225,"type":0},{"id":0,"sLen":0,"time":73988.7640449438,"type":0},{"id":0,"sLen":0,"time":74157.3033707866,"type":0},{"id":0,"sLen":0,"time":74241.5730337079,"type":0},{"id":0,"sLen":0,"time":74325.8426966292,"type":0},{"id":0,"sLen":0,"time":74410.1123595506,"type":0},{"id":3,"sLen":0,"time":74494.382022472,"type":0},{"id":3,"sLen":0,"time":74578.6516853933,"type":0},{"id":3,"sLen":0,"time":74662.9213483146,"type":0},{"id":3,"sLen":0,"time":74747.191011236,"type":0},{"id":0,"sLen":0,"time":74831.4606741573,"type":0},{"id":0,"sLen":0,"time":74915.7303370787,"type":0},{"id":0,"sLen":0,"time":75000,"type":0},{"id":0,"sLen":0,"time":75084.2696629214,"type":0},{"id":3,"sLen":0,"time":75168.5393258427,"type":0},{"id":0,"sLen":0,"time":75505.6179775281,"type":0},{"id":3,"sLen":0,"time":75842.6966292135,"type":0},{"id":0,"sLen":0,"time":76179.7752808989,"type":0},{"id":0,"sLen":0,"time":76516.8539325843,"type":0},{"id":3,"sLen":0,"time":76853.9325842697,"type":0},{"id":3,"sLen":0,"time":77191.0112359551,"type":0},{"id":0,"sLen":0,"time":77528.0898876405,"type":0},{"id":3,"sLen":0,"time":77865.1685393259,"type":0},{"id":3,"sLen":0,"time":78033.7078651686,"type":0},{"id":0,"sLen":0,"time":78202.2471910113,"type":0},{"id":0,"sLen":0,"time":78539.3258426967,"type":0},{"id":3,"sLen":0,"time":78876.4044943821,"type":0},{"id":0,"sLen":0,"time":79213.4831460675,"type":0},{"id":0,"sLen":0,"time":79550.5617977529,"type":0},{"id":3,"sLen":0,"time":79887.6404494383,"type":0},{"id":0,"sLen":0,"time":80140.4494382023,"type":0},{"id":0,"sLen":0,"time":80224.7191011237,"type":0},{"id":0,"sLen":0,"time":80308.988764045,"type":0},{"id":0,"sLen":0,"time":80477.5280898877,"type":0},{"id":3,"sLen":84.269662921346,"time":80730.3370786517,"type":0},{"id":0,"sLen":0,"time":80898.8764044945,"type":0},{"id":3,"sLen":0,"time":81067.4157303371,"type":0},{"id":0,"sLen":0,"time":81235.9550561799,"type":0},{"id":3,"sLen":0,"time":81404.4943820225,"type":0},{"id":0,"sLen":0,"time":81573.0337078652,"type":0},{"id":3,"sLen":0,"time":81741.5730337079,"type":0},{"id":0,"sLen":0,"time":81910.1123595506,"type":0},{"id":3,"sLen":0,"time":82078.6516853933,"type":0},{"id":3,"sLen":0,"time":82247.191011236,"type":0},{"id":0,"sLen":0,"time":82415.7303370787,"type":0},{"id":3,"sLen":0,"time":82584.2696629214,"type":0},{"id":0,"sLen":0,"time":82752.8089887641,"type":0},{"id":3,"sLen":0,"time":82921.3483146068,"type":0},{"id":0,"sLen":0,"time":83089.8876404495,"type":0},{"id":3,"sLen":0,"time":83258.4269662922,"type":0},{"id":3,"sLen":0,"time":83426.9662921349,"type":0},{"id":0,"sLen":0,"time":83595.5056179776,"type":0},{"id":0,"sLen":0,"time":83764.0449438203,"type":0},{"id":0,"sLen":0,"time":83932.584269663,"type":0},{"id":3,"sLen":0,"time":84269.6629213484,"type":0},{"id":3,"sLen":0,"time":84606.7415730338,"type":0},{"id":3,"sLen":0,"time":84775.2808988765,"type":0},{"id":3,"sLen":0,"time":84943.8202247192,"type":0},{"id":3,"sLen":0,"time":85280.8988764046,"type":0},{"id":0,"sLen":0,"time":85617.97752809,"type":0},{"id":0,"sLen":0,"time":85702.2471910113,"type":0},{"id":3,"sLen":0,"time":85870.786516854,"type":0},{"id":0,"sLen":0,"time":86039.3258426967,"type":0},{"id":0,"sLen":0,"time":86292.1348314607,"type":0},{"id":3,"sLen":0,"time":86629.2134831461,"type":0},{"id":0,"sLen":0,"time":87134.8314606741,"type":0},{"id":3,"sLen":0,"time":87303.3707865169,"type":0},{"id":0,"sLen":0,"time":87640.4494382023,"type":0},{"id":3,"sLen":0,"time":87977.5280898877,"type":0},{"id":0,"sLen":0,"time":88483.1460674157,"type":0},{"id":3,"sLen":0,"time":88651.6853932584,"type":0},{"id":0,"sLen":0,"time":88988.7640449438,"type":0},{"id":3,"sLen":0,"time":89325.8426966292,"type":0},{"id":0,"sLen":0,"time":89831.4606741573,"type":0},{"id":3,"sLen":0,"time":90000,"type":0},{"id":0,"sLen":0,"time":90337.0786516854,"type":0},{"id":3,"sLen":0,"time":90674.1573033708,"type":0},{"id":0,"sLen":0,"time":91179.7752808989,"type":0},{"id":3,"sLen":0,"time":91348.3146067416,"type":0},{"id":0,"sLen":0,"time":91685.393258427,"type":0},{"id":3,"sLen":0,"time":92022.4719101124,"type":0},{"id":0,"sLen":0,"time":92528.0898876405,"type":0},{"id":3,"sLen":0,"time":92696.6292134832,"type":0},{"id":0,"sLen":0,"time":93033.7078651686,"type":0},{"id":3,"sLen":0,"time":93370.786516854,"type":0},{"id":0,"sLen":0,"time":93876.404494382,"type":0},{"id":3,"sLen":0,"time":94044.9438202248,"type":0},{"id":0,"sLen":0,"time":94382.0224719102,"type":0},{"id":3,"sLen":0,"time":94719.1011235956,"type":0},{"id":0,"sLen":0,"time":95224.7191011236,"type":0},{"id":3,"sLen":0,"time":95393.2584269663,"type":0},{"id":0,"sLen":0,"time":95730.3370786517,"type":0},{"id":0,"sLen":0,"time":96067.4157303371,"type":0},{"id":0,"sLen":0,"time":96404.4943820225,"type":0},{"id":0,"sLen":0,"time":96573.0337078652,"type":0},{"id":3,"sLen":0,"time":96741.5730337079,"type":0},{"id":3,"sLen":0,"time":96825.8426966292,"type":0},{"id":3,"sLen":0,"time":96910.1123595505,"type":0},{"id":0,"sLen":0,"time":97078.6516853933,"type":0},{"id":3,"sLen":0,"time":97415.7303370787,"type":0},{"id":3,"sLen":0,"time":97752.808988764,"type":0},{"id":0,"sLen":0,"time":97921.3483146067,"type":0},{"id":3,"sLen":0,"time":98089.8876404494,"type":0},{"id":0,"sLen":0,"time":98426.9662921348,"type":0},{"id":3,"sLen":0,"time":98764.0449438202,"type":0},{"id":0,"sLen":0,"time":99101.1235955056,"type":0},{"id":0,"sLen":0,"time":99269.6629213483,"type":0},{"id":3,"sLen":0,"time":99438.202247191,"type":0},{"id":0,"sLen":0,"time":99775.2808988764,"type":0},{"id":3,"sLen":0,"time":100112.359550562,"type":0},{"id":3,"sLen":0,"time":100449.438202247,"type":0},{"id":0,"sLen":0,"time":100617.97752809,"type":0},{"id":3,"sLen":0,"time":100786.516853933,"type":0},{"id":0,"sLen":0,"time":101123.595505618,"type":0},{"id":3,"sLen":0,"time":101460.674157303,"type":0},{"id":3,"sLen":0,"time":101797.752808989,"type":0},{"id":0,"sLen":0,"time":101966.292134831,"type":0},{"id":3,"sLen":0,"time":102134.831460674,"type":0},{"id":0,"sLen":0,"time":102471.91011236,"type":0},{"id":3,"sLen":0,"time":102808.988764045,"type":0},{"id":3,"sLen":0,"time":103146.06741573,"type":0},{"id":0,"sLen":0,"time":103314.606741573,"type":0},{"id":3,"sLen":0,"time":103483.146067416,"type":0},{"id":0,"sLen":0,"time":103820.224719101,"type":0},{"id":3,"sLen":0,"time":104157.303370787,"type":0},{"id":0,"sLen":0,"time":104494.382022472,"type":0},{"id":0,"sLen":0,"time":104662.921348315,"type":0},{"id":3,"sLen":0,"time":104831.460674157,"type":0},{"id":0,"sLen":0,"time":105168.539325843,"type":0},{"id":3,"sLen":0,"time":105505.617977528,"type":0},{"id":3,"sLen":0,"time":105842.696629213,"type":0},{"id":0,"sLen":0,"time":106011.235955056,"type":0},{"id":3,"sLen":0,"time":106179.775280899,"type":0},{"id":0,"sLen":0,"time":106516.853932584,"type":0},{"id":0,"sLen":0,"time":106853.93258427,"type":0},{"id":0,"sLen":0,"time":107191.011235955,"type":0},{"id":3,"sLen":0,"time":107528.08988764,"type":0},{"id":0,"sLen":0,"time":107865.168539326,"type":0},{"id":0,"sLen":0,"time":108202.247191011,"type":0},{"id":0,"sLen":0,"time":108539.325842697,"type":0},{"id":0,"sLen":0,"time":108876.404494382,"type":0},{"id":3,"sLen":0,"time":109213.483146068,"type":0},{"id":3,"sLen":0,"time":109550.561797753,"type":0},{"id":3,"sLen":0,"time":109887.640449438,"type":0},{"id":3,"sLen":0,"time":110224.719101124,"type":0},{"id":0,"sLen":0,"time":110561.797752809,"type":0},{"id":0,"sLen":0,"time":110730.337078652,"type":0},{"id":0,"sLen":0,"time":110898.876404495,"type":0},{"id":3,"sLen":0,"time":111235.95505618,"type":0},{"id":3,"sLen":0,"time":111573.033707865,"type":0},{"id":3,"sLen":0,"time":111910.112359551,"type":0},{"id":3,"sLen":0,"time":112247.191011236,"type":0},{"id":0,"sLen":0,"time":112584.269662922,"type":0},{"id":3,"sLen":0,"time":112921.348314607,"type":0},{"id":0,"sLen":0,"time":113258.426966292,"type":0},{"id":3,"sLen":0,"time":113932.584269663,"type":0},{"id":0,"sLen":0,"time":114269.662921349,"type":0},{"id":3,"sLen":0,"time":114438.202247191,"type":0},{"id":0,"sLen":0,"time":114606.741573034,"type":0},{"id":3,"sLen":0,"time":114943.820224719,"type":0},{"id":0,"sLen":0,"time":115280.898876405,"type":0},{"id":3,"sLen":0,"time":115617.97752809,"type":0},{"id":3,"sLen":0,"time":115786.516853933,"type":0},{"id":0,"sLen":0,"time":115955.056179776,"type":0},{"id":3,"sLen":0,"time":116123.595505618,"type":0},{"id":0,"sLen":0,"time":116292.134831461,"type":0},{"id":0,"sLen":0,"time":116629.213483146,"type":0},{"id":3,"sLen":0,"time":116966.292134832,"type":0},{"id":3,"sLen":0,"time":117303.370786517,"type":0},{"id":0,"sLen":0,"time":117640.449438203,"type":0},{"id":3,"sLen":0,"time":117977.528089888,"type":0},{"id":3,"sLen":0,"time":118061.797752809,"type":0},{"id":0,"sLen":0,"time":118314.606741573,"type":0},{"id":3,"sLen":0,"time":118398.876404495,"type":0},{"id":0,"sLen":0,"time":118651.685393258,"type":0},{"id":3,"sLen":0,"time":118820.224719101,"type":0},{"id":3,"sLen":0,"time":118904.494382022,"type":0},{"id":3,"sLen":0,"time":119073.033707865,"type":0},{"id":0,"sLen":0,"time":119241.573033708,"type":0},{"id":3,"sLen":0,"time":119410.112359551,"type":0},{"id":3,"sLen":0,"time":119494.382022472,"type":0},{"id":0,"sLen":0,"time":119662.921348315,"type":0},{"id":0,"sLen":0,"time":119831.460674157,"type":0},{"id":0,"sLen":0,"time":119915.730337079,"type":0},{"id":3,"sLen":0,"time":120000,"type":0},{"id":0,"sLen":0,"time":120168.539325843,"type":0},{"id":0,"sLen":0,"time":120252.808988764,"type":0},{"id":3,"sLen":0,"time":120505.617977528,"type":0},{"id":0,"sLen":0,"time":120674.157303371,"type":0},{"id":3,"sLen":0,"time":120758.426966292,"type":0},{"id":3,"sLen":0,"time":120842.696629213,"type":0},{"id":0,"sLen":0,"time":121011.235955056,"type":0},{"id":0,"sLen":0,"time":121095.505617978,"type":0},{"id":0,"sLen":0,"time":121179.775280899,"type":0}],"position":"girlfriend","type":2,"characters":["pico-speakers"],"visible":false}],"codenameChart":true,"stage":"tank","noteTypes":["No Anim Note"]} \ No newline at end of file +{"events":[{"params":[1],"time":5393.25842696629,"name":"Camera Movement"},{"params":[0],"time":10786.5168539326,"name":"Camera Movement"},{"params":[1],"time":14831.4606741573,"name":"Camera Movement"},{"params":[0],"time":21573.0337078652,"name":"Camera Movement"},{"params":[1],"time":24269.6629213483,"name":"Camera Movement"},{"params":[0],"time":26966.2921348315,"name":"Camera Movement"},{"params":[1],"time":28314.606741573,"name":"Camera Movement"},{"params":[0],"time":31011.2359550562,"name":"Camera Movement"},{"params":[1],"time":37752.808988764,"name":"Camera Movement"},{"params":[0],"time":43146.0674157303,"name":"Camera Movement"},{"params":[1],"time":48539.3258426966,"name":"Camera Movement"},{"params":[0],"time":53932.5842696629,"name":"Camera Movement"},{"params":[1],"time":59325.8426966292,"name":"Camera Movement"},{"params":[0],"time":62022.4719101124,"name":"Camera Movement"},{"params":[1],"time":67415.7303370787,"name":"Camera Movement"},{"params":[0],"time":75505.6179775281,"name":"Camera Movement"},{"params":[1],"time":80898.8764044945,"name":"Camera Movement"},{"params":[0],"time":86292.1348314608,"name":"Camera Movement"},{"params":[1],"time":91685.3932584271,"name":"Camera Movement"},{"params":[0],"time":97078.6516853934,"name":"Camera Movement"},{"params":[1],"time":102471.91011236,"name":"Camera Movement"},{"params":[0],"time":107865.168539326,"name":"Camera Movement"},{"params":[1],"time":113258.426966292,"name":"Camera Movement"}],"codenameChart":true,"strumLines":[{"keyCount":4,"position":"dad","notes":[{"id":1,"sLen":0,"time":0,"type":0},{"id":2,"sLen":0,"time":168.539322,"type":0},{"id":0,"sLen":0,"time":337.078644,"type":0},{"id":0,"sLen":0,"time":505.617981,"type":0},{"id":2,"sLen":0,"time":674.1573,"type":0},{"id":3,"sLen":0,"time":842.696655,"type":0},{"id":1,"sLen":0,"time":1011.23596,"type":0},{"id":3,"sLen":0,"time":1179.77527,"type":0},{"id":0,"sLen":0,"time":1348.31458,"type":0},{"id":3,"sLen":0,"time":1516.85388,"type":0},{"id":2,"sLen":0,"time":1685.39331,"type":0},{"id":3,"sLen":0,"time":1853.93262,"type":0},{"id":1,"sLen":0,"time":2022.47192,"type":0},{"id":3,"sLen":0,"time":2191.01123,"type":0},{"id":0,"sLen":0,"time":2359.55054,"type":0},{"id":3,"sLen":0,"time":2528.08984,"type":0},{"id":1,"sLen":0,"time":2696.62915,"type":0},{"id":2,"sLen":0,"time":2865.16846,"type":0},{"id":0,"sLen":0,"time":3033.70776,"type":0},{"id":0,"sLen":0,"time":3202.247,"type":0},{"id":2,"sLen":0,"time":3370.78662,"type":0},{"id":3,"sLen":0,"time":3539.326,"type":0},{"id":1,"sLen":0,"time":3707.86523,"type":0},{"id":3,"sLen":0,"time":3876.40454,"type":0},{"id":0,"sLen":0,"time":4044.94385,"type":0},{"id":3,"sLen":0,"time":4213.483,"type":0},{"id":2,"sLen":0,"time":4382.02246,"type":0},{"id":3,"sLen":0,"time":4550.562,"type":0},{"id":1,"sLen":0,"time":4719.101,"type":0},{"id":3,"sLen":0,"time":4887.64063,"type":0},{"id":0,"sLen":0,"time":5056.17969,"type":0},{"id":3,"sLen":0,"time":5224.719,"type":0},{"id":0,"sLen":0,"time":11123.5957,"type":0},{"id":1,"sLen":0,"time":11292.1348,"type":0},{"id":3,"sLen":252.808989999994,"time":11460.6738,"type":0},{"id":1,"sLen":0,"time":11797.7529,"type":0},{"id":0,"sLen":0,"time":12134.8311,"type":0},{"id":0,"sLen":0,"time":12471.91,"type":0},{"id":0,"sLen":0,"time":12808.9883,"type":0},{"id":0,"sLen":0,"time":12977.5283,"type":0},{"id":2,"sLen":0,"time":13146.0674,"type":0},{"id":3,"sLen":0,"time":13230.3369,"type":0},{"id":2,"sLen":0,"time":13314.6064,"type":0},{"id":1,"sLen":0,"time":13483.1465,"type":0},{"id":3,"sLen":0,"time":13651.6855,"type":0},{"id":2,"sLen":0,"time":13820.2246,"type":0},{"id":1,"sLen":0,"time":13988.7637,"type":0},{"id":3,"sLen":0,"time":14157.3037,"type":0},{"id":2,"sLen":0,"time":14325.8428,"type":0},{"id":1,"sLen":0,"time":14494.3818,"type":0},{"id":3,"sLen":0,"time":14578.6514,"type":0},{"id":2,"sLen":0,"time":14662.9209,"type":0},{"id":0,"sLen":0,"time":14831.4609,"type":0},{"id":3,"sLen":0,"time":15000,"type":0},{"id":2,"sLen":0,"time":15168.5391,"type":0},{"id":3,"sLen":0,"time":15337.0791,"type":0},{"id":1,"sLen":0,"time":15505.6182,"type":0},{"id":3,"sLen":0,"time":15674.1572,"type":0},{"id":0,"sLen":0,"time":15842.6963,"type":0},{"id":3,"sLen":0,"time":16011.2363,"type":0},{"id":3,"sLen":0,"time":21573.0332,"type":0},{"id":2,"sLen":0,"time":21741.5723,"type":0},{"id":0,"sLen":0,"time":21910.1133,"type":0},{"id":2,"sLen":0,"time":22078.6523,"type":0},{"id":3,"sLen":0,"time":22247.1914,"type":0},{"id":2,"sLen":0,"time":22415.73,"type":0},{"id":0,"sLen":0,"time":22584.27,"type":0},{"id":2,"sLen":0,"time":22752.8086,"type":0},{"id":3,"sLen":0,"time":22921.3477,"type":0},{"id":2,"sLen":0,"time":23005.6172,"type":0},{"id":3,"sLen":0,"time":23089.8867,"type":0},{"id":3,"sLen":0,"time":23258.4277,"type":0},{"id":2,"sLen":0,"time":23426.9668,"type":0},{"id":3,"sLen":0,"time":23595.5059,"type":0},{"id":0,"sLen":0,"time":23764.0449,"type":0},{"id":1,"sLen":0,"time":23932.584,"type":0},{"id":0,"sLen":0,"time":24101.123,"type":0},{"id":2,"sLen":0,"time":24269.6621,"type":0},{"id":3,"sLen":0,"time":26966.293,"type":0},{"id":2,"sLen":0,"time":27134.832,"type":0},{"id":3,"sLen":0,"time":27303.3711,"type":0},{"id":0,"sLen":0,"time":27471.91,"type":0},{"id":3,"sLen":0,"time":27640.45,"type":0},{"id":2,"sLen":0,"time":27808.9883,"type":0},{"id":3,"sLen":0,"time":27977.5273,"type":0},{"id":0,"sLen":0,"time":28146.0684,"type":0},{"id":3,"sLen":0,"time":28314.6074,"type":0},{"id":2,"sLen":0,"time":28398.877,"type":0},{"id":3,"sLen":0,"time":28483.1465,"type":0},{"id":0,"sLen":0,"time":28651.6855,"type":0},{"id":3,"sLen":0,"time":28820.2246,"type":0},{"id":2,"sLen":0,"time":28988.7637,"type":0},{"id":0,"sLen":0,"time":29157.3027,"type":0},{"id":3,"sLen":0,"time":29325.8418,"type":0},{"id":1,"sLen":0,"time":29494.3828,"type":0},{"id":0,"sLen":0,"time":29662.9219,"type":0},{"id":0,"sLen":0,"time":30000,"type":0},{"id":0,"sLen":0,"time":30337.0781,"type":0},{"id":0,"sLen":0,"time":30674.1582,"type":0},{"id":0,"sLen":0,"time":31011.2363,"type":0},{"id":0,"sLen":0,"time":31348.3145,"type":0},{"id":0,"sLen":0,"time":31685.3926,"type":0},{"id":0,"sLen":0,"time":31853.9316,"type":0},{"id":0,"sLen":0,"time":32022.4727,"type":0},{"id":0,"sLen":0,"time":32191.0117,"type":0},{"id":2,"sLen":0,"time":32359.55,"type":0},{"id":3,"sLen":0,"time":32528.09,"type":0},{"id":1,"sLen":0,"time":32696.6289,"type":0},{"id":3,"sLen":0,"time":32865.168,"type":0},{"id":0,"sLen":0,"time":33033.707,"type":0},{"id":3,"sLen":0,"time":33202.2461,"type":0},{"id":1,"sLen":0,"time":33370.7852,"type":0},{"id":3,"sLen":0,"time":33539.3242,"type":0},{"id":3,"sLen":0,"time":33623.5938,"type":0},{"id":1,"sLen":0,"time":33707.8633,"type":0},{"id":3,"sLen":0,"time":33876.4063,"type":0},{"id":1,"sLen":0,"time":34044.9453,"type":0},{"id":2,"sLen":0,"time":34213.4844,"type":0},{"id":0,"sLen":0,"time":34382.0234,"type":0},{"id":2,"sLen":0,"time":34550.5625,"type":0},{"id":0,"sLen":0,"time":34719.1,"type":0},{"id":3,"sLen":0,"time":34887.64,"type":0},{"id":1,"sLen":0,"time":34971.91,"type":0},{"id":2,"sLen":0,"time":35056.18,"type":0},{"id":3,"sLen":0,"time":35224.72,"type":0},{"id":2,"sLen":0,"time":35393.2578,"type":0},{"id":3,"sLen":0,"time":35561.7969,"type":0},{"id":0,"sLen":0,"time":35730.3359,"type":0},{"id":1,"sLen":0,"time":35898.875,"type":0},{"id":0,"sLen":0,"time":36067.4141,"type":0},{"id":2,"sLen":0,"time":36151.6836,"type":0},{"id":0,"sLen":0,"time":36235.9531,"type":0},{"id":3,"sLen":0,"time":36404.4961,"type":0},{"id":1,"sLen":0,"time":36573.0352,"type":0},{"id":0,"sLen":0,"time":36741.5742,"type":0},{"id":1,"sLen":0,"time":36910.1133,"type":0},{"id":0,"sLen":0,"time":37078.6523,"type":0},{"id":1,"sLen":0,"time":37247.19,"type":0},{"id":1,"sLen":0,"time":37415.73,"type":0},{"id":3,"sLen":0,"time":37584.27,"type":0},{"id":3,"sLen":0,"time":37668.54,"type":0},{"id":3,"sLen":0,"time":43146.0664,"type":0},{"id":1,"sLen":0,"time":43230.3359,"type":0},{"id":3,"sLen":0,"time":43314.6055,"type":0},{"id":0,"sLen":0,"time":43483.1445,"type":0},{"id":3,"sLen":0,"time":43651.6836,"type":0},{"id":1,"sLen":0,"time":43735.9531,"type":0},{"id":2,"sLen":0,"time":43820.2266,"type":0},{"id":0,"sLen":0,"time":43988.7656,"type":0},{"id":1,"sLen":0,"time":44157.3047,"type":0},{"id":3,"sLen":0,"time":44325.8438,"type":0},{"id":3,"sLen":0,"time":44410.1133,"type":0},{"id":2,"sLen":0,"time":44494.3828,"type":0},{"id":3,"sLen":0,"time":44662.92,"type":0},{"id":0,"sLen":0,"time":44747.19,"type":0},{"id":1,"sLen":0,"time":44831.46,"type":0},{"id":0,"sLen":0,"time":45000,"type":0},{"id":2,"sLen":0,"time":45168.54,"type":0},{"id":3,"sLen":0,"time":45337.08,"type":0},{"id":1,"sLen":0,"time":45505.6172,"type":0},{"id":3,"sLen":0,"time":45674.1563,"type":0},{"id":2,"sLen":0,"time":45758.4258,"type":0},{"id":0,"sLen":0,"time":45842.6953,"type":0},{"id":3,"sLen":0,"time":46011.2344,"type":0},{"id":1,"sLen":0,"time":46179.7734,"type":0},{"id":3,"sLen":0,"time":46348.3164,"type":0},{"id":0,"sLen":0,"time":46516.8555,"type":0},{"id":1,"sLen":0,"time":46685.3945,"type":0},{"id":3,"sLen":0,"time":46853.9336,"type":0},{"id":2,"sLen":0,"time":46938.2031,"type":0},{"id":3,"sLen":0,"time":47022.4727,"type":0},{"id":1,"sLen":0,"time":47191.01,"type":0},{"id":3,"sLen":0,"time":47359.55,"type":0},{"id":0,"sLen":0,"time":47528.09,"type":0},{"id":3,"sLen":0,"time":47696.63,"type":0},{"id":1,"sLen":0,"time":47865.168,"type":0},{"id":3,"sLen":0,"time":48033.707,"type":0},{"id":2,"sLen":0,"time":48202.2461,"type":0},{"id":2,"sLen":0,"time":48370.7852,"type":0},{"id":1,"sLen":0,"time":53932.5859,"type":0},{"id":2,"sLen":0,"time":54101.125,"type":0},{"id":0,"sLen":0,"time":54269.6641,"type":0},{"id":0,"sLen":0,"time":54438.2031,"type":0},{"id":2,"sLen":0,"time":54606.7422,"type":0},{"id":3,"sLen":0,"time":54775.28,"type":0},{"id":1,"sLen":0,"time":54943.82,"type":0},{"id":3,"sLen":0,"time":55112.36,"type":0},{"id":0,"sLen":0,"time":55280.9,"type":0},{"id":1,"sLen":0,"time":55449.4375,"type":0},{"id":2,"sLen":0,"time":55617.9766,"type":0},{"id":3,"sLen":0,"time":55786.5156,"type":0},{"id":1,"sLen":0,"time":55955.0547,"type":0},{"id":3,"sLen":0,"time":56123.5938,"type":0},{"id":0,"sLen":0,"time":56292.1367,"type":0},{"id":3,"sLen":0,"time":56460.6758,"type":0},{"id":1,"sLen":0,"time":56629.2148,"type":0},{"id":2,"sLen":0,"time":56797.7539,"type":0},{"id":0,"sLen":0,"time":56966.293,"type":0},{"id":0,"sLen":0,"time":57134.832,"type":0},{"id":2,"sLen":0,"time":57303.37,"type":0},{"id":3,"sLen":0,"time":57471.91,"type":0},{"id":1,"sLen":0,"time":57640.45,"type":0},{"id":3,"sLen":0,"time":57808.99,"type":0},{"id":0,"sLen":0,"time":57977.5273,"type":0},{"id":2,"sLen":0,"time":58146.0664,"type":0},{"id":3,"sLen":0,"time":58230.3359,"type":0},{"id":1,"sLen":0,"time":58314.6055,"type":0},{"id":3,"sLen":0,"time":58483.1445,"type":0},{"id":1,"sLen":0,"time":58651.6836,"type":0},{"id":3,"sLen":0,"time":58820.2266,"type":0},{"id":0,"sLen":0,"time":58988.7656,"type":0},{"id":3,"sLen":0,"time":59157.3047,"type":0},{"id":0,"sLen":168.539321999997,"time":59325.8438,"type":0},{"id":0,"sLen":168.539321999997,"time":60000,"type":0},{"id":0,"sLen":168.539322000004,"time":60674.1563,"type":0},{"id":0,"sLen":168.539321999997,"time":61348.3164,"type":0},{"id":1,"sLen":0,"time":62022.4719101124,"type":1},{"id":1,"sLen":252.808989999998,"time":64719.1,"type":0},{"id":0,"sLen":0,"time":65056.18,"type":0},{"id":1,"sLen":0,"time":65224.72,"type":0},{"id":3,"sLen":252.808989999998,"time":65393.2578,"type":0},{"id":1,"sLen":0,"time":65730.3359,"type":0},{"id":0,"sLen":0,"time":66067.4141,"type":0},{"id":0,"sLen":0,"time":66404.49,"type":0},{"id":0,"sLen":0,"time":66741.57,"type":0},{"id":0,"sLen":0,"time":66910.11,"type":0},{"id":2,"sLen":0,"time":67078.65,"type":0},{"id":3,"sLen":0,"time":67162.92,"type":0},{"id":2,"sLen":0,"time":67247.19,"type":0},{"id":1,"sLen":0,"time":67415.73,"type":0},{"id":3,"sLen":0,"time":67584.27,"type":0},{"id":2,"sLen":0,"time":67752.81,"type":0},{"id":1,"sLen":0,"time":67921.35,"type":0},{"id":3,"sLen":0,"time":68089.89,"type":0},{"id":2,"sLen":0,"time":68258.43,"type":0},{"id":1,"sLen":0,"time":68426.97,"type":0},{"id":3,"sLen":0,"time":68511.2344,"type":0},{"id":2,"sLen":0,"time":68595.51,"type":0},{"id":0,"sLen":0,"time":68764.05,"type":0},{"id":3,"sLen":0,"time":68932.5859,"type":0},{"id":2,"sLen":0,"time":69101.125,"type":0},{"id":3,"sLen":0,"time":69269.6641,"type":0},{"id":1,"sLen":0,"time":69438.2,"type":0},{"id":3,"sLen":0,"time":69606.74,"type":0},{"id":0,"sLen":0,"time":69775.28,"type":0},{"id":3,"sLen":0,"time":69943.82,"type":0},{"id":0,"sLen":0,"time":75505.62,"type":0},{"id":0,"sLen":0,"time":75674.16,"type":0},{"id":1,"sLen":0,"time":75842.6953,"type":0},{"id":0,"sLen":0,"time":76011.2344,"type":0},{"id":2,"sLen":0,"time":76095.51,"type":0},{"id":0,"sLen":0,"time":76179.77,"type":0},{"id":1,"sLen":0,"time":76348.31,"type":0},{"id":3,"sLen":0,"time":76516.85,"type":0},{"id":0,"sLen":0,"time":76685.39,"type":0},{"id":1,"sLen":0,"time":76853.93,"type":0},{"id":3,"sLen":0,"time":76938.2,"type":0},{"id":1,"sLen":0,"time":77022.47,"type":0},{"id":0,"sLen":0,"time":77191.01,"type":0},{"id":1,"sLen":0,"time":77359.55,"type":0},{"id":2,"sLen":252.808990000005,"time":77528.0859,"type":0},{"id":0,"sLen":0,"time":77865.17,"type":0},{"id":0,"sLen":0,"time":78033.71,"type":0},{"id":1,"sLen":0,"time":78202.25,"type":0},{"id":0,"sLen":0,"time":78370.79,"type":0},{"id":3,"sLen":0,"time":78455.0547,"type":0},{"id":3,"sLen":0,"time":78539.33,"type":0},{"id":3,"sLen":0,"time":78707.87,"type":0},{"id":0,"sLen":0,"time":78792.13,"type":0},{"id":1,"sLen":0,"time":78876.41,"type":0},{"id":1,"sLen":0,"time":79044.9453,"type":0},{"id":3,"sLen":0,"time":79129.21,"type":0},{"id":2,"sLen":0,"time":79213.4844,"type":0},{"id":0,"sLen":0,"time":79382.02,"type":0},{"id":1,"sLen":0,"time":79466.29,"type":0},{"id":2,"sLen":0,"time":79550.56,"type":0},{"id":3,"sLen":0,"time":79634.83,"type":0},{"id":2,"sLen":0,"time":79719.1,"type":0},{"id":0,"sLen":84.2696600000054,"time":79803.37,"type":0},{"id":3,"sLen":0,"time":79971.91,"type":0},{"id":2,"sLen":0,"time":80056.18,"type":0},{"id":0,"sLen":0,"time":80224.72,"type":0},{"id":3,"sLen":0,"time":80393.26,"type":0},{"id":0,"sLen":0,"time":80561.8,"type":0},{"id":2,"sLen":0,"time":80730.3359,"type":0},{"id":1,"sLen":0,"time":86292.13,"type":0},{"id":0,"sLen":0,"time":86376.41,"type":0},{"id":3,"sLen":0,"time":86460.67,"type":0},{"id":2,"sLen":0,"time":86629.21,"type":0},{"id":1,"sLen":0,"time":86713.4844,"type":0},{"id":3,"sLen":0,"time":86797.75,"type":0},{"id":2,"sLen":0,"time":86966.29,"type":0},{"id":3,"sLen":0,"time":87134.83,"type":0},{"id":2,"sLen":0,"time":87303.37,"type":0},{"id":0,"sLen":0,"time":87471.91,"type":0},{"id":1,"sLen":0,"time":87640.45,"type":0},{"id":0,"sLen":0,"time":87724.72,"type":0},{"id":3,"sLen":0,"time":87808.99,"type":0},{"id":2,"sLen":0,"time":87977.53,"type":0},{"id":1,"sLen":0,"time":88061.8,"type":0},{"id":3,"sLen":0,"time":88146.07,"type":0},{"id":2,"sLen":0,"time":88314.61,"type":0},{"id":3,"sLen":0,"time":88483.15,"type":0},{"id":1,"sLen":0,"time":88651.69,"type":0},{"id":3,"sLen":0,"time":88820.23,"type":0},{"id":2,"sLen":0,"time":88988.7656,"type":0},{"id":3,"sLen":0,"time":89073.03,"type":0},{"id":2,"sLen":0,"time":89157.3047,"type":0},{"id":1,"sLen":0,"time":89325.84,"type":0},{"id":0,"sLen":0,"time":89410.11,"type":0},{"id":1,"sLen":0,"time":89494.38,"type":0},{"id":3,"sLen":0,"time":89662.92,"type":0},{"id":3,"sLen":0,"time":89831.46,"type":0},{"id":1,"sLen":0,"time":89915.73,"type":0},{"id":3,"sLen":0,"time":90000,"type":0},{"id":1,"sLen":0,"time":90168.54,"type":0},{"id":0,"sLen":0,"time":90337.08,"type":0},{"id":3,"sLen":0,"time":90421.35,"type":0},{"id":1,"sLen":0,"time":90505.62,"type":0},{"id":0,"sLen":0,"time":90589.89,"type":0},{"id":1,"sLen":0,"time":90674.16,"type":0},{"id":3,"sLen":0,"time":90842.6953,"type":0},{"id":0,"sLen":0,"time":91011.2344,"type":0},{"id":3,"sLen":0,"time":91095.51,"type":0},{"id":1,"sLen":0,"time":91179.77,"type":0},{"id":0,"sLen":0,"time":91264.05,"type":0},{"id":1,"sLen":168.539321999997,"time":91348.31,"type":0},{"id":0,"sLen":0,"time":97078.65,"type":0},{"id":3,"sLen":0,"time":97162.92,"type":0},{"id":2,"sLen":0,"time":97247.19,"type":0},{"id":3,"sLen":0,"time":97415.73,"type":0},{"id":0,"sLen":0,"time":97584.27,"type":0},{"id":3,"sLen":0,"time":97668.54,"type":0},{"id":2,"sLen":0,"time":97752.81,"type":0},{"id":3,"sLen":0,"time":97921.35,"type":0},{"id":0,"sLen":0,"time":98089.89,"type":0},{"id":3,"sLen":0,"time":98174.16,"type":0},{"id":2,"sLen":0,"time":98258.43,"type":0},{"id":3,"sLen":0,"time":98426.97,"type":0},{"id":0,"sLen":0,"time":98595.51,"type":0},{"id":3,"sLen":0,"time":98679.77,"type":0},{"id":2,"sLen":0,"time":98764.05,"type":0},{"id":3,"sLen":0,"time":98932.5859,"type":0},{"id":2,"sLen":0,"time":99101.125,"type":0},{"id":1,"sLen":0,"time":99269.6641,"type":0},{"id":0,"sLen":0,"time":99438.2,"type":0},{"id":3,"sLen":0,"time":99606.74,"type":0},{"id":0,"sLen":0,"time":99775.28,"type":0},{"id":3,"sLen":0,"time":99859.55,"type":0},{"id":2,"sLen":0,"time":99943.82,"type":0},{"id":3,"sLen":0,"time":100112.359,"type":0},{"id":0,"sLen":0,"time":100280.9,"type":0},{"id":3,"sLen":0,"time":100365.172,"type":0},{"id":2,"sLen":0,"time":100449.438,"type":0},{"id":3,"sLen":0,"time":100617.977,"type":0},{"id":0,"sLen":0,"time":100786.516,"type":0},{"id":3,"sLen":0,"time":100870.789,"type":0},{"id":2,"sLen":0,"time":100955.055,"type":0},{"id":3,"sLen":0,"time":101123.594,"type":0},{"id":0,"sLen":0,"time":101292.133,"type":0},{"id":3,"sLen":0,"time":101376.406,"type":0},{"id":2,"sLen":0,"time":101460.672,"type":0},{"id":3,"sLen":0,"time":101629.211,"type":0},{"id":2,"sLen":0,"time":101797.75,"type":0},{"id":1,"sLen":0,"time":101966.289,"type":0},{"id":0,"sLen":0,"time":102134.828,"type":0},{"id":3,"sLen":0,"time":102303.367,"type":0},{"id":0,"sLen":0,"time":102471.906,"type":0},{"id":3,"sLen":0,"time":102556.18,"type":0},{"id":2,"sLen":0,"time":102640.453,"type":0},{"id":3,"sLen":0,"time":102808.992,"type":0},{"id":0,"sLen":0,"time":102977.531,"type":0},{"id":3,"sLen":0,"time":103061.8,"type":0},{"id":2,"sLen":0,"time":103146.07,"type":0},{"id":3,"sLen":0,"time":103314.609,"type":0},{"id":0,"sLen":0,"time":103483.148,"type":0},{"id":3,"sLen":0,"time":103567.414,"type":0},{"id":2,"sLen":0,"time":103651.688,"type":0},{"id":3,"sLen":0,"time":103820.227,"type":0},{"id":0,"sLen":0,"time":103988.766,"type":0},{"id":3,"sLen":0,"time":104073.031,"type":0},{"id":2,"sLen":0,"time":104157.305,"type":0},{"id":3,"sLen":0,"time":104325.844,"type":0},{"id":2,"sLen":0,"time":104494.383,"type":0},{"id":1,"sLen":0,"time":104662.922,"type":0},{"id":0,"sLen":0,"time":104831.461,"type":0},{"id":3,"sLen":0,"time":105000,"type":0},{"id":0,"sLen":0,"time":105168.539,"type":0},{"id":3,"sLen":0,"time":105252.813,"type":0},{"id":2,"sLen":0,"time":105337.078,"type":0},{"id":3,"sLen":0,"time":105505.617,"type":0},{"id":0,"sLen":0,"time":105674.156,"type":0},{"id":3,"sLen":0,"time":105758.43,"type":0},{"id":2,"sLen":0,"time":105842.695,"type":0},{"id":3,"sLen":0,"time":106011.234,"type":0},{"id":0,"sLen":0,"time":106179.773,"type":0},{"id":3,"sLen":0,"time":106264.047,"type":0},{"id":2,"sLen":0,"time":106348.313,"type":0},{"id":3,"sLen":0,"time":106516.852,"type":0},{"id":0,"sLen":0,"time":106685.391,"type":0},{"id":3,"sLen":0,"time":106769.664,"type":0},{"id":2,"sLen":0,"time":106853.93,"type":0},{"id":3,"sLen":0,"time":107022.469,"type":0},{"id":2,"sLen":0,"time":107191.008,"type":0},{"id":1,"sLen":0,"time":107359.547,"type":0},{"id":0,"sLen":0,"time":107528.086,"type":0},{"id":3,"sLen":0,"time":107696.633,"type":0},{"id":3,"sLen":0,"time":107865.172,"type":0},{"id":2,"sLen":0,"time":107949.438,"type":0},{"id":3,"sLen":0,"time":108033.711,"type":0},{"id":2,"sLen":0,"time":108117.977,"type":0},{"id":0,"sLen":0,"time":108202.25,"type":0},{"id":1,"sLen":0,"time":108370.789,"type":0},{"id":3,"sLen":0,"time":108539.328,"type":0},{"id":2,"sLen":0,"time":108623.594,"type":0},{"id":3,"sLen":0,"time":108707.867,"type":0},{"id":2,"sLen":0,"time":108792.133,"type":0},{"id":0,"sLen":0,"time":108876.406,"type":0},{"id":1,"sLen":0,"time":109044.945,"type":0},{"id":3,"sLen":0,"time":109213.484,"type":0},{"id":0,"sLen":0,"time":109297.75,"type":0},{"id":2,"sLen":0,"time":109382.023,"type":0},{"id":3,"sLen":0,"time":109466.289,"type":0},{"id":0,"sLen":0,"time":109550.563,"type":0},{"id":1,"sLen":0,"time":109719.1,"type":0},{"id":3,"sLen":0,"time":109887.641,"type":0},{"id":2,"sLen":0,"time":109971.906,"type":0},{"id":3,"sLen":0,"time":110056.18,"type":0},{"id":2,"sLen":0,"time":110140.453,"type":0},{"id":0,"sLen":0,"time":110224.719,"type":0},{"id":1,"sLen":0,"time":110393.258,"type":0},{"id":2,"sLen":0,"time":113258.43,"type":0},{"id":3,"sLen":0,"time":113426.969,"type":0},{"id":1,"sLen":0,"time":113595.508,"type":0},{"id":3,"sLen":0,"time":113764.047,"type":0},{"id":0,"sLen":0,"time":113932.586,"type":0},{"id":1,"sLen":0,"time":114101.125,"type":0},{"id":0,"sLen":0,"time":114269.664,"type":0},{"id":3,"sLen":0,"time":114353.93,"type":0},{"id":1,"sLen":0,"time":114438.2,"type":0},{"id":0,"sLen":0,"time":114522.469,"type":0},{"id":2,"sLen":0,"time":114606.742,"type":0},{"id":3,"sLen":0,"time":114775.281,"type":0},{"id":1,"sLen":0,"time":114943.82,"type":0},{"id":3,"sLen":0,"time":115112.359,"type":0},{"id":0,"sLen":0,"time":115280.9,"type":0},{"id":3,"sLen":0,"time":115365.172,"type":0},{"id":1,"sLen":0,"time":115449.438,"type":0},{"id":0,"sLen":0,"time":115533.711,"type":0},{"id":3,"sLen":0,"time":115617.977,"type":0},{"id":1,"sLen":0,"time":115786.516,"type":0},{"id":2,"sLen":589.887633999984,"time":115955.055,"type":0}],"type":0,"characters":["tankman"],"visible":true},{"keyCount":4,"position":"boyfriend","notes":[{"id":1,"sLen":0,"time":5393.2583,"type":0},{"id":2,"sLen":0,"time":5561.798,"type":0},{"id":0,"sLen":0,"time":5730.337,"type":0},{"id":0,"sLen":0,"time":5898.87646,"type":0},{"id":2,"sLen":0,"time":6067.41553,"type":0},{"id":3,"sLen":0,"time":6235.955,"type":0},{"id":1,"sLen":0,"time":6404.494,"type":0},{"id":3,"sLen":0,"time":6573.03369,"type":0},{"id":0,"sLen":0,"time":6741.573,"type":0},{"id":3,"sLen":0,"time":6910.1123,"type":0},{"id":2,"sLen":0,"time":7078.652,"type":0},{"id":3,"sLen":0,"time":7247.191,"type":0},{"id":1,"sLen":0,"time":7415.73047,"type":0},{"id":3,"sLen":0,"time":7584.26953,"type":0},{"id":0,"sLen":0,"time":7752.809,"type":0},{"id":3,"sLen":0,"time":7921.348,"type":0},{"id":1,"sLen":0,"time":8089.8877,"type":0},{"id":2,"sLen":0,"time":8258.427,"type":0},{"id":0,"sLen":0,"time":8426.966,"type":0},{"id":0,"sLen":0,"time":8595.506,"type":0},{"id":2,"sLen":0,"time":8764.045,"type":0},{"id":3,"sLen":0,"time":8932.584,"type":0},{"id":1,"sLen":0,"time":9101.124,"type":0},{"id":3,"sLen":0,"time":9269.663,"type":0},{"id":0,"sLen":0,"time":9438.202,"type":0},{"id":3,"sLen":0,"time":9606.741,"type":0},{"id":2,"sLen":0,"time":9775.281,"type":0},{"id":3,"sLen":0,"time":9943.82,"type":0},{"id":1,"sLen":0,"time":10112.3594,"type":0},{"id":3,"sLen":0,"time":10280.8984,"type":0},{"id":0,"sLen":0,"time":10449.4385,"type":0},{"id":3,"sLen":0,"time":10617.9775,"type":0},{"id":1,"sLen":252.808990000001,"time":14831.4609,"type":0},{"id":2,"sLen":252.80899,"time":15168.5391,"type":0},{"id":3,"sLen":252.80899,"time":15505.6182,"type":0},{"id":0,"sLen":252.808990000001,"time":15842.6963,"type":0},{"id":1,"sLen":252.80899,"time":16179.7754,"type":0},{"id":0,"sLen":0,"time":16516.8535,"type":0},{"id":1,"sLen":0,"time":16685.3926,"type":0},{"id":3,"sLen":252.808990000001,"time":16853.9316,"type":0},{"id":1,"sLen":0,"time":17191.0117,"type":0},{"id":0,"sLen":0,"time":17528.09,"type":0},{"id":0,"sLen":0,"time":17865.168,"type":0},{"id":0,"sLen":0,"time":18202.248,"type":0},{"id":0,"sLen":0,"time":18370.7871,"type":0},{"id":2,"sLen":0,"time":18539.3262,"type":0},{"id":3,"sLen":0,"time":18623.5957,"type":0},{"id":2,"sLen":0,"time":18707.8652,"type":0},{"id":1,"sLen":0,"time":18876.4043,"type":0},{"id":3,"sLen":0,"time":19044.9434,"type":0},{"id":2,"sLen":0,"time":19213.4824,"type":0},{"id":1,"sLen":0,"time":19382.0215,"type":0},{"id":3,"sLen":0,"time":19550.5625,"type":0},{"id":2,"sLen":0,"time":19719.1016,"type":0},{"id":1,"sLen":0,"time":19887.64,"type":0},{"id":3,"sLen":0,"time":19971.91,"type":0},{"id":2,"sLen":0,"time":20056.18,"type":0},{"id":0,"sLen":0,"time":20224.7188,"type":0},{"id":3,"sLen":0,"time":20393.2578,"type":0},{"id":2,"sLen":0,"time":20561.7969,"type":0},{"id":3,"sLen":0,"time":20730.3379,"type":0},{"id":1,"sLen":0,"time":20898.877,"type":0},{"id":3,"sLen":0,"time":21067.416,"type":0},{"id":0,"sLen":0,"time":21235.9551,"type":0},{"id":3,"sLen":0,"time":21404.4941,"type":0},{"id":1,"sLen":0,"time":21573.0332,"type":0},{"id":1,"sLen":0,"time":21910.1133,"type":0},{"id":1,"sLen":0,"time":22247.1914,"type":0},{"id":1,"sLen":0,"time":22584.27,"type":0},{"id":2,"sLen":0,"time":22921.3477,"type":0},{"id":2,"sLen":0,"time":23258.4277,"type":0},{"id":3,"sLen":252.808990000001,"time":23595.5059,"type":0},{"id":0,"sLen":252.808990000016,"time":23932.584,"type":0},{"id":3,"sLen":0,"time":24269.6621,"type":0},{"id":2,"sLen":0,"time":24438.2031,"type":0},{"id":0,"sLen":0,"time":24606.7422,"type":0},{"id":2,"sLen":0,"time":24775.2813,"type":0},{"id":3,"sLen":0,"time":24943.82,"type":0},{"id":2,"sLen":0,"time":25112.36,"type":0},{"id":0,"sLen":0,"time":25280.8984,"type":0},{"id":2,"sLen":0,"time":25449.4375,"type":0},{"id":3,"sLen":0,"time":25617.9766,"type":0},{"id":2,"sLen":0,"time":25702.248,"type":0},{"id":3,"sLen":0,"time":25786.5176,"type":0},{"id":3,"sLen":0,"time":25955.0566,"type":0},{"id":2,"sLen":0,"time":26123.5957,"type":0},{"id":3,"sLen":0,"time":26292.1348,"type":0},{"id":0,"sLen":0,"time":26460.6738,"type":0},{"id":1,"sLen":0,"time":26629.2129,"type":0},{"id":0,"sLen":0,"time":26797.752,"type":0},{"id":2,"sLen":505.617980999999,"time":26966.293,"type":0},{"id":3,"sLen":505.617980999985,"time":27640.45,"type":0},{"id":1,"sLen":505.617980999999,"time":28314.6074,"type":0},{"id":2,"sLen":505.617980999999,"time":28988.7637,"type":0},{"id":3,"sLen":0,"time":29662.9219,"type":0},{"id":2,"sLen":0,"time":29831.46,"type":0},{"id":3,"sLen":0,"time":30000,"type":0},{"id":0,"sLen":0,"time":30168.54,"type":0},{"id":3,"sLen":0,"time":30337.0781,"type":0},{"id":2,"sLen":0,"time":30505.6172,"type":0},{"id":3,"sLen":0,"time":30674.1582,"type":0},{"id":0,"sLen":0,"time":30842.6973,"type":0},{"id":3,"sLen":0,"time":31011.2363,"type":0},{"id":2,"sLen":0,"time":31095.5059,"type":0},{"id":3,"sLen":0,"time":31179.7754,"type":0},{"id":0,"sLen":0,"time":31348.3145,"type":0},{"id":3,"sLen":0,"time":31516.8535,"type":0},{"id":2,"sLen":0,"time":31685.3926,"type":0},{"id":0,"sLen":0,"time":31853.9316,"type":0},{"id":3,"sLen":0,"time":32022.4727,"type":0},{"id":1,"sLen":0,"time":32191.0117,"type":0},{"id":1,"sLen":674.157300000003,"time":32359.55,"type":0},{"id":2,"sLen":0,"time":37752.81,"type":0},{"id":3,"sLen":0,"time":37921.3477,"type":0},{"id":1,"sLen":0,"time":38089.8867,"type":0},{"id":3,"sLen":0,"time":38258.4258,"type":0},{"id":0,"sLen":0,"time":38426.9648,"type":0},{"id":3,"sLen":0,"time":38595.5039,"type":0},{"id":1,"sLen":0,"time":38764.043,"type":0},{"id":3,"sLen":0,"time":38932.5859,"type":0},{"id":3,"sLen":0,"time":39016.8555,"type":0},{"id":1,"sLen":0,"time":39101.125,"type":0},{"id":3,"sLen":0,"time":39269.6641,"type":0},{"id":1,"sLen":0,"time":39438.2031,"type":0},{"id":2,"sLen":0,"time":39606.7422,"type":0},{"id":0,"sLen":0,"time":39775.28,"type":0},{"id":2,"sLen":0,"time":39943.82,"type":0},{"id":0,"sLen":0,"time":40112.36,"type":0},{"id":3,"sLen":0,"time":40280.9,"type":0},{"id":1,"sLen":0,"time":40365.168,"type":0},{"id":2,"sLen":0,"time":40449.4375,"type":0},{"id":3,"sLen":0,"time":40617.9766,"type":0},{"id":2,"sLen":0,"time":40786.5156,"type":0},{"id":3,"sLen":0,"time":40955.0547,"type":0},{"id":0,"sLen":0,"time":41123.5938,"type":0},{"id":1,"sLen":0,"time":41292.1367,"type":0},{"id":0,"sLen":0,"time":41460.6758,"type":0},{"id":2,"sLen":0,"time":41544.9453,"type":0},{"id":0,"sLen":0,"time":41629.2148,"type":0},{"id":3,"sLen":0,"time":41797.7539,"type":0},{"id":1,"sLen":0,"time":41966.293,"type":0},{"id":0,"sLen":0,"time":42134.832,"type":0},{"id":1,"sLen":0,"time":42303.37,"type":0},{"id":0,"sLen":0,"time":42471.91,"type":0},{"id":1,"sLen":0,"time":42640.45,"type":0},{"id":1,"sLen":0,"time":42808.99,"type":0},{"id":3,"sLen":0,"time":42977.5273,"type":0},{"id":3,"sLen":0,"time":43061.7969,"type":0},{"id":3,"sLen":0,"time":48539.3242,"type":0},{"id":1,"sLen":0,"time":48623.5938,"type":0},{"id":3,"sLen":0,"time":48707.8633,"type":0},{"id":0,"sLen":0,"time":48876.4063,"type":0},{"id":3,"sLen":0,"time":49044.9453,"type":0},{"id":1,"sLen":0,"time":49129.2148,"type":0},{"id":2,"sLen":0,"time":49213.4844,"type":0},{"id":0,"sLen":0,"time":49382.0234,"type":0},{"id":1,"sLen":0,"time":49550.5625,"type":0},{"id":3,"sLen":0,"time":49719.1,"type":0},{"id":3,"sLen":0,"time":49803.37,"type":0},{"id":2,"sLen":0,"time":49887.64,"type":0},{"id":3,"sLen":0,"time":50056.18,"type":0},{"id":0,"sLen":0,"time":50140.45,"type":0},{"id":1,"sLen":0,"time":50224.72,"type":0},{"id":0,"sLen":0,"time":50393.2578,"type":0},{"id":2,"sLen":0,"time":50561.7969,"type":0},{"id":3,"sLen":0,"time":50730.3359,"type":0},{"id":1,"sLen":0,"time":50898.875,"type":0},{"id":3,"sLen":0,"time":51067.4141,"type":0},{"id":2,"sLen":0,"time":51151.6836,"type":0},{"id":0,"sLen":0,"time":51235.9531,"type":0},{"id":3,"sLen":0,"time":51404.4961,"type":0},{"id":1,"sLen":0,"time":51573.0352,"type":0},{"id":3,"sLen":0,"time":51741.5742,"type":0},{"id":0,"sLen":0,"time":51910.1133,"type":0},{"id":1,"sLen":0,"time":52078.6523,"type":0},{"id":3,"sLen":0,"time":52247.19,"type":0},{"id":2,"sLen":0,"time":52331.46,"type":0},{"id":3,"sLen":0,"time":52415.73,"type":0},{"id":1,"sLen":0,"time":52584.27,"type":0},{"id":3,"sLen":0,"time":52752.81,"type":0},{"id":0,"sLen":0,"time":52921.3477,"type":0},{"id":3,"sLen":0,"time":53089.8867,"type":0},{"id":1,"sLen":0,"time":53258.4258,"type":0},{"id":3,"sLen":0,"time":53426.9648,"type":0},{"id":2,"sLen":0,"time":53595.5039,"type":0},{"id":2,"sLen":0,"time":53764.043,"type":0},{"id":1,"sLen":842.696654999971,"time":53932.5859,"type":0},{"id":2,"sLen":505.617981000003,"time":55955.0547,"type":0},{"id":1,"sLen":674.157299999999,"time":56629.2148,"type":0},{"id":0,"sLen":252.808989999998,"time":58651.6836,"type":0},{"id":2,"sLen":84.2696599999836,"time":58988.7656,"type":0},{"id":1,"sLen":0,"time":59157.3047,"type":0},{"id":1,"sLen":0,"time":59325.8438,"type":0},{"id":2,"sLen":0,"time":59494.3828,"type":0},{"id":0,"sLen":0,"time":59662.92,"type":0},{"id":0,"sLen":0,"time":59831.46,"type":0},{"id":2,"sLen":0,"time":60000,"type":0},{"id":3,"sLen":0,"time":60168.54,"type":0},{"id":1,"sLen":0,"time":60337.08,"type":0},{"id":3,"sLen":0,"time":60505.6172,"type":0},{"id":0,"sLen":0,"time":60674.1563,"type":0},{"id":1,"sLen":0,"time":60842.6953,"type":0},{"id":2,"sLen":0,"time":61011.2344,"type":0},{"id":3,"sLen":0,"time":61179.7734,"type":0},{"id":1,"sLen":0,"time":61348.3164,"type":0},{"id":3,"sLen":0,"time":61516.8555,"type":0},{"id":0,"sLen":0,"time":61685.3945,"type":0},{"id":3,"sLen":0,"time":61853.9336,"type":0},{"id":1,"sLen":0,"time":62022.4727,"type":0},{"id":2,"sLen":0,"time":62191.01,"type":0},{"id":0,"sLen":0,"time":62359.55,"type":0},{"id":0,"sLen":0,"time":62528.09,"type":0},{"id":2,"sLen":0,"time":62696.63,"type":0},{"id":3,"sLen":0,"time":62865.168,"type":0},{"id":1,"sLen":0,"time":63033.707,"type":0},{"id":3,"sLen":0,"time":63202.2461,"type":0},{"id":0,"sLen":0,"time":63370.7852,"type":0},{"id":2,"sLen":0,"time":63539.3242,"type":0},{"id":3,"sLen":0,"time":63623.5938,"type":0},{"id":1,"sLen":0,"time":63707.8633,"type":0},{"id":3,"sLen":0,"time":63876.4063,"type":0},{"id":1,"sLen":0,"time":64044.9453,"type":0},{"id":3,"sLen":0,"time":64213.4844,"type":0},{"id":0,"sLen":0,"time":64382.0234,"type":0},{"id":3,"sLen":0,"time":64550.5625,"type":0},{"id":1,"sLen":252.808989999998,"time":64719.1,"type":0},{"id":0,"sLen":0,"time":65056.18,"type":0},{"id":1,"sLen":0,"time":65224.72,"type":0},{"id":3,"sLen":252.808989999998,"time":65393.2578,"type":0},{"id":1,"sLen":168.539321999997,"time":65730.3359,"type":0},{"id":3,"sLen":421.348327999993,"time":66067.4141,"type":0},{"id":0,"sLen":84.2696600000054,"time":66573.03,"type":0},{"id":1,"sLen":421.348327999993,"time":66741.57,"type":0},{"id":0,"sLen":0,"time":67247.19,"type":0},{"id":2,"sLen":589.887633999999,"time":67415.73,"type":0},{"id":1,"sLen":252.808990000005,"time":68089.89,"type":0},{"id":2,"sLen":252.808990000005,"time":68426.97,"type":0},{"id":3,"sLen":589.887633999999,"time":68764.05,"type":0},{"id":0,"sLen":589.887633999999,"time":69438.2,"type":0},{"id":1,"sLen":252.808990000005,"time":70112.36,"type":0},{"id":0,"sLen":0,"time":70449.44,"type":0},{"id":1,"sLen":0,"time":70617.98,"type":0},{"id":3,"sLen":252.808990000005,"time":70786.5156,"type":0},{"id":1,"sLen":0,"time":71123.59,"type":0},{"id":0,"sLen":0,"time":71460.67,"type":0},{"id":0,"sLen":0,"time":71797.75,"type":0},{"id":0,"sLen":0,"time":72134.83,"type":0},{"id":0,"sLen":0,"time":72303.37,"type":0},{"id":2,"sLen":0,"time":72471.91,"type":0},{"id":3,"sLen":0,"time":72556.18,"type":0},{"id":2,"sLen":0,"time":72640.45,"type":0},{"id":1,"sLen":0,"time":72808.99,"type":0},{"id":3,"sLen":0,"time":72977.53,"type":0},{"id":2,"sLen":0,"time":73146.07,"type":0},{"id":1,"sLen":0,"time":73314.61,"type":0},{"id":3,"sLen":0,"time":73483.15,"type":0},{"id":2,"sLen":0,"time":73651.69,"type":0},{"id":1,"sLen":0,"time":73820.23,"type":0},{"id":3,"sLen":0,"time":73904.49,"type":0},{"id":2,"sLen":0,"time":73988.7656,"type":0},{"id":0,"sLen":0,"time":74157.3047,"type":0},{"id":3,"sLen":0,"time":74325.84,"type":0},{"id":2,"sLen":0,"time":74494.38,"type":0},{"id":3,"sLen":0,"time":74662.92,"type":0},{"id":1,"sLen":0,"time":74831.46,"type":0},{"id":3,"sLen":0,"time":75000,"type":0},{"id":0,"sLen":0,"time":75168.54,"type":0},{"id":3,"sLen":0,"time":75337.08,"type":0},{"id":1,"sLen":1264.04492,"time":75505.62,"type":0},{"id":0,"sLen":1264.04492,"time":76853.93,"type":0},{"id":3,"sLen":1264.04492,"time":78202.25,"type":0},{"id":0,"sLen":1264.04492,"time":79550.56,"type":0},{"id":0,"sLen":0,"time":80898.875,"type":0},{"id":0,"sLen":0,"time":81067.4141,"type":0},{"id":1,"sLen":0,"time":81235.95,"type":0},{"id":0,"sLen":0,"time":81404.49,"type":0},{"id":2,"sLen":0,"time":81488.7656,"type":0},{"id":0,"sLen":0,"time":81573.03,"type":0},{"id":1,"sLen":0,"time":81741.57,"type":0},{"id":3,"sLen":0,"time":81910.11,"type":0},{"id":0,"sLen":0,"time":82078.65,"type":0},{"id":1,"sLen":0,"time":82247.19,"type":0},{"id":3,"sLen":0,"time":82331.46,"type":0},{"id":1,"sLen":0,"time":82415.73,"type":0},{"id":0,"sLen":0,"time":82584.27,"type":0},{"id":1,"sLen":0,"time":82752.81,"type":0},{"id":2,"sLen":252.808990000005,"time":82921.35,"type":0},{"id":0,"sLen":0,"time":83258.43,"type":0},{"id":0,"sLen":0,"time":83426.97,"type":0},{"id":1,"sLen":0,"time":83595.51,"type":0},{"id":0,"sLen":0,"time":83764.05,"type":0},{"id":3,"sLen":0,"time":83848.31,"type":0},{"id":3,"sLen":0,"time":83932.5859,"type":0},{"id":3,"sLen":0,"time":84101.125,"type":0},{"id":0,"sLen":0,"time":84185.39,"type":0},{"id":1,"sLen":0,"time":84269.6641,"type":0},{"id":1,"sLen":0,"time":84438.2,"type":0},{"id":3,"sLen":0,"time":84522.47,"type":0},{"id":2,"sLen":0,"time":84606.74,"type":0},{"id":0,"sLen":0,"time":84775.28,"type":0},{"id":1,"sLen":0,"time":84859.55,"type":0},{"id":2,"sLen":0,"time":84943.82,"type":0},{"id":3,"sLen":0,"time":85028.0859,"type":0},{"id":2,"sLen":0,"time":85112.36,"type":0},{"id":0,"sLen":84.2696600000054,"time":85196.63,"type":0},{"id":3,"sLen":0,"time":85365.17,"type":0},{"id":2,"sLen":0,"time":85449.44,"type":0},{"id":0,"sLen":0,"time":85617.98,"type":0},{"id":3,"sLen":0,"time":85786.5156,"type":0},{"id":0,"sLen":0,"time":85955.0547,"type":0},{"id":2,"sLen":0,"time":86123.59,"type":0},{"id":1,"sLen":0,"time":91685.39,"type":0},{"id":0,"sLen":0,"time":91769.6641,"type":0},{"id":3,"sLen":0,"time":91853.93,"type":0},{"id":2,"sLen":0,"time":92022.47,"type":0},{"id":1,"sLen":0,"time":92106.74,"type":0},{"id":3,"sLen":0,"time":92191.01,"type":0},{"id":2,"sLen":0,"time":92359.55,"type":0},{"id":3,"sLen":0,"time":92528.0859,"type":0},{"id":2,"sLen":0,"time":92696.63,"type":0},{"id":0,"sLen":0,"time":92865.17,"type":0},{"id":1,"sLen":0,"time":93033.71,"type":0},{"id":0,"sLen":0,"time":93117.98,"type":0},{"id":3,"sLen":0,"time":93202.25,"type":0},{"id":2,"sLen":0,"time":93370.79,"type":0},{"id":1,"sLen":0,"time":93455.0547,"type":0},{"id":3,"sLen":0,"time":93539.33,"type":0},{"id":2,"sLen":0,"time":93707.87,"type":0},{"id":3,"sLen":0,"time":93876.41,"type":0},{"id":1,"sLen":0,"time":94044.9453,"type":0},{"id":3,"sLen":0,"time":94213.4844,"type":0},{"id":2,"sLen":0,"time":94382.02,"type":0},{"id":3,"sLen":0,"time":94466.29,"type":0},{"id":2,"sLen":0,"time":94550.56,"type":0},{"id":1,"sLen":0,"time":94719.1,"type":0},{"id":0,"sLen":0,"time":94803.37,"type":0},{"id":1,"sLen":0,"time":94887.64,"type":0},{"id":3,"sLen":0,"time":95056.18,"type":0},{"id":3,"sLen":0,"time":95224.72,"type":0},{"id":1,"sLen":0,"time":95308.99,"type":0},{"id":3,"sLen":0,"time":95393.26,"type":0},{"id":1,"sLen":0,"time":95561.8,"type":0},{"id":0,"sLen":0,"time":95730.3359,"type":0},{"id":3,"sLen":0,"time":95814.61,"type":0},{"id":1,"sLen":0,"time":95898.875,"type":0},{"id":0,"sLen":0,"time":95983.15,"type":0},{"id":1,"sLen":0,"time":96067.4141,"type":0},{"id":3,"sLen":0,"time":96235.95,"type":0},{"id":0,"sLen":0,"time":96404.49,"type":0},{"id":3,"sLen":0,"time":96488.7656,"type":0},{"id":1,"sLen":0,"time":96573.03,"type":0},{"id":0,"sLen":0,"time":96657.3047,"type":0},{"id":1,"sLen":337.078643999994,"time":96741.57,"type":0},{"id":0,"sLen":589.887633999941,"time":98426.97,"type":0},{"id":2,"sLen":589.887633999999,"time":99101.125,"type":0},{"id":0,"sLen":589.887633999999,"time":101123.594,"type":0},{"id":2,"sLen":589.887633999984,"time":101797.75,"type":0},{"id":3,"sLen":0,"time":102471.906,"type":0},{"id":0,"sLen":0,"time":102556.18,"type":0},{"id":2,"sLen":0,"time":102640.453,"type":0},{"id":0,"sLen":0,"time":102808.992,"type":0},{"id":3,"sLen":0,"time":102977.531,"type":0},{"id":0,"sLen":0,"time":103061.8,"type":0},{"id":2,"sLen":0,"time":103146.07,"type":0},{"id":0,"sLen":0,"time":103314.609,"type":0},{"id":3,"sLen":0,"time":103483.148,"type":0},{"id":0,"sLen":0,"time":103567.414,"type":0},{"id":2,"sLen":0,"time":103651.688,"type":0},{"id":0,"sLen":0,"time":103820.227,"type":0},{"id":3,"sLen":0,"time":103988.766,"type":0},{"id":0,"sLen":0,"time":104073.031,"type":0},{"id":2,"sLen":0,"time":104157.305,"type":0},{"id":0,"sLen":0,"time":104325.844,"type":0},{"id":2,"sLen":0,"time":104494.383,"type":0},{"id":1,"sLen":0,"time":104662.922,"type":0},{"id":3,"sLen":0,"time":104831.461,"type":0},{"id":0,"sLen":0,"time":105000,"type":0},{"id":3,"sLen":0,"time":105168.539,"type":0},{"id":0,"sLen":0,"time":105252.813,"type":0},{"id":2,"sLen":0,"time":105337.078,"type":0},{"id":0,"sLen":0,"time":105505.617,"type":0},{"id":3,"sLen":0,"time":105674.156,"type":0},{"id":0,"sLen":0,"time":105758.43,"type":0},{"id":2,"sLen":0,"time":105842.695,"type":0},{"id":0,"sLen":0,"time":106011.234,"type":0},{"id":3,"sLen":0,"time":106179.773,"type":0},{"id":0,"sLen":0,"time":106264.047,"type":0},{"id":2,"sLen":0,"time":106348.313,"type":0},{"id":0,"sLen":0,"time":106516.852,"type":0},{"id":3,"sLen":0,"time":106685.391,"type":0},{"id":0,"sLen":0,"time":106769.664,"type":0},{"id":2,"sLen":0,"time":106853.93,"type":0},{"id":0,"sLen":0,"time":107022.469,"type":0},{"id":2,"sLen":0,"time":107191.008,"type":0},{"id":1,"sLen":0,"time":107359.547,"type":0},{"id":3,"sLen":0,"time":107528.086,"type":0},{"id":0,"sLen":0,"time":107696.633,"type":0},{"id":3,"sLen":589.887634000042,"time":107865.172,"type":0},{"id":1,"sLen":589.887634000042,"time":108539.328,"type":0},{"id":2,"sLen":589.887633999999,"time":109213.484,"type":0},{"id":1,"sLen":589.887633999999,"time":109887.641,"type":0},{"id":3,"sLen":0,"time":110561.8,"type":0},{"id":2,"sLen":0,"time":110646.07,"type":0},{"id":3,"sLen":0,"time":110730.336,"type":0},{"id":2,"sLen":0,"time":110814.609,"type":0},{"id":0,"sLen":0,"time":110898.875,"type":0},{"id":1,"sLen":0,"time":111067.414,"type":0},{"id":3,"sLen":0,"time":111235.953,"type":0},{"id":2,"sLen":0,"time":111320.227,"type":0},{"id":3,"sLen":0,"time":111404.492,"type":0},{"id":2,"sLen":0,"time":111488.766,"type":0},{"id":0,"sLen":0,"time":111573.031,"type":0},{"id":1,"sLen":0,"time":111741.57,"type":0},{"id":3,"sLen":0,"time":111910.109,"type":0},{"id":0,"sLen":0,"time":111994.383,"type":0},{"id":2,"sLen":0,"time":112078.648,"type":0},{"id":3,"sLen":0,"time":112162.922,"type":0},{"id":0,"sLen":0,"time":112247.188,"type":0},{"id":1,"sLen":0,"time":112415.727,"type":0},{"id":3,"sLen":0,"time":112584.273,"type":0},{"id":2,"sLen":0,"time":112668.539,"type":0},{"id":3,"sLen":0,"time":112752.813,"type":0},{"id":2,"sLen":0,"time":112837.078,"type":0},{"id":0,"sLen":0,"time":112921.352,"type":0},{"id":1,"sLen":0,"time":113089.891,"type":0},{"id":3,"sLen":589.887633999999,"time":113258.43,"type":0},{"id":1,"sLen":589.887633999999,"time":113932.586,"type":0},{"id":0,"sLen":589.887633999984,"time":114606.742,"type":0},{"id":1,"sLen":589.887633999999,"time":115280.9,"type":0},{"id":2,"sLen":0,"time":115955.055,"type":0},{"id":3,"sLen":0,"time":116123.594,"type":0},{"id":1,"sLen":0,"time":116292.133,"type":0},{"id":3,"sLen":0,"time":116460.672,"type":0},{"id":0,"sLen":0,"time":116629.211,"type":0},{"id":1,"sLen":0,"time":116797.75,"type":0},{"id":0,"sLen":0,"time":116966.289,"type":0},{"id":3,"sLen":0,"time":117050.563,"type":0},{"id":1,"sLen":0,"time":117134.828,"type":0},{"id":0,"sLen":0,"time":117219.1,"type":0},{"id":2,"sLen":0,"time":117303.367,"type":0},{"id":3,"sLen":0,"time":117471.906,"type":0},{"id":1,"sLen":0,"time":117640.453,"type":0},{"id":3,"sLen":0,"time":117808.992,"type":0},{"id":0,"sLen":0,"time":117977.531,"type":0},{"id":3,"sLen":0,"time":118061.8,"type":0},{"id":1,"sLen":0,"time":118146.07,"type":0},{"id":0,"sLen":0,"time":118230.336,"type":0},{"id":3,"sLen":0,"time":118314.609,"type":0},{"id":1,"sLen":0,"time":118483.148,"type":0}],"type":1,"characters":["bf-holding-gf"],"visible":true},{"keyCount":4,"position":"girlfriend","notes":[{"id":0,"sLen":0,"time":0,"type":0},{"id":3,"sLen":0,"time":168.539325842697,"type":0},{"id":3,"sLen":0,"time":252.808988764045,"type":0},{"id":3,"sLen":0,"time":421.348314606742,"type":0},{"id":0,"sLen":0,"time":589.887640449438,"type":0},{"id":3,"sLen":0,"time":758.426966292135,"type":0},{"id":3,"sLen":0,"time":842.696629213483,"type":0},{"id":0,"sLen":0,"time":1011.23595505618,"type":0},{"id":0,"sLen":0,"time":1179.77528089888,"type":0},{"id":0,"sLen":0,"time":1264.04494382022,"type":0},{"id":3,"sLen":0,"time":1348.31460674157,"type":0},{"id":0,"sLen":0,"time":1516.85393258427,"type":0},{"id":0,"sLen":0,"time":1601.12359550562,"type":0},{"id":3,"sLen":0,"time":1853.93258426966,"type":0},{"id":0,"sLen":0,"time":2022.47191011236,"type":0},{"id":3,"sLen":0,"time":2106.74157303371,"type":0},{"id":3,"sLen":0,"time":2191.01123595506,"type":0},{"id":0,"sLen":0,"time":2359.55056179775,"type":0},{"id":0,"sLen":0,"time":2696.62921348315,"type":0},{"id":3,"sLen":0,"time":2865.16853932584,"type":0},{"id":3,"sLen":0,"time":2949.43820224719,"type":0},{"id":3,"sLen":0,"time":3117.97752808989,"type":0},{"id":0,"sLen":0,"time":3286.51685393258,"type":0},{"id":3,"sLen":0,"time":3455.05617977528,"type":0},{"id":3,"sLen":0,"time":3539.32584269663,"type":0},{"id":0,"sLen":0,"time":3707.86516853933,"type":0},{"id":0,"sLen":0,"time":3876.40449438202,"type":0},{"id":0,"sLen":0,"time":3960.67415730337,"type":0},{"id":3,"sLen":0,"time":4044.94382022472,"type":0},{"id":0,"sLen":0,"time":4213.48314606742,"type":0},{"id":0,"sLen":0,"time":4297.75280898876,"type":0},{"id":3,"sLen":0,"time":4550.56179775281,"type":0},{"id":0,"sLen":0,"time":4719.10112359551,"type":0},{"id":3,"sLen":0,"time":4803.37078651685,"type":0},{"id":3,"sLen":0,"time":4887.6404494382,"type":0},{"id":0,"sLen":0,"time":5056.1797752809,"type":0},{"id":0,"sLen":0,"time":5140.44943820225,"type":0},{"id":0,"sLen":0,"time":5224.7191011236,"type":0},{"id":0,"sLen":0,"time":5393.25842696629,"type":0},{"id":3,"sLen":0,"time":5561.79775280899,"type":0},{"id":3,"sLen":0,"time":5646.06741573034,"type":0},{"id":3,"sLen":0,"time":5814.60674157303,"type":0},{"id":0,"sLen":0,"time":5983.14606741573,"type":0},{"id":3,"sLen":0,"time":6151.68539325843,"type":0},{"id":3,"sLen":0,"time":6235.95505617978,"type":0},{"id":0,"sLen":0,"time":6404.49438202247,"type":0},{"id":0,"sLen":0,"time":6573.03370786517,"type":0},{"id":0,"sLen":0,"time":6657.30337078652,"type":0},{"id":3,"sLen":0,"time":6741.57303370786,"type":0},{"id":0,"sLen":0,"time":6910.11235955056,"type":0},{"id":0,"sLen":0,"time":6994.38202247191,"type":0},{"id":3,"sLen":0,"time":7247.19101123595,"type":0},{"id":0,"sLen":0,"time":7415.73033707865,"type":0},{"id":3,"sLen":0,"time":7500,"type":0},{"id":3,"sLen":0,"time":7584.26966292135,"type":0},{"id":0,"sLen":0,"time":7752.80898876405,"type":0},{"id":0,"sLen":0,"time":8089.88764044944,"type":0},{"id":3,"sLen":0,"time":8258.42696629213,"type":0},{"id":3,"sLen":0,"time":8342.69662921348,"type":0},{"id":3,"sLen":0,"time":8511.23595505618,"type":0},{"id":0,"sLen":0,"time":8679.77528089888,"type":0},{"id":3,"sLen":0,"time":8848.31460674157,"type":0},{"id":3,"sLen":0,"time":8932.58426966292,"type":0},{"id":0,"sLen":0,"time":9101.12359550562,"type":0},{"id":0,"sLen":0,"time":9269.66292134831,"type":0},{"id":0,"sLen":0,"time":9353.93258426966,"type":0},{"id":3,"sLen":0,"time":9438.20224719101,"type":0},{"id":0,"sLen":0,"time":9606.74157303371,"type":0},{"id":0,"sLen":0,"time":9691.01123595505,"type":0},{"id":3,"sLen":0,"time":9943.8202247191,"type":0},{"id":0,"sLen":0,"time":10112.3595505618,"type":0},{"id":3,"sLen":0,"time":10196.6292134831,"type":0},{"id":3,"sLen":0,"time":10280.8988764045,"type":0},{"id":0,"sLen":0,"time":10449.4382022472,"type":0},{"id":0,"sLen":0,"time":21235.9550561798,"type":0},{"id":3,"sLen":0,"time":21320.2247191011,"type":0},{"id":0,"sLen":0,"time":21404.4943820225,"type":0},{"id":0,"sLen":0,"time":21573.0337078652,"type":0},{"id":3,"sLen":0,"time":21910.1123595506,"type":0},{"id":0,"sLen":0,"time":22247.191011236,"type":0},{"id":3,"sLen":0,"time":22584.2696629213,"type":0},{"id":0,"sLen":0,"time":22921.3483146067,"type":0},{"id":0,"sLen":0,"time":23258.4269662921,"type":0},{"id":3,"sLen":0,"time":23595.5056179775,"type":0},{"id":3,"sLen":0,"time":23932.5842696629,"type":0},{"id":0,"sLen":0,"time":24269.6629213483,"type":0},{"id":3,"sLen":0,"time":24606.7415730337,"type":0},{"id":0,"sLen":0,"time":24943.8202247191,"type":0},{"id":3,"sLen":0,"time":25280.8988764045,"type":0},{"id":0,"sLen":0,"time":25617.9775280899,"type":0},{"id":0,"sLen":0,"time":25955.0561797753,"type":0},{"id":3,"sLen":0,"time":26292.1348314607,"type":0},{"id":3,"sLen":0,"time":26629.2134831461,"type":0},{"id":3,"sLen":0,"time":26713.4831460674,"type":0},{"id":3,"sLen":0,"time":26797.7528089888,"type":0},{"id":3,"sLen":0,"time":26966.2921348315,"type":0},{"id":0,"sLen":0,"time":27303.3707865169,"type":0},{"id":0,"sLen":0,"time":27640.4494382022,"type":0},{"id":3,"sLen":0,"time":27977.5280898876,"type":0},{"id":3,"sLen":0,"time":28314.606741573,"type":0},{"id":0,"sLen":0,"time":28651.6853932584,"type":0},{"id":3,"sLen":0,"time":28988.7640449438,"type":0},{"id":0,"sLen":0,"time":29325.8426966292,"type":0},{"id":0,"sLen":0,"time":29662.9213483146,"type":0},{"id":0,"sLen":0,"time":29831.4606741573,"type":0},{"id":0,"sLen":0,"time":30000,"type":0},{"id":3,"sLen":0,"time":30168.5393258427,"type":0},{"id":3,"sLen":0,"time":30337.0786516854,"type":0},{"id":3,"sLen":0,"time":30505.6179775281,"type":0},{"id":3,"sLen":0,"time":30674.1573033708,"type":0},{"id":0,"sLen":0,"time":30842.6966292135,"type":0},{"id":0,"sLen":0,"time":31011.2359550562,"type":0},{"id":3,"sLen":0,"time":31348.3146067416,"type":0},{"id":3,"sLen":0,"time":31685.393258427,"type":0},{"id":0,"sLen":0,"time":31853.9325842697,"type":0},{"id":0,"sLen":0,"time":32359.5505617978,"type":0},{"id":3,"sLen":0,"time":32696.6292134831,"type":0},{"id":0,"sLen":0,"time":33033.7078651685,"type":0},{"id":0,"sLen":0,"time":33202.2471910112,"type":0},{"id":3,"sLen":0,"time":33370.7865168539,"type":0},{"id":0,"sLen":0,"time":33707.8651685393,"type":0},{"id":3,"sLen":0,"time":34044.9438202247,"type":0},{"id":3,"sLen":0,"time":34382.0224719101,"type":0},{"id":0,"sLen":0,"time":34550.5617977528,"type":0},{"id":3,"sLen":0,"time":34719.1011235955,"type":0},{"id":0,"sLen":0,"time":35056.1797752809,"type":0},{"id":3,"sLen":0,"time":35393.2584269663,"type":0},{"id":0,"sLen":0,"time":35730.3370786517,"type":0},{"id":0,"sLen":0,"time":35898.8764044944,"type":0},{"id":3,"sLen":0,"time":36067.4157303371,"type":0},{"id":0,"sLen":0,"time":36404.4943820225,"type":0},{"id":3,"sLen":0,"time":36741.5730337079,"type":0},{"id":3,"sLen":0,"time":37078.6516853933,"type":0},{"id":0,"sLen":0,"time":37247.191011236,"type":0},{"id":3,"sLen":0,"time":37415.7303370787,"type":0},{"id":0,"sLen":0,"time":37752.808988764,"type":0},{"id":3,"sLen":0,"time":38089.8876404494,"type":0},{"id":3,"sLen":0,"time":38426.9662921348,"type":0},{"id":0,"sLen":0,"time":38595.5056179775,"type":0},{"id":3,"sLen":0,"time":38764.0449438202,"type":0},{"id":0,"sLen":0,"time":39101.1235955056,"type":0},{"id":3,"sLen":0,"time":39438.202247191,"type":0},{"id":3,"sLen":0,"time":39775.2808988764,"type":0},{"id":0,"sLen":0,"time":39943.8202247191,"type":0},{"id":3,"sLen":0,"time":40112.3595505618,"type":0},{"id":0,"sLen":0,"time":40449.4382022472,"type":0},{"id":3,"sLen":0,"time":40786.5168539326,"type":0},{"id":3,"sLen":0,"time":41123.595505618,"type":0},{"id":0,"sLen":0,"time":41292.1348314607,"type":0},{"id":3,"sLen":0,"time":41460.6741573034,"type":0},{"id":0,"sLen":0,"time":41797.7528089888,"type":0},{"id":0,"sLen":0,"time":42134.8314606742,"type":0},{"id":0,"sLen":0,"time":42471.9101123596,"type":0},{"id":0,"sLen":0,"time":42640.4494382022,"type":0},{"id":3,"sLen":0,"time":42808.9887640449,"type":0},{"id":3,"sLen":0,"time":42893.2584269663,"type":0},{"id":3,"sLen":0,"time":42977.5280898876,"type":0},{"id":0,"sLen":0,"time":43146.0674157303,"type":0},{"id":3,"sLen":0,"time":43483.1460674157,"type":0},{"id":3,"sLen":0,"time":43820.2247191011,"type":0},{"id":0,"sLen":0,"time":43988.7640449438,"type":0},{"id":3,"sLen":0,"time":44157.3033707865,"type":0},{"id":0,"sLen":0,"time":44494.3820224719,"type":0},{"id":3,"sLen":0,"time":44831.4606741573,"type":0},{"id":0,"sLen":0,"time":45168.5393258427,"type":0},{"id":0,"sLen":0,"time":45337.0786516854,"type":0},{"id":3,"sLen":0,"time":45505.6179775281,"type":0},{"id":0,"sLen":0,"time":45842.6966292135,"type":0},{"id":3,"sLen":0,"time":46179.7752808989,"type":0},{"id":3,"sLen":0,"time":46516.8539325843,"type":0},{"id":0,"sLen":0,"time":46685.393258427,"type":0},{"id":3,"sLen":0,"time":46853.9325842697,"type":0},{"id":0,"sLen":0,"time":47191.0112359551,"type":0},{"id":3,"sLen":0,"time":47528.0898876404,"type":0},{"id":3,"sLen":0,"time":47865.1685393258,"type":0},{"id":0,"sLen":0,"time":48033.7078651685,"type":0},{"id":3,"sLen":0,"time":48202.2471910112,"type":0},{"id":0,"sLen":0,"time":48539.3258426966,"type":0},{"id":3,"sLen":0,"time":48876.404494382,"type":0},{"id":3,"sLen":0,"time":49213.4831460674,"type":0},{"id":0,"sLen":0,"time":49382.0224719101,"type":0},{"id":3,"sLen":0,"time":49550.5617977528,"type":0},{"id":0,"sLen":0,"time":49887.6404494382,"type":0},{"id":3,"sLen":0,"time":50224.7191011236,"type":0},{"id":0,"sLen":0,"time":50561.797752809,"type":0},{"id":0,"sLen":0,"time":50730.3370786517,"type":0},{"id":3,"sLen":0,"time":50898.8764044944,"type":0},{"id":0,"sLen":0,"time":51235.9550561798,"type":0},{"id":3,"sLen":0,"time":51573.0337078652,"type":0},{"id":3,"sLen":0,"time":51910.1123595506,"type":0},{"id":0,"sLen":0,"time":52078.6516853933,"type":0},{"id":3,"sLen":0,"time":52247.191011236,"type":0},{"id":0,"sLen":0,"time":52584.2696629213,"type":0},{"id":0,"sLen":0,"time":52921.3483146067,"type":0},{"id":0,"sLen":0,"time":53258.4269662921,"type":0},{"id":0,"sLen":0,"time":53426.9662921348,"type":0},{"id":3,"sLen":0,"time":53595.5056179775,"type":0},{"id":3,"sLen":0,"time":53679.7752808989,"type":0},{"id":3,"sLen":0,"time":53764.0449438202,"type":0},{"id":0,"sLen":0,"time":53932.5842696629,"type":0},{"id":3,"sLen":0,"time":54101.1235955056,"type":0},{"id":3,"sLen":0,"time":54185.393258427,"type":0},{"id":3,"sLen":0,"time":54353.9325842697,"type":0},{"id":0,"sLen":0,"time":54522.4719101124,"type":0},{"id":3,"sLen":0,"time":54691.0112359551,"type":0},{"id":3,"sLen":0,"time":54775.2808988764,"type":0},{"id":0,"sLen":0,"time":54943.8202247191,"type":0},{"id":0,"sLen":0,"time":55112.3595505618,"type":0},{"id":0,"sLen":0,"time":55196.6292134831,"type":0},{"id":3,"sLen":0,"time":55280.8988764045,"type":0},{"id":0,"sLen":0,"time":55449.4382022472,"type":0},{"id":0,"sLen":0,"time":55533.7078651685,"type":0},{"id":3,"sLen":0,"time":55786.5168539326,"type":0},{"id":0,"sLen":0,"time":55955.0561797753,"type":0},{"id":3,"sLen":0,"time":56039.3258426966,"type":0},{"id":3,"sLen":0,"time":56123.595505618,"type":0},{"id":0,"sLen":0,"time":56292.1348314607,"type":0},{"id":0,"sLen":0,"time":56629.2134831461,"type":0},{"id":3,"sLen":0,"time":56797.7528089888,"type":0},{"id":3,"sLen":0,"time":56882.0224719101,"type":0},{"id":3,"sLen":0,"time":57050.5617977528,"type":0},{"id":0,"sLen":0,"time":57219.1011235955,"type":0},{"id":3,"sLen":0,"time":57387.6404494382,"type":0},{"id":3,"sLen":0,"time":57471.9101123595,"type":0},{"id":0,"sLen":0,"time":57640.4494382022,"type":0},{"id":0,"sLen":0,"time":57808.9887640449,"type":0},{"id":0,"sLen":0,"time":57893.2584269663,"type":0},{"id":3,"sLen":0,"time":57977.5280898876,"type":0},{"id":0,"sLen":0,"time":58146.0674157303,"type":0},{"id":0,"sLen":0,"time":58230.3370786517,"type":0},{"id":3,"sLen":0,"time":58483.1460674157,"type":0},{"id":0,"sLen":0,"time":58651.6853932584,"type":0},{"id":3,"sLen":0,"time":58735.9550561798,"type":0},{"id":3,"sLen":0,"time":58820.2247191011,"type":0},{"id":0,"sLen":0,"time":58988.7640449438,"type":0},{"id":0,"sLen":0,"time":59073.0337078652,"type":0},{"id":0,"sLen":0,"time":59157.3033707865,"type":0},{"id":0,"sLen":0,"time":59325.8426966292,"type":0},{"id":3,"sLen":0,"time":59494.3820224719,"type":0},{"id":3,"sLen":0,"time":59578.6516853933,"type":0},{"id":3,"sLen":0,"time":59747.191011236,"type":0},{"id":0,"sLen":0,"time":59915.7303370786,"type":0},{"id":3,"sLen":0,"time":60084.2696629213,"type":0},{"id":3,"sLen":0,"time":60168.5393258427,"type":0},{"id":0,"sLen":0,"time":60337.0786516854,"type":0},{"id":0,"sLen":0,"time":60505.6179775281,"type":0},{"id":0,"sLen":0,"time":60589.8876404494,"type":0},{"id":3,"sLen":0,"time":60674.1573033708,"type":0},{"id":0,"sLen":0,"time":60842.6966292135,"type":0},{"id":0,"sLen":0,"time":60926.9662921348,"type":0},{"id":3,"sLen":0,"time":61179.7752808989,"type":0},{"id":0,"sLen":0,"time":61348.3146067416,"type":0},{"id":3,"sLen":0,"time":61432.5842696629,"type":0},{"id":3,"sLen":0,"time":61516.8539325843,"type":0},{"id":0,"sLen":0,"time":61685.393258427,"type":0},{"id":0,"sLen":0,"time":62022.4719101124,"type":0},{"id":3,"sLen":0,"time":62191.0112359551,"type":0},{"id":3,"sLen":0,"time":62275.2808988764,"type":0},{"id":3,"sLen":0,"time":62443.8202247191,"type":0},{"id":0,"sLen":0,"time":62612.3595505618,"type":0},{"id":3,"sLen":0,"time":62780.8988764045,"type":0},{"id":3,"sLen":0,"time":62865.1685393258,"type":0},{"id":0,"sLen":0,"time":63033.7078651685,"type":0},{"id":0,"sLen":0,"time":63202.2471910112,"type":0},{"id":0,"sLen":0,"time":63286.5168539326,"type":0},{"id":3,"sLen":0,"time":63370.7865168539,"type":0},{"id":0,"sLen":0,"time":63539.3258426966,"type":0},{"id":0,"sLen":0,"time":63623.595505618,"type":0},{"id":3,"sLen":0,"time":63876.404494382,"type":0},{"id":0,"sLen":0,"time":64044.9438202247,"type":0},{"id":3,"sLen":0,"time":64129.2134831461,"type":0},{"id":3,"sLen":0,"time":64213.4831460674,"type":0},{"id":0,"sLen":0,"time":64382.0224719101,"type":0},{"id":3,"sLen":0,"time":64719.1011235955,"type":0},{"id":0,"sLen":0,"time":65056.1797752809,"type":0},{"id":0,"sLen":0,"time":65393.2584269663,"type":0},{"id":0,"sLen":0,"time":65730.3370786517,"type":0},{"id":0,"sLen":0,"time":66067.4157303371,"type":0},{"id":3,"sLen":0,"time":66404.4943820225,"type":0},{"id":3,"sLen":0,"time":66741.5730337079,"type":0},{"id":3,"sLen":0,"time":67078.6516853933,"type":0},{"id":3,"sLen":0,"time":67415.7303370787,"type":0},{"id":0,"sLen":0,"time":67752.808988764,"type":0},{"id":0,"sLen":0,"time":68089.8876404494,"type":0},{"id":0,"sLen":0,"time":68426.9662921348,"type":0},{"id":0,"sLen":0,"time":68764.0449438202,"type":0},{"id":3,"sLen":0,"time":69101.1235955056,"type":0},{"id":3,"sLen":0,"time":69438.202247191,"type":0},{"id":3,"sLen":0,"time":69775.2808988764,"type":0},{"id":3,"sLen":0,"time":69859.5505617978,"type":0},{"id":3,"sLen":0,"time":69943.8202247191,"type":0},{"id":3,"sLen":0,"time":70112.3595505618,"type":0},{"id":0,"sLen":0,"time":70449.4382022472,"type":0},{"id":0,"sLen":0,"time":70786.5168539326,"type":0},{"id":0,"sLen":0,"time":71123.595505618,"type":0},{"id":0,"sLen":0,"time":71460.6741573034,"type":0},{"id":3,"sLen":0,"time":71797.7528089888,"type":0},{"id":3,"sLen":0,"time":72134.8314606741,"type":0},{"id":3,"sLen":0,"time":72471.9101123595,"type":0},{"id":3,"sLen":0,"time":72556.1797752809,"type":0},{"id":3,"sLen":0,"time":72640.4494382023,"type":0},{"id":3,"sLen":0,"time":72808.9887640449,"type":0},{"id":3,"sLen":0,"time":72893.2584269663,"type":0},{"id":3,"sLen":0,"time":72977.5280898877,"type":0},{"id":0,"sLen":0,"time":73146.0674157303,"type":0},{"id":0,"sLen":0,"time":73230.3370786517,"type":0},{"id":0,"sLen":0,"time":73314.6067415731,"type":0},{"id":0,"sLen":0,"time":73483.1460674157,"type":0},{"id":0,"sLen":0,"time":73567.4157303371,"type":0},{"id":0,"sLen":0,"time":73651.6853932585,"type":0},{"id":0,"sLen":0,"time":73820.2247191011,"type":0},{"id":0,"sLen":0,"time":73904.4943820225,"type":0},{"id":0,"sLen":0,"time":73988.7640449438,"type":0},{"id":0,"sLen":0,"time":74157.3033707866,"type":0},{"id":0,"sLen":0,"time":74241.5730337079,"type":0},{"id":0,"sLen":0,"time":74325.8426966292,"type":0},{"id":0,"sLen":0,"time":74410.1123595506,"type":0},{"id":3,"sLen":0,"time":74494.382022472,"type":0},{"id":3,"sLen":0,"time":74578.6516853933,"type":0},{"id":3,"sLen":0,"time":74662.9213483146,"type":0},{"id":3,"sLen":0,"time":74747.191011236,"type":0},{"id":0,"sLen":0,"time":74831.4606741573,"type":0},{"id":0,"sLen":0,"time":74915.7303370787,"type":0},{"id":0,"sLen":0,"time":75000,"type":0},{"id":0,"sLen":0,"time":75084.2696629214,"type":0},{"id":3,"sLen":0,"time":75168.5393258427,"type":0},{"id":0,"sLen":0,"time":75505.6179775281,"type":0},{"id":3,"sLen":0,"time":75842.6966292135,"type":0},{"id":0,"sLen":0,"time":76179.7752808989,"type":0},{"id":0,"sLen":0,"time":76516.8539325843,"type":0},{"id":3,"sLen":0,"time":76853.9325842697,"type":0},{"id":3,"sLen":0,"time":77191.0112359551,"type":0},{"id":0,"sLen":0,"time":77528.0898876405,"type":0},{"id":3,"sLen":0,"time":77865.1685393259,"type":0},{"id":3,"sLen":0,"time":78033.7078651686,"type":0},{"id":0,"sLen":0,"time":78202.2471910113,"type":0},{"id":0,"sLen":0,"time":78539.3258426967,"type":0},{"id":3,"sLen":0,"time":78876.4044943821,"type":0},{"id":0,"sLen":0,"time":79213.4831460675,"type":0},{"id":0,"sLen":0,"time":79550.5617977529,"type":0},{"id":3,"sLen":0,"time":79887.6404494383,"type":0},{"id":0,"sLen":0,"time":80140.4494382023,"type":0},{"id":0,"sLen":0,"time":80224.7191011237,"type":0},{"id":0,"sLen":0,"time":80308.988764045,"type":0},{"id":0,"sLen":0,"time":80477.5280898877,"type":0},{"id":3,"sLen":84.269662921346,"time":80730.3370786517,"type":0},{"id":0,"sLen":0,"time":80898.8764044945,"type":0},{"id":3,"sLen":0,"time":81067.4157303371,"type":0},{"id":0,"sLen":0,"time":81235.9550561799,"type":0},{"id":3,"sLen":0,"time":81404.4943820225,"type":0},{"id":0,"sLen":0,"time":81573.0337078652,"type":0},{"id":3,"sLen":0,"time":81741.5730337079,"type":0},{"id":0,"sLen":0,"time":81910.1123595506,"type":0},{"id":3,"sLen":0,"time":82078.6516853933,"type":0},{"id":3,"sLen":0,"time":82247.191011236,"type":0},{"id":0,"sLen":0,"time":82415.7303370787,"type":0},{"id":3,"sLen":0,"time":82584.2696629214,"type":0},{"id":0,"sLen":0,"time":82752.8089887641,"type":0},{"id":3,"sLen":0,"time":82921.3483146068,"type":0},{"id":0,"sLen":0,"time":83089.8876404495,"type":0},{"id":3,"sLen":0,"time":83258.4269662922,"type":0},{"id":3,"sLen":0,"time":83426.9662921349,"type":0},{"id":0,"sLen":0,"time":83595.5056179776,"type":0},{"id":0,"sLen":0,"time":83764.0449438203,"type":0},{"id":0,"sLen":0,"time":83932.584269663,"type":0},{"id":3,"sLen":0,"time":84269.6629213484,"type":0},{"id":3,"sLen":0,"time":84606.7415730338,"type":0},{"id":3,"sLen":0,"time":84775.2808988765,"type":0},{"id":3,"sLen":0,"time":84943.8202247192,"type":0},{"id":3,"sLen":0,"time":85280.8988764046,"type":0},{"id":0,"sLen":0,"time":85617.97752809,"type":0},{"id":0,"sLen":0,"time":85702.2471910113,"type":0},{"id":3,"sLen":0,"time":85870.786516854,"type":0},{"id":0,"sLen":0,"time":86039.3258426967,"type":0},{"id":0,"sLen":0,"time":86292.1348314607,"type":0},{"id":3,"sLen":0,"time":86629.2134831461,"type":0},{"id":0,"sLen":0,"time":87134.8314606741,"type":0},{"id":3,"sLen":0,"time":87303.3707865169,"type":0},{"id":0,"sLen":0,"time":87640.4494382023,"type":0},{"id":3,"sLen":0,"time":87977.5280898877,"type":0},{"id":0,"sLen":0,"time":88483.1460674157,"type":0},{"id":3,"sLen":0,"time":88651.6853932584,"type":0},{"id":0,"sLen":0,"time":88988.7640449438,"type":0},{"id":3,"sLen":0,"time":89325.8426966292,"type":0},{"id":0,"sLen":0,"time":89831.4606741573,"type":0},{"id":3,"sLen":0,"time":90000,"type":0},{"id":0,"sLen":0,"time":90337.0786516854,"type":0},{"id":3,"sLen":0,"time":90674.1573033708,"type":0},{"id":0,"sLen":0,"time":91179.7752808989,"type":0},{"id":3,"sLen":0,"time":91348.3146067416,"type":0},{"id":0,"sLen":0,"time":91685.393258427,"type":0},{"id":3,"sLen":0,"time":92022.4719101124,"type":0},{"id":0,"sLen":0,"time":92528.0898876405,"type":0},{"id":3,"sLen":0,"time":92696.6292134832,"type":0},{"id":0,"sLen":0,"time":93033.7078651686,"type":0},{"id":3,"sLen":0,"time":93370.786516854,"type":0},{"id":0,"sLen":0,"time":93876.404494382,"type":0},{"id":3,"sLen":0,"time":94044.9438202248,"type":0},{"id":0,"sLen":0,"time":94382.0224719102,"type":0},{"id":3,"sLen":0,"time":94719.1011235956,"type":0},{"id":0,"sLen":0,"time":95224.7191011236,"type":0},{"id":3,"sLen":0,"time":95393.2584269663,"type":0},{"id":0,"sLen":0,"time":95730.3370786517,"type":0},{"id":0,"sLen":0,"time":96067.4157303371,"type":0},{"id":0,"sLen":0,"time":96404.4943820225,"type":0},{"id":0,"sLen":0,"time":96573.0337078652,"type":0},{"id":3,"sLen":0,"time":96741.5730337079,"type":0},{"id":3,"sLen":0,"time":96825.8426966292,"type":0},{"id":3,"sLen":0,"time":96910.1123595505,"type":0},{"id":0,"sLen":0,"time":97078.6516853933,"type":0},{"id":3,"sLen":0,"time":97415.7303370787,"type":0},{"id":3,"sLen":0,"time":97752.808988764,"type":0},{"id":0,"sLen":0,"time":97921.3483146067,"type":0},{"id":3,"sLen":0,"time":98089.8876404494,"type":0},{"id":0,"sLen":0,"time":98426.9662921348,"type":0},{"id":3,"sLen":0,"time":98764.0449438202,"type":0},{"id":0,"sLen":0,"time":99101.1235955056,"type":0},{"id":0,"sLen":0,"time":99269.6629213483,"type":0},{"id":3,"sLen":0,"time":99438.202247191,"type":0},{"id":0,"sLen":0,"time":99775.2808988764,"type":0},{"id":3,"sLen":0,"time":100112.359550562,"type":0},{"id":3,"sLen":0,"time":100449.438202247,"type":0},{"id":0,"sLen":0,"time":100617.97752809,"type":0},{"id":3,"sLen":0,"time":100786.516853933,"type":0},{"id":0,"sLen":0,"time":101123.595505618,"type":0},{"id":3,"sLen":0,"time":101460.674157303,"type":0},{"id":3,"sLen":0,"time":101797.752808989,"type":0},{"id":0,"sLen":0,"time":101966.292134831,"type":0},{"id":3,"sLen":0,"time":102134.831460674,"type":0},{"id":0,"sLen":0,"time":102471.91011236,"type":0},{"id":3,"sLen":0,"time":102808.988764045,"type":0},{"id":3,"sLen":0,"time":103146.06741573,"type":0},{"id":0,"sLen":0,"time":103314.606741573,"type":0},{"id":3,"sLen":0,"time":103483.146067416,"type":0},{"id":0,"sLen":0,"time":103820.224719101,"type":0},{"id":3,"sLen":0,"time":104157.303370787,"type":0},{"id":0,"sLen":0,"time":104494.382022472,"type":0},{"id":0,"sLen":0,"time":104662.921348315,"type":0},{"id":3,"sLen":0,"time":104831.460674157,"type":0},{"id":0,"sLen":0,"time":105168.539325843,"type":0},{"id":3,"sLen":0,"time":105505.617977528,"type":0},{"id":3,"sLen":0,"time":105842.696629213,"type":0},{"id":0,"sLen":0,"time":106011.235955056,"type":0},{"id":3,"sLen":0,"time":106179.775280899,"type":0},{"id":0,"sLen":0,"time":106516.853932584,"type":0},{"id":0,"sLen":0,"time":106853.93258427,"type":0},{"id":0,"sLen":0,"time":107191.011235955,"type":0},{"id":3,"sLen":0,"time":107528.08988764,"type":0},{"id":0,"sLen":0,"time":107865.168539326,"type":0},{"id":0,"sLen":0,"time":108202.247191011,"type":0},{"id":0,"sLen":0,"time":108539.325842697,"type":0},{"id":0,"sLen":0,"time":108876.404494382,"type":0},{"id":3,"sLen":0,"time":109213.483146068,"type":0},{"id":3,"sLen":0,"time":109550.561797753,"type":0},{"id":3,"sLen":0,"time":109887.640449438,"type":0},{"id":3,"sLen":0,"time":110224.719101124,"type":0},{"id":0,"sLen":0,"time":110561.797752809,"type":0},{"id":0,"sLen":0,"time":110730.337078652,"type":0},{"id":0,"sLen":0,"time":110898.876404495,"type":0},{"id":3,"sLen":0,"time":111235.95505618,"type":0},{"id":3,"sLen":0,"time":111573.033707865,"type":0},{"id":3,"sLen":0,"time":111910.112359551,"type":0},{"id":3,"sLen":0,"time":112247.191011236,"type":0},{"id":0,"sLen":0,"time":112584.269662922,"type":0},{"id":3,"sLen":0,"time":112921.348314607,"type":0},{"id":0,"sLen":0,"time":113258.426966292,"type":0},{"id":3,"sLen":0,"time":113932.584269663,"type":0},{"id":0,"sLen":0,"time":114269.662921349,"type":0},{"id":3,"sLen":0,"time":114438.202247191,"type":0},{"id":0,"sLen":0,"time":114606.741573034,"type":0},{"id":3,"sLen":0,"time":114943.820224719,"type":0},{"id":0,"sLen":0,"time":115280.898876405,"type":0},{"id":3,"sLen":0,"time":115617.97752809,"type":0},{"id":3,"sLen":0,"time":115786.516853933,"type":0},{"id":0,"sLen":0,"time":115955.056179776,"type":0},{"id":3,"sLen":0,"time":116123.595505618,"type":0},{"id":0,"sLen":0,"time":116292.134831461,"type":0},{"id":0,"sLen":0,"time":116629.213483146,"type":0},{"id":3,"sLen":0,"time":116966.292134832,"type":0},{"id":3,"sLen":0,"time":117303.370786517,"type":0},{"id":0,"sLen":0,"time":117640.449438203,"type":0},{"id":3,"sLen":0,"time":117977.528089888,"type":0},{"id":3,"sLen":0,"time":118061.797752809,"type":0},{"id":0,"sLen":0,"time":118314.606741573,"type":0},{"id":3,"sLen":0,"time":118398.876404495,"type":0},{"id":0,"sLen":0,"time":118651.685393258,"type":0},{"id":3,"sLen":0,"time":118820.224719101,"type":0},{"id":3,"sLen":0,"time":118904.494382022,"type":0},{"id":3,"sLen":0,"time":119073.033707865,"type":0},{"id":0,"sLen":0,"time":119241.573033708,"type":0},{"id":3,"sLen":0,"time":119410.112359551,"type":0},{"id":3,"sLen":0,"time":119494.382022472,"type":0},{"id":0,"sLen":0,"time":119662.921348315,"type":0},{"id":0,"sLen":0,"time":119831.460674157,"type":0},{"id":0,"sLen":0,"time":119915.730337079,"type":0},{"id":3,"sLen":0,"time":120000,"type":0},{"id":0,"sLen":0,"time":120168.539325843,"type":0},{"id":0,"sLen":0,"time":120252.808988764,"type":0},{"id":3,"sLen":0,"time":120505.617977528,"type":0},{"id":0,"sLen":0,"time":120674.157303371,"type":0},{"id":3,"sLen":0,"time":120758.426966292,"type":0},{"id":3,"sLen":0,"time":120842.696629213,"type":0},{"id":0,"sLen":0,"time":121011.235955056,"type":0},{"id":0,"sLen":0,"time":121095.505617978,"type":0},{"id":0,"sLen":0,"time":121179.775280899,"type":0}],"visible":false,"type":2,"characters":["pico-speakers"]}],"scrollSpeed":2.6,"chartVersion":"1.6.0","stage":"tank","noteTypes":["No Anim Note"]} \ No newline at end of file diff --git a/assets/songs/stress/charts/normal.json b/assets/songs/stress/charts/normal.json index 111dc971..58f18c9f 100644 --- a/assets/songs/stress/charts/normal.json +++ b/assets/songs/stress/charts/normal.json @@ -1 +1 @@ -{"events":[{"params":[1],"time":5393.25842696629,"name":"Camera Movement"},{"params":[0],"time":10786.5168539326,"name":"Camera Movement"},{"params":[1],"time":14831.4606741573,"name":"Camera Movement"},{"params":[0],"time":21573.0337078652,"name":"Camera Movement"},{"params":[1],"time":24269.6629213483,"name":"Camera Movement"},{"params":[0],"time":26966.2921348315,"name":"Camera Movement"},{"params":[1],"time":28314.606741573,"name":"Camera Movement"},{"params":[0],"time":31011.2359550562,"name":"Camera Movement"},{"params":[1],"time":37752.808988764,"name":"Camera Movement"},{"params":[0],"time":43146.0674157303,"name":"Camera Movement"},{"params":[1],"time":48539.3258426966,"name":"Camera Movement"},{"params":[0],"time":53932.5842696629,"name":"Camera Movement"},{"params":[1],"time":59325.8426966292,"name":"Camera Movement"},{"params":[0],"time":62022.4719101124,"name":"Camera Movement"},{"params":[0,"good!"],"time":62022.4719101124,"name":"Play Animation"},{"params":[1],"time":67415.7303370787,"name":"Camera Movement"},{"params":[0],"time":75505.6179775281,"name":"Camera Movement"},{"params":[1],"time":80898.8764044945,"name":"Camera Movement"},{"params":[0],"time":86292.1348314608,"name":"Camera Movement"},{"params":[1],"time":91685.3932584271,"name":"Camera Movement"},{"params":[0],"time":97078.6516853934,"name":"Camera Movement"},{"params":[1],"time":102471.91011236,"name":"Camera Movement"},{"params":[0],"time":107865.168539326,"name":"Camera Movement"},{"params":[1],"time":113258.426966292,"name":"Camera Movement"}],"scrollSpeed":2.2,"strumLines":[{"notes":[{"id":2,"sLen":0,"time":0,"type":0},{"id":3,"sLen":0,"time":168.539322,"type":0},{"id":0,"sLen":0,"time":337.078644,"type":0},{"id":0,"sLen":0,"time":674.1573,"type":0},{"id":3,"sLen":0,"time":842.696655,"type":0},{"id":2,"sLen":0,"time":1011.23596,"type":0},{"id":1,"sLen":0,"time":1348.31458,"type":0},{"id":0,"sLen":0,"time":1516.85388,"type":0},{"id":3,"sLen":0,"time":1685.39331,"type":0},{"id":2,"sLen":0,"time":1853.93262,"type":0},{"id":0,"sLen":0,"time":2022.47192,"type":0},{"id":3,"sLen":0,"time":2191.01123,"type":0},{"id":1,"sLen":0,"time":2359.55054,"type":0},{"id":0,"sLen":0,"time":2528.08984,"type":0},{"id":2,"sLen":0,"time":2696.62915,"type":0},{"id":3,"sLen":0,"time":2865.16846,"type":0},{"id":0,"sLen":0,"time":3033.70776,"type":0},{"id":0,"sLen":0,"time":3370.78662,"type":0},{"id":3,"sLen":0,"time":3539.326,"type":0},{"id":2,"sLen":0,"time":3707.86523,"type":0},{"id":1,"sLen":0,"time":4044.94385,"type":0},{"id":0,"sLen":0,"time":4213.483,"type":0},{"id":3,"sLen":0,"time":4382.02246,"type":0},{"id":2,"sLen":0,"time":4550.562,"type":0},{"id":0,"sLen":0,"time":4719.101,"type":0},{"id":3,"sLen":0,"time":4887.64063,"type":0},{"id":1,"sLen":0,"time":5056.17969,"type":0},{"id":0,"sLen":0,"time":5224.719,"type":0},{"id":0,"sLen":0,"time":11123.5957,"type":0},{"id":1,"sLen":0,"time":11292.1348,"type":0},{"id":3,"sLen":252.808989999998,"time":11460.6738,"type":0},{"id":1,"sLen":252.80899,"time":11797.7529,"type":0},{"id":0,"sLen":0,"time":12134.8311,"type":0},{"id":0,"sLen":0,"time":12471.91,"type":0},{"id":0,"sLen":0,"time":12808.9883,"type":0},{"id":0,"sLen":0,"time":12977.5283,"type":0},{"id":3,"sLen":0,"time":13146.0674,"type":0},{"id":1,"sLen":0,"time":13483.1465,"type":0},{"id":0,"sLen":0,"time":13651.6855,"type":0},{"id":3,"sLen":0,"time":13820.2246,"type":0},{"id":1,"sLen":0,"time":13988.7637,"type":0},{"id":0,"sLen":0,"time":14157.3037,"type":0},{"id":3,"sLen":0,"time":14325.8428,"type":0},{"id":1,"sLen":0,"time":14494.3818,"type":0},{"id":0,"sLen":0,"time":14662.9209,"type":0},{"id":1,"sLen":0,"time":14831.4609,"type":0},{"id":0,"sLen":0,"time":15000,"type":0},{"id":3,"sLen":0,"time":15168.5391,"type":0},{"id":1,"sLen":0,"time":15337.0791,"type":0},{"id":0,"sLen":0,"time":15505.6182,"type":0},{"id":3,"sLen":0,"time":15674.1572,"type":0},{"id":1,"sLen":0,"time":15842.6963,"type":0},{"id":0,"sLen":0,"time":16011.2363,"type":0},{"id":3,"sLen":0,"time":21573.0332,"type":0},{"id":2,"sLen":0,"time":21741.5723,"type":0},{"id":0,"sLen":0,"time":21910.1133,"type":0},{"id":2,"sLen":0,"time":22078.6523,"type":0},{"id":3,"sLen":0,"time":22247.1914,"type":0},{"id":2,"sLen":0,"time":22415.73,"type":0},{"id":0,"sLen":0,"time":22584.27,"type":0},{"id":2,"sLen":0,"time":22752.8086,"type":0},{"id":3,"sLen":0,"time":22921.3477,"type":0},{"id":2,"sLen":0,"time":23089.8867,"type":0},{"id":3,"sLen":0,"time":23258.4277,"type":0},{"id":2,"sLen":0,"time":23426.9668,"type":0},{"id":3,"sLen":0,"time":23595.5059,"type":0},{"id":0,"sLen":0,"time":23764.0449,"type":0},{"id":1,"sLen":0,"time":23932.584,"type":0},{"id":0,"sLen":0,"time":24101.123,"type":0},{"id":2,"sLen":0,"time":24269.6621,"type":0},{"id":3,"sLen":0,"time":26966.293,"type":0},{"id":2,"sLen":0,"time":27134.832,"type":0},{"id":0,"sLen":0,"time":27303.3711,"type":0},{"id":2,"sLen":0,"time":27471.91,"type":0},{"id":3,"sLen":0,"time":27640.45,"type":0},{"id":2,"sLen":0,"time":27808.9883,"type":0},{"id":0,"sLen":0,"time":27977.5273,"type":0},{"id":2,"sLen":0,"time":28146.0684,"type":0},{"id":3,"sLen":0,"time":28314.6074,"type":0},{"id":2,"sLen":0,"time":28483.1465,"type":0},{"id":3,"sLen":0,"time":28651.6855,"type":0},{"id":2,"sLen":0,"time":28820.2246,"type":0},{"id":3,"sLen":0,"time":28988.7637,"type":0},{"id":0,"sLen":0,"time":29157.3027,"type":0},{"id":1,"sLen":0,"time":29325.8418,"type":0},{"id":0,"sLen":0,"time":29494.3828,"type":0},{"id":0,"sLen":0,"time":29662.9219,"type":0},{"id":0,"sLen":0,"time":30000,"type":0},{"id":0,"sLen":0,"time":30337.0781,"type":0},{"id":0,"sLen":0,"time":30674.1582,"type":0},{"id":0,"sLen":0,"time":31011.2363,"type":0},{"id":0,"sLen":0,"time":31348.3145,"type":0},{"id":0,"sLen":0,"time":31685.3926,"type":0},{"id":0,"sLen":0,"time":31853.9316,"type":0},{"id":0,"sLen":0,"time":32022.4727,"type":0},{"id":0,"sLen":0,"time":32191.0117,"type":0},{"id":0,"sLen":0,"time":32359.55,"type":0},{"id":2,"sLen":0,"time":32696.6289,"type":0},{"id":3,"sLen":0,"time":32865.168,"type":0},{"id":0,"sLen":0,"time":33033.707,"type":0},{"id":2,"sLen":0,"time":33370.7852,"type":0},{"id":3,"sLen":0,"time":33539.3242,"type":0},{"id":1,"sLen":0,"time":33707.8633,"type":0},{"id":3,"sLen":0,"time":33876.4063,"type":0},{"id":1,"sLen":0,"time":34044.9453,"type":0},{"id":0,"sLen":0,"time":34213.4844,"type":0},{"id":1,"sLen":0,"time":34382.0234,"type":0},{"id":3,"sLen":0,"time":34550.5625,"type":0},{"id":1,"sLen":0,"time":34719.1,"type":0},{"id":0,"sLen":0,"time":34887.64,"type":0},{"id":0,"sLen":0,"time":35056.18,"type":0},{"id":2,"sLen":0,"time":35393.2578,"type":0},{"id":3,"sLen":0,"time":35561.7969,"type":0},{"id":0,"sLen":0,"time":35730.3359,"type":0},{"id":2,"sLen":0,"time":36067.4141,"type":0},{"id":3,"sLen":0,"time":36235.9531,"type":0},{"id":3,"sLen":0,"time":36404.4961,"type":0},{"id":1,"sLen":0,"time":36573.0352,"type":0},{"id":0,"sLen":0,"time":36741.5742,"type":0},{"id":1,"sLen":0,"time":36910.1133,"type":0},{"id":0,"sLen":0,"time":37078.6523,"type":0},{"id":1,"sLen":0,"time":37247.19,"type":0},{"id":1,"sLen":0,"time":37415.73,"type":0},{"id":0,"sLen":0,"time":37584.27,"type":0},{"id":3,"sLen":0,"time":43146.0664,"type":0},{"id":1,"sLen":0,"time":43314.6055,"type":0},{"id":0,"sLen":0,"time":43483.1445,"type":0},{"id":3,"sLen":0,"time":43651.6836,"type":0},{"id":2,"sLen":0,"time":43820.2266,"type":0},{"id":0,"sLen":0,"time":44157.3047,"type":0},{"id":3,"sLen":0,"time":44325.8438,"type":0},{"id":2,"sLen":0,"time":44494.3828,"type":0},{"id":3,"sLen":0,"time":44662.92,"type":0},{"id":0,"sLen":0,"time":44831.46,"type":0},{"id":2,"sLen":0,"time":45000,"type":0},{"id":3,"sLen":0,"time":45168.54,"type":0},{"id":2,"sLen":0,"time":45337.08,"type":0},{"id":3,"sLen":0,"time":45505.6172,"type":0},{"id":1,"sLen":0,"time":45674.1563,"type":0},{"id":0,"sLen":0,"time":45842.6953,"type":0},{"id":3,"sLen":0,"time":46011.2344,"type":0},{"id":1,"sLen":0,"time":46179.7734,"type":0},{"id":3,"sLen":0,"time":46348.3164,"type":0},{"id":0,"sLen":0,"time":46516.8555,"type":0},{"id":3,"sLen":0,"time":46685.3945,"type":0},{"id":1,"sLen":0,"time":46853.9336,"type":0},{"id":3,"sLen":0,"time":47022.4727,"type":0},{"id":2,"sLen":0,"time":47191.01,"type":0},{"id":1,"sLen":0,"time":47528.09,"type":0},{"id":1,"sLen":0,"time":47865.168,"type":0},{"id":1,"sLen":0,"time":48202.2461,"type":0},{"id":2,"sLen":0,"time":53932.5859,"type":0},{"id":3,"sLen":0,"time":54101.125,"type":0},{"id":0,"sLen":0,"time":54269.6641,"type":0},{"id":0,"sLen":0,"time":54606.7422,"type":0},{"id":3,"sLen":0,"time":54775.28,"type":0},{"id":2,"sLen":0,"time":54943.82,"type":0},{"id":1,"sLen":0,"time":55280.9,"type":0},{"id":0,"sLen":0,"time":55449.4375,"type":0},{"id":3,"sLen":0,"time":55617.9766,"type":0},{"id":2,"sLen":0,"time":55786.5156,"type":0},{"id":0,"sLen":0,"time":55955.0547,"type":0},{"id":3,"sLen":0,"time":56123.5938,"type":0},{"id":1,"sLen":0,"time":56292.1367,"type":0},{"id":0,"sLen":0,"time":56460.6758,"type":0},{"id":2,"sLen":0,"time":56629.2148,"type":0},{"id":3,"sLen":0,"time":56797.7539,"type":0},{"id":0,"sLen":0,"time":56966.293,"type":0},{"id":0,"sLen":0,"time":57303.37,"type":0},{"id":3,"sLen":0,"time":57471.91,"type":0},{"id":2,"sLen":0,"time":57640.45,"type":0},{"id":1,"sLen":0,"time":57977.5273,"type":0},{"id":0,"sLen":0,"time":58146.0664,"type":0},{"id":3,"sLen":0,"time":58314.6055,"type":0},{"id":2,"sLen":0,"time":58483.1445,"type":0},{"id":0,"sLen":0,"time":58651.6836,"type":0},{"id":3,"sLen":0,"time":58820.2266,"type":0},{"id":1,"sLen":84.2696599999908,"time":58988.7656,"type":0},{"id":0,"sLen":0,"time":59157.3047,"type":0},{"id":0,"sLen":168.539321999997,"time":59325.8438,"type":0},{"id":0,"sLen":168.539321999997,"time":60000,"type":0},{"id":0,"sLen":168.539322000004,"time":60674.1563,"type":0},{"id":0,"sLen":168.539321999997,"time":61348.3164,"type":0},{"id":1,"sLen":0,"time":62022.4727,"type":1},{"id":1,"sLen":252.808989999998,"time":64719.1,"type":0},{"id":0,"sLen":252.808989999998,"time":65056.18,"type":0},{"id":3,"sLen":252.808989999998,"time":65393.2578,"type":0},{"id":1,"sLen":252.808990000005,"time":65730.3359,"type":0},{"id":0,"sLen":0,"time":66067.4141,"type":0},{"id":0,"sLen":0,"time":66404.49,"type":0},{"id":0,"sLen":0,"time":66741.57,"type":0},{"id":0,"sLen":0,"time":66910.11,"type":0},{"id":2,"sLen":0,"time":67078.65,"type":0},{"id":1,"sLen":0,"time":67415.73,"type":0},{"id":0,"sLen":0,"time":67584.27,"type":0},{"id":3,"sLen":0,"time":67752.81,"type":0},{"id":1,"sLen":0,"time":67921.35,"type":0},{"id":0,"sLen":0,"time":68089.89,"type":0},{"id":3,"sLen":0,"time":68258.43,"type":0},{"id":1,"sLen":0,"time":68426.97,"type":0},{"id":3,"sLen":0,"time":68595.51,"type":0},{"id":1,"sLen":0,"time":68764.05,"type":0},{"id":0,"sLen":0,"time":68932.5859,"type":0},{"id":3,"sLen":0,"time":69101.125,"type":0},{"id":2,"sLen":0,"time":69269.6641,"type":0},{"id":0,"sLen":0,"time":69438.2,"type":0},{"id":3,"sLen":0,"time":69606.74,"type":0},{"id":1,"sLen":0,"time":69775.28,"type":0},{"id":0,"sLen":0,"time":69943.82,"type":0},{"id":0,"sLen":0,"time":75505.62,"type":0},{"id":0,"sLen":0,"time":75674.16,"type":0},{"id":0,"sLen":0,"time":75842.6953,"type":0},{"id":2,"sLen":0,"time":76011.2344,"type":0},{"id":3,"sLen":0,"time":76179.77,"type":0},{"id":2,"sLen":0,"time":76348.31,"type":0},{"id":0,"sLen":0,"time":76516.85,"type":0},{"id":2,"sLen":0,"time":76685.39,"type":0},{"id":3,"sLen":0,"time":76853.93,"type":0},{"id":0,"sLen":0,"time":77022.47,"type":0},{"id":3,"sLen":0,"time":77191.01,"type":0},{"id":0,"sLen":0,"time":77359.55,"type":0},{"id":1,"sLen":252.808990000005,"time":77528.0859,"type":0},{"id":0,"sLen":0,"time":77865.17,"type":0},{"id":0,"sLen":0,"time":78033.71,"type":0},{"id":1,"sLen":0,"time":78202.25,"type":0},{"id":0,"sLen":0,"time":78370.79,"type":0},{"id":3,"sLen":0,"time":78539.33,"type":0},{"id":1,"sLen":0,"time":78876.41,"type":0},{"id":0,"sLen":0,"time":79044.9453,"type":0},{"id":2,"sLen":0,"time":79213.4844,"type":0},{"id":0,"sLen":0,"time":79382.02,"type":0},{"id":1,"sLen":0,"time":79550.56,"type":0},{"id":0,"sLen":0,"time":79803.37,"type":0},{"id":1,"sLen":0,"time":80056.18,"type":0},{"id":0,"sLen":0,"time":80224.72,"type":0},{"id":3,"sLen":0,"time":80393.26,"type":0},{"id":0,"sLen":0,"time":80561.8,"type":0},{"id":2,"sLen":0,"time":80730.3359,"type":0},{"id":0,"sLen":0,"time":86292.13,"type":0},{"id":0,"sLen":0,"time":86460.67,"type":0},{"id":1,"sLen":0,"time":86629.21,"type":0},{"id":1,"sLen":0,"time":86797.75,"type":0},{"id":3,"sLen":0,"time":86966.29,"type":0},{"id":3,"sLen":0,"time":87134.83,"type":0},{"id":0,"sLen":0,"time":87303.37,"type":0},{"id":1,"sLen":0,"time":87471.91,"type":0},{"id":0,"sLen":0,"time":87640.45,"type":0},{"id":0,"sLen":0,"time":87808.99,"type":0},{"id":1,"sLen":0,"time":87977.53,"type":0},{"id":1,"sLen":0,"time":88146.07,"type":0},{"id":3,"sLen":0,"time":88314.61,"type":0},{"id":3,"sLen":0,"time":88483.15,"type":0},{"id":0,"sLen":0,"time":88651.69,"type":0},{"id":3,"sLen":0,"time":88820.23,"type":0},{"id":2,"sLen":0,"time":88988.7656,"type":0},{"id":0,"sLen":0,"time":89157.3047,"type":0},{"id":3,"sLen":0,"time":89325.84,"type":0},{"id":2,"sLen":0,"time":89494.38,"type":0},{"id":0,"sLen":0,"time":89662.92,"type":0},{"id":0,"sLen":0,"time":89831.46,"type":0},{"id":3,"sLen":0,"time":90000,"type":0},{"id":1,"sLen":0,"time":90168.54,"type":0},{"id":0,"sLen":0,"time":90337.08,"type":0},{"id":1,"sLen":0,"time":90505.62,"type":0},{"id":3,"sLen":0,"time":90674.16,"type":0},{"id":1,"sLen":0,"time":90842.6953,"type":0},{"id":0,"sLen":0,"time":91011.2344,"type":0},{"id":1,"sLen":0,"time":91179.77,"type":0},{"id":3,"sLen":337.078643999994,"time":91348.31,"type":0},{"id":2,"sLen":0,"time":97078.65,"type":0},{"id":0,"sLen":0,"time":97247.19,"type":0},{"id":3,"sLen":0,"time":97415.73,"type":0},{"id":2,"sLen":0,"time":97584.27,"type":0},{"id":0,"sLen":0,"time":97752.81,"type":0},{"id":3,"sLen":0,"time":97921.35,"type":0},{"id":2,"sLen":0,"time":98089.89,"type":0},{"id":0,"sLen":0,"time":98258.43,"type":0},{"id":3,"sLen":0,"time":98426.97,"type":0},{"id":2,"sLen":0,"time":98595.51,"type":0},{"id":0,"sLen":0,"time":98764.05,"type":0},{"id":3,"sLen":0,"time":98932.5859,"type":0},{"id":1,"sLen":0,"time":99101.125,"type":0},{"id":3,"sLen":0,"time":99269.6641,"type":0},{"id":0,"sLen":0,"time":99438.2,"type":0},{"id":3,"sLen":0,"time":99606.74,"type":0},{"id":2,"sLen":0,"time":99775.28,"type":0},{"id":0,"sLen":0,"time":99943.82,"type":0},{"id":3,"sLen":0,"time":100112.359,"type":0},{"id":2,"sLen":0,"time":100280.9,"type":0},{"id":0,"sLen":0,"time":100449.438,"type":0},{"id":3,"sLen":0,"time":100617.977,"type":0},{"id":2,"sLen":0,"time":100786.516,"type":0},{"id":0,"sLen":0,"time":100955.055,"type":0},{"id":3,"sLen":0,"time":101123.594,"type":0},{"id":2,"sLen":0,"time":101292.133,"type":0},{"id":0,"sLen":0,"time":101460.672,"type":0},{"id":3,"sLen":0,"time":101629.211,"type":0},{"id":1,"sLen":0,"time":101797.75,"type":0},{"id":3,"sLen":0,"time":101966.289,"type":0},{"id":0,"sLen":0,"time":102134.828,"type":0},{"id":3,"sLen":0,"time":102303.367,"type":0},{"id":2,"sLen":0,"time":102471.906,"type":0},{"id":0,"sLen":0,"time":102640.453,"type":0},{"id":3,"sLen":0,"time":102808.992,"type":0},{"id":2,"sLen":0,"time":102977.531,"type":0},{"id":0,"sLen":0,"time":103146.07,"type":0},{"id":3,"sLen":0,"time":103314.609,"type":0},{"id":2,"sLen":0,"time":103483.148,"type":0},{"id":0,"sLen":0,"time":103651.688,"type":0},{"id":3,"sLen":0,"time":103820.227,"type":0},{"id":2,"sLen":0,"time":103988.766,"type":0},{"id":0,"sLen":0,"time":104157.305,"type":0},{"id":3,"sLen":0,"time":104325.844,"type":0},{"id":1,"sLen":0,"time":104494.383,"type":0},{"id":3,"sLen":0,"time":104662.922,"type":0},{"id":0,"sLen":0,"time":104831.461,"type":0},{"id":3,"sLen":0,"time":105000,"type":0},{"id":2,"sLen":0,"time":105168.539,"type":0},{"id":0,"sLen":0,"time":105337.078,"type":0},{"id":3,"sLen":0,"time":105505.617,"type":0},{"id":2,"sLen":0,"time":105674.156,"type":0},{"id":0,"sLen":0,"time":105842.695,"type":0},{"id":3,"sLen":0,"time":106011.234,"type":0},{"id":2,"sLen":0,"time":106179.773,"type":0},{"id":0,"sLen":0,"time":106348.313,"type":0},{"id":3,"sLen":0,"time":106516.852,"type":0},{"id":2,"sLen":0,"time":106685.391,"type":0},{"id":0,"sLen":0,"time":106853.93,"type":0},{"id":3,"sLen":0,"time":107022.469,"type":0},{"id":1,"sLen":0,"time":107191.008,"type":0},{"id":3,"sLen":0,"time":107359.547,"type":0},{"id":0,"sLen":0,"time":107528.086,"type":0},{"id":3,"sLen":0,"time":107696.633,"type":0},{"id":1,"sLen":0,"time":107865.172,"type":0},{"id":0,"sLen":0,"time":107949.438,"type":0},{"id":1,"sLen":0,"time":108033.711,"type":0},{"id":1,"sLen":0,"time":108202.25,"type":0},{"id":0,"sLen":0,"time":108370.789,"type":0},{"id":1,"sLen":0,"time":108539.328,"type":0},{"id":0,"sLen":0,"time":108623.594,"type":0},{"id":1,"sLen":0,"time":108707.867,"type":0},{"id":1,"sLen":0,"time":108876.406,"type":0},{"id":0,"sLen":0,"time":109044.945,"type":0},{"id":1,"sLen":0,"time":109213.484,"type":0},{"id":3,"sLen":0,"time":109297.75,"type":0},{"id":1,"sLen":0,"time":109382.023,"type":0},{"id":1,"sLen":0,"time":109550.563,"type":0},{"id":0,"sLen":0,"time":109719.1,"type":0},{"id":1,"sLen":0,"time":109887.641,"type":0},{"id":0,"sLen":0,"time":109971.906,"type":0},{"id":1,"sLen":0,"time":110056.18,"type":0},{"id":1,"sLen":0,"time":110224.719,"type":0},{"id":0,"sLen":0,"time":110393.258,"type":0},{"id":2,"sLen":0,"time":113258.43,"type":0},{"id":3,"sLen":0,"time":113426.969,"type":0},{"id":2,"sLen":0,"time":113595.508,"type":0},{"id":3,"sLen":0,"time":113764.047,"type":0},{"id":0,"sLen":0,"time":113932.586,"type":0},{"id":1,"sLen":0,"time":114101.125,"type":0},{"id":0,"sLen":0,"time":114269.664,"type":0},{"id":1,"sLen":0,"time":114438.2,"type":0},{"id":2,"sLen":0,"time":114606.742,"type":0},{"id":3,"sLen":0,"time":114775.281,"type":0},{"id":2,"sLen":0,"time":114943.82,"type":0},{"id":3,"sLen":0,"time":115112.359,"type":0},{"id":0,"sLen":0,"time":115280.9,"type":0},{"id":3,"sLen":0,"time":115449.438,"type":0},{"id":0,"sLen":0,"time":115617.977,"type":0},{"id":1,"sLen":0,"time":115786.516,"type":0},{"id":2,"sLen":589.887633999984,"time":115955.055,"type":0}],"position":"dad","characters":["tankman"],"visible":true,"type":0},{"notes":[{"id":2,"sLen":0,"time":5393.2583,"type":0},{"id":3,"sLen":0,"time":5561.798,"type":0},{"id":0,"sLen":0,"time":5730.337,"type":0},{"id":0,"sLen":0,"time":6067.41553,"type":0},{"id":3,"sLen":0,"time":6235.955,"type":0},{"id":2,"sLen":0,"time":6404.494,"type":0},{"id":1,"sLen":0,"time":6741.573,"type":0},{"id":0,"sLen":0,"time":6910.1123,"type":0},{"id":3,"sLen":0,"time":7078.652,"type":0},{"id":2,"sLen":0,"time":7247.191,"type":0},{"id":0,"sLen":0,"time":7415.73047,"type":0},{"id":3,"sLen":0,"time":7584.26953,"type":0},{"id":1,"sLen":0,"time":7752.809,"type":0},{"id":0,"sLen":0,"time":7921.348,"type":0},{"id":2,"sLen":0,"time":8089.8877,"type":0},{"id":3,"sLen":0,"time":8258.427,"type":0},{"id":0,"sLen":0,"time":8426.966,"type":0},{"id":0,"sLen":0,"time":8764.045,"type":0},{"id":3,"sLen":0,"time":8932.584,"type":0},{"id":2,"sLen":0,"time":9101.124,"type":0},{"id":1,"sLen":0,"time":9438.202,"type":0},{"id":0,"sLen":0,"time":9606.741,"type":0},{"id":3,"sLen":0,"time":9775.281,"type":0},{"id":2,"sLen":0,"time":9943.82,"type":0},{"id":0,"sLen":0,"time":10112.3594,"type":0},{"id":3,"sLen":0,"time":10280.8984,"type":0},{"id":1,"sLen":0,"time":10449.4385,"type":0},{"id":0,"sLen":0,"time":10617.9775,"type":0},{"id":0,"sLen":252.808990000001,"time":14831.4609,"type":0},{"id":1,"sLen":252.80899,"time":15168.5391,"type":0},{"id":3,"sLen":252.80899,"time":15505.6182,"type":0},{"id":1,"sLen":252.808990000001,"time":15842.6963,"type":0},{"id":3,"sLen":252.80899,"time":16179.7754,"type":0},{"id":0,"sLen":0,"time":16516.8535,"type":0},{"id":1,"sLen":0,"time":16685.3926,"type":0},{"id":3,"sLen":252.808990000001,"time":16853.9316,"type":0},{"id":1,"sLen":252.808990000001,"time":17191.0117,"type":0},{"id":0,"sLen":0,"time":17528.09,"type":0},{"id":0,"sLen":0,"time":17865.168,"type":0},{"id":0,"sLen":0,"time":18202.248,"type":0},{"id":0,"sLen":0,"time":18370.7871,"type":0},{"id":3,"sLen":0,"time":18539.3262,"type":0},{"id":1,"sLen":0,"time":18876.4043,"type":0},{"id":0,"sLen":0,"time":19044.9434,"type":0},{"id":3,"sLen":0,"time":19213.4824,"type":0},{"id":1,"sLen":0,"time":19382.0215,"type":0},{"id":0,"sLen":0,"time":19550.5625,"type":0},{"id":3,"sLen":0,"time":19719.1016,"type":0},{"id":1,"sLen":0,"time":19887.64,"type":0},{"id":0,"sLen":0,"time":20056.18,"type":0},{"id":1,"sLen":0,"time":20224.7188,"type":0},{"id":0,"sLen":0,"time":20393.2578,"type":0},{"id":3,"sLen":0,"time":20561.7969,"type":0},{"id":1,"sLen":0,"time":20730.3379,"type":0},{"id":0,"sLen":0,"time":20898.877,"type":0},{"id":3,"sLen":0,"time":21067.416,"type":0},{"id":1,"sLen":0,"time":21235.9551,"type":0},{"id":0,"sLen":0,"time":21404.4941,"type":0},{"id":3,"sLen":0,"time":21573.0332,"type":0},{"id":3,"sLen":0,"time":21910.1133,"type":0},{"id":3,"sLen":0,"time":22247.1914,"type":0},{"id":3,"sLen":0,"time":22584.27,"type":0},{"id":1,"sLen":0,"time":22921.3477,"type":0},{"id":1,"sLen":0,"time":23258.4277,"type":0},{"id":3,"sLen":252.808990000001,"time":23595.5059,"type":0},{"id":0,"sLen":252.808990000009,"time":23932.584,"type":0},{"id":3,"sLen":0,"time":24269.6621,"type":0},{"id":2,"sLen":0,"time":24438.2031,"type":0},{"id":0,"sLen":0,"time":24606.7422,"type":0},{"id":2,"sLen":0,"time":24775.2813,"type":0},{"id":3,"sLen":0,"time":24943.82,"type":0},{"id":2,"sLen":0,"time":25112.36,"type":0},{"id":0,"sLen":0,"time":25280.8984,"type":0},{"id":2,"sLen":0,"time":25449.4375,"type":0},{"id":3,"sLen":0,"time":25617.9766,"type":0},{"id":2,"sLen":0,"time":25786.5176,"type":0},{"id":3,"sLen":0,"time":25955.0566,"type":0},{"id":2,"sLen":0,"time":26123.5957,"type":0},{"id":3,"sLen":0,"time":26292.1348,"type":0},{"id":0,"sLen":0,"time":26460.6738,"type":0},{"id":1,"sLen":0,"time":26629.2129,"type":0},{"id":0,"sLen":0,"time":26797.752,"type":0},{"id":2,"sLen":589.887634000006,"time":26966.293,"type":0},{"id":3,"sLen":589.887633999995,"time":27640.45,"type":0},{"id":1,"sLen":589.887633999999,"time":28314.6074,"type":0},{"id":0,"sLen":589.887634000006,"time":28988.7637,"type":0},{"id":3,"sLen":0,"time":29662.9219,"type":0},{"id":2,"sLen":0,"time":29831.46,"type":0},{"id":0,"sLen":0,"time":30000,"type":0},{"id":2,"sLen":0,"time":30168.54,"type":0},{"id":3,"sLen":0,"time":30337.0781,"type":0},{"id":2,"sLen":0,"time":30505.6172,"type":0},{"id":0,"sLen":0,"time":30674.1582,"type":0},{"id":2,"sLen":0,"time":30842.6973,"type":0},{"id":3,"sLen":0,"time":31011.2363,"type":0},{"id":2,"sLen":0,"time":31179.7754,"type":0},{"id":3,"sLen":0,"time":31348.3145,"type":0},{"id":2,"sLen":0,"time":31516.8535,"type":0},{"id":3,"sLen":0,"time":31685.3926,"type":0},{"id":0,"sLen":0,"time":31853.9316,"type":0},{"id":1,"sLen":0,"time":32022.4727,"type":0},{"id":0,"sLen":0,"time":32191.0117,"type":0},{"id":1,"sLen":674.157300000003,"time":32359.55,"type":0},{"id":0,"sLen":0,"time":37752.81,"type":0},{"id":2,"sLen":0,"time":38089.8867,"type":0},{"id":3,"sLen":0,"time":38258.4258,"type":0},{"id":0,"sLen":0,"time":38426.9648,"type":0},{"id":2,"sLen":0,"time":38764.043,"type":0},{"id":3,"sLen":0,"time":38932.5859,"type":0},{"id":1,"sLen":0,"time":39101.125,"type":0},{"id":3,"sLen":0,"time":39269.6641,"type":0},{"id":1,"sLen":0,"time":39438.2031,"type":0},{"id":0,"sLen":0,"time":39606.7422,"type":0},{"id":1,"sLen":0,"time":39775.28,"type":0},{"id":3,"sLen":0,"time":39943.82,"type":0},{"id":1,"sLen":0,"time":40112.36,"type":0},{"id":0,"sLen":0,"time":40280.9,"type":0},{"id":0,"sLen":0,"time":40449.4375,"type":0},{"id":2,"sLen":0,"time":40786.5156,"type":0},{"id":3,"sLen":0,"time":40955.0547,"type":0},{"id":0,"sLen":0,"time":41123.5938,"type":0},{"id":2,"sLen":0,"time":41460.6758,"type":0},{"id":3,"sLen":0,"time":41629.2148,"type":0},{"id":3,"sLen":0,"time":41797.7539,"type":0},{"id":1,"sLen":0,"time":41966.293,"type":0},{"id":0,"sLen":0,"time":42134.832,"type":0},{"id":1,"sLen":0,"time":42303.37,"type":0},{"id":0,"sLen":0,"time":42471.91,"type":0},{"id":1,"sLen":0,"time":42640.45,"type":0},{"id":1,"sLen":0,"time":42808.99,"type":0},{"id":0,"sLen":0,"time":42977.5273,"type":0},{"id":3,"sLen":0,"time":48539.3242,"type":0},{"id":1,"sLen":0,"time":48707.8633,"type":0},{"id":0,"sLen":0,"time":48876.4063,"type":0},{"id":3,"sLen":0,"time":49044.9453,"type":0},{"id":2,"sLen":0,"time":49213.4844,"type":0},{"id":0,"sLen":0,"time":49550.5625,"type":0},{"id":3,"sLen":0,"time":49719.1,"type":0},{"id":2,"sLen":0,"time":49887.64,"type":0},{"id":3,"sLen":0,"time":50056.18,"type":0},{"id":0,"sLen":0,"time":50224.72,"type":0},{"id":2,"sLen":0,"time":50393.2578,"type":0},{"id":3,"sLen":0,"time":50561.7969,"type":0},{"id":2,"sLen":0,"time":50730.3359,"type":0},{"id":3,"sLen":0,"time":50898.875,"type":0},{"id":1,"sLen":0,"time":51067.4141,"type":0},{"id":0,"sLen":0,"time":51235.9531,"type":0},{"id":3,"sLen":0,"time":51404.4961,"type":0},{"id":1,"sLen":0,"time":51573.0352,"type":0},{"id":3,"sLen":0,"time":51741.5742,"type":0},{"id":0,"sLen":0,"time":51910.1133,"type":0},{"id":3,"sLen":0,"time":52078.6523,"type":0},{"id":1,"sLen":0,"time":52247.19,"type":0},{"id":3,"sLen":0,"time":52415.73,"type":0},{"id":2,"sLen":0,"time":52584.27,"type":0},{"id":1,"sLen":0,"time":52921.3477,"type":0},{"id":1,"sLen":0,"time":53258.4258,"type":0},{"id":1,"sLen":0,"time":53595.5039,"type":0},{"id":1,"sLen":1011.23595999999,"time":53932.5859,"type":0},{"id":2,"sLen":505.617981000003,"time":55955.0547,"type":0},{"id":1,"sLen":674.157299999999,"time":56629.2148,"type":0},{"id":0,"sLen":252.808989999998,"time":58651.6836,"type":0},{"id":2,"sLen":84.2696599999908,"time":58988.7656,"type":0},{"id":0,"sLen":0,"time":59157.3047,"type":0},{"id":2,"sLen":0,"time":59325.8438,"type":0},{"id":3,"sLen":0,"time":59494.3828,"type":0},{"id":0,"sLen":0,"time":59662.92,"type":0},{"id":0,"sLen":0,"time":60000,"type":0},{"id":3,"sLen":0,"time":60168.54,"type":0},{"id":2,"sLen":0,"time":60337.08,"type":0},{"id":1,"sLen":0,"time":60674.1563,"type":0},{"id":0,"sLen":0,"time":60842.6953,"type":0},{"id":3,"sLen":0,"time":61011.2344,"type":0},{"id":2,"sLen":0,"time":61179.7734,"type":0},{"id":0,"sLen":0,"time":61348.3164,"type":0},{"id":3,"sLen":0,"time":61516.8555,"type":0},{"id":1,"sLen":0,"time":61685.3945,"type":0},{"id":0,"sLen":0,"time":61853.9336,"type":0},{"id":2,"sLen":0,"time":62022.4727,"type":0},{"id":3,"sLen":0,"time":62191.01,"type":0},{"id":0,"sLen":0,"time":62359.55,"type":0},{"id":0,"sLen":0,"time":62696.63,"type":0},{"id":3,"sLen":0,"time":62865.168,"type":0},{"id":2,"sLen":0,"time":63033.707,"type":0},{"id":1,"sLen":0,"time":63370.7852,"type":0},{"id":0,"sLen":0,"time":63539.3242,"type":0},{"id":3,"sLen":0,"time":63707.8633,"type":0},{"id":2,"sLen":0,"time":63876.4063,"type":0},{"id":0,"sLen":0,"time":64044.9453,"type":0},{"id":3,"sLen":0,"time":64213.4844,"type":0},{"id":1,"sLen":0,"time":64382.0234,"type":0},{"id":0,"sLen":0,"time":64550.5625,"type":0},{"id":1,"sLen":252.808989999998,"time":64719.1,"type":0},{"id":0,"sLen":252.808989999998,"time":65056.18,"type":0},{"id":3,"sLen":252.808989999998,"time":65393.2578,"type":0},{"id":1,"sLen":252.808990000005,"time":65730.3359,"type":0},{"id":3,"sLen":505.617981000003,"time":66067.4141,"type":0},{"id":1,"sLen":505.617981000003,"time":66741.57,"type":0},{"id":0,"sLen":505.617981000003,"time":67415.73,"type":0},{"id":1,"sLen":252.808990000005,"time":68089.89,"type":0},{"id":0,"sLen":252.808990000005,"time":68426.97,"type":0},{"id":3,"sLen":589.887633999999,"time":68764.05,"type":0},{"id":0,"sLen":589.887633999999,"time":69438.2,"type":0},{"id":1,"sLen":252.808990000005,"time":70112.36,"type":0},{"id":0,"sLen":252.808990000005,"time":70449.44,"type":0},{"id":3,"sLen":252.808990000005,"time":70786.5156,"type":0},{"id":1,"sLen":252.808990000005,"time":71123.59,"type":0},{"id":0,"sLen":0,"time":71460.67,"type":0},{"id":0,"sLen":0,"time":71797.75,"type":0},{"id":0,"sLen":0,"time":72134.83,"type":0},{"id":0,"sLen":0,"time":72303.37,"type":0},{"id":2,"sLen":0,"time":72471.91,"type":0},{"id":1,"sLen":0,"time":72808.99,"type":0},{"id":0,"sLen":0,"time":72977.53,"type":0},{"id":3,"sLen":0,"time":73146.07,"type":0},{"id":1,"sLen":0,"time":73314.61,"type":0},{"id":0,"sLen":0,"time":73483.15,"type":0},{"id":3,"sLen":0,"time":73651.69,"type":0},{"id":1,"sLen":0,"time":73820.23,"type":0},{"id":3,"sLen":0,"time":73988.7656,"type":0},{"id":1,"sLen":0,"time":74157.3047,"type":0},{"id":0,"sLen":0,"time":74325.84,"type":0},{"id":3,"sLen":0,"time":74494.38,"type":0},{"id":2,"sLen":0,"time":74662.92,"type":0},{"id":0,"sLen":0,"time":74831.46,"type":0},{"id":3,"sLen":0,"time":75000,"type":0},{"id":1,"sLen":0,"time":75168.54,"type":0},{"id":0,"sLen":0,"time":75337.08,"type":0},{"id":1,"sLen":1264.04492,"time":75505.62,"type":0},{"id":0,"sLen":1264.04492,"time":76853.93,"type":0},{"id":3,"sLen":1264.04492,"time":78202.25,"type":0},{"id":0,"sLen":1264.04492,"time":79550.56,"type":0},{"id":0,"sLen":0,"time":80898.875,"type":0},{"id":0,"sLen":0,"time":81067.4141,"type":0},{"id":0,"sLen":0,"time":81235.95,"type":0},{"id":2,"sLen":0,"time":81404.49,"type":0},{"id":3,"sLen":0,"time":81573.03,"type":0},{"id":2,"sLen":0,"time":81741.57,"type":0},{"id":0,"sLen":0,"time":81910.11,"type":0},{"id":2,"sLen":0,"time":82078.65,"type":0},{"id":3,"sLen":0,"time":82247.19,"type":0},{"id":0,"sLen":0,"time":82415.73,"type":0},{"id":3,"sLen":0,"time":82584.27,"type":0},{"id":0,"sLen":0,"time":82752.81,"type":0},{"id":1,"sLen":252.808990000005,"time":82921.35,"type":0},{"id":0,"sLen":0,"time":83258.43,"type":0},{"id":0,"sLen":0,"time":83426.97,"type":0},{"id":1,"sLen":0,"time":83595.51,"type":0},{"id":0,"sLen":0,"time":83764.05,"type":0},{"id":3,"sLen":0,"time":83932.5859,"type":0},{"id":1,"sLen":0,"time":84269.6641,"type":0},{"id":0,"sLen":0,"time":84438.2,"type":0},{"id":2,"sLen":0,"time":84606.74,"type":0},{"id":0,"sLen":0,"time":84775.28,"type":0},{"id":1,"sLen":0,"time":84943.82,"type":0},{"id":0,"sLen":0,"time":85196.63,"type":0},{"id":1,"sLen":0,"time":85449.44,"type":0},{"id":0,"sLen":0,"time":85617.98,"type":0},{"id":3,"sLen":0,"time":85786.5156,"type":0},{"id":0,"sLen":0,"time":85955.0547,"type":0},{"id":2,"sLen":0,"time":86123.59,"type":0},{"id":0,"sLen":0,"time":91685.39,"type":0},{"id":0,"sLen":0,"time":91853.93,"type":0},{"id":1,"sLen":0,"time":92022.47,"type":0},{"id":1,"sLen":0,"time":92191.01,"type":0},{"id":3,"sLen":0,"time":92359.55,"type":0},{"id":3,"sLen":0,"time":92528.0859,"type":0},{"id":0,"sLen":0,"time":92696.63,"type":0},{"id":1,"sLen":0,"time":92865.17,"type":0},{"id":0,"sLen":0,"time":93033.71,"type":0},{"id":0,"sLen":0,"time":93202.25,"type":0},{"id":1,"sLen":0,"time":93370.79,"type":0},{"id":1,"sLen":0,"time":93539.33,"type":0},{"id":3,"sLen":0,"time":93707.87,"type":0},{"id":3,"sLen":0,"time":93876.41,"type":0},{"id":0,"sLen":0,"time":94044.9453,"type":0},{"id":3,"sLen":0,"time":94213.4844,"type":0},{"id":2,"sLen":0,"time":94382.02,"type":0},{"id":0,"sLen":0,"time":94550.56,"type":0},{"id":3,"sLen":0,"time":94719.1,"type":0},{"id":2,"sLen":0,"time":94887.64,"type":0},{"id":0,"sLen":0,"time":95056.18,"type":0},{"id":0,"sLen":0,"time":95224.72,"type":0},{"id":3,"sLen":0,"time":95393.26,"type":0},{"id":1,"sLen":0,"time":95561.8,"type":0},{"id":0,"sLen":0,"time":95730.3359,"type":0},{"id":1,"sLen":0,"time":95898.875,"type":0},{"id":3,"sLen":0,"time":96067.4141,"type":0},{"id":1,"sLen":0,"time":96235.95,"type":0},{"id":0,"sLen":0,"time":96404.49,"type":0},{"id":1,"sLen":0,"time":96573.03,"type":0},{"id":3,"sLen":337.078643999994,"time":96741.57,"type":0},{"id":0,"sLen":589.88763399997,"time":98426.97,"type":0},{"id":2,"sLen":589.887633999999,"time":99101.125,"type":0},{"id":0,"sLen":589.887633999999,"time":101123.594,"type":0},{"id":2,"sLen":589.887633999984,"time":101797.75,"type":0},{"id":2,"sLen":0,"time":102471.906,"type":0},{"id":3,"sLen":0,"time":102640.453,"type":0},{"id":0,"sLen":0,"time":102808.992,"type":0},{"id":2,"sLen":0,"time":102977.531,"type":0},{"id":3,"sLen":0,"time":103146.07,"type":0},{"id":0,"sLen":0,"time":103314.609,"type":0},{"id":2,"sLen":0,"time":103483.148,"type":0},{"id":3,"sLen":0,"time":103651.688,"type":0},{"id":0,"sLen":0,"time":103820.227,"type":0},{"id":2,"sLen":0,"time":103988.766,"type":0},{"id":3,"sLen":0,"time":104157.305,"type":0},{"id":0,"sLen":0,"time":104325.844,"type":0},{"id":1,"sLen":0,"time":104494.383,"type":0},{"id":0,"sLen":0,"time":104662.922,"type":0},{"id":3,"sLen":0,"time":104831.461,"type":0},{"id":0,"sLen":0,"time":105000,"type":0},{"id":2,"sLen":0,"time":105168.539,"type":0},{"id":3,"sLen":0,"time":105337.078,"type":0},{"id":0,"sLen":0,"time":105505.617,"type":0},{"id":2,"sLen":0,"time":105674.156,"type":0},{"id":3,"sLen":0,"time":105842.695,"type":0},{"id":0,"sLen":0,"time":106011.234,"type":0},{"id":2,"sLen":0,"time":106179.773,"type":0},{"id":3,"sLen":0,"time":106348.313,"type":0},{"id":0,"sLen":0,"time":106516.852,"type":0},{"id":2,"sLen":0,"time":106685.391,"type":0},{"id":3,"sLen":0,"time":106853.93,"type":0},{"id":0,"sLen":0,"time":107022.469,"type":0},{"id":1,"sLen":0,"time":107191.008,"type":0},{"id":0,"sLen":0,"time":107359.547,"type":0},{"id":3,"sLen":0,"time":107528.086,"type":0},{"id":0,"sLen":0,"time":107696.633,"type":0},{"id":3,"sLen":589.887634000013,"time":107865.172,"type":0},{"id":1,"sLen":589.887634000013,"time":108539.328,"type":0},{"id":0,"sLen":589.887633999999,"time":109213.484,"type":0},{"id":1,"sLen":589.887633999999,"time":109887.641,"type":0},{"id":1,"sLen":0,"time":110561.8,"type":0},{"id":0,"sLen":0,"time":110646.07,"type":0},{"id":1,"sLen":0,"time":110730.336,"type":0},{"id":1,"sLen":0,"time":110898.875,"type":0},{"id":0,"sLen":0,"time":111067.414,"type":0},{"id":1,"sLen":0,"time":111235.953,"type":0},{"id":0,"sLen":0,"time":111320.227,"type":0},{"id":1,"sLen":0,"time":111404.492,"type":0},{"id":1,"sLen":0,"time":111573.031,"type":0},{"id":0,"sLen":0,"time":111741.57,"type":0},{"id":1,"sLen":0,"time":111910.109,"type":0},{"id":3,"sLen":0,"time":111994.383,"type":0},{"id":1,"sLen":0,"time":112078.648,"type":0},{"id":1,"sLen":0,"time":112247.188,"type":0},{"id":0,"sLen":0,"time":112415.727,"type":0},{"id":1,"sLen":0,"time":112584.273,"type":0},{"id":0,"sLen":0,"time":112668.539,"type":0},{"id":1,"sLen":0,"time":112752.813,"type":0},{"id":1,"sLen":0,"time":112921.352,"type":0},{"id":0,"sLen":0,"time":113089.891,"type":0},{"id":3,"sLen":589.887633999999,"time":113258.43,"type":0},{"id":1,"sLen":589.887633999999,"time":113932.586,"type":0},{"id":0,"sLen":589.887633999984,"time":114606.742,"type":0},{"id":1,"sLen":589.887633999999,"time":115280.9,"type":0},{"id":2,"sLen":0,"time":115955.055,"type":0},{"id":3,"sLen":0,"time":116123.594,"type":0},{"id":2,"sLen":0,"time":116292.133,"type":0},{"id":3,"sLen":0,"time":116460.672,"type":0},{"id":0,"sLen":0,"time":116629.211,"type":0},{"id":1,"sLen":0,"time":116797.75,"type":0},{"id":0,"sLen":0,"time":116966.289,"type":0},{"id":1,"sLen":0,"time":117134.828,"type":0},{"id":2,"sLen":0,"time":117303.367,"type":0},{"id":3,"sLen":0,"time":117471.906,"type":0},{"id":2,"sLen":0,"time":117640.453,"type":0},{"id":3,"sLen":0,"time":117808.992,"type":0},{"id":0,"sLen":0,"time":117977.531,"type":0},{"id":3,"sLen":0,"time":118146.07,"type":0},{"id":0,"sLen":0,"time":118314.609,"type":0},{"id":1,"sLen":0,"time":118483.148,"type":0}],"position":"boyfriend","characters":["bf-holding-gf"],"visible":true,"type":1},{"notes":[{"id":0,"sLen":0,"time":0,"type":0},{"id":3,"sLen":0,"time":168.539325842697,"type":0},{"id":3,"sLen":0,"time":252.808988764045,"type":0},{"id":3,"sLen":0,"time":421.348314606742,"type":0},{"id":0,"sLen":0,"time":589.887640449438,"type":0},{"id":3,"sLen":0,"time":758.426966292135,"type":0},{"id":3,"sLen":0,"time":842.696629213483,"type":0},{"id":0,"sLen":0,"time":1011.23595505618,"type":0},{"id":0,"sLen":0,"time":1179.77528089888,"type":0},{"id":0,"sLen":0,"time":1264.04494382022,"type":0},{"id":3,"sLen":0,"time":1348.31460674157,"type":0},{"id":0,"sLen":0,"time":1516.85393258427,"type":0},{"id":0,"sLen":0,"time":1601.12359550562,"type":0},{"id":3,"sLen":0,"time":1853.93258426966,"type":0},{"id":0,"sLen":0,"time":2022.47191011236,"type":0},{"id":3,"sLen":0,"time":2106.74157303371,"type":0},{"id":3,"sLen":0,"time":2191.01123595506,"type":0},{"id":0,"sLen":0,"time":2359.55056179775,"type":0},{"id":0,"sLen":0,"time":2696.62921348315,"type":0},{"id":3,"sLen":0,"time":2865.16853932584,"type":0},{"id":3,"sLen":0,"time":2949.43820224719,"type":0},{"id":3,"sLen":0,"time":3117.97752808989,"type":0},{"id":0,"sLen":0,"time":3286.51685393258,"type":0},{"id":3,"sLen":0,"time":3455.05617977528,"type":0},{"id":3,"sLen":0,"time":3539.32584269663,"type":0},{"id":0,"sLen":0,"time":3707.86516853933,"type":0},{"id":0,"sLen":0,"time":3876.40449438202,"type":0},{"id":0,"sLen":0,"time":3960.67415730337,"type":0},{"id":3,"sLen":0,"time":4044.94382022472,"type":0},{"id":0,"sLen":0,"time":4213.48314606742,"type":0},{"id":0,"sLen":0,"time":4297.75280898876,"type":0},{"id":3,"sLen":0,"time":4550.56179775281,"type":0},{"id":0,"sLen":0,"time":4719.10112359551,"type":0},{"id":3,"sLen":0,"time":4803.37078651685,"type":0},{"id":3,"sLen":0,"time":4887.6404494382,"type":0},{"id":0,"sLen":0,"time":5056.1797752809,"type":0},{"id":0,"sLen":0,"time":5140.44943820225,"type":0},{"id":0,"sLen":0,"time":5224.7191011236,"type":0},{"id":0,"sLen":0,"time":5393.25842696629,"type":0},{"id":3,"sLen":0,"time":5561.79775280899,"type":0},{"id":3,"sLen":0,"time":5646.06741573034,"type":0},{"id":3,"sLen":0,"time":5814.60674157303,"type":0},{"id":0,"sLen":0,"time":5983.14606741573,"type":0},{"id":3,"sLen":0,"time":6151.68539325843,"type":0},{"id":3,"sLen":0,"time":6235.95505617978,"type":0},{"id":0,"sLen":0,"time":6404.49438202247,"type":0},{"id":0,"sLen":0,"time":6573.03370786517,"type":0},{"id":0,"sLen":0,"time":6657.30337078652,"type":0},{"id":3,"sLen":0,"time":6741.57303370786,"type":0},{"id":0,"sLen":0,"time":6910.11235955056,"type":0},{"id":0,"sLen":0,"time":6994.38202247191,"type":0},{"id":3,"sLen":0,"time":7247.19101123595,"type":0},{"id":0,"sLen":0,"time":7415.73033707865,"type":0},{"id":3,"sLen":0,"time":7500,"type":0},{"id":3,"sLen":0,"time":7584.26966292135,"type":0},{"id":0,"sLen":0,"time":7752.80898876405,"type":0},{"id":0,"sLen":0,"time":8089.88764044944,"type":0},{"id":3,"sLen":0,"time":8258.42696629213,"type":0},{"id":3,"sLen":0,"time":8342.69662921348,"type":0},{"id":3,"sLen":0,"time":8511.23595505618,"type":0},{"id":0,"sLen":0,"time":8679.77528089888,"type":0},{"id":3,"sLen":0,"time":8848.31460674157,"type":0},{"id":3,"sLen":0,"time":8932.58426966292,"type":0},{"id":0,"sLen":0,"time":9101.12359550562,"type":0},{"id":0,"sLen":0,"time":9269.66292134831,"type":0},{"id":0,"sLen":0,"time":9353.93258426966,"type":0},{"id":3,"sLen":0,"time":9438.20224719101,"type":0},{"id":0,"sLen":0,"time":9606.74157303371,"type":0},{"id":0,"sLen":0,"time":9691.01123595505,"type":0},{"id":3,"sLen":0,"time":9943.8202247191,"type":0},{"id":0,"sLen":0,"time":10112.3595505618,"type":0},{"id":3,"sLen":0,"time":10196.6292134831,"type":0},{"id":3,"sLen":0,"time":10280.8988764045,"type":0},{"id":0,"sLen":0,"time":10449.4382022472,"type":0},{"id":0,"sLen":0,"time":21235.9550561798,"type":0},{"id":3,"sLen":0,"time":21320.2247191011,"type":0},{"id":0,"sLen":0,"time":21404.4943820225,"type":0},{"id":0,"sLen":0,"time":21573.0337078652,"type":0},{"id":3,"sLen":0,"time":21910.1123595506,"type":0},{"id":0,"sLen":0,"time":22247.191011236,"type":0},{"id":3,"sLen":0,"time":22584.2696629213,"type":0},{"id":0,"sLen":0,"time":22921.3483146067,"type":0},{"id":0,"sLen":0,"time":23258.4269662921,"type":0},{"id":3,"sLen":0,"time":23595.5056179775,"type":0},{"id":3,"sLen":0,"time":23932.5842696629,"type":0},{"id":0,"sLen":0,"time":24269.6629213483,"type":0},{"id":3,"sLen":0,"time":24606.7415730337,"type":0},{"id":0,"sLen":0,"time":24943.8202247191,"type":0},{"id":3,"sLen":0,"time":25280.8988764045,"type":0},{"id":0,"sLen":0,"time":25617.9775280899,"type":0},{"id":0,"sLen":0,"time":25955.0561797753,"type":0},{"id":3,"sLen":0,"time":26292.1348314607,"type":0},{"id":3,"sLen":0,"time":26629.2134831461,"type":0},{"id":3,"sLen":0,"time":26713.4831460674,"type":0},{"id":3,"sLen":0,"time":26797.7528089888,"type":0},{"id":3,"sLen":0,"time":26966.2921348315,"type":0},{"id":0,"sLen":0,"time":27303.3707865169,"type":0},{"id":0,"sLen":0,"time":27640.4494382022,"type":0},{"id":3,"sLen":0,"time":27977.5280898876,"type":0},{"id":3,"sLen":0,"time":28314.606741573,"type":0},{"id":0,"sLen":0,"time":28651.6853932584,"type":0},{"id":3,"sLen":0,"time":28988.7640449438,"type":0},{"id":0,"sLen":0,"time":29325.8426966292,"type":0},{"id":0,"sLen":0,"time":29662.9213483146,"type":0},{"id":0,"sLen":0,"time":29831.4606741573,"type":0},{"id":0,"sLen":0,"time":30000,"type":0},{"id":3,"sLen":0,"time":30168.5393258427,"type":0},{"id":3,"sLen":0,"time":30337.0786516854,"type":0},{"id":3,"sLen":0,"time":30505.6179775281,"type":0},{"id":3,"sLen":0,"time":30674.1573033708,"type":0},{"id":0,"sLen":0,"time":30842.6966292135,"type":0},{"id":0,"sLen":0,"time":31011.2359550562,"type":0},{"id":3,"sLen":0,"time":31348.3146067416,"type":0},{"id":3,"sLen":0,"time":31685.393258427,"type":0},{"id":0,"sLen":0,"time":31853.9325842697,"type":0},{"id":0,"sLen":0,"time":32359.5505617978,"type":0},{"id":3,"sLen":0,"time":32696.6292134831,"type":0},{"id":0,"sLen":0,"time":33033.7078651685,"type":0},{"id":0,"sLen":0,"time":33202.2471910112,"type":0},{"id":3,"sLen":0,"time":33370.7865168539,"type":0},{"id":0,"sLen":0,"time":33707.8651685393,"type":0},{"id":3,"sLen":0,"time":34044.9438202247,"type":0},{"id":3,"sLen":0,"time":34382.0224719101,"type":0},{"id":0,"sLen":0,"time":34550.5617977528,"type":0},{"id":3,"sLen":0,"time":34719.1011235955,"type":0},{"id":0,"sLen":0,"time":35056.1797752809,"type":0},{"id":3,"sLen":0,"time":35393.2584269663,"type":0},{"id":0,"sLen":0,"time":35730.3370786517,"type":0},{"id":0,"sLen":0,"time":35898.8764044944,"type":0},{"id":3,"sLen":0,"time":36067.4157303371,"type":0},{"id":0,"sLen":0,"time":36404.4943820225,"type":0},{"id":3,"sLen":0,"time":36741.5730337079,"type":0},{"id":3,"sLen":0,"time":37078.6516853933,"type":0},{"id":0,"sLen":0,"time":37247.191011236,"type":0},{"id":3,"sLen":0,"time":37415.7303370787,"type":0},{"id":0,"sLen":0,"time":37752.808988764,"type":0},{"id":3,"sLen":0,"time":38089.8876404494,"type":0},{"id":3,"sLen":0,"time":38426.9662921348,"type":0},{"id":0,"sLen":0,"time":38595.5056179775,"type":0},{"id":3,"sLen":0,"time":38764.0449438202,"type":0},{"id":0,"sLen":0,"time":39101.1235955056,"type":0},{"id":3,"sLen":0,"time":39438.202247191,"type":0},{"id":3,"sLen":0,"time":39775.2808988764,"type":0},{"id":0,"sLen":0,"time":39943.8202247191,"type":0},{"id":3,"sLen":0,"time":40112.3595505618,"type":0},{"id":0,"sLen":0,"time":40449.4382022472,"type":0},{"id":3,"sLen":0,"time":40786.5168539326,"type":0},{"id":3,"sLen":0,"time":41123.595505618,"type":0},{"id":0,"sLen":0,"time":41292.1348314607,"type":0},{"id":3,"sLen":0,"time":41460.6741573034,"type":0},{"id":0,"sLen":0,"time":41797.7528089888,"type":0},{"id":0,"sLen":0,"time":42134.8314606742,"type":0},{"id":0,"sLen":0,"time":42471.9101123596,"type":0},{"id":0,"sLen":0,"time":42640.4494382022,"type":0},{"id":3,"sLen":0,"time":42808.9887640449,"type":0},{"id":3,"sLen":0,"time":42893.2584269663,"type":0},{"id":3,"sLen":0,"time":42977.5280898876,"type":0},{"id":0,"sLen":0,"time":43146.0674157303,"type":0},{"id":3,"sLen":0,"time":43483.1460674157,"type":0},{"id":3,"sLen":0,"time":43820.2247191011,"type":0},{"id":0,"sLen":0,"time":43988.7640449438,"type":0},{"id":3,"sLen":0,"time":44157.3033707865,"type":0},{"id":0,"sLen":0,"time":44494.3820224719,"type":0},{"id":3,"sLen":0,"time":44831.4606741573,"type":0},{"id":0,"sLen":0,"time":45168.5393258427,"type":0},{"id":0,"sLen":0,"time":45337.0786516854,"type":0},{"id":3,"sLen":0,"time":45505.6179775281,"type":0},{"id":0,"sLen":0,"time":45842.6966292135,"type":0},{"id":3,"sLen":0,"time":46179.7752808989,"type":0},{"id":3,"sLen":0,"time":46516.8539325843,"type":0},{"id":0,"sLen":0,"time":46685.393258427,"type":0},{"id":3,"sLen":0,"time":46853.9325842697,"type":0},{"id":0,"sLen":0,"time":47191.0112359551,"type":0},{"id":3,"sLen":0,"time":47528.0898876404,"type":0},{"id":3,"sLen":0,"time":47865.1685393258,"type":0},{"id":0,"sLen":0,"time":48033.7078651685,"type":0},{"id":3,"sLen":0,"time":48202.2471910112,"type":0},{"id":0,"sLen":0,"time":48539.3258426966,"type":0},{"id":3,"sLen":0,"time":48876.404494382,"type":0},{"id":3,"sLen":0,"time":49213.4831460674,"type":0},{"id":0,"sLen":0,"time":49382.0224719101,"type":0},{"id":3,"sLen":0,"time":49550.5617977528,"type":0},{"id":0,"sLen":0,"time":49887.6404494382,"type":0},{"id":3,"sLen":0,"time":50224.7191011236,"type":0},{"id":0,"sLen":0,"time":50561.797752809,"type":0},{"id":0,"sLen":0,"time":50730.3370786517,"type":0},{"id":3,"sLen":0,"time":50898.8764044944,"type":0},{"id":0,"sLen":0,"time":51235.9550561798,"type":0},{"id":3,"sLen":0,"time":51573.0337078652,"type":0},{"id":3,"sLen":0,"time":51910.1123595506,"type":0},{"id":0,"sLen":0,"time":52078.6516853933,"type":0},{"id":3,"sLen":0,"time":52247.191011236,"type":0},{"id":0,"sLen":0,"time":52584.2696629213,"type":0},{"id":0,"sLen":0,"time":52921.3483146067,"type":0},{"id":0,"sLen":0,"time":53258.4269662921,"type":0},{"id":0,"sLen":0,"time":53426.9662921348,"type":0},{"id":3,"sLen":0,"time":53595.5056179775,"type":0},{"id":3,"sLen":0,"time":53679.7752808989,"type":0},{"id":3,"sLen":0,"time":53764.0449438202,"type":0},{"id":0,"sLen":0,"time":53932.5842696629,"type":0},{"id":3,"sLen":0,"time":54101.1235955056,"type":0},{"id":3,"sLen":0,"time":54185.393258427,"type":0},{"id":3,"sLen":0,"time":54353.9325842697,"type":0},{"id":0,"sLen":0,"time":54522.4719101124,"type":0},{"id":3,"sLen":0,"time":54691.0112359551,"type":0},{"id":3,"sLen":0,"time":54775.2808988764,"type":0},{"id":0,"sLen":0,"time":54943.8202247191,"type":0},{"id":0,"sLen":0,"time":55112.3595505618,"type":0},{"id":0,"sLen":0,"time":55196.6292134831,"type":0},{"id":3,"sLen":0,"time":55280.8988764045,"type":0},{"id":0,"sLen":0,"time":55449.4382022472,"type":0},{"id":0,"sLen":0,"time":55533.7078651685,"type":0},{"id":3,"sLen":0,"time":55786.5168539326,"type":0},{"id":0,"sLen":0,"time":55955.0561797753,"type":0},{"id":3,"sLen":0,"time":56039.3258426966,"type":0},{"id":3,"sLen":0,"time":56123.595505618,"type":0},{"id":0,"sLen":0,"time":56292.1348314607,"type":0},{"id":0,"sLen":0,"time":56629.2134831461,"type":0},{"id":3,"sLen":0,"time":56797.7528089888,"type":0},{"id":3,"sLen":0,"time":56882.0224719101,"type":0},{"id":3,"sLen":0,"time":57050.5617977528,"type":0},{"id":0,"sLen":0,"time":57219.1011235955,"type":0},{"id":3,"sLen":0,"time":57387.6404494382,"type":0},{"id":3,"sLen":0,"time":57471.9101123595,"type":0},{"id":0,"sLen":0,"time":57640.4494382022,"type":0},{"id":0,"sLen":0,"time":57808.9887640449,"type":0},{"id":0,"sLen":0,"time":57893.2584269663,"type":0},{"id":3,"sLen":0,"time":57977.5280898876,"type":0},{"id":0,"sLen":0,"time":58146.0674157303,"type":0},{"id":0,"sLen":0,"time":58230.3370786517,"type":0},{"id":3,"sLen":0,"time":58483.1460674157,"type":0},{"id":0,"sLen":0,"time":58651.6853932584,"type":0},{"id":3,"sLen":0,"time":58735.9550561798,"type":0},{"id":3,"sLen":0,"time":58820.2247191011,"type":0},{"id":0,"sLen":0,"time":58988.7640449438,"type":0},{"id":0,"sLen":0,"time":59073.0337078652,"type":0},{"id":0,"sLen":0,"time":59157.3033707865,"type":0},{"id":0,"sLen":0,"time":59325.8426966292,"type":0},{"id":3,"sLen":0,"time":59494.3820224719,"type":0},{"id":3,"sLen":0,"time":59578.6516853933,"type":0},{"id":3,"sLen":0,"time":59747.191011236,"type":0},{"id":0,"sLen":0,"time":59915.7303370786,"type":0},{"id":3,"sLen":0,"time":60084.2696629213,"type":0},{"id":3,"sLen":0,"time":60168.5393258427,"type":0},{"id":0,"sLen":0,"time":60337.0786516854,"type":0},{"id":0,"sLen":0,"time":60505.6179775281,"type":0},{"id":0,"sLen":0,"time":60589.8876404494,"type":0},{"id":3,"sLen":0,"time":60674.1573033708,"type":0},{"id":0,"sLen":0,"time":60842.6966292135,"type":0},{"id":0,"sLen":0,"time":60926.9662921348,"type":0},{"id":3,"sLen":0,"time":61179.7752808989,"type":0},{"id":0,"sLen":0,"time":61348.3146067416,"type":0},{"id":3,"sLen":0,"time":61432.5842696629,"type":0},{"id":3,"sLen":0,"time":61516.8539325843,"type":0},{"id":0,"sLen":0,"time":61685.393258427,"type":0},{"id":0,"sLen":0,"time":62022.4719101124,"type":0},{"id":3,"sLen":0,"time":62191.0112359551,"type":0},{"id":3,"sLen":0,"time":62275.2808988764,"type":0},{"id":3,"sLen":0,"time":62443.8202247191,"type":0},{"id":0,"sLen":0,"time":62612.3595505618,"type":0},{"id":3,"sLen":0,"time":62780.8988764045,"type":0},{"id":3,"sLen":0,"time":62865.1685393258,"type":0},{"id":0,"sLen":0,"time":63033.7078651685,"type":0},{"id":0,"sLen":0,"time":63202.2471910112,"type":0},{"id":0,"sLen":0,"time":63286.5168539326,"type":0},{"id":3,"sLen":0,"time":63370.7865168539,"type":0},{"id":0,"sLen":0,"time":63539.3258426966,"type":0},{"id":0,"sLen":0,"time":63623.595505618,"type":0},{"id":3,"sLen":0,"time":63876.404494382,"type":0},{"id":0,"sLen":0,"time":64044.9438202247,"type":0},{"id":3,"sLen":0,"time":64129.2134831461,"type":0},{"id":3,"sLen":0,"time":64213.4831460674,"type":0},{"id":0,"sLen":0,"time":64382.0224719101,"type":0},{"id":3,"sLen":0,"time":64719.1011235955,"type":0},{"id":0,"sLen":0,"time":65056.1797752809,"type":0},{"id":0,"sLen":0,"time":65393.2584269663,"type":0},{"id":0,"sLen":0,"time":65730.3370786517,"type":0},{"id":0,"sLen":0,"time":66067.4157303371,"type":0},{"id":3,"sLen":0,"time":66404.4943820225,"type":0},{"id":3,"sLen":0,"time":66741.5730337079,"type":0},{"id":3,"sLen":0,"time":67078.6516853933,"type":0},{"id":3,"sLen":0,"time":67415.7303370787,"type":0},{"id":0,"sLen":0,"time":67752.808988764,"type":0},{"id":0,"sLen":0,"time":68089.8876404494,"type":0},{"id":0,"sLen":0,"time":68426.9662921348,"type":0},{"id":0,"sLen":0,"time":68764.0449438202,"type":0},{"id":3,"sLen":0,"time":69101.1235955056,"type":0},{"id":3,"sLen":0,"time":69438.202247191,"type":0},{"id":3,"sLen":0,"time":69775.2808988764,"type":0},{"id":3,"sLen":0,"time":69859.5505617978,"type":0},{"id":3,"sLen":0,"time":69943.8202247191,"type":0},{"id":3,"sLen":0,"time":70112.3595505618,"type":0},{"id":0,"sLen":0,"time":70449.4382022472,"type":0},{"id":0,"sLen":0,"time":70786.5168539326,"type":0},{"id":0,"sLen":0,"time":71123.595505618,"type":0},{"id":0,"sLen":0,"time":71460.6741573034,"type":0},{"id":3,"sLen":0,"time":71797.7528089888,"type":0},{"id":3,"sLen":0,"time":72134.8314606741,"type":0},{"id":3,"sLen":0,"time":72471.9101123595,"type":0},{"id":3,"sLen":0,"time":72556.1797752809,"type":0},{"id":3,"sLen":0,"time":72640.4494382023,"type":0},{"id":3,"sLen":0,"time":72808.9887640449,"type":0},{"id":3,"sLen":0,"time":72893.2584269663,"type":0},{"id":3,"sLen":0,"time":72977.5280898877,"type":0},{"id":0,"sLen":0,"time":73146.0674157303,"type":0},{"id":0,"sLen":0,"time":73230.3370786517,"type":0},{"id":0,"sLen":0,"time":73314.6067415731,"type":0},{"id":0,"sLen":0,"time":73483.1460674157,"type":0},{"id":0,"sLen":0,"time":73567.4157303371,"type":0},{"id":0,"sLen":0,"time":73651.6853932585,"type":0},{"id":0,"sLen":0,"time":73820.2247191011,"type":0},{"id":0,"sLen":0,"time":73904.4943820225,"type":0},{"id":0,"sLen":0,"time":73988.7640449438,"type":0},{"id":0,"sLen":0,"time":74157.3033707866,"type":0},{"id":0,"sLen":0,"time":74241.5730337079,"type":0},{"id":0,"sLen":0,"time":74325.8426966292,"type":0},{"id":0,"sLen":0,"time":74410.1123595506,"type":0},{"id":3,"sLen":0,"time":74494.382022472,"type":0},{"id":3,"sLen":0,"time":74578.6516853933,"type":0},{"id":3,"sLen":0,"time":74662.9213483146,"type":0},{"id":3,"sLen":0,"time":74747.191011236,"type":0},{"id":0,"sLen":0,"time":74831.4606741573,"type":0},{"id":0,"sLen":0,"time":74915.7303370787,"type":0},{"id":0,"sLen":0,"time":75000,"type":0},{"id":0,"sLen":0,"time":75084.2696629214,"type":0},{"id":3,"sLen":0,"time":75168.5393258427,"type":0},{"id":0,"sLen":0,"time":75505.6179775281,"type":0},{"id":3,"sLen":0,"time":75842.6966292135,"type":0},{"id":0,"sLen":0,"time":76179.7752808989,"type":0},{"id":0,"sLen":0,"time":76516.8539325843,"type":0},{"id":3,"sLen":0,"time":76853.9325842697,"type":0},{"id":3,"sLen":0,"time":77191.0112359551,"type":0},{"id":0,"sLen":0,"time":77528.0898876405,"type":0},{"id":3,"sLen":0,"time":77865.1685393259,"type":0},{"id":3,"sLen":0,"time":78033.7078651686,"type":0},{"id":0,"sLen":0,"time":78202.2471910113,"type":0},{"id":0,"sLen":0,"time":78539.3258426967,"type":0},{"id":3,"sLen":0,"time":78876.4044943821,"type":0},{"id":0,"sLen":0,"time":79213.4831460675,"type":0},{"id":0,"sLen":0,"time":79550.5617977529,"type":0},{"id":3,"sLen":0,"time":79887.6404494383,"type":0},{"id":0,"sLen":0,"time":80140.4494382023,"type":0},{"id":0,"sLen":0,"time":80224.7191011237,"type":0},{"id":0,"sLen":0,"time":80308.988764045,"type":0},{"id":0,"sLen":0,"time":80477.5280898877,"type":0},{"id":3,"sLen":84.269662921346,"time":80730.3370786517,"type":0},{"id":0,"sLen":0,"time":80898.8764044945,"type":0},{"id":3,"sLen":0,"time":81067.4157303371,"type":0},{"id":0,"sLen":0,"time":81235.9550561799,"type":0},{"id":3,"sLen":0,"time":81404.4943820225,"type":0},{"id":0,"sLen":0,"time":81573.0337078652,"type":0},{"id":3,"sLen":0,"time":81741.5730337079,"type":0},{"id":0,"sLen":0,"time":81910.1123595506,"type":0},{"id":3,"sLen":0,"time":82078.6516853933,"type":0},{"id":3,"sLen":0,"time":82247.191011236,"type":0},{"id":0,"sLen":0,"time":82415.7303370787,"type":0},{"id":3,"sLen":0,"time":82584.2696629214,"type":0},{"id":0,"sLen":0,"time":82752.8089887641,"type":0},{"id":3,"sLen":0,"time":82921.3483146068,"type":0},{"id":0,"sLen":0,"time":83089.8876404495,"type":0},{"id":3,"sLen":0,"time":83258.4269662922,"type":0},{"id":3,"sLen":0,"time":83426.9662921349,"type":0},{"id":0,"sLen":0,"time":83595.5056179776,"type":0},{"id":0,"sLen":0,"time":83764.0449438203,"type":0},{"id":0,"sLen":0,"time":83932.584269663,"type":0},{"id":3,"sLen":0,"time":84269.6629213484,"type":0},{"id":3,"sLen":0,"time":84606.7415730338,"type":0},{"id":3,"sLen":0,"time":84775.2808988765,"type":0},{"id":3,"sLen":0,"time":84943.8202247192,"type":0},{"id":3,"sLen":0,"time":85280.8988764046,"type":0},{"id":0,"sLen":0,"time":85617.97752809,"type":0},{"id":0,"sLen":0,"time":85702.2471910113,"type":0},{"id":3,"sLen":0,"time":85870.786516854,"type":0},{"id":0,"sLen":0,"time":86039.3258426967,"type":0},{"id":0,"sLen":0,"time":86292.1348314607,"type":0},{"id":3,"sLen":0,"time":86629.2134831461,"type":0},{"id":0,"sLen":0,"time":87134.8314606741,"type":0},{"id":3,"sLen":0,"time":87303.3707865169,"type":0},{"id":0,"sLen":0,"time":87640.4494382023,"type":0},{"id":3,"sLen":0,"time":87977.5280898877,"type":0},{"id":0,"sLen":0,"time":88483.1460674157,"type":0},{"id":3,"sLen":0,"time":88651.6853932584,"type":0},{"id":0,"sLen":0,"time":88988.7640449438,"type":0},{"id":3,"sLen":0,"time":89325.8426966292,"type":0},{"id":0,"sLen":0,"time":89831.4606741573,"type":0},{"id":3,"sLen":0,"time":90000,"type":0},{"id":0,"sLen":0,"time":90337.0786516854,"type":0},{"id":3,"sLen":0,"time":90674.1573033708,"type":0},{"id":0,"sLen":0,"time":91179.7752808989,"type":0},{"id":3,"sLen":0,"time":91348.3146067416,"type":0},{"id":0,"sLen":0,"time":91685.393258427,"type":0},{"id":3,"sLen":0,"time":92022.4719101124,"type":0},{"id":0,"sLen":0,"time":92528.0898876405,"type":0},{"id":3,"sLen":0,"time":92696.6292134832,"type":0},{"id":0,"sLen":0,"time":93033.7078651686,"type":0},{"id":3,"sLen":0,"time":93370.786516854,"type":0},{"id":0,"sLen":0,"time":93876.404494382,"type":0},{"id":3,"sLen":0,"time":94044.9438202248,"type":0},{"id":0,"sLen":0,"time":94382.0224719102,"type":0},{"id":3,"sLen":0,"time":94719.1011235956,"type":0},{"id":0,"sLen":0,"time":95224.7191011236,"type":0},{"id":3,"sLen":0,"time":95393.2584269663,"type":0},{"id":0,"sLen":0,"time":95730.3370786517,"type":0},{"id":0,"sLen":0,"time":96067.4157303371,"type":0},{"id":0,"sLen":0,"time":96404.4943820225,"type":0},{"id":0,"sLen":0,"time":96573.0337078652,"type":0},{"id":3,"sLen":0,"time":96741.5730337079,"type":0},{"id":3,"sLen":0,"time":96825.8426966292,"type":0},{"id":3,"sLen":0,"time":96910.1123595505,"type":0},{"id":0,"sLen":0,"time":97078.6516853933,"type":0},{"id":3,"sLen":0,"time":97415.7303370787,"type":0},{"id":3,"sLen":0,"time":97752.808988764,"type":0},{"id":0,"sLen":0,"time":97921.3483146067,"type":0},{"id":3,"sLen":0,"time":98089.8876404494,"type":0},{"id":0,"sLen":0,"time":98426.9662921348,"type":0},{"id":3,"sLen":0,"time":98764.0449438202,"type":0},{"id":0,"sLen":0,"time":99101.1235955056,"type":0},{"id":0,"sLen":0,"time":99269.6629213483,"type":0},{"id":3,"sLen":0,"time":99438.202247191,"type":0},{"id":0,"sLen":0,"time":99775.2808988764,"type":0},{"id":3,"sLen":0,"time":100112.359550562,"type":0},{"id":3,"sLen":0,"time":100449.438202247,"type":0},{"id":0,"sLen":0,"time":100617.97752809,"type":0},{"id":3,"sLen":0,"time":100786.516853933,"type":0},{"id":0,"sLen":0,"time":101123.595505618,"type":0},{"id":3,"sLen":0,"time":101460.674157303,"type":0},{"id":3,"sLen":0,"time":101797.752808989,"type":0},{"id":0,"sLen":0,"time":101966.292134831,"type":0},{"id":3,"sLen":0,"time":102134.831460674,"type":0},{"id":0,"sLen":0,"time":102471.91011236,"type":0},{"id":3,"sLen":0,"time":102808.988764045,"type":0},{"id":3,"sLen":0,"time":103146.06741573,"type":0},{"id":0,"sLen":0,"time":103314.606741573,"type":0},{"id":3,"sLen":0,"time":103483.146067416,"type":0},{"id":0,"sLen":0,"time":103820.224719101,"type":0},{"id":3,"sLen":0,"time":104157.303370787,"type":0},{"id":0,"sLen":0,"time":104494.382022472,"type":0},{"id":0,"sLen":0,"time":104662.921348315,"type":0},{"id":3,"sLen":0,"time":104831.460674157,"type":0},{"id":0,"sLen":0,"time":105168.539325843,"type":0},{"id":3,"sLen":0,"time":105505.617977528,"type":0},{"id":3,"sLen":0,"time":105842.696629213,"type":0},{"id":0,"sLen":0,"time":106011.235955056,"type":0},{"id":3,"sLen":0,"time":106179.775280899,"type":0},{"id":0,"sLen":0,"time":106516.853932584,"type":0},{"id":0,"sLen":0,"time":106853.93258427,"type":0},{"id":0,"sLen":0,"time":107191.011235955,"type":0},{"id":3,"sLen":0,"time":107528.08988764,"type":0},{"id":0,"sLen":0,"time":107865.168539326,"type":0},{"id":0,"sLen":0,"time":108202.247191011,"type":0},{"id":0,"sLen":0,"time":108539.325842697,"type":0},{"id":0,"sLen":0,"time":108876.404494382,"type":0},{"id":3,"sLen":0,"time":109213.483146068,"type":0},{"id":3,"sLen":0,"time":109550.561797753,"type":0},{"id":3,"sLen":0,"time":109887.640449438,"type":0},{"id":3,"sLen":0,"time":110224.719101124,"type":0},{"id":0,"sLen":0,"time":110561.797752809,"type":0},{"id":0,"sLen":0,"time":110730.337078652,"type":0},{"id":0,"sLen":0,"time":110898.876404495,"type":0},{"id":3,"sLen":0,"time":111235.95505618,"type":0},{"id":3,"sLen":0,"time":111573.033707865,"type":0},{"id":3,"sLen":0,"time":111910.112359551,"type":0},{"id":3,"sLen":0,"time":112247.191011236,"type":0},{"id":0,"sLen":0,"time":112584.269662922,"type":0},{"id":3,"sLen":0,"time":112921.348314607,"type":0},{"id":0,"sLen":0,"time":113258.426966292,"type":0},{"id":3,"sLen":0,"time":113932.584269663,"type":0},{"id":0,"sLen":0,"time":114269.662921349,"type":0},{"id":3,"sLen":0,"time":114438.202247191,"type":0},{"id":0,"sLen":0,"time":114606.741573034,"type":0},{"id":3,"sLen":0,"time":114943.820224719,"type":0},{"id":0,"sLen":0,"time":115280.898876405,"type":0},{"id":3,"sLen":0,"time":115617.97752809,"type":0},{"id":3,"sLen":0,"time":115786.516853933,"type":0},{"id":0,"sLen":0,"time":115955.056179776,"type":0},{"id":3,"sLen":0,"time":116123.595505618,"type":0},{"id":0,"sLen":0,"time":116292.134831461,"type":0},{"id":0,"sLen":0,"time":116629.213483146,"type":0},{"id":3,"sLen":0,"time":116966.292134832,"type":0},{"id":3,"sLen":0,"time":117303.370786517,"type":0},{"id":0,"sLen":0,"time":117640.449438203,"type":0},{"id":3,"sLen":0,"time":117977.528089888,"type":0},{"id":3,"sLen":0,"time":118061.797752809,"type":0},{"id":0,"sLen":0,"time":118314.606741573,"type":0},{"id":3,"sLen":0,"time":118398.876404495,"type":0},{"id":0,"sLen":0,"time":118651.685393258,"type":0},{"id":3,"sLen":0,"time":118820.224719101,"type":0},{"id":3,"sLen":0,"time":118904.494382022,"type":0},{"id":3,"sLen":0,"time":119073.033707865,"type":0},{"id":0,"sLen":0,"time":119241.573033708,"type":0},{"id":3,"sLen":0,"time":119410.112359551,"type":0},{"id":3,"sLen":0,"time":119494.382022472,"type":0},{"id":0,"sLen":0,"time":119662.921348315,"type":0},{"id":0,"sLen":0,"time":119831.460674157,"type":0},{"id":0,"sLen":0,"time":119915.730337079,"type":0},{"id":3,"sLen":0,"time":120000,"type":0},{"id":0,"sLen":0,"time":120168.539325843,"type":0},{"id":0,"sLen":0,"time":120252.808988764,"type":0},{"id":3,"sLen":0,"time":120505.617977528,"type":0},{"id":0,"sLen":0,"time":120674.157303371,"type":0},{"id":3,"sLen":0,"time":120758.426966292,"type":0},{"id":3,"sLen":0,"time":120842.696629213,"type":0},{"id":0,"sLen":0,"time":121011.235955056,"type":0},{"id":0,"sLen":0,"time":121095.505617978,"type":0},{"id":0,"sLen":0,"time":121179.775280899,"type":0}],"position":"girlfriend","type":2,"characters":["pico-speakers"],"visible":false}],"codenameChart":true,"stage":"tank","noteTypes":["No Anim Note"]} \ No newline at end of file +{"events":[{"params":[1],"time":5393.25842696629,"name":"Camera Movement"},{"params":[0],"time":10786.5168539326,"name":"Camera Movement"},{"params":[1],"time":14831.4606741573,"name":"Camera Movement"},{"params":[0],"time":21573.0337078652,"name":"Camera Movement"},{"params":[1],"time":24269.6629213483,"name":"Camera Movement"},{"params":[0],"time":26966.2921348315,"name":"Camera Movement"},{"params":[1],"time":28314.606741573,"name":"Camera Movement"},{"params":[0],"time":31011.2359550562,"name":"Camera Movement"},{"params":[1],"time":37752.808988764,"name":"Camera Movement"},{"params":[0],"time":43146.0674157303,"name":"Camera Movement"},{"params":[1],"time":48539.3258426966,"name":"Camera Movement"},{"params":[0],"time":53932.5842696629,"name":"Camera Movement"},{"params":[1],"time":59325.8426966292,"name":"Camera Movement"},{"params":[0],"time":62022.4719101124,"name":"Camera Movement"},{"params":[1],"time":67415.7303370787,"name":"Camera Movement"},{"params":[0],"time":75505.6179775281,"name":"Camera Movement"},{"params":[1],"time":80898.8764044945,"name":"Camera Movement"},{"params":[0],"time":86292.1348314608,"name":"Camera Movement"},{"params":[1],"time":91685.3932584271,"name":"Camera Movement"},{"params":[0],"time":97078.6516853934,"name":"Camera Movement"},{"params":[1],"time":102471.91011236,"name":"Camera Movement"},{"params":[0],"time":107865.168539326,"name":"Camera Movement"},{"params":[1],"time":113258.426966292,"name":"Camera Movement"}],"scrollSpeed":2.2,"strumLines":[{"notes":[{"id":2,"sLen":0,"time":0,"type":0},{"id":3,"sLen":0,"time":168.539322,"type":0},{"id":0,"sLen":0,"time":337.078644,"type":0},{"id":0,"sLen":0,"time":674.1573,"type":0},{"id":3,"sLen":0,"time":842.696655,"type":0},{"id":2,"sLen":0,"time":1011.23596,"type":0},{"id":1,"sLen":0,"time":1348.31458,"type":0},{"id":0,"sLen":0,"time":1516.85388,"type":0},{"id":3,"sLen":0,"time":1685.39331,"type":0},{"id":2,"sLen":0,"time":1853.93262,"type":0},{"id":0,"sLen":0,"time":2022.47192,"type":0},{"id":3,"sLen":0,"time":2191.01123,"type":0},{"id":1,"sLen":0,"time":2359.55054,"type":0},{"id":0,"sLen":0,"time":2528.08984,"type":0},{"id":2,"sLen":0,"time":2696.62915,"type":0},{"id":3,"sLen":0,"time":2865.16846,"type":0},{"id":0,"sLen":0,"time":3033.70776,"type":0},{"id":0,"sLen":0,"time":3370.78662,"type":0},{"id":3,"sLen":0,"time":3539.326,"type":0},{"id":2,"sLen":0,"time":3707.86523,"type":0},{"id":1,"sLen":0,"time":4044.94385,"type":0},{"id":0,"sLen":0,"time":4213.483,"type":0},{"id":3,"sLen":0,"time":4382.02246,"type":0},{"id":2,"sLen":0,"time":4550.562,"type":0},{"id":0,"sLen":0,"time":4719.101,"type":0},{"id":3,"sLen":0,"time":4887.64063,"type":0},{"id":1,"sLen":0,"time":5056.17969,"type":0},{"id":0,"sLen":0,"time":5224.719,"type":0},{"id":0,"sLen":0,"time":11123.5957,"type":0},{"id":1,"sLen":0,"time":11292.1348,"type":0},{"id":3,"sLen":252.808989999998,"time":11460.6738,"type":0},{"id":1,"sLen":252.80899,"time":11797.7529,"type":0},{"id":0,"sLen":0,"time":12134.8311,"type":0},{"id":0,"sLen":0,"time":12471.91,"type":0},{"id":0,"sLen":0,"time":12808.9883,"type":0},{"id":0,"sLen":0,"time":12977.5283,"type":0},{"id":3,"sLen":0,"time":13146.0674,"type":0},{"id":1,"sLen":0,"time":13483.1465,"type":0},{"id":0,"sLen":0,"time":13651.6855,"type":0},{"id":3,"sLen":0,"time":13820.2246,"type":0},{"id":1,"sLen":0,"time":13988.7637,"type":0},{"id":0,"sLen":0,"time":14157.3037,"type":0},{"id":3,"sLen":0,"time":14325.8428,"type":0},{"id":1,"sLen":0,"time":14494.3818,"type":0},{"id":0,"sLen":0,"time":14662.9209,"type":0},{"id":1,"sLen":0,"time":14831.4609,"type":0},{"id":0,"sLen":0,"time":15000,"type":0},{"id":3,"sLen":0,"time":15168.5391,"type":0},{"id":1,"sLen":0,"time":15337.0791,"type":0},{"id":0,"sLen":0,"time":15505.6182,"type":0},{"id":3,"sLen":0,"time":15674.1572,"type":0},{"id":1,"sLen":0,"time":15842.6963,"type":0},{"id":0,"sLen":0,"time":16011.2363,"type":0},{"id":3,"sLen":0,"time":21573.0332,"type":0},{"id":2,"sLen":0,"time":21741.5723,"type":0},{"id":0,"sLen":0,"time":21910.1133,"type":0},{"id":2,"sLen":0,"time":22078.6523,"type":0},{"id":3,"sLen":0,"time":22247.1914,"type":0},{"id":2,"sLen":0,"time":22415.73,"type":0},{"id":0,"sLen":0,"time":22584.27,"type":0},{"id":2,"sLen":0,"time":22752.8086,"type":0},{"id":3,"sLen":0,"time":22921.3477,"type":0},{"id":2,"sLen":0,"time":23089.8867,"type":0},{"id":3,"sLen":0,"time":23258.4277,"type":0},{"id":2,"sLen":0,"time":23426.9668,"type":0},{"id":3,"sLen":0,"time":23595.5059,"type":0},{"id":0,"sLen":0,"time":23764.0449,"type":0},{"id":1,"sLen":0,"time":23932.584,"type":0},{"id":0,"sLen":0,"time":24101.123,"type":0},{"id":2,"sLen":0,"time":24269.6621,"type":0},{"id":3,"sLen":0,"time":26966.293,"type":0},{"id":2,"sLen":0,"time":27134.832,"type":0},{"id":0,"sLen":0,"time":27303.3711,"type":0},{"id":2,"sLen":0,"time":27471.91,"type":0},{"id":3,"sLen":0,"time":27640.45,"type":0},{"id":2,"sLen":0,"time":27808.9883,"type":0},{"id":0,"sLen":0,"time":27977.5273,"type":0},{"id":2,"sLen":0,"time":28146.0684,"type":0},{"id":3,"sLen":0,"time":28314.6074,"type":0},{"id":2,"sLen":0,"time":28483.1465,"type":0},{"id":3,"sLen":0,"time":28651.6855,"type":0},{"id":2,"sLen":0,"time":28820.2246,"type":0},{"id":3,"sLen":0,"time":28988.7637,"type":0},{"id":0,"sLen":0,"time":29157.3027,"type":0},{"id":1,"sLen":0,"time":29325.8418,"type":0},{"id":0,"sLen":0,"time":29494.3828,"type":0},{"id":0,"sLen":0,"time":29662.9219,"type":0},{"id":0,"sLen":0,"time":30000,"type":0},{"id":0,"sLen":0,"time":30337.0781,"type":0},{"id":0,"sLen":0,"time":30674.1582,"type":0},{"id":0,"sLen":0,"time":31011.2363,"type":0},{"id":0,"sLen":0,"time":31348.3145,"type":0},{"id":0,"sLen":0,"time":31685.3926,"type":0},{"id":0,"sLen":0,"time":31853.9316,"type":0},{"id":0,"sLen":0,"time":32022.4727,"type":0},{"id":0,"sLen":0,"time":32191.0117,"type":0},{"id":0,"sLen":0,"time":32359.55,"type":0},{"id":2,"sLen":0,"time":32696.6289,"type":0},{"id":3,"sLen":0,"time":32865.168,"type":0},{"id":0,"sLen":0,"time":33033.707,"type":0},{"id":2,"sLen":0,"time":33370.7852,"type":0},{"id":3,"sLen":0,"time":33539.3242,"type":0},{"id":1,"sLen":0,"time":33707.8633,"type":0},{"id":3,"sLen":0,"time":33876.4063,"type":0},{"id":1,"sLen":0,"time":34044.9453,"type":0},{"id":0,"sLen":0,"time":34213.4844,"type":0},{"id":1,"sLen":0,"time":34382.0234,"type":0},{"id":3,"sLen":0,"time":34550.5625,"type":0},{"id":1,"sLen":0,"time":34719.1,"type":0},{"id":0,"sLen":0,"time":34887.64,"type":0},{"id":0,"sLen":0,"time":35056.18,"type":0},{"id":2,"sLen":0,"time":35393.2578,"type":0},{"id":3,"sLen":0,"time":35561.7969,"type":0},{"id":0,"sLen":0,"time":35730.3359,"type":0},{"id":2,"sLen":0,"time":36067.4141,"type":0},{"id":3,"sLen":0,"time":36235.9531,"type":0},{"id":3,"sLen":0,"time":36404.4961,"type":0},{"id":1,"sLen":0,"time":36573.0352,"type":0},{"id":0,"sLen":0,"time":36741.5742,"type":0},{"id":1,"sLen":0,"time":36910.1133,"type":0},{"id":0,"sLen":0,"time":37078.6523,"type":0},{"id":1,"sLen":0,"time":37247.19,"type":0},{"id":1,"sLen":0,"time":37415.73,"type":0},{"id":0,"sLen":0,"time":37584.27,"type":0},{"id":3,"sLen":0,"time":43146.0664,"type":0},{"id":1,"sLen":0,"time":43314.6055,"type":0},{"id":0,"sLen":0,"time":43483.1445,"type":0},{"id":3,"sLen":0,"time":43651.6836,"type":0},{"id":2,"sLen":0,"time":43820.2266,"type":0},{"id":0,"sLen":0,"time":44157.3047,"type":0},{"id":3,"sLen":0,"time":44325.8438,"type":0},{"id":2,"sLen":0,"time":44494.3828,"type":0},{"id":3,"sLen":0,"time":44662.92,"type":0},{"id":0,"sLen":0,"time":44831.46,"type":0},{"id":2,"sLen":0,"time":45000,"type":0},{"id":3,"sLen":0,"time":45168.54,"type":0},{"id":2,"sLen":0,"time":45337.08,"type":0},{"id":3,"sLen":0,"time":45505.6172,"type":0},{"id":1,"sLen":0,"time":45674.1563,"type":0},{"id":0,"sLen":0,"time":45842.6953,"type":0},{"id":3,"sLen":0,"time":46011.2344,"type":0},{"id":1,"sLen":0,"time":46179.7734,"type":0},{"id":3,"sLen":0,"time":46348.3164,"type":0},{"id":0,"sLen":0,"time":46516.8555,"type":0},{"id":3,"sLen":0,"time":46685.3945,"type":0},{"id":1,"sLen":0,"time":46853.9336,"type":0},{"id":3,"sLen":0,"time":47022.4727,"type":0},{"id":2,"sLen":0,"time":47191.01,"type":0},{"id":1,"sLen":0,"time":47528.09,"type":0},{"id":1,"sLen":0,"time":47865.168,"type":0},{"id":1,"sLen":0,"time":48202.2461,"type":0},{"id":2,"sLen":0,"time":53932.5859,"type":0},{"id":3,"sLen":0,"time":54101.125,"type":0},{"id":0,"sLen":0,"time":54269.6641,"type":0},{"id":0,"sLen":0,"time":54606.7422,"type":0},{"id":3,"sLen":0,"time":54775.28,"type":0},{"id":2,"sLen":0,"time":54943.82,"type":0},{"id":1,"sLen":0,"time":55280.9,"type":0},{"id":0,"sLen":0,"time":55449.4375,"type":0},{"id":3,"sLen":0,"time":55617.9766,"type":0},{"id":2,"sLen":0,"time":55786.5156,"type":0},{"id":0,"sLen":0,"time":55955.0547,"type":0},{"id":3,"sLen":0,"time":56123.5938,"type":0},{"id":1,"sLen":0,"time":56292.1367,"type":0},{"id":0,"sLen":0,"time":56460.6758,"type":0},{"id":2,"sLen":0,"time":56629.2148,"type":0},{"id":3,"sLen":0,"time":56797.7539,"type":0},{"id":0,"sLen":0,"time":56966.293,"type":0},{"id":0,"sLen":0,"time":57303.37,"type":0},{"id":3,"sLen":0,"time":57471.91,"type":0},{"id":2,"sLen":0,"time":57640.45,"type":0},{"id":1,"sLen":0,"time":57977.5273,"type":0},{"id":0,"sLen":0,"time":58146.0664,"type":0},{"id":3,"sLen":0,"time":58314.6055,"type":0},{"id":2,"sLen":0,"time":58483.1445,"type":0},{"id":0,"sLen":0,"time":58651.6836,"type":0},{"id":3,"sLen":0,"time":58820.2266,"type":0},{"id":1,"sLen":84.2696599999908,"time":58988.7656,"type":0},{"id":0,"sLen":0,"time":59157.3047,"type":0},{"id":0,"sLen":168.539321999997,"time":59325.8438,"type":0},{"id":0,"sLen":168.539321999997,"time":60000,"type":0},{"id":0,"sLen":168.539322000004,"time":60674.1563,"type":0},{"id":0,"sLen":168.539321999997,"time":61348.3164,"type":0},{"id":1,"sLen":0,"time":62022.4727,"type":1},{"id":1,"sLen":252.808989999998,"time":64719.1,"type":0},{"id":0,"sLen":252.808989999998,"time":65056.18,"type":0},{"id":3,"sLen":252.808989999998,"time":65393.2578,"type":0},{"id":1,"sLen":252.808990000005,"time":65730.3359,"type":0},{"id":0,"sLen":0,"time":66067.4141,"type":0},{"id":0,"sLen":0,"time":66404.49,"type":0},{"id":0,"sLen":0,"time":66741.57,"type":0},{"id":0,"sLen":0,"time":66910.11,"type":0},{"id":2,"sLen":0,"time":67078.65,"type":0},{"id":1,"sLen":0,"time":67415.73,"type":0},{"id":0,"sLen":0,"time":67584.27,"type":0},{"id":3,"sLen":0,"time":67752.81,"type":0},{"id":1,"sLen":0,"time":67921.35,"type":0},{"id":0,"sLen":0,"time":68089.89,"type":0},{"id":3,"sLen":0,"time":68258.43,"type":0},{"id":1,"sLen":0,"time":68426.97,"type":0},{"id":3,"sLen":0,"time":68595.51,"type":0},{"id":1,"sLen":0,"time":68764.05,"type":0},{"id":0,"sLen":0,"time":68932.5859,"type":0},{"id":3,"sLen":0,"time":69101.125,"type":0},{"id":2,"sLen":0,"time":69269.6641,"type":0},{"id":0,"sLen":0,"time":69438.2,"type":0},{"id":3,"sLen":0,"time":69606.74,"type":0},{"id":1,"sLen":0,"time":69775.28,"type":0},{"id":0,"sLen":0,"time":69943.82,"type":0},{"id":0,"sLen":0,"time":75505.62,"type":0},{"id":0,"sLen":0,"time":75674.16,"type":0},{"id":0,"sLen":0,"time":75842.6953,"type":0},{"id":2,"sLen":0,"time":76011.2344,"type":0},{"id":3,"sLen":0,"time":76179.77,"type":0},{"id":2,"sLen":0,"time":76348.31,"type":0},{"id":0,"sLen":0,"time":76516.85,"type":0},{"id":2,"sLen":0,"time":76685.39,"type":0},{"id":3,"sLen":0,"time":76853.93,"type":0},{"id":0,"sLen":0,"time":77022.47,"type":0},{"id":3,"sLen":0,"time":77191.01,"type":0},{"id":0,"sLen":0,"time":77359.55,"type":0},{"id":1,"sLen":252.808990000005,"time":77528.0859,"type":0},{"id":0,"sLen":0,"time":77865.17,"type":0},{"id":0,"sLen":0,"time":78033.71,"type":0},{"id":1,"sLen":0,"time":78202.25,"type":0},{"id":0,"sLen":0,"time":78370.79,"type":0},{"id":3,"sLen":0,"time":78539.33,"type":0},{"id":1,"sLen":0,"time":78876.41,"type":0},{"id":0,"sLen":0,"time":79044.9453,"type":0},{"id":2,"sLen":0,"time":79213.4844,"type":0},{"id":0,"sLen":0,"time":79382.02,"type":0},{"id":1,"sLen":0,"time":79550.56,"type":0},{"id":0,"sLen":0,"time":79803.37,"type":0},{"id":1,"sLen":0,"time":80056.18,"type":0},{"id":0,"sLen":0,"time":80224.72,"type":0},{"id":3,"sLen":0,"time":80393.26,"type":0},{"id":0,"sLen":0,"time":80561.8,"type":0},{"id":2,"sLen":0,"time":80730.3359,"type":0},{"id":0,"sLen":0,"time":86292.13,"type":0},{"id":0,"sLen":0,"time":86460.67,"type":0},{"id":1,"sLen":0,"time":86629.21,"type":0},{"id":1,"sLen":0,"time":86797.75,"type":0},{"id":3,"sLen":0,"time":86966.29,"type":0},{"id":3,"sLen":0,"time":87134.83,"type":0},{"id":0,"sLen":0,"time":87303.37,"type":0},{"id":1,"sLen":0,"time":87471.91,"type":0},{"id":0,"sLen":0,"time":87640.45,"type":0},{"id":0,"sLen":0,"time":87808.99,"type":0},{"id":1,"sLen":0,"time":87977.53,"type":0},{"id":1,"sLen":0,"time":88146.07,"type":0},{"id":3,"sLen":0,"time":88314.61,"type":0},{"id":3,"sLen":0,"time":88483.15,"type":0},{"id":0,"sLen":0,"time":88651.69,"type":0},{"id":3,"sLen":0,"time":88820.23,"type":0},{"id":2,"sLen":0,"time":88988.7656,"type":0},{"id":0,"sLen":0,"time":89157.3047,"type":0},{"id":3,"sLen":0,"time":89325.84,"type":0},{"id":2,"sLen":0,"time":89494.38,"type":0},{"id":0,"sLen":0,"time":89662.92,"type":0},{"id":0,"sLen":0,"time":89831.46,"type":0},{"id":3,"sLen":0,"time":90000,"type":0},{"id":1,"sLen":0,"time":90168.54,"type":0},{"id":0,"sLen":0,"time":90337.08,"type":0},{"id":1,"sLen":0,"time":90505.62,"type":0},{"id":3,"sLen":0,"time":90674.16,"type":0},{"id":1,"sLen":0,"time":90842.6953,"type":0},{"id":0,"sLen":0,"time":91011.2344,"type":0},{"id":1,"sLen":0,"time":91179.77,"type":0},{"id":3,"sLen":337.078643999994,"time":91348.31,"type":0},{"id":2,"sLen":0,"time":97078.65,"type":0},{"id":0,"sLen":0,"time":97247.19,"type":0},{"id":3,"sLen":0,"time":97415.73,"type":0},{"id":2,"sLen":0,"time":97584.27,"type":0},{"id":0,"sLen":0,"time":97752.81,"type":0},{"id":3,"sLen":0,"time":97921.35,"type":0},{"id":2,"sLen":0,"time":98089.89,"type":0},{"id":0,"sLen":0,"time":98258.43,"type":0},{"id":3,"sLen":0,"time":98426.97,"type":0},{"id":2,"sLen":0,"time":98595.51,"type":0},{"id":0,"sLen":0,"time":98764.05,"type":0},{"id":3,"sLen":0,"time":98932.5859,"type":0},{"id":1,"sLen":0,"time":99101.125,"type":0},{"id":3,"sLen":0,"time":99269.6641,"type":0},{"id":0,"sLen":0,"time":99438.2,"type":0},{"id":3,"sLen":0,"time":99606.74,"type":0},{"id":2,"sLen":0,"time":99775.28,"type":0},{"id":0,"sLen":0,"time":99943.82,"type":0},{"id":3,"sLen":0,"time":100112.359,"type":0},{"id":2,"sLen":0,"time":100280.9,"type":0},{"id":0,"sLen":0,"time":100449.438,"type":0},{"id":3,"sLen":0,"time":100617.977,"type":0},{"id":2,"sLen":0,"time":100786.516,"type":0},{"id":0,"sLen":0,"time":100955.055,"type":0},{"id":3,"sLen":0,"time":101123.594,"type":0},{"id":2,"sLen":0,"time":101292.133,"type":0},{"id":0,"sLen":0,"time":101460.672,"type":0},{"id":3,"sLen":0,"time":101629.211,"type":0},{"id":1,"sLen":0,"time":101797.75,"type":0},{"id":3,"sLen":0,"time":101966.289,"type":0},{"id":0,"sLen":0,"time":102134.828,"type":0},{"id":3,"sLen":0,"time":102303.367,"type":0},{"id":2,"sLen":0,"time":102471.906,"type":0},{"id":0,"sLen":0,"time":102640.453,"type":0},{"id":3,"sLen":0,"time":102808.992,"type":0},{"id":2,"sLen":0,"time":102977.531,"type":0},{"id":0,"sLen":0,"time":103146.07,"type":0},{"id":3,"sLen":0,"time":103314.609,"type":0},{"id":2,"sLen":0,"time":103483.148,"type":0},{"id":0,"sLen":0,"time":103651.688,"type":0},{"id":3,"sLen":0,"time":103820.227,"type":0},{"id":2,"sLen":0,"time":103988.766,"type":0},{"id":0,"sLen":0,"time":104157.305,"type":0},{"id":3,"sLen":0,"time":104325.844,"type":0},{"id":1,"sLen":0,"time":104494.383,"type":0},{"id":3,"sLen":0,"time":104662.922,"type":0},{"id":0,"sLen":0,"time":104831.461,"type":0},{"id":3,"sLen":0,"time":105000,"type":0},{"id":2,"sLen":0,"time":105168.539,"type":0},{"id":0,"sLen":0,"time":105337.078,"type":0},{"id":3,"sLen":0,"time":105505.617,"type":0},{"id":2,"sLen":0,"time":105674.156,"type":0},{"id":0,"sLen":0,"time":105842.695,"type":0},{"id":3,"sLen":0,"time":106011.234,"type":0},{"id":2,"sLen":0,"time":106179.773,"type":0},{"id":0,"sLen":0,"time":106348.313,"type":0},{"id":3,"sLen":0,"time":106516.852,"type":0},{"id":2,"sLen":0,"time":106685.391,"type":0},{"id":0,"sLen":0,"time":106853.93,"type":0},{"id":3,"sLen":0,"time":107022.469,"type":0},{"id":1,"sLen":0,"time":107191.008,"type":0},{"id":3,"sLen":0,"time":107359.547,"type":0},{"id":0,"sLen":0,"time":107528.086,"type":0},{"id":3,"sLen":0,"time":107696.633,"type":0},{"id":1,"sLen":0,"time":107865.172,"type":0},{"id":0,"sLen":0,"time":107949.438,"type":0},{"id":1,"sLen":0,"time":108033.711,"type":0},{"id":1,"sLen":0,"time":108202.25,"type":0},{"id":0,"sLen":0,"time":108370.789,"type":0},{"id":1,"sLen":0,"time":108539.328,"type":0},{"id":0,"sLen":0,"time":108623.594,"type":0},{"id":1,"sLen":0,"time":108707.867,"type":0},{"id":1,"sLen":0,"time":108876.406,"type":0},{"id":0,"sLen":0,"time":109044.945,"type":0},{"id":1,"sLen":0,"time":109213.484,"type":0},{"id":3,"sLen":0,"time":109297.75,"type":0},{"id":1,"sLen":0,"time":109382.023,"type":0},{"id":1,"sLen":0,"time":109550.563,"type":0},{"id":0,"sLen":0,"time":109719.1,"type":0},{"id":1,"sLen":0,"time":109887.641,"type":0},{"id":0,"sLen":0,"time":109971.906,"type":0},{"id":1,"sLen":0,"time":110056.18,"type":0},{"id":1,"sLen":0,"time":110224.719,"type":0},{"id":0,"sLen":0,"time":110393.258,"type":0},{"id":2,"sLen":0,"time":113258.43,"type":0},{"id":3,"sLen":0,"time":113426.969,"type":0},{"id":2,"sLen":0,"time":113595.508,"type":0},{"id":3,"sLen":0,"time":113764.047,"type":0},{"id":0,"sLen":0,"time":113932.586,"type":0},{"id":1,"sLen":0,"time":114101.125,"type":0},{"id":0,"sLen":0,"time":114269.664,"type":0},{"id":1,"sLen":0,"time":114438.2,"type":0},{"id":2,"sLen":0,"time":114606.742,"type":0},{"id":3,"sLen":0,"time":114775.281,"type":0},{"id":2,"sLen":0,"time":114943.82,"type":0},{"id":3,"sLen":0,"time":115112.359,"type":0},{"id":0,"sLen":0,"time":115280.9,"type":0},{"id":3,"sLen":0,"time":115449.438,"type":0},{"id":0,"sLen":0,"time":115617.977,"type":0},{"id":1,"sLen":0,"time":115786.516,"type":0},{"id":2,"sLen":589.887633999984,"time":115955.055,"type":0}],"position":"dad","characters":["tankman"],"visible":true,"type":0},{"notes":[{"id":2,"sLen":0,"time":5393.2583,"type":0},{"id":3,"sLen":0,"time":5561.798,"type":0},{"id":0,"sLen":0,"time":5730.337,"type":0},{"id":0,"sLen":0,"time":6067.41553,"type":0},{"id":3,"sLen":0,"time":6235.955,"type":0},{"id":2,"sLen":0,"time":6404.494,"type":0},{"id":1,"sLen":0,"time":6741.573,"type":0},{"id":0,"sLen":0,"time":6910.1123,"type":0},{"id":3,"sLen":0,"time":7078.652,"type":0},{"id":2,"sLen":0,"time":7247.191,"type":0},{"id":0,"sLen":0,"time":7415.73047,"type":0},{"id":3,"sLen":0,"time":7584.26953,"type":0},{"id":1,"sLen":0,"time":7752.809,"type":0},{"id":0,"sLen":0,"time":7921.348,"type":0},{"id":2,"sLen":0,"time":8089.8877,"type":0},{"id":3,"sLen":0,"time":8258.427,"type":0},{"id":0,"sLen":0,"time":8426.966,"type":0},{"id":0,"sLen":0,"time":8764.045,"type":0},{"id":3,"sLen":0,"time":8932.584,"type":0},{"id":2,"sLen":0,"time":9101.124,"type":0},{"id":1,"sLen":0,"time":9438.202,"type":0},{"id":0,"sLen":0,"time":9606.741,"type":0},{"id":3,"sLen":0,"time":9775.281,"type":0},{"id":2,"sLen":0,"time":9943.82,"type":0},{"id":0,"sLen":0,"time":10112.3594,"type":0},{"id":3,"sLen":0,"time":10280.8984,"type":0},{"id":1,"sLen":0,"time":10449.4385,"type":0},{"id":0,"sLen":0,"time":10617.9775,"type":0},{"id":0,"sLen":252.808990000001,"time":14831.4609,"type":0},{"id":1,"sLen":252.80899,"time":15168.5391,"type":0},{"id":3,"sLen":252.80899,"time":15505.6182,"type":0},{"id":1,"sLen":252.808990000001,"time":15842.6963,"type":0},{"id":3,"sLen":252.80899,"time":16179.7754,"type":0},{"id":0,"sLen":0,"time":16516.8535,"type":0},{"id":1,"sLen":0,"time":16685.3926,"type":0},{"id":3,"sLen":252.808990000001,"time":16853.9316,"type":0},{"id":1,"sLen":252.808990000001,"time":17191.0117,"type":0},{"id":0,"sLen":0,"time":17528.09,"type":0},{"id":0,"sLen":0,"time":17865.168,"type":0},{"id":0,"sLen":0,"time":18202.248,"type":0},{"id":0,"sLen":0,"time":18370.7871,"type":0},{"id":3,"sLen":0,"time":18539.3262,"type":0},{"id":1,"sLen":0,"time":18876.4043,"type":0},{"id":0,"sLen":0,"time":19044.9434,"type":0},{"id":3,"sLen":0,"time":19213.4824,"type":0},{"id":1,"sLen":0,"time":19382.0215,"type":0},{"id":0,"sLen":0,"time":19550.5625,"type":0},{"id":3,"sLen":0,"time":19719.1016,"type":0},{"id":1,"sLen":0,"time":19887.64,"type":0},{"id":0,"sLen":0,"time":20056.18,"type":0},{"id":1,"sLen":0,"time":20224.7188,"type":0},{"id":0,"sLen":0,"time":20393.2578,"type":0},{"id":3,"sLen":0,"time":20561.7969,"type":0},{"id":1,"sLen":0,"time":20730.3379,"type":0},{"id":0,"sLen":0,"time":20898.877,"type":0},{"id":3,"sLen":0,"time":21067.416,"type":0},{"id":1,"sLen":0,"time":21235.9551,"type":0},{"id":0,"sLen":0,"time":21404.4941,"type":0},{"id":3,"sLen":0,"time":21573.0332,"type":0},{"id":3,"sLen":0,"time":21910.1133,"type":0},{"id":3,"sLen":0,"time":22247.1914,"type":0},{"id":3,"sLen":0,"time":22584.27,"type":0},{"id":1,"sLen":0,"time":22921.3477,"type":0},{"id":1,"sLen":0,"time":23258.4277,"type":0},{"id":3,"sLen":252.808990000001,"time":23595.5059,"type":0},{"id":0,"sLen":252.808990000009,"time":23932.584,"type":0},{"id":3,"sLen":0,"time":24269.6621,"type":0},{"id":2,"sLen":0,"time":24438.2031,"type":0},{"id":0,"sLen":0,"time":24606.7422,"type":0},{"id":2,"sLen":0,"time":24775.2813,"type":0},{"id":3,"sLen":0,"time":24943.82,"type":0},{"id":2,"sLen":0,"time":25112.36,"type":0},{"id":0,"sLen":0,"time":25280.8984,"type":0},{"id":2,"sLen":0,"time":25449.4375,"type":0},{"id":3,"sLen":0,"time":25617.9766,"type":0},{"id":2,"sLen":0,"time":25786.5176,"type":0},{"id":3,"sLen":0,"time":25955.0566,"type":0},{"id":2,"sLen":0,"time":26123.5957,"type":0},{"id":3,"sLen":0,"time":26292.1348,"type":0},{"id":0,"sLen":0,"time":26460.6738,"type":0},{"id":1,"sLen":0,"time":26629.2129,"type":0},{"id":0,"sLen":0,"time":26797.752,"type":0},{"id":2,"sLen":589.887634000006,"time":26966.293,"type":0},{"id":3,"sLen":589.887633999995,"time":27640.45,"type":0},{"id":1,"sLen":589.887633999999,"time":28314.6074,"type":0},{"id":0,"sLen":589.887634000006,"time":28988.7637,"type":0},{"id":3,"sLen":0,"time":29662.9219,"type":0},{"id":2,"sLen":0,"time":29831.46,"type":0},{"id":0,"sLen":0,"time":30000,"type":0},{"id":2,"sLen":0,"time":30168.54,"type":0},{"id":3,"sLen":0,"time":30337.0781,"type":0},{"id":2,"sLen":0,"time":30505.6172,"type":0},{"id":0,"sLen":0,"time":30674.1582,"type":0},{"id":2,"sLen":0,"time":30842.6973,"type":0},{"id":3,"sLen":0,"time":31011.2363,"type":0},{"id":2,"sLen":0,"time":31179.7754,"type":0},{"id":3,"sLen":0,"time":31348.3145,"type":0},{"id":2,"sLen":0,"time":31516.8535,"type":0},{"id":3,"sLen":0,"time":31685.3926,"type":0},{"id":0,"sLen":0,"time":31853.9316,"type":0},{"id":1,"sLen":0,"time":32022.4727,"type":0},{"id":0,"sLen":0,"time":32191.0117,"type":0},{"id":1,"sLen":674.157300000003,"time":32359.55,"type":0},{"id":0,"sLen":0,"time":37752.81,"type":0},{"id":2,"sLen":0,"time":38089.8867,"type":0},{"id":3,"sLen":0,"time":38258.4258,"type":0},{"id":0,"sLen":0,"time":38426.9648,"type":0},{"id":2,"sLen":0,"time":38764.043,"type":0},{"id":3,"sLen":0,"time":38932.5859,"type":0},{"id":1,"sLen":0,"time":39101.125,"type":0},{"id":3,"sLen":0,"time":39269.6641,"type":0},{"id":1,"sLen":0,"time":39438.2031,"type":0},{"id":0,"sLen":0,"time":39606.7422,"type":0},{"id":1,"sLen":0,"time":39775.28,"type":0},{"id":3,"sLen":0,"time":39943.82,"type":0},{"id":1,"sLen":0,"time":40112.36,"type":0},{"id":0,"sLen":0,"time":40280.9,"type":0},{"id":0,"sLen":0,"time":40449.4375,"type":0},{"id":2,"sLen":0,"time":40786.5156,"type":0},{"id":3,"sLen":0,"time":40955.0547,"type":0},{"id":0,"sLen":0,"time":41123.5938,"type":0},{"id":2,"sLen":0,"time":41460.6758,"type":0},{"id":3,"sLen":0,"time":41629.2148,"type":0},{"id":3,"sLen":0,"time":41797.7539,"type":0},{"id":1,"sLen":0,"time":41966.293,"type":0},{"id":0,"sLen":0,"time":42134.832,"type":0},{"id":1,"sLen":0,"time":42303.37,"type":0},{"id":0,"sLen":0,"time":42471.91,"type":0},{"id":1,"sLen":0,"time":42640.45,"type":0},{"id":1,"sLen":0,"time":42808.99,"type":0},{"id":0,"sLen":0,"time":42977.5273,"type":0},{"id":3,"sLen":0,"time":48539.3242,"type":0},{"id":1,"sLen":0,"time":48707.8633,"type":0},{"id":0,"sLen":0,"time":48876.4063,"type":0},{"id":3,"sLen":0,"time":49044.9453,"type":0},{"id":2,"sLen":0,"time":49213.4844,"type":0},{"id":0,"sLen":0,"time":49550.5625,"type":0},{"id":3,"sLen":0,"time":49719.1,"type":0},{"id":2,"sLen":0,"time":49887.64,"type":0},{"id":3,"sLen":0,"time":50056.18,"type":0},{"id":0,"sLen":0,"time":50224.72,"type":0},{"id":2,"sLen":0,"time":50393.2578,"type":0},{"id":3,"sLen":0,"time":50561.7969,"type":0},{"id":2,"sLen":0,"time":50730.3359,"type":0},{"id":3,"sLen":0,"time":50898.875,"type":0},{"id":1,"sLen":0,"time":51067.4141,"type":0},{"id":0,"sLen":0,"time":51235.9531,"type":0},{"id":3,"sLen":0,"time":51404.4961,"type":0},{"id":1,"sLen":0,"time":51573.0352,"type":0},{"id":3,"sLen":0,"time":51741.5742,"type":0},{"id":0,"sLen":0,"time":51910.1133,"type":0},{"id":3,"sLen":0,"time":52078.6523,"type":0},{"id":1,"sLen":0,"time":52247.19,"type":0},{"id":3,"sLen":0,"time":52415.73,"type":0},{"id":2,"sLen":0,"time":52584.27,"type":0},{"id":1,"sLen":0,"time":52921.3477,"type":0},{"id":1,"sLen":0,"time":53258.4258,"type":0},{"id":1,"sLen":0,"time":53595.5039,"type":0},{"id":1,"sLen":1011.23595999999,"time":53932.5859,"type":0},{"id":2,"sLen":505.617981000003,"time":55955.0547,"type":0},{"id":1,"sLen":674.157299999999,"time":56629.2148,"type":0},{"id":0,"sLen":252.808989999998,"time":58651.6836,"type":0},{"id":2,"sLen":84.2696599999908,"time":58988.7656,"type":0},{"id":0,"sLen":0,"time":59157.3047,"type":0},{"id":2,"sLen":0,"time":59325.8438,"type":0},{"id":3,"sLen":0,"time":59494.3828,"type":0},{"id":0,"sLen":0,"time":59662.92,"type":0},{"id":0,"sLen":0,"time":60000,"type":0},{"id":3,"sLen":0,"time":60168.54,"type":0},{"id":2,"sLen":0,"time":60337.08,"type":0},{"id":1,"sLen":0,"time":60674.1563,"type":0},{"id":0,"sLen":0,"time":60842.6953,"type":0},{"id":3,"sLen":0,"time":61011.2344,"type":0},{"id":2,"sLen":0,"time":61179.7734,"type":0},{"id":0,"sLen":0,"time":61348.3164,"type":0},{"id":3,"sLen":0,"time":61516.8555,"type":0},{"id":1,"sLen":0,"time":61685.3945,"type":0},{"id":0,"sLen":0,"time":61853.9336,"type":0},{"id":2,"sLen":0,"time":62022.4727,"type":0},{"id":3,"sLen":0,"time":62191.01,"type":0},{"id":0,"sLen":0,"time":62359.55,"type":0},{"id":0,"sLen":0,"time":62696.63,"type":0},{"id":3,"sLen":0,"time":62865.168,"type":0},{"id":2,"sLen":0,"time":63033.707,"type":0},{"id":1,"sLen":0,"time":63370.7852,"type":0},{"id":0,"sLen":0,"time":63539.3242,"type":0},{"id":3,"sLen":0,"time":63707.8633,"type":0},{"id":2,"sLen":0,"time":63876.4063,"type":0},{"id":0,"sLen":0,"time":64044.9453,"type":0},{"id":3,"sLen":0,"time":64213.4844,"type":0},{"id":1,"sLen":0,"time":64382.0234,"type":0},{"id":0,"sLen":0,"time":64550.5625,"type":0},{"id":1,"sLen":252.808989999998,"time":64719.1,"type":0},{"id":0,"sLen":252.808989999998,"time":65056.18,"type":0},{"id":3,"sLen":252.808989999998,"time":65393.2578,"type":0},{"id":1,"sLen":252.808990000005,"time":65730.3359,"type":0},{"id":3,"sLen":505.617981000003,"time":66067.4141,"type":0},{"id":1,"sLen":505.617981000003,"time":66741.57,"type":0},{"id":0,"sLen":505.617981000003,"time":67415.73,"type":0},{"id":1,"sLen":252.808990000005,"time":68089.89,"type":0},{"id":0,"sLen":252.808990000005,"time":68426.97,"type":0},{"id":3,"sLen":589.887633999999,"time":68764.05,"type":0},{"id":0,"sLen":589.887633999999,"time":69438.2,"type":0},{"id":1,"sLen":252.808990000005,"time":70112.36,"type":0},{"id":0,"sLen":252.808990000005,"time":70449.44,"type":0},{"id":3,"sLen":252.808990000005,"time":70786.5156,"type":0},{"id":1,"sLen":252.808990000005,"time":71123.59,"type":0},{"id":0,"sLen":0,"time":71460.67,"type":0},{"id":0,"sLen":0,"time":71797.75,"type":0},{"id":0,"sLen":0,"time":72134.83,"type":0},{"id":0,"sLen":0,"time":72303.37,"type":0},{"id":2,"sLen":0,"time":72471.91,"type":0},{"id":1,"sLen":0,"time":72808.99,"type":0},{"id":0,"sLen":0,"time":72977.53,"type":0},{"id":3,"sLen":0,"time":73146.07,"type":0},{"id":1,"sLen":0,"time":73314.61,"type":0},{"id":0,"sLen":0,"time":73483.15,"type":0},{"id":3,"sLen":0,"time":73651.69,"type":0},{"id":1,"sLen":0,"time":73820.23,"type":0},{"id":3,"sLen":0,"time":73988.7656,"type":0},{"id":1,"sLen":0,"time":74157.3047,"type":0},{"id":0,"sLen":0,"time":74325.84,"type":0},{"id":3,"sLen":0,"time":74494.38,"type":0},{"id":2,"sLen":0,"time":74662.92,"type":0},{"id":0,"sLen":0,"time":74831.46,"type":0},{"id":3,"sLen":0,"time":75000,"type":0},{"id":1,"sLen":0,"time":75168.54,"type":0},{"id":0,"sLen":0,"time":75337.08,"type":0},{"id":1,"sLen":1264.04492,"time":75505.62,"type":0},{"id":0,"sLen":1264.04492,"time":76853.93,"type":0},{"id":3,"sLen":1264.04492,"time":78202.25,"type":0},{"id":0,"sLen":1264.04492,"time":79550.56,"type":0},{"id":0,"sLen":0,"time":80898.875,"type":0},{"id":0,"sLen":0,"time":81067.4141,"type":0},{"id":0,"sLen":0,"time":81235.95,"type":0},{"id":2,"sLen":0,"time":81404.49,"type":0},{"id":3,"sLen":0,"time":81573.03,"type":0},{"id":2,"sLen":0,"time":81741.57,"type":0},{"id":0,"sLen":0,"time":81910.11,"type":0},{"id":2,"sLen":0,"time":82078.65,"type":0},{"id":3,"sLen":0,"time":82247.19,"type":0},{"id":0,"sLen":0,"time":82415.73,"type":0},{"id":3,"sLen":0,"time":82584.27,"type":0},{"id":0,"sLen":0,"time":82752.81,"type":0},{"id":1,"sLen":252.808990000005,"time":82921.35,"type":0},{"id":0,"sLen":0,"time":83258.43,"type":0},{"id":0,"sLen":0,"time":83426.97,"type":0},{"id":1,"sLen":0,"time":83595.51,"type":0},{"id":0,"sLen":0,"time":83764.05,"type":0},{"id":3,"sLen":0,"time":83932.5859,"type":0},{"id":1,"sLen":0,"time":84269.6641,"type":0},{"id":0,"sLen":0,"time":84438.2,"type":0},{"id":2,"sLen":0,"time":84606.74,"type":0},{"id":0,"sLen":0,"time":84775.28,"type":0},{"id":1,"sLen":0,"time":84943.82,"type":0},{"id":0,"sLen":0,"time":85196.63,"type":0},{"id":1,"sLen":0,"time":85449.44,"type":0},{"id":0,"sLen":0,"time":85617.98,"type":0},{"id":3,"sLen":0,"time":85786.5156,"type":0},{"id":0,"sLen":0,"time":85955.0547,"type":0},{"id":2,"sLen":0,"time":86123.59,"type":0},{"id":0,"sLen":0,"time":91685.39,"type":0},{"id":0,"sLen":0,"time":91853.93,"type":0},{"id":1,"sLen":0,"time":92022.47,"type":0},{"id":1,"sLen":0,"time":92191.01,"type":0},{"id":3,"sLen":0,"time":92359.55,"type":0},{"id":3,"sLen":0,"time":92528.0859,"type":0},{"id":0,"sLen":0,"time":92696.63,"type":0},{"id":1,"sLen":0,"time":92865.17,"type":0},{"id":0,"sLen":0,"time":93033.71,"type":0},{"id":0,"sLen":0,"time":93202.25,"type":0},{"id":1,"sLen":0,"time":93370.79,"type":0},{"id":1,"sLen":0,"time":93539.33,"type":0},{"id":3,"sLen":0,"time":93707.87,"type":0},{"id":3,"sLen":0,"time":93876.41,"type":0},{"id":0,"sLen":0,"time":94044.9453,"type":0},{"id":3,"sLen":0,"time":94213.4844,"type":0},{"id":2,"sLen":0,"time":94382.02,"type":0},{"id":0,"sLen":0,"time":94550.56,"type":0},{"id":3,"sLen":0,"time":94719.1,"type":0},{"id":2,"sLen":0,"time":94887.64,"type":0},{"id":0,"sLen":0,"time":95056.18,"type":0},{"id":0,"sLen":0,"time":95224.72,"type":0},{"id":3,"sLen":0,"time":95393.26,"type":0},{"id":1,"sLen":0,"time":95561.8,"type":0},{"id":0,"sLen":0,"time":95730.3359,"type":0},{"id":1,"sLen":0,"time":95898.875,"type":0},{"id":3,"sLen":0,"time":96067.4141,"type":0},{"id":1,"sLen":0,"time":96235.95,"type":0},{"id":0,"sLen":0,"time":96404.49,"type":0},{"id":1,"sLen":0,"time":96573.03,"type":0},{"id":3,"sLen":337.078643999994,"time":96741.57,"type":0},{"id":0,"sLen":589.88763399997,"time":98426.97,"type":0},{"id":2,"sLen":589.887633999999,"time":99101.125,"type":0},{"id":0,"sLen":589.887633999999,"time":101123.594,"type":0},{"id":2,"sLen":589.887633999984,"time":101797.75,"type":0},{"id":2,"sLen":0,"time":102471.906,"type":0},{"id":3,"sLen":0,"time":102640.453,"type":0},{"id":0,"sLen":0,"time":102808.992,"type":0},{"id":2,"sLen":0,"time":102977.531,"type":0},{"id":3,"sLen":0,"time":103146.07,"type":0},{"id":0,"sLen":0,"time":103314.609,"type":0},{"id":2,"sLen":0,"time":103483.148,"type":0},{"id":3,"sLen":0,"time":103651.688,"type":0},{"id":0,"sLen":0,"time":103820.227,"type":0},{"id":2,"sLen":0,"time":103988.766,"type":0},{"id":3,"sLen":0,"time":104157.305,"type":0},{"id":0,"sLen":0,"time":104325.844,"type":0},{"id":1,"sLen":0,"time":104494.383,"type":0},{"id":0,"sLen":0,"time":104662.922,"type":0},{"id":3,"sLen":0,"time":104831.461,"type":0},{"id":0,"sLen":0,"time":105000,"type":0},{"id":2,"sLen":0,"time":105168.539,"type":0},{"id":3,"sLen":0,"time":105337.078,"type":0},{"id":0,"sLen":0,"time":105505.617,"type":0},{"id":2,"sLen":0,"time":105674.156,"type":0},{"id":3,"sLen":0,"time":105842.695,"type":0},{"id":0,"sLen":0,"time":106011.234,"type":0},{"id":2,"sLen":0,"time":106179.773,"type":0},{"id":3,"sLen":0,"time":106348.313,"type":0},{"id":0,"sLen":0,"time":106516.852,"type":0},{"id":2,"sLen":0,"time":106685.391,"type":0},{"id":3,"sLen":0,"time":106853.93,"type":0},{"id":0,"sLen":0,"time":107022.469,"type":0},{"id":1,"sLen":0,"time":107191.008,"type":0},{"id":0,"sLen":0,"time":107359.547,"type":0},{"id":3,"sLen":0,"time":107528.086,"type":0},{"id":0,"sLen":0,"time":107696.633,"type":0},{"id":3,"sLen":589.887634000013,"time":107865.172,"type":0},{"id":1,"sLen":589.887634000013,"time":108539.328,"type":0},{"id":0,"sLen":589.887633999999,"time":109213.484,"type":0},{"id":1,"sLen":589.887633999999,"time":109887.641,"type":0},{"id":1,"sLen":0,"time":110561.8,"type":0},{"id":0,"sLen":0,"time":110646.07,"type":0},{"id":1,"sLen":0,"time":110730.336,"type":0},{"id":1,"sLen":0,"time":110898.875,"type":0},{"id":0,"sLen":0,"time":111067.414,"type":0},{"id":1,"sLen":0,"time":111235.953,"type":0},{"id":0,"sLen":0,"time":111320.227,"type":0},{"id":1,"sLen":0,"time":111404.492,"type":0},{"id":1,"sLen":0,"time":111573.031,"type":0},{"id":0,"sLen":0,"time":111741.57,"type":0},{"id":1,"sLen":0,"time":111910.109,"type":0},{"id":3,"sLen":0,"time":111994.383,"type":0},{"id":1,"sLen":0,"time":112078.648,"type":0},{"id":1,"sLen":0,"time":112247.188,"type":0},{"id":0,"sLen":0,"time":112415.727,"type":0},{"id":1,"sLen":0,"time":112584.273,"type":0},{"id":0,"sLen":0,"time":112668.539,"type":0},{"id":1,"sLen":0,"time":112752.813,"type":0},{"id":1,"sLen":0,"time":112921.352,"type":0},{"id":0,"sLen":0,"time":113089.891,"type":0},{"id":3,"sLen":589.887633999999,"time":113258.43,"type":0},{"id":1,"sLen":589.887633999999,"time":113932.586,"type":0},{"id":0,"sLen":589.887633999984,"time":114606.742,"type":0},{"id":1,"sLen":589.887633999999,"time":115280.9,"type":0},{"id":2,"sLen":0,"time":115955.055,"type":0},{"id":3,"sLen":0,"time":116123.594,"type":0},{"id":2,"sLen":0,"time":116292.133,"type":0},{"id":3,"sLen":0,"time":116460.672,"type":0},{"id":0,"sLen":0,"time":116629.211,"type":0},{"id":1,"sLen":0,"time":116797.75,"type":0},{"id":0,"sLen":0,"time":116966.289,"type":0},{"id":1,"sLen":0,"time":117134.828,"type":0},{"id":2,"sLen":0,"time":117303.367,"type":0},{"id":3,"sLen":0,"time":117471.906,"type":0},{"id":2,"sLen":0,"time":117640.453,"type":0},{"id":3,"sLen":0,"time":117808.992,"type":0},{"id":0,"sLen":0,"time":117977.531,"type":0},{"id":3,"sLen":0,"time":118146.07,"type":0},{"id":0,"sLen":0,"time":118314.609,"type":0},{"id":1,"sLen":0,"time":118483.148,"type":0}],"position":"boyfriend","characters":["bf-holding-gf"],"visible":true,"type":1},{"notes":[{"id":0,"sLen":0,"time":0,"type":0},{"id":3,"sLen":0,"time":168.539325842697,"type":0},{"id":3,"sLen":0,"time":252.808988764045,"type":0},{"id":3,"sLen":0,"time":421.348314606742,"type":0},{"id":0,"sLen":0,"time":589.887640449438,"type":0},{"id":3,"sLen":0,"time":758.426966292135,"type":0},{"id":3,"sLen":0,"time":842.696629213483,"type":0},{"id":0,"sLen":0,"time":1011.23595505618,"type":0},{"id":0,"sLen":0,"time":1179.77528089888,"type":0},{"id":0,"sLen":0,"time":1264.04494382022,"type":0},{"id":3,"sLen":0,"time":1348.31460674157,"type":0},{"id":0,"sLen":0,"time":1516.85393258427,"type":0},{"id":0,"sLen":0,"time":1601.12359550562,"type":0},{"id":3,"sLen":0,"time":1853.93258426966,"type":0},{"id":0,"sLen":0,"time":2022.47191011236,"type":0},{"id":3,"sLen":0,"time":2106.74157303371,"type":0},{"id":3,"sLen":0,"time":2191.01123595506,"type":0},{"id":0,"sLen":0,"time":2359.55056179775,"type":0},{"id":0,"sLen":0,"time":2696.62921348315,"type":0},{"id":3,"sLen":0,"time":2865.16853932584,"type":0},{"id":3,"sLen":0,"time":2949.43820224719,"type":0},{"id":3,"sLen":0,"time":3117.97752808989,"type":0},{"id":0,"sLen":0,"time":3286.51685393258,"type":0},{"id":3,"sLen":0,"time":3455.05617977528,"type":0},{"id":3,"sLen":0,"time":3539.32584269663,"type":0},{"id":0,"sLen":0,"time":3707.86516853933,"type":0},{"id":0,"sLen":0,"time":3876.40449438202,"type":0},{"id":0,"sLen":0,"time":3960.67415730337,"type":0},{"id":3,"sLen":0,"time":4044.94382022472,"type":0},{"id":0,"sLen":0,"time":4213.48314606742,"type":0},{"id":0,"sLen":0,"time":4297.75280898876,"type":0},{"id":3,"sLen":0,"time":4550.56179775281,"type":0},{"id":0,"sLen":0,"time":4719.10112359551,"type":0},{"id":3,"sLen":0,"time":4803.37078651685,"type":0},{"id":3,"sLen":0,"time":4887.6404494382,"type":0},{"id":0,"sLen":0,"time":5056.1797752809,"type":0},{"id":0,"sLen":0,"time":5140.44943820225,"type":0},{"id":0,"sLen":0,"time":5224.7191011236,"type":0},{"id":0,"sLen":0,"time":5393.25842696629,"type":0},{"id":3,"sLen":0,"time":5561.79775280899,"type":0},{"id":3,"sLen":0,"time":5646.06741573034,"type":0},{"id":3,"sLen":0,"time":5814.60674157303,"type":0},{"id":0,"sLen":0,"time":5983.14606741573,"type":0},{"id":3,"sLen":0,"time":6151.68539325843,"type":0},{"id":3,"sLen":0,"time":6235.95505617978,"type":0},{"id":0,"sLen":0,"time":6404.49438202247,"type":0},{"id":0,"sLen":0,"time":6573.03370786517,"type":0},{"id":0,"sLen":0,"time":6657.30337078652,"type":0},{"id":3,"sLen":0,"time":6741.57303370786,"type":0},{"id":0,"sLen":0,"time":6910.11235955056,"type":0},{"id":0,"sLen":0,"time":6994.38202247191,"type":0},{"id":3,"sLen":0,"time":7247.19101123595,"type":0},{"id":0,"sLen":0,"time":7415.73033707865,"type":0},{"id":3,"sLen":0,"time":7500,"type":0},{"id":3,"sLen":0,"time":7584.26966292135,"type":0},{"id":0,"sLen":0,"time":7752.80898876405,"type":0},{"id":0,"sLen":0,"time":8089.88764044944,"type":0},{"id":3,"sLen":0,"time":8258.42696629213,"type":0},{"id":3,"sLen":0,"time":8342.69662921348,"type":0},{"id":3,"sLen":0,"time":8511.23595505618,"type":0},{"id":0,"sLen":0,"time":8679.77528089888,"type":0},{"id":3,"sLen":0,"time":8848.31460674157,"type":0},{"id":3,"sLen":0,"time":8932.58426966292,"type":0},{"id":0,"sLen":0,"time":9101.12359550562,"type":0},{"id":0,"sLen":0,"time":9269.66292134831,"type":0},{"id":0,"sLen":0,"time":9353.93258426966,"type":0},{"id":3,"sLen":0,"time":9438.20224719101,"type":0},{"id":0,"sLen":0,"time":9606.74157303371,"type":0},{"id":0,"sLen":0,"time":9691.01123595505,"type":0},{"id":3,"sLen":0,"time":9943.8202247191,"type":0},{"id":0,"sLen":0,"time":10112.3595505618,"type":0},{"id":3,"sLen":0,"time":10196.6292134831,"type":0},{"id":3,"sLen":0,"time":10280.8988764045,"type":0},{"id":0,"sLen":0,"time":10449.4382022472,"type":0},{"id":0,"sLen":0,"time":21235.9550561798,"type":0},{"id":3,"sLen":0,"time":21320.2247191011,"type":0},{"id":0,"sLen":0,"time":21404.4943820225,"type":0},{"id":0,"sLen":0,"time":21573.0337078652,"type":0},{"id":3,"sLen":0,"time":21910.1123595506,"type":0},{"id":0,"sLen":0,"time":22247.191011236,"type":0},{"id":3,"sLen":0,"time":22584.2696629213,"type":0},{"id":0,"sLen":0,"time":22921.3483146067,"type":0},{"id":0,"sLen":0,"time":23258.4269662921,"type":0},{"id":3,"sLen":0,"time":23595.5056179775,"type":0},{"id":3,"sLen":0,"time":23932.5842696629,"type":0},{"id":0,"sLen":0,"time":24269.6629213483,"type":0},{"id":3,"sLen":0,"time":24606.7415730337,"type":0},{"id":0,"sLen":0,"time":24943.8202247191,"type":0},{"id":3,"sLen":0,"time":25280.8988764045,"type":0},{"id":0,"sLen":0,"time":25617.9775280899,"type":0},{"id":0,"sLen":0,"time":25955.0561797753,"type":0},{"id":3,"sLen":0,"time":26292.1348314607,"type":0},{"id":3,"sLen":0,"time":26629.2134831461,"type":0},{"id":3,"sLen":0,"time":26713.4831460674,"type":0},{"id":3,"sLen":0,"time":26797.7528089888,"type":0},{"id":3,"sLen":0,"time":26966.2921348315,"type":0},{"id":0,"sLen":0,"time":27303.3707865169,"type":0},{"id":0,"sLen":0,"time":27640.4494382022,"type":0},{"id":3,"sLen":0,"time":27977.5280898876,"type":0},{"id":3,"sLen":0,"time":28314.606741573,"type":0},{"id":0,"sLen":0,"time":28651.6853932584,"type":0},{"id":3,"sLen":0,"time":28988.7640449438,"type":0},{"id":0,"sLen":0,"time":29325.8426966292,"type":0},{"id":0,"sLen":0,"time":29662.9213483146,"type":0},{"id":0,"sLen":0,"time":29831.4606741573,"type":0},{"id":0,"sLen":0,"time":30000,"type":0},{"id":3,"sLen":0,"time":30168.5393258427,"type":0},{"id":3,"sLen":0,"time":30337.0786516854,"type":0},{"id":3,"sLen":0,"time":30505.6179775281,"type":0},{"id":3,"sLen":0,"time":30674.1573033708,"type":0},{"id":0,"sLen":0,"time":30842.6966292135,"type":0},{"id":0,"sLen":0,"time":31011.2359550562,"type":0},{"id":3,"sLen":0,"time":31348.3146067416,"type":0},{"id":3,"sLen":0,"time":31685.393258427,"type":0},{"id":0,"sLen":0,"time":31853.9325842697,"type":0},{"id":0,"sLen":0,"time":32359.5505617978,"type":0},{"id":3,"sLen":0,"time":32696.6292134831,"type":0},{"id":0,"sLen":0,"time":33033.7078651685,"type":0},{"id":0,"sLen":0,"time":33202.2471910112,"type":0},{"id":3,"sLen":0,"time":33370.7865168539,"type":0},{"id":0,"sLen":0,"time":33707.8651685393,"type":0},{"id":3,"sLen":0,"time":34044.9438202247,"type":0},{"id":3,"sLen":0,"time":34382.0224719101,"type":0},{"id":0,"sLen":0,"time":34550.5617977528,"type":0},{"id":3,"sLen":0,"time":34719.1011235955,"type":0},{"id":0,"sLen":0,"time":35056.1797752809,"type":0},{"id":3,"sLen":0,"time":35393.2584269663,"type":0},{"id":0,"sLen":0,"time":35730.3370786517,"type":0},{"id":0,"sLen":0,"time":35898.8764044944,"type":0},{"id":3,"sLen":0,"time":36067.4157303371,"type":0},{"id":0,"sLen":0,"time":36404.4943820225,"type":0},{"id":3,"sLen":0,"time":36741.5730337079,"type":0},{"id":3,"sLen":0,"time":37078.6516853933,"type":0},{"id":0,"sLen":0,"time":37247.191011236,"type":0},{"id":3,"sLen":0,"time":37415.7303370787,"type":0},{"id":0,"sLen":0,"time":37752.808988764,"type":0},{"id":3,"sLen":0,"time":38089.8876404494,"type":0},{"id":3,"sLen":0,"time":38426.9662921348,"type":0},{"id":0,"sLen":0,"time":38595.5056179775,"type":0},{"id":3,"sLen":0,"time":38764.0449438202,"type":0},{"id":0,"sLen":0,"time":39101.1235955056,"type":0},{"id":3,"sLen":0,"time":39438.202247191,"type":0},{"id":3,"sLen":0,"time":39775.2808988764,"type":0},{"id":0,"sLen":0,"time":39943.8202247191,"type":0},{"id":3,"sLen":0,"time":40112.3595505618,"type":0},{"id":0,"sLen":0,"time":40449.4382022472,"type":0},{"id":3,"sLen":0,"time":40786.5168539326,"type":0},{"id":3,"sLen":0,"time":41123.595505618,"type":0},{"id":0,"sLen":0,"time":41292.1348314607,"type":0},{"id":3,"sLen":0,"time":41460.6741573034,"type":0},{"id":0,"sLen":0,"time":41797.7528089888,"type":0},{"id":0,"sLen":0,"time":42134.8314606742,"type":0},{"id":0,"sLen":0,"time":42471.9101123596,"type":0},{"id":0,"sLen":0,"time":42640.4494382022,"type":0},{"id":3,"sLen":0,"time":42808.9887640449,"type":0},{"id":3,"sLen":0,"time":42893.2584269663,"type":0},{"id":3,"sLen":0,"time":42977.5280898876,"type":0},{"id":0,"sLen":0,"time":43146.0674157303,"type":0},{"id":3,"sLen":0,"time":43483.1460674157,"type":0},{"id":3,"sLen":0,"time":43820.2247191011,"type":0},{"id":0,"sLen":0,"time":43988.7640449438,"type":0},{"id":3,"sLen":0,"time":44157.3033707865,"type":0},{"id":0,"sLen":0,"time":44494.3820224719,"type":0},{"id":3,"sLen":0,"time":44831.4606741573,"type":0},{"id":0,"sLen":0,"time":45168.5393258427,"type":0},{"id":0,"sLen":0,"time":45337.0786516854,"type":0},{"id":3,"sLen":0,"time":45505.6179775281,"type":0},{"id":0,"sLen":0,"time":45842.6966292135,"type":0},{"id":3,"sLen":0,"time":46179.7752808989,"type":0},{"id":3,"sLen":0,"time":46516.8539325843,"type":0},{"id":0,"sLen":0,"time":46685.393258427,"type":0},{"id":3,"sLen":0,"time":46853.9325842697,"type":0},{"id":0,"sLen":0,"time":47191.0112359551,"type":0},{"id":3,"sLen":0,"time":47528.0898876404,"type":0},{"id":3,"sLen":0,"time":47865.1685393258,"type":0},{"id":0,"sLen":0,"time":48033.7078651685,"type":0},{"id":3,"sLen":0,"time":48202.2471910112,"type":0},{"id":0,"sLen":0,"time":48539.3258426966,"type":0},{"id":3,"sLen":0,"time":48876.404494382,"type":0},{"id":3,"sLen":0,"time":49213.4831460674,"type":0},{"id":0,"sLen":0,"time":49382.0224719101,"type":0},{"id":3,"sLen":0,"time":49550.5617977528,"type":0},{"id":0,"sLen":0,"time":49887.6404494382,"type":0},{"id":3,"sLen":0,"time":50224.7191011236,"type":0},{"id":0,"sLen":0,"time":50561.797752809,"type":0},{"id":0,"sLen":0,"time":50730.3370786517,"type":0},{"id":3,"sLen":0,"time":50898.8764044944,"type":0},{"id":0,"sLen":0,"time":51235.9550561798,"type":0},{"id":3,"sLen":0,"time":51573.0337078652,"type":0},{"id":3,"sLen":0,"time":51910.1123595506,"type":0},{"id":0,"sLen":0,"time":52078.6516853933,"type":0},{"id":3,"sLen":0,"time":52247.191011236,"type":0},{"id":0,"sLen":0,"time":52584.2696629213,"type":0},{"id":0,"sLen":0,"time":52921.3483146067,"type":0},{"id":0,"sLen":0,"time":53258.4269662921,"type":0},{"id":0,"sLen":0,"time":53426.9662921348,"type":0},{"id":3,"sLen":0,"time":53595.5056179775,"type":0},{"id":3,"sLen":0,"time":53679.7752808989,"type":0},{"id":3,"sLen":0,"time":53764.0449438202,"type":0},{"id":0,"sLen":0,"time":53932.5842696629,"type":0},{"id":3,"sLen":0,"time":54101.1235955056,"type":0},{"id":3,"sLen":0,"time":54185.393258427,"type":0},{"id":3,"sLen":0,"time":54353.9325842697,"type":0},{"id":0,"sLen":0,"time":54522.4719101124,"type":0},{"id":3,"sLen":0,"time":54691.0112359551,"type":0},{"id":3,"sLen":0,"time":54775.2808988764,"type":0},{"id":0,"sLen":0,"time":54943.8202247191,"type":0},{"id":0,"sLen":0,"time":55112.3595505618,"type":0},{"id":0,"sLen":0,"time":55196.6292134831,"type":0},{"id":3,"sLen":0,"time":55280.8988764045,"type":0},{"id":0,"sLen":0,"time":55449.4382022472,"type":0},{"id":0,"sLen":0,"time":55533.7078651685,"type":0},{"id":3,"sLen":0,"time":55786.5168539326,"type":0},{"id":0,"sLen":0,"time":55955.0561797753,"type":0},{"id":3,"sLen":0,"time":56039.3258426966,"type":0},{"id":3,"sLen":0,"time":56123.595505618,"type":0},{"id":0,"sLen":0,"time":56292.1348314607,"type":0},{"id":0,"sLen":0,"time":56629.2134831461,"type":0},{"id":3,"sLen":0,"time":56797.7528089888,"type":0},{"id":3,"sLen":0,"time":56882.0224719101,"type":0},{"id":3,"sLen":0,"time":57050.5617977528,"type":0},{"id":0,"sLen":0,"time":57219.1011235955,"type":0},{"id":3,"sLen":0,"time":57387.6404494382,"type":0},{"id":3,"sLen":0,"time":57471.9101123595,"type":0},{"id":0,"sLen":0,"time":57640.4494382022,"type":0},{"id":0,"sLen":0,"time":57808.9887640449,"type":0},{"id":0,"sLen":0,"time":57893.2584269663,"type":0},{"id":3,"sLen":0,"time":57977.5280898876,"type":0},{"id":0,"sLen":0,"time":58146.0674157303,"type":0},{"id":0,"sLen":0,"time":58230.3370786517,"type":0},{"id":3,"sLen":0,"time":58483.1460674157,"type":0},{"id":0,"sLen":0,"time":58651.6853932584,"type":0},{"id":3,"sLen":0,"time":58735.9550561798,"type":0},{"id":3,"sLen":0,"time":58820.2247191011,"type":0},{"id":0,"sLen":0,"time":58988.7640449438,"type":0},{"id":0,"sLen":0,"time":59073.0337078652,"type":0},{"id":0,"sLen":0,"time":59157.3033707865,"type":0},{"id":0,"sLen":0,"time":59325.8426966292,"type":0},{"id":3,"sLen":0,"time":59494.3820224719,"type":0},{"id":3,"sLen":0,"time":59578.6516853933,"type":0},{"id":3,"sLen":0,"time":59747.191011236,"type":0},{"id":0,"sLen":0,"time":59915.7303370786,"type":0},{"id":3,"sLen":0,"time":60084.2696629213,"type":0},{"id":3,"sLen":0,"time":60168.5393258427,"type":0},{"id":0,"sLen":0,"time":60337.0786516854,"type":0},{"id":0,"sLen":0,"time":60505.6179775281,"type":0},{"id":0,"sLen":0,"time":60589.8876404494,"type":0},{"id":3,"sLen":0,"time":60674.1573033708,"type":0},{"id":0,"sLen":0,"time":60842.6966292135,"type":0},{"id":0,"sLen":0,"time":60926.9662921348,"type":0},{"id":3,"sLen":0,"time":61179.7752808989,"type":0},{"id":0,"sLen":0,"time":61348.3146067416,"type":0},{"id":3,"sLen":0,"time":61432.5842696629,"type":0},{"id":3,"sLen":0,"time":61516.8539325843,"type":0},{"id":0,"sLen":0,"time":61685.393258427,"type":0},{"id":0,"sLen":0,"time":62022.4719101124,"type":0},{"id":3,"sLen":0,"time":62191.0112359551,"type":0},{"id":3,"sLen":0,"time":62275.2808988764,"type":0},{"id":3,"sLen":0,"time":62443.8202247191,"type":0},{"id":0,"sLen":0,"time":62612.3595505618,"type":0},{"id":3,"sLen":0,"time":62780.8988764045,"type":0},{"id":3,"sLen":0,"time":62865.1685393258,"type":0},{"id":0,"sLen":0,"time":63033.7078651685,"type":0},{"id":0,"sLen":0,"time":63202.2471910112,"type":0},{"id":0,"sLen":0,"time":63286.5168539326,"type":0},{"id":3,"sLen":0,"time":63370.7865168539,"type":0},{"id":0,"sLen":0,"time":63539.3258426966,"type":0},{"id":0,"sLen":0,"time":63623.595505618,"type":0},{"id":3,"sLen":0,"time":63876.404494382,"type":0},{"id":0,"sLen":0,"time":64044.9438202247,"type":0},{"id":3,"sLen":0,"time":64129.2134831461,"type":0},{"id":3,"sLen":0,"time":64213.4831460674,"type":0},{"id":0,"sLen":0,"time":64382.0224719101,"type":0},{"id":3,"sLen":0,"time":64719.1011235955,"type":0},{"id":0,"sLen":0,"time":65056.1797752809,"type":0},{"id":0,"sLen":0,"time":65393.2584269663,"type":0},{"id":0,"sLen":0,"time":65730.3370786517,"type":0},{"id":0,"sLen":0,"time":66067.4157303371,"type":0},{"id":3,"sLen":0,"time":66404.4943820225,"type":0},{"id":3,"sLen":0,"time":66741.5730337079,"type":0},{"id":3,"sLen":0,"time":67078.6516853933,"type":0},{"id":3,"sLen":0,"time":67415.7303370787,"type":0},{"id":0,"sLen":0,"time":67752.808988764,"type":0},{"id":0,"sLen":0,"time":68089.8876404494,"type":0},{"id":0,"sLen":0,"time":68426.9662921348,"type":0},{"id":0,"sLen":0,"time":68764.0449438202,"type":0},{"id":3,"sLen":0,"time":69101.1235955056,"type":0},{"id":3,"sLen":0,"time":69438.202247191,"type":0},{"id":3,"sLen":0,"time":69775.2808988764,"type":0},{"id":3,"sLen":0,"time":69859.5505617978,"type":0},{"id":3,"sLen":0,"time":69943.8202247191,"type":0},{"id":3,"sLen":0,"time":70112.3595505618,"type":0},{"id":0,"sLen":0,"time":70449.4382022472,"type":0},{"id":0,"sLen":0,"time":70786.5168539326,"type":0},{"id":0,"sLen":0,"time":71123.595505618,"type":0},{"id":0,"sLen":0,"time":71460.6741573034,"type":0},{"id":3,"sLen":0,"time":71797.7528089888,"type":0},{"id":3,"sLen":0,"time":72134.8314606741,"type":0},{"id":3,"sLen":0,"time":72471.9101123595,"type":0},{"id":3,"sLen":0,"time":72556.1797752809,"type":0},{"id":3,"sLen":0,"time":72640.4494382023,"type":0},{"id":3,"sLen":0,"time":72808.9887640449,"type":0},{"id":3,"sLen":0,"time":72893.2584269663,"type":0},{"id":3,"sLen":0,"time":72977.5280898877,"type":0},{"id":0,"sLen":0,"time":73146.0674157303,"type":0},{"id":0,"sLen":0,"time":73230.3370786517,"type":0},{"id":0,"sLen":0,"time":73314.6067415731,"type":0},{"id":0,"sLen":0,"time":73483.1460674157,"type":0},{"id":0,"sLen":0,"time":73567.4157303371,"type":0},{"id":0,"sLen":0,"time":73651.6853932585,"type":0},{"id":0,"sLen":0,"time":73820.2247191011,"type":0},{"id":0,"sLen":0,"time":73904.4943820225,"type":0},{"id":0,"sLen":0,"time":73988.7640449438,"type":0},{"id":0,"sLen":0,"time":74157.3033707866,"type":0},{"id":0,"sLen":0,"time":74241.5730337079,"type":0},{"id":0,"sLen":0,"time":74325.8426966292,"type":0},{"id":0,"sLen":0,"time":74410.1123595506,"type":0},{"id":3,"sLen":0,"time":74494.382022472,"type":0},{"id":3,"sLen":0,"time":74578.6516853933,"type":0},{"id":3,"sLen":0,"time":74662.9213483146,"type":0},{"id":3,"sLen":0,"time":74747.191011236,"type":0},{"id":0,"sLen":0,"time":74831.4606741573,"type":0},{"id":0,"sLen":0,"time":74915.7303370787,"type":0},{"id":0,"sLen":0,"time":75000,"type":0},{"id":0,"sLen":0,"time":75084.2696629214,"type":0},{"id":3,"sLen":0,"time":75168.5393258427,"type":0},{"id":0,"sLen":0,"time":75505.6179775281,"type":0},{"id":3,"sLen":0,"time":75842.6966292135,"type":0},{"id":0,"sLen":0,"time":76179.7752808989,"type":0},{"id":0,"sLen":0,"time":76516.8539325843,"type":0},{"id":3,"sLen":0,"time":76853.9325842697,"type":0},{"id":3,"sLen":0,"time":77191.0112359551,"type":0},{"id":0,"sLen":0,"time":77528.0898876405,"type":0},{"id":3,"sLen":0,"time":77865.1685393259,"type":0},{"id":3,"sLen":0,"time":78033.7078651686,"type":0},{"id":0,"sLen":0,"time":78202.2471910113,"type":0},{"id":0,"sLen":0,"time":78539.3258426967,"type":0},{"id":3,"sLen":0,"time":78876.4044943821,"type":0},{"id":0,"sLen":0,"time":79213.4831460675,"type":0},{"id":0,"sLen":0,"time":79550.5617977529,"type":0},{"id":3,"sLen":0,"time":79887.6404494383,"type":0},{"id":0,"sLen":0,"time":80140.4494382023,"type":0},{"id":0,"sLen":0,"time":80224.7191011237,"type":0},{"id":0,"sLen":0,"time":80308.988764045,"type":0},{"id":0,"sLen":0,"time":80477.5280898877,"type":0},{"id":3,"sLen":84.269662921346,"time":80730.3370786517,"type":0},{"id":0,"sLen":0,"time":80898.8764044945,"type":0},{"id":3,"sLen":0,"time":81067.4157303371,"type":0},{"id":0,"sLen":0,"time":81235.9550561799,"type":0},{"id":3,"sLen":0,"time":81404.4943820225,"type":0},{"id":0,"sLen":0,"time":81573.0337078652,"type":0},{"id":3,"sLen":0,"time":81741.5730337079,"type":0},{"id":0,"sLen":0,"time":81910.1123595506,"type":0},{"id":3,"sLen":0,"time":82078.6516853933,"type":0},{"id":3,"sLen":0,"time":82247.191011236,"type":0},{"id":0,"sLen":0,"time":82415.7303370787,"type":0},{"id":3,"sLen":0,"time":82584.2696629214,"type":0},{"id":0,"sLen":0,"time":82752.8089887641,"type":0},{"id":3,"sLen":0,"time":82921.3483146068,"type":0},{"id":0,"sLen":0,"time":83089.8876404495,"type":0},{"id":3,"sLen":0,"time":83258.4269662922,"type":0},{"id":3,"sLen":0,"time":83426.9662921349,"type":0},{"id":0,"sLen":0,"time":83595.5056179776,"type":0},{"id":0,"sLen":0,"time":83764.0449438203,"type":0},{"id":0,"sLen":0,"time":83932.584269663,"type":0},{"id":3,"sLen":0,"time":84269.6629213484,"type":0},{"id":3,"sLen":0,"time":84606.7415730338,"type":0},{"id":3,"sLen":0,"time":84775.2808988765,"type":0},{"id":3,"sLen":0,"time":84943.8202247192,"type":0},{"id":3,"sLen":0,"time":85280.8988764046,"type":0},{"id":0,"sLen":0,"time":85617.97752809,"type":0},{"id":0,"sLen":0,"time":85702.2471910113,"type":0},{"id":3,"sLen":0,"time":85870.786516854,"type":0},{"id":0,"sLen":0,"time":86039.3258426967,"type":0},{"id":0,"sLen":0,"time":86292.1348314607,"type":0},{"id":3,"sLen":0,"time":86629.2134831461,"type":0},{"id":0,"sLen":0,"time":87134.8314606741,"type":0},{"id":3,"sLen":0,"time":87303.3707865169,"type":0},{"id":0,"sLen":0,"time":87640.4494382023,"type":0},{"id":3,"sLen":0,"time":87977.5280898877,"type":0},{"id":0,"sLen":0,"time":88483.1460674157,"type":0},{"id":3,"sLen":0,"time":88651.6853932584,"type":0},{"id":0,"sLen":0,"time":88988.7640449438,"type":0},{"id":3,"sLen":0,"time":89325.8426966292,"type":0},{"id":0,"sLen":0,"time":89831.4606741573,"type":0},{"id":3,"sLen":0,"time":90000,"type":0},{"id":0,"sLen":0,"time":90337.0786516854,"type":0},{"id":3,"sLen":0,"time":90674.1573033708,"type":0},{"id":0,"sLen":0,"time":91179.7752808989,"type":0},{"id":3,"sLen":0,"time":91348.3146067416,"type":0},{"id":0,"sLen":0,"time":91685.393258427,"type":0},{"id":3,"sLen":0,"time":92022.4719101124,"type":0},{"id":0,"sLen":0,"time":92528.0898876405,"type":0},{"id":3,"sLen":0,"time":92696.6292134832,"type":0},{"id":0,"sLen":0,"time":93033.7078651686,"type":0},{"id":3,"sLen":0,"time":93370.786516854,"type":0},{"id":0,"sLen":0,"time":93876.404494382,"type":0},{"id":3,"sLen":0,"time":94044.9438202248,"type":0},{"id":0,"sLen":0,"time":94382.0224719102,"type":0},{"id":3,"sLen":0,"time":94719.1011235956,"type":0},{"id":0,"sLen":0,"time":95224.7191011236,"type":0},{"id":3,"sLen":0,"time":95393.2584269663,"type":0},{"id":0,"sLen":0,"time":95730.3370786517,"type":0},{"id":0,"sLen":0,"time":96067.4157303371,"type":0},{"id":0,"sLen":0,"time":96404.4943820225,"type":0},{"id":0,"sLen":0,"time":96573.0337078652,"type":0},{"id":3,"sLen":0,"time":96741.5730337079,"type":0},{"id":3,"sLen":0,"time":96825.8426966292,"type":0},{"id":3,"sLen":0,"time":96910.1123595505,"type":0},{"id":0,"sLen":0,"time":97078.6516853933,"type":0},{"id":3,"sLen":0,"time":97415.7303370787,"type":0},{"id":3,"sLen":0,"time":97752.808988764,"type":0},{"id":0,"sLen":0,"time":97921.3483146067,"type":0},{"id":3,"sLen":0,"time":98089.8876404494,"type":0},{"id":0,"sLen":0,"time":98426.9662921348,"type":0},{"id":3,"sLen":0,"time":98764.0449438202,"type":0},{"id":0,"sLen":0,"time":99101.1235955056,"type":0},{"id":0,"sLen":0,"time":99269.6629213483,"type":0},{"id":3,"sLen":0,"time":99438.202247191,"type":0},{"id":0,"sLen":0,"time":99775.2808988764,"type":0},{"id":3,"sLen":0,"time":100112.359550562,"type":0},{"id":3,"sLen":0,"time":100449.438202247,"type":0},{"id":0,"sLen":0,"time":100617.97752809,"type":0},{"id":3,"sLen":0,"time":100786.516853933,"type":0},{"id":0,"sLen":0,"time":101123.595505618,"type":0},{"id":3,"sLen":0,"time":101460.674157303,"type":0},{"id":3,"sLen":0,"time":101797.752808989,"type":0},{"id":0,"sLen":0,"time":101966.292134831,"type":0},{"id":3,"sLen":0,"time":102134.831460674,"type":0},{"id":0,"sLen":0,"time":102471.91011236,"type":0},{"id":3,"sLen":0,"time":102808.988764045,"type":0},{"id":3,"sLen":0,"time":103146.06741573,"type":0},{"id":0,"sLen":0,"time":103314.606741573,"type":0},{"id":3,"sLen":0,"time":103483.146067416,"type":0},{"id":0,"sLen":0,"time":103820.224719101,"type":0},{"id":3,"sLen":0,"time":104157.303370787,"type":0},{"id":0,"sLen":0,"time":104494.382022472,"type":0},{"id":0,"sLen":0,"time":104662.921348315,"type":0},{"id":3,"sLen":0,"time":104831.460674157,"type":0},{"id":0,"sLen":0,"time":105168.539325843,"type":0},{"id":3,"sLen":0,"time":105505.617977528,"type":0},{"id":3,"sLen":0,"time":105842.696629213,"type":0},{"id":0,"sLen":0,"time":106011.235955056,"type":0},{"id":3,"sLen":0,"time":106179.775280899,"type":0},{"id":0,"sLen":0,"time":106516.853932584,"type":0},{"id":0,"sLen":0,"time":106853.93258427,"type":0},{"id":0,"sLen":0,"time":107191.011235955,"type":0},{"id":3,"sLen":0,"time":107528.08988764,"type":0},{"id":0,"sLen":0,"time":107865.168539326,"type":0},{"id":0,"sLen":0,"time":108202.247191011,"type":0},{"id":0,"sLen":0,"time":108539.325842697,"type":0},{"id":0,"sLen":0,"time":108876.404494382,"type":0},{"id":3,"sLen":0,"time":109213.483146068,"type":0},{"id":3,"sLen":0,"time":109550.561797753,"type":0},{"id":3,"sLen":0,"time":109887.640449438,"type":0},{"id":3,"sLen":0,"time":110224.719101124,"type":0},{"id":0,"sLen":0,"time":110561.797752809,"type":0},{"id":0,"sLen":0,"time":110730.337078652,"type":0},{"id":0,"sLen":0,"time":110898.876404495,"type":0},{"id":3,"sLen":0,"time":111235.95505618,"type":0},{"id":3,"sLen":0,"time":111573.033707865,"type":0},{"id":3,"sLen":0,"time":111910.112359551,"type":0},{"id":3,"sLen":0,"time":112247.191011236,"type":0},{"id":0,"sLen":0,"time":112584.269662922,"type":0},{"id":3,"sLen":0,"time":112921.348314607,"type":0},{"id":0,"sLen":0,"time":113258.426966292,"type":0},{"id":3,"sLen":0,"time":113932.584269663,"type":0},{"id":0,"sLen":0,"time":114269.662921349,"type":0},{"id":3,"sLen":0,"time":114438.202247191,"type":0},{"id":0,"sLen":0,"time":114606.741573034,"type":0},{"id":3,"sLen":0,"time":114943.820224719,"type":0},{"id":0,"sLen":0,"time":115280.898876405,"type":0},{"id":3,"sLen":0,"time":115617.97752809,"type":0},{"id":3,"sLen":0,"time":115786.516853933,"type":0},{"id":0,"sLen":0,"time":115955.056179776,"type":0},{"id":3,"sLen":0,"time":116123.595505618,"type":0},{"id":0,"sLen":0,"time":116292.134831461,"type":0},{"id":0,"sLen":0,"time":116629.213483146,"type":0},{"id":3,"sLen":0,"time":116966.292134832,"type":0},{"id":3,"sLen":0,"time":117303.370786517,"type":0},{"id":0,"sLen":0,"time":117640.449438203,"type":0},{"id":3,"sLen":0,"time":117977.528089888,"type":0},{"id":3,"sLen":0,"time":118061.797752809,"type":0},{"id":0,"sLen":0,"time":118314.606741573,"type":0},{"id":3,"sLen":0,"time":118398.876404495,"type":0},{"id":0,"sLen":0,"time":118651.685393258,"type":0},{"id":3,"sLen":0,"time":118820.224719101,"type":0},{"id":3,"sLen":0,"time":118904.494382022,"type":0},{"id":3,"sLen":0,"time":119073.033707865,"type":0},{"id":0,"sLen":0,"time":119241.573033708,"type":0},{"id":3,"sLen":0,"time":119410.112359551,"type":0},{"id":3,"sLen":0,"time":119494.382022472,"type":0},{"id":0,"sLen":0,"time":119662.921348315,"type":0},{"id":0,"sLen":0,"time":119831.460674157,"type":0},{"id":0,"sLen":0,"time":119915.730337079,"type":0},{"id":3,"sLen":0,"time":120000,"type":0},{"id":0,"sLen":0,"time":120168.539325843,"type":0},{"id":0,"sLen":0,"time":120252.808988764,"type":0},{"id":3,"sLen":0,"time":120505.617977528,"type":0},{"id":0,"sLen":0,"time":120674.157303371,"type":0},{"id":3,"sLen":0,"time":120758.426966292,"type":0},{"id":3,"sLen":0,"time":120842.696629213,"type":0},{"id":0,"sLen":0,"time":121011.235955056,"type":0},{"id":0,"sLen":0,"time":121095.505617978,"type":0},{"id":0,"sLen":0,"time":121179.775280899,"type":0}],"position":"girlfriend","type":2,"characters":["pico-speakers"],"visible":false}],"codenameChart":true,"stage":"tank","noteTypes":["No Anim Note"]} \ No newline at end of file diff --git a/assets/songs/stress/events.json b/assets/songs/stress/events.json new file mode 100644 index 00000000..1561a533 --- /dev/null +++ b/assets/songs/stress/events.json @@ -0,0 +1 @@ +{"events":[{"params":[0,"good!",true],"time":62022.4719101124,"name":"Play Animation"}]} \ No newline at end of file diff --git a/assets/songs/thorns/charts/easy.json b/assets/songs/thorns/charts/easy.json index b4e7b644..61cc8c95 100644 --- a/assets/songs/thorns/charts/easy.json +++ b/assets/songs/thorns/charts/easy.json @@ -1 +1 @@ -{"codenameChart":true,"strumLines":[{"position":"dad","notes":[{"id":3,"sLen":157.89473,"time":0,"type":0},{"id":1,"sLen":157.89473,"time":631.5789,"type":0},{"id":0,"sLen":0,"time":947.3684,"type":0},{"id":3,"sLen":0,"time":1263.15784,"type":0},{"id":3,"sLen":0,"time":1736.842,"type":0},{"id":3,"sLen":0,"time":2210.52612,"type":0},{"id":3,"sLen":0,"time":2526.31567,"type":0},{"id":3,"sLen":0,"time":2842.10522,"type":0},{"id":1,"sLen":0,"time":3157.89453,"type":0},{"id":0,"sLen":0,"time":3473.684,"type":0},{"id":3,"sLen":0,"time":3789.47363,"type":0},{"id":3,"sLen":0,"time":4263.15771,"type":0},{"id":3,"sLen":0,"time":4736.842,"type":0},{"id":2,"sLen":157.894729999998,"time":10105.2627,"type":0},{"id":2,"sLen":0,"time":10736.8418,"type":0},{"id":3,"sLen":0,"time":10894.7363,"type":0},{"id":2,"sLen":157.89473,"time":11368.4209,"type":0},{"id":2,"sLen":157.89473,"time":11842.1045,"type":0},{"id":2,"sLen":157.89473,"time":12315.7891,"type":0},{"id":2,"sLen":157.89473,"time":12631.5781,"type":0},{"id":2,"sLen":0,"time":13263.1572,"type":0},{"id":3,"sLen":0,"time":13421.0518,"type":0},{"id":2,"sLen":157.89473,"time":13894.7363,"type":0},{"id":2,"sLen":157.89473,"time":14368.42,"type":0},{"id":0,"sLen":157.89473,"time":14842.1045,"type":0},{"id":1,"sLen":0,"time":20210.5254,"type":0},{"id":3,"sLen":0,"time":20526.3145,"type":0},{"id":3,"sLen":0,"time":21000,"type":0},{"id":2,"sLen":0,"time":21473.6836,"type":0},{"id":2,"sLen":0,"time":21947.3672,"type":0},{"id":3,"sLen":0,"time":22421.05,"type":0},{"id":1,"sLen":0,"time":22736.8418,"type":0},{"id":3,"sLen":0,"time":23052.63,"type":0},{"id":3,"sLen":0,"time":23526.3145,"type":0},{"id":2,"sLen":0,"time":23999.998,"type":0},{"id":2,"sLen":0,"time":24473.6836,"type":0},{"id":3,"sLen":0,"time":24947.3672,"type":0},{"id":2,"sLen":0,"time":30315.79,"type":0},{"id":3,"sLen":0,"time":30631.5781,"type":0},{"id":3,"sLen":0,"time":30947.3672,"type":0},{"id":0,"sLen":0,"time":31263.1563,"type":0},{"id":0,"sLen":0,"time":31578.9453,"type":0},{"id":0,"sLen":0,"time":31894.7363,"type":0},{"id":3,"sLen":0,"time":32210.5254,"type":0},{"id":1,"sLen":0,"time":32526.3145,"type":0},{"id":0,"sLen":0,"time":32842.1055,"type":0},{"id":0,"sLen":0,"time":33157.8945,"type":0},{"id":0,"sLen":0,"time":33473.6836,"type":0},{"id":3,"sLen":0,"time":33789.4727,"type":0},{"id":1,"sLen":0,"time":34105.26,"type":0},{"id":1,"sLen":0,"time":34421.05,"type":0},{"id":1,"sLen":0,"time":34736.84,"type":0},{"id":1,"sLen":236.842100000002,"time":35052.63,"type":0},{"id":0,"sLen":0,"time":40421.05,"type":0},{"id":0,"sLen":0,"time":40736.84,"type":0},{"id":3,"sLen":0,"time":41052.63,"type":0},{"id":0,"sLen":0,"time":41368.418,"type":0},{"id":2,"sLen":0,"time":41684.207,"type":0},{"id":2,"sLen":0,"time":42315.79,"type":0},{"id":0,"sLen":0,"time":42631.58,"type":0},{"id":0,"sLen":0,"time":42947.3672,"type":0},{"id":0,"sLen":0,"time":43263.1563,"type":0},{"id":3,"sLen":0,"time":43578.9453,"type":0},{"id":0,"sLen":0,"time":43894.7344,"type":0},{"id":2,"sLen":0,"time":44210.5234,"type":0},{"id":2,"sLen":0,"time":44842.1,"type":0},{"id":3,"sLen":0,"time":45157.8945,"type":0},{"id":3,"sLen":157.89473,"time":50526.3125,"type":0},{"id":1,"sLen":157.89473,"time":51157.89,"type":0},{"id":0,"sLen":0,"time":51473.6836,"type":0},{"id":3,"sLen":0,"time":51789.4727,"type":0},{"id":3,"sLen":0,"time":52263.1563,"type":0},{"id":3,"sLen":0,"time":52736.84,"type":0},{"id":3,"sLen":0,"time":53052.63,"type":0},{"id":3,"sLen":0,"time":53368.418,"type":0},{"id":1,"sLen":0,"time":53684.207,"type":0},{"id":0,"sLen":0,"time":53999.9961,"type":0},{"id":3,"sLen":0,"time":54315.7852,"type":0},{"id":3,"sLen":0,"time":54789.4727,"type":0},{"id":3,"sLen":0,"time":55263.1563,"type":0},{"id":2,"sLen":0,"time":60631.58,"type":0},{"id":3,"sLen":0,"time":60947.3672,"type":0},{"id":3,"sLen":0,"time":61263.1563,"type":0},{"id":0,"sLen":0,"time":61578.9453,"type":0},{"id":0,"sLen":0,"time":61894.7344,"type":0},{"id":0,"sLen":0,"time":62210.5234,"type":0},{"id":3,"sLen":0,"time":62526.3125,"type":0},{"id":1,"sLen":0,"time":62842.1,"type":0},{"id":0,"sLen":0,"time":63157.89,"type":0},{"id":0,"sLen":0,"time":63473.68,"type":0},{"id":0,"sLen":0,"time":63789.4727,"type":0},{"id":3,"sLen":0,"time":64105.26,"type":0},{"id":1,"sLen":0,"time":64421.05,"type":0},{"id":1,"sLen":0,"time":64736.84,"type":0},{"id":1,"sLen":0,"time":65052.63,"type":0},{"id":1,"sLen":236.842100000002,"time":65368.418,"type":0},{"id":0,"sLen":0,"time":70736.8359,"type":0},{"id":0,"sLen":0,"time":71052.625,"type":0},{"id":3,"sLen":0,"time":71368.4141,"type":0},{"id":0,"sLen":0,"time":71684.21,"type":0},{"id":2,"sLen":0,"time":72000,"type":0},{"id":2,"sLen":0,"time":72631.58,"type":0},{"id":0,"sLen":0,"time":72947.37,"type":0},{"id":0,"sLen":0,"time":73263.16,"type":0},{"id":0,"sLen":0,"time":73578.9453,"type":0},{"id":3,"sLen":0,"time":73894.7344,"type":0},{"id":0,"sLen":0,"time":74210.52,"type":0},{"id":2,"sLen":0,"time":74526.31,"type":0},{"id":2,"sLen":0,"time":75157.89,"type":0},{"id":3,"sLen":0,"time":75473.68,"type":0},{"id":2,"sLen":0,"time":80842.1,"type":0},{"id":0,"sLen":0,"time":81473.68,"type":0},{"id":3,"sLen":0,"time":81631.58,"type":0},{"id":1,"sLen":0,"time":81789.47,"type":0},{"id":2,"sLen":0,"time":82105.26,"type":0},{"id":0,"sLen":0,"time":82736.8359,"type":0},{"id":3,"sLen":0,"time":82894.7344,"type":0},{"id":1,"sLen":0,"time":83052.625,"type":0},{"id":2,"sLen":0,"time":83368.4141,"type":0},{"id":0,"sLen":0,"time":84000,"type":0},{"id":3,"sLen":0,"time":84157.89,"type":0},{"id":1,"sLen":0,"time":84315.79,"type":0},{"id":2,"sLen":0,"time":84631.58,"type":0},{"id":0,"sLen":0,"time":85263.16,"type":0},{"id":3,"sLen":0,"time":85421.05,"type":0},{"id":1,"sLen":0,"time":85578.9453,"type":0},{"id":3,"sLen":157.89473,"time":90947.37,"type":0},{"id":1,"sLen":157.89473,"time":91578.9453,"type":0},{"id":0,"sLen":0,"time":91894.7344,"type":0},{"id":3,"sLen":0,"time":92210.52,"type":0},{"id":3,"sLen":0,"time":92684.2,"type":0},{"id":3,"sLen":0,"time":93157.89,"type":0},{"id":3,"sLen":0,"time":93473.68,"type":0},{"id":3,"sLen":0,"time":93789.47,"type":0},{"id":1,"sLen":0,"time":94105.26,"type":0},{"id":0,"sLen":0,"time":94421.05,"type":0},{"id":3,"sLen":0,"time":94736.8359,"type":0},{"id":3,"sLen":0,"time":95210.52,"type":0},{"id":3,"sLen":0,"time":95684.2,"type":0}],"characters":["spirit"],"visible":true,"type":0},{"position":"boyfriend","notes":[{"id":3,"sLen":157.89473,"time":5052.63135,"type":0},{"id":1,"sLen":157.89473,"time":5684.21045,"type":0},{"id":0,"sLen":0,"time":5999.99951,"type":0},{"id":3,"sLen":0,"time":6315.789,"type":0},{"id":3,"sLen":0,"time":6789.473,"type":0},{"id":3,"sLen":0,"time":7263.15771,"type":0},{"id":3,"sLen":0,"time":7578.94727,"type":0},{"id":3,"sLen":0,"time":7894.73633,"type":0},{"id":1,"sLen":0,"time":8210.526,"type":0},{"id":0,"sLen":0,"time":8526.315,"type":0},{"id":3,"sLen":0,"time":8842.10449,"type":0},{"id":3,"sLen":0,"time":9315.789,"type":0},{"id":3,"sLen":0,"time":9789.474,"type":0},{"id":2,"sLen":157.89473,"time":15157.8945,"type":0},{"id":2,"sLen":0,"time":15789.4727,"type":0},{"id":3,"sLen":0,"time":15947.3682,"type":0},{"id":2,"sLen":157.89473,"time":16421.0527,"type":0},{"id":2,"sLen":157.89473,"time":16894.7363,"type":0},{"id":2,"sLen":157.89473,"time":17368.42,"type":0},{"id":2,"sLen":157.89473,"time":17684.209,"type":0},{"id":2,"sLen":0,"time":18315.79,"type":0},{"id":3,"sLen":0,"time":18473.6836,"type":0},{"id":2,"sLen":157.89473,"time":18947.3672,"type":0},{"id":2,"sLen":157.89473,"time":19421.05,"type":0},{"id":0,"sLen":157.89473,"time":19894.7363,"type":0},{"id":0,"sLen":0,"time":24789.4727,"type":0},{"id":1,"sLen":0,"time":25263.1563,"type":0},{"id":3,"sLen":0,"time":25578.9453,"type":0},{"id":3,"sLen":0,"time":26052.63,"type":0},{"id":2,"sLen":0,"time":26526.3145,"type":0},{"id":2,"sLen":0,"time":26999.998,"type":0},{"id":3,"sLen":0,"time":27473.6836,"type":0},{"id":1,"sLen":0,"time":27789.4727,"type":0},{"id":3,"sLen":0,"time":28105.2617,"type":0},{"id":3,"sLen":0,"time":28578.9453,"type":0},{"id":2,"sLen":0,"time":29052.63,"type":0},{"id":2,"sLen":0,"time":29526.3145,"type":0},{"id":3,"sLen":0,"time":29999.998,"type":0},{"id":2,"sLen":0,"time":35368.418,"type":0},{"id":3,"sLen":0,"time":35684.207,"type":0},{"id":3,"sLen":0,"time":36000,"type":0},{"id":0,"sLen":0,"time":36315.79,"type":0},{"id":0,"sLen":0,"time":36631.58,"type":0},{"id":0,"sLen":0,"time":36947.3672,"type":0},{"id":3,"sLen":0,"time":37263.1563,"type":0},{"id":1,"sLen":0,"time":37578.9453,"type":0},{"id":0,"sLen":0,"time":37894.7344,"type":0},{"id":0,"sLen":0,"time":38210.5234,"type":0},{"id":0,"sLen":0,"time":38526.3125,"type":0},{"id":3,"sLen":0,"time":38842.1,"type":0},{"id":1,"sLen":0,"time":39157.8945,"type":0},{"id":1,"sLen":0,"time":39473.6836,"type":0},{"id":1,"sLen":0,"time":39789.4727,"type":0},{"id":1,"sLen":236.842100000002,"time":40105.26,"type":0},{"id":0,"sLen":0,"time":45473.6836,"type":0},{"id":0,"sLen":0,"time":45789.4727,"type":0},{"id":3,"sLen":0,"time":46105.26,"type":0},{"id":0,"sLen":0,"time":46421.05,"type":0},{"id":2,"sLen":0,"time":46736.84,"type":0},{"id":2,"sLen":0,"time":47368.418,"type":0},{"id":0,"sLen":0,"time":47684.207,"type":0},{"id":0,"sLen":0,"time":47999.9961,"type":0},{"id":0,"sLen":0,"time":48315.79,"type":0},{"id":3,"sLen":0,"time":48631.58,"type":0},{"id":0,"sLen":0,"time":48947.3672,"type":0},{"id":2,"sLen":0,"time":49263.1563,"type":0},{"id":2,"sLen":0,"time":49894.7344,"type":0},{"id":3,"sLen":0,"time":50210.5234,"type":0},{"id":3,"sLen":157.89473,"time":55578.9453,"type":0},{"id":1,"sLen":157.89473,"time":56210.5234,"type":0},{"id":0,"sLen":0,"time":56526.3125,"type":0},{"id":3,"sLen":0,"time":56842.1,"type":0},{"id":3,"sLen":0,"time":57315.7852,"type":0},{"id":3,"sLen":0,"time":57789.4727,"type":0},{"id":3,"sLen":0,"time":58105.26,"type":0},{"id":3,"sLen":0,"time":58421.05,"type":0},{"id":1,"sLen":0,"time":58736.84,"type":0},{"id":0,"sLen":0,"time":59052.63,"type":0},{"id":3,"sLen":0,"time":59368.418,"type":0},{"id":3,"sLen":0,"time":59842.1,"type":0},{"id":3,"sLen":0,"time":60315.7852,"type":0},{"id":2,"sLen":0,"time":65684.21,"type":0},{"id":3,"sLen":0,"time":66000,"type":0},{"id":3,"sLen":0,"time":66315.79,"type":0},{"id":0,"sLen":0,"time":66631.58,"type":0},{"id":0,"sLen":0,"time":66947.37,"type":0},{"id":0,"sLen":0,"time":67263.16,"type":0},{"id":3,"sLen":0,"time":67578.9453,"type":0},{"id":1,"sLen":0,"time":67894.7344,"type":0},{"id":0,"sLen":0,"time":68210.52,"type":0},{"id":0,"sLen":0,"time":68526.31,"type":0},{"id":3,"sLen":0,"time":68684.21,"type":0},{"id":0,"sLen":0,"time":68842.1,"type":0},{"id":3,"sLen":0,"time":69157.89,"type":0},{"id":1,"sLen":0,"time":69315.79,"type":0},{"id":1,"sLen":0,"time":69473.68,"type":0},{"id":1,"sLen":0,"time":69789.47,"type":0},{"id":1,"sLen":0,"time":70105.26,"type":0},{"id":1,"sLen":236.842099999994,"time":70421.05,"type":0},{"id":0,"sLen":0,"time":75789.47,"type":0},{"id":0,"sLen":0,"time":76105.26,"type":0},{"id":3,"sLen":0,"time":76421.05,"type":0},{"id":0,"sLen":0,"time":76736.8359,"type":0},{"id":2,"sLen":0,"time":77052.625,"type":0},{"id":2,"sLen":0,"time":77684.2,"type":0},{"id":0,"sLen":0,"time":78000,"type":0},{"id":0,"sLen":0,"time":78315.79,"type":0},{"id":0,"sLen":0,"time":78631.58,"type":0},{"id":3,"sLen":0,"time":78947.37,"type":0},{"id":0,"sLen":0,"time":79263.16,"type":0},{"id":2,"sLen":0,"time":79578.9453,"type":0},{"id":2,"sLen":0,"time":80210.52,"type":0},{"id":3,"sLen":0,"time":80526.31,"type":0},{"id":2,"sLen":0,"time":85894.7344,"type":0},{"id":0,"sLen":0,"time":86526.31,"type":0},{"id":3,"sLen":0,"time":86684.2,"type":0},{"id":1,"sLen":0,"time":86842.1,"type":0},{"id":2,"sLen":0,"time":87157.89,"type":0},{"id":0,"sLen":0,"time":87789.47,"type":0},{"id":3,"sLen":0,"time":87947.37,"type":0},{"id":1,"sLen":0,"time":88105.26,"type":0},{"id":2,"sLen":0,"time":88421.05,"type":0},{"id":0,"sLen":0,"time":89052.625,"type":0},{"id":3,"sLen":0,"time":89210.52,"type":0},{"id":1,"sLen":0,"time":89368.4141,"type":0},{"id":2,"sLen":0,"time":89684.2,"type":0},{"id":0,"sLen":0,"time":90315.79,"type":0},{"id":3,"sLen":0,"time":90473.68,"type":0},{"id":1,"sLen":0,"time":90631.58,"type":0},{"id":3,"sLen":157.89473,"time":95999.99,"type":0},{"id":1,"sLen":157.89473,"time":96631.58,"type":0},{"id":0,"sLen":0,"time":96947.37,"type":0},{"id":3,"sLen":0,"time":97263.16,"type":0},{"id":3,"sLen":0,"time":97736.8359,"type":0},{"id":3,"sLen":0,"time":98210.52,"type":0},{"id":3,"sLen":0,"time":98526.31,"type":0},{"id":3,"sLen":0,"time":98842.1,"type":0},{"id":1,"sLen":0,"time":99157.89,"type":0},{"id":0,"sLen":0,"time":99473.68,"type":0},{"id":3,"sLen":0,"time":99789.47,"type":0},{"id":3,"sLen":0,"time":100263.156,"type":0},{"id":3,"sLen":0,"time":100736.836,"type":0}],"characters":["bf-pixel"],"visible":true,"type":1},{"position":"girlfriend","notes":[],"characters":["gf-pixel"],"visible":false,"type":2}],"stage":"school-evil","scrollSpeed":1.1,"noteTypes":[]} \ No newline at end of file +{"events":[{"params":[1],"time":5052.63157894737,"name":"Camera Movement"},{"params":[0],"time":10105.2631578947,"name":"Camera Movement"},{"params":[1],"time":15157.8947368421,"name":"Camera Movement"},{"params":[0],"time":20210.5263157895,"name":"Camera Movement"},{"params":[1],"time":25263.1578947368,"name":"Camera Movement"},{"params":[0],"time":30315.7894736842,"name":"Camera Movement"},{"params":[1],"time":35368.4210526316,"name":"Camera Movement"},{"params":[0],"time":40421.0526315789,"name":"Camera Movement"},{"params":[1],"time":45473.6842105263,"name":"Camera Movement"},{"params":[0],"time":50526.3157894737,"name":"Camera Movement"},{"params":[1],"time":55578.9473684211,"name":"Camera Movement"},{"params":[0],"time":60631.5789473684,"name":"Camera Movement"},{"params":[1],"time":65684.2105263158,"name":"Camera Movement"},{"params":[0],"time":70736.8421052632,"name":"Camera Movement"},{"params":[1],"time":75789.4736842105,"name":"Camera Movement"},{"params":[0],"time":80842.1052631579,"name":"Camera Movement"},{"params":[1],"time":85894.7368421053,"name":"Camera Movement"},{"params":[0],"time":90947.3684210526,"name":"Camera Movement"},{"params":[1],"time":96000,"name":"Camera Movement"}],"codenameChart":true,"strumLines":[{"position":"dad","notes":[{"id":3,"sLen":157.89473,"time":0,"type":0},{"id":1,"sLen":157.89473,"time":631.5789,"type":0},{"id":0,"sLen":0,"time":947.3684,"type":0},{"id":3,"sLen":0,"time":1263.15784,"type":0},{"id":3,"sLen":0,"time":1736.842,"type":0},{"id":3,"sLen":0,"time":2210.52612,"type":0},{"id":3,"sLen":0,"time":2526.31567,"type":0},{"id":3,"sLen":0,"time":2842.10522,"type":0},{"id":1,"sLen":0,"time":3157.89453,"type":0},{"id":0,"sLen":0,"time":3473.684,"type":0},{"id":3,"sLen":0,"time":3789.47363,"type":0},{"id":3,"sLen":0,"time":4263.15771,"type":0},{"id":3,"sLen":0,"time":4736.842,"type":0},{"id":2,"sLen":157.894729999998,"time":10105.2627,"type":0},{"id":2,"sLen":0,"time":10736.8418,"type":0},{"id":3,"sLen":0,"time":10894.7363,"type":0},{"id":2,"sLen":157.89473,"time":11368.4209,"type":0},{"id":2,"sLen":157.89473,"time":11842.1045,"type":0},{"id":2,"sLen":157.89473,"time":12315.7891,"type":0},{"id":2,"sLen":157.89473,"time":12631.5781,"type":0},{"id":2,"sLen":0,"time":13263.1572,"type":0},{"id":3,"sLen":0,"time":13421.0518,"type":0},{"id":2,"sLen":157.89473,"time":13894.7363,"type":0},{"id":2,"sLen":157.89473,"time":14368.42,"type":0},{"id":0,"sLen":157.89473,"time":14842.1045,"type":0},{"id":1,"sLen":0,"time":20210.5254,"type":0},{"id":3,"sLen":0,"time":20526.3145,"type":0},{"id":3,"sLen":0,"time":21000,"type":0},{"id":2,"sLen":0,"time":21473.6836,"type":0},{"id":2,"sLen":0,"time":21947.3672,"type":0},{"id":3,"sLen":0,"time":22421.05,"type":0},{"id":1,"sLen":0,"time":22736.8418,"type":0},{"id":3,"sLen":0,"time":23052.63,"type":0},{"id":3,"sLen":0,"time":23526.3145,"type":0},{"id":2,"sLen":0,"time":23999.998,"type":0},{"id":2,"sLen":0,"time":24473.6836,"type":0},{"id":3,"sLen":0,"time":24947.3672,"type":0},{"id":2,"sLen":0,"time":30315.79,"type":0},{"id":3,"sLen":0,"time":30631.5781,"type":0},{"id":3,"sLen":0,"time":30947.3672,"type":0},{"id":0,"sLen":0,"time":31263.1563,"type":0},{"id":0,"sLen":0,"time":31578.9453,"type":0},{"id":0,"sLen":0,"time":31894.7363,"type":0},{"id":3,"sLen":0,"time":32210.5254,"type":0},{"id":1,"sLen":0,"time":32526.3145,"type":0},{"id":0,"sLen":0,"time":32842.1055,"type":0},{"id":0,"sLen":0,"time":33157.8945,"type":0},{"id":0,"sLen":0,"time":33473.6836,"type":0},{"id":3,"sLen":0,"time":33789.4727,"type":0},{"id":1,"sLen":0,"time":34105.26,"type":0},{"id":1,"sLen":0,"time":34421.05,"type":0},{"id":1,"sLen":0,"time":34736.84,"type":0},{"id":1,"sLen":236.842100000002,"time":35052.63,"type":0},{"id":0,"sLen":0,"time":40421.05,"type":0},{"id":0,"sLen":0,"time":40736.84,"type":0},{"id":3,"sLen":0,"time":41052.63,"type":0},{"id":0,"sLen":0,"time":41368.418,"type":0},{"id":2,"sLen":0,"time":41684.207,"type":0},{"id":2,"sLen":0,"time":42315.79,"type":0},{"id":0,"sLen":0,"time":42631.58,"type":0},{"id":0,"sLen":0,"time":42947.3672,"type":0},{"id":0,"sLen":0,"time":43263.1563,"type":0},{"id":3,"sLen":0,"time":43578.9453,"type":0},{"id":0,"sLen":0,"time":43894.7344,"type":0},{"id":2,"sLen":0,"time":44210.5234,"type":0},{"id":2,"sLen":0,"time":44842.1,"type":0},{"id":3,"sLen":0,"time":45157.8945,"type":0},{"id":3,"sLen":157.89473,"time":50526.3125,"type":0},{"id":1,"sLen":157.89473,"time":51157.89,"type":0},{"id":0,"sLen":0,"time":51473.6836,"type":0},{"id":3,"sLen":0,"time":51789.4727,"type":0},{"id":3,"sLen":0,"time":52263.1563,"type":0},{"id":3,"sLen":0,"time":52736.84,"type":0},{"id":3,"sLen":0,"time":53052.63,"type":0},{"id":3,"sLen":0,"time":53368.418,"type":0},{"id":1,"sLen":0,"time":53684.207,"type":0},{"id":0,"sLen":0,"time":53999.9961,"type":0},{"id":3,"sLen":0,"time":54315.7852,"type":0},{"id":3,"sLen":0,"time":54789.4727,"type":0},{"id":3,"sLen":0,"time":55263.1563,"type":0},{"id":2,"sLen":0,"time":60631.58,"type":0},{"id":3,"sLen":0,"time":60947.3672,"type":0},{"id":3,"sLen":0,"time":61263.1563,"type":0},{"id":0,"sLen":0,"time":61578.9453,"type":0},{"id":0,"sLen":0,"time":61894.7344,"type":0},{"id":0,"sLen":0,"time":62210.5234,"type":0},{"id":3,"sLen":0,"time":62526.3125,"type":0},{"id":1,"sLen":0,"time":62842.1,"type":0},{"id":0,"sLen":0,"time":63157.89,"type":0},{"id":0,"sLen":0,"time":63473.68,"type":0},{"id":0,"sLen":0,"time":63789.4727,"type":0},{"id":3,"sLen":0,"time":64105.26,"type":0},{"id":1,"sLen":0,"time":64421.05,"type":0},{"id":1,"sLen":0,"time":64736.84,"type":0},{"id":1,"sLen":0,"time":65052.63,"type":0},{"id":1,"sLen":236.842100000002,"time":65368.418,"type":0},{"id":0,"sLen":0,"time":70736.8359,"type":0},{"id":0,"sLen":0,"time":71052.625,"type":0},{"id":3,"sLen":0,"time":71368.4141,"type":0},{"id":0,"sLen":0,"time":71684.21,"type":0},{"id":2,"sLen":0,"time":72000,"type":0},{"id":2,"sLen":0,"time":72631.58,"type":0},{"id":0,"sLen":0,"time":72947.37,"type":0},{"id":0,"sLen":0,"time":73263.16,"type":0},{"id":0,"sLen":0,"time":73578.9453,"type":0},{"id":3,"sLen":0,"time":73894.7344,"type":0},{"id":0,"sLen":0,"time":74210.52,"type":0},{"id":2,"sLen":0,"time":74526.31,"type":0},{"id":2,"sLen":0,"time":75157.89,"type":0},{"id":3,"sLen":0,"time":75473.68,"type":0},{"id":2,"sLen":0,"time":80842.1,"type":0},{"id":0,"sLen":0,"time":81473.68,"type":0},{"id":3,"sLen":0,"time":81631.58,"type":0},{"id":1,"sLen":0,"time":81789.47,"type":0},{"id":2,"sLen":0,"time":82105.26,"type":0},{"id":0,"sLen":0,"time":82736.8359,"type":0},{"id":3,"sLen":0,"time":82894.7344,"type":0},{"id":1,"sLen":0,"time":83052.625,"type":0},{"id":2,"sLen":0,"time":83368.4141,"type":0},{"id":0,"sLen":0,"time":84000,"type":0},{"id":3,"sLen":0,"time":84157.89,"type":0},{"id":1,"sLen":0,"time":84315.79,"type":0},{"id":2,"sLen":0,"time":84631.58,"type":0},{"id":0,"sLen":0,"time":85263.16,"type":0},{"id":3,"sLen":0,"time":85421.05,"type":0},{"id":1,"sLen":0,"time":85578.9453,"type":0},{"id":3,"sLen":157.89473,"time":90947.37,"type":0},{"id":1,"sLen":157.89473,"time":91578.9453,"type":0},{"id":0,"sLen":0,"time":91894.7344,"type":0},{"id":3,"sLen":0,"time":92210.52,"type":0},{"id":3,"sLen":0,"time":92684.2,"type":0},{"id":3,"sLen":0,"time":93157.89,"type":0},{"id":3,"sLen":0,"time":93473.68,"type":0},{"id":3,"sLen":0,"time":93789.47,"type":0},{"id":1,"sLen":0,"time":94105.26,"type":0},{"id":0,"sLen":0,"time":94421.05,"type":0},{"id":3,"sLen":0,"time":94736.8359,"type":0},{"id":3,"sLen":0,"time":95210.52,"type":0},{"id":3,"sLen":0,"time":95684.2,"type":0}],"characters":["spirit"],"visible":true,"type":0},{"position":"boyfriend","notes":[{"id":3,"sLen":157.89473,"time":5052.63135,"type":0},{"id":1,"sLen":157.89473,"time":5684.21045,"type":0},{"id":0,"sLen":0,"time":5999.99951,"type":0},{"id":3,"sLen":0,"time":6315.789,"type":0},{"id":3,"sLen":0,"time":6789.473,"type":0},{"id":3,"sLen":0,"time":7263.15771,"type":0},{"id":3,"sLen":0,"time":7578.94727,"type":0},{"id":3,"sLen":0,"time":7894.73633,"type":0},{"id":1,"sLen":0,"time":8210.526,"type":0},{"id":0,"sLen":0,"time":8526.315,"type":0},{"id":3,"sLen":0,"time":8842.10449,"type":0},{"id":3,"sLen":0,"time":9315.789,"type":0},{"id":3,"sLen":0,"time":9789.474,"type":0},{"id":2,"sLen":157.89473,"time":15157.8945,"type":0},{"id":2,"sLen":0,"time":15789.4727,"type":0},{"id":3,"sLen":0,"time":15947.3682,"type":0},{"id":2,"sLen":157.89473,"time":16421.0527,"type":0},{"id":2,"sLen":157.89473,"time":16894.7363,"type":0},{"id":2,"sLen":157.89473,"time":17368.42,"type":0},{"id":2,"sLen":157.89473,"time":17684.209,"type":0},{"id":2,"sLen":0,"time":18315.79,"type":0},{"id":3,"sLen":0,"time":18473.6836,"type":0},{"id":2,"sLen":157.89473,"time":18947.3672,"type":0},{"id":2,"sLen":157.89473,"time":19421.05,"type":0},{"id":0,"sLen":157.89473,"time":19894.7363,"type":0},{"id":0,"sLen":0,"time":24789.4727,"type":0},{"id":1,"sLen":0,"time":25263.1563,"type":0},{"id":3,"sLen":0,"time":25578.9453,"type":0},{"id":3,"sLen":0,"time":26052.63,"type":0},{"id":2,"sLen":0,"time":26526.3145,"type":0},{"id":2,"sLen":0,"time":26999.998,"type":0},{"id":3,"sLen":0,"time":27473.6836,"type":0},{"id":1,"sLen":0,"time":27789.4727,"type":0},{"id":3,"sLen":0,"time":28105.2617,"type":0},{"id":3,"sLen":0,"time":28578.9453,"type":0},{"id":2,"sLen":0,"time":29052.63,"type":0},{"id":2,"sLen":0,"time":29526.3145,"type":0},{"id":3,"sLen":0,"time":29999.998,"type":0},{"id":2,"sLen":0,"time":35368.418,"type":0},{"id":3,"sLen":0,"time":35684.207,"type":0},{"id":3,"sLen":0,"time":36000,"type":0},{"id":0,"sLen":0,"time":36315.79,"type":0},{"id":0,"sLen":0,"time":36631.58,"type":0},{"id":0,"sLen":0,"time":36947.3672,"type":0},{"id":3,"sLen":0,"time":37263.1563,"type":0},{"id":1,"sLen":0,"time":37578.9453,"type":0},{"id":0,"sLen":0,"time":37894.7344,"type":0},{"id":0,"sLen":0,"time":38210.5234,"type":0},{"id":0,"sLen":0,"time":38526.3125,"type":0},{"id":3,"sLen":0,"time":38842.1,"type":0},{"id":1,"sLen":0,"time":39157.8945,"type":0},{"id":1,"sLen":0,"time":39473.6836,"type":0},{"id":1,"sLen":0,"time":39789.4727,"type":0},{"id":1,"sLen":236.842100000002,"time":40105.26,"type":0},{"id":0,"sLen":0,"time":45473.6836,"type":0},{"id":0,"sLen":0,"time":45789.4727,"type":0},{"id":3,"sLen":0,"time":46105.26,"type":0},{"id":0,"sLen":0,"time":46421.05,"type":0},{"id":2,"sLen":0,"time":46736.84,"type":0},{"id":2,"sLen":0,"time":47368.418,"type":0},{"id":0,"sLen":0,"time":47684.207,"type":0},{"id":0,"sLen":0,"time":47999.9961,"type":0},{"id":0,"sLen":0,"time":48315.79,"type":0},{"id":3,"sLen":0,"time":48631.58,"type":0},{"id":0,"sLen":0,"time":48947.3672,"type":0},{"id":2,"sLen":0,"time":49263.1563,"type":0},{"id":2,"sLen":0,"time":49894.7344,"type":0},{"id":3,"sLen":0,"time":50210.5234,"type":0},{"id":3,"sLen":157.89473,"time":55578.9453,"type":0},{"id":1,"sLen":157.89473,"time":56210.5234,"type":0},{"id":0,"sLen":0,"time":56526.3125,"type":0},{"id":3,"sLen":0,"time":56842.1,"type":0},{"id":3,"sLen":0,"time":57315.7852,"type":0},{"id":3,"sLen":0,"time":57789.4727,"type":0},{"id":3,"sLen":0,"time":58105.26,"type":0},{"id":3,"sLen":0,"time":58421.05,"type":0},{"id":1,"sLen":0,"time":58736.84,"type":0},{"id":0,"sLen":0,"time":59052.63,"type":0},{"id":3,"sLen":0,"time":59368.418,"type":0},{"id":3,"sLen":0,"time":59842.1,"type":0},{"id":3,"sLen":0,"time":60315.7852,"type":0},{"id":2,"sLen":0,"time":65684.21,"type":0},{"id":3,"sLen":0,"time":66000,"type":0},{"id":3,"sLen":0,"time":66315.79,"type":0},{"id":0,"sLen":0,"time":66631.58,"type":0},{"id":0,"sLen":0,"time":66947.37,"type":0},{"id":0,"sLen":0,"time":67263.16,"type":0},{"id":3,"sLen":0,"time":67578.9453,"type":0},{"id":1,"sLen":0,"time":67894.7344,"type":0},{"id":0,"sLen":0,"time":68210.52,"type":0},{"id":0,"sLen":0,"time":68526.31,"type":0},{"id":3,"sLen":0,"time":68684.21,"type":0},{"id":0,"sLen":0,"time":68842.1,"type":0},{"id":3,"sLen":0,"time":69157.89,"type":0},{"id":1,"sLen":0,"time":69315.79,"type":0},{"id":1,"sLen":0,"time":69473.68,"type":0},{"id":1,"sLen":0,"time":69789.47,"type":0},{"id":1,"sLen":0,"time":70105.26,"type":0},{"id":1,"sLen":236.842099999994,"time":70421.05,"type":0},{"id":0,"sLen":0,"time":75789.47,"type":0},{"id":0,"sLen":0,"time":76105.26,"type":0},{"id":3,"sLen":0,"time":76421.05,"type":0},{"id":0,"sLen":0,"time":76736.8359,"type":0},{"id":2,"sLen":0,"time":77052.625,"type":0},{"id":2,"sLen":0,"time":77684.2,"type":0},{"id":0,"sLen":0,"time":78000,"type":0},{"id":0,"sLen":0,"time":78315.79,"type":0},{"id":0,"sLen":0,"time":78631.58,"type":0},{"id":3,"sLen":0,"time":78947.37,"type":0},{"id":0,"sLen":0,"time":79263.16,"type":0},{"id":2,"sLen":0,"time":79578.9453,"type":0},{"id":2,"sLen":0,"time":80210.52,"type":0},{"id":3,"sLen":0,"time":80526.31,"type":0},{"id":2,"sLen":0,"time":85894.7344,"type":0},{"id":0,"sLen":0,"time":86526.31,"type":0},{"id":3,"sLen":0,"time":86684.2,"type":0},{"id":1,"sLen":0,"time":86842.1,"type":0},{"id":2,"sLen":0,"time":87157.89,"type":0},{"id":0,"sLen":0,"time":87789.47,"type":0},{"id":3,"sLen":0,"time":87947.37,"type":0},{"id":1,"sLen":0,"time":88105.26,"type":0},{"id":2,"sLen":0,"time":88421.05,"type":0},{"id":0,"sLen":0,"time":89052.625,"type":0},{"id":3,"sLen":0,"time":89210.52,"type":0},{"id":1,"sLen":0,"time":89368.4141,"type":0},{"id":2,"sLen":0,"time":89684.2,"type":0},{"id":0,"sLen":0,"time":90315.79,"type":0},{"id":3,"sLen":0,"time":90473.68,"type":0},{"id":1,"sLen":0,"time":90631.58,"type":0},{"id":3,"sLen":157.89473,"time":95999.99,"type":0},{"id":1,"sLen":157.89473,"time":96631.58,"type":0},{"id":0,"sLen":0,"time":96947.37,"type":0},{"id":3,"sLen":0,"time":97263.16,"type":0},{"id":3,"sLen":0,"time":97736.8359,"type":0},{"id":3,"sLen":0,"time":98210.52,"type":0},{"id":3,"sLen":0,"time":98526.31,"type":0},{"id":3,"sLen":0,"time":98842.1,"type":0},{"id":1,"sLen":0,"time":99157.89,"type":0},{"id":0,"sLen":0,"time":99473.68,"type":0},{"id":3,"sLen":0,"time":99789.47,"type":0},{"id":3,"sLen":0,"time":100263.156,"type":0},{"id":3,"sLen":0,"time":100736.836,"type":0}],"characters":["bf-pixel"],"visible":true,"type":1},{"position":"girlfriend","notes":[],"characters":["gf-pixel"],"visible":false,"type":2}],"stage":"school-evil","scrollSpeed":1.1,"noteTypes":[]} \ No newline at end of file diff --git a/assets/songs/thorns/charts/hard.json b/assets/songs/thorns/charts/hard.json index f7de248f..0a554666 100644 --- a/assets/songs/thorns/charts/hard.json +++ b/assets/songs/thorns/charts/hard.json @@ -1 +1 @@ -{"codenameChart":true,"strumLines":[{"position":"dad","notes":[{"id":3,"sLen":157.89473,"time":0,"type":0},{"id":3,"sLen":0,"time":315.789459,"type":0},{"id":3,"sLen":0,"time":473.6842,"type":0},{"id":1,"sLen":157.89473,"time":631.5789,"type":0},{"id":0,"sLen":0,"time":947.3684,"type":0},{"id":3,"sLen":0,"time":1263.15784,"type":0},{"id":1,"sLen":0,"time":1578.94727,"type":0},{"id":3,"sLen":0,"time":1736.842,"type":0},{"id":1,"sLen":0,"time":2052.63159,"type":0},{"id":3,"sLen":0,"time":2210.52612,"type":0},{"id":3,"sLen":0,"time":2526.31567,"type":0},{"id":3,"sLen":0,"time":2684.21045,"type":0},{"id":3,"sLen":0,"time":2842.10522,"type":0},{"id":3,"sLen":0,"time":2999.99976,"type":0},{"id":1,"sLen":0,"time":3157.89453,"type":0},{"id":1,"sLen":0,"time":3315.78931,"type":0},{"id":0,"sLen":0,"time":3473.684,"type":0},{"id":0,"sLen":0,"time":3631.57886,"type":0},{"id":3,"sLen":0,"time":3789.47363,"type":0},{"id":1,"sLen":0,"time":4105.263,"type":0},{"id":3,"sLen":0,"time":4263.15771,"type":0},{"id":1,"sLen":0,"time":4578.94727,"type":0},{"id":3,"sLen":0,"time":4736.842,"type":0},{"id":2,"sLen":157.894729999998,"time":10105.2627,"type":0},{"id":1,"sLen":78.9473648000057,"time":10421.0518,"type":0},{"id":2,"sLen":0,"time":10736.8418,"type":0},{"id":3,"sLen":0,"time":10894.7363,"type":0},{"id":1,"sLen":78.9473647999985,"time":11052.6309,"type":0},{"id":2,"sLen":157.89473,"time":11368.4209,"type":0},{"id":1,"sLen":0,"time":11684.21,"type":0},{"id":2,"sLen":157.89473,"time":11842.1045,"type":0},{"id":1,"sLen":0,"time":12157.8945,"type":0},{"id":2,"sLen":157.89473,"time":12315.7891,"type":0},{"id":2,"sLen":157.89473,"time":12631.5781,"type":0},{"id":1,"sLen":78.9473648000003,"time":12947.3682,"type":0},{"id":2,"sLen":0,"time":13263.1572,"type":0},{"id":3,"sLen":0,"time":13421.0518,"type":0},{"id":1,"sLen":78.9473648000003,"time":13578.9463,"type":0},{"id":2,"sLen":157.89473,"time":13894.7363,"type":0},{"id":1,"sLen":0,"time":14210.5254,"type":0},{"id":2,"sLen":157.89473,"time":14368.42,"type":0},{"id":1,"sLen":0,"time":14684.21,"type":0},{"id":0,"sLen":157.89473,"time":14842.1045,"type":0},{"id":1,"sLen":0,"time":20210.5254,"type":0},{"id":0,"sLen":0,"time":20368.42,"type":0},{"id":3,"sLen":0,"time":20526.3145,"type":0},{"id":1,"sLen":0,"time":20684.209,"type":0},{"id":0,"sLen":0,"time":20842.1035,"type":0},{"id":3,"sLen":0,"time":21000,"type":0},{"id":1,"sLen":0,"time":21157.8945,"type":0},{"id":0,"sLen":0,"time":21315.79,"type":0},{"id":2,"sLen":0,"time":21473.6836,"type":0},{"id":1,"sLen":0,"time":21631.5781,"type":0},{"id":0,"sLen":0,"time":21789.4727,"type":0},{"id":2,"sLen":0,"time":21947.3672,"type":0},{"id":1,"sLen":0,"time":22105.2617,"type":0},{"id":0,"sLen":0,"time":22263.1563,"type":0},{"id":3,"sLen":0,"time":22421.05,"type":0},{"id":1,"sLen":0,"time":22578.9473,"type":0},{"id":1,"sLen":0,"time":22736.8418,"type":0},{"id":0,"sLen":0,"time":22894.7363,"type":0},{"id":3,"sLen":0,"time":23052.63,"type":0},{"id":1,"sLen":0,"time":23210.5254,"type":0},{"id":0,"sLen":0,"time":23368.42,"type":0},{"id":3,"sLen":0,"time":23526.3145,"type":0},{"id":1,"sLen":0,"time":23684.209,"type":0},{"id":0,"sLen":0,"time":23842.1035,"type":0},{"id":2,"sLen":0,"time":23999.998,"type":0},{"id":1,"sLen":0,"time":24157.8945,"type":0},{"id":0,"sLen":0,"time":24315.79,"type":0},{"id":2,"sLen":0,"time":24473.6836,"type":0},{"id":1,"sLen":0,"time":24631.5781,"type":0},{"id":0,"sLen":0,"time":24789.4727,"type":0},{"id":3,"sLen":0,"time":24947.3672,"type":0},{"id":1,"sLen":0,"time":25105.2617,"type":0},{"id":2,"sLen":0,"time":30315.79,"type":0},{"id":0,"sLen":0,"time":30473.6836,"type":0},{"id":3,"sLen":0,"time":30631.5781,"type":0},{"id":1,"sLen":0,"time":30789.4727,"type":0},{"id":3,"sLen":236.842100000002,"time":30947.3672,"type":0},{"id":0,"sLen":0,"time":31263.1563,"type":0},{"id":3,"sLen":0,"time":31421.05,"type":0},{"id":0,"sLen":0,"time":31578.9453,"type":0},{"id":2,"sLen":0,"time":31736.84,"type":0},{"id":0,"sLen":0,"time":31894.7363,"type":0},{"id":1,"sLen":0,"time":32052.63,"type":0},{"id":3,"sLen":236.842100000002,"time":32210.5254,"type":0},{"id":1,"sLen":0,"time":32526.3145,"type":0},{"id":3,"sLen":0,"time":32684.209,"type":0},{"id":0,"sLen":0,"time":32842.1055,"type":0},{"id":2,"sLen":0,"time":33000,"type":0},{"id":0,"sLen":0,"time":33157.8945,"type":0},{"id":3,"sLen":0,"time":33315.79,"type":0},{"id":0,"sLen":0,"time":33473.6836,"type":0},{"id":2,"sLen":0,"time":33631.58,"type":0},{"id":3,"sLen":0,"time":33789.4727,"type":0},{"id":1,"sLen":0,"time":33947.3672,"type":0},{"id":1,"sLen":0,"time":34105.26,"type":0},{"id":0,"sLen":0,"time":34263.1563,"type":0},{"id":1,"sLen":0,"time":34421.05,"type":0},{"id":3,"sLen":0,"time":34578.9453,"type":0},{"id":1,"sLen":0,"time":34736.84,"type":0},{"id":0,"sLen":0,"time":34894.7344,"type":0},{"id":1,"sLen":236.842100000002,"time":35052.63,"type":0},{"id":0,"sLen":0,"time":40421.05,"type":0},{"id":1,"sLen":0,"time":40578.9453,"type":0},{"id":0,"sLen":0,"time":40736.84,"type":0},{"id":2,"sLen":0,"time":40894.7344,"type":0},{"id":3,"sLen":0,"time":41052.63,"type":0},{"id":1,"sLen":0,"time":41210.5234,"type":0},{"id":0,"sLen":0,"time":41368.418,"type":0},{"id":3,"sLen":0,"time":41526.3125,"type":0},{"id":2,"sLen":0,"time":41684.207,"type":0},{"id":0,"sLen":0,"time":41842.1,"type":0},{"id":1,"sLen":0,"time":42000,"type":0},{"id":0,"sLen":0,"time":42157.8945,"type":0},{"id":2,"sLen":0,"time":42315.79,"type":0},{"id":3,"sLen":0,"time":42473.6836,"type":0},{"id":0,"sLen":0,"time":42631.58,"type":0},{"id":3,"sLen":0,"time":42789.4727,"type":0},{"id":0,"sLen":0,"time":42947.3672,"type":0},{"id":1,"sLen":0,"time":43105.26,"type":0},{"id":0,"sLen":0,"time":43263.1563,"type":0},{"id":2,"sLen":0,"time":43421.05,"type":0},{"id":3,"sLen":0,"time":43578.9453,"type":0},{"id":1,"sLen":0,"time":43736.84,"type":0},{"id":0,"sLen":0,"time":43894.7344,"type":0},{"id":3,"sLen":0,"time":44052.63,"type":0},{"id":2,"sLen":0,"time":44210.5234,"type":0},{"id":0,"sLen":0,"time":44368.418,"type":0},{"id":1,"sLen":0,"time":44526.3125,"type":0},{"id":0,"sLen":0,"time":44684.207,"type":0},{"id":2,"sLen":0,"time":44842.1,"type":0},{"id":0,"sLen":0,"time":44999.9961,"type":0},{"id":3,"sLen":0,"time":45157.8945,"type":0},{"id":0,"sLen":0,"time":45315.79,"type":0},{"id":3,"sLen":157.89473,"time":50526.3125,"type":0},{"id":3,"sLen":0,"time":50842.1,"type":0},{"id":3,"sLen":0,"time":50999.9961,"type":0},{"id":1,"sLen":157.89473,"time":51157.89,"type":0},{"id":0,"sLen":0,"time":51473.6836,"type":0},{"id":3,"sLen":0,"time":51789.4727,"type":0},{"id":1,"sLen":0,"time":52105.26,"type":0},{"id":3,"sLen":0,"time":52263.1563,"type":0},{"id":1,"sLen":0,"time":52578.9453,"type":0},{"id":3,"sLen":0,"time":52736.84,"type":0},{"id":3,"sLen":0,"time":53052.63,"type":0},{"id":3,"sLen":0,"time":53210.5234,"type":0},{"id":3,"sLen":0,"time":53368.418,"type":0},{"id":3,"sLen":0,"time":53526.3125,"type":0},{"id":1,"sLen":0,"time":53684.207,"type":0},{"id":1,"sLen":0,"time":53842.1,"type":0},{"id":0,"sLen":0,"time":53999.9961,"type":0},{"id":0,"sLen":0,"time":54157.89,"type":0},{"id":3,"sLen":0,"time":54315.7852,"type":0},{"id":1,"sLen":0,"time":54631.58,"type":0},{"id":3,"sLen":0,"time":54789.4727,"type":0},{"id":1,"sLen":0,"time":55105.26,"type":0},{"id":3,"sLen":0,"time":55263.1563,"type":0},{"id":2,"sLen":0,"time":60631.58,"type":0},{"id":0,"sLen":0,"time":60789.4727,"type":0},{"id":3,"sLen":0,"time":60947.3672,"type":0},{"id":1,"sLen":0,"time":61105.26,"type":0},{"id":3,"sLen":0,"time":61263.1563,"type":0},{"id":3,"sLen":0,"time":61421.05,"type":0},{"id":0,"sLen":0,"time":61578.9453,"type":0},{"id":3,"sLen":0,"time":61736.84,"type":0},{"id":0,"sLen":0,"time":61894.7344,"type":0},{"id":2,"sLen":0,"time":62052.63,"type":0},{"id":0,"sLen":0,"time":62210.5234,"type":0},{"id":1,"sLen":0,"time":62368.418,"type":0},{"id":3,"sLen":0,"time":62526.3125,"type":0},{"id":3,"sLen":0,"time":62684.207,"type":0},{"id":1,"sLen":0,"time":62842.1,"type":0},{"id":3,"sLen":0,"time":62999.9961,"type":0},{"id":0,"sLen":0,"time":63157.89,"type":0},{"id":2,"sLen":0,"time":63315.7852,"type":0},{"id":0,"sLen":0,"time":63473.68,"type":0},{"id":3,"sLen":0,"time":63631.5742,"type":0},{"id":0,"sLen":0,"time":63789.4727,"type":0},{"id":2,"sLen":0,"time":63947.3672,"type":0},{"id":3,"sLen":0,"time":64105.26,"type":0},{"id":1,"sLen":0,"time":64263.1563,"type":0},{"id":1,"sLen":0,"time":64421.05,"type":0},{"id":0,"sLen":0,"time":64578.9453,"type":0},{"id":1,"sLen":0,"time":64736.84,"type":0},{"id":3,"sLen":0,"time":64894.7344,"type":0},{"id":1,"sLen":0,"time":65052.63,"type":0},{"id":0,"sLen":0,"time":65210.5234,"type":0},{"id":1,"sLen":236.842100000002,"time":65368.418,"type":0},{"id":0,"sLen":0,"time":70736.8359,"type":0},{"id":1,"sLen":0,"time":70894.7344,"type":0},{"id":0,"sLen":0,"time":71052.625,"type":0},{"id":2,"sLen":0,"time":71210.52,"type":0},{"id":3,"sLen":0,"time":71368.4141,"type":0},{"id":1,"sLen":0,"time":71526.31,"type":0},{"id":0,"sLen":0,"time":71684.21,"type":0},{"id":3,"sLen":0,"time":71842.1,"type":0},{"id":2,"sLen":0,"time":72000,"type":0},{"id":0,"sLen":0,"time":72157.89,"type":0},{"id":1,"sLen":0,"time":72315.79,"type":0},{"id":0,"sLen":0,"time":72473.68,"type":0},{"id":2,"sLen":0,"time":72631.58,"type":0},{"id":3,"sLen":0,"time":72789.47,"type":0},{"id":0,"sLen":0,"time":72947.37,"type":0},{"id":3,"sLen":0,"time":73105.26,"type":0},{"id":0,"sLen":0,"time":73263.16,"type":0},{"id":1,"sLen":0,"time":73421.05,"type":0},{"id":0,"sLen":0,"time":73578.9453,"type":0},{"id":2,"sLen":0,"time":73736.8359,"type":0},{"id":3,"sLen":0,"time":73894.7344,"type":0},{"id":1,"sLen":0,"time":74052.625,"type":0},{"id":0,"sLen":0,"time":74210.52,"type":0},{"id":3,"sLen":0,"time":74368.4141,"type":0},{"id":2,"sLen":0,"time":74526.31,"type":0},{"id":0,"sLen":0,"time":74684.21,"type":0},{"id":1,"sLen":0,"time":74842.1,"type":0},{"id":0,"sLen":0,"time":75000,"type":0},{"id":2,"sLen":0,"time":75157.89,"type":0},{"id":0,"sLen":0,"time":75315.79,"type":0},{"id":3,"sLen":0,"time":75473.68,"type":0},{"id":0,"sLen":0,"time":75631.58,"type":0},{"id":2,"sLen":0,"time":80842.1,"type":0},{"id":3,"sLen":0,"time":81000,"type":0},{"id":1,"sLen":0,"time":81157.89,"type":0},{"id":3,"sLen":0,"time":81315.79,"type":0},{"id":0,"sLen":0,"time":81473.68,"type":0},{"id":3,"sLen":0,"time":81631.58,"type":0},{"id":1,"sLen":0,"time":81789.47,"type":0},{"id":1,"sLen":0,"time":81947.37,"type":0},{"id":2,"sLen":0,"time":82105.26,"type":0},{"id":3,"sLen":0,"time":82263.16,"type":0},{"id":1,"sLen":0,"time":82421.05,"type":0},{"id":3,"sLen":0,"time":82578.9453,"type":0},{"id":0,"sLen":0,"time":82736.8359,"type":0},{"id":3,"sLen":0,"time":82894.7344,"type":0},{"id":1,"sLen":0,"time":83052.625,"type":0},{"id":1,"sLen":0,"time":83210.52,"type":0},{"id":2,"sLen":0,"time":83368.4141,"type":0},{"id":3,"sLen":0,"time":83526.31,"type":0},{"id":1,"sLen":0,"time":83684.2,"type":0},{"id":3,"sLen":0,"time":83842.1,"type":0},{"id":0,"sLen":0,"time":84000,"type":0},{"id":3,"sLen":0,"time":84157.89,"type":0},{"id":1,"sLen":0,"time":84315.79,"type":0},{"id":1,"sLen":0,"time":84473.68,"type":0},{"id":2,"sLen":0,"time":84631.58,"type":0},{"id":3,"sLen":0,"time":84789.47,"type":0},{"id":1,"sLen":0,"time":84947.37,"type":0},{"id":3,"sLen":0,"time":85105.26,"type":0},{"id":0,"sLen":0,"time":85263.16,"type":0},{"id":3,"sLen":0,"time":85421.05,"type":0},{"id":1,"sLen":0,"time":85578.9453,"type":0},{"id":1,"sLen":0,"time":85736.8359,"type":0},{"id":3,"sLen":157.89473,"time":90947.37,"type":0},{"id":3,"sLen":0,"time":91263.16,"type":0},{"id":3,"sLen":0,"time":91421.05,"type":0},{"id":1,"sLen":157.89473,"time":91578.9453,"type":0},{"id":0,"sLen":0,"time":91894.7344,"type":0},{"id":3,"sLen":0,"time":92210.52,"type":0},{"id":1,"sLen":0,"time":92526.31,"type":0},{"id":3,"sLen":0,"time":92684.2,"type":0},{"id":1,"sLen":0,"time":92999.99,"type":0},{"id":3,"sLen":0,"time":93157.89,"type":0},{"id":3,"sLen":0,"time":93473.68,"type":0},{"id":3,"sLen":0,"time":93631.58,"type":0},{"id":3,"sLen":0,"time":93789.47,"type":0},{"id":3,"sLen":0,"time":93947.37,"type":0},{"id":1,"sLen":0,"time":94105.26,"type":0},{"id":1,"sLen":0,"time":94263.16,"type":0},{"id":0,"sLen":0,"time":94421.05,"type":0},{"id":0,"sLen":0,"time":94578.9453,"type":0},{"id":3,"sLen":0,"time":94736.8359,"type":0},{"id":1,"sLen":0,"time":95052.625,"type":0},{"id":3,"sLen":0,"time":95210.52,"type":0},{"id":1,"sLen":0,"time":95526.31,"type":0},{"id":3,"sLen":0,"time":95684.2,"type":0}],"characters":["spirit"],"visible":true,"type":0},{"position":"boyfriend","notes":[{"id":3,"sLen":157.89473,"time":5052.63135,"type":0},{"id":3,"sLen":0,"time":5368.421,"type":0},{"id":3,"sLen":0,"time":5526.31543,"type":0},{"id":1,"sLen":157.89473,"time":5684.21045,"type":0},{"id":0,"sLen":0,"time":5999.99951,"type":0},{"id":3,"sLen":0,"time":6315.789,"type":0},{"id":1,"sLen":0,"time":6631.57861,"type":0},{"id":3,"sLen":0,"time":6789.473,"type":0},{"id":1,"sLen":0,"time":7105.2627,"type":0},{"id":3,"sLen":0,"time":7263.15771,"type":0},{"id":3,"sLen":0,"time":7578.94727,"type":0},{"id":3,"sLen":0,"time":7736.842,"type":0},{"id":3,"sLen":0,"time":7894.73633,"type":0},{"id":3,"sLen":0,"time":8052.63135,"type":0},{"id":1,"sLen":0,"time":8210.526,"type":0},{"id":1,"sLen":0,"time":8368.421,"type":0},{"id":0,"sLen":0,"time":8526.315,"type":0},{"id":0,"sLen":0,"time":8684.21,"type":0},{"id":3,"sLen":0,"time":8842.10449,"type":0},{"id":1,"sLen":0,"time":9157.895,"type":0},{"id":3,"sLen":0,"time":9315.789,"type":0},{"id":1,"sLen":0,"time":9631.578,"type":0},{"id":3,"sLen":0,"time":9789.474,"type":0},{"id":2,"sLen":157.89473,"time":15157.8945,"type":0},{"id":1,"sLen":78.9473648000003,"time":15473.6836,"type":0},{"id":2,"sLen":0,"time":15789.4727,"type":0},{"id":3,"sLen":0,"time":15947.3682,"type":0},{"id":1,"sLen":78.9473648000003,"time":16105.2627,"type":0},{"id":2,"sLen":157.89473,"time":16421.0527,"type":0},{"id":1,"sLen":0,"time":16736.8418,"type":0},{"id":2,"sLen":157.89473,"time":16894.7363,"type":0},{"id":1,"sLen":0,"time":17210.5254,"type":0},{"id":2,"sLen":157.89473,"time":17368.42,"type":0},{"id":2,"sLen":157.89473,"time":17684.209,"type":0},{"id":1,"sLen":78.9473647999985,"time":18000,"type":0},{"id":2,"sLen":0,"time":18315.79,"type":0},{"id":3,"sLen":0,"time":18473.6836,"type":0},{"id":1,"sLen":78.9473647999985,"time":18631.5781,"type":0},{"id":2,"sLen":157.89473,"time":18947.3672,"type":0},{"id":1,"sLen":0,"time":19263.1563,"type":0},{"id":2,"sLen":157.89473,"time":19421.05,"type":0},{"id":1,"sLen":0,"time":19736.8418,"type":0},{"id":0,"sLen":157.89473,"time":19894.7363,"type":0},{"id":3,"sLen":0,"time":24789.4727,"type":0},{"id":2,"sLen":0,"time":24947.3672,"type":0},{"id":0,"sLen":0,"time":25105.2617,"type":0},{"id":1,"sLen":0,"time":25263.1563,"type":0},{"id":0,"sLen":0,"time":25421.05,"type":0},{"id":3,"sLen":0,"time":25578.9453,"type":0},{"id":1,"sLen":0,"time":25736.8418,"type":0},{"id":0,"sLen":0,"time":25894.7363,"type":0},{"id":3,"sLen":0,"time":26052.63,"type":0},{"id":1,"sLen":0,"time":26210.5254,"type":0},{"id":0,"sLen":0,"time":26368.42,"type":0},{"id":2,"sLen":0,"time":26526.3145,"type":0},{"id":1,"sLen":0,"time":26684.209,"type":0},{"id":0,"sLen":0,"time":26842.1035,"type":0},{"id":2,"sLen":0,"time":26999.998,"type":0},{"id":1,"sLen":0,"time":27157.8926,"type":0},{"id":0,"sLen":0,"time":27315.79,"type":0},{"id":3,"sLen":0,"time":27473.6836,"type":0},{"id":1,"sLen":0,"time":27631.5781,"type":0},{"id":1,"sLen":0,"time":27789.4727,"type":0},{"id":0,"sLen":0,"time":27947.3672,"type":0},{"id":3,"sLen":0,"time":28105.2617,"type":0},{"id":1,"sLen":0,"time":28263.1563,"type":0},{"id":0,"sLen":0,"time":28421.05,"type":0},{"id":3,"sLen":0,"time":28578.9453,"type":0},{"id":1,"sLen":0,"time":28736.84,"type":0},{"id":0,"sLen":0,"time":28894.7363,"type":0},{"id":2,"sLen":0,"time":29052.63,"type":0},{"id":1,"sLen":0,"time":29210.5254,"type":0},{"id":0,"sLen":0,"time":29368.42,"type":0},{"id":2,"sLen":0,"time":29526.3145,"type":0},{"id":1,"sLen":0,"time":29684.209,"type":0},{"id":0,"sLen":0,"time":29842.1035,"type":0},{"id":3,"sLen":0,"time":29999.998,"type":0},{"id":1,"sLen":0,"time":30157.8926,"type":0},{"id":2,"sLen":0,"time":35368.418,"type":0},{"id":0,"sLen":0,"time":35526.3125,"type":0},{"id":3,"sLen":0,"time":35684.207,"type":0},{"id":1,"sLen":0,"time":35842.1055,"type":0},{"id":3,"sLen":236.842100000002,"time":36000,"type":0},{"id":0,"sLen":0,"time":36315.79,"type":0},{"id":3,"sLen":0,"time":36473.6836,"type":0},{"id":0,"sLen":0,"time":36631.58,"type":0},{"id":2,"sLen":0,"time":36789.4727,"type":0},{"id":0,"sLen":0,"time":36947.3672,"type":0},{"id":1,"sLen":0,"time":37105.26,"type":0},{"id":3,"sLen":236.842100000002,"time":37263.1563,"type":0},{"id":1,"sLen":0,"time":37578.9453,"type":0},{"id":3,"sLen":0,"time":37736.84,"type":0},{"id":0,"sLen":0,"time":37894.7344,"type":0},{"id":2,"sLen":0,"time":38052.63,"type":0},{"id":0,"sLen":0,"time":38210.5234,"type":0},{"id":3,"sLen":0,"time":38368.418,"type":0},{"id":0,"sLen":0,"time":38526.3125,"type":0},{"id":2,"sLen":0,"time":38684.207,"type":0},{"id":3,"sLen":0,"time":38842.1,"type":0},{"id":1,"sLen":0,"time":39000,"type":0},{"id":1,"sLen":0,"time":39157.8945,"type":0},{"id":0,"sLen":0,"time":39315.79,"type":0},{"id":1,"sLen":0,"time":39473.6836,"type":0},{"id":3,"sLen":0,"time":39631.58,"type":0},{"id":1,"sLen":0,"time":39789.4727,"type":0},{"id":0,"sLen":0,"time":39947.3672,"type":0},{"id":1,"sLen":236.842100000002,"time":40105.26,"type":0},{"id":0,"sLen":0,"time":45473.6836,"type":0},{"id":1,"sLen":0,"time":45631.58,"type":0},{"id":0,"sLen":0,"time":45789.4727,"type":0},{"id":2,"sLen":0,"time":45947.3672,"type":0},{"id":3,"sLen":0,"time":46105.26,"type":0},{"id":1,"sLen":0,"time":46263.1563,"type":0},{"id":0,"sLen":0,"time":46421.05,"type":0},{"id":3,"sLen":0,"time":46578.9453,"type":0},{"id":2,"sLen":0,"time":46736.84,"type":0},{"id":0,"sLen":0,"time":46894.7344,"type":0},{"id":1,"sLen":0,"time":47052.63,"type":0},{"id":0,"sLen":0,"time":47210.5234,"type":0},{"id":2,"sLen":0,"time":47368.418,"type":0},{"id":3,"sLen":0,"time":47526.3125,"type":0},{"id":0,"sLen":0,"time":47684.207,"type":0},{"id":3,"sLen":0,"time":47842.1,"type":0},{"id":0,"sLen":0,"time":47999.9961,"type":0},{"id":1,"sLen":0,"time":48157.89,"type":0},{"id":0,"sLen":0,"time":48315.79,"type":0},{"id":2,"sLen":0,"time":48473.6836,"type":0},{"id":3,"sLen":0,"time":48631.58,"type":0},{"id":1,"sLen":0,"time":48789.4727,"type":0},{"id":0,"sLen":0,"time":48947.3672,"type":0},{"id":3,"sLen":0,"time":49105.26,"type":0},{"id":2,"sLen":0,"time":49263.1563,"type":0},{"id":0,"sLen":0,"time":49421.05,"type":0},{"id":1,"sLen":0,"time":49578.9453,"type":0},{"id":0,"sLen":0,"time":49736.84,"type":0},{"id":2,"sLen":0,"time":49894.7344,"type":0},{"id":0,"sLen":0,"time":50052.63,"type":0},{"id":3,"sLen":0,"time":50210.5234,"type":0},{"id":0,"sLen":0,"time":50368.418,"type":0},{"id":3,"sLen":157.89473,"time":55578.9453,"type":0},{"id":3,"sLen":0,"time":55894.7344,"type":0},{"id":3,"sLen":0,"time":56052.63,"type":0},{"id":1,"sLen":157.89473,"time":56210.5234,"type":0},{"id":0,"sLen":0,"time":56526.3125,"type":0},{"id":3,"sLen":0,"time":56842.1,"type":0},{"id":1,"sLen":0,"time":57157.89,"type":0},{"id":3,"sLen":0,"time":57315.7852,"type":0},{"id":1,"sLen":0,"time":57631.58,"type":0},{"id":3,"sLen":0,"time":57789.4727,"type":0},{"id":3,"sLen":0,"time":58105.26,"type":0},{"id":3,"sLen":0,"time":58263.1563,"type":0},{"id":3,"sLen":0,"time":58421.05,"type":0},{"id":3,"sLen":0,"time":58578.9453,"type":0},{"id":1,"sLen":0,"time":58736.84,"type":0},{"id":1,"sLen":0,"time":58894.7344,"type":0},{"id":0,"sLen":0,"time":59052.63,"type":0},{"id":0,"sLen":0,"time":59210.5234,"type":0},{"id":3,"sLen":0,"time":59368.418,"type":0},{"id":1,"sLen":0,"time":59684.207,"type":0},{"id":3,"sLen":0,"time":59842.1,"type":0},{"id":1,"sLen":0,"time":60157.89,"type":0},{"id":3,"sLen":0,"time":60315.7852,"type":0},{"id":2,"sLen":0,"time":65684.21,"type":0},{"id":0,"sLen":0,"time":65842.1,"type":0},{"id":3,"sLen":0,"time":66000,"type":0},{"id":1,"sLen":0,"time":66157.89,"type":0},{"id":3,"sLen":0,"time":66315.79,"type":0},{"id":3,"sLen":0,"time":66473.68,"type":0},{"id":0,"sLen":0,"time":66631.58,"type":0},{"id":3,"sLen":0,"time":66789.47,"type":0},{"id":0,"sLen":0,"time":66947.37,"type":0},{"id":2,"sLen":0,"time":67105.26,"type":0},{"id":0,"sLen":0,"time":67263.16,"type":0},{"id":1,"sLen":0,"time":67421.05,"type":0},{"id":3,"sLen":0,"time":67578.9453,"type":0},{"id":3,"sLen":0,"time":67736.8359,"type":0},{"id":1,"sLen":0,"time":67894.7344,"type":0},{"id":3,"sLen":0,"time":68052.625,"type":0},{"id":0,"sLen":0,"time":68210.52,"type":0},{"id":2,"sLen":0,"time":68368.4141,"type":0},{"id":0,"sLen":0,"time":68526.31,"type":0},{"id":3,"sLen":0,"time":68684.21,"type":0},{"id":0,"sLen":0,"time":68842.1,"type":0},{"id":2,"sLen":0,"time":69000,"type":0},{"id":3,"sLen":0,"time":69157.89,"type":0},{"id":1,"sLen":0,"time":69315.79,"type":0},{"id":1,"sLen":0,"time":69473.68,"type":0},{"id":0,"sLen":0,"time":69631.58,"type":0},{"id":1,"sLen":0,"time":69789.47,"type":0},{"id":3,"sLen":0,"time":69947.37,"type":0},{"id":1,"sLen":0,"time":70105.26,"type":0},{"id":0,"sLen":0,"time":70263.16,"type":0},{"id":1,"sLen":236.842099999994,"time":70421.05,"type":0},{"id":0,"sLen":0,"time":75789.47,"type":0},{"id":1,"sLen":0,"time":75947.37,"type":0},{"id":0,"sLen":0,"time":76105.26,"type":0},{"id":2,"sLen":0,"time":76263.16,"type":0},{"id":3,"sLen":0,"time":76421.05,"type":0},{"id":1,"sLen":0,"time":76578.9453,"type":0},{"id":0,"sLen":0,"time":76736.8359,"type":0},{"id":3,"sLen":0,"time":76894.7344,"type":0},{"id":2,"sLen":0,"time":77052.625,"type":0},{"id":0,"sLen":0,"time":77210.52,"type":0},{"id":1,"sLen":0,"time":77368.4141,"type":0},{"id":0,"sLen":0,"time":77526.31,"type":0},{"id":2,"sLen":0,"time":77684.2,"type":0},{"id":3,"sLen":0,"time":77842.1,"type":0},{"id":0,"sLen":0,"time":78000,"type":0},{"id":3,"sLen":0,"time":78157.89,"type":0},{"id":0,"sLen":0,"time":78315.79,"type":0},{"id":1,"sLen":0,"time":78473.68,"type":0},{"id":0,"sLen":0,"time":78631.58,"type":0},{"id":2,"sLen":0,"time":78789.47,"type":0},{"id":3,"sLen":0,"time":78947.37,"type":0},{"id":1,"sLen":0,"time":79105.26,"type":0},{"id":0,"sLen":0,"time":79263.16,"type":0},{"id":3,"sLen":0,"time":79421.05,"type":0},{"id":2,"sLen":0,"time":79578.9453,"type":0},{"id":0,"sLen":0,"time":79736.8359,"type":0},{"id":1,"sLen":0,"time":79894.7344,"type":0},{"id":0,"sLen":0,"time":80052.625,"type":0},{"id":2,"sLen":0,"time":80210.52,"type":0},{"id":0,"sLen":0,"time":80368.4141,"type":0},{"id":3,"sLen":0,"time":80526.31,"type":0},{"id":0,"sLen":0,"time":80684.2,"type":0},{"id":2,"sLen":0,"time":85894.7344,"type":0},{"id":3,"sLen":0,"time":86052.625,"type":0},{"id":1,"sLen":0,"time":86210.52,"type":0},{"id":3,"sLen":0,"time":86368.4141,"type":0},{"id":0,"sLen":0,"time":86526.31,"type":0},{"id":3,"sLen":0,"time":86684.2,"type":0},{"id":1,"sLen":0,"time":86842.1,"type":0},{"id":1,"sLen":0,"time":86999.99,"type":0},{"id":2,"sLen":0,"time":87157.89,"type":0},{"id":3,"sLen":0,"time":87315.79,"type":0},{"id":1,"sLen":0,"time":87473.68,"type":0},{"id":3,"sLen":0,"time":87631.58,"type":0},{"id":0,"sLen":0,"time":87789.47,"type":0},{"id":3,"sLen":0,"time":87947.37,"type":0},{"id":1,"sLen":0,"time":88105.26,"type":0},{"id":1,"sLen":0,"time":88263.16,"type":0},{"id":2,"sLen":0,"time":88421.05,"type":0},{"id":3,"sLen":0,"time":88578.9453,"type":0},{"id":1,"sLen":0,"time":88736.8359,"type":0},{"id":3,"sLen":0,"time":88894.7344,"type":0},{"id":0,"sLen":0,"time":89052.625,"type":0},{"id":3,"sLen":0,"time":89210.52,"type":0},{"id":1,"sLen":0,"time":89368.4141,"type":0},{"id":1,"sLen":0,"time":89526.31,"type":0},{"id":2,"sLen":0,"time":89684.2,"type":0},{"id":3,"sLen":0,"time":89842.1,"type":0},{"id":1,"sLen":0,"time":89999.99,"type":0},{"id":3,"sLen":0,"time":90157.89,"type":0},{"id":0,"sLen":0,"time":90315.79,"type":0},{"id":3,"sLen":0,"time":90473.68,"type":0},{"id":1,"sLen":0,"time":90631.58,"type":0},{"id":1,"sLen":0,"time":90789.47,"type":0},{"id":3,"sLen":157.89473,"time":95999.99,"type":0},{"id":3,"sLen":0,"time":96315.78,"type":0},{"id":3,"sLen":0,"time":96473.68,"type":0},{"id":1,"sLen":157.89473,"time":96631.58,"type":0},{"id":0,"sLen":0,"time":96947.37,"type":0},{"id":3,"sLen":0,"time":97263.16,"type":0},{"id":1,"sLen":0,"time":97578.9453,"type":0},{"id":3,"sLen":0,"time":97736.8359,"type":0},{"id":1,"sLen":0,"time":98052.625,"type":0},{"id":3,"sLen":0,"time":98210.52,"type":0},{"id":3,"sLen":0,"time":98526.31,"type":0},{"id":3,"sLen":0,"time":98684.2,"type":0},{"id":3,"sLen":0,"time":98842.1,"type":0},{"id":3,"sLen":0,"time":98999.99,"type":0},{"id":1,"sLen":0,"time":99157.89,"type":0},{"id":1,"sLen":0,"time":99315.78,"type":0},{"id":0,"sLen":0,"time":99473.68,"type":0},{"id":0,"sLen":0,"time":99631.58,"type":0},{"id":3,"sLen":0,"time":99789.47,"type":0},{"id":1,"sLen":0,"time":100105.258,"type":0},{"id":3,"sLen":0,"time":100263.156,"type":0},{"id":1,"sLen":0,"time":100578.945,"type":0},{"id":3,"sLen":0,"time":100736.836,"type":0}],"characters":["bf-pixel"],"visible":true,"type":1},{"position":"girlfriend","notes":[],"characters":["gf-pixel"],"visible":false,"type":2}],"stage":"school-evil","scrollSpeed":1.5,"noteTypes":[]} \ No newline at end of file +{"events":[{"params":[1],"time":5052.63157894737,"name":"Camera Movement"},{"params":[0],"time":10105.2631578947,"name":"Camera Movement"},{"params":[1],"time":15157.8947368421,"name":"Camera Movement"},{"params":[0],"time":20210.5263157895,"name":"Camera Movement"},{"params":[1],"time":25263.1578947368,"name":"Camera Movement"},{"params":[0],"time":30315.7894736842,"name":"Camera Movement"},{"params":[1],"time":35368.4210526316,"name":"Camera Movement"},{"params":[0],"time":40421.0526315789,"name":"Camera Movement"},{"params":[1],"time":45473.6842105263,"name":"Camera Movement"},{"params":[0],"time":50526.3157894737,"name":"Camera Movement"},{"params":[1],"time":55578.9473684211,"name":"Camera Movement"},{"params":[0],"time":60631.5789473684,"name":"Camera Movement"},{"params":[1],"time":65684.2105263158,"name":"Camera Movement"},{"params":[0],"time":70736.8421052632,"name":"Camera Movement"},{"params":[1],"time":75789.4736842105,"name":"Camera Movement"},{"params":[0],"time":80842.1052631579,"name":"Camera Movement"},{"params":[1],"time":85894.7368421053,"name":"Camera Movement"},{"params":[0],"time":90947.3684210526,"name":"Camera Movement"},{"params":[1],"time":96000,"name":"Camera Movement"}],"stage":"school-evil","strumLines":[{"keyCount":4,"notes":[{"id":3,"sLen":157.89473,"time":0,"type":0},{"id":3,"sLen":0,"time":315.789459,"type":0},{"id":3,"sLen":0,"time":473.6842,"type":0},{"id":1,"sLen":157.89473,"time":631.5789,"type":0},{"id":0,"sLen":0,"time":947.3684,"type":0},{"id":3,"sLen":0,"time":1263.15784,"type":0},{"id":1,"sLen":0,"time":1578.94727,"type":0},{"id":3,"sLen":0,"time":1736.842,"type":0},{"id":1,"sLen":0,"time":2052.63159,"type":0},{"id":3,"sLen":0,"time":2210.52612,"type":0},{"id":3,"sLen":0,"time":2526.31567,"type":0},{"id":3,"sLen":0,"time":2684.21045,"type":0},{"id":3,"sLen":0,"time":2842.10522,"type":0},{"id":3,"sLen":0,"time":2999.99976,"type":0},{"id":1,"sLen":0,"time":3157.89453,"type":0},{"id":1,"sLen":0,"time":3315.78931,"type":0},{"id":0,"sLen":0,"time":3473.684,"type":0},{"id":0,"sLen":0,"time":3631.57886,"type":0},{"id":3,"sLen":0,"time":3789.47363,"type":0},{"id":1,"sLen":0,"time":4105.263,"type":0},{"id":3,"sLen":0,"time":4263.15771,"type":0},{"id":1,"sLen":0,"time":4578.94727,"type":0},{"id":3,"sLen":0,"time":4736.842,"type":0},{"id":2,"sLen":157.894729999998,"time":10105.2627,"type":0},{"id":1,"sLen":78.9473648000076,"time":10421.0518,"type":0},{"id":2,"sLen":0,"time":10736.8418,"type":0},{"id":3,"sLen":0,"time":10894.7363,"type":0},{"id":1,"sLen":78.9473647999985,"time":11052.6309,"type":0},{"id":2,"sLen":157.89473,"time":11368.4209,"type":0},{"id":1,"sLen":0,"time":11684.21,"type":0},{"id":2,"sLen":157.89473,"time":11842.1045,"type":0},{"id":1,"sLen":0,"time":12157.8945,"type":0},{"id":2,"sLen":157.89473,"time":12315.7891,"type":0},{"id":2,"sLen":157.89473,"time":12631.5781,"type":0},{"id":1,"sLen":78.9473648000003,"time":12947.3682,"type":0},{"id":2,"sLen":0,"time":13263.1572,"type":0},{"id":3,"sLen":0,"time":13421.0518,"type":0},{"id":1,"sLen":78.9473648000003,"time":13578.9463,"type":0},{"id":2,"sLen":157.89473,"time":13894.7363,"type":0},{"id":1,"sLen":0,"time":14210.5254,"type":0},{"id":2,"sLen":157.89473,"time":14368.42,"type":0},{"id":1,"sLen":0,"time":14684.21,"type":0},{"id":0,"sLen":157.89473,"time":14842.1045,"type":0},{"id":1,"sLen":0,"time":20210.5254,"type":0},{"id":0,"sLen":0,"time":20368.42,"type":0},{"id":3,"sLen":0,"time":20526.3145,"type":0},{"id":1,"sLen":0,"time":20684.209,"type":0},{"id":0,"sLen":0,"time":20842.1035,"type":0},{"id":3,"sLen":0,"time":21000,"type":0},{"id":1,"sLen":0,"time":21157.8945,"type":0},{"id":0,"sLen":0,"time":21315.79,"type":0},{"id":2,"sLen":0,"time":21473.6836,"type":0},{"id":1,"sLen":0,"time":21631.5781,"type":0},{"id":0,"sLen":0,"time":21789.4727,"type":0},{"id":2,"sLen":0,"time":21947.3672,"type":0},{"id":1,"sLen":0,"time":22105.2617,"type":0},{"id":0,"sLen":0,"time":22263.1563,"type":0},{"id":3,"sLen":0,"time":22421.05,"type":0},{"id":1,"sLen":0,"time":22578.9473,"type":0},{"id":1,"sLen":0,"time":22736.8418,"type":0},{"id":0,"sLen":0,"time":22894.7363,"type":0},{"id":3,"sLen":0,"time":23052.63,"type":0},{"id":1,"sLen":0,"time":23210.5254,"type":0},{"id":0,"sLen":0,"time":23368.42,"type":0},{"id":3,"sLen":0,"time":23526.3145,"type":0},{"id":1,"sLen":0,"time":23684.209,"type":0},{"id":0,"sLen":0,"time":23842.1035,"type":0},{"id":2,"sLen":0,"time":23999.998,"type":0},{"id":1,"sLen":0,"time":24157.8945,"type":0},{"id":0,"sLen":0,"time":24315.79,"type":0},{"id":2,"sLen":0,"time":24473.6836,"type":0},{"id":1,"sLen":0,"time":24631.5781,"type":0},{"id":0,"sLen":0,"time":24789.4727,"type":0},{"id":3,"sLen":0,"time":24947.3672,"type":0},{"id":1,"sLen":0,"time":25105.2617,"type":0},{"id":2,"sLen":0,"time":30315.79,"type":0},{"id":0,"sLen":0,"time":30473.6836,"type":0},{"id":3,"sLen":0,"time":30631.5781,"type":0},{"id":1,"sLen":0,"time":30789.4727,"type":0},{"id":3,"sLen":236.842100000002,"time":30947.3672,"type":0},{"id":0,"sLen":0,"time":31263.1563,"type":0},{"id":3,"sLen":0,"time":31421.05,"type":0},{"id":0,"sLen":0,"time":31578.9453,"type":0},{"id":2,"sLen":0,"time":31736.84,"type":0},{"id":0,"sLen":0,"time":31894.7363,"type":0},{"id":1,"sLen":0,"time":32052.63,"type":0},{"id":3,"sLen":236.842100000002,"time":32210.5254,"type":0},{"id":1,"sLen":0,"time":32526.3145,"type":0},{"id":3,"sLen":0,"time":32684.209,"type":0},{"id":0,"sLen":0,"time":32842.1055,"type":0},{"id":2,"sLen":0,"time":33000,"type":0},{"id":0,"sLen":0,"time":33157.8945,"type":0},{"id":3,"sLen":0,"time":33315.79,"type":0},{"id":0,"sLen":0,"time":33473.6836,"type":0},{"id":2,"sLen":0,"time":33631.58,"type":0},{"id":3,"sLen":0,"time":33789.4727,"type":0},{"id":1,"sLen":0,"time":33947.3672,"type":0},{"id":1,"sLen":0,"time":34105.26,"type":0},{"id":0,"sLen":0,"time":34263.1563,"type":0},{"id":1,"sLen":0,"time":34421.05,"type":0},{"id":3,"sLen":0,"time":34578.9453,"type":0},{"id":1,"sLen":0,"time":34736.84,"type":0},{"id":0,"sLen":0,"time":34894.7344,"type":0},{"id":1,"sLen":236.842100000002,"time":35052.63,"type":0},{"id":0,"sLen":0,"time":40421.05,"type":0},{"id":1,"sLen":0,"time":40578.9453,"type":0},{"id":0,"sLen":0,"time":40736.84,"type":0},{"id":2,"sLen":0,"time":40894.7344,"type":0},{"id":3,"sLen":0,"time":41052.63,"type":0},{"id":1,"sLen":0,"time":41210.5234,"type":0},{"id":0,"sLen":0,"time":41368.418,"type":0},{"id":3,"sLen":0,"time":41526.3125,"type":0},{"id":2,"sLen":0,"time":41684.207,"type":0},{"id":0,"sLen":0,"time":41842.1,"type":0},{"id":1,"sLen":0,"time":42000,"type":0},{"id":0,"sLen":0,"time":42157.8945,"type":0},{"id":2,"sLen":0,"time":42315.79,"type":0},{"id":3,"sLen":0,"time":42473.6836,"type":0},{"id":0,"sLen":0,"time":42631.58,"type":0},{"id":3,"sLen":0,"time":42789.4727,"type":0},{"id":0,"sLen":0,"time":42947.3672,"type":0},{"id":1,"sLen":0,"time":43105.26,"type":0},{"id":0,"sLen":0,"time":43263.1563,"type":0},{"id":2,"sLen":0,"time":43421.05,"type":0},{"id":3,"sLen":0,"time":43578.9453,"type":0},{"id":1,"sLen":0,"time":43736.84,"type":0},{"id":0,"sLen":0,"time":43894.7344,"type":0},{"id":3,"sLen":0,"time":44052.63,"type":0},{"id":2,"sLen":0,"time":44210.5234,"type":0},{"id":0,"sLen":0,"time":44368.418,"type":0},{"id":1,"sLen":0,"time":44526.3125,"type":0},{"id":0,"sLen":0,"time":44684.207,"type":0},{"id":2,"sLen":0,"time":44842.1,"type":0},{"id":0,"sLen":0,"time":44999.9961,"type":0},{"id":3,"sLen":0,"time":45157.8945,"type":0},{"id":0,"sLen":0,"time":45315.79,"type":0},{"id":3,"sLen":157.89473,"time":50526.3125,"type":0},{"id":3,"sLen":0,"time":50842.1,"type":0},{"id":3,"sLen":0,"time":50999.9961,"type":0},{"id":1,"sLen":157.89473,"time":51157.89,"type":0},{"id":0,"sLen":0,"time":51473.6836,"type":0},{"id":3,"sLen":0,"time":51789.4727,"type":0},{"id":1,"sLen":0,"time":52105.26,"type":0},{"id":3,"sLen":0,"time":52263.1563,"type":0},{"id":1,"sLen":0,"time":52578.9453,"type":0},{"id":3,"sLen":0,"time":52736.84,"type":0},{"id":3,"sLen":0,"time":53052.63,"type":0},{"id":3,"sLen":0,"time":53210.5234,"type":0},{"id":3,"sLen":0,"time":53368.418,"type":0},{"id":3,"sLen":0,"time":53526.3125,"type":0},{"id":1,"sLen":0,"time":53684.207,"type":0},{"id":1,"sLen":0,"time":53842.1,"type":0},{"id":0,"sLen":0,"time":53999.9961,"type":0},{"id":0,"sLen":0,"time":54157.89,"type":0},{"id":3,"sLen":0,"time":54315.7852,"type":0},{"id":1,"sLen":0,"time":54631.58,"type":0},{"id":3,"sLen":0,"time":54789.4727,"type":0},{"id":1,"sLen":0,"time":55105.26,"type":0},{"id":3,"sLen":0,"time":55263.1563,"type":0},{"id":2,"sLen":0,"time":60631.58,"type":0},{"id":0,"sLen":0,"time":60789.4727,"type":0},{"id":3,"sLen":0,"time":60947.3672,"type":0},{"id":1,"sLen":0,"time":61105.26,"type":0},{"id":3,"sLen":0,"time":61263.1563,"type":0},{"id":3,"sLen":0,"time":61421.05,"type":0},{"id":0,"sLen":0,"time":61578.9453,"type":0},{"id":3,"sLen":0,"time":61736.84,"type":0},{"id":0,"sLen":0,"time":61894.7344,"type":0},{"id":2,"sLen":0,"time":62052.63,"type":0},{"id":0,"sLen":0,"time":62210.5234,"type":0},{"id":1,"sLen":0,"time":62368.418,"type":0},{"id":3,"sLen":0,"time":62526.3125,"type":0},{"id":3,"sLen":0,"time":62684.207,"type":0},{"id":1,"sLen":0,"time":62842.1,"type":0},{"id":3,"sLen":0,"time":62999.9961,"type":0},{"id":0,"sLen":0,"time":63157.89,"type":0},{"id":2,"sLen":0,"time":63315.7852,"type":0},{"id":0,"sLen":0,"time":63473.68,"type":0},{"id":3,"sLen":0,"time":63631.5742,"type":0},{"id":0,"sLen":0,"time":63789.4727,"type":0},{"id":2,"sLen":0,"time":63947.3672,"type":0},{"id":3,"sLen":0,"time":64105.26,"type":0},{"id":1,"sLen":0,"time":64263.1563,"type":0},{"id":1,"sLen":0,"time":64421.05,"type":0},{"id":0,"sLen":0,"time":64578.9453,"type":0},{"id":1,"sLen":0,"time":64736.84,"type":0},{"id":3,"sLen":0,"time":64894.7344,"type":0},{"id":1,"sLen":0,"time":65052.63,"type":0},{"id":0,"sLen":0,"time":65210.5234,"type":0},{"id":1,"sLen":236.842100000002,"time":65368.418,"type":0},{"id":0,"sLen":0,"time":70736.8359,"type":0},{"id":1,"sLen":0,"time":70894.7344,"type":0},{"id":0,"sLen":0,"time":71052.625,"type":0},{"id":2,"sLen":0,"time":71210.52,"type":0},{"id":3,"sLen":0,"time":71368.4141,"type":0},{"id":1,"sLen":0,"time":71526.31,"type":0},{"id":0,"sLen":0,"time":71684.21,"type":0},{"id":3,"sLen":0,"time":71842.1,"type":0},{"id":2,"sLen":0,"time":72000,"type":0},{"id":0,"sLen":0,"time":72157.89,"type":0},{"id":1,"sLen":0,"time":72315.79,"type":0},{"id":0,"sLen":0,"time":72473.68,"type":0},{"id":2,"sLen":0,"time":72631.58,"type":0},{"id":3,"sLen":0,"time":72789.47,"type":0},{"id":0,"sLen":0,"time":72947.37,"type":0},{"id":3,"sLen":0,"time":73105.26,"type":0},{"id":0,"sLen":0,"time":73263.16,"type":0},{"id":1,"sLen":0,"time":73421.05,"type":0},{"id":0,"sLen":0,"time":73578.9453,"type":0},{"id":2,"sLen":0,"time":73736.8359,"type":0},{"id":3,"sLen":0,"time":73894.7344,"type":0},{"id":1,"sLen":0,"time":74052.625,"type":0},{"id":0,"sLen":0,"time":74210.52,"type":0},{"id":3,"sLen":0,"time":74368.4141,"type":0},{"id":2,"sLen":0,"time":74526.31,"type":0},{"id":0,"sLen":0,"time":74684.21,"type":0},{"id":1,"sLen":0,"time":74842.1,"type":0},{"id":0,"sLen":0,"time":75000,"type":0},{"id":2,"sLen":0,"time":75157.89,"type":0},{"id":0,"sLen":0,"time":75315.79,"type":0},{"id":3,"sLen":0,"time":75473.68,"type":0},{"id":0,"sLen":0,"time":75631.58,"type":0},{"id":2,"sLen":0,"time":80842.1,"type":0},{"id":3,"sLen":0,"time":81000,"type":0},{"id":1,"sLen":0,"time":81157.89,"type":0},{"id":3,"sLen":0,"time":81315.79,"type":0},{"id":0,"sLen":0,"time":81473.68,"type":0},{"id":3,"sLen":0,"time":81631.58,"type":0},{"id":1,"sLen":0,"time":81789.47,"type":0},{"id":1,"sLen":0,"time":81947.37,"type":0},{"id":2,"sLen":0,"time":82105.26,"type":0},{"id":3,"sLen":0,"time":82263.16,"type":0},{"id":1,"sLen":0,"time":82421.05,"type":0},{"id":3,"sLen":0,"time":82578.9453,"type":0},{"id":0,"sLen":0,"time":82736.8359,"type":0},{"id":3,"sLen":0,"time":82894.7344,"type":0},{"id":1,"sLen":0,"time":83052.625,"type":0},{"id":1,"sLen":0,"time":83210.52,"type":0},{"id":2,"sLen":0,"time":83368.4141,"type":0},{"id":3,"sLen":0,"time":83526.31,"type":0},{"id":1,"sLen":0,"time":83684.2,"type":0},{"id":3,"sLen":0,"time":83842.1,"type":0},{"id":0,"sLen":0,"time":84000,"type":0},{"id":3,"sLen":0,"time":84157.89,"type":0},{"id":1,"sLen":0,"time":84315.79,"type":0},{"id":1,"sLen":0,"time":84473.68,"type":0},{"id":2,"sLen":0,"time":84631.58,"type":0},{"id":3,"sLen":0,"time":84789.47,"type":0},{"id":1,"sLen":0,"time":84947.37,"type":0},{"id":3,"sLen":0,"time":85105.26,"type":0},{"id":0,"sLen":0,"time":85263.16,"type":0},{"id":3,"sLen":0,"time":85421.05,"type":0},{"id":1,"sLen":0,"time":85578.9453,"type":0},{"id":1,"sLen":0,"time":85736.8359,"type":0},{"id":3,"sLen":157.89473,"time":90947.37,"type":0},{"id":3,"sLen":0,"time":91263.16,"type":0},{"id":3,"sLen":0,"time":91421.05,"type":0},{"id":1,"sLen":157.89473,"time":91578.9453,"type":0},{"id":0,"sLen":0,"time":91894.7344,"type":0},{"id":3,"sLen":0,"time":92210.52,"type":0},{"id":1,"sLen":0,"time":92526.31,"type":0},{"id":3,"sLen":0,"time":92684.2,"type":0},{"id":1,"sLen":0,"time":92999.99,"type":0},{"id":3,"sLen":0,"time":93157.89,"type":0},{"id":3,"sLen":0,"time":93473.68,"type":0},{"id":3,"sLen":0,"time":93631.58,"type":0},{"id":3,"sLen":0,"time":93789.47,"type":0},{"id":3,"sLen":0,"time":93947.37,"type":0},{"id":1,"sLen":0,"time":94105.26,"type":0},{"id":1,"sLen":0,"time":94263.16,"type":0},{"id":0,"sLen":0,"time":94421.05,"type":0},{"id":0,"sLen":0,"time":94578.9453,"type":0},{"id":3,"sLen":0,"time":94736.8359,"type":0},{"id":1,"sLen":0,"time":95052.625,"type":0},{"id":3,"sLen":0,"time":95210.52,"type":0},{"id":1,"sLen":0,"time":95526.31,"type":0},{"id":3,"sLen":0,"time":95684.2,"type":0}],"position":"dad","type":0,"characters":["spirit"],"visible":true},{"keyCount":4,"notes":[{"id":3,"sLen":157.89473,"time":5052.63135,"type":0},{"id":3,"sLen":0,"time":5368.421,"type":0},{"id":3,"sLen":0,"time":5526.31543,"type":0},{"id":1,"sLen":157.89473,"time":5684.21045,"type":0},{"id":0,"sLen":0,"time":5999.99951,"type":0},{"id":3,"sLen":0,"time":6315.789,"type":0},{"id":1,"sLen":0,"time":6631.57861,"type":0},{"id":3,"sLen":0,"time":6789.473,"type":0},{"id":1,"sLen":0,"time":7105.2627,"type":0},{"id":3,"sLen":0,"time":7263.15771,"type":0},{"id":3,"sLen":0,"time":7578.94727,"type":0},{"id":3,"sLen":0,"time":7736.842,"type":0},{"id":3,"sLen":0,"time":7894.73633,"type":0},{"id":3,"sLen":0,"time":8052.63135,"type":0},{"id":1,"sLen":0,"time":8210.526,"type":0},{"id":1,"sLen":0,"time":8368.421,"type":0},{"id":0,"sLen":0,"time":8526.315,"type":0},{"id":0,"sLen":0,"time":8684.21,"type":0},{"id":3,"sLen":0,"time":8842.10449,"type":0},{"id":1,"sLen":0,"time":9157.895,"type":0},{"id":3,"sLen":0,"time":9315.789,"type":0},{"id":1,"sLen":0,"time":9631.578,"type":0},{"id":3,"sLen":0,"time":9789.474,"type":0},{"id":2,"sLen":157.89473,"time":15157.8945,"type":0},{"id":1,"sLen":78.9473648000003,"time":15473.6836,"type":0},{"id":2,"sLen":0,"time":15789.4727,"type":0},{"id":3,"sLen":0,"time":15947.3682,"type":0},{"id":1,"sLen":78.9473648000003,"time":16105.2627,"type":0},{"id":2,"sLen":157.89473,"time":16421.0527,"type":0},{"id":1,"sLen":0,"time":16736.8418,"type":0},{"id":2,"sLen":157.89473,"time":16894.7363,"type":0},{"id":1,"sLen":0,"time":17210.5254,"type":0},{"id":2,"sLen":157.89473,"time":17368.42,"type":0},{"id":2,"sLen":157.89473,"time":17684.209,"type":0},{"id":1,"sLen":78.9473647999985,"time":18000,"type":0},{"id":2,"sLen":0,"time":18315.79,"type":0},{"id":3,"sLen":0,"time":18473.6836,"type":0},{"id":1,"sLen":78.9473647999985,"time":18631.5781,"type":0},{"id":2,"sLen":157.89473,"time":18947.3672,"type":0},{"id":1,"sLen":0,"time":19263.1563,"type":0},{"id":2,"sLen":157.89473,"time":19421.05,"type":0},{"id":1,"sLen":0,"time":19736.8418,"type":0},{"id":0,"sLen":157.89473,"time":19894.7363,"type":0},{"id":3,"sLen":0,"time":24789.4727,"type":0},{"id":2,"sLen":0,"time":24947.3672,"type":0},{"id":0,"sLen":0,"time":25105.2617,"type":0},{"id":1,"sLen":0,"time":25263.1563,"type":0},{"id":0,"sLen":0,"time":25421.05,"type":0},{"id":3,"sLen":0,"time":25578.9453,"type":0},{"id":1,"sLen":0,"time":25736.8418,"type":0},{"id":0,"sLen":0,"time":25894.7363,"type":0},{"id":3,"sLen":0,"time":26052.63,"type":0},{"id":1,"sLen":0,"time":26210.5254,"type":0},{"id":0,"sLen":0,"time":26368.42,"type":0},{"id":2,"sLen":0,"time":26526.3145,"type":0},{"id":1,"sLen":0,"time":26684.209,"type":0},{"id":0,"sLen":0,"time":26842.1035,"type":0},{"id":2,"sLen":0,"time":26999.998,"type":0},{"id":1,"sLen":0,"time":27157.8926,"type":0},{"id":0,"sLen":0,"time":27315.79,"type":0},{"id":3,"sLen":0,"time":27473.6836,"type":0},{"id":1,"sLen":0,"time":27631.5781,"type":0},{"id":1,"sLen":0,"time":27789.4727,"type":0},{"id":0,"sLen":0,"time":27947.3672,"type":0},{"id":3,"sLen":0,"time":28105.2617,"type":0},{"id":1,"sLen":0,"time":28263.1563,"type":0},{"id":0,"sLen":0,"time":28421.05,"type":0},{"id":3,"sLen":0,"time":28578.9453,"type":0},{"id":1,"sLen":0,"time":28736.84,"type":0},{"id":0,"sLen":0,"time":28894.7363,"type":0},{"id":2,"sLen":0,"time":29052.63,"type":0},{"id":1,"sLen":0,"time":29210.5254,"type":0},{"id":0,"sLen":0,"time":29368.42,"type":0},{"id":2,"sLen":0,"time":29526.3145,"type":0},{"id":1,"sLen":0,"time":29684.209,"type":0},{"id":0,"sLen":0,"time":29842.1035,"type":0},{"id":3,"sLen":0,"time":29999.998,"type":0},{"id":1,"sLen":0,"time":30157.8926,"type":0},{"id":2,"sLen":0,"time":35368.418,"type":0},{"id":0,"sLen":0,"time":35526.3125,"type":0},{"id":3,"sLen":0,"time":35684.207,"type":0},{"id":1,"sLen":0,"time":35842.1055,"type":0},{"id":3,"sLen":236.842100000002,"time":36000,"type":0},{"id":0,"sLen":0,"time":36315.79,"type":0},{"id":3,"sLen":0,"time":36473.6836,"type":0},{"id":0,"sLen":0,"time":36631.58,"type":0},{"id":2,"sLen":0,"time":36789.4727,"type":0},{"id":0,"sLen":0,"time":36947.3672,"type":0},{"id":1,"sLen":0,"time":37105.26,"type":0},{"id":3,"sLen":236.842100000002,"time":37263.1563,"type":0},{"id":1,"sLen":0,"time":37578.9453,"type":0},{"id":3,"sLen":0,"time":37736.84,"type":0},{"id":0,"sLen":0,"time":37894.7344,"type":0},{"id":2,"sLen":0,"time":38052.63,"type":0},{"id":0,"sLen":0,"time":38210.5234,"type":0},{"id":3,"sLen":0,"time":38368.418,"type":0},{"id":0,"sLen":0,"time":38526.3125,"type":0},{"id":2,"sLen":0,"time":38684.207,"type":0},{"id":3,"sLen":0,"time":38842.1,"type":0},{"id":1,"sLen":0,"time":39000,"type":0},{"id":1,"sLen":0,"time":39157.8945,"type":0},{"id":0,"sLen":0,"time":39315.79,"type":0},{"id":1,"sLen":0,"time":39473.6836,"type":0},{"id":3,"sLen":0,"time":39631.58,"type":0},{"id":1,"sLen":0,"time":39789.4727,"type":0},{"id":0,"sLen":0,"time":39947.3672,"type":0},{"id":1,"sLen":236.842100000002,"time":40105.26,"type":0},{"id":0,"sLen":0,"time":45473.6836,"type":0},{"id":1,"sLen":0,"time":45631.58,"type":0},{"id":0,"sLen":0,"time":45789.4727,"type":0},{"id":2,"sLen":0,"time":45947.3672,"type":0},{"id":3,"sLen":0,"time":46105.26,"type":0},{"id":1,"sLen":0,"time":46263.1563,"type":0},{"id":0,"sLen":0,"time":46421.05,"type":0},{"id":3,"sLen":0,"time":46578.9453,"type":0},{"id":2,"sLen":0,"time":46736.84,"type":0},{"id":0,"sLen":0,"time":46894.7344,"type":0},{"id":1,"sLen":0,"time":47052.63,"type":0},{"id":0,"sLen":0,"time":47210.5234,"type":0},{"id":2,"sLen":0,"time":47368.418,"type":0},{"id":3,"sLen":0,"time":47526.3125,"type":0},{"id":0,"sLen":0,"time":47684.207,"type":0},{"id":3,"sLen":0,"time":47842.1,"type":0},{"id":0,"sLen":0,"time":47999.9961,"type":0},{"id":1,"sLen":0,"time":48157.89,"type":0},{"id":0,"sLen":0,"time":48315.79,"type":0},{"id":2,"sLen":0,"time":48473.6836,"type":0},{"id":3,"sLen":0,"time":48631.58,"type":0},{"id":1,"sLen":0,"time":48789.4727,"type":0},{"id":0,"sLen":0,"time":48947.3672,"type":0},{"id":3,"sLen":0,"time":49105.26,"type":0},{"id":2,"sLen":0,"time":49263.1563,"type":0},{"id":0,"sLen":0,"time":49421.05,"type":0},{"id":1,"sLen":0,"time":49578.9453,"type":0},{"id":0,"sLen":0,"time":49736.84,"type":0},{"id":2,"sLen":0,"time":49894.7344,"type":0},{"id":0,"sLen":0,"time":50052.63,"type":0},{"id":3,"sLen":0,"time":50210.5234,"type":0},{"id":0,"sLen":0,"time":50368.418,"type":0},{"id":3,"sLen":157.89473,"time":55578.9453,"type":0},{"id":3,"sLen":0,"time":55894.7344,"type":0},{"id":3,"sLen":0,"time":56052.63,"type":0},{"id":1,"sLen":157.89473,"time":56210.5234,"type":0},{"id":0,"sLen":0,"time":56526.3125,"type":0},{"id":3,"sLen":0,"time":56842.1,"type":0},{"id":1,"sLen":0,"time":57157.89,"type":0},{"id":3,"sLen":0,"time":57315.7852,"type":0},{"id":1,"sLen":0,"time":57631.58,"type":0},{"id":3,"sLen":0,"time":57789.4727,"type":0},{"id":3,"sLen":0,"time":58105.26,"type":0},{"id":3,"sLen":0,"time":58263.1563,"type":0},{"id":3,"sLen":0,"time":58421.05,"type":0},{"id":3,"sLen":0,"time":58578.9453,"type":0},{"id":1,"sLen":0,"time":58736.84,"type":0},{"id":1,"sLen":0,"time":58894.7344,"type":0},{"id":0,"sLen":0,"time":59052.63,"type":0},{"id":0,"sLen":0,"time":59210.5234,"type":0},{"id":3,"sLen":0,"time":59368.418,"type":0},{"id":1,"sLen":0,"time":59684.207,"type":0},{"id":3,"sLen":0,"time":59842.1,"type":0},{"id":1,"sLen":0,"time":60157.89,"type":0},{"id":3,"sLen":0,"time":60315.7852,"type":0},{"id":2,"sLen":0,"time":65684.21,"type":0},{"id":0,"sLen":0,"time":65842.1,"type":0},{"id":3,"sLen":0,"time":66000,"type":0},{"id":1,"sLen":0,"time":66157.89,"type":0},{"id":3,"sLen":0,"time":66315.79,"type":0},{"id":3,"sLen":0,"time":66473.68,"type":0},{"id":0,"sLen":0,"time":66631.58,"type":0},{"id":3,"sLen":0,"time":66789.47,"type":0},{"id":0,"sLen":0,"time":66947.37,"type":0},{"id":2,"sLen":0,"time":67105.26,"type":0},{"id":0,"sLen":0,"time":67263.16,"type":0},{"id":1,"sLen":0,"time":67421.05,"type":0},{"id":3,"sLen":0,"time":67578.9453,"type":0},{"id":3,"sLen":0,"time":67736.8359,"type":0},{"id":1,"sLen":0,"time":67894.7344,"type":0},{"id":3,"sLen":0,"time":68052.625,"type":0},{"id":0,"sLen":0,"time":68210.52,"type":0},{"id":2,"sLen":0,"time":68368.4141,"type":0},{"id":0,"sLen":0,"time":68526.31,"type":0},{"id":3,"sLen":0,"time":68684.21,"type":0},{"id":0,"sLen":0,"time":68842.1,"type":0},{"id":2,"sLen":0,"time":69000,"type":0},{"id":3,"sLen":0,"time":69157.89,"type":0},{"id":1,"sLen":0,"time":69315.79,"type":0},{"id":1,"sLen":0,"time":69473.68,"type":0},{"id":0,"sLen":0,"time":69631.58,"type":0},{"id":1,"sLen":0,"time":69789.47,"type":0},{"id":3,"sLen":0,"time":69947.37,"type":0},{"id":1,"sLen":0,"time":70105.26,"type":0},{"id":0,"sLen":0,"time":70263.16,"type":0},{"id":1,"sLen":236.842099999994,"time":70421.05,"type":0},{"id":0,"sLen":0,"time":75789.47,"type":0},{"id":1,"sLen":0,"time":75947.37,"type":0},{"id":0,"sLen":0,"time":76105.26,"type":0},{"id":2,"sLen":0,"time":76263.16,"type":0},{"id":3,"sLen":0,"time":76421.05,"type":0},{"id":1,"sLen":0,"time":76578.9453,"type":0},{"id":0,"sLen":0,"time":76736.8359,"type":0},{"id":3,"sLen":0,"time":76894.7344,"type":0},{"id":2,"sLen":0,"time":77052.625,"type":0},{"id":0,"sLen":0,"time":77210.52,"type":0},{"id":1,"sLen":0,"time":77368.4141,"type":0},{"id":0,"sLen":0,"time":77526.31,"type":0},{"id":2,"sLen":0,"time":77684.2,"type":0},{"id":3,"sLen":0,"time":77842.1,"type":0},{"id":0,"sLen":0,"time":78000,"type":0},{"id":3,"sLen":0,"time":78157.89,"type":0},{"id":0,"sLen":0,"time":78315.79,"type":0},{"id":1,"sLen":0,"time":78473.68,"type":0},{"id":0,"sLen":0,"time":78631.58,"type":0},{"id":2,"sLen":0,"time":78789.47,"type":0},{"id":3,"sLen":0,"time":78947.37,"type":0},{"id":1,"sLen":0,"time":79105.26,"type":0},{"id":0,"sLen":0,"time":79263.16,"type":0},{"id":3,"sLen":0,"time":79421.05,"type":0},{"id":2,"sLen":0,"time":79578.9453,"type":0},{"id":0,"sLen":0,"time":79736.8359,"type":0},{"id":1,"sLen":0,"time":79894.7344,"type":0},{"id":0,"sLen":0,"time":80052.625,"type":0},{"id":2,"sLen":0,"time":80210.52,"type":0},{"id":0,"sLen":0,"time":80368.4141,"type":0},{"id":3,"sLen":0,"time":80526.31,"type":0},{"id":0,"sLen":0,"time":80684.2,"type":0},{"id":2,"sLen":0,"time":85894.7344,"type":0},{"id":3,"sLen":0,"time":86052.625,"type":0},{"id":1,"sLen":0,"time":86210.52,"type":0},{"id":3,"sLen":0,"time":86368.4141,"type":0},{"id":0,"sLen":0,"time":86526.31,"type":0},{"id":3,"sLen":0,"time":86684.2,"type":0},{"id":1,"sLen":0,"time":86842.1,"type":0},{"id":1,"sLen":0,"time":86999.99,"type":0},{"id":2,"sLen":0,"time":87157.89,"type":0},{"id":3,"sLen":0,"time":87315.79,"type":0},{"id":1,"sLen":0,"time":87473.68,"type":0},{"id":3,"sLen":0,"time":87631.58,"type":0},{"id":0,"sLen":0,"time":87789.47,"type":0},{"id":3,"sLen":0,"time":87947.37,"type":0},{"id":1,"sLen":0,"time":88105.26,"type":0},{"id":1,"sLen":0,"time":88263.16,"type":0},{"id":2,"sLen":0,"time":88421.05,"type":0},{"id":3,"sLen":0,"time":88578.9453,"type":0},{"id":1,"sLen":0,"time":88736.8359,"type":0},{"id":3,"sLen":0,"time":88894.7344,"type":0},{"id":0,"sLen":0,"time":89052.625,"type":0},{"id":3,"sLen":0,"time":89210.52,"type":0},{"id":1,"sLen":0,"time":89368.4141,"type":0},{"id":1,"sLen":0,"time":89526.31,"type":0},{"id":2,"sLen":0,"time":89684.2,"type":0},{"id":3,"sLen":0,"time":89842.1,"type":0},{"id":1,"sLen":0,"time":89999.99,"type":0},{"id":3,"sLen":0,"time":90157.89,"type":0},{"id":0,"sLen":0,"time":90315.79,"type":0},{"id":3,"sLen":0,"time":90473.68,"type":0},{"id":1,"sLen":0,"time":90631.58,"type":0},{"id":1,"sLen":0,"time":90789.47,"type":0},{"id":3,"sLen":157.89473,"time":95999.99,"type":0},{"id":3,"sLen":0,"time":96315.78,"type":0},{"id":3,"sLen":0,"time":96473.68,"type":0},{"id":1,"sLen":157.89473,"time":96631.58,"type":0},{"id":0,"sLen":0,"time":96947.37,"type":0},{"id":3,"sLen":0,"time":97263.16,"type":0},{"id":1,"sLen":0,"time":97578.9453,"type":0},{"id":3,"sLen":0,"time":97736.8359,"type":0},{"id":1,"sLen":0,"time":98052.625,"type":0},{"id":3,"sLen":0,"time":98210.52,"type":0},{"id":3,"sLen":0,"time":98526.31,"type":0},{"id":3,"sLen":0,"time":98684.2,"type":0},{"id":3,"sLen":0,"time":98842.1,"type":0},{"id":3,"sLen":0,"time":98999.99,"type":0},{"id":1,"sLen":0,"time":99157.89,"type":0},{"id":1,"sLen":0,"time":99315.78,"type":0},{"id":0,"sLen":0,"time":99473.68,"type":0},{"id":0,"sLen":0,"time":99631.58,"type":0},{"id":3,"sLen":0,"time":99789.47,"type":0},{"id":1,"sLen":0,"time":100105.258,"type":0},{"id":3,"sLen":0,"time":100263.156,"type":0},{"id":1,"sLen":0,"time":100578.945,"type":0},{"id":3,"sLen":0,"time":100736.836,"type":0}],"position":"boyfriend","type":1,"characters":["bf-pixel"],"visible":true},{"keyCount":4,"notes":[],"position":"girlfriend","type":2,"characters":["gf-pixel"],"visible":false}],"scrollSpeed":1.5,"chartVersion":"1.6.0","codenameChart":true,"noteTypes":[]} \ No newline at end of file diff --git a/assets/songs/thorns/charts/normal.json b/assets/songs/thorns/charts/normal.json index f8c2234d..08e32b1c 100644 --- a/assets/songs/thorns/charts/normal.json +++ b/assets/songs/thorns/charts/normal.json @@ -1 +1 @@ -{"codenameChart":true,"strumLines":[{"position":"dad","notes":[{"id":3,"sLen":157.89473,"time":0,"type":0},{"id":1,"sLen":157.89473,"time":631.5789,"type":0},{"id":0,"sLen":0,"time":947.3684,"type":0},{"id":3,"sLen":0,"time":1263.15784,"type":0},{"id":1,"sLen":0,"time":1578.94727,"type":0},{"id":3,"sLen":0,"time":1736.842,"type":0},{"id":1,"sLen":0,"time":2052.63159,"type":0},{"id":3,"sLen":0,"time":2210.52612,"type":0},{"id":3,"sLen":0,"time":2526.31567,"type":0},{"id":3,"sLen":0,"time":2842.10522,"type":0},{"id":1,"sLen":0,"time":3157.89453,"type":0},{"id":0,"sLen":0,"time":3473.684,"type":0},{"id":3,"sLen":0,"time":3789.47363,"type":0},{"id":1,"sLen":0,"time":4105.263,"type":0},{"id":3,"sLen":0,"time":4263.15771,"type":0},{"id":1,"sLen":0,"time":4578.94727,"type":0},{"id":3,"sLen":0,"time":4736.842,"type":0},{"id":2,"sLen":157.894729999998,"time":10105.2627,"type":0},{"id":2,"sLen":0,"time":10736.8418,"type":0},{"id":3,"sLen":0,"time":10894.7363,"type":0},{"id":1,"sLen":78.9473647999985,"time":11052.6309,"type":0},{"id":2,"sLen":157.89473,"time":11368.4209,"type":0},{"id":2,"sLen":157.89473,"time":11842.1045,"type":0},{"id":2,"sLen":157.89473,"time":12315.7891,"type":0},{"id":2,"sLen":157.89473,"time":12631.5781,"type":0},{"id":2,"sLen":0,"time":13263.1572,"type":0},{"id":3,"sLen":0,"time":13421.0518,"type":0},{"id":1,"sLen":78.9473648000003,"time":13578.9463,"type":0},{"id":2,"sLen":157.89473,"time":13894.7363,"type":0},{"id":2,"sLen":157.89473,"time":14368.42,"type":0},{"id":0,"sLen":157.89473,"time":14842.1045,"type":0},{"id":1,"sLen":0,"time":20210.5254,"type":0},{"id":3,"sLen":0,"time":20526.3145,"type":0},{"id":1,"sLen":0,"time":20684.209,"type":0},{"id":3,"sLen":0,"time":21000,"type":0},{"id":1,"sLen":0,"time":21157.8945,"type":0},{"id":2,"sLen":0,"time":21473.6836,"type":0},{"id":1,"sLen":0,"time":21631.5781,"type":0},{"id":2,"sLen":0,"time":21947.3672,"type":0},{"id":1,"sLen":0,"time":22105.2617,"type":0},{"id":3,"sLen":0,"time":22421.05,"type":0},{"id":1,"sLen":0,"time":22578.9473,"type":0},{"id":1,"sLen":0,"time":22736.8418,"type":0},{"id":3,"sLen":0,"time":23052.63,"type":0},{"id":1,"sLen":0,"time":23210.5254,"type":0},{"id":3,"sLen":0,"time":23526.3145,"type":0},{"id":1,"sLen":0,"time":23684.209,"type":0},{"id":2,"sLen":0,"time":23999.998,"type":0},{"id":1,"sLen":0,"time":24157.8945,"type":0},{"id":2,"sLen":0,"time":24473.6836,"type":0},{"id":1,"sLen":0,"time":24631.5781,"type":0},{"id":3,"sLen":0,"time":24947.3672,"type":0},{"id":1,"sLen":0,"time":25105.2617,"type":0},{"id":2,"sLen":0,"time":30315.79,"type":0},{"id":3,"sLen":0,"time":30631.5781,"type":0},{"id":1,"sLen":0,"time":30789.4727,"type":0},{"id":3,"sLen":0,"time":30947.3672,"type":0},{"id":0,"sLen":0,"time":31263.1563,"type":0},{"id":3,"sLen":0,"time":31421.05,"type":0},{"id":0,"sLen":0,"time":31578.9453,"type":0},{"id":0,"sLen":0,"time":31894.7363,"type":0},{"id":1,"sLen":0,"time":32052.63,"type":0},{"id":3,"sLen":0,"time":32210.5254,"type":0},{"id":1,"sLen":0,"time":32526.3145,"type":0},{"id":3,"sLen":0,"time":32684.209,"type":0},{"id":0,"sLen":0,"time":32842.1055,"type":0},{"id":0,"sLen":0,"time":33157.8945,"type":0},{"id":3,"sLen":0,"time":33315.79,"type":0},{"id":0,"sLen":0,"time":33473.6836,"type":0},{"id":3,"sLen":0,"time":33789.4727,"type":0},{"id":1,"sLen":0,"time":33947.3672,"type":0},{"id":1,"sLen":0,"time":34105.26,"type":0},{"id":1,"sLen":0,"time":34421.05,"type":0},{"id":3,"sLen":0,"time":34578.9453,"type":0},{"id":1,"sLen":0,"time":34736.84,"type":0},{"id":1,"sLen":236.842100000002,"time":35052.63,"type":0},{"id":0,"sLen":0,"time":40421.05,"type":0},{"id":0,"sLen":0,"time":40736.84,"type":0},{"id":3,"sLen":0,"time":41052.63,"type":0},{"id":1,"sLen":0,"time":41210.5234,"type":0},{"id":0,"sLen":0,"time":41368.418,"type":0},{"id":3,"sLen":0,"time":41526.3125,"type":0},{"id":2,"sLen":0,"time":41684.207,"type":0},{"id":1,"sLen":0,"time":42000,"type":0},{"id":2,"sLen":0,"time":42315.79,"type":0},{"id":3,"sLen":0,"time":42473.6836,"type":0},{"id":0,"sLen":0,"time":42631.58,"type":0},{"id":3,"sLen":0,"time":42789.4727,"type":0},{"id":0,"sLen":0,"time":42947.3672,"type":0},{"id":0,"sLen":0,"time":43263.1563,"type":0},{"id":3,"sLen":0,"time":43578.9453,"type":0},{"id":1,"sLen":0,"time":43736.84,"type":0},{"id":0,"sLen":0,"time":43894.7344,"type":0},{"id":3,"sLen":0,"time":44052.63,"type":0},{"id":2,"sLen":0,"time":44210.5234,"type":0},{"id":1,"sLen":0,"time":44526.3125,"type":0},{"id":2,"sLen":0,"time":44842.1,"type":0},{"id":0,"sLen":0,"time":44999.9961,"type":0},{"id":3,"sLen":0,"time":45157.8945,"type":0},{"id":0,"sLen":0,"time":45315.79,"type":0},{"id":3,"sLen":157.89473,"time":50526.3125,"type":0},{"id":1,"sLen":157.89473,"time":51157.89,"type":0},{"id":0,"sLen":0,"time":51473.6836,"type":0},{"id":3,"sLen":0,"time":51789.4727,"type":0},{"id":1,"sLen":0,"time":52105.26,"type":0},{"id":3,"sLen":0,"time":52263.1563,"type":0},{"id":1,"sLen":0,"time":52578.9453,"type":0},{"id":3,"sLen":0,"time":52736.84,"type":0},{"id":3,"sLen":0,"time":53052.63,"type":0},{"id":3,"sLen":0,"time":53368.418,"type":0},{"id":1,"sLen":0,"time":53684.207,"type":0},{"id":0,"sLen":0,"time":53999.9961,"type":0},{"id":3,"sLen":0,"time":54315.7852,"type":0},{"id":1,"sLen":0,"time":54631.58,"type":0},{"id":3,"sLen":0,"time":54789.4727,"type":0},{"id":1,"sLen":0,"time":55105.26,"type":0},{"id":3,"sLen":0,"time":55263.1563,"type":0},{"id":2,"sLen":0,"time":60631.58,"type":0},{"id":3,"sLen":0,"time":60947.3672,"type":0},{"id":1,"sLen":0,"time":61105.26,"type":0},{"id":3,"sLen":0,"time":61263.1563,"type":0},{"id":0,"sLen":0,"time":61578.9453,"type":0},{"id":3,"sLen":0,"time":61736.84,"type":0},{"id":0,"sLen":0,"time":61894.7344,"type":0},{"id":0,"sLen":0,"time":62210.5234,"type":0},{"id":1,"sLen":0,"time":62368.418,"type":0},{"id":3,"sLen":0,"time":62526.3125,"type":0},{"id":1,"sLen":0,"time":62842.1,"type":0},{"id":3,"sLen":0,"time":62999.9961,"type":0},{"id":0,"sLen":0,"time":63157.89,"type":0},{"id":0,"sLen":0,"time":63473.68,"type":0},{"id":3,"sLen":0,"time":63631.5742,"type":0},{"id":0,"sLen":0,"time":63789.4727,"type":0},{"id":3,"sLen":0,"time":64105.26,"type":0},{"id":1,"sLen":0,"time":64263.1563,"type":0},{"id":1,"sLen":0,"time":64421.05,"type":0},{"id":1,"sLen":0,"time":64736.84,"type":0},{"id":3,"sLen":0,"time":64894.7344,"type":0},{"id":1,"sLen":0,"time":65052.63,"type":0},{"id":1,"sLen":236.842100000002,"time":65368.418,"type":0},{"id":0,"sLen":0,"time":70736.8359,"type":0},{"id":0,"sLen":0,"time":71052.625,"type":0},{"id":3,"sLen":0,"time":71368.4141,"type":0},{"id":1,"sLen":0,"time":71526.31,"type":0},{"id":0,"sLen":0,"time":71684.21,"type":0},{"id":3,"sLen":0,"time":71842.1,"type":0},{"id":2,"sLen":0,"time":72000,"type":0},{"id":1,"sLen":0,"time":72315.79,"type":0},{"id":2,"sLen":0,"time":72631.58,"type":0},{"id":3,"sLen":0,"time":72789.47,"type":0},{"id":0,"sLen":0,"time":72947.37,"type":0},{"id":3,"sLen":0,"time":73105.26,"type":0},{"id":0,"sLen":0,"time":73263.16,"type":0},{"id":0,"sLen":0,"time":73578.9453,"type":0},{"id":3,"sLen":0,"time":73894.7344,"type":0},{"id":1,"sLen":0,"time":74052.625,"type":0},{"id":0,"sLen":0,"time":74210.52,"type":0},{"id":3,"sLen":0,"time":74368.4141,"type":0},{"id":2,"sLen":0,"time":74526.31,"type":0},{"id":1,"sLen":0,"time":74842.1,"type":0},{"id":2,"sLen":0,"time":75157.89,"type":0},{"id":0,"sLen":0,"time":75315.79,"type":0},{"id":3,"sLen":0,"time":75473.68,"type":0},{"id":0,"sLen":0,"time":75631.58,"type":0},{"id":2,"sLen":0,"time":80842.1,"type":0},{"id":1,"sLen":0,"time":81157.89,"type":0},{"id":0,"sLen":0,"time":81473.68,"type":0},{"id":3,"sLen":0,"time":81631.58,"type":0},{"id":1,"sLen":0,"time":81789.47,"type":0},{"id":2,"sLen":0,"time":82105.26,"type":0},{"id":1,"sLen":0,"time":82421.05,"type":0},{"id":0,"sLen":0,"time":82736.8359,"type":0},{"id":3,"sLen":0,"time":82894.7344,"type":0},{"id":1,"sLen":0,"time":83052.625,"type":0},{"id":2,"sLen":0,"time":83368.4141,"type":0},{"id":1,"sLen":0,"time":83684.2,"type":0},{"id":0,"sLen":0,"time":84000,"type":0},{"id":3,"sLen":0,"time":84157.89,"type":0},{"id":1,"sLen":0,"time":84315.79,"type":0},{"id":2,"sLen":0,"time":84631.58,"type":0},{"id":1,"sLen":0,"time":84947.37,"type":0},{"id":0,"sLen":0,"time":85263.16,"type":0},{"id":3,"sLen":0,"time":85421.05,"type":0},{"id":1,"sLen":0,"time":85578.9453,"type":0},{"id":3,"sLen":157.89473,"time":90947.37,"type":0},{"id":1,"sLen":157.89473,"time":91578.9453,"type":0},{"id":0,"sLen":0,"time":91894.7344,"type":0},{"id":3,"sLen":0,"time":92210.52,"type":0},{"id":1,"sLen":0,"time":92526.31,"type":0},{"id":3,"sLen":0,"time":92684.2,"type":0},{"id":1,"sLen":0,"time":92999.99,"type":0},{"id":3,"sLen":0,"time":93157.89,"type":0},{"id":3,"sLen":0,"time":93473.68,"type":0},{"id":3,"sLen":0,"time":93789.47,"type":0},{"id":1,"sLen":0,"time":94105.26,"type":0},{"id":0,"sLen":0,"time":94421.05,"type":0},{"id":3,"sLen":0,"time":94736.8359,"type":0},{"id":1,"sLen":0,"time":95052.625,"type":0},{"id":3,"sLen":0,"time":95210.52,"type":0},{"id":1,"sLen":0,"time":95526.31,"type":0},{"id":3,"sLen":0,"time":95684.2,"type":0}],"characters":["spirit"],"visible":true,"type":0},{"position":"boyfriend","notes":[{"id":3,"sLen":157.89473,"time":5052.63135,"type":0},{"id":1,"sLen":157.89473,"time":5684.21045,"type":0},{"id":0,"sLen":0,"time":5999.99951,"type":0},{"id":3,"sLen":0,"time":6315.789,"type":0},{"id":1,"sLen":0,"time":6631.57861,"type":0},{"id":3,"sLen":0,"time":6789.473,"type":0},{"id":1,"sLen":0,"time":7105.2627,"type":0},{"id":3,"sLen":0,"time":7263.15771,"type":0},{"id":3,"sLen":0,"time":7578.94727,"type":0},{"id":3,"sLen":0,"time":7894.73633,"type":0},{"id":1,"sLen":0,"time":8210.526,"type":0},{"id":0,"sLen":0,"time":8526.315,"type":0},{"id":3,"sLen":0,"time":8842.10449,"type":0},{"id":1,"sLen":0,"time":9157.895,"type":0},{"id":3,"sLen":0,"time":9315.789,"type":0},{"id":1,"sLen":0,"time":9631.578,"type":0},{"id":3,"sLen":0,"time":9789.474,"type":0},{"id":2,"sLen":157.89473,"time":15157.8945,"type":0},{"id":2,"sLen":0,"time":15789.4727,"type":0},{"id":3,"sLen":0,"time":15947.3682,"type":0},{"id":1,"sLen":78.9473648000003,"time":16105.2627,"type":0},{"id":2,"sLen":157.89473,"time":16421.0527,"type":0},{"id":2,"sLen":157.89473,"time":16894.7363,"type":0},{"id":2,"sLen":157.89473,"time":17368.42,"type":0},{"id":2,"sLen":157.89473,"time":17684.209,"type":0},{"id":2,"sLen":0,"time":18315.79,"type":0},{"id":3,"sLen":0,"time":18473.6836,"type":0},{"id":1,"sLen":78.9473647999985,"time":18631.5781,"type":0},{"id":2,"sLen":157.89473,"time":18947.3672,"type":0},{"id":2,"sLen":157.89473,"time":19421.05,"type":0},{"id":0,"sLen":157.89473,"time":19894.7363,"type":0},{"id":3,"sLen":0,"time":24789.4727,"type":0},{"id":2,"sLen":0,"time":24947.3672,"type":0},{"id":0,"sLen":0,"time":25105.2617,"type":0},{"id":1,"sLen":0,"time":25263.1563,"type":0},{"id":3,"sLen":0,"time":25578.9453,"type":0},{"id":1,"sLen":0,"time":25736.8418,"type":0},{"id":3,"sLen":0,"time":26052.63,"type":0},{"id":1,"sLen":0,"time":26210.5254,"type":0},{"id":2,"sLen":0,"time":26526.3145,"type":0},{"id":1,"sLen":0,"time":26684.209,"type":0},{"id":2,"sLen":0,"time":26999.998,"type":0},{"id":1,"sLen":0,"time":27157.8926,"type":0},{"id":3,"sLen":0,"time":27473.6836,"type":0},{"id":1,"sLen":0,"time":27631.5781,"type":0},{"id":1,"sLen":0,"time":27789.4727,"type":0},{"id":3,"sLen":0,"time":28105.2617,"type":0},{"id":1,"sLen":0,"time":28263.1563,"type":0},{"id":3,"sLen":0,"time":28578.9453,"type":0},{"id":1,"sLen":0,"time":28736.84,"type":0},{"id":2,"sLen":0,"time":29052.63,"type":0},{"id":1,"sLen":0,"time":29210.5254,"type":0},{"id":2,"sLen":0,"time":29526.3145,"type":0},{"id":1,"sLen":0,"time":29684.209,"type":0},{"id":3,"sLen":0,"time":29999.998,"type":0},{"id":1,"sLen":0,"time":30157.8926,"type":0},{"id":2,"sLen":0,"time":35368.418,"type":0},{"id":3,"sLen":0,"time":35684.207,"type":0},{"id":1,"sLen":0,"time":35842.1055,"type":0},{"id":3,"sLen":0,"time":36000,"type":0},{"id":0,"sLen":0,"time":36315.79,"type":0},{"id":3,"sLen":0,"time":36473.6836,"type":0},{"id":0,"sLen":0,"time":36631.58,"type":0},{"id":0,"sLen":0,"time":36947.3672,"type":0},{"id":1,"sLen":0,"time":37105.26,"type":0},{"id":3,"sLen":0,"time":37263.1563,"type":0},{"id":1,"sLen":0,"time":37578.9453,"type":0},{"id":3,"sLen":0,"time":37736.84,"type":0},{"id":0,"sLen":0,"time":37894.7344,"type":0},{"id":0,"sLen":0,"time":38210.5234,"type":0},{"id":3,"sLen":0,"time":38368.418,"type":0},{"id":0,"sLen":0,"time":38526.3125,"type":0},{"id":3,"sLen":0,"time":38842.1,"type":0},{"id":1,"sLen":0,"time":39000,"type":0},{"id":1,"sLen":0,"time":39157.8945,"type":0},{"id":1,"sLen":0,"time":39473.6836,"type":0},{"id":3,"sLen":0,"time":39631.58,"type":0},{"id":1,"sLen":0,"time":39789.4727,"type":0},{"id":1,"sLen":236.842100000002,"time":40105.26,"type":0},{"id":0,"sLen":0,"time":45473.6836,"type":0},{"id":0,"sLen":0,"time":45789.4727,"type":0},{"id":3,"sLen":0,"time":46105.26,"type":0},{"id":1,"sLen":0,"time":46263.1563,"type":0},{"id":0,"sLen":0,"time":46421.05,"type":0},{"id":3,"sLen":0,"time":46578.9453,"type":0},{"id":2,"sLen":0,"time":46736.84,"type":0},{"id":1,"sLen":0,"time":47052.63,"type":0},{"id":2,"sLen":0,"time":47368.418,"type":0},{"id":3,"sLen":0,"time":47526.3125,"type":0},{"id":0,"sLen":0,"time":47684.207,"type":0},{"id":3,"sLen":0,"time":47842.1,"type":0},{"id":0,"sLen":0,"time":47999.9961,"type":0},{"id":0,"sLen":0,"time":48315.79,"type":0},{"id":3,"sLen":0,"time":48631.58,"type":0},{"id":1,"sLen":0,"time":48789.4727,"type":0},{"id":0,"sLen":0,"time":48947.3672,"type":0},{"id":3,"sLen":0,"time":49105.26,"type":0},{"id":2,"sLen":0,"time":49263.1563,"type":0},{"id":1,"sLen":0,"time":49578.9453,"type":0},{"id":2,"sLen":0,"time":49894.7344,"type":0},{"id":0,"sLen":0,"time":50052.63,"type":0},{"id":3,"sLen":0,"time":50210.5234,"type":0},{"id":0,"sLen":0,"time":50368.418,"type":0},{"id":3,"sLen":157.89473,"time":55578.9453,"type":0},{"id":1,"sLen":157.89473,"time":56210.5234,"type":0},{"id":0,"sLen":0,"time":56526.3125,"type":0},{"id":3,"sLen":0,"time":56842.1,"type":0},{"id":1,"sLen":0,"time":57157.89,"type":0},{"id":3,"sLen":0,"time":57315.7852,"type":0},{"id":1,"sLen":0,"time":57631.58,"type":0},{"id":3,"sLen":0,"time":57789.4727,"type":0},{"id":3,"sLen":0,"time":58105.26,"type":0},{"id":3,"sLen":0,"time":58421.05,"type":0},{"id":1,"sLen":0,"time":58736.84,"type":0},{"id":0,"sLen":0,"time":59052.63,"type":0},{"id":3,"sLen":0,"time":59368.418,"type":0},{"id":1,"sLen":0,"time":59684.207,"type":0},{"id":3,"sLen":0,"time":59842.1,"type":0},{"id":1,"sLen":0,"time":60157.89,"type":0},{"id":3,"sLen":0,"time":60315.7852,"type":0},{"id":2,"sLen":0,"time":65684.21,"type":0},{"id":3,"sLen":0,"time":66000,"type":0},{"id":1,"sLen":0,"time":66157.89,"type":0},{"id":3,"sLen":0,"time":66315.79,"type":0},{"id":0,"sLen":0,"time":66631.58,"type":0},{"id":3,"sLen":0,"time":66789.47,"type":0},{"id":0,"sLen":0,"time":66947.37,"type":0},{"id":0,"sLen":0,"time":67263.16,"type":0},{"id":1,"sLen":0,"time":67421.05,"type":0},{"id":3,"sLen":0,"time":67578.9453,"type":0},{"id":1,"sLen":0,"time":67894.7344,"type":0},{"id":3,"sLen":0,"time":68052.625,"type":0},{"id":0,"sLen":0,"time":68210.52,"type":0},{"id":0,"sLen":0,"time":68526.31,"type":0},{"id":3,"sLen":0,"time":68684.21,"type":0},{"id":0,"sLen":0,"time":68842.1,"type":0},{"id":3,"sLen":0,"time":69157.89,"type":0},{"id":1,"sLen":0,"time":69315.79,"type":0},{"id":1,"sLen":0,"time":69473.68,"type":0},{"id":1,"sLen":0,"time":69789.47,"type":0},{"id":3,"sLen":0,"time":69947.37,"type":0},{"id":1,"sLen":0,"time":70105.26,"type":0},{"id":1,"sLen":236.842099999994,"time":70421.05,"type":0},{"id":0,"sLen":0,"time":75789.47,"type":0},{"id":0,"sLen":0,"time":76105.26,"type":0},{"id":3,"sLen":0,"time":76421.05,"type":0},{"id":1,"sLen":0,"time":76578.9453,"type":0},{"id":0,"sLen":0,"time":76736.8359,"type":0},{"id":3,"sLen":0,"time":76894.7344,"type":0},{"id":2,"sLen":0,"time":77052.625,"type":0},{"id":1,"sLen":0,"time":77368.4141,"type":0},{"id":2,"sLen":0,"time":77684.2,"type":0},{"id":3,"sLen":0,"time":77842.1,"type":0},{"id":0,"sLen":0,"time":78000,"type":0},{"id":3,"sLen":0,"time":78157.89,"type":0},{"id":0,"sLen":0,"time":78315.79,"type":0},{"id":0,"sLen":0,"time":78631.58,"type":0},{"id":3,"sLen":0,"time":78947.37,"type":0},{"id":1,"sLen":0,"time":79105.26,"type":0},{"id":0,"sLen":0,"time":79263.16,"type":0},{"id":3,"sLen":0,"time":79421.05,"type":0},{"id":2,"sLen":0,"time":79578.9453,"type":0},{"id":1,"sLen":0,"time":79894.7344,"type":0},{"id":2,"sLen":0,"time":80210.52,"type":0},{"id":0,"sLen":0,"time":80368.4141,"type":0},{"id":3,"sLen":0,"time":80526.31,"type":0},{"id":0,"sLen":0,"time":80684.2,"type":0},{"id":2,"sLen":0,"time":85894.7344,"type":0},{"id":1,"sLen":0,"time":86210.52,"type":0},{"id":0,"sLen":0,"time":86526.31,"type":0},{"id":3,"sLen":0,"time":86684.2,"type":0},{"id":1,"sLen":0,"time":86842.1,"type":0},{"id":2,"sLen":0,"time":87157.89,"type":0},{"id":1,"sLen":0,"time":87473.68,"type":0},{"id":0,"sLen":0,"time":87789.47,"type":0},{"id":3,"sLen":0,"time":87947.37,"type":0},{"id":1,"sLen":0,"time":88105.26,"type":0},{"id":2,"sLen":0,"time":88421.05,"type":0},{"id":1,"sLen":0,"time":88736.8359,"type":0},{"id":0,"sLen":0,"time":89052.625,"type":0},{"id":3,"sLen":0,"time":89210.52,"type":0},{"id":1,"sLen":0,"time":89368.4141,"type":0},{"id":2,"sLen":0,"time":89684.2,"type":0},{"id":1,"sLen":0,"time":89999.99,"type":0},{"id":0,"sLen":0,"time":90315.79,"type":0},{"id":3,"sLen":0,"time":90473.68,"type":0},{"id":1,"sLen":0,"time":90631.58,"type":0},{"id":3,"sLen":157.89473,"time":95999.99,"type":0},{"id":1,"sLen":157.89473,"time":96631.58,"type":0},{"id":0,"sLen":0,"time":96947.37,"type":0},{"id":3,"sLen":0,"time":97263.16,"type":0},{"id":1,"sLen":0,"time":97578.9453,"type":0},{"id":3,"sLen":0,"time":97736.8359,"type":0},{"id":1,"sLen":0,"time":98052.625,"type":0},{"id":3,"sLen":0,"time":98210.52,"type":0},{"id":3,"sLen":0,"time":98526.31,"type":0},{"id":3,"sLen":0,"time":98842.1,"type":0},{"id":1,"sLen":0,"time":99157.89,"type":0},{"id":0,"sLen":0,"time":99473.68,"type":0},{"id":3,"sLen":0,"time":99789.47,"type":0},{"id":1,"sLen":0,"time":100105.258,"type":0},{"id":3,"sLen":0,"time":100263.156,"type":0},{"id":1,"sLen":0,"time":100578.945,"type":0},{"id":3,"sLen":0,"time":100736.836,"type":0}],"characters":["bf-pixel"],"visible":true,"type":1},{"position":"girlfriend","notes":[],"characters":["gf-pixel"],"visible":false,"type":2}],"stage":"school-evil","scrollSpeed":1.3,"noteTypes":[]} \ No newline at end of file +{"events":[{"params":[1],"time":5052.63157894737,"name":"Camera Movement"},{"params":[0],"time":10105.2631578947,"name":"Camera Movement"},{"params":[1],"time":15157.8947368421,"name":"Camera Movement"},{"params":[0],"time":20210.5263157895,"name":"Camera Movement"},{"params":[1],"time":25263.1578947368,"name":"Camera Movement"},{"params":[0],"time":30315.7894736842,"name":"Camera Movement"},{"params":[1],"time":35368.4210526316,"name":"Camera Movement"},{"params":[0],"time":40421.0526315789,"name":"Camera Movement"},{"params":[1],"time":45473.6842105263,"name":"Camera Movement"},{"params":[0],"time":50526.3157894737,"name":"Camera Movement"},{"params":[1],"time":55578.9473684211,"name":"Camera Movement"},{"params":[0],"time":60631.5789473684,"name":"Camera Movement"},{"params":[1],"time":65684.2105263158,"name":"Camera Movement"},{"params":[0],"time":70736.8421052632,"name":"Camera Movement"},{"params":[1],"time":75789.4736842105,"name":"Camera Movement"},{"params":[0],"time":80842.1052631579,"name":"Camera Movement"},{"params":[1],"time":85894.7368421053,"name":"Camera Movement"},{"params":[0],"time":90947.3684210526,"name":"Camera Movement"},{"params":[1],"time":96000,"name":"Camera Movement"}],"codenameChart":true,"strumLines":[{"position":"dad","notes":[{"id":3,"sLen":157.89473,"time":0,"type":0},{"id":1,"sLen":157.89473,"time":631.5789,"type":0},{"id":0,"sLen":0,"time":947.3684,"type":0},{"id":3,"sLen":0,"time":1263.15784,"type":0},{"id":1,"sLen":0,"time":1578.94727,"type":0},{"id":3,"sLen":0,"time":1736.842,"type":0},{"id":1,"sLen":0,"time":2052.63159,"type":0},{"id":3,"sLen":0,"time":2210.52612,"type":0},{"id":3,"sLen":0,"time":2526.31567,"type":0},{"id":3,"sLen":0,"time":2842.10522,"type":0},{"id":1,"sLen":0,"time":3157.89453,"type":0},{"id":0,"sLen":0,"time":3473.684,"type":0},{"id":3,"sLen":0,"time":3789.47363,"type":0},{"id":1,"sLen":0,"time":4105.263,"type":0},{"id":3,"sLen":0,"time":4263.15771,"type":0},{"id":1,"sLen":0,"time":4578.94727,"type":0},{"id":3,"sLen":0,"time":4736.842,"type":0},{"id":2,"sLen":157.894729999998,"time":10105.2627,"type":0},{"id":2,"sLen":0,"time":10736.8418,"type":0},{"id":3,"sLen":0,"time":10894.7363,"type":0},{"id":1,"sLen":78.9473647999985,"time":11052.6309,"type":0},{"id":2,"sLen":157.89473,"time":11368.4209,"type":0},{"id":2,"sLen":157.89473,"time":11842.1045,"type":0},{"id":2,"sLen":157.89473,"time":12315.7891,"type":0},{"id":2,"sLen":157.89473,"time":12631.5781,"type":0},{"id":2,"sLen":0,"time":13263.1572,"type":0},{"id":3,"sLen":0,"time":13421.0518,"type":0},{"id":1,"sLen":78.9473648000003,"time":13578.9463,"type":0},{"id":2,"sLen":157.89473,"time":13894.7363,"type":0},{"id":2,"sLen":157.89473,"time":14368.42,"type":0},{"id":0,"sLen":157.89473,"time":14842.1045,"type":0},{"id":1,"sLen":0,"time":20210.5254,"type":0},{"id":3,"sLen":0,"time":20526.3145,"type":0},{"id":1,"sLen":0,"time":20684.209,"type":0},{"id":3,"sLen":0,"time":21000,"type":0},{"id":1,"sLen":0,"time":21157.8945,"type":0},{"id":2,"sLen":0,"time":21473.6836,"type":0},{"id":1,"sLen":0,"time":21631.5781,"type":0},{"id":2,"sLen":0,"time":21947.3672,"type":0},{"id":1,"sLen":0,"time":22105.2617,"type":0},{"id":3,"sLen":0,"time":22421.05,"type":0},{"id":1,"sLen":0,"time":22578.9473,"type":0},{"id":1,"sLen":0,"time":22736.8418,"type":0},{"id":3,"sLen":0,"time":23052.63,"type":0},{"id":1,"sLen":0,"time":23210.5254,"type":0},{"id":3,"sLen":0,"time":23526.3145,"type":0},{"id":1,"sLen":0,"time":23684.209,"type":0},{"id":2,"sLen":0,"time":23999.998,"type":0},{"id":1,"sLen":0,"time":24157.8945,"type":0},{"id":2,"sLen":0,"time":24473.6836,"type":0},{"id":1,"sLen":0,"time":24631.5781,"type":0},{"id":3,"sLen":0,"time":24947.3672,"type":0},{"id":1,"sLen":0,"time":25105.2617,"type":0},{"id":2,"sLen":0,"time":30315.79,"type":0},{"id":3,"sLen":0,"time":30631.5781,"type":0},{"id":1,"sLen":0,"time":30789.4727,"type":0},{"id":3,"sLen":0,"time":30947.3672,"type":0},{"id":0,"sLen":0,"time":31263.1563,"type":0},{"id":3,"sLen":0,"time":31421.05,"type":0},{"id":0,"sLen":0,"time":31578.9453,"type":0},{"id":0,"sLen":0,"time":31894.7363,"type":0},{"id":1,"sLen":0,"time":32052.63,"type":0},{"id":3,"sLen":0,"time":32210.5254,"type":0},{"id":1,"sLen":0,"time":32526.3145,"type":0},{"id":3,"sLen":0,"time":32684.209,"type":0},{"id":0,"sLen":0,"time":32842.1055,"type":0},{"id":0,"sLen":0,"time":33157.8945,"type":0},{"id":3,"sLen":0,"time":33315.79,"type":0},{"id":0,"sLen":0,"time":33473.6836,"type":0},{"id":3,"sLen":0,"time":33789.4727,"type":0},{"id":1,"sLen":0,"time":33947.3672,"type":0},{"id":1,"sLen":0,"time":34105.26,"type":0},{"id":1,"sLen":0,"time":34421.05,"type":0},{"id":3,"sLen":0,"time":34578.9453,"type":0},{"id":1,"sLen":0,"time":34736.84,"type":0},{"id":1,"sLen":236.842100000002,"time":35052.63,"type":0},{"id":0,"sLen":0,"time":40421.05,"type":0},{"id":0,"sLen":0,"time":40736.84,"type":0},{"id":3,"sLen":0,"time":41052.63,"type":0},{"id":1,"sLen":0,"time":41210.5234,"type":0},{"id":0,"sLen":0,"time":41368.418,"type":0},{"id":3,"sLen":0,"time":41526.3125,"type":0},{"id":2,"sLen":0,"time":41684.207,"type":0},{"id":1,"sLen":0,"time":42000,"type":0},{"id":2,"sLen":0,"time":42315.79,"type":0},{"id":3,"sLen":0,"time":42473.6836,"type":0},{"id":0,"sLen":0,"time":42631.58,"type":0},{"id":3,"sLen":0,"time":42789.4727,"type":0},{"id":0,"sLen":0,"time":42947.3672,"type":0},{"id":0,"sLen":0,"time":43263.1563,"type":0},{"id":3,"sLen":0,"time":43578.9453,"type":0},{"id":1,"sLen":0,"time":43736.84,"type":0},{"id":0,"sLen":0,"time":43894.7344,"type":0},{"id":3,"sLen":0,"time":44052.63,"type":0},{"id":2,"sLen":0,"time":44210.5234,"type":0},{"id":1,"sLen":0,"time":44526.3125,"type":0},{"id":2,"sLen":0,"time":44842.1,"type":0},{"id":0,"sLen":0,"time":44999.9961,"type":0},{"id":3,"sLen":0,"time":45157.8945,"type":0},{"id":0,"sLen":0,"time":45315.79,"type":0},{"id":3,"sLen":157.89473,"time":50526.3125,"type":0},{"id":1,"sLen":157.89473,"time":51157.89,"type":0},{"id":0,"sLen":0,"time":51473.6836,"type":0},{"id":3,"sLen":0,"time":51789.4727,"type":0},{"id":1,"sLen":0,"time":52105.26,"type":0},{"id":3,"sLen":0,"time":52263.1563,"type":0},{"id":1,"sLen":0,"time":52578.9453,"type":0},{"id":3,"sLen":0,"time":52736.84,"type":0},{"id":3,"sLen":0,"time":53052.63,"type":0},{"id":3,"sLen":0,"time":53368.418,"type":0},{"id":1,"sLen":0,"time":53684.207,"type":0},{"id":0,"sLen":0,"time":53999.9961,"type":0},{"id":3,"sLen":0,"time":54315.7852,"type":0},{"id":1,"sLen":0,"time":54631.58,"type":0},{"id":3,"sLen":0,"time":54789.4727,"type":0},{"id":1,"sLen":0,"time":55105.26,"type":0},{"id":3,"sLen":0,"time":55263.1563,"type":0},{"id":2,"sLen":0,"time":60631.58,"type":0},{"id":3,"sLen":0,"time":60947.3672,"type":0},{"id":1,"sLen":0,"time":61105.26,"type":0},{"id":3,"sLen":0,"time":61263.1563,"type":0},{"id":0,"sLen":0,"time":61578.9453,"type":0},{"id":3,"sLen":0,"time":61736.84,"type":0},{"id":0,"sLen":0,"time":61894.7344,"type":0},{"id":0,"sLen":0,"time":62210.5234,"type":0},{"id":1,"sLen":0,"time":62368.418,"type":0},{"id":3,"sLen":0,"time":62526.3125,"type":0},{"id":1,"sLen":0,"time":62842.1,"type":0},{"id":3,"sLen":0,"time":62999.9961,"type":0},{"id":0,"sLen":0,"time":63157.89,"type":0},{"id":0,"sLen":0,"time":63473.68,"type":0},{"id":3,"sLen":0,"time":63631.5742,"type":0},{"id":0,"sLen":0,"time":63789.4727,"type":0},{"id":3,"sLen":0,"time":64105.26,"type":0},{"id":1,"sLen":0,"time":64263.1563,"type":0},{"id":1,"sLen":0,"time":64421.05,"type":0},{"id":1,"sLen":0,"time":64736.84,"type":0},{"id":3,"sLen":0,"time":64894.7344,"type":0},{"id":1,"sLen":0,"time":65052.63,"type":0},{"id":1,"sLen":236.842100000002,"time":65368.418,"type":0},{"id":0,"sLen":0,"time":70736.8359,"type":0},{"id":0,"sLen":0,"time":71052.625,"type":0},{"id":3,"sLen":0,"time":71368.4141,"type":0},{"id":1,"sLen":0,"time":71526.31,"type":0},{"id":0,"sLen":0,"time":71684.21,"type":0},{"id":3,"sLen":0,"time":71842.1,"type":0},{"id":2,"sLen":0,"time":72000,"type":0},{"id":1,"sLen":0,"time":72315.79,"type":0},{"id":2,"sLen":0,"time":72631.58,"type":0},{"id":3,"sLen":0,"time":72789.47,"type":0},{"id":0,"sLen":0,"time":72947.37,"type":0},{"id":3,"sLen":0,"time":73105.26,"type":0},{"id":0,"sLen":0,"time":73263.16,"type":0},{"id":0,"sLen":0,"time":73578.9453,"type":0},{"id":3,"sLen":0,"time":73894.7344,"type":0},{"id":1,"sLen":0,"time":74052.625,"type":0},{"id":0,"sLen":0,"time":74210.52,"type":0},{"id":3,"sLen":0,"time":74368.4141,"type":0},{"id":2,"sLen":0,"time":74526.31,"type":0},{"id":1,"sLen":0,"time":74842.1,"type":0},{"id":2,"sLen":0,"time":75157.89,"type":0},{"id":0,"sLen":0,"time":75315.79,"type":0},{"id":3,"sLen":0,"time":75473.68,"type":0},{"id":0,"sLen":0,"time":75631.58,"type":0},{"id":2,"sLen":0,"time":80842.1,"type":0},{"id":1,"sLen":0,"time":81157.89,"type":0},{"id":0,"sLen":0,"time":81473.68,"type":0},{"id":3,"sLen":0,"time":81631.58,"type":0},{"id":1,"sLen":0,"time":81789.47,"type":0},{"id":2,"sLen":0,"time":82105.26,"type":0},{"id":1,"sLen":0,"time":82421.05,"type":0},{"id":0,"sLen":0,"time":82736.8359,"type":0},{"id":3,"sLen":0,"time":82894.7344,"type":0},{"id":1,"sLen":0,"time":83052.625,"type":0},{"id":2,"sLen":0,"time":83368.4141,"type":0},{"id":1,"sLen":0,"time":83684.2,"type":0},{"id":0,"sLen":0,"time":84000,"type":0},{"id":3,"sLen":0,"time":84157.89,"type":0},{"id":1,"sLen":0,"time":84315.79,"type":0},{"id":2,"sLen":0,"time":84631.58,"type":0},{"id":1,"sLen":0,"time":84947.37,"type":0},{"id":0,"sLen":0,"time":85263.16,"type":0},{"id":3,"sLen":0,"time":85421.05,"type":0},{"id":1,"sLen":0,"time":85578.9453,"type":0},{"id":3,"sLen":157.89473,"time":90947.37,"type":0},{"id":1,"sLen":157.89473,"time":91578.9453,"type":0},{"id":0,"sLen":0,"time":91894.7344,"type":0},{"id":3,"sLen":0,"time":92210.52,"type":0},{"id":1,"sLen":0,"time":92526.31,"type":0},{"id":3,"sLen":0,"time":92684.2,"type":0},{"id":1,"sLen":0,"time":92999.99,"type":0},{"id":3,"sLen":0,"time":93157.89,"type":0},{"id":3,"sLen":0,"time":93473.68,"type":0},{"id":3,"sLen":0,"time":93789.47,"type":0},{"id":1,"sLen":0,"time":94105.26,"type":0},{"id":0,"sLen":0,"time":94421.05,"type":0},{"id":3,"sLen":0,"time":94736.8359,"type":0},{"id":1,"sLen":0,"time":95052.625,"type":0},{"id":3,"sLen":0,"time":95210.52,"type":0},{"id":1,"sLen":0,"time":95526.31,"type":0},{"id":3,"sLen":0,"time":95684.2,"type":0}],"characters":["spirit"],"visible":true,"type":0},{"position":"boyfriend","notes":[{"id":3,"sLen":157.89473,"time":5052.63135,"type":0},{"id":1,"sLen":157.89473,"time":5684.21045,"type":0},{"id":0,"sLen":0,"time":5999.99951,"type":0},{"id":3,"sLen":0,"time":6315.789,"type":0},{"id":1,"sLen":0,"time":6631.57861,"type":0},{"id":3,"sLen":0,"time":6789.473,"type":0},{"id":1,"sLen":0,"time":7105.2627,"type":0},{"id":3,"sLen":0,"time":7263.15771,"type":0},{"id":3,"sLen":0,"time":7578.94727,"type":0},{"id":3,"sLen":0,"time":7894.73633,"type":0},{"id":1,"sLen":0,"time":8210.526,"type":0},{"id":0,"sLen":0,"time":8526.315,"type":0},{"id":3,"sLen":0,"time":8842.10449,"type":0},{"id":1,"sLen":0,"time":9157.895,"type":0},{"id":3,"sLen":0,"time":9315.789,"type":0},{"id":1,"sLen":0,"time":9631.578,"type":0},{"id":3,"sLen":0,"time":9789.474,"type":0},{"id":2,"sLen":157.89473,"time":15157.8945,"type":0},{"id":2,"sLen":0,"time":15789.4727,"type":0},{"id":3,"sLen":0,"time":15947.3682,"type":0},{"id":1,"sLen":78.9473648000003,"time":16105.2627,"type":0},{"id":2,"sLen":157.89473,"time":16421.0527,"type":0},{"id":2,"sLen":157.89473,"time":16894.7363,"type":0},{"id":2,"sLen":157.89473,"time":17368.42,"type":0},{"id":2,"sLen":157.89473,"time":17684.209,"type":0},{"id":2,"sLen":0,"time":18315.79,"type":0},{"id":3,"sLen":0,"time":18473.6836,"type":0},{"id":1,"sLen":78.9473647999985,"time":18631.5781,"type":0},{"id":2,"sLen":157.89473,"time":18947.3672,"type":0},{"id":2,"sLen":157.89473,"time":19421.05,"type":0},{"id":0,"sLen":157.89473,"time":19894.7363,"type":0},{"id":3,"sLen":0,"time":24789.4727,"type":0},{"id":2,"sLen":0,"time":24947.3672,"type":0},{"id":0,"sLen":0,"time":25105.2617,"type":0},{"id":1,"sLen":0,"time":25263.1563,"type":0},{"id":3,"sLen":0,"time":25578.9453,"type":0},{"id":1,"sLen":0,"time":25736.8418,"type":0},{"id":3,"sLen":0,"time":26052.63,"type":0},{"id":1,"sLen":0,"time":26210.5254,"type":0},{"id":2,"sLen":0,"time":26526.3145,"type":0},{"id":1,"sLen":0,"time":26684.209,"type":0},{"id":2,"sLen":0,"time":26999.998,"type":0},{"id":1,"sLen":0,"time":27157.8926,"type":0},{"id":3,"sLen":0,"time":27473.6836,"type":0},{"id":1,"sLen":0,"time":27631.5781,"type":0},{"id":1,"sLen":0,"time":27789.4727,"type":0},{"id":3,"sLen":0,"time":28105.2617,"type":0},{"id":1,"sLen":0,"time":28263.1563,"type":0},{"id":3,"sLen":0,"time":28578.9453,"type":0},{"id":1,"sLen":0,"time":28736.84,"type":0},{"id":2,"sLen":0,"time":29052.63,"type":0},{"id":1,"sLen":0,"time":29210.5254,"type":0},{"id":2,"sLen":0,"time":29526.3145,"type":0},{"id":1,"sLen":0,"time":29684.209,"type":0},{"id":3,"sLen":0,"time":29999.998,"type":0},{"id":1,"sLen":0,"time":30157.8926,"type":0},{"id":2,"sLen":0,"time":35368.418,"type":0},{"id":3,"sLen":0,"time":35684.207,"type":0},{"id":1,"sLen":0,"time":35842.1055,"type":0},{"id":3,"sLen":0,"time":36000,"type":0},{"id":0,"sLen":0,"time":36315.79,"type":0},{"id":3,"sLen":0,"time":36473.6836,"type":0},{"id":0,"sLen":0,"time":36631.58,"type":0},{"id":0,"sLen":0,"time":36947.3672,"type":0},{"id":1,"sLen":0,"time":37105.26,"type":0},{"id":3,"sLen":0,"time":37263.1563,"type":0},{"id":1,"sLen":0,"time":37578.9453,"type":0},{"id":3,"sLen":0,"time":37736.84,"type":0},{"id":0,"sLen":0,"time":37894.7344,"type":0},{"id":0,"sLen":0,"time":38210.5234,"type":0},{"id":3,"sLen":0,"time":38368.418,"type":0},{"id":0,"sLen":0,"time":38526.3125,"type":0},{"id":3,"sLen":0,"time":38842.1,"type":0},{"id":1,"sLen":0,"time":39000,"type":0},{"id":1,"sLen":0,"time":39157.8945,"type":0},{"id":1,"sLen":0,"time":39473.6836,"type":0},{"id":3,"sLen":0,"time":39631.58,"type":0},{"id":1,"sLen":0,"time":39789.4727,"type":0},{"id":1,"sLen":236.842100000002,"time":40105.26,"type":0},{"id":0,"sLen":0,"time":45473.6836,"type":0},{"id":0,"sLen":0,"time":45789.4727,"type":0},{"id":3,"sLen":0,"time":46105.26,"type":0},{"id":1,"sLen":0,"time":46263.1563,"type":0},{"id":0,"sLen":0,"time":46421.05,"type":0},{"id":3,"sLen":0,"time":46578.9453,"type":0},{"id":2,"sLen":0,"time":46736.84,"type":0},{"id":1,"sLen":0,"time":47052.63,"type":0},{"id":2,"sLen":0,"time":47368.418,"type":0},{"id":3,"sLen":0,"time":47526.3125,"type":0},{"id":0,"sLen":0,"time":47684.207,"type":0},{"id":3,"sLen":0,"time":47842.1,"type":0},{"id":0,"sLen":0,"time":47999.9961,"type":0},{"id":0,"sLen":0,"time":48315.79,"type":0},{"id":3,"sLen":0,"time":48631.58,"type":0},{"id":1,"sLen":0,"time":48789.4727,"type":0},{"id":0,"sLen":0,"time":48947.3672,"type":0},{"id":3,"sLen":0,"time":49105.26,"type":0},{"id":2,"sLen":0,"time":49263.1563,"type":0},{"id":1,"sLen":0,"time":49578.9453,"type":0},{"id":2,"sLen":0,"time":49894.7344,"type":0},{"id":0,"sLen":0,"time":50052.63,"type":0},{"id":3,"sLen":0,"time":50210.5234,"type":0},{"id":0,"sLen":0,"time":50368.418,"type":0},{"id":3,"sLen":157.89473,"time":55578.9453,"type":0},{"id":1,"sLen":157.89473,"time":56210.5234,"type":0},{"id":0,"sLen":0,"time":56526.3125,"type":0},{"id":3,"sLen":0,"time":56842.1,"type":0},{"id":1,"sLen":0,"time":57157.89,"type":0},{"id":3,"sLen":0,"time":57315.7852,"type":0},{"id":1,"sLen":0,"time":57631.58,"type":0},{"id":3,"sLen":0,"time":57789.4727,"type":0},{"id":3,"sLen":0,"time":58105.26,"type":0},{"id":3,"sLen":0,"time":58421.05,"type":0},{"id":1,"sLen":0,"time":58736.84,"type":0},{"id":0,"sLen":0,"time":59052.63,"type":0},{"id":3,"sLen":0,"time":59368.418,"type":0},{"id":1,"sLen":0,"time":59684.207,"type":0},{"id":3,"sLen":0,"time":59842.1,"type":0},{"id":1,"sLen":0,"time":60157.89,"type":0},{"id":3,"sLen":0,"time":60315.7852,"type":0},{"id":2,"sLen":0,"time":65684.21,"type":0},{"id":3,"sLen":0,"time":66000,"type":0},{"id":1,"sLen":0,"time":66157.89,"type":0},{"id":3,"sLen":0,"time":66315.79,"type":0},{"id":0,"sLen":0,"time":66631.58,"type":0},{"id":3,"sLen":0,"time":66789.47,"type":0},{"id":0,"sLen":0,"time":66947.37,"type":0},{"id":0,"sLen":0,"time":67263.16,"type":0},{"id":1,"sLen":0,"time":67421.05,"type":0},{"id":3,"sLen":0,"time":67578.9453,"type":0},{"id":1,"sLen":0,"time":67894.7344,"type":0},{"id":3,"sLen":0,"time":68052.625,"type":0},{"id":0,"sLen":0,"time":68210.52,"type":0},{"id":0,"sLen":0,"time":68526.31,"type":0},{"id":3,"sLen":0,"time":68684.21,"type":0},{"id":0,"sLen":0,"time":68842.1,"type":0},{"id":3,"sLen":0,"time":69157.89,"type":0},{"id":1,"sLen":0,"time":69315.79,"type":0},{"id":1,"sLen":0,"time":69473.68,"type":0},{"id":1,"sLen":0,"time":69789.47,"type":0},{"id":3,"sLen":0,"time":69947.37,"type":0},{"id":1,"sLen":0,"time":70105.26,"type":0},{"id":1,"sLen":236.842099999994,"time":70421.05,"type":0},{"id":0,"sLen":0,"time":75789.47,"type":0},{"id":0,"sLen":0,"time":76105.26,"type":0},{"id":3,"sLen":0,"time":76421.05,"type":0},{"id":1,"sLen":0,"time":76578.9453,"type":0},{"id":0,"sLen":0,"time":76736.8359,"type":0},{"id":3,"sLen":0,"time":76894.7344,"type":0},{"id":2,"sLen":0,"time":77052.625,"type":0},{"id":1,"sLen":0,"time":77368.4141,"type":0},{"id":2,"sLen":0,"time":77684.2,"type":0},{"id":3,"sLen":0,"time":77842.1,"type":0},{"id":0,"sLen":0,"time":78000,"type":0},{"id":3,"sLen":0,"time":78157.89,"type":0},{"id":0,"sLen":0,"time":78315.79,"type":0},{"id":0,"sLen":0,"time":78631.58,"type":0},{"id":3,"sLen":0,"time":78947.37,"type":0},{"id":1,"sLen":0,"time":79105.26,"type":0},{"id":0,"sLen":0,"time":79263.16,"type":0},{"id":3,"sLen":0,"time":79421.05,"type":0},{"id":2,"sLen":0,"time":79578.9453,"type":0},{"id":1,"sLen":0,"time":79894.7344,"type":0},{"id":2,"sLen":0,"time":80210.52,"type":0},{"id":0,"sLen":0,"time":80368.4141,"type":0},{"id":3,"sLen":0,"time":80526.31,"type":0},{"id":0,"sLen":0,"time":80684.2,"type":0},{"id":2,"sLen":0,"time":85894.7344,"type":0},{"id":1,"sLen":0,"time":86210.52,"type":0},{"id":0,"sLen":0,"time":86526.31,"type":0},{"id":3,"sLen":0,"time":86684.2,"type":0},{"id":1,"sLen":0,"time":86842.1,"type":0},{"id":2,"sLen":0,"time":87157.89,"type":0},{"id":1,"sLen":0,"time":87473.68,"type":0},{"id":0,"sLen":0,"time":87789.47,"type":0},{"id":3,"sLen":0,"time":87947.37,"type":0},{"id":1,"sLen":0,"time":88105.26,"type":0},{"id":2,"sLen":0,"time":88421.05,"type":0},{"id":1,"sLen":0,"time":88736.8359,"type":0},{"id":0,"sLen":0,"time":89052.625,"type":0},{"id":3,"sLen":0,"time":89210.52,"type":0},{"id":1,"sLen":0,"time":89368.4141,"type":0},{"id":2,"sLen":0,"time":89684.2,"type":0},{"id":1,"sLen":0,"time":89999.99,"type":0},{"id":0,"sLen":0,"time":90315.79,"type":0},{"id":3,"sLen":0,"time":90473.68,"type":0},{"id":1,"sLen":0,"time":90631.58,"type":0},{"id":3,"sLen":157.89473,"time":95999.99,"type":0},{"id":1,"sLen":157.89473,"time":96631.58,"type":0},{"id":0,"sLen":0,"time":96947.37,"type":0},{"id":3,"sLen":0,"time":97263.16,"type":0},{"id":1,"sLen":0,"time":97578.9453,"type":0},{"id":3,"sLen":0,"time":97736.8359,"type":0},{"id":1,"sLen":0,"time":98052.625,"type":0},{"id":3,"sLen":0,"time":98210.52,"type":0},{"id":3,"sLen":0,"time":98526.31,"type":0},{"id":3,"sLen":0,"time":98842.1,"type":0},{"id":1,"sLen":0,"time":99157.89,"type":0},{"id":0,"sLen":0,"time":99473.68,"type":0},{"id":3,"sLen":0,"time":99789.47,"type":0},{"id":1,"sLen":0,"time":100105.258,"type":0},{"id":3,"sLen":0,"time":100263.156,"type":0},{"id":1,"sLen":0,"time":100578.945,"type":0},{"id":3,"sLen":0,"time":100736.836,"type":0}],"characters":["bf-pixel"],"visible":true,"type":1},{"position":"girlfriend","notes":[],"characters":["gf-pixel"],"visible":false,"type":2}],"stage":"school-evil","scrollSpeed":1.3,"noteTypes":[]} \ No newline at end of file diff --git a/assets/songs/thorns/events.json b/assets/songs/thorns/events.json index 3fcdc848..6d9cb51f 100644 --- a/assets/songs/thorns/events.json +++ b/assets/songs/thorns/events.json @@ -1 +1 @@ -{"events":[{"params":[1],"time":5052.63157894737,"name":"Camera Movement"},{"params":[0],"time":10105.2631578947,"name":"Camera Movement"},{"params":[1],"time":15157.8947368421,"name":"Camera Movement"},{"params":[0],"time":20210.5263157895,"name":"Camera Movement"},{"params":["aberrationCoolThing",""],"time":20210.5263157895,"name":"HScript Call"},{"params":[1],"time":25263.1578947368,"name":"Camera Movement"},{"params":[0],"time":30315.7894736842,"name":"Camera Movement"},{"params":["aberrationCoolThing",""],"time":30315.7894736842,"name":"HScript Call"},{"params":[1],"time":35368.4210526316,"name":"Camera Movement"},{"params":[0],"time":40421.0526315789,"name":"Camera Movement"},{"params":[1],"time":45473.6842105263,"name":"Camera Movement"},{"params":[0],"time":50526.3157894737,"name":"Camera Movement"},{"params":["aberrationCoolThing",""],"time":50526.3157894737,"name":"HScript Call"},{"params":[1],"time":55578.947368421,"name":"Camera Movement"},{"params":[0],"time":60631.5789473684,"name":"Camera Movement"},{"params":["aberrationCoolThing",""],"time":60631.5789473684,"name":"HScript Call"},{"params":[1],"time":65684.2105263157,"name":"Camera Movement"},{"params":[0],"time":70736.8421052631,"name":"Camera Movement"},{"params":[1],"time":75789.4736842105,"name":"Camera Movement"},{"params":[0],"time":80842.1052631578,"name":"Camera Movement"},{"params":["aberrationCoolThing",""],"time":80842.1052631578,"name":"HScript Call"},{"params":[1],"time":85894.7368421052,"name":"Camera Movement"},{"params":[0],"time":90947.3684210525,"name":"Camera Movement"},{"params":["aberrationCoolThing",""],"time":90947.3684210525,"name":"HScript Call"},{"params":[1],"time":95999.9999999999,"name":"Camera Movement"}]} \ No newline at end of file +{"events":[{"params":["aberrationCoolThing",""],"time":20210.5263157895,"name":"HScript Call"},{"params":["aberrationCoolThing",""],"name":"HScript Call","time":30315.7894736842},{"params":["aberrationCoolThing",""],"time":50526.3157894737,"name":"HScript Call"},{"params":["aberrationCoolThing",""],"time":60631.5789473684,"name":"HScript Call"},{"params":["aberrationCoolThing",""],"time":80842.1052631578,"name":"HScript Call"},{"params":["aberrationCoolThing",""],"time":90947.3684210525,"name":"HScript Call"}]} \ No newline at end of file diff --git a/assets/songs/tutorial/charts/easy.json b/assets/songs/tutorial/charts/easy.json index 49a8146f..6f8aad51 100644 --- a/assets/songs/tutorial/charts/easy.json +++ b/assets/songs/tutorial/charts/easy.json @@ -1 +1 @@ -{"codenameChart":true,"strumLines":[{"position":"girlfriend","notes":[{"id":0,"sLen":0,"time":9600,"type":0},{"id":3,"sLen":0,"time":10800,"type":0},{"id":0,"sLen":0,"time":12000,"type":0},{"id":3,"sLen":0,"time":13200,"type":0},{"id":2,"sLen":0,"time":19200,"type":0},{"id":1,"sLen":0,"time":20400,"type":0},{"id":2,"sLen":0,"time":21600,"type":0},{"id":1,"sLen":0,"time":22800,"type":0},{"id":0,"sLen":0,"time":28800,"type":0},{"id":2,"sLen":0,"time":30000,"type":0},{"id":1,"sLen":0,"time":31200,"type":0},{"id":3,"sLen":0,"time":32400,"type":0},{"id":1,"sLen":0,"time":38400,"type":0},{"id":1,"sLen":0,"time":39000,"type":0},{"id":2,"sLen":0,"time":39600,"type":0},{"id":1,"sLen":0,"time":40800,"type":0},{"id":1,"sLen":0,"time":41400,"type":0},{"id":3,"sLen":0,"time":42000,"type":0},{"id":1,"sLen":0,"time":48000,"type":0},{"id":2,"sLen":0,"time":48300,"type":0},{"id":3,"sLen":0,"time":48600,"type":0},{"id":2,"sLen":0,"time":48900,"type":0},{"id":3,"sLen":0,"time":49800,"type":0}],"visible":true,"type":0,"characters":["gf"]},{"position":"boyfriend","notes":[{"id":0,"sLen":0,"time":14400,"type":0},{"id":3,"sLen":0,"time":15600,"type":0},{"id":0,"sLen":0,"time":16800,"type":0},{"id":3,"sLen":0,"time":18000,"type":0},{"id":2,"sLen":0,"time":24000,"type":0},{"id":1,"sLen":0,"time":25200,"type":0},{"id":2,"sLen":0,"time":26400,"type":0},{"id":1,"sLen":0,"time":27600,"type":0},{"id":0,"sLen":0,"time":33600,"type":0},{"id":2,"sLen":0,"time":34800,"type":0},{"id":1,"sLen":0,"time":36000,"type":0},{"id":3,"sLen":0,"time":37200,"type":0},{"id":1,"sLen":0,"time":43200,"type":0},{"id":1,"sLen":0,"time":43800,"type":0},{"id":2,"sLen":0,"time":44400,"type":0},{"id":1,"sLen":0,"time":45600,"type":0},{"id":1,"sLen":0,"time":46200,"type":0},{"id":3,"sLen":0,"time":46800,"type":0},{"id":1,"sLen":0,"time":50400,"type":0},{"id":2,"sLen":0,"time":50700,"type":0},{"id":3,"sLen":0,"time":51000,"type":0},{"id":2,"sLen":0,"time":51300,"type":0},{"id":3,"sLen":0,"time":52200,"type":0},{"id":3,"sLen":0,"time":52800,"type":0},{"id":1,"sLen":0,"time":53400,"type":0},{"id":0,"sLen":0,"time":54000,"type":0},{"id":1,"sLen":0,"time":54600,"type":0},{"id":2,"sLen":0,"time":55200,"type":0},{"id":3,"sLen":0,"time":55800,"type":0},{"id":0,"sLen":0,"time":56400,"type":0},{"id":2,"sLen":0,"time":57000,"type":0},{"id":1,"sLen":750,"time":57600,"type":0}],"visible":true,"type":1,"characters":["bf"]}],"scrollSpeed":1,"noteTypes":[]} \ No newline at end of file +{"events":[{"params":[1],"name":"Camera Movement","time":0},{"params":[0],"name":"Camera Movement","time":9600},{"params":[1],"name":"Camera Movement","time":14400},{"params":[0],"name":"Camera Movement","time":19200},{"params":[1],"name":"Camera Movement","time":24000},{"params":[0],"name":"Camera Movement","time":28800},{"params":[1],"name":"Camera Movement","time":33600},{"params":[0],"name":"Camera Movement","time":38400},{"params":[1],"name":"Camera Movement","time":43200},{"params":[0],"name":"Camera Movement","time":48000},{"params":[1],"name":"Camera Movement","time":50400}],"codenameChart":true,"strumLines":[{"position":"girlfriend","notes":[{"id":0,"sLen":0,"time":9600,"type":0},{"id":3,"sLen":0,"time":10800,"type":0},{"id":0,"sLen":0,"time":12000,"type":0},{"id":3,"sLen":0,"time":13200,"type":0},{"id":2,"sLen":0,"time":19200,"type":0},{"id":1,"sLen":0,"time":20400,"type":0},{"id":2,"sLen":0,"time":21600,"type":0},{"id":1,"sLen":0,"time":22800,"type":0},{"id":0,"sLen":0,"time":28800,"type":0},{"id":2,"sLen":0,"time":30000,"type":0},{"id":1,"sLen":0,"time":31200,"type":0},{"id":3,"sLen":0,"time":32400,"type":0},{"id":1,"sLen":0,"time":38400,"type":0},{"id":1,"sLen":0,"time":39000,"type":0},{"id":2,"sLen":0,"time":39600,"type":0},{"id":1,"sLen":0,"time":40800,"type":0},{"id":1,"sLen":0,"time":41400,"type":0},{"id":3,"sLen":0,"time":42000,"type":0},{"id":1,"sLen":0,"time":48000,"type":0},{"id":2,"sLen":0,"time":48300,"type":0},{"id":3,"sLen":0,"time":48600,"type":0},{"id":2,"sLen":0,"time":48900,"type":0},{"id":3,"sLen":0,"time":49800,"type":0}],"visible":true,"type":0,"characters":["gf"]},{"position":"boyfriend","notes":[{"id":0,"sLen":0,"time":14400,"type":0},{"id":3,"sLen":0,"time":15600,"type":0},{"id":0,"sLen":0,"time":16800,"type":0},{"id":3,"sLen":0,"time":18000,"type":0},{"id":2,"sLen":0,"time":24000,"type":0},{"id":1,"sLen":0,"time":25200,"type":0},{"id":2,"sLen":0,"time":26400,"type":0},{"id":1,"sLen":0,"time":27600,"type":0},{"id":0,"sLen":0,"time":33600,"type":0},{"id":2,"sLen":0,"time":34800,"type":0},{"id":1,"sLen":0,"time":36000,"type":0},{"id":3,"sLen":0,"time":37200,"type":0},{"id":1,"sLen":0,"time":43200,"type":0},{"id":1,"sLen":0,"time":43800,"type":0},{"id":2,"sLen":0,"time":44400,"type":0},{"id":1,"sLen":0,"time":45600,"type":0},{"id":1,"sLen":0,"time":46200,"type":0},{"id":3,"sLen":0,"time":46800,"type":0},{"id":1,"sLen":0,"time":50400,"type":0},{"id":2,"sLen":0,"time":50700,"type":0},{"id":3,"sLen":0,"time":51000,"type":0},{"id":2,"sLen":0,"time":51300,"type":0},{"id":3,"sLen":0,"time":52200,"type":0},{"id":3,"sLen":0,"time":52800,"type":0},{"id":1,"sLen":0,"time":53400,"type":0},{"id":0,"sLen":0,"time":54000,"type":0},{"id":1,"sLen":0,"time":54600,"type":0},{"id":2,"sLen":0,"time":55200,"type":0},{"id":3,"sLen":0,"time":55800,"type":0},{"id":0,"sLen":0,"time":56400,"type":0},{"id":2,"sLen":0,"time":57000,"type":0},{"id":1,"sLen":750,"time":57600,"type":0}],"visible":true,"type":1,"characters":["bf"]}],"scrollSpeed":1,"noteTypes":[]} \ No newline at end of file diff --git a/assets/songs/tutorial/charts/hard.json b/assets/songs/tutorial/charts/hard.json index c417029f..81f479b9 100644 --- a/assets/songs/tutorial/charts/hard.json +++ b/assets/songs/tutorial/charts/hard.json @@ -1 +1 @@ -{"codenameChart":true,"strumLines":[{"position":"girlfriend","notes":[{"id":0,"sLen":0,"time":9600,"type":0},{"id":3,"sLen":0,"time":10800,"type":0},{"id":0,"sLen":0,"time":12000,"type":0},{"id":3,"sLen":0,"time":13200,"type":0},{"id":2,"sLen":0,"time":19200,"type":0},{"id":1,"sLen":0,"time":20400,"type":0},{"id":2,"sLen":0,"time":21600,"type":0},{"id":1,"sLen":0,"time":22800,"type":0},{"id":0,"sLen":0,"time":28800,"type":0},{"id":2,"sLen":0,"time":30000,"type":0},{"id":1,"sLen":0,"time":31200,"type":0},{"id":3,"sLen":0,"time":32400,"type":0},{"id":1,"sLen":0,"time":38400,"type":0},{"id":1,"sLen":0,"time":39000,"type":0},{"id":2,"sLen":0,"time":39600,"type":0},{"id":1,"sLen":0,"time":40800,"type":0},{"id":1,"sLen":0,"time":41400,"type":0},{"id":3,"sLen":0,"time":42000,"type":0},{"id":1,"sLen":0,"time":48000,"type":0},{"id":2,"sLen":0,"time":48300,"type":0},{"id":3,"sLen":0,"time":48600,"type":0},{"id":2,"sLen":0,"time":48900,"type":0},{"id":3,"sLen":0,"time":49800,"type":0}],"visible":true,"type":0,"characters":["gf"]},{"position":"boyfriend","notes":[{"id":0,"sLen":0,"time":14400,"type":0},{"id":3,"sLen":0,"time":15600,"type":0},{"id":0,"sLen":0,"time":16800,"type":0},{"id":3,"sLen":0,"time":18000,"type":0},{"id":2,"sLen":0,"time":24000,"type":0},{"id":1,"sLen":0,"time":25200,"type":0},{"id":2,"sLen":0,"time":26400,"type":0},{"id":1,"sLen":0,"time":27600,"type":0},{"id":0,"sLen":0,"time":33600,"type":0},{"id":2,"sLen":0,"time":34800,"type":0},{"id":1,"sLen":0,"time":36000,"type":0},{"id":3,"sLen":0,"time":37200,"type":0},{"id":1,"sLen":0,"time":43200,"type":0},{"id":1,"sLen":0,"time":43800,"type":0},{"id":2,"sLen":0,"time":44400,"type":0},{"id":1,"sLen":0,"time":45600,"type":0},{"id":1,"sLen":0,"time":46200,"type":0},{"id":3,"sLen":0,"time":46800,"type":0},{"id":1,"sLen":0,"time":50400,"type":0},{"id":2,"sLen":0,"time":50700,"type":0},{"id":3,"sLen":0,"time":51000,"type":0},{"id":2,"sLen":0,"time":51300,"type":0},{"id":3,"sLen":0,"time":52200,"type":0},{"id":3,"sLen":0,"time":52800,"type":0},{"id":1,"sLen":0,"time":53400,"type":0},{"id":0,"sLen":0,"time":54000,"type":0},{"id":1,"sLen":0,"time":54600,"type":0},{"id":2,"sLen":0,"time":55200,"type":0},{"id":3,"sLen":0,"time":55800,"type":0},{"id":0,"sLen":0,"time":56400,"type":0},{"id":2,"sLen":0,"time":57000,"type":0},{"id":1,"sLen":750,"time":57600,"type":0},{"id":0,"sLen":0,"time":62400,"type":0},{"id":1,"sLen":0,"time":62550,"type":0},{"id":2,"sLen":0,"time":62700,"type":0},{"id":3,"sLen":0,"time":62850,"type":0},{"id":2,"sLen":0,"time":63000,"type":0},{"id":1,"sLen":0,"time":63150,"type":0},{"id":0,"sLen":0,"time":63300,"type":0},{"id":1,"sLen":0,"time":63450,"type":0},{"id":2,"sLen":0,"time":63600,"type":0},{"id":3,"sLen":0,"time":63750,"type":0},{"id":2,"sLen":0,"time":63900,"type":0},{"id":3,"sLen":0,"time":64050,"type":0},{"id":2,"sLen":0,"time":64200,"type":0},{"id":3,"sLen":0,"time":64350,"type":0},{"id":1,"sLen":0,"time":64500,"type":0},{"id":0,"sLen":0,"time":64650,"type":0}],"visible":true,"type":1,"characters":["bf"]}],"scrollSpeed":1,"noteTypes":[]} \ No newline at end of file +{"events":[{"params":[1],"name":"Camera Movement","time":0},{"params":[0],"name":"Camera Movement","time":9600},{"params":[1],"name":"Camera Movement","time":14400},{"params":[0],"name":"Camera Movement","time":19200},{"params":[1],"name":"Camera Movement","time":24000},{"params":[0],"name":"Camera Movement","time":28800},{"params":[1],"name":"Camera Movement","time":33600},{"params":[0],"name":"Camera Movement","time":38400},{"params":[1],"name":"Camera Movement","time":43200},{"params":[0],"name":"Camera Movement","time":48000},{"params":[1],"name":"Camera Movement","time":50400}],"codenameChart":true,"scrollSpeed":1,"strumLines":[{"keyCount":4,"notes":[{"id":0,"sLen":0,"time":9600,"type":0},{"id":3,"sLen":0,"time":10800,"type":0},{"id":0,"sLen":0,"time":12000,"type":0},{"id":3,"sLen":0,"time":13200,"type":0},{"id":2,"sLen":0,"time":19200,"type":0},{"id":1,"sLen":0,"time":20400,"type":0},{"id":2,"sLen":0,"time":21600,"type":0},{"id":1,"sLen":0,"time":22800,"type":0},{"id":0,"sLen":0,"time":28800,"type":0},{"id":2,"sLen":0,"time":30000,"type":0},{"id":1,"sLen":0,"time":31200,"type":0},{"id":3,"sLen":0,"time":32400,"type":0},{"id":1,"sLen":0,"time":38400,"type":0},{"id":1,"sLen":0,"time":39000,"type":0},{"id":2,"sLen":0,"time":39600,"type":0},{"id":1,"sLen":0,"time":40800,"type":0},{"id":1,"sLen":0,"time":41400,"type":0},{"id":3,"sLen":0,"time":42000,"type":0},{"id":1,"sLen":0,"time":48000,"type":0},{"id":2,"sLen":0,"time":48300,"type":0},{"id":3,"sLen":0,"time":48600,"type":0},{"id":2,"sLen":0,"time":48900,"type":0},{"id":3,"sLen":0,"time":49800,"type":0}],"position":"girlfriend","characters":["gf"],"visible":true,"type":0},{"keyCount":4,"notes":[{"id":0,"sLen":0,"time":14400,"type":0},{"id":3,"sLen":0,"time":15600,"type":0},{"id":0,"sLen":0,"time":16800,"type":0},{"id":3,"sLen":0,"time":18000,"type":0},{"id":2,"sLen":0,"time":24000,"type":0},{"id":1,"sLen":0,"time":25200,"type":0},{"id":2,"sLen":0,"time":26400,"type":0},{"id":1,"sLen":0,"time":27600,"type":0},{"id":0,"sLen":0,"time":33600,"type":0},{"id":2,"sLen":0,"time":34800,"type":0},{"id":1,"sLen":0,"time":36000,"type":0},{"id":3,"sLen":0,"time":37200,"type":0},{"id":1,"sLen":0,"time":43200,"type":0},{"id":1,"sLen":0,"time":43800,"type":0},{"id":2,"sLen":0,"time":44400,"type":0},{"id":1,"sLen":0,"time":45600,"type":0},{"id":1,"sLen":0,"time":46200,"type":0},{"id":3,"sLen":0,"time":46800,"type":0},{"id":1,"sLen":0,"time":50400,"type":0},{"id":2,"sLen":0,"time":50700,"type":0},{"id":3,"sLen":0,"time":51000,"type":0},{"id":2,"sLen":0,"time":51300,"type":0},{"id":3,"sLen":0,"time":52200,"type":0},{"id":3,"sLen":0,"time":52800,"type":0},{"id":1,"sLen":0,"time":53400,"type":0},{"id":0,"sLen":0,"time":54000,"type":0},{"id":1,"sLen":0,"time":54600,"type":0},{"id":2,"sLen":0,"time":55200,"type":0},{"id":3,"sLen":0,"time":55800,"type":0},{"id":0,"sLen":0,"time":56400,"type":0},{"id":2,"sLen":0,"time":57000,"type":0},{"id":1,"sLen":750,"time":57600,"type":0},{"id":0,"sLen":0,"time":62400,"type":0},{"id":1,"sLen":0,"time":62550,"type":0},{"id":2,"sLen":0,"time":62700,"type":0},{"id":3,"sLen":0,"time":62850,"type":0},{"id":2,"sLen":0,"time":63000,"type":0},{"id":1,"sLen":0,"time":63150,"type":0},{"id":0,"sLen":0,"time":63300,"type":0},{"id":1,"sLen":0,"time":63450,"type":0},{"id":2,"sLen":0,"time":63600,"type":0},{"id":3,"sLen":0,"time":63750,"type":0},{"id":2,"sLen":0,"time":63900,"type":0},{"id":3,"sLen":0,"time":64050,"type":0},{"id":2,"sLen":0,"time":64200,"type":0},{"id":3,"sLen":0,"time":64350,"type":0},{"id":1,"sLen":0,"time":64500,"type":0},{"id":0,"sLen":0,"time":64650,"type":0}],"position":"boyfriend","characters":["bf"],"visible":true,"type":1}],"chartVersion":"1.6.0","noteTypes":[]} \ No newline at end of file diff --git a/assets/songs/tutorial/charts/normal.json b/assets/songs/tutorial/charts/normal.json index 49a8146f..6f8aad51 100644 --- a/assets/songs/tutorial/charts/normal.json +++ b/assets/songs/tutorial/charts/normal.json @@ -1 +1 @@ -{"codenameChart":true,"strumLines":[{"position":"girlfriend","notes":[{"id":0,"sLen":0,"time":9600,"type":0},{"id":3,"sLen":0,"time":10800,"type":0},{"id":0,"sLen":0,"time":12000,"type":0},{"id":3,"sLen":0,"time":13200,"type":0},{"id":2,"sLen":0,"time":19200,"type":0},{"id":1,"sLen":0,"time":20400,"type":0},{"id":2,"sLen":0,"time":21600,"type":0},{"id":1,"sLen":0,"time":22800,"type":0},{"id":0,"sLen":0,"time":28800,"type":0},{"id":2,"sLen":0,"time":30000,"type":0},{"id":1,"sLen":0,"time":31200,"type":0},{"id":3,"sLen":0,"time":32400,"type":0},{"id":1,"sLen":0,"time":38400,"type":0},{"id":1,"sLen":0,"time":39000,"type":0},{"id":2,"sLen":0,"time":39600,"type":0},{"id":1,"sLen":0,"time":40800,"type":0},{"id":1,"sLen":0,"time":41400,"type":0},{"id":3,"sLen":0,"time":42000,"type":0},{"id":1,"sLen":0,"time":48000,"type":0},{"id":2,"sLen":0,"time":48300,"type":0},{"id":3,"sLen":0,"time":48600,"type":0},{"id":2,"sLen":0,"time":48900,"type":0},{"id":3,"sLen":0,"time":49800,"type":0}],"visible":true,"type":0,"characters":["gf"]},{"position":"boyfriend","notes":[{"id":0,"sLen":0,"time":14400,"type":0},{"id":3,"sLen":0,"time":15600,"type":0},{"id":0,"sLen":0,"time":16800,"type":0},{"id":3,"sLen":0,"time":18000,"type":0},{"id":2,"sLen":0,"time":24000,"type":0},{"id":1,"sLen":0,"time":25200,"type":0},{"id":2,"sLen":0,"time":26400,"type":0},{"id":1,"sLen":0,"time":27600,"type":0},{"id":0,"sLen":0,"time":33600,"type":0},{"id":2,"sLen":0,"time":34800,"type":0},{"id":1,"sLen":0,"time":36000,"type":0},{"id":3,"sLen":0,"time":37200,"type":0},{"id":1,"sLen":0,"time":43200,"type":0},{"id":1,"sLen":0,"time":43800,"type":0},{"id":2,"sLen":0,"time":44400,"type":0},{"id":1,"sLen":0,"time":45600,"type":0},{"id":1,"sLen":0,"time":46200,"type":0},{"id":3,"sLen":0,"time":46800,"type":0},{"id":1,"sLen":0,"time":50400,"type":0},{"id":2,"sLen":0,"time":50700,"type":0},{"id":3,"sLen":0,"time":51000,"type":0},{"id":2,"sLen":0,"time":51300,"type":0},{"id":3,"sLen":0,"time":52200,"type":0},{"id":3,"sLen":0,"time":52800,"type":0},{"id":1,"sLen":0,"time":53400,"type":0},{"id":0,"sLen":0,"time":54000,"type":0},{"id":1,"sLen":0,"time":54600,"type":0},{"id":2,"sLen":0,"time":55200,"type":0},{"id":3,"sLen":0,"time":55800,"type":0},{"id":0,"sLen":0,"time":56400,"type":0},{"id":2,"sLen":0,"time":57000,"type":0},{"id":1,"sLen":750,"time":57600,"type":0}],"visible":true,"type":1,"characters":["bf"]}],"scrollSpeed":1,"noteTypes":[]} \ No newline at end of file +{"events":[{"params":[1],"name":"Camera Movement","time":0},{"params":[0],"name":"Camera Movement","time":9600},{"params":[1],"name":"Camera Movement","time":14400},{"params":[0],"name":"Camera Movement","time":19200},{"params":[1],"name":"Camera Movement","time":24000},{"params":[0],"name":"Camera Movement","time":28800},{"params":[1],"name":"Camera Movement","time":33600},{"params":[0],"name":"Camera Movement","time":38400},{"params":[1],"name":"Camera Movement","time":43200},{"params":[0],"name":"Camera Movement","time":48000},{"params":[1],"name":"Camera Movement","time":50400}],"codenameChart":true,"strumLines":[{"position":"girlfriend","notes":[{"id":0,"sLen":0,"time":9600,"type":0},{"id":3,"sLen":0,"time":10800,"type":0},{"id":0,"sLen":0,"time":12000,"type":0},{"id":3,"sLen":0,"time":13200,"type":0},{"id":2,"sLen":0,"time":19200,"type":0},{"id":1,"sLen":0,"time":20400,"type":0},{"id":2,"sLen":0,"time":21600,"type":0},{"id":1,"sLen":0,"time":22800,"type":0},{"id":0,"sLen":0,"time":28800,"type":0},{"id":2,"sLen":0,"time":30000,"type":0},{"id":1,"sLen":0,"time":31200,"type":0},{"id":3,"sLen":0,"time":32400,"type":0},{"id":1,"sLen":0,"time":38400,"type":0},{"id":1,"sLen":0,"time":39000,"type":0},{"id":2,"sLen":0,"time":39600,"type":0},{"id":1,"sLen":0,"time":40800,"type":0},{"id":1,"sLen":0,"time":41400,"type":0},{"id":3,"sLen":0,"time":42000,"type":0},{"id":1,"sLen":0,"time":48000,"type":0},{"id":2,"sLen":0,"time":48300,"type":0},{"id":3,"sLen":0,"time":48600,"type":0},{"id":2,"sLen":0,"time":48900,"type":0},{"id":3,"sLen":0,"time":49800,"type":0}],"visible":true,"type":0,"characters":["gf"]},{"position":"boyfriend","notes":[{"id":0,"sLen":0,"time":14400,"type":0},{"id":3,"sLen":0,"time":15600,"type":0},{"id":0,"sLen":0,"time":16800,"type":0},{"id":3,"sLen":0,"time":18000,"type":0},{"id":2,"sLen":0,"time":24000,"type":0},{"id":1,"sLen":0,"time":25200,"type":0},{"id":2,"sLen":0,"time":26400,"type":0},{"id":1,"sLen":0,"time":27600,"type":0},{"id":0,"sLen":0,"time":33600,"type":0},{"id":2,"sLen":0,"time":34800,"type":0},{"id":1,"sLen":0,"time":36000,"type":0},{"id":3,"sLen":0,"time":37200,"type":0},{"id":1,"sLen":0,"time":43200,"type":0},{"id":1,"sLen":0,"time":43800,"type":0},{"id":2,"sLen":0,"time":44400,"type":0},{"id":1,"sLen":0,"time":45600,"type":0},{"id":1,"sLen":0,"time":46200,"type":0},{"id":3,"sLen":0,"time":46800,"type":0},{"id":1,"sLen":0,"time":50400,"type":0},{"id":2,"sLen":0,"time":50700,"type":0},{"id":3,"sLen":0,"time":51000,"type":0},{"id":2,"sLen":0,"time":51300,"type":0},{"id":3,"sLen":0,"time":52200,"type":0},{"id":3,"sLen":0,"time":52800,"type":0},{"id":1,"sLen":0,"time":53400,"type":0},{"id":0,"sLen":0,"time":54000,"type":0},{"id":1,"sLen":0,"time":54600,"type":0},{"id":2,"sLen":0,"time":55200,"type":0},{"id":3,"sLen":0,"time":55800,"type":0},{"id":0,"sLen":0,"time":56400,"type":0},{"id":2,"sLen":0,"time":57000,"type":0},{"id":1,"sLen":750,"time":57600,"type":0}],"visible":true,"type":1,"characters":["bf"]}],"scrollSpeed":1,"noteTypes":[]} \ No newline at end of file diff --git a/assets/songs/tutorial/events.json b/assets/songs/tutorial/events.json index a97b4b89..bb95d83e 100644 --- a/assets/songs/tutorial/events.json +++ b/assets/songs/tutorial/events.json @@ -1 +1 @@ -{"events":[{"params":[1],"name":"Camera Movement","time":0},{"params":[0],"name":"Camera Movement","time":9600},{"params":[1],"name":"Camera Movement","time":14400},{"params":[0,"cheer"],"name":"Play Animation","time":18000},{"params":[1,"hey"],"name":"Play Animation","time":18600},{"params":[0],"name":"Camera Movement","time":19200},{"params":[1],"name":"Camera Movement","time":24000},{"params":[0,"cheer"],"time":27600,"name":"Play Animation"},{"params":[1,"hey"],"time":28200,"name":"Play Animation"},{"params":[0],"name":"Camera Movement","time":28800},{"params":[1],"name":"Camera Movement","time":33600},{"params":[0],"name":"Camera Movement","time":38400},{"params":[1],"name":"Camera Movement","time":43200},{"params":[0],"name":"Camera Movement","time":48000},{"params":[1],"name":"Camera Movement","time":50400}]} \ No newline at end of file +{"events":[{"params":[0,"cheer"],"name":"Play Animation","time":18000},{"params":[1,"hey"],"name":"Play Animation","time":18600},{"params":[0,"cheer"],"time":27600,"name":"Play Animation"},{"params":[1,"hey"],"time":28200,"name":"Play Animation"}]} \ No newline at end of file diff --git a/assets/sounds/sfxTodo.md b/assets/sounds/sfxTodo.md new file mode 100644 index 00000000..74fdf30e --- /dev/null +++ b/assets/sounds/sfxTodo.md @@ -0,0 +1,54 @@ +List of SFX to-do for editors, +maybe some other menus will need them in the future, +but for now just editors! + +Putting this txt in here so others can edit and commit this to suggest or add missing sfx! +- Cass (GlitchDotSmh) + +[] = Not done +[!] = Priority/In progress +[x] = Done + +# Editor SFX + +## Global: +- Opening/Closing Context/Dropdown Menus [] +- Opening/Closing Pop-up Windows [] +- Mouse Click [x] +- UI Button being clicked [] +- Focusing/Unfocusing a text field [] +- Typing in a text field [] +- Saving [] +- Auto-Saving [] +- Copying [] +- Pasting [] +- Undoing [] +- Redoing [] +- Deleting [] + +## Chart Editor: +- Placing Notes [] +- Deleting Notes [] +- Subtracting Sustain [] +- Adding Sustain [] +- Picking Up Notes [] +- Moving Notes [] +- Dropping Notes [] +- Selecting Notes (via `CTRL + Drag` or `CTRL + A`) [] +- Hitsounds [] +- Metronome [] +- Creating an Event [] +- Pulling up Event Editing menu [] +- Scrolling Up/Down (via `Scroll Wheel` or `W/S`) [] +- Note Snapping Change [] +- Locking Strums [] +- Unlocking Strums [] +- Creating Strum [] + +## Character/Stage Editor +- Selecting a Stage asset [] +- Switching Stage [] +- Changing Offset (via `Arrows`, `WASD`, or `Mouse Drag`) [] +- Enabling/Disabling Ghost Animation [] + + diff --git a/commandline/ArgParser.hx b/commandline/ArgParser.hx index 61ff15b8..94ddcaac 100644 --- a/commandline/ArgParser.hx +++ b/commandline/ArgParser.hx @@ -17,18 +17,18 @@ class ArgParser { this.args = args; this.options = new Map(); - var copy = args.copy(); + final copy = args.copy(); var i = 0; while(copy.length > 0) { var arg = copy.shift(); // this parses the -NAME=VALUE if (arg.startsWith("-")) { var key = arg.substr(1); - var isLongKey = key.startsWith("-"); + final isLongKey = key.startsWith("-"); if (isLongKey) { key = key.substr(1); } - var split = key.split("="); + final split = key.split("="); var longName = split.shift(); if(!isLongKey) { // Allow -ABC to be parsed as -A -B -C @@ -43,8 +43,8 @@ class ArgParser { // Parse the last option with value support } - var name = rename(longName); - var value = (split.length > 0) ? split.join("=") : null; + final name = rename(longName); + final value = (split.length > 0) ? split.join("=") : null; options.set(name, value); args.splice(i, 1); diff --git a/commandline/Main.hx b/commandline/Main.hx index fc0c7333..beb170f0 100644 --- a/commandline/Main.hx +++ b/commandline/Main.hx @@ -49,7 +49,8 @@ class Main { "If you want a full build which contains all assets, run \"cne release\" or \"cne test-release\"", "Additional arguments will be sent to the lime compiler.", "", - "-debug : Builds a debug build." + "-debug : Builds a debug build.", + "-clean : Compiled files will be deleted before compiling." ].join("\n") }, { @@ -67,7 +68,20 @@ class Main { "If you want a full build which contains all assets, run \"cne release\" or \"cne test-release\"", "Additional arguments will be sent to the lime compiler.", "", - "-debug : Builds a debug build." + "-debug : Builds a debug build.", + "-clean : Compiled files will be deleted before compiling." + ].join("\n") + }, + { + names: ["run"], + doc: "Runs the last build that was created.", + func: Compiler.run, + dDoc: [ + "Usage: run <optional arguments>", + "", + "This will run the last build that was created.", + "If the last build was a debug build, you need to pass the -debug argument to run it.", + "Additional arguments will be sent to the lime compiler." ].join("\n") }, { @@ -79,7 +93,9 @@ class Main { "", "This will create and run a final ready-for-release build,", "which means this build will be able to be release on websites such as GameBanana without worrying about source-dependant stuff.", - "Additional arguments will be sent to the lime compiler." + "Additional arguments will be sent to the lime compiler.", + "", + "-clean : Compiled files will be deleted before compiling." ].join("\n") }, { @@ -91,7 +107,9 @@ class Main { "", "This will create and run a final ready-for-release build,", "which means this build will be able to be release on websites such as GameBanana without worrying about source-dependant stuff.", - "Additional arguments will be sent to the lime compiler." + "Additional arguments will be sent to the lime compiler.", + "", + "-clean : Compiled files will be deleted before compiling." ].join("\n") }, { @@ -113,7 +131,7 @@ class Main { public static function main() { initCommands(); - var args = Sys.args(); + final args = Sys.args(); var commandName = args.shift(); if (commandName != null) commandName = commandName.toLowerCase(); diff --git a/commandline/Utils.hx b/commandline/Utils.hx index 20216822..3cc94a40 100644 --- a/commandline/Utils.hx +++ b/commandline/Utils.hx @@ -17,11 +17,11 @@ class Utils { condition = condition.replace('&&', '&').replace('||', '|').replace(" ", ""); - var stack:Array<Dynamic> = []; + final stack:Array<Dynamic> = []; var i = 0; while (i < condition.length) { - var char = condition.charAt(i); + final char = condition.charAt(i); switch (char) { case '(': @@ -32,7 +32,7 @@ class Utils { if (condition.charAt(j) == '(') balance++; if (condition.charAt(j) == ')') balance--; } - var subExpr = condition.substr(i + 1, j - i - 1); + final subExpr = condition.substr(i + 1, j - i - 1); stack.push(evaluateArgsCondition(subExpr, args)); i = j; case '&', '|': @@ -55,8 +55,8 @@ class Utils { public static function evaluate(stack:Array<Dynamic>):Bool { var result = stack.shift(); while (stack.length > 0) { - var op = stack.shift(); - var next = stack.shift(); + final op = stack.shift(); + final next = stack.shift(); switch(op) { case '&': result = result && next; diff --git a/commandline/commands/Compiler.hx b/commandline/commands/Compiler.hx index 25ff5f06..671c7ffa 100644 --- a/commandline/commands/Compiler.hx +++ b/commandline/commands/Compiler.hx @@ -2,21 +2,26 @@ package commands; class Compiler { public static function test(args:Array<String>) { - __build(args, ["test", getBuildTarget(), "-DTEST_BUILD"]); + __runLime(args, ["test", getBuildTarget(), "-DTEST_BUILD"]); } public static function build(args:Array<String>) { - __build(args, ["build", getBuildTarget(), "-DTEST_BUILD"]); + __runLime(args, ["build", getBuildTarget(), "-DTEST_BUILD"]); } public static function release(args:Array<String>) { - __build(args, ["build", getBuildTarget()]); + __runLime(args, ["build", getBuildTarget()]); } public static function testRelease(args:Array<String>) { - __build(args, ["test", getBuildTarget()]); + __runLime(args, ["test", getBuildTarget()]); + } + public static function run(args:Array<String>) { + __runLime(args, ["run", getBuildTarget()]); } - private static function __build(args:Array<String>, arg:Array<String>) { - for (a in args) arg.push(a); - arg = ['run', 'lime'].concat(arg); + private static function __runLime(args:Array<String>, arg:Array<String>) { + arg.insert(0, "lime"); + arg.insert(0, "run"); + for(a in args) + arg.push(a); Sys.command("haxelib", arg); } diff --git a/commandline/commands/Optimizer.hx b/commandline/commands/Optimizer.hx index b82d9182..a30d2949 100644 --- a/commandline/commands/Optimizer.hx +++ b/commandline/commands/Optimizer.hx @@ -4,17 +4,17 @@ import sys.io.File; class Optimizer { public static function main(args:Array<String>) { - var args = ArgParser.parse(args); - var saveOld = args.existsOption("no-old"); + final args = ArgParser.parse(args); + final saveOld = !args.existsOption("no-old"); if(args.length == 0) { Sys.println(Main.curCommand.dDoc); return; } - var filename = args.get(0); + final filename = args.get(0); - var data = File.getContent(filename); + final data = File.getContent(filename); var json = null; try { json = haxe.Json.parse(data); diff --git a/commandline/commands/Setup.hx b/commandline/commands/Setup.hx index 600211ab..2ddac7eb 100644 --- a/commandline/commands/Setup.hx +++ b/commandline/commands/Setup.hx @@ -44,6 +44,7 @@ class Setup { var REINSTALL_ALL = args.existsOption("reinstall"); var SILENT = args.existsOption("silent-progress"); var FAST = args.existsOption("fast"); + // TODO: add only install missing libs // to prevent messing with currently installed libs if (!FileSystem.exists('.haxelib')) @@ -67,13 +68,13 @@ class Setup { return; } - var events:Array<Event> = []; - var libsXML:Access = new Access(Xml.parse(File.getContent(libFile)).firstElement()); + final events:Array<Event> = []; + final libsXML:Access = new Access(Xml.parse(File.getContent(libFile)).firstElement()); function handleLib(libNode:Access) { switch(libNode.name) { case "lib" | "git": - var lib:Library = { + final lib:Library = { name: libNode.att.name, type: libNode.name, skipDeps: libNode.has.skipDeps ? libNode.att.skipDeps == "true" : false, @@ -122,15 +123,16 @@ class Setup { } } - var platform = switch(Sys.systemName()) { + final platform = switch(Sys.systemName()) { case "Windows": "windows"; case "Mac": "mac"; case "Linux": "linux"; case def: def.toLowerCase(); } - var defines = args.args.copy(); + final defines = args.args.copy(); defines.push(platform); + defines.push("true"); if(args.args.length == 0) defines.push("all"); function parse(libNode:Access) { @@ -148,6 +150,8 @@ class Setup { } } + final shouldCheckHaxeVersion = !libsXML.has.checkVersion || Utils.evaluateArgsCondition(libsXML.att.checkVersion, defines); + for (libNode in libsXML.elements) { parse(libNode); } @@ -199,10 +203,10 @@ class Setup { } var oldCwd = Sys.getCwd(); if(lib != null) { - var libPrefix = '.haxelib/$lib'; + final libPrefix = '.haxelib/$lib'; if(FileSystem.exists(libPrefix)) { if(FileSystem.exists(libPrefix + '/.dev')) { // haxelib dev - var devPath = File.getContent(libPrefix + '/.dev'); + final devPath = File.getContent(libPrefix + '/.dev'); if(!FileSystem.exists(devPath)) { Sys.println('Cannot find dev path $devPath for $lib'); Sys.setCwd(oldCwd); @@ -210,7 +214,7 @@ class Setup { } Sys.setCwd(devPath + dir); } else if(FileSystem.exists(libPrefix + '/.current')) { - var version = StringTools.replace(File.getContent(libPrefix + '/.current'), ".", ","); + final version = StringTools.replace(File.getContent(libPrefix + '/.current'), ".", ","); if(!FileSystem.exists(libPrefix + '/$version')) { Sys.println('Cannot find version $version of $lib'); Sys.setCwd(oldCwd); @@ -225,13 +229,13 @@ class Setup { } } for(line in cmd.lines) { - var line = StringTools.replace(line, "$PLATFORM", platform); + final line = StringTools.replace(line, "$PLATFORM", platform); //Sys.println(line); command(line); } Sys.setCwd(oldCwd); case PRINT: - var data:PrintData = event.data; + final data:PrintData = event.data; if(data.pretty) prettyPrint(data.text); else @@ -239,30 +243,28 @@ class Setup { } } - /*var proc = new Process('haxe --version'); - proc.exitCode(true); - var haxeVer = proc.stdout.readLine(); - if (haxeVer != "4.2.5") { + if(shouldCheckHaxeVersion) { + final proc = new Process('haxe --version'); + proc.exitCode(true); + final haxeVer = proc.stdout.readLine(); + // check for outdated haxe - var curHaxeVer = [for(v in haxeVer.split(".")) Std.parseInt(v)]; - var requiredHaxeVer = [4, 2, 5]; - for(i in 0...requiredHaxeVer.length) { - if (curHaxeVer[i] < requiredHaxeVer[i]) { - prettyPrint("!! WARNING !!"); - Sys.println("Your current Haxe version is outdated."); - Sys.println('You\'re using ${haxeVer}, while the required version is 4.2.5.'); - Sys.println('The engine may not compile with your current version of Haxe.'); - Sys.println('We recommend upgrading to 4.2.5'); - break; - } else if (curHaxeVer[i] > requiredHaxeVer[i]) { - prettyPrint("!! WARNING !!" - + "\nUsing Haxe 4.3.0 and above is currently not recommended due to lack of testing."); - Sys.println(''); - Sys.println('We recommend downgrading back to 4.2.5.'); + final curHaxeVer = [for(v in haxeVer.split(".")) Std.parseInt(v)]; + final minumumVersion = [4, 2, 5]; + for(i in 0...minumumVersion.length) { + if (curHaxeVer[i] > minumumVersion[i]) break; + if (curHaxeVer[i] < minumumVersion[i]) { + prettyPrint([ + "!! WARNING !!", + "Your current Haxe version is outdated.", + 'You\'re using ${haxeVer}, whilst the required version is 4.2.5 or newer.', + 'The engine may not compile with your current version of Haxe.', + 'We recommend upgrading to 4.2.5 or newer' + ].join("\n")); break; } } - }*/ + } // vswhere.exe its used to find any visual studio related installations on the system, including full visual studio ide installations, visual studio build tools installations, and other related components - Nex if (CHECK_VSTUDIO && Compiler.getBuildTarget().toLowerCase() == "windows" && new Process('"C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe" -property catalog_productDisplayVersion').exitCode(true) == 1) { @@ -280,7 +282,7 @@ class Setup { } public static function prettyPrint(text:String) { - var lines = text.split("\n"); + final lines = text.split("\n"); var length = -1; for(line in lines) if(line.length > length) @@ -298,9 +300,9 @@ class Setup { public static function centerText(text:String, width:Int):String { - var centerOffset = (width - text.length) / 2; - var left = repeat(' ', Math.floor(centerOffset)); - var right = repeat(' ', Math.ceil(centerOffset)); + final centerOffset = (width - text.length) / 2; + final left = repeat(' ', Math.floor(centerOffset)); + final right = repeat(' ', Math.ceil(centerOffset)); return left + text + right; } diff --git a/libs.xml b/libs.xml index 891b4c69..beafb5ff 100644 --- a/libs.xml +++ b/libs.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<libraries> +<libraries checkVersion="update || all"> <if cond="update || all"> <print pretty="true">Preparing installation...</print> @@ -16,6 +16,7 @@ <git name="hscript-improved" url="https://github.com/CodenameCrew/hscript-improved" ref="codename-dev" /> <git name="flxanimate" url="https://github.com/CodenameCrew/cne-flxanimate" /> <git name="hxdiscord_rpc" url="https://github.com/CodenameCrew/cne-hxdiscord_rpc" skipDeps="true" /> + <git name="funkin-modchart" url="https://github.com/TheoDevelops/FunkinModchart" skipDeps="true" /> <lib name="hxvlc" version="1.9.3" skipDeps="true" /> <!-- Documentation and other features --> @@ -24,6 +25,7 @@ <git name="grig.audio" url="https://gitlab.com/haxe-grig/grig.audio.git" ref="57f5d47f2533fd0c3dcd025a86cb86c0dfa0b6d2" /> <!-- <lib name="dox" /> --> <lib name="markdown" /> + <lib name="hxcpp-debug-server" /> <lib name="nape-haxe4" /> diff --git a/project.xml b/project.xml index 68c34f54..31cffad6 100644 --- a/project.xml +++ b/project.xml @@ -28,7 +28,7 @@ <!-- ____________________________ Window Settings ___________________________ --> <!--These window settings apply to all targets--> - <window width="1280" height="720" fps="" background="#000000" hardware="true" vsync="false" /> + <window width="1280" height="720" background="#000000" hardware="true" vsync="false" /> <!--HTML5-specific--> <window if="html5" resizable="true" /> @@ -109,19 +109,25 @@ <!-- Comment this out to disable Discord RPC !--> <section if="cpp"> - <define name="DISCORD_RPC" if="desktop"/> + <!-- <define name="DISCORD_RPC" if="desktop"/> --> </section> + <!-- Uncomment this to achive faster compile times, will strip extraneous and commonly unused features. --> + <define name="STRIPPED_COMPILE" unless="debug"/> + + <!-- Comment this out to disable modcharting features --> + <define name="MODCHARTING_FEATURES" unless="STRIPPED_COMPILE" /> + <!-- Comment this out to disable GitHub API integration !--> <define name="GITHUB_API" unless="web || hl"/> <!-- Comment this out to prevent unused classes from being compiled. Reduces compilation time at the cost of limited HScript flexibility. !--> - <define name="COMPILE_ALL_CLASSES" /> + <define name="COMPILE_ALL_CLASSES" unless="STRIPPED_COMPILE" /> <!-- Comment this out to prevent extending classes in hscript. Reduces compilation time at the cost of limited HScript flexibility. !--> - <define name="CUSTOM_CLASSES" /> + <define name="CUSTOM_CLASSES" unless="STRIPPED_COMPILE" /> <!-- Comment this out to disable multithreading !--> <haxedef name="ALLOW_MULTITHREADING" unless="web || flash" /> @@ -142,13 +148,13 @@ <haxedef name="NDLLS_SUPPORTED" unless="web || iphonesim || hl" /> <!-- Comment this out to disable support for Away3D Flixel Integration (reduces compile times) !--> - <define name="THREE_D_SUPPORT" /> + <define name="THREE_D_SUPPORT" unless="STRIPPED_COMPILE" /> <!-- Comment this out to disable support for Funkin Audio Visualizer (reduces compile times) !--> - <define name="AUDIO_VIS_SUPPORT" /> + <define name="AUDIO_VIS_SUPPORT" unless="STRIPPED_COMPILE" /> <!-- Comment this out to disable support for Nape integration (flixel.addons.nape) (reduces compile times) !--> - <define name="NAPE_ENABLED" /> + <define name="NAPE_ENABLED" unless="STRIPPED_COMPILE" /> <!-- Comment this out to disable video cutscene support, in case of compilation errors. !--> <section if="cpp"> @@ -173,8 +179,6 @@ <haxedef name="SOFTCODED_CLASSES" if="SOFTCODED_CLASSES" /> <haxedef name="openfl_dpi_aware" if="openfl_dpi_aware" /> - <haxedef name="EXPERIMENTAL_FLXGRAPHIC_DESTROY_FIX" /> - <!-- _______________________________ Libraries ______________________________ --> <!-- KEEP BOTH OF THESE AS THERE ARE SOME PARTICULAR CASES! --> @@ -198,9 +202,18 @@ <haxelib name="hscript-improved" /> <haxelib name="hxdiscord_rpc" if="DISCORD_RPC"/> <haxelib name="markdown" /> + <haxelib name="hxcpp-debug-server" if="TEST_BUILD debug" /> <!-- _________________________________ Additional stuff _______________________________ --> + <section if="MODCHARTING_FEATURES"> + <haxedef name="FM_ENGINE" value="CODENAME"/> + <haxedef name="FM_ENGINE_VERSION" value="1.0"/> + + <haxelib name="funkin-modchart" /> + <haxeflag name="--macro" value="modchart.backend.macros.Macro.includeFiles()"/> + </section> + <section if="COMPILE_ALL_CLASSES"> <haxeflag name="-dce" value="no" /> <haxeflag name="--macro" value="funkin.backend.system.macros.Macros.addAdditionalClasses()" /> @@ -217,4 +230,7 @@ <haxedef name="HXCPP_CATCH_SEGV" /> <haxedef name="hscriptPos" /> <!-- for logging --> + + <!-- EXPERMENTAL FEATURES --> + <haxedef name="EXPERIMENTAL_FLXGRAPHIC_DESTROY_FIX" /> </project> diff --git a/setup-dev-branch.xml b/setup-dev-branch.xml index 64fb0c03..0835f21c 100644 --- a/setup-dev-branch.xml +++ b/setup-dev-branch.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<libraries> +<libraries checkVersion="false"> <if cond="time-fix"> <print pretty="true">Switching to branch 'time-fix'...</print> <!-- <cmd inLib="lime" cmd="git checkout time-fix" /> --> diff --git a/setup-dev.xml b/setup-dev.xml index e4c2fb1c..09bc60dd 100644 --- a/setup-dev.xml +++ b/setup-dev.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<libraries> +<libraries checkVersion="false"> <print pretty="true">Setting up dev libs...</print> <cmd> @@ -94,5 +94,20 @@ </cmd> </if> + <if cond="ne_eo"> + <!-- Ignore this its only for Ne_Eo --> + <cmd> + <line>haxelib dev lime ../../lime</line> + <line>haxelib dev openfl ../../openfl</line> + <line>haxelib dev flixel ../../flixel</line> + <line>haxelib dev flixel-addons ../../flixel-addons</line> + <line>haxelib dev hscript-improved ../../hscript-improved</line> + <line>haxelib dev flxanimate ../../flxanimate</line> + <!-- <line>haxelib dev hxdiscord_rpc ../../hxdiscord_rpc</line> --> + <line>haxelib dev away3d ../../away3d</line> + <line>haxelib dev hxcpp ../../../hxcpp</line> + </cmd> + </if> + <print pretty="true">Setup for dev libs complete!</print> </libraries> diff --git a/source/external/ExternalMac.hx b/source/external/ExternalMac.hx new file mode 100644 index 00000000..b25da9bf --- /dev/null +++ b/source/external/ExternalMac.hx @@ -0,0 +1,12 @@ +package external; + +#if (mac && cpp) +@:build(external.LinkerMacro.xml('external_code.xml')) +@:include('Mac.h') +@:native('ExternalMac') +extern class ExternalMac +{ + @:native('ExternalMac::setCursorIcon') + public static function setCursorIcon(icon:Int, customCursor:cpp.ConstCharStar, customX:Single, customY:Single):Bool; +} +#end diff --git a/source/external/LinkerMacro.hx b/source/external/LinkerMacro.hx new file mode 100644 index 00000000..3f3912b9 --- /dev/null +++ b/source/external/LinkerMacro.hx @@ -0,0 +1,40 @@ +package external; + +import haxe.io.Path; +import haxe.macro.Expr; +import haxe.macro.Context; +import haxe.xml.Printer; +import sys.FileSystem; + +class LinkerMacro +{ + public static macro function xml(filename:String = 'Build.xml'):Array<Field> + { + var pos:Position = Context.currentPos(); + + var sourcePath:String = Path.directory(Context.getPosInfos(pos).file); + + if (!Path.isAbsolute(sourcePath)) + sourcePath = FileSystem.absolutePath(sourcePath); + + sourcePath = Path.removeTrailingSlashes(Path.normalize(sourcePath)); + + var includeElement:Xml = Xml.createElement('include'); + + includeElement.set('name', Path.join([sourcePath, filename])); + + @:privateAccess + { + var printer:Printer = new Printer(true); + + printer.writeNode(includeElement, '\n'); + + Context.getLocalClass().get().meta.add(':buildXml', [{ + expr: EConst(CString(printer.output.toString())), + pos: pos + }], pos); + } + + return Context.getBuildFields(); + } +} \ No newline at end of file diff --git a/source/external/external_code.xml b/source/external/external_code.xml new file mode 100644 index 00000000..308b0d35 --- /dev/null +++ b/source/external/external_code.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xml> + <pragma once="true" /> + + <files id="haxe"> + <compilerflag value="-I${this_dir}/include/mac" if="macos" /> + </files> + + <files id="__main__"> + <compilerflag value="-I${this_dir}/include/mac" if="macos" /> + </files> + + <files id="external-code"> + <section if="macos"> + <compilerflag value="-I${this_dir}/include/mac" /> + + <file name="${this_dir}/src/mac/CursorHelper.mm" /> + <file name="${this_dir}/src/mac/Mac.mm" /> + </section> + </files> + + <target id="haxe"> + <section if="macos"> + <vflag name="-framework" value="IOKit" /> + <vflag name="-framework" value="AppKit" /> + <vflag name="-framework" value="Cocoa" /> + <vflag name="-framework" value="Foundation" /> + </section> + + <files id="external-code" /> + </target> +</xml> diff --git a/source/external/include/mac/CursorHelper.h b/source/external/include/mac/CursorHelper.h new file mode 100644 index 00000000..6045293a --- /dev/null +++ b/source/external/include/mac/CursorHelper.h @@ -0,0 +1,7 @@ +#import <Foundation/Foundation.h> + +@interface CursorHelper : NSObject + ++ (NSCursor *)getCursorForSelector:(SEL)selector defaultCursor:(NSCursor *)defaultCursor; + +@end \ No newline at end of file diff --git a/source/external/include/mac/Mac.h b/source/external/include/mac/Mac.h new file mode 100644 index 00000000..516b5050 --- /dev/null +++ b/source/external/include/mac/Mac.h @@ -0,0 +1,13 @@ +#ifndef MAC_H +#define MAC_H + +#if defined(__APPLE__) + +namespace ExternalMac +{ + bool setCursorIcon(int icon, const char *customCursor, float customX, float customY); +} + +#endif + +#endif diff --git a/source/external/src/mac/CursorHelper.mm b/source/external/src/mac/CursorHelper.mm new file mode 100644 index 00000000..3999bbb5 --- /dev/null +++ b/source/external/src/mac/CursorHelper.mm @@ -0,0 +1,24 @@ +#import <Cocoa/Cocoa.h> +#import "CursorHelper.h" + +@implementation CursorHelper + ++ (NSCursor *)getCursorForSelector:(SEL)selector defaultCursor:(NSCursor *)defaultCursor +{ + NSCursor *cursor = nil; + + if ([NSCursor respondsToSelector:selector]) + { + cursor = [NSCursor performSelector:selector]; + } + else + { + NSLog(@"CursorHelper: selector %@ not found", NSStringFromSelector(selector)); + + cursor = defaultCursor; + } + + return cursor; +} + +@end diff --git a/source/external/src/mac/Mac.mm b/source/external/src/mac/Mac.mm new file mode 100644 index 00000000..de4c62f8 --- /dev/null +++ b/source/external/src/mac/Mac.mm @@ -0,0 +1,140 @@ +#include <unordered_map> + +#import <Cocoa/Cocoa.h> +#import "CursorHelper.h" + +#define ARROW 0 +#define CROSSHAIR 1 +#define CLICK 2 +#define IBEAM 3 +#define MOVE 4 +#define HAND 5 +#define DRAG 6 +#define DRAG_OPEN 7 +#define WAIT 8 +#define WAIT_ARROW 9 +#define DISABLED 10 +#define RESIZE_TR 11 +#define RESIZE_BL 12 +#define RESIZE_TL 13 +#define RESIZE_BR 14 +#define RESIZE_H 15 +#define RESIZE_V 16 +#define RESIZE_T 17 +#define RESIZE_B 18 +#define RESIZE_L 19 +#define RESIZE_R 20 +#define RESIZE_TLBR 21 +#define RESIZE_TRBL 22 +#define CUSTOM -1 + +namespace ExternalMac +{ + std::unordered_map<const char *, NSCursor *> customCursorCache; + + bool setCursorIcon(int icon, const char *customCursor, float customX, float customY) + { + NSCursor *cursor = nil; + + switch (icon) + { + case ARROW: + cursor = [NSCursor arrowCursor]; + break; + case CLICK: + cursor = [NSCursor pointingHandCursor]; + break; + case CROSSHAIR: + cursor = [NSCursor crosshairCursor]; + break; + case HAND: + cursor = [NSCursor closedHandCursor]; + break; + case IBEAM: + cursor = [NSCursor IBeamCursor]; + break; + case RESIZE_H: + cursor = [NSCursor resizeLeftRightCursor]; + break; + case RESIZE_V: + cursor = [NSCursor resizeUpDownCursor]; + break; + // TODO: DEFAULTS + case RESIZE_TL: + cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeNorthWestCursor)defaultCursor:nil]; + break; + case RESIZE_TR: + cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeNorthEastCursor)defaultCursor:nil]; + break; + case RESIZE_BL: + cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeSouthWestCursor)defaultCursor:nil]; + break; + case RESIZE_BR: + cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeSouthEastCursor)defaultCursor:nil]; + break; + case RESIZE_T: + cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeNorthCursor)defaultCursor:nil]; + break; + case RESIZE_B: + cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeSouthCursor)defaultCursor:nil]; + break; + case RESIZE_L: + cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeWestCursor)defaultCursor:nil]; + break; + case RESIZE_R: + cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeEastCursor)defaultCursor:nil]; + break; + case RESIZE_TLBR: + cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeNorthWestSouthEastCursor)defaultCursor:nil]; + break; + case RESIZE_TRBL: + cursor = [CursorHelper getCursorForSelector:@selector(_windowResizeNorthEastSouthWestCursor)defaultCursor:nil]; + break; + case WAIT: + cursor = [CursorHelper getCursorForSelector:@selector(_waitCursor)defaultCursor:nil]; + break; + case WAIT_ARROW: + cursor = [CursorHelper getCursorForSelector:@selector(_waitCursor)defaultCursor:nil]; + break; + case DISABLED: + cursor = [NSCursor operationNotAllowedCursor]; + break; + case DRAG: + cursor = [NSCursor closedHandCursor]; + break; + case DRAG_OPEN: + cursor = [NSCursor openHandCursor]; + break; + case CUSTOM: + if (customCursorCache.find(customCursor) != customCursorCache.end()) + cursor = customCursorCache[customCursor]; + else + { + NSString *cursorImageName = [NSString stringWithUTF8String:customCursor]; + NSImage *tmpImage = [[NSImage alloc] initWithContentsOfFile:cursorImageName]; + + if (tmpImage != nil) + { + cursor = [[NSCursor alloc] initWithImage:tmpImage hotSpot:NSMakePoint(customX, customY)]; + customCursorCache[customCursor] = cursor; + [tmpImage release]; + } + else + NSLog(@"Failed to load image %@", cursorImageName); + + [cursorImageName release]; + } + + break; + } + + if (cursor != nil) + { + [cursor set]; + + return true; + } + + return false; + } +} diff --git a/source/flx3d/Flx3DCamera.hx b/source/flx3d/Flx3DCamera.hx index 129ace5d..d4f76229 100644 --- a/source/flx3d/Flx3DCamera.hx +++ b/source/flx3d/Flx3DCamera.hx @@ -73,7 +73,7 @@ class Flx3DCamera extends FlxCamera { var context = new AssetLoaderContext(); var noExt = Path.withoutExtension(assetPath); trace(noExt); - context.mapUrlToData('${Path.withoutDirectory(noExt)}.mtl', '$noExt.mtl'); + context.mapUrlToData(Path.withoutDirectory(noExt) + '.mtl', noExt + '.mtl'); var material:TextureMaterial = null; if (texturePath != null) diff --git a/source/flx3d/Flx3DUtil.hx b/source/flx3d/Flx3DUtil.hx index b90076a8..71706e35 100644 --- a/source/flx3d/Flx3DUtil.hx +++ b/source/flx3d/Flx3DUtil.hx @@ -14,7 +14,7 @@ import flixel.FlxG; * @see https://twitter.com/Ne_Eo_Twitch */ @:access(away3d.core.managers.Stage3DManager) -class Flx3DUtil +final class Flx3DUtil { #if THREE_D_SUPPORT /** diff --git a/source/flx3d/Flx3DView.hx b/source/flx3d/Flx3DView.hx index 5413fe94..59413e77 100644 --- a/source/flx3d/Flx3DView.hx +++ b/source/flx3d/Flx3DView.hx @@ -46,7 +46,7 @@ class Flx3DView extends FlxView3D { var context = new AssetLoaderContext(); var noExt = Path.withoutExtension(assetPath); trace(noExt); - context.mapUrlToData('${Path.withoutDirectory(noExt)}.mtl', '$noExt.mtl'); + context.mapUrlToData(Path.withoutDirectory(noExt) + '.mtl', noExt + '.mtl'); var material:TextureMaterial = null; if (texturePath != null) diff --git a/source/funkin/backend/FunkinSprite.hx b/source/funkin/backend/FunkinSprite.hx index 4f0ecac9..90ac68c9 100644 --- a/source/funkin/backend/FunkinSprite.hx +++ b/source/funkin/backend/FunkinSprite.hx @@ -32,6 +32,15 @@ enum abstract XMLAnimType(Int) default: def; } } + + @:to public function toString():String { + return switch (cast this) + { + case NONE: "none"; + case BEAT: "beat"; + case LOOP: "loop"; + } + } } class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements IOffsetCompatible implements IXMLEvents @@ -42,9 +51,10 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I public var beatAnims:Array<BeatAnim> = []; public var name:String; public var zoomFactor:Float = 1; - public var initialZoom:Float = 1; public var debugMode:Bool = false; public var animDatas:Map<String, AnimData> = []; + public var animEnabled:Bool = true; + public var zoomFactorEnabled:Bool = true; public var globalCurFrame(get, set):Int; @@ -133,6 +143,7 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I { frames = Paths.getFrames(path, true); } + return this; } public function onPropertySet(property:String, value:Dynamic) { @@ -143,9 +154,9 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I private var countedBeat = 0; public function beatHit(curBeat:Int) { + if(!animEnabled) return; if (lastAnimContext != LOCK && beatAnims.length > 0 && (curBeat + beatOffset) % beatInterval == 0) { - if(skipNegativeBeats && curBeat < 0) return; // TODO: find a solution without countedBeat var anim = beatAnims[FlxMath.wrap(countedBeat++, 0, beatAnims.length - 1)]; if (anim.name != null && anim.name != "null" && anim.name != "none") @@ -161,49 +172,6 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I { } - public override function getScreenBounds(?newRect:FlxRect, ?camera:FlxCamera):FlxRect - { - __doPreZoomScaleProcedure(camera); - var r = super.getScreenBounds(newRect, camera); - __doPostZoomScaleProcedure(); - return r; - } - - public override function drawComplex(camera:FlxCamera) - { - super.drawComplex(camera); - } - - public override function doAdditionalMatrixStuff(matrix:FlxMatrix, camera:FlxCamera) - { - super.doAdditionalMatrixStuff(matrix, camera); - matrix.translate(-camera.width / 2, -camera.height / 2); - - var requestedZoom = FlxMath.lerp(1, camera.zoom, zoomFactor); - var diff = requestedZoom / camera.zoom; - matrix.scale(diff, diff); - matrix.translate(camera.width / 2, camera.height / 2); - } - - public override function getScreenPosition(?point:FlxPoint, ?Camera:FlxCamera):FlxPoint - { - if (__shouldDoScaleProcedure()) - { - __oldScrollFactor.set(scrollFactor.x, scrollFactor.y); - var requestedZoom = FlxMath.lerp(initialZoom, camera.zoom, zoomFactor); - var diff = requestedZoom / camera.zoom; - - scrollFactor.scale(1 / diff); - - var r = super.getScreenPosition(point, Camera); - - scrollFactor.set(__oldScrollFactor.x, __oldScrollFactor.y); - - return r; - } - return super.getScreenPosition(point, Camera); - } - // ANIMATE ATLAS DRAWING #if REGION public override function draw() @@ -222,7 +190,7 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I public function copyAtlasValues() { @:privateAccess { - animateAtlas.cameras = cameras; + animateAtlas.cameras = cameras; // investigate if we can use _cameras animateAtlas.scrollFactor = scrollFactor; animateAtlas.scale = scale; animateAtlas.offset = offset; @@ -261,33 +229,60 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I } #end - // SCALING FUNCS - #if REGION - private inline function __shouldDoScaleProcedure() - return zoomFactor != 1; + // ZOOM FACTOR + private inline function __shouldDoZoomFactor() + return zoomFactorEnabled && zoomFactor != 1; - static var __oldScrollFactor:FlxPoint = new FlxPoint(); - static var __oldScale:FlxPoint = new FlxPoint(); - var __skipZoomProcedure:Bool = false; - - private function __doPreZoomScaleProcedure(camera:FlxCamera) + public override function getScreenBounds(?newRect:FlxRect, ?camera:FlxCamera):FlxRect { - if (__skipZoomProcedure = !__shouldDoScaleProcedure()) - return; - __oldScale.set(scale.x, scale.y); - var requestedZoom = FlxMath.lerp(initialZoom, camera.zoom, zoomFactor); - var diff = requestedZoom * camera.zoom; + if (camera == null) + camera = FlxG.camera; - scale.scale(diff); + var r = super.getScreenBounds(newRect, camera); + + if(__shouldDoZoomFactor()) { + r.x -= camera.width / 2; + r.y -= camera.height / 2; + + var ratio = (camera.zoom > 0 ? Math.max : Math.min)(0, FlxMath.lerp(1 / camera.zoom, 1, zoomFactor)); + r.x *= ratio; + r.y *= ratio; + r.width *= ratio; + r.height *= ratio; + + r.x += camera.width / 2; + r.y += camera.height / 2; + } + return r; } - private function __doPostZoomScaleProcedure() + override public function isOnScreen(?camera:FlxCamera):Bool { - if (__skipZoomProcedure) - return; - scale.set(__oldScale.x, __oldScale.y); + if (forceIsOnScreen) + return true; + + if (camera == null) + camera = FlxG.camera; + + var bounds = getScreenBounds(_rect, camera); + if (bounds.width == 0 && bounds.height == 0) + return false; + return camera.containsRect(bounds); + } + + // ZOOM FACTOR RENDERING + public override function doAdditionalMatrixStuff(matrix:FlxMatrix, camera:FlxCamera) + { + super.doAdditionalMatrixStuff(matrix, camera); + if(__shouldDoZoomFactor()) { + matrix.translate(-camera.width / 2, -camera.height / 2); + + var requestedZoom = (camera.zoom >= 0 ? Math.max : Math.min)(FlxMath.lerp(1, camera.zoom, zoomFactor), 0); + var diff = requestedZoom / camera.zoom; + matrix.scale(diff, diff); + matrix.translate(camera.width / 2, camera.height / 2); + } } - #end // OFFSETTING #if REGION @@ -413,6 +408,11 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I return animateAtlas != null ? animateAtlas.anim.isAtEnd : (animation.curAnim != null ? animation.curAnim.isAtEnd : false); } + override function updateAnimation(elapsed:Float) { + if (animEnabled) + super.updateAnimation(elapsed); + } + // Backwards compat (the names used to be all different and it sucked, please lets use the same format in the future) - Nex public inline function hasAnimation(AnimName:String) return hasAnim(AnimName); public inline function removeAnimation(name:String) return removeAnim(name); diff --git a/source/funkin/backend/MusicBeatState.hx b/source/funkin/backend/MusicBeatState.hx index 63eeef6c..38d51281 100644 --- a/source/funkin/backend/MusicBeatState.hx +++ b/source/funkin/backend/MusicBeatState.hx @@ -141,7 +141,9 @@ class MusicBeatState extends FlxState implements IBeatReceiver script.load(); } } + #if EXPERMENTAL_SCRIPT_RELOADING else stateScripts.reload(); + #end } } diff --git a/source/funkin/backend/MusicBeatSubstate.hx b/source/funkin/backend/MusicBeatSubstate.hx index a9cd55bf..5ad900d4 100644 --- a/source/funkin/backend/MusicBeatSubstate.hx +++ b/source/funkin/backend/MusicBeatSubstate.hx @@ -122,7 +122,9 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver script.load(); } } + #if EXPERMENTAL_SCRIPT_RELOADING else stateScripts.reload(); + #end } } diff --git a/source/funkin/backend/MusicBeatTransition.hx b/source/funkin/backend/MusicBeatTransition.hx index 0cb44df0..e2c1d0cd 100644 --- a/source/funkin/backend/MusicBeatTransition.hx +++ b/source/funkin/backend/MusicBeatTransition.hx @@ -4,8 +4,10 @@ import flixel.FlxState; import flixel.tweens.FlxTween; import funkin.backend.scripting.Script; import funkin.backend.scripting.events.CancellableEvent; +import funkin.backend.scripting.events.ResizeEvent; import funkin.backend.scripting.events.TransitionCreationEvent; import funkin.backend.utils.FunkinParentDisabler; +import funkin.editors.ui.UIState; class MusicBeatTransition extends MusicBeatSubstate { public static var script:String = Flags.DEFAULT_TRANSITION_SCRIPT; @@ -52,23 +54,12 @@ class MusicBeatTransition extends MusicBeatSubstate { return; } - blackSpr = new FlxSprite(0, transOut ? -transitionCamera.height : transitionCamera.height).makeGraphic(1, 1, -1); - blackSpr.scale.set(transitionCamera.width, transitionCamera.height); + add(blackSpr = new FlxSprite(0, transOut ? -transitionCamera.height : transitionCamera.height).makeGraphic(1, 1, -1)); blackSpr.color = 0xFF000000; - blackSpr.updateHitbox(); - add(blackSpr); + add(transitionSprite = new FunkinSprite().loadSprite(Paths.image('menus/transitionSpr'))); + resizeDefaultSprites(); - transitionSprite = new FunkinSprite(); - transitionSprite.loadSprite(Paths.image('menus/transitionSpr')); - if (transitionSprite.animateAtlas == null) { - transitionSprite.setGraphicSize(transitionCamera.width, transitionCamera.height); - transitionSprite.updateHitbox(); - } else { - transitionSprite.screenCenter(); - } transitionCamera.flipY = !transOut; - add(transitionSprite); - transitionCamera.scroll.y = transitionCamera.height; transitionTween = FlxTween.tween(transitionCamera.scroll, {y: -transitionCamera.height}, 2/3, { ease: FlxEase.sineOut, @@ -110,6 +101,28 @@ class MusicBeatTransition extends MusicBeatSubstate { transitionScript.call('postUpdate', [elapsed]); } + public override function onResize(w:Int, h:Int) { + super.onResize(w, h); + if (FlxG.state is UIState && UIState.resolutionAware) resizeDefaultSprites(); + transitionScript.call('onResize', [EventManager.get(ResizeEvent).recycle(w, h, null, null)]); + } + + public function resizeDefaultSprites() { + if (blackSpr != null) { + blackSpr.scale.set(transitionCamera.width, transitionCamera.height); + blackSpr.updateHitbox(); + } + + if (transitionSprite != null) { + if (transitionSprite.animateAtlas == null) { + transitionSprite.setGraphicSize(transitionCamera.width, transitionCamera.height); + transitionSprite.updateHitbox(); + } else { + transitionSprite.screenCenter(); + } + } + } + public function finish() { var event = new CancellableEvent(); transitionScript.call('onFinish', [event]); diff --git a/source/funkin/backend/assets/ModsFolder.hx b/source/funkin/backend/assets/ModsFolder.hx index 898987e1..850fdd2b 100644 --- a/source/funkin/backend/assets/ModsFolder.hx +++ b/source/funkin/backend/assets/ModsFolder.hx @@ -97,7 +97,7 @@ class ModsFolder { return mods; for (modFolder in modsList) { - if (FileSystem.isDirectory('${modsPath}${modFolder}')) { + if (FileSystem.isDirectory(modsPath + modFolder)) { mods.push(modFolder); } else { var ext = Path.extension(modFolder).toLowerCase(); diff --git a/source/funkin/backend/assets/Paths.hx b/source/funkin/backend/assets/Paths.hx index eda84cfb..4b725a5b 100644 --- a/source/funkin/backend/assets/Paths.hx +++ b/source/funkin/backend/assets/Paths.hx @@ -93,13 +93,12 @@ class Paths return getPath('images/$key.$ext', library); } - inline static public function script(key:String, ?library:String, isAssetsPath:Bool = false) { + static public function script(key:String, ?library:String, isAssetsPath:Bool = false) { var scriptPath = isAssetsPath ? key : getPath(key, library); if (!OpenFlAssets.exists(scriptPath)) { var p:String; for(ex in Script.scriptExtensions) { - p = '$scriptPath.$ex'; - if (OpenFlAssets.exists(p)) { + if (OpenFlAssets.exists(p = scriptPath + '.' + ex)) { scriptPath = p; break; } diff --git a/source/funkin/backend/chart/Chart.hx b/source/funkin/backend/chart/Chart.hx index 9ea7cbdc..8735fc81 100644 --- a/source/funkin/backend/chart/Chart.hx +++ b/source/funkin/backend/chart/Chart.hx @@ -45,6 +45,8 @@ enum abstract ChartFormat(Int) { } class Chart { + public static final version:String = "1.6.0"; + public static function cleanSongData(data:Dynamic):Dynamic { if (Reflect.hasField(data, "song")) { var field:Dynamic = Reflect.field(data, "song"); @@ -75,9 +77,8 @@ class Chart { if (Assets.exists(path)) { try { data = Json.parse(Assets.getText(path)).events; - } catch(e) { - Logs.trace('Failed to load song event data for ${songName} ($path): ${Std.string(e)}', ERROR); - } + for (event in data) event.global = true; + } catch(e) Logs.trace('Failed to load song event data for ${songName} ($path): ${Std.string(e)}', ERROR); } return data; } @@ -211,6 +212,14 @@ class Chart { base.events = base.events.concat(extraEvents); #end + /** + * Set defaults on strum lines + */ + for(strumLine in base.strumLines) { + if(strumLine.keyCount == null) + strumLine.keyCount = 4; + } + return base; } @@ -239,9 +248,10 @@ class Chart { if (saveSettings == null) saveSettings = {}; if (saveSettings.saveMetaInChart == null) saveSettings.saveMetaInChart = true; - if (saveSettings.saveEventsInChart == null) saveSettings.saveEventsInChart = true; + if (saveSettings.saveLocalEvents == null) saveSettings.saveLocalEvents = true; + if (saveSettings.saveGlobalEvents == null) saveSettings.saveGlobalEvents = false; - var filteredChart = filterChartForSaving(chart, saveSettings.saveMetaInChart, saveSettings.saveEventsInChart); + var filteredChart = filterChartForSaving(chart, saveSettings.saveMetaInChart, saveSettings.saveLocalEvents, saveSettings.saveGlobalEvents); var meta = filteredChart.meta; #if sys @@ -261,11 +271,27 @@ class Chart { return filteredChart; } - public static function filterChartForSaving(chart:ChartData, ?saveMetaInChart:Null<Bool>, ?saveEventsInChart:Null<Bool>):ChartData { + public static function filterChartForSaving(chart:ChartData, ?saveMetaInChart:Bool, ?saveLocalEvents:Bool, ?saveGlobalEvents:Bool):ChartData { var data = Reflect.copy(chart); // make a copy of the chart to leave the OG intact - data.meta = saveMetaInChart != true ? null : data.meta = Reflect.copy(chart.meta); // also make a copy of the metadata to leave the OG intact. + if (saveMetaInChart != true) { + data.meta = null; + } else { + data.meta = Reflect.copy(chart.meta); // also make a copy of the metadata to leave the OG intact. + if (data.meta != null && Reflect.hasField(data.meta, "parsedColor")) Reflect.deleteField(data.meta, "parsedColor"); + } + + // in this part abt the events, i gotta account that these booleans can be null - Nex + if (saveLocalEvents != true && saveGlobalEvents != true) data.events = null; + else { + data.events = []; + for (event in chart.events) if ((saveLocalEvents == true && event.global != true) || (saveGlobalEvents == true && event.global == true)) { + var copy = Reflect.copy(event); + if (saveLocalEvents == true ? event.global != true : event.global == true) Reflect.deleteField(copy, "global"); // should NOT delete the field when saving with the local events and the event should have been global - Nex + data.events.push(copy); + } + if (data.events.length == 0) data.events = null; + } - data.events = saveEventsInChart != true ? null : Reflect.copy(chart.events); // same here once again data.fromMods = null; var sortedData:Dynamic = {}; @@ -287,7 +313,8 @@ class Chart { typedef ChartSaveSettings = { var ?overrideExistingMeta:Bool; var ?saveMetaInChart:Bool; - var ?saveEventsInChart:Bool; + var ?saveLocalEvents:Bool; + var ?saveGlobalEvents:Bool; var ?prettyPrint:Bool; var ?folder:String; } \ No newline at end of file diff --git a/source/funkin/backend/chart/ChartData.hx b/source/funkin/backend/chart/ChartData.hx index 8a9d03ce..ba1b7ae7 100644 --- a/source/funkin/backend/chart/ChartData.hx +++ b/source/funkin/backend/chart/ChartData.hx @@ -10,7 +10,9 @@ typedef ChartData = { public var stage:String; public var scrollSpeed:Float; public var noteTypes:Array<String>; + public var ?bookmarks:Array<ChartBookmark>; + public var ?chartVersion:String; public var ?fromMods:Bool; } @@ -37,8 +39,10 @@ typedef ChartStrumLine = { var ?visible:Null<Bool>; var ?strumPos:Array<Float>; var ?strumScale:Float; + var ?strumSpacing:Float; var ?scrollSpeed:Float; var ?vocalsSuffix:String; + var ?keyCount:Int; // default=4 var ?strumLinePos:Float; // Backwards compatibility } @@ -50,10 +54,17 @@ typedef ChartNote = { var sLen:Float; // sustain length of the note (ms) } +typedef ChartBookmark = { + var time:Float; + var name:String; + var color:String; +} + typedef ChartEvent = { var name:String; var time:Float; var params:Array<Dynamic>; + var ?global:Bool; // If its from a global event file like events.json; this field might be saved rarely in the chart json directly but modders can mess with this for messing with the saving funcs too - Nex } enum abstract ChartStrumLineType(Int) from Int to Int { diff --git a/source/funkin/backend/chart/EventsData.hx b/source/funkin/backend/chart/EventsData.hx index 9a733ed7..6b8bfc73 100644 --- a/source/funkin/backend/chart/EventsData.hx +++ b/source/funkin/backend/chart/EventsData.hx @@ -10,7 +10,7 @@ import openfl.Assets; using StringTools; class EventsData { - public static var defaultEventsList:Array<String> = ["HScript Call", "Camera Movement", "Camera Position", "Add Camera Zoom", "Camera Zoom", "Camera Modulo Change", "Camera Flash", "BPM Change", "Scroll Speed Change", "Alt Animation Toggle", "Play Animation"]; + public static var defaultEventsList:Array<String> = ["HScript Call", "Camera Movement", "Camera Position", "Add Camera Zoom", "Camera Zoom", "Camera Modulo Change", "Camera Flash", "BPM Change", "Continuous BPM Change", "Time Signature Change", "Scroll Speed Change", "Alt Animation Toggle", "Play Animation"]; public static var defaultEventsParams:Map<String, Array<EventParamInfo>> = [ "HScript Call" => [ {name: "Function Name", type: TString, defValue: "myFunc"}, @@ -75,8 +75,10 @@ class EventsData { {name: "Multiplicative?", type: TBool, defValue: true} ], "Camera Modulo Change" => [ - {name: "Modulo Interval (Beats)", type: TInt(1, 9999999, 1), defValue: 4}, - {name: "Bump Strength", type: TFloat(0.1, 10, 0.01, 2), defValue: 1} + {name: "Modulo Interval", type: TInt(1, 9999999, 1), defValue: 4}, + {name: "Bump Strength", type: TFloat(0.1, 10, 0.01, 2), defValue: 1}, + {name: "Every Beat Type", type: TDropDown(['BEAT', 'MEASURE', 'STEP']), defValue: 'BEAT'}, + {name: "Beat Offset", type: TFloat(-10, 10, 0.25, 2), defValue: 0} ], "Camera Flash" => [ {name: "Reversed?", type: TBool, defValue: false}, @@ -84,7 +86,9 @@ class EventsData { {name: "Time (Steps)", type: TFloat(0.25, 9999, 0.25, 2), defValue: 4}, {name: "Camera", type: TDropDown(['camGame', 'camHUD']), defValue: "camHUD"} ], - "BPM Change" => [{name: "Target BPM", type: TFloat(1.00, null, 0.001, 3), defValue: 100}], + "BPM Change" => [{name: "Target BPM", type: TFloat(1.00, 9999, 0.001, 3), defValue: 100}], + "Continuous BPM Change" => [{name: "Target BPM", type: TFloat(1.00, 9999, 0.001, 3), defValue: 100}, {name: "Time (steps)", type: TFloat(0.25, 9999, 0.25, 2), defValue: 4}], + "Time Signature Change" => [{name: "Target Numerator", type: TFloat(1), defValue: 4}, {name: "Target Denominator", type: TFloat(1), defValue: 4}, {name: "Denominator is Steps Per Beat", type: TBool, defValue: false}], "Scroll Speed Change" => [ {name: "Tween Speed?", type: TBool, defValue: true}, {name: "New Speed", type: TFloat(0.01, 99, 0.01, 2), defValue: 1.}, diff --git a/source/funkin/backend/chart/FNFLegacyParser.hx b/source/funkin/backend/chart/FNFLegacyParser.hx index 2ba3e809..84e96aa2 100644 --- a/source/funkin/backend/chart/FNFLegacyParser.hx +++ b/source/funkin/backend/chart/FNFLegacyParser.hx @@ -55,6 +55,18 @@ class FNFLegacyParser { continue; // Yoshi Engine charts crash fix } + var newBeatsPerMeasure:Float = section.sectionBeats != null ? section.sectionBeats : data.beatsPerMeasure.getDefault(4); // Default to 4 if sectionBeats is null or undefined (oops :3) + + if (newBeatsPerMeasure != beatsPerMeasure) { + beatsPerMeasure = newBeatsPerMeasure; + + result.events.push({ + time: curTime, + name: "Time Signature Change", + params: [newBeatsPerMeasure, 4] + }); + } + if (camFocusedBF != (camFocusedBF = section.mustHitSection)) { result.events.push({ time: curTime, @@ -178,7 +190,8 @@ class FNFLegacyParser { mustHitSection: notes[section-1] != null ? notes[section-1].mustHitSection : false, bpm: notes[section-1] != null ? notes[section-1].bpm : chart.meta.bpm, changeBPM: false, - altAnim: notes[section-1] != null ? notes[section-1].altAnim : false + altAnim: notes[section-1] != null ? notes[section-1].altAnim : false, + sectionBeats: notes[section-1] != null ? notes[section-1].sectionBeats : chart.meta.beatsPerMeasure.getDefault(4) }; var sectionEndTime:Float = Conductor.getTimeForStep(Conductor.getMeasureLength() * (section+1)); @@ -192,6 +205,8 @@ class FNFLegacyParser { case "BPM Change": baseSection.changeBPM = true; baseSection.bpm = event.params[0]; + case "Time Signature Change": + baseSection.sectionBeats = event.params[0]; } } notes[section] = baseSection; diff --git a/source/funkin/backend/scripting/EventManager.hx b/source/funkin/backend/scripting/EventManager.hx index 34c1a473..162d72fa 100644 --- a/source/funkin/backend/scripting/EventManager.hx +++ b/source/funkin/backend/scripting/EventManager.hx @@ -3,7 +3,7 @@ package funkin.backend.scripting; import flixel.FlxState; import funkin.backend.scripting.events.*; -class EventManager { +final class EventManager { // map doesn't work for that public static var eventValues:Array<CancellableEvent> = []; public static var eventKeys:Array<Class<CancellableEvent>> = []; diff --git a/source/funkin/backend/scripting/Script.hx b/source/funkin/backend/scripting/Script.hx index be62b4ad..1f5c3ddc 100644 --- a/source/funkin/backend/scripting/Script.hx +++ b/source/funkin/backend/scripting/Script.hx @@ -295,7 +295,7 @@ class Script extends FlxBasic implements IFlxDestroyable { if(remappedNames.exists(fileName)) fileName = remappedNames.get(fileName); Logs.traceColored([ - Logs.logText('${fileName}: ', GREEN), + Logs.logText(fileName + ': ', GREEN), Logs.logText(Std.string(v)) ], TRACE); } diff --git a/source/funkin/backend/system/CommandLineHandler.hx b/source/funkin/backend/system/CommandLineHandler.hx index 1545108f..e6d74007 100644 --- a/source/funkin/backend/system/CommandLineHandler.hx +++ b/source/funkin/backend/system/CommandLineHandler.hx @@ -2,7 +2,7 @@ package funkin.backend.system; #if sys import sys.FileSystem; -class CommandLineHandler { +final class CommandLineHandler { public static function parseCommandLine(cmd:Array<String>) { var i:Int = 0; while(i < cmd.length) { diff --git a/source/funkin/backend/system/Conductor.hx b/source/funkin/backend/system/Conductor.hx index aaf47774..21249cd6 100644 --- a/source/funkin/backend/system/Conductor.hx +++ b/source/funkin/backend/system/Conductor.hx @@ -4,56 +4,58 @@ import flixel.FlxState; import flixel.util.FlxSignal.FlxTypedSignal; import funkin.backend.chart.ChartData; import funkin.backend.system.interfaces.IBeatReceiver; +import funkin.editors.charter.Charter; -typedef BPMChangeEvent = -{ - var stepTime:Float; - var songTime:Float; - var bpm:Float; +enum BeatType { + BEAT; + MEASURE; + STEP; } -class Conductor +@:structInit +class BPMChangeEvent { + public var songTime:Float; + public var bpm:Float; + public var beatsPerMeasure:Float = 4; + public var stepsPerBeat:Int = 4; + + public var endSongTime:Float = 0; + public var endStepTime:Float = 0; + public var continuous:Bool = false; + + public var stepTime:Float; + public var beatTime:Float; + public var measureTime:Float; +} + +final class Conductor +{ + public static function getBeats(?every:BeatType, interval:Float, offset:Float = 0):Float { + final beat = switch(every) { + case MEASURE: curMeasureFloat; + case STEP: curStepFloat; + default: curBeatFloat; + } + if (interval <= 0) return beat - offset; + else return Math.floor((beat - offset) / interval) * interval; + } + /** * FlxSignals */ public static var onMeasureHit:FlxTypedSignal<Int->Void> = new FlxTypedSignal(); public static var onBeatHit:FlxTypedSignal<Int->Void> = new FlxTypedSignal(); public static var onStepHit:FlxTypedSignal<Int->Void> = new FlxTypedSignal(); - public static var onBPMChange:FlxTypedSignal<Float->Void> = new FlxTypedSignal(); - - /** - * Current BPM - */ - public static var bpm:Float = 100; - - /** - * Current Crochet (time per beat), in milliseconds. - */ - public static var crochet:Float = ((60 / bpm) * 1000); // beats in milliseconds - - /** - * Current StepCrochet (time per step), in milliseconds. - */ - public static var stepCrochet:Float = crochet / 4; // steps in milliseconds - - /** - * Number of beats per measure (top number in time signature). Defaults to 4. - */ - public static var beatsPerMeasure:Float = 4; - - /** - * Number of steps per beat (bottom number in time signature). Defaults to 4. - */ - public static var stepsPerBeat:Int = 4; - + public static var onBPMChange:FlxTypedSignal<(Float,Float)->Void> = new FlxTypedSignal(); + public static var onTimeSignatureChange:FlxTypedSignal<(Float,Float)->Void> = new FlxTypedSignal(); /** * Current position of the song, in milliseconds. */ public static var songPosition(get, default):Float; private static function get_songPosition() { - if (songOffset != Options.songOffset) trace(songOffset = Options.songOffset); + if (songOffset != Options.songOffset) songOffset = Options.songOffset; return songPosition - songOffset; } @@ -62,6 +64,88 @@ class Conductor */ public static var songOffset:Float = 0; + + /** + * Current bpmChangeMap index + */ + public static var curChangeIndex:Int = 0; + + /** + * Current bpmChangeMap + */ + public static var curChange(get, never):BPMChangeEvent; + private static var dummyChange:BPMChangeEvent = {bpm: 100, beatsPerMeasure: 4, stepsPerBeat: 4, songTime: 0, stepTime: 0, beatTime: 0, measureTime: 0}; + private static function get_curChange() + return bpmChangeMap.length == 0 ? dummyChange : bpmChangeMap[curChangeIndex]; + + /** + * Current BPM + */ + public static var bpm(get, never):Float; + private static function get_bpm() + return (curChangeIndex == 0 || bpmChangeMap.length == 0) ? startingBPM : getTimeWithIndexInBPM(songPosition, curChangeIndex); + + /** + * Starting BPM + */ + public static var startingBPM(get, never):Float; + private static function get_startingBPM() + return (bpmChangeMap.length == 0 ? dummyChange : bpmChangeMap[0]).bpm; + + /** + * Current Crochet (time per beat), in milliseconds. + * It should be crotchet but ehhh, now it's there for backward compatibility. + */ + public static var crochet(get, never):Float; + private static function get_crochet() return 15000 * stepsPerBeat / bpm; + + /** + * Current StepCrochet (time per step), in milliseconds. + */ + public static var stepCrochet(get, never):Float; + private static function get_stepCrochet() return 15000 / bpm; + + /** + * Number of beats per mesure (top number in time signature). Defaults to 4. + */ + public static var beatsPerMeasure(get, never):Float; + private static function get_beatsPerMeasure() + return (bpmChangeMap.length == 0 ? dummyChange : bpmChangeMap[curChangeIndex]).beatsPerMeasure; + + /** + * Number of steps per beat. Defaults to 4. + * Not a divisor number for time signature, it does the complete opposite. + * It's because CNE Conductor is based in sixteenth note instead of beat. + */ + public static var stepsPerBeat(get, never):Int; + private static function get_stepsPerBeat() + return (bpmChangeMap.length == 0 ? dummyChange : bpmChangeMap[curChangeIndex]).stepsPerBeat; + + /** + * How much value notes to divide for beat (bottom or divisor number in time signature). + * Only for a convinient way to access divisor instead of multiply by steps per beat. + */ + public static var denominator(get, never):Float; + private static function get_denominator() return FlxMath.roundDecimal(16 / stepsPerBeat, 2); + + /** + * Last step from BPM Change + */ + public static var lastStepChange(get, never):Float; + private static function get_lastStepChange() return curChange.stepTime; + + /** + * Last beat from BPM Change + */ + public static var lastBeatChange(get, never):Float; + private static function get_lastBeatChange() return curChange.beatTime; + + /** + * Last measure from BPM Change + */ + public static var lastMeasureChange(get, never):Float; + private static function get_lastMeasureChange() return curChange.measureTime; + /** * Current step */ @@ -77,7 +161,6 @@ class Conductor */ public static var curMeasure:Int = 0; - /** * Current step, as a `Float` (ex: 4.94, instead of 4) */ @@ -100,54 +183,164 @@ class Conductor /** * Array of all BPM changes that have been mapped. */ - public static var bpmChangeMap:Array<BPMChangeEvent> = []; + public static var bpmChangeMap:Array<BPMChangeEvent>; + + /** + * Array of all events that have been rejected by the Conductor. + */ + public static var invalidEvents:Array<ChartEvent> = []; + + private static var validEventNames:Array<String> = ["BPM Change", "Time Signature Change", "Continuous BPM Change"]; @:dox(hide) public function new() {} public static function reset() { songPosition = lastSongPos = curBeatFloat = curStepFloat = curBeat = curStep = 0; - bpmChangeMap = []; - changeBPM(0); + curChangeIndex = 0; + changeBPM(); } + public static function changeBPM(bpm:Float = 100, beatsPerMeasure:Float = 4, stepsPerBeat:Int = 4) + bpmChangeMap = [{bpm: bpm, beatsPerMeasure: beatsPerMeasure, stepsPerBeat: stepsPerBeat, songTime: 0, stepTime: 0, beatTime: 0, measureTime: 0}]; + public static function setupSong(SONG:ChartData) { reset(); mapBPMChanges(SONG); - changeBPM(SONG.meta.bpm, cast SONG.meta.beatsPerMeasure.getDefault(4), cast SONG.meta.stepsPerBeat.getDefault(4)); } + + private static function mapBPMChange(curChange:BPMChangeEvent, time:Float, bpm:Float):BPMChangeEvent { + var beatTime:Float, measureTime:Float, stepTime:Float; + stepTime = (curChange.continuous ? curChange.endStepTime : curChange.stepTime) + (time - (curChange.continuous ? curChange.endSongTime : curChange.songTime)) / (15000 / curChange.bpm); + + beatTime = curChange.beatTime + (stepTime - curChange.stepTime) / curChange.stepsPerBeat; + measureTime = curChange.measureTime + (beatTime - curChange.beatTime) / curChange.beatsPerMeasure; + + bpmChangeMap.push(curChange = { + songTime: time, + stepTime: stepTime, + beatTime: beatTime, + measureTime: measureTime, + bpm: bpm, + beatsPerMeasure: curChange.beatsPerMeasure, + stepsPerBeat: curChange.stepsPerBeat + }); + return curChange; + } + /** * Maps BPM changes from a song. * @param song Song to map BPM changes from. */ - public static function mapBPMChanges(song:ChartData) - { - bpmChangeMap = [ - { - stepTime: 0, - songTime: 0, - bpm: song.meta.bpm - } - ]; - + public static function mapBPMChanges(song:ChartData) { + var curChange:BPMChangeEvent = { + songTime: 0, + stepTime: 0, + beatTime: 0, + measureTime: 0, + bpm: song.meta.bpm, + beatsPerMeasure: song.meta.beatsPerMeasure.getDefault(4), + stepsPerBeat: CoolUtil.floorInt(song.meta.stepsPerBeat.getDefault(4)) + }; + curChangeIndex = 0; + bpmChangeMap = [curChange]; + invalidEvents = []; if (song.events == null) return; - var curBPM:Float = song.meta.bpm; - var songTime:Float = 0; - var stepTime:Float = 0; + // fix the sort first... + var events:Array<ChartEvent> = [for (e in song.events) if (e.params != null && validEventNames.contains(e.name)) e]; + events.sort((a, b) -> MathUtil.equal(a.time, b.time) ? (a.name == "Time Signature Change" ? -1 : 1) : Std.int(a.time - b.time)); - for(e in song.events) if (e.name == "BPM Change" && e.params != null && e.params[0] is Float) { - if (e.params[0] == curBPM) continue; - var steps = (e.time - songTime) / ((60 / curBPM) * 1000 / 4); - stepTime += steps; - songTime = e.time; - curBPM = e.params[0]; + for (e in events) curChange = mapEvent(e, curChange); + } - bpmChangeMap.push({ - stepTime: stepTime, - songTime: songTime, - bpm: curBPM - }); + private static function mapEvent(e:ChartEvent, curChange:BPMChangeEvent) { + var name = e.name, params = e.params, time = e.time; + if (curChange.continuous && MathUtil.lessThan(time, curChange.endSongTime)) { //ensure that you cannot place any conductor events during a continuous change + invalidEvents.push(e); + Logs.trace('Invalid Conductor event "${e.name}" at ${e.time} (Intersecting continuous change!)', WARNING); + return curChange; } + + if (name == "BPM Change" && params[0] is Float && curChange.bpm != params[0]) + curChange = mapBPMChange(curChange, time, params[0]); + else if (name == "Time Signature Change") { + //if (beatsPerMeasure == curChange.beatsPerMeasure && stepsPerBeat == curChange.stepsPerBeat) continue; + /* TODO: make so time sigs doesnt stop the bpm change if its in the duration of bpm change */ + + if (curChange.songTime != time) curChange = mapBPMChange(curChange, time, curChange.bpm); + curChange.beatsPerMeasure = params[0]; + + if (params[2]) curChange.stepsPerBeat = params[1]; + else curChange.stepsPerBeat = Math.floor(16 / params[1]); // convert from denominator to stepsPerBeat + + curChange.stepTime = CoolUtil.floorInt(curChange.stepTime + .99998); + curChange.beatTime = CoolUtil.floorInt(curChange.beatTime + .99998); + curChange.measureTime = CoolUtil.floorInt(curChange.measureTime + .99998); + } + else if (name == "Continuous BPM Change") { + var prevBPM = curChange.bpm; + if (curChange.bpm == params[0]) { + invalidEvents.push(e); + return curChange; //DO NOT!!!! + } + + curChange = mapBPMChange(curChange, time, params[0]); + curChange.endSongTime = time + (params[1]) / (curChange.bpm - prevBPM) * Math.log(curChange.bpm / prevBPM) * 15000; + curChange.endStepTime = curChange.stepTime + params[1]; + curChange.continuous = true; + } + return curChange; + } + + public static function mapCharterBPMChanges(song:ChartData) { + var curChange:BPMChangeEvent = { + songTime: 0, + stepTime: 0, + beatTime: 0, + measureTime: 0, + bpm: song.meta.bpm, + beatsPerMeasure: song.meta.beatsPerMeasure.getDefault(4), + stepsPerBeat: CoolUtil.floorInt(song.meta.stepsPerBeat.getDefault(4)) + }; + bpmChangeMap = [curChange]; + invalidEvents = []; + + var grpEvents = Charter.instance.leftEventsGroup.members.concat(Charter.instance.rightEventsGroup.members); + var n = grpEvents.length; + + if (n != 0) { + grpEvents.sort((a, b) -> a.step < b.step ? -1 : 1); + curChangeIndex = 0; + + var i = 0, i2 = 0, j = 0, x = 0, events = [], ce, e; + while (i < n) { + x += (ce = grpEvents[i]).events.length; + if (++i < n && grpEvents[i].step == ce.step) continue; + + var eventTime = Conductor.getStepsInTime(ce.step, curChangeIndex); + if (Math.isNaN(eventTime)) eventTime = 0; + + events.resize(x); + while (i2 < i) { + x = 0; + ce = grpEvents[i2++]; + while (x < ce.events.length) if (validEventNames.contains((e = ce.events[x++]).name)) { + events[j++] = e; + if (eventTime != 0) e.time = eventTime; + } + } + events.resize(x = j); + events.sort((a, b) -> MathUtil.equal(a.time, b.time) ? (a.name == "Time Signature Change" ? -1 : 1) : 0); + + j = 0; + while (j < x) curChange = mapEvent(events[j++], curChange); + curChangeIndex = bpmChangeMap.length - 1; + + x = j = 0; + } + } + + curChangeIndex = 0; } private static var elapsed:Float; @@ -164,12 +357,10 @@ class Conductor return; } - if (lastSongPos != (lastSongPos = FlxG.sound.music.time - songOffset)) { - // update conductor + if (lastSongPos != (lastSongPos = FlxG.sound.music.time - songOffset)) songPosition = lastSongPos; - } else { + else songPosition += songOffset + elapsed * 1000; - } } private static function onStateSwitch(newState:FlxState) { @@ -177,137 +368,247 @@ class Conductor reset(); } private static var __lastChange:BPMChangeEvent; + private static var __updateStep:Bool; private static var __updateBeat:Bool; private static var __updateMeasure:Bool; private static function update() { - if (FlxG.state != null && FlxG.state is MusicBeatState) { - var state:MusicBeatState = cast FlxG.state; - if(state.cancelConductorUpdate) - return; - } + if (FlxG.state != null && FlxG.state is MusicBeatState && cast(FlxG.state, MusicBeatState).cancelConductorUpdate) return; __updateSongPos(FlxG.elapsed); - if (bpm > 0) { - // updates curBeat and stuff - __lastChange = { - stepTime: 0, - songTime: 0, - bpm: 0 - }; - for (change in Conductor.bpmChangeMap) - { - if (Conductor.songPosition >= change.songTime) - __lastChange = change; + var oldStep = curStep, oldBeat = curBeat, oldMeasure = curMeasure, oldChangeIndex = curChangeIndex; + + if ((curChangeIndex = getTimeInChangeIndex(songPosition, curChangeIndex)) > 0) { + var change = curChange; + curStepFloat = getTimeWithBPMInSteps(songPosition, curChangeIndex, getTimeWithIndexInBPM(songPosition, curChangeIndex)); + curBeatFloat = change.beatTime + (curStepFloat - change.stepTime) / change.stepsPerBeat; + curMeasureFloat = change.measureTime + (curBeatFloat - change.beatTime) / change.beatsPerMeasure; + } + else + curMeasureFloat = (curBeatFloat = (curStepFloat = songPosition / stepCrochet) / stepsPerBeat) / beatsPerMeasure; + + if (curChangeIndex != oldChangeIndex) { + var prev = bpmChangeMap[oldChangeIndex]; + if (prev != null) { + if (beatsPerMeasure != prev.beatsPerMeasure || stepsPerBeat != prev.stepsPerBeat) + onTimeSignatureChange.dispatch(beatsPerMeasure, stepsPerBeat); + + if (curChange.bpm != prev.bpm) onBPMChange.dispatch(curChange.bpm, curChange.endSongTime); } + else { + onTimeSignatureChange.dispatch(beatsPerMeasure, stepsPerBeat); + onBPMChange.dispatch(curChange.bpm, curChange.endSongTime); + } + } - if (__lastChange.bpm > 0 && bpm != __lastChange.bpm) changeBPM(__lastChange.bpm); + if (__updateStep = (curStep != (curStep = CoolUtil.floorInt(curStepFloat)))) { + if (curStep > oldStep) for (i in oldStep...curStep) onStepHit.dispatch(i + 1); + else onStepHit.dispatch(curStep); + } - curStepFloat = __lastChange.stepTime + ((Conductor.songPosition - __lastChange.songTime) / Conductor.stepCrochet); - curBeatFloat = curStepFloat / stepsPerBeat; - curMeasureFloat = curBeatFloat / beatsPerMeasure; + if (__updateBeat = (curBeat != (curBeat = CoolUtil.floorInt(curBeatFloat)))) { + if (curBeat > oldBeat) for (i in oldBeat...curBeat) onBeatHit.dispatch(i + 1); + else onBeatHit.dispatch(curBeat); + } - var oldStep = curStep; - var oldBeat = curBeat; - var oldMeasure = curMeasure; - if (curStep != (curStep = CoolUtil.floorInt(curStepFloat))) { - if (curStep < oldStep && oldStep - curStep < 2) return; - // updates step - __updateBeat = curBeat != (curBeat = CoolUtil.floorInt(curBeatFloat)); - __updateMeasure = __updateBeat && (curMeasure != (curMeasure = CoolUtil.floorInt(curMeasureFloat))); + if (__updateMeasure = (curMeasure != (curMeasure = CoolUtil.floorInt(curMeasureFloat)))) { + if (curMeasure > oldMeasure) for (i in oldMeasure...curMeasure) onMeasureHit.dispatch(i + 1); + else onMeasureHit.dispatch(curMeasure); + } - if (curStep > oldStep) { - for(i in oldStep...curStep) { - onStepHit.dispatch(i+1); + if (__updateStep || __updateBeat || __updateMeasure) { + var state = FlxG.state; + while (state != null) { + if (state is IBeatReceiver && (state.subState == null || state.persistentUpdate)) { + var st = cast(state, IBeatReceiver); + + if (__updateStep) { + if (curStep > oldStep) for (i in oldStep...curStep) st.stepHit(i + 1); + else st.stepHit(curStep); + } + + if (__updateBeat) { + if (curBeat > oldBeat) for (i in oldBeat...curBeat) st.beatHit(i + 1); + else st.beatHit(curBeat); + } + + if (__updateMeasure) { + if (curMeasure > oldMeasure) for (i in oldMeasure...curMeasure) st.measureHit(i + 1); + else st.measureHit(curMeasure); } } - if (__updateBeat && curBeat > oldBeat) { - for(i in oldBeat...curBeat) { - onBeatHit.dispatch(i+1); - } - } - if (__updateMeasure && curMeasure > oldMeasure) { - for(i in oldMeasure...curMeasure) { - onMeasureHit.dispatch(i+1); - } - } - - if (FlxG.state is IBeatReceiver) { - var state = FlxG.state; - while(state != null) { - if (state is IBeatReceiver && (state.subState == null || state.persistentUpdate)) { - var st:IBeatReceiver = cast state; - if (curStep > oldStep) { - for(i in oldStep...curStep) { - st.stepHit(i+1); - } - } - if (__updateBeat && curBeat > oldBeat) { - for(i in oldBeat...curBeat) { - st.beatHit(i+1); - } - } - if (__updateMeasure && curMeasure > oldMeasure) { - for(i in oldMeasure...curMeasure) { - st.measureHit(i+1); - } - } - } - state = state.subState; - } - } - + state = state.subState; } } } - public static function changeBPM(newBpm:Float, beatsPerMeasure:Float = 4, stepsPerBeat:Int = 4) - { - bpm = newBpm; - - crochet = ((60 / bpm) * 1000); - stepCrochet = crochet / stepsPerBeat; - - Conductor.beatsPerMeasure = beatsPerMeasure; - Conductor.stepsPerBeat = stepsPerBeat; - - onBPMChange.dispatch(bpm); + public static function getTimeInChangeIndex(time:Float, index:Int = 0):Int { + if (bpmChangeMap.length < 2) return bpmChangeMap.length - 1; + else if (bpmChangeMap[index = CoolUtil.boundInt(index, 0, bpmChangeMap.length - 1)].songTime > time) { + while (--index > 0) if (time > bpmChangeMap[index].songTime) return index; + return 0; + } + else { + for (i in index...bpmChangeMap.length) if (bpmChangeMap[i].songTime > time) return i - 1; + return bpmChangeMap.length - 1; + } } - public static function getTimeForStep(step:Float) { - var bpmChange:BPMChangeEvent = { - stepTime: 0, - songTime: 0, - bpm: bpm - }; - - for(change in bpmChangeMap) - if (change.stepTime < step && change.stepTime >= bpmChange.stepTime) - bpmChange = change; - - return bpmChange.songTime + ((step - bpmChange.stepTime) * ((60 / bpmChange.bpm) * (1000/stepsPerBeat))); + public static function getStepsInChangeIndex(stepTime:Float, index:Int = 0):Int { + if (bpmChangeMap.length < 2) return bpmChangeMap.length - 1; + else if (bpmChangeMap[index = CoolUtil.boundInt(index, 0, bpmChangeMap.length - 1)].stepTime > stepTime) { + while (--index > 0) if (stepTime > bpmChangeMap[index].stepTime) return index; + return 0; + } + else { + for (i in index...bpmChangeMap.length) if (bpmChangeMap[i].stepTime > stepTime) return i - 1; + return bpmChangeMap.length - 1; + } } - public static function getStepForTime(time:Float) { - var bpmChange:BPMChangeEvent = { - stepTime: 0, - songTime: 0, - bpm: bpm - }; - - for(change in bpmChangeMap) - if (change.songTime < time && change.songTime >= bpmChange.songTime) - bpmChange = change; - - return bpmChange.stepTime + ((time - bpmChange.songTime) / ((60 / bpmChange.bpm) * (1000/stepsPerBeat))); + public static function getBeatsInChangeIndex(beatTime:Float, index:Int = 0):Int { + if (bpmChangeMap.length < 2) return bpmChangeMap.length - 1; + else if (bpmChangeMap[index = CoolUtil.boundInt(index, 0, bpmChangeMap.length - 1)].beatTime > beatTime) { + while (--index > 0) if (beatTime > bpmChangeMap[index].beatTime) return index; + return 0; + } + else { + for (i in index...bpmChangeMap.length) if (bpmChangeMap[i].beatTime > beatTime) return i - 1; + return bpmChangeMap.length - 1; + } } + public static function getMeasuresInChangeIndex(measureTime:Float, index:Int = 0):Int { + if (bpmChangeMap.length < 2) return bpmChangeMap.length - 1; + else if (bpmChangeMap[index = CoolUtil.boundInt(index, 0, bpmChangeMap.length - 1)].measureTime > measureTime) { + while (--index > 0) if (measureTime > bpmChangeMap[index].measureTime) return index; + return 0; + } + else { + for (i in index...bpmChangeMap.length) if (bpmChangeMap[i].measureTime > measureTime) return i - 1; + return bpmChangeMap.length - 1; + } + } + + public static function getTimeWithIndexInBPM(time:Float, index:Int):Float { + var bpmChange = bpmChangeMap[index]; + if (bpmChange.continuous && time < bpmChange.endSongTime && index > 0) { + var prevBPM = bpmChangeMap[index - 1].bpm; + if (time <= bpmChange.songTime) return prevBPM; + + var ratio = (time - bpmChange.songTime) / (bpmChange.endSongTime - bpmChange.songTime); + return Math.pow(prevBPM, 1 - ratio) * Math.pow(bpmChange.bpm, ratio); + } + return bpmChange.bpm; + } + + public static function getStepsWithIndexInBPM(stepTime:Float, index:Int):Float { + var bpmChange = bpmChangeMap[index]; + if (bpmChange.continuous && index > 0) { + var prevBPM = bpmChangeMap[index - 1].bpm; + if (stepTime <= bpmChange.stepTime) return prevBPM; + + var endStepTime = bpmChange.stepTime + (bpmChange.endSongTime - bpmChange.songTime) * (bpmChange.bpm - prevBPM) / Math.log(bpmChange.bpm / prevBPM) / 15000; + if (stepTime < endStepTime) return FlxMath.remapToRange(stepTime, bpmChange.stepTime, endStepTime, prevBPM, bpmChange.bpm); + } + return bpmChange.bpm; + } + + public static function getTimeInBPM(time:Float):Float { + if (bpmChangeMap.length == 0) return dummyChange.bpm; + return getTimeWithIndexInBPM(time, getTimeInChangeIndex(time)); + } + + public static function getTimeWithBPMInSteps(time:Float, index:Int, bpm:Float):Float { + var bpmChange = bpmChangeMap[index]; + if (bpmChange.continuous && time > bpmChange.songTime && index > 0) { + var prevBPM = bpmChangeMap[index - 1].bpm; + if (time > bpmChange.endSongTime) + return bpmChange.stepTime + (((bpmChange.endSongTime - bpmChange.songTime) * (bpmChange.bpm - prevBPM)) + / Math.log(bpmChange.bpm / prevBPM) + (time - bpmChange.endSongTime) * bpm) / 15000; + else + return bpmChange.stepTime + (time - bpmChange.songTime) * (bpm - prevBPM) / Math.log(bpm / prevBPM) / 15000; + } + else { + return bpmChange.stepTime + (time - bpmChange.songTime) / (15000 / bpm); + } + } + + public static function getTimeInBeats(time:Float, from:Int = 0):Float { + var index = getTimeInChangeIndex(time, from); + if (index == -1) return time / (60000 / dummyChange.bpm); + else if (index == 0) return time / (15000 / bpmChangeMap[index].bpm) / bpmChangeMap[index].stepsPerBeat; + else { + var change = bpmChangeMap[index]; + return change.beatTime + (getTimeWithBPMInSteps(time, index, getTimeWithIndexInBPM(time, index)) - change.stepTime) / change.stepsPerBeat; + } + } + + public static function getTimeInSteps(time:Float, from:Int = 0):Float { + var index = getTimeInChangeIndex(time, from); + return index < 1 ? time / (15000 / getTimeInBPM(time)) : getTimeWithBPMInSteps(time, index, getTimeWithIndexInBPM(time, index)); + } + + @:noCompletion + @:haxe.warning("-WDeprecated") + public static inline function getStepForTime(time:Float):Float return getTimeInSteps(time); + + public static function getStepsWithBPMInTime(stepTime:Float, index:Int, bpm:Float):Float { + var bpmChange = bpmChangeMap[index]; + if (bpmChange.continuous && stepTime > bpmChange.stepTime && index > 0) { + var prevBPM = bpmChangeMap[index - 1].bpm; + var time = bpmChange.songTime + (stepTime - bpmChange.stepTime) / (bpm - prevBPM) * Math.log(bpm / prevBPM) * 15000; + if (time > bpmChange.endSongTime) + return (15000 * (stepTime - bpmChange.stepTime) - ((bpmChange.endSongTime - bpmChange.songTime) * (bpm - prevBPM)) + / Math.log(bpm / prevBPM)) / bpm + bpmChange.endSongTime; + else + return time; + } + else { + return bpmChange.songTime + (stepTime - bpmChange.stepTime) * (15000 / bpm); + } + } + + public static function getMeasuresInTime(measureTime:Float, from:Int = 0):Float { + var index = getMeasuresInChangeIndex(measureTime, from); + if (index == -1) return measureTime * (15000 / dummyChange.bpm); + else if (index == 0) return measureTime * (15000 / bpmChangeMap[index].bpm) * bpmChangeMap[index].stepsPerBeat * bpmChangeMap[index].beatsPerMeasure; + else { + var change = bpmChangeMap[index]; + var stepTime = change.stepTime + (measureTime - change.measureTime) * change.stepsPerBeat * change.beatsPerMeasure; + return getStepsWithBPMInTime(stepTime, index, getStepsWithIndexInBPM(stepTime, index)); + } + } + + public static function getBeatsInTime(beatTime:Float, from:Int = 0):Float { + var index = getBeatsInChangeIndex(beatTime, from); + if (index == -1) return beatTime * (15000 / dummyChange.bpm); + else if (index == 0) return beatTime * (15000 / bpmChangeMap[index].bpm) * bpmChangeMap[index].stepsPerBeat; + else { + var change = bpmChangeMap[index]; + var stepTime = change.stepTime + (beatTime - change.beatTime) * change.stepsPerBeat; + return getStepsWithBPMInTime(stepTime, index, getStepsWithIndexInBPM(stepTime, index)); + } + } + + public static function getStepsInTime(stepTime:Float, from:Int = 0):Float { + var index = getStepsInChangeIndex(stepTime, from); + return index < 1 ? stepTime * (15000 / bpmChangeMap[index].bpm) : getStepsWithBPMInTime(stepTime, index, getStepsWithIndexInBPM(stepTime, index)); + } + + @:noCompletion + @:haxe.warning("-WDeprecated") + public static inline function getTimeForStep(steps:Float):Float return getStepsInTime(steps); + public static inline function getMeasureLength() return stepsPerBeat * beatsPerMeasure; public static inline function getMeasuresLength() { if (FlxG.sound.music == null) return 0.0; - return getStepForTime(FlxG.sound.music.length) / getMeasureLength(); + var length = FlxG.sound.music.length; + var index = getTimeInChangeIndex(length, bpmChangeMap.length - 1); + var change = bpmChangeMap[index]; + return change.measureTime + (getTimeInBeats(length, index) - change.beatTime) / change.beatsPerMeasure; } -} +} \ No newline at end of file diff --git a/source/funkin/backend/system/Controls.hx b/source/funkin/backend/system/Controls.hx index 48036036..0778469d 100644 --- a/source/funkin/backend/system/Controls.hx +++ b/source/funkin/backend/system/Controls.hx @@ -173,6 +173,7 @@ class Controls extends FlxActionSet @:pressed("dev-reload") public var DEV_RELOAD_HOLD(get, set): Bool; @:justReleased("dev-reload") public var DEV_RELOAD_R(get, set): Bool; + @:allow(funkin.backend.utils.ControlsUtil) var byName:Map<String, FlxActionDigital> = []; public var gamepadsAdded:Array<Int> = []; @@ -242,13 +243,13 @@ class Controls extends FlxActionSet macro_forEachBound(control, (action, _) -> removeKeys(action, keys)); } - inline static function addKeys(action:FlxActionDigital, keys:Array<FlxKey>, state:FlxInputState) + public inline static function addKeys(action:FlxActionDigital, keys:Array<FlxKey>, state:FlxInputState) { for (key in keys) action.addKey(key, state); } - static function removeKeys(action:FlxActionDigital, keys:Array<FlxKey>) + public static function removeKeys(action:FlxActionDigital, keys:Array<FlxKey>) { var i = action.inputs.length; while (i-- > 0) @@ -325,13 +326,13 @@ class Controls extends FlxActionSet macro_forEachBound(control, (action, _) -> removeButtons(action, gamepadID, buttons)); } - inline static function addButtons(action:FlxActionDigital, buttons:Array<FlxGamepadInputID>, state, id) + public inline static function addButtons(action:FlxActionDigital, buttons:Array<FlxGamepadInputID>, state, id) { for (button in buttons) action.addGamepad(button, state, id); } - static function removeButtons(action:FlxActionDigital, gamepadID:Int, buttons:Array<FlxGamepadInputID>) + public static function removeButtons(action:FlxActionDigital, gamepadID:Int, buttons:Array<FlxGamepadInputID>) { var i = action.inputs.length; while (i-- > 0) @@ -342,8 +343,21 @@ class Controls extends FlxActionSet } } - inline static function isGamepad(input:FlxActionInput, deviceID:Int) + public inline static function isGamepad(input:FlxActionInput, deviceID:Int) { return input.device == GAMEPAD && (deviceID == FlxInputDeviceID.ALL || input.deviceID == deviceID); } -} + + @:nullSafety(Off) + public inline function getJustPressed(name:String) { + return ControlsUtil.getJustPressed(this, name); + } + @:nullSafety(Off) + public inline function getJustReleased(name:String) { + return ControlsUtil.getJustReleased(this, name); + } + @:nullSafety(Off) + public inline function getPressed(name:String) { + return ControlsUtil.getPressed(this, name); + } +} \ No newline at end of file diff --git a/source/funkin/backend/system/FakeCamera.hx b/source/funkin/backend/system/FakeCamera.hx new file mode 100644 index 00000000..99d0eb1b --- /dev/null +++ b/source/funkin/backend/system/FakeCamera.hx @@ -0,0 +1,50 @@ +package funkin.backend.system; + +import flixel.graphics.FlxGraphic; +import flixel.graphics.frames.FlxFrame; +import flixel.graphics.tile.FlxDrawTrianglesItem; +import flixel.math.FlxMatrix; +import flixel.math.FlxPoint; +import flixel.system.FlxAssets.FlxShader; +import openfl.display.BitmapData; +import openfl.display.BlendMode; +import openfl.geom.ColorTransform; +import openfl.geom.Point; +import openfl.geom.Rectangle; + +class FakeCamera extends FlxCamera { + public static final instance = new FakeCamera(); + + public function new() { + super(); + + visible = false; + } + + override function startTrianglesBatch(graphic:FlxGraphic, smoothing:Bool = false, isColored:Bool = false, ?blend:BlendMode, ?hasColorOffsets:Bool, ?shader:FlxShader) { return null;} + override function startQuadBatch(graphic:FlxGraphic, colored:Bool, hasColorOffsets:Bool = false, ?blend:BlendMode, smooth:Bool = false, ?shader:FlxShader) { return null;} + override function clearDrawStack() {} + override function render() {} + + public override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {} + public override function copyPixels(?frame:FlxFrame, ?pixels:BitmapData, ?sourceRect:Rectangle, destPoint:Point, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) {} + public override function drawTriangles(graphic:FlxGraphic, vertices:DrawData<Float>, indices:DrawData<Int>, uvtData:DrawData<Float>, ?colors:DrawData<Int>, ?position:FlxPoint, ?blend:BlendMode, repeat:Bool = false, smoothing:Bool = false, ?transform:ColorTransform, ?shader:FlxShader):Void {} + + public override function update(elapsed:Float) {} + + // override function set_zoom(Zoom:Float):Float return zoom = (Zoom == 0) ? FlxCamera.defaultZoom : Zoom; + + public override function setScale(X:Float, Y:Float):Void {} +} + +class FakeCallCamera extends FakeCamera { + public static final instance = new FakeCallCamera(); + public var ignoreDraws:Bool = false; + + public dynamic function onDraw(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) { + } + + override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) { + if (!ignoreDraws) onDraw(frame, pixels, matrix, transform, blend, smoothing, shader); + } +} \ No newline at end of file diff --git a/source/funkin/backend/system/Flags.hx b/source/funkin/backend/system/Flags.hx index 934919f1..4f15bb29 100644 --- a/source/funkin/backend/system/Flags.hx +++ b/source/funkin/backend/system/Flags.hx @@ -111,7 +111,9 @@ class Flags { public static var DEFAULT_GAMEOVER_LOSS_SFX:String = "gameOverSFX"; public static var DEFAULT_GAMEOVER_RETRY_SFX:String = "gameOverEnd"; - public static var DEFAULT_CAM_ZOOM_INTERVAL:Int = 4; + public static var DEFAULT_CAM_ZOOM_INTERVAL:Int = 1; + public static var DEFAULT_CAM_ZOOM_OFFSET:Float = 0; + //public static var DEFAULT_CAM_ZOOM_EVERY:BeatType = MEASURE; public static var DEFAULT_CAM_ZOOM_STRENGTH:Int = 1; public static var DEFAULT_CAM_ZOOM:Float = 1.05; // what zoom level it defaults to public static var DEFAULT_HUD_ZOOM:Float = 1.0; diff --git a/source/funkin/backend/system/Logs.hx b/source/funkin/backend/system/Logs.hx index dda3b9ee..44aebb6b 100644 --- a/source/funkin/backend/system/Logs.hx +++ b/source/funkin/backend/system/Logs.hx @@ -6,7 +6,7 @@ import funkin.backend.utils.NativeAPI.ConsoleColor; import funkin.backend.utils.NativeAPI; import haxe.Log; -class Logs { +final class Logs { private static var __showing:Bool = false; public static var nativeTrace = Log.trace; diff --git a/source/funkin/backend/system/Main.hx b/source/funkin/backend/system/Main.hx index 95b4542c..ab435a7c 100644 --- a/source/funkin/backend/system/Main.hx +++ b/source/funkin/backend/system/Main.hx @@ -1,5 +1,7 @@ package funkin.backend.system; +import sys.io.File; +import sys.FileSystem; import flixel.addons.transition.FlxTransitionSprite.GraphicTransTileDiamond; import flixel.addons.transition.FlxTransitionableState; import flixel.addons.transition.TransitionData; @@ -31,7 +33,7 @@ class Main extends Sprite public static var instance:Main; public static var modToLoad:String = null; - public static var forceGPUOnlyBitmapsOff:Bool = #if windows false #else true #end; + public static var forceGPUOnlyBitmapsOff:Bool = #if desktop false #else true #end; public static var noTerminalColor:Bool = false; public static var verbose:Bool = false; @@ -168,6 +170,9 @@ class Main extends Sprite ModsFolder.init(); #if MOD_SUPPORT + if (FileSystem.exists("mods/autoload.txt")) + modToLoad = File.getContent("mods/autoload.txt").trim(); + ModsFolder.switchMod(modToLoad.getDefault(Options.lastLoadedMod)); #end diff --git a/source/funkin/backend/system/MainState.hx b/source/funkin/backend/system/MainState.hx index bb734a22..d4b5ef4d 100644 --- a/source/funkin/backend/system/MainState.hx +++ b/source/funkin/backend/system/MainState.hx @@ -36,6 +36,7 @@ class MainState extends FlxState { #end Options.save(); + ControlsUtil.resetCustomControls(); FlxG.bitmap.reset(); FlxG.sound.destroy(true); @@ -109,6 +110,7 @@ class MainState extends FlxState { Main.refreshAssets(); DiscordUtil.init(); EventsData.reloadEvents(); + ControlsUtil.loadCustomControls(); TitleState.initialized = false; if (Framerate.isLoaded) diff --git a/source/funkin/backend/system/framerate/ConductorInfo.hx b/source/funkin/backend/system/framerate/ConductorInfo.hx index c13e026f..e615bbba 100644 --- a/source/funkin/backend/system/framerate/ConductorInfo.hx +++ b/source/funkin/backend/system/framerate/ConductorInfo.hx @@ -12,7 +12,7 @@ class ConductorInfo extends FramerateCategory { _text += '\n - ${Conductor.curStep} steps'; _text += '\n - ${Conductor.curMeasure} measures'; _text += '\nCurrent BPM: ${Conductor.bpm}'; - _text += '\nTime Signature: ${Conductor.beatsPerMeasure}/${Conductor.stepsPerBeat}'; + _text += '\nTime Signature: ${Conductor.beatsPerMeasure}/${Conductor.denominator}'; this.text.text = _text; super.__enterFrame(t); diff --git a/source/funkin/backend/system/framerate/Framerate.hx b/source/funkin/backend/system/framerate/Framerate.hx index c325e81c..2acc7601 100644 --- a/source/funkin/backend/system/framerate/Framerate.hx +++ b/source/funkin/backend/system/framerate/Framerate.hx @@ -118,7 +118,7 @@ class Framerate extends Sprite { x = 10 + offset.x; y = 2 + offset.y; - var width = Math.max(fpsCounter.width, #if SHOW_BUILD_ON_FPS Math.max(memoryCounter.width, codenameBuildField.width) #else memoryCounter.width #end) + (x*2); + var width = MathUtil.maxSmart(fpsCounter.width, memoryCounter.width #if SHOW_BUILD_ON_FPS , codenameBuildField.width #end) + (x*2); var height = #if SHOW_BUILD_ON_FPS codenameBuildField.y + codenameBuildField.height #else memoryCounter.y + memoryCounter.height #end; bgSprite.x = -x; bgSprite.y = offset.x; diff --git a/source/funkin/backend/system/framerate/SystemInfo.hx b/source/funkin/backend/system/framerate/SystemInfo.hx index 30aa095b..154712bd 100644 --- a/source/funkin/backend/system/framerate/SystemInfo.hx +++ b/source/funkin/backend/system/framerate/SystemInfo.hx @@ -3,6 +3,10 @@ package funkin.backend.system.framerate; import funkin.backend.system.Logs; import funkin.backend.utils.MemoryUtil; import funkin.backend.utils.native.HiddenProcess; +#if cpp +import cpp.Float64; +import cpp.UInt64; +#end using StringTools; @@ -17,7 +21,7 @@ class SystemInfo extends FramerateCategory { static var __formattedSysText:String = ""; - public static inline function init() { + public static function init() { #if linux var process = new HiddenProcess("cat", ["/etc/os-release"]); if (process.exitCode() != 0) Logs.trace('Unable to grab OS Label', ERROR, RED); @@ -107,11 +111,13 @@ class SystemInfo extends FramerateCategory { #end if(openfl.display3D.Context3D.__glMemoryTotalAvailable != -1) { - var vRAMBytes:UInt = cast flixel.FlxG.stage.context3D.gl.getParameter(openfl.display3D.Context3D.__glMemoryTotalAvailable); + var vRAMBytes:Int = cast flixel.FlxG.stage.context3D.gl.getParameter(openfl.display3D.Context3D.__glMemoryTotalAvailable); if (vRAMBytes == 1000 || vRAMBytes == 1 || vRAMBytes <= 0) Logs.trace('Unable to grab GPU VRAM', ERROR, RED); - else - vRAM = CoolUtil.getSizeString(vRAMBytes * 1000); + else { + var vRAMBytesFloat:#if cpp Float64 #else Float #end = vRAMBytes*1024; + vRAM = CoolUtil.getSizeString64(vRAMBytesFloat); + } } } else Logs.trace('Unable to grab GPU Info', ERROR, RED); diff --git a/source/funkin/backend/system/modules/AudioSwitchFix.hx b/source/funkin/backend/system/modules/AudioSwitchFix.hx index 941846e0..47eea017 100644 --- a/source/funkin/backend/system/modules/AudioSwitchFix.hx +++ b/source/funkin/backend/system/modules/AudioSwitchFix.hx @@ -11,7 +11,7 @@ import lime.media.AudioManager; * hi gray itsa me yoshicrafter29 i fixed it hehe */ @:dox(hide) -class AudioSwitchFix { +final class AudioSwitchFix { @:noCompletion private static function onStateSwitch(state:FlxState):Void { #if windows diff --git a/source/funkin/backend/system/modules/CrashHandler.hx b/source/funkin/backend/system/modules/CrashHandler.hx index 1debede9..61c5029d 100644 --- a/source/funkin/backend/system/modules/CrashHandler.hx +++ b/source/funkin/backend/system/modules/CrashHandler.hx @@ -7,7 +7,7 @@ import openfl.errors.Error; import openfl.events.ErrorEvent; import openfl.events.UncaughtErrorEvent; -class CrashHandler { +final class CrashHandler { public static function init() { Lib.current.loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtError); #if cpp diff --git a/source/funkin/backend/system/updating/UpdateAvailableScreen.hx b/source/funkin/backend/system/updating/UpdateAvailableScreen.hx index 8814dcec..c788cdf9 100644 --- a/source/funkin/backend/system/updating/UpdateAvailableScreen.hx +++ b/source/funkin/backend/system/updating/UpdateAvailableScreen.hx @@ -100,7 +100,7 @@ class UpdateAvailableScreen extends MusicBeatState { public override function update(elapsed:Float) { super.update(elapsed); - destY = FlxMath.bound(destY - (FlxG.mouse.wheel * 75), 0, Math.max(0, changeLogText.height - FlxG.height + versionCheckBG.height + 20 + optionsBG.height)); + destY = CoolUtil.bound(destY - (FlxG.mouse.wheel * 75), 0, Math.max(0, changeLogText.height - FlxG.height + versionCheckBG.height + 20 + optionsBG.height)); FlxG.camera.scroll.y = lerp(FlxG.camera.scroll.y, destY, 1/3); if (controls.LEFT_P || controls.RIGHT_P) { diff --git a/source/funkin/backend/utils/BitmapUtil.hx b/source/funkin/backend/utils/BitmapUtil.hx index 776e9330..e4f7596c 100644 --- a/source/funkin/backend/utils/BitmapUtil.hx +++ b/source/funkin/backend/utils/BitmapUtil.hx @@ -2,8 +2,10 @@ package funkin.backend.utils; import flixel.util.FlxColor; import openfl.display.BitmapData; +import openfl.geom.Point; +import openfl.geom.Rectangle; -class BitmapUtil { +final class BitmapUtil { /** * Returns the most present color in a Bitmap. * @param bmap Bitmap @@ -18,7 +20,7 @@ class BitmapUtil { for(y in 0...bmap.height) { for(x in 0...bmap.width) { color = bmap.getPixel32(x, y); - fixedColor = 0xFF000000 + (color % 0x1000000); + fixedColor = 0xFF000000 | (color & 0xFFFFFF); if (!colorMap.exists(fixedColor)) colorMap[fixedColor] = 0; colorMap[fixedColor] += color.alphaFloat; @@ -49,7 +51,7 @@ class BitmapUtil { for(y in 0...bmap.height) { for(x in 0...bmap.width) { color = bmap.getPixel32(x, y); - fixedColor = 0xFF000000 + (color % 0x1000000); + fixedColor = 0xFF000000 | (color & 0xFFFFFF); if (!colorMap.exists(fixedColor)) colorMap[fixedColor] = 0; colorMap[fixedColor] += color.alphaFloat * 0.33 + (0.67 * (color.saturation * (2 * (color.lightness > 0.5 ? 0.5 - (color.lightness) : color.lightness)))); @@ -66,4 +68,49 @@ class BitmapUtil { } return mostPresentColor; } + + /** + * Returns a new bitmap without any empty transperent space on the edges + * @param bitmap The bitmap to be cropped + */ + public static function crop(bitmap:BitmapData) { + var bitmapBounds:Rectangle = BitmapUtil.bounds(bitmap); + + var croppedBitmap:BitmapData = new BitmapData(Std.int(bitmapBounds.width), Std.int(bitmapBounds.height), true, 0x00000000); + croppedBitmap.copyPixels(bitmap, bitmapBounds, new Point(0,0)); + return croppedBitmap; + } + + /** + * Get bounds of non empty pixels in the bitmap + * @param bitmap + * @return + */ + public static function bounds(bitmap:BitmapData, ?limit:Rectangle = null):Rectangle { + var hasLimit:Bool = limit != null; + // Searching bounds + var startX:Int = hasLimit ? Std.int(limit.x) : 0; + var startY:Int = hasLimit ? Std.int(limit.y) : 0; + var endX:Int = hasLimit ? Std.int(limit.width) : bitmap.width; + var endY:Int = hasLimit ? Std.int(limit.height) : bitmap.height; + + // Detected bounds + var minX:Int = endX; + var minY:Int = endY; + var maxX:Int = startX; + var maxY:Int = startY; + + for (y in startY...endY) { + for (x in startX...endX) { + if (bitmap.getPixel32(x, y) != 0x00000000) { + if (x < minX) minX = x; + if (y < minY) minY = y; + if (x > maxX) maxX = x; + if (y > maxY) maxY = y; + } + } + } + + return new Rectangle(minX, minY, maxX-minX+1, maxY-minY+1); + } } \ No newline at end of file diff --git a/source/funkin/backend/utils/ControlsUtil.hx b/source/funkin/backend/utils/ControlsUtil.hx new file mode 100644 index 00000000..9d48c25c --- /dev/null +++ b/source/funkin/backend/utils/ControlsUtil.hx @@ -0,0 +1,166 @@ +package funkin.backend.utils; + +import funkin.options.PlayerSettings; +import flixel.input.FlxInput; +import flixel.input.actions.FlxAction; +import funkin.backend.system.Controls; +import flixel.input.actions.FlxActionInput; +import flixel.input.actions.FlxActionManager; +import flixel.input.actions.FlxActionSet; +import flixel.input.gamepad.FlxGamepadInputID; +import flixel.input.keyboard.FlxKey; +import haxe.xml.Access; + +class ControlsUtil { + + private static var storedCustomControls:Array<String> = []; + + public static function initCustomControl(controls:Controls, name:String) { + if (controls.byName.exists(name)) return; + + controls.byName.set(name, new FlxActionDigital(name)); + controls.byName.set(name+"_P", new FlxActionDigital(name+"_P")); + controls.byName.set(name+"_R", new FlxActionDigital(name+"_R")); + storedCustomControls.push(name); + } + + public static function addKeysToCustomControl(controls:Controls, name:String, keys:Array<FlxKey>) { + if (!controls.byName.exists(name)) return; + + var pressed:FlxActionDigital = controls.byName.get(name); + if (pressed != null) Controls.addKeys(pressed, keys, PRESSED); + + var justPressed:FlxActionDigital = controls.byName.get(name+"_P"); + if (justPressed != null) Controls.addKeys(justPressed, keys, JUST_PRESSED); + + var justReleased:FlxActionDigital = controls.byName.get(name+"_R"); + if (justReleased != null) Controls.addKeys(justReleased, keys, JUST_RELEASED); + } + + public static function removeKeysFromCustomControl(controls:Controls, name:String, keys:Array<FlxKey>) { + if (!controls.byName.exists(name)) return; + + var pressed:FlxActionDigital = controls.byName.get(name); + if (pressed != null) Controls.removeKeys(pressed, keys); + + var justPressed:FlxActionDigital = controls.byName.get(name+"_P"); + if (justPressed != null) Controls.removeKeys(justPressed, keys); + + var justReleased:FlxActionDigital = controls.byName.get(name+"_R"); + if (justReleased != null) Controls.removeKeys(justReleased, keys); + } + + public static function resetCustomControls() { + removeCustomControls(PlayerSettings.solo.controls); + removeCustomControls(PlayerSettings.player1.controls); + removeCustomControls(PlayerSettings.player2.controls); + storedCustomControls = []; + } + + private static inline function removeCustomControls(controls:Controls) { + for (name in storedCustomControls) { + var pressed:FlxActionDigital = controls.byName.get(name); + if (pressed != null) pressed.destroy(); + + var justPressed:FlxActionDigital = controls.byName.get(name+"_P"); + if (justPressed != null) justPressed.destroy(); + + var justReleased:FlxActionDigital = controls.byName.get(name+"_R"); + if (justReleased != null) justReleased.destroy(); + + controls.byName.remove(name); + controls.byName.remove(name+"_P"); + controls.byName.remove(name+"_R"); + } + } + + + private static inline function checkControl(controls:Controls, name:String) { + var control = getControl(controls, name); + if (control != null) return control.check(); + return false; + } + + public static inline function getControl(controls:Controls, name:String) : FlxActionDigital { + return controls.byName.get(name); + } + + public static inline function getJustPressed(controls:Controls, name:String) { + return checkControl(controls, name + "_P"); + } + public static inline function getJustReleased(controls:Controls, name:String) { + return checkControl(controls, name + "_R"); + } + public static inline function getPressed(controls:Controls, name:String) { + return checkControl(controls, name); + } + + public static function loadCustomControls() { + var xmlPath = Paths.xml("config/controls"); + for(source in [funkin.backend.assets.AssetsLibraryList.AssetSource.SOURCE, funkin.backend.assets.AssetsLibraryList.AssetSource.MODS]) { + if (Paths.assetsTree.existsSpecific(xmlPath, "TEXT", source)) { + var access:Access = null; + try { + access = new Access(Xml.parse(Paths.assetsTree.getSpecificAsset(xmlPath, "TEXT", source)).firstElement()); + } catch(e) { + Logs.trace('Error while parsing controls.xml: ${Std.string(e)}', ERROR); + } + + if (access != null) { + parseControlsXml(access); + } + } + } + } + + private static inline function parseControlsXml(xml:Access) { + for (category in xml.elements) { + for (control in category.elements) { + if (!control.has.name) { + continue; + } + + var name = control.getAtt("name"); + var saveName = control.getAtt("saveName").getDefault(""); + var defaultKeyP1 = control.getAtt("keyP1").getDefault(""); + var defaultKeyP2 = control.getAtt("keyP2").getDefault(""); + + var keyP1:Null<FlxKey> = 0; + var keyP2:Null<FlxKey> = 0; + + ControlsUtil.initCustomControl(PlayerSettings.solo.controls, name); + ControlsUtil.initCustomControl(PlayerSettings.player1.controls, name); + ControlsUtil.initCustomControl(PlayerSettings.player2.controls, name); + + if (saveName != "") { + var p1:Null<FlxKey> = Reflect.getProperty(FlxG.save.data, "P1_"+saveName); + var p2:Null<FlxKey> = Reflect.getProperty(FlxG.save.data, "P2_"+saveName); + + //check for save data (and load default if it doesnt exist) + if (p1 != null) { + keyP1 = p1; + } else { + if (defaultKeyP1 != "") keyP1 = FlxKey.fromString(defaultKeyP1); + Reflect.setProperty(FlxG.save.data, "P1_"+saveName, keyP1); + } + + if (p2 != null) { + keyP2 = p2; + } else { + if (defaultKeyP2 != "") keyP2 = FlxKey.fromString(defaultKeyP2); + Reflect.setProperty(FlxG.save.data, "P2_"+saveName, keyP2); + } + + } else { + //if no save data just load the default + if (defaultKeyP1 != "") keyP1 = FlxKey.fromString(defaultKeyP1); + if (defaultKeyP2 != "") keyP2 = FlxKey.fromString(defaultKeyP2); + } + + ControlsUtil.addKeysToCustomControl(PlayerSettings.solo.controls, name, [keyP1, keyP2]); + ControlsUtil.addKeysToCustomControl(PlayerSettings.player1.controls, name, [keyP1, 0]); + ControlsUtil.addKeysToCustomControl(PlayerSettings.player2.controls, name, [0, keyP2]); + } + } + } +} \ No newline at end of file diff --git a/source/funkin/backend/utils/CoolUtil.hx b/source/funkin/backend/utils/CoolUtil.hx index 82de2d1c..065efb6b 100644 --- a/source/funkin/backend/utils/CoolUtil.hx +++ b/source/funkin/backend/utils/CoolUtil.hx @@ -1,5 +1,13 @@ package funkin.backend.utils; +#if cpp +import cpp.Float64; +#end +import flxanimate.data.AnimationData.AnimAtlas; +import flixel.graphics.FlxGraphic; +import openfl.display.BitmapData; +import flixel.graphics.frames.FlxAtlasFrames; +import flixel.graphics.frames.FlxFramesCollection; #if sys import sys.FileSystem; #end @@ -37,6 +45,9 @@ import haxe.io.Path; import haxe.xml.Access; import lime.utils.Assets; import openfl.geom.ColorTransform; +import flixel.math.FlxPoint; +import haxe.Constraints.IMap; +import haxe.EnumTools.EnumValueTools; using StringTools; @@ -44,7 +55,7 @@ using StringTools; * Various utilities, that have no specific Util class. **/ @:allow(funkin.game.PlayState) -class CoolUtil +final class CoolUtil { /** * Gets the last exception stack. Useful for debugging. @@ -233,10 +244,8 @@ class CoolUtil * Whenever a value is NaN or not. * @param v Value */ - public static inline function isNaN(v:Dynamic) { - if (v is Float || v is Int) - return Math.isNaN(cast(v, Float)); - return false; + public static inline function isNaN(v:Dynamic):Bool { + return (v is Float) ? Math.isNaN(cast(v, Float)) : false; } /** @@ -285,22 +294,41 @@ class CoolUtil return str; } + private static var sizeLabels:Array<String> = ["B", "KB", "MB", "GB", "TB"]; + /** * Returns a string representation of a size, following this format: `1.02 GB`, `134.00 MB` * @param size Size to convert to string * @return String Result string representation */ public static function getSizeString(size:Float):String { - var labels = ["B", "KB", "MB", "GB", "TB"]; var rSize:Float = size; var label:Int = 0; - while(rSize > 1024 && label < labels.length-1) { + var len = sizeLabels.length; + while(rSize >= 1024 && label < len-1) { label++; rSize /= 1024; } - return '${Std.int(rSize) + "." + addZeros(Std.string(Std.int((rSize % 1) * 100)), 2)}${labels[label]}'; + return Std.int(rSize) + ((label <= 1) ? "" : "." + addZeros(Std.string(Std.int((rSize % 1) * 100)), 2)) + sizeLabels[label]; } + /** + * Returns a string representation of a size, following this format: `1.02 GB`, `134.00 MB`, using Float64 on cpp targets + * @param size Size to convert to string + * @return String Result string representation + */ + public static function getSizeString64(size: #if cpp Float64 #else Float #end):String { + var rSize: #if cpp Float64 #else Float #end = size; + var label:Int = 0; + var len = sizeLabels.length; + while(rSize >= 1024 && label < len-1) { + label++; + rSize /= 1024; + } + return Std.int(rSize) + ((label <= 1) ? "" : "." + addZeros(Std.string(Std.int((rSize % 1) * 100)), 2)) + sizeLabels[label]; + } + + /** * Replaces in a string any kind of IP with `[Your IP]` making the string safer to trace. * @param msg String to check and edit @@ -339,7 +367,7 @@ class CoolUtil * @return FPS-Modified Ratio */ @:noUsing public static inline function getFPSRatio(ratio:Float):Float { - return FlxMath.bound(ratio * 60 * FlxG.elapsed, 0, 1); + return CoolUtil.bound(ratio * 60 * FlxG.elapsed, 0.0, 1.0); } /** * Tries to get a color from a `Dynamic` variable. @@ -480,6 +508,23 @@ class CoolUtil * @return Array<Int> Final array */ @:noUsing public static inline function numberArray(max:Int, ?min:Int = 0):Array<Int> + { + /*return [for (i in min...max) i];*/ + // Reason for change: + // Old one uses push instead of creating an array with the wanted size. + var len:Int = CoolUtil.maxInt(max - min, 0); + #if cpp + var arr:Array<Int> = untyped __cpp__("::Array_obj< int >::__new({0})", len); + #else + var arr:Array<Int> = []; + arr.resize(len); + #end + for(i in 0...len) + arr[i] = i + min; + return arr; + } + + @:noUsing public static inline function numberArrayOld(max:Int, ?min:Int = 0):Array<Int> { return [for (i in min...max) i]; } @@ -512,6 +557,21 @@ class CoolUtil sprite.scale.set(nScale, nScale); } + public static function setGraphicSizeFloat(sprite:FlxSprite, Width:Float = 0, Height:Float = 0):Void + { + if (Width <= 0 && Height <= 0) + return; + + var newScaleX:Float = Width / sprite.frameWidth; + var newScaleY:Float = Height / sprite.frameHeight; + sprite.scale.set(newScaleX, newScaleY); + + if (Width <= 0) + sprite.scale.x = newScaleY; + else if (Height <= 0) + sprite.scale.y = newScaleX; + } + /** * Returns a simple string representation of a FlxKey. Used in Controls options. * @param key Key @@ -569,7 +629,6 @@ class CoolUtil case Y: obj.y = (cam.height - obj.height) / 2; case NONE: - } } @@ -591,8 +650,16 @@ class CoolUtil * @param name Name of the attribute */ public static inline function getAtt(xml:Access, name:String) { - if (!xml.has.resolve(name)) return null; - return xml.att.resolve(name); + /*if (!xml.has.resolve(name)) return null; + return xml.att.resolve(name);*/ + // Reason for change: + // Old one has error checking 4 times, this one has it once. + var xml:Xml = xml.x; + if (xml.nodeType != Element) { + throw 'Bad node type, expected Element but found ${xml.nodeType}'; + } + @:privateAccess + return xml.attributeMap.exists(name) ? xml.attributeMap.get(name) : null; } /** @@ -691,7 +758,7 @@ class CoolUtil * Gets the macro class created by hscript-improved for an abstract / enum */ @:noUsing public static inline function getMacroAbstractClass(className:String) { - return Type.resolveClass('${className}_HSC'); + return Type.resolveClass(className + '_HSC'); } /** @@ -708,6 +775,8 @@ class CoolUtil i--; } return i;*/ + // Reason for change: + // Old one was made because at the time we didnt know the lastIndexOf function. return array.lastIndexOf(element); } @@ -748,6 +817,24 @@ class CoolUtil #end } + /** + * Browse a path in the operating system's explorer + * @param path + */ + public static inline function browsePath(path:String) { + var formattedPath:String = Path.normalize(path); + + #if windows + formattedPath = formattedPath.replace("/", "\\"); + Sys.command("explorer", [formattedPath]); + #elseif mac + Sys.command("open", [formattedPath]); + #elseif linux + var cmd = Sys.command("xdg-open", [formattedPath]); + if (cmd != 0) cmd = Sys.command("/usr/bin/xdg-open", [formattedPath]); + #end + } + /** * Converts a timestamp to a readable format such as `01:22` (`mm:ss`) */ @@ -773,6 +860,15 @@ class CoolUtil @:noUsing public static inline function maxInt(p1:Int, p2:Int) return p1 < p2 ? p2 : p1; + /** + * Equivalent of `Math.min`, except doesn't require a Int -> Float -> Int conversion. + * @param p1 + * @param p2 + * @return return p1 > p2 ? p2 : p1 + */ + @:noUsing public static inline function minInt(p1:Int, p2:Int) + return p1 > p2 ? p2 : p1; + /** * Equivalent of `Math.floor`, except doesn't require a Int -> Float -> Int conversion. * @param e Value to get the floor of. @@ -818,6 +914,65 @@ class CoolUtil @:noUsing public static inline function flxeaseFromString(mainEase:String, ?suffix:String) return Reflect.field(FlxEase, mainEase + (mainEase == "linear" || suffix == null ? "" : suffix)); + /* + * Returns the filename of a path, without the extension. + * @param path Path to get the filename from + * @return Filename + */ + @:noUsing public static inline function getFilename(file:String) { + var file = new haxe.io.Path(file); + return file.file; + } + + @:noUsing public static function getClosestAngle(angle:Float, targetAngle:Float):Float { + var diff:Float = angle - targetAngle; + if (diff < -180) diff += 360; + else if (diff > 180) diff -= 360; + return angle - diff; + } + + /** + * Returns the screen position of an object, while taking the camera zoom into account. + * + * @param object Any `FlxObject` + * @param camera The desired "screen" coordinate space. If `null`, `FlxG.camera` is used. + * @param result Optional arg for the returning point + * @return The screen position of the object. + */ + public static function worldToScreenPosition(object:FlxObject, ?camera:FlxCamera, ?result:FlxPoint) { + if (result == null) + result = FlxPoint.get(); + if (camera == null) + camera = FlxG.camera; + + result.set(object.x, object.y); + result.x = (((result.x - camera.scroll.x * object.scrollFactor.x) * camera.zoom) - ((camera.width * 0.5) * (camera.zoom - camera.initialZoom))); + result.y = (((result.y - camera.scroll.y * object.scrollFactor.y) * camera.zoom) - ((camera.height * 0.5) * (camera.zoom - camera.initialZoom))); + return result; + } + + /** + * Returns the screen position of an point, while taking the camera zoom into account. + * + * @param object Any `FlxObject` + * @param camera The desired "screen" coordinate space. If `null`, `FlxG.camera` is used. + * @param result Optional arg for the returning point + * @return The screen position of the object. + */ + public static function pointToScreenPosition(object:FlxPoint, ?camera:FlxCamera, ?result:FlxPoint) { + if (result == null) + result = FlxPoint.get(); + if (camera == null) + camera = FlxG.camera; + + result.set(object.x, object.y); + result.x = (((result.x - camera.scroll.x) * camera.zoom) - ((camera.width * 0.5) * (camera.zoom - camera.initialZoom))); + result.y = (((result.y - camera.scroll.y) * camera.zoom) - ((camera.height * 0.5) * (camera.zoom - camera.initialZoom))); + + object.putWeak(); + return result; + } + /** * Sorts an array alphabetically. * @param array Array to sort @@ -862,14 +1017,34 @@ class CoolUtil } #end - /* - * Returns the filename of a path, without the extension. - * @param path Path to get the filename from - * @return Filename - */ - @:noUsing public static inline function getFilename(file:String) { - var file = new haxe.io.Path(file); - return file.file; + public static inline function bound(Value:Float, Min:Float, Max:Float):Float { + #if cpp + var _hx_tmp1:Float = Value; + var _hx_tmp2:Float = Min; + var _hx_tmp3:Float = Max; + return untyped __cpp__("((({0}) < ({1})) ? ({1}) : (({0}) > ({2})) ? ({2}) : ({0}))", _hx_tmp1, _hx_tmp2, _hx_tmp3); + #else + return (Value < Min) ? Min : (Value > Max) ? Max : Value; + #end + } + + public static inline function boundInt(Value:Int, Min:Int, Max:Int):Int { + #if cpp + var _hx_tmp1:Int = Value; + var _hx_tmp2:Int = Min; + var _hx_tmp3:Int = Max; + return untyped __cpp__("((({0}) < ({1})) ? ({1}) : (({0}) > ({2})) ? ({2}) : ({0}))", _hx_tmp1, _hx_tmp2, _hx_tmp3); + #else + return (Value < Min) ? Min : (Value > Max) ? Max : Value; + #end + } + + public static inline function boolToInt(b:Bool):Int { + #if cpp + return untyped __cpp__("(({0}) ? 1 : 0)", b); + #else + return b ? 1 : 0; + #end } /** @@ -977,6 +1152,183 @@ class CoolUtil return result; } + /** + * Gets the luminance of the given color + * @param color Color to use + * @return Number between 0 and 1 + */ + public static function getLuminance(color:FlxColor):Float { + return (0.2126*color.redFloat + 0.7152*color.greenFloat + 0.0722*color.blueFloat); + } + + /** + * ! REQUIRES FULL PATH!!! + * @param path + * @return Bool + */ + public static function imageHasFrameData(path:String):String { + if (FileSystem.exists(Path.withExtension(path, "xml"))) return "xml"; + if (FileSystem.exists(Path.withExtension(path, "txt"))) return "txt"; + if (FileSystem.exists(Path.withExtension(path, "json"))) return "json"; + + return null; + } + + // loads frames with no image, but with data, so you can parse it + public static function loadFramesFromData(data:String, ext:String = null):FlxFramesCollection { + var frames:FlxFramesCollection = null; + var tempBitmap:BitmapData = new BitmapData(1, 1, false); + + // Worlds hackiest work around alert??? + var graphic:FlxGraphic = FlxG.bitmap.add(tempBitmap); + @:privateAccess { + // prevent errors from being shown, such as Size is too small + graphic.width = 9999999; + graphic.height = 9999999; + } + + try { + switch (ext) { + case "xml": frames = FlxAtlasFrames.fromSparrow(graphic, Xml.parse(data)); + case "txt": frames = FlxAtlasFrames.fromSpriteSheetPacker(graphic, data); + case "json": frames = FlxAtlasFrames.fromAseprite(graphic, data); + } + } catch (e) {trace(e);} + + return frames; + } + + public static function removeBOM(str:String):String { + return StringTools.replace(str, String.fromCharCode(0xFEFF), ""); + } + + public static function getAnimsListFromFrames(frames:FlxFramesCollection, ext:String = null):Array<String> { + if (frames == null) return []; + + var animsList:Array<String> = []; + for (frame in frames.frames) { + var animName:String = ext == "txt" ? frame.name.split("_")[0] : frame.name.substr(0, frame.name.length-4); + if (!animsList.contains(animName)) + animsList.push(animName); + } + + return animsList; + } + + public static function getAnimsListFromAtlas(atlas:AnimAtlas):Array<String> { + if (atlas == null) return []; + + var animsList:Array<String> = []; + if (atlas.AN.SN != null) animsList.push(atlas.AN.SN); + if (atlas.SD != null) + for (symbol in atlas.SD.S) + if (symbol.SN != null) animsList.push(symbol.SN); + + return animsList; + } + + public static function getAnimsListFromSprite(spr:FunkinSprite):Array<String> { + if (spr.animateAtlas != null) { + return [for (symbol => timeline in spr.animateAtlas.anim.symbolDictionary) symbol]; + } else + return getAnimsListFromFrames(spr.frames); + } + + // TODO: check this for bugs + // Code from https://github.com/elnabo/equals/blob/master/src/equals/Equal.hx, (MIT License), but updated to work with haxe 4 + public static function deepEqual<T> (a:T, b:T) : Bool { + if (a == b) { return true; } // if physical equality + if (isNull(a) || isNull(b)) { + return false; + } + + switch (Type.typeof(a)) { + case TNull, TInt, TBool, TUnknown: + return a == b; + case TFloat: + return Math.isNaN(cast a) && Math.isNaN(cast b); // only valid true result remaining + case TFunction: + return Reflect.compareMethods(a, b); // only physical equality can be tested for function + case TEnum(_): + if (EnumValueTools.getIndex(cast a) != EnumValueTools.getIndex(cast b)) { + return false; + } + var a_args = EnumValueTools.getParameters(cast a); + var b_args = EnumValueTools.getParameters(cast b); + return deepEqual(a_args, b_args); + case TClass(_): + if ((a is String) && (b is String)) { + return a == b; + } + if ((a is Array) && (b is Array)) { + var a = cast(a, Array<Dynamic>); + var b = cast(b, Array<Dynamic>); + if (a.length != b.length) { return false; } + for (i in 0...a.length) { + if (!deepEqual(a[i], b[i])) { + return false; + } + } + return true; + } + + if ((a is IMap) && (b is IMap)) { + var a = cast(a, IMap<Dynamic, Dynamic>); + var b = cast(b, IMap<Dynamic, Dynamic>); + var a_keys = [ for (key in a.keys()) key ]; + var b_keys = [ for (key in b.keys()) key ]; + a_keys.sort(Reflect.compare); + b_keys.sort(Reflect.compare); + if (!deepEqual(a_keys, b_keys)) { return false; } + for (key in a_keys) { + if (!deepEqual(a.get(key), b.get(key))) { + return false; + } + } + return true; + } + + if ((a is Date) && (b is Date)) { + return cast(a, Date).getTime() == cast(b, Date).getTime(); + } + + if ((a is haxe.io.Bytes) && (b is haxe.io.Bytes)) { + return deepEqual(cast(a, haxe.io.Bytes).getData(), cast(b, haxe.io.Bytes).getData()); + } + + case TObject: + } + + for (field in Reflect.fields(a)) { + var pa = Reflect.field(a, field); + var pb = Reflect.field(b, field); + if (isFunction(pa)) { + // ignore function as only physical equality can be tested, unless null + if (isNull(pa) != isNull(pb)) { + return false; + } + continue; + } + if (!deepEqual(pa, pb)) { + return false; + } + } + + return true; + } + + static inline function isNull(a:Dynamic):Bool { + return Type.enumEq(Type.typeof(a), TNull); + } + + static inline function isFunction(a:Dynamic):Bool { + return Type.enumEq(Type.typeof(a), TFunction); + } + + public static inline function isMapEmpty<K, V>(map: Map<K, V>): Bool { + return !map.keys().hasNext(); + } + public inline static function parsePropertyString(fieldPath:String):Array<OneOfTwo<String, Int>> { return FlxTween.parseFieldString(fieldPath); } diff --git a/source/funkin/backend/utils/DiscordUtil.hx b/source/funkin/backend/utils/DiscordUtil.hx index 68bdf128..8a7b7658 100644 --- a/source/funkin/backend/utils/DiscordUtil.hx +++ b/source/funkin/backend/utils/DiscordUtil.hx @@ -17,7 +17,7 @@ import lime.app.Application; import sys.thread.Thread; #end -class DiscordUtil +final class DiscordUtil { public static var currentID(default, set):String = null; public static var discordThread:#if DISCORD_RPC Thread #else Dynamic #end = null; diff --git a/source/funkin/backend/utils/DrawUtil.hx b/source/funkin/backend/utils/DrawUtil.hx new file mode 100644 index 00000000..c21830c6 --- /dev/null +++ b/source/funkin/backend/utils/DrawUtil.hx @@ -0,0 +1,105 @@ +package funkin.backend.utils; + +import flixel.math.FlxRect; +import flixel.util.FlxColor; +import flixel.math.FlxAngle; +import flixel.math.FlxPoint; + +final class DrawUtil { + public static var line:FlxSprite = null; + public static var dot:FlxSprite = null; + public static var square:FlxSprite = null; + + public static inline function drawSquare(x:Float, y:Float, ?scale:Float = 1) { + if (square == null) createDrawers(); + + square.setPosition(x, y); + + square.scale.set(scale, scale); + square.updateHitbox(); + + square.x -= square.width / 2; + square.y -= square.height / 2; + square.draw(); + } + + public static inline function drawDot(x:Float, y:Float, ?scale:Float = 1) { + if (dot == null) createDrawers(); + + dot.setPosition(x, y); + + dot.scale.set(scale, scale); + dot.updateHitbox(); + + dot.x -= dot.width / 2; + dot.y -= dot.height / 2; + dot.draw(); + } + + public static inline function drawLine(point1:FlxPoint, point2:FlxPoint, thickness:Float = 1, ?color:Null<FlxColor>) { + if (line == null) createDrawers(); + + var dx:Float = point2.x - point1.x; + var dy:Float = point2.y - point1.y; + + var angle:Float = Math.atan2(dy, dx); + var distance:Float = Math.sqrt(dx * dx + dy * dy); + + // Math.ceil to prevent flickering + line.setPosition(point1.x, point1.y); + line.angle = angle * FlxAngle.TO_DEG; + line.origin.set(0, line.frameHeight / 2); + line.scale.x = distance / line.frameWidth; + line.scale.y = thickness; + line.y -= line.height / 2; + if (color != null) line.color = color; + line.draw(); + + line.angle = 0; + line.scale.x = line.scale.y = 1; + line.updateHitbox(); + + point1.putWeak(); point2.putWeak(); + } + + public static inline function drawRect(rect:FlxRect, thickness:Float = 1, ?color:Null<FlxColor>) { + if (rect.width <= 0 || rect.height <= 0) return; + DrawUtil.drawLine(FlxPoint.weak(rect.x, rect.y), FlxPoint.weak(rect.x + rect.width, rect.y), thickness, color); + DrawUtil.drawLine(FlxPoint.weak(rect.x, rect.y), FlxPoint.weak(rect.x, rect.y + rect.height), thickness, color); + DrawUtil.drawLine(FlxPoint.weak(rect.x + rect.width, rect.y), FlxPoint.weak(rect.x + rect.width, rect.y + rect.height), thickness, color); + DrawUtil.drawLine(FlxPoint.weak(rect.x, rect.y + rect.height), FlxPoint.weak(rect.x + rect.width, rect.y + rect.height), thickness, color); + } + + public static inline function createDrawers() { + dot = new FlxSprite().loadGraphic(Paths.image("editors/stage/selectionDot"), true, 32, 32); + dot.antialiasing = true; + dot.animation.add("default", [0], 0, false); + dot.animation.add("hollow", [1], 0, false); + dot.animation.play("default"); + dot.camera = FlxG.camera; + dot.forceIsOnScreen = true; + + line = new FlxSprite().makeGraphic(1, 1, FlxColor.WHITE); + line.camera = FlxG.camera; + line.forceIsOnScreen = true; + + square = new FlxSprite().makeGraphic(32, 32, FlxColor.WHITE); + square.camera = FlxG.camera; + square.forceIsOnScreen = true; + } + + public static function destroyDrawers() { + if(dot != null) { + dot.destroy(); + dot = null; + } + if(line != null) { + line.destroy(); + line = null; + } + if(square != null) { + square.destroy(); + square = null; + } + } +} \ No newline at end of file diff --git a/source/funkin/backend/utils/EngineUtil.hx b/source/funkin/backend/utils/EngineUtil.hx index 9f0e9157..ebfe916c 100644 --- a/source/funkin/backend/utils/EngineUtil.hx +++ b/source/funkin/backend/utils/EngineUtil.hx @@ -3,7 +3,7 @@ package funkin.backend.utils; import funkin.backend.scripting.MultiThreadedScript; import funkin.backend.scripting.Script; -class EngineUtil { +final class EngineUtil { /** * Starts a new multithreaded script. * This script will share all the variables with the current one, which means already existing callbacks will be replaced by new ones on conflict. diff --git a/source/funkin/backend/utils/HttpUtil.hx b/source/funkin/backend/utils/HttpUtil.hx index d5e9db92..cfd34005 100644 --- a/source/funkin/backend/utils/HttpUtil.hx +++ b/source/funkin/backend/utils/HttpUtil.hx @@ -2,7 +2,7 @@ package funkin.backend.utils; import haxe.Http; -class HttpUtil +final class HttpUtil { public static var userAgent:String = Flags.USER_AGENT; public static function requestText(url:String) diff --git a/source/funkin/backend/utils/MarkdownUtil.hx b/source/funkin/backend/utils/MarkdownUtil.hx index c3692f38..e481198d 100644 --- a/source/funkin/backend/utils/MarkdownUtil.hx +++ b/source/funkin/backend/utils/MarkdownUtil.hx @@ -3,7 +3,7 @@ package funkin.backend.utils; import flixel.text.FlxText; import flixel.util.FlxColor; -class MarkdownUtil { +final class MarkdownUtil { public static function applyMarkdownText(text:FlxText, str:String) { text.textField.htmlText = Markdown.markdownToHtml(prepareMarkdown(str)); var changes:Array<FlxTextFormatMarkerPair> = [ diff --git a/source/funkin/backend/utils/MathUtil.hx b/source/funkin/backend/utils/MathUtil.hx new file mode 100644 index 00000000..2ca2f5a2 --- /dev/null +++ b/source/funkin/backend/utils/MathUtil.hx @@ -0,0 +1,202 @@ +package funkin.backend.utils; + +import haxe.macro.Expr; + +final class MathUtil { + /** + * Returns the maximum value in the arguments. + * @param args Array of values + * + * @return The maximum value + **/ + public static function maxInt(...args:Int):Int { + var max = args[0]; + for(i in 1...args.length) { + var arg = args[i]; + if(arg > max) + max = arg; + } + return max; + } + + /** + * Returns the minimum value in the arguments. + * @param args Array of values + * + * @return The minimum value + **/ + public static function minInt(...args:Int):Int { + var min = args[0]; + for(i in 1...args.length) { + var arg = args[i]; + if(arg < min) + min = arg; + } + return min; + } + + /** + * Returns the maximum value in the arguments. + * + * NOTE: If you are using this in compile time, you should use `MathUtil.maxSmart` instead of this for better performance. + * + * @param args Array of values + * + * @return The maximum value + **/ + public static function max(...args:Float):Float { + var max = args[0]; + for(i in 1...args.length) { + var arg = args[i]; + if(arg > max) + max = arg; + } + return max; + } + + /** + * Returns the minimum value in the arguments. + * + * NOTE: If you are using this in compile time, you should use `MathUtil.minSmart` instead of this for better performance. + * + * @param args Array of values + * + * @return The minimum value + **/ + public static function min(...args:Float):Float { + var min = args[0]; + for(i in 1...args.length) { + var arg = args[i]; + if(arg < min) + min = arg; + } + return min; + } + + /** + * Checks if a is less than b with considering a margin of error. + * + * @param a Float + * @param b Float + * @param margin Float (Default: EPSILON) + * + * @return Bool + **/ + public static function lessThan(a:Float, b:Float, margin:Float = 0.0000001):Bool { + return a < b - margin; + } + + /** + * Checks if a is less than or equally b with considering a margin of error. + * + * @param a Float + * @param b Float + * @param margin Float (Default: EPSILON) + * + * @return Bool + **/ + public static function lessThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool { + return a <= b - margin; + } + + /** + * Checks if a is greater than b with considering a margin of error. + * + * @param a Float + * @param b Float + * @param margin Float (Default: EPSILON) + * + * @return Bool + **/ + public static function greaterThan(a:Float, b:Float, margin:Float = 0.0000001):Bool { + return a > b + margin; + } + + /** + * Checks if a is greater than or equally b with considering a margin of error. + * + * @param a Float + * @param b Float + * @param margin Float (Default: EPSILON) + * + * @return Bool + **/ + public static function greaterThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool { + return a >= b + margin; + } + + /** + * Checks if a is approximately equal to b. + * + * @param a Float + * @param b Float + * @param margin Float (Default: EPSILON) + * + * @return Bool + **/ + public static function equal(a:Float, b:Float, margin:Float = 0.0000001):Bool { + return Math.abs(a - b) <= margin; + } + + /** + * Checks if a are not approximately equal to b. + * + * @param a Float + * @param b Float + * @param margin Float (Default: EPSILON) + * + * @return Bool + **/ + public static function notEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool { + return Math.abs(a - b) > margin; + } + + /** + * Shortcut to `Math.max` but with infinite amount of arguments + * + * Might not preserve the order of arguments, please test this. + * + * Dont use this in hscript, it doesnt work, it only works on compile time + **/ + @:dox(hide) public static macro function maxSmart(..._args:Expr):Expr { + return genericMinMaxSmart(_args.toArray(), "Math.max"); + } + + /** + * Shortcut to `Math.min` but with infinite amount of arguments + * + * Might not preserve the order of arguments, please test this. + * + * Dont use this in hscript, it doesnt work, it only works on compile time + **/ + @:dox(hide) public static macro function minSmart(..._args:Expr):Expr { + return genericMinMaxSmart(_args.toArray(), "Math.min"); + } + + #if macro + @:dox(hide) private static function genericMinMaxSmart(_args:Array<Expr>, funcPath:String):Expr { + var args = _args.copy(); + if (args.length == 0) return macro 0; + + var func = funcPath.split("."); + + function nested(lst:Array<Expr>):Expr { + if (lst.length == 1) { + return macro ${lst[0]}; + } else if (lst.length == 2) { + return macro $p{func}(${lst[0]}, ${lst[1]}); + } else { + var mid = Std.int(lst.length / 2); + return macro $p{func}(${nested(lst.slice(0, mid))}, ${nested(lst.slice(mid, lst.length))}); + } + } + + var expr = nested(args); + + //var printer = new haxe.macro.Printer(); + //trace(printer.printExpr(expr)); + + return macro $expr; + } + #end +} \ No newline at end of file diff --git a/source/funkin/backend/utils/MatrixUtil.hx b/source/funkin/backend/utils/MatrixUtil.hx new file mode 100644 index 00000000..851d5dbb --- /dev/null +++ b/source/funkin/backend/utils/MatrixUtil.hx @@ -0,0 +1,223 @@ +package funkin.backend.utils; + +import flixel.graphics.frames.FlxFrame; +import flixel.FlxCamera; +import flixel.math.FlxPoint; +import flixel.util.typeLimit.OneOfTwo; +import funkin.backend.system.FakeCamera; +import funkin.backend.system.FakeCamera.FakeCallCamera; +import flixel.graphics.FlxGraphic; +import flixel.graphics.frames.FlxFrame; +import flixel.graphics.tile.FlxDrawTrianglesItem; +import flixel.math.FlxMatrix; +import flixel.math.FlxPoint; +import flixel.system.FlxAssets.FlxShader; +import openfl.display.BitmapData; +import openfl.display.BlendMode; +import openfl.geom.ColorTransform; +import openfl.geom.Point; +import openfl.geom.Rectangle; + +interface IPrePostDraw { + public function preDraw():Void; + public function postDraw():Void; +} + +@:access(flixel.FlxCamera) +@:access(flixel.FlxSprite) +@:access(flixel.math.FlxMatrix) +@:access(openfl.geom.Matrix) +final class MatrixUtil { + public static function getMatrixPosition(sprite:FlxSprite, points:OneOfTwo<FlxPoint, Array<FlxPoint>>, ?camera:FlxCamera, _width:Float = 1, _height:Float = 1):Array<FlxPoint> + { + //if(_width == -1) _width = sprite.width; + //if(_height == -1) _height = sprite.height; + if(camera == null) camera = sprite.camera; + if(points is FlxBasePoint) points = [points]; + var isFunkinSprite = sprite is FunkinSprite; + var funkinSprite:FunkinSprite = null; + if(isFunkinSprite) funkinSprite = cast sprite; + var isAnimateAtlas = isFunkinSprite && funkinSprite.animateAtlas != null; + + var nc:FakeCamera = isAnimateAtlas ? FakeCallCamera.instance : FakeCamera.instance; + nc.zoom = camera.zoom; + nc.scroll.set(camera.scroll.x, camera.scroll.y); + nc.pixelPerfectRender = camera.pixelPerfectRender; + + var points:Array<FlxPoint> = cast points; + + if(isAnimateAtlas) { + var cnc = FakeCallCamera.instance; + var sprite = funkinSprite; + var oldOnDraw = cnc.onDraw; + + // TODO: fix rotation + // TODO: fix skew + + var boundsTopLeft = new FlxPoint(Math.POSITIVE_INFINITY, Math.POSITIVE_INFINITY); + var boundsTopRight = new FlxPoint(Math.NEGATIVE_INFINITY, Math.POSITIVE_INFINITY); + var boundsBottomLeft = new FlxPoint(Math.POSITIVE_INFINITY, Math.NEGATIVE_INFINITY); + var boundsBottomRight = new FlxPoint(Math.NEGATIVE_INFINITY, Math.NEGATIVE_INFINITY); + + cnc.onDraw = function(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false, ?shader:FlxShader) { + // cnc is already perfect rn + // so we need to manually process the limb + var points = [ + // corners + FlxPoint.get(0, 0), + FlxPoint.get(1, 0), + FlxPoint.get(0, 1), + FlxPoint.get(1, 1) + ]; + + rawTransformPoints(points, matrix, frame.sourceSize.x, frame.sourceSize.y); + + // sort the points, incase like they were transformed with a scale -1 matrix + + var topLeft:FlxPoint = points[0]; + var topRight:FlxPoint = points[1]; + var bottomLeft:FlxPoint = points[2]; + var bottomRight:FlxPoint = points[3]; + + if (points[0].y > points[1].y) { + topLeft = points[1]; + topRight = points[0]; + } + if (points[2].y > points[3].y) { + bottomLeft = points[3]; + bottomRight = points[2]; + } + + if (topLeft.x > topRight.x) { + var temp = topLeft; + topLeft = topRight; + topRight = temp; + } + + if (bottomLeft.x > bottomRight.x) { + var temp = bottomLeft; + bottomLeft = bottomRight; + bottomRight = temp; + } + + // points should now contain the corners of the limb + + // trace("Corners of limb: " + points); + + // set the max bounds of each corner of the limb + if(topLeft.x < boundsTopLeft.x) boundsTopLeft.x = topLeft.x; + if(topLeft.y < boundsTopLeft.y) boundsTopLeft.y = topLeft.y; + if(topRight.x > boundsTopRight.x) boundsTopRight.x = topRight.x; + if(topRight.y < boundsTopRight.y) boundsTopRight.y = topRight.y; + if(bottomLeft.x < boundsBottomLeft.x) boundsBottomLeft.x = bottomLeft.x; + if(bottomLeft.y > boundsBottomLeft.y) boundsBottomLeft.y = bottomLeft.y; + if(bottomRight.x > boundsBottomRight.x) boundsBottomRight.x = bottomRight.x; + if(bottomRight.y > boundsBottomRight.y) boundsBottomRight.y = bottomRight.y; + + // recycle the points + for(point in points) point.put(); + } + var cameras = sprite._cameras; + var oldVisible = cnc.visible; + cnc.visible = true; + sprite._cameras = [cnc]; + sprite.draw(); + sprite._cameras = cameras; + cnc.visible = oldVisible; + cnc.onDraw = oldOnDraw; + + // fake transform + + //trace(""); + //trace("topLeft: " + boundsTopLeft); + //trace("topRight: " + boundsTopRight); + //trace("bottomLeft: " + boundsBottomLeft); + //trace("bottomRight: " + boundsBottomRight); + //trace("Before transform"); + //trace(points); + + for(point in points) { + //var x = matrix.__transformX(point.x * _width, point.y * _height); + //var y = matrix.__transformY(point.x * _width, point.y * _height); + // apply using boundsTopLeft, boundsTopRight, boundsBottomLeft, boundsBottomRight + // matrix doesnt work for this + var x = FlxMath.lerp( + FlxMath.lerp(boundsTopLeft.x, boundsTopRight.x, point.x), + FlxMath.lerp(boundsBottomLeft.x, boundsBottomRight.x, point.x), + point.y + ); + var y = FlxMath.lerp( + FlxMath.lerp(boundsTopLeft.y, boundsTopRight.y, point.x), + FlxMath.lerp(boundsBottomLeft.y, boundsBottomRight.y, point.x), + point.y + ); + + //trace("(" + point.x + ", " + point.y + ") -> (" + x + ", " + y + ")"); + + // reset to ingame coords + x += camera.scroll.x; + y += camera.scroll.y; + + if(isFunkinSprite) { + var ratio = 1 - FlxMath.lerp(1 / camera.zoom, 1, funkinSprite.zoomFactor); + x += camera.width / 2 * ratio; + y += camera.height / 2 * ratio; + } + point.set(x, y); + } + + //trace("After transform"); + //trace(points); + } else { + if(sprite is IPrePostDraw) { + var postDraw = cast(sprite, IPrePostDraw); + postDraw.preDraw(); + sprite.drawComplex(nc); + postDraw.postDraw(); + } else { + sprite.drawComplex(nc); + } + transformPoints(sprite, points, sprite._matrix, camera, _width, _height); + } + + return points; + } + + /** + * Warning: modifies the points in the array + **/ + public static function transformPoints(sprite:FlxSprite, points:Array<FlxPoint>, matrix:FlxMatrix, ?camera:FlxCamera, _width:Float = 1, _height:Float = 1, doCameraTransform:Bool = true):Array<FlxPoint> { + var isFunkinSprite = sprite is FunkinSprite; + var funkinSprite:FunkinSprite = null; + if(isFunkinSprite) funkinSprite = cast sprite; + + for(point in points) { + var x = matrix.__transformX(point.x * _width, point.y * _height); + var y = matrix.__transformY(point.x * _width, point.y * _height); + + if(doCameraTransform) { + // reset to ingame coords + x += camera.scroll.x; + y += camera.scroll.y; + + if(isFunkinSprite) { + var ratio = 1 - FlxMath.lerp(1 / camera.zoom, 1, funkinSprite.zoomFactor); + x += camera.width / 2 * ratio; + y += camera.height / 2 * ratio; + } + } + point.set(x, y); + } + return points; + } + + private static function rawTransformPoints(points:Array<FlxPoint>, matrix:FlxMatrix, _width:Float = 1, _height:Float = 1):Array<FlxPoint> { + for(point in points) { + var x = matrix.__transformX(point.x * _width, point.y * _height); + var y = matrix.__transformY(point.x * _width, point.y * _height); + + point.set(x, y); + } + return points; + } +} \ No newline at end of file diff --git a/source/funkin/backend/utils/MemoryUtil.hx b/source/funkin/backend/utils/MemoryUtil.hx index 9e45aa9f..2de56184 100644 --- a/source/funkin/backend/utils/MemoryUtil.hx +++ b/source/funkin/backend/utils/MemoryUtil.hx @@ -20,7 +20,7 @@ using StringTools; * * DISCLAIMER: Hardware info is only available on Native platforms. **/ -class MemoryUtil { +final class MemoryUtil { public static var disableCount:Int = 0; public static function askDisable() { @@ -88,14 +88,18 @@ class MemoryUtil { */ public static function getTotalMem():Float { - #if windows - return funkin.backend.utils.native.Windows.getTotalRam(); - #elseif mac - return funkin.backend.utils.native.Mac.getTotalRam(); - #elseif linux - return funkin.backend.utils.native.Linux.getTotalRam(); + #if cpp + #if windows + return funkin.backend.utils.native.Windows.getTotalRam(); + #elseif mac + return funkin.backend.utils.native.Mac.getTotalRam(); + #elseif linux + return funkin.backend.utils.native.Linux.getTotalRam(); + #else + return 0; + #end #else - return 0; + return 0; #end } diff --git a/source/funkin/backend/utils/NativeAPI.hx b/source/funkin/backend/utils/NativeAPI.hx index 143497df..d3b5e758 100644 --- a/source/funkin/backend/utils/NativeAPI.hx +++ b/source/funkin/backend/utils/NativeAPI.hx @@ -1,5 +1,8 @@ package funkin.backend.utils; +import haxe.macro.Expr.Case; +import openfl.ui.Mouse; +import openfl.ui.MouseCursor; import funkin.backend.utils.native.*; import flixel.util.typeLimit.OneOfTwo; import flixel.util.typeLimit.OneOfThree; @@ -200,6 +203,17 @@ class NativeAPI { #end } + /** + * Set cursor icon. + **/ + public static function setCursorIcon(icon:CodeCursor) { + #if (mac && cpp) + Mac.setMouseCursorIcon(cast icon); + #else + Mouse.cursor = icon.toOpenFL(); + #end + } + public static function consoleColorToANSI(color:ConsoleColor) { return switch(color) { case BLACK: 30; @@ -289,4 +303,102 @@ enum abstract MessageBoxIcon(Int) { var MSG_QUESTION = 0x00000020; var MSG_WARNING = 0x00000030; var MSG_INFORMATION = 0x00000040; +} + +enum abstract CodeCursor(String) { + var CUSTOM;// = "arrow"; + var ARROW;// = "arrow"; + var CLICK;// = "click"; + var CROSSHAIR;// = "crosshair"; + var HAND;// = "hand"; + var IBEAM;// = "ibeam"; + var MOVE;// = "move"; + + var RESIZE_H;// = "resize_we"; + var RESIZE_V;// = "resize_ns"; + var RESIZE_TL;// = "resize_nw"; + var RESIZE_TR;// = "resize_ne"; + var RESIZE_BL;// = "resize_sw"; + var RESIZE_BR;// = "resize_se"; + var RESIZE_T;// = "resize_n"; + var RESIZE_B;// = "resize_b"; + var RESIZE_L;// = "resize_w"; + var RESIZE_R;// = "resize_e"; + + var RESIZE_TLBR;// = "resize_nw_se"; + var RESIZE_TRBL;// = "resize_ne_sw"; + + var WAIT;// = "wait"; + var WAIT_ARROW;// = "waitarrow"; + var DISABLED;// = "disabled"; + var DRAG;// = "drag"; + var DRAG_OPEN;// = "dragopen"; + + @:to public function toOpenFL():MouseCursor { + return @:privateAccess switch(cast this) { + case ARROW: MouseCursor.ARROW; + case CROSSHAIR: MouseCursor.__CROSSHAIR; + case CLICK: MouseCursor.BUTTON; + case IBEAM: MouseCursor.IBEAM; + case MOVE: MouseCursor.__MOVE; + case HAND: MouseCursor.HAND; + case DRAG: MouseCursor.HAND; + case DRAG_OPEN: MouseCursor.ARROW; // Could be HAND, but it might be better to use ARROW since on windows it would be weird to have the dragging cursor + case WAIT: MouseCursor.__WAIT; + case WAIT_ARROW: MouseCursor.__WAIT_ARROW; + + case DISABLED: MouseCursor.ARROW; + + case RESIZE_TR: MouseCursor.__RESIZE_NESW; + case RESIZE_BL: MouseCursor.__RESIZE_NESW; + case RESIZE_TL: MouseCursor.__RESIZE_NWSE; + case RESIZE_BR: MouseCursor.__RESIZE_NWSE; + case RESIZE_H: MouseCursor.__RESIZE_WE; + case RESIZE_V: MouseCursor.__RESIZE_NS; + + case RESIZE_T: MouseCursor.__RESIZE_NS; + case RESIZE_B: MouseCursor.__RESIZE_NS; + case RESIZE_L: MouseCursor.__RESIZE_WE; + case RESIZE_R: MouseCursor.__RESIZE_WE; + + case RESIZE_TLBR: MouseCursor.__RESIZE_NWSE; + case RESIZE_TRBL: MouseCursor.__RESIZE_NESW; + case CUSTOM: MouseCursor.__CUSTOM; + //default: ARROW; + } + } + + @:to public function toInt():Int { + return switch(cast this) { + case ARROW: 0; + case CROSSHAIR: 1; + case CLICK: 2; + case IBEAM: 3; + case MOVE: 4; + case HAND: 5; + case DRAG: 6; + case DRAG_OPEN: 7; + case WAIT: 8; + case WAIT_ARROW: 9; + + case DISABLED: 10; + + case RESIZE_TR: 11; + case RESIZE_BL: 12; + case RESIZE_TL: 13; + case RESIZE_BR: 14; + case RESIZE_H: 15; + case RESIZE_V: 16; + + case RESIZE_T: 17; + case RESIZE_B: 18; + case RESIZE_L: 19; + case RESIZE_R: 20; + + case RESIZE_TLBR: 21; + case RESIZE_TRBL: 22; + + case CUSTOM: -1; + } + } } \ No newline at end of file diff --git a/source/funkin/backend/utils/NdllUtil.hx b/source/funkin/backend/utils/NdllUtil.hx index b4e84daa..9bada658 100644 --- a/source/funkin/backend/utils/NdllUtil.hx +++ b/source/funkin/backend/utils/NdllUtil.hx @@ -14,7 +14,7 @@ package funkin.backend.utils; * - The Function cannot be found in the NDLL * then an empty function will be returned instead, and a message will be shown in logs. */ -class NdllUtil { +final class NdllUtil { #if NDLLS_SUPPORTED #if windows public static final os:String = "windows"; #end #if linux public static final os:String = "linux"; #end diff --git a/source/funkin/backend/utils/ShaderResizeFix.hx b/source/funkin/backend/utils/ShaderResizeFix.hx index 96242859..97fc73fe 100644 --- a/source/funkin/backend/utils/ShaderResizeFix.hx +++ b/source/funkin/backend/utils/ShaderResizeFix.hx @@ -2,7 +2,7 @@ package funkin.backend.utils; import openfl.display.Sprite; -class ShaderResizeFix { +final class ShaderResizeFix { public static var doResizeFix:Bool = true; public static function init() { diff --git a/source/funkin/backend/utils/SortedArrayUtil.hx b/source/funkin/backend/utils/SortedArrayUtil.hx index 6afc0603..e637d74f 100644 --- a/source/funkin/backend/utils/SortedArrayUtil.hx +++ b/source/funkin/backend/utils/SortedArrayUtil.hx @@ -5,7 +5,7 @@ package funkin.backend.utils; * * DISCLAIMER: All types of utils only usable in a sorted array. */ -class SortedArrayUtil { +final class SortedArrayUtil { /** * Gets the index of a possible new element of an Array of T using an efficient algorithm. * @param array Array of T to check in diff --git a/source/funkin/backend/utils/ThreadUtil.hx b/source/funkin/backend/utils/ThreadUtil.hx index fe2cb5bf..8c2f654c 100644 --- a/source/funkin/backend/utils/ThreadUtil.hx +++ b/source/funkin/backend/utils/ThreadUtil.hx @@ -1,7 +1,7 @@ package funkin.backend.utils; #if ALLOW_MULTITHREADING -class ThreadUtil { +final class ThreadUtil { /** * Creates a new Thread with an error handler. * @param func Function to execute diff --git a/source/funkin/backend/utils/WindowUtils.hx b/source/funkin/backend/utils/WindowUtils.hx index 3a9f39ce..065a1573 100644 --- a/source/funkin/backend/utils/WindowUtils.hx +++ b/source/funkin/backend/utils/WindowUtils.hx @@ -2,7 +2,7 @@ package funkin.backend.utils; import openfl.Lib; -class WindowUtils { +final class WindowUtils { public static var title(default, set):String; private static function set_title(value:String):String { title = value; diff --git a/source/funkin/backend/utils/XMLUtil.hx b/source/funkin/backend/utils/XMLUtil.hx index a41294fd..48392a18 100644 --- a/source/funkin/backend/utils/XMLUtil.hx +++ b/source/funkin/backend/utils/XMLUtil.hx @@ -34,7 +34,7 @@ typedef TextFormat = { text:String, format:Dynamic } * Class made to make XML parsing easier. * Used in Stage.hx, Character.hx, and more. */ -class XMLUtil { +final class XMLUtil { /** * Applies a property XML node to an object. * The format for the XML is as follows: @@ -201,10 +201,15 @@ class XMLUtil { if(node.has.color) spr.color = FlxColor.fromString(node.getAtt("color")).getDefault(0xFFFFFFFF); + if(node.has.angle) + spr.angle = Std.parseFloat(node.getAtt("angle")).getDefault(spr.angle); + if (node.has.playOnCountdown) spr.skipNegativeBeats = node.att.playOnCountdown == "true"; if (node.has.beatInterval) spr.beatInterval = Std.parseInt(node.att.beatInterval); + if (node.has.interval) + spr.beatInterval = Std.parseInt(node.att.interval); if (node.has.beatOffset) spr.beatOffset = Std.parseInt(node.att.beatOffset); diff --git a/source/funkin/backend/utils/ZipUtil.hx b/source/funkin/backend/utils/ZipUtil.hx index d347ab6d..0e87fd55 100644 --- a/source/funkin/backend/utils/ZipUtil.hx +++ b/source/funkin/backend/utils/ZipUtil.hx @@ -24,7 +24,7 @@ using StringTools; // import ZipUtils; ZipUtils.uncompressZip(ZipUtils.openZip("E:\\Desktop\\test\\termination lua.ycemod"), "E:\\Desktop\\test\\uncompressed\\"); // import ZipUtils; var e = ZipUtils.createZipFile("file.ycemod"); ZipUtils.writeFolderToZip(e, "./mods/Friday Night Funkin'/", "Friday Night Funkin'/"); e.flush(); e.close(); -class ZipUtil { +final class ZipUtil { public static var bannedNames:Array<String> = [".git", ".gitignore", ".github", ".vscode", ".gitattributes", "readme.txt"]; /** diff --git a/source/funkin/backend/utils/native/Linux.hx b/source/funkin/backend/utils/native/Linux.hx index 01e6a84b..661c5842 100644 --- a/source/funkin/backend/utils/native/Linux.hx +++ b/source/funkin/backend/utils/native/Linux.hx @@ -2,7 +2,8 @@ package funkin.backend.utils.native; #if linux @:cppFileCode("#include <stdio.h>") -class Linux { +@:dox(hide) +final class Linux { @:functionCode(' FILE *meminfo = fopen("/proc/meminfo", "r"); diff --git a/source/funkin/backend/utils/native/Mac.hx b/source/funkin/backend/utils/native/Mac.hx index fa68700a..afd9d589 100644 --- a/source/funkin/backend/utils/native/Mac.hx +++ b/source/funkin/backend/utils/native/Mac.hx @@ -1,8 +1,13 @@ package funkin.backend.utils.native; -#if mac -@:cppFileCode("#include <sys/sysctl.h>") -class Mac { +#if (mac && cpp) +import funkin.backend.utils.NativeAPI.CodeCursor; +import openfl.ui.Mouse; + +@:headerInclude('sys/sysctl.h') +@:dox(hide) +final class Mac +{ @:functionCode(' int mib [] = { CTL_HW, HW_MEMSIZE }; int64_t value = 0; @@ -17,5 +22,39 @@ class Mac { { return 0; } + + public static function setMouseCursorIcon(icon:CodeCursor):Void + { + final valid:Bool = external.ExternalMac.setCursorIcon(icon.toInt(), null, 0, 0); + + if (!valid) + Mouse.cursor = icon.toOpenFL(); + } } -#end \ No newline at end of file +#end + + +/* + +Cursor + +Description +The arrow cursor (arrow) +The I-beam cursor for indicating insertion points (iBeam) +The cross-hair cursor (crosshair) +The closed-hand cursor (closedHand) +The open-hand cursor (openHand) +The pointing-hand cursor (pointingHand) +The resize-left cursor (resizeLeft) +The resize-right cursor (resizeRight) +The resize-left-and-right cursor (resizeLeftRight) +The resize-up cursor (resizeUp) +The resize-down cursor (resizeDown) +The resize-up-and-down cursor (resizeUpDown) +The disappearing item cursor (disappearingItem) +The I-Beam text cursor for vertical layout (iBeamCursorForVerticalLayout). +The not allowed cursor (operationNotAllowed). +The drag link cursor (dragLink). +The drag copy cursor (dragCopy). +The contextual menu cursor (contextualMenu). +*/ diff --git a/source/funkin/backend/utils/native/Windows.hx b/source/funkin/backend/utils/native/Windows.hx index 9ec12843..78aa5e10 100644 --- a/source/funkin/backend/utils/native/Windows.hx +++ b/source/funkin/backend/utils/native/Windows.hx @@ -114,7 +114,7 @@ class AudioFixClient : public IMMNotificationClient { AudioFixClient *curAudioFix; ') @:dox(hide) -class Windows { +final class Windows { public static var __audioChangeCallback:Void->Void = function() { trace("test"); diff --git a/source/funkin/editors/DebugOptions.hx b/source/funkin/editors/DebugOptions.hx index 05987a27..7a38ef7b 100644 --- a/source/funkin/editors/DebugOptions.hx +++ b/source/funkin/editors/DebugOptions.hx @@ -35,6 +35,10 @@ class DebugOptionsScreen extends OptionsScreen { NativeAPI.allocConsole(); })); #end + add(new Checkbox( + "Resizable Editors", + "If checked, this will allow the editors to render beyond the base 1280x720 resolution of FNF (allowing for more detail and space...)", + "editorsResizable")); add(new Checkbox( "Editor SFXs", "If checked, will play sound effects when working on editors (ex: will play SFXs when checking checkboxes...)", diff --git a/source/funkin/editors/EditorPicker.hx b/source/funkin/editors/EditorPicker.hx index f9350780..b77f7226 100644 --- a/source/funkin/editors/EditorPicker.hx +++ b/source/funkin/editors/EditorPicker.hx @@ -20,7 +20,7 @@ class EditorPicker extends MusicBeatSubstate { { name: "Stage Editor", id: "stage", - state: null + state: funkin.editors.stage.StageSelection }, #if debug { diff --git a/source/funkin/editors/EditorTreeMenu.hx b/source/funkin/editors/EditorTreeMenu.hx index 84f04d34..7bf5a0fb 100644 --- a/source/funkin/editors/EditorTreeMenu.hx +++ b/source/funkin/editors/EditorTreeMenu.hx @@ -12,6 +12,8 @@ class EditorTreeMenu extends TreeMenu { public override function create() { super.create(); + UIState.setResolutionAware(); + FlxG.camera.fade(0xFF000000, 0.5, true); bg = new FlxBackdrop(); diff --git a/source/funkin/editors/ModConfigWarning.hx b/source/funkin/editors/ModConfigWarning.hx index ca1fc34f..a7a9037e 100644 --- a/source/funkin/editors/ModConfigWarning.hx +++ b/source/funkin/editors/ModConfigWarning.hx @@ -43,7 +43,7 @@ MOD_DISCORD_LOGO_TEXT=""'; super.createPost(); hadPopup = true; - var substate = new UIWarningSubstate("Missing mod folder configuration!", "Your mod is currently missing a mod config file!\n\n\nWould you like to automatically generate one?\n\n(PS: If this is not your mod please disable Developer mode to stop this popup from appearing.)", [ + var substate = new UIWarningSubstate("Missing mod folder configuration!", "Your mod is currently missing a mod config file!\n\nWould you like to automatically generate one?\n\n(PS: If this is not your mod please disable Developer mode to stop this popup from appearing.)", [ { label: "Not Now", color: 0x969533, @@ -69,7 +69,6 @@ MOD_DISCORD_LOGO_TEXT=""'; } } ], false); - substate.bHeight = 300; openSubState(substate); } } \ No newline at end of file diff --git a/source/funkin/editors/UIDebugState.hx b/source/funkin/editors/UIDebugState.hx index 3a0bf57f..e7270da6 100644 --- a/source/funkin/editors/UIDebugState.hx +++ b/source/funkin/editors/UIDebugState.hx @@ -1,9 +1,17 @@ package funkin.editors; +import funkin.editors.ui.old.OldUISliceSpriteTri; +import funkin.editors.ui.UITopMenu.UITopMenuButton; import flixel.tweens.FlxTween; import funkin.editors.ui.*; +import funkin.game.Character; class UIDebugState extends UIState { + public var topMenuSpr:UITopMenu; + + public var testingUIItems:Array<FlxSprite> = []; + public var testingUIHidden:Bool = false; + public override function create() { super.create(); @@ -14,7 +22,11 @@ class UIDebugState extends UIState { bg.scrollFactor.set(); add(bg); - add(new UITopMenu([ + var gf = new Character(0, 0, "gf"); + gf.dance(); + add(gf); + + add(topMenuSpr = new UITopMenu([ { label: "File", childs: [ @@ -45,15 +57,57 @@ class UIDebugState extends UIState { }, { label: "Help" + }, + { + label: "9-Splice Benchmark >", + childs: [ + { + label: "Hide Other UI Items", + onSelect: (t) -> { + testingUIHidden = !testingUIHidden; + t.icon = testingUIHidden ? 1 : 0; + + for (item in testingUIItems) + item.visible = !testingUIHidden; + }, + icon: testingUIHidden ? 1 : 0 + }, + { + label: "Clear Benchmark Sprites", + onSelect: (t) -> { + for (splice in spliceSprites) { + splice.kill(); + splice.destroy(); + + this.remove(splice); + } + + spliceSprites.resize(0); + MemoryUtil.clearMajor(); + } + }, + null, + { + label: "Draw Triangles?", + onSelect: (t) -> { + spliceUseTris = !spliceUseTris; + t.icon = spliceUseTris ? 1 : 0; + + spliceCl = spliceUseTris ? OldUISliceSpriteTri : UISliceSprite; + trace(spliceCl); + }, + icon: spliceUseTris ? 1 : 0 + } + ] } ])); - add(new UICheckbox(10, 40, "Test unchecked", false)); - add(new UICheckbox(10, 70, "Test checked", true)); - add(new UIButton(10, 100, "Test button", function() { + testingUIItems.push(cast add(new UICheckbox(10, 40, "Test unchecked", false))); + testingUIItems.push(cast add(new UICheckbox(10, 70, "Test checked", true))); + testingUIItems.push(cast add(new UIButton(10, 100, "Test button", function() { trace("Hello, World!"); - }, 130, 32)); - add(new UIButton(10, 140, "Warning test", function() { + }, 130, 32))); + testingUIItems.push(cast add(new UIButton(10, 140, "Warning test", function() { openSubState(new UIWarningSubstate("Test", "This is a test message", [ { label: "Alt. Choice", @@ -68,8 +122,8 @@ class UIDebugState extends UIState { } } ])); - }, 130, 32)); - add(new UIButton(10, 180, "Warning test (Overflowing)", function() { + }, 130, 32))); + testingUIItems.push(cast add(new UIButton(10, 180, "Warning test (Overflowing)", function() { openSubState(new UIWarningSubstate("Test", "This is a test message", [ { label: "Alt. Choice", @@ -98,18 +152,54 @@ class UIDebugState extends UIState { onClick: function(t) {} } ])); - }, 130, 48)); - add(new UITextBox(10, 220, "")); + }, 130, 48))); + testingUIItems.push(cast add(new UITextBox(10, 220, ""))); - var spliceSprite:UISliceSprite = new UISliceSprite(650, 350, 30, 30, "editors/ui/context-bg"); - add(spliceSprite); - FlxTween.num(0, 1, 2, {type: PINGPONG}, function (v:Float) { - spliceSprite.bWidth = Std.int(lerp(12, 200, v)); spliceSprite.bHeight = Std.int(lerp(12, 200, v)); - }); + spliceText = new UIText(0, 0, 0, "(0) Sprites"); + add(spliceText); } + public var spliceSprites:Array<FlxSprite> = []; + public var spliceText:UIText; + + public var spliceTimer:Float = 0; + public var spliceCoolDown:Float = 0.04; + + public var spliceUseTris:Bool = false; + public var spliceCl:Class<FlxSprite> = UISliceSprite; + public override function update(elapsed:Float) { super.update(elapsed); + + if (FlxG.keys.pressed.SPACE) { + spliceTimer += elapsed; + if (spliceTimer >= spliceCoolDown) { + spliceTimer -= spliceCoolDown; + + for (i in 0...3) { + var width:Int = FlxG.random.int(10, 200); + var height:Int = FlxG.random.int(10, 200); + + var spliceSprite:FlxSprite = Type.createInstance(spliceCl, + [ + FlxG.random.float(0, FlxG.width-width), + FlxG.random.float(22, FlxG.height-height), + width, height, "editors/ui/context-bg" + ] + ); + + spliceSprites.push(cast add(spliceSprite)); + } + } + } + + var spliceTopButton:UITopMenuButton = cast topMenuSpr.members[topMenuSpr.members.length-1]; + + spliceText.x = spliceTopButton.x + spliceTopButton.bWidth - 2; + spliceText.y = spliceTopButton.label.y+1; + + spliceText.text = '(${spliceSprites.length}) 9-Splice Sprites'; + if (FlxG.mouse.justReleasedRight) { openContextMenu([ { diff --git a/source/funkin/editors/UndoList.hx b/source/funkin/editors/UndoList.hx index cefdc12f..79d61a87 100644 --- a/source/funkin/editors/UndoList.hx +++ b/source/funkin/editors/UndoList.hx @@ -35,4 +35,10 @@ class UndoList<T> { public inline function save() savedLength = undoList.length; + + public inline function clear() { + undoList = []; + redoList = []; + save(); + } } \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterAnimButton.hx b/source/funkin/editors/character/CharacterAnimButton.hx new file mode 100644 index 00000000..61182562 --- /dev/null +++ b/source/funkin/editors/character/CharacterAnimButton.hx @@ -0,0 +1,516 @@ +package funkin.editors.character; + +import flixel.text.FlxText.FlxTextFormat; +import flixel.text.FlxText.FlxTextFormatMarkerPair; +import flxanimate.animate.FlxAnim.FlxSymbolAnimation; +import flxanimate.animate.FlxSymbol; +import flxanimate.animate.FlxElement; +import flxanimate.animate.SymbolParameters; +import flxanimate.animate.FlxKeyFrame; +import flxanimate.animate.FlxTimeline; +import flixel.graphics.frames.FlxFrame; +import flixel.animation.FlxAnimation; +import openfl.geom.Rectangle; +import funkin.backend.utils.XMLUtil.AnimData; +import flixel.math.FlxPoint; +import flixel.util.FlxColor; + +using StringTools; + +class CharacterAnimButton extends UIButton { + public var anim:String = null; + public var valid:Bool = true; + public var data:AnimData = null; + public var parent:CharacterAnimsWindow; + + public var animationDisplayBG:UISliceSprite; + public var nameTextBox:UITextBox; + public var animTextBox:UIAutoCompleteTextBox; + public var positionXStepper:UINumericStepper; + public var positionYStepper:UINumericStepper; + public var fpsStepper:UINumericStepper; + public var loopedCheckbox:UICheckbox; + public var indicesTextBox:UITextBox; + public var XYComma:UIText; + + public var editButton:UIButton; + public var editIcon:FlxSprite; + + public var deleteButton:UIButton; + public var deleteIcon:FlxSprite; + public var ghostButton:UIButton; + public var ghostIcon:FlxSprite; + + public var animIcon:FlxSprite; + + public var labels:Map<UISprite, UIText> = []; + + public var foldableButtons:Array<FlxSprite> = []; + public var closed:Bool = true; + + public function new(x:Float, y:Float, animData:AnimData, parent:CharacterAnimsWindow) { + this.anim = animData.name; + this.data = animData; + this.parent = parent; + super(x,y, '${animData.name} (${animData.x}, ${animData.y})', function () { + CharacterEditor.instance.playAnimation(this.anim); + }, Std.int(500-16-32), 208); + + function addLabelOn(ui:UISprite, text:String, ?size:Int):UIText { + var uiText:UIText = new UIText(ui.x, ui.y-18, 0, text, size); + members.push(uiText); labels.set(ui, uiText); + return uiText; + } + + autoAlpha = autoFrames = autoFollow = false; + + frames = Paths.getFrames('editors/ui/inputbox'); + field.fieldWidth = 0; framesOffset = 9; + field.size = 14; + + animationDisplayBG = new UISliceSprite(x+12, y+12+18+12, 128, 128, 'editors/ui/inputbox-small'); + members.push(animationDisplayBG); + foldableButtons.push(animationDisplayBG); + + nameTextBox = new UITextBox(animationDisplayBG.x+126+16, animationDisplayBG.y, animData.name, 104, 22, false, true); + nameTextBox.onChange = (newName:String) -> {this.changeName(newName);}; + members.push(nameTextBox); + foldableButtons.push(nameTextBox); + addLabelOn(nameTextBox, "Name", 12); + + animTextBox = new UIAutoCompleteTextBox(nameTextBox.x + 100, nameTextBox.y, animData.anim, 156, 22, false, true); + animTextBox.onChange = (newAnim:String) -> {this.changeAnim(newAnim);}; + members.push(animTextBox); + foldableButtons.push(animTextBox); + addLabelOn(animTextBox, "Animation", 12); + + positionXStepper = new UINumericStepper(animTextBox.x, animTextBox.y+32+18, animData.x, 0.001, 2, null, null, 64, 22, true); + positionXStepper.onChange = (text:String) -> { + @:privateAccess positionXStepper.__onChange(text); + this.changeOffset(positionXStepper.value, null); + }; + members.push(positionXStepper); + foldableButtons.push(positionXStepper); + addLabelOn(positionXStepper, "Position (X,Y)", 12); + + members.push(XYComma = new UIText(positionXStepper.x+104-32+0, positionXStepper.y + 9, 0, ",", 18)); + foldableButtons.push(XYComma); + + positionYStepper = new UINumericStepper(positionXStepper.x+104-32+26, positionXStepper.y, animData.y, 0.001, 2, null, null, 64, 22, true); + positionYStepper.onChange = (text:String) -> { + @:privateAccess positionYStepper.__onChange(text); + this.changeOffset(null, positionYStepper.value); + }; + members.push(positionYStepper); + foldableButtons.push(positionYStepper); + + fpsStepper = new UINumericStepper(animTextBox.x + 200 + 26, animTextBox.y, animData.fps, 0.1, 2, 1, 100, 52, 22, true); + fpsStepper.onChange = (text:String) -> { + @:privateAccess fpsStepper.__onChange(text); + this.changeFPS(fpsStepper.value); + }; + members.push(fpsStepper); + foldableButtons.push(fpsStepper); + addLabelOn(fpsStepper, "FPS", 12); + + loopedCheckbox = new UICheckbox(fpsStepper.x + 82 - 32 + 26, fpsStepper.y, "Looping?", animData.loop, 0, true); + loopedCheckbox.onChecked = (newLooping:Bool) -> {this.changeLooping(newLooping);}; + members.push(loopedCheckbox); + foldableButtons.push(loopedCheckbox); + + loopedCheckbox.x += 8; loopedCheckbox.y += 6; + + indicesTextBox = new UITextBox(nameTextBox.x, nameTextBox.y, CoolUtil.formatNumberRange(animData.indices.getDefault([]), ", "), 278, 22, false, true); + indicesTextBox.onChange = (text:String) -> { + this.changeIndicies(CoolUtil.parseNumberRange(text)); + } + members.push(indicesTextBox); + foldableButtons.push(indicesTextBox); + addLabelOn(indicesTextBox, "Indices (frames)", 12); + + animIcon = new FlxSprite(x-(10+16), y+8).loadGraphic(Paths.image("editors/character/anim-icons"), true, 16, 12); + animIcon.animation.add("play", [0]); + animIcon.animation.add("error", [1]); + // animIcon.color = 0xFFD60E0E; + animIcon.animation.play("play"); + animIcon.antialiasing = false; + animIcon.updateHitbox(); + members.push(animIcon); + + deleteButton = new UIButton(0, 0, null, () -> {parent.deleteAnimation(this);}, 28*2,24); + deleteButton.frames = Paths.getFrames("editors/ui/grayscale-button"); + deleteButton.color = 0xFFAC3D3D; + members.push(deleteButton); + + deleteIcon = new FlxSprite(x-(10+16), y+8).loadGraphic(Paths.image("editors/deleter")); + deleteIcon.color = 0xFFD60E0E; + deleteIcon.antialiasing = false; + members.push(deleteIcon); + + editButton = new UIButton(0, 0, null, () -> { + if (!this.closed) UIUtil.confirmUISelections(this); + this.closed = !this.closed; + }, 28,24); + editButton.frames = Paths.getFrames("editors/ui/grayscale-button"); + editButton.color = 0xFFAFAA12; + members.push(editButton); + + editIcon = new FlxSprite().loadGraphic(Paths.image('editors/character/edit-button')); + //editIcon.color = 0xFFE8E801; + editIcon.antialiasing = false; + members.push(editIcon); + + ghostButton = new UIButton(0, 0, null, toggleGhost, 28, 24); + ghostButton.frames = Paths.getFrames("editors/ui/grayscale-button"); + ghostButton.color = 0xFFADADAD; + ghostButton.alpha = 0.5; + members.push(ghostButton); + + ghostIcon = new FlxSprite(x-(10+16), y+8).loadGraphic(Paths.image('editors/character/ghost'), true, 16, 12); + ghostIcon.animation.add("alive", [0]); + ghostIcon.animation.add("dead", [1]); + ghostIcon.animation.play("dead"); + ghostIcon.color = 0xFFADADAD; + ghostIcon.antialiasing = false; + ghostIcon.updateHitbox(); + members.push(ghostIcon); + + checkValid(); + } + + public inline function updateButtonsPos() { + animIcon.follow(this, 22, (52/2) - (animIcon.height/2) + (animIcon.animation.name == "error" ? 2 : 0)); + field.follow(this, 22+16+10, (52/2) - (field.height/2) + 1); + + deleteButton.follow(this, 380, 14); + deleteIcon.follow(deleteButton, (deleteButton.bWidth/2)-6.5, (deleteButton.bHeight/2)-6); + + editButton.follow(this, 340, 14); + editIcon.follow(editButton, (editButton.bWidth/2)-6, (editButton.bHeight/2)-6); + + ghostButton.follow(this, 300, 14); + ghostIcon.follow(ghostButton, (ghostButton.bWidth/2)-8, (ghostButton.bHeight/2)-6); + + animationDisplayBG.follow(this, 16, 61); + nameTextBox.follow(this, 158, 72); + animTextBox.follow(this, 282, 72); + + positionXStepper.follow(this, 158, 118); + positionYStepper.follow(this, 218, 118); + XYComma.follow(positionXStepper, 64-24, 6); + + fpsStepper.follow(this, 282, 118); + loopedCheckbox.follow(this, 336, 122); + + indicesTextBox.follow(this, 158, 164); + + for (ui => text in labels) { + text.follow(ui, -(2+(ui is UICheckbox?12:0)), -(18+(ui is UICheckbox?6:0))); + text.visible = text.active = ui.visible; + } + } + + public override function update(elapsed:Float) { + bHeight = closed ? 52 : 208; + + for (button in foldableButtons) + button.visible = button.active = !closed; + + super.update(elapsed); + } + + public function changeName(newName:String, addToUndo:Bool = true) @:privateAccess { + if (newName == anim) return; + + if (parent.character.getNameList().indexOf(newName) != -1) { + nameTextBox.label.text = anim; + return; + } + + var oldName:String = anim; + if (parent.character.animateAtlas != null) { + var animSymbol:FlxSymbolAnimation = __getAnimationSymbol(); + + parent.character.animateAtlas.anim.animsMap.remove(anim); + parent.character.animateAtlas.anim.animsMap.set(newName, animSymbol); + } else { + var flxAnimation:FlxAnimation = __getFlxAnimation(); + flxAnimation.name = newName; + + parent.character.animation._animations.remove(anim); + parent.character.animation._animations.set(newName, flxAnimation); + } + + var animData:AnimData = parent.character.animDatas[anim]; + animData.name = newName; + + parent.character.animDatas.remove(anim); + parent.character.animDatas.set(newName, animData); + + var animOffset:FlxPoint = parent.character.animOffsets[anim]; + parent.character.animOffsets.remove(anim); + parent.character.animOffsets.set(newName, animOffset); + + var displayFrame:{scale:Float, animBounds:Rectangle, frame:Int} = parent.displayAnimsFramesList[anim]; + parent.displayAnimsFramesList.remove(anim); + parent.displayAnimsFramesList.set(newName, displayFrame); + + this.anim = newName; + nameTextBox.label.text = newName; + updateText(); + + if (addToUndo) CharacterEditor.undos.addToUndo(CAnimEditName(ID, oldName, newName)); + } + + public function changeAnim(newAnim:String, addToUndo:Bool = true) @:privateAccess { + var animData:AnimData = parent.character.animDatas[anim]; + if (newAnim == animData.anim) return; + + var oldAnim:String = animData.anim; + animData.anim = newAnim; + + if (parent.character.animateAtlas != null) + __refreshAnimation(); + else { + var flxAnimation:FlxAnimation = __getFlxAnimation(); + flxAnimation.prefix = newAnim; + + refreshFlxAnimationFrames(flxAnimation, animData); + + if (valid) { + parent.buildAnimDisplay(anim, data); + animationDisplayBG.alpha = 1; + } else { + parent.removeAnimDisplay(anim); + animationDisplayBG.alpha = 0.4; + } + } + + if (parent.character.getAnimName() == anim) + CharacterEditor.instance.playAnimation(anim); + + animTextBox.label.text = newAnim; + + if (addToUndo) CharacterEditor.undos.addToUndo(CAnimEditAnim(ID, oldAnim, newAnim)); + } + + public function changeOffset(newOffsetX:Null<Float>, newOffsetY:Null<Float>, addToUndo:Bool = true) { + var animData:AnimData = parent.character.animDatas[anim]; + + if (newOffsetX != null && newOffsetY != null && newOffsetX == animData.x && newOffsetY == animData.y) + return; + + var oldPosition:FlxPoint = FlxPoint.get(animData.x, animData.y); + + if (newOffsetX != null) animData.x = newOffsetX; + if (newOffsetY != null) animData.y = newOffsetY; + + parent.character.animOffsets[anim].set(animData.x, animData.y); + + if (parent.character.getAnimName() == anim && Options.playAnimOnOffset) + CharacterEditor.instance.playAnimation(anim); + + positionXStepper.label.text = Std.string(animData.x); + positionYStepper.label.text = Std.string(animData.y); + + updateText(); + + if (addToUndo) CharacterEditor.undos.addToUndo(CAnimEditOffset(ID, oldPosition, FlxPoint.get(animData.x, animData.y))); + } + + public function changeFPS(newFPS:Float, addToUndo:Bool = true) @:privateAccess { + var animData:AnimData = parent.character.animDatas[anim]; + var oldFPS:Float = animData.fps; + animData.fps = newFPS; + + if (parent.character.animateAtlas != null) { + var animSymbol:FlxSymbolAnimation = __getAnimationSymbol(); + animSymbol.frameRate = newFPS; + } else { + var flxAnimation:FlxAnimation = __getFlxAnimation(); + flxAnimation.frameRate = newFPS; + } + + if (parent.character.getAnimName() == anim) + CharacterEditor.instance.playAnimation(anim); + + fpsStepper.label.text = Std.string(newFPS); + + if (addToUndo) CharacterEditor.undos.addToUndo(CAnimEditFPS(ID, oldFPS, newFPS)); + } + + public function changeLooping(newLooping:Bool, addToUndo:Bool = true) @:privateAccess { + var animData:AnimData = parent.character.animDatas[anim]; + + animData.loop = newLooping; + + if (parent.character.animateAtlas != null) { + var animSymbol:FlxSymbolAnimation = __getAnimationSymbol(); + animSymbol.instance.symbol.loop = animData.loop ? Loop : PlayOnce; + } else { + var flxAnimation:FlxAnimation = __getFlxAnimation(); + flxAnimation.looped = animData.loop; + } + + if (parent.character.getAnimName() == anim) + CharacterEditor.instance.playAnimation(anim); + + loopedCheckbox.checked = newLooping; + + if (addToUndo) CharacterEditor.undos.addToUndo(CAnimEditLooping(ID, newLooping)); + } + + public function changeIndicies(indicies:Array<Int>, addToUndo:Bool = true) @:privateAccess { + var animData:AnimData = parent.character.animDatas[anim]; + var oldIndices:Array<Int> = animData.indices.copy(); + + animData.indices = indicies; + + __refreshAnimation(); + + if (parent.character.getAnimName() == anim) + CharacterEditor.instance.playAnimation(anim); + + indicesTextBox.label.text = animData.indices.join(", "); + + if (addToUndo) CharacterEditor.undos.addToUndo(CAnimEditIndices(ID, oldIndices, indicies)); + } + + public inline function refreshFlxAnimationFrames(flxAnimation:FlxAnimation, animData:AnimData) @:privateAccess { + try { + if (animData.indices.length > 0) { + var frameIndices:Array<Int> = new Array<Int>(); + parent.character.animation.byIndicesHelper(frameIndices, flxAnimation.prefix, animData.indices, ""); + + flxAnimation.frames = frameIndices; + } else { + final animFrames:Array<FlxFrame> = new Array<FlxFrame>(); + parent.character.animation.findByPrefix(animFrames, flxAnimation.prefix); + + final frameIndices:Array<Int> = []; + parent.character.animation.byPrefixHelper(frameIndices, animFrames, flxAnimation.prefix); + + flxAnimation.frames = frameIndices; + } + + if (flxAnimation.frames.length <= 0) invalidate(); + else validate(); + } catch (e) { + trace('ERROR REFRESHING FLXANIMATION FRAMES: $e'); + invalidate(); + } + } + + public inline function refreshSymbolKeyFrames(symbol:FlxSymbolAnimation, animData:AnimData) @:privateAccess { + var wasRefreshed:Bool = false; + + try { + parent.character.animateAtlas.anim.animsMap.remove(animData.name); + if (animData.indices.length > 0) + parent.character.animateAtlas.anim.addBySymbolIndices(animData.name, animData.anim, animData.indices, animData.fps, animData.loop); + else + parent.character.animateAtlas.anim.addBySymbol(animData.name, animData.anim, animData.fps, animData.loop); + wasRefreshed = parent.character.animateAtlas.anim.animsMap.exists(animData.name); + if (!wasRefreshed) __getAnimationSymbol(); + } catch (e) { + trace('ERROR REFRESHING SYMBOL FRAMES: $e'); + } + + validate(wasRefreshed); + } + + public function toggleGhost() { + if (valid && parent.ghosts.indexOf(anim) == -1) { + parent.ghosts.push(anim); + ghostIcon.animation.play("alive", true); + ghostIcon.color = 0xFFFFFFFF; + } else { + parent.ghosts.remove(anim); + ghostIcon.animation.play("dead", true); + ghostIcon.color = 0xFFADADAD; + } + } + + public override function draw() { + updateButtonsPos(); + super.draw(); + + if (!closed && parent.displayAnimsFramesList.exists(anim)) { + var displayData:{frame:Int, scale:Float, animBounds:Rectangle} = parent.displayAnimsFramesList.get(anim); + parent.displayWindowSprite.frame = parent.displayWindowSprite.frames.frames[displayData.frame]; + + parent.displayWindowSprite.scale.x = parent.displayWindowSprite.scale.y = displayData.scale; + parent.displayWindowSprite.updateHitbox(); + + parent.displayWindowSprite.follow( + this, 16+(128/2)-((parent.displayWindowSprite.frame.sourceSize.x*displayData.scale)/2), + 8+32+8+2+11+(128/2)-((parent.displayWindowSprite.frame.sourceSize.y*displayData.scale)/2) + ); + parent.displayWindowSprite.draw(); + } + } + + public function updateText() { + field.applyMarkup( + '${anim} ' + (valid ? '(${parent.character.animOffsets[anim].x}, ${parent.character.animOffsets[anim].y})' : "|(Invalid Animation!!!)|"), + [new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFF29797), "|")]); + } + + public function validate(isValid:Bool = true) { + var wasValid:Bool = valid; + this.valid = isValid; + + animIcon.animation.play(valid ? "play" :"error"); + color = valid ? 0xFFFFFFFF : 0xFFD60E0E; + + ghostIcon.visible = ghostButton.visible = isValid; + XYComma.alpha = isValid ? 1 : 0.4; + for (uiElement in [nameTextBox, positionXStepper, positionYStepper, fpsStepper, loopedCheckbox, indicesTextBox]) { + uiElement.selectable = isValid; + if (labels[uiElement] != null) + labels[uiElement].alpha = isValid ? 1 : 0.4; + } + if (!valid) toggleGhost(); + updateText(); + + if (!wasValid && isValid && CharacterEditor.instance.characterFakeAnim == anim) + CharacterEditor.instance.playAnimation(this.anim); + } + + public inline function invalidate() + validate(false); + + public inline function __getFlxAnimation():Null<FlxAnimation> @:privateAccess { + if (!parent.character.animation._animations.exists(anim)) { + final flxanim = new FlxAnimation(parent.character.animation, anim, [], data.fps, data.loop); + flxanim.prefix = data.anim; // sobbing + + parent.character.animation._animations.set(anim, flxanim); + parent.character.animDatas.set(anim, data); + } + return parent.character.animation._animations[anim]; + } + + public inline function __getAnimationSymbol():Null<FlxSymbolAnimation> @:privateAccess { + if (!parent.character.animateAtlas.anim.animsMap.exists(anim)) + XMLUtil.addAnimToSprite(parent.character, data); + return parent.character.animateAtlas.anim.animsMap[anim]; + } + + + @:noCompletion function __refreshAnimation() @:privateAccess { + if (parent.character.animateAtlas != null) refreshSymbolKeyFrames(__getAnimationSymbol(), data); + else refreshFlxAnimationFrames(__getFlxAnimation(), data); + + if (valid) { + parent.buildAnimDisplay(anim, data); + animationDisplayBG.alpha = 1; + } else { + parent.removeAnimDisplay(anim); + animationDisplayBG.alpha = 0.4; + } + } + + public inline function checkValid() + __refreshAnimation(); +} \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterAnimButtons.hx b/source/funkin/editors/character/CharacterAnimButtons.hx deleted file mode 100644 index c80913cd..00000000 --- a/source/funkin/editors/character/CharacterAnimButtons.hx +++ /dev/null @@ -1,89 +0,0 @@ -package funkin.editors.character; - -import flixel.math.FlxPoint; -import flixel.util.FlxColor; - -class CharacterAnimButtons extends UIButton { - public var anim:String = ""; - - public var editButton:UIButton; - public var editIcon:FlxSprite; - - public var ghostButton:UIButton; - public var ghostIcon:FlxSprite; - - public var deleteButton:UIButton; - public var deleteIcon:FlxSprite; - - public function new(x:Float,y:Float,anim:String, offset:FlxPoint) { - this.anim = anim; - super(x,y, '$anim (${offset.x}, ${offset.y})', function () { - CharacterEditor.instance.playAnimation(this.anim); - }, 282); - autoAlpha = false; - - ghostButton = new UIButton(x+282+17, y, "", function () { - CharacterEditor.instance.ghostAnim(this.anim); - }, 32); - ghostButton.autoAlpha = false; - members.push(ghostButton); - - ghostIcon = new FlxSprite(ghostButton.x + 8, ghostButton.y + 8).loadGraphic(Paths.image('editors/character/ghost-button'), true, 16, 16); - ghostIcon.animation.add("alive", [0]); - ghostIcon.animation.add("dead", [1]); - ghostIcon.animation.play("dead"); ghostIcon.alpha = 0.5; - ghostIcon.antialiasing = false; - ghostIcon.updateHitbox(); - members.push(ghostIcon); - - editButton = new UIButton(ghostButton.x+32+17, y, "", function () { - CharacterEditor.instance.editAnimWithUI(this.anim); - }, 32); - editButton.frames = Paths.getFrames("editors/ui/grayscale-button"); - editButton.color = FlxColor.YELLOW; - editButton.autoAlpha = false; - members.push(editButton); - - editIcon = new FlxSprite(editButton.x + 8, editButton.y + 8).loadGraphic(Paths.image('editors/character/edit-button')); - editIcon.antialiasing = false; - members.push(editIcon); - - deleteButton = new UIButton(editButton.x+32+17, y, "", function () { - CharacterEditor.instance.deleteAnim(this.anim); - }, 32); - deleteButton.color = FlxColor.RED; - deleteButton.autoAlpha = false; - members.push(deleteButton); - - deleteIcon = new FlxSprite(deleteButton.x + (15/2), deleteButton.y + 8).loadGraphic(Paths.image('editors/delete-button')); - deleteIcon.antialiasing = false; - members.push(deleteIcon); - } - - public override function update(elapsed:Float) { - editButton.selectable = ghostButton.selectable = deleteButton.selectable = selectable; - editButton.shouldPress = ghostButton.shouldPress = deleteButton.shouldPress = shouldPress; - - hovered = !deleteButton.hovered; - updatePos(); - super.update(elapsed); - } - - public function updateInfo(anim:String, offset:FlxPoint, ghost:Bool) { - this.anim = anim; - - field.text = '$anim (${offset.x}, ${offset.y})'; - ghostIcon.animation.play(ghost ? "alive" : "dead"); - ghostIcon.alpha = ghost ? 1 : 0.5; - } - - public inline function updatePos() { - // buttons - deleteButton.x = (editButton.x = (ghostButton.x = (x+282+17))+32+17)+32+17; - deleteButton.y = editButton.y = ghostButton.y = y; - // icons - ghostIcon.x = ghostButton.x + 8; ghostIcon.y = ghostButton.y + 8; - editIcon.x = editButton.x + 8; editIcon.y = editButton.y + 8; - deleteIcon.x = deleteButton.x + (15/2); deleteIcon.y = deleteButton.y + 8; - } -} \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterAnimScreen.hx b/source/funkin/editors/character/CharacterAnimScreen.hx deleted file mode 100644 index 1a867dbd..00000000 --- a/source/funkin/editors/character/CharacterAnimScreen.hx +++ /dev/null @@ -1,117 +0,0 @@ -package funkin.editors.character; - -import flixel.animation.FlxAnimation; -import flixel.math.FlxPoint; -import funkin.backend.utils.XMLUtil.AnimData; - -class CharacterAnimScreen extends UISubstateWindow { - public var animData:AnimData; - - public var nameTextBox:UITextBox; - public var animTextBox:UITextBox; - public var fpsStepper:UINumericStepper; - public var loopedCheckbox:UICheckbox; - public var indicesTextBox:UITextBox; - public var offsetXStepper:UINumericStepper; - public var offsetYStepper:UINumericStepper; - - public var saveButton:UIButton; - public var closeButton:UIButton; - - public var onSave:(animData:AnimData) -> Void = null; - - public function new(animData:AnimData, ?onSave:(animData:AnimData) -> Void) { - this.animData = animData; - this.onSave = onSave; - super(); - } - - public override function create() { - var creatingAnim:Bool = animData == null; - - if (creatingAnim) - animData = { - name: "name", - anim: "anim", - fps: 24, - loop: false, - animType: NONE, - x: 0, - y: 0, - indices: [] - }; - - winTitle = creatingAnim ? 'Creating Animation' : 'Animation ${animData.name} properties'; - winWidth = 690; winHeight = 314; - - super.create(); - - function addLabelOn(ui:UISprite, text:String) - add(new UIText(ui.x, ui.y - 24, 0, text)); - - var title:UIText; - add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, creatingAnim ? "Create New Animation" : "Edit Animation Properties", 28)); - - nameTextBox = new UITextBox(title.x, title.y + title.height + 38, animData.name, 160); - add(nameTextBox); - addLabelOn(nameTextBox, "Name"); - - animTextBox = new UITextBox(nameTextBox.x + 160 + 26, nameTextBox.y, animData.anim, 200); - add(animTextBox); - addLabelOn(animTextBox, "Animation"); - - fpsStepper = new UINumericStepper(animTextBox.x + 200 + 26, animTextBox.y, animData.fps, 0.1, 2, 1, 100, 82); - add(fpsStepper); - addLabelOn(fpsStepper, "FPS"); - - loopedCheckbox = new UICheckbox(fpsStepper.x + 82 - 32 + 26, fpsStepper.y, "Looped", animData.loop); - add(loopedCheckbox); - addLabelOn(loopedCheckbox, "Looping"); - loopedCheckbox.x += 8; loopedCheckbox.y += 6; - - indicesTextBox = new UITextBox(nameTextBox.x, nameTextBox.y + 32 + 40, CoolUtil.formatNumberRange(animData.indices, ", "), 270); - add(indicesTextBox); - addLabelOn(indicesTextBox, "Indices"); - - offsetXStepper = new UINumericStepper(indicesTextBox.x + 270 + 26, indicesTextBox.y, animData.x, 0.001, 2, null, null, 84); - add(offsetXStepper); - addLabelOn(offsetXStepper, "Offset (X,Y)"); - - add(new UIText(offsetXStepper.x + 84 - 32 + 0, offsetXStepper.y + 9, 0, ",", 22)); - - offsetYStepper = new UINumericStepper(offsetXStepper.x + 84 - 32 + 26, offsetXStepper.y, animData.y, 0.001, 2, null, null, 84); - add(offsetYStepper); - - saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() { - saveAnimData(); - close(); - }, 125); - add(saveButton); - - closeButton = new UIButton(saveButton.x - 20, saveButton.y, creatingAnim ? "Cancel" : "Close", function() { - if (creatingAnim && onSave != null) onSave(null); - close(); - }, 125); - add(closeButton); - closeButton.color = 0xFFFF0000; - closeButton.x -= closeButton.bWidth; - } - - public function saveAnimData() { - for (stepper in [offsetXStepper, offsetYStepper, fpsStepper]) - @:privateAccess stepper.__onChange(stepper.label.text); - - animData = { - name: nameTextBox.label.text, - anim: animTextBox.label.text, - fps: fpsStepper.value, - loop: loopedCheckbox.checked, - animType: NONE, - x: offsetXStepper.value, - y: offsetYStepper.value, - indices: CoolUtil.parseNumberRange(indicesTextBox.label.text) - }; - - if (onSave != null) onSave(animData); - } -} \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterAnimsWindow.hx b/source/funkin/editors/character/CharacterAnimsWindow.hx new file mode 100644 index 00000000..a58f790f --- /dev/null +++ b/source/funkin/editors/character/CharacterAnimsWindow.hx @@ -0,0 +1,171 @@ +package funkin.editors.character; + +import flixel.util.typeLimit.OneOfTwo; +import funkin.backend.utils.XMLUtil.AnimData; +import flixel.animation.FlxAnimation; +import flixel.graphics.FlxGraphic; +import openfl.geom.Rectangle; +import flixel.graphics.frames.FlxFrame; +import funkin.game.Character; +import flixel.animation.FlxPrerotatedAnimation; + +using funkin.backend.utils.BitmapUtil; + +class CharacterAnimsWindow extends UIButtonList<CharacterAnimButton> { + public var character:CharacterGhost; + + public var displayWindowSprite:FlxSprite; + public var displayWindowGraphic:FlxGraphic; + public var displayAnimsFramesList:Map<String, {scale:Float, animBounds:Rectangle, frame:OneOfTwo<Int, String>}> = []; + + public var animButtons:Map<String, CharacterAnimButton> = []; + public var animsList:Array<String> = []; + + public function new(x:Float, y:Float, character:CharacterGhost) { + super(x, y, Std.int(500-16), 419, null, FlxPoint.get(Std.int(500-16-32), 208)); + this.character = character; + + cameraSpacing = 0; + frames = Paths.getFrames('editors/ui/inputbox'); + + buttonCameras.pixelPerfectRender = true; + + if (Assets.exists(Paths.image('characters/${character.sprite}')) && character.animateAtlas == null) + displayWindowGraphic = FlxG.bitmap.add(Assets.getBitmapData(Paths.image('characters/${character.sprite}'), true, false)); + + displayWindowSprite = new FlxSprite(); + displayWindowSprite.loadGraphicFromSprite(character); + displayWindowSprite.antialiasing = character.antialiasing; + displayWindowSprite.flipX = character.flipX; + + alpha = 0.7; + + for (anim in character.getAnimOrder()) + addAnimation(character.animDatas.get(anim), -1, false); + addButton.callback = generateAnimation; + + setAnimAutoComplete(CoolUtil.getAnimsListFromSprite(character)); + + nextscrollY = 0; + // dragCallback = (button:CharacterAnimButton, oldID:Int, newID:Int) -> {} + } + + public var ghosts:Array<String> = []; + var __movingAnimOldOrder:Int = -1; + public override function update(elapsed:Float) { + var __oldMoving:CharacterAnimButton = curMoving; + super.update(elapsed); + + if (curMoving != null && __oldMoving == null) + __movingAnimOldOrder = curMoving.ID; + + if (curMoving == null && __oldMoving != null) + if (__movingAnimOldOrder != __oldMoving.ID) + CharacterEditor.undos.addToUndo(CAnimEditOrder(__movingAnimOldOrder, __oldMoving.ID)); + + + animsList = [for (button in buttons) button.anim]; + character.ghosts = ghosts; + } + + public function buildAnimDisplay(name:String, anim:AnimData) @:privateAccess { + if (character.animateAtlas == null) { + var anim:FlxAnimation = character.animation._animations[anim.name]; + if (anim == null || anim.frames.length <= 0) return; + + var frameIndex:Int = anim.frames.getDefault([0])[0]; + var frame:FlxFrame = displayWindowSprite.frames.frames[frameIndex]; + + var frameRect:Rectangle = new Rectangle(frame.offset.x, frame.offset.y, frame.sourceSize.x, frame.sourceSize.y); + var animBounds:Rectangle = displayWindowGraphic != null ? displayWindowGraphic.bitmap.bounds(frameRect) : frameRect; + + displayAnimsFramesList.set(name, {frame: anim.frames.getDefault([0])[0], scale: 104/Math.max(animBounds.width, animBounds.height), animBounds: animBounds}); + } else { + character.storeAtlasState(); + + /* + character.animateAtlas.anim.play(anim.name, true, false, 0); + character.animateAtlas.anim.stop(); + + var animBounds:Rectangle = MatrixUtil.getBounds(character).copyToFlash(); + displayAnimsFramesList.set(name, {frame: anim.anim, scale: 104/animBounds.height, animBounds: animBounds}); + */ + + character.restoreAtlasState(); + } + } + + public function removeAnimDisplay(name:String) + displayAnimsFramesList.remove(name); + + public function deleteAnimation(button:CharacterAnimButton, addToUndo:Bool = true) { + if (buttons.members.length <= 1) return; + if (character.getAnimName() == button.anim) + @:privateAccess CharacterEditor.instance._animation_down(null); + + character.removeAnimation(button.anim); + if (character.animOffsets.exists(button.anim)) character.animOffsets.remove(button.anim); + if (character.animDatas.exists(button.anim)) character.animDatas.remove(button.anim); + + if (addToUndo) CharacterEditor.undos.addToUndo(CAnimDelete(button.ID, button.data)); + remove(button); button.destroy(); + } + + public function generateAnimation() { + var animName:String = "New Anim"; + var animNames:Array<String> = character.getNameList(); + + var newAnimCount:Int = 0; + while (animNames.indexOf(animName) != -1) { + newAnimCount++; + animName = 'New Anim - $newAnimCount'; + } + + if (__autoCompleteAnims.length <= 0) + setAnimAutoComplete(CoolUtil.getAnimsListFromSprite(character)); + + var animData:AnimData = { + name: animName, + anim: __autoCompleteAnims[0], + fps: 24, loop: false, + x: 0, y: 0, + indices: [], + animType: NONE, + }; + addAnimation(animData); + } + + public function addAnimation(animData:AnimData, animID:Int = -1, addToUndo:Bool = true) @:privateAccess { + XMLUtil.addAnimToSprite(character, animData); + + var newButton:CharacterAnimButton = new CharacterAnimButton(0, 0, animData, this); + newButton.alpha = 0.25; animButtons.set(animData.name, newButton); + newButton.animTextBox.suggestItems = __autoCompleteAnims; + + if (animID == -1) add(newButton); + else insert(newButton, animID); + + if (newButton.valid) + buildAnimDisplay(animData.name, animData); + + if (addToUndo) + CharacterEditor.undos.addToUndo(CAnimCreate(newButton.ID, newButton.data)); + + var nextButtonY:Float = 0; + for (buttonID in 0...newButton.ID) + nextButtonY += buttons.members[buttonID].bHeight + buttonOffset.y; + nextscrollY = nextButtonY; + } + + @:noCompletion var __autoCompleteAnims:Array<String> = []; + public inline function setAnimAutoComplete(anims:Array<String>) { + __autoCompleteAnims = anims.copy(); + for (button in buttons) + button.animTextBox.suggestItems = __autoCompleteAnims; + } + + public function findValid():Null<String> { + for (button in buttons) if (button.valid) return button.anim; + return null; + } +} \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterCreationScreen.hx b/source/funkin/editors/character/CharacterCreationScreen.hx new file mode 100644 index 00000000..5bb5062e --- /dev/null +++ b/source/funkin/editors/character/CharacterCreationScreen.hx @@ -0,0 +1,149 @@ +package funkin.editors.character; + +import haxe.xml.Printer; +import funkin.game.Character; +import funkin.editors.ui.UIImageExplorer.ImageSaveData; +import flixel.text.FlxText.FlxTextFormat; +import flixel.text.FlxText.FlxTextFormatMarkerPair; + +class CharacterCreationScreen extends UISubstateWindow { + private var onSave:(String, ImageSaveData, Xml)-> Void = null; + + public var characterNameTextBox:UITextBox; + public var imageExplorer:UIImageExplorer; + + public var saveButton:UIButton; + public var closeButton:UIButton; + + public function new(?onSave:(String, ImageSaveData, Xml)->Void) { + super(); + if (onSave != null) this.onSave = onSave; + } + + public override function create() { + winTitle = "Creating New Character"; + + winWidth = 360; + winHeight = 520; + + super.create(); + + function addLabelOn(ui:UISprite, text:String):UIText { + var text:UIText = new UIText(ui.x, ui.y - 24, 0, text); + ui.members.push(text); + return text; + } + + characterNameTextBox = new UITextBox(windowSpr.x + 20, windowSpr.y + 30 + 16 + 20, "character", 320); + characterNameTextBox.onChange = (_) -> {checkRequired();}; + add(characterNameTextBox); + addLabelOn(characterNameTextBox, "Character File Name").applyMarkup( + "Character File Name $* Required$", + [new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]); + + imageExplorer = new UIImageExplorer(characterNameTextBox.x, characterNameTextBox.y + 30 + 16 + 20, null, 320, 58, (_, _) -> {onLoadImage();}, "images/characters"); + add(imageExplorer); + addLabelOn(imageExplorer, "Character Image File").applyMarkup( + "Character Image File $* Required$", + [new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]); + imageExplorer.maxSize.y -= 100; + + saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() { + close(); + createCharacter(); + }, 125); + add(saveButton); + + closeButton = new UIButton(saveButton.x - 20 - saveButton.bWidth, saveButton.y, "Cancel", function() { + close(); + }, 125); + add(closeButton); + closeButton.color = 0xFFFF0000; + + onLoadImage(); + } + + public function onLoadImage() { + refreshWindowSize(); + checkRequired(); + } + + public function refreshWindowSize() { + if (imageExplorer == null) return; + windowSpr.bWidth = 20 + imageExplorer.bWidth + 20; + windowSpr.bHeight = 30 + 16 + 20 + 32 + 30 + 10 + imageExplorer.bHeight + 14 + saveButton.bHeight + 14; + + saveButton.x = windowSpr.x + windowSpr.bWidth - 20 - saveButton.bWidth; + closeButton.x = saveButton.x - 20 - closeButton.bWidth; + closeButton.y = saveButton.y = imageExplorer.y + imageExplorer.bHeight + 14; + } + + public function checkRequired() { + saveButton.selectable = characterNameTextBox.label.text.length > 0 && !CoolUtil.isMapEmpty(imageExplorer.imageFiles) && (imageExplorer.animationList.length > 0); + } + + public function createCharacter() { + var imageSaveData:ImageSaveData = imageExplorer.getSaveData(); + + var xml:Xml = Xml.createElement("character"); + xml.attributeOrder = Character.characterProperties.copy(); + + xml.set("sprite", '${imageSaveData.directory.length > 0 ? '${imageSaveData.directory}/' : ""}' + imageSaveData.imageName); + + // Look for animations >:D + var animationList:Array<String> = imageExplorer.animationList.copy(); + animationList.sort((a, b) -> { + var lengthCompare = a.length - b.length; + if (lengthCompare != 0) return lengthCompare; + + // Miss animations don't show up as regular if they are the same length as regular >:D + var aIsMiss = a.toLowerCase().contains("miss"); + var bIsMiss = b.toLowerCase().contains("miss"); + return aIsMiss == bIsMiss ? 0 : (aIsMiss ? 1 : -1); + }); + + var animationsFound:Map<String, String> = [ + "singLEFT" => null, + "singRIGHT" => null, + "singUP" => null, + "singDOWN" => null, + "idle" => null + ]; + + for (anim => found in animationsFound) { + var animToLookFor:String = StringTools.replace(anim, "sing", "").toLowerCase(); + for (imageAnim in animationList) + if (imageAnim.toLowerCase().contains(animToLookFor)) { + animationsFound.set(anim, imageAnim); + animationList.remove(imageAnim); + break; + } + } + + // Add said animations >:D + for (anim => found in animationsFound) { + var animXml:Xml = Xml.createElement('anim'); + animXml.attributeOrder = Character.characterAnimProperties; + + animXml.set("name", anim); + animXml.set("anim", found.getDefault(animationList[0])); + + xml.addChild(animXml); + } + + // Do the rest only if not atlas (until we make it animations and not symbols >:D) + if (!imageSaveData.isAtlas) { + for (imageAnim in animationList) { + var animXml:Xml = Xml.createElement('anim'); + animXml.attributeOrder = Character.characterAnimProperties; + + animXml.set("name", imageAnim); + animXml.set("anim", imageAnim); + + xml.addChild(animXml); + } + } + + if (onSave != null) onSave(characterNameTextBox.label.text, imageSaveData, xml); + } +} \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterEditor.hx b/source/funkin/editors/character/CharacterEditor.hx index f9cb45d3..74e59c04 100644 --- a/source/funkin/editors/character/CharacterEditor.hx +++ b/source/funkin/editors/character/CharacterEditor.hx @@ -1,45 +1,64 @@ package funkin.editors.character; +import funkin.editors.character.CharacterInfoScreen.CharacterExtraInfo; +import funkin.editors.extra.AxisGizmo; +import flixel.math.FlxRect; +import funkin.editors.stage.StageEditor; +import funkin.game.Stage; +import funkin.editors.ui.UITopMenu.UITopMenuButton; +import funkin.editors.extra.CameraHoverDummy; +import openfl.display.BitmapData; import flixel.math.FlxPoint; import funkin.backend.system.framerate.Framerate; import funkin.backend.utils.XMLUtil.AnimData; import funkin.editors.ui.UIContextMenu.UIContextMenuOption; +import funkin.editors.ui.UIContextMenu.UIContextMenuOptionSpr; import funkin.game.Character; import haxe.xml.Access; import haxe.xml.Printer; +import funkin.editors.ui.UIImageExplorer.ImageSaveData; +import sys.FileSystem; +import sys.io.File; class CharacterEditor extends UIState { static var __character:String; - public var character:Character; - - public var ghosts:CharacterGhostsHandler; + public var character:CharacterGhost; public static var instance(get, never):CharacterEditor; private static inline function get_instance() return FlxG.state is CharacterEditor ? cast FlxG.state : null; - /** - * CHARACTER UI STUFF - */ - public var characterBG:FunkinSprite; public var topMenu:Array<UIContextMenuOption>; - public var topMenuSpr:UITopMenu; + @:noCompletion private var animationIndex:Int = 5; + @:noCompletion private var stageIndex:Int = 3; + public var topMenuSpr:UITopMenu; + // public var dragOffsetsCheckbox:UICheckbox; + // public var lockCameraCheckbox:UICheckbox; + + public var axisGizmo:AxisGizmo; + public var characterGizmo:CharacterGizmos; public var uiGroup:FlxTypedGroup<FlxSprite> = new FlxTypedGroup<FlxSprite>(); - // WINDOWS + public var cameraHoverDummy:CameraHoverDummy; + public var characterPropertiesWindow:CharacterPropertiesWindow; - public var characterAnimsWindow:UIButtonList<CharacterAnimButtons>; + public var characterAnimsWindow:CharacterAnimsWindow; - // camera for the character itself so that it can be unzoomed/zoomed in again public var charCamera:FlxCamera; - // camera for the animations list - public var animsCamera:FlxCamera; - // camera for the ui + public var gizmosCamera:FlxCamera; public var uiCamera:FlxCamera; - public var undos:UndoList<CharacterChange> = new UndoList<CharacterChange>(); + public var animationText:UIText; + + public var currentStage:String = null; + public var stage:Stage = null; + + public var stageSprites:Array<FlxBasic> = []; + public var stagePosition:String = null; + + public static var undos:UndoList<CharacterEditorChange>; public function new(character:String) { super(); @@ -49,6 +68,8 @@ class CharacterEditor extends UIState { public override function create() { super.create(); + undos = new UndoList<CharacterEditorChange>(); + WindowUtils.suffix = " (Character Editor)"; SaveWarning.selectionClass = CharacterSelection; SaveWarning.saveFunc = () -> {_file_save(null);}; @@ -82,6 +103,17 @@ class CharacterEditor extends UIState { { label: "Edit", childs: [ + { + label: "Copy Offset", + keybind: [CONTROL, C], + onSelect: _edit_copy_offset + }, + { + label: "Paste Offset", + keybind: [CONTROL, V], + onSelect: _edit_paste_offset + }, + null, { label: "Undo", keybind: [CONTROL, Z], @@ -91,44 +123,19 @@ class CharacterEditor extends UIState { label: "Redo", keybinds: [[CONTROL, Y], [CONTROL, SHIFT, Z]], onSelect: _edit_redo - } - ] - }, - { - label: "Character", - childs: [ - { - label: "New Animation", - keybind: [CONTROL, N], - onSelect: _char_add_anim, - }, - { - label: "Edit Animation", - onSelect: _char_edit_anim, - }, - { - label: "Delete Animation", - keybind: [DELETE], - onSelect: _char_remove_anim, }, null, { - label: "Edit Info", - onSelect: _char_edit_info, - } - ] - }, - { - label: "Playback", - childs: [ - { - label: "Play Animation", - keybind: [SPACE], - onSelect: _playback_play_anim, + label: "Edit information", + color: 0xFF959829, icon: 4, + onCreate: function (button:UIContextMenuOptionSpr) {button.label.offset.x = button.icon.offset.x = -2;}, + onSelect: _edit_info }, { - label: "Stop Animation", - onSelect: _playback_stop_anim + label: "Edit sprite", + color: 0xFF959829, icon: 4, + onCreate: function (button:UIContextMenuOptionSpr) {button.label.offset.x = button.icon.offset.x = -2;}, + onSelect: _edit_sprite } ] }, @@ -177,6 +184,11 @@ class CharacterEditor extends UIState { onSelect: _offsets_extra_right, }, null, + { + label: "Drag Offsets With Mouse?", + onSelect: _offsets_drag_offsets_mouse, + icon: Options.characterDragging ? 1 : 0 + }, { label: "Clear Offsets", keybind: [CONTROL, R], @@ -184,6 +196,10 @@ class CharacterEditor extends UIState { } ] }, + { + label: "Stage", + childs: buildStagesUI() + }, { label: "View", childs: [ @@ -202,56 +218,113 @@ class CharacterEditor extends UIState { keybind: [CONTROL, NUMPADZERO], onSelect: _view_zoomreset }, - + null, + { + label: "Character Hitbox?", + onSelect: _view_character_show_hitbox, + icon: Options.characterHitbox ? 1 : 0 + }, + { + label: "Character Camera?", + onSelect: _view_character_show_camera, + icon: Options.characterCamera ? 1 : 0 + }, + { + label: "XY Axis?", + onSelect: _view_character_show_axis, + icon: Options.characterAxis ? 1 : 0 + } + ] + }, + { + label: "Animation >", + childs: [ + { + label: "Play Animation", + keybind: [SPACE], + onSelect: _animation_play, + }, + { + label: "Stop Animation", + onSelect: _animation_stop + }, + null, + { + label: "Change Animation ↑", + keybind: [W], + onSelect: _animation_up + }, + { + label: "Change Animation ↓", + keybind: [S], + onSelect: _animation_down + }, + null, + { + label: "Play Animation On Offset?", + onSelect: _animation_toggle_anim_playing_offsets, + icon: Options.playAnimOnOffset ? 1 : 0 + }, ] }, ]; charCamera = FlxG.camera; + gizmosCamera = new FlxCamera(); + gizmosCamera.bgColor = 0; + FlxG.cameras.add(gizmosCamera, false); + + axisGizmo = new AxisGizmo(); + axisGizmo.cameras = [gizmosCamera]; + add(axisGizmo); + + characterGizmo = new CharacterGizmos(); + characterGizmo.boxGizmo = Options.characterHitbox; + characterGizmo.cameraGizmo = Options.characterCamera; + characterGizmo.cameras = [gizmosCamera]; + add(characterGizmo); + uiCamera = new FlxCamera(); uiCamera.bgColor = 0; - animsCamera = new FlxCamera(800-23,140+23+30+46,450+23,511-24-30); - animsCamera.bgColor = 0; - - characterBG = new FunkinSprite(0, 0, Paths.image('editors/character/WOAH')); - characterBG.cameras = [charCamera]; - characterBG.screenCenter(); - characterBG.scale.set(FlxG.width/characterBG.width, FlxG.height/characterBG.height); - characterBG.scrollFactor.set(); - add(characterBG); FlxG.cameras.add(uiCamera); - FlxG.cameras.add(animsCamera); - character = new Character(0,0, __character, false, false); + character = new CharacterGhost(0,0, __character, false, false); character.debugMode = true; character.cameras = [charCamera]; - ghosts = new CharacterGhostsHandler(character); - ghosts.cameras = [charCamera]; + characterGizmo.character = character; + + changeCharacterIsPlayer(character.playerOffsets); - add(ghosts); add(character); - topMenuSpr = new UITopMenu(topMenu); - topMenuSpr.cameras = uiGroup.cameras = [uiCamera]; + uiGroup.cameras = [uiCamera]; + add(cameraHoverDummy = new CameraHoverDummy(uiGroup, FlxPoint.weak(0, 0))); - characterPropertiesWindow = new CharacterPropertiesWindow(); + characterPropertiesWindow = new CharacterPropertiesWindow((FlxG.width-(440)-16) - (((500-16)-(440))/2), 23+12+10, character); uiGroup.add(characterPropertiesWindow); - characterAnimsWindow = new UIButtonList<CharacterAnimButtons>(777, 209, 473, 488, "Character Animations", FlxPoint.get(429, 32)); - characterAnimsWindow.addButton.callback = function() CharacterEditor.instance.createAnimWithUI(); - var animOrder = character.getAnimOrder(); - for (i=>anim in animOrder) - characterAnimsWindow.add(new CharacterAnimButtons(0,0, anim, character.getAnimOffset(anim))); - uiGroup.add(characterAnimsWindow); + topMenuSpr = new UITopMenu(topMenu); + uiGroup.add(topMenuSpr); - playAnimation(animOrder[0]); + animationText = new UIText(0, 0, 0, ""); + uiGroup.add(animationText); + + characterAnimsWindow = new CharacterAnimsWindow((FlxG.width-(500-16)-16), characterPropertiesWindow.y+224+16, character); + uiGroup.add(characterPropertiesWindow.animsWindow = characterAnimsWindow); - add(topMenuSpr); add(uiGroup); + playAnimation(character.getAnimOrder()[0]); + changeStage("stage"); + + if (Options.editorsResizable) + UIState.setResolutionAware(); + + _view_focus_character(null); + if(Framerate.isLoaded) { Framerate.fpsCounter.alpha = 0.4; Framerate.memoryCounter.alpha = 0.4; @@ -262,6 +335,10 @@ class CharacterEditor extends UIState { } override function destroy() { + _point.put(); + draggingOffset.put(); + clipboard.put(); + super.destroy(); if(Framerate.isLoaded) { Framerate.fpsCounter.alpha = 1; @@ -270,61 +347,104 @@ class CharacterEditor extends UIState { } } - //private var movingCam:Bool = false; - //private var camDragSpeed:Float = 1.2; - - private var nextScroll:FlxPoint = FlxPoint.get(0,0); + var _point:FlxPoint = new FlxPoint(); + var _nextScroll:FlxPoint = FlxPoint.get(0,0); + var _cameraZoomMulti:Float = 1; + public var draggingCharacter:Bool = false; + public var draggingOffset:FlxPoint = new FlxPoint(); public override function update(elapsed:Float) { - super.update(elapsed); + if(FlxG.keys.justPressed.ANY && currentFocus == null) + UIUtil.processShortcuts(topMenu); - if (true) { - if(FlxG.keys.justPressed.ANY) - UIUtil.processShortcuts(topMenu); - } - - if (character != null) - characterPropertiesWindow.characterInfo.text = '${character.getNameList().length} Animations\nFlipped: ${character.flipX}\nSprite: ${character.sprite}\nAnim: ${character.getAnimName()}\nOffset: (${character.frameOffset.x}, ${character.frameOffset.y})'; - - if (!(characterPropertiesWindow.hovered || characterAnimsWindow.hovered) && !characterAnimsWindow.dragging) { + if (cameraHoverDummy.hovered && !draggingCharacter) { if (FlxG.mouse.wheel != 0) { zoom += 0.25 * FlxG.mouse.wheel; __camZoom = Math.pow(2, zoom); } if (FlxG.mouse.justReleasedRight) { + var mousePos = FlxG.mouse.getScreenPosition(uiCamera); closeCurrentContextMenu(); - openContextMenu(topMenu[2].childs); + openContextMenu(topMenu[2].childs, null, mousePos.x, mousePos.y); } - if (FlxG.mouse.pressed) { - nextScroll.set(nextScroll.x - FlxG.mouse.deltaScreenX, nextScroll.y - FlxG.mouse.deltaScreenY); - currentCursor = HAND; + + if (FlxG.mouse.pressed && !FlxG.mouse.justPressed) { + _nextScroll.set(_nextScroll.x - FlxG.mouse.deltaScreenX, _nextScroll.y - FlxG.mouse.deltaScreenY); + cameraHoverDummy.cursor = HAND; } else - currentCursor = ARROW; + cameraHoverDummy.cursor = ARROW; } else if (!FlxG.mouse.pressed) currentCursor = ARROW; charCamera.scroll.set( - lerp(charCamera.scroll.x, nextScroll.x, 0.35), - lerp(charCamera.scroll.y, nextScroll.y, 0.35) + lerp(charCamera.scroll.x, _nextScroll.x, 0.35), + lerp(charCamera.scroll.y, _nextScroll.y, 0.35) ); - charCamera.zoom = lerp(charCamera.zoom, __camZoom, 0.125); + charCamera.zoom = lerp(charCamera.zoom, __camZoom*_cameraZoomMulti, 0.125); - characterBG.scale.set(FlxG.width/characterBG.width, FlxG.height/characterBG.height); - characterBG.scale.set(characterBG.scale.x / charCamera.zoom, characterBG.scale.y / charCamera.zoom); + if (topMenuSpr.members[animationIndex] != null) { + var animationTopButton:UITopMenuButton = cast topMenuSpr.members[animationIndex]; + animationText.x = animationTopButton.x + animationTopButton.bWidth + 6; + animationText.y = Std.int((animationTopButton.bHeight - animationText.height) / 2); + } + animationText.text = '"${characterFakeAnim}"'; + + StageEditor.calcSpriteBounds(character); + + if (Options.characterDragging) + handleMouseOffsets(); WindowUtils.prefix = undos.unsaved ? Flags.UNDO_PREFIX : ""; SaveWarning.showWarning = undos.unsaved; + + super.update(elapsed); + } + + inline function handleMouseOffsets() { + if (draggingCharacter) { + cameraHoverDummy.cursor = DRAG; + + if (FlxG.mouse.justReleased) { + draggingOffset.x /= character.scale.x; + draggingOffset.y /= character.scale.y; + + _change_offset((draggingOffset.x * (character.isFlippedOffsets() ? -1 : 1)), draggingOffset.y); + + draggingOffset.set(0, 0); draggingCharacter = false; + character.extraOffset = draggingOffset; + } else { + draggingOffset.x += FlxG.mouse.deltaScreenX; draggingOffset.y += FlxG.mouse.deltaScreenY; + character.extraOffset = draggingOffset; + } + } else { + var point = FlxG.mouse.getWorldPosition(charCamera, _point); + var bounds:FlxRect = cast character.extra.get(StageEditor.exID("bounds")); + if (bounds.containsPoint(point)) { + cameraHoverDummy.cursor = #if (mac) DRAG_OPEN; #else CLICK; #end + if (FlxG.mouse.justPressed) + draggingCharacter = true; + } + } + } + + public override function onResize(width:Int, height:Int) { + super.onResize(width, height); + if (!UIState.resolutionAware) return; + + if (width < FlxG.initialWidth || height < FlxG.initialHeight) { + width = FlxG.initialWidth; height = FlxG.initialHeight; + } + + characterPropertiesWindow.x = (FlxG.width-(440)-16) - (((500-16)-(440))/2); + characterAnimsWindow.x = (FlxG.width-(500-16)-16); + + characterAnimsWindow.bHeight = Std.int(FlxG.height-(characterPropertiesWindow.y+characterPropertiesWindow.bHeight)-(32)); } // TOP MENU OPTIONS #if REGION - function _file_exit(_) { - if (undos.unsaved) SaveWarning.triggerWarning(); - else FlxG.switchState(new CharacterSelection()); - } - function _file_new(_) { } @@ -334,10 +454,10 @@ class CharacterEditor extends UIState { '${Paths.getAssetsRoot()}/data/characters/${character.curCharacter}.xml', buildCharacter() ); - undos.save(); #else _file_saveas(_); #end + undos.save(); } function _file_saveas(_) { @@ -347,322 +467,466 @@ class CharacterEditor extends UIState { undos.save(); } + function _file_exit(_) { + if (undos.unsaved) SaveWarning.triggerWarning(); + else {undos = null; FlxG.switchState(new CharacterSelection());} + } + function buildCharacter():String { + if (character.extra.exists(StageEditor.exID("bounds"))) + character.extra.remove(StageEditor.exID("bounds")); + var charXML:Xml = character.buildXML([ for (button in characterAnimsWindow.buttons.members) button.anim ]); - // clean - if (charXML.exists("gameOverChar") && character.gameOverCharacter == Flags.DEFAULT_GAMEOVER_CHARACTER) charXML.remove("gameOverChar"); - if (charXML.exists("camx") && character.cameraOffset.x == 0) charXML.remove("camx"); - if (charXML.exists("camy") && character.cameraOffset.y == 0) charXML.remove("camy"); - if (charXML.exists("holdTime") && character.holdTime == 4) charXML.remove("holdTime"); - if (charXML.exists("flipX") && !character.flipX) charXML.remove("flipX"); - if (charXML.exists("scale") && character.scale.x == 1) charXML.remove("scale"); - if (charXML.exists("antialiasing") && character.antialiasing) charXML.remove("antialiasing"); - if (charXML.exists("sprite") && character.sprite == character.curCharacter) charXML.remove("sprite"); - if (charXML.exists("icon") && character.icon == character.curCharacter) charXML.remove("icon"); - - for (a in charXML.elements()) - if (a.nodeName == "anim") { - if (a.exists("x") && a.get("x") == "0") a.remove("x"); - if (a.exists("y") && a.get("y") == "0") a.remove("y"); - if (a.exists("fps") && a.get("fps") == "24") a.remove("fps"); - if (a.exists("loop") && a.get("loop") == "false") a.remove("loop"); - } - - var xmlThingYea:String = "<!DOCTYPE codename-engine-character>\n" + Printer.print(charXML, Options.editorPrettyPrint); - return Options.editorPrettyPrint ? xmlThingYea : xmlThingYea.replace("\n", ""); + return "<!DOCTYPE codename-engine-character>\n" + Printer.print(charXML, true); } - function _edit_undo(_) { - var undo = undos.undo(); - switch(undo) { - case null: - // do nothing - case CEditInfo(oldInfo, newInfo): - editInfo(oldInfo, false); - case CCreateAnim(animID, animData): - deleteAnim(animData.name, false); - case CEditAnim(name, oldData, animData): - editAnim(name, oldData, false); - case CDeleteAnim(animID, animData): - createAnim(animData, animID, false); - case CChangeOffset(name, change): - changeOffset(name, change * -1, false); - case CResetOffsets(oldOffsets): - for (anim => offsets in oldOffsets) { - character.animOffsets.set(anim, offsets.clone()); - ghosts.setOffsets(anim, offsets.clone()); + var clipboard:FlxPoint = FlxPoint.get(); + function _edit_copy_offset(_) { + clipboard.copyFrom(character.animOffsets[character.getAnimName()]); + } + function _edit_paste_offset(_) { + _set_offset(clipboard.x, clipboard.y); + } + + function _undo(undo:CharacterEditorChange) { + switch (undo) { + case null: // do nothing + case CCharEditPosition(oldPos, newPos): + characterPropertiesWindow.changePosition(oldPos.x, oldPos.y, false); + case CCharEditCamPosition(oldPos, newPos): + characterPropertiesWindow.changeCamPosition(oldPos.x, oldPos.y, false); + case CCharEditScale(oldScale, newScale): + characterPropertiesWindow.changeScale(oldScale, false); + case CCharEditFlipped(newFlipped): + characterPropertiesWindow.changeFlipX(!newFlipped, false); + case CCharEditAntialiasing(newAntialiasing): + characterPropertiesWindow.changeAntialiasing(!newAntialiasing, false); + case CCharEditDesignedAs(newIsPlayer): + changeCharacterDesginedAs(!newIsPlayer, false); + case CCharEditInfo(oldInfo, newInfo): + characterPropertiesWindow.editCharacterInfo(oldInfo, false); + case CCharEditSprite(fileID): + var cneisdPath:String = './.temp/__undo__${Type.getClassName(Type.getClass(FlxG.state))}__${fileID}.cneisd'; + if (FileSystem.exists(cneisdPath)) { + try { + var cneisdData:String = File.getContent(cneisdPath); + var imageSaveData:ImageSaveData = UIImageExplorer.deserializeSaveDataGlobal(cneisdData); + + UIImageExplorer.saveFilesGlobal(imageSaveData, '${Paths.getAssetsRoot()}/images/characters', () -> { + characterPropertiesWindow.changeSprite('${imageSaveData.directory.length > 0 ? '${imageSaveData.directory}/' : ""}' + imageSaveData.imageName); + }, false); + } catch (e) { + trace('ERROR COMPLETING UNDO: $e'); + }; } - - for (charButton in characterAnimsWindow.buttons.members) - charButton.updateInfo(charButton.anim, character.getAnimOffset(charButton.anim), ghosts.animGhosts[charButton.anim].visible); - - changeOffset(character.getAnimName(), FlxPoint.get(0, 0), false); // apply da new offsets + case CAnimCreate(animID, animData): + characterAnimsWindow.deleteAnimation(characterAnimsWindow.buttons.members[animID], false); + case CAnimDelete(animID, animData): + characterAnimsWindow.addAnimation(animData, animID, false); + playAnimation(animData.name); + case CAnimEditOrder(animID, newAnimID): + var button:CharacterAnimButton = characterAnimsWindow.buttons.members[newAnimID]; + characterAnimsWindow.buttons.members.remove(button); + characterAnimsWindow.buttons.members.insert(animID, button); + case CAnimEditName(animID, oldName, newName): + var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID]; + button.changeName(oldName, false); + case CAnimEditAnim(animID, oldAnim, newAnim): + var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID]; + button.changeAnim(oldAnim, false); + case CAnimEditOffset(animID, oldOffset, newOffset): + var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID]; + button.changeOffset(oldOffset.x, oldOffset.y, false); + case CAnimEditFPS(animID, oldFPS, newFPS): + var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID]; + button.changeFPS(oldFPS, false); + case CAnimEditLooping(animID, newLooping): + var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID]; + button.changeLooping(!newLooping, false); + case CAnimEditIndices(animID, oldIndicies, newIndicies): + var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID]; + button.changeIndicies(oldIndicies, false); + case CCharClearOffsets(oldOffsets): + for (anim => offset in oldOffsets) + if (characterAnimsWindow.animButtons[anim] != null) + characterAnimsWindow.animButtons[anim].changeOffset(offset.x, offset.y, false); } } - function _edit_redo(_) { - var redo = undos.redo(); - switch(redo) { - case null: - // do nothing - case CEditInfo(oldInfo, newInfo): - editInfo(newInfo, false); - case CCreateAnim(animID, animData): - createAnim(animData, animID, false); - case CEditAnim(name, oldData, animData): - editAnim(oldData.name, animData, false); - case CDeleteAnim(animID, animData): - deleteAnim(animData.name, false); - case CChangeOffset(name, change): - changeOffset(name, change, false); - case CResetOffsets(oldOffsets): + function _edit_undo(_) { + if (draggingCharacter) return; + _undo(undos.undo()); + } + + function _redo(redo:CharacterEditorChange) { + switch (redo) { + case null: // do nothing + case CCharEditPosition(oldPos, newPos): + characterPropertiesWindow.changePosition(newPos.x, newPos.y, false); + case CCharEditCamPosition(oldPos, newPos): + characterPropertiesWindow.changeCamPosition(newPos.x, newPos.y, false); + case CCharEditScale(oldScale, newScale): + characterPropertiesWindow.changeScale(newScale, false); + case CCharEditFlipped(newFlipped): + characterPropertiesWindow.changeFlipX(newFlipped, false); + case CCharEditAntialiasing(newAntialiasing): + characterPropertiesWindow.changeAntialiasing(newAntialiasing, false); + case CCharEditDesignedAs(newIsPlayer): + changeCharacterDesginedAs(newIsPlayer, false); + case CCharEditInfo(oldInfo, newInfo): + characterPropertiesWindow.editCharacterInfo(newInfo, false); + case CCharEditSprite(fileID): + var cneisdPath:String = './.temp/__redo__${Type.getClassName(Type.getClass(FlxG.state))}__${fileID}.cneisd'; + if (FileSystem.exists(cneisdPath)) { + try { + var cneisdData:String = File.getContent(cneisdPath); + var imageSaveData:ImageSaveData = UIImageExplorer.deserializeSaveDataGlobal(cneisdData); + + UIImageExplorer.saveFilesGlobal(imageSaveData, '${Paths.getAssetsRoot()}/images/characters', () -> { + characterPropertiesWindow.changeSprite('${imageSaveData.directory.length > 0 ? '${imageSaveData.directory}/' : ""}' + imageSaveData.imageName); + }, false); + } catch (e) { + trace('ERROR COMPLETING UNDO: $e'); + }; + } + case CAnimCreate(animID, animData): + characterAnimsWindow.addAnimation(animData, animID, false); + playAnimation(animData.name); + case CAnimDelete(animID, animData): + characterAnimsWindow.deleteAnimation(characterAnimsWindow.buttons.members[animID], false); + case CAnimEditOrder(animID, newAnimID): + var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID]; + characterAnimsWindow.buttons.members.remove(button); + characterAnimsWindow.buttons.members.insert(newAnimID, button); + case CAnimEditName(animID, oldName, newName): + var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID]; + button.changeName(newName, false); + case CAnimEditAnim(animID, oldAnim, newAnim): + var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID]; + button.changeAnim(newAnim, false); + case CAnimEditOffset(animID, oldOffset, newOffset): + var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID]; + button.changeOffset(newOffset.x, newOffset.y, false); + case CAnimEditFPS(animID, oldFPS, newFPS): + var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID]; + button.changeFPS(newFPS, false); + case CAnimEditLooping(animID, newLooping): + var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID]; + button.changeLooping(newLooping, false); + case CAnimEditIndices(animID, oldIndicies, newIndicies): + var button:CharacterAnimButton = characterAnimsWindow.buttons.members[animID]; + button.changeIndicies(newIndicies, false); + case CCharClearOffsets(oldOffsets): clearOffsets(false); } } - function _char_add_anim(_) { - createAnimWithUI(); + function _edit_redo(_) { + _redo(undos.redo()); } - function _char_edit_anim(_) { - editAnimWithUI(character.getAnimName()); + function _edit_info(_) + characterPropertiesWindow.editCharacterInfoUI(); + function _edit_sprite(_) {} + + function _offsets_left(_) _change_offset(-1, 0); + function _offsets_up(_) _change_offset(0, -1); + function _offsets_down(_) _change_offset(0, 1); + function _offsets_right(_) _change_offset(1, 0); + function _offsets_extra_left(_) _change_offset(-5, 0); + function _offsets_extra_up(_) _change_offset(0, -5); + function _offsets_extra_down(_) _change_offset(0, 5); + function _offsets_extra_right(_) _change_offset(5, 0); + + function _offsets_drag_offsets_mouse(t) { + t.icon = (Options.characterDragging = !Options.characterDragging) ? 1 : 0; } - function _char_remove_anim(_) { - deleteAnim(character.getAnimName()); - } - - function _char_edit_info(_) { - editInfoWithUI(); - } - - public function createAnimWithUI() { - FlxG.state.openSubState(new CharacterAnimScreen(null, (_) -> { - if (_ != null) createAnim(_); - })); - } - - public function editAnimWithUI(name:String) { - FlxG.state.openSubState(new CharacterAnimScreen(character.animDatas.get(name), (_) -> { - if (_ != null) editAnim(name, _); - })); - } - - public function createAnim(animData:AnimData, animID:Int = -1, addToUndo:Bool = true) { - XMLUtil.addAnimToSprite(character, animData); - ghosts.createGhost(animData.name); - var newButton = new CharacterAnimButtons(0, 0, animData.name, FlxPoint.get(animData.x,animData.y)); - if (animID == -1) characterAnimsWindow.add(newButton); - else characterAnimsWindow.insert(newButton, animID); - - playAnimation(animData.name); - - if (addToUndo) - undos.addToUndo(CCreateAnim(character.getNameList().length, animData)); - } - - public function editAnim(name:String, animData:AnimData, addToUndo:Bool = true) { - var oldAnimData:AnimData = character.animDatas.get(name); - var buttoner:CharacterAnimButtons = null; - for (button in characterAnimsWindow.buttons.members) - if (button.anim == name) buttoner = button; - - ghosts.removeGhost(name); - XMLUtil.addAnimToSprite(character, animData); - ghosts.createGhost(animData.name); - buttoner.updateInfo(animData.name, character.getAnimOffset(animData.name), ghosts.animGhosts[animData.name].visible); - - if (character.getAnimName() == animData.name) // update anim ifs its currently selected - playAnimation(animData.name); - - if (addToUndo) - undos.addToUndo(CEditAnim(animData.name, oldAnimData, animData)); - } - - public function deleteAnim(name:String, addToUndo:Bool = true) { - var animOrder:Array<String> = [ - for (button in characterAnimsWindow.buttons.members) - button.anim - ]; - - if (character.getNameList().length-1 == 0) - @:privateAccess character.animation._curAnim = null; - else - playAnimation(animOrder[Std.int(Math.abs(animOrder.indexOf(name)-1))]); - - for(button in characterAnimsWindow.buttons.members) - if(button.anim == name) characterAnimsWindow.remove(button); - - // undo shit blah blah - var oldID:Int = animOrder.indexOf(name); - var oldAnimData:AnimData = character.animDatas.get(name); - - ghosts.removeGhost(name); - character.removeAnimation(name); - if (character.animOffsets.exists(name)) character.animOffsets.remove(name); - if (character.animDatas.exists(name)) character.animDatas.remove(name); - - if (addToUndo) - undos.addToUndo(CDeleteAnim(oldID, oldAnimData)); - } - - public function editInfoWithUI() { - FlxG.state.openSubState(new CharacterInfoScreen(character, (_) -> { - if (_ != null) editInfo(_); - })); - } - - public function editInfo(newInfo:Xml, addToUndo:Bool = true) { - var lastAnim:String = character.getAnimName(); - - var oldInfo = character.buildXML(); - character.applyXML(new Access(newInfo)); - ghosts.updateInfos(newInfo); - - playAnimation(lastAnim); - - if (addToUndo) - undos.addToUndo(CEditInfo(oldInfo, newInfo)); - } - - public function ghostAnim(anim:String) { - var ghost:Character = ghosts.animGhosts.get(anim); - ghost.visible = !ghost.visible; - - var animButton:CharacterAnimButtons = null; - for (button in characterAnimsWindow.buttons.members) - if (button.anim == anim) animButton = button; - animButton.updateInfo(anim, character.getAnimOffset(anim), ghost.visible); - } - - function _playback_play_anim(_) { - if (character.getNameList().length != 0) - playAnimation(character.getAnimName()); - } - - function _playback_stop_anim(_) { - if (character.getNameList().length != 0) - character.stopAnimation(); - } - - public function playAnimation(anim:String) { - character.playAnim(anim, true); - - for(i in characterAnimsWindow.buttons.members) - i.alpha = i.anim == anim ? 1 : 0.25; - } - - function _offsets_left(_) { - changeOffset(character.getAnimName(), FlxPoint.get(!character.isFlippedOffsets() ? 1 : -1, 0)); - } - - function _offsets_up(_) { - changeOffset(character.getAnimName(), FlxPoint.get(0, 1)); - } - - function _offsets_down(_) { - changeOffset(character.getAnimName(), FlxPoint.get(0, -1)); - } - - function _offsets_right(_) { - changeOffset(character.getAnimName(), FlxPoint.get(!character.isFlippedOffsets() ? -1 : 1, 0)); - } - - function _offsets_extra_left(_) { - changeOffset(character.getAnimName(), FlxPoint.get(!character.isFlippedOffsets() ? 5 : -5, 0)); - } - - function _offsets_extra_up(_) { - changeOffset(character.getAnimName(), FlxPoint.get(0, 5)); - } - - function _offsets_extra_down(_) { - changeOffset(character.getAnimName(), FlxPoint.get(0, -5)); - } - - function _offsets_extra_right(_) { - changeOffset(character.getAnimName(), FlxPoint.get(!character.isFlippedOffsets() ? -5 : 5, 0)); - } - - function _offsets_clear(_) { - clearOffsets(); - } - - function changeOffset(anim:String, change:FlxPoint, addToUndo:Bool = true) { - if (character.getNameList().length == 0) return; - - var animData = character.animDatas.get(anim); - if (animData != null) { - animData.x += change.x; - animData.y += change.y; - } - - character.animOffsets.set(anim, character.getAnimOffset(anim) + change); - for (i in characterAnimsWindow.buttons.members) - if (i.anim == anim) - i.updateInfo(anim, character.getAnimOffset(anim), ghosts.animGhosts[anim].visible); - character.frameOffset.set(character.getAnimOffset(anim).x, character.getAnimOffset(anim).y); - - ghosts.updateOffsets(anim, change); - - if (addToUndo) - undos.addToUndo(CChangeOffset(anim, change)); - } + function _offsets_clear(_) clearOffsets(); function clearOffsets(addToUndo:Bool = true) { - if (character.getNameList().length == 0) return; - var oldOffsets:Map<String, FlxPoint> = [ for (anim => offsets in character.animOffsets) anim => offsets.clone() ]; - for (anim in character.getNameList()) { - character.animOffsets[anim].zero(); - for (i in characterAnimsWindow.buttons.members) - if (i.anim == anim) - i.updateInfo(anim, character.getAnimOffset(anim), ghosts.animGhosts[anim].visible); + + for (anim => button in characterAnimsWindow.animButtons) + button.changeOffset(0, 0, false); + + if (addToUndo) undos.addToUndo(CCharClearOffsets(oldOffsets)); + } + + function _change_offset(x:Float, y:Float) { + if (characterFakeAnim == null) return; + _set_offset( + character.animOffsets[characterFakeAnim].x - x, + character.animOffsets[characterFakeAnim].y - y + ); + } + + function _set_offset(x:Float, y:Float) { + if (characterAnimsWindow.animButtons[characterFakeAnim] == null || !characterAnimsWindow.animButtons[characterFakeAnim].valid) return; + characterAnimsWindow.animButtons.get(characterFakeAnim).changeOffset( + FlxMath.roundDecimal(x, 2), FlxMath.roundDecimal(y, 2) + ); + } + + function buildStagesUI() { + var stageTopButton:UITopMenuButton = topMenuSpr == null ? null : cast topMenuSpr.members[stageIndex]; + var newChilds:Array<UIContextMenuOption> = []; + + var stageFileList = Stage.getList(true); + if (stageFileList.length == 0) stageFileList = Stage.getList(false); + + for (stage in stageFileList) + newChilds.push({ + label: 'Use "$stage"?', + icon: currentStage == stage ? 1 : 0, + onSelect: (_) -> {changeStage(stage);} + }); + + newChilds.push(null); + newChilds.push({ + label: "Use No Stage?", + icon: currentStage == null ? 1 : 0, + onSelect: (_) -> {changeStage(null);} + }); + + if (stageTopButton != null) stageTopButton.contextMenu = newChilds; + return newChilds; + } + + function changeStage(__stage:String) { + if (stage != null) { + if (stage.characterPoses.exists(stagePosition)) + stage.characterPoses[stagePosition].revertCharacter(character); + + for (sprite in stageSprites) { + remove(sprite); + sprite.destroy(); + } + stageSprites.clear(); + + stage.destroy(); + stage = null; + } + remove(character); + + if (__stage == null) { + updateStagePositions([]); + changeStagePosition("NONE"); + + add(character); + } else { + stage = new Stage(__stage, this, false); + stage.onAddSprite = (sprite:FlxBasic) -> { + sprite.cameras = [charCamera]; + stageSprites.push(sprite); + }; + + stage.loadXml(stage.stageXML, true); + add(stage); + + stagePosition = character.playerOffsets || character.isPlayer ? "BOYFRIEND" : "DAD"; + updateStagePositions([ + for (pose in stage.characterPoses.keys()) + pose.toUpperCase() + ]); + + changeStagePosition(stagePosition); + changeCharacterDesginedAs(stagePosition.toUpperCase() == "BOYFRIEND", false); } - changeOffset(character.getAnimName(), FlxPoint.get(), false); - ghosts.clearOffsets(); - - if (addToUndo) - undos.addToUndo(CResetOffsets(oldOffsets)); + currentStage = __stage; + buildStagesUI(); } - var zoom(default, set):Float = 0; - var __camZoom(default, set):Float = 1; - function set_zoom(val:Float) { - return zoom = FlxMath.bound(val, -3.5, 1.75); // makes zooming not lag behind when continuing scrolling + function updateStagePositions(stagePositions:Array<String>) @:privateAccess { + if (stage != null && stagePositions.length > 0) { + characterPropertiesWindow.testAsDropDown.items = + UIDropDown.getItems(characterPropertiesWindow.testAsDropDown.options = stagePositions); + } else + characterPropertiesWindow.testAsDropDown.items = + UIDropDown.getItems(characterPropertiesWindow.testAsDropDown.options = ["NONE"]); } - function set___camZoom(val:Float) { - return __camZoom = FlxMath.bound(val, 0.1, 3); + + public function changeStagePosition(position:String) { + if (stage != null && position != "NONE") { + if (stage.characterPoses.exists(stagePosition)) + stage.characterPoses[stagePosition].revertCharacter(character); + + stagePosition = position.toLowerCase(); + remove(character); + + if (stage.characterPoses.exists(stagePosition)) + stage.applyCharStuff(character, stagePosition, 0); + _animation_play(null); + } else + stagePosition = position.toLowerCase(); + + characterPropertiesWindow.testAsDropDown.index = characterPropertiesWindow.testAsDropDown.options.indexOf(stagePosition.toUpperCase()); + characterPropertiesWindow.testAsDropDown.label.text = stagePosition.toUpperCase(); + } + + public function changeCharacterDesginedAs(player:Bool, addToUndo:Bool = true) @:privateAccess { + if (stage == null) { + changeCharacterIsPlayer(player); + return; + } + + if (stage.characterPoses.exists(stagePosition)) + stage.characterPoses[stagePosition].revertCharacter(character); + + changeCharacterIsPlayer(player); + updateCharacterStagePosition(); + _animation_play(null); + + characterPropertiesWindow.designedAsDropDown.index = characterPropertiesWindow.designedAsDropDown.options.indexOf(player ? "BOYFRIEND" : "DAD"); + characterPropertiesWindow.designedAsDropDown.label.text = player ? "BOYFRIEND" : "DAD"; + + if (addToUndo) CharacterEditor.undos.addToUndo(CCharEditDesignedAs(player)); + } + + public inline function updateCharacterStagePosition() { + remove(character); + if (stage.characterPoses.exists(stagePosition)) + stage.applyCharStuff(character, stagePosition, 0); + } + + public function changeCharacterIsPlayer(player:Bool) @:privateAccess { + if (character.__swappedLeftRightAnims) + character.swapLeftRightAnimations(); + if (character.isPlayer) + character.flipX = !character.__baseFlipped; + + character.isPlayer = player; + character.fixChar(false, false); } function _view_zoomin(_) { zoom += 0.25; __camZoom = Math.pow(2, zoom); } + function _view_zoomout(_) { zoom -= 0.25; __camZoom = Math.pow(2, zoom); } + function _view_zoomreset(_) { zoom = 0; __camZoom = Math.pow(2, zoom); } + + function _view_character_show_hitbox(t) { + t.icon = (Options.characterHitbox = !Options.characterHitbox) ? 1 : 0; + characterGizmo.boxGizmo = Options.characterHitbox; + } + + function _view_character_show_camera(t) { + t.icon = (Options.characterCamera = !Options.characterCamera) ? 1 : 0; + characterGizmo.cameraGizmo = Options.characterCamera; + } + + function _view_character_show_axis(t) { + t.icon = (Options.characterAxis = !Options.characterAxis) ? 1 : 0; + axisGizmo.visible = Options.characterAxis; + } + + function _view_focus_character(_) { + if (character == null) return; + + var characterMidpoint:FlxPoint = character.getMidpoint(); + characterMidpoint.x -= (FlxG.width/2)-character.globalOffset.x; + characterMidpoint.y -= (FlxG.height/2)-character.globalOffset.y; + _nextScroll.set(characterMidpoint.x, characterMidpoint.y); + characterMidpoint.put(); + } + + var zoom(default, set):Float = 0; + var __camZoom(default, set):Float = 1; + function set_zoom(val:Float) { + return zoom = CoolUtil.bound(val, -3.5, 1.75); // makes zooming not lag behind when continuing scrolling + } + function set___camZoom(val:Float) { + return __camZoom = CoolUtil.bound(val, 0.1, 3); + } + + function _animation_play(_) { + if (character.getNameList().length != 0) + playAnimation(characterFakeAnim); + } + + function _animation_stop(_) { + if (character.getNameList().length != 0) + character.stopAnimation(); + } + + function _animation_up(_) + playAnimation( + characterAnimsWindow.animsList[ + FlxMath.wrap( + characterAnimsWindow.animsList.indexOf(characterFakeAnim) - 1, + 0, characterAnimsWindow.animsList.length-1 + )] + ); + function _animation_down(_) + playAnimation( + characterAnimsWindow.animsList[FlxMath.wrap( + characterAnimsWindow.animsList.indexOf(characterFakeAnim) + 1, + 0, characterAnimsWindow.animsList.length-1 + )] + ); + + function _animation_toggle_anim_playing_offsets(t) { + t.icon = (Options.playAnimOnOffset = !Options.playAnimOnOffset) ? 1 : 0; + } + + // The animation thats playing regardless if its valid or not + public var characterFakeAnim:String = ""; + public function playAnimation(anim:String) @:privateAccess { + characterFakeAnim = anim; + if (characterAnimsWindow.animButtons[anim] != null && characterAnimsWindow.animButtons[anim].valid) { + character.playAnim(anim, true); + character.colorTransform.__identity(); + } else { + var validAnimation:String = characterAnimsWindow.findValid(); + if (validAnimation != null) character.playAnim(validAnimation, true); + _animation_stop(null); + character.colorTransform.color = 0xFFEF0202; + } + + for(i in characterAnimsWindow.buttons.members) + i.alpha = i.anim == anim ? 1 : 0.25; + } #end } -enum CharacterChange { - CEditInfo(oldInfo:Xml, newInfo:Xml); - CCreateAnim(animID:Int, animData:AnimData); - CEditAnim(name:String, oldData:AnimData, animData:AnimData); - CDeleteAnim(animID:Int, animData:AnimData); - CChangeOffset(name:String, change:FlxPoint); - CResetOffsets(oldOffsets:Map<String, FlxPoint>); -} +enum CharacterEditorChange { + CCharEditPosition(oldPos:FlxPoint, newPos:FlxPoint); + CCharEditCamPosition(oldPos:FlxPoint, newPos:FlxPoint); + CCharEditScale(oldScale:Float, newScale:Float); + CCharEditFlipped(newFlipped:Bool); + CCharEditAntialiasing(newAntialiasing:Bool); + CCharEditDesignedAs(newIsPlayer:Bool); + + CCharEditInfo(oldInfo:CharacterExtraInfo, newInfo:CharacterExtraInfo); + CCharEditSprite(fileID:Int); + + CAnimCreate(animID:Int, animData:AnimData); + CAnimDelete(animID:Int, animData:AnimData); + CAnimEditOrder(animID:Int, newAnimID:Int); + + CAnimEditName(animID:Int, oldName:String, newName:String); + CAnimEditAnim(animID:Int, oldAnim:String, newAnim:String); + CAnimEditOffset(animID:Int, oldOffset:FlxPoint, newOffset:FlxPoint); + CAnimEditFPS(animID:Int, oldFPS:Float, newFPS:Float); + CAnimEditLooping(animID:Int, newLooping:Bool); + CAnimEditIndices(animID:Int, oldIndicies:Array<Int>, newIndicies:Array<Int>); + + CCharClearOffsets(oldOffsets:Map<String, FlxPoint>); +} \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterGhost.hx b/source/funkin/editors/character/CharacterGhost.hx new file mode 100644 index 00000000..81c7c5dd --- /dev/null +++ b/source/funkin/editors/character/CharacterGhost.hx @@ -0,0 +1,91 @@ +package funkin.editors.character; + +import funkin.backend.system.FakeCamera.FakeCallCamera; +import openfl.geom.ColorTransform; +import flixel.animation.FlxAnimation; +import funkin.game.Character; + +typedef AtlasState = { + var oldAnim:String; + var oldFrame:Int; + var oldTick:Float; + var oldPlaying:Bool; +}; + +class CharacterGhost extends Character { + public var ghosts:Array<String> = []; + public override function draw() @:privateAccess { + ghostDraw = FakeCallCamera.instance.ignoreDraws = true; + + var wasInvalidFrame:Bool = !colorTransform.__isDefault(false); + colorTransform.__identity(); + + if (animateAtlas != null) { + storeAtlasState(); + for (anim in ghosts) { + animateAtlas.anim.play(anim, true, false, 0); + setAnimOffset(anim); + + alpha = 0.4; color = 0xFFAEAEAE; + super.draw(); + } + + if (ghosts.length > 0) restoreAtlasState(); + } + else { + for (anim in ghosts) @:privateAccess { + alpha = 0.4; color = 0xFFAEAEAE; + + var flxanim:FlxAnimation = animation._animations.get(anim); + var frameIndex:Int = flxanim.frames.getDefault([0])[0]; + frame = frames.frames[frameIndex]; + + setAnimOffset(anim); + super.draw(); + } + + if (ghosts.length > 0) + frame = frames.frames[animation.frameIndex]; + } + ghostDraw = FakeCallCamera.instance.ignoreDraws = false; + + alpha = 1; color = 0xFFFFFFFF; + if (wasInvalidFrame) { + frameOffset.set(0, 0); + offset.set(globalOffset.x * (isPlayer != playerOffsets ? 1 : -1), -globalOffset.y); + colorTransform.color = 0xFFEF0202; + } else + setAnimOffset(animateAtlas != null ? atlasPlayingAnim : animation.name); + + super.draw(); + } + + public function setAnimOffset(anim:String) { + var daOffset:FlxPoint = animOffsets.get(anim); + if (daOffset != null) { + frameOffset.set(daOffset.x, daOffset.y); + daOffset.putWeak(); + } + + offset.set(globalOffset.x * (isPlayer != playerOffsets ? 1 : -1), -globalOffset.y); + } + + // This gets annoying lmao -lunar + private var atlasState:AtlasState = null; + public function storeAtlasState():AtlasState @:privateAccess { + return atlasState = { + oldAnim: atlasPlayingAnim, + oldFrame: animateAtlas.anim.curFrame, + oldTick: animateAtlas.anim._tick, + oldPlaying: animateAtlas.anim.isPlaying, + }; + } + + public function restoreAtlasState(state:AtlasState = null) @:privateAccess { + if (state == null) state = atlasState; + + animateAtlas.anim.play(state.oldAnim, true, false, state.oldFrame); + animateAtlas.anim._tick = state.oldTick; + animateAtlas.anim.isPlaying = state.oldPlaying; + } +} \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterGhostsHandler.hx b/source/funkin/editors/character/CharacterGhostsHandler.hx deleted file mode 100644 index fe823898..00000000 --- a/source/funkin/editors/character/CharacterGhostsHandler.hx +++ /dev/null @@ -1,75 +0,0 @@ -package funkin.editors.character; - -import flixel.math.FlxPoint; -import funkin.game.Character; -import haxe.xml.Access; - -class CharacterGhostsHandler extends FlxTypedGroup<Character> { - public var character:Character; - public var animGhosts:Map<String, Character> = []; - - public function new(character:Character) { - this.character = character; - super(); - - for (anim in character.getNameList()) - createGhost(anim); - } - - public function createGhost(anim:String) { - var ghost:Character = new Character(0,0, character.curCharacter); - ghost.applyXML(character.xml); // apply cur character stuff - ghost.playAnim(anim); - ghost.stopAnimation(); - ghost.alpha = 0.5; - ghost.visible = false; - add(ghost); - - animGhosts.set(anim, ghost); - } - - public function removeGhost(anim:String) { - var ghost:Character = animGhosts.get(anim); - @:privateAccess if (ghost != null) { - ghost.animation._curAnim = null; - animGhosts.remove(anim); - ghost.destroy(); - } - } - - public function updateInfos(xml:Xml) { - for (anim => ghost in animGhosts) { - ghost.applyXML(new Access(xml)); - ghost.playAnim(anim); - ghost.stopAnimation(); - } - - rePositionGhosts(); - } - - public function updateOffsets(name:String, change:FlxPoint) { - for (anim => ghost in animGhosts) - ghost.animOffsets.set(name, ghost.getAnimOffset(name) + change); - - rePositionGhosts(); - } - - public function setOffsets(name:String, offset:FlxPoint) { - for (anim => ghost in animGhosts) - ghost.animOffsets.set(name, offset); - - rePositionGhosts(); - } - - public function clearOffsets() { - for (anim => ghost in animGhosts) - ghost.animOffsets[anim].zero(); - - rePositionGhosts(); - } - - inline function rePositionGhosts() { - for (ghost in animGhosts) - ghost.frameOffset.set(ghost.getAnimOffset(ghost.getAnimName()).x, ghost.getAnimOffset(ghost.getAnimName()).y); - } -} \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterGizmos.hx b/source/funkin/editors/character/CharacterGizmos.hx new file mode 100644 index 00000000..acfd6382 --- /dev/null +++ b/source/funkin/editors/character/CharacterGizmos.hx @@ -0,0 +1,76 @@ +package funkin.editors.character; + +import funkin.editors.stage.StageEditor; +import flixel.math.FlxRect; +import funkin.game.Character; + +class CharacterGizmos extends FlxSprite { + public var character:Character; + + public var boxGizmo:Bool = true; + public var cameraGizmo:Bool = true; + + public override function draw() { + super.draw(); + + if (character == null) return; + + if (boxGizmo) drawHitbox(); + if (cameraGizmo) drawCamera(); + } + + public function drawHitbox() { + for (camera in cameras) { + if (character.animateAtlas != null) { + var bounds:FlxRect = cast character.extra.get(StageEditor.exID("bounds")); + character._rect.copyFrom(bounds.getDefault(FlxRect.weak())); + + character._rect.x -= character.cameras[0].viewX; + character._rect.y -= character.cameras[0].viewY; + } else if (character._matrix != null && character.frame != null) { + character._rect.set( + character._matrix.tx, character._matrix.ty, + Math.abs(character.frame.frame.width * character._matrix.a), + Math.abs(character.frame.frame.height * character._matrix.d) + ); + if (character._matrix.a < 0) character._rect.x -= character._rect.width; + if (character._matrix.d < 0) character._rect.y -= character._rect.height; + + character._rect.offset(-character.cameras[0].viewMarginLeft, -character.cameras[0].viewMarginTop); + } + + character._rect.x *= character.cameras[0].zoom; + character._rect.y *= character.cameras[0].zoom; + character._rect.width *= character.cameras[0].zoom; + character._rect.height *= character.cameras[0].zoom; + + if (DrawUtil.line == null) DrawUtil.createDrawers(); + DrawUtil.line.camera = camera; DrawUtil.line.alpha = 0.85; + + DrawUtil.drawRect(character._rect, 1, 0xFF007B8F); + } + } + + public function drawCamera() { + for (camera in cameras) { + var camPos:FlxPoint = character.getCameraPosition(); + camPos -= camera.scroll; + + if (DrawUtil.line == null) DrawUtil.createDrawers(); + DrawUtil.line.camera = camera; DrawUtil.line.alpha = 1; + + DrawUtil.drawLine( + CoolUtil.pointToScreenPosition(FlxPoint.weak(camPos.x - 8, camPos.y), FlxG.camera, FlxPoint.weak()), + CoolUtil.pointToScreenPosition(FlxPoint.weak(camPos.x + 8, camPos.y), FlxG.camera, FlxPoint.weak()), + 1, 0xFF00A0B9); + + DrawUtil.drawLine( + CoolUtil.pointToScreenPosition(FlxPoint.weak(camPos.x, camPos.y - 8), FlxG.camera, FlxPoint.weak()), + CoolUtil.pointToScreenPosition(FlxPoint.weak(camPos.x, camPos.y + 8), FlxG.camera, FlxPoint.weak()), + 1, 0xFF00A0B9); + + camPos.put(); + + } + } +} \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterInfoScreen.hx b/source/funkin/editors/character/CharacterInfoScreen.hx index 42905838..8241c198 100644 --- a/source/funkin/editors/character/CharacterInfoScreen.hx +++ b/source/funkin/editors/character/CharacterInfoScreen.hx @@ -1,46 +1,48 @@ package funkin.editors.character; -import flixel.math.FlxPoint; +import flixel.util.FlxColor; +import funkin.editors.stage.StageEditor; import funkin.editors.extra.PropertyButton; +import flixel.graphics.FlxGraphic; +import openfl.geom.Point; +import openfl.geom.Rectangle; +import openfl.display.BitmapData; import funkin.game.Character; -import funkin.game.HealthIcon; -import haxe.xml.Access; + +using funkin.backend.utils.BitmapUtil; + +typedef CharacterExtraInfo = { + var icon:String; + var iconColor:Null<FlxColor>; + var holdTime:Float; + var customProperties:Map<String, Dynamic>; +} class CharacterInfoScreen extends UISubstateWindow { public var character:Character; - public var spriteTextBox:UITextBox; - public var iconTextBox:UITextBox; - public var iconSprite:HealthIcon; - public var gameOverCharTextBox:UITextBox; - public var antialiasingCheckbox:UICheckbox; - public var flipXCheckbox:UICheckbox; + public var iconColorPicker:UIIconColorPicker; public var iconColorWheel:UIColorwheel; - public var positionXStepper:UINumericStepper; - public var positionYStepper:UINumericStepper; - public var cameraXStepper:UINumericStepper; - public var cameraYStepper:UINumericStepper; - public var scaleStepper:UINumericStepper; - public var singTimeStepper:UINumericStepper; + + public var useDurationCheckbox:UICheckbox; + public var durationStepper:UINumericStepper; + public var customPropertiesButtonList:UIButtonList<PropertyButton>; - public var isPlayerCheckbox:UICheckbox; - public var isGFCheckbox:UICheckbox; public var saveButton:UIButton; public var closeButton:UIButton; - public var onSave:(xml:Xml) -> Void = null; + public var onSave:(info:CharacterExtraInfo) -> Void = null; - public function new(character:Character, ?onSave:(xml:Xml) -> Void) { + public function new(character:Character, onSave:(info:CharacterExtraInfo) -> Void) { this.character = character; this.onSave = onSave; super(); } public override function create() { - winTitle = "Editing Character"; - winWidth = 1014; - winHeight = 600; + winTitle = 'Edit Character Properties...'; + winWidth = 824; winHeight = 400; super.create(); @@ -48,155 +50,68 @@ class CharacterInfoScreen extends UISubstateWindow { add(new UIText(ui.x, ui.y - 24, 0, text)); var title:UIText; - add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Sprite Data", 28)); + add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Edit Character Info", 28)); - spriteTextBox = new UITextBox(title.x, title.y + title.height + 38, character.sprite, 200); - add(spriteTextBox); - addLabelOn(spriteTextBox, "Sprite"); + iconColorPicker = new UIIconColorPicker(title.x, title.y + title.height + 38, character.getIcon(), character.antialiasing, null); + add(iconColorPicker); + addLabelOn(iconColorPicker, "Icon"); - iconTextBox = new UITextBox(spriteTextBox.x + 200 + 26, spriteTextBox.y, character.getIcon(), 150); - iconTextBox.onChange = (newIcon:String) -> {updateIcon(newIcon);} - add(iconTextBox); - addLabelOn(iconTextBox, "Icon"); - - updateIcon(character.getIcon()); - - gameOverCharTextBox = new UITextBox(iconTextBox.x + 150 + (75 + 12), iconTextBox.y, character.gameOverCharacter, 200); - add(gameOverCharTextBox); - addLabelOn(gameOverCharTextBox, "Game Over Character"); - - antialiasingCheckbox = new UICheckbox(spriteTextBox.x, spriteTextBox.y + 10 + 32 + 28, "Antialiasing", character.antialiasing); - add(antialiasingCheckbox); - addLabelOn(antialiasingCheckbox, "Antialiased"); - - flipXCheckbox = new UICheckbox(antialiasingCheckbox.x + 172, spriteTextBox.y + 10 + 32 + 28, "FlipX", character.flipX); - add(flipXCheckbox); - addLabelOn(flipXCheckbox, "Flipped"); - - iconColorWheel = new UIColorwheel(gameOverCharTextBox.x + 200 + 20, gameOverCharTextBox.y, character.iconColor); + iconColorWheel = new UIColorwheel(iconColorPicker.x+12+125+12+22, iconColorPicker.y, character.iconColor); add(iconColorWheel); addLabelOn(iconColorWheel, "Icon Color"); if (character.iconColor != null) iconColorWheel.colorChanged = true; + iconColorPicker.colorWheel = iconColorWheel; - add(title = new UIText(spriteTextBox.x, spriteTextBox.y + 10 + 46 + 84, 0, "Character Data", 28)); + durationStepper = new UINumericStepper(iconColorWheel.x, iconColorWheel.y + 125 + 36, character.holdTime == -1 ? 4 : character.holdTime, 0.001, 2, 0, 9999999, 74); + add(durationStepper); + addLabelOn(durationStepper, "Sing Duration"); - positionXStepper = new UINumericStepper(title.x, title.y + title.height + 36, character.globalOffset.x, 0.001, 2, null, null, 84); - add(positionXStepper); - addLabelOn(positionXStepper, "Position (X,Y)"); + useDurationCheckbox = new UICheckbox(durationStepper.x + durationStepper.bWidth + 20, durationStepper.y+6, "Use Sing Duration?", character.holdTime != -1); + useDurationCheckbox.onChecked = (checked:Bool) -> {durationStepper.selectable = checked;}; + add(useDurationCheckbox); - add(new UIText(positionXStepper.x + 84 - 32 + 0, positionXStepper.y + 9, 0, ",", 22)); + durationStepper.selectable = useDurationCheckbox.checked; - positionYStepper = new UINumericStepper(positionXStepper.x + 84 - 32 + 26, positionXStepper.y, character.globalOffset.y, 0.001, 2, null, null, 84); - add(positionYStepper); - - cameraXStepper = new UINumericStepper(positionYStepper.x + 36 + 84 - 32, positionYStepper.y, character.cameraOffset.x, 0.001, 2, null, null, 84); - add(cameraXStepper); - addLabelOn(cameraXStepper, "Camera Position (X,Y)"); - - add(new UIText(cameraXStepper.x + 84 - 32 + 0, cameraXStepper.y + 9, 0, ",", 22)); - - cameraYStepper = new UINumericStepper(cameraXStepper.x + 84 - 32 + 26, cameraXStepper.y, character.cameraOffset.y, 0.001, 2, null, null, 84); - add(cameraYStepper); - - scaleStepper = new UINumericStepper(cameraYStepper.x + 84 - 32 + 90, cameraYStepper.y, character.scale.x, 0.001, 2, null, null, 74); - add(scaleStepper); - addLabelOn(scaleStepper, "Scale"); - - singTimeStepper = new UINumericStepper(scaleStepper.x + 74 - 32 + 36, scaleStepper.y, character.holdTime, 0.001, 2, null, null, 74); - add(singTimeStepper); - addLabelOn(singTimeStepper, "Sing Duration (Steps)"); - - customPropertiesButtonList = new UIButtonList<PropertyButton>(singTimeStepper.x + singTimeStepper.width + 200, singTimeStepper.y, 290, 200, '', FlxPoint.get(280, 35), null, 5); + customPropertiesButtonList = new UIButtonList<PropertyButton>(iconColorWheel.x+iconColorWheel.bWidth+22, iconColorWheel.y, 290, 200, '', FlxPoint.get(280, 35), null, 5); customPropertiesButtonList.frames = Paths.getFrames('editors/ui/inputbox'); customPropertiesButtonList.cameraSpacing = 0; customPropertiesButtonList.addButton.callback = function() { customPropertiesButtonList.add(new PropertyButton("newProperty", "valueHere", customPropertiesButtonList)); } + for (prop=>val in character.extra) - customPropertiesButtonList.add(new PropertyButton(prop, val, customPropertiesButtonList)); + if (prop != StageEditor.exID("bounds")) + customPropertiesButtonList.add(new PropertyButton(prop, val, customPropertiesButtonList)); + add(customPropertiesButtonList); addLabelOn(customPropertiesButtonList, "Custom Values (Advanced)"); - isPlayerCheckbox = new UICheckbox(positionXStepper.x, positionXStepper.y + 10 + 32 + 28, "isPlayer", character.playerOffsets); - add(isPlayerCheckbox); - addLabelOn(isPlayerCheckbox, "Is Player"); - - isGFCheckbox = new UICheckbox(isPlayerCheckbox.x + 128, positionXStepper.y + 10 + 32 + 28, "isGF", false); - add(isGFCheckbox); - addLabelOn(isGFCheckbox, "Is GF"); - - for (checkbox in [isPlayerCheckbox, isGFCheckbox, antialiasingCheckbox, flipXCheckbox]) - {checkbox.y += 4; checkbox.x += 6;} - - saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight- 20, "Save & Close", function() { + saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() { saveCharacterInfo(); close(); }, 125); - saveButton.x -= saveButton.bWidth; - saveButton.y -= saveButton.bHeight; + add(saveButton); - closeButton = new UIButton(saveButton.x - 20, saveButton.y, "Close", function() { - if (onSave != null) onSave(null); + closeButton = new UIButton(saveButton.x - 20 - saveButton.bWidth, saveButton.y, "Close", function() { close(); }, 125); - closeButton.x -= closeButton.bWidth; - closeButton.color = 0xFFFF0000; add(closeButton); - add(saveButton); + closeButton.color = 0xFFFF0000; } - function updateIcon(icon:String) { - if (iconSprite == null) add(iconSprite = new HealthIcon()); + public function saveCharacterInfo() { + UIUtil.confirmUISelections(this); - iconSprite.setIcon(icon); - var size = Std.int(150 * 0.5); - iconSprite.setUnstretchedGraphicSize(size, size, true); - iconSprite.updateHitbox(); - iconSprite.setPosition(iconTextBox.x + iconTextBox.bWidth + 8, iconTextBox.y + (iconTextBox.bHeight / 2) - (iconSprite.height / 2)); - iconSprite.scrollFactor.set(1, 1); - } - - function saveCharacterInfo() { - for (stepper in [positionXStepper, positionYStepper, cameraXStepper, cameraYStepper, singTimeStepper, scaleStepper]) - @:privateAccess stepper.__onChange(stepper.label.text); - - var xml = Xml.createElement("character"); - xml.set("isPlayer", isPlayerCheckbox.checked ? "true" : "false"); - xml.set("x", Std.string(positionXStepper.value)); - xml.set("y", Std.string(positionYStepper.value)); - xml.set("gameOverChar", gameOverCharTextBox.label.text); - xml.set("camx", Std.string(cameraXStepper.value)); - xml.set("camy", Std.string(cameraYStepper.value)); - xml.set("holdTime", Std.string(singTimeStepper.value)); - xml.set("flipX", Std.string(flipXCheckbox.checked)); - xml.set("icon", iconTextBox.label.text); - xml.set("scale", Std.string(scaleStepper.value)); - xml.set("antialiasing", antialiasingCheckbox.checked ? "true" : "false"); - xml.set("sprite", spriteTextBox.label.text); - if (iconColorWheel.colorChanged) - xml.set("color", iconColorWheel.curColor.toWebString()); - for (val in customPropertiesButtonList.buttons.members) - xml.set(val.propertyText.label.text, val.valueText.label.text); - - for (anim in character.animDatas) - { - var animXml:Xml = Xml.createElement('anim'); - animXml.set("name", anim.name); - animXml.set("anim", anim.anim); - animXml.set("loop", Std.string(anim.loop)); - animXml.set("fps", Std.string(anim.fps)); - var offset:FlxPoint = character.getAnimOffset(anim.name); - animXml.set("x", Std.string(offset.x)); - animXml.set("y", Std.string(offset.y)); - offset.put(); - - if (anim.indices.length > 0) - animXml.set("indices", CoolUtil.formatNumberRange(anim.indices)); - xml.addChild(animXml); - } - - if (onSave != null) onSave(xml); + if (onSave != null) onSave({ + icon: iconColorPicker.iconTextBox.label.text, + iconColor: iconColorWheel.curColor, + holdTime: useDurationCheckbox.checked ? durationStepper.value : -1, + customProperties: [ + for (val in customPropertiesButtonList.buttons.members) + val.propertyText.label.text => val.valueText.label.text + ] + }); } } \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterPropertiesWindow.hx b/source/funkin/editors/character/CharacterPropertiesWindow.hx index 4ffe5329..e32c0d9c 100644 --- a/source/funkin/editors/character/CharacterPropertiesWindow.hx +++ b/source/funkin/editors/character/CharacterPropertiesWindow.hx @@ -1,27 +1,278 @@ package funkin.editors.character; -import flixel.util.FlxColor; +import funkin.backend.FlxAnimate; +import haxe.io.Path; +import funkin.editors.character.CharacterInfoScreen.CharacterExtraInfo; +import funkin.game.Character; -class CharacterPropertiesWindow extends UIWindow { - public var newButton:UIButton; - public var editButton:UIButton; +class CharacterPropertiesWindow extends UISliceSprite { + public var character:Character; + public var animsWindow:CharacterAnimsWindow; - public var characterInfo:UIText; + public var positionXStepper:UINumericStepper; + public var positionYStepper:UINumericStepper; + public var positionXYComma:UIText; + public var scaleStepper:UINumericStepper; + public var editCharacterButton:UIButton; + public var editSpriteButton:UIButton; + public var flipXCheckbox:UICheckbox; - public function new() { - super(800-23,23 + 23, 450 + 23, 140, "Character Properties"); + public var cameraXStepper:UINumericStepper; + public var cameraYStepper:UINumericStepper; + public var cameraXYComma:UIText; + public var antialiasingCheckbox:UICheckbox; + public var testAsDropDown:UIDropDown; + public var designedAsDropDown:UIDropDown; - newButton = new UIButton( x +(450 + 23)- 25 - 3 - 200, y + 12 + 31, "New Character", null, 200); - members.push(newButton); - newButton.color = FlxColor.GREEN; + public var labels:Map<UISprite, UIText> = []; - editButton = new UIButton(newButton.x, newButton.y + 12 + 30 + 4, "Edit Character Info", function () { - CharacterEditor.instance.editInfoWithUI(); - }, 200); - members.push(editButton); + public function new(x:Float, y:Float, character:Character) @:privateAccess { + super(x, y, 424+16, 204+20, "editors/ui/inputbox"); - characterInfo = new UIText(x + 450 + 20 - 42 - 400, y + 36 + 10, 400, "0 Animations:\nFlipped:\nSprite:\nAnim:\nOffset: (,)"); - characterInfo.alignment = LEFT; - members.push(characterInfo); + function addLabelOn(ui:UISprite, text:String) { + var uiText:UIText = new UIText(ui.x, ui.y-24, 0, text); + members.push(uiText); labels.set(ui, uiText); + } + + positionXStepper = new UINumericStepper(x+16, y+36, character.globalOffset.x, 0.001, 2, null, null, 104); + positionXStepper.onChange = (text:String) -> { + @:privateAccess positionXStepper.__onChange(text); + this.changePosition(positionXStepper.value, null); + }; + members.push(positionXStepper); + addLabelOn(positionXStepper, "Position (X,Y)"); + + members.push(positionXYComma = new UIText(positionXStepper.x+104-32+0, positionXStepper.y + 9, 0, ",", 22)); + + positionYStepper = new UINumericStepper(positionXStepper.x+104-32+26, positionXStepper.y, character.globalOffset.y, 0.001, 2, null, null, 104); + positionYStepper.onChange = (text:String) -> { + @:privateAccess positionYStepper.__onChange(text); + this.changePosition(null, positionYStepper.value); + }; + members.push(positionYStepper); + + scaleStepper = new UINumericStepper(positionYStepper.x+104-32+26, positionYStepper.y, character.scale.x, 0.001, 2, 0, null, 90); + scaleStepper.onChange = (text:String) -> { + @:privateAccess scaleStepper.__onChange(text); + this.changeScale(scaleStepper.value); + }; + members.push(scaleStepper); + addLabelOn(scaleStepper, "Scale"); + + editCharacterButton = new UIButton(scaleStepper.x + 90 -32 + 26, scaleStepper.y-20, "Edit Info", editCharacterInfoUI, 120, 24); + editCharacterButton.field.size -= 2; + members.push(editCharacterButton); + + editSpriteButton = new UIButton(editCharacterButton.x, editCharacterButton.y+24+6, "Edit Sprite", editCharacterSpriteUI, 120, 24); + editSpriteButton.field.size -= 2; + members.push(editSpriteButton); + + flipXCheckbox = new UICheckbox(scaleStepper.x+22, scaleStepper.y+32+14, "Flipped?", character.isPlayer ? !character.__baseFlipped : character.__baseFlipped); + flipXCheckbox.onChecked = (checked:Bool) -> {this.changeFlipX(checked);}; + members.push(flipXCheckbox); + + cameraXStepper = new UINumericStepper(positionXStepper.x, positionXStepper.y+32+32+4, character.cameraOffset.x, 0.001, 2, null, null, 104); + cameraXStepper.onChange = (text:String) -> { + @:privateAccess cameraXStepper.__onChange(text); + this.changeCamPosition(cameraXStepper.value, null); + }; + members.push(cameraXStepper); + addLabelOn(cameraXStepper, "Camera Position (X,Y)"); + + members.push(cameraXYComma = new UIText(cameraXStepper.x + 104-32+0, cameraXStepper.y+9, 0, ",", 22)); + + cameraYStepper = new UINumericStepper(cameraXStepper.x+104-32+26, cameraXStepper.y, character.cameraOffset.y, 0.001, 2, null, null, 104); + cameraYStepper.onChange = (text:String) -> { + @:privateAccess cameraYStepper.__onChange(text); + this.changeCamPosition(null, cameraYStepper.value); + }; + members.push(cameraYStepper); + + antialiasingCheckbox = new UICheckbox(scaleStepper.x+22, flipXCheckbox.y+32, "Antialiased?", character.antialiasing); + antialiasingCheckbox.onChecked = (checked:Bool) -> {this.changeAntialiasing(checked);}; + members.push(antialiasingCheckbox); + + testAsDropDown = new UIDropDown(cameraXStepper.x, cameraXStepper.y+32+32+4, 193, 32, ["BOYFRIEND", "DAD"], character.playerOffsets ? 0 : 1); + testAsDropDown.onChange = (index:Int) -> { + CharacterEditor.instance.changeStagePosition(testAsDropDown.options[index]); + }; + members.push(testAsDropDown); + addLabelOn(testAsDropDown, "Test Character As..."); + testAsDropDown.bWidth = 193; //REFUSES TO FUCKING SET TO 170 PIECE OF SHIT!! + + designedAsDropDown = new UIDropDown(testAsDropDown.x+193+22, testAsDropDown.y, 193, 32, ["BOYFRIEND", "DAD"], character.playerOffsets ? 0 : 1); + designedAsDropDown.onChange = (index:Int) -> { + CharacterEditor.instance.changeCharacterDesginedAs(designedAsDropDown.options[index] == "BOYFRIEND"); + }; + members.push(designedAsDropDown); + addLabelOn(designedAsDropDown, "Char Desgined As..."); + designedAsDropDown.bWidth = 193; + + alpha = 0.7; + + this.character = character; + } + + public function changePosition(newPosX:Null<Float>, newPosY:Null<Float>, addToUndo:Bool = true) { + if (newPosX != null && newPosY != null && newPosX == character.globalOffset.x && newPosY == character.globalOffset.y) + return; + else if (addToUndo) { + if (newPosX != null && newPosX == character.globalOffset.x) return; + if (newPosY != null && newPosY == character.globalOffset.y) return; + } + var oldPosition:FlxPoint = character.globalOffset.clone(); + + if (newPosX != null) character.globalOffset.x = newPosX; + if (newPosY != null) character.globalOffset.y = newPosY; + + CharacterEditor.instance.playAnimation(character.getAnimName()); + + positionXStepper.label.text = Std.string(character.globalOffset.x); + positionYStepper.label.text = Std.string(character.globalOffset.y); + + if (addToUndo) CharacterEditor.undos.addToUndo(CCharEditPosition(oldPosition, character.globalOffset.clone())); + else oldPosition.put(); + } + + public function changeScale(newScale:Float, addToUndo:Bool = true) { + if (character.scale.x == newScale) return; + var oldScale:Float = character.scale.x; + + character.scale.set(newScale, newScale); + character.updateHitbox(); + + CharacterEditor.instance.playAnimation(character.getAnimName()); + + scaleStepper.label.text = Std.string(newScale); + if (addToUndo) CharacterEditor.undos.addToUndo(CCharEditScale(oldScale, newScale)); + } + + public function editCharacterInfoUI() { + CharacterEditor.instance.openSubState(new CharacterInfoScreen(character, (info:CharacterExtraInfo) -> {editCharacterInfo(info);})); + } + + public function editCharacterInfo(info:CharacterExtraInfo, addToUndo:Bool = true) { + var oldInfo:CharacterExtraInfo = { + icon: character.icon, + iconColor: character.iconColor, + holdTime: character.holdTime, + customProperties: character.extra.copy() + }; + + character.icon = info.icon; + character.iconColor = info.iconColor; + character.holdTime = info.holdTime; + character.extra = info.customProperties.copy(); + + if (addToUndo) CharacterEditor.undos.addToUndo(CCharEditInfo(oldInfo, info)); + } + + public function editCharacterSpriteUI() { + CharacterEditor.instance.openSubState(new CharacterSpriteScreen('characters/${character.sprite}', (sprite:String, isAtlas:Bool) -> { + changeSprite(sprite); + })); + } + + public function changeSprite(sprite:String) @:privateAccess { + var path:String = Paths.image('characters/$sprite'); + var noExt:String = Path.withoutExtension(path); + + character.sprite = sprite; + + animsWindow.displayWindowSprite.animation.reset(); + animsWindow.displayAnimsFramesList.clear(); + if (animsWindow.displayWindowGraphic != null) + animsWindow.displayWindowGraphic.destroy(); + + if (Assets.exists('$noExt/Animation.json')) { + if (character.animateAtlas == null) { + character.animation.reset(); + character.animateAtlas = new FlxAnimate(character.x, character.y); + } else + character.animateAtlas.anim.stop(); + + character.atlasPath = noExt; + character.animateAtlas.loadAtlas(noExt); + } else { + if (character.animateAtlas != null) { + character.animateAtlas.destroy(); + character.animateAtlas = null; + character.atlasPlayingAnim = character.atlasPath = null; + } + character.frames = Paths.getFrames(path, true); + + animsWindow.displayWindowSprite.loadGraphicFromSprite(character); + if (Assets.exists(Paths.image('characters/${character.sprite}'))) + animsWindow.displayWindowGraphic = FlxG.bitmap.add(Assets.getBitmapData(Paths.image('characters/${character.sprite}'), true, false)); + } + + character.animDatas.clear(); + for (anim in animsWindow.buttons) anim.checkValid(); // Re-add all animations + + CharacterEditor.instance.playAnimation(animsWindow.findValid().getDefault(animsWindow.buttons.members[0].anim)); + animsWindow.setAnimAutoComplete(CoolUtil.getAnimsListFromSprite(character)); + } + + public function changeFlipX(newFlipX:Bool, addToUndo:Bool = true) @:privateAccess { + character.flipX = character.isPlayer ? !newFlipX : newFlipX; + character.__baseFlipped = character.flipX; + + CharacterEditor.instance.playAnimation(character.getAnimName()); + + flipXCheckbox.checked = newFlipX; + if (addToUndo) CharacterEditor.undos.addToUndo(CCharEditFlipped(newFlipX)); + } + + public function changeCamPosition(newPosX:Null<Float>, newPosY:Null<Float>, addToUndo:Bool = true) { + if (newPosX != null && newPosY != null && newPosX == character.cameraOffset.x && newPosY == character.cameraOffset.y) + return; + else if (addToUndo) { + if (newPosX != null && newPosX == character.cameraOffset.x) return; + if (newPosY != null && newPosY == character.cameraOffset.y) return; + } + var oldCamPosition:FlxPoint = character.cameraOffset.clone(); + + if (newPosX != null) character.cameraOffset.x = newPosX; + if (newPosY != null) character.cameraOffset.y = newPosY; + + cameraXStepper.label.text = Std.string(character.cameraOffset.x); + cameraYStepper.label.text = Std.string(character.cameraOffset.y); + + if (addToUndo) CharacterEditor.undos.addToUndo(CCharEditCamPosition(oldCamPosition, character.cameraOffset.clone())); + else oldCamPosition.put(); + } + + public function changeAntialiasing(newAntialiasing:Bool, addToUndo:Bool = true) { + if (character.antialiasing == newAntialiasing) return; + character.antialiasing = newAntialiasing; + animsWindow.displayWindowSprite.antialiasing = newAntialiasing; + + antialiasingCheckbox.checked = newAntialiasing; + if (addToUndo) CharacterEditor.undos.addToUndo(CCharEditAntialiasing(newAntialiasing)); + } + + public function updateButtonsPos() { + positionXStepper.follow(this, 16, 36); + positionYStepper.follow(this, 16+104-32+26, 36); + positionXYComma.follow(this, 16+104-32+0, 36 + 9); + scaleStepper.follow(this, (16+104-32+26)+104-32+26, 36); + editCharacterButton.follow(this, ((16+104-32+26)+104-32+26)+90-32+26, 36-20); + editSpriteButton.follow(this, ((16+104-32+26)+104-32+26)+90-32+26, (36-20)+24+6); + flipXCheckbox.follow(this, (16+104-32+26)+104-32+26+22, 36+32+14); + + cameraXStepper.follow(this, 16, 36+32+32+4); + cameraYStepper.follow(this, (16)+104-32+26, 36+32+32+4); + cameraXYComma.follow(this, 16 + 104-32+0, (36+32+32+4)+9); + antialiasingCheckbox.follow(this, ((16+104-32+26)+104-32+26)+22, 36+32+14+32); + testAsDropDown.follow(this, 16, (36+32+32+4)+32+32+4); + designedAsDropDown.follow(this, (16)+193+22, (36+32+32+4)+32+32+4); + + for (ui => text in labels) + text.follow(ui, 0, -24); + } + + public override function draw() { + updateButtonsPos(); + super.draw(); } } \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterSelection.hx b/source/funkin/editors/character/CharacterSelection.hx index cf5e26d6..f298d951 100644 --- a/source/funkin/editors/character/CharacterSelection.hx +++ b/source/funkin/editors/character/CharacterSelection.hx @@ -1,21 +1,26 @@ package funkin.editors.character; +import haxe.xml.Printer; +import funkin.editors.ui.UIImageExplorer.ImageSaveData; import funkin.game.Character; import funkin.options.OptionsScreen; import funkin.options.type.IconOption; import funkin.options.type.NewOption; import funkin.options.type.TextOption; import funkin.options.type.OptionType; +import funkin.backend.assets.ModsFolder; class CharacterSelection extends EditorTreeMenu { + public var modsList:Array<String> = []; + public override function create() { bgType = "charter"; super.create(); var isMods:Bool = true; - var modsList:Array<String> = Character.getList(true, true); + modsList = Character.getList(true, true); if(modsList.length == 0) { modsList = Character.getList(false, true); @@ -43,10 +48,8 @@ class CharacterSelection extends EditorTreeMenu } } - list.insert(0, new NewOption("New Character", "New Character", function() { - openSubState(new UIWarningSubstate("New Character: Feature Not Implemented!", "This feature isn't implemented yet. Please wait for more cne updates to have this functional.\n\n\n- Codename Devs", [ - {label: "Ok", color: 0xFFFF0000, onClick: function(t) {}} - ])); + list.insert(0, new NewOption("New Character", "Press ACCEPT to create a new character.", function() { + openSubState(new CharacterCreationScreen(createCharacter)); })); return list; @@ -64,4 +67,29 @@ class CharacterSelection extends EditorTreeMenu main.changeSelection(1); } + + public function createCharacter(name:String, imageSaveData:ImageSaveData, xml:Xml) { + var characterAlreadyExists:Bool = modsList.contains(name); + if (characterAlreadyExists) { + openSubState(new UIWarningSubstate("Creating Character: Error!", "The character you are trying to create already exists, if you would like to override it delete the character first!", [ + {label: "Ok", color: 0xFFFF0000, onClick: function(t) {}} + ])); + return; + } + + // Save Data file + var characterPath:String = '${Paths.getAssetsRoot()}/data/characters/${name}.xml'; + CoolUtil.safeSaveFile(characterPath, "<!DOCTYPE codename-engine-character>\n" + Printer.print(xml, true)); + + // Save Image files + UIImageExplorer.saveFilesGlobal(imageSaveData, '${Paths.getAssetsRoot()}/images/characters'); + + // Add to Menu >:D + var option:IconOption = new IconOption(name, "Press ACCEPT to edit this character.", Character.getIconFromCharName(name), + function() { + FlxG.switchState(new CharacterEditor(name)); + } + ); + optionsTree.members[optionsTree.members.length-1].insert(1, option); + } } \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterSpriteScreen.hx b/source/funkin/editors/character/CharacterSpriteScreen.hx new file mode 100644 index 00000000..a3497127 --- /dev/null +++ b/source/funkin/editors/character/CharacterSpriteScreen.hx @@ -0,0 +1,94 @@ +package funkin.editors.character; + +import haxe.io.Bytes; +import flixel.util.typeLimit.OneOfTwo; +import flixel.text.FlxText.FlxTextFormat; +import funkin.editors.ui.UIImageExplorer.ImageSaveData; +import flixel.text.FlxText.FlxTextFormatMarkerPair; + +class CharacterSpriteScreen extends UISubstateWindow { + private var imagePath:String = null; + private var onSave:(String, Bool) -> Void = null; + + public var ogImageSaveData:ImageSaveData; + public var imageExplorer:UIImageExplorer; + + public var saveButton:UIButton; + public var closeButton:UIButton; + + public function new(imagePath:String, ?onSave:(String, Bool)->Void) { + super(); + this.imagePath = imagePath; + if (onSave != null) this.onSave = onSave; + } + + public override function create() { + winTitle = 'Edit character sprite'; + winWidth = 360; winHeight = 183; + + function addLabelOn(ui:UISprite, text:String):UIText { + var text:UIText = new UIText(ui.x, ui.y - 24, 0, text); + ui.members.push(text); + return text; + } + + super.create(); + + imageExplorer = new UIImageExplorer(20, windowSpr.y + 30 + 16 + 20, imagePath, 320, 58, (_, _) -> {onLoadImage();}, "images/characters"); + add(imageExplorer); + addLabelOn(imageExplorer, "Character Image File").applyMarkup( + "Character Image File $* Required$", + [new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]); + + ogImageSaveData = imageExplorer.getSaveData(); + + saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight - 20, "Save & Close", function() { + addToUndo(); // should be async?? -lunar + imageExplorer.saveFiles('${Paths.getAssetsRoot()}/images/characters', () -> { + onSave('${imageExplorer.saveData.directory.length > 0 ? '${imageExplorer.saveData.directory}/' : ""}' + imageExplorer.imageName, imageExplorer.isAtlas); + close(); + }); + }, 125); + saveButton.x -= saveButton.bWidth; + saveButton.y -= saveButton.bHeight; + saveButton.selectable = false; + + closeButton = new UIButton(saveButton.x - 20, saveButton.y, "Cancel", function() { + close(); + }, 125); + closeButton.color = 0xFFFF0000; + closeButton.x -= closeButton.bWidth; + //closeButton.y -= closeButton.bHeight; + add(closeButton); + add(saveButton); + + refreshWindowSize(); + } + + public function onLoadImage() { + refreshWindowSize(); + + if (imageExplorer == null || imageExplorer.imageFiles == null) return; + var filesSame = CoolUtil.deepEqual(ogImageSaveData.imageFiles, imageExplorer.imageFiles); + saveButton.selectable = !filesSame && !CoolUtil.isMapEmpty(imageExplorer.imageFiles) && (imageExplorer.animationList.length > 0); + } + + public function refreshWindowSize() { + if (imageExplorer == null) return; + windowSpr.bWidth = 20 + imageExplorer.bWidth + 20; + windowSpr.bHeight = 30 + 16 + 20 + imageExplorer.bHeight + 14 + saveButton.bHeight + 14; + + saveButton.x = windowSpr.x + windowSpr.bWidth - 20 - saveButton.bWidth; + closeButton.x = saveButton.x - 20 - closeButton.bWidth; + closeButton.y = saveButton.y = imageExplorer.y + imageExplorer.bHeight + 14; + } + + public static var idCounter:Int = -1; + public inline function addToUndo() { + idCounter = FlxMath.wrap(idCounter + FlxG.random.int(1, 57349), 0, 9999); + CharacterEditor.undos.addToUndo(CCharEditSprite(idCounter)); + + CoolUtil.safeSaveFile('./.temp/__undo__${Type.getClassName(Type.getClass(FlxG.state))}__${idCounter}.cneisd', UIImageExplorer.serializeSaveDataGlobal(ogImageSaveData)); + CoolUtil.safeSaveFile('./.temp/__redo__${Type.getClassName(Type.getClass(FlxG.state))}__${idCounter}.cneisd', UIImageExplorer.serializeSaveDataGlobal(imageExplorer.getSaveData())); + } +} \ No newline at end of file diff --git a/source/funkin/editors/charter/ChartCreationScreen.hx b/source/funkin/editors/charter/ChartCreationScreen.hx index 87bd3c93..f6461f4f 100644 --- a/source/funkin/editors/charter/ChartCreationScreen.hx +++ b/source/funkin/editors/charter/ChartCreationScreen.hx @@ -67,7 +67,7 @@ class ChartCreationScreen extends UISubstateWindow { add(scrollSpeedTextBox); addLabelOn(scrollSpeedTextBox, "Scroll Speed"); - strumLineList = new UIButtonList<StrumLineButton>(difficultyNameTextBox.x, difficultyNameTextBox.y+difficultyNameTextBox.bHeight+36, 620, (552-179)-16, "", FlxPoint.get(620, 246), null, 6); + strumLineList = new UIButtonList<StrumLineButton>(difficultyNameTextBox.x, difficultyNameTextBox.y+difficultyNameTextBox.bHeight+36, 620, (552-179)-16, null, FlxPoint.get(620, 246), null, 6); strumLineList.frames = Paths.getFrames('editors/ui/inputbox'); strumLineList.cameraSpacing = 0; @@ -144,8 +144,7 @@ class ChartCreationScreen extends UISubstateWindow { var strumLines:Array<ChartStrumLine> = []; for (strline in strumLineList.buttons.members) { - for (stepper in [strline.hudXStepper, strline.hudYStepper, strline.hudScaleStepper]) - @:privateAccess stepper.__onChange(stepper.label.text); + UIUtil.confirmUISelections(strline); strumLines.push({ characters: [for (char in strline.charactersList.buttons.members) char.textBox.label.text], @@ -207,7 +206,7 @@ class StrumLineButton extends UIButton { return uiText; } - charactersList = new UIButtonList<CompactCharacterButton>(16, 8+26, 210, 160, "", FlxPoint.get(200, 40), null, 5); + charactersList = new UIButtonList<CompactCharacterButton>(16, 8+26, 210, 160, null, FlxPoint.get(200, 40), null, 5); charactersList.frames = Paths.getFrames('editors/ui/inputbox'); charactersList.cameraSpacing = 0; @@ -270,7 +269,7 @@ class StrumLineButton extends UIButton { } members.push(usesChartScrollSpeed); - deleteButton = new UIButton(16, 246-32-11, "", function () { + deleteButton = new UIButton(16, 246-32-11, null, function () { parent.remove(this); }, 620-32); deleteButton.color = 0xFFFF0000; @@ -315,7 +314,7 @@ class CompactCharacterButton extends UIButton { public var deleteIcon:FlxSprite; public function new(char:String, charsList:Array<String>, parent:UIButtonList<CompactCharacterButton>) { - super(0, 0, "", null, 200, 40); + super(0, 0, null, null, 200, 40); autoAlpha = false; charIcon = new HealthIcon(funkin.game.Character.getIconFromCharName(char)); @@ -338,7 +337,7 @@ class CompactCharacterButton extends UIButton { charIcon.setPosition(10, bHeight/2 - charIcon.height / 2); } - deleteButton = new UIButton(textBox.x + 115 + 16, bHeight/2 - (32/2), "", function () { + deleteButton = new UIButton(textBox.x + 115 + 16, bHeight/2 - (32/2), null, function () { parent.remove(this); }, 32); deleteButton.color = 0xFFFF0000; diff --git a/source/funkin/editors/charter/ChartDataScreen.hx b/source/funkin/editors/charter/ChartDataScreen.hx index e3e1d9cc..71219726 100644 --- a/source/funkin/editors/charter/ChartDataScreen.hx +++ b/source/funkin/editors/charter/ChartDataScreen.hx @@ -4,6 +4,7 @@ import flixel.math.FlxPoint; import funkin.backend.chart.ChartData.ChartMetaData; import funkin.backend.chart.ChartData; import funkin.game.Stage; +import funkin.game.HudCamera; using StringTools; @@ -15,6 +16,9 @@ class ChartDataScreen extends UISubstateWindow { public var scrollSpeedStepper:UINumericStepper; public var stageTextBox:UIAutoCompleteTextBox; + public var strumLineCam:HudCamera; + public var previewStrumLine:CharterPreviewStrumLine; + public function new(data:ChartData) { super(); this.data = data; @@ -60,11 +64,19 @@ class ChartDataScreen extends UISubstateWindow { add(closeButton); closeButton.color = 0xFFFF0000; closeButton.x -= closeButton.bWidth; + + strumLineCam = new HudCamera(); + strumLineCam.downscroll = Options.downscroll; + strumLineCam.bgColor = 0; + strumLineCam.alpha = 0; + FlxG.cameras.add(strumLineCam, false); + previewStrumLine = new CharterPreviewStrumLine(96, 50, 1, 1, 4, 0); + previewStrumLine.camera = strumLineCam; + add(previewStrumLine); } - public function saveInfo() - { - @:privateAccess scrollSpeedStepper.__onChange(scrollSpeedStepper.label.text); + public function saveInfo() { + UIUtil.confirmUISelections(this); var oldData:{stage:String, speed:Float} = {stage: PlayState.SONG.stage, speed: PlayState.SONG.scrollSpeed}; @@ -73,4 +85,19 @@ class ChartDataScreen extends UISubstateWindow { Charter.undos.addToUndo(CEditChartData(oldData, {stage: stageTextBox.label.text, speed: scrollSpeedStepper.value})); } + + override public function update(elapsed:Float) { + var scrollSpeed:Float = 0.0; + if (scrollSpeedStepper.hovered || scrollSpeedStepper.focused) + scrollSpeed = scrollSpeedStepper.value; + + strumLineCam.alpha = CoolUtil.fpsLerp(strumLineCam.alpha, scrollSpeed == 0.0 ? 0.0 : 1.0, 0.2); + previewStrumLine.updatePos(96, 50, 1, 1, 4, scrollSpeed); + + super.update(elapsed); + } + override public function destroy() { + super.destroy(); + FlxG.cameras.remove(strumLineCam); + } } \ No newline at end of file diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx index 36cc38f1..69411caa 100644 --- a/source/funkin/editors/charter/Charter.hx +++ b/source/funkin/editors/charter/Charter.hx @@ -1,21 +1,29 @@ package funkin.editors.charter; // ! FUCK YOU CHUF (your biggest fan -lunar) <3 +// import flixel.FlxLayer; import flixel.input.keyboard.FlxKey; import flixel.math.FlxPoint; import flixel.sound.FlxSound; +import flixel.util.FlxSort; import funkin.backend.chart.*; import funkin.backend.chart.ChartData; import funkin.backend.system.Conductor; import funkin.backend.system.framerate.Framerate; -import funkin.editors.charter.CharterBackdropGroup.CharterBackdropDummy; import funkin.editors.charter.CharterBackdropGroup.EventBackdrop; +import funkin.editors.charter.CharterBackdropGroup.CharterGridSeperatorBase; +import funkin.editors.charter.CharterEvent; import funkin.editors.charter.CharterStrumline; +import funkin.editors.extra.CameraHoverDummy; import funkin.editors.ui.UIContextMenu.UIContextMenuOption; import funkin.editors.ui.UIContextMenu.UIContextMenuOptionSpr; import funkin.editors.ui.UIState; import funkin.editors.ui.UITopMenu.UITopMenuButton; import haxe.Json; +#if sys +import sys.FileSystem; +#end +import flixel.util.FlxColor; class Charter extends UIState { public static var __song:String; @@ -38,19 +46,24 @@ class Charter extends UIState { @:noCompletion private var playbackIndex:Int = 7; @:noCompletion private var snapIndex:Int = 6; @:noCompletion private var noteIndex:Int = 5; + @:noCompletion private var songIndex:Int = 4; public var scrollBar:UIScrollBar; public var songPosInfo:UIText; + public var shouldScroll:Bool = true; + public var quantButtons:Array<CharterQuantButton> = []; public var playBackSlider:UISlider; public var topMenuSpr:UITopMenu; public var gridBackdrops:CharterBackdropGroup; - public var eventsBackdrop:EventBackdrop; - public var addEventSpr:CharterEventAdd; + public var leftEventsBackdrop:EventBackdrop; + public var localAddEventSpr:CharterEventAdd; + public var rightEventsBackdrop:EventBackdrop; + public var globalAddEventSpr:CharterEventAdd; - public var gridBackdropDummy:CharterBackdropDummy; + public var gridBackdropDummy:CameraHoverDummy; public var noteHoverer:CharterNoteHoverer; public var noteDeleteAnims:CharterDeleteAnim; @@ -72,7 +85,11 @@ class Charter extends UIState { public var strumLines:CharterStrumLineGroup = new CharterStrumLineGroup(); public var notesGroup:CharterNoteGroup = new CharterNoteGroup(); - public var eventsGroup:CharterEventGroup = new CharterEventGroup(); + + public var leftEventRowText:UIText; + public var rightEventRowText:UIText; + public var leftEventsGroup:CharterEventGroup = new CharterEventGroup(); + public var rightEventsGroup:CharterEventGroup = new CharterEventGroup(); public var charterCamera:FlxCamera; public var uiCamera:FlxCamera; @@ -88,6 +105,8 @@ class Charter extends UIState { public static var clipboard:Array<CharterCopyboardObject> = []; public static var waveformHandler:CharterWaveformHandler; + //public var textLayer:FlxLayer; + public function new(song:String, diff:String, reload:Bool = true) { super(); if (song != null) { @@ -102,65 +121,67 @@ class Charter extends UIState { WindowUtils.suffix = " (Chart Editor)"; SaveWarning.selectionClass = CharterSelection; - SaveWarning.saveFunc = () -> {_file_save(null);}; + SaveWarning.saveFunc = () -> saveEverything(); topMenu = [ { label: "File", childs: [ - { + /*{ label: "New" }, - null, + null,*/ { label: "Save", keybind: [CONTROL, S], + onSelect: _file_save_all, + }, + null, + { + label: "Save chart", + keybind: [CONTROL, SHIFT, S], onSelect: _file_save, }, { - label: "Save As...", - keybind: [CONTROL, SHIFT, S], + label: "Save chart as...", onSelect: _file_saveas, }, null, { - label: "Save Without Events", - keybind: [CONTROL, ALT, TAB, S], - onSelect: _file_save_no_events, - }, - { - label: "Save Without Events As...", - keybind: [CONTROL, SHIFT, ALT, TAB, S], - onSelect: _file_saveas_no_events, - }, - { - label: "Save Events Separately", - keybind: [CONTROL, TAB, S], + label: "Save global events", + keybind: [CONTROL, B, S], onSelect: _file_events_save, }, { - label: "Save Events Separately As...", - keybind: [CONTROL, SHIFT, TAB, S], + label: "Save global events as...", onSelect: _file_events_saveas, }, + { + label: "Save chart without local events", + keybind: [CONTROL, ALT, S], + onSelect: _file_save_no_events, + }, + { + label: "Save chart without local events as...", + onSelect: _file_saveas_no_events, + }, null, { - label: "Save Meta", - keybind: [CONTROL, ALT, S], + label: "Save meta", + keybind: [CONTROL, M, S], onSelect: _file_meta_save, }, { - label: "Save Meta As...", - keybind: [CONTROL, ALT ,SHIFT, S], + label: "Save meta as...", onSelect: _file_meta_saveas, }, null, { - label: "Export For FNF Legacy...", + label: "Export for FNF Legacy as...", onSelect: _file_saveas_fnflegacy, }, { - label: "Export For Psych Engine...", + label: "Export for Psych Engine as...", onSelect: _file_saveas_psych, }, null, @@ -282,36 +303,37 @@ class Charter extends UIState { icon: Options.charterShowBeats ? 1 : 0 }, null, + { + label: 'Rainbow Waveforms', + onSelect: _view_switchWaveformRainbow, + icon: Options.charterRainbowWaveforms ? 1 : 0 + }, { label: 'Low Detail Waveforms', onSelect: _view_switchWaveformDetail, icon: Options.charterLowDetailWaveforms ? 1 : 0 + }, + null, + { + label: "Scroll left", + keybind: [SHIFT, LEFT], + onSelect: _view_scrollleft + }, + { + label: "Scroll right", + keybind: [SHIFT, RIGHT], + onSelect: _view_scrollright + }, + { + label: "Reset scroll", + keybind: [SHIFT, DOWN], + onSelect: _view_scrollreset } ] }, { label: "Song", - childs: [ - { - label: "Go back to the start", - keybind: [HOME], - onSelect: _song_start - }, - { - label: "Go to the end", - keybind: [END], - onSelect: _song_end - }, - null, - { - label: "Mute instrumental", - onSelect: _song_muteinst - }, - { - label: "Mute voices", - onSelect: _song_mutevoices - } - ] + childs: buildSongUI() }, { label: "Note >", @@ -377,9 +399,9 @@ class Charter extends UIState { onSelect: _playback_metronome, icon: Options.charterMetronomeEnabled ? 1 : 0 }, - { + /*{ label: "Visual metronome" - }, + },*/ ] } ]; @@ -404,12 +426,30 @@ class Charter extends UIState { gridBackdrops = new CharterBackdropGroup(strumLines); gridBackdrops.notesGroup = this.notesGroup; - eventsBackdrop = new EventBackdrop(); - eventsBackdrop.x = -eventsBackdrop.width; - eventsBackdrop.cameras = [charterCamera]; - eventsGroup.eventsBackdrop = eventsBackdrop; + leftEventRowText = new UIText(0, -40, 0, "Local Events\n(Only This Difficulty)", 12); + leftEventRowText.alignment = "center"; leftEventRowText.alpha = 0.75; - add(gridBackdropDummy = new CharterBackdropDummy(gridBackdrops)); + leftEventsBackdrop = new EventBackdrop(false); + leftEventsBackdrop.x = -leftEventsBackdrop.width; + + leftEventRowText.cameras = leftEventsBackdrop.cameras = leftEventsGroup.cameras = [charterCamera]; + leftEventsGroup.eventsBackdrop = leftEventsBackdrop; + leftEventsGroup.eventsRowText = leftEventRowText; + + rightEventRowText = new UIText(0, -40, 0, "Global Events\n(All Difficulties)", 12); + rightEventRowText.alignment = "center"; rightEventRowText.alpha = 0.75; + + rightEventsBackdrop = new EventBackdrop(true); + rightEventsBackdrop.x = 0; + + rightEventRowText.cameras = rightEventsBackdrop.cameras = rightEventsGroup.cameras = [charterCamera]; + rightEventsGroup.eventsBackdrop = rightEventsBackdrop; + rightEventsGroup.eventsRowText = rightEventRowText; + + // thank you neo for pointing out im stupid -lunar + // this is future lunar i completely forgot what neo pointed out but hes awesome go follow him on twitter + + add(gridBackdropDummy = new CameraHoverDummy(gridBackdrops, FlxPoint.weak(1, 0))); selectionBox = new UISliceSprite(0, 0, 2, 2, 'editors/ui/selection'); selectionBox.visible = false; selectionBox.scrollFactor.set(1, 1); @@ -418,7 +458,7 @@ class Charter extends UIState { noteHoverer = new CharterNoteHoverer(); noteDeleteAnims = new CharterDeleteAnim(); - selectionBox.cameras = notesGroup.cameras = gridBackdrops.cameras = + selectionBox.cameras = notesGroup.cameras = gridBackdrops.cameras = noteHoverer.cameras = noteDeleteAnims.cameras = [charterCamera]; topMenuSpr = new UITopMenu(topMenu); @@ -435,7 +475,7 @@ class Charter extends UIState { }; uiGroup.add(scrollBar); - songPosInfo = new UIText(FlxG.width - 30 - 400, scrollBar.y + 10, 400, "00:00\nBeat: 0\nStep: 0\nMeasure: 0\nBPM: 0\nTime Signature: 4/4"); + songPosInfo = new UIText(FlxG.width - 30 - 400, scrollBar.y + 10, 400, "00:00 / 00:00\nBeat: 0\nStep: 0\nMeasure: 0\nBPM: 0\nTime Signature: 4/4"); songPosInfo.alignment = RIGHT; uiGroup.add(songPosInfo); @@ -479,16 +519,29 @@ class Charter extends UIState { strumlineInfoBG.cameras = [charterCamera]; strumLines.cameras = [charterCamera]; - addEventSpr = new CharterEventAdd(); - addEventSpr.x -= addEventSpr.bWidth; - addEventSpr.cameras = [charterCamera]; - addEventSpr.alpha = 0; + localAddEventSpr = new CharterEventAdd(false); + localAddEventSpr.x -= localAddEventSpr.bWidth; + localAddEventSpr.cameras = [charterCamera]; + localAddEventSpr.alpha = 0; + + globalAddEventSpr = new CharterEventAdd(true); + globalAddEventSpr.x = 0; + globalAddEventSpr.cameras = [charterCamera]; + globalAddEventSpr.alpha = 0; // adds grid and notes so that they're ALWAYS behind the UI add(gridBackdrops); - add(eventsBackdrop); - add(addEventSpr); - add(eventsGroup); + add(leftEventsBackdrop); + add(rightEventsBackdrop); + add(localAddEventSpr); + add(globalAddEventSpr); + add(leftEventsGroup); + add(rightEventsGroup); + add(leftEventRowText); + add(localAddEventSpr.sideText); + add(rightEventRowText); + add(globalAddEventSpr.sideText); + add(noteHoverer); add(noteDeleteAnims); add(notesGroup); @@ -505,12 +558,14 @@ class Charter extends UIState { loadSong(); - if(Framerate.isLoaded) { + if (Framerate.isLoaded) { Framerate.fpsCounter.alpha = 0.4; Framerate.memoryCounter.alpha = 0.4; Framerate.codenameBuildField.alpha = 0.4; } - updateDisplaySprites(); + + if (Options.editorsResizable) + UIState.setResolutionAware(); // ! IF YOU EVER WANNA VIEW IN THE FUTURE, JUST USE A FLXSPRITE :D -lunar /*var dataDisplay:FlxSprite = new FlxSprite().loadGraphic(waveformHandler.waveDatas.get("Voices.ogg")); @@ -574,27 +629,28 @@ class Charter extends UIState { notesGroup.autoSort = true; // create events - eventsGroup.autoSort = false; + rightEventsGroup.autoSort = leftEventsGroup.autoSort = false; var __last:CharterEvent = null; var __lastTime:Float = Math.NaN; - for(e in PlayState.SONG.events) { + for (e in PlayState.SONG.events) { if (e == null) continue; if (__last != null && __lastTime == e.time) { __last.events.push(e); } else { __last = new CharterEvent(Conductor.getStepForTime(e.time), [e]); __lastTime = e.time; - eventsGroup.add(__last); + (__last.global ? rightEventsGroup : leftEventsGroup).add(__last); } } - eventsGroup.sortEvents(); - eventsGroup.autoSort = true; - - for(e in eventsGroup.members) - e.refreshEventIcons(); + for (grp in [leftEventsGroup, rightEventsGroup]) { + grp.sortEvents(); + grp.autoSort = true; + for (e in grp.members) e.refreshEventIcons(); + } buildNoteTypesUI(); + updateBookmarks(); refreshBPMSensitive(); // Just for now until i add event stacking -lunar @@ -611,7 +667,9 @@ class Charter extends UIState { scrollBar.length = __endStep = Conductor.getStepForTime(length); gridBackdrops.bottomLimitY = __endStep * 40; - eventsBackdrop.bottomSeparator.y = gridBackdrops.bottomLimitY-2; + leftEventsBackdrop.bottomSeparator.y = rightEventsBackdrop.bottomSeparator.y = gridBackdrops.bottomLimitY-2; + + CharterGridSeperatorBase.lastConductorSprY = Math.NEGATIVE_INFINITY; updateWaveforms(); } @@ -621,7 +679,7 @@ class Charter extends UIState { return sound != null && sound._sound != null && sound._sound.length > 0; } - public function updateWaveforms() { + public function getWavesToGenerate():Array<{name:String, sound:FlxSound}> { var wavesToGenerate:Array<{name:String, sound:FlxSound}> = []; if(isSoundLoaded(FlxG.sound.music)) @@ -637,6 +695,12 @@ class Charter extends UIState { sound: strumLine.vocals }); + return wavesToGenerate; + } + + public function updateWaveforms() { + var wavesToGenerate:Array<{name:String, sound:FlxSound}> = getWavesToGenerate(); + var oldWaveformList:Array<String> = waveformHandler.waveformList; var newWaveformList:Array<String> = [for (data in wavesToGenerate) data.name]; @@ -693,7 +757,7 @@ class Charter extends UIState { else selection = [s]; } - for (group in [notesGroup, eventsGroup]) { + for (group in [notesGroup, leftEventsGroup, rightEventsGroup]) { var group:FlxTypedGroup<Dynamic> = cast group; group.forEach(function(s) { s.selected = false; @@ -717,21 +781,21 @@ class Charter extends UIState { if (autoSaveTimer < Options.charterAutoSaveWarningTime && !autoSaveNotif.cancelled && !autoSaveNotif.showedAnimation) { if (Options.charterAutoSavesSeparateFolder) __autoSaveLocation = __diff.toLowerCase() + DateTools.format(Date.now(), "%m-%d_%H-%M"); - autoSaveNotif.startAutoSave(autoSaveTimer, - !Options.charterAutoSavesSeparateFolder ? 'Saved chart at ${__diff.toLowerCase()}.json!' : + autoSaveNotif.startAutoSave(autoSaveTimer, + !Options.charterAutoSavesSeparateFolder ? 'Saved chart at ${__diff.toLowerCase()}.json!' : 'Saved chart at $__autoSaveLocation.json!' ); } if (autoSaveTimer <= 0) { autoSaveTimer = Options.charterAutoSaveTime; if (!autoSaveNotif.cancelled) { - buildChart(); + buildChart(); var songPath:String = '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}'; - + if (Options.charterAutoSavesSeparateFolder) - Chart.save(songPath, PlayState.SONG, __autoSaveLocation, {saveMetaInChart: false, folder: "autosaves", prettyPrint: Options.editorPrettyPrint}); - else - Chart.save(songPath, PlayState.SONG, __diff.toLowerCase(), {saveMetaInChart: false, prettyPrint: Options.editorPrettyPrint}); + Chart.save(songPath, PlayState.SONG, __autoSaveLocation, {saveMetaInChart: true, saveLocalEvents: true, saveGlobalEvents: true, folder: "autosaves", prettyPrint: Options.editorPrettyPrint}); + else // These two chart saves are particular, to avoid any kind of loss, stuff like meta, global and local events will be save all together - Nex + Chart.save(songPath, PlayState.SONG, __diff.toLowerCase(), {saveMetaInChart: true, saveLocalEvents: true, saveGlobalEvents: true, prettyPrint: Options.editorPrettyPrint}); undos.save(); } autoSaveNotif.cancelled = false; @@ -745,7 +809,7 @@ class Charter extends UIState { /** * NOTE DRAG HANDLING */ - mousePos = FlxG.mouse.getWorldPosition(charterCamera); + FlxG.mouse.getWorldPosition(charterCamera, mousePos); if (!gridBackdropDummy.hoveredByChild && !FlxG.mouse.pressed) gridActionType = NONE; selectionBox.visible = false; @@ -761,14 +825,14 @@ class Charter extends UIState { if (FlxG.mouse.justReleased) isSelecting = false; } else { if (FlxG.keys.pressed.SHIFT) { - for (group in [notesGroup, eventsGroup]) { + for (group in [notesGroup, leftEventsGroup, rightEventsGroup]) { var group:FlxTypedGroup<Dynamic> = cast group; for(n in group) if (n.handleSelection(selectionBox) && selection.contains(n)) selection.remove(n); } } else if (FlxG.keys.pressed.CONTROL) { - for (group in [notesGroup, eventsGroup]) { + for (group in [notesGroup, leftEventsGroup, rightEventsGroup]) { var group:FlxTypedGroup<Dynamic> = cast group; for(n in group) if (n.handleSelection(selectionBox) && !selection.contains(n)) @@ -776,7 +840,7 @@ class Charter extends UIState { } } else { selection = []; - for (group in [notesGroup, eventsGroup]) { + for (group in [notesGroup, leftEventsGroup, rightEventsGroup]) { var group:FlxTypedGroup<Dynamic> = cast group; for(n in group) if (n.handleSelection(selectionBox)) @@ -797,15 +861,18 @@ class Charter extends UIState { if (selectionDragging) { gridBackdrops.draggingObj = null; selection.loop(function (n:CharterNote) { - n.snappedToStrumline = false; + n.snappedToGrid = false; n.setPosition(n.fullID * 40 + (mousePos.x - dragStartPos.x), n.step * 40 + (mousePos.y - dragStartPos.y)); - n.y = FlxMath.bound(n.y, 0, (__endStep*40) - n.height); - n.x = FlxMath.bound(n.x, 0, ((strumLines.members.length * 4)-1) * 40); + n.y = CoolUtil.bound(n.y, 0, (__endStep*40) - n.height); + n.x = CoolUtil.bound(n.x, 0, (strumLines.totalKeyCount-1) * 40); n.cursor = HAND; }, function (e:CharterEvent) { - e.y = e.step * 40 + (mousePos.y - dragStartPos.y) - 17; - e.y = FlxMath.bound(e.y, -17, (__endStep*40)-17); + e.snappedToGrid = false; + e.setPosition(e.eventsBackdrop.x + (e.global ? 0 : e.eventsBackdrop.width - e.bWidth) + (mousePos.x - dragStartPos.x), e.step * 40 + (mousePos.y - dragStartPos.y) - 17); + e.y = CoolUtil.bound(e.y, -17, (__endStep*40)-17); e.cursor = HAND; + + e.displayGlobal = e.x + (e.bWidth/2) > ((strumLines.totalKeyCount*40)/2); }); currentCursor = HAND; } else { @@ -829,7 +896,7 @@ class Charter extends UIState { if (s is CharterNote) { var note:CharterNote = cast s; if (note.fullID + changePoint.y < 0) boundedChange.y += Math.abs(note.fullID + changePoint.y); - if (note.fullID + changePoint.y > (strumLines.members.length*4)-1) boundedChange.y -= (note.fullID + changePoint.y) - ((strumLines.members.length*4)-1); + if (note.fullID + changePoint.y > strumLines.totalKeyCount-1) boundedChange.y -= (note.fullID + changePoint.y) - (strumLines.totalKeyCount-1); } s.handleDrag(boundedChange); @@ -837,18 +904,20 @@ class Charter extends UIState { changePoint.put(); } - if (s is CharterNote) { - var s:CharterNote = cast s; - s.snappedToStrumline = true; - } + s.snappedToGrid = true; if (s is UISprite) { var s:UISprite = cast s; - s.cursor = BUTTON; + s.cursor = CLICK; } } + checkSelectionForBPMUpdates(); if (!(verticalChange == 0 && horizontalChange == 0)) { - notesGroup.sortNotes(); eventsGroup.sortEvents(); - undos.addToUndo(CSelectionDrag(undoDrags)); + notesGroup.sortNotes(); + + undos.addToUndo(CChangeBundle([ + CSelectionDrag(undoDrags), + updateEventsGroups(selection) + ])); } gridActionType = NONE; @@ -870,7 +939,7 @@ class Charter extends UIState { } var id = Math.floor(mousePos.x / 40); - var mouseOnGrid = id >= 0 && id < 4 * gridBackdrops.strumlinesAmount && mousePos.y >= 0; + var mouseOnGrid = id >= 0 && id < strumLines.totalKeyCount && mousePos.y >= 0; if (FlxG.mouse.justReleased) { for (n in selection) n.selected = false; @@ -878,9 +947,10 @@ class Charter extends UIState { if (mouseOnGrid && mousePos.y > 0 && mousePos.y < (__endStep)*40) { var note = new CharterNote(); + var targetStrumline = strumLines.getStrumlineFromID(id); note.updatePos( - FlxMath.bound(FlxG.keys.pressed.SHIFT ? ((mousePos.y-20) / 40) : quantStep(mousePos.y/40), 0, __endStep-1), - id % 4, 0, noteType, strumLines.members[Std.int(id/4)] + CoolUtil.bound(FlxG.keys.pressed.SHIFT ? ((mousePos.y-20) / 40) : quantStep(mousePos.y/40), 0, __endStep-1), + (id-targetStrumline.startingID) % targetStrumline.keyCount, 0, noteType, targetStrumline ); notesGroup.add(note); selection = [note]; @@ -909,7 +979,7 @@ class Charter extends UIState { case SUSTAIN_DRAG: selectionDragging = FlxG.mouse.pressed; if (selectionDragging) { - currentCursor = BUTTON; + currentCursor = CLICK; selection.loop(function (n:CharterNote) { var change:Float = Math.max((mousePos.y-(FlxG.keys.pressed.SHIFT ? dragStartPos.y : quantStep(dragStartPos.y))) / 40, -n.susLength); n.tempSusLength = change; @@ -954,24 +1024,31 @@ class Charter extends UIState { var mousePos = FlxG.mouse.getScreenPosition(uiCamera); closeCurrentContextMenu(); openContextMenu(topMenu[1].childs, null, mousePos.x, mousePos.y); - mousePos.put(); } gridActionType = NONE; deletedNotes = []; } } - addEventSpr.selectable = !selectionBox.visible; + localAddEventSpr.selectable = !selectionBox.visible; + globalAddEventSpr.selectable = !selectionBox.visible; var inBoundsY:Bool = (mousePos.y > 0 && mousePos.y < (__endStep)*40); // Event Spr - if (mousePos.x < 0 && mousePos.x > -addEventSpr.bWidth && gridActionType == NONE && inBoundsY) { - addEventSpr.incorporeal = false; - addEventSpr.sprAlpha = lerp(addEventSpr.sprAlpha, 0.75, 0.25); - var event = getHoveredEvent(mousePos.y); - if (event != null) addEventSpr.updateEdit(event); - else addEventSpr.updatePos(FlxG.keys.pressed.SHIFT ? ((mousePos.y) / 40) : quantStepRounded(mousePos.y/40)); - } else addEventSpr.sprAlpha = lerp(addEventSpr.sprAlpha, 0, 0.25); + for (addEventSpr in [localAddEventSpr, globalAddEventSpr]) { + addEventSpr.incorporeal = true; + if ((!addEventSpr.global ? mousePos.x < 0 : mousePos.x > strumLines.totalKeyCount * 40) && gridActionType == NONE && inBoundsY) { + var event = getHoveredEvent(mousePos.y, !addEventSpr.global ? leftEventsGroup : rightEventsGroup); + var hoveredWidth:Float = event != null ? 27 + 40 + event.bWidth : addEventSpr.bWidth; + if ((!addEventSpr.global ? mousePos.x > -hoveredWidth : mousePos.x < strumLines.totalKeyCount * 40 + hoveredWidth)) { + addEventSpr.incorporeal = false; + + if (event != null) addEventSpr.updateEdit(event); + else addEventSpr.updatePos(FlxG.keys.pressed.SHIFT ? ((mousePos.y) / 40) : quantStepRounded(mousePos.y/40)); + } + } + addEventSpr.sprAlpha = lerp(addEventSpr.sprAlpha, !addEventSpr.incorporeal ? 0.75 : 0, 0.25); + } noteHoverer.showHoverer = Charter.instance.gridBackdropDummy.hovered; } @@ -988,9 +1065,9 @@ class Charter extends UIState { public function ratioRound(val:Float, ratio:Float):Int return Math.floor(val) + ((Math.abs(val % 1) > ratio ? 1 : 0) * (val > 0 ? 1 : -1)); - public function getHoveredEvent(y:Float) { + public function getHoveredEvent(y:Float, group:CharterEventGroup) { var eventHovered:CharterEvent = null; - eventsGroup.forEach(function(e) { + group.forEach(function(e) { if (eventHovered != null) return; @@ -1011,7 +1088,7 @@ class Charter extends UIState { note.kill(); } else if (selected is CharterEvent) { var event:CharterEvent = cast selected; - eventsGroup.remove(event); + (event.global ? rightEventsGroup : leftEventsGroup).remove(event); event.kill(); } @@ -1031,17 +1108,13 @@ class Charter extends UIState { notesGroup.add(n); }, function (e:CharterEvent) { e.revive(); - eventsGroup.add(e); + (e.global ? rightEventsGroup : leftEventsGroup).add(e); e.refreshEventIcons(); }, false); notesGroup.sortNotes(); notesGroup.autoSort = true; - for (s in selection) - if (s is CharterEvent) { - Charter.instance.updateBPMEvents(); - break; - } + checkSelectionForBPMUpdates(); if (addToUndo) undos.addToUndo(CCreateSelection(selection)); @@ -1052,7 +1125,7 @@ class Charter extends UIState { if (selection.length <= 0) return []; notesGroup.autoSort = false; - for (group in [notesGroup, eventsGroup]) { + for (group in [notesGroup, leftEventsGroup, rightEventsGroup]) { var group:FlxTypedGroup<Dynamic> = cast group; var member:Int = 0; while(member < group.members.length) { @@ -1065,21 +1138,38 @@ class Charter extends UIState { notesGroup.sortNotes(); notesGroup.autoSort = true; - for (s in selection) - if (s is CharterEvent) { - Charter.instance.updateBPMEvents(); - break; - } + checkSelectionForBPMUpdates(); if (addToUndo) undos.addToUndo(CDeleteSelection(selection)); return []; } + public function updateEventsGroups(selection:Selection):CharterChange { + if (selection.length <= 0) return CEditEventGroups([]); + var eventsChanged:Array<CharterEvent> = []; + + selection.loop(function (n:CharterNote) {}, function (e:CharterEvent) { + if (e.displayGlobal != e.global) { + (e.global ? rightEventsGroup : leftEventsGroup).remove(e); + (e.displayGlobal ? rightEventsGroup : leftEventsGroup).add(e); + + e.global = e.displayGlobal; + eventsChanged.push(e); + } + }, true); + + leftEventsGroup.sortEvents(); rightEventsGroup.sortEvents(); + for (e in eventsChanged) e.update(0); // remove little stutter + + return CEditEventGroups(eventsChanged); + } + // STRUMLINE DELETION/CREATION public function createStrumline(strumLineID:Int, strL:ChartStrumLine, addToUndo:Bool = true, ?__createNotes:Bool = true) { var cStr = new CharterStrumline(strL); strumLines.insert(strumLineID, cStr); + strumLines.refreshStrumlineIDs(); strumLines.snapStrums(); if (__createNotes) { @@ -1093,6 +1183,8 @@ class Charter extends UIState { createSelection(toBeCreated, false); } + updateBookmarks(); + if (addToUndo) undos.addToUndo(CCreateStrumLine(strumLineID, strL)); } @@ -1113,8 +1205,11 @@ class Charter extends UIState { var strL = strumLines.members[strumLineID].strumLine; strumLines.members[strumLineID].destroy(); strumLines.members.remove(strumLines.members[strumLineID]); + strumLines.refreshStrumlineIDs(); strumLines.snapStrums(); + updateBookmarks(); + if (addToUndo) { var newStrL = Reflect.copy(strL); newStrL.notes = undoNotes; @@ -1152,6 +1247,7 @@ class Charter extends UIState { FlxG.state.openSubState(new CharterStrumlineScreen(strID, strL, (_) -> { strumLines.members[strID].strumLine = _; strumLines.members[strID].updateInfo(); + strumLines.refreshStrumlineIDs(); undos.addToUndo(CEditStrumLine(strID, oldData, _)); @@ -1174,17 +1270,37 @@ class Charter extends UIState { var __crochet:Float; var __firstFrame:Bool = true; + var __timer:Float = 0; public override function update(elapsed:Float) { + if (Options.charterRainbowWaveforms) { + __timer += elapsed/8; + for (shader in waveformHandler.waveShaders) + shader.data.time.value = [__timer]; + } + updateNoteLogic(elapsed); updateAutoSaving(elapsed); + for (bs in __bookmarkObjects) + { + var bars:Array<FlxSprite> = bs[0]; + var text:UIText = bs[1]; + if (bars == null || bars.length == 0) continue; + for (i => spr in bars) { + if (spr == null || strumLines.members[i] == null) continue; + spr.x = strumLines.members[i].x; + } + if (text != null) + text.x = strumLines.members[0].x + 4; + } + if (FlxG.sound.music.playing || __firstFrame) { gridBackdrops.conductorSprY = curStepFloat * 40; } else { gridBackdrops.conductorSprY = lerp(gridBackdrops.conductorSprY, curStepFloat * 40, __firstFrame ? 1 : 1/3); } charterCamera.scroll.set( - ((((40*4) * gridBackdrops.strumlinesAmount) - FlxG.width) / 2), + lerp(charterCamera.scroll.x, (((40*strumLines.totalKeyCount) - FlxG.width) / 2) + sideScroll, __firstFrame ? 1 : 1/3), gridBackdrops.conductorSprY - (FlxG.height * 0.5) ); @@ -1197,7 +1313,6 @@ class Charter extends UIState { var snapButton:UITopMenuButton = cast topMenuSpr.members[snapIndex]; var lastButtonX = playBackButton.x-10; - var buttonI:Int = 0; for (button in quantButtons) { button.visible = ((button.quant == quant) || (button.quant == quants[FlxMath.wrap(quants.indexOf(quant)-1, 0, quants.length-1)]) || @@ -1232,23 +1347,27 @@ class Charter extends UIState { if (true) { __crochet = ((60 / Conductor.bpm) * 1000); - if(FlxG.keys.justPressed.ANY && !strumLines.isDragging && this.currentFocus == null) + if(FlxG.keys.justPressed.ANY && !strumLines.isDragging && this.currentFocus == null && (this.subState == null || !(this.subState is CharterEventScreenNew))) UIUtil.processShortcuts(topMenu); - if (FlxG.keys.pressed.CONTROL) { - if (FlxG.mouse.wheel != 0) { - zoom += 0.25 * FlxG.mouse.wheel; - __camZoom = Math.pow(2, zoom); - } - } else { - if (!FlxG.sound.music.playing && FlxG.mouse.wheel != 0) { - Conductor.songPosition -= (__crochet * FlxG.mouse.wheel) - Conductor.songOffset; + if (!topMenuSpr.anyMenuOpened) { + if (FlxG.mouse.wheel != 0 && shouldScroll) { + if (FlxG.keys.pressed.CONTROL) { + zoom += 0.25 * FlxG.mouse.wheel; + __camZoom = Math.pow(2, zoom); + } else if (FlxG.keys.pressed.SHIFT) { + sideScroll -= 40 * FlxG.mouse.wheel; + } else { + if (!FlxG.sound.music.playing) { + Conductor.songPosition -= (__crochet * FlxG.mouse.wheel) - Conductor.songOffset; + } + } } } } var songLength = FlxG.sound.music.getDefault(vocals).length; - Conductor.songPosition = FlxMath.bound(Conductor.songPosition + Conductor.songOffset, 0, songLength); + Conductor.songPosition = CoolUtil.bound(Conductor.songPosition + Conductor.songOffset, 0, songLength); if (Conductor.songPosition >= songLength - Conductor.songOffset) { FlxG.sound.music.pause(); @@ -1256,12 +1375,13 @@ class Charter extends UIState { for (strumLine in strumLines.members) strumLine.vocals.pause(); } - songPosInfo.text = '${CoolUtil.timeToStr(Conductor.songPosition)} / ${CoolUtil.timeToStr(songLength)}' - + '\nStep: ${curStep}' - + '\nBeat: ${curBeat}' - + '\nMeasure: ${curMeasure}' - + '\nBPM: ${Conductor.bpm}' - + '\nTime Signature: ${Conductor.beatsPerMeasure}/${Conductor.stepsPerBeat}'; + var curChange = Conductor.curChange; + songPosInfo.text = '${CoolUtil.timeToStr(songPos)} / ${CoolUtil.timeToStr(songLength)}' + + '\nStep: $curStep' + + '\nBeat: $curBeat' + + '\nMeasure: $curMeasure' + + '\nBPM: ${(curChange != null && curChange.continuous && curChange.endSongTime > songPos) ? FlxMath.roundDecimal(Conductor.bpm, 3) : Conductor.bpm}' + + '\nTime Signature: ${Conductor.beatsPerMeasure}/${Conductor.denominator}'; if (charterCamera.zoom != (charterCamera.zoom = lerp(charterCamera.zoom, __camZoom, __firstFrame ? 1 : 0.125))) updateDisplaySprites(); @@ -1281,7 +1401,15 @@ class Charter extends UIState { function updateDisplaySprites() { gridBackdrops.strumlinesAmount = strumLines.members.length; - charterBG.scale.set(1 / charterCamera.zoom, 1 / charterCamera.zoom); + var scaleX:Float = (FlxG.width/charterBG.width); + var scaleY:Float = (FlxG.height/charterBG.height); + + var bgScale:Float = scaleX > scaleY ? scaleX : scaleY; + + charterBG.scale.set( + (1 / charterCamera.zoom) * bgScale, + (1 / charterCamera.zoom) * bgScale + ); strumlineInfoBG.scale.set(FlxG.width / charterCamera.zoom, 1); strumlineInfoBG.updateHitbox(); @@ -1291,22 +1419,141 @@ class Charter extends UIState { for(id=>str in strumLines.members) if (str != null) str.y = strumlineInfoBG.y; - strumlineAddButton.x = 0; + //strumlineAddButton.x = 0; strumlineAddButton.y = strumlineInfoBG.y; strumlineLockButton.y = strumlineInfoBG.y; strumlineLockButton.text.visible = strumlineLockButton.button.selectable = strumlineLockButton.button.visible = strumLines.members.length > 0; } + public override function onResize(width:Int, height:Int) { + super.onResize(width, height); + if (!UIState.resolutionAware) return; + + if (width < FlxG.initialWidth || height < FlxG.initialHeight) { + width = FlxG.initialWidth; height = FlxG.initialHeight; + } + + scrollBar.x = width - 20; + scrollBar.scale.y = Std.int(height - scrollBar.y); + scrollBar.updateHitbox(); + + songPosInfo.x = width - 30 - 400; + playBackSlider.x = width - 160 - 26 - 20; + + updateDisplaySprites(); + updateBookmarks(); + charterBG.screenCenter(); + } + var zoom(default, set):Float = 0; var __camZoom(default, set):Float = 1; function set_zoom(val:Float) { - return zoom = FlxMath.bound(val, -3.5, 1.75); // makes zooming not lag behind when continuing scrolling + return zoom = CoolUtil.bound(val, -3.5, 1.75); // makes zooming not lag behind when continuing scrolling } function set___camZoom(val:Float) { - return __camZoom = FlxMath.bound(val, 0.1, 3); + return __camZoom = CoolUtil.bound(val, 0.1, 3); } + var sideScroll(default, set):Float = 0; + function set_sideScroll(val:Float) { + return sideScroll = FlxMath.bound(val, -(40*strumLines.totalKeyCount) / 2, (40*strumLines.totalKeyCount) / 2); + } + + // SAVE FUNCS + #if REGION + public static function saveEverything(shouldBuild:Bool = true) { + if (shouldBuild && instance != null) instance.buildChart(); + saveChart(false); + saveEvents(false); + saveMeta(false); + } + + public static function saveChart(shouldBuild:Bool = true, withEvents:Bool = true) { + #if sys + saveTo('${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}', !withEvents, shouldBuild); + if (undos != null) undos.save(); + #else + saveChartAs(shouldBuild, withEvents); + #end + } + + public static function saveChartAs(shouldBuild:Bool = true, withEvents:Bool = true) { + saveAs(Chart.filterChartForSaving(PlayState.SONG, false, withEvents, false), null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null, { + defaultSaveFile: '${__diff.toLowerCase()}.json' + }, null, shouldBuild); + if (undos != null) undos.save(); + } + + public static function saveEvents(shouldBuild:Bool = true) { + #if sys + if (shouldBuild && instance != null) instance.buildChart(); + var data = {events: Chart.filterChartForSaving(PlayState.SONG, false, false, true).events}; + + var path = '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}/events.json'; + if (data.events != null && data.events.length > 0) CoolUtil.safeSaveFile(path, Json.stringify(data, null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null)); + else if (FileSystem.exists(path)) FileSystem.deleteFile(path); // Instead of replacing with a useless empty file, deletes the file directly - Nex + #else + saveEventsAs(shouldBuild); + #end + } + + public static function saveEventsAs(shouldBuild:Bool = true) { + if (shouldBuild && instance != null) instance.buildChart(); + var data = {events: Chart.filterChartForSaving(PlayState.SONG, false, false, true).events}; + + saveAs(data, null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null, { + defaultSaveFile: 'events.json' + }, null, false); + } + + public static function saveMeta(shouldBuild:Bool = true) { + #if sys + if (shouldBuild && instance != null) instance.buildChart(); + CoolUtil.safeSaveFile( + '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}/meta.json', + Json.stringify(PlayState.SONG.meta == null ? {} : Chart.filterChartForSaving(PlayState.SONG, true, false, false).meta, null, Flags.JSON_PRETTY_PRINT) + ); + #else + saveMetaAs(shouldBuild); + #end + } + + public static function saveMetaAs(shouldBuild:Bool = true) { + saveAs(PlayState.SONG.meta == null ? {} : Chart.filterChartForSaving(PlayState.SONG, true, false, false).meta, null, Flags.JSON_PRETTY_PRINT, { // always pretty print meta + defaultSaveFile: 'meta.json' + }, null, shouldBuild); + } + + public static function saveLegacyChartAs(shouldBuild:Bool = true) { + saveAs(FNFLegacyParser.encode(PlayState.SONG), null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null, { + defaultSaveFile: '${__song.toLowerCase().replace(" ", "-")}${__diff.toLowerCase() == Flags.DEFAULT_DIFFICULTY ? "" : '-${__diff.toLowerCase()}'}.json', + }, null, shouldBuild); + } + + public static function savePsychChartAs(shouldBuild:Bool = true) { + saveAs(PsychParser.encode(PlayState.SONG), null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null, { + defaultSaveFile: '${__song.toLowerCase().replace(" ", "-")}${__diff.toLowerCase() == Flags.DEFAULT_DIFFICULTY ? "" : '-${__diff.toLowerCase()}'}.json', + }, null, shouldBuild); + } + + public static function saveAs(data:Dynamic, ?replacer:(key:Dynamic, value:Dynamic) -> Dynamic, ?space:String, ?options:SaveSubstate.SaveSubstateData, ?saveOptions:Map<String, Bool>, shouldBuild:Bool = true) { + if (shouldBuild && instance != null) instance.buildChart(); + var cur = FlxG.state; + while(true) { + if (instance != null || cur.subState == null) return cur.openSubState(new SaveSubstate(Json.stringify(data, replacer, space), options, saveOptions)); + else cur = cur.subState; + } + } + + #if sys + public static function saveTo(path:String, separateEvents:Bool = false, shouldBuild:Bool = true) { + if (shouldBuild && instance != null) instance.buildChart(); + Chart.save(path, PlayState.SONG, __diff.toLowerCase(), {saveMetaInChart: false, saveLocalEvents: !separateEvents, prettyPrint: Options.editorPrettyPrint}); + } + #end + #end + // TOP MENU OPTIONS #if REGION function _file_exit(_) { @@ -1314,92 +1561,17 @@ class Charter extends UIState { else {undos = null; FlxG.switchState(new CharterSelection()); Charter.instance.__clearStatics();} } - function _file_save(_) { - #if sys - saveTo('${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}'); - undos.save(); - return; - #end - _file_saveas(_); - } - - function _file_saveas(_) { - openSubState(new SaveSubstate(Json.stringify(Chart.filterChartForSaving(PlayState.SONG, false), null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null), { - defaultSaveFile: '${__diff.toLowerCase()}.json' - })); - undos.save(); - } - - function _file_save_no_events(_) { - #if sys - saveTo('${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}', true); - undos.save(); - return; - #end - _file_saveas(_); - } - - function _file_saveas_no_events(_) { - openSubState(new SaveSubstate(Json.stringify(Chart.filterChartForSaving(PlayState.SONG, false, false), null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null), { - defaultSaveFile: '${__diff.toLowerCase()}.json' - })); - undos.save(); - } - - function _file_meta_save(_) { - #if sys - CoolUtil.safeSaveFile( - '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}/meta.json', - PlayState.SONG.meta == null ? null : Chart.makeMetaSaveable(PlayState.SONG.meta) - ); - #else - _file_meta_saveas(_); - #end - } - - function _file_meta_saveas(_) { - openSubState(new SaveSubstate(PlayState.SONG.meta == null ? null : Chart.makeMetaSaveable(PlayState.SONG.meta), { // always pretty print meta - defaultSaveFile: 'meta.json' - })); - } - - function _file_saveas_fnflegacy(_) { - openSubState(new SaveSubstate(Json.stringify(FNFLegacyParser.encode(PlayState.SONG), null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null), { - defaultSaveFile: '${__song.toLowerCase().replace(" ", "-")}${__diff.toLowerCase() == Flags.DEFAULT_DIFFICULTY ? "" : '-${__diff.toLowerCase()}'}.json', - })); - } - - function _file_saveas_psych(_) { - openSubState(new SaveSubstate(Json.stringify(PsychParser.encode(PlayState.SONG), null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null), { - defaultSaveFile: '${__song.toLowerCase().replace(" ", "-")}${__diff.toLowerCase() == Flags.DEFAULT_DIFFICULTY ? "" : '-${__diff.toLowerCase()}'}.json', - })); - } - - function _file_events_save(_) { - #if sys - CoolUtil.safeSaveFile( - '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}/events.json', - Json.stringify({events: PlayState.SONG.events == null ? [] : PlayState.SONG.events}, null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null) - ); - #else - _file_events_saveas(_); - #end - } - - function _file_events_saveas(_) { - #if sys - openSubState(new SaveSubstate(Json.stringify({events: PlayState.SONG.events == null ? [] : PlayState.SONG.events}, null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null), { - defaultSaveFile: 'events.json' - })); - #end - } - - #if sys - function saveTo(path:String, separateEvents:Bool = false) { - buildChart(); - Chart.save(path, PlayState.SONG, __diff.toLowerCase(), {saveMetaInChart: false, saveEventsInChart: !separateEvents, prettyPrint: Options.editorPrettyPrint}); - } - #end + function _file_save_all(_) saveEverything(); + function _file_save(_) saveChart(); + function _file_saveas(_) saveChartAs(); + function _file_events_save(_) saveEvents(); + function _file_events_saveas(_) saveEventsAs(); + function _file_save_no_events(_) saveChart(true, false); + function _file_saveas_no_events(_) saveChartAs(true, false); + function _file_meta_save(_) saveMeta(); + function _file_meta_saveas(_) saveMetaAs(); + function _file_saveas_fnflegacy(_) saveLegacyChartAs(); + function _file_saveas_psych(_) savePsychChartAs(); function _edit_copy(_) { if(selection.length == 0) return; @@ -1415,7 +1587,7 @@ class Charter extends UIState { CNote(note.step - minStep, note.id, note.strumLineID, note.susLength, note.type); } else if (s is CharterEvent) { var event:CharterEvent = cast s; - CEvent(event.step - minStep, [for (event in event.events) Reflect.copy(event)]); + CEvent(event.step - minStep, [for (event in event.events) Reflect.copy(event)], event.global); } ]; } @@ -1428,19 +1600,21 @@ class Charter extends UIState { switch(c) { case CNote(step, id, strumLineID, susLength, type): var note = new CharterNote(); - note.updatePos(minStep + step, id, susLength, type, strumLines.members[Std.int(FlxMath.bound(strumLineID, 0, strumLines.length-1))]); + note.updatePos(minStep + step, id, susLength, type, strumLines.members[CoolUtil.boundInt(strumLineID, 0, strumLines.length-1)]); notesGroup.add(note); sObjects.push(note); - case CEvent(step, events): - var event = new CharterEvent(minStep + step, events); + case CEvent(step, events, global): + var event = new CharterEvent(minStep + step, events, global); event.refreshEventIcons(); - eventsGroup.add(event); + (global ? rightEventsGroup : leftEventsGroup).add(event); sObjects.push(event); } } selection = sObjects; _edit_copy(_); // to fix stupid bugs + checkSelectionForBPMUpdates(); + undos.addToUndo(CCreateSelection(sObjects.copy())); } @@ -1459,11 +1633,7 @@ class Charter extends UIState { selection = deleteSelection(selection, true); } - function _edit_undo(_) { - if (strumLines.isDragging || selectionDragging || (subState != null && !(subState is UIContextMenu))) return; - - selection = []; - var undo = undos.undo(); + function _undo(undo:CharterChange) { switch(undo) { case null: // do nothing case CDeleteStrumLine(strumLineID, strumLine): @@ -1477,6 +1647,7 @@ class Charter extends UIState { case CEditStrumLine(strumLineID, oldStrumLine, newStrumLine): strumLines.members[strumLineID].strumLine = oldStrumLine; strumLines.members[strumLineID].updateInfo(); + strumLines.refreshStrumlineIDs(); case CCreateSelection(selection): deleteSelection(selection, false); case CDeleteSelection(selection): @@ -1486,6 +1657,7 @@ class Charter extends UIState { if (s.selectable.draggable) s.selectable.handleDrag(s.change * -1); selection = [for (s in selectionDrags) s.selectable]; + checkSelectionForBPMUpdates(); case CEditSustains(changes): for(n in changes) n.note.updatePos(n.note.step, n.note.id, n.before, n.note.type); @@ -1494,22 +1666,34 @@ class Charter extends UIState { event.refreshEventIcons(); Charter.instance.updateBPMEvents(); + case CEditEventGroups(events): + for (event in events) event.displayGlobal = !event.global; + updateEventsGroups(cast events); + case CEditChartData(oldData, newData): PlayState.SONG.stage = oldData.stage; PlayState.SONG.scrollSpeed = oldData.speed; case CEditNoteTypes(oldArray, newArray): noteTypes = oldArray; changeNoteType(null, false); + case CEditBookmarks(oldArray, newArray): + PlayState.SONG.bookmarks = oldArray; + updateBookmarks(); case CEditSpecNotesType(notes, oldTypes, newTypes): for(i=>note in notes) note.updatePos(note.step, note.id, note.susLength, oldTypes[i]); + case CChangeBundle(changes): + for (change in changes) _undo(change); } } - function _edit_redo(_) { + function _edit_undo(_) { if (strumLines.isDragging || selectionDragging || (subState != null && !(subState is UIContextMenu))) return; selection = []; - var redo = undos.redo(); + _undo(undos.undo()); + } + + function _redo(redo:CharterChange) { switch(redo) { case null: // do nothing case CDeleteStrumLine(strumLineID, strumLine): @@ -1523,6 +1707,7 @@ class Charter extends UIState { case CEditStrumLine(strumLineID, oldStrumLine, newStrumLine): strumLines.members[strumLineID].strumLine = newStrumLine; strumLines.members[strumLineID].updateInfo(); + strumLines.refreshStrumlineIDs(); case CCreateSelection(selection): createSelection(selection, false); case CDeleteSelection(selection): @@ -1531,6 +1716,7 @@ class Charter extends UIState { for (s in selectionDrags) if (s.selectable.draggable) s.selectable.handleDrag(s.change); //this.selection = selection; + checkSelectionForBPMUpdates(); case CEditSustains(changes): for(n in changes) n.note.updatePos(n.note.step, n.note.id, n.after, n.note.type); @@ -1539,17 +1725,33 @@ class Charter extends UIState { event.refreshEventIcons(); Charter.instance.updateBPMEvents(); + case CEditEventGroups(events): + for (event in events) event.displayGlobal = !event.global; + updateEventsGroups(cast events); + case CEditChartData(oldData, newData): PlayState.SONG.stage = newData.stage; PlayState.SONG.scrollSpeed = newData.speed; case CEditNoteTypes(oldArray, newArray): noteTypes = newArray; changeNoteType(null, false); + case CEditBookmarks(oldArray, newArray): + PlayState.SONG.bookmarks = newArray; + updateBookmarks(); case CEditSpecNotesType(notes, oldTypes, newTypes): for(i=>note in notes) note.updatePos(note.step, note.id, note.susLength, newTypes[i]); + case CChangeBundle(changes): + for (change in changes) _redo(change); } } + function _edit_redo(_) { + if (strumLines.isDragging || selectionDragging || (subState != null && !(subState is UIContextMenu))) return; + + selection = []; + _redo(undos.redo()); + } + inline function _chart_playtest(_) playtestChart(0, false); inline function _chart_playtest_here(_) @@ -1629,6 +1831,171 @@ class Charter extends UIState { if (FlxG.sound.music.playing) return; Conductor.songPosition = FlxG.sound.music.length; } + + public function getBookmarkList():Array<ChartBookmark> { + var bookmarks:Array<ChartBookmark> = []; + try { + if (PlayState.SONG.bookmarks != null) + bookmarks = PlayState.SONG.bookmarks; + } catch (e) {} + + return bookmarks; + } + + function _bookmarks_add(_) { + var addBookmarkAt = function(name:String, color:FlxColor, daStep:Float) + { + var currentBookmarks:Array<ChartBookmark> = getBookmarkList(); + var newBookmarks:Array<ChartBookmark> = getBookmarkList(); + newBookmarks.push({time: daStep, name: name, color: color.toWebString()}); + + PlayState.SONG.bookmarks = newBookmarks; + updateBookmarks(); + undos.addToUndo(CEditBookmarks(currentBookmarks, newBookmarks)); + } + + if (FlxG.keys.pressed.SHIFT) + addBookmarkAt("New Bookmark", 0xFF911DD9, curStepFloat); + else { + FlxG.state.openSubState(new CharterBookmarkCreation(curStepFloat, (success, n, c, s) -> { + if (success) + addBookmarkAt(n, c, s); + })); + } + } + function _bookmarks_edit_list(_) + FlxG.state.openSubState(new CharterBookmarkList()); //idk why its FlxG.state but it looks so off lmfao + + public var __bookmarkObjects:Array<Dynamic> = []; + public var __scrollbarBookmarks:Array<Dynamic> = []; + public function updateBookmarks() + { + for (bs in __bookmarkObjects) + { + var bars:Array<FlxSprite> = bs[0]; + var text:UIText = bs[1]; + + if (bars != null) { + for (spr in bars) { + if (spr == null) continue; + remove(spr); + spr.kill(); + } + } + if (text != null) { + remove(text); + text.kill(); + } + } + for (bar in __scrollbarBookmarks) { + if (bar == null) continue; + uiGroup.remove(bar); + bar.kill(); + } + __bookmarkObjects.clear(); + __scrollbarBookmarks.clear(); + for (b in getBookmarkList()) + { + var bookmarkcolor:FlxColor = b.color != null ? FlxColor.fromString(b.color) : 0xff9d00ff; + var luminance = CoolUtil.getLuminance(bookmarkcolor); + + var sprites = []; + for (str in strumLines.members) + { + var bookmarkspr = new FlxSprite(str.x, (b.time * 40)).makeSolid(str.keyCount * 40, 4, bookmarkcolor); + bookmarkspr.updateHitbox(); + bookmarkspr.camera = charterCamera; + add(bookmarkspr); + sprites.push(bookmarkspr); + } + + var bookmarkText = new UIText(strumLines.members[0].x + 4, 0, 400, b.name, 15, bookmarkcolor, true); + bookmarkText.y = sprites[0].y - (bookmarkText.height + 2); + bookmarkText.camera = charterCamera; + add(bookmarkText); + + if (luminance < 0.5) + bookmarkText.borderColor = 0x88FFFFFF; + + __bookmarkObjects.push([sprites, bookmarkText]); + + var yPos = scrollBar.y + CoolUtil.bound( + FlxMath.remapToRange( + b.time, + 0, + scrollBar.length + scrollBar.size, + 0, + scrollBar.height + ), + 0, + scrollBar.height + ); + + var bookmarkspr = new FlxSprite(scrollBar.x - 10, yPos).makeSolid(40, 4, bookmarkcolor); + uiGroup.add(bookmarkspr); + sprites.push(bookmarkspr); + __scrollbarBookmarks.push(bookmarkspr); + } + + buildSongUI(); + } + + function buildSongUI():Array<UIContextMenuOption> { + var songTopButton:UITopMenuButton = topMenuSpr == null ? null : cast topMenuSpr.members[songIndex]; + var newChilds:Array<UIContextMenuOption> = [ + { + label: "Go back to the start", + keybind: [HOME], + onSelect: _song_start + }, + { + label: "Go to the end", + keybind: [END], + onSelect: _song_end + }, + null, + { + label: "Add Bookmark Here", + onSelect: _bookmarks_add + }, + { + label: "Edit Bookmark List", + color: 0xFF959829, icon: 4, + onCreate: function (button:UIContextMenuOptionSpr) {button.label.offset.x = button.icon.offset.x = -2; button.icon.offset.y = -1;}, + onSelect: _bookmarks_edit_list + }, + null + ]; + + var bookmarks:Array<ChartBookmark> = getBookmarkList(); + + if (bookmarks.length > 0) + { + for (b in bookmarks) + { + newChilds.push({ + label: "Go To \"" + b.name + "\"", + onSelect: function(_) { Conductor.songPosition = Conductor.getTimeForStep(b.time); } + }); + } + newChilds.push(null); + } + + + newChilds.push({ + label: "Mute instrumental", + onSelect: _song_muteinst + }); + + newChilds.push({ + label: "Mute voices", + onSelect: _song_mutevoices + }); + + if (songTopButton != null) songTopButton.contextMenu = newChilds; + return newChilds; + } + function _view_zoomin(_) { zoom += 0.25; __camZoom = Math.pow(2, zoom); @@ -1643,17 +2010,31 @@ class Charter extends UIState { } function _view_showeventSecSeparator(t) { t.icon = (Options.charterShowSections = !Options.charterShowSections) ? 1 : 0; - eventsBackdrop.eventSecSeparator.visible = gridBackdrops.sectionsVisible = Options.charterShowSections; } function _view_showeventBeatSeparator(t) { t.icon = (Options.charterShowBeats = !Options.charterShowBeats) ? 1 : 0; - eventsBackdrop.eventBeatSeparator.visible = gridBackdrops.beatsVisible = Options.charterShowBeats; + } + function _view_switchWaveformRainbow(t) { + t.icon = (Options.charterRainbowWaveforms = !Options.charterRainbowWaveforms) ? 1 : 0; + + waveformHandler.clearWaveforms(); + updateWaveforms(); } function _view_switchWaveformDetail(t) { t.icon = (Options.charterLowDetailWaveforms = !Options.charterLowDetailWaveforms) ? 1 : 0; for (shader in waveformHandler.waveShaders) shader.data.lowDetail.value = [Options.charterLowDetailWaveforms]; } + function _view_scrollleft(_) { + sideScroll -= 40; + } + function _view_scrollright(_) { + sideScroll += 40; + } + function _view_scrollreset(_) { + sideScroll = 0; + } + inline function _snap_increasesnap(_) changequant(1); inline function _snap_decreasesnap(_) changequant(-1); inline function _snap_resetsnap(_) setquant(16); @@ -1726,7 +2107,7 @@ class Charter extends UIState { inline public function changeNoteType(?newID:Int, checkSelection:Bool = true) { if(newID != null) noteType = newID; - noteType = Std.int(FlxMath.bound(noteType, 0, noteTypes.length)); + noteType = CoolUtil.boundInt(noteType, 0, noteTypes.length); buildNoteTypesUI(); var changedNotes:{notes:Array<CharterNote>, oldTypes:Array<Int>, newTypes:Array<Int>} = {notes:[], oldTypes:[], newTypes:[]}; @@ -1823,6 +2204,7 @@ class Charter extends UIState { public function buildChart() { PlayState.SONG.strumLines = []; PlayState.SONG.noteTypes = this.noteTypes; + PlayState.SONG.chartVersion = Chart.version; for(s in strumLines) { s.strumLine.notes = []; PlayState.SONG.strumLines.push(s.strumLine); @@ -1837,24 +2219,50 @@ class Charter extends UIState { public function buildEvents() { PlayState.SONG.events = []; - eventsGroup.sortEvents(); - for(e in eventsGroup.members) { - for(event in e.events) { - event.time = Conductor.getTimeForStep(e.step); - PlayState.SONG.events.push(event); - } + + var events:Array<CharterEvent> = leftEventsGroup.members.concat(rightEventsGroup.members); + events.sort(rightEventsGroup.sortEventsFilter.bind(FlxSort.ASCENDING)); + for (e in events) for (event in e.events) { + event.time = Conductor.getTimeForStep(e.step); + PlayState.SONG.events.push(event); } } public function updateBPMEvents() { + leftEventsGroup.sortEvents(); + rightEventsGroup.sortEvents(); + Conductor.mapCharterBPMChanges(PlayState.SONG); buildEvents(); - Conductor.mapBPMChanges(PlayState.SONG); - Conductor.changeBPM(PlayState.SONG.meta.bpm, cast PlayState.SONG.meta.beatsPerMeasure.getDefault(4), cast PlayState.SONG.meta.stepsPerBeat.getDefault(4)); + for(e in leftEventsGroup.members) { + for(event in e.events) { + if (event.name == "BPM Change" || event.name == "Time Signature Change" || event.name == "Continuous BPM Change") { + e.refreshEventIcons(); + break; + } + } + } + + for(e in rightEventsGroup.members) { + for(event in e.events) { + if (event.name == "BPM Change" || event.name == "Time Signature Change" || event.name == "Continuous BPM Change") { + e.refreshEventIcons(); + break; + } + } + } refreshBPMSensitive(); } + public inline function checkSelectionForBPMUpdates() { + for (s in selection) + if (s is CharterEvent) { + updateBPMEvents(); + break; + } + } + public inline function hitsoundsEnabled(id:Int) return strumLines.members[id] != null && strumLines.members[id].hitsounds; @@ -1869,8 +2277,10 @@ class Charter extends UIState { @:noCompletion public function __relinkSingleSelection(selectable:ICharterSelectable):ICharterSelectable { if (selectable is CharterNote) return selectable.ID == -1 ? cast(selectable, CharterNote) : notesGroup.members[selectable.ID]; - else if (selectable is CharterEvent) - return selectable.ID == -1 ? cast(selectable, CharterEvent) : eventsGroup.members[selectable.ID]; + else if (selectable is CharterEvent) { + var event:CharterEvent = cast(selectable, CharterEvent); + return selectable.ID == -1 ? event : (event.global ? rightEventsGroup : leftEventsGroup).members[selectable.ID]; + } return null; } @@ -1979,14 +2389,18 @@ enum CharterChange { CSelectionDrag(selectionDrags:Array<SelectionDragChange>); CEditSustains(notes:Array<NoteSustainChange>); CEditEvent(event:CharterEvent, oldEvents:Array<ChartEvent>, newEvents:Array<ChartEvent>); + CEditEventGroups(events:Array<CharterEvent>); CEditChartData(oldData:{stage:String, speed:Float}, newData:{stage:String, speed:Float}); CEditNoteTypes(oldArray:Array<String>, newArray:Array<String>); + CEditBookmarks(oldArray:Array<ChartBookmark>, newArray:Array<ChartBookmark>); CEditSpecNotesType(notes:Array<CharterNote>, oldNoteTypes:Array<Int>, newNoteTypes:Array<Int>); + + CChangeBundle(changes:Array<CharterChange>); } enum CharterCopyboardObject { CNote(step:Float, id:Int, strumLineID:Int, susLength:Float, type:Int); - CEvent(step:Float, events:Array<ChartEvent>); + CEvent(step:Float, events:Array<ChartEvent>, global:Bool); } typedef NoteSustainChange = { @@ -2024,6 +2438,7 @@ interface ICharterSelectable { public var selected:Bool; public var hovered:Bool; public var draggable:Bool; + public var snappedToGrid:Bool; public function handleSelection(selectionBox:UISliceSprite):Bool; public function handleDrag(change:FlxPoint):Void; diff --git a/source/funkin/editors/charter/CharterAutoSaveUI.hx b/source/funkin/editors/charter/CharterAutoSaveUI.hx index 0e77c4dd..37391f1a 100644 --- a/source/funkin/editors/charter/CharterAutoSaveUI.hx +++ b/source/funkin/editors/charter/CharterAutoSaveUI.hx @@ -39,7 +39,7 @@ class CharterAutoSaveUI extends UISliceSprite { progressBar.antialiasing = false; progressBar.clipRect = new FlxRect(0, 0, progressBar.width, progressBar.height); members.push(progressBar); - cancelButton = new UIButton(x-(10+16), y+8, "", () -> { + cancelButton = new UIButton(x-(10+16), y+8, null, () -> { if (cancelled || !showedAnimation) return; cancelled = true; __timer.cancel(); __tween.cancel(); diff --git a/source/funkin/editors/charter/CharterBackdropGroup.hx b/source/funkin/editors/charter/CharterBackdropGroup.hx index 481bd76b..a4e35ce6 100644 --- a/source/funkin/editors/charter/CharterBackdropGroup.hx +++ b/source/funkin/editors/charter/CharterBackdropGroup.hx @@ -4,16 +4,45 @@ import flixel.addons.display.FlxBackdrop; import flixel.graphics.FlxGraphic; import funkin.backend.system.Conductor; import openfl.geom.Rectangle; +import funkin.backend.shaders.CustomShader; + +class FlxFastTypedGroup<T:FlxBasic> extends FlxTypedGroup<T> { + public function new(?maxSize:Int = 0) { + super(maxSize); + } + + override public function add(Object:T):T + { + if (Object == null) + { + FlxG.log.warn("Cannot add a `null` object to a FlxGroup."); + return null; + } + + // removed the check for multiple + // removed the check for null reuse + + // If the group is full, return the Object + if (maxSize > 0 && length >= maxSize) + return Object; + + // If we made it this far, we need to add the object to the group. + members.push(Object); + length++; + + if (_memberAdded != null) + _memberAdded.dispatch(Object); + + return Object; + } +} class CharterBackdropGroup extends FlxTypedGroup<CharterBackdrop> { public var strumLineGroup:CharterStrumLineGroup; public var notesGroup:CharterNoteGroup; - var __gridGraphic:FlxGraphic; public var conductorSprY:Float = 0; public var bottomLimitY:Float = 0; - public var sectionsVisible:Bool = true; - public var beatsVisible:Bool = true; // Just here so you can update display sprites all dat and above public var strumlinesAmount:Int = 0; @@ -21,23 +50,11 @@ class CharterBackdropGroup extends FlxTypedGroup<CharterBackdrop> { public function new(strumLineGroup:CharterStrumLineGroup) { super(); this.strumLineGroup = strumLineGroup; - - __gridGraphic = FlxG.bitmap.create(160, 160, 0xFF272727, true); - __gridGraphic.bitmap.lock(); - - // Checkerboard - for(y in 0...4) - for(x in 0...2) __gridGraphic.bitmap.fillRect(new Rectangle(40*((x*2)+(y%2)), 40*y, 40, 40), 0xFF545454); - - // Edges - __gridGraphic.bitmap.fillRect(new Rectangle(0, 0, 1, 160), 0xFFDDDDDD); - __gridGraphic.bitmap.fillRect(new Rectangle(159, 0, 1, 160), 0xFFDDDDDD); - __gridGraphic.bitmap.unlock(); } public function createGrids(amount:Int = 0) { for (i in 0...amount) { - var grid = new CharterBackdrop(__gridGraphic); + var grid = new CharterBackdrop(); grid.active = grid.visible = false; add(grid); } @@ -53,7 +70,7 @@ class CharterBackdropGroup extends FlxTypedGroup<CharterBackdrop> { if (strumLine == null) continue; if (members[i] == null) - members[i] = recycle(CharterBackdrop, () -> {return new CharterBackdrop(__gridGraphic);}); + members[i] = recycle(CharterBackdrop, () -> {return new CharterBackdrop();}); var grid = members[i]; grid.cameras = this.cameras; @@ -61,16 +78,15 @@ class CharterBackdropGroup extends FlxTypedGroup<CharterBackdrop> { grid.conductorFollowerSpr.y = conductorSprY; grid.bottomSeparator.y = (grid.bottomLimit.y = bottomLimitY)-2; - grid.sectionSeparator.visible = sectionsVisible; - grid.beatSeparator.visible = beatsVisible; grid.waveformSprite.shader = strumLine.waveformShader; grid.notesGroup.clear(); notesGroup.forEach((n) -> { - var onStr:Bool = (n.snappedToStrumline ? n.strumLineID : Std.int(FlxMath.bound((n.x+n.width)/(40*4), 0, strumLineGroup.members.length-1))) == i; - if(n.exists && n.visible && onStr) - grid.notesGroup.add(n); + if(n.exists && n.visible) { + var onStr:Bool = (n.snappedToGrid ? n.strumLineID : CoolUtil.boundInt(Std.int((n.x+n.width)/(40*strumLine.keyCount)), 0, strumLineGroup.members.length-1)) == i; + if(onStr) grid.notesGroup.add(n); + } }); grid.active = grid.visible = true; @@ -97,8 +113,7 @@ class CharterBackdropGroup extends FlxTypedGroup<CharterBackdrop> { if (cameras != null) FlxCamera._defaultCameras = cameras; - while (i < length) - { + while (i < length) { basic = members[i++]; if (basic != null && basic != draggingObj && basic.exists && basic.visible) basic.draw(); @@ -109,7 +124,43 @@ class CharterBackdropGroup extends FlxTypedGroup<CharterBackdrop> { } } -class CharterBackdrop extends FlxTypedGroup<Dynamic> { +// Batches note draws (neos idea) >:D -lunar +class NotesDrawGroup extends FlxFastTypedGroup<CharterNote> { + public override function draw() @:privateAccess { + var oldDefaultCameras = FlxCamera._defaultCameras; + if (cameras != null) + FlxCamera._defaultCameras = cameras; + + var i:Int = 0; + var note:CharterNote = null; + + while (i < length) { + note = members[i++]; + if (note != null && note.exists && note.visible) { + if (note.snappedToGrid) note.x = (note.strumLine != null ? note.strumLine.x : 0) + (note.id % (note.strumLine != null ? note.strumLine.keyCount : 4)) * 40; + note.drawMembers(); + } + } + + i = 0; note = null; + while (i < length) { + note = members[i++]; + if (note != null && note.exists && note.visible) + note.drawSuper(); + } + + i = 0; note = null; + while (i < length) { + note = members[i++]; + if (note != null && note.exists && note.visible) + note.drawNoteTypeText(); + } + + FlxCamera._defaultCameras = oldDefaultCameras; + } +} + +class CharterBackdrop extends FlxTypedGroup<FlxBasic> { public var gridBackDrop:FlxBackdrop; public var topLimit:FlxSprite; public var topSeparator:FlxSprite; @@ -119,40 +170,36 @@ class CharterBackdrop extends FlxTypedGroup<Dynamic> { public var waveformSprite:FlxSprite; public var conductorFollowerSpr:FlxSprite; - public var beatSeparator:FlxBackdrop; - public var sectionSeparator:FlxBackdrop; + public var beatSeparator:CharterGridSeperator; - public var notesGroup:FlxTypedGroup<CharterNote> = new FlxTypedGroup<CharterNote>(); + public var notesGroup:NotesDrawGroup = new NotesDrawGroup(); public var strumLine:CharterStrumline; - public function new(gridGraphic:FlxGraphic) { + public var gridShader:CustomShader = new CustomShader("engine/charterGrid"); + var __lastKeyCount:Int = 4; + + public function new() { super(); - gridBackDrop = new FlxBackdrop(gridGraphic, Y, 0, 0); + gridBackDrop = new FlxBackdrop(null, Y, 0, 0); + gridBackDrop.makeSolid(1, 1, -1); + gridBackDrop.shader = gridShader; add(gridBackDrop); + gridShader.hset("segments", 4); waveformSprite = new FlxSprite().makeSolid(1, 1, 0xFF000000); waveformSprite.scale.set(160, 1); - waveformSprite.updateHitbox(); + waveformSprite.updateHitbox(); add(waveformSprite); - sectionSeparator = new FlxBackdrop(null, Y, 0, 0); - sectionSeparator.y = -2; - sectionSeparator.visible = Options.charterShowSections; + beatSeparator = new CharterGridSeperator(); + beatSeparator.makeSolid(1, 1, -1); + beatSeparator.alpha = 0.5; + beatSeparator.scrollFactor.set(1, 1); + beatSeparator.scale.set((4 * 40), 2); + beatSeparator.updateHitbox(); - beatSeparator = new FlxBackdrop(null, Y, 0, 0); - beatSeparator.y = -1; - beatSeparator.visible = Options.charterShowBeats; - - for(sep in [sectionSeparator, beatSeparator]) { - sep.makeSolid(1, 1, -1); - sep.alpha = 0.5; - sep.scrollFactor.set(1, 1); - sep.scale.set((4 * 40), sep == sectionSeparator ? 4 : 2); - sep.updateHitbox(); - } add(beatSeparator); - add(sectionSeparator); add(notesGroup); bottomSeparator = new FlxSprite(0,-2); @@ -194,32 +241,41 @@ class CharterBackdrop extends FlxTypedGroup<Dynamic> { public function updateSprites() { var x:Float = 0; // fuck you var alpha:Float = 0.9; + var keyCount:Int = 4; if (strumLine != null) { x = strumLine.x; alpha = strumLine.strumLine.visible ? 0.9 : 0.4; + keyCount = strumLine.keyCount; } else alpha = 0.9; - for (spr in [gridBackDrop, sectionSeparator, beatSeparator, topLimit, bottomLimit, + for (spr in [gridBackDrop, beatSeparator, topLimit, bottomLimit, topSeparator, bottomSeparator, conductorFollowerSpr, waveformSprite]) { spr.x = x; if (spr != waveformSprite) spr.alpha = alpha; spr.cameras = this.cameras; } - sectionSeparator.spacing.y = (10 * Conductor.beatsPerMeasure * Conductor.stepsPerBeat) - 1; - beatSeparator.spacing.y = (20 * Conductor.stepsPerBeat) - 1; + gridBackDrop.setGraphicSize(40*keyCount, 160); + gridBackDrop.updateHitbox(); + if (__lastKeyCount != keyCount) gridShader.hset("segments", keyCount); + __lastKeyCount = keyCount; - topLimit.scale.set(4 * 40, Math.ceil(FlxG.height / cameras[0].zoom)); + topLimit.scale.set(keyCount * 40, Math.ceil(FlxG.height / cameras[0].zoom)); topLimit.updateHitbox(); topLimit.y = -topLimit.height; - bottomLimit.scale.set(4 * 40, Math.ceil(FlxG.height / cameras[0].zoom)); + bottomLimit.scale.set(keyCount * 40, Math.ceil(FlxG.height / cameras[0].zoom)); bottomLimit.updateHitbox(); + for (spr in [conductorFollowerSpr, beatSeparator, topSeparator, bottomSeparator]) { + spr.scale.x = keyCount * 40; + spr.updateHitbox(); + } + waveformSprite.visible = waveformSprite.shader != null; if (waveformSprite.shader == null) return; - waveformSprite.scale.set(160, FlxG.height * (1/cameras[0].zoom)); + waveformSprite.scale.set(keyCount * 40, FlxG.height * (1/cameras[0].zoom)); waveformSprite.updateHitbox(); waveformSprite.y = (cameras[0].scroll.y+FlxG.height/2)-(waveformSprite.height/2); @@ -237,57 +293,178 @@ class CharterBackdrop extends FlxTypedGroup<Dynamic> { } } -class CharterBackdropDummy extends UISprite { - var parent:CharterBackdropGroup; - public function new(parent:CharterBackdropGroup) { - super(); - this.parent = parent; - cameras = parent.cameras; - scrollFactor.set(1, 0); +class CharterGridSeperatorBase extends FlxSprite { + + private static var minStep:Float = 0; + private static var maxStep:Float = 0; + + private static var minBeat:Float = 0; + private static var maxBeat:Float = 0; + + private static var minMeasure:Float = 0; + private static var maxMeasure:Float = 0; + + private static var lastMinBeat:Float = -1; + private static var lastMaxBeat:Float = -1; + + private static var lastMinMeasure:Float = -1; + private static var lastMaxMeasure:Float = -1; + + public static var lastConductorSprY:Float = Math.NEGATIVE_INFINITY; + + private static var beatStepTimes:Array<Float> = []; + private static var measureStepTimes:Array<Float> = []; + private static var timeSignatureChangeGaps:Array<Float> = []; + + private function recalculateBeats() { + var conductorSprY = Charter.instance.gridBackdrops.conductorSprY; + if (conductorSprY == lastConductorSprY) return; + + var zoomOffset = ((FlxG.height * (1/cameras[0].zoom)) * 0.5); + + minStep = (conductorSprY - zoomOffset)/40; + maxStep = (conductorSprY + zoomOffset)/40; + + var minTime:Float = Conductor.getStepsInTime(minStep); + var maxTime:Float = Conductor.getStepsInTime(maxStep); + + var minBpmChange = Conductor.bpmChangeMap[Conductor.getTimeInChangeIndex(minTime)]; + var maxBpmChange = Conductor.bpmChangeMap[Conductor.getTimeInChangeIndex(maxTime)]; + + minBeat = Conductor.getTimeInBeats(minTime); + maxBeat = Conductor.getTimeInBeats(maxTime); + + minMeasure = minBpmChange.measureTime + (minBeat - minBpmChange.beatTime) / minBpmChange.beatsPerMeasure; + maxMeasure = maxBpmChange.measureTime + (maxBeat - maxBpmChange.beatTime) / maxBpmChange.beatsPerMeasure; + + //cap out the beats/measures at the end of the song + var endTime = Conductor.getStepsInTime(Charter.instance.__endStep); + var endBeat = Conductor.getTimeInBeats(endTime); + var endBpmChange = Conductor.bpmChangeMap[Conductor.getTimeInChangeIndex(endTime)]; + var endMeasure = endBpmChange.measureTime + (endBeat - endBpmChange.beatTime) / endBpmChange.beatsPerMeasure; + + if (maxBeat > endBeat) maxBeat = endBeat; + if (maxMeasure > endMeasure) maxMeasure = endMeasure; + if (minMeasure < 0) minMeasure = 0; + if (minBeat < 0) minBeat = 0; + + //only calculate if needed + if ((minBeat != lastMinBeat) || (maxBeat != lastMaxBeat) || (minMeasure != lastMinMeasure) || (maxMeasure != lastMaxMeasure) || lastConductorSprY == Math.NEGATIVE_INFINITY) { + calculateTimeSignatureGaps(); + calculateStepTimes(); + lastMinBeat = minBeat; + lastMaxBeat = maxBeat; + lastMinMeasure = minMeasure; + lastMaxMeasure = maxMeasure; + } + + lastConductorSprY = conductorSprY; } - public override function updateButton() { - camera.getViewRect(__rect); - UIState.state.updateRectButtonHandler(this, __rect, onHovered); + private inline function calculateTimeSignatureGaps() { + //for time signatures that start mid step + timeSignatureChangeGaps.splice(0, timeSignatureChangeGaps.length); + for (i => change in Conductor.bpmChangeMap) { + if (change.stepTime >= minStep && change.stepTime <= maxStep) { + //get step while ignoring the current change + var index:Int = CoolUtil.boundInt(i-1, 0, Conductor.bpmChangeMap.length - 1); + var step:Float = Conductor.getTimeWithBPMInSteps(change.songTime, index, Conductor.getTimeWithIndexInBPM(change.songTime, index)); + + if (Math.ceil(step) - step > 0 && (step - Math.floor(step)) > FlxMath.EPSILON) { //mid step change + timeSignatureChangeGaps.push(step); + } + } + } } - public override function draw() { - @:privateAccess - __lastDrawCameras = cameras.copy(); + private inline function calculateStepTimes() { + beatStepTimes.splice(0, beatStepTimes.length); + for (i in Math.floor(minBeat)...Math.ceil(maxBeat)) { + beatStepTimes.push(Conductor.getTimeInSteps(Conductor.getBeatsInTime(i))); + } + measureStepTimes.splice(0, measureStepTimes.length); + for (i in Math.floor(minMeasure)...Math.ceil(maxMeasure)) { + measureStepTimes.push(Conductor.getTimeInSteps(Conductor.getMeasuresInTime(i))); + } + } + + override public function draw() { + + //should only need to recalculate once per frame and will be shared across each instance + recalculateBeats(); + + drawTimeSignatureChangeGaps(); + + if (Options.charterShowBeats) drawBeats(); + if (Options.charterShowSections) drawMeasures(); + } + + private function drawBeats(offset:Float = 0.0) { + for (i in beatStepTimes) { + y = (i*40)+offset; + super.draw(); + } + } + private function drawMeasures(offset:Float = 0.0) { + for (i in measureStepTimes) { + y = (i*40)+offset; + super.draw(); + } + } + private function drawTimeSignatureChangeGaps() { + if (timeSignatureChangeGaps.length == 0) return; + var prevColor = color; + var prevBlend = blend; + + color = 0xFF888888; + blend = MULTIPLY; + + for (step in timeSignatureChangeGaps) { + y = step*40; + var diff = Math.ceil(step) - step; + scale.y = diff*40; + updateHitbox(); + + super.draw(); + } + + color = prevColor; + blend = prevBlend; + } +} + +class CharterGridSeperator extends CharterGridSeperatorBase { + override private function drawBeats(offset:Float = 0.0) { + scale.y = 2; + updateHitbox(); + super.drawBeats(-2); + } + override private function drawMeasures(offset:Float = 0.0) { + scale.y = 4; + updateHitbox(); + super.drawMeasures(-3); } } class EventBackdrop extends FlxBackdrop { - public var eventBeatSeparator:FlxBackdrop; - public var eventSecSeparator:FlxBackdrop; + public var eventBeatSeparator:CharterEventGridSeperator; public var topSeparator:FlxSprite; public var bottomSeparator:FlxSprite; - public function new() { + public var global:Bool = false; + + public function new(global:Bool) { super(Paths.image('editors/charter/events-grid'), Y, 0, 0); + this.global = flipX = flipY = global; alpha = 0.9; // Separators - eventSecSeparator = new FlxBackdrop(null, Y, 0, 0); - eventSecSeparator.y = -2; - eventSecSeparator.visible = Options.charterShowSections; - - eventBeatSeparator = new FlxBackdrop(null, Y, 0, 0); - eventBeatSeparator.y = -1; - eventBeatSeparator.visible = Options.charterShowBeats; - - for(sep in [eventSecSeparator, eventBeatSeparator]) { - sep.makeSolid(1, 1, -1); - sep.alpha = 0.5; - sep.scrollFactor.set(1, 1); - } - - eventSecSeparator.scale.set(20, 4); - eventSecSeparator.updateHitbox(); - - eventBeatSeparator.scale.set(10, 2); - eventBeatSeparator.updateHitbox(); + eventBeatSeparator = new CharterEventGridSeperator(); + eventBeatSeparator.makeSolid(1, 1, -1); + eventBeatSeparator.alpha = 0.5; + eventBeatSeparator.scrollFactor.set(1, 1); + eventBeatSeparator.global = global; bottomSeparator = new FlxSprite(0,-2); bottomSeparator.makeSolid(1, 1, -1); @@ -308,23 +485,34 @@ class EventBackdrop extends FlxBackdrop { public override function draw() { super.draw(); - eventSecSeparator.spacing.y = (10 * Conductor.beatsPerMeasure * Conductor.stepsPerBeat) - 1; - eventBeatSeparator.spacing.y = (20 * Conductor.stepsPerBeat) - 1; - - eventSecSeparator.cameras = cameras; - eventSecSeparator.x = (x+width) - 20; - if (eventSecSeparator.visible) eventSecSeparator.draw(); - eventBeatSeparator.cameras = cameras; - eventBeatSeparator.x = (x+width) - 10; - if (eventBeatSeparator.visible) eventBeatSeparator.draw(); + eventBeatSeparator.xPos = global ? x : x+width; + eventBeatSeparator.draw(); - topSeparator.x = (x+width) - 20; + topSeparator.x = global ? x : (x+width) - 20; topSeparator.cameras = this.cameras; - if (!eventSecSeparator.visible) topSeparator.draw(); + if (!Options.charterShowSections) topSeparator.draw(); - bottomSeparator.x = (x+width) - 20; + bottomSeparator.x = global ? x : (x+width) - 20; bottomSeparator.cameras = this.cameras; bottomSeparator.draw(); } +} + +class CharterEventGridSeperator extends CharterGridSeperatorBase { + public var xPos:Float = 0.0; + public var global:Bool = false; + override private function drawBeats(offset:Float = 0.0) { + scale.set(10, 2); + updateHitbox(); + x = global ? xPos : xPos-10; + super.drawBeats(-2); + } + override private function drawMeasures(offset:Float = 0.0) { + scale.set(20, 4); + updateHitbox(); + x = global ? xPos : xPos-20; + super.drawMeasures(-3); + } + override private function drawTimeSignatureChangeGaps() {} } \ No newline at end of file diff --git a/source/funkin/editors/charter/CharterBookmarkCreation.hx b/source/funkin/editors/charter/CharterBookmarkCreation.hx new file mode 100644 index 00000000..a0a7ab19 --- /dev/null +++ b/source/funkin/editors/charter/CharterBookmarkCreation.hx @@ -0,0 +1,65 @@ +package funkin.editors.charter; + +import flixel.util.FlxColor; +import flixel.math.FlxPoint; +import funkin.backend.chart.ChartData.ChartBookmark; + +class CharterBookmarkCreation extends UISubstateWindow { + public var saveButton:UIButton; + public var closeButton:UIButton; + public var textBox:UITextBox; + public var colorPicker:UICompactColorwheel; + + var callback:Bool->String->FlxColor->Float->Void; + + var bookmarkStep:Float = 0; + + override public function new(step:Float, close:Bool->String->FlxColor->Float->Void) + { + super(); + + winWidth = 320; + winHeight = 250; + winTitle = 'Creating New Bookmark'; + + callback = close; + + bookmarkStep = step; + } + + public override function create() { + FlxG.sound.music.pause(); + Charter.instance.vocals.pause(); + + function addLabelOn(ui:UISprite, text:String):UIText { + var text:UIText = new UIText(ui.x, ui.y - 24, 0, text); + ui.members.push(text); + return text; + } + + super.create(); + + textBox = new UITextBox(windowSpr.x + 20, windowSpr.y + 70, "New Bookmark", 276); + textBox.antialiasing = true; + add(textBox); + addLabelOn(textBox, "Bookmark Name:"); + + colorPicker = new UICompactColorwheel(textBox.x, textBox.y + textBox.bHeight + 34, flixel.util.FlxColor.RED); + add(colorPicker); + addLabelOn(colorPicker, "Bookmark Color:"); + + saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() { + callback(true, textBox.label.text, colorPicker.curColor, bookmarkStep); + close(); + }, 125); + add(saveButton); + + closeButton = new UIButton(saveButton.x - 20, saveButton.y, "Close", function() { + callback(false, "", FlxColor.TRANSPARENT, -1); + close(); + }, 125); + add(closeButton); + closeButton.color = 0xFFFF0000; + closeButton.x -= closeButton.bWidth; + } +} \ No newline at end of file diff --git a/source/funkin/editors/charter/CharterBookmarkList.hx b/source/funkin/editors/charter/CharterBookmarkList.hx new file mode 100644 index 00000000..032177db --- /dev/null +++ b/source/funkin/editors/charter/CharterBookmarkList.hx @@ -0,0 +1,95 @@ +package funkin.editors.charter; + +import flixel.math.FlxPoint; +import funkin.backend.chart.ChartData.ChartBookmark; + +class CharterBookmarkList extends UISubstateWindow { + public var bookmarkList:UIButtonList<BookmarkButton>; + public var saveButton:UIButton; + public var closeButton:UIButton; + + public override function create() { + FlxG.sound.music.pause(); + Charter.instance.vocals.pause(); + + winTitle = 'Bookmark List Editor'; + winWidth = 380; + winHeight = 390; + + super.create(); + + var title:UIText; + add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Edit Bookmarks", 28)); + + bookmarkList = new UIButtonList<BookmarkButton>(20, title.y + title.height + 10, winWidth - 40, 342 - 85 - 16, null, FlxPoint.get(winWidth - 40, (342 - 85 - 16)/4), null, 0); + bookmarkList.cameraSpacing = 0; + + for (b in Charter.instance.getBookmarkList()) + bookmarkList.add(new BookmarkButton(0, 0, b, bookmarkList)); + + add(bookmarkList); + bookmarkList.frames = Paths.getFrames('editors/ui/inputbox'); + + bookmarkList.content.remove(bookmarkList.addButton); //i dont need it + + saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() { + saveList(); + close(); + }, 125); + add(saveButton); + + closeButton = new UIButton(saveButton.x - 20, saveButton.y, "Close", function() { + close(); + }, 125); + add(closeButton); + closeButton.color = 0xFFFF0000; + closeButton.x -= closeButton.bWidth; + } + + public function saveList() { + var oldList:Array<ChartBookmark> = Charter.instance.getBookmarkList(); + var newList:Array<ChartBookmark> = [for (btn in bookmarkList.buttons.members) {time: btn.bookmark.time, name: btn.textBox.label.text, color: btn.bookmark.color}]; + + PlayState.SONG.bookmarks = newList; + Charter.instance.updateBookmarks(); + Charter.undos.addToUndo(CEditBookmarks(oldList, newList)); + } +} + +class BookmarkButton extends UIButton { + public var textBox:UITextBox; + public var deleteButton:UIButton; + public var deleteIcon:FlxSprite; + public var bookmark:ChartBookmark; + + public function new(x:Float, y:Float, bookmark:ChartBookmark, parent:UIButtonList<BookmarkButton>) { + super(x, y, '', null, Std.int(parent.buttonSize.x), Std.int(parent.buttonSize.y)); + this.bookmark = bookmark; + autoAlpha = false; + + members.push(textBox = new UITextBox(10, 10, bookmark.name, 276)); + textBox.antialiasing = true; + + deleteButton = new UIButton(textBox.x + textBox.label.width + 10, textBox.y, null, function () { + parent.remove(this); + }, 32); + deleteButton.color = 0xFFFF0000; + deleteButton.autoAlpha = false; + members.push(deleteButton); + + deleteIcon = new FlxSprite(deleteButton.x + (15/2), deleteButton.y + 8).loadGraphic(Paths.image('editors/delete-button')); + deleteIcon.antialiasing = false; + members.push(deleteIcon); + } + + override function update(elapsed:Float) { + textBox.y = (y + bHeight/2) - (textBox.bHeight/2); + deleteButton.x = textBox.x + textBox.bWidth + 14; deleteButton.y = textBox.y; + deleteIcon.x = deleteButton.x + (15/2); deleteIcon.y = deleteButton.y + 8; + + deleteButton.selectable = selectable; + deleteButton.shouldPress = shouldPress; + + super.update(elapsed); + } +} \ No newline at end of file diff --git a/source/funkin/editors/charter/CharterDeleteAnim.hx b/source/funkin/editors/charter/CharterDeleteAnim.hx index df3f565b..2e23d38a 100644 --- a/source/funkin/editors/charter/CharterDeleteAnim.hx +++ b/source/funkin/editors/charter/CharterDeleteAnim.hx @@ -11,7 +11,7 @@ class CharterDeleteAnim extends CharterNote { super(); sustainSpr.color = 0xFF630000; color = 0xFF605A5A; - snappedToStrumline = selectable = autoAlpha = false; + snappedToGrid = selectable = autoAlpha = false; @:privateAccess __animSpeed = 1.25; garbageIcon = new FlxSprite().loadGraphic(Paths.image("editors/deleter")); @@ -40,12 +40,12 @@ class CharterDeleteAnim extends CharterNote { FlxG.mouse.getWorldPosition(Charter.instance.uiCamera, __mousePos); if (FlxG.mouse.pressedRight && __deletionTimer <= 0) { garbageIcon.setPosition( - __mousePos.x + garbageIcon.width/2 + (.65*FlxG.random.float(-1, 1)) + 2, + __mousePos.x + garbageIcon.width/2 + (.65*FlxG.random.float(-1, 1)) + 2, __mousePos.y - garbageIcon.height + (.65*FlxG.random.float(-1, 1)) - 8 ); garbageCircle.setPosition( - __mousePos.x - garbageCircle.width/2, + __mousePos.x - garbageCircle.width/2, (__mousePos.y - garbageCircle.height/2) + 2 ); } @@ -56,7 +56,7 @@ class CharterDeleteAnim extends CharterNote { public override function draw() @:privateAccess { for (deleteData in deleteNotes) { - y = deleteData.note.y + (deleteData.time>deleteTime*.5 ? (deleteData.time/deleteTime)*FlxG.random.float(-1.1, 1.1) : 0); // lunar when no shake :(( + y = deleteData.note.y + (deleteData.time>deleteTime*.5 ? (deleteData.time/deleteTime)*FlxG.random.float(-1.1, 1.1) : 0); // lunar when no shake :(( x = deleteData.note.x + (deleteData.time>deleteTime*.5 ? (deleteData.time/deleteTime)*FlxG.random.float(-1.1, 1.1) : 0); // lunar when no shake :(( angle = deleteData.note.angle; alpha = 1; animation.curAnim.curFrame = 3; @@ -65,12 +65,17 @@ class CharterDeleteAnim extends CharterNote { sustainSpr.updateHitbox(); sustainSpr.follow(this, 15, 20); sustainSpr.exists = deleteData.note.susLength != 0; sustainSpr.alpha = .8; - typeText.text = Std.string(deleteData.note.type); - typeText.exists = deleteData.note.type != 0; typeText.alpha = .4; - typeText.follow(this, 20 - (typeText.frameWidth/2), 20 - (typeText.frameHeight/2)); + type = deleteData.note.type; + typeAlpha = .4; + //typeText.text = Std.string(deleteData.note.type); + //typeText.exists = deleteData.note.type != 0; typeAlpha = .4; + //typeText.follow(this, 20 - (typeText.frameWidth/2), 20 - (typeText.frameHeight/2)); - for (member in [this, sustainSpr, typeText]) - member.alpha *= FlxEase.quadInOut(deleteData.time/deleteTime); + var mult = FlxEase.quadInOut(deleteData.time/deleteTime); + + for (member in [this, sustainSpr]) + member.alpha *= mult; + typeAlpha *= mult; super.draw(); } diff --git a/source/funkin/editors/charter/CharterEvent.hx b/source/funkin/editors/charter/CharterEvent.hx index c7092527..05b2a7e9 100644 --- a/source/funkin/editors/charter/CharterEvent.hx +++ b/source/funkin/editors/charter/CharterEvent.hx @@ -1,13 +1,21 @@ package funkin.editors.charter; +import openfl.display.BitmapData; +import openfl.geom.ColorTransform; +import flixel.group.FlxSpriteGroup; import flixel.math.FlxPoint; import flixel.system.FlxAssets.FlxGraphicAsset; import funkin.backend.chart.ChartData.ChartEvent; +import funkin.backend.scripting.DummyScript; +import funkin.backend.scripting.Script; +import funkin.backend.system.Conductor; import funkin.editors.charter.Charter.ICharterSelectable; import funkin.editors.charter.CharterBackdropGroup.EventBackdrop; import funkin.game.Character; import funkin.game.HealthIcon; +using flixel.util.FlxColorTransformUtil; + class CharterEvent extends UISliceSprite implements ICharterSelectable { public var events:Array<ChartEvent>; public var step:Float; @@ -19,51 +27,138 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable { public var eventsBackdrop:EventBackdrop; public var snappedToGrid:Bool = true; - public function new(step:Float, ?events:Array<ChartEvent>) { + public var displayGlobal:Bool = false; + public var global(default, set):Bool = false; + private function set_global(val:Bool) { + for (event in events) event.global = val; + return global = val; + } + + public function new(step:Float, ?events:Array<ChartEvent>, ?global:Bool) { super(-100, (step * 40) - 17, 100, 34, 'editors/charter/event-spr'); this.step = step; this.events = events.getDefault([]); - cursor = BUTTON; + this.global = displayGlobal = (global == null ? events[0] != null && events[0].global == true : global); + this.color = displayGlobal ? 0xffc8bd23 : 0xFFFFFFFF; + + cursor = CLICK; } public override function update(elapsed:Float) { super.update(elapsed); - if (snappedToGrid && eventsBackdrop != null) - x = eventsBackdrop.x + eventsBackdrop.width - (bWidth = 37 + (icons.length * 22)); + if (snappedToGrid && eventsBackdrop != null) { + bWidth = 37 + (icons.length * 22); + x = eventsBackdrop.x + (global ? 0 : eventsBackdrop.width - bWidth); + } for(k=>i in icons) { i.follow(this, (k * 22) + 30 - (i.width / 2), (bHeight - i.height) / 2); } - colorTransform.redMultiplier = colorTransform.greenMultiplier = colorTransform.blueMultiplier = selected ? 0.75 : 1; - colorTransform.redOffset = colorTransform.greenOffset = selected ? 96 : 0; - colorTransform.blueOffset = selected ? 168 : 0; + @:bypassAccessor color = CoolUtil.lerpColor(this.color, displayGlobal ? 0xffc8bd23 : 0xFFFFFFFF, 1/3); + colorTransform.setMultipliers(color.redFloat, color.greenFloat, color.blueFloat, alpha); + colorTransform.setOffsets(0, 0, 0, 0); + selectedColorTransform(colorTransform); + useColorTransform = true; - for (sprite in icons) - sprite.colorTransform = colorTransform; + for (sprite in icons) @:privateAccess { + sprite.colorTransform.__identity(); + selectedColorTransform(sprite.colorTransform); + } + + flipX = displayGlobal; } + @:noCompletion private inline function selectedColorTransform(transform:ColorTransform) { + transform.redMultiplier *= selected ? 0.75 : 1; + transform.greenMultiplier *= selected ? 0.75 : 1; + transform.blueMultiplier *= selected ? 0.75 : 1; + + transform.redOffset += selected ? 96 : 0; + transform.greenOffset += selected ? 96 : 0; + transform.blueOffset += selected ? 168 : 0; + } + + /** + * Pack data is a list of 4 strings separated by `________PACKSEP________` + * [0] Event Name + * [1] Event Script + * [2] Event JSON Info + * [3] Event Icon + * [4] Event UI Script / Icon Script + **/ + @:dox(hide) public static function getPackData(name:String):Array<String> { + var packFile = Paths.pack('events/${name}'); + if (Assets.exists(packFile)) { + return Assets.getText(packFile).split('________PACKSEP________'); + } + return null; + } + + @:dox(hide) public static function getUIScript(event:ChartEvent, caller:String):Script { + var uiScript = Paths.script('data/events/${event.name}.ui'); + var script:Script = null; + if(Assets.exists(uiScript)) { + script = Script.create(uiScript); + } else { + var packData = getPackData(event.name); + if(packData != null) { + var scriptFile = packData[4]; + if(scriptFile != null) { + script = Script.fromString(scriptFile, uiScript); + } + } + } + + if(script != null && !(script is DummyScript)) { + // classes and functions + script.set("EventIconGroup", EventIconGroup); // automatically imported + script.set("EventNumber", EventNumber); // automatically imported + script.set("getIconFromStrumline", getIconFromStrumline); + script.set("getIconFromCharName", getIconFromCharName); + script.set("generateDefaultIcon", generateDefaultIcon); + script.set("generateEventIconDurationArrow", generateEventIconDurationArrow); + script.set("generateEventIconNumbers", generateEventIconNumbers); + script.set("generateEventIconWarning", generateEventIconWarning); + script.set("getPackData", getPackData); + script.set("getEventComponent", getEventComponent); + // data + script.set("event", event); + script.set("caller", caller); + + script.load(); + } + + return script; + } + + /** + * Generates the default event icon for the wanted event + * @param name The name of the event + * @return The icon + **/ private static function generateDefaultIcon(name:String) { var isBase64:Bool = false; var path:String = Paths.image('editors/charter/event-icons/$name'); var defaultPath = Paths.image('editors/charter/event-icons/Unknown'); - if (!Assets.exists(path)) path = defaultPath; + if(!Assets.exists(path)) { + path = defaultPath; - var packPath = Paths.pack('events/$name'); - if (Assets.exists(packPath)) { - var packText = Assets.getText(packPath).split('________PACKSEP________'); - var packImg = packText[3]; - if(packImg != null && packImg.length > 0) { - isBase64 = !packImg.startsWith("assets/"); - path = packImg; + var packData = getPackData(name); + if(packData != null) { + var packImg = packData[3]; + if(packImg != null && packImg.length > 0) { + isBase64 = !packImg.startsWith("assets/"); + path = packImg; + } } } path = path.trim(); var graphic:FlxGraphicAsset = try { - isBase64 ? openfl.display.BitmapData.fromBase64(path, 'UTF8') : path; + isBase64 ? BitmapData.fromBase64(path, 'UTF8') : path; } catch(e:Dynamic) { Logs.trace('Failed to load event icon: ${e.toString()}', ERROR); isBase64 = false; @@ -75,27 +170,176 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable { graphic = defaultPath; } - return new FlxSprite().loadGraphic(graphic); + return new FlxSprite(graphic); } - public static function generateEventIcon(event:ChartEvent) { - return switch(event.name) { - default: - generateDefaultIcon(event.name); - case "Camera Movement": - // custom icon for camera movement - var state:Charter = cast FlxG.state; - if (event.params != null && event.params[0] != null && event.params[0] >= 0 && event.params[0] < state.strumLines.length) { - // camera movement, use health icon - var icon = Character.getIconFromCharName(state.strumLines.members[event.params[0]].strumLine.characters[0]); - var healthIcon = new HealthIcon(icon); - healthIcon.setUnstretchedGraphicSize(32, 32, false); - healthIcon.scrollFactor.set(1, 1); - healthIcon.active = false; - healthIcon; - } else - generateDefaultIcon(event.name); + /** + * Gets a component sprite from the editors/charter/event-icons/components folder + * If you wanna use a number, please use the EventNumber class instead + * @param type The type of component to get + * @param x The x position of the sprite (optional) + * @param y The y position of the sprite (optional) + * @return The component sprite + **/ + public static function getEventComponent(type:String, x:Float = 0.0, y:Float = 0.0) { + var componentPath = Paths.image("editors/charter/event-icons/components/" + type); + if (Assets.exists(componentPath)) return new FlxSprite(x, y, componentPath); + + Logs.trace('Could not find component $type', WARNING); + return null; + } + + /** + * Expected to be called from inside of a ui script, + * calling this elsewhere might cause unexpected results or crashes + **/ + public static function getIconFromStrumline(index:Null<Int>) { + var state = cast(FlxG.state, Charter); + if (index != null && index >= 0 && index < state.strumLines.length) { + return getIconFromCharName(state.strumLines.members[index].strumLine.characters[0]); } + return null; + } + + public static function getIconFromCharName(name:String) { + var icon = Character.getIconFromCharName(name); + var healthIcon = new HealthIcon(icon); + CoolUtil.setUnstretchedGraphicSize(healthIcon, 32, 32, false); + healthIcon.scrollFactor.set(1, 1); + healthIcon.active = false; + return healthIcon; + } + + public static function generateEventIcon(event:ChartEvent, inMenu:Bool = true):FlxSprite { + var script = getUIScript(event, "event-icon"); + if(script != null && !(script is DummyScript)) { + script.set("inMenu", inMenu); + if(script.get("generateIcon") != null) { + var res:FlxSprite = script.call("generateIcon"); + if(res != null) + return res; + } + } + + switch(event.name) { + case "Time Signature Change": + if(event.params != null && (event.params[0] >= 0 || event.params[1] >= 0)) { + var group = new EventIconGroup(); + group.add(generateDefaultIcon(event.name)); + group.add({ // top + var num = new EventNumber(9, -1, event.params[0], EventNumber.ALIGN_CENTER); + num.active = false; + num; + }); + group.add({ // bottom + var num = new EventNumber(9, 10, event.params[1], EventNumber.ALIGN_CENTER); + num.active = false; + num; + }); + if (Conductor.invalidEvents.contains(event)) generateEventIconWarning(group); + return group; + } + case "Continuous BPM Change": + if(event.params != null && event.params[1] != null) { + var group = new EventIconGroup(); + group.add(generateDefaultIcon("BPM Change Start")); + if (!inMenu) { + generateEventIconDurationArrow(group, event.params[1]); + group.members[0].y -= 2; + generateEventIconNumbers(group, event.params[0], 3); + } + if (Conductor.invalidEvents.contains(event)) generateEventIconWarning(group); + return group; + } else { + return generateDefaultIcon("BPM Change Start"); + } + case "BPM Change": + if(event.params != null && event.params[0] != null) { + var group = new EventIconGroup(); + group.add(generateDefaultIcon(event.name)); + if (!inMenu) { + group.members[0].y -= 2; + generateEventIconNumbers(group, event.params[0], 3); + } + if (Conductor.invalidEvents.contains(event)) generateEventIconWarning(group); + return group; + } + + case "Scroll Speed Change": + if(event.params != null && !inMenu) { + var group = new EventIconGroup(); + group.add(generateDefaultIcon(event.name)); + if (event.params[0]) generateEventIconDurationArrow(group, event.params[2]); + group.members[0].y -= 2; + generateEventIconNumbers(group, event.params[1]); + return group; + } + + case "Camera Movement": + // camera movement, use health icon + if(event.params != null) { + var icon = getIconFromStrumline(event.params[0]); + if(icon != null) return icon; + } + } + return generateDefaultIcon(event.name); + } + + private static function generateEventIconNumbers(group:EventIconGroup, number:Float, x:Float = 4, y:Float = 15, spacing:Float = 5, precision:Int = 3) { + group.add({ + var num = new EventNumber(x, y, number, EventNumber.ALIGN_CENTER, spacing, precision); + if (num.numWidth > 20) { + num.scale.x = num.scale.y = 20 / num.numWidth; + } + num.active = false; + num; + }); + } + + private static function generateEventIconDurationArrow(group:EventIconGroup, stepDuration:Float) { + //var group = new EventIconGroup(); + //group.add(generateDefaultIcon(startIcon)); + + var xOffset = 4; + var yGap = 24; + var endGap = 2; + + if (stepDuration >= 0.55) { //min time for showing arrow + var tail = new FlxSprite(xOffset, yGap); + var arrow = new FlxSprite(xOffset, (stepDuration * 40) + endGap); + var arrowSegment = new FlxSprite(xOffset, yGap); + tail.frames = arrow.frames = arrowSegment.frames = Paths.getSparrowAtlas("editors/charter/event-icons/components/arrow-down"); + + group.add({ + tail.animation.addByPrefix("tail", "tail"); + tail.animation.play("tail"); + tail; + }); + + group.add({ + arrowSegment.animation.addByPrefix("segment", "segment"); + arrowSegment.animation.play("segment"); + arrowSegment.scale.y = endGap + (stepDuration * 40) - (tail.height + yGap); + arrowSegment.updateHitbox(); + arrowSegment.y += tail.height; + arrowSegment; + }); + + group.add({ + arrow.animation.addByPrefix("arrow", "arrow"); + arrow.animation.play("arrow"); + arrow; + }); + } + } + + private static function generateEventIconWarning(group:EventIconGroup) { + for (spr in group) { + spr.colorTransform.redMultiplier = spr.colorTransform.greenMultiplier = spr.colorTransform.blueMultiplier = 0.5; + spr.colorTransform.redOffset = 100; + } + group.add(getEventComponent("warning", 16, -8)); + group.copyColorTransformToChildren = false; } public override function onHovered() { @@ -111,7 +355,7 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable { } public function handleDrag(change:FlxPoint) { - var newStep:Float = step = FlxMath.bound(step + change.x, 0, Charter.instance.__endStep-1); + var newStep:Float = step = CoolUtil.bound(step + change.x, 0, Charter.instance.__endStep-1); y = ((newStep) * 40) - 17; } @@ -123,18 +367,152 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable { } for(event in events) { - var spr = generateEventIcon(event); + var spr = generateEventIcon(event, false); icons.push(spr); members.push(spr); } draggable = true; - for (event in events) - if (event.name == "BPM Change") { - draggable = false; - break; + + bWidth = 37 + (icons.length * 22); + x = (snappedToGrid && eventsBackdrop != null && global ? eventsBackdrop.x - bWidth : (global ? 0 : -bWidth)); + } +} + +class EventIconGroup extends FlxSpriteGroup { + public var forceWidth:Float = 16; + public var forceHeight:Float = 16; + public var dontTransformChildren:Bool = false; + public var copyColorTransformToChildren:Bool = true; + + public function new() { + super(); + colorTransform = new ColorTransform(); + scrollFactor.set(1, 1); + } + + override function preAdd(sprite:FlxSprite):Void + { + super.preAdd(sprite); + sprite.scrollFactor.set(1, 1); + } + + public override function transformChildren<V>(Function:FlxSprite->V->Void, Value:V):Void + { + if (dontTransformChildren) + return; + + super.transformChildren(Function, Value); + } + + override function set_x(Value:Float):Float + { + if (exists && x != Value) + transformChildren(xTransform, Value - x); // offset + return x = Value; + } + + override function set_y(Value:Float):Float + { + if (exists && y != Value) + transformChildren(yTransform, Value - y); // offset + return y = Value; + } + + override function get_width() { + return forceWidth; + } + override function get_height() { + return forceHeight; + } + + override public function draw() { + @:privateAccess + if (copyColorTransformToChildren && colorTransform != null) for (child in members) child.colorTransform.__copyFrom(colorTransform); + super.draw(); + } + + override function update(elapsed:Float) { + super.update(elapsed); + } +} + +class EventNumber extends FlxSprite { + public static inline final ALIGN_NORMAL:Int = 0; + public static inline final ALIGN_CENTER:Int = 1; + + public var digits:Array<Int> = []; + public static inline final FRAME_POINT:Int = 10; + public static inline final FRAME_NEGATIVE:Int = 11; + + public var align:Int = ALIGN_NORMAL; + public var spacing:Float = 6; + + public function new(x:Float, y:Float, number:Float, ?align:Int, spacing:Float = 6, precision:Int = 3) { + super(x, y); + this.digits = []; + this.align = align == null ? ALIGN_NORMAL : align; + this.spacing = spacing; + + if (number == 0) { + this.digits.insert(0, 0); + } + else { + var decimals:Float = FlxMath.roundDecimal(Math.abs(number % 1), precision); + if (decimals > 0) this.digits.insert(0, FRAME_POINT); + while(decimals > 0) { + this.digits.push(Math.floor(decimals * 10)); + decimals = FlxMath.roundDecimal((decimals * 10) % 1, precision); } - x = (snappedToGrid && eventsBackdrop != null ? eventsBackdrop.x : 0) - (bWidth = 37 + (icons.length * 22)); + var ints = Std.int(Math.abs(number)); + if (ints == 0) this.digits.insert(0, 0); + while (ints > 0) { + this.digits.insert(0, ints % 10); + ints = Std.int(ints / 10); + } + + if (number < 0) { + this.digits.insert(0, FRAME_NEGATIVE); + } + } + + loadGraphic(Paths.image('editors/charter/event-icons/components/eventNums'), true, 6, 7); + } + + override function update(elapsed:Float) { + super.update(elapsed); + } + + override function draw() { + var baseX = x; + var offsetX = 0.0; + if(align == ALIGN_CENTER) offsetX = -(digits.length - 1) * spacing * Math.abs(scale.x) / 2; + + x = baseX + offsetX; + for (i in 0...digits.length) { + frame = frames.frames[digits[i]]; + super.draw(); + x += spacing * Math.abs(scale.x); + } + x = baseX; + } + + public var numWidth(get, never):Float; + private function get_numWidth():Float { + return Math.abs(scale.x) * spacing * digits.length; + } + public var numHeight(get, never):Float; + private function get_numHeight():Float { + return Math.abs(scale.y) * frameHeight; + } + + public override function updateHitbox():Void { + var numWidth = this.numWidth; + var numHeight = this.numHeight; + width = numWidth; + height = numHeight; + offset.set(-0.5 * (numWidth - spacing * digits.length), -0.5 * (numHeight - frameHeight)); + centerOrigin(); } } \ No newline at end of file diff --git a/source/funkin/editors/charter/CharterEventAdd.hx b/source/funkin/editors/charter/CharterEventAdd.hx index 038725d3..09f96de5 100644 --- a/source/funkin/editors/charter/CharterEventAdd.hx +++ b/source/funkin/editors/charter/CharterEventAdd.hx @@ -7,31 +7,42 @@ class CharterEventAdd extends UISliceSprite { public var curCharterEvent:CharterEvent = null; - public function new() { + public var sideText:UIText; + public var global:Bool = false; + + public function new(global:Bool) { super(0, 0, 100, 34, 'editors/charter/event-spr-add'); + this.global = flipX = global; + + sideText = new UIText(0, -40, 0, global ? "Global Event" : "Local Event", 12); + sideText.alignment = "center"; sideText.alpha = 0.75; + text = new UIText(0, 0, 0, ""); members.push(text); - cursor = BUTTON; + cursor = CLICK; } public override function onHovered() { super.onHovered(); - if (FlxG.mouse.justReleased) { + if (FlxG.mouse.justReleased && FlxG.state.subState == null) { if (curCharterEvent != null) - Charter.instance.openSubState(new CharterEventScreen(step, curCharterEvent)); + Charter.instance.openSubState(new CharterEventScreenNew(curCharterEvent)); else - Charter.instance.openSubState(new CharterEventScreen(step)); + Charter.instance.openSubState(new CharterEventScreen(step, global)); } } public override function update(elapsed:Float) { super.update(elapsed); - text.follow(this, 20, (bHeight - text.height) / 2); + text.follow(this, global ? bWidth - text.width - (text.text == "Add event" ? 15 : 20) : 20, (bHeight - text.height) / 2); + sideText.follow(this, (bWidth/2) - (sideText.fieldWidth/2), -(sideText.height + 2)); alpha = sprAlpha * 0.75; text.alpha = sprAlpha; + + sideText.alpha = sprAlpha; } public function updatePos(step:Float) { @@ -39,15 +50,15 @@ class CharterEventAdd extends UISliceSprite { this.step = step; this.y = (step * 40) - (bHeight / 2); text.text = "Add event"; - framesOffset = 0; - x = -(bWidth = 37 + Math.ceil(text.width)); + framesOffset = 0; bWidth = 37 + Math.ceil(text.width); + x = global ? Charter.instance.strumLines.members[Charter.instance.strumLines.members.length-1].x + (40*Charter.instance.strumLines.members[Charter.instance.strumLines.members.length-1].keyCount) : -(bWidth); } public function updateEdit(event:CharterEvent) { curCharterEvent = event; this.y = event.y; text.text = "Edit"; - framesOffset = 9; - x = -(bWidth = 27 + Math.ceil(text.width) + event.bWidth); + framesOffset = 9; bWidth = 27 + Math.ceil(text.width) + event.bWidth; + x = global ? Charter.instance.strumLines.members[Charter.instance.strumLines.members.length-1].x + (40*Charter.instance.strumLines.members[Charter.instance.strumLines.members.length-1].keyCount) : -(bWidth); } } \ No newline at end of file diff --git a/source/funkin/editors/charter/CharterEventGroup.hx b/source/funkin/editors/charter/CharterEventGroup.hx index b81ee554..d7be0864 100644 --- a/source/funkin/editors/charter/CharterEventGroup.hx +++ b/source/funkin/editors/charter/CharterEventGroup.hx @@ -5,6 +5,7 @@ import funkin.editors.charter.CharterBackdropGroup.EventBackdrop; class CharterEventGroup extends FlxTypedGroup<CharterEvent> { public var eventsBackdrop:EventBackdrop; + public var eventsRowText:UIText; public var autoSort:Bool = true; var __lastSort:Int = 0; @@ -14,6 +15,7 @@ class CharterEventGroup extends FlxTypedGroup<CharterEvent> { if (autoSort && members.length != __lastSort) sortEvents(); + eventsRowText.y = FlxMath.lerp(eventsRowText.y, -40 + (members[0] != null ? Math.min(members[0].y, 0) : 0), 1/20); super.update(elapsed); } @@ -26,21 +28,25 @@ class CharterEventGroup extends FlxTypedGroup<CharterEvent> { for (event in members) { event.eventsBackdrop = eventsBackdrop; event.snappedToGrid = true; + event.cameras = cameras; } super.draw(); } public inline function sortEvents() { __lastSort = members.length; - this.sort(function(i, e1, e2) { - return FlxSort.byValues(FlxSort.ASCENDING, e1.step, e2.step); - }); + this.sort(sortEventsFilter); updateEventsIDs(); + updateEventsBackdrop(); } public inline function updateEventsIDs() for (i => n in members) n.ID = i; + public inline function updateEventsBackdrop() + for (event in members) + event.eventsBackdrop = eventsBackdrop; + public inline function filterEvents() { for (event in members) if (event.events.length == 0) { @@ -48,4 +54,7 @@ class CharterEventGroup extends FlxTypedGroup<CharterEvent> { event.kill(); } } + + public dynamic function sortEventsFilter(i:Int, e1:CharterEvent, e2:CharterEvent) + return FlxSort.byValues(FlxSort.ASCENDING, e1.step, e2.step); } \ No newline at end of file diff --git a/source/funkin/editors/charter/CharterEventScreen.hx b/source/funkin/editors/charter/CharterEventScreen.hx index 052e7062..3f035703 100644 --- a/source/funkin/editors/charter/CharterEventScreen.hx +++ b/source/funkin/editors/charter/CharterEventScreen.hx @@ -26,15 +26,17 @@ class CharterEventScreen extends UISubstateWindow { public var saveButton:UIButton; public var closeButton:UIButton; - public function new(step:Float, ?chartEvent:Null<CharterEvent>) { + public var global:Bool = false; + + public function new(step:Float, global:Bool, ?chartEvent:Null<CharterEvent>) { if (chartEvent != null) this.chartEvent = chartEvent; - this.step = step; + this.step = step; this.global = global; super(); } public override function create() { var creatingEvent:Bool = chartEvent == null; - if (creatingEvent) chartEvent = new CharterEvent(step, []); + if (creatingEvent) chartEvent = new CharterEvent(step, [], global); winTitle = creatingEvent ? "Create Event Group" : "Edit Event Group"; winWidth = 960; @@ -47,7 +49,7 @@ class CharterEventScreen extends UISubstateWindow { events = chartEvent.events.copy(); - eventsList = new UIButtonList<EventButton>(0,0,75, 570, "", FlxPoint.get(73, 40), null, 0); + eventsList = new UIButtonList<EventButton>(0,0,75, 570, null, FlxPoint.get(73, 40), null, 0); eventsList.drawTop = false; eventsList.addButton.callback = () -> openSubState(new CharterEventTypeSelection(function(eventName) { events.push({ @@ -57,7 +59,7 @@ class CharterEventScreen extends UISubstateWindow { }); eventsList.add(new EventButton(events[events.length-1], CharterEvent.generateEventIcon(events[events.length-1]), events.length-1, this, eventsList)); changeTab(events.length-1); - })); + }, chartEvent.step)); for (k=>i in events) eventsList.add(new EventButton(i, CharterEvent.generateEventIcon(i), k, this, eventsList)); add(eventsList); @@ -69,21 +71,20 @@ class CharterEventScreen extends UISubstateWindow { saveCurTab(); chartEvent.refreshEventIcons(); + Charter.instance.updateBPMEvents(); + if (events.length <= 0 && !creatingEvent) Charter.instance.deleteSelection([chartEvent]); else if (events.length > 0) { var oldEvents:Array<ChartEvent> = chartEvent.events.copy(); - chartEvent.events = [ - for (i in eventsList.buttons.members) i.event - ]; + chartEvent.events = [for (i in eventsList.buttons.members) i.event]; + chartEvent.global = global; + + chartEvent.refreshEventIcons(); if (creatingEvent && events.length > 0) Charter.instance.createSelection([chartEvent]); else { - chartEvent.events = [for (i in eventsList.buttons.members) i.event]; - chartEvent.refreshEventIcons(); - Charter.instance.updateBPMEvents(); - Charter.undos.addToUndo(CEditEvent(chartEvent, oldEvents, [for (event in events) Reflect.copy(event)])); } } @@ -247,17 +248,17 @@ class EventButton extends UIButton { public function new(event:ChartEvent, icon:FlxSprite, id:Int, substate:CharterEventScreen, parent:UIButtonList<EventButton>) { this.icon = icon; this.event = event; - super(0,0,"" ,function() { + super(0, 0, null, function() { substate.changeTab(id); for(i in parent.buttons.members) i.alpha = i == this ? 1 : 0.25; - },73,40); + }, 73, 40); autoAlpha = false; members.push(icon); icon.setPosition(18 - icon.width / 2, 20 - icon.height / 2); - deleteButton = new UIButton(bWidth - 30, y + (bHeight - 26) / 2, "", function () { + deleteButton = new UIButton(bWidth - 30, y + (bHeight - 26) / 2, null, function () { substate.events.splice(id, 1); substate.changeTab(id, false); parent.remove(this); diff --git a/source/funkin/editors/charter/CharterEventScreenNew.hx b/source/funkin/editors/charter/CharterEventScreenNew.hx new file mode 100644 index 00000000..2ea20ce6 --- /dev/null +++ b/source/funkin/editors/charter/CharterEventScreenNew.hx @@ -0,0 +1,381 @@ +package funkin.editors.charter; + +import flixel.group.FlxGroup; +import flixel.util.FlxColor; +import funkin.backend.chart.ChartData.ChartEvent; +import funkin.backend.chart.EventsData; +import funkin.backend.system.Conductor; +import funkin.game.Character; +import funkin.game.Stage; + +using StringTools; + +class CharterEventScreenNew extends MusicBeatSubstate { + public var cam:FlxCamera; + public var eventCam:FlxCamera; + public var chartEvent:CharterEvent; + + public var events:Array<ChartEvent> = []; + public var eventsList:UIButtonList<EventButtonNew>; + + public var eventName:UIText; + + public var paramsPanel:FlxGroup; + public var paramsFields:Array<FlxBasic> = []; + + public var downIndicator:UIText; + + public var bWidth:Float; + + var bg:UISliceSprite; + + public function new(?chartEvent:Null<CharterEvent>) { + if (chartEvent != null) this.chartEvent = chartEvent; + super(); + } + + public override function create() { + super.create(); + + FlxG.sound.music.pause(); // prevent the song from continuing + Charter.instance.vocals.pause(); + for (strumLine in Charter.instance.strumLines.members) strumLine.vocals.pause(); + + events = chartEvent.events.copy(); + + FlxG.state.persistentUpdate = true; + + camera = cam = new FlxCamera(); + cam.bgColor = 0; + + eventCam = new FlxCamera(); + eventCam.bgColor = 0; + + FlxG.cameras.add(cam, false); + FlxG.cameras.add(eventCam, false); + + bg = new UISliceSprite(0, 0, 0, 0, 'editors/ui/inputbox'); + bg.alpha = 0.75; + bg.cameras = [cam]; + bg.setPosition(0, 0); + add(bg); + + paramsPanel = new FlxGroup(); + paramsPanel.cameras = [eventCam]; + add(paramsPanel); + + eventName = new UIText(0, 0, 0, "", 24); + eventName.cameras = [eventCam]; + add(eventName); + + downIndicator = new UIText(0, 0, 0, "↓", 16); + downIndicator.cameras = [eventCam]; + downIndicator.scrollFactor.set(); + downIndicator.borderSize = 2; + downIndicator.alpha = 0; + add(downIndicator); + + eventsList = new UIButtonList<EventButtonNew>(10, 10, 75, 570, null, FlxPoint.get(75, 40), null, 0); + eventsList.alpha = 0; + eventsList.cameras = [cam]; + eventsList.addButton.callback = () -> { + __ignoreLastClick = true; // Stop closing >:D + openSubState(new CharterEventTypeSelection(function(eventName) { + events.push({ + time: Conductor.getTimeForStep(chartEvent.step), + params: [], + name: eventName + }); + eventsList.add(new EventButtonNew(events[events.length-1], CharterEvent.generateEventIcon(events[events.length-1]), events.length-1, this, eventsList)); + changeTab(events.length-1); + }, chartEvent.step)); + }; + for (k=>i in events) + eventsList.add(new EventButtonNew(i, CharterEvent.generateEventIcon(i), k, this, eventsList)); + add(eventsList); + + // this took forever to find out omg >:D -lunar + eventsList.cameraSpacing = 0; eventsList.topHeight = 0; + + changeTab(0); + boundWindow(); + } + + public var curEvent:Int = -1; + public var winHeight:Float = 0; + + public function changeTab(id:Int, save:Bool = true) { + if (save) + saveCurTab(); + + // destroy old elements + paramsFields = []; + for(e in paramsPanel) { + e.destroy(); + paramsPanel.remove(e); + } + + winHeight = eventName.y + eventName.height + 6; + eventCam.scroll.y = 0; + + if (id >= 0 && id < events.length) { + curEvent = id; + var curEvent = events[curEvent]; + eventName.text = curEvent.name; + bWidth = eventName.x + eventName.width+16; + + // add new elements + for(k=>param in EventsData.getEventParams(curEvent.name)) { + function addLabel() { + var label:UIText = new UIText(eventName.x+6, winHeight, 0, param.name); + winHeight += label.height + 4; + paramsPanel.add(label); + + bWidth = Math.max(bWidth, label.x+label.width+10); + }; + + var value:Dynamic = CoolUtil.getDefault(curEvent.params[k], param.defValue); + var lastAdded = switch(param.type) { + case TString: + addLabel(); + var textBox:UITextBox = new UITextBox(eventName.x+6, winHeight, cast value); + paramsPanel.add(textBox); paramsFields.push(textBox); + textBox; + case TBool: + winHeight += 2; + var checkbox = new UICheckbox(eventName.x+8, winHeight, param.name, cast value); + paramsPanel.add(checkbox); paramsFields.push(checkbox); + checkbox; + case TInt(min, max, step): + addLabel(); + var numericStepper = new UINumericStepper(eventName.x+6, winHeight, cast value, step.getDefault(1), 0, min, max); + paramsPanel.add(numericStepper); paramsFields.push(numericStepper); + numericStepper; + case TFloat(min, max, step, precision): + addLabel(); + var numericStepper = new UINumericStepper(eventName.x+6, winHeight, cast value, step.getDefault(1), precision, min, max); + paramsPanel.add(numericStepper); paramsFields.push(numericStepper); + numericStepper; + case TStrumLine: + addLabel(); + var dropdown = new UIDropDown(eventName.x+6, winHeight, 320, 32, [for(k=>s in cast(FlxG.state, Charter).strumLines.members) 'Strumline #${k+1} (${s.strumLine.characters[0]})'], cast value); + paramsPanel.add(dropdown); paramsFields.push(dropdown); + dropdown; + case TColorWheel: + addLabel(); + var colorWheel = new UIColorwheel(eventName.x+6, winHeight, value is String ? FlxColor.fromString(value) : Std.int(value)); + paramsPanel.add(colorWheel); paramsFields.push(colorWheel); + colorWheel; + case TDropDown(options): + addLabel(); + var optionIndex = options.indexOf(cast value); + if(optionIndex < 0) { + optionIndex = 0; + } + var dropdown = new UIDropDown(eventName.x+6, winHeight, 320, 32, options, optionIndex); + paramsPanel.add(dropdown); paramsFields.push(dropdown); + dropdown; + case TCharacter: + addLabel(); + var charFileList = Character.getList(false); + var textBox:UIAutoCompleteTextBox = new UIAutoCompleteTextBox(eventName.x+6, winHeight, cast value); + textBox.suggestItems = charFileList; + paramsPanel.add(textBox); paramsFields.push(textBox); + textBox; + case TStage: + addLabel(); + var stageFileList = Stage.getList(false); + var textBox:UIAutoCompleteTextBox = new UIAutoCompleteTextBox(eventName.x+6, winHeight, cast value); + textBox.suggestItems = stageFileList; + paramsPanel.add(textBox); paramsFields.push(textBox); + textBox; + default: + paramsFields.push(null); + null; + } + if (lastAdded is UISliceSprite) { + winHeight += cast(lastAdded, UISliceSprite).bHeight + 4; + bWidth = Math.max(bWidth, eventName.x + 6 + cast(lastAdded, UISliceSprite).bWidth + 6 + 10); + } + else if (lastAdded is FlxSprite) { + winHeight += cast(lastAdded, FlxSprite).height + 6; + bWidth = Math.max(bWidth, eventName.x + 6 + cast(lastAdded, FlxSprite).width + 6 + 10); + } + } + } else { + eventName.text = "No event"; + curEvent = -1; + + bWidth = eventName.x + eventName.width+16; + } + + winHeight = Math.max(winHeight, (eventsList.buttonSize.y * (eventsList.buttons.length + 1))-4) + 10; + bWidth += 10+75+10+4; // add events list width to account for event name being on a diff cam +4 because margins >:D + update(0); + } + + public function boundWindow() { + var screenPos:FlxPoint = CoolUtil.pointToScreenPosition(FlxPoint.get(chartEvent.x, chartEvent.y + chartEvent.bHeight)); + screenPos.x -= chartEvent.global ? -8 : 68+12; screenPos.y += 8; + + bg.bWidth = cam.width = cast bWidth-6; + bg.bHeight = cam.height = cast winHeight + 10; + eventsList.bHeight = cast winHeight - 10; + + // Bound stuff from being off screen + var screenSpaceY:Float = (screenPos.y + winHeight + 10) - FlxG.height; + screenPos.x = FlxMath.bound(screenPos.x, 4, (FlxG.width - 20) - bWidth - 4); // FlxG.width - 20 for the scroll bar on the right + if (screenSpaceY > -8) { // border of 8 of screen >:D + bg.bHeight -= cast screenSpaceY + 8; cam.height -= cast screenSpaceY + 8; + + screenSpaceY -= 20; // events list is slight diff with its camera + if (screenSpaceY > -8) eventsList.bHeight -= cast screenSpaceY + 8; + } + + cam.x = screenPos.x; cam.y = screenPos.y; + + eventCam.x = cam.x+10+75+10; eventCam.y = cam.y+10; + eventCam.width = bg.bWidth-(10+75+10); eventCam.height = bg.bHeight - 20; + + screenPos.put(); + } + + @:noCompletion var __clickedWhileHovering = false; + @:noCompletion var __ignoreLastClick:Bool = false; + var sinner:Float = 0; + override public function update(elapsed:Float) { + var mousepoint = FlxG.mouse.getPositionInCameraView(cam); + boundWindow(); + + if (FlxG.mouse.justPressed && (FlxMath.inBounds(mousepoint.x, 0, bg.bWidth) && FlxMath.inBounds(mousepoint.y, 0, bg.bHeight))) __clickedWhileHovering = true; + + var boundingParamY:Float = 0; + var lastParamSprite:FlxObject = paramsFields != null ? cast paramsFields[paramsFields.length-1] : null; + if (lastParamSprite != null) boundingParamY = lastParamSprite.y; + + if (FlxMath.inBounds(mousepoint.x, 0, bg.bWidth) && FlxMath.inBounds(mousepoint.y, 0, bg.bHeight)) { + Charter.instance.shouldScroll = false; + if (FlxMath.inBounds(mousepoint.x, 10+75+10, eventCam.width) && FlxMath.inBounds(mousepoint.y, 10, eventCam.height)) { + var boundingParamYHeight:Float = boundingParamY; + if (lastParamSprite != null) { + if (lastParamSprite is UISliceSprite) boundingParamYHeight += cast(lastParamSprite, UISliceSprite).bHeight; + else if (lastParamSprite is FlxSprite) boundingParamYHeight += cast(lastParamSprite, FlxSprite).height; + } + + if (boundingParamYHeight > eventCam.height) + eventCam.scroll.y = CoolUtil.bound(eventCam.scroll.y - (FlxG.mouse.wheel * 12), 0, boundingParamYHeight - eventCam.height); + } + } else if (Charter.instance.curContextMenu == null && ((FlxG.mouse.justReleased && !__clickedWhileHovering) || FlxG.mouse.wheel != 0)) { + if (__ignoreLastClick) + __ignoreLastClick = false; + else { + Charter.instance.shouldScroll = true; + quit(); + } + } + super.update(elapsed); + + if (FlxG.mouse.justReleased) __clickedWhileHovering = false; + + sinner += elapsed; + + downIndicator.setPosition(((eventCam.width/2)) - (downIndicator.fieldWidth/2), (eventCam.height-downIndicator.height) - 2 - (FlxMath.fastSin(sinner*2) * 2)); + downIndicator.alpha = CoolUtil.fpsLerp(downIndicator.alpha, (eventCam.scroll.y+eventCam.height > boundingParamY) ? 0 : 1, 1/3); + } + + public function quit() { + saveCurTab(); + chartEvent.refreshEventIcons(); + Charter.instance.updateBPMEvents(); + + if (events.length <= 0) + Charter.instance.deleteSelection([chartEvent]); + else if (events.length > 0) { + var oldEvents:Array<ChartEvent> = chartEvent.events.copy(); + chartEvent.events = [for (i in eventsList.buttons.members) i.event]; + + Charter.undos.addToUndo(CEditEvent(chartEvent, oldEvents, [for (event in events) Reflect.copy(event)])); + } + close(); + } + + public function saveCurTab() { + if (curEvent < 0) return; + + events[curEvent].params = [ + for(p in paramsFields) { + if (p is UIDropDown) { + var dataParams = EventsData.getEventParams(events[curEvent].name); + if (dataParams[paramsFields.indexOf(p)].type == TStrumLine) cast(p, UIDropDown).index; + else cast(p, UIDropDown).label.text; + } + else if (p is UINumericStepper) { + var stepper = cast(p, UINumericStepper); + @:privateAccess stepper.__onChange(stepper.label.text); + if (stepper.precision == 0) // int + Std.int(stepper.value); + else + stepper.value; + } + else if (p is UITextBox) + cast(p, UITextBox).label.text; + else if (p is UICheckbox) + cast(p, UICheckbox).checked; + else if (p is UIColorwheel) + cast(p, UIColorwheel).curColor; + else + null; + } + ]; + } + public override function destroy() { + super.destroy(); + FlxG.cameras.remove(cam); + FlxG.cameras.remove(eventCam); + } +} + +class EventButtonNew extends UIButton { + public var icon:FlxSprite = null; + public var event:ChartEvent = null; + public var deleteButton:UIButton; + public var deleteIcon:FlxSprite; + + public function new(event:ChartEvent, icon:FlxSprite, id:Int, substate:CharterEventScreenNew, parent:UIButtonList<EventButtonNew>) { + this.icon = icon; + this.event = event; + super(0, 0, null, function() { + substate.changeTab(id); + for(i in parent.buttons.members) + i.alpha = i == this ? 1 : 0.25; + }, 73, 40); + autoAlpha = false; + + members.push(icon); + icon.setPosition(18 - icon.width / 2, 20 - icon.height / 2); + + deleteButton = new UIButton(bWidth - 30, y + (bHeight - 26) / 2, null, function () { + substate.events.splice(id, 1); + substate.changeTab(id, false); + parent.remove(this); + }, 26, 26); + deleteButton.color = FlxColor.RED; + deleteButton.autoAlpha = false; + members.push(deleteButton); + + deleteIcon = new FlxSprite(deleteButton.x + (15/2), deleteButton.y + 4).loadGraphic(Paths.image('editors/delete-button')); + deleteIcon.antialiasing = false; + members.push(deleteIcon); + } + + override function update(elapsed) { + super.update(elapsed); + + deleteButton.selectable = selectable; + deleteButton.shouldPress = shouldPress; + + icon.setPosition(x + (18 - icon.width / 2),y + (20 - icon.height / 2)); + deleteButton.setPosition(x + (bWidth - 30), y + (bHeight - 26) / 2); + deleteIcon.setPosition(deleteButton.x + (10/2), deleteButton.y + 4); + } +} \ No newline at end of file diff --git a/source/funkin/editors/charter/CharterEventTypeSelection.hx b/source/funkin/editors/charter/CharterEventTypeSelection.hx index bfa6cfe7..c701b1b9 100644 --- a/source/funkin/editors/charter/CharterEventTypeSelection.hx +++ b/source/funkin/editors/charter/CharterEventTypeSelection.hx @@ -1,9 +1,11 @@ package funkin.editors.charter; +import funkin.backend.system.Conductor; import funkin.backend.chart.EventsData; class CharterEventTypeSelection extends UISubstateWindow { var callback:String->Void; + var eventStepTime:Float; var buttons:Array<UIButton> = []; @@ -13,9 +15,10 @@ class CharterEventTypeSelection extends UISubstateWindow { var upIndicator:UIText; var downIndicator:UIText; - public function new(callback:String->Void) { + public function new(callback:String->Void, eventStepTime:Float) { super(); this.callback = callback; + this.eventStepTime = eventStepTime; } public override function create() { @@ -28,10 +31,19 @@ class CharterEventTypeSelection extends UISubstateWindow { FlxG.cameras.add(buttonCameras, false); buttonCameras.bgColor = 0; - buttonsBG = new UIWindow(10, 41, buttonCameras.width, buttonCameras.height, ""); + buttonsBG = new UIWindow(10, 41, buttonCameras.width, buttonCameras.height, null); buttonsBG.frames = Paths.getFrames('editors/ui/inputbox'); add(buttonsBG); + var disableConductorEvents:Bool = false; + var disableOnlyContinuousChanges:Bool = false; + for (change in Conductor.bpmChangeMap) { + if (change.continuous && MathUtil.greaterThanEqual(eventStepTime, change.stepTime) && MathUtil.lessThan(eventStepTime, change.endStepTime)) { + disableOnlyContinuousChanges = MathUtil.equal(eventStepTime, change.stepTime); //allow time sig and instant bpm changes on the same event + disableConductorEvents = !disableOnlyContinuousChanges; + } + } + for(k=>eventName in EventsData.eventsList) { var button = new UIButton(0, (32 * k), eventName, function() { close(); @@ -52,6 +64,11 @@ class CharterEventTypeSelection extends UISubstateWindow { icon.x = button.x + 8; icon.y = button.y + Math.abs(button.bHeight - icon.height) / 2; add(icon); + + if (disableConductorEvents && (eventName == "Time Signature Change" || eventName == "Continuous BPM Change" || eventName == "BPM Change") || (disableOnlyContinuousChanges && eventName == "Continuous BPM Change")) { + button.selectable = button.shouldPress = false; + button.autoAlpha = true; + } } windowSpr.bHeight = 61 + (32 * (17)); @@ -79,7 +96,7 @@ class CharterEventTypeSelection extends UISubstateWindow { sinner += elapsed; for (button in buttons) - button.selectable = buttonsBG.hovered; + if (button.shouldPress) button.selectable = buttonsBG.hovered; buttonCameras.zoom = subCam.zoom; @@ -87,7 +104,7 @@ class CharterEventTypeSelection extends UISubstateWindow { buttonCameras.y = -subCam.scroll.y + Std.int(windowSpr.y+41); if (buttons.length > 16) - buttonCameras.scroll.y = FlxMath.bound(buttonCameras.scroll.y - (buttonsBG.hovered ? FlxG.mouse.wheel : 0) * 12, 0, + buttonCameras.scroll.y = CoolUtil.bound(buttonCameras.scroll.y - (buttonsBG.hovered ? FlxG.mouse.wheel : 0) * 12, 0, (buttons[buttons.length-1].y + buttons[buttons.length-1].bHeight) - buttonCameras.height); upIndicator.setPosition((buttonsBG.bWidth/2) - (upIndicator.fieldWidth/2), 22 + (FlxMath.fastSin(sinner*2) * 4)); diff --git a/source/funkin/editors/charter/CharterMetaDataScreen.hx b/source/funkin/editors/charter/CharterMetaDataScreen.hx index 689f0a3d..73ea87d8 100644 --- a/source/funkin/editors/charter/CharterMetaDataScreen.hx +++ b/source/funkin/editors/charter/CharterMetaDataScreen.hx @@ -15,7 +15,7 @@ class CharterMetaDataScreen extends UISubstateWindow { public var songNameTextBox:UITextBox; public var bpmStepper:UINumericStepper; public var beatsPerMeasureStepper:UINumericStepper; - public var stepsPerBeatStepper :UINumericStepper; + public var denominatorStepper:UINumericStepper; public var customPropertiesButtonList:UIButtonList<PropertyButton>; public var displayNameTextBox:UITextBox; @@ -62,8 +62,8 @@ class CharterMetaDataScreen extends UISubstateWindow { add(new UIText(beatsPerMeasureStepper.x + 30, beatsPerMeasureStepper.y + 3, 0, "/", 22)); - stepsPerBeatStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, metadata.stepsPerBeat, 1, 0, 1, null, 54); - add(stepsPerBeatStepper); + denominatorStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, Math.floor(16 / metadata.stepsPerBeat), 1, 0, 1, null, 54); + add(denominatorStepper); add(title = new UIText(songNameTextBox.x, songNameTextBox.y + 10 + 46, 0, "Menus Data (Freeplay/Story)", 28)); @@ -93,7 +93,7 @@ class CharterMetaDataScreen extends UISubstateWindow { add(difficultiesTextBox); addLabelOn(difficultiesTextBox, "Difficulties"); - customPropertiesButtonList = new UIButtonList<PropertyButton>(stepsPerBeatStepper.x + 80 + 26 + 105, songNameTextBox.y, 290, 316, '', FlxPoint.get(280, 35), null, 5); + customPropertiesButtonList = new UIButtonList<PropertyButton>(denominatorStepper.x + 80 + 26 + 105, songNameTextBox.y, 290, 316, '', FlxPoint.get(280, 35), null, 5); customPropertiesButtonList.frames = Paths.getFrames('editors/ui/inputbox'); customPropertiesButtonList.cameraSpacing = 0; customPropertiesButtonList.addButton.callback = function() { @@ -136,8 +136,7 @@ class CharterMetaDataScreen extends UISubstateWindow { } public function saveMeta() { - for (stepper in [bpmStepper, beatsPerMeasureStepper, stepsPerBeatStepper]) - @:privateAccess stepper.__onChange(stepper.label.text); + UIUtil.confirmUISelections(this); var customVals = {}; for (vals in customPropertiesButtonList.buttons.members) { @@ -148,7 +147,7 @@ class CharterMetaDataScreen extends UISubstateWindow { name: songNameTextBox.label.text, bpm: bpmStepper.value, beatsPerMeasure: Std.int(beatsPerMeasureStepper.value), - stepsPerBeat: Std.int(stepsPerBeatStepper.value), + stepsPerBeat: Std.int(16 / denominatorStepper.value), displayName: displayNameTextBox.label.text, icon: iconTextBox.label.text, color: colorWheel.curColor, diff --git a/source/funkin/editors/charter/CharterNote.hx b/source/funkin/editors/charter/CharterNote.hx index 9295be65..715cf3f7 100644 --- a/source/funkin/editors/charter/CharterNote.hx +++ b/source/funkin/editors/charter/CharterNote.hx @@ -1,5 +1,6 @@ package funkin.editors.charter; +import flixel.math.FlxAngle; import flixel.math.FlxPoint; import flixel.tweens.FlxTween; import flixel.util.FlxColor; @@ -28,6 +29,8 @@ class CharterNote extends UISprite implements ICharterSelectable { public var selected:Bool = false; public var draggable:Bool = true; + static var noteTypeTexts:Array<UIText> = []; + public function new() { super(); @@ -38,39 +41,51 @@ class CharterNote extends UISprite implements ICharterSelectable { this.setUnstretchedGraphicSize(40, 40, false); sustainSpr = new UISprite(10, 20); - sustainSpr.makeSolid(1, 1, -1); + sustainSpr.makeSolid(1, 1, -1, false, "note-sustain"); sustainSpr.scale.set(10, 0); members.push(sustainSpr); - typeText = new UIText(x, y, 0, Std.string(type)); + this.type = 0; - cursor = sustainSpr.cursor = BUTTON; + cursor = sustainSpr.cursor = CLICK; moves = false; } public override function updateButtonHandler() { - __rect.x = x; - __rect.y = y; - __rect.width = 40; - __rect.height = 40; + __rect.set(x, y, 40, 40); UIState.state.updateRectButtonHandler(this, __rect, onHovered); } public var step:Float; public var id:Int; public var susLength:Float; - public var type:Int; + public var type(default, set):Null<Int> = null; + public var typeVisible:Bool = true; + public var typeAlpha:Float = 1; + + function set_type(v:Null<Int>) { + if(v != this.type) { + if(noteTypeTexts[v] == null || noteTypeTexts[v].graphic.isDestroyed) { + noteTypeTexts[v] = new UIText(x, y, 0, Std.string(v)); + noteTypeTexts[v].exists = v != 0; + //noteTypeTexts[v].layer = (Charter.instance != null) ? Charter.instance.textLayer : null; + } + + typeText = noteTypeTexts[v]; + } + return this.type = v; + } public var strumLine:CharterStrumline; public var strumLineID(get, default):Int = -1; public function get_strumLineID():Int return strumLineID = (strumLine == null ? strumLineID : Charter.instance.strumLines.members.indexOf(strumLine)); - public var snappedToStrumline:Bool = true; + public var snappedToGrid:Bool = true; public var fullID(get, never):Int; // instead of %4 get fullID (for mousepos stuff) public function get_fullID():Int - return (strumLineID * 4) + id; + return strumLine.startingID + id; public function updatePos(step:Float, id:Int, susLength:Float = 0, ?type:Int = 0, ?strumLine:CharterStrumline = null) { this.step = step; @@ -79,16 +94,14 @@ class CharterNote extends UISprite implements ICharterSelectable { this.type = type; if (strumLine != null) this.strumLine = strumLine; - typeText.exists = type != 0; - typeText.text = Std.string(this.type); - typeText.follow(this, 20 - (typeText.frameWidth/2), 20 - (typeText.frameHeight/2)); + sustainSpr.exists = susLength != 0; y = step * 40; if (angleTween != null) angleTween.cancel(); - var destAngle = switch(animation.curAnim.curFrame = (id % 4)) { - case 0: -90; + var destAngle:Float = switch(animation.curAnim.curFrame = (id % 4)) { + case 0: 270; case 1: 180; case 2: 0; case 3: 90; @@ -107,7 +120,9 @@ class CharterNote extends UISprite implements ICharterSelectable { if(angleTween != null) angleTween.cancel(); - angleTween = FlxTween.tween(this, {angle: destAngle}, (2/3)/__animSpeed, {ease: function(t) { + destAngle = CoolUtil.getClosestAngle(angle, destAngle); + + angleTween = FlxTween.angle(this, angle, destAngle, (2/3)/__animSpeed, {ease: function(t) { return ((Math.sin(t * Math.PI) * 0.35) * 3 * t * Math.sqrt(1 - t)) + t; }}); } @@ -117,7 +132,7 @@ class CharterNote extends UISprite implements ICharterSelectable { angleTween.cancel(); angleTween = null; angle = switch(animation.curAnim.curFrame = (id % 4)) { - case 0: -90; + case 0: 270; case 1: 180; case 2: 0; case 3: 90; @@ -132,28 +147,28 @@ class CharterNote extends UISprite implements ICharterSelectable { public override function update(elapsed:Float) { super.update(elapsed); - var sprLength:Float = (40 * (susLength+tempSusLength)) + ((susLength+tempSusLength) != 0 ? (height/2) : 0); - sustainSpr.scale.set(10, __susInstaLerp ? sprLength : CoolUtil.fpsLerp(sustainSpr.scale.y, sprLength, 1/2)); - sustainSpr.updateHitbox(); - sustainSpr.follow(this, 15, 20); + if(susLength != 0) { + var sprLength:Float = (40 * (susLength+tempSusLength)) + ((susLength+tempSusLength) != 0 ? (height/2) : 0); + sustainSpr.scale.set(10, __susInstaLerp ? sprLength : CoolUtil.fpsLerp(sustainSpr.scale.y, sprLength, 1/2)); + sustainSpr.updateHitbox(); + sustainSpr.follow(this, 15, 20); + } sustainDraggable = false; if (!hovered && susLength != 0) { - UIState.state.updateSpriteRect(sustainSpr); + sustainSpr.updateSpriteRect(); sustainDraggable = UIState.state.isOverlapping(sustainSpr, @:privateAccess sustainSpr.__rect); } - if (typeText.exists) - typeText.follow(this, 20 - (typeText.frameWidth/2), 20 - (typeText.frameHeight/2)); - if (__passed != (__passed = step < Conductor.curStepFloat + (Conductor.songOffset / Conductor.stepCrochet))) { if (__passed && FlxG.sound.music.playing && Charter.instance.hitsoundsEnabled(strumLineID)) Charter.instance.hitsound.replay(); } if (strumLine != null) { - sustainSpr.alpha = alpha = !strumLine.strumLine.visible ? (__passed ? 0.2 : 0.4) : (__passed ? 0.6 : 1); - typeText.alpha = !strumLine.strumLine.visible ? (__passed ? 0.4 : 0.6) : (__passed ? 0.8 : 1); + var isVisible = strumLine.strumLine.visible; + sustainSpr.alpha = alpha = !isVisible ? (__passed ? 0.2 : 0.4) : (__passed ? 0.6 : 1); + typeAlpha = !isVisible ? (__passed ? 0.4 : 0.6) : (__passed ? 0.8 : 1); } colorTransform.redMultiplier = colorTransform.greenMultiplier = colorTransform.blueMultiplier = selected ? 0.75 : 1; @@ -173,18 +188,27 @@ class CharterNote extends UISprite implements ICharterSelectable { } public function handleDrag(change:FlxPoint) { - var newStep = FlxMath.bound(step + change.x, 0, Charter.instance.__endStep-1); - var newID:Int = Std.int(FlxMath.bound(fullID + Std.int(change.y), 0, (Charter.instance.strumLines.members.length*4)-1)); + var newStep = CoolUtil.bound(step + change.x, 0, Charter.instance.__endStep-1); + var newID:Int = Std.int(FlxMath.bound(fullID + Std.int(change.y), 0, Charter.instance.strumLines.totalKeyCount-1)); + var newStrumLine = Charter.instance.strumLines.getStrumlineFromID(newID); - updatePos(newStep, newID % 4, susLength, type, Charter.instance.strumLines.members[Std.int(newID/4)]); + updatePos(newStep, (newID - newStrumLine.startingID) % newStrumLine.keyCount, susLength, type, newStrumLine); } public override function draw() { - if (snappedToStrumline) - x = (strumLine != null ? strumLine.x : 0) + (id % 4) * 40; + if (snappedToGrid) + x = (strumLine != null ? strumLine.x : 0) + (id % (strumLine != null ? strumLine.keyCount : 4)) * 40; drawMembers(); drawSuper(); - if(typeText.exists && typeText.visible) typeText.draw(); + drawNoteTypeText(); + } + + public inline function drawNoteTypeText() { + if(typeText.exists && typeText.visible && typeVisible) { + typeText.alpha = typeAlpha; + typeText.follow(this, 20 - (typeText.frameWidth/2), 20 - (typeText.frameHeight/2)); + typeText.draw(); + } } } \ No newline at end of file diff --git a/source/funkin/editors/charter/CharterNoteGroup.hx b/source/funkin/editors/charter/CharterNoteGroup.hx index c2c2e388..31c7eae3 100644 --- a/source/funkin/editors/charter/CharterNoteGroup.hx +++ b/source/funkin/editors/charter/CharterNoteGroup.hx @@ -60,14 +60,14 @@ class CharterNoteGroup extends FlxTypedGroup<CharterNote> { public override function update(elapsed:Float) @:privateAccess { var oldDefaultCameras = FlxCamera._defaultCameras; - if (cameras != null) FlxCamera._defaultCameras = cameras; + if (_cameras != null) FlxCamera._defaultCameras = _cameras; if (autoSort && members.length != __lastSort) sortNotes(); forEach((n) -> { if(n.exists && n.active) { - n.cameras = n.__lastDrawCameras = cameras; + n._cameras = n.__lastDrawCameras = cameras; n.update(elapsed); } }); diff --git a/source/funkin/editors/charter/CharterNoteHoverer.hx b/source/funkin/editors/charter/CharterNoteHoverer.hx index 3ef2e543..3f7146ba 100644 --- a/source/funkin/editors/charter/CharterNoteHoverer.hx +++ b/source/funkin/editors/charter/CharterNoteHoverer.hx @@ -6,8 +6,8 @@ class CharterNoteHoverer extends CharterNote { public function new() { super(); - snappedToStrumline = selectable = autoAlpha = false; visible = sustainSpr.visible = false; - @:privateAccess __animSpeed = 1.25; typeText.visible = false; alpha = 0.4; + snappedToGrid = selectable = autoAlpha = false; visible = sustainSpr.visible = false; + @:privateAccess __animSpeed = 1.25; typeVisible = false; alpha = 0.4; } @:noCompletion var __mousePos:FlxPoint = FlxPoint.get(); @@ -17,10 +17,10 @@ class CharterNoteHoverer extends CharterNote { switch (Charter.instance.gridActionType) { case NONE: var inBoundsY:Bool = (__mousePos.y > 0 && __mousePos.y < (Charter.instance.__endStep)*40); - if ((__mousePos.x > 0 && __mousePos.x < Charter.instance.gridBackdrops.strumlinesAmount * 160 && inBoundsY) && showHoverer) { - step = FlxMath.bound(FlxG.keys.pressed.SHIFT ? ((__mousePos.y-20) / 40) : Charter.instance.quantStep(__mousePos.y/40), 0, Charter.instance.__endStep-1); - id = Math.floor(__mousePos.x / 40); y = step * 40; x = id * 40; visible = true; sustainSpr.visible = typeText.visible = false; - angle = switch(animation.curAnim.curFrame = (id % 4)) { + if ((__mousePos.x > 0 && __mousePos.x < Charter.instance.strumLines.totalKeyCount * 40 && inBoundsY) && showHoverer) { + step = CoolUtil.bound(FlxG.keys.pressed.SHIFT ? ((__mousePos.y-20) / 40) : Charter.instance.quantStep(__mousePos.y/40), 0, Charter.instance.__endStep-1); + id = Math.floor(__mousePos.x / 40); y = step * 40; x = id * 40; visible = true; sustainSpr.visible = typeVisible = false; + angle = switch(animation.curAnim.curFrame = ((id - Charter.instance.strumLines.getStrumlineFromID(id).startingID) % 4)) { case 0: -90; case 1: 180; case 2: 0; @@ -30,9 +30,9 @@ class CharterNoteHoverer extends CharterNote { } else visible = false; case NOTE_DRAG: - visible = sustainSpr.visible = typeText.visible = true; __doAnim = false; + visible = sustainSpr.visible = typeVisible = true; __doAnim = false; default: - visible = sustainSpr.visible = typeText.visible = false; __doAnim = false; + visible = sustainSpr.visible = typeVisible = false; __doAnim = false; } } @@ -53,8 +53,8 @@ class CharterNoteHoverer extends CharterNote { y -= ((draggingNote.step + verticalChange) - Charter.instance.quantStepRounded(draggingNote.step+verticalChange, verticalChange > 0 ? 0.35 : 0.65)); y *= 40; - var newID:Int = Std.int(FlxMath.bound(draggingNote.fullID + horizontalChange, 0, (Charter.instance.strumLines.members.length*4)-1)); - x = (id=newID) * 40; y = FlxMath.bound(y, 0, (Charter.instance.__endStep*40) - height); + var newID:Int = CoolUtil.boundInt(draggingNote.fullID + horizontalChange, 0, Charter.instance.strumLines.totalKeyCount-1); + x = (id=newID) * 40; y = CoolUtil.bound(y, 0, (Charter.instance.__endStep*40) - height); angle = switch(animation.curAnim.curFrame = (draggingNote.id % 4)) { case 0: -90; @@ -69,8 +69,9 @@ class CharterNoteHoverer extends CharterNote { sustainSpr.updateHitbox(); sustainSpr.alpha = alpha; sustainSpr.follow(this, 15, 20); sustainSpr.exists = draggingNote.susLength != 0; - typeText.text = Std.string(draggingNote.type); - typeText.exists = draggingNote.type != 0; + type = draggingNote.type; + //typeText.text = Std.string(draggingNote.type); + //typeText.exists = draggingNote.type != 0; typeText.follow(this, 20 - (typeText.frameWidth/2), 20 - (typeText.frameHeight/2)); super.draw(); diff --git a/source/funkin/editors/charter/CharterNoteTypesList.hx b/source/funkin/editors/charter/CharterNoteTypesList.hx index 859b32c5..7056d1b8 100644 --- a/source/funkin/editors/charter/CharterNoteTypesList.hx +++ b/source/funkin/editors/charter/CharterNoteTypesList.hx @@ -26,7 +26,7 @@ class CharterNoteTypesList extends UISubstateWindow { var noteTypesFileList = getNoteTypesList(true); if (noteTypesFileList.length == 0) noteTypesFileList = getNoteTypesList(false); - noteTypesList = new UIButtonList<NoteTypeButton>(20, title.y + title.height + 10, winWidth - 40, 342 - 85 - 16, "", FlxPoint.get(winWidth - 40, (342 - 85 - 16)/4), null, 0); + noteTypesList = new UIButtonList<NoteTypeButton>(20, title.y + title.height + 10, winWidth - 40, 342 - 85 - 16, null, FlxPoint.get(winWidth - 40, (342 - 85 - 16)/4), null, 0); noteTypesList.addButton.callback = () -> noteTypesList.add(new NoteTypeButton(0, 0, 'Note Type ${noteTypesList.buttons.members.length}', noteTypesList.buttons.members.length, noteTypesFileList, noteTypesList)); noteTypesList.cameraSpacing = 0; noteTypesList.dragCallback = (object:NoteTypeButton, oldIndex:Int, newIndex:Int) -> {object.IDText.text = '${newIndex}.';}; for (i=>noteType in Charter.instance.noteTypes) @@ -91,7 +91,7 @@ class NoteTypeButton extends UIButton { }; textBox.antialiasing = true; - deleteButton = new UIButton(textBox.x + textBox.label.width + 10, textBox.y, "", function () { + deleteButton = new UIButton(textBox.x + textBox.label.width + 10, textBox.y, null, function () { parent.remove(this); }, 32); deleteButton.color = 0xFFFF0000; diff --git a/source/funkin/editors/charter/CharterPreviewStrumLine.hx b/source/funkin/editors/charter/CharterPreviewStrumLine.hx new file mode 100644 index 00000000..91aa5cca --- /dev/null +++ b/source/funkin/editors/charter/CharterPreviewStrumLine.hx @@ -0,0 +1,72 @@ +package funkin.editors.charter; + +import funkin.game.Note; + +class CharterPreviewStrumLine extends FlxTypedGroup<FlxSprite> +{ + public function new(x:Float, y:Float, scale:Float, spacing:Float, keyCount:Int, scrollSpeed:Float){ + super(); + updatePos(x, y, scale, spacing, keyCount, scrollSpeed); + } + + private var note:FlxSprite; + + public function generateStrums(x:Float, y:Float, scale:Float, spacing:Float, keyCount:Int, scrollSpeed:Float){ + for (member in members){ + member.destroy(); + } + clear(); + + var strumAnimPrefix = ["left", "down", "up", "right"]; + for (i in 0...keyCount){ + var strum = new FlxSprite(); + strum.frames = Paths.getFrames("game/notes/default"); + strum.setGraphicSize(Std.int((strum.width * 0.7) * scale)); + strum.updateHitbox(); + + var animPrefix = strumAnimPrefix[i % 4]; + strum.animation.addByPrefix('static', 'arrow${animPrefix.toUpperCase()}'); + strum.animation.play('static'); + strum.alpha = 0.4; + add(strum); + } + + note = new FlxSprite(); + note.frames = Paths.getFrames("game/notes/default"); + note.setGraphicSize(Std.int((note.width * 0.7) * scale)); + note.updateHitbox(); + note.animation.addByPrefix('purple', 'purple0'); + note.animation.play('purple'); + note.alpha = 0.4; + add(note); + } + + var noteTime:Float = FlxG.height; + var scroll:Float = 1.0; + + public function updatePos(x:Float, y:Float, scale:Float, spacing:Float, keyCount:Int, scrollSpeed:Float){ + if (members.length-1 != keyCount) //strumline + note + generateStrums(x, y, scale, spacing, keyCount, scrollSpeed); + + for (i in 0...keyCount){ + var strum = members[i]; + + strum.x = CoolUtil.fpsLerp(strum.x, x + (Note.swagWidth * scale * spacing * i), 0.2); + strum.y = CoolUtil.fpsLerp(strum.y, y + (Note.swagWidth*0.5) - (Note.swagWidth * scale * 0.5), 0.2); + strum.scale.x = strum.scale.y = CoolUtil.fpsLerp(strum.scale.x, 0.7 * scale, 0.2); + strum.updateHitbox(); + } + + + scroll = CoolUtil.fpsLerp(scroll, scrollSpeed, 0.2); + noteTime -= FlxG.elapsed * scroll * 1000 * 0.45; + if (noteTime <= 0.0) + noteTime = FlxG.height; + + note.x = members[0].x; + note.y = members[0].y + noteTime; + note.scale.x = note.scale.y = members[0].scale.x; + note.updateHitbox(); + note.alpha = CoolUtil.fpsLerp(note.alpha, scrollSpeed == 0.0 ? 0.0 : 0.4, 0.2); + } +} \ No newline at end of file diff --git a/source/funkin/editors/charter/CharterStrumLineGroup.hx b/source/funkin/editors/charter/CharterStrumLineGroup.hx index 296cffaa..5b1633a6 100644 --- a/source/funkin/editors/charter/CharterStrumLineGroup.hx +++ b/source/funkin/editors/charter/CharterStrumLineGroup.hx @@ -8,13 +8,25 @@ class CharterStrumLineGroup extends FlxTypedGroup<CharterStrumline> { var draggingObj:CharterStrumline = null; var draggingOffset:Float = 0; + public var totalKeyCount(get, never):Int; + private var __totalKeyCount:Int = -1; + public function get_totalKeyCount():Int { + if (__totalKeyCount != -1) return __totalKeyCount; + + var v:Int = 0; + for (strumLine in members) v += strumLine.keyCount; + return __totalKeyCount = v; + } + public var draggable:Bool = false; public var isDragging(get, never):Bool; public function get_isDragging():Bool return draggingObj != null; + var __mousePos:FlxPoint = new FlxPoint(); + public override function update(elapsed:Float) { - var mousePos = FlxG.mouse.getWorldPosition(cameras[0], FlxPoint.get()); + FlxG.mouse.getWorldPosition(cameras[0], __mousePos); for (strumLine in members) { if (strumLine == null) continue; strumLine.draggable = draggable; @@ -22,37 +34,51 @@ class CharterStrumLineGroup extends FlxTypedGroup<CharterStrumline> { draggingObj = strumLine; strumLine.dragging = true; - draggingOffset = mousePos.x - strumLine.button.x; + draggingOffset = __mousePos.x - strumLine.button.x; __pastStrumlines = members.copy(); break; } } if (isDragging) { - draggingObj.x = mousePos.x - draggingOffset; + draggingObj.x = __mousePos.x - draggingOffset; this.sort(function(o, a, b) return FlxSort.byValues(o, a.x, b.x), -1); + refreshStrumlineIDs(); } for (i=>strum in members) - if (strum != null && !strum.dragging) strum.x = CoolUtil.fpsLerp(strum.x, 160 * i, 0.225); + if (strum != null && !strum.dragging) strum.x = CoolUtil.fpsLerp(strum.x, 40*strum.startingID, 0.225); - if (Charter.instance.eventsBackdrop != null && members[0] != null) - Charter.instance.eventsBackdrop.x = members[0].button.x - Charter.instance.eventsBackdrop.width; + if (Charter.instance.leftEventsBackdrop != null && members[0] != null) { + Charter.instance.leftEventsBackdrop.x = members[0].button.x - Charter.instance.leftEventsBackdrop.width; + Charter.instance.leftEventsBackdrop.alpha = members[0].strumLine.visible ? 0.9 : 0.4; + + if (Charter.instance.leftEventRowText != null) + Charter.instance.leftEventRowText.x = members[0].button.x - Charter.instance.leftEventRowText.width - 42; + } + + if (Charter.instance.rightEventsBackdrop != null && members[CoolUtil.maxInt(0, members.length-1)] != null) { + Charter.instance.rightEventsBackdrop.x = members[members.length-1].x + (40*members[members.length-1].keyCount); + Charter.instance.rightEventsBackdrop.alpha = members[CoolUtil.maxInt(0, members.length-1)].strumLine.visible ? 0.9 : 0.4; + + if (Charter.instance.rightEventRowText != null) + Charter.instance.rightEventRowText.x = Charter.instance.rightEventsBackdrop.x + 42; + } + if (Charter.instance.strumlineLockButton != null && members[0] != null) - Charter.instance.strumlineLockButton.x = members[0].x - (40*4); - if (Charter.instance.strumlineAddButton != null && members[Std.int(Math.max(0, members.length-1))] != null) - Charter.instance.strumlineAddButton.x = members[members.length-1].x + (40*4); + Charter.instance.strumlineLockButton.x = members[0].x - (160); + if (Charter.instance.strumlineAddButton != null && members[CoolUtil.maxInt(0, members.length-1)] != null) + Charter.instance.strumlineAddButton.x = members[members.length-1].x + (40*members[members.length-1].keyCount); if ((FlxG.mouse.justReleased || !draggable) && isDragging) finishDrag(); - mousePos.put(); super.update(elapsed); } public function snapStrums() { for (i=>strum in members) - if (strum != null && !strum.dragging) strum.x = 160 * i; + if (strum != null && !strum.dragging) strum.x = 40*strum.startingID; } public function orderStrumline(strumLine:CharterStrumline, newID:Int) { @@ -61,6 +87,7 @@ class CharterStrumLineGroup extends FlxTypedGroup<CharterStrumline> { members.remove(strumLine); members.insert(newID, strumLine); + refreshStrumlineIDs(); finishDrag(false); } @@ -77,21 +104,39 @@ class CharterStrumLineGroup extends FlxTypedGroup<CharterStrumline> { draggingObj = null; fixEvents(); + refreshStrumlineIDs(); } public inline function fixEvents() { - for (i in Charter.instance.eventsGroup.members) { - for (j in i.events) { - var paramTypes:Array<EventParamInfo> = EventsData.getEventParams(j.name); - for (i => param in paramTypes) { - if (param.type != TStrumLine) continue; - j.params[i] = members.indexOf(__pastStrumlines[j.params[i]]); + for (eventsGroup in [Charter.instance.leftEventsGroup, Charter.instance.rightEventsGroup]) { + for (i in eventsGroup) { + for (j in i.events) { + var paramTypes:Array<EventParamInfo> = EventsData.getEventParams(j.name); + for (i => param in paramTypes) { + if (param.type != TStrumLine) continue; + j.params[i] = members.indexOf(__pastStrumlines[j.params[i]]); + } } } } __pastStrumlines = null; } + public inline function refreshStrumlineIDs() { + __totalKeyCount = -1; + @:privateAccess + for (strumLine in members) strumLine.__startingID = -1; + } + + public function getStrumlineFromID(id:Int) { + var v:Int = 0; + for (strumLine in members) { + v += strumLine.keyCount; + if (id < v) return strumLine; + } + return members.last(); + } + override function draw() @:privateAccess { var i:Int = 0; var basic:FlxBasic = null; diff --git a/source/funkin/editors/charter/CharterStrumline.hx b/source/funkin/editors/charter/CharterStrumline.hx index afe96ad1..2d4a4d78 100644 --- a/source/funkin/editors/charter/CharterStrumline.hx +++ b/source/funkin/editors/charter/CharterStrumline.hx @@ -23,6 +23,20 @@ class CharterStrumline extends UISprite { public var vocals:FlxSound; + public var keyCount:Int = 4; + public var startingID(get, null):Int; + private var __startingID:Int = -1; + public function get_startingID():Int { + if (__startingID != -1) return __startingID; + + var index = Charter.instance.strumLines.members.indexOf(this); + if (index < 1) return __startingID = 0; //-1 or 0 + + var v:Int = 0; + for (i in 0...index) v += Charter.instance.strumLines.members[i].keyCount; + return __startingID = v; + } + public var selectedWaveform(default, set):Int = -1; public function set_selectedWaveform(value:Int):Int { if (value == -1) waveformShader = null; @@ -32,7 +46,7 @@ class CharterStrumline extends UISprite { } return selectedWaveform = value; } - public var waveformShader:CustomShader; + public var waveformShader:CustomShader; public function new(strumLine:ChartStrumLine) { super(); @@ -43,7 +57,24 @@ class CharterStrumline extends UISprite { if(strumLine.visible == null) strumLine.visible = true; + var icons = strumLine.characters != null ? strumLine.characters : []; + + keyCount = strumLine.keyCount != null ? strumLine.keyCount : 4; + healthIcons = new FlxSpriteGroup(x, y); + + var maxCol = icons.length < 4 ? icons.length : 4; + var maxRow = Math.floor((icons.length-1) / 4) + 1; + for (i=>icon in icons) { + var healthIcon = new HealthIcon(Character.getIconFromCharName(icon)); + healthIcon.scale.x = healthIcon.scale.y = Math.max((0.6 - (icons.length / 20)), 0.35); + healthIcon.updateHitbox(); + healthIcon.x = FlxMath.lerp(0, Math.min(icons.length * 20, 120), (maxCol-1 != 0 ? (i % 4) / (maxCol-1) : 0)); + healthIcon.y = (draggable ? 29 : 7) + FlxMath.lerp(0, Math.min(maxRow * 15, 60), (maxRow-1 != 0 ? Math.floor(i / 4) / (maxRow-1) : 0)); + healthIcon.alpha = strumLine.visible ? 1 : 0.4; + healthIcons.add(healthIcon); + } + members.push(healthIcons); draggingSprite = new UISprite(); @@ -51,7 +82,7 @@ class CharterStrumline extends UISprite { draggingSprite.alpha = 0.4; draggingSprite.y = 9; draggingSprite.antialiasing = true; - draggingSprite.cursor = BUTTON; + draggingSprite.cursor = CLICK; members.push(draggingSprite); button = new CharterStrumlineOptions(this); @@ -68,16 +99,16 @@ class CharterStrumline extends UISprite { public override function update(elapsed:Float) { if (FlxG.keys.justPressed.K) draggable = !draggable; - healthIcons.follow(this, ((40 * 4) - healthIcons.width) / 2, 7 + (__healthYOffset = FlxMath.lerp(__healthYOffset, draggable ? 8 : 0, 1/20))); + healthIcons.follow(this, ((40 * keyCount) - healthIcons.width) / 2, 7 + (__healthYOffset = FlxMath.lerp(__healthYOffset, draggable ? 8 : 0, 1/20))); draggingSprite.selectable = draggable; - UIState.state.updateSpriteRect(draggingSprite); + draggingSprite.updateSpriteRect(); var dragScale:Float = FlxMath.lerp(draggingSprite.scale.x, draggable ? 1 : 0.8, 1/16); draggingSprite.scale.set(dragScale, dragScale); draggingSprite.updateHitbox(); - draggingSprite.follow(this, (160/2) - (draggingSprite.width/2), 6 + (__draggingYOffset = FlxMath.lerp(__draggingYOffset, draggable ? 3 : 0, 1/12))); + draggingSprite.follow(this, ((keyCount*40)/2) - (draggingSprite.width/2), 6 + (__draggingYOffset = FlxMath.lerp(__draggingYOffset, draggable ? 3 : 0, 1/12))); var fullAlpha:Float = UIState.state.isOverlapping(draggingSprite, @:privateAccess draggingSprite.__rect) || dragging ? 0.9 : 0.35; draggingSprite.alpha = FlxMath.lerp(draggingSprite.alpha, draggable ? fullAlpha : 0, 1/12); button.follow(this, 0, 95); @@ -88,16 +119,18 @@ class CharterStrumline extends UISprite { public function updateInfo() { var icons = strumLine.characters != null ? strumLine.characters : []; + keyCount = strumLine.keyCount != null ? strumLine.keyCount : 4; + healthIcons.clear(); + var maxCol = icons.length < 4 ? icons.length : 4; + var maxRow = Math.floor((icons.length-1) / 4) + 1; for (i=>icon in icons) { var healthIcon = new HealthIcon(Character.getIconFromCharName(icon)); - var newScale = 0.6 - (icons.length / 20); - var size = Std.int(150 * newScale); - healthIcon.setUnstretchedGraphicSize(size, size, true); + healthIcon.scale.x = healthIcon.scale.y = Math.max((0.6 - (icons.length / 20)), 0.35); healthIcon.updateHitbox(); - healthIcon.x = FlxMath.lerp(0, icons.length * 20, (icons.length-1 != 0 ? i / (icons.length-1) : 0)); - healthIcon.y = draggable ? 14 : 7; + healthIcon.x = FlxMath.lerp(0, Math.min(icons.length * 20, 120), (maxCol-1 != 0 ? (i % 4) / (maxCol-1) : 0)); + healthIcon.y = (draggable ? 14 : 7) + FlxMath.lerp(0, Math.min(maxRow * 15, 60), (maxRow-1 != 0 ? Math.floor(i / 4) / (maxRow-1) : 0)); healthIcon.alpha = strumLine.visible ? 1 : 0.4; healthIcons.add(healthIcon); } @@ -113,13 +146,13 @@ class CharterStrumlineOptions extends UITopMenuButton { public function new(parent:CharterStrumline) { super(0, 95, null, "Options ↓", []); strLine = parent; - bWidth = 40 * 4; - this.label.fieldWidth = bWidth; } public override function update(elapsed:Float) { super.update(elapsed); alpha = FlxMath.lerp(1/20, 1, alpha); // so that instead of 0% it is 33% visible + bWidth = 40 * strLine.keyCount; + this.label.fieldWidth = bWidth; } public override function openContextMenu() { @@ -172,6 +205,9 @@ class CharterStrumlineOptions extends UITopMenuButton { contextMenu.insert(1+Charter.waveformHandler.waveformList.length, null); - super.openContextMenu(); + var cam = Charter.instance.charterCamera; + var point = CoolUtil.worldToScreenPosition(this, cam); + curMenu = UIState.state.openContextMenu(contextMenu, null, point.x, point.y + (bHeight*cam.zoom), Std.int(bWidth * cam.zoom)); + point.put(); } } \ No newline at end of file diff --git a/source/funkin/editors/charter/CharterStrumlineButton.hx b/source/funkin/editors/charter/CharterStrumlineButton.hx index 979dda2a..a1955248 100644 --- a/source/funkin/editors/charter/CharterStrumlineButton.hx +++ b/source/funkin/editors/charter/CharterStrumlineButton.hx @@ -24,7 +24,7 @@ class CharterStrumlineButton extends UISprite { button.scale.set(0.85,0.85); button.updateHitbox(); button.antialiasing = true; - button.cursor = BUTTON; + button.cursor = CLICK; members.push(button); this.text = new UIText(0,0, 160, text); @@ -58,8 +58,8 @@ class CharterStrumlineButton extends UISprite { super.update(elapsed); + button.updateSpriteRect(); if (UIState.state.curContextMenu == null && UIState.state.isOverlapping(button, button.__rect) && button.visible) { - UIState.state.updateSpriteRect(button); buttonScale.set(0.95, 0.95); if (FlxG.mouse.justPressed && onClick != null) { onClick(); diff --git a/source/funkin/editors/charter/CharterStrumlineScreen.hx b/source/funkin/editors/charter/CharterStrumlineScreen.hx index ba2534b7..cd19059e 100644 --- a/source/funkin/editors/charter/CharterStrumlineScreen.hx +++ b/source/funkin/editors/charter/CharterStrumlineScreen.hx @@ -1,9 +1,13 @@ package funkin.editors.charter; +import funkin.game.StrumLine; import flixel.math.FlxPoint; import funkin.backend.chart.ChartData.ChartStrumLine; import funkin.game.Character; +import funkin.game.Note; +import funkin.game.HudCamera; import funkin.game.HealthIcon; +import flixel.tweens.FlxTween; class CharterStrumlineScreen extends UISubstateWindow { public var strumLineID:Int = -1; @@ -19,12 +23,17 @@ class CharterStrumlineScreen extends UISubstateWindow { public var visibleCheckbox:UICheckbox; public var scrollSpeedStepper:UINumericStepper; public var usesChartScrollSpeed:UICheckbox; + public var keyCountStepper:UINumericStepper; + public var hudSpacingStepper:UINumericStepper; public var characterIcons:Array<HealthIcon> = []; public var saveButton:UIButton; public var closeButton:UIButton; + public var strumLineCam:HudCamera; + public var previewStrumLine:CharterPreviewStrumLine; + private var onSave:ChartStrumLine -> Void = null; public function new(strumLineID:Int, strumLine:ChartStrumLine, ?onSave:ChartStrumLine->Void) { @@ -43,11 +52,12 @@ class CharterStrumlineScreen extends UISubstateWindow { type: OPPONENT, notes: [], position: "dad", - visible: true + visible: true, + keyCount: 4 }; winTitle = creatingStrumLine ? 'Creating strumline #$strumLineID' : 'Editing strumline #$strumLineID properties'; - winWidth = 690; winHeight = 400; + winWidth = 720; winHeight = 400; FlxG.sound.music.pause(); Charter.instance.vocals.pause(); @@ -64,7 +74,7 @@ class CharterStrumlineScreen extends UISubstateWindow { var charFileList = Character.getList(true); if (charFileList.length == 0) charFileList = Character.getList(false); - charactersList = new UIButtonList<CharacterButton>(15, title.y + title.height + 36, 250, 269, "", FlxPoint.get(250, 54), null, 0); + charactersList = new UIButtonList<CharacterButton>(15, title.y + title.height + 36, 250, 269, null, FlxPoint.get(250, 54), null, 0); charactersList.addButton.callback = () -> charactersList.add(new CharacterButton(0, 0, "New Char", charFileList, charactersList)); charactersList.cameraSpacing = 0; for (i in strumLine.characters) @@ -106,17 +116,21 @@ class CharterStrumlineScreen extends UISubstateWindow { add(stagePositionDropdown); addLabelOn(stagePositionDropdown, "Stage Position"); - hudScaleStepper = new UINumericStepper(typeDropdown.x, typeDropdown.y + 64, strumLine.strumScale == null ? 1 : strumLine.strumScale, 0.001, 2, null, null, 74); + hudScaleStepper = new UINumericStepper(typeDropdown.x, typeDropdown.y + 64, strumLine.strumScale == null ? 1 : strumLine.strumScale, 0.001, 2, null, null, 84); add(hudScaleStepper); addLabelOn(hudScaleStepper, "Scale"); + hudSpacingStepper = new UINumericStepper(hudScaleStepper.x + 80 - 32 + 26, hudScaleStepper.y, strumLine.strumSpacing != null ? strumLine.strumSpacing : 1, 0.001, 10, null, null, 84); + add(hudSpacingStepper); + addLabelOn(hudSpacingStepper, "Spacing"); + var strOffset:Float = strumLine.strumLinePos == null ? (strumLine.type == 1 ? 0.75 : 0.25) : strumLine.strumLinePos; var startingPos:FlxPoint = strumLine.strumPos == null ? FlxPoint.get(strOffset, 50) : FlxPoint.get(strOffset, strumLine.strumPos[1]); - hudXStepper = new UINumericStepper(hudScaleStepper.x + 80 - 32 + 26, hudScaleStepper.y, startingPos.x, 0.01, 2, 0, 2, 84); + hudXStepper = new UINumericStepper(hudSpacingStepper.x + 80, hudScaleStepper.y, startingPos.x, 0.01, 2, 0, 2, 84); add(hudXStepper); addLabelOn(hudXStepper, "Hud Position (X [Ratio 0-1],Y)"); @@ -157,11 +171,26 @@ class CharterStrumlineScreen extends UISubstateWindow { vocalsSuffixDropDown = new UIDropDown(typeDropdown.x, hudScaleStepper.y + 128, 200, 32, suffixList, strumLine.vocalsSuffix != null && strumLine.vocalsSuffix != "" ? suffixList.indexOf(strumLine.vocalsSuffix) : 0); add(vocalsSuffixDropDown); addLabelOn(vocalsSuffixDropDown, "Vocal Suffix"); + + keyCountStepper = new UINumericStepper(stagePositionDropdown.x, vocalsSuffixDropDown.y, strumLine.keyCount != null ? strumLine.keyCount : 4, 1, 0, 1, 1000, 84); + // if (Flags.CHARTER_ADVANCED_SETTINGS) { + add(keyCountStepper); + addLabelOn(keyCountStepper, "Key Count"); + // } + + strumLineCam = new HudCamera(); + strumLineCam.downscroll = Options.downscroll; + strumLineCam.bgColor = 0; + strumLineCam.alpha = 0; + FlxG.cameras.add(strumLineCam, false); + previewStrumLine = new CharterPreviewStrumLine(0, 0, 0, 1, 4, 0); + previewStrumLine.camera = strumLineCam; + add(previewStrumLine); + FlxTween.tween(strumLineCam, {alpha: 1}, 0.25, {ease: FlxEase.cubeOut}); } function saveStrumline() { - for (stepper in [hudXStepper, hudYStepper, hudScaleStepper]) - @:privateAccess stepper.__onChange(stepper.label.text); + UIUtil.confirmUISelections(this); var newStrumLine:ChartStrumLine = { characters: [ @@ -175,11 +204,32 @@ class CharterStrumlineScreen extends UISubstateWindow { strumPos: [0, hudYStepper.value], strumLinePos: hudXStepper.value, strumScale: hudScaleStepper.value, - vocalsSuffix: vocalsSuffixDropDown.options[vocalsSuffixDropDown.index] != "NONE" ? vocalsSuffixDropDown.options[vocalsSuffixDropDown.index] : "" + strumSpacing: hudSpacingStepper.value, + vocalsSuffix: vocalsSuffixDropDown.options[vocalsSuffixDropDown.index] != "NONE" ? vocalsSuffixDropDown.options[vocalsSuffixDropDown.index] : "", + keyCount: Std.int(keyCountStepper.value) }; if(!usesChartScrollSpeed.checked) newStrumLine.scrollSpeed = scrollSpeedStepper.value; if (onSave != null) onSave(newStrumLine); } + + override public function update(elapsed:Float) { + var scrollSpeed:Float = 0.0; + if (usesChartScrollSpeed.hovered || scrollSpeedStepper.hovered || scrollSpeedStepper.focused) + scrollSpeed = scrollSpeedStepper.value; + + previewStrumLine.visible = visibleCheckbox.checked; + + var xOffset:Float = StrumLine.calculateStartingXPos(hudXStepper.value, hudScaleStepper.value, hudSpacingStepper.value, Std.int(keyCountStepper.value)); + previewStrumLine.updatePos(xOffset, hudYStepper.value, hudScaleStepper.value, hudSpacingStepper.value, Std.int(keyCountStepper.value), scrollSpeed); + + super.update(elapsed); + } + + override public function destroy() { + super.destroy(); + FlxTween.cancelTweensOf(strumLineCam); + FlxG.cameras.remove(strumLineCam); + } } class CharacterButton extends UIButton { @@ -189,7 +239,7 @@ class CharacterButton extends UIButton { public var deleteIcon:FlxSprite; public function new(x:Float, y:Float, char:String, charsList:Array<String>, parent:UIButtonList<CharacterButton>) { - super(x, y, "", null, 250, 54); + super(x, y, null, null, 250, 54); charIcon = new HealthIcon(Character.getIconFromCharName(char)); var size = Std.int(150 * 0.3); @@ -211,7 +261,7 @@ class CharacterButton extends UIButton { charIcon.setPosition(x + 10, bHeight/2 - charIcon.height / 2); } - deleteButton = new UIButton(textBox.x + 115 + 16, bHeight/2 - (32/2), "", function () { + deleteButton = new UIButton(textBox.x + 115 + 16, bHeight/2 - (32/2), null, function () { parent.remove(this); }, 32); deleteButton.color = 0xFFFF0000; diff --git a/source/funkin/editors/charter/CharterWaveformHandler.hx b/source/funkin/editors/charter/CharterWaveformHandler.hx index f6c5ba28..05cced6f 100644 --- a/source/funkin/editors/charter/CharterWaveformHandler.hx +++ b/source/funkin/editors/charter/CharterWaveformHandler.hx @@ -5,6 +5,7 @@ import flixel.util.FlxColor; import funkin.backend.shaders.CustomShader; import funkin.backend.system.Conductor; import openfl.display.BitmapData; +import openfl.display.ShaderInput; class CharterWaveformHandler extends FlxBasic { public var ampsNeeded:Float = 0; @@ -67,13 +68,18 @@ class CharterWaveformHandler extends FlxBasic { var waveData:BitmapData = waveDatas.get(name); - var waveShader:CustomShader = new CustomShader("engine/editorWaveforms"); + var waveShader:CustomShader = new CustomShader(Options.charterRainbowWaveforms ? "engine/editorWaveformsRainbow" : "engine/editorWaveforms"); waveShader.data.waveformSize.value = [waveData.width, waveData.height]; - waveShader.data.waveformTexture.input = waveData; + var waveformInput:ShaderInput<BitmapData> = cast waveShader.data.waveformTexture; + waveformInput.input = waveData; + waveformInput.filter = NEAREST; // force it to be nearest, incase someone changed it waveShader.data.textureRes.value = [0, 0]; waveShader.data.pixelOffset.value = [0]; waveShader.data.lowDetail.value = [Options.charterLowDetailWaveforms]; + if (Options.charterRainbowWaveforms) + waveShader.data.time.value = [0]; + waveShaders.set(name, waveShader); return waveShader; } diff --git a/source/funkin/editors/charter/SongCreationScreen.hx b/source/funkin/editors/charter/SongCreationScreen.hx index 87cd2228..75b684b0 100644 --- a/source/funkin/editors/charter/SongCreationScreen.hx +++ b/source/funkin/editors/charter/SongCreationScreen.hx @@ -27,7 +27,7 @@ class SongCreationScreen extends UISubstateWindow { public var songNameTextBox:UITextBox; public var bpmStepper:UINumericStepper; public var beatsPerMeasureStepper:UINumericStepper; - public var stepsPerBeatStepper :UINumericStepper; + public var denominatorStepper:UINumericStepper; public var instExplorer:UIFileExplorer; public var voicesExplorer:UIFileExplorer; public var importFrom:UIButton; @@ -107,10 +107,11 @@ class SongCreationScreen extends UISubstateWindow { songDataGroup.add(new UIText(beatsPerMeasureStepper.x + 30, beatsPerMeasureStepper.y + 3, 0, "/", 22)); - stepsPerBeatStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, 4, 1, 0, 1, null, 54); - songDataGroup.add(stepsPerBeatStepper); + denominatorStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, 4, 1, 0, 1, null, 54); + songDataGroup.add(denominatorStepper); - instExplorer = new UIFileExplorer(songNameTextBox.x, songNameTextBox.y + 32 + 36, null, null, Flags.SOUND_EXT, function (res) { + instExplorer = new UIFileExplorer(songNameTextBox.x, songNameTextBox.y + 32 + 36, null, null, Flags.SOUND_EXT, function (path, res) { + if (path == null || res == null) return; var audioPlayer:UIAudioPlayer = new UIAudioPlayer(instExplorer.x + 8, instExplorer.y + 8, res); instExplorer.members.push(audioPlayer); instExplorer.uiElement = audioPlayer; @@ -120,7 +121,8 @@ class SongCreationScreen extends UISubstateWindow { "Inst Audio File $* Required$", [new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]); - voicesExplorer = new UIFileExplorer(instExplorer.x + 320 + 26, instExplorer.y, null, null, Flags.SOUND_EXT, function (res) { + voicesExplorer = new UIFileExplorer(instExplorer.x + 320 + 26, instExplorer.y, null, null, Flags.SOUND_EXT, function (path, res) { + if (path == null || res == null) return; var audioPlayer:UIAudioPlayer = new UIAudioPlayer(voicesExplorer.x + 8, voicesExplorer.y + 8, res); voicesExplorer.members.push(audioPlayer); voicesExplorer.uiElement = audioPlayer; @@ -171,7 +173,6 @@ class SongCreationScreen extends UISubstateWindow { var menuTitle:UIText; selectFormatGroup.add(menuTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Import From:", 28)); - engineDropdown = new UIDropDown(menuTitle.x, menuTitle.y + menuTitle.height + 36, 480, 32, ["Psych/Legacy FNF", "V-Slice", "V-Slice Project (.fnfc)"], 0, ["Supports runtime"]); selectFormatGroup.add(engineDropdown); addLabelOn(engineDropdown, "Chart Format"); @@ -186,7 +187,8 @@ class SongCreationScreen extends UISubstateWindow { var menuTitle:UIText; importAudioGroup.add(menuTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Add Audios", 28)); - importInstExplorer = new UIFileExplorer(menuTitle.x, menuTitle.y + menuTitle.height + 36, null, null, Flags.SOUND_EXT, function (res) { + importInstExplorer = new UIFileExplorer(menuTitle.x, menuTitle.y + menuTitle.height + 36, null, null, Flags.SOUND_EXT, function (path, res) { + if (path == null || res == null) return; var audioPlayer:UIAudioPlayer = new UIAudioPlayer(importInstExplorer.x + 8, importInstExplorer.y + 8, res); importInstExplorer.members.push(audioPlayer); importInstExplorer.uiElement = audioPlayer; @@ -196,7 +198,8 @@ class SongCreationScreen extends UISubstateWindow { "Inst Audio File $* Required$", [new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]); - importVoicesExplorer = new UIFileExplorer(importInstExplorer.x + 320 + 26, importInstExplorer.y, null, null, Flags.SOUND_EXT, function (res) { + importVoicesExplorer = new UIFileExplorer(importInstExplorer.x + 320 + 26, importInstExplorer.y, null, null, Flags.SOUND_EXT, function (path, res) { + if (path == null || res == null) return; var audioPlayer:UIAudioPlayer = new UIAudioPlayer(importVoicesExplorer.x + 8, importVoicesExplorer.y + 8, res); importVoicesExplorer.members.push(audioPlayer); importVoicesExplorer.uiElement = audioPlayer; @@ -212,7 +215,7 @@ class SongCreationScreen extends UISubstateWindow { "Song file name $* Required$", [new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]); - importChartFile = new UIFileExplorer(importIdTextBox.x, importIdTextBox.y + importIdTextBox.height + 56, null, null, "fnfc", function (_) importIdTextBox.label.text = new haxe.io.Path(importChartFile.filePath).file); + importChartFile = new UIFileExplorer(importIdTextBox.x, importIdTextBox.y + importIdTextBox.height + 56, null, null, "fnfc", function (_, _) importIdTextBox.label.text = new haxe.io.Path(importChartFile.filePath).file); importDataGroup.add(importChartFile); addLabelOn(importChartFile, "Data/Chart File").applyMarkup( "Data/Chart File $* Required$", @@ -224,6 +227,7 @@ class SongCreationScreen extends UISubstateWindow { "Meta File $* Required$", [new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]); + engineDropdown = new UIDropDown(menuTitle.x, menuTitle.y + menuTitle.height + 36, 480, 32, ["Psych/Legacy FNF", "V-Slice", "V-Slice Project (.fnfc)"]); saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() { var pages = isImporting ? importPages : pages; if (curPage == pages.length-1) { @@ -395,14 +399,14 @@ class SongCreationScreen extends UISubstateWindow { ])); } } else { - for (stepper in [bpmStepper, beatsPerMeasureStepper, stepsPerBeatStepper]) + for (stepper in [bpmStepper, beatsPerMeasureStepper, denominatorStepper]) @:privateAccess stepper.__onChange(stepper.label.text); var meta:ChartMetaData = { name: songNameTextBox.label.text, bpm: bpmStepper.value, beatsPerMeasure: Std.int(beatsPerMeasureStepper.value), - stepsPerBeat: Std.int(stepsPerBeatStepper.value), + stepsPerBeat: Std.int(16 / denominatorStepper.value), displayName: displayNameTextBox.label.text, icon: iconTextBox.label.text, color: colorWheel.curColor, diff --git a/source/funkin/editors/extra/AxisGizmo.hx b/source/funkin/editors/extra/AxisGizmo.hx new file mode 100644 index 00000000..2d4c7b8d --- /dev/null +++ b/source/funkin/editors/extra/AxisGizmo.hx @@ -0,0 +1,31 @@ +package funkin.editors.extra; + +class AxisGizmo extends FlxObject { + var _worldPos:FlxPoint = FlxPoint.get(); + + public override function draw() { + super.draw(); + + for (camera in cameras) { + if (DrawUtil.line == null) DrawUtil.createDrawers(); + DrawUtil.line.camera = camera; DrawUtil.line.alpha = 0.85; + + CoolUtil.pointToScreenPosition(FlxPoint.weak(), FlxG.camera, _worldPos); + // Stole these colors directly from godot >:D -luanr + DrawUtil.drawLine( + FlxPoint.weak(_worldPos.x, 0), + FlxPoint.weak(_worldPos.x, camera.height), + 1, 0xFF76BC02); + + DrawUtil.drawLine( + FlxPoint.weak(0, _worldPos.y), + FlxPoint.weak(camera.width, _worldPos.y), + 1, 0xFFD72C47); + } + } + + public override function destroy() { + super.destroy(); + _worldPos.put(); + } +} \ No newline at end of file diff --git a/source/funkin/editors/extra/CameraHoverDummy.hx b/source/funkin/editors/extra/CameraHoverDummy.hx new file mode 100644 index 00000000..5ad69391 --- /dev/null +++ b/source/funkin/editors/extra/CameraHoverDummy.hx @@ -0,0 +1,21 @@ +package funkin.editors.extra; + +class CameraHoverDummy extends UISprite { + var parent:FlxBasic; + public function new(?parent:FlxBasic, scroll:FlxPoint) { + super(); + this.parent = parent; + cameras = parent == null ? [FlxG.camera] : parent.cameras; + scrollFactor.copyFrom(scroll); + } + + public override function updateButton() { + camera.getViewRect(__rect); + UIState.state.updateRectButtonHandler(this, __rect, onHovered); + } + + public override function draw() { + @:privateAccess + __lastDrawCameras = cameras.copy(); + } +} \ No newline at end of file diff --git a/source/funkin/editors/extra/PropertyButton.hx b/source/funkin/editors/extra/PropertyButton.hx index 0ffad946..1f7bbce2 100644 --- a/source/funkin/editors/extra/PropertyButton.hx +++ b/source/funkin/editors/extra/PropertyButton.hx @@ -6,28 +6,36 @@ class PropertyButton extends UIButton { public var deleteButton:UIButton; public var deleteIcon:FlxSprite; - public function new(property, value, parent) { - super(0, 0, '', function () {}, 280, 35); - members.push(propertyText = new UITextBox(5, 5, property, 100, 25)); - members.push(valueText = new UITextBox(propertyText.x + propertyText.bWidth + 10, 5, value, 135, 25)); + public function new(property, value, parent, width:Int = 280, height:Int = 35, nameWidth:Int = 100, valueWidth:Int = 135, inputHeight:Int = 25) { + super(0, 0, '', function () {}, width, height); + members.push(propertyText = new UITextBox(5, 5, property, nameWidth, inputHeight)); + members.push(valueText = new UITextBox(propertyText.x + propertyText.bWidth + 10, 5, value, valueWidth, inputHeight)); - deleteButton = new UIButton(valueText.x + 135, bHeight/2 - (25/2), "", function () { + var deleteSize = height - 5 * 2; + deleteButton = new UIButton(valueText.x + 135, bHeight/2 - (deleteSize/2), null, function () { parent.remove(this); - }, 25, 25); + }, deleteSize, deleteSize); deleteButton.color = 0xFFFF0000; deleteButton.autoAlpha = false; members.push(deleteButton); - deleteIcon = new FlxSprite(deleteButton.x + (15/2), deleteButton.y + 8).loadGraphic(Paths.image('editors/delete-button')); + deleteIcon = new FlxSprite(deleteButton.x, deleteButton.y).loadGraphic(Paths.image('editors/delete-button')); deleteIcon.antialiasing = false; members.push(deleteIcon); + + updatePos(); } public override function update(elapsed) { super.update(elapsed); - propertyText.follow(this, 5, 5); - valueText.follow(this, propertyText.x + propertyText.bWidth, 5); - deleteButton.follow(this, valueText.x + 135, bHeight/2 - (25/2)); - deleteIcon.follow(deleteButton, 5, 5); + updatePos(); + } + + public function updatePos() { + propertyText.follow(this, 5, bHeight/2 - (propertyText.bHeight/2)); + valueText.follow(this, propertyText.x + propertyText.bWidth, bHeight/2 - (valueText.bHeight/2)); + //deleteButton.follow(this, valueText.x + 135, bHeight/2 - (25/2)); + deleteButton.follow(this, bWidth - deleteButton.bWidth - 5, bHeight/2 - deleteButton.bHeight/2); + deleteIcon.follow(deleteButton, deleteButton.bWidth / 2 - deleteIcon.width / 2, deleteButton.bHeight / 2 - deleteIcon.height / 2); } } \ No newline at end of file diff --git a/source/funkin/editors/import.hx b/source/funkin/editors/import.hx index 3d64ccc2..de888fa0 100644 --- a/source/funkin/editors/import.hx +++ b/source/funkin/editors/import.hx @@ -3,6 +3,7 @@ package funkin.editors; import flixel.math.FlxPoint; import funkin.editors.ui.*; +import funkin.editors.ui.notifications.*; using funkin.editors.ui.UIUtil; #end \ No newline at end of file diff --git a/source/funkin/editors/stage/StageCreationScreen.hx b/source/funkin/editors/stage/StageCreationScreen.hx new file mode 100644 index 00000000..957ff896 --- /dev/null +++ b/source/funkin/editors/stage/StageCreationScreen.hx @@ -0,0 +1,62 @@ +package funkin.editors.stage; + +typedef StageCreationData = { + var name:String; + var path:String; +} + +class StageCreationScreen extends UISubstateWindow { + private var onSave:Null<StageCreationData> -> Void = null; + + public var stageNameTextBox:UITextBox; + public var stagePathTextBox:UITextBox; + public var voicesExplorer:UIFileExplorer; + + public var saveButton:UIButton; + public var closeButton:UIButton; + + public function new(?onSave:StageCreationData->Void) { + super(); + if (onSave != null) this.onSave = onSave; + } + + public override function create() { + winTitle = "Creating New Stage"; + + winWidth = 748 - 32 + 40; + winHeight = 220; + + super.create(); + + function addLabelOn(ui:UISprite, text:String):UIText { + var text:UIText = new UIText(ui.x, ui.y - 24, 0, text); + ui.members.push(text); + return text; + } + + var stageInfo:UIText; + add(stageInfo = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Stage Info", 28)); + + add(stageNameTextBox = new UITextBox(stageInfo.x, stageInfo.y + stageInfo.height + 36, "Stage Name")); + addLabelOn(stageNameTextBox, "Stage Name"); + + add(stagePathTextBox = new UITextBox(stageNameTextBox.x + 320 + 26, stageNameTextBox.y, "Stage Path")); + addLabelOn(stagePathTextBox, "Stage Path"); + + add(saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() { + saveStageInfo(); + close(); + }, 125)); + + add(closeButton = new UIButton(saveButton.x - 20 - saveButton.bWidth, saveButton.y, "Cancel", close, 125)); + closeButton.color = 0xFFFF0000; + } + + function saveStageInfo() { + if (onSave != null) onSave({ + name: stageNameTextBox.label.text, + path: stagePathTextBox.label.text + }); + } + +} \ No newline at end of file diff --git a/source/funkin/editors/stage/StageEditor.hx b/source/funkin/editors/stage/StageEditor.hx new file mode 100644 index 00000000..4e84894e --- /dev/null +++ b/source/funkin/editors/stage/StageEditor.hx @@ -0,0 +1,1368 @@ +package funkin.editors.stage; + +import flixel.math.FlxAngle; +import flixel.math.FlxPoint; +import flixel.math.FlxRect; +import flixel.util.FlxColor; +import flixel.util.FlxSort; +import funkin.backend.system.framerate.Framerate; +import funkin.backend.utils.XMLUtil.AnimData; +import funkin.editors.stage.elements.*; +import funkin.editors.stage.elements.StageSpriteButton.StageSpriteEditScreen; +import funkin.editors.ui.UIContextMenu.UIContextMenuOption; +import funkin.editors.extra.AxisGizmo; +import funkin.game.Character; +import funkin.game.Stage; +import haxe.xml.Access; +import haxe.xml.Printer; +import lime.ui.MouseCursor; +import openfl.ui.Mouse; + +using funkin.backend.utils.MatrixUtil; + +@:access(flixel.FlxSprite) +class StageEditor extends UIState { + static var __stage:String; + public var stage:Stage; + + private var _point:FlxPoint = new FlxPoint(); + + public static var instance(get, null):StageEditor; + + private static inline function get_instance() + return FlxG.state is StageEditor ? cast FlxG.state : null; + + public var topMenu:Array<UIContextMenuOption>; + public var topMenuSpr:UITopMenu; + + public var uiGroup:FlxTypedGroup<FlxSprite> = new FlxTypedGroup<FlxSprite>(); + public var stageSpritesWindow:StageSpritesWindow; + + public var xmlMap:Map<FlxObject, Access> = new Map<FlxObject, Access>(); + + public var chars:Array<Character> = []; + public var charMap:Map<String, Character> = []; + + public var stageCamera:FlxCamera; + //public var guideCamera:FlxCamera; + public var uiCamera:FlxCamera; + + public var gizmosCamera:FlxCamera; + public var axisGizmo:AxisGizmo; + + public var selection:Selection = new Selection(); + public var mouseMode:StageEditorMouseMode = NONE; + public var mousePoint:FlxPoint = new FlxPoint(); + public var clickPoint:FlxPoint = new FlxPoint(); + public var storedPos:FlxPoint = new FlxPoint(); + public var storedScale:FlxPoint = new FlxPoint(); + public var storedSkew:FlxPoint = new FlxPoint(); + public var storedAngle:Float = 0; + public var angleOffset:Float = 0; + + public var showCharacters:Bool = true; + + public static inline var SPRITE_WINDOW_WIDTH:Int = 400; + public static inline var SPRITE_WINDOW_BUTTON_HEIGHT:Int = 64; + + public var undos:UndoList<StageChange> = new UndoList<StageChange>(); + + public inline static function exID(id:String) { + return "stageEditor." + id; + } + + public function new(stage:String) { + super(); + if (stage != null) __stage = stage; + } + + public override function create() { + super.create(); + + WindowUtils.suffix = " (Stage Editor)"; + SaveWarning.selectionClass = StageSelection; + SaveWarning.saveFunc = () -> {_file_save(null);}; + + topMenu = [ + { + label: "File", + childs: [ + { + label: "Save", + keybind: [CONTROL, S], + onSelect: _file_save, + }, + { + label: "Save As...", + keybind: [CONTROL, SHIFT, S], + onSelect: _file_saveas, + }, + null, + { + label: "Exit", + onSelect: _file_exit + } + ] + }, + { + label: "Edit", + childs: [ + { + label: "Undo", + keybind: [CONTROL, Z], + onSelect: _edit_undo, + }, + { + label: "Redo", + keybind: [CONTROL, SHIFT, Z], + onSelect: _edit_redo, + }, + null, + { + label: "Edit Stage Info", + onSelect: (_) -> { + openSubState(new UISoftcodedWindow( + "layouts/stage/stageInfoScreen", + [ + "winTitle" => "Editing Stage Info", + "hasSaveButton" => true, + "hasCloseButton" => true, + "stage" => stage, + "Stage" => Stage, + "exID" => exID, + "CEditInfo" => StageChange.CEditInfo + ] + )); + }, + } + ] + }, + { + label: "Select", + childs: [ + { + label: "All", + keybind: [CONTROL, A], + onSelect: (_) -> _select_all(_), + }, + { + label: "Deselect", + keybind: [CONTROL, D], + onSelect: (_) -> _select_deselect(_), + }, + { + label: "Inverse", + keybind: [CONTROL, SHIFT, I], + onSelect: (_) -> _select_inverse(_), + }, + ] + }, + { + label: "View", + childs: [ + { + label: "Zoom in", + keybind: [CONTROL, NUMPADPLUS], + onSelect: _view_zoomin + }, + { + label: "Zoom out", + keybind: [CONTROL, NUMPADMINUS], + onSelect: _view_zoomout + }, + { + label: "Reset zoom", + keybind: [CONTROL, NUMPADZERO], + onSelect: _view_zoomreset + }, + null, + { + label: "Focus Dad", + //keybind: [CONTROL, NUMPADZERO], + onSelect: _view_focusdad + }, + { + label: "Focus Gf", + //keybind: [CONTROL, NUMPADZERO], + onSelect: _view_focusgf + }, + { + label: "Focus BF", + //keybind: [CONTROL, NUMPADZERO], + onSelect: _view_focusbf + }, + // TODO: add support for custom character focus + ] + }, + { + label: "Editor", + childs: [ + { + label: "Show Characters", + //keybind: [CONTROL, NUMPADMINUS], + onSelect: _editor_showCharacters, + icon: showCharacters ? 1 : 0 + } + ] + }, + ]; + + stageCamera = FlxG.camera; + + gizmosCamera = new FlxCamera(); + gizmosCamera.bgColor = 0; + FlxG.cameras.add(gizmosCamera, false); + + axisGizmo = new AxisGizmo(); + axisGizmo.cameras = [gizmosCamera]; + add(axisGizmo); + + uiCamera = new FlxCamera(); + uiCamera.bgColor = 0; + + uiCameras = [uiCamera]; + + //FlxG.cameras.add(guideCamera, false); + FlxG.cameras.add(uiCamera, false); + + // Load from xml + var order:Array<Dynamic> = []; + var orderNodes:Array<Access> = []; + + loadStage(order, orderNodes); + + // for (sprite => node in xmlMap) + // sprite.cameras = [stageCamera]; + + topMenuSpr = new UITopMenu(topMenu); + topMenuSpr.cameras = uiGroup.cameras = [uiCamera]; + + stageSpritesWindow = new StageSpritesWindow(Std.int(FlxG.width - 400), 25); + for (i=>sprite in order) { + var xml = (sprite != null) ? xmlMap.get(sprite) : orderNodes[i]; + if (xml != null) { + if (sprite is Character) { + var char:Character = cast sprite; + var button = new StageCharacterButton(0,0, char, xml); + char.extra.set(exID("button"), button); + stageSpritesWindow.add(button); + } else if (sprite is FunkinSprite) { + var sprite:FunkinSprite = cast sprite; + var type = sprite.extra.get(exID("type")); + var button:StageElementButton = (type == "box" || type == "solid") ? new StageSolidButton(0,0, sprite, xml) : new StageSpriteButton(0,0, sprite, xml); + sprite.extra.set(exID("button"), button); + stageSpritesWindow.add(button); + } else if(sprite == null) { + var basic = new FlxBasic(); // prevent awkward layering + insert(i, basic); + var button = new StageUnknownButton(0,0, basic, xml); + stageSpritesWindow.add(button); + } + } + } + uiGroup.add(stageSpritesWindow); + + add(topMenuSpr); + add(uiGroup); + + if(Framerate.isLoaded) { + Framerate.fpsCounter.alpha = 0.4; + Framerate.memoryCounter.alpha = 0.4; + Framerate.codenameBuildField.alpha = 0.4; + } + + // DiscordUtil.call("onEditorLoaded", ["Stage Editor", __stage]); + } + + function loadStage(order:Array<Dynamic>, orderNodes:Array<Access>) { + stage = new Stage(__stage, this, false); + stage.onXMLLoaded = function(xml:Access, elems:Array<Access>) { + return elems; + } + stage.onNodeFinished = function(node:Access, sprite:Dynamic) { + if(sprite is FlxSprite) { + sprite.moves = false; + } + if (sprite is FunkinSprite) { + sprite.animEnabled = false; + //sprite.zoomFactorEnabled = false; + } + } + stage.onNodeLoaded = function(node:Access, sprite:Dynamic):Dynamic { + var parent = new Access(node.x.parent); + //var name = ""; + //trace(node, sprite); + if(sprite is FlxSprite) { + //sprite.forceIsOnScreen = true; // hack + } + if(sprite is FunkinSprite) { + var sprite:FunkinSprite = cast sprite; + //name = sprite.name; + sprite.extra.set(exID("node"), node); + sprite.extra.set(exID("type"), node.name); + sprite.extra.set(exID("imageFile"), '${node.getAtt("sprite").getDefault(sprite.name)}'); + sprite.extra.set(exID("parentNode"), parent); + sprite.extra.set(exID("highMemory"), parent.name == "highMemory"); + sprite.extra.set(exID("lowMemory"), parent.name == "lowMemory"); + //sprite.active = false; + } + if(sprite is StageCharPos) + sprite = prepareCharacter(cast sprite, node); + order.push(sprite); + orderNodes.push(node); + xmlMap.set(sprite, node); + + return sprite; + } + stage.loadXml(stage.stageXML, true); + + if (!charMap.exists("NO_DELETE_girlfriend")) { + var xml = Xml.createElement("girlfriend"); + stage.stageXML.x.addChild(xml); + var node = new Access(xml); + var sprite = prepareCharacter(stage.characterPoses["girlfriend"], node); + order.push(sprite); + orderNodes.push(node); + xmlMap.set(sprite, node); + } + if (!charMap.exists("NO_DELETE_dad")) { + var xml = Xml.createElement("dad"); + stage.stageXML.x.addChild(xml); + var node = new Access(xml); + var sprite = prepareCharacter(stage.characterPoses["dad"], node); + order.push(sprite); + orderNodes.push(node); + xmlMap.set(sprite, node); + } + if (!charMap.exists("NO_DELETE_boyfriend")) { + var xml = Xml.createElement("boyfriend"); + stage.stageXML.x.addChild(xml); + var node = new Access(xml); + var sprite = prepareCharacter(stage.characterPoses["boyfriend"], node); + order.push(sprite); + orderNodes.push(node); + xmlMap.set(sprite, node); + } + + add(stage); + + setZoom(stage.defaultZoom); + } + + function prepareCharacter(charPos:StageCharPos, node:Access):Character { + var parent = new Access(node.x.parent); + + var charName = switch(node.name) { + case "dad" | "opponent": "dad"; + case "gf" | "girlfriend": "gf"; + case "bf" | "boyfriend" | "player": "bf"; + default: (charPos.flipX) ? "bf" : "dad"; + } + + var char = new Character(0,0, charName, charPos.flipX, true); + charName = switch(node.name) { + case "dad" | "opponent": "NO_DELETE_dad"; + case "gf" | "girlfriend": "NO_DELETE_girlfriend"; + case "bf" | "boyfriend" | "player": "NO_DELETE_boyfriend"; + default: node.att.name; + } + char.name = charName; + char.debugMode = true; + // Play first anim, and make it the last frame + var animToPlay = char.getAnimOrder()[0]; + char.playAnim(animToPlay, true, NONE); + var lastIndx = (char.animateAtlas != null) ? + char.animateAtlas.anim.length - 1 : + char.animation.curAnim.numFrames - 1; + char.playAnim(animToPlay, true, NONE, false, lastIndx); + char.stopAnimation(); + + // Add it to the stage + char.visible = true; + char.alpha = 0.75; + char.extra.set(exID("node"), node); + char.extra.set(exID("spacingX"), charPos.charSpacingX); + char.extra.set(exID("spacingY"), charPos.charSpacingY); + char.extra.set(exID("camX"), charPos.camxoffset); + char.extra.set(exID("camY"), charPos.camyoffset); + + char.extra.set(exID("parentNode"), parent); + char.extra.set(exID("highMemory"), parent.name == "highMemory"); + char.extra.set(exID("lowMemory"), parent.name == "lowMemory"); + + chars.push(char); + stage.applyCharStuff(char, charPos.name, 0); + charMap[charName] = char; + + remove(charPos, true); + charPos.destroy(); + return char; + } + + override function destroy() { + super.destroy(); + nextScroll = FlxDestroyUtil.destroy(nextScroll); + if(Framerate.isLoaded) { + Framerate.fpsCounter.alpha = 1; + Framerate.memoryCounter.alpha = 1; + Framerate.codenameBuildField.alpha = 1; + } + } + + //private var movingCam:Bool = false; + //private var camDragSpeed:Float = 1.2; + + private var movedTillRel:FlxPoint = FlxPoint.get(0,0); + private var nextScroll:FlxPoint = FlxPoint.get(0,0); + + public override function update(elapsed:Float) { + super.update(elapsed); + + if (true) { + if(FlxG.keys.justPressed.ANY) + UIUtil.processShortcuts(topMenu); + } + + //if (character != null) + // characterPropertiresWindow.characterInfo.text = '${character.getNameList().length} Animations\nFlipped: ${character.flipX}\nSprite: ${character.sprite}\nAnim: ${character.getAnimName()}\nOffset: (${character.frameOffset.x}, ${character.frameOffset.y})'; + + currentCursor = ARROW; + + FlxG.mouse.getWorldPosition(stageCamera, mousePoint); + if ((!stageSpritesWindow.hovered && !stageSpritesWindow.dragging) && !topMenuSpr.hovered) { + if (FlxG.mouse.wheel != 0) { + zoom += 0.25 * FlxG.mouse.wheel; + updateZoom(); + } + + var prevMode = mouseMode; + + // TODO: make this work with multiple selections + if(selection.length == 1) { + for(sprite in selection) { + if(sprite is FunkinSprite) { + handleSelection(cast sprite); + } + } + } + + //if (FlxG.mouse.justReleasedRight) { + // closeCurrentContextMenu(); + // openContextMenu(topMenu[2].childs); + //} + + if (mouseMode == NONE && prevMode == NONE) { + if (FlxG.mouse.pressed) { + var x = FlxG.mouse.deltaScreenX; + var y = FlxG.mouse.deltaScreenY; + movedTillRel.x += x; movedTillRel.y += y; + nextScroll.set(nextScroll.x - x, nextScroll.y - y); + currentCursor = HAND; + } + + if (FlxG.mouse.justReleased) { + if (Math.abs(movedTillRel.x) < 16 && Math.abs(movedTillRel.y) < 16) { + var point = FlxG.mouse.getWorldPosition(stageCamera, _point); + var sprites = getRealSprites(); + for (i in 0...sprites.length) { + var sprite = sprites[sprites.length - i - 1]; + //if(sprite.animateAtlas != null) continue; + + calcSpriteBounds(sprite); + //trace("Sprite: " + sprite); + //trace("Sprite bounds: " + sprite.extra.get(exID("bounds"))); + if (cast(sprite.extra.get(exID("bounds")), FlxRect).containsPoint(point)) { + selectSprite(sprite); break; + } + } + } + movedTillRel.set(); + } + } + }/* else if (!FlxG.mouse.pressed) + currentCursor = ARROW;*/ + + stageCamera.scroll.set( + lerp(stageCamera.scroll.x, nextScroll.x, 0.35), + lerp(stageCamera.scroll.y, nextScroll.y, 0.35) + ); + + stageCamera.zoom = lerp(stageCamera.zoom, __camZoom, 0.125); + + WindowUtils.prefix = undos.unsaved ? "* " : ""; + SaveWarning.showWarning = undos.unsaved; + } + + // TOP MENU OPTIONS + #if REGION + function _file_exit(_) { + if (undos.unsaved) SaveWarning.triggerWarning(); + else FlxG.switchState(new StageSelection()); + } + + function _file_save(_) { + #if sys + CoolUtil.safeSaveFile( + '${Paths.getAssetsRoot()}/data/stages/${__stage}.xml', + buildStage() + ); + undos.save(); + #else + _file_saveas(_); + #end + } + + function _file_saveas(_) { + openSubState(new SaveSubstate(buildStage(), { + defaultSaveFile: '${__stage}.xml' + })); + undos.save(); + } + + function _sprite_new(_) { + var node:Access = new Access(Xml.createElement("sprite")); + stage.stageXML.x.addChild(node.x); + node.att.name = "sprite_" + stageSpritesWindow.buttons.members.length; + + var sprite:FunkinSprite = new FunkinSprite(); + insert(members.indexOf(stage), sprite); + sprite.extra.set(exID("node"), node); + sprite.extra.set(exID("type"), node.name); + sprite.extra.set(exID("imageFile"), ''); + sprite.extra.set(exID("parentNode"), stage.stageXML.x); + sprite.extra.set(exID("highMemory"), false); + sprite.extra.set(exID("lowMemory"), false); + xmlMap.set(sprite, node); + + var button:StageSpriteButton = new StageSpriteButton(0, 0, sprite, node); + sprite.extra.set(exID("button"), button); + stageSpritesWindow.add(button); + + var substate = new StageSpriteEditScreen(button); + substate.newSprite = true; + openSubState(substate); + } + + function _character_new(_) { + var node:Access = new Access(Xml.createElement("char")); + stage.stageXML.x.addChild(node.x); + node.att.name = "character_" + stageSpritesWindow.buttons.members.length; + + var char = new Character(0,0, "bf", false, true); + char.name = node.att.name; + char.debugMode = true; + // Play first anim, and make it the last frame + var animToPlay = char.getAnimOrder()[0]; + char.playAnim(animToPlay, true, NONE); + var lastIndx = (char.animateAtlas != null) ? + char.animateAtlas.anim.length - 1 : + char.animation.curAnim.numFrames - 1; + char.playAnim(animToPlay, true, NONE, false, lastIndx); + char.stopAnimation(); + + // Add it to the stage + char.visible = true; + char.alpha = 0.75; + + char.extra.set(exID("node"), node); + char.extra.set(exID("spacingX"), 20); + char.extra.set(exID("spacingY"), 0); + char.extra.set(exID("camX"), 0); + char.extra.set(exID("camY"), 0); + + char.extra.set(exID("parentNode"), stage.stageXML); + char.extra.set(exID("highMemory"), false); + char.extra.set(exID("lowMemory"), false); + xmlMap.set(char, node); + chars.push(char); + + insert(members.indexOf(stage), char); + charMap[node.att.name] = char; + + var button = new StageCharacterButton(0,0, char, node); + char.extra.set(exID("button"), button); + stageSpritesWindow.add(button); + } + + function saveToXml(xml:Xml, name:String, value:Dynamic, ?defaultValue:Dynamic) { + if(value == null || value == defaultValue) return xml; + xml.set(name, Std.string(value)); + return xml; + } + function savePointToXml(xml:Xml, name:String, point:FlxPoint, ?defaultValueX:Float, ?defaultValueY:Float) { + if (point == null) return xml; + if(defaultValueY == null) defaultValueY = defaultValueX; + if(point.x == point.y) { + saveToXml(xml, name, point.x, defaultValueX); + } else { + saveToXml(xml, name + "x", point.x, defaultValueX); + saveToXml(xml, name + "y", point.y, defaultValueY); + } + return xml; + } + + function getBoolOfNode(node:Access, name:String) { + var xml:Xml = cast node; + + return xml.exists(name) && xml.get(name) == "true"; + } + + function buildStage():String { + var xml = Xml.createElement("stage"); + saveToXml(xml, "zoom", stage.defaultZoom, 1.05); + saveToXml(xml, "name", stage.stageName); + saveToXml(xml, "folder", stage.spritesParentFolder); + saveToXml(xml, "startCamPosX", stage.startCam.x, 0); + saveToXml(xml, "startCamPosY", stage.startCam.y, 0); + + for(prop in stage.extra.keys()) + if(!Stage.DEFAULT_ATTRIBUTES.contains(prop) && !prop.startsWith("stageEditor.")) + saveToXml(xml, prop, stage.extra.get(prop)); + + var group:Xml = null; + var curGroup:String = null; + + for(sprite in getSprites()) { + var button:StageElementButton = sprite.extra.get(exID("button")); + var newNode:Xml = null; + var sprite:FunkinSprite = button.getSprite(); + if(button is StageSolidButton) { + var button:StageSolidButton = cast button; + var node:Access = cast sprite.extra.get(exID("node")); + Logs.trace("SOLID / BOX isnt implemented yet!"); + } else if(button is StageSpriteButton) { + var button:StageSpriteButton = cast button; + var node:Access = cast sprite.extra.get(exID("node")); + var spriteXML = Xml.createElement("sprite"); + saveToXml(spriteXML, "name", sprite.name); + saveToXml(spriteXML, "x", sprite.x, 0); + saveToXml(spriteXML, "y", sprite.y, 0); + saveToXml(spriteXML, "sprite", sprite.extra.get(exID("imageFile"))); + savePointToXml(spriteXML, "scale", sprite.scale, 1); + savePointToXml(spriteXML, "scroll", sprite.scrollFactor, 1); + saveToXml(spriteXML, "skewx", sprite.skew.x, 0); + saveToXml(spriteXML, "skewy", sprite.skew.y, 0); + saveToXml(spriteXML, "alpha", sprite.alpha, 1); + saveToXml(spriteXML, "angle", sprite.angle, 0); + //saveToXml(spriteXML, "graphicSize", sprite.width, sprite.width); + //saveToXml(spriteXML, "graphicSizex", sprite.height, sprite.height); + //saveToXml(spriteXML, "graphicSizey", sprite.height, sprite.height); + saveToXml(spriteXML, "zoomfactor", sprite.zoomFactor, 1); + saveToXml(spriteXML, "updateHitbox", getBoolOfNode(node, "updateHitbox"), false); + saveToXml(spriteXML, "antialiasing", sprite.antialiasing, true); + //saveToXml(spriteXML, "width", sprite.width); + //saveToXml(spriteXML, "height", sprite.height); + saveToXml(spriteXML, "playOnCountdown", getBoolOfNode(node, "playOnCountdown"), false); + saveToXml(spriteXML, "interval", node.getAtt("beatInterval"), 2); + saveToXml(spriteXML, "interval", node.getAtt("interval"), 2); + saveToXml(spriteXML, "beatOffset", node.getAtt("beatOffset"), 0); + if(sprite.spriteAnimType != LOOP) + spriteXML.set("type", sprite.spriteAnimType.toString()); + saveToXml(spriteXML, "color", sprite.color.toWebString(), "#FFFFFF"); + // TODO: save custom parameters + //saveToXml(spriteXML, "flipX", sprite.flipX, false); + newNode = spriteXML; + } else if(button is StageCharacterButton) { + var button:StageCharacterButton = cast button; + var char:Character = button.char; + var node:Access = cast char.extra.get(exID("node")); + var defaultPos = Stage.getDefaultPos(char.name.replace("NO_DELETE_", "")); + var charXML:Xml = Xml.createElement(node.name); + if(!char.name.startsWith("NO_DELETE_")) + saveToXml(charXML, "name", char.name); + saveToXml(charXML, "x", char.x, defaultPos.x); + saveToXml(charXML, "y", char.y, defaultPos.y); + saveToXml(charXML, "camxoffset", char.extra.get(exID("camX")), 0); + saveToXml(charXML, "camyoffset", char.extra.get(exID("camY")), 0); + saveToXml(charXML, "skewx", char.skew.x, 0); + saveToXml(charXML, "skewy", char.skew.y, 0); + saveToXml(charXML, "spacingx", char.extra.get(exID("spacingX")), 20); + saveToXml(charXML, "spacingy", char.extra.get(exID("spacingY")), 0); + saveToXml(charXML, "alpha", char.alpha / 0.75, 1); + saveToXml(charXML, "angle", char.angle, 0); + saveToXml(charXML, "zoomfactor", char.zoomFactor, 1); + saveToXml(charXML, "flipX", char.isPlayer, defaultPos.flip); + savePointToXml(charXML, "scroll", char.scrollFactor, defaultPos.scroll); + savePointToXml(charXML, "scale", char.scale.scaleNew(button.charScale), 1); + // TODO: save custom parameters + newNode = charXML; + } else if(button is StageUnknownButton) { + var button:StageUnknownButton = cast button; + newNode = button.xml.x; + } + else { + Logs.trace("Unknown Stage Type : " + Type.getClassName(Type.getClass(button))); + Logs.trace("> Sprite : " + Type.getClassName(Type.getClass(sprite))); + } + + if(newNode != null && sprite != null) { + var isLowMemory = sprite.extra.get(exID("lowMemory")) == true; + var isHighMemory = sprite.extra.get(exID("highMemory")) == true; + /* // Only if this compiled :sob: + var groupName:String = null; + if ((groupName = isLowMemory ? "low-memory" : isHighMemory ? "high-memory" : null) != null) { + var a = group != null && groupName != curGroup && ((group = cast xml.addChild(group)) != null); + (group = (group == null ? Xml.createElement(curGroup = groupName) : group)).addChild(newNode); + }else xml.addChild(newNode); + */ + + var groupName = isLowMemory ? "low-memory" : isHighMemory ? "high-memory" : null; + if(group != null && groupName != curGroup) { + xml.addChild(group); + group = null; + } + if(groupName != null) + (group = (group == null ? Xml.createElement(curGroup = groupName) : group)).addChild(newNode); + else + xml.addChild(newNode); + } + } + + var xmlThingYea:String = "<!DOCTYPE codename-engine-stage>\n" + Printer.print(xml, Options.editorPrettyPrint); + return Options.editorPrettyPrint ? xmlThingYea : xmlThingYea.replace("\n", ""); + } + + function _edit_undo(_) { + var undo = undos.undo(); + switch(undo) { + case null: + // do nothing + case CEditInfo(oldInfo, newInfo): + stage.startCam.x = oldInfo.startCamX; + stage.startCam.y = oldInfo.startCamY; + stage.defaultZoom = oldInfo.zoom; + stage.stageName = oldInfo.name; + stage.spritesParentFolder = oldInfo.folder; + + for (attrib in newInfo.attrib.keys()) + stage.extra.remove(attrib); + for (attrib => val in oldInfo.attrib) + stage.extra.set(attrib, val); + case CTransform(sprite, oldInfo, newInfo): + sprite.setPosition(oldInfo.x, oldInfo.y); + sprite.scale.set(oldInfo.scaleX, oldInfo.scaleY); + sprite.skew.set(oldInfo.skewX, oldInfo.skewY); + sprite.angle = oldInfo.angle; + cast(sprite.extra.get(exID("button")), StageElementButton).updateInfo(); + } + } + + function _edit_redo(_) { + var redo = undos.redo(); + switch(redo) { + case null: + // do nothing + case CEditInfo(oldInfo, newInfo): + stage.startCam.x = newInfo.startCamX; + stage.startCam.y = newInfo.startCamY; + stage.defaultZoom = newInfo.zoom; + stage.stageName = newInfo.name; + stage.spritesParentFolder = newInfo.folder; + + for (attrib in oldInfo.attrib.keys()) + stage.extra.remove(attrib); + for (attrib => val in newInfo.attrib) + stage.extra.set(attrib, val); + case CTransform(sprite, oldInfo, newInfo): + sprite.setPosition(newInfo.x, newInfo.y); + sprite.scale.set(newInfo.scaleX, newInfo.scaleY); + sprite.skew.set(newInfo.skewX, newInfo.skewY); + sprite.angle = newInfo.angle; + cast(sprite.extra.get(exID("button")), StageElementButton).updateInfo(); + } + } + + public function selectSprite(_sprite:FunkinSprite) { + if(!UIUtil.getKeyState(CONTROL, PRESSED)) + _select_deselect(null, false); + + if(_sprite is FunkinSprite) { + if(selection.contains(_sprite)) + selection.remove(_sprite); + else + selection.push(_sprite); + } + updateSelection(); + } + + function getSprites() { + return stageSpritesWindow.buttons.members.map((o) -> o.getSprite()).filter((o) -> o != null); + } + function getRealSprites() { + return stageSpritesWindow.buttons.members.filter(o -> o.canRender()).map((o) -> o.getSprite()).filter((o) -> o != null); + } + + function updateSelection() { + var sprites = getRealSprites(); + // Unselect all + for(sprite in sprites) { + if(sprite is FunkinSprite) { + var sprite:FunkinSprite = cast sprite; + sprite.extra.set(exID("selected"), false); + sprite.extra.get(exID("button")).selected = false; + } + } + // Mark selected as selected + for(sprite in selection) { + if(sprite is FunkinSprite) { + var sprite:FunkinSprite = cast sprite; + sprite.extra.set(exID("selected"), true); + sprite.extra.get(exID("button")).selected = true; + Logs.trace("Selected " + sprite.name); + } + } + } + + function _select_all(_, checkSelection:Bool = true) { + _select_deselect(null, false); + var sprites = getRealSprites(); + selection = new Selection(sprites); + if(checkSelection) updateSelection(); + } + + function _select_deselect(_, checkSelection:Bool = true) { + selection = new Selection(); + if(checkSelection) updateSelection(); + } + + function _select_inverse(_, checkSelection:Bool = true) { + var oldSelection = selection; + _select_all(null, false); + for(sprite in oldSelection) { + selection.remove(sprite); + } + if(checkSelection) updateSelection(); + } + + var zoom(default, set):Float = 0; + var __camZoom(default, set):Float = 1; + function set_zoom(val:Float) { + return zoom = CoolUtil.bound(val, -3.5, 1.75); // makes zooming not lag behind when continuing scrolling + } + function set___camZoom(val:Float) { + return __camZoom = CoolUtil.bound(val, 0.1, 3); + } + + function _view_zoomin(_) { + zoom += 0.25; + updateZoom(); + } + function _view_zoomout(_) { + zoom -= 0.25; + updateZoom(); + } + function _view_zoomreset(_) { + setZoom(stage.defaultZoom); + } + + inline function updateZoom() { + __camZoom = Math.pow(2, zoom); + } + + inline function calculateZoom(zoom:Float) { + return Math.log(zoom) / Math.log(2); + } + + inline function setZoom(_zoom:Float) { + zoom = calculateZoom(__camZoom = _zoom); + } + + function _view_focusdad(_) { + focusCharacter(charMap["NO_DELETE_dad"]); + } + function _view_focusgf(_) { + focusCharacter(charMap["NO_DELETE_girlfriend"]); + } + function _view_focusbf(_) { + focusCharacter(charMap["NO_DELETE_boyfriend"]); + } + + function focusCharacter(char:Character) { + var point = char.getCameraPosition(); + nextScroll.set(point.x - stageCamera.width / 2, point.y - stageCamera.height / 2); + + setZoom(stage.defaultZoom); + } + + function _editor_showCharacters(t) { + showCharacters = !showCharacters; + t.icon = showCharacters ? 1 : 0; + for(char in chars) { + var button:StageCharacterButton = char.extra.get(exID("button")); + button.isHidden = !showCharacters; + button.updateInfo(); + } + } + #end + + override function draw() { + super.draw(); + + for(sprite in selection) + if(sprite is FunkinSprite) drawGuides(cast sprite); + } + + function drawGuides(sprite:FunkinSprite) { + if(sprite == null || sprite.offset == null) return; // destroyed + + var corners:Array<FlxPoint> = calcSpriteBounds(sprite); + var transformedCorners:Array<FlxPoint> = [for (corner in corners) corner.clone()]; + for (corner in transformedCorners) { + corner.x -= sprite.cameras[0].viewX; + corner.y -= sprite.cameras[0].viewY; + + corner.x *= sprite.cameras[0].zoom; + corner.y *= sprite.cameras[0].zoom; + } + + if (DrawUtil.line == null) DrawUtil.createDrawers(); + DrawUtil.line.cameras = [gizmosCamera]; DrawUtil.line.alpha = 0.85; + DrawUtil.dot.cameras = [gizmosCamera]; DrawUtil.dot.alpha = 1; + + DrawUtil.drawLine(transformedCorners[0], transformedCorners[1], 1, 0xFF007B8F); // tl - tr + DrawUtil.drawLine(transformedCorners[0], transformedCorners[2], 1, 0xFF007B8F); // tl - bl + DrawUtil.drawLine(transformedCorners[1], transformedCorners[3], 1, 0xFF007B8F); // tr - br + DrawUtil.drawLine(transformedCorners[2], transformedCorners[3], 1, 0xFF007B8F); // bl - br + + DrawUtil.drawLine(transformedCorners[7], transformedCorners[9], 1, 0xFF007B8F); // tc - ac // top center to angle center + + // cross + // DrawUtil.drawLine(transformedCorners[0], transformedCorners[3], 1, 0xFF007B8F); // tl - br + // DrawUtil.drawLine(transformedCorners[1], transformedCorners[2], 1, 0xFF007B8F); // tr - bl + + final ANGLE_INDEX = StageEditorEdge.ROTATE_CIRCLE.toInt(); + final CENTER_INDEX = StageEditorEdge.CENTER_CIRCLE.toInt(); + + if(sprite.extra.exists(exID("buttonBoxes"))) { + var oldButtonBoxes:Array<FlxPoint> = cast sprite.extra.get(exID("buttonBoxes")); + if(oldButtonBoxes != null) + for(point in oldButtonBoxes) point.put(); + } + + var buttonBoxes:Array<FlxPoint> = []; + if(sprite != null) sprite.extra.set(exID("buttonBoxes"), buttonBoxes); + + DrawUtil.dot.color = 0xFFBBE4EA; DrawUtil.dot.animation.play("default"); + for(i in 0...transformedCorners.length) { + var transformedCorner:FlxPoint = transformedCorners[i]; + + if (i != CENTER_INDEX) { + if (i == ANGLE_INDEX) { + DrawUtil.dot.animation.play("hollow"); DrawUtil.dot.color = 0xFFBBE4EA; + DrawUtil.drawDot(transformedCorner.x, transformedCorner.y, 0.5); + } else { + DrawUtil.dot.animation.play("default"); DrawUtil.dot.color = 0xFFF7A7A7; + DrawUtil.drawDot(transformedCorner.x, transformedCorner.y, 0.3); + + DrawUtil.dot.animation.play("hollow"); DrawUtil.dot.color = 0xFFBBE4EA; + DrawUtil.drawDot(transformedCorner.x, transformedCorner.y, 0.5); + } + } else if (sprite.visible) { + DrawUtil.dot.color = 0xFFBBE4EA; + DrawUtil.drawDot(transformedCorner.x, transformedCorner.y, 0.7*0.5); + DrawUtil.dot.animation.play("hollow"); + } + + buttonBoxes.push(corners[i]); + } + } + + public static function calcSpriteBounds(sprite:FunkinSprite) { + var oldWidth = sprite.width; + var oldHeight = sprite.height; + + /* + if (sprite.animateAtlas != null) { + var rect:FlxRect = MatrixUtil.getBounds(sprite); + oldWidth = rect.width; + oldHeight = rect.height; + } + */ + + var oldOffset = sprite.offset.clone(FlxPoint.weak()); + var oldOrigin = sprite.origin.clone(FlxPoint.weak()); + sprite.updateHitbox(); + sprite.offset.copyFrom(oldOffset); + sprite.origin.copyFrom(oldOrigin); + @:privateAccess sprite.updateTrig(); + + var corners = sprite.getMatrixPosition([ + // corners + FlxPoint.get(0, 0), + FlxPoint.get(1, 0), + FlxPoint.get(0, 1), + FlxPoint.get(1, 1), + // edges + FlxPoint.get(0, 0.5), + FlxPoint.get(1, 0.5), + FlxPoint.get(0.5, 1), + FlxPoint.get(0.5, 0), + // center + FlxPoint.get(0.5, 0.5), + //FlxPoint.get(0.5, -100/sprite.frameHeight) // angle + FlxPoint.get(0.5, -0.5), // angle + FlxPoint.get(1, 0) // rotate corner + ], sprite.camera, sprite.frameWidth, sprite.frameHeight); + //if(sprite.animateAtlas != null) { + // trace("Corners: " + corners); + //} + @:privateAccess corners[corners.length - 1].add( + dotCheckSize * 0.35 * sprite._cosAngle + dotCheckSize * 0.35 * sprite._sinAngle, + -dotCheckSize * 0.35 * sprite._cosAngle + dotCheckSize * 0.35 * sprite._sinAngle + ); + + //Logs.trace("Guide at " + corners[0].x + ", " + corners[0].y + " sprite at " + sprite.x + ", " + sprite.y); + + if(sprite is FunkinSprite) { + var sprite:FunkinSprite = cast sprite; + var corner0 = corners[0]; + var corner1 = corners[1]; + var corner2 = corners[2]; + var corner3 = corners[3]; + var maxX = MathUtil.maxSmart(corner0.x, corner1.x, corner2.x, corner3.x); + var maxY = MathUtil.maxSmart(corner0.y, corner1.y, corner2.y, corner3.y); + var minX = MathUtil.minSmart(corner0.x, corner1.x, corner2.x, corner3.x); + var minY = MathUtil.minSmart(corner0.y, corner1.y, corner2.y, corner3.y); + + if(!sprite.extra.exists(exID("bounds"))) { + sprite.extra.set(exID("bounds"), new FlxRect()); + } + cast(sprite.extra.get(exID("bounds")), FlxRect).set(minX, minY, maxX - minX, maxY - minY); + } + + // reset hitbox to old values + sprite.width = oldWidth; + sprite.height = oldHeight; + + return corners; + } + + var edges:Array<StageEditorEdge> = [ + //// corners + TOP_LEFT, //FlxPoint.get(0, 0), + TOP_RIGHT, //FlxPoint.get(1, 0), + BOTTOM_LEFT, //FlxPoint.get(0, 1), + BOTTOM_RIGHT, //FlxPoint.get(1, 1), + //// edges + MIDDLE_LEFT, //FlxPoint.get(0, 0.5), + MIDDLE_RIGHT, //FlxPoint.get(1, 0.5), + BOTTOM_MIDDLE, //FlxPoint.get(0.5, 1), + TOP_MIDDLE, //FlxPoint.get(0.5, 0), + + CENTER_CIRCLE, //FlxPoint.get(0.5, -0.5) + + ROTATE_CIRCLE, //FlxPoint.get(0.5, -0.5) + ROTATE_CORNER + ]; + + function tryUpdateHitbox(sprite:FunkinSprite) { + call("tryUpdateHitbox", [sprite]); + } + + function handleSelection(sprite:FunkinSprite) { + if(!sprite.extra.exists(exID("buttonBoxes"))) return; + var buttonBoxes:Array<FlxPoint> = cast sprite.extra.get(exID("buttonBoxes")); + + dotCheckSize = DrawUtil.dot.frameWidth / 0.7/stageCamera.zoom; // basically adjust it to the zoom. + + var prevMode = mouseMode; + if(FlxG.mouse.justPressed) { + for (i in StageEditorMouseMode.SKEW_TOP...(StageEditorMouseMode.SKEW_BOTTOM + 1)) { + var cappedI1 = CoolUtil.maxInt(i - StageEditorMouseMode.SKEW_TOP - 1, 0); + var cappedI2 = CoolUtil.minInt(i - StageEditorMouseMode.SKEW_TOP + 1, 3); + var point1 = buttonBoxes[cappedI1]; + var point2 = buttonBoxes[cappedI2]; + if (checkLine(point1, point2, point2.x - point1.x, point2.y - point1.y)) { + mousePoint.copyTo(clickPoint); + storedPos.set(sprite.x, sprite.y); + storedSkew.copyFrom(sprite.skew); + storedScale.copyFrom(sprite.scale); + storedAngle = sprite.angle; + mouseMode = i; + } + } + + for(i=>edge in edges) { + if(checkDot(buttonBoxes[i])) { + mouseMode = switch(edge) { + case TOP_LEFT: SCALE_TOP_LEFT; + case TOP_MIDDLE: SCALE_TOP; + case TOP_RIGHT: SCALE_TOP_RIGHT; + case MIDDLE_LEFT: SCALE_LEFT; + case MIDDLE_RIGHT: SCALE_RIGHT; + case BOTTOM_LEFT: SCALE_BOTTOM_LEFT; + case BOTTOM_MIDDLE: SCALE_BOTTOM; + case BOTTOM_RIGHT: SCALE_BOTTOM_RIGHT; + case CENTER_CIRCLE: MOVE_CENTER; + case ROTATE_CIRCLE: + angleOffset = 90; + ROTATE; + case ROTATE_CORNER: + angleOffset = Math.atan(sprite.height / sprite.width) * FlxAngle.TO_DEG; + ROTATE; + default: NONE; + } + Logs.trace("Clicked Dot: " + mouseMode.toString()); + mousePoint.copyTo(clickPoint); + storedPos.set(sprite.x, sprite.y); + storedSkew.set(sprite.skew.x, sprite.skew.y); + storedScale.copyFrom(sprite.scale); + storedAngle = sprite.angle; + } + + if(mouseMode == MOVE_CENTER){ + trace(mouseMode); + storedPos.set(sprite.x, sprite.y); + } + } + } + for(i=>edge in edges) { + if(checkDot(buttonBoxes[i])) { + // TODO: make it show both sided arrows when resizing, unless its at minimum size then show only one + // TODO: make this rotate with the sprite + currentCursor = switch(edge) { + // RESIZE_NESW; //RESIZE_NS; //RESIZE_NWSE; //RESIZE_WE; + case TOP_LEFT: RESIZE_TL; + case TOP_MIDDLE: RESIZE_T; + case TOP_RIGHT: RESIZE_TR; + case MIDDLE_LEFT: RESIZE_L; + case CENTER_CIRCLE: + #if (mac) FlxG.mouse.pressed ? DRAG : DRAG_OPEN + #elseif (linux) FlxG.mouse.pressed ? DRAG : CLICK + #else MOVE #end; + case MIDDLE_RIGHT: RESIZE_R; + case BOTTOM_LEFT: RESIZE_BL; + case BOTTOM_MIDDLE: RESIZE_B; + case BOTTOM_RIGHT: RESIZE_BR; + //case TOP_LEFT | BOTTOM_RIGHT: MouseCursor.RESIZE_NWSE; + //case TOP_MIDDLE | BOTTOM_MIDDLE: MouseCursor.RESIZE_NS; + //case TOP_RIGHT | BOTTOM_LEFT: MouseCursor.RESIZE_NESW; + //case MIDDLE_LEFT | MIDDLE_RIGHT: MouseCursor.RESIZE_WE; + + case ROTATE_CIRCLE | ROTATE_CORNER: FlxG.mouse.pressed ? DRAG : #if mac DRAG_OPEN #else CLICK #end; + default: ARROW; + } + break; + } + } + + mouseMode = (FlxG.mouse.justReleased) ? NONE : mouseMode; + + if (prevMode == NONE && mouseMode == NONE) return; + + if (prevMode != NONE && mouseMode == NONE) { + undos.addToUndo(CTransform(sprite, { + x: storedPos.x, + y: storedPos.y, + scaleX: storedScale.x, + scaleY: storedScale.y, + skewX: storedSkew.x, + skewY: storedSkew.y, + angle: storedAngle + }, { + x: sprite.x, + y: sprite.y, + scaleX: sprite.scale.x, + scaleY: sprite.scale.y, + skewX: sprite.skew.x, + skewY: sprite.skew.y, + angle: sprite.angle + })); + } + + if(mouseMode == NONE) return; + + var relative = clickPoint.subtractNew(mousePoint); + call(mouseMode.toString(), [sprite, relative]); + cast(sprite.extra.get(exID("button")), StageElementButton).updateInfo(); + relative.put(); + } + + public static var dotCheckSize:Float = 53; + + function checkDot(point:FlxPoint):Bool { + if(point!=null){ + var rect = new FlxRect(point.x - dotCheckSize/2, point.y - dotCheckSize/2, dotCheckSize, dotCheckSize); + return rect.containsPoint(mousePoint); + } + return false; + } + + function checkLine(point1:FlxPoint, point2:FlxPoint, dx:Float, dy:Float) { + var leftRegion = Math.min(point1.x, point2.x) - dotCheckSize * 0.2; + var rightRegion = Math.max(point1.x, point2.x) + dotCheckSize * 0.2; + var topRegion = Math.min(point1.y, point2.y) - dotCheckSize * 0.2; + var bottomRegion = Math.max(point1.y, point2.y) + dotCheckSize * 0.2; + + if (dx == 0.0 || dy == 0.0) + return (mousePoint.x >= leftRegion && mousePoint.x <= rightRegion) + && (mousePoint.y >= topRegion && mousePoint.y <= bottomRegion); + + var inc = dx * ((mousePoint.y - point1.y) / dy); + leftRegion += inc; + rightRegion += inc; + + inc = dy * ((mousePoint.x - point1.x) / dx); + topRegion += inc; + bottomRegion += inc; + + return (mousePoint.x >= leftRegion && mousePoint.x <= rightRegion) + && (mousePoint.y >= topRegion && mousePoint.y <= bottomRegion); + } +} + +typedef StageInfo = { + name:String, + folder:String, + startCamX:Float, + startCamY:Float, + zoom:Float, + attrib:Map<String, String> +}; + +typedef StageSprInfo = { + x:Float, + y:Float, + scaleX:Float, + scaleY:Float, + skewX:Float, + skewY:Float, // i dont wanna be cloning a bunch of FlxPoints. Theres a pool for a reason. + angle:Float +} + +enum StageChange { + CEditInfo(oldInfo:StageInfo, newInfo:StageInfo); + CTransform(sprite:FunkinSprite, oldInfo:StageSprInfo, newInfo:StageSprInfo); +} + +@:forward abstract Selection(Array<FunkinSprite>) from Array<FunkinSprite> to Array<FunkinSprite> { + public inline function new(?array:Array<FunkinSprite>) + this = array == null ? [] : array; + + // too lazy to put this in every for loop so i made it a abstract + //public inline function loop(onNote:CharterNote->Void, ?onEvent:CharterEvent->Void, ?draggableOnly:Bool = true) { + // for (s in this) { + // if (s is CharterNote && onNote != null && (draggableOnly ? s.draggable: true)) + // onNote(cast(s, CharterNote)); + // else if (s is CharterEvent && onEvent != null && (draggableOnly ? s.draggable: true)) + // onEvent(cast(s, CharterEvent)); + // } + //} +} + +enum abstract StageEditorMouseMode(Int) from Int to Int { + var NONE; + + var SCALE_LEFT; + var SCALE_BOTTOM; + var SCALE_TOP; + var SCALE_RIGHT; + var SCALE_TOP_LEFT; + var SCALE_TOP_RIGHT; + var SCALE_BOTTOM_LEFT; + var SCALE_BOTTOM_RIGHT; + + var MOVE_CENTER; + + var SKEW_TOP; + var SKEW_LEFT; + var SKEW_RIGHT; + var SKEW_BOTTOM; + + var ROTATE; + + public function toString():String { + return switch(cast this) { + case NONE: "NONE"; + case SCALE_LEFT: "SCALE_LEFT"; + case SCALE_BOTTOM: "SCALE_BOTTOM"; + case SCALE_TOP: "SCALE_TOP"; + case SCALE_RIGHT: "SCALE_RIGHT"; + case SCALE_TOP_LEFT: "SCALE_TOP_LEFT"; + case SCALE_TOP_RIGHT: "SCALE_TOP_RIGHT"; + case SCALE_BOTTOM_LEFT: "SCALE_BOTTOM_LEFT"; + case SCALE_BOTTOM_RIGHT: "SCALE_BOTTOM_RIGHT"; + case MOVE_CENTER: "MOVE_CENTER"; + case SKEW_LEFT: "SKEW_LEFT"; + case SKEW_BOTTOM: "SKEW_BOTTOM"; + case SKEW_TOP: "SKEW_TOP"; + case SKEW_RIGHT: "SKEW_RIGHT"; + case ROTATE: "ROTATE"; + } + } +} + +enum abstract StageEditorEdge(Int) { + var NONE = -1; + + var TOP_LEFT = 0; + var TOP_MIDDLE; + var TOP_RIGHT; + var MIDDLE_LEFT; + //var MIDDLE_MIDDLE; + var MIDDLE_RIGHT; + var BOTTOM_LEFT; + var BOTTOM_MIDDLE; + var BOTTOM_RIGHT; + var CENTER_CIRCLE; + + var ROTATE_CIRCLE; + var ROTATE_CORNER; + + public function toString():String { + return switch(cast this) { + case NONE: "NONE"; + case TOP_LEFT: "TOP_LEFT"; + case TOP_MIDDLE: "TOP_MIDDLE"; + case TOP_RIGHT: "TOP_RIGHT"; + case MIDDLE_LEFT: "MIDDLE_LEFT"; + //case MIDDLE_MIDDLE: "MIDDLE_MIDDLE"; + case MIDDLE_RIGHT: "MIDDLE_RIGHT"; + case BOTTOM_LEFT: "BOTTOM_LEFT"; + case BOTTOM_MIDDLE: "BOTTOM_MIDDLE"; + case BOTTOM_RIGHT: "BOTTOM_RIGHT"; + case CENTER_CIRCLE: "CENTER_CIRCLE"; + case ROTATE_CIRCLE: "ROTATE_CIRCLE"; + case ROTATE_CORNER: "ROTATE_CORNER"; + } + } + + public function toInt():Int { + return switch(cast this) { + case NONE: -1; + case TOP_LEFT: 0; + case TOP_MIDDLE: 1; + case TOP_RIGHT: 2; + case MIDDLE_LEFT: 3; + + case MIDDLE_RIGHT: 4; + case BOTTOM_LEFT: 5; + case BOTTOM_MIDDLE: 6; + case BOTTOM_RIGHT: 7; + case CENTER_CIRCLE: 8; + case ROTATE_CIRCLE: 9; + case ROTATE_CORNER: 10; + } + } +} + +class StageXMLEditScreen extends UISoftcodedWindow { + public var xml:Access; + public var saveCallback:Void->Void; + + public function new(xml:Access, saveCallback:Void->Void, type:String = "Unknown") { + this.xml = xml; + this.saveCallback = saveCallback; + super("layouts/stage/xmlEditScreen", [ + "stage" => StageEditor.instance.stage, + "xml" => xml, + "exID" => StageEditor.exID, + "type" => type + ]); + } + + override function saveData() { + super.saveData(); + if(saveCallback != null) saveCallback(); + } +} diff --git a/source/funkin/editors/stage/StageSelection.hx b/source/funkin/editors/stage/StageSelection.hx new file mode 100644 index 00000000..942f5c30 --- /dev/null +++ b/source/funkin/editors/stage/StageSelection.hx @@ -0,0 +1,65 @@ +package funkin.editors.stage; + +import funkin.editors.stage.StageCreationScreen.StageCreationData; +import funkin.game.Stage; +import funkin.options.OptionsScreen; +import funkin.options.type.NewOption; +import funkin.options.type.OptionType; +import funkin.options.type.TextOption; + +class StageSelection extends EditorTreeMenu +{ + public var stages:Array<String> = []; + + public override function create() + { + bgType = "charter"; + super.create(); + + var modsList:Array<String> = Stage.getList(true, true); + + var list:Array<OptionType> = [ + for (stage in (modsList.length == 0 ? Stage.getList(false, true) : modsList)) { + stages.push(stage); + new TextOption(stage, "Press ACCEPT to edit this stage.", + function() { + FlxG.switchState(new StageEditor(stage)); + }); + } + ]; + + list.insert(0, new NewOption("New Stage", "Press ACCEPT to create a new stage.", function() { + FlxG.state.openSubState(new StageCreationScreen(saveStage)); + })); + + main = new OptionsScreen("Stage Editor", "Select a stage to edit. HSCRIPT only stages are not supported.", list); + + //DiscordUtil.call("onEditorTreeLoaded", ["Stage Editor"]); + } + + public function saveStage(creation:StageCreationData) { + if ([for (s in stages) s.toLowerCase()].contains(creation.name.toLowerCase())) { + openSubState(new UIWarningSubstate("Creating Stage: Error!", "The stage you are trying to create alreadly exists, if you would like to override it delete the stage first!", [ + {label: "Ok", color: 0xFFFF0000, onClick: function(t) {}} + ])); + return; + } + + #if sys + // Save File + CoolUtil.safeSaveFile('${Paths.getAssetsRoot()}/data/stages/${creation.name}.xml', '<!DOCTYPE codename-engine-stage>\n<stage folder="${creation.path}">\n</stage>'); + #end + + // Add to List + var option = new TextOption(creation.name, "Press ACCEPT to edit this stage.", function() { + FlxG.switchState(new StageEditor(creation.name)); + }); + optionsTree.members[optionsTree.members.length-1].insert(1, option); + } + + override function createPost() { + super.createPost(); + + main.changeSelection(1); + } +} \ No newline at end of file diff --git a/source/funkin/editors/stage/StageSpritesWindow.hx b/source/funkin/editors/stage/StageSpritesWindow.hx new file mode 100644 index 00000000..78988e1e --- /dev/null +++ b/source/funkin/editors/stage/StageSpritesWindow.hx @@ -0,0 +1,50 @@ +package funkin.editors.stage; + +import funkin.editors.stage.elements.StageElementButton; +import funkin.editors.stage.elements.*; + +class StageSpritesWindow extends UIButtonList<StageElementButton> { + public function new(x:Float, y:Float) { + super(x, y, 400, FlxG.height-25, "Stage Sprites", FlxPoint.get(400, 64)); + + collapsable = true; + topAlpha = 0.9; + middleAlpha = 0.5; + bottomAlpha = 0.5; + buttonSpacing = 0; + dragCallback = (button, oldID, newID) -> { + var sprite:FlxBasic = (button is StageUnknownButton) ? cast(button, StageUnknownButton).basic : button.getSprite(); + var idx = StageEditor.instance.members.indexOf(sprite); + StageEditor.instance.members.splice(idx, 1); + StageEditor.instance.members.insert(newID, sprite); + } + addButton.callback = () -> @:privateAccess { + var lastDrawCam = addButton.__lastDrawCameras[0]; + var screenPos = addButton.getScreenPosition(null, lastDrawCam == null ? FlxG.camera : lastDrawCam); + StageEditor.instance.openContextMenu([ + { + label: "Sprite", + onSelect: StageEditor.instance._sprite_new, + color: 0xFF00FF00, + icon: 2 + }, + { + label: "Box", + onSelect: function(_) { + UIState.state.displayNotification(new UIBaseNotification("Creating a box isnt implemented yet!", 2, BOTTOM_LEFT)); + CoolUtil.playMenuSFX(WARNING, 0.45); + }, + color: 0xFF00FF00, + icon: 2 + }, + { + label: "Character", + onSelect: StageEditor.instance._character_new, + color: 0xFF00FF00, + icon: 2 + } + ], null, lastDrawCam.x + screenPos.x, lastDrawCam.y + screenPos.y + addButton.bHeight, addButton.bWidth); + screenPos.put(); + } + } +} \ No newline at end of file diff --git a/source/funkin/editors/stage/elements/StageCharacterButton.hx b/source/funkin/editors/stage/elements/StageCharacterButton.hx new file mode 100644 index 00000000..68156dcc --- /dev/null +++ b/source/funkin/editors/stage/elements/StageCharacterButton.hx @@ -0,0 +1,131 @@ +package funkin.editors.stage.elements; + +import funkin.editors.stage.StageEditor.StageXMLEditScreen; +import funkin.editors.ui.notifications.UIBaseNotification; +import funkin.editors.ui.UIState; +import funkin.game.Character; +import haxe.xml.Access; + +using StringTools; + +class StageCharacterButton extends StageElementButton { + public var char:Character; + public var charScale(get, null):Float; + + public function new(x:Float,y:Float, char:Character, xml:Access) { + this.char = char; + super(x,y, xml); + + color = 0xff7aa8ff; + + hasAdvancedEdit = true; + + updateInfo(); + } + + public override function update(elapsed:Float) { + super.update(elapsed); + } + + public override function updateInfo() { + char.visible = !isHidden; + char.scale.scale(1 / charScale); + super.updateInfo(); + char.scale.scale(charScale); + } + + public override function getSprite():FunkinSprite { + return char; + } + + public override function onSelect() { + StageEditor.instance.selectSprite(char); + } + + /*public override function onSelect() { + UIState.state.displayNotification(new UIBaseNotification("Selecting a character isnt implemented yet!", 2, BOTTOM_LEFT)); + CoolUtil.playMenuSFX(WARNING, 0.45); + }*/ + + public override function onEdit() { + if(!FlxG.keys.pressed.SHIFT) { + FlxG.state.openSubState(new StageCharacterEditScreen(this)); + } else { + FlxG.state.openSubState(new StageXMLEditScreen(this.xml, updateInfo, "Character")); + } + } + //public override function onEdit() { + // UIState.state.displayNotification(new UIBaseNotification("Editing a character isnt implemented yet!", 2, BOTTOM_LEFT)); + // CoolUtil.playMenuSFX(WARNING, 0.45); + //} + + public override function onDelete() { + if (char.name.startsWith("NO_DELETE_")) { + var text = "This character position is required in stages!"; + if (char.name == "NO_DELTE_girlfriend" && FlxG.random.bool(1)) + text = "You can't delete Girlfriend"; + UIState.state.displayNotification(new UIBaseNotification(text, 2, BOTTOM_LEFT)); + return; + } + + char.destroy(); + xml.x.parent.removeChild(xml.x); + StageEditor.instance.xmlMap.remove(char); + StageEditor.instance.stageSpritesWindow.remove(this); + } + + public override function onVisiblityToggle() { + isHidden = !isHidden; + updateInfo(); + } + + public override function getName():String { + return char.name.replace("NO_DELETE_", ""); + } + + public override function getPos():FlxPoint { + return FlxPoint.get(char.x, char.y); + } + + public override function updatePos() { + super.updatePos(); + } + + function get_charScale():Float { + return (char.xml.has.scale ? Std.parseFloat(char.xml.att.scale).getDefault(1) : 1); + } +} + +class StageCharacterEditScreen extends UISoftcodedWindow { + public var button:StageCharacterButton; + public var char:Character; + + public function new(button:StageCharacterButton) { + this.button = button; + this.char = button.char; + super("layouts/stage/characterEditScreen", [ + "stage" => StageEditor.instance.stage, + "char" => char, + "charScale" => button.charScale, + "button" => button, + "xml" => button.xml, + "exID" => StageEditor.exID, + "StringTools" => StringTools, + "getEx" => function(name:String):Dynamic { + return char.extra.get(StageEditor.exID(name)); + }, + "setEx" => function(name:String, value:Dynamic) { + char.extra.set(StageEditor.exID(name), value); + }, + ]); + } + + public override function create() { + super.create(); + } + + public override function saveData() { + super.saveData(); + button.updateInfo(); + } +} \ No newline at end of file diff --git a/source/funkin/editors/stage/elements/StageElementButton.hx b/source/funkin/editors/stage/elements/StageElementButton.hx new file mode 100644 index 00000000..937893c5 --- /dev/null +++ b/source/funkin/editors/stage/elements/StageElementButton.hx @@ -0,0 +1,218 @@ +package funkin.editors.stage.elements; + +import flixel.util.FlxColor; +import haxe.xml.Access; + +using flixel.util.FlxColorTransformUtil; + +class StageElementButton extends UIButton { + public var xml:Access; + + public var editButton:UIButton; + public var editIcon:FlxSprite; + + public var visibilityButton:UIButton; + public var visibilityIcon:FlxSprite; + + public var deleteButton:UIButton; + public var deleteIcon:FlxSprite; + + public var isHidden:Bool = false; + public var hasAdvancedEdit:Bool = false; + + public var selected:Bool = false; + + public var tagColor:UISliceSprite; + + public function new(x:Float,y:Float, xml:Access) { + this.xml = xml; + super(x,y, getInfoText(), function () { + onSelect(); + }, StageEditor.SPRITE_WINDOW_WIDTH, StageEditor.SPRITE_WINDOW_BUTTON_HEIGHT); + autoAlpha = false; + + tagColor = new UISliceSprite(x, y, 10, StageEditor.SPRITE_WINDOW_BUTTON_HEIGHT, 'editors/ui/button'); + tagColor.alpha = 1; // Make entire sprite transparent + tagColor.selectable = false; + tagColor.active = false; + //members.push(tagColor); + + topAlpha = middleAlpha = bottomAlpha = 0.7; + + field.alignment = LEFT; + + visibilityButton = new UIButton(x+282+17, y, null, function () { + onVisiblityToggle(); + }, 32); + visibilityButton.autoAlpha = false; + members.push(visibilityButton); + + visibilityIcon = new FlxSprite(visibilityButton.x + 8, visibilityButton.y + 8).loadGraphic(Paths.image('editors/stage/visible-icon'), true, 16, 16); + visibilityIcon.animation.add("visible", [0]); + visibilityIcon.animation.add("invisible", [1]); + visibilityIcon.animation.play("visible"); + visibilityIcon.antialiasing = false; + visibilityIcon.updateHitbox(); + members.push(visibilityIcon); + + editButton = new UIButton(visibilityButton.x+32+17, y, null, function () { + onEdit(); + }, 32); + editButton.frames = Paths.getFrames("editors/ui/grayscale-button"); + editButton.color = FlxColor.YELLOW; + editButton.autoAlpha = false; + members.push(editButton); + + editIcon = new FlxSprite(editButton.x + 8, editButton.y + 10).loadGraphic(Paths.image('editors/stage/edit-button'), true, 16, 16); + editIcon.animation.add("edit", [0]); + editIcon.animation.add("advanced", [1]); + editIcon.antialiasing = false; + members.push(editIcon); + + deleteButton = new UIButton(editButton.x+32+17, y, null, function () { + onDelete(); + }, 32); + deleteButton.color = FlxColor.RED; + deleteButton.autoAlpha = false; + members.push(deleteButton); + + deleteIcon = new FlxSprite(deleteButton.x + 8, deleteButton.y + 8).loadGraphic(Paths.image('editors/stage/trash-icon')); + deleteIcon.antialiasing = false; + members.push(deleteIcon); + + setEditNormal(); + } + + public function setEditNormal() { + editIcon.animation.play("edit"); + editButton.color = FlxColor.YELLOW; + } + + public function setEditAdvanced() { + editIcon.animation.play("advanced"); + editButton.color = 0xFFFF5B0F; + } + + + var _lastSelected:Bool = false; + + public override function update(elapsed:Float) { + editButton.selectable = visibilityButton.selectable = deleteButton.selectable = selectable; + editButton.shouldPress = visibilityButton.shouldPress = deleteButton.shouldPress = shouldPress; + + hovered = !deleteButton.hovered; + updatePos(); + super.update(elapsed); + field.x += 12; + + if(hasAdvancedEdit) { + if(FlxG.keys.pressed.SHIFT) { + setEditAdvanced(); + } else { + setEditNormal(); + } + } + + tagColor.color = color; + } + + //private var _lastHovered:Bool = false; + + public override function draw() { + //if(_lastHovered != (_lastHovered = hovered && !pressed)) { + if(_lastSelected != (_lastSelected = selected)) { + updateColorTransform(); + } + super.draw(); + } + + /*public override function setFrameOffset() { + var _frameOffset = 0; + if(selected) _frameOffset = 9; + if(hovered && pressed) _frameOffset = 18; + framesOffset = _frameOffset; + }*/ + + public override function updateColorTransform() { + super.updateColorTransform(); + + if(selected) { + useColorTransform = true; + colorTransform.setOffsets(70, 70, 70, 0); + } else { + colorTransform.setOffsets(0, 0, 0, 0); + } + } + + public function updateInfo() { + field.text = getInfoText(); + + visibilityIcon.animation.play(isHidden ? "invisible" : "visible"); + visibilityIcon.alpha = !isHidden ? 1 : 0.5; + } + + public function updatePos() { + // buttons + var spacing = 8; + var buttonY = y + (bHeight - 32) / 2; + deleteButton.x = bWidth - deleteButton.bWidth - spacing; + deleteButton.y = buttonY; + editButton.x = deleteButton.x - editButton.bWidth - spacing; + editButton.y = buttonY; + visibilityButton.x = editButton.x - visibilityButton.bWidth - spacing; + visibilityButton.y = buttonY; + //deleteButton.x = (editButton.x = (visibilityButton.x = (x+282+17))+32+17)+32+17; + //deleteButton.y = editButton.y = visibilityButton.y = y; + // icons + visibilityIcon.x = visibilityButton.x + 8; visibilityIcon.y = visibilityButton.y + 8; + editIcon.x = editButton.x + 8; editIcon.y = editButton.y + 8; + deleteIcon.x = deleteButton.x + 8; deleteIcon.y = deleteButton.y + 8; + + tagColor.x = x;// + bWidth - tagColor.bWidth; + tagColor.y = y; + } + + public function getSprite():FunkinSprite { + return null; + } + + public function canRender() { + return true; + } + + public function getName():String { + return "UNKNOWN"; + } + + public function onSelect() { + // TODO: implement + } + + public function onVisiblityToggle() { + // TODO: implement + } + + public function onEdit() { + // TODO: implement + } + + public function onDelete() { + // TODO: implement + } + + public function getPos():FlxPoint { + return FlxPoint.get(-1, -1); + } + + public function getInfoText():String { + var pos = getPos(); + var text = '${getName()} (${CoolUtil.quantize(pos.x, 100)}, ${CoolUtil.quantize(pos.y, 100)})'; + var sprite = getSprite(); + if(sprite != null) { + text += '\nScale: (${CoolUtil.quantize(sprite.scale.x, 100)}, ${CoolUtil.quantize(sprite.scale.y, 100)})'; + text += '\nScroll: (${CoolUtil.quantize(sprite.scrollFactor.x, 100)}, ${CoolUtil.quantize(sprite.scrollFactor.y, 100)})'; + } + pos.put(); + return text; + } +} \ No newline at end of file diff --git a/source/funkin/editors/stage/elements/StageSolidButton.hx b/source/funkin/editors/stage/elements/StageSolidButton.hx new file mode 100644 index 00000000..6ded6d26 --- /dev/null +++ b/source/funkin/editors/stage/elements/StageSolidButton.hx @@ -0,0 +1,16 @@ +package funkin.editors.stage.elements; + +import flixel.util.FlxColor; +import haxe.xml.Access; + +class StageSolidButton extends StageSpriteButton { + + public function new(x:Float,y:Float, sprite:FunkinSprite, xml:Access) { + super(x,y, sprite, xml); + color = 0xFFD9FF50; + } + + public override function onEdit() { + // TODO: implement + } +} \ No newline at end of file diff --git a/source/funkin/editors/stage/elements/StageSpriteButton.hx b/source/funkin/editors/stage/elements/StageSpriteButton.hx new file mode 100644 index 00000000..485eca9f --- /dev/null +++ b/source/funkin/editors/stage/elements/StageSpriteButton.hx @@ -0,0 +1,114 @@ +package funkin.editors.stage.elements; + +import funkin.editors.stage.StageEditor.StageXMLEditScreen; +import funkin.editors.stage.elements.StageElementButton; +import funkin.editors.ui.UISoftcodedWindow; +import haxe.xml.Access; + +class StageSpriteButton extends StageElementButton { + public var sprite:FunkinSprite; + + public function new(x:Float,y:Float, sprite:FunkinSprite, xml:Access) { + this.sprite = sprite; + super(x,y, xml); + + //color = 0xFFD9FF50; + + hasAdvancedEdit = true; + + updateInfo(); + } + + public override function update(elapsed:Float) { + super.update(elapsed); + } + + public override function updateInfo() { + sprite.visible = !isHidden; + super.updateInfo(); + } + + public override function getSprite():FunkinSprite { + return sprite; + } + + public override function onSelect() { + StageEditor.instance.selectSprite(sprite); + } + + public override function onVisiblityToggle() { + isHidden = !isHidden; + updateInfo(); + } + + public override function onEdit() { + if(!FlxG.keys.pressed.SHIFT) { + FlxG.state.openSubState(new StageSpriteEditScreen(this)); + } else { + FlxG.state.openSubState(new StageXMLEditScreen(this.xml, updateInfo, "Sprite")); + } + } + + public override function onDelete() { + sprite.destroy(); + xml.x.parent.removeChild(xml.x); + StageEditor.instance.stage.stageSprites.remove(sprite.name); + StageEditor.instance.xmlMap.remove(sprite); + StageEditor.instance.stageSpritesWindow.remove(this); + } + + public override function getName():String { + return xml.att.name; + } + + public override function getPos():FlxPoint { + return FlxPoint.get(sprite.x, sprite.y); + } + + public override function updatePos() { + super.updatePos(); + } +} + +class StageSpriteEditScreen extends UISoftcodedWindow { + public var newSprite:Bool = false; + public var button:StageSpriteButton; + public var sprite:FunkinSprite; + var isSaving:Bool = false; + + public function new(button:StageSpriteButton) { + this.button = button; + this.sprite = button.getSprite(); + super("layouts/stage/spriteEditScreen", [ + "stage" => StageEditor.instance.stage, + "sprite" => sprite, + "button" => button, + "xml" => button.xml, + "exID" => StageEditor.exID, + "getEx" => function(name:String):Dynamic { + return sprite.extra.get(StageEditor.exID(name)); + }, + "setEx" => function(name:String, value:Dynamic) { + sprite.extra.set(StageEditor.exID(name), value); + }, + ]); + } + + public override function create() { + super.create(); + } + + public override function saveData() { + isSaving = true; + super.saveData(); + button.updateInfo(); + } + + public override function close() { + if (!isSaving && newSprite) { + button.onDelete(); + trace("deleting sprite"); + } + super.close(); + } +} \ No newline at end of file diff --git a/source/funkin/editors/stage/elements/StageUnknownButton.hx b/source/funkin/editors/stage/elements/StageUnknownButton.hx new file mode 100644 index 00000000..a1beb0bc --- /dev/null +++ b/source/funkin/editors/stage/elements/StageUnknownButton.hx @@ -0,0 +1,107 @@ +package funkin.editors.stage.elements; + +import funkin.game.Character; +import funkin.game.Stage.StageCharPos; +import haxe.xml.Access; + +class StageUnknownButton extends StageElementButton { + public var lowMemory:Bool = false; + public var highMemory:Bool = false; + public var basic:FlxBasic; + + public function new(x:Float,y:Float, basic:FlxBasic, xml:Access) { + this.basic = basic; + super(x,y, xml); + + if(xml.x.parent != null) { + lowMemory = xml.x.parent.nodeName == "low-memory"; + highMemory = xml.x.parent.nodeName == "high-memory"; + } + + color = 0xff000000; + + visibilityButton.exists = false; + members.remove(visibilityButton); + + visibilityIcon.exists = false; + members.remove(visibilityIcon); + + setEditAdvanced(); + + updateInfo(); + } + + public override function update(elapsed:Float) { + super.update(elapsed); + } + + public override function updateInfo() { + super.updateInfo(); + } + + public override function getSprite():FunkinSprite { + var sprite = new FunkinSprite(); // to allow it to be saved + sprite.extra.set(StageEditor.exID("lowMemory"), lowMemory); + sprite.extra.set(StageEditor.exID("highMemory"), highMemory); + sprite.extra.set(StageEditor.exID("button"), this); + return sprite; + } + + public override function canRender() { + return false; + } + + public override function onSelect() { + } + + public override function onEdit() { + FlxG.state.openSubState(new StageUnknownEditScreen(this)); + } + + //public override function onEdit() { + // UIState.state.displayNotification(new UIBaseNotification("Editing a character isnt implemented yet!", 2, BOTTOM_LEFT)); + // CoolUtil.playMenuSFX(WARNING, 0.45); + //} + + public override function onVisiblityToggle() { + } + + public override function getName():String { + return xml.name; + } + + public override function getPos():FlxPoint { + return FlxPoint.get(0, 0); + } + + public override function getInfoText():String { + return 'Unknown\n<${getName()} />'; + } + + public override function updatePos() { + super.updatePos(); + } +} + +class StageUnknownEditScreen extends UISoftcodedWindow { + public var button:StageUnknownButton; + + public function new(button:StageUnknownButton) { + this.button = button; + super("layouts/stage/unknownEditScreen", [ + "stage" => StageEditor.instance.stage, + "button" => button, + "xml" => button.xml, + "exID" => StageEditor.exID + ]); + } + + public override function create() { + super.create(); + } + + public override function saveData() { + super.saveData(); + button.updateInfo(); + } +} \ No newline at end of file diff --git a/source/funkin/editors/ui/UIAudioPlayer.hx b/source/funkin/editors/ui/UIAudioPlayer.hx index c2e171fe..4b87dd4d 100644 --- a/source/funkin/editors/ui/UIAudioPlayer.hx +++ b/source/funkin/editors/ui/UIAudioPlayer.hx @@ -29,7 +29,7 @@ class UIAudioPlayer extends UIButton { public function new(x:Float, y:Float, bytes:Bytes) { sound = FlxG.sound.load(Sound.fromAudioBuffer(AudioBuffer.fromBytes(bytes))); - super(x, y, "", function () { + super(x, y, null, function () { if (sound.playing) sound.pause(); else sound.play(false, sound.time); }, 58 - 16, 58 - 16); @@ -58,7 +58,7 @@ class UIAudioPlayer extends UIButton { members.push(timeBarPlayer); timeBarSpr = cast new UISprite(timeBar.x, timeBar.y).makeSolid(timeBar.barWidth, timeBar.barHeight, 0x00FFFFFF); - timeBarSpr.cursor = BUTTON; + timeBarSpr.cursor = CLICK; members.push(timeBarSpr); volumeBar = new FlxBar(timeBar.x + timeBar.barWidth - 56, y + 6, LEFT_TO_RIGHT, 56, 10, sound, "volume", 0, 1); @@ -68,7 +68,7 @@ class UIAudioPlayer extends UIButton { members.push(volumeBar); volumeBarSpr = cast new UISprite(volumeBar.x, volumeBar.y).makeSolid(volumeBar.barWidth, volumeBar.barHeight, 0x00FFFFFF); - volumeBarSpr.cursor = BUTTON; + volumeBarSpr.cursor = CLICK; members.push(volumeBarSpr); volumeIcon = new FlxSprite(volumeBar.x - 12 - 8, volumeBar.y-1).loadGraphic(Paths.image('editors/ui/audio-icon')); diff --git a/source/funkin/editors/ui/UIAutoCompleteTextBox.hx b/source/funkin/editors/ui/UIAutoCompleteTextBox.hx index 60259c41..497fe383 100644 --- a/source/funkin/editors/ui/UIAutoCompleteTextBox.hx +++ b/source/funkin/editors/ui/UIAutoCompleteTextBox.hx @@ -9,10 +9,10 @@ class UIAutoCompleteTextBox extends UITextBox { public var suggestItems(default, set):Array<String> = []; var suggestIndex = 0; - public function new(x:Float, y:Float, text:String = "", width:Int = 320, height:Int = 32, multiline:Bool = false) { - super(x, y, text, width, height, multiline); + public function new(x:Float, y:Float, text:String = "", width:Int = 320, height:Int = 32, multiline:Bool = false, small:Bool = false) { + super(x, y, text, width, height, multiline, small); - suggestionText = new UIText(0, 0, width, ""); + suggestionText = new UIText(0, 0, width, "", small ? 12 : 15); suggestionText.color = 0xFF888888; suggestionText.visible = false; members.insert(members.indexOf(label), suggestionText); @@ -20,11 +20,7 @@ class UIAutoCompleteTextBox extends UITextBox { public override function update(elapsed:Float) { super.update(elapsed); - - var selected = selectable && focused; - - suggestionText.exists = selected; - + suggestionText.exists = selectable && focused; suggestionText.follow(label, 0, 0); } diff --git a/source/funkin/editors/ui/UIButton.hx b/source/funkin/editors/ui/UIButton.hx index c5522c5c..8aa75936 100644 --- a/source/funkin/editors/ui/UIButton.hx +++ b/source/funkin/editors/ui/UIButton.hx @@ -6,19 +6,24 @@ class UIButton extends UISliceSprite { public var shouldPress = true; public var hasBeenPressed = false; + public var autoFrames:Bool = true; + public var autoFollow:Bool = true; + public override function new(x:Float, y:Float, text:String, callback:Void->Void, w:Int = 120, h:Int = 32) { super(x, y, w, h, 'editors/ui/button'); this.callback = callback; - members.push(field = new UIText(x, y, w, text)); - field.alignment = CENTER; - field.fieldWidth = w; + if(text != null) { + members.push(field = new UIText(x, y, w, text)); + field.alignment = CENTER; + field.fieldWidth = w; + } - cursor = BUTTON; + cursor = CLICK; } public override function resize(w:Int, h:Int) { super.resize(w, h); - if (field != null) field.fieldWidth = w; + if (field != null && autoFollow) field.fieldWidth = w; } public override function onHovered() { @@ -31,14 +36,21 @@ class UIButton extends UISliceSprite { } public override function update(elapsed:Float) { - field.follow(this, 0, (bHeight - field.height) / 2); + if (autoFollow && field != null) field.follow(this, 0, (bHeight - field.height) / 2); if (!hovered && hasBeenPressed && FlxG.mouse.justReleased) hasBeenPressed = false; - if (autoAlpha) alpha = field.alpha = selectable ? 1 : 0.4; + if (autoAlpha) { + alpha = selectable ? 1 : 0.4; + if(field != null) field.alpha = alpha; + } super.update(elapsed); } public override function draw() { - framesOffset = hovered ? (pressed ? 18 : 9) : 0; + setFrameOffset(); super.draw(); } + + public function setFrameOffset() { + framesOffset = hovered ? (pressed ? 18 : 9) : 0; + } } \ No newline at end of file diff --git a/source/funkin/editors/ui/UIButtonList.hx b/source/funkin/editors/ui/UIButtonList.hx index ec8385e5..896579f9 100644 --- a/source/funkin/editors/ui/UIButtonList.hx +++ b/source/funkin/editors/ui/UIButtonList.hx @@ -14,7 +14,7 @@ class UIButtonList<T:UIButton> extends UIWindow { public var cameraSpacing = 30; public var buttonSpacing:Float = 16; - public var buttonSize:FlxPoint = FlxPoint.get(); + public var buttonSize:FlxPoint = null; public var buttonOffset:FlxPoint = FlxPoint.get(); public var dragging:Bool = false; @@ -33,7 +33,7 @@ class UIButtonList<T:UIButton> extends UIWindow { FlxG.cameras.add(buttonCameras, false); buttonCameras.bgColor = 0; - addButton = new UIButton(25, 16, "", null, Std.int(this.buttonSize.x)); + addButton = new UIButton(25, 16, null, null, Std.int(this.buttonSize.x)); addButton.autoAlpha = false; addButton.color = 0xFF00FF00; addButton.cameras = [buttonCameras]; @@ -41,14 +41,14 @@ class UIButtonList<T:UIButton> extends UIWindow { addIcon = new FlxSprite(addButton.x + addButton.bHeight / 2, addButton.y + (32/2) - 8).loadGraphic(Paths.image('editors/charter/add-button')); addIcon.antialiasing = false; addButton.members.push(addIcon); - members.push(addButton); + content.add(addButton); - members.push(buttons); + content.add(buttons); nextscrollY = buttonCameras.scroll.y = -this.buttonSpacing; } public inline function add(button:T) { - button.ID = buttons.members.length-1; + button.ID = buttons.members.length; buttons.add(button); curMovingInterval = 0; nextscrollY += button.bHeight; @@ -62,40 +62,45 @@ class UIButtonList<T:UIButton> extends UIWindow { public inline function remove(button:T) { nextscrollY -= button.bHeight; - buttons.members.remove(button); + buttons.remove(button, true); button.destroy(); } public inline function updateButtonsPos(elapsed:Float) { + var endButtonY:Float = 0; for (i => button in buttons.members) { if (button == null) continue; - + if (curMoving != button) { button.setPosition( (bWidth/2) - (buttonSize.x/2) + buttonOffset.x, - CoolUtil.fpsLerp(button.y, ((buttonSize.y+buttonSpacing) * i) + buttonOffset.y, 0.25)); + CoolUtil.fpsLerp(button.y, endButtonY + buttonOffset.y, 0.25)); } + endButtonY += button.bHeight+buttonSpacing; if (button.hovered && FlxG.mouse.justPressed) curMoving = button; } if (addButton != null) addButton.setPosition( (bWidth/2) - (buttonSize.x/2) + buttonOffset.x, - CoolUtil.fpsLerp(addButton.y, ((buttonSize.y+buttonSpacing) * buttons.members.length) + buttonOffset.y, 0.25)); + CoolUtil.fpsLerp(addButton.y, endButtonY + buttonOffset.y, 0.25)); if (curMoving != null) { curMovingInterval += FlxG.mouse.deltaY; if (Math.abs(curMovingInterval) > addButton.bHeight / 2) { curMovingInterval = 999; - curMoving.y = CoolUtil.fpsLerp(curMoving.y, FlxG.mouse.getWorldPosition(buttonCameras).y - (curMoving.bHeight / 2), 0.3); + var mousePos = FlxG.mouse.getWorldPosition(buttonCameras); + curMoving.y = CoolUtil.fpsLerp(curMoving.y, mousePos.y - (curMoving.bHeight / 2), 0.3); buttons.sort(function(o, a:T, b:T) return FlxSort.byValues(o, a.y + (a.bHeight / 2), b.y + (a.bHeight / 2)), -1); + mousePos.put(); } if (FlxG.mouse.justReleased) { curMoving = null; curMovingInterval = 0; } } - addIcon.x = addButton.x + addButton.bWidth / 2 - addIcon.width / 2; addIcon.y = addButton.y + addButton.bHeight / 2 - addIcon.height / 2; + addIcon.x = addButton.x + addButton.bWidth / 2 - addIcon.width / 2; + addIcon.y = addButton.y + addButton.bHeight / 2 - addIcon.height / 2; } var nextscrollY:Float = 0; public override function update(elapsed:Float) { @@ -104,14 +109,16 @@ class UIButtonList<T:UIButton> extends UIWindow { super.update(elapsed); - nextscrollY = FlxMath.bound(buttonCameras.scroll.y - (hovered ? FlxG.mouse.wheel : 0) * 12, -buttonSpacing, Math.max((addButton.y + 32 + (buttonSpacing*1.5)) - buttonCameras.height, -buttonSpacing)); + nextscrollY = CoolUtil.bound(nextscrollY - (hovered ? FlxG.mouse.wheel : 0) * 12, -buttonSpacing, Math.max((addButton.y + 32 + (buttonSpacing*1.5)) - buttonCameras.height, -buttonSpacing)); if (curMoving != null && dragging) { - nextscrollY -= Math.min((bHeight - 100) - FlxG.mouse.getWorldPosition(buttonCameras).y, 0) / 8; - nextscrollY += Math.min(FlxG.mouse.getWorldPosition(buttonCameras).y - 100, 0) / 8; + var mousePos = FlxG.mouse.getWorldPosition(buttonCameras); + nextscrollY -= Math.min((bHeight - 100) - mousePos.y, 0) / 8; + nextscrollY += Math.min(mousePos.y - 100, 0) / 8; + mousePos.put(); } - buttonCameras.scroll.y = nextscrollY; + buttonCameras.scroll.y = FlxMath.lerp(buttonCameras.scroll.y, nextscrollY, 1/3); for (i => button in buttons.members) { if (button == null) continue; @@ -125,9 +132,10 @@ class UIButtonList<T:UIButton> extends UIWindow { addButton.selectable = (hovered && !dragging); if (__lastDrawCameras[0] != null) { - buttonCameras.height = bHeight - cameraSpacing - 1; // -1 for the little gap at the bottom of the window + // -1 on each vertical sides for the little gap at the bottom of the window + buttonCameras.height = bHeight - cameraSpacing - 2; buttonCameras.x = __lastDrawCameras[0].x + x - __lastDrawCameras[0].scroll.x; - buttonCameras.y = __lastDrawCameras[0].y + y + cameraSpacing - __lastDrawCameras[0].scroll.y; + buttonCameras.y = __lastDrawCameras[0].y + y + cameraSpacing - __lastDrawCameras[0].scroll.y+1; buttonCameras.zoom = __lastDrawCameras[0].zoom; } } diff --git a/source/funkin/editors/ui/UICheckbox.hx b/source/funkin/editors/ui/UICheckbox.hx index a5bcdef3..edbbdd07 100644 --- a/source/funkin/editors/ui/UICheckbox.hx +++ b/source/funkin/editors/ui/UICheckbox.hx @@ -2,35 +2,38 @@ package funkin.editors.ui; class UICheckbox extends UISprite { public var checked:Bool = false; + public var small:Bool = false; public var onChecked:Bool->Void = null; public var field:UIText; public var check:FlxSprite; - public function new(x:Float, y:Float, text:String, checked:Bool = false, w:Int = 0) { + public function new(x:Float, y:Float, text:String, checked:Bool = false, w:Int = 0, small:Bool = false) { super(x, y); - loadGraphic(Paths.image('editors/ui/checkbox'), true, 20, 20); + loadGraphic(Paths.image('editors/ui/checkbox${small ? "-small" : ""}'), true, small ? 14 : 20, small ? 14 : 20); for(frame=>name in ["normal", "hover", "pressed", "checkmark"]) animation.add(name, [frame], 0, false); this.checked = checked; + this.small = small; - field = new UIText(x, y, w, text); + field = new UIText(x, y, w, text, small ? 12 : 15); check = new FlxSprite().loadGraphicFromSprite(this); check.animation.play("checkmark"); members.push(check); members.push(field); - cursor = BUTTON; + cursor = CLICK; } public override function update(elapsed:Float) { // ANIMATION HANDLING - animation.play(hovered ? (pressed ? "pressed" : "hover") : "normal"); + animation.play(hovered && selectable ? (pressed ? "pressed" : "hover") : "normal"); + this.alpha = field.alpha = selectable ? 1 : 0.4; // CHECKMARK HANDLING - check.alpha = checked ? 1 : 0; + check.alpha = selectable ? (checked ? 1 : 0) : 0; check.scale.x = CoolUtil.fpsLerp(check.scale.x, 1, 0.25); check.scale.y = CoolUtil.fpsLerp(check.scale.y, 1, 0.25); @@ -42,7 +45,7 @@ class UICheckbox extends UISprite { public inline function updatePositions() { check.follow(this); - field.follow(this, 25, 0); + field.follow(this, (small ? 14 : 20)+5, 0); } public override function draw() { @@ -65,10 +68,7 @@ class UICheckbox extends UISprite { } public override function updateButton() { - __rect.x = x; - __rect.y = y; - __rect.width = field.width + 30; - __rect.height = field.height > height ? field.height : height; + __rect.set(x, y, field.width + 30, field.height > height ? field.height : height); UIState.state.updateRectButtonHandler(this, __rect, onHovered); } } \ No newline at end of file diff --git a/source/funkin/editors/ui/UIColorwheel.hx b/source/funkin/editors/ui/UIColorwheel.hx index fb7f525b..c1ac432f 100644 --- a/source/funkin/editors/ui/UIColorwheel.hx +++ b/source/funkin/editors/ui/UIColorwheel.hx @@ -69,10 +69,11 @@ class UIColorwheel extends UISliceSprite { numStepper.antialiasing = true; numStepper.ID = i; numStepper.onChange = (text:String) -> { @:privateAccess numStepper.__onChange(text); + var val = Std.int(numStepper.value); switch (numStepper.ID) { - default: curColor.red = Std.int(numStepper.value); - case 1: curColor.green = Std.int(numStepper.value); - case 2: curColor.blue = Std.int(numStepper.value); + default: curColor.red = val; + case 1: curColor.green = val; + case 2: curColor.blue = val; } hue = curColor.hue; saturation = curColor.saturation; brightness = curColor.brightness; updateWheel(); @@ -125,28 +126,33 @@ class UIColorwheel extends UISliceSprite { // Make the colorwheel feel better static inline var hitBoxExtension:Float = 8; + // Skibidi + var selectedSprite = null; public override function update(elapsed:Float) { - if (hovered && FlxG.mouse.pressed) { - var mousePos = FlxG.mouse.getScreenPosition(__lastDrawCameras[0], FlxPoint.get()); - + var mousePos = FlxG.mouse.getScreenPosition(__lastDrawCameras[0], FlxPoint.get()); + if (hovered && FlxG.mouse.justPressed) { for (sprite in [colorPicker, colorSlider]) { var spritePos:FlxPoint = sprite.getScreenPosition(FlxPoint.get(), __lastDrawCameras[0]); - - if (((mousePos.x > (spritePos.x - (hitBoxExtension/2))) && (mousePos.x < spritePos.x - (hitBoxExtension/2) + (sprite.width + hitBoxExtension))) && ((mousePos.y > (spritePos.y - (hitBoxExtension/2))) && (mousePos.y < spritePos.y - (hitBoxExtension/2) + (sprite.height + hitBoxExtension)))) { - mousePos -= FlxPoint.weak(spritePos.x, spritePos.y); - mousePos.set(FlxMath.bound(mousePos.x, 0, sprite.width), FlxMath.bound(mousePos.y, 0, sprite.height)); - - if (sprite == colorSlider) updateColorSliderMouse(mousePos); - if (sprite == colorPicker) updateColorPickerMouse(mousePos); - updateWheel(); - - spritePos.put(); + if (FlxMath.inBounds(mousePos.x, spritePos.x - (hitBoxExtension/2), spritePos.x - (hitBoxExtension/2) + (sprite.width + hitBoxExtension)) && FlxMath.inBounds(mousePos.y, spritePos.y - (hitBoxExtension/2), spritePos.y - (hitBoxExtension/2) + (sprite.height + hitBoxExtension))) { + selectedSprite = sprite; break; } } - mousePos.put(); } + if (selectedSprite != null) { + var spritePos:FlxPoint = selectedSprite.getScreenPosition(FlxPoint.get(), __lastDrawCameras[0]); + mousePos -= FlxPoint.weak(spritePos.x, spritePos.y); + mousePos.set(CoolUtil.bound(mousePos.x, 0, selectedSprite.width), CoolUtil.bound(mousePos.y, 0, selectedSprite.height)); + + if (selectedSprite == colorSlider) updateColorSliderMouse(mousePos); + if (selectedSprite == colorPicker) updateColorPickerMouse(mousePos); + updateWheel(); + spritePos.put(); + + if (FlxG.mouse.justReleased) selectedSprite = null; + } + mousePos.put(); super.update(elapsed); } } diff --git a/source/funkin/editors/ui/UICompactColorwheel.hx b/source/funkin/editors/ui/UICompactColorwheel.hx new file mode 100644 index 00000000..7623a1de --- /dev/null +++ b/source/funkin/editors/ui/UICompactColorwheel.hx @@ -0,0 +1,103 @@ +package funkin.editors.ui; + +import flixel.util.FlxColor; +import flixel.math.FlxPoint; +import funkin.editors.ui.UIColorwheel.UIColorWheelSelector; + +using flixel.util.FlxSpriteUtil; + +class UICompactColorwheel extends UISliceSprite { + var colorSlider:FlxSprite; + var colorSliderSelector:UIColorWheelSelector; + + var whiteColorButton:UIButton; + + public var curColor:Null<FlxColor>; + public var curColorString:String = "#FFFFFF"; + + public var hue:Float; + + public function new (x:Float, y:Float, ?color:Int) { + super(x, y, Std.int((12.5 * 3) + 116), Std.int((12.5 * 2) + (16)), 'editors/ui/inputbox'); + + curColor = cast color; + hue = curColor.hue; + + colorSlider = new FlxSprite(x + 12.5, y + 12.5).makeGraphic(100, 16, FlxColor.TRANSPARENT); + colorSlider.drawRoundRect(0, 0, 100, 16, 10, 10, FlxColor.WHITE); + + colorSlider.pixels.lock(); + for (pixelx in 0...Std.int(colorSlider.width)) { + var color:Int = FlxColor.fromHSB(pixelx / (colorSlider.width-1) * 360, 1, 1); + for (pixely in 0...Std.int(colorSlider.height)) + if (colorSlider.pixels.getPixel32(pixelx, pixely) != FlxColor.TRANSPARENT) colorSlider.pixels.setPixel32(pixelx, pixely, color); + } + colorSlider.pixels.unlock(); + + whiteColorButton = new UIButton(colorSlider.x + colorSlider.width + 12.5, colorSlider.y, null, () -> { + setColor(FlxColor.WHITE); + }, 16, 16); + whiteColorButton.colorTransform.color = FlxColor.WHITE; + + colorSliderSelector = new UIColorWheelSelector(colorSlider.x, colorSlider.y); + + colorSlider.antialiasing = true; + + for (item in [colorSlider, colorSliderSelector, whiteColorButton]) + members.push(item); + + updateColor(false); + } + + inline function updateColorSliderPickerSelector() + colorSliderSelector.selector.setPosition(colorSlider.x + (hue / 360 * (colorSlider.width-1)) - 8, colorSlider.y); + + inline function updateColorSliderMouse(mousePos:FlxPoint) + hue = (mousePos.x / colorSlider.width) * 360; + + function setColor(newColor:FlxColor) { + this.color = curColor = colorSliderSelector.curColor = newColor; + } + + public function updateColor(checkChanged:Bool = true) { + if(checkChanged) colorChanged = true; + setColor(FlxColor.fromHSB(hue, 1, 1)); + + updateColorSliderPickerSelector(); + curColorString = curColor.toWebString(); + } + + // For Character Editor + public var colorChanged:Bool = false; + + // Make the colorwheel feel better + static inline var hitBoxExtenstion:Float = 8; + + var selectedSprite = null; + public override function update(elapsed:Float) { + var mousePos = FlxG.mouse.getScreenPosition(__lastDrawCameras[0], FlxPoint.get()); + if (hovered && FlxG.mouse.justPressed) { + for (sprite in [colorSlider]) { + var spritePos:FlxPoint = sprite.getScreenPosition(FlxPoint.get(), __lastDrawCameras[0]); + if (FlxMath.inBounds(mousePos.x, spritePos.x - (hitBoxExtenstion/2), spritePos.x - (hitBoxExtenstion/2) + (sprite.width + hitBoxExtenstion)) && FlxMath.inBounds(mousePos.y, spritePos.y - (hitBoxExtenstion/2), spritePos.y - (hitBoxExtenstion/2) + (sprite.height + hitBoxExtenstion))) { + selectedSprite = sprite; + break; + } + } + } + + if (selectedSprite != null) { + var spritePos:FlxPoint = selectedSprite.getScreenPosition(FlxPoint.get(), __lastDrawCameras[0]); + mousePos -= FlxPoint.weak(spritePos.x, spritePos.y); + mousePos.set(CoolUtil.bound(mousePos.x, 0, selectedSprite.width), CoolUtil.bound(mousePos.y, 0, selectedSprite.height)); + + if (selectedSprite == colorSlider) updateColorSliderMouse(mousePos); + updateColor(); + spritePos.put(); + + if (FlxG.mouse.justReleased) selectedSprite = null; + } + mousePos.put(); + super.update(elapsed); + } +} \ No newline at end of file diff --git a/source/funkin/editors/ui/UIContextMenu.hx b/source/funkin/editors/ui/UIContextMenu.hx index 8cc5bcb0..bcbf6dbe 100644 --- a/source/funkin/editors/ui/UIContextMenu.hx +++ b/source/funkin/editors/ui/UIContextMenu.hx @@ -7,6 +7,7 @@ class UIContextMenu extends MusicBeatSubstate { public var options:Array<UIContextMenuOption>; var x:Float; var y:Float; + var w:Int = 100; var contextCam:FlxCamera; var bg:UISliceSprite; @@ -15,17 +16,21 @@ class UIContextMenu extends MusicBeatSubstate { public var contextMenuOptions:Array<UIContextMenuOptionSpr> = []; public var separators:Array<FlxSprite> = []; + var scroll:Float = 0.0; + var flipped:Bool = false; + private var __oobDeletion:Bool = true; public inline function preventOutOfBoxClickDeletion() { __oobDeletion = false; } - public function new(options:Array<UIContextMenuOption>, callback:UIContextMenuCallback, x:Float, y:Float) { + public function new(options:Array<UIContextMenuOption>, callback:UIContextMenuCallback, x:Float, y:Float, ?w:Int = 100) { super(); this.options = options.getDefault([]); this.x = x; this.y = y; + this.w = w; this.callback = callback; } @@ -37,7 +42,7 @@ class UIContextMenu extends MusicBeatSubstate { contextCam.scroll.set(0, 7.5); FlxG.cameras.add(contextCam, false); - bg = new UISliceSprite(x, y, 100, 100, 'editors/ui/context-bg'); + bg = new UISliceSprite(x, y, w, 100, 'editors/ui/context-bg'); bg.cameras = [contextCam]; add(bg); @@ -75,7 +80,8 @@ class UIContextMenu extends MusicBeatSubstate { bg.bWidth = maxW + 8; bg.bHeight = Std.int(lastY - bg.y + 4); - if (bg.y + bg.bHeight > FlxG.height) { + if (bg.y + bg.bHeight > FlxG.height && bg.y > FlxG.height*0.5) { + flipped = true; bg.y -= bg.bHeight; for(o in contextMenuOptions) o.y -= bg.bHeight; @@ -102,7 +108,10 @@ class UIContextMenu extends MusicBeatSubstate { super.update(elapsed); - contextCam.scroll.y = CoolUtil.fpsLerp(contextCam.scroll.y, 0, 0.5); + if (FlxG.mouse.wheel != 0.0) + scroll = FlxMath.bound(scroll + (FlxG.mouse.wheel * -20.0), !flipped ? 0.0 : -Math.max(bg.bHeight - FlxG.height*0.5, 0.0), flipped ? 0.0 : Math.max(bg.bHeight - FlxG.height*0.5, 0.0)); + + contextCam.scroll.y = CoolUtil.fpsLerp(contextCam.scroll.y, scroll, 0.5); contextCam.alpha = CoolUtil.fpsLerp(contextCam.alpha, 1, 0.25); } diff --git a/source/funkin/editors/ui/UIDropDown.hx b/source/funkin/editors/ui/UIDropDown.hx index 9ce76d04..0dbd1545 100644 --- a/source/funkin/editors/ui/UIDropDown.hx +++ b/source/funkin/editors/ui/UIDropDown.hx @@ -1,37 +1,73 @@ package funkin.editors.ui; +import flixel.util.typeLimit.OneOfTwo; + class UIDropDown extends UISliceSprite { public var dropButton:UIButton; + public var dropIcon:FlxSprite; public var label:UIText; public var index:Int = 0; - public var options:Array<String> = []; - public var subTexts:Array<String> = []; + public var options:Array<String>; + public var items:Array<DropDownItem>; + public var key(get, never):String; + inline function get_key():String { + return items[index].label; + } + public var value(get, never):Dynamic; + inline function get_value():Dynamic { + return items[index].value; + } public var onChange:Int->Void; var curMenu:UIContextMenu = null; - public function new(x:Float, y:Float, width:Int = 320, height:Int = 32, options:Array<String>, index:Int = 0, ?subTexts:Array<String>) { + public static function getItems(options:Array<String>):Array<DropDownItem> { + return [for(i=>o in options) {label: o, value: i}]; + } + + public function new(x:Float, y:Float, width:Int = 320, height:Int = 32, _options:Array<OneOfTwo<DropDownItem, String>>, index:Int = 0) { super(x, y, width - height, height, 'editors/ui/inputbox'); // using same sprite cause fuck you - this.options = options; + var _items:Array<DropDownItem> = []; + + if(_options.length > 0) { + if(_options[0] is String) { + _items = getItems(_options); + } else { + _items = cast _options; + } + } + + this.items = _items; + this.options = [for(o in _items) o.label]; // backwards compatibility this.index = index; - if (subTexts != null) - this.subTexts = subTexts; - cursor = BUTTON; + cursor = CLICK; - label = new UIText(0, 0, width - height, options[index]); + label = new UIText(0, 0, width - height, items[index].label); members.push(label); - dropButton = new UIButton(0, 0, "V", null, height, height); + dropButton = new UIButton(0, 0, null, null, height, height); members.push(dropButton); + + dropIcon = new FlxSprite(x-(10+16), y+8).loadGraphic(Paths.image("editors/ui/dropdown-button")); + dropIcon.antialiasing = false; + members.push(dropIcon); + } + + public static function indexOfItemValue(items:Array<DropDownItem>, value:Dynamic):Int { + return [for(o in items) o.value].indexOf(value); + } + + public static function indexOfItemLabel(items:Array<DropDownItem>, str:String):Int { + return [for(o in items) o.label].indexOf(str); } public function setOption(newIndex:Int) { if (index != (index = newIndex)) { - label.text = options[index]; + label.text = items[index].label; if (onChange != null) onChange(index); } @@ -40,29 +76,30 @@ class UIDropDown extends UISliceSprite { public override function update(elapsed:Float) { var opened = curMenu.contextMenuOpened(); framesOffset = (opened || (hovered && FlxG.mouse.pressed)) ? 18 : (hovered ? 9 : 0); - if (FlxG.mouse.justReleased && (hovered || dropButton.hovered)) { - if (opened) - UIState.state.curContextMenu.preventOutOfBoxClickDeletion(); - else - openContextMenu(); - } + if (FlxG.mouse.justReleased && (hovered || dropButton.hovered) && !opened) + openContextMenu(); super.update(elapsed); label.follow(this, 4, Std.int((bHeight - label.height) / 2)); dropButton.follow(this, bWidth - bHeight, 0); + dropIcon.follow(dropButton, (dropButton.bWidth/2)-(dropIcon.width/2), (dropButton.bHeight/2)-(dropIcon.height/2)); } public function openContextMenu() { var screenPos = getScreenPosition(null, __lastDrawCameras[0] == null ? FlxG.camera : __lastDrawCameras[0]); curMenu = UIState.state.openContextMenu([ - for(k=>o in options) { + for(k=>o in items) { icon: (k == index) ? 1 : 0, - label: o, - keybindText: subTexts[k] + label: o.label } ], function(_, i, _) { setOption(i); }, __lastDrawCameras[0].x + screenPos.x, __lastDrawCameras[0].y + screenPos.y + bHeight); } +} + +typedef DropDownItem = { + var label:String; + var value:Dynamic; } \ No newline at end of file diff --git a/source/funkin/editors/ui/UIFileExplorer.hx b/source/funkin/editors/ui/UIFileExplorer.hx index 60cfba0e..4f75fd6e 100644 --- a/source/funkin/editors/ui/UIFileExplorer.hx +++ b/source/funkin/editors/ui/UIFileExplorer.hx @@ -10,31 +10,24 @@ class UIFileExplorer extends UISliceSprite { public var deleteButton:UIButton; public var deleteIcon:FlxSprite; - public var filePath:String = null; public var file:Bytes = null; - public var onFile:Bytes->Void; - - public var uiElement:UISprite; + public var filePath:String = null; + public var onFile:(String, Bytes)->Void; + public var uiElement:FlxSprite; + public var fileType:String = "txt"; - public function new(x:Float, y:Float, ?w:Int, ?h:Int, fileType:String = "txt", ?onFile:Bytes->Void) { + public function new(x:Float, y:Float, ?w:Int, ?h:Int, fileType:String = "txt", ?onFile:(String, Bytes)->Void) { super(x, y, (w != null ? w : 320), (h != null ? h : 58), 'editors/ui/inputbox'); this.fileType = fileType; if (onFile != null) this.onFile = onFile; - uploadButton = new UIButton(x + 8, y+ 8, "", function () { - if (!selectable) return; + uploadButton = new UIButton(x + 8, y+ 8, null, function () { var fileDialog = new FileDialog(); - fileDialog.onOpenFile.add(function(res, path) { - filePath = path; - file = cast res; - deleteButton.visible = deleteButton.selectable = deleteIcon.visible = !(uploadButton.visible = uploadButton.selectable = false); - - if (this.onFile != null) this.onFile(file); - }); - fileDialog.open(this.fileType); + fileDialog.onSelect.add(loadFile); + fileDialog.browse(OPEN, this.fileType); }, bWidth - 16, bHeight - 16); members.push(uploadButton); @@ -42,13 +35,13 @@ class UIFileExplorer extends UISliceSprite { uploadIcon.antialiasing = false; uploadButton.members.push(uploadIcon); - deleteButton = new UIButton(x + bWidth - (bHeight - 16) - 8, y + 8, "", removeFile, bHeight - 16, bHeight - 16); + deleteButton = new UIButton(x + bWidth - (bHeight - 16) - 8, y + 8, null, removeFile, bHeight - 16, bHeight - 16); deleteButton.color = 0xFFFF0000; members.push(deleteButton); deleteIcon = new FlxSprite(deleteButton.x + ((bHeight - 16)/2) - 8, deleteButton.y + ((bHeight - 16)/2) - 8).loadGraphic(Paths.image('editors/delete-button')); deleteIcon.antialiasing = false; - members.push(deleteIcon); + deleteButton.members.push(deleteIcon); deleteButton.visible = deleteButton.selectable = deleteIcon.visible = false; } @@ -68,6 +61,13 @@ class UIFileExplorer extends UISliceSprite { } } + public function loadFile(path:String) { + file = cast sys.io.File.getBytes(filePath = path); + deleteButton.visible = deleteButton.selectable = deleteIcon.visible = !(uploadButton.visible = uploadButton.selectable = false); + + if (this.onFile != null) this.onFile(filePath, file); + } + public function removeFile() { if (!selectable) return; if (uiElement != null) { @@ -75,7 +75,7 @@ class UIFileExplorer extends UISliceSprite { uiElement.destroy(); } - file = null; + file = null; onFile(null, null); MemoryUtil.clearMajor(); deleteButton.visible = deleteButton.selectable = deleteIcon.visible = !(uploadButton.visible = uploadButton.selectable = true); diff --git a/source/funkin/editors/ui/UIIconColorPicker.hx b/source/funkin/editors/ui/UIIconColorPicker.hx new file mode 100644 index 00000000..a59dc107 --- /dev/null +++ b/source/funkin/editors/ui/UIIconColorPicker.hx @@ -0,0 +1,73 @@ +package funkin.editors.ui; + +import openfl.display.BitmapData; +import flixel.util.FlxColor; + +using funkin.backend.utils.BitmapUtil; + +class UIIconColorPicker extends UISliceSprite { + public var colorWheel:UIColorwheel; + + public var iconSprite:FlxSprite; + public var iconTextBox:UITextBox; + + public var pickerButton:UIButton; + public var pickerIcon:FlxSprite; + + public function new(x:Float, y:Float, icon:String, antialaising:Bool, colorWheel:UIColorwheel) { + super(x, y, 12+125+12, 4+125+4+32+12, 'editors/ui/inputbox'); + + this.colorWheel = colorWheel; + + iconTextBox = new UITextBox(x+12, y+4+125+4, icon, 125); + iconTextBox.onChange = (newIcon:String) -> {updateIcon(newIcon);} + members.push(iconTextBox); + + members.push(iconSprite = new FlxSprite()); + iconSprite.antialiasing = antialaising; + + pickerButton = new UIButton(x+12+125-26, y+12, null, colorPick, 26, 26); + pickerButton.frames = Paths.getFrames('editors/ui/inputbox'); + members.push(pickerButton); + + pickerIcon = new FlxSprite(pickerButton.x+7, pickerButton.y+7).loadGraphic(Paths.image("editors/ui/picker")); + members.push(pickerIcon); + + updateIcon(icon); + } + + var __path:String = null; + public function updateIcon(icon:String) { + if (iconSprite.animation.exists(icon)) return; + @:privateAccess iconSprite.animation.clearAnimations(); + + var path:String = Paths.image('icons/$icon'); + if (!Assets.exists(path)) path = Paths.image('icons/face'); + + iconSprite.loadGraphic(__path = path, true, 150, 150); + iconSprite.animation.add(icon, [0], 0, false); + iconSprite.animation.play(icon); + + iconSprite.scale.set(125/150, 125/150); + iconSprite.updateHitbox(); + iconSprite.setPosition(x+12, y+4); + + __cachedColor = null; + } + + var __cachedColor:Null<FlxColor> = null; + public function colorPick() { + if (__cachedColor == null) { + var imageBitmap:BitmapData = Assets.getBitmapData(__path, true, false); + __cachedColor = imageBitmap.getMostPresentColor(); + imageBitmap.dispose(); + } + + if (colorWheel != null) { + colorWheel.hue = __cachedColor.hue; + colorWheel.saturation = __cachedColor.saturation; + colorWheel.brightness = __cachedColor.brightness; + colorWheel.updateWheel(); + } + } +} \ No newline at end of file diff --git a/source/funkin/editors/ui/UIImageExplorer.hx b/source/funkin/editors/ui/UIImageExplorer.hx new file mode 100644 index 00000000..ecf98e19 --- /dev/null +++ b/source/funkin/editors/ui/UIImageExplorer.hx @@ -0,0 +1,379 @@ +package funkin.editors.ui; + +import flixel.util.typeLimit.OneOfTwo; +import flixel.text.FlxText.FlxTextFormat; +import flixel.text.FlxText.FlxTextFormatMarkerPair; +import flxanimate.data.SpriteMapData.AnimateAtlas; +import flixel.graphics.frames.FlxFramesCollection; +import haxe.Json; +import haxe.io.Bytes; +import haxe.io.Path; +import openfl.display.BitmapData; +import sys.FileSystem; +import sys.io.File; + +using StringTools; +using funkin.backend.utils.BitmapUtil; +using flixel.util.FlxSpriteUtil; + +typedef ImageSaveData = { + var imageName:String; + var directory:String; + var isAtlas:Bool; + var imageFiles:Map<String, OneOfTwo<String, Bytes>>; +} + +// TODO: make this limited if on web +class UIImageExplorer extends UIFileExplorer { + private var allowAtlases:Bool = true; + private var allowDirectories:Bool = true; + + public var directoryButton:UIButton; + public var directoryIcon:UIText; + + public var directoryBG:UISliceSprite; + public var directoryTextBoxLabel:UIText; + public var directoryTextBox:UITextBox; + + public function new(x:Float, y:Float, image:String, ?w:Int, ?h:Int, ?onFile:(String, Bytes)->Void, ?directory:String = "images") { + super(x, y, w, h, "png, jpg", function (filePath, file) { + if (filePath != null && file != null) uploadImage(filePath, file); + if (onFile != null) onFile(filePath, file); + }); + + deleteButton.bWidth = 26; + deleteButton.bHeight = 26; + + directoryButton = new UIButton(x + bWidth - (bHeight - 16) - 8, y + 8, null, () -> {directoryBG.visible = !directoryBG.visible;}, 26, 26); + members.push(directoryButton); + + directoryIcon = new UIText(directoryButton.x, directoryButton.y, 0, "/", 14); + members.push(directoryIcon); + + directoryBG = new UISliceSprite(0, 0, 210+16, 8+12+4+22+8, 'editors/ui/inputbox'); + directoryBG.alpha = 0.9; directoryBG.members.push(directoryTextBoxLabel = new UIText(8, 6, 0, 'Directory ($directory/)', 12)); + + directoryTextBox = new UITextBox(8, 8+12+4, "", 210, 22, false, true); + directoryBG.members.push(directoryTextBox); + + if (image != null) { + var fullImagePath:String = '${Path.normalize(Sys.getCwd())}/${Paths.image(image)}'.replace('/', '\\'); + var noExt = Path.withoutExtension(fullImagePath); + if (FileSystem.exists('$noExt\\spritemap1.png')) + fullImagePath = '$noExt\\spritemap1.png'; + + if (FileSystem.exists(fullImagePath)) + loadFile(fullImagePath); + } + + allowDirectories = CoolUtil.isMapEmpty(imageFiles); __firstLoad = false; + directoryButton.visible = directoryButton.selectable = directoryIcon.visible = directoryBG.visible = false; + } + + public var isAtlas:Bool = false; + + static var ANIMATE_ATLAS_REGEX = ~/^(?:Animation\.json|spritemap(?:\d+)?\.json)$/i; // no .zip atlases tho + static var SPRITEMAP_JSON_REGEX = ~/^(?:spritemap(?:\d+)?\.json)$/i; + static var SPRITEMAP_PNG_REGEX = ~/^(?:spritemap(?:\d+)?\.png)$/i; + + public var imageName:String = null; + public var imageFiles:Map<String, OneOfTwo<String, Bytes>> = []; + public var animationList:Array<String> = []; + + public var fileText:UIText; + + public var maxSize:FlxPoint = FlxPoint.get(700, 500); + + @:noCompletion var __firstLoad:Bool = true; + public function uploadImage(filePath:String, file:Bytes) { + __resetData(); + + var imagePath:Dynamic = Path.normalize(filePath); + + var directoryPath:String = Path.directory(imagePath); + var fileName:String = Path.withoutDirectory(imagePath).toLowerCase(); + var files:Array<String> = []; + + // CHECK ATLAS + if(allowAtlases && ANIMATE_ATLAS_REGEX.match(fileName)) { + // check if directory has the other files + files = FileSystem.readDirectory(directoryPath); + var hasAnimationJson:Bool = false; + var hasSpritemapJson:Bool = false; + for(file in files) { + if(file == "Animation.json") + hasAnimationJson = true; + else if(SPRITEMAP_JSON_REGEX.match(file.toLowerCase())) + hasSpritemapJson = true; + + if(hasAnimationJson && hasSpritemapJson) { + isAtlas = true; + break; + } + } + } else if(allowAtlases && Path.extension(fileName) == "png") { + // check if the spritemap json files point to the image + files = FileSystem.readDirectory(directoryPath); + var hasAnimationJson:Bool = false; + var foundSpritemapJson:Bool = false; + for(file in files) { + if(SPRITEMAP_JSON_REGEX.match(file)) { + var content:String = CoolUtil.removeBOM(File.getContent(Path.join([directoryPath, file]))); + var json:AnimateAtlas = Json.parse(content); + if(json.meta.image.toLowerCase() == fileName.toLowerCase()) + foundSpritemapJson = true; + } else if(file == "Animation.json") + hasAnimationJson = true; + + if(hasAnimationJson && foundSpritemapJson) { + isAtlas = true; + break; + } + } + } + + // IF ATLAS FIND SPRITMAPS + var spritemaps:Array<String> = []; + var spritemapImages:Array<String> = []; + if(isAtlas) { + // this doesn't check for gaps in the numbering + for(file in files) + if(SPRITEMAP_JSON_REGEX.match(file)) + spritemaps.push(file); + + spritemaps.sort(Reflect.compare); + for(spritemap in spritemaps) { + var content:String = CoolUtil.removeBOM(File.getContent(Path.join([directoryPath, spritemap]))); + var json:AnimateAtlas = Json.parse(content); + var imageToFind:String = json.meta.image.toLowerCase(); + for(file in files) { + // lowercase since windows does case insensitive stuff, so we use lowercase to match behavior on mac and linux + // and we also store the data from the filesystem instead of the json.meta.image + if(file.toLowerCase() == imageToFind) { + spritemapImages.push(file); + break; + } + } + } + + if(spritemapImages.length == 0) + isAtlas = false; + } + + // GATHER ANIMATIONS/DATA FILES!!! + var frames:FlxFramesCollection = null; + if (isAtlas) { + var dataPath:String = '$directoryPath/Animation.json'.replace('/', '\\'); + + if (FileSystem.exists(dataPath)) { + var dataPathFile:String = File.getContent(dataPath); + animationList = CoolUtil.getAnimsListFromAtlas(cast haxe.Json.parse(dataPathFile)); + + imageFiles.set(Path.withoutDirectory(dataPath), dataPathFile); + } + } else { + var dataPathExt:String = CoolUtil.imageHasFrameData(imagePath); + var dataPath:String = Path.withExtension(imagePath, dataPathExt); + var dataPathFile:String = !isAtlas && dataPathExt != null ? File.getContent(dataPath) : null; + + if (dataPathExt != null) { + frames = CoolUtil.loadFramesFromData(dataPathFile, dataPathExt); + animationList = CoolUtil.getAnimsListFromFrames(frames, dataPathExt); + + imageFiles.set(Path.withoutDirectory(dataPath), dataPathFile); + } + } + + // GATHER INFO!!! + var size:Float = 0; + var image:BitmapData = null; + if (isAtlas && spritemapImages.length > 0) { + var spritemapPath:String = Path.join([directoryPath, spritemapImages[0]]); + + imagePath = Path.normalize(spritemapPath); + directoryPath = Path.directory(imagePath); + fileName = Path.withoutDirectory(imagePath).toLowerCase(); + + for(spritemap in spritemapImages) { + var spritemapPath:String = Path.join([directoryPath, spritemap]); + + var info = FileSystem.stat(spritemapPath); + size += info.size; + + spritemapPath = spritemapPath.replace('/', '\\'); + imageFiles.set(Path.withoutDirectory(spritemapPath), sys.io.File.getBytes(spritemapPath)); + } + + file = cast sys.io.File.getBytes(filePath = spritemapPath); + image = BitmapData.fromBytes(file).crop(); + + } else { + size = file.length; + image = BitmapData.fromBytes(file).crop(); + + imageFiles.set(Path.withoutDirectory(imagePath), file); + } + + // DISPLAY IMAGE!! + uiElement = new FlxSprite().loadGraphic(image); + var imageScale:Float = 1; + if (uiElement.width < Math.min(300, maxSize.x) || uiElement.height < Math.min(200, maxSize.y)) + imageScale = Math.max(Math.min(300, maxSize.x) / uiElement.width, Math.min(200, maxSize.y) / uiElement.height); + else if (uiElement.width > maxSize.x || uiElement.height > maxSize.y) + imageScale = Math.min(maxSize.x/uiElement.width, maxSize.y/uiElement.height); + uiElement.scale.set(imageScale, imageScale); + uiElement.updateHitbox(); + + bWidth = Std.int(uiElement.width)+32; bHeight = Std.int(uiElement.height)+32+deleteButton.bHeight+4; + uiElement.x = x+16; uiElement.y = y+16+deleteButton.bHeight+4; + + uiElement.antialiasing = true; + members.push(uiElement); + + //if (!isAtlas && frames != null) { + // for (frame in frames.frames) { + // var rect = frame.frame; + // uiElement.drawRect(rect.x, rect.y, rect.width, rect.height, 0x00161E87, {thickness: Std.int(1.75/imageScale), color: 0xFF11178C}); + // } + //} + // GENERATE TEXT!!! + imagePath = new Path(imagePath); + imageName = isAtlas ? Path.withoutDirectory(directoryPath) : imagePath.file; + + var message = new StringBuf(); + if (isAtlas) message.add('${imageName}/'); + message.add('${imagePath.file}.${imagePath.ext}'); + message.add(' (${CoolUtil.getSizeString(size)}'); + + if (animationList.length > 0) { + if(animationList.length == 1) message.add(', ${animationList.length} ${isAtlas ? "Symbol" : "Animation"} Found'); + else message.add(', ${animationList.length} ${isAtlas ? "Symbol" : "Animation"}s Found'); + } else message.add(', #NO ${isAtlas ? "Symbol" : "Animation"}s Found#'); + + if (isAtlas) { + if(spritemaps.length == 1) message.add(', ${spritemaps.length} Spritemap Found'); + else message.add(', ${spritemaps.length} Spritemaps Found'); + } + message.add(')'); + message.add(isAtlas ? " - Atlas" : " - Spritemap"); + + fileText = new UIText(x+20, y+16, bWidth-20-deleteButton.bWidth-16, ""); + fileText.applyMarkup(message.toString(), [new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "#")]); + members.push(fileText); + + deleteButton.x = x + bWidth - deleteButton.bWidth - 16; + deleteButton.y = y + 12; + + deleteIcon.x = deleteButton.x + deleteButton.bWidth/2 - 8; + deleteIcon.y = deleteButton.y + deleteButton.bHeight/2 - 8; + + directoryButton.x = deleteButton.x - deleteButton.bWidth - 12; + directoryButton.y = y + 12; + + directoryIcon.x = directoryButton.x + directoryButton.bWidth/2 - (directoryIcon.width/2); + directoryIcon.y = directoryButton.y + directoryButton.bHeight/2 - (directoryIcon.height/2); + + directoryBG.x = x + bWidth - directoryBG.bWidth - 16; + directoryBG.y = directoryButton.y + directoryButton.bHeight + 8; + + directoryTextBoxLabel.x = directoryBG.x + 8; + directoryTextBoxLabel.y = directoryBG.y + 6; + + directoryTextBox.x = directoryBG.x + 8; + directoryTextBox.y = directoryBG.y + 8+12+4; + + if (!__firstLoad) allowDirectories = true; + + directoryButton.visible = directoryButton.selectable = directoryIcon.visible = allowDirectories; + directoryBG.visible = false; + if (directoryButton.visible) fileText.fieldWidth -= directoryButton.bWidth + 12; + + members.push(directoryBG); __firstLoad = false; + } + + public var saveData:ImageSaveData = null; + public inline function getSaveData():ImageSaveData { + return saveData = { + imageName: this.imageName, + directory: Path.removeTrailingSlashes(directoryTextBox.label.text), + isAtlas: this.isAtlas, + imageFiles: this.imageFiles.copy() + }; + } + + public inline function saveFiles(directory:String, ?onFinishSaving:Void->Void, ?checkExisting:Bool = true) { + UIImageExplorer.saveFilesGlobal(getSaveData(), directory, onFinishSaving, checkExisting); + } + + public static function serializeSaveDataGlobal(data:ImageSaveData):String + return haxe.Serializer.run(data); + + public static function deserializeSaveDataGlobal(data:String):ImageSaveData + return cast haxe.Unserializer.run(data); + + public static function saveFilesGlobal(imageData:ImageSaveData, directory:String, ?onFinishSaving:Void->Void, ?checkExisting:Bool = true) { + directory += '${directory.length > 0 ? '/${imageData.directory}' : ""}'; + if (imageData.isAtlas) directory += '/${imageData.imageName}'; + + var alreadlyExistingFiles:Array<String> = []; + for (name => file in imageData.imageFiles) + if (FileSystem.exists('$directory/$name')) + alreadlyExistingFiles.push('$directory/$name'); + + function deleteExistingFiles() { + for (file in alreadlyExistingFiles) + FileSystem.deleteFile(file); + alreadlyExistingFiles = []; + } + + function acuttalySaveFiles() { + for (name => file in imageData.imageFiles) + if (!alreadlyExistingFiles.contains('$directory/$name')) { + CoolUtil.safeSaveFile('$directory/$name', file); + trace('SAVED: $directory/$name'); + } + + if (onFinishSaving != null) onFinishSaving(); + } + + if (alreadlyExistingFiles.length > 0 && checkExisting) @:privateAccess { + (FlxG.state.subState != null && !FlxG.state._requestSubStateReset ? FlxG.state.subState : FlxG.state).openSubState(new UIWarningSubstate("Alreadly Existing Files!!!", + "The following files alreadly exist: \n\n" + alreadlyExistingFiles.join('\n') + "\n\nIMPORTANT: OVERRIDING CAN NOT BE UNDONE!!!!!!!!", + [ { + label: "Override Files", + color: 0xFFFF0000, + onClick: (_) -> { + deleteExistingFiles(); + acuttalySaveFiles(); + } + }, + { + label: "Use Existing", + onClick: (_) -> {if (onFinishSaving != null) onFinishSaving();} + } + ], false)); + } else acuttalySaveFiles(); + } + + public override function removeFile() { + __resetData(); + bWidth = 320; bHeight = 58; + + members.remove(fileText); + fileText.destroy(); + + members.remove(directoryBG); + directoryButton.visible = directoryButton.selectable = directoryIcon.visible = directoryBG.visible = false; + + super.removeFile(); + } + + @:noCompletion inline function __resetData() { + imageFiles.clear(); isAtlas = false; animationList = []; + } + + public override function destroy() { + maxSize.put(); + super.destroy(); + } +} \ No newline at end of file diff --git a/source/funkin/editors/ui/UINumericStepper.hx b/source/funkin/editors/ui/UINumericStepper.hx index c561846c..ff640414 100644 --- a/source/funkin/editors/ui/UINumericStepper.hx +++ b/source/funkin/editors/ui/UINumericStepper.hx @@ -7,8 +7,8 @@ class UINumericStepper extends UITextBox { public var max:Null<Float> = null; public var step:Float = 1; - public function new(x:Float, y:Float, value:Float = 0, step:Float = 1, precision:Int = 0, ?min:Float, ?max:Float, w:Int = 180, h:Int = 32) { - super(x, y, "", w - h, h, false); + public function new(x:Float, y:Float, value:Float = 0, step:Float = 1, precision:Int = 0, ?min:Float, ?max:Float, w:Int = 180, h:Int = 32, small:Bool = false) { + super(x, y, "", w - h, h, false, small); onChange = __onChange; @@ -31,7 +31,7 @@ class UINumericStepper extends UITextBox { private function set_value(v:Float) { if (min != null && max != null) { - v = FlxMath.bound(v, min, max); + v = CoolUtil.bound(v, min, max); } else if (min != null) { v = Math.max(v, min); } else if (max != null) { diff --git a/source/funkin/editors/ui/UIRadioButton.hx b/source/funkin/editors/ui/UIRadioButton.hx new file mode 100644 index 00000000..9c009ece --- /dev/null +++ b/source/funkin/editors/ui/UIRadioButton.hx @@ -0,0 +1,102 @@ +package funkin.editors.ui; + +import flixel.util.typeLimit.OneOfTwo; + +class UIRadioButton extends UISprite { + public var checked:Bool = false; + public var onChecked:Bool->Void = null; + + public var field:UIText; + public var check:FlxSprite; + + public var parent:OneOfTwo<UIWindow, UISubstateWindow> = null; + public var forID:String = null; + + public function new(x:Float, y:Float, text:String, checked:Bool = false, forID:String = null, w:Int = 0) { + super(x, y); + this.forID = forID; + loadGraphic(Paths.image('editors/ui/radiobutton'), true, 20, 20); + for(frame=>name in ["normal", "hover", "pressed", "selected"]) + animation.add(name, [frame], 0, false); + + this.checked = checked; + this.targetScale = checked ? 1 : 0; + + field = new UIText(x, y, w, text); + check = new FlxSprite().loadGraphicFromSprite(this); + check.animation.play("selected"); + check.scale.set(targetScale, targetScale); + + members.push(check); + members.push(field); + + cursor = CLICK; + + autoAlpha = false; + } + + public var targetScale:Float = 1; + + public override function update(elapsed:Float) { + // ANIMATION HANDLING + animation.play(hovered ? (pressed ? "pressed" : "hover") : "normal"); + + // CHECKMARK HANDLING + if(autoAlpha) + check.alpha = checked ? 1 : 0; + check.scale.x = CoolUtil.fpsLerp(check.scale.x, targetScale, 0.25); + check.scale.y = CoolUtil.fpsLerp(check.scale.y, targetScale, 0.25); + + // POSITION HANDLING + updatePositions(); + + super.update(elapsed); + } + + public inline function updatePositions() { + check.follow(this); + field.follow(this, 25, 0); + } + + public override function draw() { + updatePositions(); + super.draw(); + } + + public override function onHovered() { + super.onHovered(); + if (FlxG.mouse.justReleased) { + // clicked + checked = !checked; + check.scale.set(1.25, 1.25); + targetScale = checked ? 1 : 0; + + if (Options.editorSFX) + CoolUtil.playMenuSFX(checked ? CHECKED : UNCHECKED, 0.5); + + if(parent != null) { + var members = (parent is UIWindow) ? (cast(parent, UIWindow)).members : (cast(parent, UISubstateWindow)).members; + var radios:Array<UIRadioButton> = cast members.filter((o) -> o is UIRadioButton && o != this); + //trace(radios); + for(radio in radios) { + if(radio.forID != null && radio.forID == forID) { + radio.checked = false; + radio.targetScale = 0; + } + } + } + + if (onChecked != null) + onChecked(checked); + } + } + + public override function updateButton() { + __rect.set(x, y, field.width + 30, field.height > height ? field.height : height); + UIState.state.updateRectButtonHandler(this, __rect, onHovered); + } + + public override function toString() { + return "RadioButton(" + field.text + ", " + checked + ", " + forID + ")"; + } +} \ No newline at end of file diff --git a/source/funkin/editors/ui/UIScaleMode.hx b/source/funkin/editors/ui/UIScaleMode.hx new file mode 100644 index 00000000..5d67bc7c --- /dev/null +++ b/source/funkin/editors/ui/UIScaleMode.hx @@ -0,0 +1,55 @@ +package funkin.editors.ui; + +import flixel.system.scaleModes.StageSizeScaleMode; + +class UIScaleMode extends StageSizeScaleMode { + override public function onMeasure(Width:Int, Height:Int):Void { + if (Width < FlxG.initialWidth || Height < FlxG.initialHeight) { + @:privateAccess { + FlxG.width = FlxG.initialWidth; + FlxG.height = FlxG.initialHeight; + } + + updateGameSize(Width, Height); + updateDeviceSize(Width, Height); + updateScaleOffset(); + updateGamePosition(); + } else { + super.onMeasure(Width, Height); + + for (camera in FlxG.cameras.list) { + camera.width = Width; + camera.height = Height; + } + } + } + + override function updateGameSize(Width:Int, Height:Int):Void + { + var ratio:Float = FlxG.width / FlxG.height; + var realRatio:Float = Width / Height; + + var scaleY:Bool = realRatio < ratio; + + if (scaleY) + { + gameSize.x = Width; + gameSize.y = Math.floor(gameSize.x / ratio); + } + else + { + gameSize.y = Height; + gameSize.x = Math.floor(gameSize.y * ratio); + } + + @:privateAccess { + for(camera in FlxG.cameras.list) { + camera.width = FlxG.initialWidth; + camera.height = FlxG.initialHeight; + } + + FlxG.width = FlxG.initialWidth; + FlxG.height = FlxG.initialHeight; + } + } +} \ No newline at end of file diff --git a/source/funkin/editors/ui/UIScrollBar.hx b/source/funkin/editors/ui/UIScrollBar.hx index 55514e09..b94f4cd5 100644 --- a/source/funkin/editors/ui/UIScrollBar.hx +++ b/source/funkin/editors/ui/UIScrollBar.hx @@ -24,7 +24,7 @@ class UIScrollBar extends UISprite { this.length = length; thumb = new UISliceSprite(0, 0, w, h, 'editors/ui/scrollbar'); - thumb.cursor = BUTTON; + thumb.cursor = CLICK; members.push(thumb); thumbIcon = new FlxSprite(0, 0, Paths.image('editors/ui/scrollbar-icon')); @@ -48,7 +48,7 @@ class UIScrollBar extends UISprite { if (isScrolling) { thumb.framesOffset = 18; var mousePos = FlxG.mouse.getScreenPosition(__lastDrawCameras[0], FlxPoint.get()); - var yPos = FlxMath.bound(FlxMath.remapToRange(mousePos.y, y, y+height, -(size/2), length + size), 0, length); + var yPos = CoolUtil.bound(FlxMath.remapToRange(mousePos.y, y, y+height, -(size/2), length + size), 0, length); if (yPos >= 0 && yPos < length) { value = yPos; if (onChange != null) diff --git a/source/funkin/editors/ui/UISliceSprite.hx b/source/funkin/editors/ui/UISliceSprite.hx index 09226205..15008dbf 100644 --- a/source/funkin/editors/ui/UISliceSprite.hx +++ b/source/funkin/editors/ui/UISliceSprite.hx @@ -5,7 +5,7 @@ import flixel.graphics.frames.FlxFrame; class UISliceSprite extends UISprite { public var bWidth:Int = 120; public var bHeight:Int = 20; - public var framesOffset:Int = 0; + public var framesOffset(default, set):Int = 0; public var incorporeal:Bool = false; @@ -14,14 +14,12 @@ class UISliceSprite extends UISprite { frames = Paths.getFrames(path); resize(w, h); + calculateFrames(); } public override function updateButton() { if (incorporeal) return; - __rect.x = x; - __rect.y = y; - __rect.width = bWidth; - __rect.height = bHeight; + __rect.set(x, y, bWidth, bHeight); UIState.state.updateRectButtonHandler(this, __rect, onHovered); } @@ -30,112 +28,198 @@ class UISliceSprite extends UISprite { bHeight = h; } + public var topAlpha:Null<Float> = null; + public var middleAlpha:Null<Float> = null; + public var bottomAlpha:Null<Float> = null; + public var drawTop:Bool = true; public var drawMiddle:Bool = true; public var drawBottom:Bool = true; + var topleft:FlxFrame = null; + var top:FlxFrame = null; + var topright:FlxFrame = null; + var middleleft:FlxFrame = null; + var middle:FlxFrame = null; + var middleright:FlxFrame = null; + var bottomleft:FlxFrame = null; + var bottom:FlxFrame = null; + var bottomright:FlxFrame = null; + + public var topHeight:Int = 0; + public var bottomHeight:Int = 0; + public var leftWidth:Int = 0; + public var rightWidth:Int = 0; + + override function set_frames(val) { + super.set_frames(val); + calculateFrames(); + return val; + } + + function set_framesOffset(value:Int) { + if(value != framesOffset) { + framesOffset = value; + calculateFrames(); + } + return value; + } + + function calculateFrames() { + if(frames == null) return; + topleft = frames.frames[framesOffset]; + top = frames.frames[framesOffset + 1]; + topright = frames.frames[framesOffset + 2]; + middleleft = frames.frames[framesOffset + 3]; + middle = frames.frames[framesOffset + 4]; + middleright = frames.frames[framesOffset + 5]; + bottomleft = frames.frames[framesOffset + 6]; + bottom = frames.frames[framesOffset + 7]; + bottomright = frames.frames[framesOffset + 8]; + + leftWidth = Std.int(MathUtil.maxSmart(topleft.frame.width, middleleft.frame.width, bottomleft.frame.width)); + rightWidth = Std.int(MathUtil.maxSmart(topright.frame.width, middleright.frame.width, bottomright.frame.width)); + topHeight = Std.int(MathUtil.maxSmart(topleft.frame.height, top.frame.height, topright.frame.height)); + bottomHeight = Std.int(MathUtil.maxSmart(topleft.frame.height, top.frame.height, topright.frame.height)); + } + + @:pure private static inline function getFixedSize(value:Float, total:Float):Float { + return value * Math.min(total / (value * 2), 1); + } + public override function draw() @:privateAccess { + var lastPixelPerfect:Bool = cameras[0] != null ? cameras[0].pixelPerfectRender : false; + if (cameras[0] != null) cameras[0].pixelPerfectRender = false; + + var topLeft = (flipX ? (flipY ? bottomright : topright) : (flipY ? bottomleft : topleft)); + var topMiddle = (flipY ? bottom : top); + var topRight = (flipX ? (flipY ? bottomleft : topleft) : (flipY ? bottomright : topright)); + var middleLeft = (flipX ? middleright : middleleft); + var middleRight = (flipX ? middleleft : middleright); + var bottomLeft = (flipX ? (flipY ? topright : bottomright) : (flipY ? topleft : bottomleft)); + var bottomMiddle = (flipY ? top : bottom); + var bottomRight = (flipX ? (flipY ? topleft : bottomleft) : (flipY ? topright : bottomright)); + var x:Float = this.x; var y:Float = this.y; if (visible && !(bWidth == 0 || bHeight == 0)) { - var topleft:FlxFrame = frames.frames[framesOffset]; - var top:FlxFrame = frames.frames[framesOffset + 1]; - var topright:FlxFrame = frames.frames[framesOffset + 2]; - var middleleft:FlxFrame = frames.frames[framesOffset + 3]; - var middle:FlxFrame = frames.frames[framesOffset + 4]; - var middleright:FlxFrame = frames.frames[framesOffset + 5]; - var bottomleft:FlxFrame = frames.frames[framesOffset + 6]; - var bottom:FlxFrame = frames.frames[framesOffset + 7]; - var bottomright:FlxFrame = frames.frames[framesOffset + 8]; + var topLeftWidth:Float = getFixedSize(topLeft.frame.width, bWidth); + var topLeftHeight:Float = getFixedSize(topLeft.frame.height, bHeight); + //var topMiddleWidth:Float = getFixedSize(topMiddle.frame.width, bWidth); + var topMiddleHeight:Float = getFixedSize(topMiddle.frame.height, bHeight); + var topRightWidth:Float = getFixedSize(topRight.frame.width, bWidth); + var topRightHeight:Float = getFixedSize(topRight.frame.height, bHeight); + var middleLeftWidth:Float = getFixedSize(middleLeft.frame.width, bWidth); + //var middleLeftHeight:Float = getFixedSize(middleLeft.frame.height, bHeight); + //var middleMiddleWidth:Float = getFixedSize(middle.frame.width, bWidth); + //var middleMiddleHeight:Float = getFixedSize(middle.frame.height, bHeight); + var middleRightWidth:Float = getFixedSize(middleRight.frame.width, bWidth); + //var middleRightHeight:Float = getFixedSize(middleRight.frame.height, bHeight); + var bottomLeftWidth:Float = getFixedSize(bottomLeft.frame.width, bWidth); + var bottomLeftHeight:Float = getFixedSize(bottomLeft.frame.height, bHeight); + //var bottomMiddleWidth:Float = getFixedSize(bottomMiddle.frame.width, bWidth); + var bottomMiddleHeight:Float = getFixedSize(bottomMiddle.frame.height, bHeight); + var bottomRightWidth:Float = getFixedSize(bottomRight.frame.width, bWidth); + var bottomRightHeight:Float = getFixedSize(bottomRight.frame.height, bHeight); + var oldAlpha = alpha; // TOP if (drawTop) { // TOP LEFT - frame = topleft; + if(topAlpha != null) alpha = topAlpha; + frame = topLeft; setPosition(x, y); __setSize( - topleft.frame.width * Math.min(bWidth/(topleft.frame.width*2), 1), - topleft.frame.height * Math.min(bHeight/(topleft.frame.height*2), 1) + topLeftWidth, + topLeftHeight ); super.drawSuper(); // TOP - if (bWidth > topleft.frame.width + topright.frame.width) { - frame = top; - setPosition(x + topleft.frame.width, y); - __setSize(bWidth - topleft.frame.width - topright.frame.width, top.frame.height * Math.min(bHeight/(top.frame.height*2), 1)); + if (bWidth > topLeft.frame.width + topRight.frame.width) { + frame = topMiddle; + setPosition(x + topLeft.frame.width, y); + __setSize( + bWidth - topLeft.frame.width - topRight.frame.width, + topMiddleHeight + ); super.drawSuper(); } // TOP RIGHT - setPosition(x + bWidth - (topright.frame.width * Math.min(bWidth/(topright.frame.width*2), 1)), y); - frame = topright; + setPosition(x + bWidth - topRightWidth, y); + frame = topRight; __setSize( - topright.frame.width * Math.min(bWidth/(topright.frame.width*2), 1), - topright.frame.height * Math.min(bHeight/(topright.frame.height*2), 1) + topRightWidth, + topRightHeight ); super.drawSuper(); } // MIDDLE - if (drawMiddle && bHeight > top.frame.height + bottom.frame.height) { - var middleHeight:Float = bHeight - (topleft.frame.height * Math.min(bHeight/(topleft.frame.height*2), 1)) - - bottomleft.frame.height * Math.min(bHeight/(bottomleft.frame.height*2), 1); + if (drawMiddle && bHeight > topMiddle.frame.height + bottomMiddle.frame.height) { + if(middleAlpha != null) alpha = middleAlpha; + var middleHeight:Float = bHeight - topLeftHeight - bottomLeftHeight; // MIDDLE LEFT - frame = middleleft; - setPosition(x, y + top.frame.height); - __setSize(middleleft.frame.width * Math.min(bWidth/(middleleft.frame.width*2), 1), middleHeight); + frame = middleLeft; + setPosition(x, y + topMiddle.frame.height); + __setSize(middleLeftWidth, middleHeight); super.drawSuper(); - if (bWidth > (middleleft.frame.width * Math.min(bWidth/(middleleft.frame.width*2), 1)) + middleright.frame.width) { + if (bWidth > (middleLeftWidth) + middleRight.frame.width) { // MIDDLE frame = middle; - setPosition(x + topleft.frame.width, y + top.frame.height); - __setSize(bWidth - middleleft.frame.width - middleright.frame.width, middleHeight); + setPosition(x + topLeft.frame.width, y + topMiddle.frame.height); + __setSize(bWidth - middleLeft.frame.width - middleRight.frame.width, middleHeight); super.drawSuper(); } // MIDDLE RIGHT - frame = middleright; - setPosition(x + bWidth - (topright.frame.width * Math.min(bWidth/(topright.frame.width*2), 1)), y + top.frame.height); - __setSize(middleright.frame.width * Math.min(bWidth/(middleright.frame.width*2), 1), middleHeight); + frame = middleRight; + setPosition(x + bWidth - (topRightWidth), y + topMiddle.frame.height); + __setSize(middleRightWidth, middleHeight); super.drawSuper(); } // BOTTOM if (drawBottom) { + if(bottomAlpha != null) alpha = bottomAlpha; // BOTTOM LEFT - frame = bottomleft; - setPosition(x, y + bHeight - (bottomleft.frame.height * Math.min(bHeight/(bottomleft.frame.height*2), 1))); + frame = bottomLeft; + setPosition(x, y + bHeight - (bottomLeftHeight)); __setSize( - bottomleft.frame.width * Math.min(bWidth/(bottomleft.frame.width*2), 1), - bottomleft.frame.height * Math.min(bHeight/(bottomleft.frame.height*2), 1) + bottomLeftWidth, + bottomLeftHeight ); super.drawSuper(); - if (bWidth > bottomleft.frame.width + bottomright.frame.width) { + if (bWidth > bottomLeft.frame.width + bottomRight.frame.width) { // BOTTOM - frame = bottom; - setPosition(x + bottomleft.frame.width, y + bHeight - (bottom.frame.height * Math.min(bHeight/(bottom.frame.height*2), 1))); - __setSize(bWidth - bottomleft.frame.width - bottomright.frame.width, bottom.frame.height * Math.min(bHeight/(bottom.frame.height*2), 1)); + frame = bottomMiddle; + setPosition(x + bottomLeft.frame.width, y + bHeight - (bottomMiddleHeight)); + __setSize(bWidth - bottomLeft.frame.width - bottomRight.frame.width, bottomMiddleHeight); super.drawSuper(); } // BOTTOM RIGHT - frame = bottomright; + frame = bottomRight; + setPosition( - x + bWidth - (bottomright.frame.width * Math.min(bWidth/(bottomright.frame.width*2), 1)), - y + bHeight - (bottomright.frame.height * Math.min(bHeight/(bottomright.frame.height*2), 1)) + x + bWidth - (bottomRightWidth), + y + bHeight - (bottomRightHeight) ); __setSize( - bottomright.frame.width * Math.min(bWidth/(bottomright.frame.width*2), 1), - bottomright.frame.height * Math.min(bHeight/(bottomright.frame.height*2), 1) + bottomRightWidth, + bottomRightHeight ); super.drawSuper(); + } + alpha = oldAlpha; } + if (cameras[0] != null) cameras[0].pixelPerfectRender = lastPixelPerfect; setPosition(x, y); super.drawMembers(); diff --git a/source/funkin/editors/ui/UISlider.hx b/source/funkin/editors/ui/UISlider.hx index 20fc219f..659f8a2c 100644 --- a/source/funkin/editors/ui/UISlider.hx +++ b/source/funkin/editors/ui/UISlider.hx @@ -46,7 +46,7 @@ class UISlider extends UISprite { makeGraphic(barWidth, 12, 0x00000000, true); this.drawRoundRect(0, 0, barWidth, 12, 8, 6, 0xFFC0C0C0); this.drawRoundRect(1, 1, barWidth-2, 12-2, 8, 5, 0xFF140013); - cursor = BUTTON; + cursor = CLICK; progressbar = new UISprite(centered ? barWidth/2 : 0); progressbar.makeGraphic(barWidth, 8, 0x00000000, true); @@ -64,7 +64,7 @@ class UISlider extends UISprite { var selectableBar:UISprite = new UISprite(x,y); selectableBar.loadGraphic(Paths.image("editors/ui/slider")); selectableBar.antialiasing = true; - selectableBar.cursor = BUTTON; + selectableBar.cursor = CLICK; members.push(selectableBar); switch (i) { @@ -75,12 +75,12 @@ class UISlider extends UISprite { selectableHitbox = new UISprite(x,y); selectableHitbox.makeSolid(barWidth, 18, -1); - selectableHitbox.cursor = BUTTON; + selectableHitbox.cursor = CLICK; selectableHitbox.alpha = 0; members.push(selectableHitbox); valueStepper = new UINumericStepper(x - 64 - 64, y, 1, 0.01, 2, segments[0].start, segments[segments.length-1].end, 0, 16); - valueStepper.antialiasing = valueStepper.label.antialiasing = true; + valueStepper.antialiasing = true; valueStepper.onChange = function (text:String) { @:privateAccess valueStepper.__onChange(text); this.value = valueStepper.value; @@ -103,12 +103,14 @@ class UISlider extends UISprite { valueStepper.bWidth = Std.int(FlxMath.lerp(valueStepper.bWidth, __stepperWidth, 1/2.25)); valueStepper.follow(this, -startText.width-10 - valueStepper.bWidth - 4, (height-valueStepper.bHeight)/2); + //@:privateAccess valueStepper.__framesDirty = true; + var lastBarProgress:Float = __barProgress; if (selectableHitbox.hovered && FlxG.mouse.justPressed) isSliding = true; if (isSliding) { var mousePos = FlxG.mouse.getScreenPosition(__lastDrawCameras[0], FlxPoint.get()); - __barProgress = FlxMath.bound(mousePos.x-x, 0, barWidth)/barWidth; + __barProgress = CoolUtil.bound(mousePos.x-x, 0, barWidth)/barWidth; mousePos.put(); if (FlxG.mouse.justReleased) isSliding = false; } @@ -120,7 +122,7 @@ class UISlider extends UISprite { visualProgress = FlxMath.lerp(visualProgress, __barProgress, 1/2.25); progressbar.follow(this, progressCentered ? barWidth/2 : 0, (height-progressbar.height)/2); - progressbar.scale.x = FlxMath.bound(visualProgress-(progressCentered?0.5:0),-1,1); + progressbar.scale.x = CoolUtil.bound(visualProgress-(progressCentered?0.5:0),-1,1); progressbar.colorTransform.color = FlxColor.interpolate(progressbar.colorTransform.color, selectableHitbox.hovered ? 0xFF7F00BF : 0xFF67009B, 1/14); selectableBar.follow(this, (visualProgress * barWidth) - (selectableBar.width/2), (height-selectableBar.height)/2); selectableBarHighlight.follow(selectableBar); diff --git a/source/funkin/editors/ui/UISoftcodedWindow.hx b/source/funkin/editors/ui/UISoftcodedWindow.hx new file mode 100644 index 00000000..e791f883 --- /dev/null +++ b/source/funkin/editors/ui/UISoftcodedWindow.hx @@ -0,0 +1,340 @@ +package funkin.editors.ui; + +import flixel.math.FlxPoint; +import funkin.backend.chart.ChartData.ChartMetaData; +import funkin.backend.scripting.HScript; +import funkin.backend.scripting.Script; +import funkin.editors.extra.PropertyButton; +import funkin.editors.stage.elements.StageElementButton; +import funkin.editors.ui.UIDropDown; +import haxe.xml.Access; +import hscript.*; +import hscript.Expr.Error; +import hscript.Interp; +import hscript.Parser; +import haxe.io.Path; + +using StringTools; + +class UISoftcodedWindow extends UISubstateWindow { + public var saveButton:UIButton; + public var closeButton:UIButton; + + var elementMap:Map<String, UISprite> = []; + + var xmlPath:String; + var filename:String; + var customVariables:Map<String, Dynamic>; + + public function new(xmlPath:String, customVariables:Map<String, Dynamic> = null) { + super(); + this.xmlPath = xmlPath; + this.filename = Path.withoutDirectory(xmlPath); + this.customVariables = customVariables; + } + + var parser = HScript.initParser(); + var interp = new Interp(); + + function set(name:String, value:Dynamic) { + interp.variables.set(name, value); + } + + function get(name:String):Dynamic { + return interp.variables.get(name); + } + function varExists(name:String):Bool { + return interp.variables.exists(name); + } + + function execString(input:String):String { + if(input == null) return null; + if(input.indexOf("${") == -1) return input; + var text = ""; + while(input.length > 0) { + var idx = input.indexOf("${"); + if(idx == -1) { + text += input; + break; + } + text += input.substring(0, idx); + var ei = input.indexOf("}", idx); + if(ei == -1) { + text += input.substring(idx); + break; + } + var block = input.substring(idx+2, ei); + text += exec(block); + input = input.substring(ei + 1); + } + return text; + } + + function execDefault(code:String, defaultValue:Dynamic):Dynamic { + return CoolUtil.getDefault(exec(code), defaultValue); + } + + function execAtt(el:Access, key:String, ?defaultValue:Dynamic):Dynamic { + return execDefault(el.getAtt(key), defaultValue); + } + + function exec(code:String):Dynamic { + try { + if (code != null && code.trim() != "") + return interp.execute(parser.parseString(code, filename)); + } catch(e:Error) { + _errorHandler(e); + } catch(e) { + _errorHandler(new Error(ECustom(e.toString()), 0, 0, filename, 0)); + } + return null; + } + + function _errorHandler(error:Error) { + var fileName = error.origin; + var fn = '$fileName:${error.line}: '; + var err = error.toString(); + if (err.startsWith(fn)) err = err.substr(fn.length); + + Logs.traceColored([ + Logs.logText(fn, GREEN), + Logs.logText(err, RED) + ], ERROR); + } + + public override function create() { + var layout = new Access(Xml.parse(Assets.getText(Paths.xml("editors/" + xmlPath))).firstElement()); + + for(k=>e in Script.getDefaultVariables()) { + set(k, e); + } + set("self", this); + set("hasSaveButton", true); + set("hasCloseButton", true); + set("getRadioButtons", function(forID:String) { + var radios:Array<UIRadioButton> = cast members.filter((o) -> o is UIRadioButton); + return cast radios.filter((o) -> o.forID == forID); + }); + for(k=>v in customVariables) { + set(k, v); + } + set("last", null); + + if(varExists("winTitle")) winTitle = get("winTitle"); + if(varExists("winWidth")) winWidth = get("winWidth"); + if(varExists("winHeight")) winHeight = get("winHeight"); + + winTitle = execString(layout.getAtt("title").getDefault(winTitle)); + winWidth = Std.parseInt(layout.getAtt("width")).getDefault(winWidth); + winHeight = Std.parseInt(layout.getAtt("height")).getDefault(winHeight); + + super.create(); + + function addLabelOn(ui:UISprite, text:String) + add(new UIText(ui.x, ui.y - 24, 0, text)); + + function getArr(i:Iterator<Access>) { + return [for(e in i) e]; + } + + function addLayout(elements:Array<Access>) { + for(el in elements) { + var type = el.name; + + switch(type) { + case "section": + if(execAtt(el, "if", true) != true) + continue; + if(execAtt(el, "unless", false) != false) + continue; + addLayout(getArr(el.elements)); + continue; + case "set": + set(execString(el.getAtt("name")), execAtt(el, "value")); + continue; + case "exec": + exec(el.innerData); + continue; + } + + var name:String = execString(el.getAtt("name")); + var label:String = execString(el.getAtt("label")); + var x:Float = execAtt(el, "x", 0); + var y:Float = execAtt(el, "y", 0); + + var sprite:FlxSprite = switch(type) { + case "image": + var spr = XMLUtil.createSpriteFromXML(el, "", LOOP); + spr.x = x; + spr.y = y; + cast add(spr); + case "solid": + var spr = new FunkinSprite(x, y).makeSolid( + execAtt(el, "width", 100), + execAtt(el, "height", 100), + execAtt(el, "color", 0xFFFFFFFF) + ); + cast add(spr); + case "textbox": + var text:String = execString(el.getAtt("text")); + text = Std.string(execAtt(el, "value", text)); + var textBox = new UITextBox(x, y, text, execAtt(el, "width"), execAtt(el, "height"), execAtt(el, "multiline", false));//(el.getAtt("multiline").getDefault("false") == "true")); + add(textBox); + addLabelOn(textBox, label); + textBox; + case "title": + cast add(new UIText(x, y, execAtt(el, "width", 0), execString(el.getAtt("text")), execAtt(el, "size", 28))); + case "label": + cast add(new UIText(x, y - 24, execAtt(el, "width", 0), execString(el.getAtt("text")), execAtt(el, "size", 15))); + case "text": + cast add(new UIText(x, y, execAtt(el, "width", 0), execString(el.getAtt("text")), execAtt(el, "size", 15))); + case "stepper": + // x:Float, y:Float, value:Float = 0, step:Float = 1, precision:Int = 0, ?min:Float, ?max:Float, w:Int = 180, h:Int = 32 + var stepper = new UINumericStepper( + x, + y, + execAtt(el, "value", 0), + execAtt(el, "step", 1), + execAtt(el, "precision", 0), + execAtt(el, "min"), + execAtt(el, "max"), + execAtt(el, "width"), + execAtt(el, "height") + ); + add(stepper); + addLabelOn(stepper, label); + stepper; + case "checkbox": + var checkbox = new UICheckbox(x, y, execString(el.getAtt("text")), execAtt(el, "value", false)); + add(checkbox); + addLabelOn(checkbox, label); + checkbox; + case "radio": + var radio = new UIRadioButton(x, y, execString(el.getAtt("text")), execAtt(el, "value", false), execString(el.getAtt("for"))); + radio.parent = this; + add(radio); + addLabelOn(radio, label); + radio; + case "slider": + var slider = new UISlider(x, y, execAtt(el, "width"), execAtt(el, "value", 0), execAtt(el, "segments", []), execAtt(el, "centered", false)); + add(slider); + addLabelOn(slider, label); + slider; + case "dropdown": + var items:Array<DropDownItem> = []; + for(i=>node in el.nodes.item) + items.push({label: execString(node.getAtt("label")), value: execAtt(node, "value", i)}); + var curValue = execAtt(el, "value"); + var index = UIDropDown.indexOfItemValue(items, curValue); + if (index == -1) index = 0; + + var dropdown = new UIDropDown(x, y, execAtt(el, "width"), execAtt(el, "height"), items, index); + add(dropdown); + addLabelOn(dropdown, label); + dropdown; + case "buttonlist": + //x:Float, y:Float, width:Int, height:Int, windowName:String, buttonSize:FlxPoint, ?buttonOffset:FlxPoint, ?buttonSpacing:Float + var buttonlist = new UIButtonList<UIButton>( + x, + y, + execAtt(el, "width"), + execAtt(el, "height"), + execString(el.getAtt("title")).getDefault(""), + FlxPoint.get(execAtt(el, "buttonSizeX", 0), execAtt(el, "buttonSizeY", 0)), + FlxPoint.get(execAtt(el, "buttonOffsetX", 0), execAtt(el, "buttonOffsetY", 0)), + execAtt(el, "buttonSpacing") + ); + var old = get("buttonlist"); + set("buttonlist", buttonlist); + var texture = "editors/ui/inputbox"; + var code = ""; + for(node in getArr(el.elements)) { + switch(node.name) { + case "camSpacing": buttonlist.cameraSpacing = execAtt(node, "value", 0); + case "texture": texture = execString(node.getAtt("path")).getDefault(texture); + case "code": code = node.innerData; + } + } + buttonlist.frames = Paths.getFrames(texture); + if(code != "") + exec(code); + add(buttonlist); + addLabelOn(buttonlist, label); + set("buttonlist", old); + buttonlist; + // UIColorwheel + // UIAudioPlayer + // UIButtonList + // UIFileExplorer + // UIAutoCompleteTextbox + default: { + Logs.trace("Unknown element type: " + type, ERROR, RED); + Logs.trace("Skipping element: " + el.x.toString(), ERROR, RED); + continue; + } + } + + set("last", sprite); + if(name != null) set(name, sprite); + if(sprite is UISprite) elementMap.set(name, cast sprite); + } + } + + addLayout(getArr(layout.elements)); + + if(get("hasSaveButton")) { + saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight - 20, "Save & Close", function() { + saveData(); + close(); + }, 125); + saveButton.x -= saveButton.bWidth; + saveButton.y -= saveButton.bHeight; + add(saveButton); + } + + if(get("hasCloseButton")) { + var x = saveButton != null ? saveButton.x - 20 : windowSpr.x + windowSpr.bWidth - 20; + var y = saveButton != null ? saveButton.y : windowSpr.y + windowSpr.bHeight - 20; + closeButton = new UIButton(x, y, "Close", function() { + close(); + }, 125); + if(saveButton != null) + closeButton.color = 0xFFFF0000; + closeButton.x -= closeButton.bWidth; + if(saveButton == null) + closeButton.y -= closeButton.bHeight; + add(closeButton); + } + } + + function getElement(name:String):UISprite { + return elementMap.get(name); + } + + function callFunc(name:String, args:Array<Dynamic>) { + if (interp == null) return null; + if (!interp.variables.exists(name)) return null; + + var func = interp.variables.get(name); + if (func != null && Reflect.isFunction(func)) + return Reflect.callMethod(null, func, args); + + return null; + } + + override function update(elapsed:Float) { + super.update(elapsed); + callFunc("onUpdate", [elapsed]); + } + + public function saveData() { + for (el in elementMap) { + if(el is UINumericStepper) { + var el:UINumericStepper = cast el; + @:privateAccess el.__onChange(el.label.text); + } + } + + callFunc("onSave", []); + } +} \ No newline at end of file diff --git a/source/funkin/editors/ui/UISprite.hx b/source/funkin/editors/ui/UISprite.hx index 1246e6de..5e0e5ee7 100644 --- a/source/funkin/editors/ui/UISprite.hx +++ b/source/funkin/editors/ui/UISprite.hx @@ -1,6 +1,7 @@ package funkin.editors.ui; import flixel.math.FlxRect; +import funkin.backend.utils.NativeAPI.CodeCursor; import openfl.ui.MouseCursor; @:allow(funkin.editors.ui.UIState) @@ -21,7 +22,7 @@ class UISprite extends FlxSprite { public var hoverCallback:Void->Void = null; - public var cursor:MouseCursor = ARROW; + public var cursor:CodeCursor = ARROW; public var focused(get, set):Bool; public var selectable:Bool = true; @@ -55,7 +56,8 @@ class UISprite extends FlxSprite { FlxCamera._defaultCameras = cameras; for(m in members) - m.update(elapsed); + if(m.exists && m.active) + m.update(elapsed); FlxCamera._defaultCameras = __oldDefCams; } @@ -114,4 +116,8 @@ class UISprite extends FlxSprite { if (hoverCallback != null) hoverCallback(); } + + public function updateSpriteRect() { + __rect.set(x, y, width, height); + } } \ No newline at end of file diff --git a/source/funkin/editors/ui/UIState.hx b/source/funkin/editors/ui/UIState.hx index 1a66821d..fb578b61 100644 --- a/source/funkin/editors/ui/UIState.hx +++ b/source/funkin/editors/ui/UIState.hx @@ -1,12 +1,16 @@ package funkin.editors.ui; +import openfl.Lib; +import funkin.editors.ui.notifications.UIBaseNotification; import flixel.math.FlxPoint; import flixel.math.FlxRect; import funkin.backend.system.framerate.Framerate; +import funkin.backend.utils.NativeAPI.CodeCursor; import funkin.editors.ui.UIContextMenu.UIContextMenuCallback; import funkin.editors.ui.UIContextMenu.UIContextMenuOption; import lime.ui.KeyCode; import lime.ui.KeyModifier; +import flixel.system.scaleModes.StageSizeScaleMode; import openfl.ui.Mouse; import openfl.ui.MouseCursor; @@ -22,11 +26,15 @@ class UIState extends MusicBeatState { public var hoveredSprite:UISprite = null; public var currentFocus:IUIFocusable = null; - public var currentCursor:MouseCursor = ARROW; + public var currentCursor:CodeCursor = ARROW; + + public var uiCameras:Array<FlxCamera> = []; private var __rect:FlxRect; private var __mousePos:FlxPoint; + static var __point:FlxPoint = new FlxPoint(); + public override function create() { __rect = new FlxRect(); __mousePos = FlxPoint.get(); @@ -59,37 +67,22 @@ class UIState extends MusicBeatState { currentFocus.onTextEdit(str, start, end); } - public inline function updateSpriteRect(spr:UISprite) { - spr.__rect.x = spr.x; - spr.__rect.y = spr.y; - spr.__rect.width = spr.width; - spr.__rect.height = spr.height; - } - public function updateButtonHandler(spr:UISprite, buttonHandler:Void->Void) { - spr.__rect.x = spr.x; - spr.__rect.y = spr.y; - spr.__rect.width = spr.width; - spr.__rect.height = spr.height; + spr.updateSpriteRect(); updateRectButtonHandler(spr, spr.__rect, buttonHandler); } public function isOverlapping(spr:UISprite, rect:FlxRect) { for(camera in spr.__lastDrawCameras) { - var pos = FlxG.mouse.getScreenPosition(camera, FlxPoint.get()); - __rect.x = rect.x; - __rect.y = rect.y; - __rect.width = rect.width; - __rect.height = rect.height; + var pos = FlxG.mouse.getScreenPosition(camera, __point); + __rect.copyFrom(rect); - __rect.x -= camera.scroll.x * spr.scrollFactor.x; - __rect.y -= camera.scroll.y * spr.scrollFactor.y; + __rect.x = __rect.x - camera.scroll.x * spr.scrollFactor.x; + __rect.y = __rect.y - camera.scroll.y * spr.scrollFactor.y; if (((pos.x > __rect.x) && (pos.x < __rect.x + __rect.width)) && ((pos.y > __rect.y) && (pos.y < __rect.y + __rect.height))) { - pos.put(); return true; } - pos.put(); } return false; } @@ -117,18 +110,29 @@ class UIState extends MusicBeatState { FlxG.sound.keysAllowed = currentFocus != null ? !(currentFocus is UITextBox) : true; - if (hoveredSprite != null) { - Mouse.cursor = hoveredSprite.cursor; - hoveredSprite = null; + if (hoveredSprite != null && hoveredSprite.cursor != null) { + NativeAPI.setCursorIcon(hoveredSprite.cursor); } else { - Mouse.cursor = currentCursor; + NativeAPI.setCursorIcon(currentCursor); } + hoveredSprite = null; } public override function destroy() { + if (resolutionAware) { + resolutionAware = false; + + for (camera in FlxG.cameras.list) { + camera.width = FlxG.initialWidth; + camera.height = FlxG.initialHeight; + } + FlxG.scaleMode = Main.scaleMode; + } + super.destroy(); __mousePos.put(); + DrawUtil.destroyDrawers(); WindowUtils.resetAffixes(); SaveWarning.reset(); @@ -145,7 +149,7 @@ class UIState extends MusicBeatState { } } - public function openContextMenu(options:Array<UIContextMenuOption>, ?callback:UIContextMenuCallback, ?x:Float, ?y:Float) { + public function openContextMenu(options:Array<UIContextMenuOption>, ?callback:UIContextMenuCallback, ?x:Float, ?y:Float, ?w:Int) { var state = FlxG.state; while(state.subState != null && !(state._requestSubStateReset && state._requestedSubState == null)) state = state.subState; @@ -153,7 +157,32 @@ class UIState extends MusicBeatState { state.persistentDraw = true; state.persistentUpdate = true; - state.openSubState(curContextMenu = new UIContextMenu(options, callback, x.getDefault(__mousePos.x), y.getDefault(__mousePos.y))); + state.openSubState(curContextMenu = new UIContextMenu(options, callback, x.getDefault(__mousePos.x), y.getDefault(__mousePos.y), w)); return curContextMenu; } + + public function displayNotification(notification:UIBaseNotification) { + notification.cameras = uiCameras; + notification.onRemove = (notif) -> { + notification.onRemove = null; + remove(notif, true); + }; + add(notification); + notification.update(0); + notification.appearAnimation(); + // TODO: future tooltips + //notification.x = __mousePos.x; + //notification.y = __mousePos.y; + //notification.alpha = 0; + //notification.appearAnimation(); + //FlxTween.tween(notification, {x: __mousePos.x, y: __mousePos.y, alpha: 1}, .3, {ease: FlxEase.circInOut}); + } + + public static var resolutionAware:Bool = false; + public static var uiScaleMode:UIScaleMode = new UIScaleMode(); + + public static function setResolutionAware() { + resolutionAware = true; + FlxG.scaleMode = uiScaleMode; + } } \ No newline at end of file diff --git a/source/funkin/editors/ui/UISubstateWindow.hx b/source/funkin/editors/ui/UISubstateWindow.hx index 71550ef0..be975868 100644 --- a/source/funkin/editors/ui/UISubstateWindow.hx +++ b/source/funkin/editors/ui/UISubstateWindow.hx @@ -38,6 +38,7 @@ class UISubstateWindow extends MusicBeatSubstate { var winWidth:Int = 560; var winHeight:Int = 570; var winTitle:String = ""; + var winSkin:String = "editors/ui/normal-popup"; public override function create() { super.create(); @@ -72,11 +73,13 @@ class UISubstateWindow extends MusicBeatSubstate { subCam.zoom = 0.1; FlxG.cameras.add(subCam, false); - windowSpr = new UISliceSprite(0, 0, winWidth, winHeight, "editors/ui/normal-popup"); + windowSpr = new UISliceSprite(0, 0, winWidth, winHeight, winSkin); add(windowSpr); - add(titleSpr = new UIText(windowSpr.x + 25, windowSpr.y, windowSpr.bWidth - 50, winTitle, 15, -1)); - titleSpr.y = windowSpr.y + ((30 - titleSpr.height) / 2); + if(winTitle != null) { + add(titleSpr = new UIText(windowSpr.x + 25, windowSpr.y, windowSpr.bWidth - 50, winTitle, 15, -1)); + titleSpr.y = windowSpr.y + ((30 - titleSpr.height) / 2); + } FlxTween.tween(camera, {alpha: 1}, 0.25, {ease: FlxEase.cubeOut}); FlxTween.tween(camera, {zoom: 1}, 0.66, {ease: FlxEase.elasticOut}); diff --git a/source/funkin/editors/ui/UITextBox.hx b/source/funkin/editors/ui/UITextBox.hx index 42473d83..f2939ac9 100644 --- a/source/funkin/editors/ui/UITextBox.hx +++ b/source/funkin/editors/ui/UITextBox.hx @@ -17,10 +17,10 @@ class UITextBox extends UISliceSprite implements IUIFocusable { var __wasFocused:Bool = false; - public function new(x:Float, y:Float, text:String = "", width:Int = 320, height:Int = 32, multiline:Bool = false) { - super(x, y, width, height, 'editors/ui/inputbox'); + public function new(x:Float, y:Float, text:String = "", width:Int = 320, height:Int = 32, multiline:Bool = false, small:Bool = false) { + super(x, y, width, height, 'editors/ui/inputbox${small ? "-small" : ""}'); - label = new UIText(0, 0, width, text); + label = new UIText(0, 0, width, text, small ? 12 : 15); members.push(label); caretSpr = new FlxSprite(0, 0); diff --git a/source/funkin/editors/ui/UITopMenu.hx b/source/funkin/editors/ui/UITopMenu.hx index c4b0471d..d705da4e 100644 --- a/source/funkin/editors/ui/UITopMenu.hx +++ b/source/funkin/editors/ui/UITopMenu.hx @@ -46,7 +46,7 @@ class UITopMenuButton extends UISliceSprite { super(x, y, 0, 23, "editors/ui/menu-item"); this.contextMenu = contextMenu; this.parent = parent; - cursor = BUTTON; + cursor = CLICK; this.label = new UIText(4, 0, 0, label); this.label.alignment = CENTER; @@ -69,10 +69,7 @@ class UITopMenuButton extends UISliceSprite { var opened = curMenu != null ? curMenu.contextMenuOpened() : false; if(opened && FlxG.mouse.justPressed) { - __rect.x = x; - __rect.y = y; - __rect.width = bWidth; - __rect.height = bHeight; + __rect.set(x, y, bWidth, bHeight); if(UIState.state.isOverlapping(this, __rect)) { curMenu.close(); justClosed = 2; diff --git a/source/funkin/editors/ui/UIUtil.hx b/source/funkin/editors/ui/UIUtil.hx index 2d2a2843..c1054cff 100644 --- a/source/funkin/editors/ui/UIUtil.hx +++ b/source/funkin/editors/ui/UIUtil.hx @@ -1,12 +1,17 @@ package funkin.editors.ui; +import flixel.util.typeLimit.OneOfTwo; +import flixel.input.FlxInput.FlxInputState; import flixel.input.keyboard.FlxKey; import funkin.editors.ui.UIContextMenu.UIContextMenuOption; +import flixel.group.FlxGroup; +@:access(flixel.FlxSprite) class UIUtil { public static function follow(spr:FlxSprite, target:FlxSprite, x:Float = 0, y:Float = 0) { - spr.cameras = target is UISprite ? ({var _:UISprite = cast target;_;}).__lastDrawCameras : target.cameras; - spr.setPosition(target.x + x, target.y + y); + spr._cameras = target is UISprite ? ({var _:UISprite = cast target;_;}).__lastDrawCameras : target.cameras; + spr.x = target.x + x; + spr.y = target.y + y; spr.scrollFactor.set(target.scrollFactor.x, target.scrollFactor.y); } @@ -14,6 +19,19 @@ class UIUtil { return contextMenu != null && UIState.state.curContextMenu == contextMenu; } + @:noUsing public static function fixKey(key:FlxKey):FlxKey { + return switch(key) { + #if mac + case CONTROL: WINDOWS; // Remap control to the meta key + #end + default: key; + } + } + + public static function getKeyState(key:FlxKey, Status:FlxInputState):Bool { + return FlxG.keys.checkStatus(fixKey(key), Status); + } + /** * Process all options with shortcuts present in a `Array<UIContextMenuOption>`. Also checks children. * @param topMenuOptions @@ -41,14 +59,7 @@ class UIUtil { var shouldPress = Std.int(key) > 0; if(!shouldPress) key = -key; - var k = switch(key) { - #if mac - case CONTROL: - WINDOWS; - #end - default: - key; - } + var k = fixKey(key); if (FlxG.keys.checkStatus(k, shouldPress ? JUST_PRESSED : JUST_RELEASED)) { justPressed = true; } else if (!FlxG.keys.checkStatus(k, shouldPress ? PRESSED : RELEASED)) { @@ -116,4 +127,36 @@ class UIUtil { public static inline function prettify(str:String) { return [for(s in str.split(" ")) [for(k=>l in s.split("")) k == 0 ? l.toUpperCase() : l.toLowerCase()].join("")].join(" "); } + + /** + * For when the user closes out before confriming all ui selections + * @param ui the thingy you wanna check + */ + public static function confirmUISelections(ui:Dynamic) { + var members:Array<FlxBasic> = []; + + if (Reflect.fields(ui).contains("members")) // hasField doesnt work >:( + members = Reflect.field(ui, "members"); + else return; + + for (member in members) { + if (member is UINumericStepper) @:privateAccess { + var stepper:UINumericStepper = cast member; + if (stepper.onChange != stepper.__onChange && stepper.__wasFocused) { + stepper.onChange(stepper.label.text); + stepper.__wasFocused = false; + } else stepper.__onChange(stepper.label.text); + + } else if (member is UITextBox) @:privateAccess { + var textbox:UITextBox = cast member; + if (textbox.__wasFocused) { + textbox.onChange(textbox.label.text); + textbox.__wasFocused = false; + } + } + + if (member is UISprite || ui is UIState) + confirmUISelections(cast member); + } + } } \ No newline at end of file diff --git a/source/funkin/editors/ui/UIWarningSubstate.hx b/source/funkin/editors/ui/UIWarningSubstate.hx index b29b6bce..7b1f775a 100644 --- a/source/funkin/editors/ui/UIWarningSubstate.hx +++ b/source/funkin/editors/ui/UIWarningSubstate.hx @@ -22,6 +22,8 @@ class UIWarningSubstate extends MusicBeatSubstate { _; }; + public var bHeight:Int = 232; + var title:String; var message:String; var buttons:Array<WarningButton>; @@ -32,8 +34,6 @@ class UIWarningSubstate extends MusicBeatSubstate { var warnCam:FlxCamera; - public var bHeight:Int = 232; - public override function onSubstateOpen() { super.onSubstateOpen(); parent.persistentUpdate = false; @@ -71,14 +71,25 @@ class UIWarningSubstate extends MusicBeatSubstate { warnCam.zoom = 0.1; FlxG.cameras.add(warnCam, false); + var spr = new UISliceSprite(0, 0, CoolUtil.maxInt(560, 30 + (170 * buttons.length)), bHeight, 'editors/ui/${isError ? "normal" : "grayscale"}-popup'); + + var sprIcon:FlxSprite = new FlxSprite(spr.x + 18, spr.y + 28 + 26).loadGraphic(Paths.image('editors/warnings/${isError ? "error" : "warning"}')); + sprIcon.scale.set(1.4, 1.4); + sprIcon.updateHitbox(); + + messageSpr = new UIText(0,0, spr.bWidth - 100 - (26 * 2), message); + spr.bHeight = Std.int(bHeight + Math.abs(Math.min(sprIcon.height-messageSpr.height, 0))); + spr.x = (FlxG.width - spr.bWidth) / 2; spr.y = (FlxG.height - spr.bHeight) / 2; spr.color = isError ? 0xFFFF0000 : 0xFFFFFF00; add(spr); - add(titleSpr = new UIText(spr.x + 25, spr.y, spr.bWidth - 50, title, 15, -1)); - titleSpr.y = spr.y + ((30 - titleSpr.height) / 2); + if(title != null) { + add(titleSpr = new UIText(spr.x + 25, spr.y, spr.bWidth - 50, title, 15, -1)); + titleSpr.y = spr.y + ((30 - titleSpr.height) / 2); + } var sprIcon:FlxSprite = new FlxSprite(spr.x + 18, spr.y + 28 + 26).loadGraphic(Paths.image('editors/warnings/${isError ? "error" : "warning"}')); sprIcon.scale.set(1.4, 1.4); @@ -86,7 +97,9 @@ class UIWarningSubstate extends MusicBeatSubstate { sprIcon.antialiasing = true; add(sprIcon); - add(messageSpr = new UIText(sprIcon.x + 70 + 16 + 20, sprIcon.y + 16, spr.bWidth - 100 - (26 * 2), message)); + messageSpr.x = sprIcon.x + 70 + 16 + 20; + messageSpr.y = sprIcon.y + 16; + add(messageSpr); var xPos = (FlxG.width - (30 + (170 * buttons.length))) / 2; for(k=>b in buttons) { diff --git a/source/funkin/editors/ui/UIWindow.hx b/source/funkin/editors/ui/UIWindow.hx index 6853ce06..e6ac6388 100644 --- a/source/funkin/editors/ui/UIWindow.hx +++ b/source/funkin/editors/ui/UIWindow.hx @@ -2,19 +2,49 @@ package funkin.editors.ui; class UIWindow extends UISliceSprite { public var titleSpr:UIText; + public var collapsable:Bool = false; + public var content:FlxTypedGroup<FlxBasic>; - public override function new(x:Float, y:Float, w:Int, h:Int, title:String) { + public override function new(x:Float, y:Float, w:Int, h:Int, ?title:String) { super(x, y, w, h, "editors/ui/normal-popup"); - members.push(titleSpr = new UIText(x + 25, y, bWidth - 50, title, 15, -1)); - titleSpr.y = y + ((30 - titleSpr.height) / 2); + if(title != null && title.length != 0) { + members.push(titleSpr = new UIText(x + 25, y, bWidth - 50, title, 15, -1)); + titleSpr.y = y + ((30 - titleSpr.height) / 2); + } + + content = new FlxTypedGroup<FlxBasic>(); + members.push(content); } public override function update(elapsed:Float) { super.update(elapsed); - __rect.x = x; __rect.y = y+23; - __rect.width = bWidth; __rect.height = bHeight-23; - hovered = UIState.state.isOverlapping(this, __rect); + cursor = ARROW; + + if(collapsable) { + __rect.set(x, y, bWidth, topHeight); + + if(UIState.state.isOverlapping(this, __rect)) { + cursor = CLICK; + if(FlxG.mouse.justPressed) { + content.exists = !content.exists; + drawMiddle = !drawMiddle; + drawBottom = !drawBottom; + } + } else { + if(!content.exists) { + cursor = null; + } + } + } + + __rect.x = x; + __rect.width = bWidth; + if(content.exists) { + __rect.y = y+topHeight; + __rect.height = bHeight-topHeight; + } + hovered = content.exists && UIState.state.isOverlapping(this, __rect); } -} \ No newline at end of file +} diff --git a/source/funkin/editors/ui/notifications/UIBaseNotification.hx b/source/funkin/editors/ui/notifications/UIBaseNotification.hx new file mode 100644 index 00000000..b86ba3c7 --- /dev/null +++ b/source/funkin/editors/ui/notifications/UIBaseNotification.hx @@ -0,0 +1,166 @@ +package funkin.editors.ui.notifications; + +import flixel.group.FlxGroup; +import flixel.math.FlxRect; +import flixel.tweens.FlxTween; +import flixel.util.FlxTimer; + +using flixel.util.FlxSpriteUtil; + +class UIBaseNotification extends UISliceSprite { + public var textField:UIText; + public var closeButton:UIButton; + + public var onRemove:(notif:UIBaseNotification)->Void = null; + + public var showedAnimation:Bool = false; + public var closed:Bool = false; + + public function new(text:String, showTimer:Float = 5, corner:Corner = BOTTOM_LEFT) { + super(x, y, 340, 46, "editors/ui/inputbox"); + + closeButton = new UIButton(0, 0, "X", () -> { + if (closed) return; + closed = true; + + (new FlxTimer()).start(0.2, (_) -> {disappearAnimation();}); + }, 32, 32); + closeButton.hoverCallback = () -> { + closeButton.color = 0xFFAC3D3D; + closeButton.field.color = 0xFFffffff; + //closeButton.field.color = closeButton.hovered ? 0xFFffffff : 0xFF7f7f7f; + } + closeButton.autoAlpha = false; + closeButton.alpha = 0.0; + + textField = new UIText(0, 0, 0, text, 14); + textField.alignment = LEFT; + textField.fieldWidth = bWidth - 32 - 8; + members.push(textField); + + if(textField.height > bHeight) + bHeight = Std.int(textField.height); + + alpha = 0; + textField.alpha = 0; + + showingTimer = showTimer; + + members.push(closeButton); + + var margin = 20; + + switch(corner) { + case TOP_LEFT: + x = margin; + y = margin; + case TOP_RIGHT: + x = FlxG.width - bWidth - margin; + y = margin; + case BOTTOM_LEFT: + x = margin; + y = FlxG.height - bHeight - margin; + case BOTTOM_RIGHT: + x = FlxG.width - bWidth - margin; + y = FlxG.height - bHeight - margin; + } + + defaultX = x; + defaultY = y; + + switch(corner) { + case TOP_LEFT | BOTTOM_LEFT: + x -= bWidth - margin - 20; + case TOP_RIGHT | BOTTOM_RIGHT: + x += bWidth - margin - 20; + } + + closingX = x; + closingY = y; + + alpha = 0; + } + + public var defaultX:Float = 0; + public var defaultY:Float = 0; + + public var closingX:Float = 0; + public var closingY:Float = 0; + + public var showingTimer:Float = 5; + public var timerActive:Bool = false; + + public var progress:Float = 0; + public override function update(elapsed:Float) { + closeButton.color = 0xFFFFFFFF; + closeButton.field.color = 0xFF7f7f7f; + super.update(elapsed); + + if(timerActive) { + showingTimer -= elapsed; + if(showingTimer <= 0) { + timerActive = false; + idleDisappearAnimation(); + } + } + + textField.follow(this, 5, bHeight / 2 - textField.height / 2); + closeButton.follow(this, bWidth-closeButton.bWidth - 7, bHeight / 2 - closeButton.bHeight / 2); + closeButton.field.alpha = alpha * .7; + //closeButton.alpha = alpha; + textField.alpha = alpha; + } + + public function appearAnimation() { + x = closingX; alpha=0; FlxTween.cancelTweensOf(this, ["x", "alpha"]); + FlxTween.tween(this, {x: defaultX}, .4, {ease: FlxEase.circInOut}); + FlxTween.tween(this, {alpha: 1}, .3, {ease: FlxEase.sineOut, startDelay: .1, onComplete: (_) -> { + if(showingTimer > 0) timerActive = true; + }}); + + showedAnimation = true; + } + + public var isClosing:Bool = false; + + public function disappearAnimation() { + if (isClosing) return; + isClosing = true; + + // TODO: make it so you can click through the notification + closeButton.active = false; + closeButton.selectable = false; + closeButton.canBeHovered = false; + selectable = false; + canBeHovered = false; + + x = defaultX; alpha = 1; FlxTween.cancelTweensOf(this, ["x", "alpha"]); + FlxTween.tween(this, {alpha: 0}, .3, {ease: FlxEase.sineOut, onComplete: (_) -> { + if(onRemove != null) onRemove(this); + destroy(); + }}); + + showedAnimation = false; + } + + public function idleDisappearAnimation() { + if (isClosing) return; + isClosing = true; + + x = defaultX; alpha = 1; FlxTween.cancelTweensOf(this, ["x", "alpha"]); + FlxTween.tween(this, {alpha: 0}, .3, {ease: FlxEase.sineOut}); + FlxTween.tween(this, {x: closingX}, .4, {ease: FlxEase.circInOut, onComplete: (_) -> { + if(onRemove != null) onRemove(this); + destroy(); + }}); + + showedAnimation = false; + } +} + +enum abstract Corner(Int) { + var TOP_LEFT = 0; + var TOP_RIGHT = 1; + var BOTTOM_LEFT = 2; + var BOTTOM_RIGHT = 3; +} \ No newline at end of file diff --git a/source/funkin/editors/ui/old/OldUISliceSpriteTri.hx b/source/funkin/editors/ui/old/OldUISliceSpriteTri.hx new file mode 100644 index 00000000..e560dee5 --- /dev/null +++ b/source/funkin/editors/ui/old/OldUISliceSpriteTri.hx @@ -0,0 +1,434 @@ +package funkin.editors.ui.old; + +import flixel.math.FlxRect; +import flixel.graphics.frames.FlxFrame; +import flixel.graphics.tile.FlxGraphicsShader; +import flixel.graphics.tile.FlxDrawTrianglesItem.DrawData; + +class OldUISliceSpriteTri extends UISprite { + public var bWidth(default, set):Int = 120; + public var bHeight(default, set):Int = 20; + public var framesOffset(default, set):Int = 0; + + public var incorporeal:Bool = false; + + public function new(x:Float, y:Float, w:Int, h:Int, path:String) { + super(x, y); + + frames = Paths.getFrames(path); + resize(w, h); + + calculateFrames(); + __genMesh(); + } + + public override function updateButton() { + if (incorporeal) return; + __rect.set(x, y, bWidth, bHeight); + UIState.state.updateRectButtonHandler(this, __rect, onHovered); + } + + public function resize(w:Int, h:Int) { + bWidth = w; + bHeight = h; + } + + @:deprecated public var topAlpha:Null<Float> = null; + @:deprecated public var middleAlpha:Null<Float> = null; + @:deprecated public var bottomAlpha:Null<Float> = null; + + public var drawTop(default, set):Bool = true; + public var drawMiddle(default, set):Bool = true; + public var drawBottom(default, set):Bool = true; + + private function set_drawTop(value:Bool):Bool { + if(value != drawTop) { + drawTop = value; + __meshDirty = true; + } + return value; + } + + private function set_drawMiddle(value:Bool):Bool { + if(value != drawMiddle) { + drawMiddle = value; + __meshDirty = true; + } + return value; + } + + private function set_drawBottom(value:Bool):Bool { + if(value != drawBottom) { + drawBottom = value; + __meshDirty = true; + } + return value; + } + + var topleft:FlxFrame = null; + var top:FlxFrame = null; + var topright:FlxFrame = null; + var middleleft:FlxFrame = null; + var middle:FlxFrame = null; + var middleright:FlxFrame = null; + var bottomleft:FlxFrame = null; + var bottom:FlxFrame = null; + var bottomright:FlxFrame = null; + + public var topHeight:Int = 0; + public var bottomHeight:Int = 0; + public var leftWidth:Int = 0; + public var rightWidth:Int = 0; + + override function set_frames(val) { + super.set_frames(val); + __framesDirty = true; + return val; + } + + function set_framesOffset(value:Int) { + if(value != framesOffset) { + framesOffset = value; + __framesDirty = true; + } + return value; + } + + function calculateFrames() { + if(frames == null) return; + topleft = frames.frames[framesOffset]; + top = frames.frames[framesOffset + 1]; + topright = frames.frames[framesOffset + 2]; + middleleft = frames.frames[framesOffset + 3]; + middle = frames.frames[framesOffset + 4]; + middleright = frames.frames[framesOffset + 5]; + bottomleft = frames.frames[framesOffset + 6]; + bottom = frames.frames[framesOffset + 7]; + bottomright = frames.frames[framesOffset + 8]; + + leftWidth = Std.int(MathUtil.maxSmart(topleft.frame.width, middleleft.frame.width, bottomleft.frame.width)); + rightWidth = Std.int(MathUtil.maxSmart(topright.frame.width, middleright.frame.width, bottomright.frame.width)); + topHeight = Std.int(MathUtil.maxSmart(topleft.frame.height, top.frame.height, topright.frame.height)); + bottomHeight = Std.int(MathUtil.maxSmart(topleft.frame.height, top.frame.height, topright.frame.height)); + + __meshDirty = true; + } + + public override function draw() @:privateAccess { + checkEmptyFrame(); + + if (alpha != 0 && _frame.type != FlxFrameType.EMPTY) { + if (__framesDirty) calculateFrames(); + if (__meshDirty) __genMesh(); + + for (camera in cameras) + { + if (!camera.visible || !camera.exists || !isOnScreen(camera)) + continue; + + getScreenPosition(_point, camera).subtractPoint(offset); + + #if !flash + camera.drawTriangles(graphic, vertices, indices, uvtData, colors, _point, blend, false, antialiasing, colorTransform, shader); + #else + camera.drawTriangles(graphic, vertices, indices, uvtData, colors, _point, blend, false, antialiasing); + #end + + #if FLX_DEBUG + FlxBasic.visibleCount++; + #end + } + } + + super.drawMembers(); + __lastDrawCameras = cameras.copy(); + } + + var vertices:DrawData<Float> = new DrawData<Float>(); + var indices:DrawData<Int> = new DrawData<Int>(); + var uvtData:DrawData<Float> = new DrawData<Float>(); + var colors:DrawData<Int> = new DrawData<Int>(); + + var __framesDirty:Bool = false; + var __meshDirty:Bool = false; + + private function set_bWidth(value:Int):Int { + if(value != bWidth) { + bWidth = value; + __meshDirty = true; + } + return value; + } + + private function set_bHeight(value:Int):Int { + if(value != bHeight) { + bHeight = value; + __meshDirty = true; + } + return value; + } + + private static inline function getFixedSize(value:Float, total:Float):Float { + return value * Math.min(total / (value * 2), 1); + } + + private function __genMesh() { + indices.length = 0; + vertices.length = 0; + //colors.length = 0; + uvtData.length = 0; + + var sliceTL:Quad = null; + var sliceTM:Quad = null; + var sliceTR:Quad = null; + var sliceML:Quad = null; + var sliceMM:Quad = null; + var sliceMR:Quad = null; + var sliceBL:Quad = null; + var sliceBM:Quad = null; + var sliceBR:Quad = null; + + // TOP PART + if (drawTop) { + // TOP LEFT + var topLeftWidth:Float = getFixedSize(topleft.frame.width, bWidth); + var topLeftHeight:Float = getFixedSize(topleft.frame.height, bHeight); + sliceTL = __genSliceQuad( + 0, 0, + topLeftWidth, topLeftHeight, + topleft, null, null + ); + + // TOP MIDDLE + if (bWidth > topleft.frame.width + topright.frame.width) { + var topWidth:Float = bWidth - topleft.frame.width - topright.frame.width; + var topHeight:Float = getFixedSize(top.frame.height, bHeight); + sliceTM = __genSliceQuad( + topLeftWidth, 0, + topWidth, topHeight, + top, sliceTL, null + ); + } + + // TOP RIGHT + var topRightWidth:Float = getFixedSize(topright.frame.width, bWidth); + var topRightHeight:Float = getFixedSize(topright.frame.height, bHeight); + sliceTR = __genSliceQuad( + bWidth - getFixedSize(topright.frame.width, bWidth), 0, + topRightWidth, topRightHeight, + topright, sliceTM, null + ); + } + + if (drawMiddle && bHeight > top.frame.height + bottom.frame.height) { + var middleHeight:Float = bHeight - getFixedSize(topleft.frame.height, bHeight) - getFixedSize(bottomleft.frame.height, bHeight); + + // MIDDLE LEFT + var middleLeftWidth:Float = getFixedSize(middleleft.frame.width, bWidth); + sliceML = __genSliceQuad( + 0, top.frame.height, + middleLeftWidth, middleHeight, + middleleft, null, sliceTL + ); + + // MIDDLE + if (bWidth > getFixedSize(middleleft.frame.width, bWidth) + middleright.frame.width) { + var middleWidth:Float = bWidth - middleleft.frame.width - middleright.frame.width; + sliceMM = __genSliceQuad( + topleft.frame.width, top.frame.height, + middleWidth, middleHeight, + middle, sliceML, sliceTM + ); + } + + // MIDDLE RIGHT + var middleRightWidth:Float = getFixedSize(middleright.frame.width, bWidth); + sliceMR = __genSliceQuad( + bWidth - getFixedSize(middleright.frame.width, bWidth), top.frame.height, + middleRightWidth, middleHeight, + middleright, sliceMM, sliceTR + ); + } + + // BOTTOM + if (drawBottom) { + // BOTTOM LEFT + var bottomLeftWidth:Float = getFixedSize(bottomleft.frame.width, bWidth); + var bottomLeftHeight:Float = getFixedSize(bottomleft.frame.height, bHeight); + //var bottomLeftHeight:Float = bottomleft.frame.height * Math.min(bHeight/(bottomleft.frame.height*2), 1); + sliceBL = __genSliceQuad( + 0, bHeight - getFixedSize(bottomleft.frame.height, bHeight), + bottomLeftWidth, bottomLeftHeight, + bottomleft, null, sliceML + ); + + // BOTTOM MIDDLE + if (bWidth > bottomleft.frame.width + bottomright.frame.width) { + var bottomMiddleWidth:Float = bWidth - bottomleft.frame.width - bottomright.frame.width; + var bottomMiddleHeight:Float = getFixedSize(bottom.frame.height, bHeight); + sliceBM = __genSliceQuad( + bottomleft.frame.width, bHeight - getFixedSize(bottom.frame.height, bHeight), + bottomMiddleWidth, bottomMiddleHeight, + bottom, sliceBL, sliceMM + ); + } + + // BOTTOM RIGHT + var bottomRightWidth:Float = getFixedSize(bottomright.frame.width, bWidth); + var bottomRightHeight:Float = getFixedSize(bottomright.frame.height, bHeight); + sliceBR = __genSliceQuad( + bWidth - getFixedSize(bottomright.frame.width, bWidth), + bHeight - getFixedSize(bottomright.frame.height, bHeight), + bottomRightWidth, bottomRightHeight, + bottomright, sliceBM, sliceMR + ); + } + + __meshDirty = false; + } + + private function __genSliceQuad(x:Float, y:Float, width:Float, height:Float, frame:FlxFrame, leftSlice:Quad, topSlice:Quad):Quad { + var indicesOffset:Int = Std.int(vertices.length / 2); + + if(leftSlice != null && topSlice != null) { + indices.push(topSlice.bl); + indices.push(topSlice.br); + indices.push(indicesOffset); + + indices.push(leftSlice.tr); + indices.push(indicesOffset); + indices.push(leftSlice.br); + + vertices.push(x + width); + vertices.push(y + height); + + uvtData.push(frame.uv.width); + uvtData.push(frame.uv.height); + + // add when openfl fixes this + //colors.push(color); + + return new Quad(topSlice.bl, topSlice.br, indicesOffset, leftSlice.br); + } + if(leftSlice != null) { + indices.push(leftSlice.tr); + indices.push(indicesOffset); + indices.push(indicesOffset + 1); + + indices.push(leftSlice.tr); + indices.push(indicesOffset + 1); + indices.push(leftSlice.br); + + vertices.push(x + width); + vertices.push(y); + vertices.push(x + width); + vertices.push(y + height); + + uvtData.push(frame.uv.width); + uvtData.push(frame.uv.y); + uvtData.push(frame.uv.width); + uvtData.push(frame.uv.height); + + // add when openfl fixes this + //colors.push(color); + //colors.push(color); + + return new Quad(leftSlice.tr, indicesOffset, indicesOffset + 1, leftSlice.br); + } + if(topSlice != null) { + indices.push(topSlice.bl); + indices.push(topSlice.br); + indices.push(indicesOffset); + + indices.push(topSlice.bl); + indices.push(indicesOffset); + indices.push(indicesOffset + 1); + + vertices.push(x + width); + vertices.push(y + height); + vertices.push(x); + vertices.push(y + height); + + uvtData.push(frame.uv.width); + uvtData.push(frame.uv.height); + uvtData.push(frame.uv.x); + uvtData.push(frame.uv.height); + + // add when openfl fixes this + //colors.push(color); + //colors.push(color); + + return new Quad(topSlice.bl, topSlice.br, indicesOffset, indicesOffset + 1); + } + indices.push(indicesOffset); + indices.push(indicesOffset + 1); + indices.push(indicesOffset + 2); + + indices.push(indicesOffset); + indices.push(indicesOffset + 2); + indices.push(indicesOffset + 3); + + vertices.push(x); + vertices.push(y); + vertices.push(x + width); + vertices.push(y); + vertices.push(x + width); + vertices.push(y + height); + vertices.push(x); + vertices.push(y + height); + + uvtData.push(frame.uv.x); + uvtData.push(frame.uv.y); + uvtData.push(frame.uv.width); + uvtData.push(frame.uv.y); + uvtData.push(frame.uv.width); + uvtData.push(frame.uv.height); + uvtData.push(frame.uv.x); + uvtData.push(frame.uv.height); + + // add when openfl fixes this + //colors.push(color); + //colors.push(color); + //colors.push(color); + //colors.push(color); + + return new Quad(indicesOffset, indicesOffset + 1, indicesOffset + 2, indicesOffset + 3); + } + + @:access(flixel.FlxCamera) + override function getBoundingBox(camera:FlxCamera):FlxRect { + getScreenPosition(_point, camera); + + _rect.set(_point.x, _point.y, bWidth, bHeight); + _rect = camera.transformRect(_rect); + + // if (isPixelPerfectRender(camera)) + // _rect.floor(); + + return _rect; + } + + override public function destroy():Void + { + vertices = null; + indices = null; + uvtData = null; + colors = null; + + super.destroy(); + } +} + +final class Quad { + public var tl:Int; + public var tr:Int; + public var br:Int; + public var bl:Int; + + public function new(tl:Int, tr:Int, br:Int, bl:Int) { + this.tl = tl; + this.tr = tr; + this.br = br; + this.bl = bl; + } +} \ No newline at end of file diff --git a/source/funkin/game/Character.hx b/source/funkin/game/Character.hx index 79a30aab..8a0efac6 100644 --- a/source/funkin/game/Character.hx +++ b/source/funkin/game/Character.hx @@ -1,5 +1,8 @@ package funkin.game; +import sys.FileSystem; +import flixel.util.FlxSpriteUtil; +import openfl.display.Graphics; import flixel.util.typeLimit.OneOfTwo; import flixel.graphics.frames.FlxFrame; import flixel.math.FlxPoint; @@ -17,6 +20,7 @@ import funkin.backend.scripting.events.DrawEvent; import funkin.backend.system.Conductor; import funkin.backend.system.interfaces.IBeatReceiver; import funkin.backend.system.interfaces.IOffsetCompatible; +import funkin.backend.utils.MatrixUtil; import funkin.backend.utils.XMLUtil; import haxe.Exception; import haxe.io.Path; @@ -28,7 +32,7 @@ using StringTools; @:allow(funkin.desktop.editors.CharacterEditor) @:allow(funkin.game.StrumLine) @:allow(funkin.game.PlayState) -class Character extends FunkinSprite implements IBeatReceiver implements IOffsetCompatible { +class Character extends FunkinSprite implements IBeatReceiver implements IOffsetCompatible implements IPrePostDraw { public var isPlayer:Bool = false; public var curCharacter:String = Flags.DEFAULT_CHARACTER; public var sprite:String = Flags.DEFAULT_CHARACTER; @@ -44,6 +48,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset public var cameraOffset:FlxPoint = FlxPoint.get(0, 0); public var globalOffset:FlxPoint = FlxPoint.get(0, 0); + public var extraOffset:FlxPoint = FlxPoint.get(0, 0); public var xml:Access; public var scripts:ScriptPack; @@ -76,16 +81,13 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset if(!disableScripts) script = Script.create(Paths.script(Path.withoutExtension(Paths.xml('characters/$curCharacter')), null, true)); - else - script = new DummyScript(curCharacter); - script.load(); - - buildCharacter(xml); - scripts.call("create"); - if (script == null) script = new DummyScript(curCharacter); + script.load(); + + scripts.call("create"); + buildCharacter(xml); scripts.call("postCreate"); } @@ -101,9 +103,6 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset if (isPlayer != playerOffsets && switchAnims) swapLeftRightAnimations(); - var o:FlxPoint = getAnimOffset(getAnimName()); - frameOffset.set(o.x, o.y); - o.put(); if (isPlayer) flipX = !flipX; __baseFlipped = flipX; } @@ -173,13 +172,14 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset } /** - * Whenever the character should dance on beat or not. Set to false for `gf`, since the dance animation is automatically handled by PlayState. + * Whenever the character should dance on beat or not. */ public var danceOnBeat:Bool = true; public override function beatHit(curBeat:Int) { scripts.call("beatHit", [curBeat]); - if (danceOnBeat && (curBeat + beatOffset) % beatInterval == 0 && !__lockAnimThisFrame) + if (skipNegativeBeats && curBeat < 0) return; + if (danceOnBeat && (curBeat + beatOffset) % (beatInterval * CoolUtil.maxInt(Math.floor(4 / Conductor.stepsPerBeat), 1)) == 0 && !__lockAnimThisFrame) tryDance(); } @@ -206,34 +206,51 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset } public function isFlippedOffsets() - return (isPlayer != playerOffsets) != (flipX != __baseFlipped); + return debugMode ? false : (isPlayer != playerOffsets) != (flipX != __baseFlipped); + var __reversePreDrawProcedure:Bool = false; + + public function preDraw() { + if (!ghostDraw) { + x += extraOffset.x; + y += extraOffset.y; + } + + if (__reversePreDrawProcedure = isFlippedOffsets()) { + __reverseDrawProcedure = true; + flipX = !flipX; + scale.x *= -1; + } + } + + public function postDraw() { + if (__reversePreDrawProcedure) { + flipX = !flipX; + scale.x *= -1; + __reverseDrawProcedure = false; + } + + if (!ghostDraw) { + x -= extraOffset.x; + y -= extraOffset.y; + } + } + + public var ghostDraw:Bool = false; public override function draw() { var e = EventManager.get(DrawEvent).recycle(); script.call("draw", [e]); - if(!e.cancelled) { - if (isFlippedOffsets()) { - __reverseDrawProcedure = true; - flipX = !flipX; - scale.x *= -1; - - super.draw(); - - flipX = !flipX; - scale.x *= -1; - __reverseDrawProcedure = false; - } else super.draw(); - } + preDraw(); + super.draw(); + postDraw(); script.call("postDraw", [e]); } public var singAnims = ["singLEFT", "singDOWN", "singUP", "singRIGHT"]; public inline function getSingAnim(direction:Int, suffix:String = ""):String - { return singAnims[direction % singAnims.length] + suffix; - } /** * Like `playSingAnimUnsafe` but checks if the character has the animation with the suffix part, otherwise it plays the animation without the suffix part. @@ -287,6 +304,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset cameraOffset.put(); globalOffset.put(); + extraOffset.put(); } @:noCompletion var __reverseTrailProcedure:Bool = false; @@ -409,7 +427,8 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset if (holdTime != 4) xml.set("holdTime", Std.string(FlxMath.roundDecimal(holdTime, 4))); - if (flipX) xml.set("flipX", Std.string(flipX)); + var realFlipped:Bool = isPlayer ? !__baseFlipped : __baseFlipped; + if (realFlipped) xml.set("flipX", "true"); xml.set("icon", getIcon()); if (gameOverCharacter != Character.FALLBACK_DEAD_CHARACTER) xml.set("gameOverChar", gameOverCharacter); @@ -419,7 +438,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset if (scale.x != 1) xml.set("scale", Std.string(FlxMath.roundDecimal(scale.x, 4))); if (!antialiasing) xml.set("antialiasing", antialiasing == true ? "true" : "false"); - if (playerOffsets) xml.set("isPlayer", playerOffsets == true ? "true" : "false"); + if (isPlayer) xml.set("isPlayer", isPlayer == true ? "true" : "false"); var anims:Array<AnimData> = []; if (animsOrder != null) { diff --git a/source/funkin/game/NoteGroup.hx b/source/funkin/game/NoteGroup.hx index ac56dfe8..ec67ba5c 100644 --- a/source/funkin/game/NoteGroup.hx +++ b/source/funkin/game/NoteGroup.hx @@ -65,9 +65,9 @@ class NoteGroup extends FlxTypedGroup<Note> { } } - public override function draw() { - @:privateAccess var oldDefaultCameras = FlxCamera._defaultCameras; - @:privateAccess if (cameras != null) FlxCamera._defaultCameras = cameras; + public override function draw() @:privateAccess { + var oldDefaultCameras = FlxCamera._defaultCameras; + if (_cameras != null) FlxCamera._defaultCameras = _cameras; var oldCur = __currentlyLooping; __currentlyLooping = true; @@ -84,7 +84,7 @@ class NoteGroup extends FlxTypedGroup<Note> { } __currentlyLooping = oldCur; - @:privateAccess FlxCamera._defaultCameras = oldDefaultCameras; + FlxCamera._defaultCameras = oldDefaultCameras; } /** diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index f5f6bc5b..93faa4e8 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -377,13 +377,28 @@ class PlayState extends MusicBeatState */ public var camZooming:Bool = false; /** - * Interval of cam zooming (beats). - * For example: if set to 4, the camera will zoom every 4 beats. - */ - public var camZoomingInterval:Int = Flags.DEFAULT_CAM_ZOOM_INTERVAL; + * Interval of cam zooming (in conductor values). + * Example: If Interval is 1 and Beat Type is on MEASURE, it'll zoom every a measure. + * NOTE: Will set to 4 if not found any other time signatures unlike 4/4 + */ + public var camZoomingInterval:Float = Flags.DEFAULT_CAM_ZOOM_INTERVAL; /** - * How strong the cam zooms should be (defaults to 1) - */ + * Number of Conductor values to offset camZooming by. + */ + public var camZoomingOffset:Float = Flags.DEFAULT_CAM_ZOOM_OFFSET; + /** + * Beat type for interval of cam zooming. + * Example: If Beat Type is on STEP and Interval is 2, it'll zoom every 2 steps. + * NOTE: Will set to BEAT if not found any other time signatures unlike 4/4 + */ + public var camZoomingEvery:BeatType = MEASURE; + /** + * Stores what was the last beat for the cam zooming intervals. + */ + public var camZoomingLastBeat:Float; + /** + * How strong the cam zooms should be (defaults to 1) + */ public var camZoomingStrength:Float = Flags.DEFAULT_CAM_ZOOM_STRENGTH; /** * Default multiplier for `maxCamZoom`. @@ -667,7 +682,7 @@ class PlayState extends MusicBeatState for (event in SONG.events) songEvents.pushOnce(event.name); for (file in Paths.getFolderContent('data/events/', true, fromMods ? MODS : BOTH)) { - var fileName:String = Path.withoutExtension(Path.withoutDirectory(file)); + var fileName:String = CoolUtil.getFilename(file); if (EventsData.eventsList.contains(fileName) && songEvents.contains(fileName)) { addScript(file); } @@ -723,7 +738,9 @@ class PlayState extends MusicBeatState var strOffset:Float = strumLine.strumLinePos != null ? strumLine.strumLinePos : (strumLine.type == 1 ? 0.75 : 0.25); var strScale:Float = strumLine.strumScale != null ? strumLine.strumScale : 1; - var strXPos:Float = (FlxG.width * strOffset) - (Note.swagWidth * strScale * 2); + var strSpacing:Float = strumLine.strumSpacing == null ? 1 : strumLine.strumSpacing; + var keyCount:Int = strumLine.keyCount == null ? 4 : strumLine.keyCount; + var strXPos:Float = StrumLine.calculateStartingXPos(strOffset, strScale, strSpacing, keyCount); var startingPos:FlxPoint = strumLine.strumPos != null ? FlxPoint.get(strumLine.strumPos[0] == 0 ? strXPos : strumLine.strumPos[0], strumLine.strumPos[1]) : FlxPoint.get(strXPos, this.strumLine.y); @@ -840,10 +857,7 @@ class PlayState extends MusicBeatState SaveWarning.showWarning = Charter.undos.unsaved; SaveWarning.selectionClass = CharterSelection; SaveWarning.warningFunc = saveWarn; - SaveWarning.saveFunc = () -> { - @:privateAccess Chart.save('${Paths.getAssetsRoot()}/songs/${Charter.__song}', - SONG, Charter.__diff, {saveMetaInChart: false, prettyPrint: Options.editorPrettyPrint}); - } + SaveWarning.saveFunc = () -> Charter.saveEverything(false); } } @@ -1056,26 +1070,34 @@ class PlayState extends MusicBeatState { if (songData == null) songData = SONG; - events = songData.events != null ? songData.events.copy() : []; - // get first camera focus - for(e in events) { - if (e.time > 10) break; - if (e.name == "Camera Movement") { - executeEvent(e); - break; + var foundCam = false; + var foundSigs = songData.meta.beatsPerMeasure.getDefault(4) != 4 || songData.meta.stepsPerBeat.getDefault(4) != 4; + + if (events == null) events = []; + else events = [ + for (e in songData.events) { + switch (e.name) { + case "Camera Movement": if (!foundCam && e.time < 10) { + foundCam = true; + executeEvent(e); + } + case "Time Signature Change": if (!foundSigs && (e.params[0] != 4 || e.params[1] != 4)) { + foundSigs = true; + } + } + e; } + ]; + + if (!foundSigs) { + camZoomingInterval = 4; + camZoomingEvery = BEAT; } + events.sort(function(p1, p2) { return FlxSort.byValues(FlxSort.DESCENDING, p1.time, p2.time); }); - var beatsPerMeasure:Float = songData.meta.beatsPerMeasure.getDefault(Flags.DEFAULT_BEATS_PER_MEASURE); - var stepsPerBeat:Int = songData.meta.stepsPerBeat.getDefault(Flags.DEFAULT_STEPS_PER_BEAT); - - camZoomingInterval = Std.int(beatsPerMeasure); - - Conductor.changeBPM(songData.meta.bpm, beatsPerMeasure, stepsPerBeat); - curSong = songData.meta.name.toLowerCase(); curSongID = curSong.replace(" ", "-"); @@ -1099,7 +1121,7 @@ class PlayState extends MusicBeatState @:dox(hide) private inline function generateStrums(amount:Null<Int> = null):Void { for(p in strumLines) { - var kc = amount != null ? amount : Flags.DEFAULT_STRUM_AMOUNT; + var kc = amount != null ? amount : p.data.keyCount; p.generateStrums(kc); } } @@ -1319,6 +1341,15 @@ class PlayState extends MusicBeatState if (canAccessDebugMenus && chartingMode && controls.DEV_ACCESS) FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, false)); + if (Options.camZoomOnBeat && camZooming && FlxG.camera.zoom < maxCamZoom) { + var beat = Conductor.getBeats(camZoomingEvery, camZoomingInterval, camZoomingOffset); + if (camZoomingLastBeat != beat) { + camZoomingLastBeat = beat; + FlxG.camera.zoom += 0.015 * camZoomingStrength; + camHUD.zoom += 0.03 * camZoomingStrength; + } + } + if (doIconBop) for (icon in iconArray) if (icon.updateBump != null) @@ -1540,6 +1571,12 @@ class PlayState extends MusicBeatState case "Camera Modulo Change": camZoomingInterval = event.params[0]; camZoomingStrength = event.params[1]; + if (event.params[2] != null) camZoomingEvery = switch (event.params[2].toUpperCase()) { + case "STEP": STEP; + case "MEASURE": MEASURE; + default: BEAT; + } + if (event.params[3] != null) camZoomingOffset = event.params[3]; case "Camera Flash": var camera:FlxCamera = event.params[3] == "camHUD" ? camHUD : camGame; @@ -1989,13 +2026,6 @@ class PlayState extends MusicBeatState { super.beatHit(curBeat); - if (camZoomingInterval < 1) camZoomingInterval = 1; - if (Options.camZoomOnBeat && camZooming && FlxG.camera.zoom < maxCamZoom && curBeat % camZoomingInterval == 0) - { - FlxG.camera.zoom += Flags.CAM_BOP_STRENGTH * camZoomingStrength; - camHUD.zoom += Flags.HUD_BOP_STRENGTH * camZoomingStrength; - } - if (doIconBop) for (icon in iconArray) if (icon.bump != null) diff --git a/source/funkin/game/SplashGroup.hx b/source/funkin/game/SplashGroup.hx index 901fc99d..77c807f7 100644 --- a/source/funkin/game/SplashGroup.hx +++ b/source/funkin/game/SplashGroup.hx @@ -37,6 +37,7 @@ class SplashGroup extends FlxTypedGroup<Splash> { // immediately draw once and put image in GPU to prevent freezes // TODO: change to graphics cache + @:privateAccess splash.drawComplex(FlxG.camera); } catch(e:Dynamic) { Logs.trace('Couldn\'t parse splash data for "${path}": ${e.toString()}', ERROR); @@ -45,14 +46,17 @@ class SplashGroup extends FlxTypedGroup<Splash> { maxSize = Flags.MAX_SPLASHES; } + var _scale:Float = 1.0; + var _alpha:Float = 1.0; + var _antialiasing:Bool = true; + function createSplash(imagePath:String) { var splash = new Splash(); - splash.antialiasing = true; splash.active = splash.visible = false; splash.loadSprite(Paths.image(imagePath)); - if (xml.has.scale) splash.scale.scale(Std.parseFloat(xml.att.scale).getDefault(1)); - if (xml.has.alpha) splash.alpha = Std.parseFloat(xml.att.alpha).getDefault(1); - if (xml.has.antialiasing) splash.antialiasing = xml.att.antialiasing == "true"; + _scale = xml.has.scale ? Std.parseFloat(xml.att.scale).getDefault(1) : 1; + _alpha = xml.has.alpha ? Std.parseFloat(xml.att.alpha).getDefault(1) : 1; + _antialiasing = xml.has.antialiasing ? xml.att.antialiasing == "true" : true; return splash; } @@ -82,6 +86,8 @@ class SplashGroup extends FlxTypedGroup<Splash> { } splash.animation.finishCallback = function(name:String) { splash.active = splash.visible = false; + splash.strum = null; + splash.strumID = null; }; } @@ -101,8 +107,9 @@ class SplashGroup extends FlxTypedGroup<Splash> { public function getSplashAnim(id:Int):String { if (animationNames.length <= 0) return null; id %= animationNames.length; - if (animationNames[id] == null || animationNames[id].length <= 0) return null; - return animationNames[id][FlxG.random.int(0, animationNames[id].length - 1)]; + var animNames = animationNames[id]; + if (animNames == null || animNames.length <= 0) return null; + return animNames[FlxG.random.int(0, animNames.length - 1)]; } var __splash:Splash; @@ -113,8 +120,13 @@ class SplashGroup extends FlxTypedGroup<Splash> { __splash.strum = strum; __splash.strumID = strum.ID; + @:privateAccess + __splash.scale.x = __splash.scale.y = _scale * strum.strumLine.strumScale; + __splash.alpha = _alpha; + __splash.antialiasing = _antialiasing; + __splash.cameras = strum.lastDrawCameras; - __splash.setPosition(strum.x + ((strum.width - __splash.width) / 2), strum.y + ((strum.height - __splash.height) / 2)); + __splash.setPosition(strum.x + 0.5 * (strum.width - __splash.width), strum.y + 0.5 * (strum.height - __splash.height)); __splash.active = __splash.visible = true; __splash.playAnim(getSplashAnim(strum.ID), true); __splash.scrollFactor.set(strum.scrollFactor.x, strum.scrollFactor.y); diff --git a/source/funkin/game/Stage.hx b/source/funkin/game/Stage.hx index fabf383e..40475f15 100644 --- a/source/funkin/game/Stage.hx +++ b/source/funkin/game/Stage.hx @@ -1,5 +1,6 @@ package funkin.game; +import flixel.util.typeLimit.OneOfTwo; import flixel.FlxState; import flixel.math.FlxPoint; import haxe.xml.Access; @@ -18,9 +19,12 @@ using StringTools; * A class that handles loading a stage and putting the sprites into the state. **/ class Stage extends FlxBasic implements IBeatReceiver { - public var stageName:String = ""; + public var extra:Map<String, Dynamic> = []; + public var stageXML:Access; public var stagePath:String; + public var stageFile:String; + public var stageName:String; public var stageSprites:Map<String, FlxSprite> = []; public var stageScript:Script; public var state:FlxState; @@ -30,12 +34,15 @@ class Stage extends FlxBasic implements IBeatReceiver { public var defaultZoom:Float = 1.05; public var startCam = new FlxPoint(); - private var spritesParentFolder = ""; + public var onXMLLoaded:(Access, Array<Access>)->Array<Access> = null; + public var onNodeLoaded:(Access, Dynamic)->Dynamic = null; + public var onNodeFinished:(Access, Dynamic)->Void = null; + public var onAddSprite:(FlxObject)->Void = null; + public var onXMLPostLoaded:(Access, Array<Access>)->Array<Access> = null; - /** - * Gets a sprite from the stage. - **/ - public function getSprite(name:String) + public var spritesParentFolder = ""; + + public inline function getSprite(name:String) return stageSprites[name]; /** @@ -45,55 +52,71 @@ class Stage extends FlxBasic implements IBeatReceiver { for (k=>e in stageSprites) script.set(k, e); public function prepareInfos(node:Access) - return XMLImportedScriptInfo.prepareInfos(node, PlayState.instance != null ? PlayState.instance.scripts : null, (infos) -> xmlImportedScripts.push(infos)); + return PlayState.instance == null ? null : XMLImportedScriptInfo.prepareInfos(node, PlayState.instance.scripts, (infos) -> xmlImportedScripts.push(infos)); - public function new(stage:String, ?state:FlxState) { + public function new(stage:String, ?state:FlxState, autoLoad:Bool = true) { super(); if (state == null) state = PlayState.instance; if (state == null) state = FlxG.state; this.state = state; - stagePath = Paths.xml('stages/$stage'); + stageFile = stage; + stagePath = Paths.xml('stages/$stageFile'); try if (Assets.exists(stagePath)) stageXML = new Access(Xml.parse(Assets.getText(stagePath)).firstElement()) - catch(e) Logs.trace('Couldn\'t load stage "$stage": ${e.message}', ERROR); + catch(e) Logs.trace('Couldn\'t load stage "$stageFile": ${e.message}', ERROR); - if (PlayState.instance != null) { - stageScript = Script.create(Paths.script('data/stages/$stage')); + if (autoLoad) loadXml(stageXML); + } + + public static var DEFAULT_ATTRIBUTES:Array<String> = ["name", "startCamPosX", "startCamPosY", "zoom", "folder"]; + + public function loadXml(xml:Access, forceLoadAll:Bool = false) { + if (PlayState.instance == state) { + stageScript = Script.create(Paths.script('data/stages/$stageFile')); PlayState.instance.scripts.add(stageScript); stageScript.load(); } var event = null; - if (stageXML != null) { + var elems:Array<Access> = []; + if (xml != null) { var parsed:Null<Float>; - if((parsed = Std.parseFloat(stageXML.getAtt("startCamPosX"))).isNotNull()) startCam.x = parsed; - if((parsed = Std.parseFloat(stageXML.getAtt("startCamPosY"))).isNotNull()) startCam.y = parsed; - if((parsed = Std.parseFloat(stageXML.getAtt("zoom"))).isNotNull()) defaultZoom = parsed; + if((parsed = Std.parseFloat(xml.getAtt("startCamPosX"))).isNotNull()) startCam.x = parsed; + if((parsed = Std.parseFloat(xml.getAtt("startCamPosY"))).isNotNull()) startCam.y = parsed; + if((parsed = Std.parseFloat(xml.getAtt("zoom"))).isNotNull()) defaultZoom = parsed; - stageName = stageXML.getAtt("name").getDefault(stage); + stageName = xml.getAtt("name").getDefault(stageFile); if (PlayState.instance == state) { - if(stageXML.has.startCamPosX) PlayState.instance.camFollow.x = startCam.x; - if(stageXML.has.startCamPosY) PlayState.instance.camFollow.y = startCam.y; - if(stageXML.has.zoom) PlayState.instance.defaultCamZoom = defaultZoom; + if(xml.has.startCamPosX) PlayState.instance.camFollow.x = startCam.x; + if(xml.has.startCamPosY) PlayState.instance.camFollow.y = startCam.y; + if(xml.has.zoom) PlayState.instance.defaultCamZoom = defaultZoom; } - - if (stageXML.has.folder) { - spritesParentFolder = stageXML.att.folder; + if (xml.has.folder) { + spritesParentFolder = xml.att.folder; if (!spritesParentFolder.endsWith("/")) spritesParentFolder += "/"; } - var elems = []; - for(node in stageXML.elements) { - if (node.name == "high-memory" && !Options.lowMemoryMode) for(e in node.elements) __pushNcheckNode(elems, e); + // Load custom attributes + for(att in xml.x.attributes()) + if(!DEFAULT_ATTRIBUTES.contains(att)) + extra.set(att, xml.x.get(att)); + + // some way to tag that the sprites are from the group + for(node in xml.elements) { + if (node.name == "high-memory" && (!Options.lowMemoryMode || forceLoadAll)) for(e in node.elements) __pushNcheckNode(elems, e); + else if (node.name == "low-memory" && (Options.lowMemoryMode || forceLoadAll)) for(e in node.elements) __pushNcheckNode(elems, e); else __pushNcheckNode(elems, node); } - if (PlayState.instance != null) { + if (PlayState.instance == state) { event = EventManager.get(StageXMLEvent).recycle(this, stageXML, elems); elems = PlayState.instance.gameAndCharsEvent("onStageXMLParsed", event).elems; } + if(onXMLLoaded != null) { + elems = onXMLLoaded(xml, elems); + } for(node in elems) { var sprite:Dynamic = switch(node.name) { @@ -102,66 +125,37 @@ class Stage extends FlxBasic implements IBeatReceiver { var spr = XMLUtil.createSpriteFromXML(node, spritesParentFolder, LOOP); - if (!node.has.zoomfactor && PlayState.instance != null) - spr.initialZoom = PlayState.instance.defaultCamZoom; - stageSprites.set(spr.name, spr); - state.add(spr); - spr; + addSprite(spr); case "box" | "solid": if (!node.has.name || !node.has.width || !node.has.height) continue; - var spr = new FunkinSprite( - (node.has.x) ? Std.parseFloat(node.att.x).getDefault(0) : 0, - (node.has.y) ? Std.parseFloat(node.att.y).getDefault(0) : 0 - ); - - // just to make sure, its better to apply these before it gets inside of xmlUtil (because of mainly updateHitbox i guess), so im removing them before it gets passed - Nex - var toRemove = ["x", "y", "width", "height", "color"]; var isSolid = node.name == "solid"; + var spr = new FunkinSprite(); (isSolid ? spr.makeSolid : spr.makeGraphic)( Std.parseInt(node.att.width), Std.parseInt(node.att.height), (node.has.color) ? CoolUtil.getColorFromDynamic(node.att.color) : -1 ); - if (isSolid) toRemove.push("updateHitbox"); // makesolid already calls updateHitbox - Nex - for (a in toRemove) node.x.remove(a); + if (isSolid) node.x.remove("updateHitbox"); + for (a in ["width", "height", "color"]) node.x.remove(a); XMLUtil.loadSpriteFromXML(spr, node, "", NONE, false); - if (!node.has.zoomfactor && PlayState.instance != null) - spr.initialZoom = PlayState.instance.defaultCamZoom; - - stageSprites.set(node.getAtt("name"), spr); - state.add(spr); - spr; + stageSprites.set(spr.name, spr); + addSprite(spr); case "boyfriend" | "bf" | "player": - addCharPos("boyfriend", node, { - x: 770, - y: 100, - scroll: 1, - flip: true - }); + addCharPos("boyfriend", node, getDefaultPos("boyfriend")); case "girlfriend" | "gf": - addCharPos("girlfriend", node, { - x: 400, - y: 130, - scroll: 0.95, - flip: false - }); + addCharPos("girlfriend", node, getDefaultPos("girlfriend")); case "dad" | "opponent": - addCharPos("dad", node, { - x: 100, - y: 100, - scroll: 1, - flip: false - }); + addCharPos("dad", node, getDefaultPos("dad")); case "character" | "char": if (!node.has.name) continue; addCharPos(node.att.name, node); case "ratings" | "combo": - if (PlayState.instance == null) continue; + if (PlayState.instance != state) continue; PlayState.instance.comboGroup.setPosition( Std.parseFloat(node.getAtt("x")).getDefault(PlayState.instance.comboGroup.x), Std.parseFloat(node.getAtt("y")).getDefault(PlayState.instance.comboGroup.y) @@ -174,61 +168,85 @@ class Stage extends FlxBasic implements IBeatReceiver { default: null; } - if(PlayState.instance != null) { + if(PlayState.instance == state) { sprite = PlayState.instance.gameAndCharsEvent("onStageNodeParsed", EventManager.get(StageNodeEvent).recycle(this, node, sprite, node.name)).sprite; } + if(onNodeLoaded != null) { + sprite = onNodeLoaded(node, sprite); + } if (sprite != null) { for(e in node.nodes.property) XMLUtil.applyXMLProperty(sprite, e); } + + if(onNodeFinished != null) { + onNodeFinished(node, sprite); + } } } if (characterPoses["girlfriend"] == null) - addCharPos("girlfriend", null, { - x: 400, - y: 130, - scroll: 0.95, - flip: false - }); + addCharPos("girlfriend", null, getDefaultPos("girlfriend")); if (characterPoses["dad"] == null) - addCharPos("dad", null, { - x: 100, - y: 100, - scroll: 1, - flip: false - }); + addCharPos("dad", null, getDefaultPos("dad")); if (characterPoses["boyfriend"] == null) - addCharPos("boyfriend", null, { + addCharPos("boyfriend", null, getDefaultPos("boyfriend")); + + if (PlayState.instance == state) { + setStagesSprites(stageScript); + + // i know this for gets run twice under, but its better like this in case a script modifies the short lived ones, i dont wanna save them in an array; more dynamic like this - Nex + for (info in xmlImportedScripts) if (info.importStageSprites) { + var script = info.getScript(); + if (script != null) setStagesSprites(script); + } + + // idk lemme check anyways just in case scripts did smth - Nex + if (event != null) PlayState.instance.scripts.event("onPostStageCreation", event); + + // shortlived scripts destroy when the stage finishes setting up - Nex + for (info in xmlImportedScripts) if (info.shortLived) { + var script = info.getScript(); + if (script == null) continue; + + PlayState.instance.scripts.remove(script); + script.destroy(); + } + } + if(onXMLPostLoaded != null) { + elems = onXMLPostLoaded(xml, elems); + } + } + + public static function getDefaultPos(name:String):StageCharPosInfo { + return switch(name) { + case "boyfriend" | "bf" | "player": { x: 770, y: 100, scroll: 1, flip: true - }); - - if (PlayState.instance == null) return; - - setStagesSprites(stageScript); - - // i know this for gets run twice under, but its better like this in case a script modifies the short lived ones, i don't wanna save them in an array; more dynamic like this - Nex - for (info in xmlImportedScripts) if (info.importStageSprites) { - var script = info.getScript(); - if (script != null) setStagesSprites(script); - } - - // idk lemme check anyways just in case scripts did smth - Nex - if (event != null) PlayState.instance.gameAndCharsEvent("onPostStageCreation", event); - - // shortlived scripts destroy when the stage finishes setting up - Nex - for (info in xmlImportedScripts) if (info.shortLived) { - var script = info.getScript(); - if (script == null) continue; - - PlayState.instance.scripts.remove(script); - script.destroy(); + }; + case "girlfriend" | "gf": { + x: 400, + y: 130, + scroll: 0.95, + flip: false + }; + case "dad" | "opponent": { + x: 100, + y: 100, + scroll: 1, + flip: false + }; + default: { + x: 0, + y: 0, + scroll: 1, + flip: false + }; } } @@ -247,6 +265,7 @@ class Stage extends FlxBasic implements IBeatReceiver { public function addCharPos(name:String, node:Access, ?nonXMLInfo:StageCharPosInfo):StageCharPos { var charPos = new StageCharPos(); charPos.visible = charPos.active = false; + charPos.name = name; if (nonXMLInfo != null) { charPos.setPosition(nonXMLInfo.x, nonXMLInfo.y); @@ -257,33 +276,51 @@ class Stage extends FlxBasic implements IBeatReceiver { if (node != null) { charPos.x = Std.parseFloat(node.getAtt("x")).getDefault(charPos.x); charPos.y = Std.parseFloat(node.getAtt("y")).getDefault(charPos.y); + charPos.charSpacingX = Std.parseFloat(node.getAtt("spacingx")).getDefault(charPos.charSpacingX); + charPos.charSpacingY = Std.parseFloat(node.getAtt("spacingy")).getDefault(charPos.charSpacingY); charPos.camxoffset = Std.parseFloat(node.getAtt("camxoffset")).getDefault(charPos.camxoffset); charPos.camyoffset = Std.parseFloat(node.getAtt("camyoffset")).getDefault(charPos.camyoffset); charPos.skewX = Std.parseFloat(node.getAtt("skewx")).getDefault(charPos.skewX); charPos.skewY = Std.parseFloat(node.getAtt("skewy")).getDefault(charPos.skewY); charPos.alpha = Std.parseFloat(node.getAtt("alpha")).getDefault(charPos.alpha); + charPos.angle = Std.parseFloat(node.getAtt("angle")).getDefault(charPos.angle); charPos.flipX = (node.has.flip || node.has.flipX) ? (node.getAtt("flip") == "true" || node.getAtt("flipX") == "true") : charPos.flipX; + charPos.zoomFactor = Std.parseFloat(node.getAtt("zoomfactor")).getDefault(charPos.zoomFactor); - var scale = Std.parseFloat(node.getAtt("scale")).getDefault(charPos.scale.x); - charPos.scale.set(scale, scale); + if (node.has.scale) { + var scale:Null<Float> = Std.parseFloat(node.att.scale); + if (scale.isNotNull()) charPos.scale.set(scale, scale); + } + if (node.has.scalex) { + var scale:Null<Float> = Std.parseFloat(node.att.scalex); + if (scale.isNotNull()) charPos.scale.x = scale; + } + if (node.has.scaley) { + var scale:Null<Float> = Std.parseFloat(node.att.scaley); + if (scale.isNotNull()) charPos.scale.y = scale; + } if (node.has.scroll) { var scroll:Null<Float> = Std.parseFloat(node.att.scroll); if (scroll != null) charPos.scrollFactor.set(scroll, scroll); - } else { - if (node.has.scrollx) { - var scroll:Null<Float> = Std.parseFloat(node.att.scrollx); - if (scroll != null) charPos.scrollFactor.x = scroll; - } - if (node.has.scrolly) { - var scroll:Null<Float> = Std.parseFloat(node.att.scrolly); - if (scroll != null) charPos.scrollFactor.y = scroll; - } + } + if (node.has.scrollx) { + var scroll:Null<Float> = Std.parseFloat(node.att.scrollx); + if (scroll != null) charPos.scrollFactor.x = scroll; + } + if (node.has.scrolly) { + var scroll:Null<Float> = Std.parseFloat(node.att.scrolly); + if (scroll != null) charPos.scrollFactor.y = scroll; } } - state.add(charPos); - return characterPoses[name] = charPos; + return addSprite(characterPoses[name] = charPos); + } + + function addSprite<T:FlxObject>(sprite:T):T { + state.add(sprite); + if(onAddSprite != null) onAddSprite(sprite); + return sprite; } /** @@ -326,19 +363,27 @@ class Stage extends FlxBasic implements IBeatReceiver { * Gets a list of stages that are available to be used. * @param mods Whenever only the mods folder should be checked **/ - public static function getList(?mods:Bool = false):Array<String> { + public static function getList(?mods:Bool = false, ?xmlOnly:Bool = false):Array<String> { var list:Array<String> = []; - for (path in Paths.getFolderContent('data/stages/', true, mods ? MODS : BOTH)) - if (Path.extension(path) == "xml" || Path.extension(path) == "hx") { - var file:String = Path.withoutDirectory(Path.withoutExtension(path)); - list.pushOnce(file); + var extensions:Array<String> = ["xml"]; + if (!xmlOnly) extensions.push("hx"); + + for (path in Paths.getFolderContent("data/stages/", false, mods ? MODS : BOTH)) { + var extension = Path.extension(path); + if (extensions.contains(extension)) { + list.pushOnce("test"); + list.pushOnce(Path.withoutExtension(path)); } + } return list; } } class StageCharPos extends FlxObject { + public var extra:Map<String, Dynamic> = []; + + public var name:String; public var charSpacingX:Float = 20; public var charSpacingY:Float = 0; public var camxoffset:Float = 0; @@ -348,6 +393,7 @@ class StageCharPos extends FlxObject { public var alpha:Float = 1; public var flipX:Bool = false; public var scale:FlxPoint = FlxPoint.get(1, 1); + public var zoomFactor:Float = 1; public function new() { super(); @@ -360,13 +406,52 @@ class StageCharPos extends FlxObject { super.destroy(); } + private var _id:Float = -1; + + private var oldInfo:OldCharInfo = null; + public function prepareCharacter(char:Character, id:Float = 0) { + _id = id; + oldInfo = getOldInfo(char); char.setPosition(x + (id * charSpacingX), y + (id * charSpacingY)); char.scrollFactor.set(scrollFactor.x, scrollFactor.y); - char.scale.x *= scale.x; char.scale.y *= scale.y; + if (!char.debugMode) + char.scale.x *= scale.x; char.scale.y *= scale.y; char.cameraOffset += FlxPoint.weak(camxoffset, camyoffset); char.skew.x += skewX; char.skew.y += skewY; char.alpha *= alpha; + char.angle += angle; + char.zoomFactor *= zoomFactor; + } + + public function getOldInfo(char:Character) { + return { + x: char.x, y: char.y, + scrollX: char.scrollFactor.x, scrollY: char.scrollFactor.y, + scaleX: char.scale.x, scaleY: char.scale.y, + camxoffset: char.cameraOffset.x, camyoffset: char.cameraOffset.y, + skewX: char.skew.x, skewY: char.skew.y, + alpha: char.alpha, zoomFactor: char.zoomFactor, + angle: char.angle + } + } + + public function revertCharacter(char:Character) { + if(oldInfo == null) return; + for(field in Reflect.fields(oldInfo)) { + switch(field) { + case "scrollX": char.scrollFactor.x = oldInfo.scrollX; + case "scrollY": char.scrollFactor.y = oldInfo.scrollY; + case "scaleX": char.scale.x = oldInfo.scaleX; + case "scaleY": char.scale.y = oldInfo.scaleY; + case "camxoffset": char.cameraOffset.x = oldInfo.camxoffset; + case "camyoffset": char.cameraOffset.y = oldInfo.camyoffset; + case "skewX": char.skew.x = oldInfo.skewX; + case "skewY": char.skew.y = oldInfo.skewY; + default: Reflect.setProperty(char, field, Reflect.field(oldInfo, field)); + } + } + oldInfo = null; } } typedef StageCharPosInfo = { @@ -375,3 +460,19 @@ typedef StageCharPosInfo = { var flip:Bool; var scroll:Float; } + +typedef OldCharInfo = { + var x:Float; + var y:Float; + var scrollX:Float; + var scrollY:Float; + var scaleX:Float; + var scaleY:Float; + var camxoffset:Float; + var camyoffset:Float; + var skewX:Float; + var skewY:Float; + var alpha:Float; + var zoomFactor:Float; + var angle:Float; +} diff --git a/source/funkin/game/Strum.hx b/source/funkin/game/Strum.hx index b2f412d8..d5031945 100644 --- a/source/funkin/game/Strum.hx +++ b/source/funkin/game/Strum.hx @@ -27,7 +27,7 @@ class Strum extends FlxSprite { /** * The strum line that this strum belongs to. **/ - public var strumLine:StrumLine; + public var strumLine:StrumLine = null; /** * The scroll speed of the notes. @@ -68,7 +68,7 @@ class Strum extends FlxSprite { public var getJustReleased:StrumLine->Bool = null; @:dox(hide) public inline function __getPressed(strumLine:StrumLine):Bool { - return getPressed != null ? getPressed(strumLine) : switch(ID) { + return getPressed != null ? getPressed(strumLine) : strumLine.members.length != 4 ? ControlsUtil.getPressed(strumLine.controls, strumLine.members.length+"k"+ID) : switch(ID) { case 0: strumLine.controls.NOTE_LEFT; case 1: strumLine.controls.NOTE_DOWN; case 2: strumLine.controls.NOTE_UP; @@ -77,7 +77,7 @@ class Strum extends FlxSprite { } } @:dox(hide) public inline function __getJustPressed(strumLine:StrumLine) { - return getJustPressed != null ? getJustPressed(strumLine) : switch(ID) { + return getJustPressed != null ? getJustPressed(strumLine) : strumLine.members.length != 4 ? ControlsUtil.getJustPressed(strumLine.controls, strumLine.members.length+"k"+ID) : switch(ID) { case 0: strumLine.controls.NOTE_LEFT_P; case 1: strumLine.controls.NOTE_DOWN_P; case 2: strumLine.controls.NOTE_UP_P; @@ -86,7 +86,7 @@ class Strum extends FlxSprite { } } @:dox(hide) public inline function __getJustReleased(strumLine:StrumLine) { - return getJustReleased != null ? getJustReleased(strumLine) : switch(ID) { + return getJustReleased != null ? getJustReleased(strumLine) : strumLine.members.length != 4 ? ControlsUtil.getJustReleased(strumLine.controls, strumLine.members.length+"k"+ID) : switch(ID) { case 0: strumLine.controls.NOTE_LEFT_R; case 1: strumLine.controls.NOTE_DOWN_R; case 2: strumLine.controls.NOTE_UP_R; @@ -132,7 +132,7 @@ class Strum extends FlxSprite { super.draw(); } - @:noCompletion public static final PIX180:Float = 565.4866776461628; // 180 * Math.PI + @:noCompletion public static inline final PIX180:Float = 565.4866776461628; // 180 * Math.PI @:noCompletion public static final N_WIDTHDIV2:Float = Note.swagWidth / 2; /** diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index d126b371..27b998d3 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -354,7 +354,7 @@ class StrumLine extends FlxTypedGroup<Strum> { public function createStrum(i:Int, ?animPrefix:String, ?spritePath:String, ?playIntroAnimation:Bool) { if (animPrefix == null) animPrefix = strumAnimPrefix[i % strumAnimPrefix.length]; - var babyArrow:Strum = new Strum(startingPos.x + ((Note.swagWidth * strumScale) * i), startingPos.y); + var babyArrow:Strum = new Strum(startingPos.x + (Note.swagWidth * strumScale * (data.strumSpacing != null ? data.strumSpacing : 1) * i), startingPos.y + (Note.swagWidth*0.5) - (Note.swagWidth * strumScale * 0.5)); babyArrow.ID = i; babyArrow.strumLine = this; @@ -389,7 +389,7 @@ class StrumLine extends FlxTypedGroup<Strum> { { babyArrow.y -= 10; babyArrow.alpha = 0; - FlxTween.tween(babyArrow, {y: babyArrow.y + 10, alpha: 1}, 1, {ease: FlxEase.circOut, startDelay: 0.5 + (0.2 * i)}); + FlxTween.tween(babyArrow, {y: babyArrow.y + 10, alpha: 1}, 1, {ease: FlxEase.circOut, startDelay: 0.5 + (0.2 * i * (4 / (data.keyCount != null ? data.keyCount : 4)))}); } babyArrow.playAnim('static'); @@ -415,6 +415,10 @@ class StrumLine extends FlxTypedGroup<Strum> { } } + public static inline function calculateStartingXPos(hudXRatio:Float, scale:Float, spacing:Float, keyCount:Int) { + return (FlxG.width * hudXRatio) - ((Note.swagWidth * scale * ((keyCount/2)-0.5) * spacing) + Note.swagWidth * 0.5 * scale); + } + /** * SETTERS & GETTERS */ diff --git a/source/funkin/menus/FreeplayState.hx b/source/funkin/menus/FreeplayState.hx index 17b79551..bed77535 100644 --- a/source/funkin/menus/FreeplayState.hx +++ b/source/funkin/menus/FreeplayState.hx @@ -229,7 +229,7 @@ class FreeplayState extends MusicBeatState } scoreText.text = "PERSONAL BEST:" + lerpScore; - scoreBG.scale.set(Math.max(Math.max(diffText.width, scoreText.width), coopText.width) + 8, (coopText.visible ? coopText.y + coopText.height : 66)); + scoreBG.scale.set(MathUtil.maxSmart(diffText.width, scoreText.width, coopText.width) + 8, (coopText.visible ? coopText.y + coopText.height : 66)); scoreBG.updateHitbox(); scoreBG.x = FlxG.width - scoreBG.width; @@ -287,12 +287,13 @@ class FreeplayState extends MusicBeatState function updateCoopModes() { __opponentMode = false; __coopMode = false; - if (songs[curSelected].coopAllowed && songs[curSelected].opponentModeAllowed) { + var curSong = songs[curSelected]; + if (curSong.coopAllowed && curSong.opponentModeAllowed) { __opponentMode = curCoopMode % 2 == 1; __coopMode = curCoopMode >= 2; - } else if (songs[curSelected].coopAllowed) { + } else if (curSong.coopAllowed) { __coopMode = curCoopMode == 1; - } else if (songs[curSelected].opponentModeAllowed) { + } else if (curSong.opponentModeAllowed) { __opponentMode = curCoopMode == 1; } } diff --git a/source/funkin/options/Options.hx b/source/funkin/options/Options.hx index 7b0c9c91..8a3493ad 100644 --- a/source/funkin/options/Options.hx +++ b/source/funkin/options/Options.hx @@ -40,6 +40,9 @@ class Options public static var songOffset:Float = 0; public static var framerate:Int = 120; public static var gpuOnlyBitmaps:Bool = #if (mac || web) false #else true #end; // causes issues on mac and web + #if MODCHARTING_FEATURES + public static var modchartingHoldSubdivisions:Int = 4; + #end public static var lastLoadedMod:String = null; @@ -49,6 +52,7 @@ class Options public static var intensiveBlur:Bool = true; public static var editorSFX:Bool = true; public static var editorPrettyPrint:Bool = false; + public static var editorsResizable:Bool = true; public static var maxUndos:Int = 120; /** @@ -67,6 +71,7 @@ class Options public static var charterShowSections:Bool = true; public static var charterShowBeats:Bool = true; public static var charterEnablePlaytestScripts:Bool = true; + public static var charterRainbowWaveforms:Bool = false; public static var charterLowDetailWaveforms:Bool = false; public static var charterAutoSaves:Bool = true; public static var charterAutoSaveTime:Float = 60*5; @@ -74,10 +79,18 @@ class Options public static var charterAutoSavesSeparateFolder:Bool = false; /** - * PLAYER 1 CONTROLS - */ + * CHARACTER EDITOR + */ + public static var stageSelected:String = null; + public static var characterHitbox:Bool = true; + public static var characterCamera:Bool = true; + public static var characterAxis:Bool = true; + public static var characterDragging:Bool = true; + public static var playAnimOnOffset:Bool = false; - // Notes + /** + * PLAYER 1 CONTROLS + */ public static var P1_NOTE_LEFT:Array<FlxKey> = [A]; public static var P1_NOTE_DOWN:Array<FlxKey> = [S]; public static var P1_NOTE_UP:Array<FlxKey> = [W]; diff --git a/source/funkin/options/OptionsScreen.hx b/source/funkin/options/OptionsScreen.hx index b703d994..4a5eb806 100644 --- a/source/funkin/options/OptionsScreen.hx +++ b/source/funkin/options/OptionsScreen.hx @@ -36,7 +36,7 @@ class OptionsScreen extends FlxTypedSpriteGroup<OptionType> { option.selected = false; option.y = __firstFrame ? y : CoolUtil.fpsLerp(option.y, y, 0.25); - option.x = x + (-50 + (Math.abs(Math.cos((option.y + (optionHeight / 2) - (FlxG.camera.scroll.y + (FlxG.height / 2))) / (FlxG.height * 1.25) * Math.PI)) * 150)); + option.x = x + (-50 + (Math.abs(Math.cos((option.y + (optionHeight / 2) - (FlxG.camera.scroll.y + (FlxG.camera.height / 2))) / (FlxG.camera.height * 1.25) * Math.PI)) * 150)); } if (__firstFrame) { __firstFrame = false; diff --git a/source/funkin/options/TreeMenu.hx b/source/funkin/options/TreeMenu.hx index 92540656..b7bbde3e 100644 --- a/source/funkin/options/TreeMenu.hx +++ b/source/funkin/options/TreeMenu.hx @@ -17,17 +17,23 @@ class TreeMenu extends UIState { public var pathDesc:FunkinText; public var pathBG:FlxSprite; + public var screenScroll(default, set):Float; + private inline function set_screenScroll(val:Float) { + FlxG.camera.scroll.x = val * FlxG.camera.width; + return screenScroll = val; + } + public static var lastState:Class<FlxState> = null; // Static for fixing the softlock bugs when resetting the state - Nex public function new(scriptsAllowed:Bool = true, ?scriptName:String) { - if(lastState == null) lastState = Type.getClass(FlxG.state); + if (lastState == null) lastState = Type.getClass(FlxG.state); super(scriptsAllowed, scriptName); } public override function createPost() { if (main == null) main = new OptionsScreen("Fallback Treemenu", "Please set the \"main\" variable in your extended class before createPost", [new TextOption("Oops! No Options", "This doesn't look like it was supposed to happen...", () -> FlxG.resetState() ) ]); - FlxG.camera.scroll.set(-FlxG.width, 0); + screenScroll = -1; pathLabel = new FunkinText(4, 4, FlxG.width - 8, "> Tree Menu", 32, true); pathLabel.borderSize = 1.25; @@ -65,10 +71,10 @@ class TreeMenu extends UIState { if (menuChangeTween != null) menuChangeTween.cancel(); - menuChangeTween = FlxTween.tween(FlxG.camera.scroll, {x: FlxG.width * Math.max(0, (optionsTree.members.length-1))}, 1.5, {ease: menuTransitionEase, onComplete: function(t) { + menuChangeTween = FlxTween.num(screenScroll, Math.max(0, (optionsTree.members.length-1)), 1.5, {ease: menuTransitionEase, onComplete: function(t) { optionsTree.clearLastMenu(); menuChangeTween = null; - }}); + }}, (val:Float) -> screenScroll = val); var t = ""; for(o in optionsTree.members) @@ -99,13 +105,27 @@ class TreeMenu extends UIState { var menuChangeTween:FlxTween; public override function update(elapsed:Float) { super.update(elapsed); - + Framerate.offset.y = pathBG.height; // in case path gets so long it goes offscreen pathLabel.x = lerp(pathLabel.x, Math.max(0, FlxG.width - 4 - pathLabel.width), 0.125); } + public override function onResize(width:Int, height:Int) { + super.onResize(width, height); + if (!UIState.resolutionAware) return; + + if (width < FlxG.initialWidth || height < FlxG.initialHeight) { + width = FlxG.initialWidth; height = FlxG.initialHeight; + } + + screenScroll = screenScroll; // Updating the cam position - Nex + + if (pathDesc != null && pathLabel != null) + pathDesc.width = pathLabel.width = width - 8; + } + public static inline function menuTransitionEase(e:Float) return FlxEase.quintInOut(FlxEase.cubeOut(e)); } diff --git a/source/funkin/options/categories/GameplayOptions.hx b/source/funkin/options/categories/GameplayOptions.hx index 15c0ba60..8bd3c49b 100644 --- a/source/funkin/options/categories/GameplayOptions.hx +++ b/source/funkin/options/categories/GameplayOptions.hx @@ -53,7 +53,7 @@ class GameplayOptions extends OptionsScreen { __lastBeat = Conductor.curBeat; } - var beat = Math.floor(Conductor.getStepForTime(FlxG.sound.music.time) / Conductor.stepsPerBeat); + var beat = Math.floor(Conductor.getTimeInBeats(FlxG.sound.music.time)); if (__lastSongBeat != beat) { __metronome.replay(); __lastSongBeat = beat; diff --git a/source/funkin/options/keybinds/KeybindSetting.hx b/source/funkin/options/keybinds/KeybindSetting.hx index 8201f013..5250fbc5 100644 --- a/source/funkin/options/keybinds/KeybindSetting.hx +++ b/source/funkin/options/keybinds/KeybindSetting.hx @@ -15,16 +15,19 @@ class KeybindSetting extends FlxTypedSpriteGroup<FlxSprite> { public var p2Selected:Bool = false; public var value:String; + public var custom:Bool = false; + public var option1:Null<FlxKey>; public var option2:Null<FlxKey>; - public function new(x:Float, y:Float, name:String, value:String, ?sparrowIcon:String, ?sparrowAnim:String) { + public function new(x:Float, y:Float, name:String, value:String, ?sparrowIcon:String, ?sparrowAnim:String, ?custom:Bool = false) { super(); this.value = value; + this.custom = custom; title = new Alphabet(0, 0, name, true); add(title); - var controlArrayP1:Array<FlxKey> = Reflect.field(Options, 'P1_${value}'); - var controlArrayP2:Array<FlxKey> = Reflect.field(Options, 'P2_${value}'); + var controlArrayP1:Array<FlxKey> = custom ? [Reflect.getProperty(FlxG.save.data, 'P1_${value}')] : Reflect.field(Options, 'P1_${value}'); + var controlArrayP2:Array<FlxKey> = custom ? [Reflect.getProperty(FlxG.save.data, 'P2_${value}')] : Reflect.field(Options, 'P2_${value}'); option1 = controlArrayP1[0]; option2 = controlArrayP2[0]; @@ -76,19 +79,35 @@ class KeybindSetting extends FlxTypedSpriteGroup<FlxSprite> { KeybindsOptions.instance.openSubState(new ChangeKeybindSubState(function(key:FlxKey) { flicker.stop(); flicker.destroy(); - if (p2) - Reflect.setField(Options, 'P2_$value', [option2 = key]); - else - Reflect.setField(Options, 'P1_$value', [option1 = key]); + if (!custom) { + if (p2) + Reflect.setField(Options, 'P2_$value', [option2 = key]); + else + Reflect.setField(Options, 'P1_$value', [option1 = key]); + } else { + if (p2) + Reflect.setProperty(FlxG.save.data, 'P2_$value', option2 = key); + else + Reflect.setProperty(FlxG.save.data, 'P1_$value', option1 = key); + } + updateText(); callback(); }, function() { flicker.stop(); flicker.destroy(); - if (p2) - Reflect.setField(Options, 'P2_$value', [option2 = 0]); - else - Reflect.setField(Options, 'P1_$value', [option1 = 0]); + if (!custom) { + if (p2) + Reflect.setField(Options, 'P2_$value', [option2 = 0]); + else + Reflect.setField(Options, 'P1_$value', [option1 = 0]); + } else { + if (p2) + Reflect.setProperty(FlxG.save.data, 'P2_$value', option2 = 0); + else + Reflect.setProperty(FlxG.save.data, 'P1_$value', option1 = 0); + } + updateText(); cancelCallback(); })); } diff --git a/source/funkin/options/keybinds/KeybindsOptions.hx b/source/funkin/options/keybinds/KeybindsOptions.hx index 1f8c3b43..861296ba 100644 --- a/source/funkin/options/keybinds/KeybindsOptions.hx +++ b/source/funkin/options/keybinds/KeybindsOptions.hx @@ -1,23 +1,14 @@ package funkin.options.keybinds; import flixel.util.FlxColor; - +import haxe.xml.Access; using StringTools; -typedef KeybindsCategory = { - var name:String; - var settings:Array<KeybindsSettings>; - var ?devModeOnly:Bool; -} -typedef KeybindsSettings = { - var name:String; - var control:String; -} class KeybindsOptions extends MusicBeatSubstate { public static var instance:KeybindsOptions; - public var categories:Array<KeybindsCategory> = [ + public var categories:Array<ControlsCategory> = [ { name: 'Notes', settings: [ @@ -174,6 +165,9 @@ class KeybindsOptions extends MusicBeatSubstate { FlxG.camera.follow(camFollow, LOCKON, 0.125); } + var customCategories = loadCustomCategories(); + for (i in customCategories) categories.push(i); + var k:Int = 0; for (category in categories) { if (category.devModeOnly && !Options.devMode) continue; @@ -201,8 +195,10 @@ class KeybindsOptions extends MusicBeatSubstate { }; e.name = e.name.substring(5, e.name.length - 1); } + if (e.sparrowIcon != null) sparrowIcon = e.sparrowIcon; + if (e.sparrowAnim != null) sparrowAnim = e.sparrowAnim; - var text = new KeybindSetting(100, k * 75, e.name, e.control, sparrowIcon, sparrowAnim); + var text = new KeybindSetting(100, k * 75, e.name, e.control, sparrowIcon, sparrowAnim, e.custom == null ? false : e.custom); if (!isSubState) text.bind1.color = text.bind2.color = FlxColor.BLACK; alphabets.add(text); @@ -250,7 +246,9 @@ class KeybindsOptions extends MusicBeatSubstate { MusicBeatState.skipTransIn = true; FlxG.switchState(new OptionsMenu()); } + ControlsUtil.resetCustomControls(); Options.applyKeybinds(); + ControlsUtil.loadCustomControls(); Options.save(); return; } @@ -294,9 +292,65 @@ class KeybindsOptions extends MusicBeatSubstate { var minH = FlxG.height / 2; var maxH = alphabets.members[alphabets.length-1].y + alphabets.members[alphabets.length-1].height - (FlxG.height / 2); if (minH < maxH) - camFollow.setPosition(FlxG.width / 2, FlxMath.bound(alphabet.y + (alphabet.height / 2) - (35), minH, maxH)); + camFollow.setPosition(FlxG.width / 2, CoolUtil.bound(alphabet.y + (alphabet.height / 2) - 35, minH, maxH)); else camFollow.setPosition(FlxG.width / 2, FlxG.height / 2); } } + + public function loadCustomCategories() { + var customCategories:Array<ControlsCategory> = []; + + var xmlPath = Paths.xml("config/controls"); + for(source in [funkin.backend.assets.AssetsLibraryList.AssetSource.SOURCE, funkin.backend.assets.AssetsLibraryList.AssetSource.MODS]) { + if (Paths.assetsTree.existsSpecific(xmlPath, "TEXT", source)) { + var access:Access = null; + try { + access = new Access(Xml.parse(Paths.assetsTree.getSpecificAsset(xmlPath, "TEXT", source)).firstElement()); + } catch(e) { + Logs.trace('Error while parsing controls.xml: ${Std.string(e)}', ERROR); + } + + if (access != null) { + for (category in access.elements) { + if (!category.has.name) continue; + + var cat:ControlsCategory = { + name: category.getAtt("name"), + settings: [] + }; + + for (control in category.elements) { + if (control.has.menuName && control.has.saveName) { + cat.settings.push({ + name: control.getAtt("menuName"), + control: control.getAtt("saveName"), + custom: true, + sparrowIcon: control.getAtt("menuIcon").getDefault(null), + sparrowAnim: control.getAtt("menuAnim").getDefault(null) + }); + } + } + + customCategories.push(cat); + } + } + } + } + return customCategories; + } +} + +typedef KeybindSettingData = { + var name:String; + var control:String; + var ?custom:Bool; + var ?sparrowIcon:String; + var ?sparrowAnim:String; +} + +typedef ControlsCategory = { + var name:String; + var settings:Array<KeybindSettingData>; + var ?devModeOnly:Bool; } \ No newline at end of file diff --git a/source/funkin/options/type/ArrayOption.hx b/source/funkin/options/type/ArrayOption.hx index 2d708b1d..55636b7a 100644 --- a/source/funkin/options/type/ArrayOption.hx +++ b/source/funkin/options/type/ArrayOption.hx @@ -34,7 +34,7 @@ class ArrayOption extends OptionType { var fieldValue = Reflect.field(parent, optionName); if(fieldValue != null) - this.currentSelection = Std.int(Math.max(0, options.indexOf(fieldValue))); + this.currentSelection = CoolUtil.maxInt(0, options.indexOf(fieldValue)); this.optionName = optionName; diff --git a/source/funkin/options/type/NewOption.hx b/source/funkin/options/type/NewOption.hx index 6102ca60..28255b8c 100644 --- a/source/funkin/options/type/NewOption.hx +++ b/source/funkin/options/type/NewOption.hx @@ -17,7 +17,7 @@ class NewOption extends TextOption { iconSpr.setPosition(90 - iconSpr.width, (__text.height - iconSpr.height) / 2); iconSpr.scale.set(1.4, 1.4); iconSpr.updateHitbox(); - iconSpr.offset.set(20, -15); + iconSpr.offset.set(15, -15); add(iconSpr); } } \ No newline at end of file diff --git a/source/haxe/Timer.hx b/source/haxe/Timer.hx new file mode 100644 index 00000000..5092af8c --- /dev/null +++ b/source/haxe/Timer.hx @@ -0,0 +1,297 @@ +package haxe; + +#if !lime_cffi +// Original haxe.Timer class + +/* + * Copyright (C)2005-2018 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/** + The Timer class allows you to create asynchronous timers on platforms that + support events. + + The intended usage is to create an instance of the Timer class with a given + interval, set its run() method to a custom function to be invoked and + eventually call stop() to stop the Timer. + + Note that a running Timer may or may not prevent the program to exit + automatically when main() returns. + + It is also possible to extend this class and override its run() method in + the child class. +**/ +class Timer +{ + #if (flash || js) + private var id:Null<Int>; + #elseif java + private var timer:java.util.Timer; + private var task:java.util.TimerTask; + #elseif (haxe_ver >= "3.4.0") + private var event:MainLoop.MainEvent; + #end + + /** + Creates a new timer that will run every `time_ms` milliseconds. + + After creating the Timer instance, it calls `this.run` repeatedly, + with delays of `time_ms` milliseconds, until `this.stop` is called. + + The first invocation occurs after `time_ms` milliseconds, not + immediately. + + The accuracy of this may be platform-dependent. + **/ + public function new(time_ms:Int) + { + #if flash + var me = this; + id = untyped __global__["flash.utils.setInterval"](function() + { + me.run(); + }, time_ms); + #elseif js + var me = this; + id = untyped setInterval(function() me.run(), time_ms); + #elseif java + timer = new java.util.Timer(); + timer.scheduleAtFixedRate(task = new TimerTask(this), haxe.Int64.ofInt(time_ms), haxe.Int64.ofInt(time_ms)); + #elseif (haxe_ver >= "3.4.0") + var dt = time_ms / 1000; + event = MainLoop.add(function() + { + @:privateAccess event.nextRun += dt; + run(); + }); + event.delay(dt); + #end + } + + /** + Stops `this` Timer. + + After calling this method, no additional invocations of `this.run` + will occur. + + It is not possible to restart `this` Timer once stopped. + **/ + public function stop() + { + #if (flash || js) + if (id == null) return; + #if flash + untyped __global__["flash.utils.clearInterval"](id); + #elseif js + untyped clearInterval(id); + #end + id = null; + #elseif java + if (timer != null) + { + timer.cancel(); + timer = null; + } + task = null; + #elseif (haxe_ver >= "3.4.0") + if (event != null) + { + event.stop(); + event = null; + } + #end + } + + /** + This method is invoked repeatedly on `this` Timer. + + It can be overridden in a subclass, or rebound directly to a custom + function: + var timer = new haxe.Timer(1000); // 1000ms delay + timer.run = function() { ... } + + Once bound, it can still be rebound to different functions until `this` + Timer is stopped through a call to `this.stop`. + **/ + public dynamic function run() {} + + /** + Invokes `f` after `time_ms` milliseconds. + + This is a convenience function for creating a new Timer instance with + `time_ms` as argument, binding its run() method to `f` and then stopping + `this` Timer upon the first invocation. + + If `f` is null, the result is unspecified. + **/ + public static function delay(f:Void->Void, time_ms:Int) + { + var t = new haxe.Timer(time_ms); + t.run = function() + { + t.stop(); + f(); + }; + return t; + } + + /** + Measures the time it takes to execute `f`, in seconds with fractions. + + This is a convenience function for calculating the difference between + Timer.stamp() before and after the invocation of `f`. + + The difference is passed as argument to Log.trace(), with "s" appended + to denote the unit. The optional `pos` argument is passed through. + + If `f` is null, the result is unspecified. + **/ + public static function measure<T>(f:Void->T, ?pos:PosInfos):T + { + var t0 = stamp(); + var r = f(); + Log.trace((stamp() - t0) + "s", pos); + return r; + } + + /** + Returns a timestamp, in seconds with fractions. + + The value itself might differ depending on platforms, only differences + between two values make sense. + **/ + public static inline function stamp():Float + { + #if flash + return flash.Lib.getTimer() / 1000; + #elseif (neko || php) + return Sys.time(); + #elseif js + return Date.now().getTime() / 1000; + #elseif cpp + return untyped __global__.__time_stamp(); + #elseif python + return Sys.cpuTime(); + #elseif sys + return Sys.time(); + #else + return 0; + #end + } +} + +#if java +@:nativeGen +private class TimerTask extends java.util.TimerTask +{ + var timer:Timer; + + public function new(timer:Timer):Void + { + super(); + this.timer = timer; + } + + @:overload override public function run():Void + { + timer.run(); + } +} +#end +#else +import lime.system.System; + +class Timer +{ + private static var sRunningTimers:Array<Timer> = []; + + private var mTime:Float; + private var mFireAt:Float; + private var mRunning:Bool; + + public function new(time:Float) + { + mTime = time; + sRunningTimers.push(this); + mFireAt = getMS() + mTime; + mRunning = true; + } + + public static function delay(f:Void->Void, time:Int) + { + var t = new Timer(time); + + t.run = function() + { + t.stop(); + f(); + }; + + return t; + } + + private static function getMS():Float + { + return System.getTimer(); + } + + public static function measure<T>(f:Void->T, ?pos:PosInfos):T + { + var t0 = stamp(); + var r = f(); + Log.trace((stamp() - t0) + "s", pos); + return r; + } + + dynamic public function run() {} + + public static inline function stamp():Float + { + var timer = System.getTimer(); + return (timer > 0 ? timer / 1000 : 0); + } + + public function stop():Void + { + /*if (mRunning) + { + + for (i in 0...sRunningTimers.length) + { + if (sRunningTimers[i] == this) + { + sRunningTimers[i] = null; + break; + } + } + }*/ + mRunning = false; + } + + @:noCompletion private function __check(inTime:Float) + { + if (inTime >= mFireAt) + { + mFireAt += mTime; + run(); + } + } +} +#end diff --git a/source/lime/_internal/backend/native/NativeApplication.hx b/source/lime/_internal/backend/native/NativeApplication.hx new file mode 100644 index 00000000..ac8ab467 --- /dev/null +++ b/source/lime/_internal/backend/native/NativeApplication.hx @@ -0,0 +1,981 @@ +package lime._internal.backend.native; + +import haxe.Timer; +import lime._internal.backend.native.NativeCFFI; +import lime.app.Application; +import lime.graphics.opengl.GL; +import lime.graphics.OpenGLRenderContext; +import lime.graphics.RenderContext; +import lime.math.Rectangle; +import lime.media.AudioManager; +import lime.system.Clipboard; +import lime.system.Display; +import lime.system.DisplayMode; +import lime.system.JNI; +import lime.system.Sensor; +import lime.system.SensorType; +import lime.system.System; +import lime.ui.Gamepad; +import lime.ui.Joystick; +import lime.ui.JoystickHatPosition; +import lime.ui.KeyCode; +import lime.ui.KeyModifier; +import lime.ui.Touch; +import lime.ui.Window; + +#if !lime_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +@:access(haxe.Timer) +@:access(lime._internal.backend.native.NativeCFFI) +@:access(lime._internal.backend.native.NativeOpenGLRenderContext) +@:access(lime._internal.backend.native.NativeWindow) +@:access(lime.app.Application) +@:access(lime.graphics.opengl.GL) +@:access(lime.graphics.OpenGLRenderContext) +@:access(lime.graphics.Renderer) +@:access(lime.system.Clipboard) +@:access(lime.system.Sensor) +@:access(lime.ui.Gamepad) +@:access(lime.ui.Joystick) +@:access(lime.ui.Window) +class NativeApplication +{ + private var applicationEventInfo = new ApplicationEventInfo(UPDATE); + private var clipboardEventInfo = new ClipboardEventInfo(); + private var currentTouches = new Map<Int, Touch>(); + private var dropEventInfo = new DropEventInfo(); + private var gamepadEventInfo = new GamepadEventInfo(); + private var joystickEventInfo = new JoystickEventInfo(); + private var keyEventInfo = new KeyEventInfo(); + private var mouseEventInfo = new MouseEventInfo(); + private var renderEventInfo = new RenderEventInfo(RENDER); + private var sensorEventInfo = new SensorEventInfo(); + private var textEventInfo = new TextEventInfo(); + private var touchEventInfo = new TouchEventInfo(); + private var unusedTouchesPool = new List<Touch>(); + private var windowEventInfo = new WindowEventInfo(); + + public var handle:Dynamic; + + private var pauseTimer:Int; + private var parent:Application; + private var toggleFullscreen:Bool; + + private static function __init__() + { + #if (lime_cffi && !macro) + var init = NativeCFFI; + #end + } + + public function new(parent:Application):Void + { + this.parent = parent; + pauseTimer = -1; + toggleFullscreen = true; + + AudioManager.init(); + + #if (ios || android || tvos) + Sensor.registerSensor(SensorType.ACCELEROMETER, 0); + #end + + #if (!macro && lime_cffi) + handle = NativeCFFI.lime_application_create(); + #end + } + + private function advanceTimer():Void + { + #if lime_cffi + if (pauseTimer > -1) + { + var offset = System.getTimer() - pauseTimer; + for(timer in Timer.sRunningTimers) { + if(timer != null && timer.mRunning) timer.mFireAt += offset; + } + pauseTimer = -1; + } + #end + } + + public function exec():Int + { + #if !macro + #if lime_cffi + NativeCFFI.lime_application_event_manager_register(handleApplicationEvent, applicationEventInfo); + NativeCFFI.lime_clipboard_event_manager_register(handleClipboardEvent, clipboardEventInfo); + NativeCFFI.lime_drop_event_manager_register(handleDropEvent, dropEventInfo); + NativeCFFI.lime_gamepad_event_manager_register(handleGamepadEvent, gamepadEventInfo); + NativeCFFI.lime_joystick_event_manager_register(handleJoystickEvent, joystickEventInfo); + NativeCFFI.lime_key_event_manager_register(handleKeyEvent, keyEventInfo); + NativeCFFI.lime_mouse_event_manager_register(handleMouseEvent, mouseEventInfo); + NativeCFFI.lime_render_event_manager_register(handleRenderEvent, renderEventInfo); + NativeCFFI.lime_text_event_manager_register(handleTextEvent, textEventInfo); + NativeCFFI.lime_touch_event_manager_register(handleTouchEvent, touchEventInfo); + NativeCFFI.lime_window_event_manager_register(handleWindowEvent, windowEventInfo); + #if (ios || android || tvos) + NativeCFFI.lime_sensor_event_manager_register(handleSensorEvent, sensorEventInfo); + #end + #end + + #if (nodejs && lime_cffi) + NativeCFFI.lime_application_init(handle); + + var eventLoop = function() + { + var active = NativeCFFI.lime_application_update(handle); + + if (!active) + { + untyped process.exitCode = NativeCFFI.lime_application_quit(handle); + parent.onExit.dispatch(untyped process.exitCode); + } + else + { + untyped setImmediate(eventLoop); + } + } + + untyped setImmediate(eventLoop); + return 0; + #elseif lime_cffi + var result = NativeCFFI.lime_application_exec(handle); + + #if (!webassembly && !ios && !nodejs) + parent.onExit.dispatch(result); + #end + + return result; + #end + #end + + return 0; + } + + public function exit():Void + { + AudioManager.shutdown(); + + #if (!macro && lime_cffi) + NativeCFFI.lime_application_quit(handle); + #end + } + + private function handleApplicationEvent():Void + { + switch (applicationEventInfo.type) + { + case UPDATE: + updateTimer(); + + parent.onUpdate.dispatch(applicationEventInfo.deltaTime); + + default: + } + } + + private function handleClipboardEvent():Void + { + Clipboard.__update(); + } + + private function handleDropEvent():Void + { + for (window in parent.windows) + { + window.onDropFile.dispatch(#if hl @:privateAccess String.fromUTF8(dropEventInfo.file) #else dropEventInfo.file #end); + } + } + + private function handleGamepadEvent():Void + { + switch (gamepadEventInfo.type) + { + case AXIS_MOVE: + var gamepad = Gamepad.devices.get(gamepadEventInfo.id); + if (gamepad != null) gamepad.onAxisMove.dispatch(gamepadEventInfo.axis, gamepadEventInfo.axisValue); + + case BUTTON_DOWN: + var gamepad = Gamepad.devices.get(gamepadEventInfo.id); + if (gamepad != null) gamepad.onButtonDown.dispatch(gamepadEventInfo.button); + + case BUTTON_UP: + var gamepad = Gamepad.devices.get(gamepadEventInfo.id); + if (gamepad != null) gamepad.onButtonUp.dispatch(gamepadEventInfo.button); + + case CONNECT: + Gamepad.__connect(gamepadEventInfo.id); + + case DISCONNECT: + Gamepad.__disconnect(gamepadEventInfo.id); + } + } + + private function handleJoystickEvent():Void + { + switch (joystickEventInfo.type) + { + case AXIS_MOVE: + var joystick = Joystick.devices.get(joystickEventInfo.id); + if (joystick != null) joystick.onAxisMove.dispatch(joystickEventInfo.index, joystickEventInfo.x); + + case HAT_MOVE: + var joystick = Joystick.devices.get(joystickEventInfo.id); + if (joystick != null) joystick.onHatMove.dispatch(joystickEventInfo.index, joystickEventInfo.eventValue); + + case TRACKBALL_MOVE: // I guess this was just removed ?? + + case BUTTON_DOWN: + var joystick = Joystick.devices.get(joystickEventInfo.id); + if (joystick != null) joystick.onButtonDown.dispatch(joystickEventInfo.index); + + case BUTTON_UP: + var joystick = Joystick.devices.get(joystickEventInfo.id); + if (joystick != null) joystick.onButtonUp.dispatch(joystickEventInfo.index); + + case CONNECT: + Joystick.__connect(joystickEventInfo.id); + + case DISCONNECT: + Joystick.__disconnect(joystickEventInfo.id); + } + } + + private function handleKeyEvent():Void + { + var window = parent.__windowByID.get(keyEventInfo.windowID); + + if (window != null) + { + var type:KeyEventType = keyEventInfo.type; + var int32:Float = keyEventInfo.keyCode; + var keyCode:KeyCode = Std.int(int32); + var modifier:KeyModifier = keyEventInfo.modifier; + + switch (type) + { + case KEY_DOWN: + window.onKeyDown.dispatch(keyCode, modifier); + + case KEY_UP: + window.onKeyUp.dispatch(keyCode, modifier); + } + + #if (windows || linux) + if (keyCode == RETURN) + { + if (type == KEY_DOWN) + { + if (toggleFullscreen && modifier.altKey && (!modifier.ctrlKey && !modifier.shiftKey && !modifier.metaKey)) + { + toggleFullscreen = false; + + if (!window.onKeyDown.canceled) + { + window.fullscreen = !window.fullscreen; + } + } + } + else + { + toggleFullscreen = true; + } + } + + #if rpi + if (keyCode == ESCAPE && modifier == KeyModifier.NONE && type == KEY_UP && !window.onKeyUp.canceled) + { + System.exit(0); + } + #end + #elseif mac + if (keyCode == F) + { + if (type == KEY_DOWN) + { + if (toggleFullscreen && (modifier.ctrlKey && modifier.metaKey) && (!modifier.altKey && !modifier.shiftKey)) + { + toggleFullscreen = false; + + if (!window.onKeyDown.canceled) + { + window.fullscreen = !window.fullscreen; + } + } + } + else + { + toggleFullscreen = true; + } + } + #elseif android + if (keyCode == APP_CONTROL_BACK && modifier == KeyModifier.NONE && type == KEY_UP && !window.onKeyUp.canceled) + { + var mainActivity = JNI.createStaticField("org/haxe/extension/Extension", "mainActivity", "Landroid/app/Activity;"); + var moveTaskToBack = JNI.createMemberMethod("android/app/Activity", "moveTaskToBack", "(Z)Z"); + + moveTaskToBack(mainActivity.get(), true); + } + #end + } + } + + private function handleMouseEvent():Void + { + var window = parent.__windowByID.get(mouseEventInfo.windowID); + + if (window != null) + { + switch (mouseEventInfo.type) + { + case MOUSE_DOWN: + window.clickCount = mouseEventInfo.clickCount; + window.onMouseDown.dispatch(mouseEventInfo.x, mouseEventInfo.y, mouseEventInfo.button); + window.clickCount = 0; + + case MOUSE_UP: + window.clickCount = mouseEventInfo.clickCount; + window.onMouseUp.dispatch(mouseEventInfo.x, mouseEventInfo.y, mouseEventInfo.button); + window.clickCount = 0; + + case MOUSE_MOVE: + window.onMouseMove.dispatch(mouseEventInfo.x, mouseEventInfo.y); + window.onMouseMoveRelative.dispatch(mouseEventInfo.movementX, mouseEventInfo.movementY); + + case MOUSE_WHEEL: + window.onMouseWheel.dispatch(mouseEventInfo.x, mouseEventInfo.y, UNKNOWN); + + default: + } + } + } + + private function handleRenderEvent():Void + { + // TODO: Allow windows to render independently + + for (window in parent.__windows) + { + if (window == null) continue; + + // parent.renderer = renderer; + + switch (renderEventInfo.type) + { + case RENDER: + if (window.context != null) + { + window.__backend.render(); + window.onRender.dispatch(window.context); + + if (!window.onRender.canceled) + { + window.__backend.contextFlip(); + } + } + + case RENDER_CONTEXT_LOST: + if (window.__backend.useHardware && window.context != null) + { + switch (window.context.type) + { + case OPENGL, OPENGLES, WEBGL: + #if (lime_cffi && (lime_opengl || lime_opengles) && !display) + var gl = window.context.gl; + (gl : NativeOpenGLRenderContext).__contextLost(); + if (GL.context == gl) GL.context = null; + #end + + default: + } + + window.context = null; + window.onRenderContextLost.dispatch(); + } + + case RENDER_CONTEXT_RESTORED: + if (window.__backend.useHardware) + { + // GL.context = new OpenGLRenderContext (); + // window.context.gl = GL.context; + + window.onRenderContextRestored.dispatch(window.context); + } + } + } + } + + private function handleSensorEvent():Void + { + var sensor = Sensor.sensorByID.get(sensorEventInfo.id); + + if (sensor != null) + { + sensor.onUpdate.dispatch(sensorEventInfo.x, sensorEventInfo.y, sensorEventInfo.z); + } + } + + private function handleTextEvent():Void + { + var window = parent.__windowByID.get(textEventInfo.windowID); + + if (window != null) + { + switch (textEventInfo.type) + { + case TEXT_INPUT: + window.onTextInput.dispatch(#if hl @:privateAccess String.fromUTF8(textEventInfo.text) #else textEventInfo.text #end); + + case TEXT_EDIT: + window.onTextEdit.dispatch(#if hl @:privateAccess String.fromUTF8(textEventInfo.text) #else textEventInfo.text #end, textEventInfo.start, + textEventInfo.length); + + default: + } + } + } + + private function handleTouchEvent():Void + { + switch (touchEventInfo.type) + { + case TOUCH_START: + var touch = unusedTouchesPool.pop(); + + if (touch == null) + { + touch = new Touch(touchEventInfo.x, touchEventInfo.y, touchEventInfo.id, touchEventInfo.dx, touchEventInfo.dy, touchEventInfo.pressure, + touchEventInfo.device); + } + else + { + touch.x = touchEventInfo.x; + touch.y = touchEventInfo.y; + touch.id = touchEventInfo.id; + touch.dx = touchEventInfo.dx; + touch.dy = touchEventInfo.dy; + touch.pressure = touchEventInfo.pressure; + touch.device = touchEventInfo.device; + } + + currentTouches.set(touch.id, touch); + + Touch.onStart.dispatch(touch); + + case TOUCH_END: + var touch = currentTouches.get(touchEventInfo.id); + + if (touch != null) + { + touch.x = touchEventInfo.x; + touch.y = touchEventInfo.y; + touch.dx = touchEventInfo.dx; + touch.dy = touchEventInfo.dy; + touch.pressure = touchEventInfo.pressure; + + Touch.onEnd.dispatch(touch); + + currentTouches.remove(touchEventInfo.id); + unusedTouchesPool.add(touch); + } + + case TOUCH_MOVE: + var touch = currentTouches.get(touchEventInfo.id); + + if (touch != null) + { + touch.x = touchEventInfo.x; + touch.y = touchEventInfo.y; + touch.dx = touchEventInfo.dx; + touch.dy = touchEventInfo.dy; + touch.pressure = touchEventInfo.pressure; + + Touch.onMove.dispatch(touch); + } + + default: + } + } + + private function handleWindowEvent():Void + { + var window = parent.__windowByID.get(windowEventInfo.windowID); + + if (window != null) + { + switch (windowEventInfo.type) + { + case WINDOW_ACTIVATE: + advanceTimer(); + window.onActivate.dispatch(); + AudioManager.resume(); + + case WINDOW_CLOSE: + window.close(); + + case WINDOW_DEACTIVATE: + window.onDeactivate.dispatch(); + AudioManager.suspend(); + pauseTimer = System.getTimer(); + + case WINDOW_ENTER: + window.onEnter.dispatch(); + + case WINDOW_EXPOSE: + window.onExpose.dispatch(); + + case WINDOW_FOCUS_IN: + window.onFocusIn.dispatch(); + + case WINDOW_FOCUS_OUT: + window.onFocusOut.dispatch(); + + case WINDOW_LEAVE: + window.onLeave.dispatch(); + + case WINDOW_MAXIMIZE: + window.__maximized = true; + window.__fullscreen = false; + window.__minimized = false; + window.onMaximize.dispatch(); + + case WINDOW_MINIMIZE: + window.__minimized = true; + window.__maximized = false; + window.__fullscreen = false; + window.onMinimize.dispatch(); + + case WINDOW_MOVE: + window.__x = windowEventInfo.x; + window.__y = windowEventInfo.y; + window.onMove.dispatch(windowEventInfo.x, windowEventInfo.y); + + case WINDOW_RESIZE: + window.__width = windowEventInfo.width; + window.__height = windowEventInfo.height; + window.onResize.dispatch(windowEventInfo.width, windowEventInfo.height); + + case WINDOW_RESTORE: + window.__fullscreen = false; + window.__minimized = false; + window.onRestore.dispatch(); + + case WINDOW_SHOW: + window.onShow.dispatch(); + + case WINDOW_HIDE: + window.onHide.dispatch(); + } + } + } + + private function updateTimer():Void + { + #if lime_cffi + if (Timer.sRunningTimers.length > 0) + { + var currentTime = System.getTimer(); + var foundNull = false; + var timer; + + for (i in 0...Timer.sRunningTimers.length) + { + timer = Timer.sRunningTimers[i]; + + if (timer != null && timer.mRunning) + { + if (currentTime >= timer.mFireAt) + { + timer.mFireAt += timer.mTime; + timer.run(); + } + } + else + { + foundNull = true; + } + } + + if (foundNull) + { + Timer.sRunningTimers = Timer.sRunningTimers.filter(function(val) + { + return val != null && val.mRunning; + }); + } + } + + #if (haxe_ver >= 4.2) + #if target.threaded + sys.thread.Thread.current().events.progress(); + #else + // Duplicate code required because Haxe 3 can't handle + // #if (haxe_ver >= 4.2 && target.threaded) + @:privateAccess haxe.EntryPoint.processEvents(); + #end + #else + @:privateAccess haxe.EntryPoint.processEvents(); + #end + #end + } +} + +@:keep /*private*/ class ApplicationEventInfo +{ + public var deltaTime:Int; + public var type:ApplicationEventType; + + public function new(type:ApplicationEventType = null, deltaTime:Int = 0) + { + this.type = type; + this.deltaTime = deltaTime; + } + + public function clone():ApplicationEventInfo + { + return new ApplicationEventInfo(type, deltaTime); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract ApplicationEventType(Int) +{ + var UPDATE = 0; + var EXIT = 1; +} + +@:keep /*private*/ class ClipboardEventInfo +{ + public var type:ClipboardEventType; + + public function new(type:ClipboardEventType = null) + { + this.type = type; + } + + public function clone():ClipboardEventInfo + { + return new ClipboardEventInfo(type); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract ClipboardEventType(Int) +{ + var UPDATE = 0; +} + +@:keep /*private*/ class DropEventInfo +{ + public var file:#if hl hl.Bytes #else String #end; + public var type:DropEventType; + + public function new(type:DropEventType = null, file = null) + { + this.type = type; + this.file = file; + } + + public function clone():DropEventInfo + { + return new DropEventInfo(type, file); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract DropEventType(Int) +{ + var DROP_FILE = 0; +} + +@:keep /*private*/ class GamepadEventInfo +{ + public var axis:Int; + public var button:Int; + public var id:Int; + public var type:GamepadEventType; + public var axisValue:Float; + + public function new(type:GamepadEventType = null, id:Int = 0, button:Int = 0, axis:Int = 0, value:Float = 0) + { + this.type = type; + this.id = id; + this.button = button; + this.axis = axis; + this.axisValue = value; + } + + public function clone():GamepadEventInfo + { + return new GamepadEventInfo(type, id, button, axis, axisValue); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract GamepadEventType(Int) +{ + var AXIS_MOVE = 0; + var BUTTON_DOWN = 1; + var BUTTON_UP = 2; + var CONNECT = 3; + var DISCONNECT = 4; +} + +@:keep /*private*/ class JoystickEventInfo +{ + public var id:Int; + public var index:Int; + public var type:JoystickEventType; + public var eventValue:Int; + public var x:Float; + public var y:Float; + + public function new(type:JoystickEventType = null, id:Int = 0, index:Int = 0, value:Int = 0, x:Float = 0, y:Float = 0) + { + this.type = type; + this.id = id; + this.index = index; + this.eventValue = value; + this.x = x; + this.y = y; + } + + public function clone():JoystickEventInfo + { + return new JoystickEventInfo(type, id, index, eventValue, x, y); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract JoystickEventType(Int) +{ + var AXIS_MOVE = 0; + var HAT_MOVE = 1; + var TRACKBALL_MOVE = 2; + var BUTTON_DOWN = 3; + var BUTTON_UP = 4; + var CONNECT = 5; + var DISCONNECT = 6; +} + +@:keep /*private*/ class KeyEventInfo +{ + public var keyCode: Float; + public var modifier:Int; + public var type:KeyEventType; + public var windowID:Int; + + public function new(type:KeyEventType = null, windowID:Int = 0, keyCode: Float = 0, modifier:Int = 0) + { + this.type = type; + this.windowID = windowID; + this.keyCode = keyCode; + this.modifier = modifier; + } + + public function clone():KeyEventInfo + { + return new KeyEventInfo(type, windowID, keyCode, modifier); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract KeyEventType(Int) +{ + var KEY_DOWN = 0; + var KEY_UP = 1; +} + +@:keep /*private*/ class MouseEventInfo +{ + public var button:Int; + public var movementX:Float; + public var movementY:Float; + public var type:MouseEventType; + public var windowID:Int; + public var x:Float; + public var y:Float; + public var clickCount:Int; + + public function new(type:MouseEventType = null, windowID:Int = 0, x:Float = 0, y:Float = 0, button:Int = 0, movementX:Float = 0, movementY:Float = 0, clickCount:Int = 0) + { + this.type = type; + this.windowID = 0; + this.x = x; + this.y = y; + this.button = button; + this.movementX = movementX; + this.movementY = movementY; + this.clickCount = clickCount; + } + + public function clone():MouseEventInfo + { + return new MouseEventInfo(type, windowID, x, y, button, movementX, movementY, clickCount); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract MouseEventType(Int) +{ + var MOUSE_DOWN = 0; + var MOUSE_UP = 1; + var MOUSE_MOVE = 2; + var MOUSE_WHEEL = 3; +} + +@:keep /*private*/ class RenderEventInfo +{ + public var type:RenderEventType; + + public function new(type:RenderEventType = null) + { + this.type = type; + } + + public function clone():RenderEventInfo + { + return new RenderEventInfo(type); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract RenderEventType(Int) +{ + var RENDER = 0; + var RENDER_CONTEXT_LOST = 1; + var RENDER_CONTEXT_RESTORED = 2; +} + +@:keep /*private*/ class SensorEventInfo +{ + public var id:Int; + public var x:Float; + public var y:Float; + public var z:Float; + public var type:SensorEventType; + + public function new(type:SensorEventType = null, id:Int = 0, x:Float = 0, y:Float = 0, z:Float = 0) + { + this.type = type; + this.id = id; + this.x = x; + this.y = y; + this.z = z; + } + + public function clone():SensorEventInfo + { + return new SensorEventInfo(type, id, x, y, z); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract SensorEventType(Int) +{ + var ACCELEROMETER = 0; +} + +@:keep /*private*/ class TextEventInfo +{ + public var id:Int; + public var length:Int; + public var start:Int; + public var text:#if hl hl.Bytes #else String #end; + public var type:TextEventType; + public var windowID:Int; + + public function new(type:TextEventType = null, windowID:Int = 0, text = null, start:Int = 0, length:Int = 0) + { + this.type = type; + this.windowID = windowID; + this.text = text; + this.start = start; + this.length = length; + } + + public function clone():TextEventInfo + { + return new TextEventInfo(type, windowID, text, start, length); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract TextEventType(Int) +{ + var TEXT_INPUT = 0; + var TEXT_EDIT = 1; +} + +@:keep /*private*/ class TouchEventInfo +{ + public var device:Int; + public var dx:Float; + public var dy:Float; + public var id:Int; + public var pressure:Float; + public var type:TouchEventType; + public var x:Float; + public var y:Float; + + public function new(type:TouchEventType = null, x:Float = 0, y:Float = 0, id:Int = 0, dx:Float = 0, dy:Float = 0, pressure:Float = 0, device:Int = 0) + { + this.type = type; + this.x = x; + this.y = y; + this.id = id; + this.dx = dx; + this.dy = dy; + this.pressure = pressure; + this.device = device; + } + + public function clone():TouchEventInfo + { + return new TouchEventInfo(type, x, y, id, dx, dy, pressure, device); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract TouchEventType(Int) +{ + var TOUCH_START = 0; + var TOUCH_END = 1; + var TOUCH_MOVE = 2; +} + +@:keep /*private*/ class WindowEventInfo +{ + public var height:Int; + public var type:WindowEventType; + public var width:Int; + public var windowID:Int; + public var x:Int; + public var y:Int; + + public function new(type:WindowEventType = null, windowID:Int = 0, width:Int = 0, height:Int = 0, x:Int = 0, y:Int = 0) + { + this.type = type; + this.windowID = windowID; + this.width = width; + this.height = height; + this.x = x; + this.y = y; + } + + public function clone():WindowEventInfo + { + return new WindowEventInfo(type, windowID, width, height, x, y); + } +} + +#if (haxe_ver >= 4.0) private enum #else @:enum private #end abstract WindowEventType(Int) +{ + var WINDOW_ACTIVATE = 0; + var WINDOW_CLOSE = 1; + var WINDOW_DEACTIVATE = 2; + var WINDOW_ENTER = 3; + var WINDOW_EXPOSE = 4; + var WINDOW_FOCUS_IN = 5; + var WINDOW_FOCUS_OUT = 6; + var WINDOW_LEAVE = 7; + var WINDOW_MAXIMIZE = 8; + var WINDOW_MINIMIZE = 9; + var WINDOW_MOVE = 10; + var WINDOW_RESIZE = 11; + var WINDOW_RESTORE = 12; + var WINDOW_SHOW = 13; + var WINDOW_HIDE = 14; +} diff --git a/source/lime/utils/ObjectPool.hx b/source/lime/utils/ObjectPool.hx new file mode 100644 index 00000000..f05715c3 --- /dev/null +++ b/source/lime/utils/ObjectPool.hx @@ -0,0 +1,347 @@ +package lime.utils; + +import haxe.ds.ObjectMap; + + +/** + A generic object pool for reusing objects. + **/ +#if !lime_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +#if !js @:generic #end class ObjectPool<T> +{ + /** + The number of active objects in the pool. + **/ + public var activeObjects(default, null):Int; + + /** + The number of inactive objects in the pool. + **/ + public var inactiveObjects(default, null):Int; + + /** + The total size of the object pool (both active and inactive objects). + **/ + public var size(get, set):Null<Int>; + + @:noCompletion private var __inactiveObject0:T; + @:noCompletion private var __inactiveObject1:T; + @:noCompletion private var __inactiveObjectList:List<T>; + @:noCompletion private var __pool:Map<T, Bool>; + @:noCompletion private var __size:Null<Int>; + + /** + Creates a new ObjectPool instance. + + @param create A function that creates a new instance of type T. + @param clean A function that cleans up an instance of type T before it is reused. + @param size The maximum size of the object pool. + **/ + public function new(create:Void->T = null, clean:T->Void = null, size:Null<Int> = null) + { + __pool = cast new ObjectMap(); + + activeObjects = 0; + inactiveObjects = 0; + + __inactiveObject0 = null; + __inactiveObject1 = null; + __inactiveObjectList = new List<T>(); + + if (create != null) + { + this.create = create; + } + if (clean != null) + { + this.clean = clean; + } + if (size != null) + { + this.size = size; + } + } + /** + Adds an object to the object pool. + + @param object The object to add to the pool. + **/ + public function add(object:T):Void + { + if (object != null && !__pool.exists(object)) + { + __pool.set(object, false); + clean(object); + __addInactive(object); + } + } + + /** + Dynamic function. + + Cleans up an object before returning it to the pool. + + @param object The object to clean up. + **/ + public dynamic function clean(object:T):Void {} + + /** + Clears the object pool, removing all objects. + **/ + public function clear():Void + { + __pool = cast new ObjectMap(); + + activeObjects = 0; + inactiveObjects = 0; + + __inactiveObject0 = null; + __inactiveObject1 = null; + __inactiveObjectList.clear(); + } + + /** + Dynamic function. + + Creates a new Object. + **/ + public dynamic function create():T + { + return null; + } + + /** + Creates a new object and adds it to the pool, or returns an existing inactive object from the pool. + + @return The object retrieved from the pool, or null if the pool is full and no new objects can be created. + **/ + public function get():T + { + var object = null; + + if (inactiveObjects > 0) + { + object = __getInactive(); + } + else if (__size == null || activeObjects < __size) + { + object = create(); + + if (object != null) + { + __pool.set(object, true); + activeObjects++; + } + } + + return object; + } + + /** + Releases an active object back into the pool. + + @param object The object to release. + **/ + public function release(object:T):Void + { + #if lime_pool_debug + if (object == null || !__pool.exists(object)) + { + Log.error("Object is not a member of the pool"); + } + else if (!__pool.get(object)) + { + Log.error("Object has already been released"); + } + #end + + activeObjects--; + + if (__size == null || activeObjects + inactiveObjects < __size) + { + clean(object); + __addInactive(object); + } + else + { + __pool.remove(object); + } + } + + /** + Removes an object from the pool. + + @param object The object to remove from the pool. + **/ + public function remove(object:T):Void + { + if (object != null && __pool.exists(object)) + { + __pool.remove(object); + + if (__inactiveObject0 == object) + { + __inactiveObject0 = null; + inactiveObjects--; + } + else if (__inactiveObject1 == object) + { + __inactiveObject1 = null; + inactiveObjects--; + } + else if (__inactiveObjectList.remove(object)) + { + inactiveObjects--; + } + else + { + activeObjects--; + } + } + } + + @:noCompletion private inline function __addInactive(object:T):Void + { + #if lime_pool_debug + __pool.set(object, false); + #end + + if (__inactiveObject0 == null) + { + __inactiveObject0 = object; + } + else if (__inactiveObject1 == null) + { + __inactiveObject1 = object; + } + else + { + __inactiveObjectList.add(object); + } + + inactiveObjects++; + } + + @:noCompletion private inline function __getInactive():T + { + var object = null; + + if (__inactiveObject0 != null) + { + object = __inactiveObject0; + __inactiveObject0 = null; + } + else if (__inactiveObject1 != null) + { + object = __inactiveObject1; + __inactiveObject1 = null; + } + else + { + object = __inactiveObjectList.pop(); + + if (__inactiveObjectList.length > 0) + { + __inactiveObject0 = __inactiveObjectList.pop(); + } + + if (__inactiveObjectList.length > 0) + { + __inactiveObject1 = __inactiveObjectList.pop(); + } + } + + #if lime_pool_debug + __pool.set(object, true); + #end + + inactiveObjects--; + activeObjects++; + + return object; + } + + @:noCompletion private function __removeInactive(count:Int):Void + { + if (count <= 0 || inactiveObjects == 0) return; + + if (__inactiveObject0 != null) + { + __pool.remove(__inactiveObject0); + __inactiveObject0 = null; + inactiveObjects--; + count--; + } + + if (count == 0 || inactiveObjects == 0) return; + + if (__inactiveObject1 != null) + { + __pool.remove(__inactiveObject1); + __inactiveObject1 = null; + inactiveObjects--; + count--; + } + + if (count == 0 || inactiveObjects == 0) return; + + for (object in __inactiveObjectList) + { + __pool.remove(object); + __inactiveObjectList.remove(object); + inactiveObjects--; + count--; + + if (count == 0 || inactiveObjects == 0) return; + } + } + + // Get & Set Methods + @:noCompletion private function get_size():Null<Int> + { + return __size; + } + + @:noCompletion private function set_size(value:Null<Int>):Null<Int> + { + if (value == null) + { + __size = null; + } + else + { + var current = inactiveObjects + activeObjects; + __size = value; + + if (current > value) + { + __removeInactive(current - value); + } + else if (value > current) + { + var object; + + for (i in 0...(value - current)) + { + object = create(); + + if (object != null) + { + __pool.set(object, false); + __inactiveObjectList.add(object); + inactiveObjects++; + } + else + { + break; + } + } + } + } + + return value; + } +}