init commit

This commit is contained in:
Return0ne 2021-11-05 17:34:27 -04:00
commit 322d1a0516
1054 changed files with 85998 additions and 0 deletions

182
source/Achievements.hx Normal file
View file

@ -0,0 +1,182 @@
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.FlxCamera;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.group.FlxSpriteGroup;
import flixel.util.FlxColor;
import flixel.text.FlxText;
using StringTools;
class Achievements {
public static var achievementsStuff:Array<Dynamic> = [ //Name, Description, Achievement save tag, Hidden achievement
["Freaky on a Friday Night", "Play on a Friday... Night.", 'friday_night_play', true],
["She Calls Me Daddy Too", "Beat Week 1 on Hard with no Misses.", 'week1_nomiss', false],
["No More Tricks", "Beat Week 2 on Hard with no Misses.", 'week2_nomiss', false],
["Call Me The Hitman", "Beat Week 3 on Hard with no Misses.", 'week3_nomiss', false],
["Lady Killer", "Beat Week 4 on Hard with no Misses.", 'week4_nomiss', false],
["Missless Christmas", "Beat Week 5 on Hard with no Misses.", 'week5_nomiss', false],
["Highscore!!", "Beat Week 6 on Hard with no Misses.", 'week6_nomiss', false],
["You'll Pay For That...", "Beat Week 7 on Hard with no Misses.", 'week7_nomiss', true],
["What a Funkin' Disaster!", "Complete a Song with a rating lower than 20%.", 'ur_bad', false],
["Perfectionist", "Complete a Song with a rating of 100%.", 'ur_good', false],
["Roadkill Enthusiast", "Watch the Henchmen die over 100 times.", 'roadkill_enthusiast', false],
["Oversinging Much...?", "Hold down a note for 20 seconds.", 'oversinging', false],
["Hyperactive", "Finish a Song without going Idle.", 'hype', false],
["Just the Two of Us", "Finish a Song pressing only two keys.", 'two_keys', false],
["Toaster Gamer", "Have you tried to run the game on a toaster?", 'toastie', false],
["Debugger", "Beat the \"Test\" Stage from the Chart Editor.", 'debugger', true]
];
public static var achievementsMap:Map<String, Bool> = new Map<String, Bool>();
public static var henchmenDeath:Int = 0;
public static function unlockAchievement(name:String):Void {
FlxG.log.add('Completed achievement "' + name +'"');
achievementsMap.set(name, true);
FlxG.sound.play(Paths.sound('confirmMenu'), 0.7);
}
public static function isAchievementUnlocked(name:String) {
if(achievementsMap.exists(name) && achievementsMap.get(name)) {
return true;
}
return false;
}
public static function getAchievementIndex(name:String) {
for (i in 0...achievementsStuff.length) {
if(achievementsStuff[i][2] == name) {
return i;
}
}
return -1;
}
public static function loadAchievements():Void {
if(FlxG.save.data != null) {
if(FlxG.save.data.achievementsMap != null) {
achievementsMap = FlxG.save.data.achievementsMap;
}
if(FlxG.save.data.achievementsUnlocked != null) {
FlxG.log.add("Trying to load stuff");
var savedStuff:Array<String> = FlxG.save.data.achievementsUnlocked;
for (i in 0...savedStuff.length) {
achievementsMap.set(savedStuff[i], true);
}
}
if(henchmenDeath == 0 && FlxG.save.data.henchmenDeath != null) {
henchmenDeath = FlxG.save.data.henchmenDeath;
}
}
// You might be asking "Why didn't you just fucking load it directly dumbass??"
// Well, Mr. Smartass, consider that this class was made for Mind Games Mod's demo,
// i'm obviously going to change the "Psyche" achievement's objective so that you have to complete the entire week
// with no misses instead of just Psychic once the full release is out. So, for not having the rest of your achievements lost on
// the full release, we only save the achievements' tag names instead. This also makes me able to rename
// achievements later as long as the tag names aren't changed of course.
// Edit: Oh yeah, just thought that this also makes me able to change the achievements orders easier later if i want to.
// So yeah, if you didn't thought about that i'm smarter than you, i think
// buffoon
}
}
class AttachedAchievement extends FlxSprite {
public var sprTracker:FlxSprite;
private var tag:String;
public function new(x:Float = 0, y:Float = 0, name:String) {
super(x, y);
changeAchievement(name);
antialiasing = ClientPrefs.globalAntialiasing;
}
public function changeAchievement(tag:String) {
this.tag = tag;
reloadAchievementImage();
}
public function reloadAchievementImage() {
if(Achievements.isAchievementUnlocked(tag)) {
loadGraphic(Paths.image('achievementgrid'), true, 150, 150);
animation.add('icon', [Achievements.getAchievementIndex(tag)], 0, false, false);
animation.play('icon');
} else {
loadGraphic(Paths.image('lockedachievement'));
}
scale.set(0.7, 0.7);
updateHitbox();
}
override function update(elapsed:Float) {
if (sprTracker != null)
setPosition(sprTracker.x - 130, sprTracker.y + 25);
super.update(elapsed);
}
}
class AchievementObject extends FlxSpriteGroup {
public var onFinish:Void->Void = null;
var alphaTween:FlxTween;
public function new(name:String, ?camera:FlxCamera = null)
{
super(x, y);
ClientPrefs.saveSettings();
var id:Int = Achievements.getAchievementIndex(name);
var achievementBG:FlxSprite = new FlxSprite(60, 50).makeGraphic(420, 120, FlxColor.BLACK);
achievementBG.scrollFactor.set();
var achievementIcon:FlxSprite = new FlxSprite(achievementBG.x + 10, achievementBG.y + 10).loadGraphic(Paths.image('achievementgrid'), true, 150, 150);
achievementIcon.animation.add('icon', [id], 0, false, false);
achievementIcon.animation.play('icon');
achievementIcon.scrollFactor.set();
achievementIcon.setGraphicSize(Std.int(achievementIcon.width * (2 / 3)));
achievementIcon.updateHitbox();
achievementIcon.antialiasing = ClientPrefs.globalAntialiasing;
var achievementName:FlxText = new FlxText(achievementIcon.x + achievementIcon.width + 20, achievementIcon.y + 16, 280, Achievements.achievementsStuff[id][0], 16);
achievementName.setFormat(Paths.font("vcr.ttf"), 16, FlxColor.WHITE, LEFT);
achievementName.scrollFactor.set();
var achievementText:FlxText = new FlxText(achievementName.x, achievementName.y + 32, 280, Achievements.achievementsStuff[id][1], 16);
achievementText.setFormat(Paths.font("vcr.ttf"), 16, FlxColor.WHITE, LEFT);
achievementText.scrollFactor.set();
add(achievementBG);
add(achievementName);
add(achievementText);
add(achievementIcon);
var cam:Array<FlxCamera> = FlxCamera.defaultCameras;
if(camera != null) {
cam = [camera];
}
alpha = 0;
achievementBG.cameras = cam;
achievementName.cameras = cam;
achievementText.cameras = cam;
achievementIcon.cameras = cam;
alphaTween = FlxTween.tween(this, {alpha: 1}, 0.5, {onComplete: function (twn:FlxTween) {
alphaTween = FlxTween.tween(this, {alpha: 0}, 0.5, {
startDelay: 2.5,
onComplete: function(twn:FlxTween) {
alphaTween = null;
remove(this);
if(onFinish != null) onFinish();
}
});
}});
}
override function destroy() {
if(alphaTween != null) {
alphaTween.cancel();
}
super.destroy();
}
}

View file

@ -0,0 +1,119 @@
package;
#if desktop
import Discord.DiscordClient;
#end
import flash.text.TextField;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.addons.display.FlxGridOverlay;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.math.FlxMath;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import lime.utils.Assets;
import flixel.FlxSubState;
import Achievements;
using StringTools;
class AchievementsMenuState extends MusicBeatState
{
var options:Array<String> = [];
private var grpOptions:FlxTypedGroup<Alphabet>;
private static var curSelected:Int = 0;
private var achievementArray:Array<AttachedAchievement> = [];
private var achievementIndex:Array<Int> = [];
private var descText:FlxText;
override function create() {
#if desktop
DiscordClient.changePresence("Achievements Menu", null);
#end
var menuBG:FlxSprite = new FlxSprite().loadGraphic(Paths.image('menuBGBlue'));
menuBG.setGraphicSize(Std.int(menuBG.width * 1.1));
menuBG.updateHitbox();
menuBG.screenCenter();
menuBG.antialiasing = ClientPrefs.globalAntialiasing;
add(menuBG);
grpOptions = new FlxTypedGroup<Alphabet>();
add(grpOptions);
for (i in 0...Achievements.achievementsStuff.length) {
if(!Achievements.achievementsStuff[i][3] || Achievements.achievementsMap.exists(Achievements.achievementsStuff[i][2])) {
options.push(Achievements.achievementsStuff[i]);
achievementIndex.push(i);
}
}
for (i in 0...options.length) {
var achieveName:String = Achievements.achievementsStuff[achievementIndex[i]][2];
var optionText:Alphabet = new Alphabet(0, (100 * i) + 210, Achievements.isAchievementUnlocked(achieveName) ? Achievements.achievementsStuff[achievementIndex[i]][0] : '?', false, false);
optionText.isMenuItem = true;
optionText.x += 280;
optionText.xAdd = 200;
optionText.targetY = i;
grpOptions.add(optionText);
var icon:AttachedAchievement = new AttachedAchievement(optionText.x - 105, optionText.y, achieveName);
icon.sprTracker = optionText;
achievementArray.push(icon);
add(icon);
}
descText = new FlxText(150, 600, 980, "", 32);
descText.setFormat(Paths.font("vcr.ttf"), 32, FlxColor.WHITE, CENTER, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
descText.scrollFactor.set();
descText.borderSize = 2.4;
add(descText);
changeSelection();
super.create();
}
override function update(elapsed:Float) {
super.update(elapsed);
if (controls.UI_UP_P) {
changeSelection(-1);
}
if (controls.UI_DOWN_P) {
changeSelection(1);
}
if (controls.BACK) {
FlxG.sound.play(Paths.sound('cancelMenu'));
FlxG.switchState(new MainMenuState());
}
}
function changeSelection(change:Int = 0) {
curSelected += change;
if (curSelected < 0)
curSelected = options.length - 1;
if (curSelected >= options.length)
curSelected = 0;
var bullShit:Int = 0;
for (item in grpOptions.members) {
item.targetY = bullShit - curSelected;
bullShit++;
item.alpha = 0.6;
if (item.targetY == 0) {
item.alpha = 1;
}
}
for (i in 0...achievementArray.length) {
achievementArray[i].alpha = 0.6;
if(i == curSelected) {
achievementArray[i].alpha = 1;
}
}
descText.text = Achievements.achievementsStuff[achievementIndex[curSelected]][1];
}
}

502
source/Alphabet.hx Normal file
View file

@ -0,0 +1,502 @@
package;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.group.FlxSpriteGroup;
import flixel.math.FlxMath;
import flixel.util.FlxTimer;
import flixel.system.FlxSound;
using StringTools;
/**
* Loosley based on FlxTypeText lolol
*/
class Alphabet extends FlxSpriteGroup
{
public var delay:Float = 0.05;
public var paused:Bool = false;
// for menu shit
public var forceX:Float = Math.NEGATIVE_INFINITY;
public var targetY:Float = 0;
public var yMult:Float = 120;
public var xAdd:Float = 0;
public var yAdd:Float = 0;
public var isMenuItem:Bool = false;
public var textSize:Float = 1.0;
public var text:String = "";
var _finalText:String = "";
var yMulti:Float = 1;
// custom shit
// amp, backslash, question mark, apostrophy, comma, angry faic, period
var lastSprite:AlphaCharacter;
var xPosResetted:Bool = false;
var splitWords:Array<String> = [];
var isBold:Bool = false;
public var lettersArray:Array<AlphaCharacter> = [];
public var finishedText:Bool = false;
public var typed:Bool = false;
public var typingSpeed:Float = 0.05;
public function new(x:Float, y:Float, text:String = "", ?bold:Bool = false, typed:Bool = false, ?typingSpeed:Float = 0.05, ?textSize:Float = 1)
{
super(x, y);
forceX = Math.NEGATIVE_INFINITY;
this.textSize = textSize;
_finalText = text;
this.text = text;
this.typed = typed;
isBold = bold;
if (text != "")
{
if (typed)
{
startTypedText(typingSpeed);
}
else
{
addText();
}
} else {
finishedText = true;
}
}
public function changeText(newText:String, newTypingSpeed:Float = -1)
{
for (i in 0...lettersArray.length) {
var letter = lettersArray[0];
remove(letter);
lettersArray.remove(letter);
}
lettersArray = [];
splitWords = [];
loopNum = 0;
xPos = 0;
curRow = 0;
consecutiveSpaces = 0;
xPosResetted = false;
finishedText = false;
lastSprite = null;
var lastX = x;
x = 0;
_finalText = newText;
text = newText;
if(newTypingSpeed != -1) {
typingSpeed = newTypingSpeed;
}
if (text != "") {
if (typed)
{
startTypedText(typingSpeed);
} else {
addText();
}
} else {
finishedText = true;
}
x = lastX;
}
public function addText()
{
doSplitWords();
var xPos:Float = 0;
for (character in splitWords)
{
// if (character.fastCodeAt() == " ")
// {
// }
var spaceChar:Bool = (character == " " || character == "_");
if (spaceChar)
{
consecutiveSpaces++;
}
var isNumber:Bool = AlphaCharacter.numbers.indexOf(character) != -1;
var isSymbol:Bool = AlphaCharacter.symbols.indexOf(character) != -1;
var isAlphabet:Bool = AlphaCharacter.alphabet.indexOf(character.toLowerCase()) != -1;
if ((isAlphabet || isSymbol || isNumber) && (!isBold || !spaceChar))
{
if (lastSprite != null)
{
xPos = lastSprite.x + lastSprite.width;
}
if (consecutiveSpaces > 0)
{
xPos += 40 * consecutiveSpaces * textSize;
}
consecutiveSpaces = 0;
// var letter:AlphaCharacter = new AlphaCharacter(30 * loopNum, 0, textSize);
var letter:AlphaCharacter = new AlphaCharacter(xPos, 0, textSize);
if (isBold)
{
if (isNumber)
{
letter.createBoldNumber(character);
}
else if (isSymbol)
{
letter.createBoldSymbol(character);
}
else
{
letter.createBoldLetter(character);
}
}
else
{
if (isNumber)
{
letter.createNumber(character);
}
else if (isSymbol)
{
letter.createSymbol(character);
}
else
{
letter.createLetter(character);
}
}
add(letter);
lettersArray.push(letter);
lastSprite = letter;
}
// loopNum += 1;
}
}
function doSplitWords():Void
{
splitWords = _finalText.split("");
}
var loopNum:Int = 0;
var xPos:Float = 0;
public var curRow:Int = 0;
var dialogueSound:FlxSound = null;
var consecutiveSpaces:Int = 0;
var typeTimer:FlxTimer = null;
public function startTypedText(speed:Float):Void
{
_finalText = text;
doSplitWords();
// trace(arrayShit);
if(speed <= 0) {
while(!finishedText) {
timerCheck();
}
if(dialogueSound != null) dialogueSound.stop();
dialogueSound = FlxG.sound.play(Paths.sound('dialogue'));
} else {
typeTimer = new FlxTimer().start(0.1, function(tmr:FlxTimer) {
typeTimer = new FlxTimer().start(speed, function(tmr:FlxTimer) {
timerCheck(tmr);
}, 0);
});
}
}
var LONG_TEXT_ADD:Float = -24; //text is over 2 rows long, make it go up a bit
public function timerCheck(?tmr:FlxTimer = null) {
var autoBreak:Bool = false;
if ((loopNum <= splitWords.length - 2 && splitWords[loopNum] == "\\" && splitWords[loopNum+1] == "n") ||
((autoBreak = true) && xPos >= FlxG.width * 0.65 && splitWords[loopNum] == ' ' ))
{
if(autoBreak) {
if(tmr != null) tmr.loops -= 1;
loopNum += 1;
} else {
if(tmr != null) tmr.loops -= 2;
loopNum += 2;
}
yMulti += 1;
xPosResetted = true;
xPos = 0;
curRow += 1;
if(curRow == 2) y += LONG_TEXT_ADD;
}
if(loopNum <= splitWords.length && splitWords[loopNum] != null) {
var spaceChar:Bool = (splitWords[loopNum] == " " || splitWords[loopNum] == "_");
if (spaceChar)
{
consecutiveSpaces++;
}
var isNumber:Bool = AlphaCharacter.numbers.indexOf(splitWords[loopNum]) != -1;
var isSymbol:Bool = AlphaCharacter.symbols.indexOf(splitWords[loopNum]) != -1;
var isAlphabet:Bool = AlphaCharacter.alphabet.indexOf(splitWords[loopNum].toLowerCase()) != -1;
if ((isAlphabet || isSymbol || isNumber) && (!isBold || !spaceChar))
{
if (lastSprite != null && !xPosResetted)
{
lastSprite.updateHitbox();
xPos += lastSprite.width + 3;
// if (isBold)
// xPos -= 80;
}
else
{
xPosResetted = false;
}
if (consecutiveSpaces > 0)
{
xPos += 20 * consecutiveSpaces * textSize;
}
consecutiveSpaces = 0;
// var letter:AlphaCharacter = new AlphaCharacter(30 * loopNum, 0, textSize);
var letter:AlphaCharacter = new AlphaCharacter(xPos, 55 * yMulti, textSize);
letter.row = curRow;
if (isBold)
{
if (isNumber)
{
letter.createBoldNumber(splitWords[loopNum]);
}
else if (isSymbol)
{
letter.createBoldSymbol(splitWords[loopNum]);
}
else
{
letter.createBoldLetter(splitWords[loopNum]);
}
}
else
{
if (isNumber)
{
letter.createNumber(splitWords[loopNum]);
}
else if (isSymbol)
{
letter.createSymbol(splitWords[loopNum]);
}
else
{
letter.createLetter(splitWords[loopNum]);
}
}
letter.x += 90;
if(tmr != null) {
if(dialogueSound != null) dialogueSound.stop();
dialogueSound = FlxG.sound.play(Paths.sound('dialogue'));
}
add(letter);
lastSprite = letter;
}
}
loopNum++;
if(loopNum >= splitWords.length) {
if(tmr != null) {
typeTimer = null;
tmr.cancel();
tmr.destroy();
}
finishedText = true;
}
}
override function update(elapsed:Float)
{
if (isMenuItem)
{
var scaledY = FlxMath.remapToRange(targetY, 0, 1, 0, 1.3);
var lerpVal:Float = CoolUtil.boundTo(elapsed * 9.6, 0, 1);
y = FlxMath.lerp(y, (scaledY * yMult) + (FlxG.height * 0.48) + yAdd, lerpVal);
if(forceX != Math.NEGATIVE_INFINITY) {
x = forceX;
} else {
x = FlxMath.lerp(x, (targetY * 20) + 90 + xAdd, lerpVal);
}
}
super.update(elapsed);
}
public function killTheTimer() {
if(typeTimer != null) {
typeTimer.cancel();
typeTimer.destroy();
}
typeTimer = null;
}
}
class AlphaCharacter extends FlxSprite
{
public static var alphabet:String = "abcdefghijklmnopqrstuvwxyz";
public static var numbers:String = "1234567890";
public static var symbols:String = "|~#$%()*+-:;<=>@[]^_.,'!?";
public var row:Int = 0;
private var textSize:Float = 1;
public function new(x:Float, y:Float, textSize:Float)
{
super(x, y);
var tex = Paths.getSparrowAtlas('alphabet');
frames = tex;
setGraphicSize(Std.int(width * textSize));
updateHitbox();
this.textSize = textSize;
antialiasing = ClientPrefs.globalAntialiasing;
}
public function createBoldLetter(letter:String)
{
animation.addByPrefix(letter, letter.toUpperCase() + " bold", 24);
animation.play(letter);
updateHitbox();
}
public function createBoldNumber(letter:String):Void
{
animation.addByPrefix(letter, "bold" + letter, 24);
animation.play(letter);
updateHitbox();
}
public function createBoldSymbol(letter:String)
{
switch (letter)
{
case '.':
animation.addByPrefix(letter, 'PERIOD bold', 24);
case "'":
animation.addByPrefix(letter, 'APOSTRAPHIE bold', 24);
case "?":
animation.addByPrefix(letter, 'QUESTION MARK bold', 24);
case "!":
animation.addByPrefix(letter, 'EXCLAMATION POINT bold', 24);
case "(":
animation.addByPrefix(letter, 'bold (', 24);
case ")":
animation.addByPrefix(letter, 'bold )', 24);
default:
animation.addByPrefix(letter, 'bold ' + letter, 24);
}
animation.play(letter);
updateHitbox();
switch (letter)
{
case "'":
y -= 20 * textSize;
case '-':
//x -= 35 - (90 * (1.0 - textSize));
y += 20 * textSize;
case '(':
x -= 65 * textSize;
y -= 5 * textSize;
offset.x = -58 * textSize;
case ')':
x -= 20 / textSize;
y -= 5 * textSize;
offset.x = 12 * textSize;
case '.':
y += 45 * textSize;
x += 5 * textSize;
offset.x += 3 * textSize;
}
}
public function createLetter(letter:String):Void
{
var letterCase:String = "lowercase";
if (letter.toLowerCase() != letter)
{
letterCase = 'capital';
}
animation.addByPrefix(letter, letter + " " + letterCase, 24);
animation.play(letter);
updateHitbox();
y = (110 - height);
y += row * 60;
}
public function createNumber(letter:String):Void
{
animation.addByPrefix(letter, letter, 24);
animation.play(letter);
updateHitbox();
y = (110 - height);
y += row * 60;
}
public function createSymbol(letter:String)
{
switch (letter)
{
case '#':
animation.addByPrefix(letter, 'hashtag', 24);
case '.':
animation.addByPrefix(letter, 'period', 24);
case "'":
animation.addByPrefix(letter, 'apostraphie', 24);
y -= 50;
case "?":
animation.addByPrefix(letter, 'question mark', 24);
case "!":
animation.addByPrefix(letter, 'exclamation point', 24);
case ",":
animation.addByPrefix(letter, 'comma', 24);
default:
animation.addByPrefix(letter, letter, 24);
}
animation.play(letter);
updateHitbox();
y = (110 - height);
y += row * 60;
switch (letter)
{
case "'":
y -= 20;
case '-':
//x -= 35 - (90 * (1.0 - textSize));
y -= 16;
}
}
}

51
source/AttachedSprite.hx Normal file
View file

@ -0,0 +1,51 @@
package;
import flixel.FlxSprite;
using StringTools;
class AttachedSprite extends FlxSprite
{
public var sprTracker:FlxSprite;
public var xAdd:Float = 0;
public var yAdd:Float = 0;
public var angleAdd:Float = 0;
public var alphaAdd:Float = 0;
public var copyAngle:Bool = true;
public var copyAlpha:Bool = true;
public var copyVisible:Bool = false;
public function new(file:String, ?anim:String = null, ?library:String = null, ?loop:Bool = false)
{
super();
if(anim != null) {
frames = Paths.getSparrowAtlas(file, library);
animation.addByPrefix('idle', anim, 24, loop);
animation.play('idle');
} else {
loadGraphic(Paths.image(file));
}
antialiasing = ClientPrefs.globalAntialiasing;
scrollFactor.set();
}
override function update(elapsed:Float)
{
super.update(elapsed);
if (sprTracker != null) {
setPosition(sprTracker.x + xAdd, sprTracker.y + yAdd);
scrollFactor.set(sprTracker.scrollFactor.x, sprTracker.scrollFactor.y);
if(copyAngle)
angle = sprTracker.angle + angleAdd;
if(copyAlpha)
alpha = sprTracker.alpha + alphaAdd;
if(copyVisible)
visible = sprTracker.visible;
}
}
}

29
source/AttachedText.hx Normal file
View file

@ -0,0 +1,29 @@
package;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
class AttachedText extends Alphabet
{
public var offsetX:Float = 0;
public var offsetY:Float = 0;
public var sprTracker:FlxSprite;
public var copyVisible:Bool = true;
public function new(text:String = "", ?offsetX:Float = 0, ?offsetY:Float = 0, ?bold = false) {
super(0, 0, text, bold);
isMenuItem = false;
this.offsetX = offsetX;
this.offsetY = offsetY;
}
override function update(elapsed:Float) {
if (sprTracker != null) {
setPosition(sprTracker.x + offsetX, sprTracker.y + offsetY);
if(copyVisible) {
visible = sprTracker.visible;
}
}
super.update(elapsed);
}
}

37
source/BGSprite.hx Normal file
View file

@ -0,0 +1,37 @@
package;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
class BGSprite extends FlxSprite
{
private var idleAnim:String;
public function new(image:String, x:Float = 0, y:Float = 0, ?scrollX:Float = 1, ?scrollY:Float = 1, ?animArray:Array<String> = null, ?loop:Bool = false) {
super(x, y);
if (animArray != null) {
frames = Paths.getSparrowAtlas(image);
for (i in 0...animArray.length) {
var anim:String = animArray[i];
animation.addByPrefix(anim, anim, 24, loop);
if(idleAnim == null) {
idleAnim = anim;
animation.play(anim);
}
}
} else {
if(image != null) {
loadGraphic(Paths.image(image));
}
active = false;
}
scrollFactor.set(scrollX, scrollY);
antialiasing = ClientPrefs.globalAntialiasing;
}
public function dance(?forceplay:Bool = false) {
if(idleAnim != null) {
animation.play(idleAnim, forceplay);
}
}
}

View file

@ -0,0 +1,30 @@
package;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
class BackgroundDancer extends FlxSprite
{
public function new(x:Float, y:Float)
{
super(x, y);
frames = Paths.getSparrowAtlas("limo/limoDancer");
animation.addByIndices('danceLeft', 'bg dancer sketch PINK', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], "", 24, false);
animation.addByIndices('danceRight', 'bg dancer sketch PINK', [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], "", 24, false);
animation.play('danceLeft');
antialiasing = ClientPrefs.globalAntialiasing;
}
var danceDir:Bool = false;
public function dance():Void
{
danceDir = !danceDir;
if (danceDir)
animation.play('danceRight', true);
else
animation.play('danceLeft', true);
}
}

45
source/BackgroundGirls.hx Normal file
View file

@ -0,0 +1,45 @@
package;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
class BackgroundGirls extends FlxSprite
{
var isPissed:Bool = true;
public function new(x:Float, y:Float)
{
super(x, y);
// BG fangirls dissuaded
frames = Paths.getSparrowAtlas('weeb/bgFreaks');
swapDanceType();
animation.play('danceLeft');
}
var danceDir:Bool = false;
public function swapDanceType():Void
{
isPissed = !isPissed;
if(!isPissed) { //Gets unpissed
animation.addByIndices('danceLeft', 'BG girls group', CoolUtil.numberArray(14), "", 24, false);
animation.addByIndices('danceRight', 'BG girls group', CoolUtil.numberArray(30, 15), "", 24, false);
} else { //Pisses
animation.addByIndices('danceLeft', 'BG fangirls dissuaded', CoolUtil.numberArray(14), "", 24, false);
animation.addByIndices('danceRight', 'BG fangirls dissuaded', CoolUtil.numberArray(30, 15), "", 24, false);
}
dance();
}
public function dance():Void
{
danceDir = !danceDir;
if (danceDir)
animation.play('danceRight', true);
else
animation.play('danceLeft', true);
}
}

34
source/BlendModeEffect.hx Normal file
View file

@ -0,0 +1,34 @@
package;
import flixel.util.FlxColor;
import openfl.display.ShaderParameter;
typedef BlendModeShader =
{
var uBlendColor:ShaderParameter<Float>;
}
class BlendModeEffect
{
public var shader(default, null):BlendModeShader;
@:isVar
public var color(default, set):FlxColor;
public function new(shader:BlendModeShader, color:FlxColor):Void
{
shader.uBlendColor.value = [];
this.shader = shader;
this.color = color;
}
function set_color(color:FlxColor):FlxColor
{
shader.uBlendColor.value[0] = color.redFloat;
shader.uBlendColor.value[1] = color.greenFloat;
shader.uBlendColor.value[2] = color.blueFloat;
shader.uBlendColor.value[3] = color.alphaFloat;
return this.color = color;
}
}

43
source/Boyfriend.hx Normal file
View file

@ -0,0 +1,43 @@
package;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.util.FlxTimer;
using StringTools;
class Boyfriend extends Character
{
public var startedDeath:Bool = false;
public function new(x:Float, y:Float, ?char:String = 'bf')
{
super(x, y, char, true);
}
override function update(elapsed:Float)
{
if (!debugMode && animation.curAnim != null)
{
if (animation.curAnim.name.startsWith('sing'))
{
holdTimer += elapsed;
}
else
holdTimer = 0;
if (animation.curAnim.name.endsWith('miss') && animation.curAnim.finished && !debugMode)
{
playAnim('idle', true, false, 10);
}
if (animation.curAnim.name == 'firstDeath' && animation.curAnim.finished && startedDeath)
{
playAnim('deathLoop');
}
}
super.update(elapsed);
}
}

View file

@ -0,0 +1,11 @@
package;
import flixel.FlxSubState;
class ButtonRemapSubstate extends FlxSubState
{
public function new()
{
super();
}
}

315
source/Character.hx Normal file
View file

@ -0,0 +1,315 @@
package;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.animation.FlxBaseAnimation;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.tweens.FlxTween;
import flixel.util.FlxSort;
import Section.SwagSection;
#if MODS_ALLOWED
import sys.io.File;
import sys.FileSystem;
#end
import openfl.utils.Assets;
import haxe.Json;
import haxe.format.JsonParser;
using StringTools;
typedef CharacterFile = {
var animations:Array<AnimArray>;
var image:String;
var scale:Float;
var sing_duration:Float;
var healthicon:String;
var position:Array<Float>;
var camera_position:Array<Float>;
var flip_x:Bool;
var no_antialiasing:Bool;
var healthbar_colors:Array<Int>;
}
typedef AnimArray = {
var anim:String;
var name:String;
var fps:Int;
var loop:Bool;
var indices:Array<Int>;
var offsets:Array<Int>;
}
class Character extends FlxSprite
{
public var animOffsets:Map<String, Array<Dynamic>>;
public var debugMode:Bool = false;
public var isPlayer:Bool = false;
public var curCharacter:String = DEFAULT_CHARACTER;
public var colorTween:FlxTween;
public var holdTimer:Float = 0;
public var heyTimer:Float = 0;
public var specialAnim:Bool = false;
public var animationNotes:Array<Dynamic> = [];
public var stunned:Bool = false;
public var singDuration:Float = 4; //Multiplier of how long a character holds the sing pose
public var idleSuffix:String = '';
public var danceIdle:Bool = false; //Character use "danceLeft" and "danceRight" instead of "idle"
public var healthIcon:String = 'face';
public var animationsArray:Array<AnimArray> = [];
public var positionArray:Array<Float> = [0, 0];
public var cameraPosition:Array<Float> = [0, 0];
//Used on Character Editor
public var imageFile:String = '';
public var jsonScale:Float = 1;
public var noAntialiasing:Bool = false;
public var originalFlipX:Bool = false;
public var healthColorArray:Array<Int> = [255, 0, 0];
public var alreadyLoaded:Bool = true; //Used by "Change Character" event
public static var DEFAULT_CHARACTER:String = 'bf'; //In case a character is missing, it will use BF on its place
public function new(x:Float, y:Float, ?character:String = 'bf', ?isPlayer:Bool = false)
{
super(x, y);
#if (haxe >= "4.0.0")
animOffsets = new Map();
#else
animOffsets = new Map<String, Array<Dynamic>>();
#end
curCharacter = character;
this.isPlayer = isPlayer;
antialiasing = ClientPrefs.globalAntialiasing;
var library:String = null;
switch (curCharacter)
{
//case 'your character name in case you want to hardcode him instead':
default:
var characterPath:String = 'characters/' + curCharacter + '.json';
#if MODS_ALLOWED
var path:String = Paths.modFolders(characterPath);
if (!FileSystem.exists(path)) {
path = Paths.getPreloadPath(characterPath);
}
if (!FileSystem.exists(path))
#else
var path:String = Paths.getPreloadPath(characterPath);
if (!Assets.exists(path))
#end
{
path = Paths.getPreloadPath('characters/' + DEFAULT_CHARACTER + '.json'); //If a character couldn't be found, change him to BF just to prevent a crash
}
#if MODS_ALLOWED
var rawJson = File.getContent(path);
#else
var rawJson = Assets.getText(path);
#end
var json:CharacterFile = cast Json.parse(rawJson);
if(Assets.exists(Paths.getPath('images/' + json.image + '.txt', TEXT))) {
frames = Paths.getPackerAtlas(json.image);
} else {
frames = Paths.getSparrowAtlas(json.image);
}
imageFile = json.image;
if(json.scale != 1) {
jsonScale = json.scale;
setGraphicSize(Std.int(width * jsonScale));
updateHitbox();
}
positionArray = json.position;
cameraPosition = json.camera_position;
healthIcon = json.healthicon;
singDuration = json.sing_duration;
flipX = !!json.flip_x;
if(json.no_antialiasing) {
antialiasing = false;
noAntialiasing = true;
}
if(json.healthbar_colors != null && json.healthbar_colors.length > 2)
healthColorArray = json.healthbar_colors;
antialiasing = !noAntialiasing;
if(!ClientPrefs.globalAntialiasing) antialiasing = false;
animationsArray = json.animations;
if(animationsArray != null && animationsArray.length > 0) {
for (anim in animationsArray) {
var animAnim:String = '' + anim.anim;
var animName:String = '' + anim.name;
var animFps:Int = anim.fps;
var animLoop:Bool = !!anim.loop; //Bruh
var animIndices:Array<Int> = anim.indices;
if(animIndices != null && animIndices.length > 0) {
animation.addByIndices(animAnim, animName, animIndices, "", animFps, animLoop);
} else {
animation.addByPrefix(animAnim, animName, animFps, animLoop);
}
if(anim.offsets != null && anim.offsets.length > 1) {
addOffset(anim.anim, anim.offsets[0], anim.offsets[1]);
}
}
} else {
quickAnimAdd('idle', 'BF idle dance');
}
//trace('Loaded file to character ' + curCharacter);
}
originalFlipX = flipX;
recalculateDanceIdle();
dance();
if (isPlayer)
{
flipX = !flipX;
/*// Doesn't flip for BF, since his are already in the right place???
if (!curCharacter.startsWith('bf'))
{
// var animArray
if(animation.getByName('singLEFT') != null && animation.getByName('singRIGHT') != null)
{
var oldRight = animation.getByName('singRIGHT').frames;
animation.getByName('singRIGHT').frames = animation.getByName('singLEFT').frames;
animation.getByName('singLEFT').frames = oldRight;
}
// IF THEY HAVE MISS ANIMATIONS??
if (animation.getByName('singLEFTmiss') != null && animation.getByName('singRIGHTmiss') != null)
{
var oldMiss = animation.getByName('singRIGHTmiss').frames;
animation.getByName('singRIGHTmiss').frames = animation.getByName('singLEFTmiss').frames;
animation.getByName('singLEFTmiss').frames = oldMiss;
}
}*/
}
}
override function update(elapsed:Float)
{
if(!debugMode && animation.curAnim != null)
{
if(heyTimer > 0)
{
heyTimer -= elapsed;
if(heyTimer <= 0)
{
if(specialAnim && animation.curAnim.name == 'hey' || animation.curAnim.name == 'cheer')
{
specialAnim = false;
dance();
}
heyTimer = 0;
}
} else if(specialAnim && animation.curAnim.finished)
{
specialAnim = false;
dance();
}
if (!isPlayer)
{
if (animation.curAnim.name.startsWith('sing'))
{
holdTimer += elapsed;
}
if (holdTimer >= Conductor.stepCrochet * 0.001 * singDuration)
{
dance();
holdTimer = 0;
}
}
if(animation.curAnim.finished && animation.getByName(animation.curAnim.name + '-loop') != null)
{
playAnim(animation.curAnim.name + '-loop');
}
}
super.update(elapsed);
}
public var danced:Bool = false;
/**
* FOR GF DANCING SHIT
*/
public function dance()
{
if (!debugMode && !specialAnim)
{
if(danceIdle)
{
danced = !danced;
if (danced)
playAnim('danceRight' + idleSuffix);
else
playAnim('danceLeft' + idleSuffix);
}
else if(animation.getByName('idle' + idleSuffix) != null) {
playAnim('idle' + idleSuffix);
}
}
}
public function playAnim(AnimName:String, Force:Bool = false, Reversed:Bool = false, Frame:Int = 0):Void
{
specialAnim = false;
animation.play(AnimName, Force, Reversed, Frame);
var daOffset = animOffsets.get(AnimName);
if (animOffsets.exists(AnimName))
{
offset.set(daOffset[0], daOffset[1]);
}
else
offset.set(0, 0);
if (curCharacter.startsWith('gf'))
{
if (AnimName == 'singLEFT')
{
danced = true;
}
else if (AnimName == 'singRIGHT')
{
danced = false;
}
if (AnimName == 'singUP' || AnimName == 'singDOWN')
{
danced = !danced;
}
}
}
public function recalculateDanceIdle() {
danceIdle = (animation.getByName('danceLeft' + idleSuffix) != null && animation.getByName('danceRight' + idleSuffix) != null);
}
public function addOffset(name:String, x:Float = 0, y:Float = 0)
{
animOffsets[name] = [x, y];
}
public function quickAnimAdd(name:String, anim:String)
{
animation.addByPrefix(name, anim, 24, false);
}
}

80
source/ChartParser.hx Normal file
View file

@ -0,0 +1,80 @@
package;
import flixel.util.FlxStringUtil;
using StringTools;
class ChartParser
{
static public function parse(songName:String, section:Int):Array<Dynamic>
{
var IMG_WIDTH:Int = 8;
var regex:EReg = new EReg("[ \t]*((\r\n)|\r|\n)[ \t]*", "g");
var csvData = FlxStringUtil.imageToCSV(Paths.file('data/' + songName + '/' + songName + '_section' + section + '.png'));
var lines:Array<String> = regex.split(csvData);
var rows:Array<String> = lines.filter(function(line) return line != "");
csvData.replace("\n", ',');
var heightInTiles = rows.length;
var widthInTiles = 0;
var row:Int = 0;
// LMAOOOO STOLE ALL THIS FROM FLXBASETILEMAP LOLOL
var dopeArray:Array<Int> = [];
while (row < heightInTiles)
{
var rowString = rows[row];
if (rowString.endsWith(","))
rowString = rowString.substr(0, rowString.length - 1);
var columns = rowString.split(",");
if (columns.length == 0)
{
heightInTiles--;
continue;
}
if (widthInTiles == 0)
{
widthInTiles = columns.length;
}
var column = 0;
var pushedInColumn:Bool = false;
while (column < widthInTiles)
{
// the current tile to be added:
var columnString = columns[column];
var curTile = Std.parseInt(columnString);
if (curTile == null)
throw 'String in row $row, column $column is not a valid integer: "$columnString"';
if (curTile == 1)
{
if (column < 4)
dopeArray.push(column + 1);
else
{
var tempCol = (column + 1) * -1;
tempCol += 4;
dopeArray.push(tempCol);
}
pushedInColumn = true;
}
column++;
}
if (!pushedInColumn)
dopeArray.push(0);
row++;
}
return dopeArray;
}
}

51
source/CheckboxThingie.hx Normal file
View file

@ -0,0 +1,51 @@
package;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
class CheckboxThingie extends FlxSprite
{
public var sprTracker:FlxSprite;
public var daValue(default, set):Bool;
public function new(x:Float = 0, y:Float = 0, ?checked = false) {
super(x, y);
frames = Paths.getSparrowAtlas('checkboxThingie');
animation.addByPrefix("static", "Check Box unselected", 24, false);
animation.addByPrefix("checked", "Check Box selecting animation", 24, false);
antialiasing = ClientPrefs.globalAntialiasing;
setGraphicSize(Std.int(0.6 * width));
updateHitbox();
set_daValue(checked);
}
override function update(elapsed:Float) {
/*switch (animation.curAnim.name) {
case "checked":
offset.set(17, 70);
case "static":
offset.set(0, 0);
}*/
if (sprTracker != null)
setPosition(sprTracker.x - 130, sprTracker.y + 30);
/*if (sprTracker != null)
setPosition(sprTracker.x - 100, sprTracker.y + 5);*/
super.update(elapsed);
}
private function set_daValue(value:Bool):Bool {
if(value) {
if(animation.curAnim.name != 'checked') {
animation.play('checked', true);
//offset.set(17, 70);
}
} else {
animation.play("static");
//offset.set(0, 0);
}
return value;
}
}

161
source/ClientPrefs.hx Normal file
View file

@ -0,0 +1,161 @@
package;
import flixel.FlxG;
import flixel.util.FlxSave;
import flixel.input.keyboard.FlxKey;
import flixel.graphics.FlxGraphic;
import Controls;
class ClientPrefs {
public static var downScroll:Bool = false;
public static var middleScroll:Bool = false;
public static var showFPS:Bool = true;
public static var flashing:Bool = true;
public static var globalAntialiasing:Bool = true;
public static var noteSplashes:Bool = true;
public static var lowQuality:Bool = false;
public static var framerate:Int = 60;
public static var cursing:Bool = true;
public static var violence:Bool = true;
public static var camZooms:Bool = true;
public static var hideHud:Bool = false;
public static var noteOffset:Int = 0;
public static var arrowHSV:Array<Array<Int>> = [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]];
public static var imagesPersist:Bool = false;
public static var ghostTapping:Bool = true;
public static var hideTime:Bool = false;
//Every key has two binds, these binds are defined on defaultKeys! If you want your control to be changeable, you have to add it on ControlsSubState (inside OptionsState.hx)'s list
public static var keyBinds:Map<String, Dynamic> = new Map<String, Dynamic>();
public static var defaultKeys:Map<String, Dynamic>;
public static function startControls() {
//Key Bind, Name for ControlsSubState
keyBinds.set('note_left', [A, LEFT]);
keyBinds.set('note_down', [S, DOWN]);
keyBinds.set('note_up', [W, UP]);
keyBinds.set('note_right', [D, RIGHT]);
keyBinds.set('ui_left', [A, LEFT]);
keyBinds.set('ui_down', [S, DOWN]);
keyBinds.set('ui_up', [W, UP]);
keyBinds.set('ui_right', [D, RIGHT]);
keyBinds.set('accept', [SPACE, ENTER]);
keyBinds.set('back', [BACKSPACE, ESCAPE]);
keyBinds.set('pause', [ENTER, ESCAPE]);
keyBinds.set('reset', [R, NONE]);
// Don't delete this
defaultKeys = keyBinds.copy();
}
public static function saveSettings() {
FlxG.save.data.downScroll = downScroll;
FlxG.save.data.middleScroll = middleScroll;
FlxG.save.data.showFPS = showFPS;
FlxG.save.data.flashing = flashing;
FlxG.save.data.globalAntialiasing = globalAntialiasing;
FlxG.save.data.noteSplashes = noteSplashes;
FlxG.save.data.lowQuality = lowQuality;
FlxG.save.data.framerate = framerate;
//FlxG.save.data.cursing = cursing;
//FlxG.save.data.violence = violence;
FlxG.save.data.camZooms = camZooms;
FlxG.save.data.noteOffset = noteOffset;
FlxG.save.data.hideHud = hideHud;
FlxG.save.data.arrowHSV = arrowHSV;
FlxG.save.data.imagesPersist = imagesPersist;
FlxG.save.data.ghostTapping = ghostTapping;
FlxG.save.data.hideTime = hideTime;
FlxG.save.data.achievementsMap = Achievements.achievementsMap;
FlxG.save.data.henchmenDeath = Achievements.henchmenDeath;
FlxG.save.flush();
var save:FlxSave = new FlxSave();
save.bind('controls_v2', 'ninjamuffin99'); //Placing this in a separate save so that it can be manually deleted without removing your Score and stuff
save.data.customControls = keyBinds;
save.flush();
FlxG.log.add("Settings saved!");
}
public static function loadPrefs() {
if(FlxG.save.data.downScroll != null) {
downScroll = FlxG.save.data.downScroll;
}
if(FlxG.save.data.middleScroll != null) {
middleScroll = FlxG.save.data.middleScroll;
}
if(FlxG.save.data.showFPS != null) {
showFPS = FlxG.save.data.showFPS;
if(Main.fpsVar != null) {
Main.fpsVar.visible = showFPS;
}
}
if(FlxG.save.data.flashing != null) {
flashing = FlxG.save.data.flashing;
}
if(FlxG.save.data.globalAntialiasing != null) {
globalAntialiasing = FlxG.save.data.globalAntialiasing;
}
if(FlxG.save.data.noteSplashes != null) {
noteSplashes = FlxG.save.data.noteSplashes;
}
if(FlxG.save.data.lowQuality != null) {
lowQuality = FlxG.save.data.lowQuality;
}
if(FlxG.save.data.framerate != null) {
framerate = FlxG.save.data.framerate;
if(framerate > FlxG.drawFramerate) {
FlxG.updateFramerate = framerate;
FlxG.drawFramerate = framerate;
} else {
FlxG.drawFramerate = framerate;
FlxG.updateFramerate = framerate;
}
}
/*if(FlxG.save.data.cursing != null) {
cursing = FlxG.save.data.cursing;
}
if(FlxG.save.data.violence != null) {
violence = FlxG.save.data.violence;
}*/
if(FlxG.save.data.camZooms != null) {
camZooms = FlxG.save.data.camZooms;
}
if(FlxG.save.data.hideHud != null) {
hideHud = FlxG.save.data.hideHud;
}
if(FlxG.save.data.noteOffset != null) {
noteOffset = FlxG.save.data.noteOffset;
}
if(FlxG.save.data.arrowHSV != null) {
arrowHSV = FlxG.save.data.arrowHSV;
}
if(FlxG.save.data.imagesPersist != null) {
imagesPersist = FlxG.save.data.imagesPersist;
FlxGraphic.defaultPersist = ClientPrefs.imagesPersist;
}
if(FlxG.save.data.ghostTapping != null) {
ghostTapping = FlxG.save.data.ghostTapping;
}
if(FlxG.save.data.hideTime != null) {
hideTime = FlxG.save.data.hideTime;
}
var save:FlxSave = new FlxSave();
save.bind('controls_v2', 'ninjamuffin99');
if(save != null && save.data.customControls != null) {
var loadedControls:Map<String, Dynamic> = save.data.customControls;
for (control => keys in loadedControls) {
keyBinds.set(control, keys);
}
reloadControls();
}
}
public static function reloadControls() {
PlayerSettings.player1.controls.setKeyboardScheme(KeyboardScheme.Solo);
}
}

217
source/ColorSwap.hx Normal file
View file

@ -0,0 +1,217 @@
package;
import flixel.FlxG;
import flixel.system.FlxAssets.FlxShader;
class ColorSwap {
public var shader(default, null):ColorSwapShader = new ColorSwapShader();
public var hue(default, set):Float = 0;
public var saturation(default, set):Float = 0;
public var brightness(default, set):Float = 0;
private function set_hue(value:Float) {
hue = value;
shader.uTime.value[0] = hue;
return hue;
}
private function set_saturation(value:Float) {
saturation = value;
shader.uTime.value[1] = saturation;
return saturation;
}
private function set_brightness(value:Float) {
brightness = value;
shader.uTime.value[2] = brightness;
return brightness;
}
public function new()
{
shader.uTime.value = [0, 0, 0];
shader.awesomeOutline.value = [false];
}
}
class ColorSwapShader extends FlxShader {
@:glFragmentSource('
varying float openfl_Alphav;
varying vec4 openfl_ColorMultiplierv;
varying vec4 openfl_ColorOffsetv;
varying vec2 openfl_TextureCoordv;
uniform bool openfl_HasColorTransform;
uniform vec2 openfl_TextureSize;
uniform sampler2D bitmap;
uniform bool hasTransform;
uniform bool hasColorTransform;
vec4 flixel_texture2D(sampler2D bitmap, vec2 coord)
{
vec4 color = texture2D(bitmap, coord);
if (!hasTransform)
{
return color;
}
if (color.a == 0.0)
{
return vec4(0.0, 0.0, 0.0, 0.0);
}
if (!hasColorTransform)
{
return color * openfl_Alphav;
}
color = vec4(color.rgb / color.a, color.a);
mat4 colorMultiplier = mat4(0);
colorMultiplier[0][0] = openfl_ColorMultiplierv.x;
colorMultiplier[1][1] = openfl_ColorMultiplierv.y;
colorMultiplier[2][2] = openfl_ColorMultiplierv.z;
colorMultiplier[3][3] = openfl_ColorMultiplierv.w;
color = clamp(openfl_ColorOffsetv + (color * colorMultiplier), 0.0, 1.0);
if (color.a > 0.0)
{
return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav);
}
return vec4(0.0, 0.0, 0.0, 0.0);
}
uniform vec3 uTime;
uniform bool awesomeOutline;
const float offset = 1.0 / 128.0;
vec3 normalizeColor(vec3 color)
{
return vec3(
color[0] / 255.0,
color[1] / 255.0,
color[2] / 255.0
);
}
vec3 rgb2hsv(vec3 c)
{
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
vec3 hsv2rgb(vec3 c)
{
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
void main()
{
vec4 color = flixel_texture2D(bitmap, openfl_TextureCoordv);
vec4 swagColor = vec4(rgb2hsv(vec3(color[0], color[1], color[2])), color[3]);
// [0] is the hue???
swagColor[0] = swagColor[0] + uTime[0];
swagColor[1] = swagColor[1] + uTime[1];
swagColor[2] = swagColor[2] * (1.0 + uTime[2]);
if(swagColor[1] < 0.0)
{
swagColor[1] = 0.0;
}
else if(swagColor[1] > 1.0)
{
swagColor[1] = 1.0;
}
color = vec4(hsv2rgb(vec3(swagColor[0], swagColor[1], swagColor[2])), swagColor[3]);
if (awesomeOutline)
{
// Outline bullshit?
vec2 size = vec2(3, 3);
if (color.a <= 0.5) {
float w = size.x / openfl_TextureSize.x;
float h = size.y / openfl_TextureSize.y;
if (flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x + w, openfl_TextureCoordv.y)).a != 0.
|| flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x - w, openfl_TextureCoordv.y)).a != 0.
|| flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x, openfl_TextureCoordv.y + h)).a != 0.
|| flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x, openfl_TextureCoordv.y - h)).a != 0.)
color = vec4(1.0, 1.0, 1.0, 1.0);
}
}
gl_FragColor = color;
/*
if (color.a > 0.5)
gl_FragColor = color;
else
{
float a = flixel_texture2D(bitmap, vec2(openfl_TextureCoordv + offset, openfl_TextureCoordv.y)).a +
flixel_texture2D(bitmap, vec2(openfl_TextureCoordv, openfl_TextureCoordv.y - offset)).a +
flixel_texture2D(bitmap, vec2(openfl_TextureCoordv - offset, openfl_TextureCoordv.y)).a +
flixel_texture2D(bitmap, vec2(openfl_TextureCoordv, openfl_TextureCoordv.y + offset)).a;
if (color.a < 1.0 && a > 0.0)
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.8);
else
gl_FragColor = color;
} */
}')
@:glVertexSource('
attribute float openfl_Alpha;
attribute vec4 openfl_ColorMultiplier;
attribute vec4 openfl_ColorOffset;
attribute vec4 openfl_Position;
attribute vec2 openfl_TextureCoord;
varying float openfl_Alphav;
varying vec4 openfl_ColorMultiplierv;
varying vec4 openfl_ColorOffsetv;
varying vec2 openfl_TextureCoordv;
uniform mat4 openfl_Matrix;
uniform bool openfl_HasColorTransform;
uniform vec2 openfl_TextureSize;
attribute float alpha;
attribute vec4 colorMultiplier;
attribute vec4 colorOffset;
uniform bool hasColorTransform;
void main(void)
{
openfl_Alphav = openfl_Alpha;
openfl_TextureCoordv = openfl_TextureCoord;
if (openfl_HasColorTransform) {
openfl_ColorMultiplierv = openfl_ColorMultiplier;
openfl_ColorOffsetv = openfl_ColorOffset / 255.0;
}
gl_Position = openfl_Matrix * openfl_Position;
openfl_Alphav = openfl_Alpha * alpha;
if (hasColorTransform)
{
openfl_ColorOffsetv = colorOffset / 255.0;
openfl_ColorMultiplierv = colorMultiplier;
}
}')
public function new()
{
super();
}
}

69
source/Conductor.hx Normal file
View file

@ -0,0 +1,69 @@
package;
import Song.SwagSong;
/**
* ...
* @author
*/
typedef BPMChangeEvent =
{
var stepTime:Int;
var songTime:Float;
var bpm:Float;
}
class Conductor
{
public static var bpm:Float = 100;
public static var crochet:Float = ((60 / bpm) * 1000); // beats in milliseconds
public static var stepCrochet:Float = crochet / 4; // steps in milliseconds
public static var songPosition:Float;
public static var lastSongPos:Float;
public static var offset:Float = 0;
public static var safeFrames:Int = 10;
public static var safeZoneOffset:Float = (safeFrames / 60) * 1000; // is calculated in create(), is safeFrames in milliseconds
public static var bpmChangeMap:Array<BPMChangeEvent> = [];
public function new()
{
}
public static function mapBPMChanges(song:SwagSong)
{
bpmChangeMap = [];
var curBPM:Float = song.bpm;
var totalSteps:Int = 0;
var totalPos:Float = 0;
for (i in 0...song.notes.length)
{
if(song.notes[i].changeBPM && song.notes[i].bpm != curBPM)
{
curBPM = song.notes[i].bpm;
var event:BPMChangeEvent = {
stepTime: totalSteps,
songTime: totalPos,
bpm: curBPM
};
bpmChangeMap.push(event);
}
var deltaSteps:Int = song.notes[i].lengthInSteps;
totalSteps += deltaSteps;
totalPos += ((60 / curBPM) * 1000 / 4) * deltaSteps;
}
trace("new BPM map BUDDY " + bpmChangeMap);
}
public static function changeBPM(newBpm:Float)
{
bpm = newBpm;
crochet = ((60 / bpm) * 1000);
stepCrochet = crochet / 4;
}
}

916
source/Controls.hx Normal file
View file

@ -0,0 +1,916 @@
package;
import flixel.FlxG;
import flixel.input.FlxInput;
import flixel.input.actions.FlxAction;
import flixel.input.actions.FlxActionInput;
import flixel.input.actions.FlxActionInputDigital;
import flixel.input.actions.FlxActionManager;
import flixel.input.actions.FlxActionSet;
import flixel.input.gamepad.FlxGamepadButton;
import flixel.input.gamepad.FlxGamepadInputID;
import flixel.input.keyboard.FlxKey;
#if (haxe >= "4.0.0")
enum abstract Action(String) to String from String
{
var UI_UP = "ui_up";
var UI_LEFT = "ui_left";
var UI_RIGHT = "ui_right";
var UI_DOWN = "ui_down";
var UI_UP_P = "ui_up-press";
var UI_LEFT_P = "ui_left-press";
var UI_RIGHT_P = "ui_right-press";
var UI_DOWN_P = "ui_down-press";
var UI_UP_R = "ui_up-release";
var UI_LEFT_R = "ui_left-release";
var UI_RIGHT_R = "ui_right-release";
var UI_DOWN_R = "ui_down-release";
var NOTE_UP = "note_up";
var NOTE_LEFT = "note_left";
var NOTE_RIGHT = "note_right";
var NOTE_DOWN = "note_down";
var NOTE_UP_P = "note_up-press";
var NOTE_LEFT_P = "note_left-press";
var NOTE_RIGHT_P = "note_right-press";
var NOTE_DOWN_P = "note_down-press";
var NOTE_UP_R = "note_up-release";
var NOTE_LEFT_R = "note_left-release";
var NOTE_RIGHT_R = "note_right-release";
var NOTE_DOWN_R = "note_down-release";
var ACCEPT = "accept";
var BACK = "back";
var PAUSE = "pause";
var RESET = "reset";
}
#else
@:enum
abstract Action(String) to String from String
{
var UI_UP = "ui_up";
var UI_LEFT = "ui_left";
var UI_RIGHT = "ui_right";
var UI_DOWN = "ui_down";
var UI_UP_P = "ui_up-press";
var UI_LEFT_P = "ui_left-press";
var UI_RIGHT_P = "ui_right-press";
var UI_DOWN_P = "ui_down-press";
var UI_UP_R = "ui_up-release";
var UI_LEFT_R = "ui_left-release";
var UI_RIGHT_R = "ui_right-release";
var UI_DOWN_R = "ui_down-release";
var NOTE_UP = "note_up";
var NOTE_LEFT = "note_left";
var NOTE_RIGHT = "note_right";
var NOTE_DOWN = "note_down";
var NOTE_UP_P = "note_up-press";
var NOTE_LEFT_P = "note_left-press";
var NOTE_RIGHT_P = "note_right-press";
var NOTE_DOWN_P = "note_down-press";
var NOTE_UP_R = "note_up-release";
var NOTE_LEFT_R = "note_left-release";
var NOTE_RIGHT_R = "note_right-release";
var NOTE_DOWN_R = "note_down-release";
var ACCEPT = "accept";
var BACK = "back";
var PAUSE = "pause";
var RESET = "reset";
}
#end
enum Device
{
Keys;
Gamepad(id:Int);
}
/**
* Since, in many cases multiple actions should use similar keys, we don't want the
* rebinding UI to list every action. ActionBinders are what the user percieves as
* an input so, for instance, they can't set jump-press and jump-release to different keys.
*/
enum Control
{
UI_UP;
UI_LEFT;
UI_RIGHT;
UI_DOWN;
NOTE_UP;
NOTE_LEFT;
NOTE_RIGHT;
NOTE_DOWN;
RESET;
ACCEPT;
BACK;
PAUSE;
}
enum KeyboardScheme
{
Solo;
Duo(first:Bool);
None;
Custom;
}
/**
* A list of actions that a player would invoke via some input device.
* Uses FlxActions to funnel various inputs to a single action.
*/
class Controls extends FlxActionSet
{
var _ui_up = new FlxActionDigital(Action.UI_UP);
var _ui_left = new FlxActionDigital(Action.UI_LEFT);
var _ui_right = new FlxActionDigital(Action.UI_RIGHT);
var _ui_down = new FlxActionDigital(Action.UI_DOWN);
var _ui_upP = new FlxActionDigital(Action.UI_UP_P);
var _ui_leftP = new FlxActionDigital(Action.UI_LEFT_P);
var _ui_rightP = new FlxActionDigital(Action.UI_RIGHT_P);
var _ui_downP = new FlxActionDigital(Action.UI_DOWN_P);
var _ui_upR = new FlxActionDigital(Action.UI_UP_R);
var _ui_leftR = new FlxActionDigital(Action.UI_LEFT_R);
var _ui_rightR = new FlxActionDigital(Action.UI_RIGHT_R);
var _ui_downR = new FlxActionDigital(Action.UI_DOWN_R);
var _note_up = new FlxActionDigital(Action.NOTE_UP);
var _note_left = new FlxActionDigital(Action.NOTE_LEFT);
var _note_right = new FlxActionDigital(Action.NOTE_RIGHT);
var _note_down = new FlxActionDigital(Action.NOTE_DOWN);
var _note_upP = new FlxActionDigital(Action.NOTE_UP_P);
var _note_leftP = new FlxActionDigital(Action.NOTE_LEFT_P);
var _note_rightP = new FlxActionDigital(Action.NOTE_RIGHT_P);
var _note_downP = new FlxActionDigital(Action.NOTE_DOWN_P);
var _note_upR = new FlxActionDigital(Action.NOTE_UP_R);
var _note_leftR = new FlxActionDigital(Action.NOTE_LEFT_R);
var _note_rightR = new FlxActionDigital(Action.NOTE_RIGHT_R);
var _note_downR = new FlxActionDigital(Action.NOTE_DOWN_R);
var _accept = new FlxActionDigital(Action.ACCEPT);
var _back = new FlxActionDigital(Action.BACK);
var _pause = new FlxActionDigital(Action.PAUSE);
var _reset = new FlxActionDigital(Action.RESET);
#if (haxe >= "4.0.0")
var byName:Map<String, FlxActionDigital> = [];
#else
var byName:Map<String, FlxActionDigital> = new Map<String, FlxActionDigital>();
#end
public var gamepadsAdded:Array<Int> = [];
public var keyboardScheme = KeyboardScheme.None;
public var UI_UP(get, never):Bool;
inline function get_UI_UP()
return _ui_up.check();
public var UI_LEFT(get, never):Bool;
inline function get_UI_LEFT()
return _ui_left.check();
public var UI_RIGHT(get, never):Bool;
inline function get_UI_RIGHT()
return _ui_right.check();
public var UI_DOWN(get, never):Bool;
inline function get_UI_DOWN()
return _ui_down.check();
public var UI_UP_P(get, never):Bool;
inline function get_UI_UP_P()
return _ui_upP.check();
public var UI_LEFT_P(get, never):Bool;
inline function get_UI_LEFT_P()
return _ui_leftP.check();
public var UI_RIGHT_P(get, never):Bool;
inline function get_UI_RIGHT_P()
return _ui_rightP.check();
public var UI_DOWN_P(get, never):Bool;
inline function get_UI_DOWN_P()
return _ui_downP.check();
public var UI_UP_R(get, never):Bool;
inline function get_UI_UP_R()
return _ui_upR.check();
public var UI_LEFT_R(get, never):Bool;
inline function get_UI_LEFT_R()
return _ui_leftR.check();
public var UI_RIGHT_R(get, never):Bool;
inline function get_UI_RIGHT_R()
return _ui_rightR.check();
public var UI_DOWN_R(get, never):Bool;
inline function get_UI_DOWN_R()
return _ui_downR.check();
public var NOTE_UP(get, never):Bool;
inline function get_NOTE_UP()
return _note_up.check();
public var NOTE_LEFT(get, never):Bool;
inline function get_NOTE_LEFT()
return _note_left.check();
public var NOTE_RIGHT(get, never):Bool;
inline function get_NOTE_RIGHT()
return _note_right.check();
public var NOTE_DOWN(get, never):Bool;
inline function get_NOTE_DOWN()
return _note_down.check();
public var NOTE_UP_P(get, never):Bool;
inline function get_NOTE_UP_P()
return _note_upP.check();
public var NOTE_LEFT_P(get, never):Bool;
inline function get_NOTE_LEFT_P()
return _note_leftP.check();
public var NOTE_RIGHT_P(get, never):Bool;
inline function get_NOTE_RIGHT_P()
return _note_rightP.check();
public var NOTE_DOWN_P(get, never):Bool;
inline function get_NOTE_DOWN_P()
return _note_downP.check();
public var NOTE_UP_R(get, never):Bool;
inline function get_NOTE_UP_R()
return _note_upR.check();
public var NOTE_LEFT_R(get, never):Bool;
inline function get_NOTE_LEFT_R()
return _note_leftR.check();
public var NOTE_RIGHT_R(get, never):Bool;
inline function get_NOTE_RIGHT_R()
return _note_rightR.check();
public var NOTE_DOWN_R(get, never):Bool;
inline function get_NOTE_DOWN_R()
return _note_downR.check();
public var ACCEPT(get, never):Bool;
inline function get_ACCEPT()
return _accept.check();
public var BACK(get, never):Bool;
inline function get_BACK()
return _back.check();
public var PAUSE(get, never):Bool;
inline function get_PAUSE()
return _pause.check();
public var RESET(get, never):Bool;
inline function get_RESET()
return _reset.check();
#if (haxe >= "4.0.0")
public function new(name, scheme = None)
{
super(name);
add(_ui_up);
add(_ui_left);
add(_ui_right);
add(_ui_down);
add(_ui_upP);
add(_ui_leftP);
add(_ui_rightP);
add(_ui_downP);
add(_ui_upR);
add(_ui_leftR);
add(_ui_rightR);
add(_ui_downR);
add(_note_up);
add(_note_left);
add(_note_right);
add(_note_down);
add(_note_upP);
add(_note_leftP);
add(_note_rightP);
add(_note_downP);
add(_note_upR);
add(_note_leftR);
add(_note_rightR);
add(_note_downR);
add(_accept);
add(_back);
add(_pause);
add(_reset);
for (action in digitalActions)
byName[action.name] = action;
setKeyboardScheme(scheme, false);
}
#else
public function new(name, scheme:KeyboardScheme = null)
{
super(name);
add(_ui_up);
add(_ui_left);
add(_ui_right);
add(_ui_down);
add(_ui_upP);
add(_ui_leftP);
add(_ui_rightP);
add(_ui_downP);
add(_ui_upR);
add(_ui_leftR);
add(_ui_rightR);
add(_ui_downR);
add(_note_up);
add(_note_left);
add(_note_right);
add(_note_down);
add(_note_upP);
add(_note_leftP);
add(_note_rightP);
add(_note_downP);
add(_note_upR);
add(_note_leftR);
add(_note_rightR);
add(_note_downR);
add(_accept);
add(_back);
add(_pause);
add(_reset);
for (action in digitalActions)
byName[action.name] = action;
if (scheme == null)
scheme = None;
setKeyboardScheme(scheme, false);
}
#end
override function update()
{
super.update();
}
// inline
public function checkByName(name:Action):Bool
{
#if debug
if (!byName.exists(name))
throw 'Invalid name: $name';
#end
return byName[name].check();
}
public function getDialogueName(action:FlxActionDigital):String
{
var input = action.inputs[0];
return switch input.device
{
case KEYBOARD: return '[${(input.inputID : FlxKey)}]';
case GAMEPAD: return '(${(input.inputID : FlxGamepadInputID)})';
case device: throw 'unhandled device: $device';
}
}
public function getDialogueNameFromToken(token:String):String
{
return getDialogueName(getActionFromControl(Control.createByName(token.toUpperCase())));
}
function getActionFromControl(control:Control):FlxActionDigital
{
return switch (control)
{
case UI_UP: _ui_up;
case UI_DOWN: _ui_down;
case UI_LEFT: _ui_left;
case UI_RIGHT: _ui_right;
case NOTE_UP: _note_up;
case NOTE_DOWN: _note_down;
case NOTE_LEFT: _note_left;
case NOTE_RIGHT: _note_right;
case ACCEPT: _accept;
case BACK: _back;
case PAUSE: _pause;
case RESET: _reset;
}
}
static function init():Void
{
var actions = new FlxActionManager();
FlxG.inputs.add(actions);
}
/**
* Calls a function passing each action bound by the specified control
* @param control
* @param func
* @return ->Void)
*/
function forEachBound(control:Control, func:FlxActionDigital->FlxInputState->Void)
{
switch (control)
{
case UI_UP:
func(_ui_up, PRESSED);
func(_ui_upP, JUST_PRESSED);
func(_ui_upR, JUST_RELEASED);
case UI_LEFT:
func(_ui_left, PRESSED);
func(_ui_leftP, JUST_PRESSED);
func(_ui_leftR, JUST_RELEASED);
case UI_RIGHT:
func(_ui_right, PRESSED);
func(_ui_rightP, JUST_PRESSED);
func(_ui_rightR, JUST_RELEASED);
case UI_DOWN:
func(_ui_down, PRESSED);
func(_ui_downP, JUST_PRESSED);
func(_ui_downR, JUST_RELEASED);
case NOTE_UP:
func(_note_up, PRESSED);
func(_note_upP, JUST_PRESSED);
func(_note_upR, JUST_RELEASED);
case NOTE_LEFT:
func(_note_left, PRESSED);
func(_note_leftP, JUST_PRESSED);
func(_note_leftR, JUST_RELEASED);
case NOTE_RIGHT:
func(_note_right, PRESSED);
func(_note_rightP, JUST_PRESSED);
func(_note_rightR, JUST_RELEASED);
case NOTE_DOWN:
func(_note_down, PRESSED);
func(_note_downP, JUST_PRESSED);
func(_note_downR, JUST_RELEASED);
case ACCEPT:
func(_accept, JUST_PRESSED);
case BACK:
func(_back, JUST_PRESSED);
case PAUSE:
func(_pause, JUST_PRESSED);
case RESET:
func(_reset, JUST_PRESSED);
}
}
public function replaceBinding(control:Control, device:Device, ?toAdd:Int, ?toRemove:Int)
{
if (toAdd == toRemove)
return;
switch (device)
{
case Keys:
if (toRemove != null)
unbindKeys(control, [toRemove]);
if (toAdd != null)
bindKeys(control, [toAdd]);
case Gamepad(id):
if (toRemove != null)
unbindButtons(control, id, [toRemove]);
if (toAdd != null)
bindButtons(control, id, [toAdd]);
}
}
public function copyFrom(controls:Controls, ?device:Device)
{
#if (haxe >= "4.0.0")
for (name => action in controls.byName)
{
for (input in action.inputs)
{
if (device == null || isDevice(input, device))
byName[name].add(cast input);
}
}
#else
for (name in controls.byName.keys())
{
var action = controls.byName[name];
for (input in action.inputs)
{
if (device == null || isDevice(input, device))
byName[name].add(cast input);
}
}
#end
switch (device)
{
case null:
// add all
#if (haxe >= "4.0.0")
for (gamepad in controls.gamepadsAdded)
if (!gamepadsAdded.contains(gamepad))
gamepadsAdded.push(gamepad);
#else
for (gamepad in controls.gamepadsAdded)
if (gamepadsAdded.indexOf(gamepad) == -1)
gamepadsAdded.push(gamepad);
#end
mergeKeyboardScheme(controls.keyboardScheme);
case Gamepad(id):
gamepadsAdded.push(id);
case Keys:
mergeKeyboardScheme(controls.keyboardScheme);
}
}
inline public function copyTo(controls:Controls, ?device:Device)
{
controls.copyFrom(this, device);
}
function mergeKeyboardScheme(scheme:KeyboardScheme):Void
{
if (scheme != None)
{
switch (keyboardScheme)
{
case None:
keyboardScheme = scheme;
default:
keyboardScheme = Custom;
}
}
}
/**
* Sets all actions that pertain to the binder to trigger when the supplied keys are used.
* If binder is a literal you can inline this
*/
public function bindKeys(control:Control, keys:Array<FlxKey>)
{
var copyKeys:Array<FlxKey> = keys.copy();
for (i in 0...copyKeys.length) {
if(i == NONE) copyKeys.remove(i);
}
#if (haxe >= "4.0.0")
inline forEachBound(control, (action, state) -> addKeys(action, copyKeys, state));
#else
forEachBound(control, function(action, state) addKeys(action, copyKeys, state));
#end
}
/**
* Sets all actions that pertain to the binder to trigger when the supplied keys are used.
* If binder is a literal you can inline this
*/
public function unbindKeys(control:Control, keys:Array<FlxKey>)
{
var copyKeys:Array<FlxKey> = keys.copy();
for (i in 0...copyKeys.length) {
if(i == NONE) copyKeys.remove(i);
}
#if (haxe >= "4.0.0")
inline forEachBound(control, (action, _) -> removeKeys(action, copyKeys));
#else
forEachBound(control, function(action, _) removeKeys(action, copyKeys));
#end
}
inline static function addKeys(action:FlxActionDigital, keys:Array<FlxKey>, state:FlxInputState)
{
for (key in keys)
if(key != NONE)
action.addKey(key, state);
}
static function removeKeys(action:FlxActionDigital, keys:Array<FlxKey>)
{
var i = action.inputs.length;
while (i-- > 0)
{
var input = action.inputs[i];
if (input.device == KEYBOARD && keys.indexOf(cast input.inputID) != -1)
action.remove(input);
}
}
public function setKeyboardScheme(scheme:KeyboardScheme, reset = true)
{
if (reset)
removeKeyboard();
keyboardScheme = scheme;
var keysMap = ClientPrefs.keyBinds;
#if (haxe >= "4.0.0")
switch (scheme)
{
case Solo:
inline bindKeys(Control.UI_UP, keysMap.get('ui_up'));
inline bindKeys(Control.UI_DOWN, keysMap.get('ui_down'));
inline bindKeys(Control.UI_LEFT, keysMap.get('ui_left'));
inline bindKeys(Control.UI_RIGHT, keysMap.get('ui_right'));
inline bindKeys(Control.NOTE_UP, keysMap.get('note_up'));
inline bindKeys(Control.NOTE_DOWN, keysMap.get('note_down'));
inline bindKeys(Control.NOTE_LEFT, keysMap.get('note_left'));
inline bindKeys(Control.NOTE_RIGHT, keysMap.get('note_right'));
inline bindKeys(Control.ACCEPT, keysMap.get('accept'));
inline bindKeys(Control.BACK, keysMap.get('back'));
inline bindKeys(Control.PAUSE, keysMap.get('pause'));
inline bindKeys(Control.RESET, keysMap.get('reset'));
case Duo(true):
inline bindKeys(Control.UI_UP, [W]);
inline bindKeys(Control.UI_DOWN, [S]);
inline bindKeys(Control.UI_LEFT, [A]);
inline bindKeys(Control.UI_RIGHT, [D]);
inline bindKeys(Control.NOTE_UP, [W]);
inline bindKeys(Control.NOTE_DOWN, [S]);
inline bindKeys(Control.NOTE_LEFT, [A]);
inline bindKeys(Control.NOTE_RIGHT, [D]);
inline bindKeys(Control.ACCEPT, [G, Z]);
inline bindKeys(Control.BACK, [H, X]);
inline bindKeys(Control.PAUSE, [ONE]);
inline bindKeys(Control.RESET, [R]);
case Duo(false):
inline bindKeys(Control.UI_UP, [FlxKey.UP]);
inline bindKeys(Control.UI_DOWN, [FlxKey.DOWN]);
inline bindKeys(Control.UI_LEFT, [FlxKey.LEFT]);
inline bindKeys(Control.UI_RIGHT, [FlxKey.RIGHT]);
inline bindKeys(Control.NOTE_UP, [FlxKey.UP]);
inline bindKeys(Control.NOTE_DOWN, [FlxKey.DOWN]);
inline bindKeys(Control.NOTE_LEFT, [FlxKey.LEFT]);
inline bindKeys(Control.NOTE_RIGHT, [FlxKey.RIGHT]);
inline bindKeys(Control.ACCEPT, [O]);
inline bindKeys(Control.BACK, [P]);
inline bindKeys(Control.PAUSE, [ENTER]);
inline bindKeys(Control.RESET, [BACKSPACE]);
case None: // nothing
case Custom: // nothing
}
#else
switch (scheme)
{
case Solo:
bindKeys(Control.UI_UP, [W, FlxKey.UP]);
bindKeys(Control.UI_DOWN, [S, FlxKey.DOWN]);
bindKeys(Control.UI_LEFT, [A, FlxKey.LEFT]);
bindKeys(Control.UI_RIGHT, [D, FlxKey.RIGHT]);
bindKeys(Control.NOTE_UP, [W, FlxKey.UP]);
bindKeys(Control.NOTE_DOWN, [S, FlxKey.DOWN]);
bindKeys(Control.NOTE_LEFT, [A, FlxKey.LEFT]);
bindKeys(Control.NOTE_RIGHT, [D, FlxKey.RIGHT]);
bindKeys(Control.ACCEPT, [Z, SPACE, ENTER]);
bindKeys(Control.BACK, [BACKSPACE, ESCAPE]);
bindKeys(Control.PAUSE, [P, ENTER, ESCAPE]);
bindKeys(Control.RESET, [R]);
case Duo(true):
bindKeys(Control.UI_UP, [W]);
bindKeys(Control.UI_DOWN, [S]);
bindKeys(Control.UI_LEFT, [A]);
bindKeys(Control.UI_RIGHT, [D]);
bindKeys(Control.NOTE_UP, [W]);
bindKeys(Control.NOTE_DOWN, [S]);
bindKeys(Control.NOTE_LEFT, [A]);
bindKeys(Control.NOTE_RIGHT, [D]);
bindKeys(Control.ACCEPT, [G, Z]);
bindKeys(Control.BACK, [H, X]);
bindKeys(Control.PAUSE, [ONE]);
bindKeys(Control.RESET, [R]);
case Duo(false):
bindKeys(Control.UI_UP, [FlxKey.UP]);
bindKeys(Control.UI_DOWN, [FlxKey.DOWN]);
bindKeys(Control.UI_LEFT, [FlxKey.LEFT]);
bindKeys(Control.UI_RIGHT, [FlxKey.RIGHT]);
bindKeys(Control.NOTE_UP, [FlxKey.UP]);
bindKeys(Control.NOTE_DOWN, [FlxKey.DOWN]);
bindKeys(Control.NOTE_LEFT, [FlxKey.LEFT]);
bindKeys(Control.NOTE_RIGHT, [FlxKey.RIGHT]);
bindKeys(Control.ACCEPT, [O]);
bindKeys(Control.BACK, [P]);
bindKeys(Control.PAUSE, [ENTER]);
bindKeys(Control.RESET, [BACKSPACE]);
case None: // nothing
case Custom: // nothing
}
#end
}
function removeKeyboard()
{
for (action in this.digitalActions)
{
var i = action.inputs.length;
while (i-- > 0)
{
var input = action.inputs[i];
if (input.device == KEYBOARD)
action.remove(input);
}
}
}
public function addGamepad(id:Int, ?buttonMap:Map<Control, Array<FlxGamepadInputID>>):Void
{
gamepadsAdded.push(id);
#if (haxe >= "4.0.0")
for (control => buttons in buttonMap)
inline bindButtons(control, id, buttons);
#else
for (control in buttonMap.keys())
bindButtons(control, id, buttonMap[control]);
#end
}
inline function addGamepadLiteral(id:Int, ?buttonMap:Map<Control, Array<FlxGamepadInputID>>):Void
{
gamepadsAdded.push(id);
#if (haxe >= "4.0.0")
for (control => buttons in buttonMap)
inline bindButtons(control, id, buttons);
#else
for (control in buttonMap.keys())
bindButtons(control, id, buttonMap[control]);
#end
}
public function removeGamepad(deviceID:Int = FlxInputDeviceID.ALL):Void
{
for (action in this.digitalActions)
{
var i = action.inputs.length;
while (i-- > 0)
{
var input = action.inputs[i];
if (input.device == GAMEPAD && (deviceID == FlxInputDeviceID.ALL || input.deviceID == deviceID))
action.remove(input);
}
}
gamepadsAdded.remove(deviceID);
}
public function addDefaultGamepad(id):Void
{
#if !switch
addGamepadLiteral(id, [
Control.ACCEPT => [A],
Control.BACK => [B],
Control.UI_UP => [DPAD_UP, LEFT_STICK_DIGITAL_UP],
Control.UI_DOWN => [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN],
Control.UI_LEFT => [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT],
Control.UI_RIGHT => [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT],
Control.NOTE_UP => [DPAD_UP, LEFT_STICK_DIGITAL_UP],
Control.NOTE_DOWN => [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN],
Control.NOTE_LEFT => [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT],
Control.NOTE_RIGHT => [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT],
Control.PAUSE => [START],
Control.RESET => [Y]
]);
#else
addGamepadLiteral(id, [
//Swap A and B for switch
Control.ACCEPT => [B],
Control.BACK => [A],
Control.UI_UP => [DPAD_UP, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP],
Control.UI_DOWN => [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN],
Control.UI_LEFT => [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT],
Control.UI_RIGHT => [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT],
Control.NOTE_UP => [DPAD_UP, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP],
Control.NOTE_DOWN => [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN],
Control.NOTE_LEFT => [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT],
Control.NOTE_RIGHT => [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT],
Control.PAUSE => [START],
//Swap Y and X for switch
Control.RESET => [Y],
]);
#end
}
/**
* Sets all actions that pertain to the binder to trigger when the supplied keys are used.
* If binder is a literal you can inline this
*/
public function bindButtons(control:Control, id, buttons)
{
#if (haxe >= "4.0.0")
inline forEachBound(control, (action, state) -> addButtons(action, buttons, state, id));
#else
forEachBound(control, function(action, state) addButtons(action, buttons, state, id));
#end
}
/**
* Sets all actions that pertain to the binder to trigger when the supplied keys are used.
* If binder is a literal you can inline this
*/
public function unbindButtons(control:Control, gamepadID:Int, buttons)
{
#if (haxe >= "4.0.0")
inline forEachBound(control, (action, _) -> removeButtons(action, gamepadID, buttons));
#else
forEachBound(control, function(action, _) removeButtons(action, gamepadID, buttons));
#end
}
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>)
{
var i = action.inputs.length;
while (i-- > 0)
{
var input = action.inputs[i];
if (isGamepad(input, gamepadID) && buttons.indexOf(cast input.inputID) != -1)
action.remove(input);
}
}
public function getInputsFor(control:Control, device:Device, ?list:Array<Int>):Array<Int>
{
if (list == null)
list = [];
switch (device)
{
case Keys:
for (input in getActionFromControl(control).inputs)
{
if (input.device == KEYBOARD)
list.push(input.inputID);
}
case Gamepad(id):
for (input in getActionFromControl(control).inputs)
{
if (input.deviceID == id)
list.push(input.inputID);
}
}
return list;
}
public function removeDevice(device:Device)
{
switch (device)
{
case Keys:
setKeyboardScheme(None);
case Gamepad(id):
removeGamepad(id);
}
}
static function isDevice(input:FlxActionInput, device:Device)
{
return switch device
{
case Keys: input.device == KEYBOARD;
case Gamepad(id): isGamepad(input, id);
}
}
inline static function isGamepad(input:FlxActionInput, deviceID:Int)
{
return input.device == GAMEPAD && (deviceID == FlxInputDeviceID.ALL || input.deviceID == deviceID);
}
}

79
source/CoolUtil.hx Normal file
View file

@ -0,0 +1,79 @@
package;
import flixel.FlxG;
import openfl.utils.Assets;
import lime.utils.Assets as LimeAssets;
import lime.utils.AssetLibrary;
import lime.utils.AssetManifest;
#if sys
import sys.io.File;
import sys.FileSystem;
#else
import openfl.utils.Assets;
#end
using StringTools;
class CoolUtil
{
// [Difficulty name, Chart file suffix]
public static var difficultyStuff:Array<Dynamic> = [
['Easy', '-easy'],
['Normal', ''],
['Hard', '-hard']
];
public static function difficultyString():String
{
return difficultyStuff[PlayState.storyDifficulty][0].toUpperCase();
}
public static function boundTo(value:Float, min:Float, max:Float):Float {
var newValue:Float = value;
if(newValue < min) newValue = min;
else if(newValue > max) newValue = max;
return newValue;
}
public static function coolTextFile(path:String):Array<String>
{
var daList:Array<String> = [];
#if sys
if(FileSystem.exists(path)) daList = File.getContent(path).trim().split('\n');
#else
if(Assets.exists(path)) daList = Assets.getText(path).trim().split('\n');
#end
for (i in 0...daList.length)
{
daList[i] = daList[i].trim();
}
return daList;
}
public static function numberArray(max:Int, ?min = 0):Array<Int>
{
var dumbArray:Array<Int> = [];
for (i in min...max)
{
dumbArray.push(i);
}
return dumbArray;
}
//uhhhh does this even work at all? i'm starting to doubt
public static function precacheSound(sound:String, ?library:String = null):Void {
if(!Assets.cache.hasSound(Paths.sound(sound, library))) {
FlxG.sound.cache(Paths.sound(sound, library));
}
}
public static function browserLoad(site:String) {
#if linux
Sys.command('/usr/bin/xdg-open', [site, "&"]);
#else
FlxG.openURL(site);
#end
}
}

178
source/CreditsState.hx Normal file
View file

@ -0,0 +1,178 @@
package;
#if desktop
import Discord.DiscordClient;
#end
import flash.text.TextField;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.addons.display.FlxGridOverlay;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.math.FlxMath;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import flixel.tweens.FlxTween;
import lime.utils.Assets;
using StringTools;
class CreditsState extends MusicBeatState
{
var curSelected:Int = 1;
private var grpOptions:FlxTypedGroup<Alphabet>;
private var iconArray:Array<AttachedSprite> = [];
private static var creditsStuff:Array<Dynamic> = [ //Name - Icon name - Description - Link - BG Color
['Psych Engine Team'],
['Shadow Mario', 'shadowmario', 'Main Programmer of Psych Engine', 'https://twitter.com/Shadow_Mario_', 0xFFFFDD33],
['RiverOaken', 'riveroaken', 'Main Artist/Animator of Psych Engine', 'https://twitter.com/river_oaken', 0xFFC30085],
[''],
['Engine Contributors'],
['shubs', 'shubs', 'New Input System Programmer', 'https://twitter.com/yoshubs', 0xFF4494E6],
['PolybiusProxy', 'polybiusproxy', '.MP4 Video Loader Extension', 'https://twitter.com/polybiusproxy', 0xFFE01F32],
['gedehari', 'gedehari', 'Chart Editor\'s Sound Waveform base', 'https://twitter.com/gedehari', 0xFFFF9300],
['Keoiki', 'keoiki', 'Note Splash Animations', 'https://twitter.com/Keoiki_', 0xFFFFFFFF],
['SandPlanet', 'sandplanet', 'Mascot\'s Owner\nMain Supporter of the Engine', 'https://twitter.com/SandPlanetNG', 0xFFD10616],
['bubba', 'bubba', 'Guest Composer for "Hot Dilf"', 'https://www.youtube.com/channel/UCxQTnLmv0OAS63yzk9pVfaw', 0xFF61536A],
[''],
["Funkin' Crew"],
['ninjamuffin99', 'ninjamuffin99', "Programmer of Friday Night Funkin'", 'https://twitter.com/ninja_muffin99', 0xFFF73838],
['PhantomArcade', 'phantomarcade', "Animator of Friday Night Funkin'", 'https://twitter.com/PhantomArcade3K', 0xFFFFBB1B],
['evilsk8r', 'evilsk8r', "Artist of Friday Night Funkin'", 'https://twitter.com/evilsk8r', 0xFF53E52C],
['kawaisprite', 'kawaisprite', "Composer of Friday Night Funkin'", 'https://twitter.com/kawaisprite', 0xFF6475F3]
];
var bg:FlxSprite;
var descText:FlxText;
var intendedColor:Int;
var colorTween:FlxTween;
override function create()
{
#if desktop
// Updating Discord Rich Presence
DiscordClient.changePresence("In the Menus", null);
#end
bg = new FlxSprite().loadGraphic(Paths.image('menuDesat'));
add(bg);
grpOptions = new FlxTypedGroup<Alphabet>();
add(grpOptions);
for (i in 0...creditsStuff.length)
{
var isSelectable:Bool = !unselectableCheck(i);
var optionText:Alphabet = new Alphabet(0, 70 * i, creditsStuff[i][0], !isSelectable, false);
optionText.isMenuItem = true;
optionText.screenCenter(X);
if(isSelectable) {
optionText.x -= 70;
}
optionText.forceX = optionText.x;
//optionText.yMult = 90;
optionText.targetY = i;
grpOptions.add(optionText);
if(isSelectable) {
var icon:AttachedSprite = new AttachedSprite('credits/' + creditsStuff[i][1]);
icon.xAdd = optionText.width + 10;
icon.sprTracker = optionText;
// using a FlxGroup is too much fuss!
iconArray.push(icon);
add(icon);
}
}
descText = new FlxText(50, 600, 1180, "", 32);
descText.setFormat(Paths.font("vcr.ttf"), 32, FlxColor.WHITE, CENTER, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
descText.scrollFactor.set();
descText.borderSize = 2.4;
add(descText);
bg.color = creditsStuff[curSelected][4];
intendedColor = bg.color;
changeSelection();
super.create();
}
override function update(elapsed:Float)
{
if (FlxG.sound.music.volume < 0.7)
{
FlxG.sound.music.volume += 0.5 * FlxG.elapsed;
}
var upP = controls.UI_UP_P;
var downP = controls.UI_DOWN_P;
if (upP)
{
changeSelection(-1);
}
if (downP)
{
changeSelection(1);
}
if (controls.BACK)
{
if(colorTween != null) {
colorTween.cancel();
}
FlxG.sound.play(Paths.sound('cancelMenu'));
MusicBeatState.switchState(new MainMenuState());
}
if(controls.ACCEPT) {
CoolUtil.browserLoad(creditsStuff[curSelected][3]);
}
super.update(elapsed);
}
function changeSelection(change:Int = 0)
{
FlxG.sound.play(Paths.sound('scrollMenu'), 0.4);
do {
curSelected += change;
if (curSelected < 0)
curSelected = creditsStuff.length - 1;
if (curSelected >= creditsStuff.length)
curSelected = 0;
} while(unselectableCheck(curSelected));
var newColor:Int = creditsStuff[curSelected][4];
if(newColor != intendedColor) {
if(colorTween != null) {
colorTween.cancel();
}
intendedColor = newColor;
colorTween = FlxTween.color(bg, 1, bg.color, intendedColor, {
onComplete: function(twn:FlxTween) {
colorTween = null;
}
});
}
var bullShit:Int = 0;
for (item in grpOptions.members)
{
item.targetY = bullShit - curSelected;
bullShit++;
if(!unselectableCheck(bullShit-1)) {
item.alpha = 0.6;
if (item.targetY == 0) {
item.alpha = 1;
}
}
}
descText.text = creditsStuff[curSelected][2];
}
private function unselectableCheck(num:Int):Bool {
return creditsStuff[num].length <= 1;
}
}

View file

@ -0,0 +1,95 @@
package;
import Conductor.BPMChangeEvent;
import flixel.FlxG;
import flixel.addons.ui.FlxUIState;
import flixel.math.FlxRect;
import flixel.util.FlxTimer;
import flixel.addons.transition.FlxTransitionableState;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import flixel.util.FlxGradient;
import flixel.FlxSubState;
import flixel.FlxSprite;
import flixel.FlxCamera;
class CustomFadeTransition extends MusicBeatSubstate {
public static var finishCallback:Void->Void;
private var leTween:FlxTween = null;
public static var nextCamera:FlxCamera;
var isTransIn:Bool = false;
var transBlack:FlxSprite;
var transGradient:FlxSprite;
public function new(duration:Float, isTransIn:Bool) {
super();
this.isTransIn = isTransIn;
var zoom:Float = CoolUtil.boundTo(FlxG.camera.zoom, 0.05, 1);
var width:Int = Std.int(FlxG.width / zoom);
var height:Int = Std.int(FlxG.height / zoom);
transGradient = FlxGradient.createGradientFlxSprite(width, height, (isTransIn ? [0x0, FlxColor.BLACK] : [FlxColor.BLACK, 0x0]));
transGradient.scrollFactor.set();
add(transGradient);
transBlack = new FlxSprite().makeGraphic(width, height + 400, FlxColor.BLACK);
transBlack.scrollFactor.set();
add(transBlack);
transGradient.x -= (width - FlxG.width) / 2;
transBlack.x = transGradient.x;
if(isTransIn) {
transGradient.y = transBlack.y - transBlack.height;
FlxTween.tween(transGradient, {y: transGradient.height + 50}, duration, {
onComplete: function(twn:FlxTween) {
close();
},
ease: FlxEase.linear});
} else {
transGradient.y = -transGradient.height;
transBlack.y = transGradient.y - transBlack.height + 50;
leTween = FlxTween.tween(transGradient, {y: transGradient.height + 50}, duration, {
onComplete: function(twn:FlxTween) {
if(finishCallback != null) {
finishCallback();
}
},
ease: FlxEase.linear});
}
if(nextCamera != null) {
transBlack.cameras = [nextCamera];
transGradient.cameras = [nextCamera];
}
nextCamera = null;
}
override function update(elapsed:Float) {
if(isTransIn) {
transBlack.y = transGradient.y + transGradient.height;
} else {
transBlack.y = transGradient.y - transBlack.height;
}
super.update(elapsed);
if(isTransIn) {
transBlack.y = transGradient.y + transGradient.height;
} else {
transBlack.y = transGradient.y - transBlack.height;
}
}
override function destroy() {
if(leTween != null) {
#if MODS_ALLOWED
if(isTransIn) {
Paths.destroyLoadedImages();
}
#end
finishCallback();
leTween.cancel();
}
super.destroy();
}
}

287
source/DialogueBox.hx Normal file
View file

@ -0,0 +1,287 @@
package;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.addons.text.FlxTypeText;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.group.FlxSpriteGroup;
import flixel.input.FlxKeyManager;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
using StringTools;
class DialogueBox extends FlxSpriteGroup
{
var box:FlxSprite;
var curCharacter:String = '';
var dialogue:Alphabet;
var dialogueList:Array<String> = [];
// SECOND DIALOGUE FOR THE PIXEL SHIT INSTEAD???
var swagDialogue:FlxTypeText;
var dropText:FlxText;
public var finishThing:Void->Void;
public var nextDialogueThing:Void->Void = null;
public var skipDialogueThing:Void->Void = null;
var portraitLeft:FlxSprite;
var portraitRight:FlxSprite;
var handSelect:FlxSprite;
var bgFade:FlxSprite;
public function new(talkingRight:Bool = true, ?dialogueList:Array<String>)
{
super();
switch (PlayState.SONG.song.toLowerCase())
{
case 'senpai':
FlxG.sound.playMusic(Paths.music('Lunchbox'), 0);
FlxG.sound.music.fadeIn(1, 0, 0.8);
case 'thorns':
FlxG.sound.playMusic(Paths.music('LunchboxScary'), 0);
FlxG.sound.music.fadeIn(1, 0, 0.8);
}
bgFade = new FlxSprite(-200, -200).makeGraphic(Std.int(FlxG.width * 1.3), Std.int(FlxG.height * 1.3), 0xFFB3DFd8);
bgFade.scrollFactor.set();
bgFade.alpha = 0;
add(bgFade);
new FlxTimer().start(0.83, function(tmr:FlxTimer)
{
bgFade.alpha += (1 / 5) * 0.7;
if (bgFade.alpha > 0.7)
bgFade.alpha = 0.7;
}, 5);
box = new FlxSprite(-20, 45);
var hasDialog = false;
switch (PlayState.SONG.song.toLowerCase())
{
case 'senpai':
hasDialog = true;
box.frames = Paths.getSparrowAtlas('weeb/pixelUI/dialogueBox-pixel');
box.animation.addByPrefix('normalOpen', 'Text Box Appear', 24, false);
box.animation.addByIndices('normal', 'Text Box Appear instance 1', [4], "", 24);
case 'roses':
hasDialog = true;
FlxG.sound.play(Paths.sound('ANGRY_TEXT_BOX'));
box.frames = Paths.getSparrowAtlas('weeb/pixelUI/dialogueBox-senpaiMad');
box.animation.addByPrefix('normalOpen', 'SENPAI ANGRY IMPACT SPEECH', 24, false);
box.animation.addByIndices('normal', 'SENPAI ANGRY IMPACT SPEECH instance 1', [4], "", 24);
case 'thorns':
hasDialog = true;
box.frames = Paths.getSparrowAtlas('weeb/pixelUI/dialogueBox-evil');
box.animation.addByPrefix('normalOpen', 'Spirit Textbox spawn', 24, false);
box.animation.addByIndices('normal', 'Spirit Textbox spawn instance 1', [11], "", 24);
var face:FlxSprite = new FlxSprite(320, 170).loadGraphic(Paths.image('weeb/spiritFaceForward'));
face.setGraphicSize(Std.int(face.width * 6));
add(face);
}
this.dialogueList = dialogueList;
if (!hasDialog)
return;
portraitLeft = new FlxSprite(-20, 40);
portraitLeft.frames = Paths.getSparrowAtlas('weeb/senpaiPortrait');
portraitLeft.animation.addByPrefix('enter', 'Senpai Portrait Enter', 24, false);
portraitLeft.setGraphicSize(Std.int(portraitLeft.width * PlayState.daPixelZoom * 0.9));
portraitLeft.updateHitbox();
portraitLeft.scrollFactor.set();
add(portraitLeft);
portraitLeft.visible = false;
portraitRight = new FlxSprite(0, 40);
portraitRight.frames = Paths.getSparrowAtlas('weeb/bfPortrait');
portraitRight.animation.addByPrefix('enter', 'Boyfriend portrait enter', 24, false);
portraitRight.setGraphicSize(Std.int(portraitRight.width * PlayState.daPixelZoom * 0.9));
portraitRight.updateHitbox();
portraitRight.scrollFactor.set();
add(portraitRight);
portraitRight.visible = false;
box.animation.play('normalOpen');
box.setGraphicSize(Std.int(box.width * PlayState.daPixelZoom * 0.9));
box.updateHitbox();
add(box);
box.screenCenter(X);
portraitLeft.screenCenter(X);
handSelect = new FlxSprite(1042, 590).loadGraphic(Paths.getPath('images/weeb/pixelUI/hand_textbox.png', IMAGE));
handSelect.setGraphicSize(Std.int(handSelect.width * PlayState.daPixelZoom * 0.9));
handSelect.updateHitbox();
handSelect.visible = false;
add(handSelect);
if (!talkingRight)
{
// box.flipX = true;
}
dropText = new FlxText(242, 502, Std.int(FlxG.width * 0.6), "", 32);
dropText.font = 'Pixel Arial 11 Bold';
dropText.color = 0xFFD89494;
add(dropText);
swagDialogue = new FlxTypeText(240, 500, Std.int(FlxG.width * 0.6), "", 32);
swagDialogue.font = 'Pixel Arial 11 Bold';
swagDialogue.color = 0xFF3F2021;
swagDialogue.sounds = [FlxG.sound.load(Paths.sound('pixelText'), 0.6)];
add(swagDialogue);
dialogue = new Alphabet(0, 80, "", false, true);
// dialogue.x = 90;
// add(dialogue);
}
var dialogueOpened:Bool = false;
var dialogueStarted:Bool = false;
var dialogueEnded:Bool = false;
override function update(elapsed:Float)
{
// HARD CODING CUZ IM STUPDI
if (PlayState.SONG.song.toLowerCase() == 'roses')
portraitLeft.visible = false;
if (PlayState.SONG.song.toLowerCase() == 'thorns')
{
portraitLeft.visible = false;
swagDialogue.color = FlxColor.WHITE;
dropText.color = FlxColor.BLACK;
}
dropText.text = swagDialogue.text;
if (box.animation.curAnim != null)
{
if (box.animation.curAnim.name == 'normalOpen' && box.animation.curAnim.finished)
{
box.animation.play('normal');
dialogueOpened = true;
}
}
if (dialogueOpened && !dialogueStarted)
{
startDialogue();
dialogueStarted = true;
}
if(PlayerSettings.player1.controls.ACCEPT)
{
if (dialogueEnded)
{
remove(dialogue);
if (dialogueList[1] == null && dialogueList[0] != null)
{
if (!isEnding)
{
isEnding = true;
FlxG.sound.play(Paths.sound('clickText'), 0.8);
if (PlayState.SONG.song.toLowerCase() == 'senpai' || PlayState.SONG.song.toLowerCase() == 'thorns')
FlxG.sound.music.fadeOut(1.5, 0);
new FlxTimer().start(0.2, function(tmr:FlxTimer)
{
box.alpha -= 1 / 5;
bgFade.alpha -= 1 / 5 * 0.7;
portraitLeft.visible = false;
portraitRight.visible = false;
swagDialogue.alpha -= 1 / 5;
handSelect.alpha -= 1 / 5;
dropText.alpha = swagDialogue.alpha;
}, 5);
new FlxTimer().start(1.5, function(tmr:FlxTimer)
{
finishThing();
kill();
});
}
}
else
{
dialogueList.remove(dialogueList[0]);
startDialogue();
FlxG.sound.play(Paths.sound('clickText'), 0.8);
}
}
else if (dialogueStarted)
{
FlxG.sound.play(Paths.sound('clickText'), 0.8);
swagDialogue.skip();
if(skipDialogueThing != null) {
skipDialogueThing();
}
}
}
super.update(elapsed);
}
var isEnding:Bool = false;
function startDialogue():Void
{
cleanDialog();
// var theDialog:Alphabet = new Alphabet(0, 70, dialogueList[0], false, true);
// dialogue = theDialog;
// add(theDialog);
// swagDialogue.text = ;
swagDialogue.resetText(dialogueList[0]);
swagDialogue.start(0.04, true);
swagDialogue.completeCallback = function() {
handSelect.visible = true;
dialogueEnded = true;
};
handSelect.visible = false;
dialogueEnded = false;
switch (curCharacter)
{
case 'dad':
portraitRight.visible = false;
if (!portraitLeft.visible)
{
if (PlayState.SONG.song.toLowerCase() == 'senpai') portraitLeft.visible = true;
portraitLeft.animation.play('enter');
}
case 'bf':
portraitLeft.visible = false;
if (!portraitRight.visible)
{
portraitRight.visible = true;
portraitRight.animation.play('enter');
}
}
if(nextDialogueThing != null) {
nextDialogueThing();
}
}
function cleanDialog():Void
{
var splitName:Array<String> = dialogueList[0].split(":");
curCharacter = splitName[1];
dialogueList[0] = dialogueList[0].substr(splitName[1].length + 2).trim();
}
}

533
source/DialogueBoxPsych.hx Normal file
View file

@ -0,0 +1,533 @@
package;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.addons.text.FlxTypeText;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.group.FlxSpriteGroup;
import flixel.input.FlxKeyManager;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
import flixel.FlxSubState;
import haxe.Json;
import haxe.format.JsonParser;
#if sys
import sys.FileSystem;
import sys.io.File;
#end
import openfl.utils.Assets;
using StringTools;
typedef DialogueCharacterFile = {
var image:String;
var dialogue_pos:String;
var animations:Array<DialogueAnimArray>;
var position:Array<Float>;
var scale:Float;
}
typedef DialogueAnimArray = {
var anim:String;
var loop_name:String;
var loop_offsets:Array<Int>;
var idle_name:String;
var idle_offsets:Array<Int>;
}
// Gonna try to kind of make it compatible to Forever Engine,
// love u Shubs no homo :flushedh4:
typedef DialogueFile = {
var dialogue:Array<DialogueLine>;
}
typedef DialogueLine = {
var portrait:Null<String>;
var expression:Null<String>;
var text:Null<String>;
var boxState:Null<String>;
var speed:Null<Float>;
}
class DialogueCharacter extends FlxSprite
{
private static var IDLE_SUFFIX:String = '-IDLE';
public static var DEFAULT_CHARACTER:String = 'bf';
public static var DEFAULT_SCALE:Float = 0.7;
public var jsonFile:DialogueCharacterFile = null;
#if (haxe >= "4.0.0")
public var dialogueAnimations:Map<String, DialogueAnimArray> = new Map();
#else
public var dialogueAnimations:Map<String, DialogueAnimArray> = new Map<String, DialogueAnimArray>();
#end
public var startingPos:Float = 0; //For center characters, it works as the starting Y, for everything else it works as starting X
public var isGhost:Bool = false; //For the editor
public var curCharacter:String = 'bf';
public function new(x:Float = 0, y:Float = 0, character:String = null)
{
super(x, y);
if(character == null) character = DEFAULT_CHARACTER;
this.curCharacter = character;
reloadCharacterJson(character);
frames = Paths.getSparrowAtlas('dialogue/' + jsonFile.image);
reloadAnimations();
}
public function reloadCharacterJson(character:String) {
var characterPath:String = 'images/dialogue/' + character + '.json';
var rawJson = null;
#if MODS_ALLOWED
var path:String = Paths.modFolders(characterPath);
if (!FileSystem.exists(path)) {
path = Paths.getPreloadPath(characterPath);
}
if(!FileSystem.exists(path)) {
path = Paths.getPreloadPath('images/dialogue/' + DEFAULT_CHARACTER + '.json');
}
rawJson = File.getContent(path);
#else
var path:String = Paths.getPreloadPath(characterPath);
rawJson = Assets.getText(path);
#end
jsonFile = cast Json.parse(rawJson);
}
public function reloadAnimations() {
dialogueAnimations.clear();
if(jsonFile.animations != null && jsonFile.animations.length > 0) {
for (anim in jsonFile.animations) {
animation.addByPrefix(anim.anim, anim.loop_name, 24, isGhost);
animation.addByPrefix(anim.anim + IDLE_SUFFIX, anim.idle_name, 24, true);
dialogueAnimations.set(anim.anim, anim);
}
}
}
public function playAnim(animName:String = null, playIdle:Bool = false) {
var leAnim:String = animName;
if(animName == null || !dialogueAnimations.exists(animName)) { //Anim is null, get a random animation
var arrayAnims:Array<String> = [];
for (anim in dialogueAnimations) {
arrayAnims.push(anim.anim);
}
if(arrayAnims.length > 0) {
leAnim = arrayAnims[FlxG.random.int(0, arrayAnims.length-1)];
}
}
if(dialogueAnimations.exists(leAnim) &&
(dialogueAnimations.get(leAnim).loop_name == null ||
dialogueAnimations.get(leAnim).loop_name.length < 1 ||
dialogueAnimations.get(leAnim).loop_name == dialogueAnimations.get(leAnim).idle_name)) {
playIdle = true;
}
animation.play(playIdle ? leAnim + IDLE_SUFFIX : leAnim, false);
if(dialogueAnimations.exists(leAnim)) {
var anim:DialogueAnimArray = dialogueAnimations.get(leAnim);
if(playIdle) {
offset.set(anim.idle_offsets[0], anim.idle_offsets[1]);
//trace('Setting idle offsets: ' + anim.idle_offsets);
} else {
offset.set(anim.loop_offsets[0], anim.loop_offsets[1]);
//trace('Setting loop offsets: ' + anim.loop_offsets);
}
} else {
offset.set(0, 0);
trace('Offsets not found! Dialogue character is badly formatted, anim: ' + leAnim + ', ' + (playIdle ? 'idle anim' : 'loop anim'));
}
}
public function animationIsLoop():Bool {
if(animation.curAnim == null) return false;
return !animation.curAnim.name.endsWith(IDLE_SUFFIX);
}
}
// TO DO: Clean code? Maybe? idk
class DialogueBoxPsych extends FlxSpriteGroup
{
var dialogue:Alphabet;
var dialogueList:DialogueFile = null;
public var finishThing:Void->Void;
public var nextDialogueThing:Void->Void = null;
public var skipDialogueThing:Void->Void = null;
var bgFade:FlxSprite = null;
var box:FlxSprite;
var textToType:String = '';
var arrayCharacters:Array<DialogueCharacter> = [];
var currentText:Int = 0;
var offsetPos:Float = -600;
var textBoxTypes:Array<String> = ['normal', 'angry'];
//var charPositionList:Array<String> = ['left', 'center', 'right'];
public function new(dialogueList:DialogueFile, ?song:String = null)
{
super();
if(song != null && song != '') {
FlxG.sound.playMusic(Paths.music(song), 0);
FlxG.sound.music.fadeIn(2, 0, 1);
}
bgFade = new FlxSprite(-500, -500).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.WHITE);
bgFade.scrollFactor.set();
bgFade.visible = true;
bgFade.alpha = 0;
add(bgFade);
this.dialogueList = dialogueList;
spawnCharacters();
box = new FlxSprite(70, 370);
box.frames = Paths.getSparrowAtlas('speech_bubble');
box.scrollFactor.set();
box.antialiasing = ClientPrefs.globalAntialiasing;
box.animation.addByPrefix('normal', 'speech bubble normal', 24);
box.animation.addByPrefix('normalOpen', 'Speech Bubble Normal Open', 24, false);
box.animation.addByPrefix('angry', 'AHH speech bubble', 24);
box.animation.addByPrefix('angryOpen', 'speech bubble loud open', 24, false);
box.animation.addByPrefix('center-normal', 'speech bubble middle', 24);
box.animation.addByPrefix('center-normalOpen', 'Speech Bubble Middle Open', 24, false);
box.animation.addByPrefix('center-angry', 'AHH Speech Bubble middle', 24);
box.animation.addByPrefix('center-angryOpen', 'speech bubble Middle loud open', 24, false);
box.animation.play('normal', true);
box.visible = false;
box.setGraphicSize(Std.int(box.width * 0.9));
box.updateHitbox();
add(box);
startNextDialog();
}
var dialogueStarted:Bool = false;
var dialogueEnded:Bool = false;
public static var LEFT_CHAR_X:Float = -60;
public static var RIGHT_CHAR_X:Float = -100;
public static var DEFAULT_CHAR_Y:Float = 60;
function spawnCharacters() {
#if (haxe >= "4.0.0")
var charsMap:Map<String, Bool> = new Map();
#else
var charsMap:Map<String, Bool> = new Map<String, Bool>();
#end
for (i in 0...dialogueList.dialogue.length) {
if(dialogueList.dialogue[i] != null) {
var charToAdd:String = dialogueList.dialogue[i].portrait;
if(!charsMap.exists(charToAdd) || !charsMap.get(charToAdd)) {
charsMap.set(charToAdd, true);
}
}
}
for (individualChar in charsMap.keys()) {
var x:Float = LEFT_CHAR_X;
var y:Float = DEFAULT_CHAR_Y;
var char:DialogueCharacter = new DialogueCharacter(x + offsetPos, y, individualChar);
char.setGraphicSize(Std.int(char.width * DialogueCharacter.DEFAULT_SCALE * char.jsonFile.scale));
char.updateHitbox();
char.antialiasing = ClientPrefs.globalAntialiasing;
char.scrollFactor.set();
char.alpha = 0.00001;
add(char);
var saveY:Bool = false;
switch(char.jsonFile.dialogue_pos) {
case 'center':
char.x = FlxG.width / 2;
char.x -= char.width / 2;
y = char.y;
char.y = FlxG.height + 50;
saveY = true;
case 'right':
x = FlxG.width - char.width + RIGHT_CHAR_X;
char.x = x - offsetPos;
}
x += char.jsonFile.position[0];
y += char.jsonFile.position[1];
char.x += char.jsonFile.position[0];
char.y += char.jsonFile.position[1];
char.startingPos = (saveY ? y : x);
arrayCharacters.push(char);
}
}
public static var DEFAULT_TEXT_X = 90;
public static var DEFAULT_TEXT_Y = 430;
var scrollSpeed = 4500;
var daText:Alphabet = null;
var ignoreThisFrame:Bool = true; //First frame is reserved for loading dialogue images
override function update(elapsed:Float)
{
if(ignoreThisFrame) {
ignoreThisFrame = false;
super.update(elapsed);
return;
}
if(!dialogueEnded) {
bgFade.alpha += 0.5 * elapsed;
if(bgFade.alpha > 0.5) bgFade.alpha = 0.5;
if(PlayerSettings.player1.controls.ACCEPT) {
if(!daText.finishedText) {
if(daText != null) {
daText.killTheTimer();
daText.kill();
remove(daText);
daText.destroy();
}
daText = new Alphabet(DEFAULT_TEXT_X, DEFAULT_TEXT_Y, textToType, false, true, 0.0, 0.7);
add(daText);
if(skipDialogueThing != null) {
skipDialogueThing();
}
} else if(currentText >= dialogueList.dialogue.length) {
dialogueEnded = true;
for (i in 0...textBoxTypes.length) {
var checkArray:Array<String> = ['', 'center-'];
var animName:String = box.animation.curAnim.name;
for (j in 0...checkArray.length) {
if(animName == checkArray[j] + textBoxTypes[i] || animName == checkArray[j] + textBoxTypes[i] + 'Open') {
box.animation.play(checkArray[j] + textBoxTypes[i] + 'Open', true);
}
}
}
box.animation.curAnim.curFrame = box.animation.curAnim.frames.length - 1;
box.animation.curAnim.reverse();
daText.kill();
remove(daText);
daText.destroy();
daText = null;
updateBoxOffsets(box);
FlxG.sound.music.fadeOut(1, 0);
} else {
startNextDialog();
}
FlxG.sound.play(Paths.sound('dialogueClose'));
} else if(daText.finishedText) {
var char:DialogueCharacter = arrayCharacters[lastCharacter];
if(char != null && char.animation.curAnim != null && char.animationIsLoop() && char.animation.finished) {
char.playAnim(char.animation.curAnim.name, true);
}
} else {
var char:DialogueCharacter = arrayCharacters[lastCharacter];
if(char != null && char.animation.curAnim != null && char.animation.finished) {
char.animation.curAnim.restart();
}
}
if(box.animation.curAnim.finished) {
for (i in 0...textBoxTypes.length) {
var checkArray:Array<String> = ['', 'center-'];
var animName:String = box.animation.curAnim.name;
for (j in 0...checkArray.length) {
if(animName == checkArray[j] + textBoxTypes[i] || animName == checkArray[j] + textBoxTypes[i] + 'Open') {
box.animation.play(checkArray[j] + textBoxTypes[i], true);
}
}
}
updateBoxOffsets(box);
}
if(lastCharacter != -1 && arrayCharacters.length > 0) {
for (i in 0...arrayCharacters.length) {
var char = arrayCharacters[i];
if(char != null) {
if(i != lastCharacter) {
switch(char.jsonFile.dialogue_pos) {
case 'left':
char.x -= scrollSpeed * elapsed;
if(char.x < char.startingPos + offsetPos) char.x = char.startingPos + offsetPos;
case 'center':
char.y += scrollSpeed * elapsed;
if(char.y > char.startingPos + FlxG.height) char.y = char.startingPos + FlxG.height;
case 'right':
char.x += scrollSpeed * elapsed;
if(char.x > char.startingPos - offsetPos) char.x = char.startingPos - offsetPos;
}
char.alpha -= 3 * elapsed;
if(char.alpha < 0.00001) char.alpha = 0.00001;
} else {
switch(char.jsonFile.dialogue_pos) {
case 'left':
char.x += scrollSpeed * elapsed;
if(char.x > char.startingPos) char.x = char.startingPos;
case 'center':
char.y -= scrollSpeed * elapsed;
if(char.y < char.startingPos) char.y = char.startingPos;
case 'right':
char.x -= scrollSpeed * elapsed;
if(char.x < char.startingPos) char.x = char.startingPos;
}
char.alpha += 3 * elapsed;
if(char.alpha > 1) char.alpha = 1;
}
}
}
}
} else { //Dialogue ending
if(box != null && box.animation.curAnim.curFrame <= 0) {
box.kill();
remove(box);
box.destroy();
box = null;
}
if(bgFade != null) {
bgFade.alpha -= 0.5 * elapsed;
if(bgFade.alpha <= 0) {
bgFade.kill();
remove(bgFade);
bgFade.destroy();
bgFade = null;
}
}
for (i in 0...arrayCharacters.length) {
var leChar:DialogueCharacter = arrayCharacters[i];
if(leChar != null) {
switch(arrayCharacters[i].jsonFile.dialogue_pos) {
case 'left':
leChar.x -= scrollSpeed * elapsed;
case 'center':
leChar.y += scrollSpeed * elapsed;
case 'right':
leChar.x += scrollSpeed * elapsed;
}
leChar.alpha -= elapsed * 10;
}
}
if(box == null && bgFade == null) {
for (i in 0...arrayCharacters.length) {
var leChar:DialogueCharacter = arrayCharacters[0];
if(leChar != null) {
arrayCharacters.remove(leChar);
leChar.kill();
remove(leChar);
leChar.destroy();
}
}
finishThing();
kill();
}
}
super.update(elapsed);
}
var lastCharacter:Int = -1;
var lastBoxType:String = '';
function startNextDialog():Void
{
var curDialogue:DialogueLine = null;
do {
curDialogue = dialogueList.dialogue[currentText];
} while(curDialogue == null);
if(curDialogue.text == null || curDialogue.text.length < 1) curDialogue.text = ' ';
if(curDialogue.boxState == null) curDialogue.boxState = 'normal';
if(curDialogue.speed == null || Math.isNaN(curDialogue.speed)) curDialogue.speed = 0.05;
var animName:String = curDialogue.boxState;
var boxType:String = textBoxTypes[0];
for (i in 0...textBoxTypes.length) {
if(textBoxTypes[i] == animName) {
boxType = animName;
}
}
var character:Int = 0;
box.visible = true;
for (i in 0...arrayCharacters.length) {
if(arrayCharacters[i].curCharacter == curDialogue.portrait) {
character = i;
break;
}
}
var centerPrefix:String = '';
var lePosition:String = arrayCharacters[character].jsonFile.dialogue_pos;
if(lePosition == 'center') centerPrefix = 'center-';
if(character != lastCharacter) {
box.animation.play(centerPrefix + boxType + 'Open', true);
updateBoxOffsets(box);
box.flipX = (lePosition == 'left');
} else if(boxType != lastBoxType) {
box.animation.play(centerPrefix + boxType, true);
updateBoxOffsets(box);
}
lastCharacter = character;
lastBoxType = boxType;
if(daText != null) {
daText.killTheTimer();
daText.kill();
remove(daText);
daText.destroy();
}
textToType = curDialogue.text;
daText = new Alphabet(DEFAULT_TEXT_X, DEFAULT_TEXT_Y, textToType, false, true, curDialogue.speed, 0.7);
add(daText);
var char:DialogueCharacter = arrayCharacters[character];
if(char != null) {
char.playAnim(curDialogue.expression, daText.finishedText);
if(char.animation.curAnim != null) {
var rate:Float = 24 - (((curDialogue.speed - 0.05) / 5) * 480);
if(rate < 12) rate = 12;
else if(rate > 48) rate = 48;
char.animation.curAnim.frameRate = rate;
}
}
currentText++;
if(nextDialogueThing != null) {
nextDialogueThing();
}
}
public static function parseDialogue(path:String):DialogueFile {
#if MODS_ALLOWED
var rawJson = File.getContent(path);
#else
var rawJson = Assets.getText(path);
#end
return cast Json.parse(rawJson);
}
public static function updateBoxOffsets(box:FlxSprite) { //Had to make it static because of the editors
box.centerOffsets();
box.updateHitbox();
if(box.animation.curAnim.name.startsWith('angry')) {
box.offset.set(50, 65);
} else if(box.animation.curAnim.name.startsWith('center-angry')) {
box.offset.set(50, 30);
} else {
box.offset.set(10, 0);
}
if(!box.flipX) box.offset.y += 10;
}
}

87
source/Discord.hx Normal file
View file

@ -0,0 +1,87 @@
package;
import Sys.sleep;
import discord_rpc.DiscordRpc;
using StringTools;
class DiscordClient
{
public function new()
{
trace("Discord Client starting...");
DiscordRpc.start({
clientID: "863222024192262205",
onReady: onReady,
onError: onError,
onDisconnected: onDisconnected
});
trace("Discord Client started.");
while (true)
{
DiscordRpc.process();
sleep(2);
//trace("Discord Client Update");
}
DiscordRpc.shutdown();
}
public static function shutdown()
{
DiscordRpc.shutdown();
}
static function onReady()
{
DiscordRpc.presence({
details: "In the Menus",
state: null,
largeImageKey: 'icon',
largeImageText: "Psych Engine"
});
}
static function onError(_code:Int, _message:String)
{
trace('Error! $_code : $_message');
}
static function onDisconnected(_code:Int, _message:String)
{
trace('Disconnected! $_code : $_message');
}
public static function initialize()
{
var DiscordDaemon = sys.thread.Thread.create(() ->
{
new DiscordClient();
});
trace("Discord Client initialized");
}
public static function changePresence(details:String, state:Null<String>, ?smallImageKey : String, ?hasStartTimestamp : Bool, ?endTimestamp: Float)
{
var startTimestamp:Float = if(hasStartTimestamp) Date.now().getTime() else 0;
if (endTimestamp > 0)
{
endTimestamp = startTimestamp + endTimestamp;
}
DiscordRpc.presence({
details: details,
state: state,
largeImageKey: 'icon',
largeImageText: "Engine Version: " + MainMenuState.psychEngineVersion,
smallImageKey : smallImageKey,
// Obtained times are in milliseconds so they are divided so Discord can use it
startTimestamp : Std.int(startTimestamp / 1000),
endTimestamp : Std.int(endTimestamp / 1000)
});
//trace('Discord RPC Updated. Arguments: $details, $state, $smallImageKey, $hasStartTimestamp, $endTimestamp');
}
}

67
source/FlashingState.hx Normal file
View file

@ -0,0 +1,67 @@
package;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.FlxSubState;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import flixel.effects.FlxFlicker;
import lime.app.Application;
import flixel.addons.transition.FlxTransitionableState;
import flixel.tweens.FlxTween;
import flixel.util.FlxTimer;
class FlashingState extends MusicBeatState
{
public static var leftState:Bool = false;
var warnText:FlxText;
override function create()
{
super.create();
var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK);
add(bg);
warnText = new FlxText(0, 0, FlxG.width,
"Hey, watch out!\n
This Mod contains some flashing lights!\n
Press ENTER to disable them now or go to Options Menu.\n
Press ESCAPE to ignore this message.\n
You've been warned!",
32);
warnText.setFormat("VCR OSD Mono", 32, FlxColor.WHITE, CENTER);
warnText.screenCenter(Y);
add(warnText);
}
override function update(elapsed:Float)
{
if(!leftState) {
var back:Bool = controls.BACK;
if (controls.ACCEPT || back) {
leftState = true;
FlxTransitionableState.skipNextTransIn = true;
FlxTransitionableState.skipNextTransOut = true;
if(!back) {
ClientPrefs.flashing = false;
ClientPrefs.saveSettings();
FlxG.sound.play(Paths.sound('confirmMenu'));
FlxFlicker.flicker(warnText, 1, 0.1, false, true, function(flk:FlxFlicker) {
new FlxTimer().start(0.5, function (tmr:FlxTimer) {
MusicBeatState.switchState(new TitleState());
});
});
} else {
FlxG.sound.play(Paths.sound('cancelMenu'));
FlxTween.tween(warnText, {alpha: 0}, 1, {
onComplete: function (twn:FlxTween) {
MusicBeatState.switchState(new TitleState());
}
});
}
}
}
super.update(elapsed);
}
}

View file

@ -0,0 +1,618 @@
package;
import flash.geom.Rectangle;
import flixel.addons.ui.interfaces.IFlxUIClickable;
import flixel.addons.ui.interfaces.IFlxUIWidget;
import flixel.addons.ui.interfaces.IHasParams;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.math.FlxMath;
import flixel.ui.FlxButton;
import flixel.util.FlxColor;
import flixel.util.FlxDestroyUtil;
import flixel.util.FlxStringUtil;
import flixel.addons.ui.FlxUIGroup;
import flixel.addons.ui.FlxUIText;
import flixel.addons.ui.FlxUIButton;
import flixel.addons.ui.FlxUISpriteButton;
import flixel.addons.ui.FlxUI9SliceSprite;
import flixel.addons.ui.FlxUIAssets;
import flixel.addons.ui.StrNameLabel;
import flixel.addons.ui.FlxUI;
/*
THIS IS AN EDIT OF FlxUIDropDownMenu I'VE MADE BECAUSE I'M TIRED OF IT NOT SUPPORTING SCROLLING UP/DOWN
BAH!
The differences are the following:
* Support to scrolling up/down with mouse wheel or arrow keys
* THe default drop direction is "Down" instead of "Automatic"
*/
/**
* @author larsiusprime
*/
class FlxUIDropDownMenuCustom extends FlxUIGroup implements IFlxUIWidget implements IFlxUIClickable implements IHasParams
{
public var skipButtonUpdate(default, set):Bool;
private function set_skipButtonUpdate(b:Bool):Bool
{
skipButtonUpdate = b;
header.button.skipButtonUpdate = b;
return b;
}
public var selectedId(get, set):String;
public var selectedLabel(get, set):String;
private var _selectedId:String;
private var _selectedLabel:String;
private var currentScroll:Int = 0; //Handles the scrolling
public var canScroll:Bool = true;
private function get_selectedId():String
{
return _selectedId;
}
private function set_selectedId(str:String):String
{
if (_selectedId == str)
return str;
var i:Int = 0;
for (btn in list)
{
if (btn != null && btn.name == str)
{
var item:FlxUIButton = list[i];
_selectedId = str;
if (item.label != null)
{
_selectedLabel = item.label.text;
header.text.text = item.label.text;
}
else
{
_selectedLabel = "";
header.text.text = "";
}
return str;
}
i++;
}
return str;
}
private function get_selectedLabel():String
{
return _selectedLabel;
}
private function set_selectedLabel(str:String):String
{
if (_selectedLabel == str)
return str;
var i:Int = 0;
for (btn in list)
{
if (btn.label.text == str)
{
var item:FlxUIButton = list[i];
_selectedId = item.name;
_selectedLabel = str;
header.text.text = str;
return str;
}
i++;
}
return str;
}
/**
* The header of this dropdown menu.
*/
public var header:FlxUIDropDownHeader;
/**
* The list of items that is shown when the toggle button is clicked.
*/
public var list:Array<FlxUIButton> = [];
/**
* The background for the list.
*/
public var dropPanel:FlxUI9SliceSprite;
public var params(default, set):Array<Dynamic>;
private function set_params(p:Array<Dynamic>):Array<Dynamic>
{
return params = p;
}
public var dropDirection(default, set):FlxUIDropDownMenuDropDirection = Down;
private function set_dropDirection(dropDirection):FlxUIDropDownMenuDropDirection
{
this.dropDirection = dropDirection;
updateButtonPositions();
return dropDirection;
}
public static inline var CLICK_EVENT:String = "click_dropdown";
public var callback:String->Void;
// private var _ui_control_callback:Bool->FlxUIDropDownMenuCustom->Void;
/**
* This creates a new dropdown menu.
*
* @param X x position of the dropdown menu
* @param Y y position of the dropdown menu
* @param DataList The data to be displayed
* @param Callback Optional Callback
* @param Header The header of this dropdown menu
* @param DropPanel Optional 9-slice-background for actual drop down menu
* @param ButtonList Optional list of buttons to be used for the corresponding entry in DataList
* @param UIControlCallback Used internally by FlxUI
*/
public function new(X:Float = 0, Y:Float = 0, DataList:Array<StrNameLabel>, ?Callback:String->Void, ?Header:FlxUIDropDownHeader,
?DropPanel:FlxUI9SliceSprite, ?ButtonList:Array<FlxUIButton>, ?UIControlCallback:Bool->FlxUIDropDownMenuCustom->Void)
{
super(X, Y);
callback = Callback;
header = Header;
dropPanel = DropPanel;
if (header == null)
header = new FlxUIDropDownHeader();
if (dropPanel == null)
{
var rect = new Rectangle(0, 0, header.background.width, header.background.height);
dropPanel = new FlxUI9SliceSprite(0, 0, FlxUIAssets.IMG_BOX, rect, [1, 1, 14, 14]);
}
if (DataList != null)
{
for (i in 0...DataList.length)
{
var data = DataList[i];
list.push(makeListButton(i, data.label, data.name));
}
selectSomething(DataList[0].name, DataList[0].label);
}
else if (ButtonList != null)
{
for (btn in ButtonList)
{
list.push(btn);
btn.resize(header.background.width, header.background.height);
btn.x = 1;
}
}
updateButtonPositions();
dropPanel.resize(header.background.width, getPanelHeight());
dropPanel.visible = false;
add(dropPanel);
for (btn in list)
{
add(btn);
btn.visible = false;
}
// _ui_control_callback = UIControlCallback;
header.button.onUp.callback = onDropdown;
add(header);
}
private function updateButtonPositions():Void
{
var buttonHeight = header.background.height;
dropPanel.y = header.background.y;
if (dropsUp())
dropPanel.y -= getPanelHeight();
else
dropPanel.y += buttonHeight;
var offset = dropPanel.y;
for (i in 0...currentScroll) { //Hides buttons that goes before the current scroll
var button:FlxUIButton = list[i];
if(button != null) {
button.y = -99999;
}
}
for (i in currentScroll...list.length)
{
var button:FlxUIButton = list[i];
if(button != null) {
button.y = offset;
offset += buttonHeight;
}
}
}
override function set_visible(Value:Bool):Bool
{
var vDropPanel = dropPanel.visible;
var vButtons = [];
for (i in 0...list.length)
{
if (list[i] != null)
{
vButtons.push(list[i].visible);
}
else
{
vButtons.push(false);
}
}
super.set_visible(Value);
dropPanel.visible = vDropPanel;
for (i in 0...list.length)
{
if (list[i] != null)
{
list[i].visible = vButtons[i];
}
}
return Value;
}
private function dropsUp():Bool
{
return dropDirection == Up || (dropDirection == Automatic && exceedsHeight());
}
private function exceedsHeight():Bool
{
return y + getPanelHeight() + header.background.height > FlxG.height;
}
private function getPanelHeight():Float
{
return list.length * header.background.height;
}
/**
* Change the contents with a new data list
* Replaces the old content with the new content
*/
public function setData(DataList:Array<StrNameLabel>):Void
{
var i:Int = 0;
if (DataList != null)
{
for (data in DataList)
{
var recycled:Bool = false;
if (list != null)
{
if (i <= list.length - 1)
{ // If buttons exist, try to re-use them
var btn:FlxUIButton = list[i];
if (btn != null)
{
btn.label.text = data.label; // Set the label
list[i].name = data.name; // Replace the name
recycled = true; // we successfully recycled it
}
}
}
else
{
list = [];
}
if (!recycled)
{ // If we couldn't recycle a button, make a fresh one
var t:FlxUIButton = makeListButton(i, data.label, data.name);
list.push(t);
add(t);
t.visible = false;
}
i++;
}
// Remove excess buttons:
if (list.length > DataList.length)
{ // we have more entries in the original set
for (j in DataList.length...list.length)
{ // start counting from end of list
var b:FlxUIButton = list.pop(); // remove last button on list
b.visible = false;
b.active = false;
remove(b, true); // remove from widget
b.destroy(); // destroy it
b = null;
}
}
selectSomething(DataList[0].name, DataList[0].label);
}
dropPanel.resize(header.background.width, getPanelHeight());
updateButtonPositions();
}
private function selectSomething(name:String, label:String):Void
{
header.text.text = label;
selectedId = name;
selectedLabel = label;
}
private function makeListButton(i:Int, Label:String, Name:String):FlxUIButton
{
var t:FlxUIButton = new FlxUIButton(0, 0, Label);
t.broadcastToFlxUI = false;
t.onUp.callback = onClickItem.bind(i);
t.name = Name;
t.loadGraphicSlice9([FlxUIAssets.IMG_INVIS, FlxUIAssets.IMG_HILIGHT, FlxUIAssets.IMG_HILIGHT], Std.int(header.background.width),
Std.int(header.background.height), [[1, 1, 3, 3], [1, 1, 3, 3], [1, 1, 3, 3]], FlxUI9SliceSprite.TILE_NONE);
t.labelOffsets[FlxButton.PRESSED].y -= 1; // turn off the 1-pixel depress on click
t.up_color = FlxColor.BLACK;
t.over_color = FlxColor.WHITE;
t.down_color = FlxColor.WHITE;
t.resize(header.background.width - 2, header.background.height - 1);
t.label.alignment = "left";
t.autoCenterLabel();
t.x = 1;
for (offset in t.labelOffsets)
{
offset.x += 2;
}
return t;
}
/*public function setUIControlCallback(UIControlCallback:Bool->FlxUIDropDownMenuCustom->Void):Void {
_ui_control_callback = UIControlCallback;
}*/
public function changeLabelByIndex(i:Int, NewLabel:String):Void
{
var btn:FlxUIButton = getBtnByIndex(i);
if (btn != null && btn.label != null)
{
btn.label.text = NewLabel;
}
}
public function changeLabelById(name:String, NewLabel:String):Void
{
var btn:FlxUIButton = getBtnById(name);
if (btn != null && btn.label != null)
{
btn.label.text = NewLabel;
}
}
public function getBtnByIndex(i:Int):FlxUIButton
{
if (i >= 0 && i < list.length)
{
return list[i];
}
return null;
}
public function getBtnById(name:String):FlxUIButton
{
for (btn in list)
{
if (btn.name == name)
{
return btn;
}
}
return null;
}
public override function update(elapsed:Float):Void
{
super.update(elapsed);
#if FLX_MOUSE
if (dropPanel.visible)
{
if(list.length > 1 && canScroll) {
if(FlxG.mouse.wheel > 0 || FlxG.keys.justPressed.UP) {
// Go up
--currentScroll;
if(currentScroll < 0) currentScroll = 0;
updateButtonPositions();
}
else if (FlxG.mouse.wheel < 0 || FlxG.keys.justPressed.DOWN) {
// Go down
currentScroll++;
if(currentScroll >= list.length) currentScroll = list.length-1;
updateButtonPositions();
}
}
if (FlxG.mouse.justPressed && !FlxG.mouse.overlaps(this))
{
showList(false);
}
}
#end
}
override public function destroy():Void
{
super.destroy();
dropPanel = FlxDestroyUtil.destroy(dropPanel);
list = FlxDestroyUtil.destroyArray(list);
// _ui_control_callback = null;
callback = null;
}
private function showList(b:Bool):Void
{
for (button in list)
{
button.visible = b;
button.active = b;
}
dropPanel.visible = b;
if(currentScroll != 0) {
currentScroll = 0;
updateButtonPositions();
}
FlxUI.forceFocus(b, this); // avoid overlaps
}
private function onDropdown():Void
{
(dropPanel.visible) ? showList(false) : showList(true);
}
private function onClickItem(i:Int):Void
{
var item:FlxUIButton = list[i];
selectSomething(item.name, item.label.text);
showList(false);
if (callback != null)
{
callback(item.name);
}
if (broadcastToFlxUI)
{
FlxUI.event(CLICK_EVENT, this, item.name, params);
}
}
/**
* Helper function to easily create a data list for a dropdown menu from an array of strings.
*
* @param StringArray The strings to use as data - used for both label and string ID.
* @param UseIndexID Whether to use the integer index of the current string as ID.
* @return The StrIDLabel array ready to be used in FlxUIDropDownMenuCustom's constructor
*/
public static function makeStrIdLabelArray(StringArray:Array<String>, UseIndexID:Bool = false):Array<StrNameLabel>
{
var strIdArray:Array<StrNameLabel> = [];
for (i in 0...StringArray.length)
{
var ID:String = StringArray[i];
if (UseIndexID)
{
ID = Std.string(i);
}
strIdArray[i] = new StrNameLabel(ID, StringArray[i]);
}
return strIdArray;
}
}
/**
* Header for a FlxUIDropDownMenuCustom
*/
class FlxUIDropDownHeader extends FlxUIGroup
{
/**
* The background of the header.
*/
public var background:FlxSprite;
/**
* The text that displays the currently selected item.
*/
public var text:FlxUIText;
/**
* The button that toggles the visibility of the dropdown panel.
*/
public var button:FlxUISpriteButton;
/**
* Creates a new dropdown header to be used in a FlxUIDropDownMenuCustom.
*
* @param Width Width of the dropdown - only relevant when no back sprite was specified
* @param Back Optional sprite to be placed in the background
* @param Text Optional text that displays the current value
* @param Button Optional button that toggles the dropdown list
*/
public function new(Width:Int = 120, ?Background:FlxSprite, ?Text:FlxUIText, ?Button:FlxUISpriteButton)
{
super();
background = Background;
text = Text;
button = Button;
// Background
if (background == null)
{
background = new FlxUI9SliceSprite(0, 0, FlxUIAssets.IMG_BOX, new Rectangle(0, 0, Width, 20), [1, 1, 14, 14]);
}
// Button
if (button == null)
{
button = new FlxUISpriteButton(0, 0, new FlxSprite(0, 0, FlxUIAssets.IMG_DROPDOWN));
button.loadGraphicSlice9([FlxUIAssets.IMG_BUTTON_THIN], 80, 20, [FlxStringUtil.toIntArray(FlxUIAssets.SLICE9_BUTTON)],
FlxUI9SliceSprite.TILE_NONE, -1, false, FlxUIAssets.IMG_BUTTON_SIZE, FlxUIAssets.IMG_BUTTON_SIZE);
}
button.resize(background.height, background.height);
button.x = background.x + background.width - button.width;
// Reposition and resize the button hitbox so the whole header is clickable
button.width = Width;
button.offset.x -= (Width - button.frameWidth);
button.x = offset.x;
button.label.offset.x += button.offset.x;
// Text
if (text == null)
{
text = new FlxUIText(0, 0, Std.int(background.width));
}
text.setPosition(2, 4);
text.color = FlxColor.BLACK;
add(background);
add(button);
add(text);
}
override public function destroy():Void
{
super.destroy();
background = FlxDestroyUtil.destroy(background);
text = FlxDestroyUtil.destroy(text);
button = FlxDestroyUtil.destroy(button);
}
}
enum FlxUIDropDownMenuDropDirection
{
Automatic;
Down;
Up;
}

133
source/FlxVideo.hx Normal file
View file

@ -0,0 +1,133 @@
#if web
import openfl.net.NetConnection;
import openfl.net.NetStream;
import openfl.events.NetStatusEvent;
import openfl.media.Video;
#else
import openfl.events.Event;
import vlc.VlcBitmap;
#end
import flixel.FlxBasic;
import flixel.FlxG;
class FlxVideo extends FlxBasic {
#if VIDEOS_ALLOWED
public var finishCallback:Void->Void = null;
#if desktop
public static var vlcBitmap:VlcBitmap;
#end
public function new(name:String) {
super();
#if web
var player:Video = new Video();
player.x = 0;
player.y = 0;
FlxG.addChildBelowMouse(player);
var netConnect = new NetConnection();
netConnect.connect(null);
var netStream = new NetStream(netConnect);
netStream.client = {
onMetaData: function() {
player.attachNetStream(netStream);
player.width = FlxG.width;
player.height = FlxG.height;
}
};
netConnect.addEventListener(NetStatusEvent.NET_STATUS, function(event:NetStatusEvent) {
if(event.info.code == "NetStream.Play.Complete") {
netStream.dispose();
if(FlxG.game.contains(player)) FlxG.game.removeChild(player);
if(finishCallback != null) finishCallback();
}
});
netStream.play(name);
#elseif desktop
// by Polybius, check out PolyEngine! https://github.com/polybiusproxy/PolyEngine
vlcBitmap = new VlcBitmap();
vlcBitmap.set_height(FlxG.stage.stageHeight);
vlcBitmap.set_width(FlxG.stage.stageHeight * (16 / 9));
vlcBitmap.onComplete = onVLCComplete;
vlcBitmap.onError = onVLCError;
FlxG.stage.addEventListener(Event.ENTER_FRAME, fixVolume);
vlcBitmap.repeat = 0;
vlcBitmap.inWindow = false;
vlcBitmap.fullscreen = false;
fixVolume(null);
FlxG.addChildBelowMouse(vlcBitmap);
vlcBitmap.play(checkFile(name));
#end
}
#if desktop
function checkFile(fileName:String):String
{
var pDir = "";
var appDir = "file:///" + Sys.getCwd() + "/";
if (fileName.indexOf(":") == -1) // Not a path
pDir = appDir;
else if (fileName.indexOf("file://") == -1 || fileName.indexOf("http") == -1) // C:, D: etc? ..missing "file:///" ?
pDir = "file:///";
return pDir + fileName;
}
public static function onFocus() {
if(vlcBitmap != null) {
vlcBitmap.resume();
}
}
public static function onFocusLost() {
if(vlcBitmap != null) {
vlcBitmap.pause();
}
}
function fixVolume(e:Event)
{
// shitty volume fix
vlcBitmap.volume = 0;
if(!FlxG.sound.muted && FlxG.sound.volume > 0.01) { //Kind of fixes the volume being too low when you decrease it
vlcBitmap.volume = FlxG.sound.volume * 0.5 + 0.5;
}
}
public function onVLCComplete()
{
vlcBitmap.stop();
// Clean player, just in case!
vlcBitmap.dispose();
if (FlxG.game.contains(vlcBitmap))
{
FlxG.game.removeChild(vlcBitmap);
}
if (finishCallback != null)
{
finishCallback();
}
}
function onVLCError()
{
trace("An error has occured while trying to load the video.\nPlease, check if the file you're loading exists.");
if (finishCallback != null) {
finishCallback();
}
}
#end
#end
}

420
source/FreeplayState.hx Normal file
View file

@ -0,0 +1,420 @@
package;
#if desktop
import Discord.DiscordClient;
#end
import flash.text.TextField;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.addons.display.FlxGridOverlay;
import flixel.addons.transition.FlxTransitionableState;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.math.FlxMath;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import flixel.tweens.FlxTween;
import lime.utils.Assets;
import flixel.system.FlxSound;
import openfl.utils.Assets as OpenFlAssets;
import WeekData;
using StringTools;
class FreeplayState extends MusicBeatState
{
var songs:Array<SongMetadata> = [];
var selector:FlxText;
private static var curSelected:Int = 0;
private static var curDifficulty:Int = 1;
var scoreBG:FlxSprite;
var scoreText:FlxText;
var diffText:FlxText;
var lerpScore:Int = 0;
var lerpRating:Float = 0;
var intendedScore:Int = 0;
var intendedRating:Float = 0;
private var grpSongs:FlxTypedGroup<Alphabet>;
private var curPlaying:Bool = false;
private var iconArray:Array<HealthIcon> = [];
var bg:FlxSprite;
var intendedColor:Int;
var colorTween:FlxTween;
override function create()
{
#if MODS_ALLOWED
Paths.destroyLoadedImages();
#end
WeekData.reloadWeekFiles(false);
#if desktop
// Updating Discord Rich Presence
DiscordClient.changePresence("In the Menus", null);
#end
for (i in 0...WeekData.weeksList.length) {
var leWeek:WeekData = WeekData.weeksLoaded.get(WeekData.weeksList[i]);
var leSongs:Array<String> = [];
var leChars:Array<String> = [];
for (j in 0...leWeek.songs.length) {
leSongs.push(leWeek.songs[j][0]);
leChars.push(leWeek.songs[j][1]);
}
WeekData.setDirectoryFromWeek(leWeek);
for (song in leWeek.songs) {
var colors:Array<Int> = song[2];
if(colors == null || colors.length < 3) {
colors = [146, 113, 253];
}
addSong(song[0], i, song[1], FlxColor.fromRGB(colors[0], colors[1], colors[2]));
}
}
WeekData.setDirectoryFromWeek();
var initSonglist = CoolUtil.coolTextFile(Paths.txt('freeplaySonglist'));
for (i in 0...initSonglist.length)
{
if(initSonglist[i] != null && initSonglist[i].length > 0) {
var songArray:Array<String> = initSonglist[i].split(":");
addSong(songArray[0], 0, songArray[1], Std.parseInt(songArray[2]));
}
}
// LOAD MUSIC
// LOAD CHARACTERS
bg = new FlxSprite().loadGraphic(Paths.image('menuDesat'));
bg.antialiasing = ClientPrefs.globalAntialiasing;
add(bg);
grpSongs = new FlxTypedGroup<Alphabet>();
add(grpSongs);
for (i in 0...songs.length)
{
var songText:Alphabet = new Alphabet(0, (70 * i) + 30, songs[i].songName, true, false);
songText.isMenuItem = true;
songText.targetY = i;
grpSongs.add(songText);
Paths.currentModDirectory = songs[i].folder;
var icon:HealthIcon = new HealthIcon(songs[i].songCharacter);
icon.sprTracker = songText;
// using a FlxGroup is too much fuss!
iconArray.push(icon);
add(icon);
// songText.x += 40;
// DONT PUT X IN THE FIRST PARAMETER OF new ALPHABET() !!
// songText.screenCenter(X);
}
WeekData.setDirectoryFromWeek();
scoreText = new FlxText(FlxG.width * 0.7, 5, 0, "", 32);
scoreText.setFormat(Paths.font("vcr.ttf"), 32, FlxColor.WHITE, RIGHT);
scoreBG = new FlxSprite(scoreText.x - 6, 0).makeGraphic(1, 66, 0xFF000000);
scoreBG.alpha = 0.6;
add(scoreBG);
diffText = new FlxText(scoreText.x, scoreText.y + 36, 0, "", 24);
diffText.font = scoreText.font;
add(diffText);
add(scoreText);
if(curSelected >= songs.length) curSelected = 0;
bg.color = songs[curSelected].color;
intendedColor = bg.color;
changeSelection();
changeDiff();
var swag:Alphabet = new Alphabet(1, 0, "swag");
// JUST DOIN THIS SHIT FOR TESTING!!!
/*
var md:String = Markdown.markdownToHtml(Assets.getText('CHANGELOG.md'));
var texFel:TextField = new TextField();
texFel.width = FlxG.width;
texFel.height = FlxG.height;
// texFel.
texFel.htmlText = md;
FlxG.stage.addChild(texFel);
// scoreText.textField.htmlText = md;
trace(md);
*/
var textBG:FlxSprite = new FlxSprite(0, FlxG.height - 26).makeGraphic(FlxG.width, 26, 0xFF000000);
textBG.alpha = 0.6;
add(textBG);
#if PRELOAD_ALL
var leText:String = "Press SPACE to listen to this Song / Press RESET to Reset your Score and Accuracy.";
#else
var leText:String = "Press RESET to Reset your Score and Accuracy.";
#end
var text:FlxText = new FlxText(textBG.x, textBG.y + 4, FlxG.width, leText, 18);
text.setFormat(Paths.font("vcr.ttf"), 18, FlxColor.WHITE, RIGHT);
text.scrollFactor.set();
add(text);
super.create();
}
override function closeSubState() {
changeSelection();
super.closeSubState();
}
public function addSong(songName:String, weekNum:Int, songCharacter:String, color:Int)
{
songs.push(new SongMetadata(songName, weekNum, songCharacter, color));
}
/*public function addWeek(songs:Array<String>, weekNum:Int, weekColor:Int, ?songCharacters:Array<String>)
{
if (songCharacters == null)
songCharacters = ['bf'];
var num:Int = 0;
for (song in songs)
{
addSong(song, weekNum, songCharacters[num]);
this.songs[this.songs.length-1].color = weekColor;
if (songCharacters.length != 1)
num++;
}
}*/
var instPlaying:Int = -1;
private static var vocals:FlxSound = null;
override function update(elapsed:Float)
{
if (FlxG.sound.music.volume < 0.7)
{
FlxG.sound.music.volume += 0.5 * FlxG.elapsed;
}
lerpScore = Math.floor(FlxMath.lerp(lerpScore, intendedScore, CoolUtil.boundTo(elapsed * 24, 0, 1)));
lerpRating = FlxMath.lerp(lerpRating, intendedRating, CoolUtil.boundTo(elapsed * 12, 0, 1));
if (Math.abs(lerpScore - intendedScore) <= 10)
lerpScore = intendedScore;
if (Math.abs(lerpRating - intendedRating) <= 0.01)
lerpRating = intendedRating;
scoreText.text = 'PERSONAL BEST: ' + lerpScore + ' (' + Math.floor(lerpRating * 100) + '%)';
positionHighscore();
var upP = controls.UI_UP_P;
var downP = controls.UI_DOWN_P;
var accepted = controls.ACCEPT;
var space = FlxG.keys.justPressed.SPACE;
var shiftMult:Int = 1;
if(FlxG.keys.pressed.SHIFT) shiftMult = 3;
if (upP)
{
changeSelection(-shiftMult);
}
if (downP)
{
changeSelection(shiftMult);
}
if (controls.UI_LEFT_P)
changeDiff(-1);
if (controls.UI_RIGHT_P)
changeDiff(1);
if (controls.BACK)
{
if(colorTween != null) {
colorTween.cancel();
}
FlxG.sound.play(Paths.sound('cancelMenu'));
MusicBeatState.switchState(new MainMenuState());
}
#if PRELOAD_ALL
if(space && instPlaying != curSelected)
{
destroyFreeplayVocals();
Paths.currentModDirectory = songs[curSelected].folder;
var poop:String = Highscore.formatSong(songs[curSelected].songName.toLowerCase(), curDifficulty);
PlayState.SONG = Song.loadFromJson(poop, songs[curSelected].songName.toLowerCase());
if (PlayState.SONG.needsVoices)
vocals = new FlxSound().loadEmbedded(Paths.voices(PlayState.SONG.song));
else
vocals = new FlxSound();
FlxG.sound.list.add(vocals);
FlxG.sound.playMusic(Paths.inst(PlayState.SONG.song), 0.7);
vocals.play();
vocals.persist = true;
vocals.looped = true;
vocals.volume = 0.7;
instPlaying = curSelected;
}
else #end if (accepted)
{
var songLowercase:String = Paths.formatToSongPath(songs[curSelected].songName);
var poop:String = Highscore.formatSong(songLowercase, curDifficulty);
#if MODS_ALLOWED
if(!sys.FileSystem.exists(Paths.modsJson(songLowercase + '/' + poop)) && !sys.FileSystem.exists(Paths.json(songLowercase + '/' + poop))) {
#else
if(!OpenFlAssets.exists(Paths.json(songLowercase + '/' + poop))) {
#end
poop = songLowercase;
curDifficulty = 1;
trace('Couldnt find file');
}
trace(poop);
PlayState.SONG = Song.loadFromJson(poop, songLowercase);
PlayState.isStoryMode = false;
PlayState.storyDifficulty = curDifficulty;
PlayState.storyWeek = songs[curSelected].week;
trace('CURRENT WEEK: ' + WeekData.getWeekFileName());
if(colorTween != null) {
colorTween.cancel();
}
LoadingState.loadAndSwitchState(new PlayState());
FlxG.sound.music.volume = 0;
destroyFreeplayVocals();
}
else if(controls.RESET)
{
openSubState(new ResetScoreSubState(songs[curSelected].songName, curDifficulty, songs[curSelected].songCharacter));
FlxG.sound.play(Paths.sound('scrollMenu'));
}
super.update(elapsed);
}
public static function destroyFreeplayVocals() {
if(vocals != null) {
vocals.stop();
vocals.destroy();
}
vocals = null;
}
function changeDiff(change:Int = 0)
{
curDifficulty += change;
if (curDifficulty < 0)
curDifficulty = CoolUtil.difficultyStuff.length-1;
if (curDifficulty >= CoolUtil.difficultyStuff.length)
curDifficulty = 0;
#if !switch
intendedScore = Highscore.getScore(songs[curSelected].songName, curDifficulty);
intendedRating = Highscore.getRating(songs[curSelected].songName, curDifficulty);
#end
PlayState.storyDifficulty = curDifficulty;
diffText.text = '< ' + CoolUtil.difficultyString() + ' >';
positionHighscore();
}
function changeSelection(change:Int = 0)
{
FlxG.sound.play(Paths.sound('scrollMenu'), 0.4);
curSelected += change;
if (curSelected < 0)
curSelected = songs.length - 1;
if (curSelected >= songs.length)
curSelected = 0;
var newColor:Int = songs[curSelected].color;
if(newColor != intendedColor) {
if(colorTween != null) {
colorTween.cancel();
}
intendedColor = newColor;
colorTween = FlxTween.color(bg, 1, bg.color, intendedColor, {
onComplete: function(twn:FlxTween) {
colorTween = null;
}
});
}
// selector.y = (70 * curSelected) + 30;
#if !switch
intendedScore = Highscore.getScore(songs[curSelected].songName, curDifficulty);
intendedRating = Highscore.getRating(songs[curSelected].songName, curDifficulty);
#end
var bullShit:Int = 0;
for (i in 0...iconArray.length)
{
iconArray[i].alpha = 0.6;
}
iconArray[curSelected].alpha = 1;
for (item in grpSongs.members)
{
item.targetY = bullShit - curSelected;
bullShit++;
item.alpha = 0.6;
// item.setGraphicSize(Std.int(item.width * 0.8));
if (item.targetY == 0)
{
item.alpha = 1;
// item.setGraphicSize(Std.int(item.width));
}
}
changeDiff();
Paths.currentModDirectory = songs[curSelected].folder;
}
private function positionHighscore() {
scoreText.x = FlxG.width - scoreText.width - 6;
scoreBG.scale.x = FlxG.width - scoreText.x + 6;
scoreBG.x = FlxG.width - (scoreBG.scale.x / 2);
diffText.x = Std.int(scoreBG.x + (scoreBG.width / 2));
diffText.x -= diffText.width / 2;
}
}
class SongMetadata
{
public var songName:String = "";
public var week:Int = 0;
public var songCharacter:String = "";
public var color:Int = -7179779;
public var folder:String = "";
public function new(song:String, week:Int, songCharacter:String, color:Int)
{
this.songName = song;
this.week = week;
this.songCharacter = songCharacter;
this.color = color;
this.folder = Paths.currentModDirectory;
if(this.folder == null) this.folder = '';
}
}

1386
source/FunkinLua.hx Normal file

File diff suppressed because it is too large Load diff

149
source/GameOverSubstate.hx Normal file
View file

@ -0,0 +1,149 @@
package;
import flixel.FlxG;
import flixel.FlxObject;
import flixel.FlxSubState;
import flixel.math.FlxMath;
import flixel.math.FlxPoint;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
class GameOverSubstate extends MusicBeatSubstate
{
var bf:Boyfriend;
var camFollow:FlxPoint;
var camFollowPos:FlxObject;
var updateCamera:Bool = false;
var stageSuffix:String = "";
var lePlayState:PlayState;
public static var characterName:String = 'bf';
public static var deathSoundName:String = 'fnf_loss_sfx';
public static var loopSoundName:String = 'gameOver';
public static var endSoundName:String = 'gameOverEnd';
public static function resetVariables() {
characterName = 'bf';
deathSoundName = 'fnf_loss_sfx';
loopSoundName = 'gameOver';
endSoundName = 'gameOverEnd';
}
public function new(x:Float, y:Float, camX:Float, camY:Float, state:PlayState)
{
lePlayState = state;
state.setOnLuas('inGameOver', true);
super();
Conductor.songPosition = 0;
bf = new Boyfriend(x, y, characterName);
add(bf);
camFollow = new FlxPoint(bf.getGraphicMidpoint().x, bf.getGraphicMidpoint().y);
FlxG.sound.play(Paths.sound(deathSoundName));
Conductor.changeBPM(100);
// FlxG.camera.followLerp = 1;
// FlxG.camera.focusOn(FlxPoint.get(FlxG.width / 2, FlxG.height / 2));
FlxG.camera.scroll.set();
FlxG.camera.target = null;
bf.playAnim('firstDeath');
var exclude:Array<Int> = [];
camFollowPos = new FlxObject(0, 0, 1, 1);
camFollowPos.setPosition(FlxG.camera.scroll.x + (FlxG.camera.width / 2), FlxG.camera.scroll.y + (FlxG.camera.height / 2));
add(camFollowPos);
}
override function update(elapsed:Float)
{
super.update(elapsed);
lePlayState.callOnLuas('onUpdate', [elapsed]);
if(updateCamera) {
var lerpVal:Float = CoolUtil.boundTo(elapsed * 0.6, 0, 1);
camFollowPos.setPosition(FlxMath.lerp(camFollowPos.x, camFollow.x, lerpVal), FlxMath.lerp(camFollowPos.y, camFollow.y, lerpVal));
}
if (controls.ACCEPT)
{
endBullshit();
}
if (controls.BACK)
{
FlxG.sound.music.stop();
PlayState.deathCounter = 0;
PlayState.seenCutscene = false;
if (PlayState.isStoryMode)
MusicBeatState.switchState(new StoryMenuState());
else
MusicBeatState.switchState(new FreeplayState());
FlxG.sound.playMusic(Paths.music('freakyMenu'));
lePlayState.callOnLuas('onGameOverConfirm', [false]);
}
if (bf.animation.curAnim.name == 'firstDeath')
{
if(bf.animation.curAnim.curFrame == 12)
{
FlxG.camera.follow(camFollowPos, LOCKON, 1);
updateCamera = true;
}
if (bf.animation.curAnim.finished)
{
coolStartDeath();
bf.startedDeath = true;
}
}
if (FlxG.sound.music.playing)
{
Conductor.songPosition = FlxG.sound.music.time;
}
lePlayState.callOnLuas('onUpdatePost', [elapsed]);
}
override function beatHit()
{
super.beatHit();
//FlxG.log.add('beat');
}
var isEnding:Bool = false;
function coolStartDeath(?volume:Float = 1):Void
{
FlxG.sound.playMusic(Paths.music(loopSoundName), volume);
}
function endBullshit():Void
{
if (!isEnding)
{
isEnding = true;
bf.playAnim('deathConfirm', true);
FlxG.sound.music.stop();
FlxG.sound.play(Paths.music(endSoundName));
new FlxTimer().start(0.7, function(tmr:FlxTimer)
{
FlxG.camera.fade(FlxColor.BLACK, 2, false, function()
{
MusicBeatState.resetState();
});
});
lePlayState.callOnLuas('onGameOverConfirm', [true]);
}
}
}

94
source/GitarooPause.hx Normal file
View file

@ -0,0 +1,94 @@
package;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
class GitarooPause extends MusicBeatState
{
var replayButton:FlxSprite;
var cancelButton:FlxSprite;
var replaySelect:Bool = false;
public function new():Void
{
super();
}
override function create()
{
if (FlxG.sound.music != null)
FlxG.sound.music.stop();
var bg:FlxSprite = new FlxSprite().loadGraphic(Paths.image('pauseAlt/pauseBG'));
add(bg);
var bf:FlxSprite = new FlxSprite(0, 30);
bf.frames = Paths.getSparrowAtlas('pauseAlt/bfLol');
bf.animation.addByPrefix('lol', "funnyThing", 13);
bf.animation.play('lol');
add(bf);
bf.screenCenter(X);
replayButton = new FlxSprite(FlxG.width * 0.28, FlxG.height * 0.7);
replayButton.frames = Paths.getSparrowAtlas('pauseAlt/pauseUI');
replayButton.animation.addByPrefix('selected', 'bluereplay', 0, false);
replayButton.animation.appendByPrefix('selected', 'yellowreplay');
replayButton.animation.play('selected');
add(replayButton);
cancelButton = new FlxSprite(FlxG.width * 0.58, replayButton.y);
cancelButton.frames = Paths.getSparrowAtlas('pauseAlt/pauseUI');
cancelButton.animation.addByPrefix('selected', 'bluecancel', 0, false);
cancelButton.animation.appendByPrefix('selected', 'cancelyellow');
cancelButton.animation.play('selected');
add(cancelButton);
changeThing();
super.create();
}
override function update(elapsed:Float)
{
if (controls.UI_LEFT_P || controls.UI_RIGHT_P)
changeThing();
if (controls.ACCEPT)
{
if (replaySelect)
{
MusicBeatState.switchState(new PlayState());
}
else
{
PlayState.usedPractice = false;
PlayState.changedDifficulty = false;
PlayState.seenCutscene = false;
PlayState.deathCounter = 0;
PlayState.cpuControlled = false;
MusicBeatState.switchState(new MainMenuState());
FlxG.sound.playMusic(Paths.music('freakyMenu'));
}
}
super.update(elapsed);
}
function changeThing():Void
{
replaySelect = !replaySelect;
if (replaySelect)
{
cancelButton.animation.curAnim.curFrame = 0;
replayButton.animation.curAnim.curFrame = 1;
}
else
{
cancelButton.animation.curAnim.curFrame = 1;
replayButton.animation.curAnim.curFrame = 0;
}
}
}

59
source/HealthIcon.hx Normal file
View file

@ -0,0 +1,59 @@
package;
import flixel.FlxSprite;
import openfl.utils.Assets as OpenFlAssets;
using StringTools;
class HealthIcon extends FlxSprite
{
public var sprTracker:FlxSprite;
private var isOldIcon:Bool = false;
private var isPlayer:Bool = false;
private var char:String = '';
public function new(char:String = 'bf', isPlayer:Bool = false)
{
super();
isOldIcon = (char == 'bf-old');
this.isPlayer = isPlayer;
changeIcon(char);
scrollFactor.set();
}
override function update(elapsed:Float)
{
super.update(elapsed);
if (sprTracker != null)
setPosition(sprTracker.x + sprTracker.width + 10, sprTracker.y - 30);
}
public function swapOldIcon() {
if(isOldIcon = !isOldIcon) changeIcon('bf-old');
else changeIcon('bf');
}
public function changeIcon(char:String) {
if(this.char != char) {
var name:String = 'icons/' + char;
if(!Paths.fileExists('images/' + name + '.png', IMAGE)) name = 'icons/icon-' + char; //Older versions of psych engine's support
if(!Paths.fileExists('images/' + name + '.png', IMAGE)) name = 'icons/icon-face'; //Prevents crash from missing icon
var file:Dynamic = Paths.image(name);
loadGraphic(file, true, 150, 150);
animation.add(char, [0, 1], 0, false, isPlayer);
animation.play(char);
this.char = char;
antialiasing = ClientPrefs.globalAntialiasing;
if(char.endsWith('-pixel')) {
antialiasing = false;
}
}
}
public function getCharacter():String {
return char;
}
}

135
source/Highscore.hx Normal file
View file

@ -0,0 +1,135 @@
package;
import flixel.FlxG;
using StringTools;
class Highscore
{
#if (haxe >= "4.0.0")
public static var weekScores:Map<String, Int> = new Map();
public static var songScores:Map<String, Int> = new Map();
public static var songRating:Map<String, Float> = new Map();
#else
public static var weekScores:Map<String, Int> = new Map();
public static var songScores:Map<String, Int> = new Map<String, Int>();
public static var songRating:Map<String, Float> = new Map<String, Float>();
#end
public static function resetSong(song:String, diff:Int = 0):Void
{
var daSong:String = formatSong(song, diff);
setScore(daSong, 0);
setRating(daSong, 0);
}
public static function resetWeek(week:String, diff:Int = 0):Void
{
var daWeek:String = formatSong(week, diff);
setWeekScore(daWeek, 0);
}
public static function saveScore(song:String, score:Int = 0, ?diff:Int = 0, ?rating:Float = -1):Void
{
var daSong:String = formatSong(song, diff);
if (songScores.exists(daSong)) {
if (songScores.get(daSong) < score) {
setScore(daSong, score);
if(rating >= 0) setRating(daSong, rating);
}
}
else {
setScore(daSong, score);
if(rating >= 0) setRating(daSong, rating);
}
}
public static function saveWeekScore(week:String, score:Int = 0, ?diff:Int = 0):Void
{
var daWeek:String = formatSong(week, diff);
if (weekScores.exists(daWeek))
{
if (weekScores.get(daWeek) < score)
setWeekScore(daWeek, score);
}
else
setWeekScore(daWeek, score);
}
/**
* YOU SHOULD FORMAT SONG WITH formatSong() BEFORE TOSSING IN SONG VARIABLE
*/
static function setScore(song:String, score:Int):Void
{
// Reminder that I don't need to format this song, it should come formatted!
songScores.set(song, score);
FlxG.save.data.songScores = songScores;
FlxG.save.flush();
}
static function setWeekScore(week:String, score:Int):Void
{
// Reminder that I don't need to format this song, it should come formatted!
weekScores.set(week, score);
FlxG.save.data.weekScores = weekScores;
FlxG.save.flush();
}
static function setRating(song:String, rating:Float):Void
{
// Reminder that I don't need to format this song, it should come formatted!
songRating.set(song, rating);
FlxG.save.data.songRating = songRating;
FlxG.save.flush();
}
public static function formatSong(song:String, diff:Int):String
{
return Paths.formatToSongPath(song) + CoolUtil.difficultyStuff[diff][1];
}
public static function getScore(song:String, diff:Int):Int
{
var daSong:String = formatSong(song, diff);
if (!songScores.exists(daSong))
setScore(daSong, 0);
return songScores.get(daSong);
}
public static function getRating(song:String, diff:Int):Float
{
var daSong:String = formatSong(song, diff);
if (!songRating.exists(daSong))
setRating(daSong, 0);
return songRating.get(daSong);
}
public static function getWeekScore(week:String, diff:Int):Int
{
var daWeek:String = formatSong(week, diff);
if (!weekScores.exists(daWeek))
setWeekScore(daWeek, 0);
return weekScores.get(daWeek);
}
public static function load():Void
{
if (FlxG.save.data.weekScores != null)
{
weekScores = FlxG.save.data.weekScores;
}
if (FlxG.save.data.songScores != null)
{
songScores = FlxG.save.data.songScores;
}
if (FlxG.save.data.songRating != null)
{
songRating = FlxG.save.data.songRating;
}
}
}

97
source/InputFormatter.hx Normal file
View file

@ -0,0 +1,97 @@
import flixel.FlxG;
import flixel.input.keyboard.FlxKey;
using StringTools;
class InputFormatter {
public static function getKeyName(key:FlxKey):String {
switch (key) {
case BACKSPACE:
return "BckSpc";
case CONTROL:
return "Ctrl";
case ALT:
return "Alt";
case CAPSLOCK:
return "Caps";
case PAGEUP:
return "PgUp";
case PAGEDOWN:
return "PgDown";
case ZERO:
return "0";
case ONE:
return "1";
case TWO:
return "2";
case THREE:
return "3";
case FOUR:
return "4";
case FIVE:
return "5";
case SIX:
return "6";
case SEVEN:
return "7";
case EIGHT:
return "8";
case NINE:
return "9";
case NUMPADZERO:
return "#0";
case NUMPADONE:
return "#1";
case NUMPADTWO:
return "#2";
case NUMPADTHREE:
return "#3";
case NUMPADFOUR:
return "#4";
case NUMPADFIVE:
return "#5";
case NUMPADSIX:
return "#6";
case NUMPADSEVEN:
return "#7";
case NUMPADEIGHT:
return "#8";
case NUMPADNINE:
return "#9";
case NUMPADMULTIPLY:
return "#*";
case NUMPADPLUS:
return "#+";
case NUMPADMINUS:
return "#-";
case NUMPADPERIOD:
return "#.";
case SEMICOLON:
return ";";
case COMMA:
return ",";
case PERIOD:
return ".";
//case SLASH:
// return "/";
case GRAVEACCENT:
return "`";
case LBRACKET:
return "[";
//case BACKSLASH:
// return "\\";
case RBRACKET:
return "]";
case QUOTE:
return "'";
case PRINTSCREEN:
return "PrtScrn";
case NONE:
return '---';
default:
var label:String = '' + key;
if(label.toLowerCase() == 'null') return '---';
return '' + label.charAt(0).toUpperCase() + label.substr(1).toLowerCase();
}
}
}

74
source/LatencyState.hx Normal file
View file

@ -0,0 +1,74 @@
package;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.FlxState;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.text.FlxText;
class LatencyState extends FlxState
{
var offsetText:FlxText;
var noteGrp:FlxTypedGroup<Note>;
var strumLine:FlxSprite;
override function create()
{
FlxG.sound.playMusic(Paths.sound('soundTest'));
noteGrp = new FlxTypedGroup<Note>();
add(noteGrp);
for (i in 0...32)
{
var note:Note = new Note(Conductor.crochet * i, 1);
noteGrp.add(note);
}
offsetText = new FlxText();
offsetText.screenCenter();
add(offsetText);
strumLine = new FlxSprite(FlxG.width / 2, 100).makeGraphic(FlxG.width, 5);
add(strumLine);
Conductor.changeBPM(120);
super.create();
}
override function update(elapsed:Float)
{
offsetText.text = "Offset: " + Conductor.offset + "ms";
Conductor.songPosition = FlxG.sound.music.time - Conductor.offset;
var multiply:Float = 1;
if (FlxG.keys.pressed.SHIFT)
multiply = 10;
if (FlxG.keys.justPressed.RIGHT)
Conductor.offset += 1 * multiply;
if (FlxG.keys.justPressed.LEFT)
Conductor.offset -= 1 * multiply;
if (FlxG.keys.justPressed.SPACE)
{
FlxG.sound.music.stop();
FlxG.resetState();
}
noteGrp.forEach(function(daNote:Note)
{
daNote.y = (strumLine.y - (Conductor.songPosition - daNote.strumTime) * 0.45);
daNote.x = strumLine.x + 30;
if (daNote.y < strumLine.y)
daNote.kill();
});
super.update(elapsed);
}
}

320
source/LoadingState.hx Normal file
View file

@ -0,0 +1,320 @@
package;
import lime.app.Promise;
import lime.app.Future;
import flixel.FlxG;
import flixel.FlxState;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.util.FlxTimer;
import flixel.math.FlxMath;
import openfl.utils.Assets;
import lime.utils.Assets as LimeAssets;
import lime.utils.AssetLibrary;
import lime.utils.AssetManifest;
import haxe.io.Path;
class LoadingState extends MusicBeatState
{
inline static var MIN_TIME = 1.0;
// Browsers will load create(), you can make your song load a custom directory there
// If you're compiling to desktop (or something that doesn't use NO_PRELOAD_ALL), search for getNextState instead
// I'd recommend doing it on both actually lol
// TO DO: Make this easier
var target:FlxState;
var stopMusic = false;
var directory:String;
var callbacks:MultiCallback;
var targetShit:Float = 0;
function new(target:FlxState, stopMusic:Bool, directory:String)
{
super();
this.target = target;
this.stopMusic = stopMusic;
this.directory = directory;
}
var funkay:FlxSprite;
var loadBar:FlxSprite;
override function create()
{
var bg:FlxSprite = new FlxSprite(0, 0).makeGraphic(FlxG.width, FlxG.height, 0xffcaff4d);
add(bg);
funkay = new FlxSprite(0, 0).loadGraphic(Paths.getPath('images/funkay.png', IMAGE));
funkay.setGraphicSize(0, FlxG.height);
funkay.updateHitbox();
funkay.antialiasing = ClientPrefs.globalAntialiasing;
add(funkay);
funkay.scrollFactor.set();
funkay.screenCenter();
loadBar = new FlxSprite(0, FlxG.height - 20).makeGraphic(FlxG.width, 10, 0xffff16d2);
loadBar.screenCenter(X);
loadBar.antialiasing = ClientPrefs.globalAntialiasing;
add(loadBar);
initSongsManifest().onComplete
(
function (lib)
{
callbacks = new MultiCallback(onLoad);
var introComplete = callbacks.add("introComplete");
if (PlayState.SONG != null) {
checkLoadSong(getSongPath());
if (PlayState.SONG.needsVoices)
checkLoadSong(getVocalPath());
}
checkLibrary("shared");
if(directory != null && directory.length > 0 && directory != 'shared') {
checkLibrary(directory);
}
var fadeTime = 0.5;
FlxG.camera.fade(FlxG.camera.bgColor, fadeTime, true);
new FlxTimer().start(fadeTime + MIN_TIME, function(_) introComplete());
}
);
}
function checkLoadSong(path:String)
{
if (!Assets.cache.hasSound(path))
{
var library = Assets.getLibrary("songs");
final symbolPath = path.split(":").pop();
// @:privateAccess
// library.types.set(symbolPath, SOUND);
// @:privateAccess
// library.pathGroups.set(symbolPath, [library.__cacheBreak(symbolPath)]);
var callback = callbacks.add("song:" + path);
Assets.loadSound(path).onComplete(function (_) { callback(); });
}
}
function checkLibrary(library:String) {
trace(Assets.hasLibrary(library));
if (Assets.getLibrary(library) == null)
{
@:privateAccess
if (!LimeAssets.libraryPaths.exists(library))
throw "Missing library: " + library;
var callback = callbacks.add("library:" + library);
Assets.loadLibrary(library).onComplete(function (_) { callback(); });
}
}
override function update(elapsed:Float)
{
super.update(elapsed);
funkay.setGraphicSize(Std.int(0.88 * FlxG.width + 0.9 * (funkay.width - 0.88 * FlxG.width)));
funkay.updateHitbox();
if(controls.ACCEPT)
{
funkay.setGraphicSize(Std.int(funkay.width + 60));
funkay.updateHitbox();
}
if(callbacks != null) {
targetShit = FlxMath.remapToRange(callbacks.numRemaining / callbacks.length, 1, 0, 0, 1);
loadBar.scale.x += 0.5 * (targetShit - loadBar.scale.x);
}
}
function onLoad()
{
if (stopMusic && FlxG.sound.music != null)
FlxG.sound.music.stop();
MusicBeatState.switchState(target);
}
static function getSongPath()
{
return Paths.inst(PlayState.SONG.song);
}
static function getVocalPath()
{
return Paths.voices(PlayState.SONG.song);
}
inline static public function loadAndSwitchState(target:FlxState, stopMusic = false)
{
MusicBeatState.switchState(getNextState(target, stopMusic));
}
static function getNextState(target:FlxState, stopMusic = false):FlxState
{
var directory:String = 'shared';
var weekDir:String = StageData.forceNextDirectory;
StageData.forceNextDirectory = null;
if(weekDir != null && weekDir.length > 0 && weekDir != '') directory = weekDir;
Paths.setCurrentLevel(directory);
trace('Setting asset folder to ' + directory);
#if NO_PRELOAD_ALL
var loaded:Bool = false;
if (PlayState.SONG != null) {
loaded = isSoundLoaded(getSongPath()) && (!PlayState.SONG.needsVoices || isSoundLoaded(getVocalPath())) && isLibraryLoaded("shared") && isLibraryLoaded(directory);
}
if (!loaded)
return new LoadingState(target, stopMusic, directory);
#end
if (stopMusic && FlxG.sound.music != null)
FlxG.sound.music.stop();
return target;
}
#if NO_PRELOAD_ALL
static function isSoundLoaded(path:String):Bool
{
return Assets.cache.hasSound(path);
}
static function isLibraryLoaded(library:String):Bool
{
return Assets.getLibrary(library) != null;
}
#end
override function destroy()
{
super.destroy();
callbacks = null;
}
static function initSongsManifest()
{
var id = "songs";
var promise = new Promise<AssetLibrary>();
var library = LimeAssets.getLibrary(id);
if (library != null)
{
return Future.withValue(library);
}
var path = id;
var rootPath = null;
@:privateAccess
var libraryPaths = LimeAssets.libraryPaths;
if (libraryPaths.exists(id))
{
path = libraryPaths[id];
rootPath = Path.directory(path);
}
else
{
if (StringTools.endsWith(path, ".bundle"))
{
rootPath = path;
path += "/library.json";
}
else
{
rootPath = Path.directory(path);
}
@:privateAccess
path = LimeAssets.__cacheBreak(path);
}
AssetManifest.loadFromFile(path, rootPath).onComplete(function(manifest)
{
if (manifest == null)
{
promise.error("Cannot parse asset manifest for library \"" + id + "\"");
return;
}
var library = AssetLibrary.fromManifest(manifest);
if (library == null)
{
promise.error("Cannot open library \"" + id + "\"");
}
else
{
@:privateAccess
LimeAssets.libraries.set(id, library);
library.onChange.add(LimeAssets.onChange.dispatch);
promise.completeWith(Future.withValue(library));
}
}).onError(function(_)
{
promise.error("There is no asset library with an ID of \"" + id + "\"");
});
return promise.future;
}
}
class MultiCallback
{
public var callback:Void->Void;
public var logId:String = null;
public var length(default, null) = 0;
public var numRemaining(default, null) = 0;
var unfired = new Map<String, Void->Void>();
var fired = new Array<String>();
public function new (callback:Void->Void, logId:String = null)
{
this.callback = callback;
this.logId = logId;
}
public function add(id = "untitled")
{
id = '$length:$id';
length++;
numRemaining++;
var func:Void->Void = null;
func = function ()
{
if (unfired.exists(id))
{
unfired.remove(id);
fired.push(id);
numRemaining--;
if (logId != null)
log('fired $id, $numRemaining remaining');
if (numRemaining == 0)
{
if (logId != null)
log('all callbacks fired');
callback();
}
}
else
log('already fired $id');
}
unfired[id] = func;
return func;
}
inline function log(msg):Void
{
if (logId != null)
trace('$logId: $msg');
}
public function getFired() return fired.copy();
public function getUnfired() return [for (id in unfired.keys()) id];
}

89
source/Main.hx Normal file
View file

@ -0,0 +1,89 @@
package;
import flixel.FlxG;
import flixel.FlxGame;
import flixel.FlxState;
import openfl.Assets;
import openfl.Lib;
import openfl.display.FPS;
import openfl.display.Sprite;
import openfl.events.Event;
class Main extends Sprite
{
var gameWidth:Int = 1280; // Width of the game in pixels (might be less / more in actual pixels depending on your zoom).
var gameHeight:Int = 720; // Height of the game in pixels (might be less / more in actual pixels depending on your zoom).
var initialState:Class<FlxState> = TitleState; // The FlxState the game starts with.
var zoom:Float = -1; // If -1, zoom is automatically calculated to fit the window dimensions.
var framerate:Int = 60; // How many frames per second the game should run at.
var skipSplash:Bool = true; // Whether to skip the flixel splash screen that appears in release mode.
var startFullscreen:Bool = false; // Whether to start the game in fullscreen on desktop targets
public static var fpsVar:FPS;
// You can pretty much ignore everything from here on - your code should go in your states.
public static function main():Void
{
Lib.current.addChild(new Main());
}
public function new()
{
super();
if (stage != null)
{
init();
}
else
{
addEventListener(Event.ADDED_TO_STAGE, init);
}
}
private function init(?E:Event):Void
{
if (hasEventListener(Event.ADDED_TO_STAGE))
{
removeEventListener(Event.ADDED_TO_STAGE, init);
}
setupGame();
}
private function setupGame():Void
{
var stageWidth:Int = Lib.current.stage.stageWidth;
var stageHeight:Int = Lib.current.stage.stageHeight;
if (zoom == -1)
{
var ratioX:Float = stageWidth / gameWidth;
var ratioY:Float = stageHeight / gameHeight;
zoom = Math.min(ratioX, ratioY);
gameWidth = Math.ceil(stageWidth / zoom);
gameHeight = Math.ceil(stageHeight / zoom);
}
#if !debug
initialState = TitleState;
#end
Paths.getModFolders();
ClientPrefs.startControls();
addChild(new FlxGame(gameWidth, gameHeight, initialState, zoom, framerate, framerate, skipSplash, startFullscreen));
#if !mobile
fpsVar = new FPS(10, 3, 0xFFFFFF);
addChild(fpsVar);
if(fpsVar != null) {
fpsVar.visible = ClientPrefs.showFPS;
}
#end
#if html5
FlxG.autoPause = false;
FlxG.mouse.visible = false;
#end
}
}

271
source/MainMenuState.hx Normal file
View file

@ -0,0 +1,271 @@
package;
#if desktop
import Discord.DiscordClient;
#end
import flixel.FlxG;
import flixel.FlxObject;
import flixel.FlxSprite;
import flixel.FlxCamera;
import flixel.addons.transition.FlxTransitionableState;
import flixel.effects.FlxFlicker;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.text.FlxText;
import flixel.math.FlxMath;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import lime.app.Application;
import Achievements;
import editors.MasterEditorMenu;
using StringTools;
class MainMenuState extends MusicBeatState
{
public static var psychEngineVersion:String = '0.4.2'; //This is also used for Discord RPC
public static var curSelected:Int = 0;
var menuItems:FlxTypedGroup<FlxSprite>;
private var camGame:FlxCamera;
private var camAchievement:FlxCamera;
var optionShit:Array<String> = ['story_mode', 'freeplay', #if ACHIEVEMENTS_ALLOWED 'awards', #end 'credits', #if !switch 'donate', #end 'options'];
var magenta:FlxSprite;
var camFollow:FlxObject;
var camFollowPos:FlxObject;
override function create()
{
#if desktop
// Updating Discord Rich Presence
DiscordClient.changePresence("In the Menus", null);
#end
camGame = new FlxCamera();
camAchievement = new FlxCamera();
camAchievement.bgColor.alpha = 0;
FlxG.cameras.reset(camGame);
FlxG.cameras.add(camAchievement);
FlxCamera.defaultCameras = [camGame];
transIn = FlxTransitionableState.defaultTransIn;
transOut = FlxTransitionableState.defaultTransOut;
persistentUpdate = persistentDraw = true;
var yScroll:Float = Math.max(0.25 - (0.05 * (optionShit.length - 4)), 0.1);
var bg:FlxSprite = new FlxSprite(-80).loadGraphic(Paths.image('menuBG'));
bg.scrollFactor.set(0, yScroll);
bg.setGraphicSize(Std.int(bg.width * 1.175));
bg.updateHitbox();
bg.screenCenter();
bg.antialiasing = ClientPrefs.globalAntialiasing;
add(bg);
camFollow = new FlxObject(0, 0, 1, 1);
camFollowPos = new FlxObject(0, 0, 1, 1);
add(camFollow);
add(camFollowPos);
magenta = new FlxSprite(-80).loadGraphic(Paths.image('menuDesat'));
magenta.scrollFactor.set(0, yScroll);
magenta.setGraphicSize(Std.int(magenta.width * 1.175));
magenta.updateHitbox();
magenta.screenCenter();
magenta.visible = false;
magenta.antialiasing = ClientPrefs.globalAntialiasing;
magenta.color = 0xFFfd719b;
add(magenta);
// magenta.scrollFactor.set();
menuItems = new FlxTypedGroup<FlxSprite>();
add(menuItems);
for (i in 0...optionShit.length)
{
var offset:Float = 108 - (Math.max(optionShit.length, 4) - 4) * 80;
var menuItem:FlxSprite = new FlxSprite(0, (i * 140) + offset);
menuItem.frames = Paths.getSparrowAtlas('mainmenu/menu_' + optionShit[i]);
menuItem.animation.addByPrefix('idle', optionShit[i] + " basic", 24);
menuItem.animation.addByPrefix('selected', optionShit[i] + " white", 24);
menuItem.animation.play('idle');
menuItem.ID = i;
menuItem.screenCenter(X);
menuItems.add(menuItem);
var scr:Float = (optionShit.length - 4) * 0.135;
if(optionShit.length < 6) scr = 0;
menuItem.scrollFactor.set(0, scr);
menuItem.antialiasing = ClientPrefs.globalAntialiasing;
//menuItem.setGraphicSize(Std.int(menuItem.width * 0.58));
menuItem.updateHitbox();
}
FlxG.camera.follow(camFollowPos, null, 1);
var versionShit:FlxText = new FlxText(12, FlxG.height - 44, 0, "Psych Engine v" + psychEngineVersion, 12);
versionShit.scrollFactor.set();
versionShit.setFormat("VCR OSD Mono", 16, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
add(versionShit);
var versionShit:FlxText = new FlxText(12, FlxG.height - 24, 0, "Tobee Night Funkin' v" + Application.current.meta.get('version'), 12);
versionShit.scrollFactor.set();
versionShit.setFormat("VCR OSD Mono", 16, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
add(versionShit);
// NG.core.calls.event.logEvent('swag').send();
changeItem();
#if ACHIEVEMENTS_ALLOWED
Achievements.loadAchievements();
var leDate = Date.now();
if (leDate.getDay() == 5 && leDate.getHours() >= 18) {
var achieveID:Int = Achievements.getAchievementIndex('friday_night_play');
if(!Achievements.isAchievementUnlocked(Achievements.achievementsStuff[achieveID][2])) { //It's a friday night. WEEEEEEEEEEEEEEEEEE
Achievements.achievementsMap.set(Achievements.achievementsStuff[achieveID][2], true);
giveAchievement();
ClientPrefs.saveSettings();
}
}
#end
super.create();
}
#if ACHIEVEMENTS_ALLOWED
// Unlocks "Freaky on a Friday Night" achievement
function giveAchievement() {
add(new AchievementObject('friday_night_play', camAchievement));
FlxG.sound.play(Paths.sound('confirmMenu'), 0.7);
trace('Giving achievement "friday_night_play"');
}
#end
var selectedSomethin:Bool = false;
override function update(elapsed:Float)
{
if (FlxG.sound.music.volume < 0.8)
{
FlxG.sound.music.volume += 0.5 * FlxG.elapsed;
}
var lerpVal:Float = CoolUtil.boundTo(elapsed * 5.6, 0, 1);
camFollowPos.setPosition(FlxMath.lerp(camFollowPos.x, camFollow.x, lerpVal), FlxMath.lerp(camFollowPos.y, camFollow.y, lerpVal));
if (!selectedSomethin)
{
if (controls.UI_UP_P)
{
FlxG.sound.play(Paths.sound('scrollMenu'));
changeItem(-1);
}
if (controls.UI_DOWN_P)
{
FlxG.sound.play(Paths.sound('scrollMenu'));
changeItem(1);
}
if (controls.BACK)
{
selectedSomethin = true;
FlxG.sound.play(Paths.sound('cancelMenu'));
MusicBeatState.switchState(new TitleState());
}
if (controls.ACCEPT)
{
if (optionShit[curSelected] == 'donate')
{
CoolUtil.browserLoad('https://ninja-muffin24.itch.io/funkin');
}
else
{
selectedSomethin = true;
FlxG.sound.play(Paths.sound('confirmMenu'));
if(ClientPrefs.flashing) FlxFlicker.flicker(magenta, 1.1, 0.15, false);
menuItems.forEach(function(spr:FlxSprite)
{
if (curSelected != spr.ID)
{
FlxTween.tween(spr, {alpha: 0}, 0.4, {
ease: FlxEase.quadOut,
onComplete: function(twn:FlxTween)
{
spr.kill();
}
});
}
else
{
FlxFlicker.flicker(spr, 1, 0.06, false, false, function(flick:FlxFlicker)
{
var daChoice:String = optionShit[curSelected];
switch (daChoice)
{
case 'story_mode':
MusicBeatState.switchState(new StoryMenuState());
case 'freeplay':
MusicBeatState.switchState(new FreeplayState());
case 'awards':
MusicBeatState.switchState(new AchievementsMenuState());
case 'credits':
MusicBeatState.switchState(new CreditsState());
case 'options':
MusicBeatState.switchState(new OptionsState());
}
});
}
});
}
}
#if desktop
else if (FlxG.keys.justPressed.SEVEN)
{
selectedSomethin = true;
MusicBeatState.switchState(new MasterEditorMenu());
}
#end
}
super.update(elapsed);
menuItems.forEach(function(spr:FlxSprite)
{
spr.screenCenter(X);
});
}
function changeItem(huh:Int = 0)
{
curSelected += huh;
if (curSelected >= menuItems.length)
curSelected = 0;
if (curSelected < 0)
curSelected = menuItems.length - 1;
menuItems.forEach(function(spr:FlxSprite)
{
spr.animation.play('idle');
spr.offset.y = 0;
spr.updateHitbox();
if (spr.ID == curSelected)
{
spr.animation.play('selected');
camFollow.setPosition(spr.getGraphicMidpoint().x, spr.getGraphicMidpoint().y);
spr.offset.x = 0.15 * (spr.frameWidth / 2 + 180);
spr.offset.y = 0.15 * spr.frameHeight;
FlxG.log.add(spr.frameWidth);
}
});
}
}

85
source/MenuCharacter.hx Normal file
View file

@ -0,0 +1,85 @@
package;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
#if MODS_ALLOWED
import sys.io.File;
import sys.FileSystem;
#end
import openfl.utils.Assets;
import haxe.Json;
import haxe.format.JsonParser;
typedef MenuCharacterFile = {
var image:String;
var scale:Float;
var position:Array<Int>;
var idle_anim:String;
var confirm_anim:String;
}
class MenuCharacter extends FlxSprite
{
public var character:String;
private static var DEFAULT_CHARACTER:String = 'bf';
public function new(x:Float, character:String = 'bf')
{
super(x);
changeCharacter(character);
}
public function changeCharacter(?character:String = 'bf') {
if(character == null) character = '';
if(character == this.character) return;
this.character = character;
antialiasing = ClientPrefs.globalAntialiasing;
visible = true;
var dontPlayAnim:Bool = false;
scale.set(1, 1);
updateHitbox();
switch(character) {
case '':
visible = false;
dontPlayAnim = true;
default:
var characterPath:String = 'images/menucharacters/' + character + '.json';
var rawJson = null;
#if MODS_ALLOWED
var path:String = Paths.modFolders(characterPath);
if (!FileSystem.exists(path)) {
path = Paths.getPreloadPath(characterPath);
}
if(!FileSystem.exists(path)) {
path = Paths.getPreloadPath('images/menucharacters/' + DEFAULT_CHARACTER + '.json');
}
rawJson = File.getContent(path);
#else
var path:String = Paths.getPreloadPath(characterPath);
if(!Assets.exists(path)) {
path = Paths.getPreloadPath('images/menucharacters/' + DEFAULT_CHARACTER + '.json');
}
rawJson = Assets.getText(path);
#end
var charFile:MenuCharacterFile = cast Json.parse(rawJson);
frames = Paths.getSparrowAtlas('menucharacters/' + charFile.image);
animation.addByPrefix('idle', charFile.idle_anim, 24);
animation.addByPrefix('confirm', charFile.confirm_anim, 24, false);
if(charFile.scale != 1) {
scale.set(charFile.scale, charFile.scale);
updateHitbox();
}
offset.set(charFile.position[0], charFile.position[1]);
animation.play('idle');
}
}
}

48
source/MenuItem.hx Normal file
View file

@ -0,0 +1,48 @@
package;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.math.FlxMath;
import flixel.util.FlxColor;
class MenuItem extends FlxSprite
{
public var targetY:Float = 0;
public var flashingInt:Int = 0;
public function new(x:Float, y:Float, weekName:String = '')
{
super(x, y);
loadGraphic(Paths.image('storymenu/' + weekName));
//trace('Test added: ' + WeekData.getWeekNumber(weekNum) + ' (' + weekNum + ')');
antialiasing = ClientPrefs.globalAntialiasing;
}
private var isFlashing:Bool = false;
public function startFlashing():Void
{
isFlashing = true;
}
// if it runs at 60fps, fake framerate will be 6
// if it runs at 144 fps, fake framerate will be like 14, and will update the graphic every 0.016666 * 3 seconds still???
// so it runs basically every so many seconds, not dependant on framerate??
// I'm still learning how math works thanks whoever is reading this lol
var fakeFramerate:Int = Math.round((1 / FlxG.elapsed) / 10);
override function update(elapsed:Float)
{
super.update(elapsed);
y = FlxMath.lerp(y, (targetY * 120) + 480, CoolUtil.boundTo(elapsed * 10.2, 0, 1));
if (isFlashing)
flashingInt += 1;
if (flashingInt % fakeFramerate >= Math.floor(fakeFramerate / 2))
color = 0xFF33ffff;
else
color = FlxColor.WHITE;
}
}

132
source/MusicBeatState.hx Normal file
View file

@ -0,0 +1,132 @@
package;
import Conductor.BPMChangeEvent;
import flixel.FlxG;
import flixel.addons.ui.FlxUIState;
import flixel.math.FlxRect;
import flixel.util.FlxTimer;
import flixel.addons.transition.FlxTransitionableState;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.FlxSprite;
import flixel.util.FlxColor;
import flixel.util.FlxGradient;
import flixel.FlxState;
import flixel.FlxBasic;
class MusicBeatState extends FlxUIState
{
private var lastBeat:Float = 0;
private var lastStep:Float = 0;
private var curStep:Int = 0;
private var curBeat:Int = 0;
private var controls(get, never):Controls;
inline function get_controls():Controls
return PlayerSettings.player1.controls;
override function create() {
var skip:Bool = FlxTransitionableState.skipNextTransOut;
super.create();
// Custom made Trans out
if(!skip) {
openSubState(new CustomFadeTransition(1, true));
}
FlxTransitionableState.skipNextTransOut = false;
}
#if (VIDEOS_ALLOWED && windows)
override public function onFocus():Void
{
FlxVideo.onFocus();
super.onFocus();
}
override public function onFocusLost():Void
{
FlxVideo.onFocusLost();
super.onFocusLost();
}
#end
override function update(elapsed:Float)
{
//everyStep();
var oldStep:Int = curStep;
updateCurStep();
updateBeat();
if (oldStep != curStep && curStep > 0)
stepHit();
super.update(elapsed);
}
private function updateBeat():Void
{
curBeat = Math.floor(curStep / 4);
}
private function updateCurStep():Void
{
var lastChange:BPMChangeEvent = {
stepTime: 0,
songTime: 0,
bpm: 0
}
for (i in 0...Conductor.bpmChangeMap.length)
{
if (Conductor.songPosition >= Conductor.bpmChangeMap[i].songTime)
lastChange = Conductor.bpmChangeMap[i];
}
curStep = lastChange.stepTime + Math.floor(((Conductor.songPosition - ClientPrefs.noteOffset) - lastChange.songTime) / Conductor.stepCrochet);
}
public static function switchState(nextState:FlxState) {
// Custom made Trans in
var curState:Dynamic = FlxG.state;
var leState:MusicBeatState = curState;
if(!FlxTransitionableState.skipNextTransIn) {
leState.openSubState(new CustomFadeTransition(0.7, false));
if(nextState == FlxG.state) {
CustomFadeTransition.finishCallback = function() {
FlxG.resetState();
};
//trace('resetted');
} else {
CustomFadeTransition.finishCallback = function() {
FlxG.switchState(nextState);
};
//trace('changed state');
}
return;
}
FlxTransitionableState.skipNextTransIn = false;
FlxG.switchState(nextState);
}
public static function resetState() {
MusicBeatState.switchState(FlxG.state);
}
public static function getState():MusicBeatState {
var curState:Dynamic = FlxG.state;
var leState:MusicBeatState = curState;
return leState;
}
public function stepHit():Void
{
if (curStep % 4 == 0)
beatHit();
}
public function beatHit():Void
{
//do literally nothing dumbass
}
}

View file

@ -0,0 +1,67 @@
package;
import Conductor.BPMChangeEvent;
import flixel.FlxG;
import flixel.FlxSubState;
import flixel.FlxBasic;
import flixel.FlxSprite;
class MusicBeatSubstate extends FlxSubState
{
public function new()
{
super();
}
private var lastBeat:Float = 0;
private var lastStep:Float = 0;
private var curStep:Int = 0;
private var curBeat:Int = 0;
private var controls(get, never):Controls;
inline function get_controls():Controls
return PlayerSettings.player1.controls;
override function update(elapsed:Float)
{
//everyStep();
var oldStep:Int = curStep;
updateCurStep();
curBeat = Math.floor(curStep / 4);
if (oldStep != curStep && curStep > 0)
stepHit();
super.update(elapsed);
}
private function updateCurStep():Void
{
var lastChange:BPMChangeEvent = {
stepTime: 0,
songTime: 0,
bpm: 0
}
for (i in 0...Conductor.bpmChangeMap.length)
{
if (Conductor.songPosition > Conductor.bpmChangeMap[i].songTime)
lastChange = Conductor.bpmChangeMap[i];
}
curStep = lastChange.stepTime + Math.floor((Conductor.songPosition - lastChange.songTime) / Conductor.stepCrochet);
}
public function stepHit():Void
{
if (curStep % 4 == 0)
beatHit();
}
public function beatHit():Void
{
//do literally nothing dumbass
}
}

343
source/Note.hx Normal file
View file

@ -0,0 +1,343 @@
package;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.math.FlxMath;
import flixel.util.FlxColor;
import flash.display.BitmapData;
import editors.ChartingState;
using StringTools;
class Note extends FlxSprite
{
public var strumTime:Float = 0;
public var mustPress:Bool = false;
public var noteData:Int = 0;
public var canBeHit:Bool = false;
public var tooLate:Bool = false;
public var wasGoodHit:Bool = false;
public var ignoreNote:Bool = false;
public var hitByOpponent:Bool = false;
public var noteWasHit:Bool = false;
public var prevNote:Note;
public var sustainLength:Float = 0;
public var isSustainNote:Bool = false;
public var noteType(default, set):String = null;
public var eventName:String = '';
public var eventVal1:String = '';
public var eventVal2:String = '';
public var colorSwap:ColorSwap;
public var inEditor:Bool = false;
private var earlyHitMult:Float = 0.5;
public static var swagWidth:Float = 160 * 0.7;
public static var PURP_NOTE:Int = 0;
public static var GREEN_NOTE:Int = 2;
public static var BLUE_NOTE:Int = 1;
public static var RED_NOTE:Int = 3;
// Lua shit
public var noteSplashDisabled:Bool = false;
public var noteSplashTexture:String = null;
public var noteSplashHue:Float = 0;
public var noteSplashSat:Float = 0;
public var noteSplashBrt:Float = 0;
public var offsetX:Float = 0;
public var offsetY:Float = 0;
public var offsetAngle:Float = 0;
public var multAlpha:Float = 1;
public var copyX:Bool = true;
public var copyY:Bool = true;
public var copyAngle:Bool = true;
public var copyAlpha:Bool = true;
public var hitHealth:Float = 0.023;
public var missHealth:Float = 0.0475;
public var texture(default, set):String = null;
public var noAnimation:Bool = false;
public var hitCausesMiss:Bool = false;
private function set_texture(value:String):String {
if(texture != value) {
reloadNote('', value);
}
texture = value;
return value;
}
private function set_noteType(value:String):String {
noteSplashTexture = PlayState.SONG.splashSkin;
colorSwap.hue = ClientPrefs.arrowHSV[noteData % 4][0] / 360;
colorSwap.saturation = ClientPrefs.arrowHSV[noteData % 4][1] / 100;
colorSwap.brightness = ClientPrefs.arrowHSV[noteData % 4][2] / 100;
if(noteData > -1 && noteType != value) {
switch(value) {
case 'Hurt Note':
ignoreNote = mustPress;
reloadNote('HURT');
noteSplashTexture = 'HURTnoteSplashes';
colorSwap.hue = 0;
colorSwap.saturation = 0;
colorSwap.brightness = 0;
if(isSustainNote) {
missHealth = 0.1;
} else {
missHealth = 0.3;
}
hitCausesMiss = true;
case 'No Animation':
noAnimation = true;
}
noteType = value;
}
noteSplashHue = colorSwap.hue;
noteSplashSat = colorSwap.saturation;
noteSplashBrt = colorSwap.brightness;
return value;
}
public function new(strumTime:Float, noteData:Int, ?prevNote:Note, ?sustainNote:Bool = false, ?inEditor:Bool = false)
{
super();
if (prevNote == null)
prevNote = this;
this.prevNote = prevNote;
isSustainNote = sustainNote;
this.inEditor = inEditor;
x += (ClientPrefs.middleScroll ? PlayState.STRUM_X_MIDDLESCROLL : PlayState.STRUM_X) + 50;
// MAKE SURE ITS DEFINITELY OFF SCREEN?
y -= 2000;
this.strumTime = strumTime;
if(!inEditor) this.strumTime += ClientPrefs.noteOffset;
this.noteData = noteData;
if(noteData > -1) {
texture = '';
colorSwap = new ColorSwap();
shader = colorSwap.shader;
x += swagWidth * (noteData % 4);
if(!isSustainNote) { //Doing this 'if' check to fix the warnings on Senpai songs
var animToPlay:String = '';
switch (noteData % 4)
{
case 0:
animToPlay = 'purple';
case 1:
animToPlay = 'blue';
case 2:
animToPlay = 'green';
case 3:
animToPlay = 'red';
}
animation.play(animToPlay + 'Scroll');
}
}
// trace(prevNote);
if (isSustainNote && prevNote != null)
{
alpha = 0.6;
multAlpha = 0.6;
if(ClientPrefs.downScroll) flipY = true;
offsetX += width / 2;
copyAngle = false;
switch (noteData)
{
case 0:
animation.play('purpleholdend');
case 1:
animation.play('blueholdend');
case 2:
animation.play('greenholdend');
case 3:
animation.play('redholdend');
}
updateHitbox();
offsetX -= width / 2;
if (PlayState.isPixelStage)
offsetX += 30;
if (prevNote.isSustainNote)
{
switch (prevNote.noteData)
{
case 0:
prevNote.animation.play('purplehold');
case 1:
prevNote.animation.play('bluehold');
case 2:
prevNote.animation.play('greenhold');
case 3:
prevNote.animation.play('redhold');
}
prevNote.scale.y *= Conductor.stepCrochet / 100 * 1.05 * PlayState.SONG.speed;
if(PlayState.isPixelStage) {
prevNote.scale.y *= 1.19;
}
prevNote.updateHitbox();
// prevNote.setGraphicSize();
}
if(PlayState.isPixelStage) {
scale.y *= PlayState.daPixelZoom;
updateHitbox();
}
} else if(!isSustainNote) {
earlyHitMult = 1;
}
x += offsetX;
}
function reloadNote(?prefix:String = '', ?texture:String = '', ?suffix:String = '') {
if(prefix == null) prefix = '';
if(texture == null) texture = '';
if(suffix == null) suffix = '';
var skin:String = texture;
if(texture.length < 1) {
skin = PlayState.SONG.arrowSkin;
if(skin == null || skin.length < 1) {
skin = 'NOTE_assets';
}
}
var animName:String = null;
if(animation.curAnim != null) {
animName = animation.curAnim.name;
}
var arraySkin:Array<String> = skin.split('/');
arraySkin[arraySkin.length-1] = prefix + arraySkin[arraySkin.length-1] + suffix;
var lastScaleY:Float = scale.y;
var blahblah:String = arraySkin.join('/');
if(PlayState.isPixelStage) {
if(isSustainNote) {
loadGraphic(Paths.image('pixelUI/' + blahblah + 'ENDS'));
width = width / 4;
height = height / 2;
loadGraphic(Paths.image('pixelUI/' + blahblah + 'ENDS'), true, Math.floor(width), Math.floor(height));
} else {
loadGraphic(Paths.image('pixelUI/' + blahblah));
width = width / 4;
height = height / 5;
loadGraphic(Paths.image('pixelUI/' + blahblah), true, Math.floor(width), Math.floor(height));
}
setGraphicSize(Std.int(width * PlayState.daPixelZoom));
loadPixelNoteAnims();
antialiasing = false;
} else {
frames = Paths.getSparrowAtlas(blahblah);
loadNoteAnims();
antialiasing = ClientPrefs.globalAntialiasing;
}
if(isSustainNote) {
scale.y = lastScaleY;
}
updateHitbox();
if(animName != null)
animation.play(animName, true);
if(inEditor) {
setGraphicSize(ChartingState.GRID_SIZE, ChartingState.GRID_SIZE);
updateHitbox();
}
}
function loadNoteAnims() {
animation.addByPrefix('greenScroll', 'green0');
animation.addByPrefix('redScroll', 'red0');
animation.addByPrefix('blueScroll', 'blue0');
animation.addByPrefix('purpleScroll', 'purple0');
if (isSustainNote)
{
animation.addByPrefix('purpleholdend', 'pruple end hold');
animation.addByPrefix('greenholdend', 'green hold end');
animation.addByPrefix('redholdend', 'red hold end');
animation.addByPrefix('blueholdend', 'blue hold end');
animation.addByPrefix('purplehold', 'purple hold piece');
animation.addByPrefix('greenhold', 'green hold piece');
animation.addByPrefix('redhold', 'red hold piece');
animation.addByPrefix('bluehold', 'blue hold piece');
}
setGraphicSize(Std.int(width * 0.7));
updateHitbox();
}
function loadPixelNoteAnims() {
if(isSustainNote) {
animation.add('purpleholdend', [PURP_NOTE + 4]);
animation.add('greenholdend', [GREEN_NOTE + 4]);
animation.add('redholdend', [RED_NOTE + 4]);
animation.add('blueholdend', [BLUE_NOTE + 4]);
animation.add('purplehold', [PURP_NOTE]);
animation.add('greenhold', [GREEN_NOTE]);
animation.add('redhold', [RED_NOTE]);
animation.add('bluehold', [BLUE_NOTE]);
} else {
animation.add('greenScroll', [GREEN_NOTE + 4]);
animation.add('redScroll', [RED_NOTE + 4]);
animation.add('blueScroll', [BLUE_NOTE + 4]);
animation.add('purpleScroll', [PURP_NOTE + 4]);
}
}
override function update(elapsed:Float)
{
super.update(elapsed);
if (mustPress)
{
// ok river
if (strumTime > Conductor.songPosition - Conductor.safeZoneOffset
&& strumTime < Conductor.songPosition + (Conductor.safeZoneOffset * earlyHitMult))
canBeHit = true;
else
canBeHit = false;
if (strumTime < Conductor.songPosition - Conductor.safeZoneOffset && !wasGoodHit)
tooLate = true;
}
else
{
canBeHit = false;
if (strumTime <= Conductor.songPosition)
wasGoodHit = true;
}
if (tooLate)
{
if (alpha > 0.3)
alpha = 0.3;
}
}
}

65
source/NoteSplash.hx Normal file
View file

@ -0,0 +1,65 @@
package;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
class NoteSplash extends FlxSprite
{
public var colorSwap:ColorSwap = null;
private var idleAnim:String;
private var textureLoaded:String = null;
public function new(x:Float = 0, y:Float = 0, ?note:Int = 0) {
super(x, y);
var skin:String = 'noteSplashes';
if(PlayState.SONG.splashSkin != null && PlayState.SONG.splashSkin.length > 0) skin = PlayState.SONG.splashSkin;
loadAnims(skin);
colorSwap = new ColorSwap();
shader = colorSwap.shader;
setupNoteSplash(x, y, note);
antialiasing = ClientPrefs.globalAntialiasing;
}
public function setupNoteSplash(x:Float, y:Float, note:Int = 0, texture:String = null, hueColor:Float = 0, satColor:Float = 0, brtColor:Float = 0) {
setPosition(x - Note.swagWidth * 0.95, y - Note.swagWidth);
alpha = 0.6;
if(texture == null) {
texture = 'noteSplashes';
if(PlayState.SONG.splashSkin != null && PlayState.SONG.splashSkin.length > 0) texture = PlayState.SONG.splashSkin;
}
if(textureLoaded != texture) {
loadAnims(texture);
}
colorSwap.hue = hueColor;
colorSwap.saturation = satColor;
colorSwap.brightness = brtColor;
offset.set(10, 10);
var animNum:Int = FlxG.random.int(1, 2);
animation.play('note' + note + '-' + animNum, true);
animation.curAnim.frameRate = 24 + FlxG.random.int(-2, 2);
}
function loadAnims(skin:String) {
frames = Paths.getSparrowAtlas(skin);
for (i in 1...3) {
animation.addByPrefix("note1-" + i, "note splash blue " + i, 24, false);
animation.addByPrefix("note2-" + i, "note splash green " + i, 24, false);
animation.addByPrefix("note0-" + i, "note splash purple " + i, 24, false);
animation.addByPrefix("note3-" + i, "note splash red " + i, 24, false);
}
}
override function update(elapsed:Float) {
if(animation.curAnim.finished) kill();
super.update(elapsed);
}
}

1115
source/OptionsState.hx Normal file

File diff suppressed because it is too large Load diff

54
source/OutdatedState.hx Normal file
View file

@ -0,0 +1,54 @@
package;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.FlxSubState;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import flixel.effects.FlxFlicker;
import lime.app.Application;
import flixel.addons.transition.FlxTransitionableState;
import flixel.tweens.FlxTween;
import flixel.util.FlxTimer;
class OutdatedState extends MusicBeatState
{
public static var leftState:Bool = false;
var warnText:FlxText;
override function create()
{
super.create();
var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK);
add(bg);
warnText = new FlxText(0, 0, FlxG.width,
"Sup bro, looks like you're running an \n
outdated version of Psych Engine (" + MainMenuState.psychEngineVersion + "),\n
please update to " + TitleState.updateVersion + "!\n
\n
Thank you for using the Engine!",
32);
warnText.setFormat("VCR OSD Mono", 32, FlxColor.WHITE, CENTER);
warnText.screenCenter(Y);
add(warnText);
}
override function update(elapsed:Float)
{
if(!leftState) {
if (controls.ACCEPT || controls.BACK) {
leftState = true;
CoolUtil.browserLoad("https://github.com/ShadowMario/FNF-PsychEngine/releases");
FlxG.sound.play(Paths.sound('cancelMenu'));
FlxTween.tween(warnText, {alpha: 0}, 1, {
onComplete: function (twn:FlxTween) {
MusicBeatState.switchState(new MainMenuState());
}
});
}
}
super.update(elapsed);
}
}

33
source/OverlayShader.hx Normal file
View file

@ -0,0 +1,33 @@
package;
import flixel.system.FlxAssets.FlxShader;
class OverlayShader extends FlxShader
{
@:glFragmentSource('
#pragma header
uniform vec4 uBlendColor;
vec3 blendLighten(base:Vec3, blend:Vec3) : Vec3 {
return mix(
1.0 - 2.0 * (1.0 - base) * (1.0 - blend),
2.0 * base * blend,
step( base, vec3(0.5) )
);
}
vec4 blendLighten(vec4 base, vec4 blend, float opacity)
{
return (blendLighten(base, blend) * opacity + base * (1.0 - opacity));
}
void main()
{
vec4 base = texture2D(bitmap, openfl_TextureCoordv);
gl_FragColor = blendLighten(base, uBlendColor, uBlendColor.a);
}')
public function new()
{
super();
}
}

367
source/Paths.hx Normal file
View file

@ -0,0 +1,367 @@
package;
import flixel.FlxG;
import flixel.graphics.frames.FlxAtlasFrames;
import openfl.utils.AssetType;
import openfl.utils.Assets as OpenFlAssets;
import lime.utils.Assets;
import flixel.FlxSprite;
#if MODS_ALLOWED
import sys.io.File;
import sys.FileSystem;
import flixel.graphics.FlxGraphic;
import openfl.display.BitmapData;
#end
import flash.media.Sound;
using StringTools;
class Paths
{
inline public static var SOUND_EXT = #if web "mp3" #else "ogg" #end;
inline public static var VIDEO_EXT = "mp4";
#if MODS_ALLOWED
#if (haxe >= "4.0.0")
public static var ignoreModFolders:Map<String, Bool> = new Map();
public static var customImagesLoaded:Map<String, Bool> = new Map();
public static var customSoundsLoaded:Map<String, Sound> = new Map();
#else
public static var ignoreModFolders:Map<String, Bool> = new Map<String, Bool>();
public static var customImagesLoaded:Map<String, Bool> = new Map<String, Bool>();
public static var customSoundsLoaded:Map<String, Sound> = new Map<String, Sound>();
#end
#end
public static function destroyLoadedImages(ignoreCheck:Bool = false) {
#if MODS_ALLOWED
if(!ignoreCheck && ClientPrefs.imagesPersist) return; //If there's 20+ images loaded, do a cleanup just for preventing a crash
for (key in customImagesLoaded.keys()) {
var graphic:FlxGraphic = FlxG.bitmap.get(key);
if(graphic != null) {
graphic.bitmap.dispose();
graphic.destroy();
FlxG.bitmap.removeByKey(key);
}
}
Paths.customImagesLoaded.clear();
#end
}
static public var currentModDirectory:String = null;
static var currentLevel:String;
static public function getModFolders()
{
#if MODS_ALLOWED
ignoreModFolders.set('characters', true);
ignoreModFolders.set('custom_events', true);
ignoreModFolders.set('custom_notetypes', true);
ignoreModFolders.set('data', true);
ignoreModFolders.set('songs', true);
ignoreModFolders.set('music', true);
ignoreModFolders.set('sounds', true);
ignoreModFolders.set('videos', true);
ignoreModFolders.set('images', true);
ignoreModFolders.set('stages', true);
ignoreModFolders.set('weeks', true);
#end
}
static public function setCurrentLevel(name:String)
{
currentLevel = name.toLowerCase();
}
public static function getPath(file:String, type:AssetType, ?library:Null<String> = null)
{
if (library != null)
return getLibraryPath(file, library);
if (currentLevel != null)
{
var levelPath:String = '';
if(currentLevel != 'shared') {
levelPath = getLibraryPathForce(file, currentLevel);
if (OpenFlAssets.exists(levelPath, type))
return levelPath;
}
levelPath = getLibraryPathForce(file, "shared");
if (OpenFlAssets.exists(levelPath, type))
return levelPath;
}
return getPreloadPath(file);
}
static public function getLibraryPath(file:String, library = "preload")
{
return if (library == "preload" || library == "default") getPreloadPath(file); else getLibraryPathForce(file, library);
}
inline static function getLibraryPathForce(file:String, library:String)
{
return '$library:assets/$library/$file';
}
inline public static function getPreloadPath(file:String = '')
{
return 'assets/$file';
}
inline static public function file(file:String, type:AssetType = TEXT, ?library:String)
{
return getPath(file, type, library);
}
inline static public function txt(key:String, ?library:String)
{
return getPath('data/$key.txt', TEXT, library);
}
inline static public function xml(key:String, ?library:String)
{
return getPath('data/$key.xml', TEXT, library);
}
inline static public function json(key:String, ?library:String)
{
return getPath('data/$key.json', TEXT, library);
}
inline static public function lua(key:String, ?library:String)
{
return getPath('$key.lua', TEXT, library);
}
static public function video(key:String)
{
#if MODS_ALLOWED
var file:String = modsVideo(key);
if(FileSystem.exists(file)) {
return file;
}
#end
return 'assets/videos/$key.$VIDEO_EXT';
}
static public function sound(key:String, ?library:String):Dynamic
{
#if MODS_ALLOWED
var file:String = modsSounds(key);
if(FileSystem.exists(file)) {
if(!customSoundsLoaded.exists(file)) {
customSoundsLoaded.set(file, Sound.fromFile(file));
}
return customSoundsLoaded.get(file);
}
#end
return getPath('sounds/$key.$SOUND_EXT', SOUND, library);
}
inline static public function soundRandom(key:String, min:Int, max:Int, ?library:String)
{
return sound(key + FlxG.random.int(min, max), library);
}
inline static public function music(key:String, ?library:String):Dynamic
{
#if MODS_ALLOWED
var file:String = modsMusic(key);
if(FileSystem.exists(file)) {
if(!customSoundsLoaded.exists(file)) {
customSoundsLoaded.set(file, Sound.fromFile(file));
}
return customSoundsLoaded.get(file);
}
#end
return getPath('music/$key.$SOUND_EXT', MUSIC, library);
}
inline static public function voices(song:String):Any
{
#if MODS_ALLOWED
var file:Sound = returnSongFile(modsSongs(song.toLowerCase().replace(' ', '-') + '/Voices'));
if(file != null) {
return file;
}
#end
return 'songs:assets/songs/${song.toLowerCase().replace(' ', '-')}/Voices.$SOUND_EXT';
}
inline static public function inst(song:String):Any
{
#if MODS_ALLOWED
var file:Sound = returnSongFile(modsSongs(song.toLowerCase().replace(' ', '-') + '/Inst'));
if(file != null) {
return file;
}
#end
return 'songs:assets/songs/${song.toLowerCase().replace(' ', '-')}/Inst.$SOUND_EXT';
}
#if MODS_ALLOWED
inline static private function returnSongFile(file:String):Sound
{
if(FileSystem.exists(file)) {
if(!customSoundsLoaded.exists(file)) {
customSoundsLoaded.set(file, Sound.fromFile(file));
}
return customSoundsLoaded.get(file);
}
return null;
}
#end
inline static public function image(key:String, ?library:String):Dynamic
{
#if MODS_ALLOWED
var imageToReturn:FlxGraphic = addCustomGraphic(key);
if(imageToReturn != null) return imageToReturn;
#end
return getPath('images/$key.png', IMAGE, library);
}
static public function getTextFromFile(key:String, ?ignoreMods:Bool = false):String
{
#if sys
if (!ignoreMods && FileSystem.exists(mods(key)))
return File.getContent(mods(key));
if (FileSystem.exists(getPreloadPath(key)))
return File.getContent(getPreloadPath(key));
if (currentLevel != null)
{
var levelPath:String = '';
if(currentLevel != 'shared') {
levelPath = getLibraryPathForce(key, currentLevel);
if (FileSystem.exists(levelPath))
return File.getContent(levelPath);
}
levelPath = getLibraryPathForce(key, 'shared');
if (FileSystem.exists(levelPath))
return File.getContent(levelPath);
}
#end
return Assets.getText(getPath(key, TEXT));
}
inline static public function font(key:String)
{
return 'assets/fonts/$key';
}
inline static public function fileExists(key:String, type:AssetType, ?ignoreMods:Bool = false, ?library:String)
{
#if MODS_ALLOWED
if(FileSystem.exists(mods(currentModDirectory + '/' + key)) || FileSystem.exists(mods(key))) {
return true;
}
#end
if(OpenFlAssets.exists(Paths.getPath(key, type))) {
return true;
}
return false;
}
inline static public function getSparrowAtlas(key:String, ?library:String)
{
#if MODS_ALLOWED
var imageLoaded:FlxGraphic = addCustomGraphic(key);
var xmlExists:Bool = false;
if(FileSystem.exists(modsXml(key))) {
xmlExists = true;
}
return FlxAtlasFrames.fromSparrow((imageLoaded != null ? imageLoaded : image(key, library)), (xmlExists ? File.getContent(modsXml(key)) : file('images/$key.xml', library)));
#else
return FlxAtlasFrames.fromSparrow(image(key, library), file('images/$key.xml', library));
#end
}
inline static public function getPackerAtlas(key:String, ?library:String)
{
#if MODS_ALLOWED
var imageLoaded:FlxGraphic = addCustomGraphic(key);
var txtExists:Bool = false;
if(FileSystem.exists(modsTxt(key))) {
txtExists = true;
}
return FlxAtlasFrames.fromSpriteSheetPacker((imageLoaded != null ? imageLoaded : image(key, library)), (txtExists ? File.getContent(modsTxt(key)) : file('images/$key.txt', library)));
#else
return FlxAtlasFrames.fromSpriteSheetPacker(image(key, library), file('images/$key.txt', library));
#end
}
inline static public function formatToSongPath(path:String) {
return path.toLowerCase().replace(' ', '-');
}
#if MODS_ALLOWED
static public function addCustomGraphic(key:String):FlxGraphic {
if(FileSystem.exists(modsImages(key))) {
if(!customImagesLoaded.exists(key)) {
var newBitmap:BitmapData = BitmapData.fromFile(modsImages(key));
var newGraphic:FlxGraphic = FlxGraphic.fromBitmapData(newBitmap, false, key);
newGraphic.persist = true;
FlxG.bitmap.addGraphic(newGraphic);
customImagesLoaded.set(key, true);
}
return FlxG.bitmap.get(key);
}
return null;
}
inline static public function mods(key:String = '') {
return 'mods/' + key;
}
inline static public function modsJson(key:String) {
return modFolders('data/' + key + '.json');
}
inline static public function modsVideo(key:String) {
return modFolders('videos/' + key + '.' + VIDEO_EXT);
}
inline static public function modsMusic(key:String) {
return modFolders('music/' + key + '.' + SOUND_EXT);
}
inline static public function modsSounds(key:String) {
return modFolders('sounds/' + key + '.' + SOUND_EXT);
}
inline static public function modsSongs(key:String) {
return modFolders('songs/' + key + '.' + SOUND_EXT);
}
inline static public function modsImages(key:String) {
return modFolders('images/' + key + '.png');
}
inline static public function modsXml(key:String) {
return modFolders('images/' + key + '.xml');
}
inline static public function modsTxt(key:String) {
return modFolders('images/' + key + '.txt');
}
static public function modFolders(key:String) {
if(currentModDirectory != null && currentModDirectory.length > 0) {
var fileToCheck:String = mods(currentModDirectory + '/' + key);
if(FileSystem.exists(fileToCheck)) {
return fileToCheck;
}
}
return 'mods/' + key;
}
#end
}

247
source/PauseSubState.hx Normal file
View file

@ -0,0 +1,247 @@
package;
import Controls.Control;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.FlxSubState;
import flixel.addons.transition.FlxTransitionableState;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.input.keyboard.FlxKey;
import flixel.system.FlxSound;
import flixel.text.FlxText;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import flixel.FlxCamera;
class PauseSubState extends MusicBeatSubstate
{
var grpMenuShit:FlxTypedGroup<Alphabet>;
var menuItems:Array<String> = [];
var menuItemsOG:Array<String> = ['Resume', 'Restart Song', 'Change Difficulty', 'Toggle Practice Mode', 'Botplay', 'Exit to menu'];
var difficultyChoices = [];
var curSelected:Int = 0;
var pauseMusic:FlxSound;
var practiceText:FlxText;
var botplayText:FlxText;
public static var transCamera:FlxCamera;
public function new(x:Float, y:Float)
{
super();
menuItems = menuItemsOG;
for (i in 0...CoolUtil.difficultyStuff.length) {
var diff:String = '' + CoolUtil.difficultyStuff[i][0];
difficultyChoices.push(diff);
}
difficultyChoices.push('BACK');
pauseMusic = new FlxSound().loadEmbedded(Paths.music('breakfast'), true, true);
pauseMusic.volume = 0;
pauseMusic.play(false, FlxG.random.int(0, Std.int(pauseMusic.length / 2)));
FlxG.sound.list.add(pauseMusic);
var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK);
bg.alpha = 0;
bg.scrollFactor.set();
add(bg);
var levelInfo:FlxText = new FlxText(20, 15, 0, "", 32);
levelInfo.text += PlayState.SONG.song;
levelInfo.scrollFactor.set();
levelInfo.setFormat(Paths.font("vcr.ttf"), 32);
levelInfo.updateHitbox();
add(levelInfo);
var levelDifficulty:FlxText = new FlxText(20, 15 + 32, 0, "", 32);
levelDifficulty.text += CoolUtil.difficultyString();
levelDifficulty.scrollFactor.set();
levelDifficulty.setFormat(Paths.font('vcr.ttf'), 32);
levelDifficulty.updateHitbox();
add(levelDifficulty);
var blueballedTxt:FlxText = new FlxText(20, 15 + 64, 0, "", 32);
blueballedTxt.text = "Blueballed: " + PlayState.deathCounter;
blueballedTxt.scrollFactor.set();
blueballedTxt.setFormat(Paths.font('vcr.ttf'), 32);
blueballedTxt.updateHitbox();
add(blueballedTxt);
practiceText = new FlxText(20, 15 + 101, 0, "PRACTICE MODE", 32);
practiceText.scrollFactor.set();
practiceText.setFormat(Paths.font('vcr.ttf'), 32);
practiceText.x = FlxG.width - (practiceText.width + 20);
practiceText.updateHitbox();
practiceText.visible = PlayState.practiceMode;
add(practiceText);
botplayText = new FlxText(20, FlxG.height - 40, 0, "BOTPLAY", 32);
botplayText.scrollFactor.set();
botplayText.setFormat(Paths.font('vcr.ttf'), 32);
botplayText.x = FlxG.width - (botplayText.width + 20);
botplayText.updateHitbox();
botplayText.visible = PlayState.cpuControlled;
add(botplayText);
blueballedTxt.alpha = 0;
levelDifficulty.alpha = 0;
levelInfo.alpha = 0;
levelInfo.x = FlxG.width - (levelInfo.width + 20);
levelDifficulty.x = FlxG.width - (levelDifficulty.width + 20);
blueballedTxt.x = FlxG.width - (blueballedTxt.width + 20);
FlxTween.tween(bg, {alpha: 0.6}, 0.4, {ease: FlxEase.quartInOut});
FlxTween.tween(levelInfo, {alpha: 1, y: 20}, 0.4, {ease: FlxEase.quartInOut, startDelay: 0.3});
FlxTween.tween(levelDifficulty, {alpha: 1, y: levelDifficulty.y + 5}, 0.4, {ease: FlxEase.quartInOut, startDelay: 0.5});
FlxTween.tween(blueballedTxt, {alpha: 1, y: blueballedTxt.y + 5}, 0.4, {ease: FlxEase.quartInOut, startDelay: 0.7});
grpMenuShit = new FlxTypedGroup<Alphabet>();
add(grpMenuShit);
for (i in 0...menuItems.length)
{
var songText:Alphabet = new Alphabet(0, (70 * i) + 30, menuItems[i], true, false);
songText.isMenuItem = true;
songText.targetY = i;
grpMenuShit.add(songText);
}
changeSelection();
cameras = [FlxG.cameras.list[FlxG.cameras.list.length - 1]];
}
override function update(elapsed:Float)
{
if (pauseMusic.volume < 0.5)
pauseMusic.volume += 0.01 * elapsed;
super.update(elapsed);
var upP = controls.UI_UP_P;
var downP = controls.UI_DOWN_P;
var accepted = controls.ACCEPT;
if (upP)
{
changeSelection(-1);
}
if (downP)
{
changeSelection(1);
}
if (accepted)
{
var daSelected:String = menuItems[curSelected];
for (i in 0...difficultyChoices.length-1) {
if(difficultyChoices[i] == daSelected) {
var name:String = PlayState.SONG.song.toLowerCase();
var poop = Highscore.formatSong(name, curSelected);
PlayState.SONG = Song.loadFromJson(poop, name);
PlayState.storyDifficulty = curSelected;
CustomFadeTransition.nextCamera = transCamera;
MusicBeatState.resetState();
FlxG.sound.music.volume = 0;
PlayState.changedDifficulty = true;
PlayState.cpuControlled = false;
return;
}
}
switch (daSelected)
{
case "Resume":
close();
case 'Change Difficulty':
menuItems = difficultyChoices;
regenMenu();
case 'Toggle Practice Mode':
PlayState.practiceMode = !PlayState.practiceMode;
PlayState.usedPractice = true;
practiceText.visible = PlayState.practiceMode;
case "Restart Song":
CustomFadeTransition.nextCamera = transCamera;
MusicBeatState.resetState();
FlxG.sound.music.volume = 0;
case 'Botplay':
PlayState.cpuControlled = !PlayState.cpuControlled;
PlayState.usedPractice = true;
botplayText.visible = PlayState.cpuControlled;
case "Exit to menu":
PlayState.deathCounter = 0;
PlayState.seenCutscene = false;
CustomFadeTransition.nextCamera = transCamera;
if(PlayState.isStoryMode) {
MusicBeatState.switchState(new StoryMenuState());
} else {
MusicBeatState.switchState(new FreeplayState());
}
FlxG.sound.playMusic(Paths.music('freakyMenu'));
PlayState.usedPractice = false;
PlayState.changedDifficulty = false;
PlayState.cpuControlled = false;
case 'BACK':
menuItems = menuItemsOG;
regenMenu();
}
}
}
override function destroy()
{
pauseMusic.destroy();
super.destroy();
}
function changeSelection(change:Int = 0):Void
{
curSelected += change;
FlxG.sound.play(Paths.sound('scrollMenu'), 0.4);
if (curSelected < 0)
curSelected = menuItems.length - 1;
if (curSelected >= menuItems.length)
curSelected = 0;
var bullShit:Int = 0;
for (item in grpMenuShit.members)
{
item.targetY = bullShit - curSelected;
bullShit++;
item.alpha = 0.6;
// item.setGraphicSize(Std.int(item.width * 0.8));
if (item.targetY == 0)
{
item.alpha = 1;
// item.setGraphicSize(Std.int(item.width));
}
}
}
function regenMenu():Void {
for (i in 0...grpMenuShit.members.length) {
this.grpMenuShit.remove(this.grpMenuShit.members[0], true);
}
for (i in 0...menuItems.length) {
var item = new Alphabet(0, 70 * i + 30, menuItems[i], true, false);
item.isMenuItem = true;
item.targetY = i;
grpMenuShit.add(item);
}
curSelected = 0;
changeSelection();
}
}

4014
source/PlayState.hx Normal file

File diff suppressed because it is too large Load diff

160
source/PlayerSettings.hx Normal file
View file

@ -0,0 +1,160 @@
package;
import Controls;
import flixel.FlxCamera;
import flixel.FlxG;
import flixel.util.FlxSignal;
// import ui.DeviceManager;
// import props.Player;
class PlayerSettings
{
static public var numPlayers(default, null) = 0;
static public var numAvatars(default, null) = 0;
static public var player1(default, null):PlayerSettings;
static public var player2(default, null):PlayerSettings;
#if (haxe >= "4.0.0")
static public final onAvatarAdd = new FlxTypedSignal<PlayerSettings->Void>();
static public final onAvatarRemove = new FlxTypedSignal<PlayerSettings->Void>();
#else
static public var onAvatarAdd = new FlxTypedSignal<PlayerSettings->Void>();
static public var onAvatarRemove = new FlxTypedSignal<PlayerSettings->Void>();
#end
public var id(default, null):Int;
#if (haxe >= "4.0.0")
public final controls:Controls;
#else
public var controls:Controls;
#end
// public var avatar:Player;
// public var camera(get, never):PlayCamera;
function new(id, scheme)
{
this.id = id;
this.controls = new Controls('player$id', scheme);
}
public function setKeyboardScheme(scheme)
{
controls.setKeyboardScheme(scheme);
}
/*
static public function addAvatar(avatar:Player):PlayerSettings
{
var settings:PlayerSettings;
if (player1 == null)
{
player1 = new PlayerSettings(0, Solo);
++numPlayers;
}
if (player1.avatar == null)
settings = player1;
else
{
if (player2 == null)
{
if (player1.controls.keyboardScheme.match(Duo(true)))
player2 = new PlayerSettings(1, Duo(false));
else
player2 = new PlayerSettings(1, None);
++numPlayers;
}
if (player2.avatar == null)
settings = player2;
else
throw throw 'Invalid number of players: ${numPlayers + 1}';
}
++numAvatars;
settings.avatar = avatar;
avatar.settings = settings;
splitCameras();
onAvatarAdd.dispatch(settings);
return settings;
}
static public function removeAvatar(avatar:Player):Void
{
var settings:PlayerSettings;
if (player1 != null && player1.avatar == avatar)
settings = player1;
else if (player2 != null && player2.avatar == avatar)
{
settings = player2;
if (player1.controls.keyboardScheme.match(Duo(_)))
player1.setKeyboardScheme(Solo);
}
else
throw "Cannot remove avatar that is not for a player";
settings.avatar = null;
while (settings.controls.gamepadsAdded.length > 0)
{
final id = settings.controls.gamepadsAdded.shift();
settings.controls.removeGamepad(id);
DeviceManager.releaseGamepad(FlxG.gamepads.getByID(id));
}
--numAvatars;
splitCameras();
onAvatarRemove.dispatch(avatar.settings);
}
*/
static public function init():Void
{
if (player1 == null)
{
player1 = new PlayerSettings(0, Solo);
++numPlayers;
}
var numGamepads = FlxG.gamepads.numActiveGamepads;
if (numGamepads > 0)
{
var gamepad = FlxG.gamepads.getByID(0);
if (gamepad == null)
throw 'Unexpected null gamepad. id:0';
player1.controls.addDefaultGamepad(0);
}
if (numGamepads > 1)
{
if (player2 == null)
{
player2 = new PlayerSettings(1, None);
++numPlayers;
}
var gamepad = FlxG.gamepads.getByID(1);
if (gamepad == null)
throw 'Unexpected null gamepad. id:0';
player2.controls.addDefaultGamepad(1);
}
// DeviceManager.init();
}
static public function reset()
{
player1 = null;
player2 = null;
numPlayers = 0;
}
}

View file

@ -0,0 +1,117 @@
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.FlxSubState;
import flixel.util.FlxColor;
using StringTools;
class ResetScoreSubState extends MusicBeatSubstate
{
var bg:FlxSprite;
var alphabetArray:Array<Alphabet> = [];
var icon:HealthIcon;
var onYes:Bool = false;
var yesText:Alphabet;
var noText:Alphabet;
var song:String;
var difficulty:Int;
var week:Int;
// Week -1 = Freeplay
public function new(song:String, difficulty:Int, character:String, week:Int = -1)
{
this.song = song;
this.difficulty = difficulty;
this.week = week;
super();
var name:String = song;
if(week > -1) {
name = WeekData.weeksLoaded.get(WeekData.weeksList[week]).weekName;
}
name += ' (' + CoolUtil.difficultyStuff[difficulty][0] + ')?';
bg = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK);
bg.alpha = 0;
bg.scrollFactor.set();
add(bg);
var tooLong:Float = (name.length > 18) ? 0.8 : 1; //Fucking Winter Horrorland
var text:Alphabet = new Alphabet(0, 180, "Reset the score of", true);
text.screenCenter(X);
alphabetArray.push(text);
text.alpha = 0;
add(text);
var text:Alphabet = new Alphabet(0, text.y + 90, name, true, false, 0.05, tooLong);
text.screenCenter(X);
if(week == -1) text.x += 60 * tooLong;
alphabetArray.push(text);
text.alpha = 0;
add(text);
if(week == -1) {
icon = new HealthIcon(character);
icon.setGraphicSize(Std.int(icon.width * tooLong));
icon.updateHitbox();
icon.setPosition(text.x - icon.width + (10 * tooLong), text.y - 30);
icon.alpha = 0;
add(icon);
}
yesText = new Alphabet(0, text.y + 150, 'Yes', true);
yesText.screenCenter(X);
yesText.x -= 200;
add(yesText);
noText = new Alphabet(0, text.y + 150, 'No', true);
noText.screenCenter(X);
noText.x += 200;
add(noText);
updateOptions();
}
override function update(elapsed:Float)
{
bg.alpha += elapsed * 1.5;
if(bg.alpha > 0.6) bg.alpha = 0.6;
for (i in 0...alphabetArray.length) {
var spr = alphabetArray[i];
spr.alpha += elapsed * 2.5;
}
if(week == -1) icon.alpha += elapsed * 2.5;
if(controls.UI_LEFT_P || controls.UI_RIGHT_P) {
FlxG.sound.play(Paths.sound('scrollMenu'), 1);
onYes = !onYes;
updateOptions();
}
if(controls.BACK) {
FlxG.sound.play(Paths.sound('cancelMenu'), 1);
close();
} else if(controls.ACCEPT) {
if(onYes) {
if(week == -1) {
Highscore.resetSong(song, difficulty);
} else {
Highscore.resetWeek(WeekData.weeksList[week], difficulty);
}
}
FlxG.sound.play(Paths.sound('cancelMenu'), 1);
close();
}
super.update(elapsed);
}
function updateOptions() {
var scales:Array<Float> = [0.75, 1];
var alphas:Array<Float> = [0.6, 1.25];
var confirmInt:Int = onYes ? 1 : 0;
yesText.alpha = alphas[confirmInt];
yesText.scale.set(scales[confirmInt], scales[confirmInt]);
noText.alpha = alphas[1 - confirmInt];
noText.scale.set(scales[1 - confirmInt], scales[1 - confirmInt]);
if(week == -1) icon.animation.curAnim.curFrame = confirmInt;
}
}

31
source/Section.hx Normal file
View file

@ -0,0 +1,31 @@
package;
typedef SwagSection =
{
var sectionNotes:Array<Dynamic>;
var lengthInSteps:Int;
var typeOfSection:Int;
var mustHitSection:Bool;
var bpm:Float;
var changeBPM:Bool;
var altAnim:Bool;
}
class Section
{
public var sectionNotes:Array<Dynamic> = [];
public var lengthInSteps:Int = 16;
public var typeOfSection:Int = 0;
public var mustHitSection:Bool = true;
/**
* Copies the first section into the second section!
*/
public static var COPYCAT:Int = 0;
public function new(lengthInSteps:Int = 16)
{
this.lengthInSteps = lengthInSteps;
}
}

1288
source/Snd.hx Normal file

File diff suppressed because it is too large Load diff

332
source/SndTV.hx Normal file
View file

@ -0,0 +1,332 @@
import h2d.Tweenie.TType;
//praise delahee, i'll figure out what this shit means later!
enum TVVar{
TVVVolume;
TVVPan;
}
@:publicFields
class TweenV {
static var GUID = 0;
var uid = 0;
var man : SndTV;
var parent : Snd;
var n : Float;
var ln : Float;
var speed : Float;
var from : Float;
var to : Float;
var type : TType;
var plays : Int; // -1 = infini, 1 et plus = nombre d'exécutions (1 par défaut)
var varType : TVVar;
var onUpdate : Null<TweenV->Void>;
var onEnd : Null<TweenV->Void>;
var isDebug = false;
public inline function new (
parent:Snd ,
n:Float ,
ln:Float ,
varType:TVVar,
speed:Float ,
from:Float ,
to:Float ,
type:h2d.Tweenie.TType ,
plays ,
onUpdate ,
onEnd
) {
this.parent = parent ;
this.n = n ;
this.ln = ln ;
this.varType = varType ;
this.speed = speed ;
this.from = from ;
this.to = to ;
this.type = type ;
this.plays = plays ;
this.onUpdate = onUpdate ;
this.onEnd = onEnd ;
}
public inline function reset(
parent:Snd ,
n:Float ,
ln:Float ,
varType:TVVar,
speed:Float ,
from:Float ,
to:Float ,
type:TType ,
plays:Int ,
onUpdate ,
onEnd
) {
this.parent = parent ;
this.n = n ;
this.ln = ln ;
this.speed = speed ;
this.from = from ;
this.to = to ;
this.type = type ;
this.plays = plays ;
this.onUpdate = onUpdate ;
this.onEnd = onEnd ;
this.varType = varType ;
isDebug = false;
uid = GUID++;
}
public function clear(){
n = 0.0;
ln = 0.0;
speed = 0.0;
plays = 0;
from = 0.0;
to = 0.0;
parent = null;
onEnd = null;
onUpdate = null;
isDebug = false;
uid = GUID++;
}
public
inline
function apply( val ) {
switch(varType){
case TVVVolume: {
parent.volume = val;
#if debug
if( isDebug )
trace("tv:" + val);
#end
}
case TVVPan: parent.pan = val;
}
}
public inline function kill( withCbk = true ) {
if ( withCbk )
man.terminateTween( this );
else
man.forceTerminateTween( this) ;
}
}
/**
* tween order is not respected
*/
class SndTV {
static var DEFAULT_DURATION = DateTools.seconds(1);
public var fps = 60.0;
public var isDebug = false;
var tlist : hxd.Stack<TweenV>;
public function new() {
tlist = new hxd.Stack<TweenV>();
tlist.reserve(8);
}
function onError(e) {
trace(e);
}
public function count() {
return tlist.length;
}
public inline function create(parent:Snd, vartype:TVVar, to:Float, ?tp:h2d.Tweenie.TType, ?duration_ms:Float) : TweenV{
return create_(parent, vartype, to, tp, duration_ms);
}
public function exists(p:Snd) {
for (t in tlist)
if (t.parent == p )
return true;
return false;
}
public var pool : hxd.Stack<TweenV> = new hxd.Stack();
function create_(p:Snd, vartype:TVVar,to:Float, ?tp:h2d.Tweenie.TType, ?duration_ms:Float) : TweenV{
if ( duration_ms==null )
duration_ms = DEFAULT_DURATION;
#if debug
if ( p == null ) trace("tween2 creation failed to:"+to+" tp:"+tp);
#end
if ( tp==null ) tp = TEase;
{
// on supprime les tweens précédents appliqués à la même variable
for(t in tlist.backWardIterator())
if(t.parent==p && t.varType == vartype) {
forceTerminateTween(t);
}
}
var from = switch( vartype ){
case TVVVolume : p.volume;
case TVVPan : p.pan;
}
var t : TweenV;
if (pool.length == 0){
t = new TweenV(
p,
0.0,
0.0,
vartype,
1 / ( duration_ms*fps/1000 ), // une seconde
from,
to,
tp,
1,
null,
null
);
}
else {
t = pool.pop();
t.reset(
p,
0.0,
0.0,
vartype,
1 / ( duration_ms*fps/1000 ), // une seconde
from,
to,
tp,
1,
null,
null
);
}
if( t.from==t.to )
t.ln = 1; // tweening inutile : mais on s'assure ainsi qu'un update() et un end() seront bien appelés
t.man = this;
tlist.push(t);
return t;
}
public static inline
function fastPow2(n:Float):Float {
return n*n;
}
public static inline
function fastPow3(n:Float):Float {
return n*n*n;
}
public static inline
function bezier(t:Float, p0:Float, p1:Float,p2:Float, p3:Float) {
return
fastPow3(1-t)*p0 +
3*( t*fastPow2(1-t)*p1 + fastPow2(t)*(1-t)*p2 ) +
fastPow3(t)*p3;
}
// suppression du tween sans aucun appel aux callbacks onUpdate, onUpdateT et onEnd (!)
public function killWithoutCallbacks(parent:Snd) {
for (t in tlist.backWardIterator())
if (t.parent==parent ){
forceTerminateTween(t);
return true;
}
return false;
}
public function terminate(parent:Snd) {
for (t in tlist.backWardIterator())
if (t.parent==parent){
forceTerminateTween(t);
}
}
public function forceTerminateTween(t:TweenV) {
var tOk = tlist.remove(t);
if( tOk ){
t.clear();
pool.push(t);
}
}
public function terminateTween(t:TweenV, ?fl_allowLoop=false) {
var v = t.from + (t.to - t.from) * h2d.Tweenie.interp(t.type, 1);
t.apply(v);
onUpdate(t, 1);
var ouid = t.uid;
onEnd(t);
if( ouid == t.uid ){
if( fl_allowLoop && (t.plays==-1 || t.plays>1) ) {
if( t.plays!=-1 )
t.plays--;
t.n = t.ln = 0;
}
else {
forceTerminateTween(t);
}
}
}
public function terminateAll() {
for(t in tlist)
t.ln = 1;
update();
}
inline
function onUpdate(t:TweenV, n:Float) {
if ( t.onUpdate!=null )
t.onUpdate(t);
}
inline
function onEnd(t:TweenV) {
if ( t.onEnd!=null )
t.onEnd(t);
}
public function update(?tmod = 1.0) {
if ( tlist.length > 0 ) {
for (t in tlist.backWardIterator() ) {
var dist = t.to-t.from;
if (t.type==TRand)
t.ln+=if(Std.random(100)<33) t.speed * tmod else 0;
else
t.ln += t.speed * tmod;
t.n = h2d.Tweenie.interp(t.type, t.ln);
if ( t.ln<1 ) {
// en cours...
var val = t.from + t.n*dist;
t.apply(val);
onUpdate(t, t.ln);
}
else // fini !
{
terminateTween(t, true);
}
}
}
}
}

109
source/Song.hx Normal file
View file

@ -0,0 +1,109 @@
package;
import Section.SwagSection;
import haxe.Json;
import haxe.format.JsonParser;
import lime.utils.Assets;
#if sys
import sys.io.File;
import sys.FileSystem;
#end
using StringTools;
typedef SwagSong =
{
var song:String;
var notes:Array<SwagSection>;
var bpm:Float;
var needsVoices:Bool;
var speed:Float;
var player1:String;
var player2:String;
var player3:String;
var stage:String;
var arrowSkin:String;
var splashSkin:String;
var validScore:Bool;
}
class Song
{
public var song:String;
public var notes:Array<SwagSection>;
public var bpm:Float;
public var needsVoices:Bool = true;
public var arrowSkin:String;
public var splashSkin:String;
public var speed:Float = 1;
public var stage:String;
public var player1:String = 'bf';
public var player2:String = 'dad';
public var player3:String = 'gf';
public function new(song, notes, bpm)
{
this.song = song;
this.notes = notes;
this.bpm = bpm;
}
public static function loadFromJson(jsonInput:String, ?folder:String):SwagSong
{
var rawJson = null;
var formattedFolder:String = Paths.formatToSongPath(folder);
var formattedSong:String = Paths.formatToSongPath(jsonInput);
#if MODS_ALLOWED
var moddyFile:String = Paths.modsJson(formattedFolder + '/' + formattedSong);
if(FileSystem.exists(moddyFile)) {
rawJson = File.getContent(moddyFile).trim();
}
#end
if(rawJson == null) {
#if sys
rawJson = File.getContent(Paths.json(formattedFolder + '/' + formattedSong)).trim();
#else
rawJson = Assets.getText(Paths.json(formattedFolder + '/' + formattedSong)).trim();
#end
}
while (!rawJson.endsWith("}"))
{
rawJson = rawJson.substr(0, rawJson.length - 1);
// LOL GOING THROUGH THE BULLSHIT TO CLEAN IDK WHATS STRANGE
}
// FIX THE CASTING ON WINDOWS/NATIVE
// Windows???
// trace(songData);
// trace('LOADED FROM JSON: ' + songData.notes);
/*
for (i in 0...songData.notes.length)
{
trace('LOADED FROM JSON: ' + songData.notes[i].sectionNotes);
// songData.notes[i].sectionNotes = songData.notes[i].sectionNotes
}
daNotes = songData.notes;
daSong = songData.song;
daBpm = songData.bpm; */
var songJson:SwagSong = parseJSONshit(rawJson);
if(jsonInput != 'events') StageData.loadDirectory(songJson);
return songJson;
}
public static function parseJSONshit(rawJson:String):SwagSong
{
var swagShit:SwagSong = cast Json.parse(rawJson).song;
swagShit.validScore = true;
return swagShit;
}
}

85
source/StageData.hx Normal file
View file

@ -0,0 +1,85 @@
package;
#if MODS_ALLOWED
import sys.io.File;
import sys.FileSystem;
#else
import openfl.utils.Assets;
#end
import haxe.Json;
import haxe.format.JsonParser;
import Song;
using StringTools;
typedef StageFile = {
var directory:String;
var defaultZoom:Float;
var isPixelStage:Bool;
var boyfriend:Array<Dynamic>;
var girlfriend:Array<Dynamic>;
var opponent:Array<Dynamic>;
}
class StageData {
public static var forceNextDirectory:String = null;
public static function loadDirectory(SONG:SwagSong) {
var stage:String = '';
if(SONG.stage != null) {
stage = SONG.stage;
} else if(SONG.song != null) {
switch (SONG.song.toLowerCase().replace(' ', '-'))
{
case 'spookeez' | 'south' | 'monster':
stage = 'spooky';
case 'pico' | 'blammed' | 'philly' | 'philly-nice':
stage = 'philly';
case 'milf' | 'satin-panties' | 'high':
stage = 'limo';
case 'cocoa' | 'eggnog':
stage = 'mall';
case 'winter-horrorland':
stage = 'mallEvil';
case 'senpai' | 'roses':
stage = 'school';
case 'thorns':
stage = 'schoolEvil';
default:
stage = 'stage';
}
} else {
stage = 'stage';
}
var stageFile:StageFile = getStageFile(stage);
if(stageFile == null) { //preventing crashes
forceNextDirectory = '';
} else {
forceNextDirectory = stageFile.directory;
}
}
public static function getStageFile(stage:String):StageFile {
var rawJson:String = null;
var path:String = Paths.getPreloadPath('stages/' + stage + '.json');
#if MODS_ALLOWED
var modPath:String = Paths.modFolders('stages/' + stage + '.json');
if(FileSystem.exists(modPath)) {
rawJson = File.getContent(modPath);
} else if(FileSystem.exists(path)) {
rawJson = File.getContent(path);
}
#else
if(Assets.exists(path)) {
rawJson = Assets.getText(path);
}
#end
else
{
return null;
}
return cast Json.parse(rawJson);
}
}

405
source/StoryMenuState.hx Normal file
View file

@ -0,0 +1,405 @@
package;
#if desktop
import Discord.DiscordClient;
#end
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.FlxSubState;
import flixel.addons.transition.FlxTransitionableState;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.group.FlxGroup;
import flixel.math.FlxMath;
import flixel.text.FlxText;
import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
import lime.net.curl.CURLCode;
import WeekData;
using StringTools;
class StoryMenuState extends MusicBeatState
{
// Wether you have to beat the previous week for playing this one
// Not recommended, as people usually download your mod for, you know,
// playing just the modded week then delete it.
// defaults to True
public static var weekCompleted:Map<String, Bool> = new Map<String, Bool>();
var scoreText:FlxText;
private static var curDifficulty:Int = 1;
var txtWeekTitle:FlxText;
var bgSprite:FlxSprite;
private static var curWeek:Int = 0;
var txtTracklist:FlxText;
var grpWeekText:FlxTypedGroup<MenuItem>;
var grpWeekCharacters:FlxTypedGroup<MenuCharacter>;
var grpLocks:FlxTypedGroup<FlxSprite>;
var difficultySelectors:FlxGroup;
var sprDifficultyGroup:FlxTypedGroup<FlxSprite>;
var leftArrow:FlxSprite;
var rightArrow:FlxSprite;
override function create()
{
#if MODS_ALLOWED
Paths.destroyLoadedImages();
#end
WeekData.reloadWeekFiles(true);
if(curWeek >= WeekData.weeksList.length) curWeek = 0;
persistentUpdate = persistentDraw = true;
scoreText = new FlxText(10, 10, 0, "SCORE: 49324858", 36);
scoreText.setFormat("VCR OSD Mono", 32);
txtWeekTitle = new FlxText(FlxG.width * 0.7, 10, 0, "", 32);
txtWeekTitle.setFormat("VCR OSD Mono", 32, FlxColor.WHITE, RIGHT);
txtWeekTitle.alpha = 0.7;
var rankText:FlxText = new FlxText(0, 10);
rankText.text = 'RANK: GREAT';
rankText.setFormat(Paths.font("vcr.ttf"), 32);
rankText.size = scoreText.size;
rankText.screenCenter(X);
var ui_tex = Paths.getSparrowAtlas('campaign_menu_UI_assets');
var bgYellow:FlxSprite = new FlxSprite(0, 56).makeGraphic(FlxG.width, 386, 0xFFF9CF51);
bgSprite = new FlxSprite(0, 56);
bgSprite.antialiasing = ClientPrefs.globalAntialiasing;
grpWeekText = new FlxTypedGroup<MenuItem>();
add(grpWeekText);
var blackBarThingie:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, 56, FlxColor.BLACK);
add(blackBarThingie);
grpWeekCharacters = new FlxTypedGroup<MenuCharacter>();
grpLocks = new FlxTypedGroup<FlxSprite>();
add(grpLocks);
#if desktop
// Updating Discord Rich Presence
DiscordClient.changePresence("In the Menus", null);
#end
for (i in 0...WeekData.weeksList.length)
{
WeekData.setDirectoryFromWeek(WeekData.weeksLoaded.get(WeekData.weeksList[i]));
var weekThing:MenuItem = new MenuItem(0, bgSprite.y + 396, WeekData.weeksList[i]);
weekThing.y += ((weekThing.height + 20) * i);
weekThing.targetY = i;
grpWeekText.add(weekThing);
weekThing.screenCenter(X);
weekThing.antialiasing = ClientPrefs.globalAntialiasing;
// weekThing.updateHitbox();
// Needs an offset thingie
if (weekIsLocked(i))
{
var lock:FlxSprite = new FlxSprite(weekThing.width + 10 + weekThing.x);
lock.frames = ui_tex;
lock.animation.addByPrefix('lock', 'lock');
lock.animation.play('lock');
lock.ID = i;
lock.antialiasing = ClientPrefs.globalAntialiasing;
grpLocks.add(lock);
}
}
WeekData.setDirectoryFromWeek(WeekData.weeksLoaded.get(WeekData.weeksList[0]));
var charArray:Array<String> = WeekData.weeksLoaded.get(WeekData.weeksList[0]).weekCharacters;
for (char in 0...3)
{
var weekCharacterThing:MenuCharacter = new MenuCharacter((FlxG.width * 0.25) * (1 + char) - 150, charArray[char]);
weekCharacterThing.y += 70;
grpWeekCharacters.add(weekCharacterThing);
}
difficultySelectors = new FlxGroup();
add(difficultySelectors);
leftArrow = new FlxSprite(grpWeekText.members[0].x + grpWeekText.members[0].width + 10, grpWeekText.members[0].y + 10);
leftArrow.frames = ui_tex;
leftArrow.animation.addByPrefix('idle', "arrow left");
leftArrow.animation.addByPrefix('press', "arrow push left");
leftArrow.animation.play('idle');
leftArrow.antialiasing = ClientPrefs.globalAntialiasing;
difficultySelectors.add(leftArrow);
sprDifficultyGroup = new FlxTypedGroup<FlxSprite>();
add(sprDifficultyGroup);
for (i in 0...CoolUtil.difficultyStuff.length) {
var sprDifficulty:FlxSprite = new FlxSprite(leftArrow.x + 60, leftArrow.y).loadGraphic(Paths.image('menudifficulties/' + CoolUtil.difficultyStuff[i][0].toLowerCase()));
sprDifficulty.x += (308 - sprDifficulty.width) / 2;
sprDifficulty.ID = i;
sprDifficulty.antialiasing = ClientPrefs.globalAntialiasing;
sprDifficultyGroup.add(sprDifficulty);
}
changeDifficulty();
difficultySelectors.add(sprDifficultyGroup);
rightArrow = new FlxSprite(leftArrow.x + 376, leftArrow.y);
rightArrow.frames = ui_tex;
rightArrow.animation.addByPrefix('idle', 'arrow right');
rightArrow.animation.addByPrefix('press', "arrow push right", 24, false);
rightArrow.animation.play('idle');
rightArrow.antialiasing = ClientPrefs.globalAntialiasing;
difficultySelectors.add(rightArrow);
add(bgYellow);
add(bgSprite);
add(grpWeekCharacters);
var tracksSprite:FlxSprite = new FlxSprite(FlxG.width * 0.07, bgSprite.y + 425).loadGraphic(Paths.image('Menu_Tracks'));
tracksSprite.antialiasing = ClientPrefs.globalAntialiasing;
add(tracksSprite);
txtTracklist = new FlxText(FlxG.width * 0.05, tracksSprite.y + 60, 0, "", 32);
txtTracklist.alignment = CENTER;
txtTracklist.font = rankText.font;
txtTracklist.color = 0xFFe55777;
add(txtTracklist);
// add(rankText);
add(scoreText);
add(txtWeekTitle);
changeWeek();
super.create();
}
override function closeSubState() {
persistentUpdate = true;
changeWeek();
super.closeSubState();
}
override function update(elapsed:Float)
{
// scoreText.setFormat('VCR OSD Mono', 32);
lerpScore = Math.floor(FlxMath.lerp(lerpScore, intendedScore, CoolUtil.boundTo(elapsed * 30, 0, 1)));
if(Math.abs(intendedScore - lerpScore) < 10) lerpScore = intendedScore;
scoreText.text = "WEEK SCORE:" + lerpScore;
// FlxG.watch.addQuick('font', scoreText.font);
difficultySelectors.visible = !weekIsLocked(curWeek);
if (!movedBack && !selectedWeek)
{
if (controls.UI_UP_P)
{
changeWeek(-1);
FlxG.sound.play(Paths.sound('scrollMenu'));
}
if (controls.UI_DOWN_P)
{
changeWeek(1);
FlxG.sound.play(Paths.sound('scrollMenu'));
}
if (controls.UI_RIGHT)
rightArrow.animation.play('press')
else
rightArrow.animation.play('idle');
if (controls.UI_LEFT)
leftArrow.animation.play('press');
else
leftArrow.animation.play('idle');
if (controls.UI_RIGHT_P)
changeDifficulty(1);
if (controls.UI_LEFT_P)
changeDifficulty(-1);
if (controls.ACCEPT)
{
selectWeek();
}
else if(controls.RESET)
{
persistentUpdate = false;
openSubState(new ResetScoreSubState('', curDifficulty, '', curWeek));
FlxG.sound.play(Paths.sound('scrollMenu'));
}
}
if (controls.BACK && !movedBack && !selectedWeek)
{
FlxG.sound.play(Paths.sound('cancelMenu'));
movedBack = true;
MusicBeatState.switchState(new MainMenuState());
}
super.update(elapsed);
grpLocks.forEach(function(lock:FlxSprite)
{
lock.y = grpWeekText.members[lock.ID].y;
});
}
var movedBack:Bool = false;
var selectedWeek:Bool = false;
var stopspamming:Bool = false;
function selectWeek()
{
if (!weekIsLocked(curWeek))
{
if (stopspamming == false)
{
FlxG.sound.play(Paths.sound('confirmMenu'));
grpWeekText.members[curWeek].startFlashing();
if(grpWeekCharacters.members[1].character != '') grpWeekCharacters.members[1].animation.play('confirm');
stopspamming = true;
}
// We can't use Dynamic Array .copy() because that crashes HTML5, here's a workaround.
var songArray:Array<String> = [];
var leWeek:Array<Dynamic> = WeekData.weeksLoaded.get(WeekData.weeksList[curWeek]).songs;
for (i in 0...leWeek.length) {
songArray.push(leWeek[i][0]);
}
// Nevermind that's stupid lmao
PlayState.storyPlaylist = songArray;
PlayState.isStoryMode = true;
selectedWeek = true;
var diffic = CoolUtil.difficultyStuff[curDifficulty][1];
if(diffic == null) diffic = '';
PlayState.storyDifficulty = curDifficulty;
PlayState.SONG = Song.loadFromJson(PlayState.storyPlaylist[0].toLowerCase() + diffic, PlayState.storyPlaylist[0].toLowerCase());
PlayState.storyWeek = curWeek;
PlayState.campaignScore = 0;
PlayState.campaignMisses = 0;
new FlxTimer().start(1, function(tmr:FlxTimer)
{
LoadingState.loadAndSwitchState(new PlayState(), true);
FreeplayState.destroyFreeplayVocals();
});
} else {
FlxG.sound.play(Paths.sound('cancelMenu'));
}
}
function changeDifficulty(change:Int = 0):Void
{
curDifficulty += change;
if (curDifficulty < 0)
curDifficulty = CoolUtil.difficultyStuff.length-1;
if (curDifficulty >= CoolUtil.difficultyStuff.length)
curDifficulty = 0;
sprDifficultyGroup.forEach(function(spr:FlxSprite) {
spr.visible = false;
if(curDifficulty == spr.ID) {
spr.visible = true;
spr.alpha = 0;
spr.y = leftArrow.y - 15;
FlxTween.tween(spr, {y: leftArrow.y + 15, alpha: 1}, 0.07);
}
});
#if !switch
intendedScore = Highscore.getWeekScore(WeekData.weeksList[curWeek], curDifficulty);
#end
}
var lerpScore:Int = 0;
var intendedScore:Int = 0;
function changeWeek(change:Int = 0):Void
{
curWeek += change;
if (curWeek >= WeekData.weeksList.length)
curWeek = 0;
if (curWeek < 0)
curWeek = WeekData.weeksList.length - 1;
var leWeek:WeekData = WeekData.weeksLoaded.get(WeekData.weeksList[curWeek]);
WeekData.setDirectoryFromWeek(leWeek);
var leName:String = leWeek.storyName;
txtWeekTitle.text = leName.toUpperCase();
txtWeekTitle.x = FlxG.width - (txtWeekTitle.width + 10);
var bullShit:Int = 0;
for (item in grpWeekText.members)
{
item.targetY = bullShit - curWeek;
if (item.targetY == Std.int(0) && !weekIsLocked(curWeek))
item.alpha = 1;
else
item.alpha = 0.6;
bullShit++;
}
bgSprite.visible = true;
var assetName:String = leWeek.weekBackground;
if(assetName == null || assetName.length < 1) {
bgSprite.visible = false;
} else {
bgSprite.loadGraphic(Paths.image('menubackgrounds/menu_' + assetName));
}
updateText();
}
function weekIsLocked(weekNum:Int) {
var leWeek:WeekData = WeekData.weeksLoaded.get(WeekData.weeksList[weekNum]);
return (!leWeek.startUnlocked && leWeek.weekBefore.length > 0 && (!weekCompleted.exists(leWeek.weekBefore) || !weekCompleted.get(leWeek.weekBefore)));
}
function updateText()
{
var weekArray:Array<String> = WeekData.weeksLoaded.get(WeekData.weeksList[curWeek]).weekCharacters;
for (i in 0...grpWeekCharacters.length) {
grpWeekCharacters.members[i].changeCharacter(weekArray[i]);
}
var leWeek:WeekData = WeekData.weeksLoaded.get(WeekData.weeksList[curWeek]);
var stringThing:Array<String> = [];
for (i in 0...leWeek.songs.length) {
stringThing.push(leWeek.songs[i][0]);
}
txtTracklist.text = '';
for (i in 0...stringThing.length)
{
txtTracklist.text += stringThing[i] + '\n';
}
txtTracklist.text = txtTracklist.text.toUpperCase();
txtTracklist.screenCenter(X);
txtTracklist.x -= FlxG.width * 0.35;
#if !switch
intendedScore = Highscore.getWeekScore(WeekData.weeksList[curWeek], curDifficulty);
#end
}
}

145
source/StrumNote.hx Normal file
View file

@ -0,0 +1,145 @@
package;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
using StringTools;
class StrumNote extends FlxSprite
{
private var colorSwap:ColorSwap;
public var resetAnim:Float = 0;
private var noteData:Int = 0;
private var player:Int;
public function new(x:Float, y:Float, leData:Int, player:Int) {
colorSwap = new ColorSwap();
shader = colorSwap.shader;
noteData = leData;
this.player = player;
this.noteData = leData;
super(x, y);
var skin:String = 'NOTE_assets';
if(PlayState.SONG.arrowSkin != null && PlayState.SONG.arrowSkin.length > 1) skin = PlayState.SONG.arrowSkin;
if(PlayState.isPixelStage)
{
loadGraphic(Paths.image('pixelUI/' + skin));
width = width / 4;
height = height / 5;
loadGraphic(Paths.image('pixelUI/' + skin), true, Math.floor(width), Math.floor(height));
animation.add('green', [6]);
animation.add('red', [7]);
animation.add('blue', [5]);
animation.add('purple', [4]);
antialiasing = false;
setGraphicSize(Std.int(width * PlayState.daPixelZoom));
switch (Math.abs(leData))
{
case 0:
animation.add('static', [0]);
animation.add('pressed', [4, 8], 12, false);
animation.add('confirm', [12, 16], 24, false);
case 1:
animation.add('static', [1]);
animation.add('pressed', [5, 9], 12, false);
animation.add('confirm', [13, 17], 24, false);
case 2:
animation.add('static', [2]);
animation.add('pressed', [6, 10], 12, false);
animation.add('confirm', [14, 18], 12, false);
case 3:
animation.add('static', [3]);
animation.add('pressed', [7, 11], 12, false);
animation.add('confirm', [15, 19], 24, false);
}
}
else
{
frames = Paths.getSparrowAtlas(skin);
animation.addByPrefix('green', 'arrowUP');
animation.addByPrefix('blue', 'arrowDOWN');
animation.addByPrefix('purple', 'arrowLEFT');
animation.addByPrefix('red', 'arrowRIGHT');
antialiasing = ClientPrefs.globalAntialiasing;
setGraphicSize(Std.int(width * 0.7));
switch (Math.abs(leData))
{
case 0:
animation.addByPrefix('static', 'arrowLEFT');
animation.addByPrefix('pressed', 'left press', 24, false);
animation.addByPrefix('confirm', 'left confirm', 24, false);
case 1:
animation.addByPrefix('static', 'arrowDOWN');
animation.addByPrefix('pressed', 'down press', 24, false);
animation.addByPrefix('confirm', 'down confirm', 24, false);
case 2:
animation.addByPrefix('static', 'arrowUP');
animation.addByPrefix('pressed', 'up press', 24, false);
animation.addByPrefix('confirm', 'up confirm', 24, false);
case 3:
animation.addByPrefix('static', 'arrowRIGHT');
animation.addByPrefix('pressed', 'right press', 24, false);
animation.addByPrefix('confirm', 'right confirm', 24, false);
}
}
updateHitbox();
scrollFactor.set();
}
public function postAddedToGroup() {
playAnim('static');
x += Note.swagWidth * noteData;
x += 50;
x += ((FlxG.width / 2) * player);
ID = noteData;
}
override function update(elapsed:Float) {
if(resetAnim > 0) {
resetAnim -= elapsed;
if(resetAnim <= 0) {
playAnim('static');
resetAnim = 0;
}
}
/*if(animation.curAnim.name == 'confirm' && !PlayState.isPixelStage) {
updateConfirmOffset();
}*/
super.update(elapsed);
}
public function playAnim(anim:String, ?force:Bool = false) {
animation.play(anim, force);
centerOffsets();
if(animation.curAnim == null || animation.curAnim.name == 'static') {
colorSwap.hue = 0;
colorSwap.saturation = 0;
colorSwap.brightness = 0;
} else {
colorSwap.hue = ClientPrefs.arrowHSV[noteData % 4][0] / 360;
colorSwap.saturation = ClientPrefs.arrowHSV[noteData % 4][1] / 100;
colorSwap.brightness = ClientPrefs.arrowHSV[noteData % 4][2] / 100;
if(animation.curAnim.name == 'confirm' && !PlayState.isPixelStage) {
updateConfirmOffset();
}
}
}
function updateConfirmOffset() { //TO DO: Find a calc to make the offset work fine on other angles
centerOffsets();
offset.x -= 13;
offset.y -= 13;
}
}

530
source/TitleState.hx Normal file
View file

@ -0,0 +1,530 @@
package;
#if desktop
import Discord.DiscordClient;
import sys.thread.Thread;
#end
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.FlxState;
import flixel.input.keyboard.FlxKey;
import flixel.addons.display.FlxGridOverlay;
import flixel.addons.transition.FlxTransitionSprite.GraphicTransTileDiamond;
import flixel.addons.transition.FlxTransitionableState;
import flixel.addons.transition.TransitionData;
//import flixel.graphics.FlxGraphic;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.group.FlxGroup;
import flixel.input.gamepad.FlxGamepad;
import flixel.math.FlxPoint;
import flixel.math.FlxRect;
import flixel.system.FlxSound;
import flixel.system.ui.FlxSoundTray;
import flixel.text.FlxText;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
import lime.app.Application;
import openfl.Assets;
using StringTools;
class TitleState extends MusicBeatState
{
public static var muteKeys:Array<FlxKey> = [FlxKey.ZERO];
public static var volumeDownKeys:Array<FlxKey> = [FlxKey.NUMPADMINUS, FlxKey.MINUS];
public static var volumeUpKeys:Array<FlxKey> = [FlxKey.NUMPADPLUS, FlxKey.PLUS];
static var initialized:Bool = false;
var blackScreen:FlxSprite;
var credGroup:FlxGroup;
var credTextShit:Alphabet;
var textGroup:FlxGroup;
var logoSpr:FlxSprite;
var curWacky:Array<String> = [];
var wackyImage:FlxSprite;
var easterEggEnabled:Bool = true; //Disable this to hide the easter egg
var easterEggKeyCombination:Array<FlxKey> = [FlxKey.B, FlxKey.B]; //bb stands for bbpanzu cuz he wanted this lmao
var lastKeysPressed:Array<FlxKey> = [];
var mustUpdate:Bool = false;
public static var updateVersion:String = '';
override public function create():Void
{
#if (polymod && !html5)
if (sys.FileSystem.exists('mods/')) {
var folders:Array<String> = [];
for (file in sys.FileSystem.readDirectory('mods/')) {
var path = haxe.io.Path.join(['mods/', file]);
if (sys.FileSystem.isDirectory(path)) {
folders.push(file);
}
}
if(folders.length > 0) {
polymod.Polymod.init({modRoot: "mods", dirs: folders});
}
}
#end
#if CHECK_FOR_UPDATES
if(!closedState) {
trace('checking for update');
var http = new haxe.Http("https://raw.githubusercontent.com/ShadowMario/FNF-PsychEngine/main/gitVersion.txt");
http.onData = function (data:String)
{
updateVersion = data.split('\n')[0].trim();
var curVersion:String = MainMenuState.psychEngineVersion.trim();
trace('version online: ' + updateVersion + ', your version: ' + curVersion);
if(updateVersion != curVersion) {
trace('versions arent matching!');
mustUpdate = true;
}
}
http.onError = function (error) {
trace('error: $error');
}
http.request();
}
#end
FlxG.game.focusLostFramerate = 60;
FlxG.sound.muteKeys = muteKeys;
FlxG.sound.volumeDownKeys = volumeDownKeys;
FlxG.sound.volumeUpKeys = volumeUpKeys;
PlayerSettings.init();
curWacky = FlxG.random.getObject(getIntroTextShit());
// DEBUG BULLSHIT
swagShader = new ColorSwap();
super.create();
FlxG.save.bind('funkin', 'ninjamuffin99');
ClientPrefs.loadPrefs();
Highscore.load();
if (FlxG.save.data.weekCompleted != null)
{
StoryMenuState.weekCompleted = FlxG.save.data.weekCompleted;
}
FlxG.mouse.visible = false;
#if FREEPLAY
MusicBeatState.switchState(new FreeplayState());
#elseif CHARTING
MusicBeatState.switchState(new ChartingState());
#else
if(FlxG.save.data.flashing == null && !FlashingState.leftState) {
FlxTransitionableState.skipNextTransIn = true;
FlxTransitionableState.skipNextTransOut = true;
MusicBeatState.switchState(new FlashingState());
} else {
#if desktop
DiscordClient.initialize();
Application.current.onExit.add (function (exitCode) {
DiscordClient.shutdown();
});
#end
new FlxTimer().start(1, function(tmr:FlxTimer)
{
startIntro();
});
}
#end
}
var logoBl:FlxSprite;
var gfDance:FlxSprite;
var danceLeft:Bool = false;
var titleText:FlxSprite;
var swagShader:ColorSwap = null;
function startIntro()
{
if (!initialized)
{
/*var diamond:FlxGraphic = FlxGraphic.fromClass(GraphicTransTileDiamond);
diamond.persist = true;
diamond.destroyOnNoUse = false;
FlxTransitionableState.defaultTransIn = new TransitionData(FADE, FlxColor.BLACK, 1, new FlxPoint(0, -1), {asset: diamond, width: 32, height: 32},
new FlxRect(-300, -300, FlxG.width * 1.8, FlxG.height * 1.8));
FlxTransitionableState.defaultTransOut = new TransitionData(FADE, FlxColor.BLACK, 0.7, new FlxPoint(0, 1),
{asset: diamond, width: 32, height: 32}, new FlxRect(-300, -300, FlxG.width * 1.8, FlxG.height * 1.8));
transIn = FlxTransitionableState.defaultTransIn;
transOut = FlxTransitionableState.defaultTransOut;*/
// HAD TO MODIFY SOME BACKEND SHIT
// IF THIS PR IS HERE IF ITS ACCEPTED UR GOOD TO GO
// https://github.com/HaxeFlixel/flixel-addons/pull/348
// var music:FlxSound = new FlxSound();
// music.loadStream(Paths.music('freakyMenu'));
// FlxG.sound.list.add(music);
// music.play();
if(FlxG.sound.music == null) {
FlxG.sound.playMusic(Paths.music('freakyMenu'), 0);
FlxG.sound.music.fadeIn(4, 0, 0.7);
}
}
Conductor.changeBPM(102);
persistentUpdate = true;
var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK);
// bg.antialiasing = ClientPrefs.globalAntialiasing;
// bg.setGraphicSize(Std.int(bg.width * 0.6));
// bg.updateHitbox();
add(bg);
logoBl = new FlxSprite(-150, -100);
logoBl.frames = Paths.getSparrowAtlas('logoBumpin');
logoBl.antialiasing = ClientPrefs.globalAntialiasing;
logoBl.animation.addByPrefix('bump', 'logo bumpin', 24);
logoBl.animation.play('bump');
logoBl.updateHitbox();
// logoBl.screenCenter();
// logoBl.color = FlxColor.BLACK;
swagShader = new ColorSwap();
if(!FlxG.save.data.psykaEasterEgg || !easterEggEnabled) {
gfDance = new FlxSprite(FlxG.width * 0.4, FlxG.height * 0.07);
gfDance.frames = Paths.getSparrowAtlas('gfDanceTitle');
gfDance.animation.addByIndices('danceLeft', 'gfDance', [30, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], "", 24, false);
gfDance.animation.addByIndices('danceRight', 'gfDance', [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], "", 24, false);
}
else //Psyka easter egg
{
gfDance = new FlxSprite(FlxG.width * 0.4, FlxG.height * 0.04);
gfDance.frames = Paths.getSparrowAtlas('psykaDanceTitle');
gfDance.animation.addByIndices('danceLeft', 'psykaDance', [30, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], "", 24, false);
gfDance.animation.addByIndices('danceRight', 'psykaDance', [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], "", 24, false);
}
gfDance.antialiasing = ClientPrefs.globalAntialiasing;
add(gfDance);
gfDance.shader = swagShader.shader;
add(logoBl);
//logoBl.shader = swagShader.shader;
titleText = new FlxSprite(100, FlxG.height * 0.8);
titleText.frames = Paths.getSparrowAtlas('titleEnter');
titleText.animation.addByPrefix('idle', "Press Enter to Begin", 24);
titleText.animation.addByPrefix('press', "ENTER PRESSED", 24);
titleText.antialiasing = ClientPrefs.globalAntialiasing;
titleText.animation.play('idle');
titleText.updateHitbox();
// titleText.screenCenter(X);
add(titleText);
var logo:FlxSprite = new FlxSprite().loadGraphic(Paths.image('logo'));
logo.screenCenter();
logo.antialiasing = ClientPrefs.globalAntialiasing;
// add(logo);
// FlxTween.tween(logoBl, {y: logoBl.y + 50}, 0.6, {ease: FlxEase.quadInOut, type: PINGPONG});
// FlxTween.tween(logo, {y: logoBl.y + 50}, 0.6, {ease: FlxEase.quadInOut, type: PINGPONG, startDelay: 0.1});
credGroup = new FlxGroup();
add(credGroup);
textGroup = new FlxGroup();
blackScreen = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK);
credGroup.add(blackScreen);
credTextShit = new Alphabet(0, 0, "", true);
credTextShit.screenCenter();
// credTextShit.alignment = CENTER;
credTextShit.visible = false;
logoSpr = new FlxSprite(0, FlxG.height * 0.4).loadGraphic(Paths.image('titlelogo'));
add(logoSpr);
logoSpr.visible = false;
logoSpr.setGraphicSize(Std.int(logoSpr.width * 0.55));
logoSpr.updateHitbox();
logoSpr.screenCenter(X);
logoSpr.antialiasing = ClientPrefs.globalAntialiasing;
FlxTween.tween(credTextShit, {y: credTextShit.y + 20}, 2.9, {ease: FlxEase.quadInOut, type: PINGPONG});
if (initialized)
skipIntro();
else
initialized = true;
// credGroup.add(credTextShit);
}
function getIntroTextShit():Array<Array<String>>
{
var fullText:String = Assets.getText(Paths.txt('introText'));
var firstArray:Array<String> = fullText.split('\n');
var swagGoodArray:Array<Array<String>> = [];
for (i in firstArray)
{
swagGoodArray.push(i.split('--'));
}
return swagGoodArray;
}
var transitioning:Bool = false;
override function update(elapsed:Float)
{
if (FlxG.sound.music != null)
Conductor.songPosition = FlxG.sound.music.time;
// FlxG.watch.addQuick('amp', FlxG.sound.music.amplitude);
if (FlxG.keys.justPressed.F)
{
FlxG.fullscreen = !FlxG.fullscreen;
}
var pressedEnter:Bool = FlxG.keys.justPressed.ENTER;
#if mobile
for (touch in FlxG.touches.list)
{
if (touch.justPressed)
{
pressedEnter = true;
}
}
#end
var gamepad:FlxGamepad = FlxG.gamepads.lastActive;
if (gamepad != null)
{
if (gamepad.justPressed.START)
pressedEnter = true;
#if switch
if (gamepad.justPressed.B)
pressedEnter = true;
#end
}
// EASTER EGG
if (!transitioning && skippedIntro)
{
if(pressedEnter)
{
if(titleText != null) titleText.animation.play('press');
FlxG.camera.flash(FlxColor.WHITE, 1);
FlxG.sound.play(Paths.sound('confirmMenu'), 0.7);
transitioning = true;
// FlxG.sound.music.stop();
new FlxTimer().start(1, function(tmr:FlxTimer)
{
if (mustUpdate) {
MusicBeatState.switchState(new OutdatedState());
} else {
MusicBeatState.switchState(new MainMenuState());
}
closedState = true;
});
// FlxG.sound.play(Paths.music('titleShoot'), 0.7);
}
else if(easterEggEnabled)
{
var finalKey:FlxKey = FlxG.keys.firstJustPressed();
if(finalKey != FlxKey.NONE) {
lastKeysPressed.push(finalKey); //Convert int to FlxKey
if(lastKeysPressed.length > easterEggKeyCombination.length)
{
lastKeysPressed.shift();
}
if(lastKeysPressed.length == easterEggKeyCombination.length)
{
var isDifferent:Bool = false;
for (i in 0...lastKeysPressed.length) {
if(lastKeysPressed[i] != easterEggKeyCombination[i]) {
isDifferent = true;
break;
}
}
if(!isDifferent) {
trace('Easter egg triggered!');
FlxG.save.data.psykaEasterEgg = !FlxG.save.data.psykaEasterEgg;
FlxG.sound.play(Paths.sound('secretSound'));
var black:FlxSprite = new FlxSprite(0, 0).makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK);
black.alpha = 0;
add(black);
FlxTween.tween(black, {alpha: 1}, 1, {onComplete:
function(twn:FlxTween) {
FlxTransitionableState.skipNextTransIn = true;
FlxTransitionableState.skipNextTransOut = true;
MusicBeatState.switchState(new TitleState());
}
});
lastKeysPressed = [];
closedState = true;
transitioning = true;
}
}
}
}
}
if (pressedEnter && !skippedIntro)
{
skipIntro();
}
if(swagShader != null)
{
if(controls.UI_LEFT) swagShader.hue -= elapsed * 0.1;
if(controls.UI_RIGHT) swagShader.hue += elapsed * 0.1;
}
super.update(elapsed);
}
function createCoolText(textArray:Array<String>, ?offset:Float = 0)
{
for (i in 0...textArray.length)
{
var money:Alphabet = new Alphabet(0, 0, textArray[i], true, false);
money.screenCenter(X);
money.y += (i * 60) + 200 + offset;
credGroup.add(money);
textGroup.add(money);
}
}
function addMoreText(text:String, ?offset:Float = 0)
{
if(textGroup != null) {
var coolText:Alphabet = new Alphabet(0, 0, text, true, false);
coolText.screenCenter(X);
coolText.y += (textGroup.length * 60) + 200 + offset;
credGroup.add(coolText);
textGroup.add(coolText);
}
}
function deleteCoolText()
{
while (textGroup.members.length > 0)
{
credGroup.remove(textGroup.members[0], true);
textGroup.remove(textGroup.members[0], true);
}
}
private var sickBeats:Int = 0; //Basically curBeat but won't be skipped if you hold the tab or resize the screen
private static var closedState:Bool = false;
override function beatHit()
{
super.beatHit();
if(logoBl != null)
logoBl.animation.play('bump');
if(gfDance != null) {
danceLeft = !danceLeft;
if (danceLeft)
gfDance.animation.play('danceRight');
else
gfDance.animation.play('danceLeft');
}
if(!closedState) {
sickBeats++;
switch (sickBeats)
{
case 1:
createCoolText(['Psych Engine by'], 45);
// credTextShit.visible = true;
case 3:
addMoreText('Shadow Mario', 45);
addMoreText('RiverOaken', 45);
// credTextShit.text += '\npresent...';
// credTextShit.addText();
case 4:
deleteCoolText();
// credTextShit.visible = false;
// credTextShit.text = 'In association \nwith';
// credTextShit.screenCenter();
case 5:
createCoolText(['This is a mod to'], -60);
case 7:
addMoreText('This game right below lol', -60);
logoSpr.visible = true;
// credTextShit.text += '\nNewgrounds';
case 8:
deleteCoolText();
logoSpr.visible = false;
// credTextShit.visible = false;
// credTextShit.text = 'Shoutouts Tom Fulp';
// credTextShit.screenCenter();
case 9:
createCoolText([curWacky[0]]);
// credTextShit.visible = true;
case 11:
addMoreText(curWacky[1]);
// credTextShit.text += '\nlmao';
case 12:
deleteCoolText();
// credTextShit.visible = false;
// credTextShit.text = "Friday";
// credTextShit.screenCenter();
case 13:
addMoreText('Friday');
// credTextShit.visible = true;
case 14:
addMoreText('Night');
// credTextShit.text += '\nNight';
case 15:
addMoreText('Funkin'); // credTextShit.text += '\nFunkin';
case 16:
skipIntro();
}
}
}
var skippedIntro:Bool = false;
function skipIntro():Void
{
if (!skippedIntro)
{
remove(logoSpr);
FlxG.camera.flash(FlxColor.WHITE, 4);
remove(credGroup);
skippedIntro = true;
}
}
}

186
source/WeekData.hx Normal file
View file

@ -0,0 +1,186 @@
package;
#if MODS_ALLOWED
import sys.io.File;
import sys.FileSystem;
#end
import lime.utils.Assets;
import openfl.utils.Assets as OpenFlAssets;
import haxe.Json;
import haxe.format.JsonParser;
using StringTools;
typedef WeekFile =
{
// JSON variables
var songs:Array<Dynamic>;
var weekCharacters:Array<String>;
var weekBackground:String;
var weekBefore:String;
var storyName:String;
var weekName:String;
var freeplayColor:Array<Int>;
var startUnlocked:Bool;
var hideStoryMode:Bool;
var hideFreeplay:Bool;
}
class WeekData {
public static var weeksLoaded:Map<String, WeekData> = new Map<String, WeekData>();
public static var weeksList:Array<String> = [];
public var folder:String = '';
// JSON variables
public var songs:Array<Dynamic>;
public var weekCharacters:Array<String>;
public var weekBackground:String;
public var weekBefore:String;
public var storyName:String;
public var weekName:String;
public var freeplayColor:Array<Int>;
public var startUnlocked:Bool;
public var hideStoryMode:Bool;
public var hideFreeplay:Bool;
public static function createWeekFile():WeekFile {
var weekFile:WeekFile = {
songs: [["Bopeebo", "dad", [146, 113, 253]], ["Fresh", "dad", [146, 113, 253]], ["Dad Battle", "dad", [146, 113, 253]]],
weekCharacters: ['dad', 'bf', 'gf'],
weekBackground: 'stage',
weekBefore: 'tutorial',
storyName: 'Your New Week',
weekName: 'Custom Week',
freeplayColor: [146, 113, 253],
startUnlocked: true,
hideStoryMode: false,
hideFreeplay: false
};
return weekFile;
}
// HELP: Is there any way to convert a WeekFile to WeekData without having to put all variables there manually? I'm kind of a noob in haxe lmao
public function new(weekFile:WeekFile) {
songs = weekFile.songs;
weekCharacters = weekFile.weekCharacters;
weekBackground = weekFile.weekBackground;
weekBefore = weekFile.weekBefore;
storyName = weekFile.storyName;
weekName = weekFile.weekName;
freeplayColor = weekFile.freeplayColor;
startUnlocked = weekFile.startUnlocked;
hideStoryMode = weekFile.hideStoryMode;
hideFreeplay = weekFile.hideFreeplay;
}
public static function reloadWeekFiles(isStoryMode:Null<Bool> = false)
{
weeksList = [];
weeksLoaded.clear();
#if MODS_ALLOWED
var directories:Array<String> = [Paths.mods(), Paths.getPreloadPath()];
var originalLength:Int = directories.length;
if(FileSystem.exists(Paths.mods())) {
for (folder in FileSystem.readDirectory(Paths.mods())) {
var path = haxe.io.Path.join([Paths.mods(), folder]);
if (sys.FileSystem.isDirectory(path) && !Paths.ignoreModFolders.exists(folder)) {
directories.push(path + '/');
//trace('pushed Directory: ' + folder);
}
}
}
#else
var directories:Array<String> = [Paths.getPreloadPath()];
var originalLength:Int = directories.length;
#end
var sexList:Array<String> = CoolUtil.coolTextFile(Paths.getPreloadPath('weeks/weekList.txt'));
for (i in 0...sexList.length) {
for (j in 0...directories.length) {
var fileToCheck:String = directories[j] + 'weeks/' + sexList[i] + '.json';
if(!weeksLoaded.exists(sexList[i])) {
var week:WeekFile = getWeekFile(fileToCheck);
if(week != null) {
var weekFile:WeekData = new WeekData(week);
#if MODS_ALLOWED
if(j >= originalLength) {
weekFile.folder = directories[j].substring(Paths.mods().length, directories[j].length-1);
}
#end
if(weekFile != null && (isStoryMode == null || (isStoryMode && !weekFile.hideStoryMode) || (!isStoryMode && !weekFile.hideFreeplay))) {
weeksLoaded.set(sexList[i], weekFile);
weeksList.push(sexList[i]);
}
}
}
}
}
#if MODS_ALLOWED
for (i in 0...directories.length) {
var directory:String = directories[i] + 'weeks/';
if(FileSystem.exists(directory)) {
for (file in FileSystem.readDirectory(directory)) {
var path = haxe.io.Path.join([directory, file]);
if (!sys.FileSystem.isDirectory(path) && file.endsWith('.json')) {
var weekToCheck:String = file.substr(0, file.length - 5);
if(!weeksLoaded.exists(weekToCheck)) {
var week:WeekFile = getWeekFile(path);
if(week != null) {
var weekFile:WeekData = new WeekData(week);
if(i >= originalLength) {
weekFile.folder = directories[i].substring(Paths.mods().length, directories[i].length-1);
}
if((isStoryMode && !weekFile.hideStoryMode) || (!isStoryMode && !weekFile.hideFreeplay)) {
weeksLoaded.set(weekToCheck, weekFile);
weeksList.push(weekToCheck);
}
}
}
}
}
}
}
#end
}
private static function getWeekFile(path:String):WeekFile {
var rawJson:String = null;
#if MODS_ALLOWED
if(FileSystem.exists(path)) {
rawJson = File.getContent(path);
}
#else
if(OpenFlAssets.exists(path)) {
rawJson = Assets.getText(path);
}
#end
if(rawJson != null && rawJson.length > 0) {
return cast Json.parse(rawJson);
}
return null;
}
// FUNCTIONS YOU WILL PROBABLY NEVER NEED TO USE
//To use on PlayState.hx or Highscore stuff
public static function getWeekFileName():String {
return weeksList[PlayState.storyWeek];
}
//Used on LoadingState, nothing really too relevant
public static function getCurrentWeek():WeekData {
return weeksLoaded.get(weeksList[PlayState.storyWeek]);
}
public static function setDirectoryFromWeek(?data:WeekData = null) {
Paths.currentModDirectory = '';
if(data != null && data.folder != null && data.folder.length > 0) {
Paths.currentModDirectory = data.folder;
}
}
}

138
source/WiggleEffect.hx Normal file
View file

@ -0,0 +1,138 @@
package;
// STOLEN FROM HAXEFLIXEL DEMO LOL
import flixel.system.FlxAssets.FlxShader;
enum WiggleEffectType
{
DREAMY;
WAVY;
HEAT_WAVE_HORIZONTAL;
HEAT_WAVE_VERTICAL;
FLAG;
}
class WiggleEffect
{
public var shader(default, null):WiggleShader = new WiggleShader();
public var effectType(default, set):WiggleEffectType = DREAMY;
public var waveSpeed(default, set):Float = 0;
public var waveFrequency(default, set):Float = 0;
public var waveAmplitude(default, set):Float = 0;
public function new():Void
{
shader.uTime.value = [0];
}
public function update(elapsed:Float):Void
{
shader.uTime.value[0] += elapsed;
}
public function setValue(value:Float):Void
{
shader.uTime.value[0] = value;
}
function set_effectType(v:WiggleEffectType):WiggleEffectType
{
effectType = v;
shader.effectType.value = [WiggleEffectType.getConstructors().indexOf(Std.string(v))];
return v;
}
function set_waveSpeed(v:Float):Float
{
waveSpeed = v;
shader.uSpeed.value = [waveSpeed];
return v;
}
function set_waveFrequency(v:Float):Float
{
waveFrequency = v;
shader.uFrequency.value = [waveFrequency];
return v;
}
function set_waveAmplitude(v:Float):Float
{
waveAmplitude = v;
shader.uWaveAmplitude.value = [waveAmplitude];
return v;
}
}
class WiggleShader extends FlxShader
{
@:glFragmentSource('
#pragma header
//uniform float tx, ty; // x,y waves phase
uniform float uTime;
const int EFFECT_TYPE_DREAMY = 0;
const int EFFECT_TYPE_WAVY = 1;
const int EFFECT_TYPE_HEAT_WAVE_HORIZONTAL = 2;
const int EFFECT_TYPE_HEAT_WAVE_VERTICAL = 3;
const int EFFECT_TYPE_FLAG = 4;
uniform int effectType;
/**
* How fast the waves move over time
*/
uniform float uSpeed;
/**
* Number of waves over time
*/
uniform float uFrequency;
/**
* How much the pixels are going to stretch over the waves
*/
uniform float uWaveAmplitude;
vec2 sineWave(vec2 pt)
{
float x = 0.0;
float y = 0.0;
if (effectType == EFFECT_TYPE_DREAMY)
{
float offsetX = sin(pt.y * uFrequency + uTime * uSpeed) * uWaveAmplitude;
pt.x += offsetX; // * (pt.y - 1.0); // <- Uncomment to stop bottom part of the screen from moving
}
else if (effectType == EFFECT_TYPE_WAVY)
{
float offsetY = sin(pt.x * uFrequency + uTime * uSpeed) * uWaveAmplitude;
pt.y += offsetY; // * (pt.y - 1.0); // <- Uncomment to stop bottom part of the screen from moving
}
else if (effectType == EFFECT_TYPE_HEAT_WAVE_HORIZONTAL)
{
x = sin(pt.x * uFrequency + uTime * uSpeed) * uWaveAmplitude;
}
else if (effectType == EFFECT_TYPE_HEAT_WAVE_VERTICAL)
{
y = sin(pt.y * uFrequency + uTime * uSpeed) * uWaveAmplitude;
}
else if (effectType == EFFECT_TYPE_FLAG)
{
y = sin(pt.y * uFrequency + 10.0 * pt.x + uTime * uSpeed) * uWaveAmplitude;
x = sin(pt.x * uFrequency + 5.0 * pt.y + uTime * uSpeed) * uWaveAmplitude;
}
return vec2(pt.x + x, pt.y + y);
}
void main()
{
vec2 uv = sineWave(openfl_TextureCoordv);
gl_FragColor = texture2D(bitmap, uv);
}')
public function new()
{
super();
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,779 @@
package editors;
#if desktop
import Discord.DiscordClient;
#end
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.addons.display.FlxGridOverlay;
import flixel.addons.transition.FlxTransitionableState;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.math.FlxMath;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import flixel.system.FlxSound;
import flixel.addons.ui.FlxInputText;
import flixel.addons.ui.FlxUI9SliceSprite;
import flixel.addons.ui.FlxUI;
import flixel.addons.ui.FlxUICheckBox;
import flixel.addons.ui.FlxUIInputText;
import flixel.addons.ui.FlxUINumericStepper;
import flixel.addons.ui.FlxUITabMenu;
import flixel.ui.FlxButton;
import openfl.net.FileReference;
import openfl.events.Event;
import openfl.events.IOErrorEvent;
import flash.net.FileFilter;
import haxe.Json;
import DialogueBoxPsych;
import flixel.FlxCamera;
import flixel.group.FlxSpriteGroup;
import lime.system.Clipboard;
#if sys
import sys.io.File;
#end
using StringTools;
class DialogueCharacterEditorState extends MusicBeatState
{
var box:FlxSprite;
var daText:Alphabet = null;
private static var TIP_TEXT_MAIN:String =
'JKLI - Move camera (Hold Shift to move 4x faster)
\nQ/E - Zoom out/in
\nR - Reset Camera
\nH - Toggle Speech Bubble
\nSpace - Reset text';
private static var TIP_TEXT_OFFSET:String =
'JKLI - Move camera (Hold Shift to move 4x faster)
\nQ/E - Zoom out/in
\nR - Reset Camera
\nH - Toggle Ghosts
\nWASD - Move Looping animation offset (Red)
\nArrow Keys - Move Idle/Finished animation offset (Blue)
\nHold Shift to move offsets 10x faster';
var tipText:FlxText;
var offsetLoopText:FlxText;
var offsetIdleText:FlxText;
var camGame:FlxCamera;
var camOther:FlxCamera;
var mainGroup:FlxSpriteGroup;
var hudGroup:FlxSpriteGroup;
var character:DialogueCharacter;
var ghostLoop:DialogueCharacter;
var ghostIdle:DialogueCharacter;
override function create() {
persistentUpdate = persistentDraw = true;
camGame = new FlxCamera();
camOther = new FlxCamera();
camGame.bgColor = FlxColor.fromHSL(0, 0, 0.5);
camOther.bgColor.alpha = 0;
FlxG.cameras.reset(camGame);
FlxG.cameras.add(camOther);
FlxCamera.defaultCameras = [camOther];
mainGroup = new FlxSpriteGroup();
mainGroup.cameras = [camGame];
hudGroup = new FlxSpriteGroup();
hudGroup.cameras = [camGame];
add(mainGroup);
add(hudGroup);
character = new DialogueCharacter();
character.scrollFactor.set();
mainGroup.add(character);
ghostLoop = new DialogueCharacter();
ghostLoop.alpha = 0;
ghostLoop.color = FlxColor.RED;
ghostLoop.isGhost = true;
ghostLoop.jsonFile = character.jsonFile;
ghostLoop.cameras = [camGame];
add(ghostLoop);
ghostIdle = new DialogueCharacter();
ghostIdle.alpha = 0;
ghostIdle.color = FlxColor.BLUE;
ghostIdle.isGhost = true;
ghostIdle.jsonFile = character.jsonFile;
ghostIdle.cameras = [camGame];
add(ghostIdle);
box = new FlxSprite(70, 370);
box.frames = Paths.getSparrowAtlas('speech_bubble');
box.scrollFactor.set();
box.antialiasing = ClientPrefs.globalAntialiasing;
box.animation.addByPrefix('normal', 'speech bubble normal', 24);
box.animation.addByPrefix('center', 'speech bubble middle', 24);
box.animation.play('normal', true);
box.setGraphicSize(Std.int(box.width * 0.9));
box.updateHitbox();
hudGroup.add(box);
tipText = new FlxText(10, 10, FlxG.width - 20, TIP_TEXT_MAIN, 8);
tipText.setFormat(Paths.font("vcr.ttf"), 16, FlxColor.WHITE, RIGHT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
tipText.cameras = [camOther];
tipText.scrollFactor.set();
add(tipText);
offsetLoopText = new FlxText(10, 10, 0, '', 32);
offsetLoopText.setFormat(Paths.font("vcr.ttf"), 32, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
offsetLoopText.cameras = [camOther];
offsetLoopText.scrollFactor.set();
add(offsetLoopText);
offsetLoopText.visible = false;
offsetIdleText = new FlxText(10, 46, 0, '', 32);
offsetIdleText.setFormat(Paths.font("vcr.ttf"), 32, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
offsetIdleText.cameras = [camOther];
offsetIdleText.scrollFactor.set();
add(offsetIdleText);
offsetIdleText.visible = false;
reloadCharacter();
updateTextBox();
reloadText();
addEditorBox();
FlxG.mouse.visible = true;
updateCharTypeBox();
super.create();
}
var UI_typebox:FlxUITabMenu;
var UI_mainbox:FlxUITabMenu;
function addEditorBox() {
var tabs = [
{name: 'Character Type', label: 'Character Type'},
];
UI_typebox = new FlxUITabMenu(null, tabs, true);
UI_typebox.resize(120, 180);
UI_typebox.x = 900;
UI_typebox.y = FlxG.height - UI_typebox.height - 50;
UI_typebox.scrollFactor.set();
UI_typebox.camera = camGame;
addTypeUI();
add(UI_typebox);
var tabs = [
{name: 'Animations', label: 'Animations'},
{name: 'Character', label: 'Character'},
];
UI_mainbox = new FlxUITabMenu(null, tabs, true);
UI_mainbox.resize(200, 250);
UI_mainbox.x = UI_typebox.x + UI_typebox.width;
UI_mainbox.y = FlxG.height - UI_mainbox.height - 50;
UI_mainbox.scrollFactor.set();
UI_mainbox.camera = camGame;
addAnimationsUI();
addCharacterUI();
add(UI_mainbox);
UI_mainbox.selected_tab_id = 'Character';
lastTab = UI_mainbox.selected_tab_id;
}
var leftCheckbox:FlxUICheckBox;
var centerCheckbox:FlxUICheckBox;
var rightCheckbox:FlxUICheckBox;
function addTypeUI() {
var tab_group = new FlxUI(null, UI_typebox);
tab_group.name = "Character Type";
leftCheckbox = new FlxUICheckBox(10, 20, null, null, "Left", 100);
leftCheckbox.callback = function()
{
character.jsonFile.dialogue_pos = 'left';
updateCharTypeBox();
};
centerCheckbox = new FlxUICheckBox(leftCheckbox.x, leftCheckbox.y + 40, null, null, "Center", 100);
centerCheckbox.callback = function()
{
character.jsonFile.dialogue_pos = 'center';
updateCharTypeBox();
};
rightCheckbox = new FlxUICheckBox(centerCheckbox.x, centerCheckbox.y + 40, null, null, "Right", 100);
rightCheckbox.callback = function()
{
character.jsonFile.dialogue_pos = 'right';
updateCharTypeBox();
};
tab_group.add(leftCheckbox);
tab_group.add(centerCheckbox);
tab_group.add(rightCheckbox);
UI_typebox.addGroup(tab_group);
}
var curSelectedAnim:String;
var animationArray:Array<String> = [];
var animationDropDown:FlxUIDropDownMenuCustom;
var animationInputText:FlxUIInputText;
var loopInputText:FlxUIInputText;
var idleInputText:FlxUIInputText;
function addAnimationsUI() {
var tab_group = new FlxUI(null, UI_mainbox);
tab_group.name = "Animations";
animationDropDown = new FlxUIDropDownMenuCustom(10, 30, FlxUIDropDownMenuCustom.makeStrIdLabelArray([''], true), function(animation:String) {
var anim:String = animationArray[Std.parseInt(animation)];
if(character.dialogueAnimations.exists(anim)) {
ghostLoop.playAnim(anim);
ghostIdle.playAnim(anim, true);
curSelectedAnim = anim;
var animShit:DialogueAnimArray = character.dialogueAnimations.get(curSelectedAnim);
offsetLoopText.text = 'Loop: ' + animShit.loop_offsets;
offsetIdleText.text = 'Idle: ' + animShit.idle_offsets;
animationInputText.text = animShit.anim;
loopInputText.text = animShit.loop_name;
idleInputText.text = animShit.idle_name;
}
});
animationInputText = new FlxUIInputText(15, 85, 80, '', 8);
blockPressWhileTypingOn.push(animationInputText);
loopInputText = new FlxUIInputText(animationInputText.x, animationInputText.y + 35, 150, '', 8);
blockPressWhileTypingOn.push(loopInputText);
idleInputText = new FlxUIInputText(loopInputText.x, loopInputText.y + 40, 150, '', 8);
blockPressWhileTypingOn.push(idleInputText);
var addUpdateButton:FlxButton = new FlxButton(10, idleInputText.y + 30, "Add/Update", function() {
var theAnim:String = animationInputText.text.trim();
if(character.dialogueAnimations.exists(theAnim)) //Update
{
for (i in 0...character.jsonFile.animations.length) {
var animArray:DialogueAnimArray = character.jsonFile.animations[i];
if(animArray.anim.trim() == theAnim) {
animArray.loop_name = loopInputText.text;
animArray.idle_name = idleInputText.text;
break;
}
}
character.reloadAnimations();
ghostLoop.reloadAnimations();
ghostIdle.reloadAnimations();
if(curSelectedAnim == theAnim) {
ghostLoop.playAnim(theAnim);
ghostIdle.playAnim(theAnim, true);
}
}
else //Add
{
var newAnim:DialogueAnimArray = {
anim: theAnim,
loop_name: loopInputText.text,
loop_offsets: [0, 0],
idle_name: idleInputText.text,
idle_offsets: [0, 0]
}
character.jsonFile.animations.push(newAnim);
var lastSelected:String = animationDropDown.selectedLabel;
character.reloadAnimations();
ghostLoop.reloadAnimations();
ghostIdle.reloadAnimations();
reloadAnimationsDropDown();
animationDropDown.selectedLabel = lastSelected;
}
});
var removeUpdateButton:FlxButton = new FlxButton(100, addUpdateButton.y, "Remove", function() {
for (i in 0...character.jsonFile.animations.length) {
var animArray:DialogueAnimArray = character.jsonFile.animations[i];
if(animArray != null && animArray.anim.trim() == animationInputText.text.trim()) {
var lastSelected:String = animationDropDown.selectedLabel;
character.jsonFile.animations.remove(animArray);
character.reloadAnimations();
ghostLoop.reloadAnimations();
ghostIdle.reloadAnimations();
reloadAnimationsDropDown();
if(character.jsonFile.animations.length > 0 && lastSelected == animArray.anim.trim()) {
var animToPlay:String = character.jsonFile.animations[0].anim;
ghostLoop.playAnim(animToPlay);
ghostIdle.playAnim(animToPlay, true);
}
animationDropDown.selectedLabel = lastSelected;
animationInputText.text = '';
loopInputText.text = '';
idleInputText.text = '';
break;
}
}
});
tab_group.add(new FlxText(animationDropDown.x, animationDropDown.y - 18, 0, 'Animations:'));
tab_group.add(new FlxText(animationInputText.x, animationInputText.y - 18, 0, 'Animation name:'));
tab_group.add(new FlxText(loopInputText.x, loopInputText.y - 18, 0, 'Loop name on .XML file:'));
tab_group.add(new FlxText(idleInputText.x, idleInputText.y - 18, 0, 'Idle/Finished name on .XML file:'));
tab_group.add(animationInputText);
tab_group.add(loopInputText);
tab_group.add(idleInputText);
tab_group.add(addUpdateButton);
tab_group.add(removeUpdateButton);
tab_group.add(animationDropDown);
UI_mainbox.addGroup(tab_group);
reloadAnimationsDropDown();
}
function reloadAnimationsDropDown() {
animationArray = [];
for (anim in character.jsonFile.animations) {
animationArray.push(anim.anim);
}
if(animationArray.length < 1) animationArray = [''];
animationDropDown.setData(FlxUIDropDownMenuCustom.makeStrIdLabelArray(animationArray, true));
}
var imageInputText:FlxUIInputText;
var scaleStepper:FlxUINumericStepper;
var xStepper:FlxUINumericStepper;
var yStepper:FlxUINumericStepper;
var blockPressWhileTypingOn:Array<FlxUIInputText> = [];
function addCharacterUI() {
var tab_group = new FlxUI(null, UI_mainbox);
tab_group.name = "Character";
imageInputText = new FlxUIInputText(10, 30, 80, character.jsonFile.image, 8);
blockPressWhileTypingOn.push(imageInputText);
xStepper = new FlxUINumericStepper(imageInputText.x, imageInputText.y + 50, 10, character.jsonFile.position[0], -2000, 2000, 0);
yStepper = new FlxUINumericStepper(imageInputText.x + 80, xStepper.y, 10, character.jsonFile.position[1], -2000, 2000, 0);
scaleStepper = new FlxUINumericStepper(imageInputText.x, xStepper.y + 50, 0.05, character.jsonFile.scale, 0.1, 10, 2);
tab_group.add(new FlxText(10, imageInputText.y - 18, 0, 'Image file name:'));
tab_group.add(new FlxText(10, xStepper.y - 18, 0, 'Position Offset:'));
tab_group.add(new FlxText(10, scaleStepper.y - 18, 0, 'Scale:'));
tab_group.add(imageInputText);
tab_group.add(xStepper);
tab_group.add(yStepper);
tab_group.add(scaleStepper);
var reloadImageButton:FlxButton = new FlxButton(10, scaleStepper.y + 60, "Reload Image", function() {
reloadCharacter();
});
var loadButton:FlxButton = new FlxButton(reloadImageButton.x + 100, reloadImageButton.y, "Load Character", function() {
loadCharacter();
});
var saveButton:FlxButton = new FlxButton(loadButton.x, reloadImageButton.y - 30, "Save Character", function() {
saveCharacter();
});
tab_group.add(reloadImageButton);
tab_group.add(loadButton);
tab_group.add(saveButton);
UI_mainbox.addGroup(tab_group);
}
function updateCharTypeBox() {
leftCheckbox.checked = false;
centerCheckbox.checked = false;
rightCheckbox.checked = false;
switch(character.jsonFile.dialogue_pos) {
case 'left':
leftCheckbox.checked = true;
case 'center':
centerCheckbox.checked = true;
case 'right':
rightCheckbox.checked = true;
}
reloadCharacter();
updateTextBox();
}
private static var DEFAULT_TEXT:String = 'Lorem ipsum dolor sit amet';
function reloadText() {
if(daText != null) {
daText.killTheTimer();
daText.kill();
hudGroup.remove(daText);
daText.destroy();
}
daText = new Alphabet(0, 0, DEFAULT_TEXT, false, true, 0.05, 0.7);
daText.x = DialogueBoxPsych.DEFAULT_TEXT_X;
daText.y = DialogueBoxPsych.DEFAULT_TEXT_Y;
hudGroup.add(daText);
}
function reloadCharacter() {
var charsArray:Array<DialogueCharacter> = [character, ghostLoop, ghostIdle];
for (char in charsArray) {
char.frames = Paths.getSparrowAtlas('dialogue/' + character.jsonFile.image);
char.jsonFile = character.jsonFile;
char.reloadAnimations();
char.setGraphicSize(Std.int(char.width * DialogueCharacter.DEFAULT_SCALE * character.jsonFile.scale));
char.updateHitbox();
}
character.x = DialogueBoxPsych.LEFT_CHAR_X;
character.y = DialogueBoxPsych.DEFAULT_CHAR_Y;
switch(character.jsonFile.dialogue_pos) {
case 'right':
character.x = FlxG.width - character.width + DialogueBoxPsych.RIGHT_CHAR_X;
case 'center':
character.x = FlxG.width / 2;
character.x -= character.width / 2;
}
character.x += character.jsonFile.position[0] + mainGroup.x;
character.y += character.jsonFile.position[1] + mainGroup.y;
character.playAnim(); //Plays random animation
if(character.jsonFile.animations.length > 0) {
curSelectedAnim = character.jsonFile.animations[0].anim;
var animShit:DialogueAnimArray = character.dialogueAnimations.get(curSelectedAnim);
ghostLoop.playAnim(animShit.anim);
ghostIdle.playAnim(animShit.anim, true);
offsetLoopText.text = 'Loop: ' + animShit.loop_offsets;
offsetIdleText.text = 'Idle: ' + animShit.idle_offsets;
}
#if desktop
// Updating Discord Rich Presence
DiscordClient.changePresence("Dialogue Character Editor", "Editting: " + character.jsonFile.image);
#end
}
function updateTextBox() {
box.flipX = false;
var anim:String = 'normal';
switch(character.jsonFile.dialogue_pos) {
case 'left':
box.flipX = true;
case 'center':
anim = 'center';
}
box.animation.play(anim, true);
DialogueBoxPsych.updateBoxOffsets(box);
}
override function getEvent(id:String, sender:Dynamic, data:Dynamic, ?params:Array<Dynamic>) {
if(id == FlxUIInputText.CHANGE_EVENT && sender == imageInputText) {
character.jsonFile.image = imageInputText.text;
} else if(id == FlxUINumericStepper.CHANGE_EVENT && (sender is FlxUINumericStepper)) {
if(sender == scaleStepper) {
character.jsonFile.scale = scaleStepper.value;
reloadCharacter();
} else if(sender == xStepper) {
character.jsonFile.position[0] = xStepper.value;
reloadCharacter();
} else if(sender == yStepper) {
character.jsonFile.position[1] = yStepper.value;
reloadCharacter();
}
}
}
var currentGhosts:Int = 0;
var lastTab:String = 'Character';
var transitioning:Bool = false;
override function update(elapsed:Float) {
if(transitioning) {
super.update(elapsed);
return;
}
if(character.animation.curAnim != null) {
if(daText.finishedText) {
if(character.animationIsLoop()) {
character.playAnim(character.animation.curAnim.name, true);
}
} else if(character.animation.curAnim.finished) {
character.animation.curAnim.restart();
}
}
var blockInput:Bool = false;
for (inputText in blockPressWhileTypingOn) {
if(inputText.hasFocus) {
FlxG.sound.muteKeys = [];
FlxG.sound.volumeDownKeys = [];
FlxG.sound.volumeUpKeys = [];
blockInput = true;
if(FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.V && Clipboard.text != null) { //Copy paste
inputText.text = ClipboardAdd(inputText.text);
inputText.caretIndex = inputText.text.length;
getEvent(FlxUIInputText.CHANGE_EVENT, inputText, null, []);
}
if(FlxG.keys.justPressed.ENTER) inputText.hasFocus = false;
break;
}
}
if(!blockInput && !animationDropDown.dropPanel.visible) {
FlxG.sound.muteKeys = TitleState.muteKeys;
FlxG.sound.volumeDownKeys = TitleState.volumeDownKeys;
FlxG.sound.volumeUpKeys = TitleState.volumeUpKeys;
if(FlxG.keys.justPressed.SPACE && UI_mainbox.selected_tab_id == 'Character') {
character.playAnim();
updateTextBox();
reloadText();
}
//lots of Ifs lol get trolled
var offsetAdd:Int = 1;
var speed:Float = 300;
if(FlxG.keys.pressed.SHIFT) {
speed = 1200;
offsetAdd = 10;
}
var negaMult:Array<Int> = [1, 1, -1, -1];
var controlArray:Array<Bool> = [FlxG.keys.pressed.J, FlxG.keys.pressed.I, FlxG.keys.pressed.L, FlxG.keys.pressed.K];
for (i in 0...controlArray.length) {
if(controlArray[i]) {
if(i % 2 == 1) {
mainGroup.y += speed * elapsed * negaMult[i];
} else {
mainGroup.x += speed * elapsed * negaMult[i];
}
}
}
if(UI_mainbox.selected_tab_id == 'Animations' && curSelectedAnim != null && character.dialogueAnimations.exists(curSelectedAnim)) {
var moved:Bool = false;
var animShit:DialogueAnimArray = character.dialogueAnimations.get(curSelectedAnim);
var controlArrayLoop:Array<Bool> = [FlxG.keys.justPressed.A, FlxG.keys.justPressed.W, FlxG.keys.justPressed.D, FlxG.keys.justPressed.S];
var controlArrayIdle:Array<Bool> = [FlxG.keys.justPressed.LEFT, FlxG.keys.justPressed.UP, FlxG.keys.justPressed.RIGHT, FlxG.keys.justPressed.DOWN];
for (i in 0...controlArrayLoop.length) {
if(controlArrayLoop[i]) {
if(i % 2 == 1) {
animShit.loop_offsets[1] += offsetAdd * negaMult[i];
} else {
animShit.loop_offsets[0] += offsetAdd * negaMult[i];
}
moved = true;
}
}
for (i in 0...controlArrayIdle.length) {
if(controlArrayIdle[i]) {
if(i % 2 == 1) {
animShit.idle_offsets[1] += offsetAdd * negaMult[i];
} else {
animShit.idle_offsets[0] += offsetAdd * negaMult[i];
}
moved = true;
}
}
if(moved) {
offsetLoopText.text = 'Loop: ' + animShit.loop_offsets;
offsetIdleText.text = 'Idle: ' + animShit.idle_offsets;
ghostLoop.offset.set(animShit.loop_offsets[0], animShit.loop_offsets[1]);
ghostIdle.offset.set(animShit.idle_offsets[0], animShit.idle_offsets[1]);
}
}
if (FlxG.keys.pressed.Q && camGame.zoom > 0.1) {
camGame.zoom -= elapsed * camGame.zoom;
if(camGame.zoom < 0.1) camGame.zoom = 0.1;
}
if (FlxG.keys.pressed.E && camGame.zoom < 1) {
camGame.zoom += elapsed * camGame.zoom;
if(camGame.zoom > 1) camGame.zoom = 1;
}
if(FlxG.keys.justPressed.H) {
if(UI_mainbox.selected_tab_id == 'Animations') {
currentGhosts++;
if(currentGhosts > 2) currentGhosts = 0;
ghostLoop.visible = (currentGhosts != 1);
ghostIdle.visible = (currentGhosts != 2);
ghostLoop.alpha = (currentGhosts == 2 ? 1 : 0.6);
ghostIdle.alpha = (currentGhosts == 1 ? 1 : 0.6);
} else {
hudGroup.visible = !hudGroup.visible;
}
}
if(FlxG.keys.justPressed.R) {
camGame.zoom = 1;
mainGroup.setPosition(0, 0);
hudGroup.visible = true;
}
if(UI_mainbox.selected_tab_id != lastTab) {
if(UI_mainbox.selected_tab_id == 'Animations') {
hudGroup.alpha = 0;
mainGroup.alpha = 0;
ghostLoop.alpha = 0.6;
ghostIdle.alpha = 0.6;
tipText.text = TIP_TEXT_OFFSET;
offsetLoopText.visible = true;
offsetIdleText.visible = true;
currentGhosts = 0;
} else {
hudGroup.alpha = 1;
mainGroup.alpha = 1;
ghostLoop.alpha = 0;
ghostIdle.alpha = 0;
tipText.text = TIP_TEXT_MAIN;
offsetLoopText.visible = false;
offsetIdleText.visible = false;
character.playAnim();
updateTextBox();
reloadText();
}
lastTab = UI_mainbox.selected_tab_id;
currentGhosts = 0;
}
if(FlxG.keys.justPressed.ESCAPE) {
MusicBeatState.switchState(new editors.MasterEditorMenu());
FlxG.sound.playMusic(Paths.music('freakyMenu'), 1);
transitioning = true;
}
ghostLoop.setPosition(character.x, character.y);
ghostIdle.setPosition(character.x, character.y);
hudGroup.x = mainGroup.x;
hudGroup.y = mainGroup.y;
}
super.update(elapsed);
}
var _file:FileReference = null;
function loadCharacter() {
var jsonFilter:FileFilter = new FileFilter('JSON', 'json');
_file = new FileReference();
_file.addEventListener(Event.SELECT, onLoadComplete);
_file.addEventListener(Event.CANCEL, onLoadCancel);
_file.addEventListener(IOErrorEvent.IO_ERROR, onLoadError);
_file.browse([jsonFilter]);
}
function onLoadComplete(_):Void
{
_file.removeEventListener(Event.SELECT, onLoadComplete);
_file.removeEventListener(Event.CANCEL, onLoadCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
#if sys
var fullPath:String = null;
var jsonLoaded = cast Json.parse(Json.stringify(_file)); //Exploit(???) for accessing a private variable
if(jsonLoaded.__path != null) fullPath = jsonLoaded.__path; //I'm either a genious or dangerously dumb
if(fullPath != null) {
var rawJson:String = File.getContent(fullPath);
if(rawJson != null) {
var loadedChar:DialogueCharacterFile = cast Json.parse(rawJson);
if(loadedChar.dialogue_pos != null) //Make sure it's really a dialogue character
{
var cutName:String = _file.name.substr(0, _file.name.length - 5);
trace("Successfully loaded file: " + cutName);
character.jsonFile = loadedChar;
reloadCharacter();
reloadAnimationsDropDown();
updateCharTypeBox();
updateTextBox();
reloadText();
imageInputText.text = character.jsonFile.image;
scaleStepper.value = character.jsonFile.scale;
xStepper.value = character.jsonFile.position[0];
yStepper.value = character.jsonFile.position[1];
_file = null;
return;
}
}
}
_file = null;
#else
trace("File couldn't be loaded! You aren't on Desktop, are you?");
#end
}
/**
* Called when the save file dialog is cancelled.
*/
function onLoadCancel(_):Void
{
_file.removeEventListener(Event.SELECT, onLoadComplete);
_file.removeEventListener(Event.CANCEL, onLoadCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
_file = null;
trace("Cancelled file loading.");
}
/**
* Called if there is an error while saving the gameplay recording.
*/
function onLoadError(_):Void
{
_file.removeEventListener(Event.SELECT, onLoadComplete);
_file.removeEventListener(Event.CANCEL, onLoadCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
_file = null;
trace("Problem loading file");
}
function saveCharacter() {
var data:String = Json.stringify(character.jsonFile, "\t");
if (data.length > 0)
{
var splittedImage:Array<String> = imageInputText.text.trim().split('_');
var characterName:String = splittedImage[0].toLowerCase().replace(' ', '');
_file = new FileReference();
_file.addEventListener(Event.COMPLETE, onSaveComplete);
_file.addEventListener(Event.CANCEL, onSaveCancel);
_file.addEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file.save(data, characterName + ".json");
}
}
function onSaveComplete(_):Void
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
FlxG.log.notice("Successfully saved file.");
}
/**
* Called when the save file dialog is cancelled.
*/
function onSaveCancel(_):Void
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
}
/**
* Called if there is an error while saving the gameplay recording.
*/
function onSaveError(_):Void
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
FlxG.log.error("Problem saving file");
}
function ClipboardAdd(prefix:String = ''):String {
if(prefix.toLowerCase().endsWith('v')) //probably copy paste attempt
{
prefix = prefix.substring(0, prefix.length-1);
}
var text:String = prefix + Clipboard.text.replace('\n', '');
return text;
}
}

View file

@ -0,0 +1,546 @@
package editors;
#if desktop
import Discord.DiscordClient;
#end
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.addons.display.FlxGridOverlay;
import flixel.addons.transition.FlxTransitionableState;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.math.FlxMath;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import flixel.system.FlxSound;
import flixel.addons.ui.FlxInputText;
import flixel.addons.ui.FlxUI9SliceSprite;
import flixel.addons.ui.FlxUI;
import flixel.addons.ui.FlxUICheckBox;
import flixel.addons.ui.FlxUIInputText;
import flixel.addons.ui.FlxUINumericStepper;
import flixel.addons.ui.FlxUITabMenu;
import flixel.ui.FlxButton;
import openfl.net.FileReference;
import openfl.events.Event;
import openfl.events.IOErrorEvent;
import flash.net.FileFilter;
import haxe.Json;
import DialogueBoxPsych;
import lime.system.Clipboard;
#if sys
import sys.io.File;
#end
using StringTools;
class DialogueEditorState extends MusicBeatState
{
var character:DialogueCharacter;
var box:FlxSprite;
var daText:Alphabet;
var selectedText:FlxText;
var animText:FlxText;
var defaultLine:DialogueLine;
var dialogueFile:DialogueFile = null;
override function create() {
persistentUpdate = persistentDraw = true;
FlxG.camera.bgColor = FlxColor.fromHSL(0, 0, 0.5);
defaultLine = {
portrait: DialogueCharacter.DEFAULT_CHARACTER,
expression: 'talk',
text: DEFAULT_TEXT,
boxState: DEFAULT_BUBBLETYPE,
speed: 0.05
};
dialogueFile = {
dialogue: [
copyDefaultLine()
]
};
character = new DialogueCharacter();
character.scrollFactor.set();
add(character);
box = new FlxSprite(70, 370);
box.frames = Paths.getSparrowAtlas('speech_bubble');
box.scrollFactor.set();
box.antialiasing = ClientPrefs.globalAntialiasing;
box.animation.addByPrefix('normal', 'speech bubble normal', 24);
box.animation.addByPrefix('angry', 'AHH speech bubble', 24);
box.animation.addByPrefix('center', 'speech bubble middle', 24);
box.animation.addByPrefix('center-angry', 'AHH Speech Bubble middle', 24);
box.animation.play('normal', true);
box.setGraphicSize(Std.int(box.width * 0.9));
box.updateHitbox();
add(box);
addEditorBox();
FlxG.mouse.visible = true;
var addLineText:FlxText = new FlxText(10, 10, FlxG.width - 20, 'Press O to remove the current dialogue line, Press P to add another line after the current one.', 8);
addLineText.setFormat(Paths.font("vcr.ttf"), 16, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
addLineText.scrollFactor.set();
add(addLineText);
selectedText = new FlxText(10, 32, FlxG.width - 20, '', 8);
selectedText.setFormat(Paths.font("vcr.ttf"), 24, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
selectedText.scrollFactor.set();
add(selectedText);
animText = new FlxText(10, 62, FlxG.width - 20, '', 8);
animText.setFormat(Paths.font("vcr.ttf"), 24, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
animText.scrollFactor.set();
add(animText);
changeText();
super.create();
}
var UI_box:FlxUITabMenu;
function addEditorBox() {
var tabs = [
{name: 'Dialogue Line', label: 'Dialogue Line'},
];
UI_box = new FlxUITabMenu(null, tabs, true);
UI_box.resize(250, 190);
UI_box.x = FlxG.width - UI_box.width - 10;
UI_box.y = 10;
UI_box.scrollFactor.set();
UI_box.alpha = 0.8;
addDialogueLineUI();
add(UI_box);
}
var characterInputText:FlxUIInputText;
var lineInputText:FlxUIInputText;
var angryCheckbox:FlxUICheckBox;
var speedStepper:FlxUINumericStepper;
function addDialogueLineUI() {
var tab_group = new FlxUI(null, UI_box);
tab_group.name = "Dialogue Line";
characterInputText = new FlxUIInputText(10, 20, 80, DialogueCharacter.DEFAULT_CHARACTER, 8);
blockPressWhileTypingOn.push(characterInputText);
speedStepper = new FlxUINumericStepper(10, characterInputText.y + 40, 0.005, 0.05, 0, 0.5, 3);
angryCheckbox = new FlxUICheckBox(speedStepper.x + 120, speedStepper.y, null, null, "Angry Textbox", 200);
angryCheckbox.callback = function()
{
updateTextBox();
dialogueFile.dialogue[curSelected].boxState = (angryCheckbox.checked ? 'angry' : 'normal');
};
lineInputText = new FlxUIInputText(10, speedStepper.y + 45, 200, DEFAULT_TEXT, 8);
blockPressWhileTypingOn.push(lineInputText);
var loadButton:FlxButton = new FlxButton(20, lineInputText.y + 30, "Load Dialogue", function() {
loadDialogue();
});
var saveButton:FlxButton = new FlxButton(loadButton.x + 120, loadButton.y, "Save Dialogue", function() {
saveDialogue();
});
tab_group.add(new FlxText(10, speedStepper.y - 18, 0, 'Interval/Speed (ms):'));
tab_group.add(new FlxText(10, characterInputText.y - 18, 0, 'Character:'));
tab_group.add(new FlxText(10, lineInputText.y - 18, 0, 'Text:'));
tab_group.add(characterInputText);
tab_group.add(angryCheckbox);
tab_group.add(speedStepper);
tab_group.add(lineInputText);
tab_group.add(lineInputText);
tab_group.add(loadButton);
tab_group.add(saveButton);
UI_box.addGroup(tab_group);
}
function copyDefaultLine():DialogueLine {
var copyLine:DialogueLine = {
portrait: defaultLine.portrait,
expression: defaultLine.expression,
text: defaultLine.text,
boxState: defaultLine.boxState,
speed: defaultLine.speed
};
return copyLine;
}
function updateTextBox() {
box.flipX = false;
var isAngry:Bool = angryCheckbox.checked;
var anim:String = isAngry ? 'angry' : 'normal';
switch(character.jsonFile.dialogue_pos) {
case 'left':
box.flipX = true;
case 'center':
if(isAngry) {
anim = 'center-angry';
} else {
anim = 'center';
}
}
box.animation.play(anim, true);
DialogueBoxPsych.updateBoxOffsets(box);
}
function reloadCharacter() {
character.frames = Paths.getSparrowAtlas('dialogue/' + character.jsonFile.image);
character.jsonFile = character.jsonFile;
character.reloadAnimations();
character.setGraphicSize(Std.int(character.width * DialogueCharacter.DEFAULT_SCALE * character.jsonFile.scale));
character.updateHitbox();
character.x = DialogueBoxPsych.LEFT_CHAR_X;
character.y = DialogueBoxPsych.DEFAULT_CHAR_Y;
switch(character.jsonFile.dialogue_pos) {
case 'right':
character.x = FlxG.width - character.width + DialogueBoxPsych.RIGHT_CHAR_X;
case 'center':
character.x = FlxG.width / 2;
character.x -= character.width / 2;
}
character.x += character.jsonFile.position[0];
character.y += character.jsonFile.position[1];
character.playAnim(); //Plays random animation
characterAnimSpeed();
if(character.animation.curAnim != null) {
animText.text = 'Animation: ' + character.jsonFile.animations[curAnim].anim + ' (' + (curAnim + 1) +' / ' + character.jsonFile.animations.length + ') - Press W or S to scroll';
} else {
animText.text = 'ERROR! NO ANIMATIONS FOUND';
}
}
private static var DEFAULT_TEXT:String = "coolswag";
private static var DEFAULT_SPEED:Float = 0.05;
private static var DEFAULT_BUBBLETYPE:String = "normal";
function reloadText(speed:Float = 0.05) {
if(daText != null) {
daText.killTheTimer();
daText.kill();
remove(daText);
daText.destroy();
}
if(Math.isNaN(speed) || speed < 0.001) speed = 0.0;
var textToType:String = lineInputText.text;
if(textToType == null || textToType.length < 1) textToType = ' ';
daText = new Alphabet(DialogueBoxPsych.DEFAULT_TEXT_X, DialogueBoxPsych.DEFAULT_TEXT_Y, textToType, false, true, speed, 0.7);
add(daText);
if(speed > 0) {
if(character.jsonFile.animations.length > curAnim && character.jsonFile.animations[curAnim] != null) {
character.playAnim(character.jsonFile.animations[curAnim].anim);
}
characterAnimSpeed();
}
#if desktop
// Updating Discord Rich Presence
var rpcText:String = lineInputText.text;
if(rpcText == null || rpcText.length < 1) rpcText = '(Empty)';
if(rpcText.length < 3) rpcText += ' '; //Fixes a bug on RPC that triggers an error when the text is too short
DiscordClient.changePresence("Dialogue Editor", rpcText);
#end
}
override function getEvent(id:String, sender:Dynamic, data:Dynamic, ?params:Array<Dynamic>) {
if(id == FlxUIInputText.CHANGE_EVENT && (sender is FlxUIInputText)) {
if(sender == characterInputText) {
character.reloadCharacterJson(characterInputText.text);
reloadCharacter();
updateTextBox();
if(character.jsonFile.animations.length > 0) {
curAnim = 0;
if(character.jsonFile.animations.length > curAnim && character.jsonFile.animations[curAnim] != null) {
character.playAnim(character.jsonFile.animations[curAnim].anim, daText.finishedText);
animText.text = 'Animation: ' + character.jsonFile.animations[curAnim].anim + ' (' + (curAnim + 1) +' / ' + character.jsonFile.animations.length + ') - Press W or S to scroll';
} else {
animText.text = 'ERROR! NO ANIMATIONS FOUND';
}
characterAnimSpeed();
}
dialogueFile.dialogue[curSelected].portrait = characterInputText.text;
} else if(sender == lineInputText) {
reloadText(0);
dialogueFile.dialogue[curSelected].text = lineInputText.text;
}
} else if(id == FlxUINumericStepper.CHANGE_EVENT && (sender == speedStepper)) {
reloadText(speedStepper.value);
dialogueFile.dialogue[curSelected].speed = speedStepper.value;
if(Math.isNaN(dialogueFile.dialogue[curSelected].speed) || dialogueFile.dialogue[curSelected].speed == null || dialogueFile.dialogue[curSelected].speed < 0.001) {
dialogueFile.dialogue[curSelected].speed = 0.0;
}
}
}
var curSelected:Int = 0;
var curAnim:Int = 0;
var blockPressWhileTypingOn:Array<FlxUIInputText> = [];
var transitioning:Bool = false;
override function update(elapsed:Float) {
if(transitioning) {
super.update(elapsed);
return;
}
if(character.animation.curAnim != null) {
if(daText.finishedText) {
if(character.animationIsLoop() && character.animation.curAnim.finished) {
character.playAnim(character.animation.curAnim.name, true);
}
} else if(character.animation.curAnim.finished) {
character.animation.curAnim.restart();
}
}
var blockInput:Bool = false;
for (inputText in blockPressWhileTypingOn) {
if(inputText.hasFocus) {
FlxG.sound.muteKeys = [];
FlxG.sound.volumeDownKeys = [];
FlxG.sound.volumeUpKeys = [];
blockInput = true;
if(FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.V && Clipboard.text != null) { //Copy paste
inputText.text = ClipboardAdd(inputText.text);
inputText.caretIndex = inputText.text.length;
getEvent(FlxUIInputText.CHANGE_EVENT, inputText, null, []);
}
if(FlxG.keys.justPressed.ENTER) {
if(inputText == lineInputText) {
inputText.text += '\\n';
inputText.caretIndex += 2;
} else {
inputText.hasFocus = false;
}
}
break;
}
}
if(!blockInput) {
FlxG.sound.muteKeys = TitleState.muteKeys;
FlxG.sound.volumeDownKeys = TitleState.volumeDownKeys;
FlxG.sound.volumeUpKeys = TitleState.volumeUpKeys;
if(FlxG.keys.justPressed.SPACE) {
reloadText(speedStepper.value);
}
if(FlxG.keys.justPressed.ESCAPE) {
MusicBeatState.switchState(new editors.MasterEditorMenu());
FlxG.sound.playMusic(Paths.music('freakyMenu'), 1);
transitioning = true;
}
var negaMult:Array<Int> = [1, -1];
var controlAnim:Array<Bool> = [FlxG.keys.justPressed.W, FlxG.keys.justPressed.S];
var controlText:Array<Bool> = [FlxG.keys.justPressed.D, FlxG.keys.justPressed.A];
for (i in 0...controlAnim.length) {
if(controlAnim[i] && character.jsonFile.animations.length > 0) {
curAnim += negaMult[i];
if(curAnim < 0) curAnim = character.jsonFile.animations.length - 1;
else if(curAnim >= character.jsonFile.animations.length) curAnim = 0;
var animToPlay:String = character.jsonFile.animations[curAnim].anim;
if(character.dialogueAnimations.exists(animToPlay)) {
character.playAnim(animToPlay, daText.finishedText);
dialogueFile.dialogue[curSelected].expression = animToPlay;
}
animText.text = 'Animation: ' + animToPlay + ' (' + (curAnim + 1) +' / ' + character.jsonFile.animations.length + ') - Press W or S to scroll';
}
if(controlText[i]) {
changeText(negaMult[i]);
}
}
if(FlxG.keys.justPressed.O) {
dialogueFile.dialogue.remove(dialogueFile.dialogue[curSelected]);
if(dialogueFile.dialogue.length < 1) //You deleted everything, dumbo!
{
dialogueFile.dialogue = [
copyDefaultLine()
];
}
changeText();
} else if(FlxG.keys.justPressed.P) {
dialogueFile.dialogue.insert(curSelected + 1, copyDefaultLine());
changeText(1);
}
}
super.update(elapsed);
}
function changeText(add:Int = 0) {
curSelected += add;
if(curSelected < 0) curSelected = dialogueFile.dialogue.length - 1;
else if(curSelected >= dialogueFile.dialogue.length) curSelected = 0;
var curDialogue:DialogueLine = dialogueFile.dialogue[curSelected];
characterInputText.text = curDialogue.portrait;
lineInputText.text = curDialogue.text;
angryCheckbox.checked = (curDialogue.boxState == 'angry');
speedStepper.value = curDialogue.speed;
curAnim = 0;
character.reloadCharacterJson(characterInputText.text);
reloadCharacter();
updateTextBox();
reloadText(curDialogue.speed);
var leLength:Int = character.jsonFile.animations.length;
if(leLength > 0) {
for (i in 0...leLength) {
var leAnim:DialogueAnimArray = character.jsonFile.animations[i];
if(leAnim != null && leAnim.anim == curDialogue.expression) {
curAnim = i;
break;
}
}
character.playAnim(character.jsonFile.animations[curAnim].anim, daText.finishedText);
animText.text = 'Animation: ' + character.jsonFile.animations[curAnim].anim + ' (' + (curAnim + 1) +' / ' + leLength + ') - Press W or S to scroll';
} else {
animText.text = 'ERROR! NO ANIMATIONS FOUND';
}
characterAnimSpeed();
selectedText.text = 'Line: (' + (curSelected + 1) + ' / ' + dialogueFile.dialogue.length + ') - Press A or D to scroll';
}
function characterAnimSpeed() {
if(character.animation.curAnim != null) {
var speed:Float = speedStepper.value;
var rate:Float = 24 - (((speed - 0.05) / 5) * 480);
if(rate < 12) rate = 12;
else if(rate > 48) rate = 48;
character.animation.curAnim.frameRate = rate;
}
}
function ClipboardAdd(prefix:String = ''):String {
if(prefix.toLowerCase().endsWith('v')) //probably copy paste attempt
{
prefix = prefix.substring(0, prefix.length-1);
}
var text:String = prefix + Clipboard.text.replace('\n', '');
return text;
}
var _file:FileReference = null;
function loadDialogue() {
var jsonFilter:FileFilter = new FileFilter('JSON', 'json');
_file = new FileReference();
_file.addEventListener(Event.SELECT, onLoadComplete);
_file.addEventListener(Event.CANCEL, onLoadCancel);
_file.addEventListener(IOErrorEvent.IO_ERROR, onLoadError);
_file.browse([jsonFilter]);
}
function onLoadComplete(_):Void
{
_file.removeEventListener(Event.SELECT, onLoadComplete);
_file.removeEventListener(Event.CANCEL, onLoadCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
#if sys
var fullPath:String = null;
var jsonLoaded = cast Json.parse(Json.stringify(_file)); //Exploit(???) for accessing a private variable
if(jsonLoaded.__path != null) fullPath = jsonLoaded.__path; //I'm either a genious or dangerously dumb
if(fullPath != null) {
var rawJson:String = File.getContent(fullPath);
if(rawJson != null) {
var loadedDialog:DialogueFile = cast Json.parse(rawJson);
if(loadedDialog.dialogue != null && loadedDialog.dialogue.length > 0) //Make sure it's really a dialogue file
{
var cutName:String = _file.name.substr(0, _file.name.length - 5);
trace("Successfully loaded file: " + cutName);
dialogueFile = loadedDialog;
changeText();
_file = null;
return;
}
}
}
_file = null;
#else
trace("File couldn't be loaded! You aren't on Desktop, are you?");
#end
}
/**
* Called when the save file dialog is cancelled.
*/
function onLoadCancel(_):Void
{
_file.removeEventListener(Event.SELECT, onLoadComplete);
_file.removeEventListener(Event.CANCEL, onLoadCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
_file = null;
trace("Cancelled file loading.");
}
/**
* Called if there is an error while saving the gameplay recording.
*/
function onLoadError(_):Void
{
_file.removeEventListener(Event.SELECT, onLoadComplete);
_file.removeEventListener(Event.CANCEL, onLoadCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
_file = null;
trace("Problem loading file");
}
function saveDialogue() {
var data:String = Json.stringify(dialogueFile, "\t");
if (data.length > 0)
{
_file = new FileReference();
_file.addEventListener(Event.COMPLETE, onSaveComplete);
_file.addEventListener(Event.CANCEL, onSaveCancel);
_file.addEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file.save(data, "dialogue.json");
}
}
function onSaveComplete(_):Void
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
FlxG.log.notice("Successfully saved file.");
}
/**
* Called when the save file dialog is cancelled.
*/
function onSaveCancel(_):Void
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
}
/**
* Called if there is an error while saving the gameplay recording.
*/
function onSaveError(_):Void
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
FlxG.log.error("Problem saving file");
}
}

267
source/editors/EditorLua.hx Normal file
View file

@ -0,0 +1,267 @@
package editors;
#if LUA_ALLOWED
import llua.Lua;
import llua.LuaL;
import llua.State;
import llua.Convert;
#end
import flixel.FlxG;
import flixel.tweens.FlxTween;
import flixel.tweens.FlxEase;
import flixel.text.FlxText;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.math.FlxPoint;
import flixel.system.FlxSound;
import flixel.util.FlxTimer;
import flixel.FlxSprite;
import flixel.FlxCamera;
import flixel.util.FlxColor;
import flixel.FlxBasic;
#if sys
import sys.FileSystem;
import sys.io.File;
#end
import Type.ValueType;
import Controls;
import DialogueBoxPsych;
using StringTools;
class EditorLua {
public static var Function_Stop = 1;
public static var Function_Continue = 0;
#if LUA_ALLOWED
public var lua:State = null;
#end
var lePlayState:editors.EditorPlayState = null;
public function new(script:String) {
#if LUA_ALLOWED
lua = LuaL.newstate();
LuaL.openlibs(lua);
Lua.init_callbacks(lua);
//trace('Lua version: ' + Lua.version());
//trace("LuaJIT version: " + Lua.versionJIT());
var result:Dynamic = LuaL.dofile(lua, script);
var resultStr:String = Lua.tostring(lua, result);
if(resultStr != null && result != 0) {
lime.app.Application.current.window.alert(resultStr, 'Error on .LUA script!');
trace('Error on .LUA script! ' + resultStr);
lua = null;
return;
}
trace('Lua file loaded succesfully:' + script);
var curState:Dynamic = FlxG.state;
lePlayState = curState;
// Lua variables
set('Function_Stop', Function_Stop);
set('Function_Continue', Function_Continue);
set('inChartEditor', true);
set('curBpm', Conductor.bpm);
set('bpm', PlayState.SONG.bpm);
set('scrollSpeed', PlayState.SONG.speed);
set('crochet', Conductor.crochet);
set('stepCrochet', Conductor.stepCrochet);
set('songLength', FlxG.sound.music.length);
set('songName', PlayState.SONG.song);
set('screenWidth', FlxG.width);
set('screenHeight', FlxG.height);
for (i in 0...4) {
set('defaultPlayerStrumX' + i, 0);
set('defaultPlayerStrumY' + i, 0);
set('defaultOpponentStrumX' + i, 0);
set('defaultOpponentStrumY' + i, 0);
}
set('downscroll', ClientPrefs.downScroll);
set('middlescroll', ClientPrefs.middleScroll);
//stuff 4 noobz like you B)
Lua_helper.add_callback(lua, "getProperty", function(variable:String) {
var killMe:Array<String> = variable.split('.');
if(killMe.length > 1) {
var coverMeInPiss:Dynamic = Reflect.getProperty(lePlayState, killMe[0]);
for (i in 1...killMe.length-1) {
coverMeInPiss = Reflect.getProperty(coverMeInPiss, killMe[i]);
}
return Reflect.getProperty(coverMeInPiss, killMe[killMe.length-1]);
}
return Reflect.getProperty(lePlayState, variable);
});
Lua_helper.add_callback(lua, "setProperty", function(variable:String, value:Dynamic) {
var killMe:Array<String> = variable.split('.');
if(killMe.length > 1) {
var coverMeInPiss:Dynamic = Reflect.getProperty(lePlayState, killMe[0]);
for (i in 1...killMe.length-1) {
coverMeInPiss = Reflect.getProperty(coverMeInPiss, killMe[i]);
}
return Reflect.setProperty(coverMeInPiss, killMe[killMe.length-1], value);
}
return Reflect.setProperty(lePlayState, variable, value);
});
Lua_helper.add_callback(lua, "getPropertyFromGroup", function(obj:String, index:Int, variable:Dynamic) {
if(Std.isOfType(Reflect.getProperty(lePlayState, obj), FlxTypedGroup)) {
return Reflect.getProperty(Reflect.getProperty(lePlayState, obj).members[index], variable);
}
var leArray:Dynamic = Reflect.getProperty(lePlayState, obj)[index];
if(leArray != null) {
if(Type.typeof(variable) == ValueType.TInt) {
return leArray[variable];
}
return Reflect.getProperty(leArray, variable);
}
return null;
});
Lua_helper.add_callback(lua, "setPropertyFromGroup", function(obj:String, index:Int, variable:Dynamic, value:Dynamic) {
if(Std.isOfType(Reflect.getProperty(lePlayState, obj), FlxTypedGroup)) {
return Reflect.setProperty(Reflect.getProperty(lePlayState, obj).members[index], variable, value);
}
var leArray:Dynamic = Reflect.getProperty(lePlayState, obj)[index];
if(leArray != null) {
if(Type.typeof(variable) == ValueType.TInt) {
return leArray[variable] = value;
}
return Reflect.setProperty(leArray, variable, value);
}
});
Lua_helper.add_callback(lua, "removeFromGroup", function(obj:String, index:Int, dontDestroy:Bool = false) {
if(Std.isOfType(Reflect.getProperty(lePlayState, obj), FlxTypedGroup)) {
var sex = Reflect.getProperty(lePlayState, obj).members[index];
if(!dontDestroy)
sex.kill();
Reflect.getProperty(lePlayState, obj).remove(sex, true);
if(!dontDestroy)
sex.destroy();
return;
}
Reflect.getProperty(lePlayState, obj).remove(Reflect.getProperty(lePlayState, obj)[index]);
});
Lua_helper.add_callback(lua, "getColorFromHex", function(color:String) {
if(!color.startsWith('0x')) color = '0xff' + color;
return Std.parseInt(color);
});
Lua_helper.add_callback(lua, "setGraphicSize", function(obj:String, x:Int, y:Int = 0) {
var poop:FlxSprite = Reflect.getProperty(lePlayState, obj);
if(poop != null) {
poop.setGraphicSize(x, y);
poop.updateHitbox();
return;
}
});
Lua_helper.add_callback(lua, "scaleObject", function(obj:String, x:Float, y:Float) {
var poop:FlxSprite = Reflect.getProperty(lePlayState, obj);
if(poop != null) {
poop.scale.set(x, y);
poop.updateHitbox();
return;
}
});
Lua_helper.add_callback(lua, "updateHitbox", function(obj:String) {
var poop:FlxSprite = Reflect.getProperty(lePlayState, obj);
if(poop != null) {
poop.updateHitbox();
return;
}
});
call('onCreate', []);
#end
}
public function call(event:String, args:Array<Dynamic>):Dynamic {
#if LUA_ALLOWED
if(lua == null) {
return Function_Continue;
}
Lua.getglobal(lua, event);
for (arg in args) {
Convert.toLua(lua, arg);
}
var result:Null<Int> = Lua.pcall(lua, args.length, 1, 0);
if(result != null && resultIsAllowed(lua, result)) {
/*var resultStr:String = Lua.tostring(lua, result);
var error:String = Lua.tostring(lua, -1);
Lua.pop(lua, 1);*/
if(Lua.type(lua, -1) == Lua.LUA_TSTRING) {
var error:String = Lua.tostring(lua, -1);
Lua.pop(lua, 1);
if(error == 'attempt to call a nil value') { //Makes it ignore warnings and not break stuff if you didn't put the functions on your lua file
return Function_Continue;
}
}
var conv:Dynamic = Convert.fromLua(lua, result);
return conv;
}
#end
return Function_Continue;
}
#if LUA_ALLOWED
function resultIsAllowed(leLua:State, leResult:Null<Int>) { //Makes it ignore warnings
switch(Lua.type(leLua, leResult)) {
case Lua.LUA_TNIL | Lua.LUA_TBOOLEAN | Lua.LUA_TNUMBER | Lua.LUA_TSTRING | Lua.LUA_TTABLE:
return true;
}
return false;
}
#end
public function set(variable:String, data:Dynamic) {
#if LUA_ALLOWED
if(lua == null) {
return;
}
Convert.toLua(lua, data);
Lua.setglobal(lua, variable);
#end
}
#if LUA_ALLOWED
public function getBool(variable:String) {
var result:String = null;
Lua.getglobal(lua, variable);
result = Convert.fromLua(lua, -1);
Lua.pop(lua, 1);
if(result == null) {
return false;
}
// YES! FINALLY IT WORKS
//trace('variable: ' + variable + ', ' + result);
return (result == 'true');
}
#end
public function stop() {
#if LUA_ALLOWED
if(lua == null) {
return;
}
Lua.close(lua);
lua = null;
#end
}
}

View file

@ -0,0 +1,878 @@
package editors;
import Section.SwagSection;
import Song.SwagSong;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.addons.transition.FlxTransitionableState;
import flixel.util.FlxColor;
import flixel.FlxSprite;
import flixel.FlxG;
import flixel.text.FlxText;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.math.FlxMath;
import flixel.math.FlxPoint;
import flixel.math.FlxRect;
import flixel.system.FlxSound;
import flixel.util.FlxSort;
import flixel.util.FlxTimer;
import FunkinLua;
using StringTools;
class EditorPlayState extends MusicBeatState
{
// Yes, this is mostly a copy of PlayState, it's kinda dumb to make a direct copy of it but... ehhh
private var strumLine:FlxSprite;
private var comboGroup:FlxTypedGroup<FlxSprite>;
public var strumLineNotes:FlxTypedGroup<StrumNote>;
public var opponentStrums:FlxTypedGroup<StrumNote>;
public var playerStrums:FlxTypedGroup<StrumNote>;
public var grpNoteSplashes:FlxTypedGroup<NoteSplash>;
public var notes:FlxTypedGroup<Note>;
public var unspawnNotes:Array<Note> = [];
var generatedMusic:Bool = false;
var vocals:FlxSound;
var startOffset:Float = 0;
var startPos:Float = 0;
public function new(startPos:Float) {
this.startPos = startPos;
Conductor.songPosition = startPos - startOffset;
startOffset = Conductor.crochet;
timerToStart = startOffset;
super();
}
var scoreTxt:FlxText;
var timerToStart:Float = 0;
private var noteTypeMap:Map<String, Bool> = new Map<String, Bool>();
override function create()
{
var bg:FlxSprite = new FlxSprite().loadGraphic(Paths.image('menuDesat'));
bg.scrollFactor.set();
bg.color = FlxColor.fromHSB(FlxG.random.int(0, 359), FlxG.random.float(0, 0.8), FlxG.random.float(0.3, 1));
add(bg);
strumLine = new FlxSprite(ClientPrefs.middleScroll ? PlayState.STRUM_X_MIDDLESCROLL : PlayState.STRUM_X, 50).makeGraphic(FlxG.width, 10);
if(ClientPrefs.downScroll) strumLine.y = FlxG.height - 150;
strumLine.scrollFactor.set();
comboGroup = new FlxTypedGroup<FlxSprite>();
add(comboGroup);
strumLineNotes = new FlxTypedGroup<StrumNote>();
opponentStrums = new FlxTypedGroup<StrumNote>();
playerStrums = new FlxTypedGroup<StrumNote>();
add(strumLineNotes);
generateStaticArrows(0);
generateStaticArrows(1);
if(ClientPrefs.middleScroll) {
opponentStrums.forEachAlive(function (note:StrumNote) {
note.visible = false;
});
}
grpNoteSplashes = new FlxTypedGroup<NoteSplash>();
add(grpNoteSplashes);
var splash:NoteSplash = new NoteSplash(100, 100, 0);
grpNoteSplashes.add(splash);
splash.alpha = 0.0;
if (PlayState.SONG.needsVoices)
vocals = new FlxSound().loadEmbedded(Paths.voices(PlayState.SONG.song));
else
vocals = new FlxSound();
generateSong(PlayState.SONG.song);
#if LUA_ALLOWED
for (notetype in noteTypeMap.keys()) {
var luaToLoad:String = Paths.modFolders('custom_notetypes/' + notetype + '.lua');
if(sys.FileSystem.exists(luaToLoad)) {
var lua:editors.EditorLua = new editors.EditorLua(luaToLoad);
new FlxTimer().start(0.1, function (tmr:FlxTimer) {
lua.stop();
lua = null;
});
}
}
#end
noteTypeMap.clear();
noteTypeMap = null;
scoreTxt = new FlxText(0, FlxG.height - 50, FlxG.width, "Hits: 0 | Misses: 0", 20);
scoreTxt.setFormat(Paths.font("vcr.ttf"), 20, FlxColor.WHITE, CENTER, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
scoreTxt.scrollFactor.set();
scoreTxt.borderSize = 1.25;
scoreTxt.visible = !ClientPrefs.hideHud;
add(scoreTxt);
var tipText:FlxText = new FlxText(10, FlxG.height - 24, 0, 'Press ESC to Go Back to Chart Editor', 16);
tipText.setFormat(Paths.font("vcr.ttf"), 16, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
tipText.borderSize = 2;
tipText.scrollFactor.set();
add(tipText);
FlxG.mouse.visible = false;
//sayGo();
super.create();
}
function sayGo() {
var go:FlxSprite = new FlxSprite().loadGraphic(Paths.image('go'));
go.scrollFactor.set();
go.updateHitbox();
go.screenCenter();
go.antialiasing = ClientPrefs.globalAntialiasing;
add(go);
FlxTween.tween(go, {y: go.y += 100, alpha: 0}, Conductor.crochet / 1000, {
ease: FlxEase.cubeInOut,
onComplete: function(twn:FlxTween)
{
go.destroy();
}
});
FlxG.sound.play(Paths.sound('introGo'), 0.6);
}
//var songScore:Int = 0;
var songHits:Int = 0;
var songMisses:Int = 0;
var ghostMisses:Int = 0;
var startingSong:Bool = true;
private function generateSong(dataPath:String):Void
{
FlxG.sound.playMusic(Paths.inst(PlayState.SONG.song), 0, false);
FlxG.sound.music.pause();
FlxG.sound.music.onComplete = endSong;
vocals.pause();
vocals.volume = 0;
var songData = PlayState.SONG;
Conductor.changeBPM(songData.bpm);
notes = new FlxTypedGroup<Note>();
add(notes);
var noteData:Array<SwagSection>;
// NEW SHIT
noteData = songData.notes;
var playerCounter:Int = 0;
var daBeats:Int = 0; // Not exactly representative of 'daBeats' lol, just how much it has looped
for (section in noteData)
{
for (songNotes in section.sectionNotes)
{
if(songNotes[1] > -1) { //Real notes
var daStrumTime:Float = songNotes[0];
if(daStrumTime >= startPos) {
var daNoteData:Int = Std.int(songNotes[1] % 4);
var gottaHitNote:Bool = section.mustHitSection;
if (songNotes[1] > 3)
{
gottaHitNote = !section.mustHitSection;
}
var oldNote:Note;
if (unspawnNotes.length > 0)
oldNote = unspawnNotes[Std.int(unspawnNotes.length - 1)];
else
oldNote = null;
var swagNote:Note = new Note(daStrumTime, daNoteData, oldNote);
swagNote.mustPress = gottaHitNote;
swagNote.sustainLength = songNotes[2];
swagNote.noteType = songNotes[3];
if(!Std.isOfType(songNotes[3], String)) swagNote.noteType = editors.ChartingState.noteTypeList[songNotes[3]]; //Backward compatibility + compatibility with Week 7 charts
swagNote.scrollFactor.set();
var susLength:Float = swagNote.sustainLength;
susLength = susLength / Conductor.stepCrochet;
unspawnNotes.push(swagNote);
var floorSus:Int = Math.floor(susLength);
if(floorSus > 0) {
for (susNote in 0...floorSus+1)
{
oldNote = unspawnNotes[Std.int(unspawnNotes.length - 1)];
var sustainNote:Note = new Note(daStrumTime + (Conductor.stepCrochet * susNote) + (Conductor.stepCrochet / FlxMath.roundDecimal(PlayState.SONG.speed, 2)), daNoteData, oldNote, true);
sustainNote.mustPress = gottaHitNote;
sustainNote.noteType = swagNote.noteType;
sustainNote.scrollFactor.set();
unspawnNotes.push(sustainNote);
if (sustainNote.mustPress)
{
sustainNote.x += FlxG.width / 2; // general offset
}
}
}
if (swagNote.mustPress)
{
swagNote.x += FlxG.width / 2; // general offset
}
else {}
if(!noteTypeMap.exists(swagNote.noteType)) {
noteTypeMap.set(swagNote.noteType, true);
}
}
}
}
daBeats += 1;
}
unspawnNotes.sort(sortByShit);
generatedMusic = true;
}
function startSong():Void
{
startingSong = false;
FlxG.sound.music.time = startPos;
FlxG.sound.music.play();
FlxG.sound.music.volume = 1;
vocals.volume = 1;
vocals.time = startPos;
vocals.play();
}
function sortByShit(Obj1:Note, Obj2:Note):Int
{
return FlxSort.byValues(FlxSort.ASCENDING, Obj1.strumTime, Obj2.strumTime);
}
private function endSong() {
LoadingState.loadAndSwitchState(new editors.ChartingState());
}
override function update(elapsed:Float) {
if (FlxG.keys.justPressed.ESCAPE)
{
FlxG.sound.music.pause();
vocals.pause();
LoadingState.loadAndSwitchState(new editors.ChartingState());
}
if (startingSong) {
timerToStart -= elapsed * 1000;
Conductor.songPosition = startPos - timerToStart;
if(timerToStart < 0) {
startSong();
}
} else {
Conductor.songPosition += elapsed * 1000;
}
var roundedSpeed:Float = FlxMath.roundDecimal(PlayState.SONG.speed, 2);
if (unspawnNotes[0] != null)
{
var time:Float = 1500;
if(roundedSpeed < 1) time /= roundedSpeed;
while (unspawnNotes.length > 0 && unspawnNotes[0].strumTime - Conductor.songPosition < time)
{
var dunceNote:Note = unspawnNotes[0];
notes.add(dunceNote);
var index:Int = unspawnNotes.indexOf(dunceNote);
unspawnNotes.splice(index, 1);
}
}
if (generatedMusic)
{
var fakeCrochet:Float = (60 / PlayState.SONG.bpm) * 1000;
notes.forEachAlive(function(daNote:Note)
{
if(!daNote.mustPress && ClientPrefs.middleScroll)
{
daNote.active = true;
daNote.visible = false;
}
else if (daNote.y > FlxG.height)
{
daNote.active = false;
daNote.visible = false;
}
else
{
daNote.visible = true;
daNote.active = true;
}
// i am so fucking sorry for this if condition
var strumX:Float = 0;
var strumY:Float = 0;
if(daNote.mustPress) {
strumX = playerStrums.members[daNote.noteData].x;
strumY = playerStrums.members[daNote.noteData].y;
} else {
strumX = opponentStrums.members[daNote.noteData].x;
strumY = opponentStrums.members[daNote.noteData].y;
}
strumX += daNote.offsetX;
strumY += daNote.offsetY;
var center:Float = strumY + Note.swagWidth / 2;
if(daNote.copyX) {
daNote.x = strumX;
}
if(daNote.copyY) {
if (ClientPrefs.downScroll) {
daNote.y = (strumY + 0.45 * (Conductor.songPosition - daNote.strumTime) * roundedSpeed);
if (daNote.isSustainNote) {
//Jesus fuck this took me so much mother fucking time AAAAAAAAAA
if (daNote.animation.curAnim.name.endsWith('end')) {
daNote.y += 10.5 * (fakeCrochet / 400) * 1.5 * roundedSpeed + (46 * (roundedSpeed - 1));
daNote.y -= 46 * (1 - (fakeCrochet / 600)) * roundedSpeed;
if(PlayState.isPixelStage) {
daNote.y += 8;
} else {
daNote.y -= 19;
}
}
daNote.y += (Note.swagWidth / 2) - (60.5 * (roundedSpeed - 1));
daNote.y += 27.5 * ((PlayState.SONG.bpm / 100) - 1) * (roundedSpeed - 1);
if(daNote.mustPress || !daNote.ignoreNote)
{
if(daNote.y - daNote.offset.y * daNote.scale.y + daNote.height >= center
&& (!daNote.mustPress || (daNote.wasGoodHit || (daNote.prevNote.wasGoodHit && !daNote.canBeHit))))
{
var swagRect = new FlxRect(0, 0, daNote.frameWidth, daNote.frameHeight);
swagRect.height = (center - daNote.y) / daNote.scale.y;
swagRect.y = daNote.frameHeight - swagRect.height;
daNote.clipRect = swagRect;
}
}
}
} else {
daNote.y = (strumY - 0.45 * (Conductor.songPosition - daNote.strumTime) * roundedSpeed);
if(daNote.mustPress || !daNote.ignoreNote)
{
if (daNote.isSustainNote
&& daNote.y + daNote.offset.y * daNote.scale.y <= center
&& (!daNote.mustPress || (daNote.wasGoodHit || (daNote.prevNote.wasGoodHit && !daNote.canBeHit))))
{
var swagRect = new FlxRect(0, 0, daNote.width / daNote.scale.x, daNote.height / daNote.scale.y);
swagRect.y = (center - daNote.y) / daNote.scale.y;
swagRect.height -= swagRect.y;
daNote.clipRect = swagRect;
}
}
}
}
if (!daNote.mustPress && daNote.wasGoodHit && !daNote.hitByOpponent && !daNote.ignoreNote)
{
if (PlayState.SONG.needsVoices)
vocals.volume = 1;
var time:Float = 0.15;
if(daNote.isSustainNote && !daNote.animation.curAnim.name.endsWith('end')) {
time += 0.15;
}
StrumPlayAnim(true, Std.int(Math.abs(daNote.noteData)) % 4, time);
daNote.hitByOpponent = true;
if (!daNote.isSustainNote)
{
daNote.kill();
notes.remove(daNote, true);
daNote.destroy();
}
}
var doKill:Bool = daNote.y < -daNote.height;
if(ClientPrefs.downScroll) doKill = daNote.y > FlxG.height;
if (doKill)
{
if (daNote.mustPress)
{
if (daNote.tooLate || !daNote.wasGoodHit)
{
//Dupe note remove
notes.forEachAlive(function(note:Note) {
if (daNote != note && daNote.mustPress && daNote.noteData == note.noteData && daNote.isSustainNote == note.isSustainNote && Math.abs(daNote.strumTime - note.strumTime) < 10) {
note.kill();
notes.remove(note, true);
note.destroy();
}
});
if(!daNote.ignoreNote) {
songMisses++;
vocals.volume = 0;
}
}
}
daNote.active = false;
daNote.visible = false;
daNote.kill();
notes.remove(daNote, true);
daNote.destroy();
}
});
}
keyShit();
scoreTxt.text = 'Hits: ' + songHits + ' | Misses: ' + songMisses;
super.update(elapsed);
}
override function beatHit()
{
super.beatHit();
if (generatedMusic)
{
notes.sort(FlxSort.byY, ClientPrefs.downScroll ? FlxSort.ASCENDING : FlxSort.DESCENDING);
}
}
override function stepHit()
{
super.stepHit();
if (FlxG.sound.music.time > Conductor.songPosition + 20 || FlxG.sound.music.time < Conductor.songPosition - 20)
{
resyncVocals();
}
}
function resyncVocals():Void
{
vocals.pause();
FlxG.sound.music.play();
Conductor.songPosition = FlxG.sound.music.time;
vocals.time = Conductor.songPosition;
vocals.play();
}
function keyShit() {
// HOLDING
var up = controls.NOTE_UP;
var right = controls.NOTE_RIGHT;
var down = controls.NOTE_DOWN;
var left = controls.NOTE_LEFT;
var upP = controls.NOTE_UP_P;
var rightP = controls.NOTE_RIGHT_P;
var downP = controls.NOTE_DOWN_P;
var leftP = controls.NOTE_LEFT_P;
var upR = controls.NOTE_UP_R;
var rightR = controls.NOTE_RIGHT_R;
var downR = controls.NOTE_DOWN_R;
var leftR = controls.NOTE_LEFT_R;
var controlArray:Array<Bool> = [leftP, downP, upP, rightP];
var controlReleaseArray:Array<Bool> = [leftR, downR, upR, rightR];
var controlHoldArray:Array<Bool> = [left, down, up, right];
// FlxG.watch.addQuick('asdfa', upP);
if (generatedMusic)
{
// rewritten inputs???
notes.forEachAlive(function(daNote:Note)
{
// hold note functions
if (daNote.isSustainNote && controlHoldArray[daNote.noteData] && daNote.canBeHit
&& daNote.mustPress && !daNote.tooLate && !daNote.wasGoodHit) {
goodNoteHit(daNote);
}
});
if (controlHoldArray.contains(true) || controlArray.contains(true)) {
var canMiss:Bool = !ClientPrefs.ghostTapping;
if (controlArray.contains(true)) {
for (i in 0...controlArray.length) {
// heavily based on my own code LOL if it aint broke dont fix it
var pressNotes:Array<Note> = [];
var notesDatas:Array<Int> = [];
var notesStopped:Bool = false;
var sortedNotesList:Array<Note> = [];
notes.forEachAlive(function(daNote:Note)
{
if (daNote.canBeHit && daNote.mustPress && !daNote.tooLate
&& !daNote.wasGoodHit && daNote.noteData == i) {
sortedNotesList.push(daNote);
notesDatas.push(daNote.noteData);
canMiss = true;
}
});
sortedNotesList.sort((a, b) -> Std.int(a.strumTime - b.strumTime));
if (sortedNotesList.length > 0) {
for (epicNote in sortedNotesList)
{
for (doubleNote in pressNotes) {
if (Math.abs(doubleNote.strumTime - epicNote.strumTime) < 10) {
doubleNote.kill();
notes.remove(doubleNote, true);
doubleNote.destroy();
} else
notesStopped = true;
}
// eee jack detection before was not super good
if (controlArray[epicNote.noteData] && !notesStopped) {
goodNoteHit(epicNote);
pressNotes.push(epicNote);
}
}
}
else if (canMiss && controlArray[i])
noteMiss(i, true);
}
}
}
}
playerStrums.forEach(function(spr:StrumNote)
{
if(controlArray[spr.ID] && spr.animation.curAnim.name != 'confirm') {
spr.playAnim('pressed');
spr.resetAnim = 0;
}
if(controlReleaseArray[spr.ID]) {
spr.playAnim('static');
spr.resetAnim = 0;
}
});
}
var combo:Int = 0;
function goodNoteHit(note:Note):Void
{
if (!note.wasGoodHit)
{
switch(note.noteType) {
case 'Hurt Note': //Hurt note
noteMiss(note.noteData);
--songMisses;
if(!note.isSustainNote) {
if(!note.noteSplashDisabled) {
spawnNoteSplashOnNote(note);
}
}
note.wasGoodHit = true;
vocals.volume = 0;
if (!note.isSustainNote)
{
note.kill();
notes.remove(note, true);
note.destroy();
}
return;
}
if (!note.isSustainNote)
{
popUpScore(note);
combo += 1;
songHits++;
if(combo > 9999) combo = 9999;
}
playerStrums.forEach(function(spr:StrumNote)
{
if (Math.abs(note.noteData) == spr.ID)
{
spr.playAnim('confirm', true);
}
});
note.wasGoodHit = true;
vocals.volume = 1;
if (!note.isSustainNote)
{
note.kill();
notes.remove(note, true);
note.destroy();
}
}
}
function noteMiss(direction:Int = 1, ?ghostMiss:Bool = false):Void
{
combo = 0;
//songScore -= 10;
if(ghostMiss) ghostMisses++;
songMisses++;
FlxG.sound.play(Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2));
vocals.volume = 0;
}
var COMBO_X:Float = 400;
var COMBO_Y:Float = 340;
private function popUpScore(note:Note = null):Void
{
var noteDiff:Float = Math.abs(note.strumTime - Conductor.songPosition + 8);
vocals.volume = 1;
var placement:String = Std.string(combo);
var coolText:FlxText = new FlxText(0, 0, 0, placement, 32);
coolText.x = COMBO_X;
coolText.y = COMBO_Y;
//
var rating:FlxSprite = new FlxSprite();
//var score:Int = 350;
var daRating:String = "sick";
if (noteDiff > Conductor.safeZoneOffset * 0.75)
{
daRating = 'shit';
//score = 50;
}
else if (noteDiff > Conductor.safeZoneOffset * 0.5)
{
daRating = 'bad';
//score = 100;
}
else if (noteDiff > Conductor.safeZoneOffset * 0.25)
{
daRating = 'good';
//score = 200;
}
if(daRating == 'sick' && !note.noteSplashDisabled)
{
spawnNoteSplashOnNote(note);
}
//songScore += score;
/* if (combo > 60)
daRating = 'sick';
else if (combo > 12)
daRating = 'good'
else if (combo > 4)
daRating = 'bad';
*/
var pixelShitPart1:String = "";
var pixelShitPart2:String = '';
if (PlayState.isPixelStage)
{
pixelShitPart1 = 'pixelUI/';
pixelShitPart2 = '-pixel';
}
rating.loadGraphic(Paths.image(pixelShitPart1 + daRating + pixelShitPart2));
rating.screenCenter();
rating.x = coolText.x - 40;
rating.y -= 60;
rating.acceleration.y = 550;
rating.velocity.y -= FlxG.random.int(140, 175);
rating.velocity.x -= FlxG.random.int(0, 10);
rating.visible = !ClientPrefs.hideHud;
var comboSpr:FlxSprite = new FlxSprite().loadGraphic(Paths.image(pixelShitPart1 + 'combo' + pixelShitPart2));
comboSpr.screenCenter();
comboSpr.x = coolText.x;
comboSpr.acceleration.y = 600;
comboSpr.velocity.y -= 150;
comboSpr.visible = !ClientPrefs.hideHud;
comboSpr.velocity.x += FlxG.random.int(1, 10);
comboGroup.add(rating);
if (!PlayState.isPixelStage)
{
rating.setGraphicSize(Std.int(rating.width * 0.7));
rating.antialiasing = ClientPrefs.globalAntialiasing;
comboSpr.setGraphicSize(Std.int(comboSpr.width * 0.7));
comboSpr.antialiasing = ClientPrefs.globalAntialiasing;
}
else
{
rating.setGraphicSize(Std.int(rating.width * PlayState.daPixelZoom * 0.7));
comboSpr.setGraphicSize(Std.int(comboSpr.width * PlayState.daPixelZoom * 0.7));
}
comboSpr.updateHitbox();
rating.updateHitbox();
var seperatedScore:Array<Int> = [];
if(combo >= 1000) {
seperatedScore.push(Math.floor(combo / 1000) % 10);
}
seperatedScore.push(Math.floor(combo / 100) % 10);
seperatedScore.push(Math.floor(combo / 10) % 10);
seperatedScore.push(combo % 10);
var daLoop:Int = 0;
for (i in seperatedScore)
{
var numScore:FlxSprite = new FlxSprite().loadGraphic(Paths.image(pixelShitPart1 + 'num' + Std.int(i) + pixelShitPart2));
numScore.screenCenter();
numScore.x = coolText.x + (43 * daLoop) - 90;
numScore.y += 80;
if (!PlayState.isPixelStage)
{
numScore.antialiasing = ClientPrefs.globalAntialiasing;
numScore.setGraphicSize(Std.int(numScore.width * 0.5));
}
else
{
numScore.setGraphicSize(Std.int(numScore.width * PlayState.daPixelZoom));
}
numScore.updateHitbox();
numScore.acceleration.y = FlxG.random.int(200, 300);
numScore.velocity.y -= FlxG.random.int(140, 160);
numScore.velocity.x = FlxG.random.float(-5, 5);
numScore.visible = !ClientPrefs.hideHud;
if (combo >= 10 || combo == 0)
comboGroup.add(numScore);
FlxTween.tween(numScore, {alpha: 0}, 0.2, {
onComplete: function(tween:FlxTween)
{
numScore.destroy();
},
startDelay: Conductor.crochet * 0.002
});
daLoop++;
}
/*
trace(combo);
trace(seperatedScore);
*/
coolText.text = Std.string(seperatedScore);
// comboGroup.add(coolText);
FlxTween.tween(rating, {alpha: 0}, 0.2, {
startDelay: Conductor.crochet * 0.001
});
FlxTween.tween(comboSpr, {alpha: 0}, 0.2, {
onComplete: function(tween:FlxTween)
{
coolText.destroy();
comboSpr.destroy();
rating.destroy();
},
startDelay: Conductor.crochet * 0.001
});
}
private function generateStaticArrows(player:Int):Void
{
for (i in 0...4)
{
// FlxG.log.add(i);
var babyArrow:StrumNote = new StrumNote(ClientPrefs.middleScroll ? PlayState.STRUM_X_MIDDLESCROLL : PlayState.STRUM_X, strumLine.y, i, player);
babyArrow.alpha = 0;
FlxTween.tween(babyArrow, {alpha: 1}, 0.5, {ease: FlxEase.circOut});
if (player == 1)
{
playerStrums.add(babyArrow);
}
else
{
opponentStrums.add(babyArrow);
}
strumLineNotes.add(babyArrow);
babyArrow.postAddedToGroup();
}
}
// For Opponent's notes glow
function StrumPlayAnim(isDad:Bool, id:Int, time:Float) {
var spr:StrumNote = null;
if(isDad) {
spr = strumLineNotes.members[id];
} else {
spr = playerStrums.members[id];
}
if(spr != null) {
spr.playAnim('confirm', true);
spr.resetAnim = time;
}
}
// Note splash shit, duh
function spawnNoteSplashOnNote(note:Note) {
if(ClientPrefs.noteSplashes && note != null) {
var strum:StrumNote = playerStrums.members[note.noteData];
if(strum != null) {
spawnNoteSplash(strum.x, strum.y, note.noteData, note);
}
}
}
function spawnNoteSplash(x:Float, y:Float, data:Int, ?note:Note = null) {
var skin:String = 'noteSplashes';
if(PlayState.SONG.splashSkin != null && PlayState.SONG.splashSkin.length > 0) skin = PlayState.SONG.splashSkin;
var hue:Float = ClientPrefs.arrowHSV[data % 4][0] / 360;
var sat:Float = ClientPrefs.arrowHSV[data % 4][1] / 100;
var brt:Float = ClientPrefs.arrowHSV[data % 4][2] / 100;
if(note != null) {
skin = note.noteSplashTexture;
hue = note.noteSplashHue;
sat = note.noteSplashSat;
brt = note.noteSplashBrt;
}
var splash:NoteSplash = grpNoteSplashes.recycle(NoteSplash);
splash.setupNoteSplash(x, y, data, skin, hue, sat, brt);
grpNoteSplashes.add(splash);
}
override function destroy() {
FlxG.sound.music.stop();
vocals.stop();
vocals.destroy();
super.destroy();
}
}

View file

@ -0,0 +1,122 @@
package editors;
#if desktop
import Discord.DiscordClient;
#end
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.addons.display.FlxGridOverlay;
import flixel.addons.transition.FlxTransitionableState;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.math.FlxMath;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import flixel.system.FlxSound;
using StringTools;
class MasterEditorMenu extends MusicBeatState
{
var options:Array<String> = [
'Week Editor',
'Menu Character Editor',
'Dialogue Editor',
'Dialogue Portrait Editor',
'Character Editor'
];
private var grpTexts:FlxTypedGroup<Alphabet>;
private var curSelected = 0;
override function create()
{
FlxG.camera.bgColor = FlxColor.BLACK;
#if desktop
// Updating Discord Rich Presence
DiscordClient.changePresence("Editors Main Menu", null);
#end
var bg:FlxSprite = new FlxSprite().loadGraphic(Paths.image('menuDesat'));
bg.scrollFactor.set();
bg.color = 0xFF353535;
add(bg);
grpTexts = new FlxTypedGroup<Alphabet>();
add(grpTexts);
for (i in 0...options.length)
{
var leText:Alphabet = new Alphabet(0, (70 * i) + 30, options[i], true, false);
leText.isMenuItem = true;
leText.targetY = i;
grpTexts.add(leText);
}
changeSelection();
super.create();
}
override function update(elapsed:Float)
{
if (controls.UI_UP_P)
{
changeSelection(-1);
}
if (controls.UI_DOWN_P)
{
changeSelection(1);
}
if (controls.BACK)
{
MusicBeatState.switchState(new MainMenuState());
}
if (controls.ACCEPT)
{
switch(options[curSelected]) {
case 'Character Editor':
LoadingState.loadAndSwitchState(new CharacterEditorState(Character.DEFAULT_CHARACTER, false));
case 'Week Editor':
MusicBeatState.switchState(new WeekEditorState());
case 'Menu Character Editor':
MusicBeatState.switchState(new MenuCharacterEditorState());
case 'Dialogue Portrait Editor':
LoadingState.loadAndSwitchState(new DialogueCharacterEditorState(), false);
case 'Dialogue Editor':
LoadingState.loadAndSwitchState(new DialogueEditorState(), false);
}
FlxG.sound.music.volume = 0;
FreeplayState.destroyFreeplayVocals();
}
var bullShit:Int = 0;
for (item in grpTexts.members)
{
item.targetY = bullShit - curSelected;
bullShit++;
item.alpha = 0.6;
// item.setGraphicSize(Std.int(item.width * 0.8));
if (item.targetY == 0)
{
item.alpha = 1;
// item.setGraphicSize(Std.int(item.width));
}
}
super.update(elapsed);
}
function changeSelection(change:Int = 0)
{
FlxG.sound.play(Paths.sound('scrollMenu'), 0.4);
curSelected += change;
if (curSelected < 0)
curSelected = options.length - 1;
if (curSelected >= options.length)
curSelected = 0;
}
}

View file

@ -0,0 +1,443 @@
package editors;
#if desktop
import Discord.DiscordClient;
#end
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.addons.display.FlxGridOverlay;
import flixel.addons.transition.FlxTransitionableState;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.math.FlxMath;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import flixel.system.FlxSound;
import flixel.addons.ui.FlxInputText;
import flixel.addons.ui.FlxUI9SliceSprite;
import flixel.addons.ui.FlxUI;
import flixel.addons.ui.FlxUICheckBox;
import flixel.addons.ui.FlxUIInputText;
import flixel.addons.ui.FlxUINumericStepper;
import flixel.addons.ui.FlxUITabMenu;
import flixel.ui.FlxButton;
import MenuCharacter;
import openfl.net.FileReference;
import openfl.events.Event;
import openfl.events.IOErrorEvent;
import flash.net.FileFilter;
import haxe.Json;
#if sys
import sys.io.File;
#end
using StringTools;
class MenuCharacterEditorState extends MusicBeatState
{
var grpWeekCharacters:FlxTypedGroup<MenuCharacter>;
var characterFile:MenuCharacterFile = null;
var txtOffsets:FlxText;
var defaultCharacters:Array<String> = ['dad', 'bf', 'gf'];
override function create() {
characterFile = {
image: 'Menu_Dad',
scale: 1,
position: [0, 0],
idle_anim: 'M Dad Idle',
confirm_anim: 'M Dad Idle'
};
#if desktop
// Updating Discord Rich Presence
DiscordClient.changePresence("Menu Character Editor", "Editting: " + characterFile.image);
#end
grpWeekCharacters = new FlxTypedGroup<MenuCharacter>();
for (char in 0...3)
{
var weekCharacterThing:MenuCharacter = new MenuCharacter((FlxG.width * 0.25) * (1 + char) - 150, defaultCharacters[char]);
weekCharacterThing.y += 70;
weekCharacterThing.alpha = 0.2;
grpWeekCharacters.add(weekCharacterThing);
}
add(new FlxSprite(0, 56).makeGraphic(FlxG.width, 386, 0xFFF9CF51));
add(grpWeekCharacters);
txtOffsets = new FlxText(20, 10, 0, "[0, 0]", 32);
txtOffsets.setFormat("VCR OSD Mono", 32, FlxColor.WHITE, CENTER);
txtOffsets.alpha = 0.7;
add(txtOffsets);
var tipText:FlxText = new FlxText(0, 540, FlxG.width,
"Arrow Keys - Change Offset (Hold shift for 10x speed)
\nSpace - Play \"Start Press\" animation (Boyfriend Character Type)", 16);
tipText.setFormat(Paths.font("vcr.ttf"), 16, FlxColor.WHITE, CENTER);
tipText.scrollFactor.set();
add(tipText);
addEditorBox();
FlxG.mouse.visible = true;
updateCharTypeBox();
super.create();
}
var UI_typebox:FlxUITabMenu;
var UI_mainbox:FlxUITabMenu;
var blockPressWhileTypingOn:Array<FlxUIInputText> = [];
function addEditorBox() {
var tabs = [
{name: 'Character Type', label: 'Character Type'},
];
UI_typebox = new FlxUITabMenu(null, tabs, true);
UI_typebox.resize(120, 180);
UI_typebox.x = 100;
UI_typebox.y = FlxG.height - UI_typebox.height - 50;
UI_typebox.scrollFactor.set();
addTypeUI();
add(UI_typebox);
var tabs = [
{name: 'Character', label: 'Character'},
];
UI_mainbox = new FlxUITabMenu(null, tabs, true);
UI_mainbox.resize(240, 180);
UI_mainbox.x = FlxG.width - UI_mainbox.width - 100;
UI_mainbox.y = FlxG.height - UI_mainbox.height - 50;
UI_mainbox.scrollFactor.set();
addCharacterUI();
add(UI_mainbox);
var loadButton:FlxButton = new FlxButton(0, 480, "Load Character", function() {
loadCharacter();
});
loadButton.screenCenter(X);
loadButton.x -= 60;
add(loadButton);
var saveButton:FlxButton = new FlxButton(0, 480, "Save Character", function() {
saveCharacter();
});
saveButton.screenCenter(X);
saveButton.x += 60;
add(saveButton);
}
var opponentCheckbox:FlxUICheckBox;
var boyfriendCheckbox:FlxUICheckBox;
var girlfriendCheckbox:FlxUICheckBox;
var curTypeSelected:Int = 0; //0 = Dad, 1 = BF, 2 = GF
function addTypeUI() {
var tab_group = new FlxUI(null, UI_typebox);
tab_group.name = "Character Type";
opponentCheckbox = new FlxUICheckBox(10, 20, null, null, "Opponent", 100);
opponentCheckbox.callback = function()
{
curTypeSelected = 0;
updateCharTypeBox();
};
boyfriendCheckbox = new FlxUICheckBox(opponentCheckbox.x, opponentCheckbox.y + 40, null, null, "Boyfriend", 100);
boyfriendCheckbox.callback = function()
{
curTypeSelected = 1;
updateCharTypeBox();
};
girlfriendCheckbox = new FlxUICheckBox(boyfriendCheckbox.x, boyfriendCheckbox.y + 40, null, null, "Girlfriend", 100);
girlfriendCheckbox.callback = function()
{
curTypeSelected = 2;
updateCharTypeBox();
};
tab_group.add(opponentCheckbox);
tab_group.add(boyfriendCheckbox);
tab_group.add(girlfriendCheckbox);
UI_typebox.addGroup(tab_group);
}
var imageInputText:FlxUIInputText;
var idleInputText:FlxUIInputText;
var confirmInputText:FlxUIInputText;
var confirmDescText:FlxText;
var scaleStepper:FlxUINumericStepper;
function addCharacterUI() {
var tab_group = new FlxUI(null, UI_mainbox);
tab_group.name = "Character";
imageInputText = new FlxUIInputText(10, 20, 80, characterFile.image, 8);
blockPressWhileTypingOn.push(imageInputText);
idleInputText = new FlxUIInputText(10, imageInputText.y + 35, 100, characterFile.idle_anim, 8);
blockPressWhileTypingOn.push(idleInputText);
confirmInputText = new FlxUIInputText(10, idleInputText.y + 35, 100, characterFile.confirm_anim, 8);
blockPressWhileTypingOn.push(confirmInputText);
var reloadImageButton:FlxButton = new FlxButton(10, confirmInputText.y + 30, "Reload Char", function() {
reloadSelectedCharacter();
});
scaleStepper = new FlxUINumericStepper(140, imageInputText.y, 0.05, 1, 0.1, 30, 2);
confirmDescText = new FlxText(10, confirmInputText.y - 18, 0, 'Start Press animation on the .XML:');
tab_group.add(new FlxText(10, imageInputText.y - 18, 0, 'Image file name:'));
tab_group.add(new FlxText(10, idleInputText.y - 18, 0, 'Idle animation on the .XML:'));
tab_group.add(new FlxText(scaleStepper.x, scaleStepper.y - 18, 0, 'Scale:'));
tab_group.add(reloadImageButton);
tab_group.add(confirmDescText);
tab_group.add(imageInputText);
tab_group.add(idleInputText);
tab_group.add(confirmInputText);
tab_group.add(scaleStepper);
UI_mainbox.addGroup(tab_group);
}
function updateCharTypeBox() {
opponentCheckbox.checked = false;
boyfriendCheckbox.checked = false;
girlfriendCheckbox.checked = false;
switch(curTypeSelected) {
case 0:
opponentCheckbox.checked = true;
case 1:
boyfriendCheckbox.checked = true;
case 2:
girlfriendCheckbox.checked = true;
}
updateCharacters();
}
function updateCharacters() {
for (i in 0...3) {
var char:MenuCharacter = grpWeekCharacters.members[i];
char.alpha = 0.2;
char.character = '';
char.changeCharacter(defaultCharacters[i]);
}
reloadSelectedCharacter();
}
function reloadSelectedCharacter() {
var char:MenuCharacter = grpWeekCharacters.members[curTypeSelected];
char.alpha = 1;
char.frames = Paths.getSparrowAtlas('menucharacters/' + characterFile.image);
char.animation.addByPrefix('idle', characterFile.idle_anim, 24);
if(curTypeSelected == 1) char.animation.addByPrefix('confirm', characterFile.confirm_anim, 24, false);
char.scale.set(characterFile.scale, characterFile.scale);
char.updateHitbox();
char.animation.play('idle');
confirmDescText.visible = (curTypeSelected == 1);
confirmInputText.visible = (curTypeSelected == 1);
updateOffset();
#if desktop
// Updating Discord Rich Presence
DiscordClient.changePresence("Menu Character Editor", "Editting: " + characterFile.image);
#end
}
override function getEvent(id:String, sender:Dynamic, data:Dynamic, ?params:Array<Dynamic>) {
if(id == FlxUIInputText.CHANGE_EVENT && (sender is FlxUIInputText)) {
if(sender == imageInputText) {
characterFile.image = imageInputText.text;
} else if(sender == idleInputText) {
characterFile.idle_anim = idleInputText.text;
} else if(sender == confirmInputText) {
characterFile.confirm_anim = confirmInputText.text;
}
} else if(id == FlxUINumericStepper.CHANGE_EVENT && (sender is FlxUINumericStepper)) {
if (sender == scaleStepper) {
characterFile.scale = scaleStepper.value;
reloadSelectedCharacter();
}
}
}
override function update(elapsed:Float) {
var blockInput:Bool = false;
for (inputText in blockPressWhileTypingOn) {
if(inputText.hasFocus) {
FlxG.sound.muteKeys = [];
FlxG.sound.volumeDownKeys = [];
FlxG.sound.volumeUpKeys = [];
blockInput = true;
if(FlxG.keys.justPressed.ENTER) inputText.hasFocus = false;
break;
}
}
if(!blockInput) {
FlxG.sound.muteKeys = TitleState.muteKeys;
FlxG.sound.volumeDownKeys = TitleState.volumeDownKeys;
FlxG.sound.volumeUpKeys = TitleState.volumeUpKeys;
if(FlxG.keys.justPressed.ESCAPE) {
FlxG.mouse.visible = false;
MusicBeatState.switchState(new editors.MasterEditorMenu());
FlxG.sound.playMusic(Paths.music('freakyMenu'));
}
var shiftMult:Int = 1;
if(FlxG.keys.pressed.SHIFT) shiftMult = 10;
if(FlxG.keys.justPressed.LEFT) {
characterFile.position[0] += shiftMult;
updateOffset();
}
if(FlxG.keys.justPressed.RIGHT) {
characterFile.position[0] -= shiftMult;
updateOffset();
}
if(FlxG.keys.justPressed.UP) {
characterFile.position[1] += shiftMult;
updateOffset();
}
if(FlxG.keys.justPressed.DOWN) {
characterFile.position[1] -= shiftMult;
updateOffset();
}
if(FlxG.keys.justPressed.SPACE && curTypeSelected == 1) {
grpWeekCharacters.members[curTypeSelected].animation.play('confirm', true);
}
}
var char:MenuCharacter = grpWeekCharacters.members[1];
if(char.animation.curAnim != null && char.animation.curAnim.name == 'confirm' && char.animation.curAnim.finished) {
char.animation.play('idle', true);
}
super.update(elapsed);
}
function updateOffset() {
var char:MenuCharacter = grpWeekCharacters.members[curTypeSelected];
char.offset.set(characterFile.position[0], characterFile.position[1]);
txtOffsets.text = '' + characterFile.position;
}
var _file:FileReference = null;
function loadCharacter() {
var jsonFilter:FileFilter = new FileFilter('JSON', 'json');
_file = new FileReference();
_file.addEventListener(Event.SELECT, onLoadComplete);
_file.addEventListener(Event.CANCEL, onLoadCancel);
_file.addEventListener(IOErrorEvent.IO_ERROR, onLoadError);
_file.browse([jsonFilter]);
}
function onLoadComplete(_):Void
{
_file.removeEventListener(Event.SELECT, onLoadComplete);
_file.removeEventListener(Event.CANCEL, onLoadCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
#if sys
var fullPath:String = null;
var jsonLoaded = cast Json.parse(Json.stringify(_file)); //Exploit(???) for accessing a private variable
if(jsonLoaded.__path != null) fullPath = jsonLoaded.__path; //I'm either a genious or dangerously dumb
if(fullPath != null) {
var rawJson:String = File.getContent(fullPath);
if(rawJson != null) {
var loadedChar:MenuCharacterFile = cast Json.parse(rawJson);
if(loadedChar.idle_anim != null && loadedChar.confirm_anim != null) //Make sure it's really a character
{
var cutName:String = _file.name.substr(0, _file.name.length - 5);
trace("Successfully loaded file: " + cutName);
characterFile = loadedChar;
reloadSelectedCharacter();
imageInputText.text = characterFile.image;
idleInputText.text = characterFile.image;
confirmInputText.text = characterFile.image;
scaleStepper.value = characterFile.scale;
updateOffset();
_file = null;
return;
}
}
}
_file = null;
#else
trace("File couldn't be loaded! You aren't on Desktop, are you?");
#end
}
/**
* Called when the save file dialog is cancelled.
*/
function onLoadCancel(_):Void
{
_file.removeEventListener(Event.SELECT, onLoadComplete);
_file.removeEventListener(Event.CANCEL, onLoadCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
_file = null;
trace("Cancelled file loading.");
}
/**
* Called if there is an error while saving the gameplay recording.
*/
function onLoadError(_):Void
{
_file.removeEventListener(Event.SELECT, onLoadComplete);
_file.removeEventListener(Event.CANCEL, onLoadCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
_file = null;
trace("Problem loading file");
}
function saveCharacter() {
var data:String = Json.stringify(characterFile, "\t");
if (data.length > 0)
{
var splittedImage:Array<String> = imageInputText.text.trim().split('_');
var characterName:String = splittedImage[splittedImage.length-1].toLowerCase().replace(' ', '');
_file = new FileReference();
_file.addEventListener(Event.COMPLETE, onSaveComplete);
_file.addEventListener(Event.CANCEL, onSaveCancel);
_file.addEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file.save(data, characterName + ".json");
}
}
function onSaveComplete(_):Void
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
FlxG.log.notice("Successfully saved file.");
}
/**
* Called when the save file dialog is cancelled.
*/
function onSaveCancel(_):Void
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
}
/**
* Called if there is an error while saving the gameplay recording.
*/
function onSaveError(_):Void
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
FlxG.log.error("Problem saving file");
}
}

View file

@ -0,0 +1,796 @@
package editors;
#if desktop
import Discord.DiscordClient;
#end
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.addons.display.FlxGridOverlay;
import flixel.addons.transition.FlxTransitionableState;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.math.FlxMath;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import flixel.system.FlxSound;
import openfl.utils.Assets;
import flixel.addons.ui.FlxInputText;
import flixel.addons.ui.FlxUI9SliceSprite;
import flixel.addons.ui.FlxUI;
import flixel.addons.ui.FlxUICheckBox;
import flixel.addons.ui.FlxUIInputText;
import flixel.addons.ui.FlxUINumericStepper;
import flixel.addons.ui.FlxUITabMenu;
import flixel.ui.FlxButton;
import openfl.net.FileReference;
import openfl.events.Event;
import openfl.events.IOErrorEvent;
import flash.net.FileFilter;
import lime.system.Clipboard;
import haxe.Json;
#if sys
import sys.io.File;
import sys.FileSystem;
#end
import WeekData;
using StringTools;
class WeekEditorState extends MusicBeatState
{
var txtWeekTitle:FlxText;
var bgSprite:FlxSprite;
var lock:FlxSprite;
var txtTracklist:FlxText;
var grpWeekCharacters:FlxTypedGroup<MenuCharacter>;
var weekThing:MenuItem;
var missingFileText:FlxText;
var weekFile:WeekFile = null;
public function new(weekFile:WeekFile = null)
{
super();
this.weekFile = WeekData.createWeekFile();
if(weekFile != null) this.weekFile = weekFile;
else weekFileName = 'week1';
}
override function create() {
txtWeekTitle = new FlxText(FlxG.width * 0.7, 10, 0, "", 32);
txtWeekTitle.setFormat("VCR OSD Mono", 32, FlxColor.WHITE, RIGHT);
txtWeekTitle.alpha = 0.7;
var ui_tex = Paths.getSparrowAtlas('campaign_menu_UI_assets');
var bgYellow:FlxSprite = new FlxSprite(0, 56).makeGraphic(FlxG.width, 386, 0xFFF9CF51);
bgSprite = new FlxSprite(0, 56);
bgSprite.antialiasing = ClientPrefs.globalAntialiasing;
weekThing = new MenuItem(0, bgSprite.y + 396, weekFileName);
weekThing.y += weekThing.height + 20;
weekThing.antialiasing = ClientPrefs.globalAntialiasing;
add(weekThing);
var blackBarThingie:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, 56, FlxColor.BLACK);
add(blackBarThingie);
grpWeekCharacters = new FlxTypedGroup<MenuCharacter>();
lock = new FlxSprite();
lock.frames = ui_tex;
lock.animation.addByPrefix('lock', 'lock');
lock.animation.play('lock');
lock.antialiasing = ClientPrefs.globalAntialiasing;
add(lock);
missingFileText = new FlxText(0, 0, FlxG.width, "");
missingFileText.setFormat(Paths.font("vcr.ttf"), 24, FlxColor.WHITE, CENTER, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
missingFileText.borderSize = 2;
missingFileText.visible = false;
add(missingFileText);
var charArray:Array<String> = weekFile.weekCharacters;
for (char in 0...3)
{
var weekCharacterThing:MenuCharacter = new MenuCharacter((FlxG.width * 0.25) * (1 + char) - 150, charArray[char]);
weekCharacterThing.y += 70;
grpWeekCharacters.add(weekCharacterThing);
}
add(bgYellow);
add(bgSprite);
add(grpWeekCharacters);
var tracksSprite:FlxSprite = new FlxSprite(FlxG.width * 0.07, bgSprite.y + 435).loadGraphic(Paths.image('Menu_Tracks'));
tracksSprite.antialiasing = ClientPrefs.globalAntialiasing;
add(tracksSprite);
txtTracklist = new FlxText(FlxG.width * 0.05, tracksSprite.y + 60, 0, "", 32);
txtTracklist.alignment = CENTER;
txtTracklist.font = Paths.font("vcr.ttf");
txtTracklist.color = 0xFFe55777;
add(txtTracklist);
add(txtWeekTitle);
addEditorBox();
reloadAllShit();
FlxG.mouse.visible = true;
super.create();
}
var UI_box:FlxUITabMenu;
var blockPressWhileTypingOn:Array<FlxUIInputText> = [];
function addEditorBox() {
var tabs = [
{name: 'Week', label: 'Week'},
{name: 'Lock', label: 'Lock'},
];
UI_box = new FlxUITabMenu(null, tabs, true);
UI_box.resize(250, 375);
UI_box.x = FlxG.width - UI_box.width;
UI_box.y = FlxG.height - UI_box.height;
UI_box.scrollFactor.set();
addWeekUI();
addLockUI();
UI_box.selected_tab_id = 'Week';
add(UI_box);
var loadWeekButton:FlxButton = new FlxButton(0, 650, "Load Week", function() {
loadWeek();
});
loadWeekButton.screenCenter(X);
loadWeekButton.x -= 120;
add(loadWeekButton);
var freeplayButton:FlxButton = new FlxButton(0, 650, "Freeplay", function() {
MusicBeatState.switchState(new WeekEditorFreeplayState(weekFile));
});
freeplayButton.screenCenter(X);
add(freeplayButton);
var saveWeekButton:FlxButton = new FlxButton(0, 650, "Save Week", function() {
saveWeek(weekFile);
});
saveWeekButton.screenCenter(X);
saveWeekButton.x += 120;
add(saveWeekButton);
}
var songsInputText:FlxUIInputText;
var backgroundInputText:FlxUIInputText;
var displayNameInputText:FlxUIInputText;
var weekNameInputText:FlxUIInputText;
var weekFileInputText:FlxUIInputText;
var opponentInputText:FlxUIInputText;
var boyfriendInputText:FlxUIInputText;
var girlfriendInputText:FlxUIInputText;
var hideCheckbox:FlxUICheckBox;
public static var weekFileName:String = 'week1';
function addWeekUI() {
var tab_group = new FlxUI(null, UI_box);
tab_group.name = "Week";
songsInputText = new FlxUIInputText(10, 30, 200, '', 8);
blockPressWhileTypingOn.push(songsInputText);
opponentInputText = new FlxUIInputText(10, songsInputText.y + 40, 70, '', 8);
blockPressWhileTypingOn.push(opponentInputText);
boyfriendInputText = new FlxUIInputText(opponentInputText.x + 75, opponentInputText.y, 70, '', 8);
blockPressWhileTypingOn.push(boyfriendInputText);
girlfriendInputText = new FlxUIInputText(boyfriendInputText.x + 75, opponentInputText.y, 70, '', 8);
blockPressWhileTypingOn.push(girlfriendInputText);
backgroundInputText = new FlxUIInputText(10, opponentInputText.y + 40, 120, '', 8);
blockPressWhileTypingOn.push(backgroundInputText);
displayNameInputText = new FlxUIInputText(10, backgroundInputText.y + 60, 200, '', 8);
blockPressWhileTypingOn.push(backgroundInputText);
weekNameInputText = new FlxUIInputText(10, displayNameInputText.y + 60, 150, '', 8);
blockPressWhileTypingOn.push(weekNameInputText);
weekFileInputText = new FlxUIInputText(10, weekNameInputText.y + 40, 100, '', 8);
blockPressWhileTypingOn.push(weekFileInputText);
reloadWeekThing();
hideCheckbox = new FlxUICheckBox(10, weekFileInputText.y + 40, null, null, "Hide Week from Story Mode?", 100);
hideCheckbox.callback = function()
{
weekFile.hideStoryMode = hideCheckbox.checked;
};
tab_group.add(new FlxText(songsInputText.x, songsInputText.y - 18, 0, 'Songs:'));
tab_group.add(new FlxText(opponentInputText.x, opponentInputText.y - 18, 0, 'Characters:'));
tab_group.add(new FlxText(backgroundInputText.x, backgroundInputText.y - 18, 0, 'Background Asset:'));
tab_group.add(new FlxText(displayNameInputText.x, displayNameInputText.y - 18, 0, 'Display Name:'));
tab_group.add(new FlxText(weekNameInputText.x, weekNameInputText.y - 18, 0, 'Week Name (for Reset Score Menu):'));
tab_group.add(new FlxText(weekFileInputText.x, weekFileInputText.y - 18, 0, 'Week File:'));
tab_group.add(songsInputText);
tab_group.add(opponentInputText);
tab_group.add(boyfriendInputText);
tab_group.add(girlfriendInputText);
tab_group.add(backgroundInputText);
tab_group.add(displayNameInputText);
tab_group.add(weekNameInputText);
tab_group.add(weekFileInputText);
tab_group.add(hideCheckbox);
UI_box.addGroup(tab_group);
}
var weekBeforeInputText:FlxUIInputText;
var lockedCheckbox:FlxUICheckBox;
function addLockUI() {
var tab_group = new FlxUI(null, UI_box);
tab_group.name = "Lock";
lockedCheckbox = new FlxUICheckBox(10, 30, null, null, "Week starts Locked", 100);
lockedCheckbox.callback = function()
{
weekFile.startUnlocked = !lockedCheckbox.checked;
lock.visible = lockedCheckbox.checked;
};
weekBeforeInputText = new FlxUIInputText(10, lockedCheckbox.y + 55, 100, '', 8);
blockPressWhileTypingOn.push(weekBeforeInputText);
tab_group.add(new FlxText(weekBeforeInputText.x, weekBeforeInputText.y - 28, 0, 'Week File name of the Week you have\nto finish for Unlocking:'));
tab_group.add(weekBeforeInputText);
tab_group.add(lockedCheckbox);
UI_box.addGroup(tab_group);
}
//Used on onCreate and when you load a week
function reloadAllShit() {
var weekString:String = weekFile.songs[0][0];
for (i in 1...weekFile.songs.length) {
weekString += ', ' + weekFile.songs[i][0];
}
songsInputText.text = weekString;
backgroundInputText.text = weekFile.weekBackground;
displayNameInputText.text = weekFile.storyName;
weekNameInputText.text = weekFile.weekName;
weekFileInputText.text = weekFileName;
opponentInputText.text = weekFile.weekCharacters[0];
boyfriendInputText.text = weekFile.weekCharacters[1];
girlfriendInputText.text = weekFile.weekCharacters[2];
hideCheckbox.checked = weekFile.hideStoryMode;
weekBeforeInputText.text = weekFile.weekBefore;
lockedCheckbox.checked = !weekFile.startUnlocked;
lock.visible = lockedCheckbox.checked;
reloadBG();
reloadWeekThing();
updateText();
}
function updateText()
{
for (i in 0...grpWeekCharacters.length) {
grpWeekCharacters.members[i].changeCharacter(weekFile.weekCharacters[i]);
}
var stringThing:Array<String> = [];
for (i in 0...weekFile.songs.length) {
stringThing.push(weekFile.songs[i][0]);
}
txtTracklist.text = '';
for (i in 0...stringThing.length)
{
txtTracklist.text += stringThing[i] + '\n';
}
txtTracklist.text = txtTracklist.text.toUpperCase();
txtTracklist.screenCenter(X);
txtTracklist.x -= FlxG.width * 0.35;
txtWeekTitle.text = weekFile.storyName.toUpperCase();
txtWeekTitle.x = FlxG.width - (txtWeekTitle.width + 10);
}
function reloadBG() {
bgSprite.visible = true;
var assetName:String = weekFile.weekBackground;
var isMissing:Bool = true;
if(assetName != null && assetName.length > 0) {
if( #if MODS_ALLOWED FileSystem.exists(Paths.modsImages('menubackgrounds/menu_' + assetName)) || #end
Assets.exists(Paths.image('menubackgrounds/menu_' + assetName), IMAGE)) {
bgSprite.loadGraphic(Paths.image('menubackgrounds/menu_' + assetName));
isMissing = false;
}
}
if(isMissing) {
bgSprite.visible = false;
}
}
function reloadWeekThing() {
weekThing.visible = true;
missingFileText.visible = false;
var assetName:String = weekFileInputText.text.trim();
var isMissing:Bool = true;
if(assetName != null && assetName.length > 0) {
if( #if MODS_ALLOWED FileSystem.exists(Paths.modsImages('storymenu/' + assetName)) || #end
Assets.exists(Paths.image('storymenu/' + assetName), IMAGE)) {
weekThing.loadGraphic(Paths.image('storymenu/' + assetName));
isMissing = false;
}
}
if(isMissing) {
weekThing.visible = false;
missingFileText.visible = true;
missingFileText.text = 'MISSING FILE: images/storymenu/' + assetName + '.png';
}
recalculateStuffPosition();
#if desktop
// Updating Discord Rich Presence
DiscordClient.changePresence("Week Editor", "Editting: " + weekFileName);
#end
}
override function getEvent(id:String, sender:Dynamic, data:Dynamic, ?params:Array<Dynamic>) {
if(id == FlxUIInputText.CHANGE_EVENT && (sender is FlxUIInputText)) {
if(sender == weekFileInputText) {
weekFileName = weekFileInputText.text.trim();
reloadWeekThing();
} else if(sender == opponentInputText || sender == boyfriendInputText || sender == girlfriendInputText) {
weekFile.weekCharacters[0] = opponentInputText.text.trim();
weekFile.weekCharacters[1] = boyfriendInputText.text.trim();
weekFile.weekCharacters[2] = girlfriendInputText.text.trim();
updateText();
} else if(sender == backgroundInputText) {
weekFile.weekBackground = backgroundInputText.text.trim();
reloadBG();
} else if(sender == displayNameInputText) {
weekFile.storyName = displayNameInputText.text.trim();
updateText();
} else if(sender == weekNameInputText) {
weekFile.weekName = weekNameInputText.text.trim();
} else if(sender == songsInputText) {
var splittedText:Array<String> = songsInputText.text.trim().split(',');
for (i in 0...splittedText.length) {
splittedText[i] = splittedText[i].trim();
}
while(splittedText.length < weekFile.songs.length) {
weekFile.songs.pop();
}
for (i in 0...splittedText.length) {
if(i >= weekFile.songs.length) { //Add new song
weekFile.songs.push([splittedText[i], 'dad', [146, 113, 253]]);
} else { //Edit song
weekFile.songs[i][0] = splittedText[i];
if(weekFile.songs[i][1] == null || weekFile.songs[i][1]) {
weekFile.songs[i][1] = 'dad';
weekFile.songs[i][2] = [146, 113, 253];
}
}
}
updateText();
} else if(sender == weekBeforeInputText) {
weekFile.weekBefore = weekBeforeInputText.text.trim();
}
}
}
override function update(elapsed:Float)
{
if(loadedWeek != null) {
weekFile = loadedWeek;
loadedWeek = null;
reloadAllShit();
}
var blockInput:Bool = false;
for (inputText in blockPressWhileTypingOn) {
if(inputText.hasFocus) {
FlxG.sound.muteKeys = [];
FlxG.sound.volumeDownKeys = [];
FlxG.sound.volumeUpKeys = [];
blockInput = true;
if(FlxG.keys.justPressed.ENTER) inputText.hasFocus = false;
break;
}
}
if(!blockInput) {
FlxG.sound.muteKeys = TitleState.muteKeys;
FlxG.sound.volumeDownKeys = TitleState.volumeDownKeys;
FlxG.sound.volumeUpKeys = TitleState.volumeUpKeys;
if(FlxG.keys.justPressed.ESCAPE) {
FlxG.mouse.visible = false;
MusicBeatState.switchState(new editors.MasterEditorMenu());
FlxG.sound.playMusic(Paths.music('freakyMenu'));
}
}
super.update(elapsed);
lock.y = weekThing.y;
missingFileText.y = weekThing.y + 36;
}
function recalculateStuffPosition() {
weekThing.screenCenter(X);
lock.x = weekThing.width + 10 + weekThing.x;
}
private static var _file:FileReference;
public static function loadWeek() {
var jsonFilter:FileFilter = new FileFilter('JSON', 'json');
_file = new FileReference();
_file.addEventListener(Event.SELECT, onLoadComplete);
_file.addEventListener(Event.CANCEL, onLoadCancel);
_file.addEventListener(IOErrorEvent.IO_ERROR, onLoadError);
_file.browse([jsonFilter]);
}
public static var loadedWeek:WeekFile = null;
public static var loadError:Bool = false;
private static function onLoadComplete(_):Void
{
_file.removeEventListener(Event.SELECT, onLoadComplete);
_file.removeEventListener(Event.CANCEL, onLoadCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
#if sys
var fullPath:String = null;
var jsonLoaded = cast Json.parse(Json.stringify(_file)); //Exploit(???) for accessing a private variable
if(jsonLoaded.__path != null) fullPath = jsonLoaded.__path; //I'm either a genious or dangerously dumb
if(fullPath != null) {
var rawJson:String = File.getContent(fullPath);
if(rawJson != null) {
loadedWeek = cast Json.parse(rawJson);
if(loadedWeek.weekCharacters != null && loadedWeek.weekName != null) //Make sure it's really a week
{
var cutName:String = _file.name.substr(0, _file.name.length - 5);
trace("Successfully loaded file: " + cutName);
loadError = false;
weekFileName = cutName;
_file = null;
return;
}
}
}
loadError = true;
loadedWeek = null;
_file = null;
#else
trace("File couldn't be loaded! You aren't on Desktop, are you?");
#end
}
/**
* Called when the save file dialog is cancelled.
*/
private static function onLoadCancel(_):Void
{
_file.removeEventListener(Event.SELECT, onLoadComplete);
_file.removeEventListener(Event.CANCEL, onLoadCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
_file = null;
trace("Cancelled file loading.");
}
/**
* Called if there is an error while saving the gameplay recording.
*/
private static function onLoadError(_):Void
{
_file.removeEventListener(Event.SELECT, onLoadComplete);
_file.removeEventListener(Event.CANCEL, onLoadCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
_file = null;
trace("Problem loading file");
}
public static function saveWeek(weekFile:WeekFile) {
var data:String = Json.stringify(weekFile, "\t");
if (data.length > 0)
{
_file = new FileReference();
_file.addEventListener(Event.COMPLETE, onSaveComplete);
_file.addEventListener(Event.CANCEL, onSaveCancel);
_file.addEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file.save(data, weekFileName + ".json");
}
}
private static function onSaveComplete(_):Void
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
FlxG.log.notice("Successfully saved file.");
}
/**
* Called when the save file dialog is cancelled.
*/
private static function onSaveCancel(_):Void
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
}
/**
* Called if there is an error while saving the gameplay recording.
*/
private static function onSaveError(_):Void
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
FlxG.log.error("Problem saving file");
}
}
class WeekEditorFreeplayState extends MusicBeatState
{
var weekFile:WeekFile = null;
public function new(weekFile:WeekFile = null)
{
super();
this.weekFile = WeekData.createWeekFile();
if(weekFile != null) this.weekFile = weekFile;
}
var bg:FlxSprite;
private var grpSongs:FlxTypedGroup<Alphabet>;
private var iconArray:Array<HealthIcon> = [];
var curSelected = 0;
override function create() {
bg = new FlxSprite().loadGraphic(Paths.image('menuDesat'));
bg.antialiasing = ClientPrefs.globalAntialiasing;
bg.color = FlxColor.WHITE;
add(bg);
grpSongs = new FlxTypedGroup<Alphabet>();
add(grpSongs);
for (i in 0...weekFile.songs.length)
{
var songText:Alphabet = new Alphabet(0, (70 * i) + 30, weekFile.songs[i][0], true, false);
songText.isMenuItem = true;
songText.targetY = i;
grpSongs.add(songText);
var icon:HealthIcon = new HealthIcon(weekFile.songs[i][1]);
icon.sprTracker = songText;
// using a FlxGroup is too much fuss!
iconArray.push(icon);
add(icon);
// songText.x += 40;
// DONT PUT X IN THE FIRST PARAMETER OF new ALPHABET() !!
// songText.screenCenter(X);
}
addEditorBox();
changeSelection();
super.create();
}
var UI_box:FlxUITabMenu;
var blockPressWhileTypingOn:Array<FlxUIInputText> = [];
function addEditorBox() {
var tabs = [
{name: 'Freeplay', label: 'Freeplay'},
];
UI_box = new FlxUITabMenu(null, tabs, true);
UI_box.resize(250, 200);
UI_box.x = FlxG.width - UI_box.width - 100;
UI_box.y = FlxG.height - UI_box.height - 60;
UI_box.scrollFactor.set();
UI_box.selected_tab_id = 'Week';
addFreeplayUI();
add(UI_box);
var blackBlack:FlxSprite = new FlxSprite(0, 670).makeGraphic(FlxG.width, 50, FlxColor.BLACK);
blackBlack.alpha = 0.6;
add(blackBlack);
var loadWeekButton:FlxButton = new FlxButton(0, 685, "Load Week", function() {
WeekEditorState.loadWeek();
});
loadWeekButton.screenCenter(X);
loadWeekButton.x -= 120;
add(loadWeekButton);
var storyModeButton:FlxButton = new FlxButton(0, 685, "Story Mode", function() {
MusicBeatState.switchState(new WeekEditorState(weekFile));
});
storyModeButton.screenCenter(X);
add(storyModeButton);
var saveWeekButton:FlxButton = new FlxButton(0, 685, "Save Week", function() {
WeekEditorState.saveWeek(weekFile);
});
saveWeekButton.screenCenter(X);
saveWeekButton.x += 120;
add(saveWeekButton);
}
override function getEvent(id:String, sender:Dynamic, data:Dynamic, ?params:Array<Dynamic>) {
if(id == FlxUIInputText.CHANGE_EVENT && (sender is FlxUIInputText)) {
weekFile.songs[curSelected][1] = iconInputText.text;
iconArray[curSelected].changeIcon(iconInputText.text);
} else if(id == FlxUINumericStepper.CHANGE_EVENT && (sender is FlxUINumericStepper)) {
if(sender == bgColorStepperR || sender == bgColorStepperG || sender == bgColorStepperB) {
updateBG();
}
}
}
var bgColorStepperR:FlxUINumericStepper;
var bgColorStepperG:FlxUINumericStepper;
var bgColorStepperB:FlxUINumericStepper;
var iconInputText:FlxUIInputText;
function addFreeplayUI() {
var tab_group = new FlxUI(null, UI_box);
tab_group.name = "Freeplay";
bgColorStepperR = new FlxUINumericStepper(10, 40, 20, 255, 0, 255, 0);
bgColorStepperG = new FlxUINumericStepper(80, 40, 20, 255, 0, 255, 0);
bgColorStepperB = new FlxUINumericStepper(150, 40, 20, 255, 0, 255, 0);
var copyColor:FlxButton = new FlxButton(10, bgColorStepperR.y + 25, "Copy Color", function() {
Clipboard.text = bg.color.red + ',' + bg.color.green + ',' + bg.color.blue;
});
var pasteColor:FlxButton = new FlxButton(140, copyColor.y, "Paste Color", function() {
if(Clipboard.text != null) {
var leColor:Array<Int> = [];
var splitted:Array<String> = Clipboard.text.trim().split(',');
for (i in 0...splitted.length) {
var toPush:Int = Std.parseInt(splitted[i]);
if(!Math.isNaN(toPush)) {
if(toPush > 255) toPush = 255;
else if(toPush < 0) toPush *= -1;
leColor.push(toPush);
}
}
if(leColor.length > 2) {
bgColorStepperR.value = leColor[0];
bgColorStepperG.value = leColor[1];
bgColorStepperB.value = leColor[2];
updateBG();
}
}
});
iconInputText = new FlxUIInputText(10, bgColorStepperR.y + 70, 100, '', 8);
var hideFreeplayCheckbox:FlxUICheckBox = new FlxUICheckBox(10, iconInputText.y + 30, null, null, "Hide Week from Freeplay?", 100);
hideFreeplayCheckbox.checked = weekFile.hideFreeplay;
hideFreeplayCheckbox.callback = function()
{
weekFile.hideFreeplay = hideFreeplayCheckbox.checked;
};
tab_group.add(new FlxText(10, bgColorStepperR.y - 18, 0, 'Selected background Color R/G/B:'));
tab_group.add(new FlxText(10, iconInputText.y - 18, 0, 'Selected icon:'));
tab_group.add(bgColorStepperR);
tab_group.add(bgColorStepperG);
tab_group.add(bgColorStepperB);
tab_group.add(copyColor);
tab_group.add(pasteColor);
tab_group.add(iconInputText);
tab_group.add(hideFreeplayCheckbox);
UI_box.addGroup(tab_group);
}
function updateBG() {
weekFile.songs[curSelected][2][0] = Math.round(bgColorStepperR.value);
weekFile.songs[curSelected][2][1] = Math.round(bgColorStepperG.value);
weekFile.songs[curSelected][2][2] = Math.round(bgColorStepperB.value);
bg.color = FlxColor.fromRGB(weekFile.songs[curSelected][2][0], weekFile.songs[curSelected][2][1], weekFile.songs[curSelected][2][2]);
}
function changeSelection(change:Int = 0) {
FlxG.sound.play(Paths.sound('scrollMenu'), 0.4);
curSelected += change;
if (curSelected < 0)
curSelected = weekFile.songs.length - 1;
if (curSelected >= weekFile.songs.length)
curSelected = 0;
var bullShit:Int = 0;
for (i in 0...iconArray.length)
{
iconArray[i].alpha = 0.6;
}
iconArray[curSelected].alpha = 1;
for (item in grpSongs.members)
{
item.targetY = bullShit - curSelected;
bullShit++;
item.alpha = 0.6;
// item.setGraphicSize(Std.int(item.width * 0.8));
if (item.targetY == 0)
{
item.alpha = 1;
// item.setGraphicSize(Std.int(item.width));
}
}
trace(weekFile.songs[curSelected]);
iconInputText.text = weekFile.songs[curSelected][1];
bgColorStepperR.value = Math.round(weekFile.songs[curSelected][2][0]);
bgColorStepperG.value = Math.round(weekFile.songs[curSelected][2][1]);
bgColorStepperB.value = Math.round(weekFile.songs[curSelected][2][2]);
updateBG();
}
override function update(elapsed:Float) {
if(WeekEditorState.loadedWeek != null) {
super.update(elapsed);
FlxTransitionableState.skipNextTransIn = true;
FlxTransitionableState.skipNextTransOut = true;
MusicBeatState.switchState(new WeekEditorFreeplayState(WeekEditorState.loadedWeek));
WeekEditorState.loadedWeek = null;
return;
}
if(iconInputText.hasFocus) {
FlxG.sound.muteKeys = [];
FlxG.sound.volumeDownKeys = [];
FlxG.sound.volumeUpKeys = [];
if(FlxG.keys.justPressed.ENTER) {
iconInputText.hasFocus = false;
}
} else {
FlxG.sound.muteKeys = TitleState.muteKeys;
FlxG.sound.volumeDownKeys = TitleState.volumeDownKeys;
FlxG.sound.volumeUpKeys = TitleState.volumeUpKeys;
if(FlxG.keys.justPressed.ESCAPE) {
FlxG.mouse.visible = false;
MusicBeatState.switchState(new editors.MasterEditorMenu());
FlxG.sound.playMusic(Paths.music('freakyMenu'));
}
if(controls.UI_UP_P) changeSelection(-1);
if(controls.UI_DOWN_P) changeSelection(1);
}
super.update(elapsed);
}
}

1
source/import.hx Normal file
View file

@ -0,0 +1 @@
import Paths;

127
source/vlc/LibVLC.hx Normal file
View file

@ -0,0 +1,127 @@
package vlc;
import cpp.Callable;
import cpp.Function;
import cpp.Pointer;
import cpp.RawPointer;
import cpp.UInt8;
import haxe.io.ArrayBufferView;
import lime.utils.UInt8Array;
// import cpp.Void;
/**
* ...
* @author Tommy S
*/
//
@:buildXml('<include name="../../../../source/vlc/LibVLCBuild.xml" />')
@:include("LibVLC.h")
@:unreflective
@:keep
@:native("LibVLC*")
extern class LibVLC
{
@:native("LibVLC::create")
public static function create():LibVLC;
@:native("setPath")
public function setPath(path:String):Void;
@:native("openMedia")
public function openMedia(path:String):Void;
@:native("play")
@:overload(function():Void
{
})
public function play(path:String):Void;
@:native("playInWindow")
@:overload(function():Void
{
})
public function playInWindow(path:String):Void;
@:native("stop")
public function stop():Void;
@:native("pause")
public function pause():Void;
@:native("resume")
public function resume():Void;
@:native("togglePause")
public function togglePause():Void;
@:native("fullscreen")
public function setWindowFullscreen(fullscreen:Bool):Void;
@:native("showMainWindow")
public function showMainWindow(show:Bool):Void;
@:native("getLength")
public function getLength():Float;
@:native("getDuration")
public function getDuration():Float;
@:native("getWidth")
public function getWidth():Int;
@:native("getHeight")
public function getHeight():Int;
// @:native("getMeta")
// public function getMeta(meta:Dynamic):String;
@:native("isPlaying")
public function isPlaying():Bool;
@:native("isSeekable")
public function isSeekable():Bool;
@:native("setVolume")
public function setVolume(volume:Float):Void;
@:native("getVolume")
public function getVolume():Float;
@:native("getTime")
public function getTime():Int;
@:native("setTime")
public function setTime(time:Int):Void;
@:native("getPosition")
public function getPosition():Float;
@:native("setPosition")
public function setPosition(pos:Float):Void;
@:native("useHWacceleration")
public function useHWacceleration(hwAcc:Bool):Void;
@:native("getLastError")
public function getLastError():String;
@:native("getRepeat")
public function getRepeat():Int;
@:native("setRepeat")
public function setRepeat(repeat:Int = -1):Void;
@:native("getPixelData")
public function getPixelData():Pointer<UInt8>;
@:native("getFPS")
public function getFPS():Float;
@:native("flags")
public var flags:Array<Int>;
public inline function dispose():Void
{
untyped __cpp__('::delete this');
}
}

View file

@ -0,0 +1,28 @@
<xml>
<set name="PROJECT_DIR" value="${this_dir}" />
<set name="CPP_DIR" value="${PROJECT_DIR}/cpp" />
<files id='haxe'>
<compilervalue name="-I" value="${CPP_DIR}/include/" />
<compilervalue name="-I" value="${CPP_DIR}/src/" />
</files>
<target id='haxe' tool='linker' toolid='exe'>
<lib name='${CPP_DIR}/lib/libvlc.lib' if='windows'/>
<lib name='${CPP_DIR}/lib/libvlccore.lib' if='windows'/>
</target>
<target id="haxe" tool="linker" if="macos">
<flag value="-framework"/>
<flag value="OpenGL"/>
<flag value="-framework"/>
<flag value="CoreVideo"/>
<flag value="-framework"/>
<flag value="IOKit"/>
<flag value="-framework"/>
<flag value="Cocoa"/>
<flag value="-framework"/>
<flag value="Glut"/>
</target>
</xml>

584
source/vlc/VlcBitmap.hx Normal file
View file

@ -0,0 +1,584 @@
package vlc;
import flixel.FlxG;
import openfl.system.Capabilities;
#if (cpp && !mobile)
import cpp.NativeArray;
import cpp.UInt8;
import haxe.ValueException;
import haxe.io.Bytes;
import lime.app.Application;
import openfl.Lib;
import openfl.display.Bitmap;
import openfl.display.BitmapData;
import openfl.display3D.textures.RectangleTexture;
import openfl.errors.Error;
import openfl.events.Event;
import openfl.geom.Rectangle;
import vlc.LibVLC;
/**
* ...
* @author Tommy S
*/
#if (cpp && !mobile)
@:cppFileCode('#include "LibVLC.cpp"')
#end
class VlcBitmap extends Bitmap
{
/////////////////////////////////////////////////////////////////////////////////////
// ===================================================================================
// Consts
//-----------------------------------------------------------------------------------
// ===================================================================================
// Properties
//-----------------------------------------------------------------------------------
public var videoWidth:Int;
public var videoHeight:Int;
public var repeat:Int = 0;
public var duration:Float;
public var length:Float;
public var inWindow:Bool;
public var initComplete:Bool;
public var fullscreen:Bool;
public var volume(default, set):Float = 1;
public var isDisposed:Bool;
public var isPlaying:Bool;
public var disposeOnStop:Bool = false;
public var time:Int;
public var onVideoReady:Void->Void;
public var onPlay:Void->Void;
public var onStop:Void->Void;
public var onPause:Void->Void;
public var onResume:Void->Void;
public var onSeek:Void->Void;
public var onBuffer:Void->Void;
public var onProgress:Void->Void;
public var onOpening:Void->Void;
public var onComplete:Void->Void;
public var onError:Void->Void;
// ===================================================================================
// Declarations
//-----------------------------------------------------------------------------------
var bufferMem:Array<UInt8>;
#if (cpp && !mobile)
var libvlc:LibVLC;
#end
// ===================================================================================
// Variables
//-----------------------------------------------------------------------------------
var frameSize:Int;
var _width:Null<Float>;
var _height:Null<Float>;
var texture:RectangleTexture;
var texture2:RectangleTexture;
var bmdBuf:BitmapData;
var bmdBuf2:BitmapData;
var oldTime:Int;
var flipBuffer:Bool;
var frameRect:Rectangle;
var screenWidth:Float;
var screenHeight:Float;
/////////////////////////////////////////////////////////////////////////////////////
public function new()
{
super(null, null, true);
#if (cpp && !mobile)
init();
#end
}
function mThread()
{
init();
}
/////////////////////////////////////////////////////////////////////////////////////
function init()
{
#if (cpp && !mobile)
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
#end
}
function onAddedToStage(e:Event):Void
{
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
libvlc = LibVLC.create();
stage.addEventListener(Event.RESIZE, onResize);
stage.addEventListener(Event.ENTER_FRAME, vLoop);
}
/////////////////////////////////////////////////////////////////////////////////////
public function play(?source:String)
{
#if (cpp && !mobile)
libvlc.setRepeat(repeat);
if (!inWindow)
{
if (source != null)
libvlc.play(source);
else
libvlc.play();
}
else
{
if (source != null)
libvlc.playInWindow(source);
else
libvlc.playInWindow();
libvlc.setWindowFullscreen(fullscreen);
}
if (onPlay != null)
onPlay();
#end
}
public function stop()
{
#if (cpp && !mobile)
isPlaying = false;
libvlc.stop();
// if (disposeOnStop)
// dispose();
if (onStop != null)
onStop();
#end
}
public function pause()
{
#if (cpp && !mobile)
isPlaying = false;
libvlc.pause();
if (onPause != null)
onPause();
#end
}
public function resume()
{
#if (cpp && !mobile)
isPlaying = true;
libvlc.resume();
if (onResume != null)
onResume();
#end
}
public function seek(seekTotime:Float)
{
#if (cpp && !mobile)
libvlc.setPosition(seekTotime);
if (onSeek != null)
onSeek();
#end
}
public function getFPS():Float
{
#if (cpp && !mobile)
if (libvlc != null && initComplete)
return libvlc.getFPS();
else
return 0;
#else
return 0;
#end
}
public function getTime():Int
{
#if (cpp && !mobile)
if (libvlc != null && initComplete)
return libvlc.getTime();
else
return 0;
#else
return 0;
#end
}
/////////////////////////////////////////////////////////////////////////////////////
function checkFlags()
{
#if (cpp && !mobile)
if (!isDisposed)
{
if (untyped __cpp__('libvlc->flags[1]') == 1)
{
untyped __cpp__('libvlc->flags[1]=-1');
statusOnPlaying();
}
if (untyped __cpp__('libvlc->flags[2]') == 1)
{
untyped __cpp__('libvlc->flags[2]=-1');
statusOnPaused();
}
if (untyped __cpp__('libvlc->flags[3]') == 1)
{
untyped __cpp__('libvlc->flags[3]=-1');
statusOnStopped();
}
if (untyped __cpp__('libvlc->flags[4]') == 1)
{
untyped __cpp__('libvlc->flags[4]=-1');
statusOnEndReached();
}
if (untyped __cpp__('libvlc->flags[5]') != -1)
{
statusOnTimeChanged(untyped __cpp__('libvlc->flags[5]'));
}
if (untyped __cpp__('libvlc->flags[6]') != -1)
{
statusOnPositionChanged(untyped __cpp__('libvlc->flags[9]'));
}
if (untyped __cpp__('libvlc->flags[9]') == 1)
{
untyped __cpp__('libvlc->flags[9]=-1');
statusOnError();
}
if (untyped __cpp__('libvlc->flags[10]') == 1)
{
untyped __cpp__('libvlc->flags[10]=-1');
statusOnSeekableChanged(0);
}
if (untyped __cpp__('libvlc->flags[11]') == 1)
{
untyped __cpp__('libvlc->flags[11]=-1');
statusOnOpening();
}
if (untyped __cpp__('libvlc->flags[12]') == 1)
{
untyped __cpp__('libvlc->flags[12]=-1');
statusOnBuffering();
}
if (untyped __cpp__('libvlc->flags[13]') == 1)
{
untyped __cpp__('libvlc->flags[13]=-1');
statusOnForward();
}
if (untyped __cpp__('libvlc->flags[14]') == 1)
{
untyped __cpp__('libvlc->flags[14]=-1');
statusOnBackward();
}
}
#end
}
/////////////////////////////////////////////////////////////////////////////////////
function onResize(e:Event):Void
{
set_height(FlxG.stage.stageHeight);
set_width(FlxG.stage.stageHeight * (16 / 9));
}
/////////////////////////////////////////////////////////////////////////////////////
function videoInitComplete()
{
#if (cpp && !mobile)
videoWidth = libvlc.getWidth();
videoHeight = libvlc.getHeight();
duration = libvlc.getDuration();
length = libvlc.getLength();
if (bitmapData != null)
bitmapData.dispose();
if (texture != null)
texture.dispose();
if (texture2 != null)
texture2.dispose();
// BitmapData
bitmapData = new BitmapData(Std.int(videoWidth), Std.int(videoHeight), true, 0);
frameRect = new Rectangle(0, 0, Std.int(videoWidth), Std.int(videoHeight));
// (Stage3D)
// texture = Lib.current.stage.stage3Ds[0].context3D.createRectangleTexture(videoWidth, videoHeight, Context3DTextureFormat.BGRA, true);
// this.bitmapData = BitmapData.fromTexture(texture);
smoothing = true;
if (_width != null)
width = _width;
else
width = videoWidth;
if (_height != null)
height = _height;
else
height = videoHeight;
bufferMem = [];
frameSize = videoWidth * videoHeight * 4;
setVolume(volume);
initComplete = true;
if (onVideoReady != null)
onVideoReady();
#end
}
/////////////////////////////////////////////////////////////////////////////////////
function vLoop(e)
{
#if (cpp && !mobile)
checkFlags();
render();
#end
}
/////////////////////////////////////////////////////////////////////////////////////
function render()
{
var cTime = Lib.getTimer();
if ((cTime - oldTime) > 28) // min 28 ms between renders, but this is not a good way to do it...
{
oldTime = cTime;
#if (cpp && !mobile)
// if (isPlaying && texture != null) // (Stage3D)
if (isPlaying)
{
try
{
NativeArray.setUnmanagedData(bufferMem, libvlc.getPixelData(), frameSize);
if (bufferMem != null)
{
// BitmapData
// libvlc.getPixelData() sometimes is null and the exe hangs ...
if (libvlc.getPixelData() != null)
bitmapData.setPixels(frameRect, Bytes.ofData(bufferMem));
// (Stage3D)
// texture.uploadFromByteArray( Bytes.ofData(cast(bufferMem)), 0 );
// this.width++; //This is a horrible hack to force the texture to update... Surely there is a better way...
// this.width--;
}
}
catch (e:Error)
{
trace("error: " + e);
throw new Error("render broke xd");
}
}
#end
}
}
/////////////////////////////////////////////////////////////////////////////////////
function setVolume(vol:Float)
{
#if (cpp && !mobile)
if (libvlc != null && initComplete)
libvlc.setVolume(vol * 100);
#end
}
public function getVolume():Float
{
#if (cpp && !mobile)
if (libvlc != null && initComplete)
return libvlc.getVolume();
else
return 0;
#else
return 0;
#end
}
/////////////////////////////////////////////////////////////////////////////////////
function statusOnOpening()
{
if (onOpening != null)
onOpening();
}
function statusOnBuffering()
{
trace("buffering");
if (onBuffer != null)
onBuffer();
}
function statusOnPlaying()
{
if (!initComplete)
{
isPlaying = true;
initComplete = true;
videoInitComplete();
}
}
function statusOnPaused()
{
if (isPlaying)
isPlaying = false;
if (onPause != null)
onPause();
}
function statusOnStopped()
{
if (isPlaying)
isPlaying = false;
if (onStop != null)
onStop();
}
function statusOnEndReached()
{
if (isPlaying)
isPlaying = false;
// trace("end reached!");
if (onComplete != null)
onComplete();
}
function statusOnTimeChanged(newTime:Int)
{
time = newTime;
if (onProgress != null)
onProgress();
}
function statusOnPositionChanged(newPos:Int)
{
}
function statusOnSeekableChanged(newPos:Int)
{
if (onSeek != null)
onSeek();
}
function statusOnForward()
{
}
function statusOnBackward()
{
}
function onDisplay()
{
// render();
}
function statusOnError()
{
trace("VLC ERROR - File not found?");
if (onError != null)
onError();
}
/////////////////////////////////////////////////////////////////////////////////////
private override function get_width():Float
{
return _width;
}
public override function set_width(value:Float):Float
{
_width = value;
return super.set_width(value);
}
private override function get_height():Float
{
return _height;
}
public override function set_height(value:Float):Float
{
_height = value;
return super.set_height(value);
}
function get_volume():Float
{
return volume;
}
function set_volume(value:Float):Float
{
setVolume(value);
return volume = value;
}
// ===================================================================================
// Dispose
//-----------------------------------------------------------------------------------
public function dispose()
{
#if (cpp && !mobile)
libvlc.stop();
#end
stage.removeEventListener(Event.ENTER_FRAME, vLoop);
if (texture != null)
{
texture.dispose();
texture = null;
}
onVideoReady = null;
onComplete = null;
onPause = null;
onPlay = null;
onResume = null;
onSeek = null;
onStop = null;
onBuffer = null;
onProgress = null;
onError = null;
bufferMem = null;
isDisposed = true;
#if (cpp && !mobile)
while (!isPlaying && !isDisposed)
{
libvlc.dispose();
libvlc = null;
}
#end
}
/////////////////////////////////////////////////////////////////////////////////////
}
#end

View file

@ -0,0 +1,69 @@
/*****************************************************************************
* deprecated.h: libvlc deprecated API
*****************************************************************************
* Copyright (C) 1998-2008 VLC authors and VideoLAN
* $Id: 7f55090fcd482489ceed9145ce2253e78fa6fd2a $
*
* Authors: Clément Stenac <zorglub@videolan.org>
* Jean-Paul Saman <jpsaman@videolan.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef LIBVLC_DEPRECATED_H
#define LIBVLC_DEPRECATED_H 1
/**
* \file
* This file defines libvlc deprecated API
*/
# ifdef __cplusplus
extern "C" {
# endif
/*****************************************************************************
* Playlist (Deprecated)
*****************************************************************************/
/** \defgroup libvlc_playlist LibVLC playlist (legacy)
* \ingroup libvlc
* @deprecated Use @ref libvlc_media_list instead.
* @{
*/
/**
* Start playing (if there is any item in the playlist).
*
* Additionnal playlist item options can be specified for addition to the
* item before it is played.
*
* \param p_instance the playlist instance
* \param i_id the item to play. If this is a negative number, the next
* item will be selected. Otherwise, the item with the given ID will be
* played
* \param i_options the number of options to add to the item
* \param ppsz_options the options to add to the item
*/
LIBVLC_DEPRECATED LIBVLC_API
void libvlc_playlist_play( libvlc_instance_t *p_instance, int i_id,
int i_options, char **ppsz_options );
/** @}*/
# ifdef __cplusplus
}
# endif
#endif /* _LIBVLC_DEPRECATED_H */

View file

@ -0,0 +1,634 @@
/*****************************************************************************
* libvlc.h: libvlc external API
*****************************************************************************
* Copyright (C) 1998-2009 VLC authors and VideoLAN
* $Id: 485f4ff7198fd86d7935613b254f074f47577dd5 $
*
* Authors: Clément Stenac <zorglub@videolan.org>
* Jean-Paul Saman <jpsaman@videolan.org>
* Pierre d'Herbemont <pdherbemont@videolan.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/**
* \file
* This file defines libvlc external API
*/
/**
* \defgroup libvlc LibVLC
* LibVLC is the external programming interface of the VLC media player.
* It is used to embed VLC into other applications or frameworks.
* @{
*/
#ifndef VLC_LIBVLC_H
#define VLC_LIBVLC_H 1
#if defined (_WIN32) && defined (DLL_EXPORT)
# define LIBVLC_API __declspec(dllexport)
#elif defined (__GNUC__) && (__GNUC__ >= 4)
# define LIBVLC_API __attribute__((visibility("default")))
#else
# define LIBVLC_API
#endif
#ifdef __LIBVLC__
/* Avoid unhelpful warnings from libvlc with our deprecated APIs */
# define LIBVLC_DEPRECATED
#elif defined(__GNUC__) && \
(__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ > 0)
# define LIBVLC_DEPRECATED __attribute__((deprecated))
#else
# define LIBVLC_DEPRECATED
#endif
#include <stdio.h>
#include <stdarg.h>
# ifdef __cplusplus
extern "C" {
# endif
#include <vlc/libvlc_structures.h>
/** \defgroup libvlc_core LibVLC core
* \ingroup libvlc
* Before it can do anything useful, LibVLC must be initialized.
* You can create one (or more) instance(s) of LibVLC in a given process,
* with libvlc_new() and destroy them with libvlc_release().
*
* \version Unless otherwise stated, these functions are available
* from LibVLC versions numbered 1.1.0 or more.
* Earlier versions (0.9.x and 1.0.x) are <b>not</b> compatible.
* @{
*/
/** \defgroup libvlc_error LibVLC error handling
* @{
*/
/**
* A human-readable error message for the last LibVLC error in the calling
* thread. The resulting string is valid until another error occurs (at least
* until the next LibVLC call).
*
* @warning
* This will be NULL if there was no error.
*/
LIBVLC_API const char *libvlc_errmsg (void);
/**
* Clears the LibVLC error status for the current thread. This is optional.
* By default, the error status is automatically overridden when a new error
* occurs, and destroyed when the thread exits.
*/
LIBVLC_API void libvlc_clearerr (void);
/**
* Sets the LibVLC error status and message for the current thread.
* Any previous error is overridden.
* \param fmt the format string
* \param ap the arguments
* \return a nul terminated string in any case
*/
LIBVLC_API const char *libvlc_vprinterr (const char *fmt, va_list ap);
/**
* Sets the LibVLC error status and message for the current thread.
* Any previous error is overridden.
* \param fmt the format string
* \param args the arguments
* \return a nul terminated string in any case
*/
LIBVLC_API const char *libvlc_printerr (const char *fmt, ...);
/**@} */
/**
* Create and initialize a libvlc instance.
* This functions accept a list of "command line" arguments similar to the
* main(). These arguments affect the LibVLC instance default configuration.
*
* \version
* Arguments are meant to be passed from the command line to LibVLC, just like
* VLC media player does. The list of valid arguments depends on the LibVLC
* version, the operating system and platform, and set of available LibVLC
* plugins. Invalid or unsupported arguments will cause the function to fail
* (i.e. return NULL). Also, some arguments may alter the behaviour or
* otherwise interfere with other LibVLC functions.
*
* \warning
* There is absolutely no warranty or promise of forward, backward and
* cross-platform compatibility with regards to libvlc_new() arguments.
* We recommend that you do not use them, other than when debugging.
*
* \param argc the number of arguments (should be 0)
* \param argv list of arguments (should be NULL)
* \return the libvlc instance or NULL in case of error
*/
LIBVLC_API libvlc_instance_t *
libvlc_new( int argc , const char *const *argv );
/**
* Decrement the reference count of a libvlc instance, and destroy it
* if it reaches zero.
*
* \param p_instance the instance to destroy
*/
LIBVLC_API void libvlc_release( libvlc_instance_t *p_instance );
/**
* Increments the reference count of a libvlc instance.
* The initial reference count is 1 after libvlc_new() returns.
*
* \param p_instance the instance to reference
*/
LIBVLC_API void libvlc_retain( libvlc_instance_t *p_instance );
/**
* Try to start a user interface for the libvlc instance.
*
* \param p_instance the instance
* \param name interface name, or NULL for default
* \return 0 on success, -1 on error.
*/
LIBVLC_API
int libvlc_add_intf( libvlc_instance_t *p_instance, const char *name );
/**
* Registers a callback for the LibVLC exit event. This is mostly useful if
* the VLC playlist and/or at least one interface are started with
* libvlc_playlist_play() or libvlc_add_intf() respectively.
* Typically, this function will wake up your application main loop (from
* another thread).
*
* \note This function should be called before the playlist or interface are
* started. Otherwise, there is a small race condition: the exit event could
* be raised before the handler is registered.
*
* \param p_instance LibVLC instance
* \param cb callback to invoke when LibVLC wants to exit,
* or NULL to disable the exit handler (as by default)
* \param opaque data pointer for the callback
* \warning This function and libvlc_wait() cannot be used at the same time.
*/
LIBVLC_API
void libvlc_set_exit_handler( libvlc_instance_t *p_instance,
void (*cb) (void *), void *opaque );
/**
* Waits until an interface causes the instance to exit.
* You should start at least one interface first, using libvlc_add_intf().
*
* \param p_instance the instance
* \warning This function wastes one thread doing basically nothing.
* libvlc_set_exit_handler() should be used instead.
*/
LIBVLC_DEPRECATED LIBVLC_API
void libvlc_wait( libvlc_instance_t *p_instance );
/**
* Sets the application name. LibVLC passes this as the user agent string
* when a protocol requires it.
*
* \param p_instance LibVLC instance
* \param name human-readable application name, e.g. "FooBar player 1.2.3"
* \param http HTTP User Agent, e.g. "FooBar/1.2.3 Python/2.6.0"
* \version LibVLC 1.1.1 or later
*/
LIBVLC_API
void libvlc_set_user_agent( libvlc_instance_t *p_instance,
const char *name, const char *http );
/**
* Sets some meta-informations about the application.
* See also libvlc_set_user_agent().
*
* \param p_instance LibVLC instance
* \param id Java-style application identifier, e.g. "com.acme.foobar"
* \param version application version numbers, e.g. "1.2.3"
* \param icon application icon name, e.g. "foobar"
* \version LibVLC 2.1.0 or later.
*/
LIBVLC_API
void libvlc_set_app_id( libvlc_instance_t *p_instance, const char *id,
const char *version, const char *icon );
/**
* Retrieve libvlc version.
*
* Example: "1.1.0-git The Luggage"
*
* \return a string containing the libvlc version
*/
LIBVLC_API const char * libvlc_get_version(void);
/**
* Retrieve libvlc compiler version.
*
* Example: "gcc version 4.2.3 (Ubuntu 4.2.3-2ubuntu6)"
*
* \return a string containing the libvlc compiler version
*/
LIBVLC_API const char * libvlc_get_compiler(void);
/**
* Retrieve libvlc changeset.
*
* Example: "aa9bce0bc4"
*
* \return a string containing the libvlc changeset
*/
LIBVLC_API const char * libvlc_get_changeset(void);
/**
* Frees an heap allocation returned by a LibVLC function.
* If you know you're using the same underlying C run-time as the LibVLC
* implementation, then you can call ANSI C free() directly instead.
*
* \param ptr the pointer
*/
LIBVLC_API void libvlc_free( void *ptr );
/** \defgroup libvlc_event LibVLC asynchronous events
* LibVLC emits asynchronous events.
*
* Several LibVLC objects (such @ref libvlc_instance_t as
* @ref libvlc_media_player_t) generate events asynchronously. Each of them
* provides @ref libvlc_event_manager_t event manager. You can subscribe to
* events with libvlc_event_attach() and unsubscribe with
* libvlc_event_detach().
* @{
*/
/**
* Event manager that belongs to a libvlc object, and from whom events can
* be received.
*/
typedef struct libvlc_event_manager_t libvlc_event_manager_t;
struct libvlc_event_t;
/**
* Type of a LibVLC event.
*/
typedef int libvlc_event_type_t;
/**
* Callback function notification
* \param p_event the event triggering the callback
*/
typedef void ( *libvlc_callback_t )( const struct libvlc_event_t *, void * );
/**
* Register for an event notification.
*
* \param p_event_manager the event manager to which you want to attach to.
* Generally it is obtained by vlc_my_object_event_manager() where
* my_object is the object you want to listen to.
* \param i_event_type the desired event to which we want to listen
* \param f_callback the function to call when i_event_type occurs
* \param user_data user provided data to carry with the event
* \return 0 on success, ENOMEM on error
*/
LIBVLC_API int libvlc_event_attach( libvlc_event_manager_t *p_event_manager,
libvlc_event_type_t i_event_type,
libvlc_callback_t f_callback,
void *user_data );
/**
* Unregister an event notification.
*
* \param p_event_manager the event manager
* \param i_event_type the desired event to which we want to unregister
* \param f_callback the function to call when i_event_type occurs
* \param p_user_data user provided data to carry with the event
*/
LIBVLC_API void libvlc_event_detach( libvlc_event_manager_t *p_event_manager,
libvlc_event_type_t i_event_type,
libvlc_callback_t f_callback,
void *p_user_data );
/**
* Get an event's type name.
*
* \param event_type the desired event
*/
LIBVLC_API const char * libvlc_event_type_name( libvlc_event_type_t event_type );
/** @} */
/** \defgroup libvlc_log LibVLC logging
* libvlc_log_* functions provide access to the LibVLC messages log.
* This is used for logging and debugging.
* @{
*/
/**
* Logging messages level.
* \note Future LibVLC versions may define new levels.
*/
enum libvlc_log_level
{
LIBVLC_DEBUG=0, /**< Debug message */
LIBVLC_NOTICE=2, /**< Important informational message */
LIBVLC_WARNING=3, /**< Warning (potential error) message */
LIBVLC_ERROR=4 /**< Error message */
};
typedef struct vlc_log_t libvlc_log_t;
/**
* Gets debugging informations about a log message: the name of the VLC module
* emitting the message and the message location within the source code.
*
* The returned module name and file name will be NULL if unknown.
* The returned line number will similarly be zero if unknown.
*
* \param ctx message context (as passed to the @ref libvlc_log_cb callback)
* \param module module name storage (or NULL) [OUT]
* \param file source code file name storage (or NULL) [OUT]
* \param line source code file line number storage (or NULL) [OUT]
* \warning The returned module name and source code file name, if non-NULL,
* are only valid until the logging callback returns.
*
* \version LibVLC 2.1.0 or later
*/
LIBVLC_API void libvlc_log_get_context(const libvlc_log_t *ctx,
const char **module, const char **file, unsigned *line);
/**
* Gets VLC object informations about a log message: the type name of the VLC
* object emitting the message, the object header if any and a temporaly-unique
* object identifier. These informations are mainly meant for <b>manual</b>
* troubleshooting.
*
* The returned type name may be "generic" if unknown, but it cannot be NULL.
* The returned header will be NULL if unset; in current versions, the header
* is used to distinguish for VLM inputs.
* The returned object ID will be zero if the message is not associated with
* any VLC object.
*
* \param ctx message context (as passed to the @ref libvlc_log_cb callback)
* \param name object name storage (or NULL) [OUT]
* \param header object header (or NULL) [OUT]
* \param line source code file line number storage (or NULL) [OUT]
* \warning The returned module name and source code file name, if non-NULL,
* are only valid until the logging callback returns.
*
* \version LibVLC 2.1.0 or later
*/
LIBVLC_API void libvlc_log_get_object(const libvlc_log_t *ctx,
const char **name, const char **header, uintptr_t *id);
/**
* Callback prototype for LibVLC log message handler.
* \param data data pointer as given to libvlc_log_set()
* \param level message level (@ref enum libvlc_log_level)
* \param ctx message context (meta-informations about the message)
* \param fmt printf() format string (as defined by ISO C11)
* \param args variable argument list for the format
* \note Log message handlers <b>must</b> be thread-safe.
* \warning The message context pointer, the format string parameters and the
* variable arguments are only valid until the callback returns.
*/
typedef void (*libvlc_log_cb)(void *data, int level, const libvlc_log_t *ctx,
const char *fmt, va_list args);
/**
* Unsets the logging callback for a LibVLC instance. This is rarely needed:
* the callback is implicitly unset when the instance is destroyed.
* This function will wait for any pending callbacks invocation to complete
* (causing a deadlock if called from within the callback).
*
* \param p_instance libvlc instance
* \version LibVLC 2.1.0 or later
*/
LIBVLC_API void libvlc_log_unset( libvlc_instance_t * );
/**
* Sets the logging callback for a LibVLC instance.
* This function is thread-safe: it will wait for any pending callbacks
* invocation to complete.
*
* \param cb callback function pointer
* \param data opaque data pointer for the callback function
*
* \note Some log messages (especially debug) are emitted by LibVLC while
* is being initialized. These messages cannot be captured with this interface.
*
* \warning A deadlock may occur if this function is called from the callback.
*
* \param p_instance libvlc instance
* \version LibVLC 2.1.0 or later
*/
LIBVLC_API void libvlc_log_set( libvlc_instance_t *,
libvlc_log_cb cb, void *data );
/**
* Sets up logging to a file.
* \param p_instance libvlc instance
* \param stream FILE pointer opened for writing
* (the FILE pointer must remain valid until libvlc_log_unset())
* \version LibVLC 2.1.0 or later
*/
LIBVLC_API void libvlc_log_set_file( libvlc_instance_t *, FILE *stream );
/**
* Always returns minus one.
* This function is only provided for backward compatibility.
*
* \param p_instance ignored
* \return always -1
*/
LIBVLC_DEPRECATED LIBVLC_API
unsigned libvlc_get_log_verbosity( const libvlc_instance_t *p_instance );
/**
* This function does nothing.
* It is only provided for backward compatibility.
*
* \param p_instance ignored
* \param level ignored
*/
LIBVLC_DEPRECATED LIBVLC_API
void libvlc_set_log_verbosity( libvlc_instance_t *p_instance, unsigned level );
/**
* This function does nothing useful.
* It is only provided for backward compatibility.
*
* \param p_instance libvlc instance
* \return an unique pointer or NULL on error
*/
LIBVLC_DEPRECATED LIBVLC_API
libvlc_log_t *libvlc_log_open( libvlc_instance_t *p_instance );
/**
* Frees memory allocated by libvlc_log_open().
*
* \param p_log libvlc log instance or NULL
*/
LIBVLC_DEPRECATED LIBVLC_API
void libvlc_log_close( libvlc_log_t *p_log );
/**
* Always returns zero.
* This function is only provided for backward compatibility.
*
* \param p_log ignored
* \return always zero
*/
LIBVLC_DEPRECATED LIBVLC_API
unsigned libvlc_log_count( const libvlc_log_t *p_log );
/**
* This function does nothing.
* It is only provided for backward compatibility.
*
* \param p_log ignored
*/
LIBVLC_DEPRECATED LIBVLC_API
void libvlc_log_clear( libvlc_log_t *p_log );
/**
* This function does nothing useful.
* It is only provided for backward compatibility.
*
* \param p_log ignored
* \return an unique pointer or NULL on error or if the parameter was NULL
*/
LIBVLC_DEPRECATED LIBVLC_API
libvlc_log_iterator_t *libvlc_log_get_iterator( const libvlc_log_t *p_log );
/**
* Frees memory allocated by libvlc_log_get_iterator().
*
* \param p_iter libvlc log iterator or NULL
*/
LIBVLC_DEPRECATED LIBVLC_API
void libvlc_log_iterator_free( libvlc_log_iterator_t *p_iter );
/**
* Always returns zero.
* This function is only provided for backward compatibility.
*
* \param p_iter ignored
* \return always zero
*/
LIBVLC_DEPRECATED LIBVLC_API
int libvlc_log_iterator_has_next( const libvlc_log_iterator_t *p_iter );
/**
* Always returns NULL.
* This function is only provided for backward compatibility.
*
* \param p_iter libvlc log iterator or NULL
* \param p_buf ignored
* \return always NULL
*/
LIBVLC_DEPRECATED LIBVLC_API
libvlc_log_message_t *libvlc_log_iterator_next( libvlc_log_iterator_t *p_iter,
libvlc_log_message_t *p_buf );
/** @} */
/**
* Description of a module.
*/
typedef struct libvlc_module_description_t
{
char *psz_name;
char *psz_shortname;
char *psz_longname;
char *psz_help;
struct libvlc_module_description_t *p_next;
} libvlc_module_description_t;
/**
* Release a list of module descriptions.
*
* \param p_list the list to be released
*/
LIBVLC_API
void libvlc_module_description_list_release( libvlc_module_description_t *p_list );
/**
* Returns a list of audio filters that are available.
*
* \param p_instance libvlc instance
*
* \return a list of module descriptions. It should be freed with libvlc_module_description_list_release().
* In case of an error, NULL is returned.
*
* \see libvlc_module_description_t
* \see libvlc_module_description_list_release
*/
LIBVLC_API
libvlc_module_description_t *libvlc_audio_filter_list_get( libvlc_instance_t *p_instance );
/**
* Returns a list of video filters that are available.
*
* \param p_instance libvlc instance
*
* \return a list of module descriptions. It should be freed with libvlc_module_description_list_release().
* In case of an error, NULL is returned.
*
* \see libvlc_module_description_t
* \see libvlc_module_description_list_release
*/
LIBVLC_API
libvlc_module_description_t *libvlc_video_filter_list_get( libvlc_instance_t *p_instance );
/** @} */
/** \defgroup libvlc_clock LibVLC time
* These functions provide access to the LibVLC time/clock.
* @{
*/
/**
* Return the current time as defined by LibVLC. The unit is the microsecond.
* Time increases monotonically (regardless of time zone changes and RTC
* adjustements).
* The origin is arbitrary but consistent across the whole system
* (e.g. the system uptim, the time since the system was booted).
* \note On systems that support it, the POSIX monotonic clock is used.
*/
LIBVLC_API
int64_t libvlc_clock(void);
/**
* Return the delay (in microseconds) until a certain timestamp.
* \param pts timestamp
* \return negative if timestamp is in the past,
* positive if it is in the future
*/
static inline int64_t libvlc_delay(int64_t pts)
{
return pts - libvlc_clock();
}
/** @} */
# ifdef __cplusplus
}
# endif
#endif /* <vlc/libvlc.h> */

View file

@ -0,0 +1,238 @@
/*****************************************************************************
* libvlc_events.h: libvlc_events external API structure
*****************************************************************************
* Copyright (C) 1998-2010 VLC authors and VideoLAN
* $Id $
*
* Authors: Filippo Carone <littlejohn@videolan.org>
* Pierre d'Herbemont <pdherbemont@videolan.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef LIBVLC_EVENTS_H
#define LIBVLC_EVENTS_H 1
/**
* \file
* This file defines libvlc_event external API
*/
# ifdef __cplusplus
extern "C" {
# endif
/**
* \ingroup libvlc_event
* @{
*/
/**
* Event types
*/
enum libvlc_event_e {
/* Append new event types at the end of a category.
* Do not remove, insert or re-order any entry.
* Keep this in sync with lib/event.c:libvlc_event_type_name(). */
libvlc_MediaMetaChanged=0,
libvlc_MediaSubItemAdded,
libvlc_MediaDurationChanged,
libvlc_MediaParsedChanged,
libvlc_MediaFreed,
libvlc_MediaStateChanged,
libvlc_MediaSubItemTreeAdded,
libvlc_MediaPlayerMediaChanged=0x100,
libvlc_MediaPlayerNothingSpecial,
libvlc_MediaPlayerOpening,
libvlc_MediaPlayerBuffering,
libvlc_MediaPlayerPlaying,
libvlc_MediaPlayerPaused,
libvlc_MediaPlayerStopped,
libvlc_MediaPlayerForward,
libvlc_MediaPlayerBackward,
libvlc_MediaPlayerEndReached,
libvlc_MediaPlayerEncounteredError,
libvlc_MediaPlayerTimeChanged,
libvlc_MediaPlayerPositionChanged,
libvlc_MediaPlayerSeekableChanged,
libvlc_MediaPlayerPausableChanged,
libvlc_MediaPlayerTitleChanged,
libvlc_MediaPlayerSnapshotTaken,
libvlc_MediaPlayerLengthChanged,
libvlc_MediaPlayerVout,
libvlc_MediaListItemAdded=0x200,
libvlc_MediaListWillAddItem,
libvlc_MediaListItemDeleted,
libvlc_MediaListWillDeleteItem,
libvlc_MediaListViewItemAdded=0x300,
libvlc_MediaListViewWillAddItem,
libvlc_MediaListViewItemDeleted,
libvlc_MediaListViewWillDeleteItem,
libvlc_MediaListPlayerPlayed=0x400,
libvlc_MediaListPlayerNextItemSet,
libvlc_MediaListPlayerStopped,
libvlc_MediaDiscovererStarted=0x500,
libvlc_MediaDiscovererEnded,
libvlc_VlmMediaAdded=0x600,
libvlc_VlmMediaRemoved,
libvlc_VlmMediaChanged,
libvlc_VlmMediaInstanceStarted,
libvlc_VlmMediaInstanceStopped,
libvlc_VlmMediaInstanceStatusInit,
libvlc_VlmMediaInstanceStatusOpening,
libvlc_VlmMediaInstanceStatusPlaying,
libvlc_VlmMediaInstanceStatusPause,
libvlc_VlmMediaInstanceStatusEnd,
libvlc_VlmMediaInstanceStatusError
};
/**
* A LibVLC event
*/
typedef struct libvlc_event_t
{
int type; /**< Event type (see @ref libvlc_event_e) */
void *p_obj; /**< Object emitting the event */
union
{
/* media descriptor */
struct
{
libvlc_meta_t meta_type;
} media_meta_changed;
struct
{
libvlc_media_t * new_child;
} media_subitem_added;
struct
{
int64_t new_duration;
} media_duration_changed;
struct
{
int new_status;
} media_parsed_changed;
struct
{
libvlc_media_t * md;
} media_freed;
struct
{
libvlc_state_t new_state;
} media_state_changed;
struct
{
libvlc_media_t * item;
} media_subitemtree_added;
/* media instance */
struct
{
float new_cache;
} media_player_buffering;
struct
{
float new_position;
} media_player_position_changed;
struct
{
libvlc_time_t new_time;
} media_player_time_changed;
struct
{
int new_title;
} media_player_title_changed;
struct
{
int new_seekable;
} media_player_seekable_changed;
struct
{
int new_pausable;
} media_player_pausable_changed;
struct
{
int new_count;
} media_player_vout;
/* media list */
struct
{
libvlc_media_t * item;
int index;
} media_list_item_added;
struct
{
libvlc_media_t * item;
int index;
} media_list_will_add_item;
struct
{
libvlc_media_t * item;
int index;
} media_list_item_deleted;
struct
{
libvlc_media_t * item;
int index;
} media_list_will_delete_item;
/* media list player */
struct
{
libvlc_media_t * item;
} media_list_player_next_item_set;
/* snapshot taken */
struct
{
char* psz_filename ;
} media_player_snapshot_taken ;
/* Length changed */
struct
{
libvlc_time_t new_length;
} media_player_length_changed;
/* VLM media */
struct
{
const char * psz_media_name;
const char * psz_instance_name;
} vlm_media_event;
/* Extra MediaPlayer */
struct
{
libvlc_media_t * new_media;
} media_player_media_changed;
} u; /**< Type-dependent event description */
} libvlc_event_t;
/**@} */
# ifdef __cplusplus
}
# endif
#endif /* _LIBVLC_EVENTS_H */

View file

@ -0,0 +1,603 @@
/*****************************************************************************
* libvlc_media.h: libvlc external API
*****************************************************************************
* Copyright (C) 1998-2009 VLC authors and VideoLAN
* $Id: eaa41f01890d6921f81af3fc4fe4a33aa8905635 $
*
* Authors: Clément Stenac <zorglub@videolan.org>
* Jean-Paul Saman <jpsaman@videolan.org>
* Pierre d'Herbemont <pdherbemont@videolan.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/**
* \file
* This file defines libvlc_media external API
*/
#ifndef VLC_LIBVLC_MEDIA_H
#define VLC_LIBVLC_MEDIA_H 1
# ifdef __cplusplus
extern "C" {
# endif
/** \defgroup libvlc_media LibVLC media
* \ingroup libvlc
* @ref libvlc_media_t is an abstract representation of a playable media.
* It consists of a media location and various optional meta data.
* @{
*/
typedef struct libvlc_media_t libvlc_media_t;
/** defgroup libvlc_meta LibVLC meta data
* \ingroup libvlc_media
* @{
*/
/** Meta data types */
typedef enum libvlc_meta_t {
libvlc_meta_Title,
libvlc_meta_Artist,
libvlc_meta_Genre,
libvlc_meta_Copyright,
libvlc_meta_Album,
libvlc_meta_TrackNumber,
libvlc_meta_Description,
libvlc_meta_Rating,
libvlc_meta_Date,
libvlc_meta_Setting,
libvlc_meta_URL,
libvlc_meta_Language,
libvlc_meta_NowPlaying,
libvlc_meta_Publisher,
libvlc_meta_EncodedBy,
libvlc_meta_ArtworkURL,
libvlc_meta_TrackID
/* Add new meta types HERE */
} libvlc_meta_t;
/** @}*/
/**
* Note the order of libvlc_state_t enum must match exactly the order of
* \see mediacontrol_PlayerStatus, \see input_state_e enums,
* and VideoLAN.LibVLC.State (at bindings/cil/src/media.cs).
*
* Expected states by web plugins are:
* IDLE/CLOSE=0, OPENING=1, BUFFERING=2, PLAYING=3, PAUSED=4,
* STOPPING=5, ENDED=6, ERROR=7
*/
typedef enum libvlc_state_t
{
libvlc_NothingSpecial=0,
libvlc_Opening,
libvlc_Buffering,
libvlc_Playing,
libvlc_Paused,
libvlc_Stopped,
libvlc_Ended,
libvlc_Error
} libvlc_state_t;
enum
{
libvlc_media_option_trusted = 0x2,
libvlc_media_option_unique = 0x100
};
typedef enum libvlc_track_type_t
{
libvlc_track_unknown = -1,
libvlc_track_audio = 0,
libvlc_track_video = 1,
libvlc_track_text = 2
} libvlc_track_type_t;
/** defgroup libvlc_media_stats_t LibVLC media statistics
* \ingroup libvlc_media
* @{
*/
typedef struct libvlc_media_stats_t
{
/* Input */
int i_read_bytes;
float f_input_bitrate;
/* Demux */
int i_demux_read_bytes;
float f_demux_bitrate;
int i_demux_corrupted;
int i_demux_discontinuity;
/* Decoders */
int i_decoded_video;
int i_decoded_audio;
/* Video Output */
int i_displayed_pictures;
int i_lost_pictures;
/* Audio output */
int i_played_abuffers;
int i_lost_abuffers;
/* Stream output */
int i_sent_packets;
int i_sent_bytes;
float f_send_bitrate;
} libvlc_media_stats_t;
/** @}*/
typedef struct libvlc_media_track_info_t
{
/* Codec fourcc */
uint32_t i_codec;
int i_id;
libvlc_track_type_t i_type;
/* Codec specific */
int i_profile;
int i_level;
union {
struct {
/* Audio specific */
unsigned i_channels;
unsigned i_rate;
} audio;
struct {
/* Video specific */
unsigned i_height;
unsigned i_width;
} video;
} u;
} libvlc_media_track_info_t;
typedef struct libvlc_audio_track_t
{
unsigned i_channels;
unsigned i_rate;
} libvlc_audio_track_t;
typedef struct libvlc_video_track_t
{
unsigned i_height;
unsigned i_width;
unsigned i_sar_num;
unsigned i_sar_den;
unsigned i_frame_rate_num;
unsigned i_frame_rate_den;
} libvlc_video_track_t;
typedef struct libvlc_subtitle_track_t
{
char *psz_encoding;
} libvlc_subtitle_track_t;
typedef struct libvlc_media_track_t
{
/* Codec fourcc */
uint32_t i_codec;
uint32_t i_original_fourcc;
int i_id;
libvlc_track_type_t i_type;
/* Codec specific */
int i_profile;
int i_level;
union {
libvlc_audio_track_t *audio;
libvlc_video_track_t *video;
libvlc_subtitle_track_t *subtitle;
};
unsigned int i_bitrate;
char *psz_language;
char *psz_description;
} libvlc_media_track_t;
/**
* Create a media with a certain given media resource location,
* for instance a valid URL.
*
* \note To refer to a local file with this function,
* the file://... URI syntax <b>must</b> be used (see IETF RFC3986).
* We recommend using libvlc_media_new_path() instead when dealing with
* local files.
*
* \see libvlc_media_release
*
* \param p_instance the instance
* \param psz_mrl the media location
* \return the newly created media or NULL on error
*/
LIBVLC_API libvlc_media_t *libvlc_media_new_location(
libvlc_instance_t *p_instance,
const char * psz_mrl );
/**
* Create a media for a certain file path.
*
* \see libvlc_media_release
*
* \param p_instance the instance
* \param path local filesystem path
* \return the newly created media or NULL on error
*/
LIBVLC_API libvlc_media_t *libvlc_media_new_path(
libvlc_instance_t *p_instance,
const char *path );
/**
* Create a media for an already open file descriptor.
* The file descriptor shall be open for reading (or reading and writing).
*
* Regular file descriptors, pipe read descriptors and character device
* descriptors (including TTYs) are supported on all platforms.
* Block device descriptors are supported where available.
* Directory descriptors are supported on systems that provide fdopendir().
* Sockets are supported on all platforms where they are file descriptors,
* i.e. all except Windows.
*
* \note This library will <b>not</b> automatically close the file descriptor
* under any circumstance. Nevertheless, a file descriptor can usually only be
* rendered once in a media player. To render it a second time, the file
* descriptor should probably be rewound to the beginning with lseek().
*
* \see libvlc_media_release
*
* \version LibVLC 1.1.5 and later.
*
* \param p_instance the instance
* \param fd open file descriptor
* \return the newly created media or NULL on error
*/
LIBVLC_API libvlc_media_t *libvlc_media_new_fd(
libvlc_instance_t *p_instance,
int fd );
/**
* Create a media as an empty node with a given name.
*
* \see libvlc_media_release
*
* \param p_instance the instance
* \param psz_name the name of the node
* \return the new empty media or NULL on error
*/
LIBVLC_API libvlc_media_t *libvlc_media_new_as_node(
libvlc_instance_t *p_instance,
const char * psz_name );
/**
* Add an option to the media.
*
* This option will be used to determine how the media_player will
* read the media. This allows to use VLC's advanced
* reading/streaming options on a per-media basis.
*
* \note The options are listed in 'vlc --long-help' from the command line,
* e.g. "-sout-all". Keep in mind that available options and their semantics
* vary across LibVLC versions and builds.
* \warning Not all options affects libvlc_media_t objects:
* Specifically, due to architectural issues most audio and video options,
* such as text renderer options, have no effects on an individual media.
* These options must be set through libvlc_new() instead.
*
* \param p_md the media descriptor
* \param psz_options the options (as a string)
*/
LIBVLC_API void libvlc_media_add_option(
libvlc_media_t *p_md,
const char * psz_options );
/**
* Add an option to the media with configurable flags.
*
* This option will be used to determine how the media_player will
* read the media. This allows to use VLC's advanced
* reading/streaming options on a per-media basis.
*
* The options are detailed in vlc --long-help, for instance
* "--sout-all". Note that all options are not usable on medias:
* specifically, due to architectural issues, video-related options
* such as text renderer options cannot be set on a single media. They
* must be set on the whole libvlc instance instead.
*
* \param p_md the media descriptor
* \param psz_options the options (as a string)
* \param i_flags the flags for this option
*/
LIBVLC_API void libvlc_media_add_option_flag(
libvlc_media_t *p_md,
const char * psz_options,
unsigned i_flags );
/**
* Retain a reference to a media descriptor object (libvlc_media_t). Use
* libvlc_media_release() to decrement the reference count of a
* media descriptor object.
*
* \param p_md the media descriptor
*/
LIBVLC_API void libvlc_media_retain( libvlc_media_t *p_md );
/**
* Decrement the reference count of a media descriptor object. If the
* reference count is 0, then libvlc_media_release() will release the
* media descriptor object. It will send out an libvlc_MediaFreed event
* to all listeners. If the media descriptor object has been released it
* should not be used again.
*
* \param p_md the media descriptor
*/
LIBVLC_API void libvlc_media_release( libvlc_media_t *p_md );
/**
* Get the media resource locator (mrl) from a media descriptor object
*
* \param p_md a media descriptor object
* \return string with mrl of media descriptor object
*/
LIBVLC_API char *libvlc_media_get_mrl( libvlc_media_t *p_md );
/**
* Duplicate a media descriptor object.
*
* \param p_md a media descriptor object.
*/
LIBVLC_API libvlc_media_t *libvlc_media_duplicate( libvlc_media_t *p_md );
/**
* Read the meta of the media.
*
* If the media has not yet been parsed this will return NULL.
*
* This methods automatically calls libvlc_media_parse_async(), so after calling
* it you may receive a libvlc_MediaMetaChanged event. If you prefer a synchronous
* version ensure that you call libvlc_media_parse() before get_meta().
*
* \see libvlc_media_parse
* \see libvlc_media_parse_async
* \see libvlc_MediaMetaChanged
*
* \param p_md the media descriptor
* \param e_meta the meta to read
* \return the media's meta
*/
LIBVLC_API char *libvlc_media_get_meta( libvlc_media_t *p_md,
libvlc_meta_t e_meta );
/**
* Set the meta of the media (this function will not save the meta, call
* libvlc_media_save_meta in order to save the meta)
*
* \param p_md the media descriptor
* \param e_meta the meta to write
* \param psz_value the media's meta
*/
LIBVLC_API void libvlc_media_set_meta( libvlc_media_t *p_md,
libvlc_meta_t e_meta,
const char *psz_value );
/**
* Save the meta previously set
*
* \param p_md the media desriptor
* \return true if the write operation was successful
*/
LIBVLC_API int libvlc_media_save_meta( libvlc_media_t *p_md );
/**
* Get current state of media descriptor object. Possible media states
* are defined in libvlc_structures.c ( libvlc_NothingSpecial=0,
* libvlc_Opening, libvlc_Buffering, libvlc_Playing, libvlc_Paused,
* libvlc_Stopped, libvlc_Ended,
* libvlc_Error).
*
* \see libvlc_state_t
* \param p_md a media descriptor object
* \return state of media descriptor object
*/
LIBVLC_API libvlc_state_t libvlc_media_get_state(
libvlc_media_t *p_md );
/**
* Get the current statistics about the media
* \param p_md: media descriptor object
* \param p_stats: structure that contain the statistics about the media
* (this structure must be allocated by the caller)
* \return true if the statistics are available, false otherwise
*
* \libvlc_return_bool
*/
LIBVLC_API int libvlc_media_get_stats( libvlc_media_t *p_md,
libvlc_media_stats_t *p_stats );
/* The following method uses libvlc_media_list_t, however, media_list usage is optionnal
* and this is here for convenience */
#define VLC_FORWARD_DECLARE_OBJECT(a) struct a
/**
* Get subitems of media descriptor object. This will increment
* the reference count of supplied media descriptor object. Use
* libvlc_media_list_release() to decrement the reference counting.
*
* \param p_md media descriptor object
* \return list of media descriptor subitems or NULL
*/
LIBVLC_API VLC_FORWARD_DECLARE_OBJECT(libvlc_media_list_t *)
libvlc_media_subitems( libvlc_media_t *p_md );
/**
* Get event manager from media descriptor object.
* NOTE: this function doesn't increment reference counting.
*
* \param p_md a media descriptor object
* \return event manager object
*/
LIBVLC_API libvlc_event_manager_t *
libvlc_media_event_manager( libvlc_media_t *p_md );
/**
* Get duration (in ms) of media descriptor object item.
*
* \param p_md media descriptor object
* \return duration of media item or -1 on error
*/
LIBVLC_API libvlc_time_t
libvlc_media_get_duration( libvlc_media_t *p_md );
/**
* Parse a media.
*
* This fetches (local) meta data and tracks information.
* The method is synchronous.
*
* \see libvlc_media_parse_async
* \see libvlc_media_get_meta
* \see libvlc_media_get_tracks_info
*
* \param p_md media descriptor object
*/
LIBVLC_API void
libvlc_media_parse( libvlc_media_t *p_md );
/**
* Parse a media.
*
* This fetches (local) meta data and tracks information.
* The method is the asynchronous of libvlc_media_parse().
*
* To track when this is over you can listen to libvlc_MediaParsedChanged
* event. However if the media was already parsed you will not receive this
* event.
*
* \see libvlc_media_parse
* \see libvlc_MediaParsedChanged
* \see libvlc_media_get_meta
* \see libvlc_media_get_tracks_info
*
* \param p_md media descriptor object
*/
LIBVLC_API void
libvlc_media_parse_async( libvlc_media_t *p_md );
/**
* Get Parsed status for media descriptor object.
*
* \see libvlc_MediaParsedChanged
*
* \param p_md media descriptor object
* \return true if media object has been parsed otherwise it returns false
*
* \libvlc_return_bool
*/
LIBVLC_API int
libvlc_media_is_parsed( libvlc_media_t *p_md );
/**
* Sets media descriptor's user_data. user_data is specialized data
* accessed by the host application, VLC.framework uses it as a pointer to
* an native object that references a libvlc_media_t pointer
*
* \param p_md media descriptor object
* \param p_new_user_data pointer to user data
*/
LIBVLC_API void
libvlc_media_set_user_data( libvlc_media_t *p_md, void *p_new_user_data );
/**
* Get media descriptor's user_data. user_data is specialized data
* accessed by the host application, VLC.framework uses it as a pointer to
* an native object that references a libvlc_media_t pointer
*
* \param p_md media descriptor object
*/
LIBVLC_API void *libvlc_media_get_user_data( libvlc_media_t *p_md );
/**
* Get media descriptor's elementary streams description
*
* Note, you need to call libvlc_media_parse() or play the media at least once
* before calling this function.
* Not doing this will result in an empty array.
*
* \deprecated Use libvlc_media_tracks_get instead
*
* \param p_md media descriptor object
* \param tracks address to store an allocated array of Elementary Streams
* descriptions (must be freed by the caller) [OUT]
*
* \return the number of Elementary Streams
*/
LIBVLC_DEPRECATED LIBVLC_API
int libvlc_media_get_tracks_info( libvlc_media_t *p_md,
libvlc_media_track_info_t **tracks );
/**
* Get media descriptor's elementary streams description
*
* Note, you need to call libvlc_media_parse() or play the media at least once
* before calling this function.
* Not doing this will result in an empty array.
*
* \version LibVLC 2.1.0 and later.
*
* \param p_md media descriptor object
* \param tracks address to store an allocated array of Elementary Streams
* descriptions (must be freed with libvlc_media_tracks_release
by the caller) [OUT]
*
* \return the number of Elementary Streams (zero on error)
*/
LIBVLC_API
unsigned libvlc_media_tracks_get( libvlc_media_t *p_md,
libvlc_media_track_t ***tracks );
/**
* Release media descriptor's elementary streams description array
*
* \version LibVLC 2.1.0 and later.
*
* \param p_tracks tracks info array to release
* \param i_count number of elements in the array
*/
LIBVLC_API
void libvlc_media_tracks_release( libvlc_media_track_t **p_tracks,
unsigned i_count );
/** @}*/
# ifdef __cplusplus
}
# endif
#endif /* VLC_LIBVLC_MEDIA_H */

View file

@ -0,0 +1,111 @@
/*****************************************************************************
* libvlc_media_discoverer.h: libvlc external API
*****************************************************************************
* Copyright (C) 1998-2009 VLC authors and VideoLAN
* $Id: cf263b0536d9b19e725e039f12ef20eaa392fec3 $
*
* Authors: Clément Stenac <zorglub@videolan.org>
* Jean-Paul Saman <jpsaman@videolan.org>
* Pierre d'Herbemont <pdherbemont@videolan.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/**
* \file
* This file defines libvlc_media_discoverer external API
*/
#ifndef VLC_LIBVLC_MEDIA_DISCOVERER_H
#define VLC_LIBVLC_MEDIA_DISCOVERER_H 1
# ifdef __cplusplus
extern "C" {
# endif
/** \defgroup libvlc_media_discoverer LibVLC media discovery
* \ingroup libvlc
* LibVLC media discovery finds available media via various means.
* This corresponds to the service discovery functionality in VLC media player.
* Different plugins find potential medias locally (e.g. user media directory),
* from peripherals (e.g. video capture device), on the local network
* (e.g. SAP) or on the Internet (e.g. Internet radios).
* @{
*/
typedef struct libvlc_media_discoverer_t libvlc_media_discoverer_t;
/**
* Discover media service by name.
*
* \param p_inst libvlc instance
* \param psz_name service name
* \return media discover object or NULL in case of error
*/
LIBVLC_API libvlc_media_discoverer_t *
libvlc_media_discoverer_new_from_name( libvlc_instance_t * p_inst,
const char * psz_name );
/**
* Release media discover object. If the reference count reaches 0, then
* the object will be released.
*
* \param p_mdis media service discover object
*/
LIBVLC_API void libvlc_media_discoverer_release( libvlc_media_discoverer_t * p_mdis );
/**
* Get media service discover object its localized name.
*
* \param p_mdis media discover object
* \return localized name
*/
LIBVLC_API char * libvlc_media_discoverer_localized_name( libvlc_media_discoverer_t * p_mdis );
/**
* Get media service discover media list.
*
* \param p_mdis media service discover object
* \return list of media items
*/
LIBVLC_API libvlc_media_list_t * libvlc_media_discoverer_media_list( libvlc_media_discoverer_t * p_mdis );
/**
* Get event manager from media service discover object.
*
* \param p_mdis media service discover object
* \return event manager object.
*/
LIBVLC_API libvlc_event_manager_t *
libvlc_media_discoverer_event_manager( libvlc_media_discoverer_t * p_mdis );
/**
* Query if media service discover object is running.
*
* \param p_mdis media service discover object
* \return true if running, false if not
*
* \libvlc_return_bool
*/
LIBVLC_API int
libvlc_media_discoverer_is_running( libvlc_media_discoverer_t * p_mdis );
/**@} */
# ifdef __cplusplus
}
# endif
#endif /* <vlc/libvlc.h> */

View file

@ -0,0 +1,99 @@
/*****************************************************************************
* libvlc_media_library.h: libvlc external API
*****************************************************************************
* Copyright (C) 1998-2009 VLC authors and VideoLAN
* $Id: fa7094a6a8aac42607490c9982d9f4d082c2794c $
*
* Authors: Clément Stenac <zorglub@videolan.org>
* Jean-Paul Saman <jpsaman@videolan.org>
* Pierre d'Herbemont <pdherbemont@videolan.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/**
* \file
* This file defines libvlc_media_library external API
*/
#ifndef VLC_LIBVLC_MEDIA_LIBRARY_H
#define VLC_LIBVLC_MEDIA_LIBRARY_H 1
# ifdef __cplusplus
extern "C" {
# endif
/** \defgroup libvlc_media_library LibVLC media library
* \ingroup libvlc
* @{
*/
typedef struct libvlc_media_library_t libvlc_media_library_t;
/**
* Create an new Media Library object
*
* \param p_instance the libvlc instance
* \return a new object or NULL on error
*/
LIBVLC_API libvlc_media_library_t *
libvlc_media_library_new( libvlc_instance_t * p_instance );
/**
* Release media library object. This functions decrements the
* reference count of the media library object. If it reaches 0,
* then the object will be released.
*
* \param p_mlib media library object
*/
LIBVLC_API void
libvlc_media_library_release( libvlc_media_library_t * p_mlib );
/**
* Retain a reference to a media library object. This function will
* increment the reference counting for this object. Use
* libvlc_media_library_release() to decrement the reference count.
*
* \param p_mlib media library object
*/
LIBVLC_API void
libvlc_media_library_retain( libvlc_media_library_t * p_mlib );
/**
* Load media library.
*
* \param p_mlib media library object
* \return 0 on success, -1 on error
*/
LIBVLC_API int
libvlc_media_library_load( libvlc_media_library_t * p_mlib );
/**
* Get media library subitems.
*
* \param p_mlib media library object
* \return media list subitems
*/
LIBVLC_API libvlc_media_list_t *
libvlc_media_library_media_list( libvlc_media_library_t * p_mlib );
/** @} */
# ifdef __cplusplus
}
# endif
#endif /* VLC_LIBVLC_MEDIA_LIBRARY_H */

View file

@ -0,0 +1,209 @@
/*****************************************************************************
* libvlc_media_list.h: libvlc_media_list API
*****************************************************************************
* Copyright (C) 1998-2008 VLC authors and VideoLAN
* $Id: 015824bf54e656cc67838452c7e99a00a452af6e $
*
* Authors: Pierre d'Herbemont
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef LIBVLC_MEDIA_LIST_H
#define LIBVLC_MEDIA_LIST_H 1
/**
* \file
* This file defines libvlc_media_list API
*/
# ifdef __cplusplus
extern "C" {
# endif
/** \defgroup libvlc_media_list LibVLC media list
* \ingroup libvlc
* A LibVLC media list holds multiple @ref libvlc_media_t media descriptors.
* @{
*/
typedef struct libvlc_media_list_t libvlc_media_list_t;
/**
* Create an empty media list.
*
* \param p_instance libvlc instance
* \return empty media list, or NULL on error
*/
LIBVLC_API libvlc_media_list_t *
libvlc_media_list_new( libvlc_instance_t *p_instance );
/**
* Release media list created with libvlc_media_list_new().
*
* \param p_ml a media list created with libvlc_media_list_new()
*/
LIBVLC_API void
libvlc_media_list_release( libvlc_media_list_t *p_ml );
/**
* Retain reference to a media list
*
* \param p_ml a media list created with libvlc_media_list_new()
*/
LIBVLC_API void
libvlc_media_list_retain( libvlc_media_list_t *p_ml );
LIBVLC_DEPRECATED int
libvlc_media_list_add_file_content( libvlc_media_list_t * p_ml,
const char * psz_uri );
/**
* Associate media instance with this media list instance.
* If another media instance was present it will be released.
* The libvlc_media_list_lock should NOT be held upon entering this function.
*
* \param p_ml a media list instance
* \param p_md media instance to add
*/
LIBVLC_API void
libvlc_media_list_set_media( libvlc_media_list_t *p_ml, libvlc_media_t *p_md );
/**
* Get media instance from this media list instance. This action will increase
* the refcount on the media instance.
* The libvlc_media_list_lock should NOT be held upon entering this function.
*
* \param p_ml a media list instance
* \return media instance
*/
LIBVLC_API libvlc_media_t *
libvlc_media_list_media( libvlc_media_list_t *p_ml );
/**
* Add media instance to media list
* The libvlc_media_list_lock should be held upon entering this function.
*
* \param p_ml a media list instance
* \param p_md a media instance
* \return 0 on success, -1 if the media list is read-only
*/
LIBVLC_API int
libvlc_media_list_add_media( libvlc_media_list_t *p_ml, libvlc_media_t *p_md );
/**
* Insert media instance in media list on a position
* The libvlc_media_list_lock should be held upon entering this function.
*
* \param p_ml a media list instance
* \param p_md a media instance
* \param i_pos position in array where to insert
* \return 0 on success, -1 if the media list is read-only
*/
LIBVLC_API int
libvlc_media_list_insert_media( libvlc_media_list_t *p_ml,
libvlc_media_t *p_md, int i_pos );
/**
* Remove media instance from media list on a position
* The libvlc_media_list_lock should be held upon entering this function.
*
* \param p_ml a media list instance
* \param i_pos position in array where to insert
* \return 0 on success, -1 if the list is read-only or the item was not found
*/
LIBVLC_API int
libvlc_media_list_remove_index( libvlc_media_list_t *p_ml, int i_pos );
/**
* Get count on media list items
* The libvlc_media_list_lock should be held upon entering this function.
*
* \param p_ml a media list instance
* \return number of items in media list
*/
LIBVLC_API int
libvlc_media_list_count( libvlc_media_list_t *p_ml );
/**
* List media instance in media list at a position
* The libvlc_media_list_lock should be held upon entering this function.
*
* \param p_ml a media list instance
* \param i_pos position in array where to insert
* \return media instance at position i_pos, or NULL if not found.
* In case of success, libvlc_media_retain() is called to increase the refcount
* on the media.
*/
LIBVLC_API libvlc_media_t *
libvlc_media_list_item_at_index( libvlc_media_list_t *p_ml, int i_pos );
/**
* Find index position of List media instance in media list.
* Warning: the function will return the first matched position.
* The libvlc_media_list_lock should be held upon entering this function.
*
* \param p_ml a media list instance
* \param p_md media instance
* \return position of media instance or -1 if media not found
*/
LIBVLC_API int
libvlc_media_list_index_of_item( libvlc_media_list_t *p_ml,
libvlc_media_t *p_md );
/**
* This indicates if this media list is read-only from a user point of view
*
* \param p_ml media list instance
* \return 1 on readonly, 0 on readwrite
*
* \libvlc_return_bool
*/
LIBVLC_API int
libvlc_media_list_is_readonly( libvlc_media_list_t * p_ml );
/**
* Get lock on media list items
*
* \param p_ml a media list instance
*/
LIBVLC_API void
libvlc_media_list_lock( libvlc_media_list_t *p_ml );
/**
* Release lock on media list items
* The libvlc_media_list_lock should be held upon entering this function.
*
* \param p_ml a media list instance
*/
LIBVLC_API void
libvlc_media_list_unlock( libvlc_media_list_t *p_ml );
/**
* Get libvlc_event_manager from this media list instance.
* The p_event_manager is immutable, so you don't have to hold the lock
*
* \param p_ml a media list instance
* \return libvlc_event_manager
*/
LIBVLC_API libvlc_event_manager_t *
libvlc_media_list_event_manager( libvlc_media_list_t *p_ml );
/** @} media_list */
# ifdef __cplusplus
}
# endif
#endif /* _LIBVLC_MEDIA_LIST_H */

View file

@ -0,0 +1,224 @@
/*****************************************************************************
* libvlc_media_list_player.h: libvlc_media_list API
*****************************************************************************
* Copyright (C) 1998-2008 VLC authors and VideoLAN
* $Id: c95ad972c7dcf380ef62e60d821af726848dae48 $
*
* Authors: Pierre d'Herbemont
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef LIBVLC_MEDIA_LIST_PLAYER_H
#define LIBVLC_MEDIA_LIST_PLAYER_H 1
/**
* \file
* This file defines libvlc_media_list_player API
*/
# ifdef __cplusplus
extern "C" {
# endif
/*****************************************************************************
* Media List Player
*****************************************************************************/
/** \defgroup libvlc_media_list_player LibVLC media list player
* \ingroup libvlc
* The LibVLC media list player plays a @ref libvlc_media_list_t list of media,
* in a certain order.
* This is required to especially support playlist files.
* The normal @ref libvlc_media_player_t LibVLC media player can only play a
* single media, and does not handle playlist files properly.
* @{
*/
typedef struct libvlc_media_list_player_t libvlc_media_list_player_t;
/**
* Defines playback modes for playlist.
*/
typedef enum libvlc_playback_mode_t
{
libvlc_playback_mode_default,
libvlc_playback_mode_loop,
libvlc_playback_mode_repeat
} libvlc_playback_mode_t;
/**
* Create new media_list_player.
*
* \param p_instance libvlc instance
* \return media list player instance or NULL on error
*/
LIBVLC_API libvlc_media_list_player_t *
libvlc_media_list_player_new( libvlc_instance_t * p_instance );
/**
* Release a media_list_player after use
* Decrement the reference count of a media player object. If the
* reference count is 0, then libvlc_media_list_player_release() will
* release the media player object. If the media player object
* has been released, then it should not be used again.
*
* \param p_mlp media list player instance
*/
LIBVLC_API void
libvlc_media_list_player_release( libvlc_media_list_player_t * p_mlp );
/**
* Retain a reference to a media player list object. Use
* libvlc_media_list_player_release() to decrement reference count.
*
* \param p_mlp media player list object
*/
LIBVLC_API void
libvlc_media_list_player_retain( libvlc_media_list_player_t *p_mlp );
/**
* Return the event manager of this media_list_player.
*
* \param p_mlp media list player instance
* \return the event manager
*/
LIBVLC_API libvlc_event_manager_t *
libvlc_media_list_player_event_manager(libvlc_media_list_player_t * p_mlp);
/**
* Replace media player in media_list_player with this instance.
*
* \param p_mlp media list player instance
* \param p_mi media player instance
*/
LIBVLC_API void
libvlc_media_list_player_set_media_player(
libvlc_media_list_player_t * p_mlp,
libvlc_media_player_t * p_mi );
/**
* Set the media list associated with the player
*
* \param p_mlp media list player instance
* \param p_mlist list of media
*/
LIBVLC_API void
libvlc_media_list_player_set_media_list(
libvlc_media_list_player_t * p_mlp,
libvlc_media_list_t * p_mlist );
/**
* Play media list
*
* \param p_mlp media list player instance
*/
LIBVLC_API
void libvlc_media_list_player_play(libvlc_media_list_player_t * p_mlp);
/**
* Toggle pause (or resume) media list
*
* \param p_mlp media list player instance
*/
LIBVLC_API
void libvlc_media_list_player_pause(libvlc_media_list_player_t * p_mlp);
/**
* Is media list playing?
*
* \param p_mlp media list player instance
* \return true for playing and false for not playing
*
* \libvlc_return_bool
*/
LIBVLC_API int
libvlc_media_list_player_is_playing( libvlc_media_list_player_t * p_mlp );
/**
* Get current libvlc_state of media list player
*
* \param p_mlp media list player instance
* \return libvlc_state_t for media list player
*/
LIBVLC_API libvlc_state_t
libvlc_media_list_player_get_state( libvlc_media_list_player_t * p_mlp );
/**
* Play media list item at position index
*
* \param p_mlp media list player instance
* \param i_index index in media list to play
* \return 0 upon success -1 if the item wasn't found
*/
LIBVLC_API
int libvlc_media_list_player_play_item_at_index(libvlc_media_list_player_t * p_mlp,
int i_index);
/**
* Play the given media item
*
* \param p_mlp media list player instance
* \param p_md the media instance
* \return 0 upon success, -1 if the media is not part of the media list
*/
LIBVLC_API
int libvlc_media_list_player_play_item(libvlc_media_list_player_t * p_mlp,
libvlc_media_t * p_md);
/**
* Stop playing media list
*
* \param p_mlp media list player instance
*/
LIBVLC_API void
libvlc_media_list_player_stop( libvlc_media_list_player_t * p_mlp);
/**
* Play next item from media list
*
* \param p_mlp media list player instance
* \return 0 upon success -1 if there is no next item
*/
LIBVLC_API
int libvlc_media_list_player_next(libvlc_media_list_player_t * p_mlp);
/**
* Play previous item from media list
*
* \param p_mlp media list player instance
* \return 0 upon success -1 if there is no previous item
*/
LIBVLC_API
int libvlc_media_list_player_previous(libvlc_media_list_player_t * p_mlp);
/**
* Sets the playback mode for the playlist
*
* \param p_mlp media list player instance
* \param e_mode playback mode specification
*/
LIBVLC_API
void libvlc_media_list_player_set_playback_mode(libvlc_media_list_player_t * p_mlp,
libvlc_playback_mode_t e_mode );
/** @} media_list_player */
# ifdef __cplusplus
}
# endif
#endif /* LIBVLC_MEDIA_LIST_PLAYER_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,73 @@
/*****************************************************************************
* libvlc_structures.h: libvlc_* new external API structures
*****************************************************************************
* Copyright (C) 1998-2008 VLC authors and VideoLAN
* $Id $
*
* Authors: Filippo Carone <littlejohn@videolan.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef LIBVLC_STRUCTURES_H
#define LIBVLC_STRUCTURES_H 1
/**
* \file
* This file defines libvlc_* new external API structures
*/
#include <stdint.h>
# ifdef __cplusplus
extern "C" {
# endif
/**
* \ingroup libvlc_core
* @{
*/
/** This structure is opaque. It represents a libvlc instance */
typedef struct libvlc_instance_t libvlc_instance_t;
typedef int64_t libvlc_time_t;
/**@} */
/**
* \ingroup libvlc_log
* @{
*/
/** This structure is opaque. It represents a libvlc log iterator */
typedef struct libvlc_log_iterator_t libvlc_log_iterator_t;
typedef struct libvlc_log_message_t
{
int i_severity; /* 0=INFO, 1=ERR, 2=WARN, 3=DBG */
const char *psz_type; /* module type */
const char *psz_name; /* module name */
const char *psz_header; /* optional header */
const char *psz_message; /* message */
} libvlc_log_message_t;
/**@} */
# ifdef __cplusplus
}
# endif
#endif

View file

@ -0,0 +1,55 @@
/*****************************************************************************
* libvlc_version.h
*****************************************************************************
* Copyright (C) 2010 Rémi Denis-Courmont
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/**
* \file
* This file defines version macros for LibVLC.
* Those macros are primilarly intended for conditional (pre)compilation.
* To get the run-time LibVLC version, use libvlc_get_version() instead
* (the run-time version may be more recent than build-time one, thanks to
* backward binary compatibility).
*
* \version This header file is available in LibVLC 1.1.4 and higher.
*/
#ifndef LIBVLC_VERSION_H
# define LIBVLC_VERSION_H 1
/** LibVLC major version number */
# define LIBVLC_VERSION_MAJOR (@VERSION_MAJOR@)
/** LibVLC minor version number */
# define LIBVLC_VERSION_MINOR (@VERSION_MINOR@)
/** LibVLC revision */
# define LIBVLC_VERSION_REVISION (@VERSION_REVISION@)
# define LIBVLC_VERSION_EXTRA (0)
/** Makes a single integer from a LibVLC version numbers */
# define LIBVLC_VERSION(maj,min,rev,extra) \
((maj << 24) | (min << 16) | (rev << 8) | (extra))
/** LibVLC full version as a single integer (for comparison) */
# define LIBVLC_VERSION_INT \
LIBVLC_VERSION(LIBVLC_VERSION_MAJOR, LIBVLC_VERSION_MINOR, \
LIBVLC_VERSION_REVISION, LIBVLC_VERSION_EXTRA)
#endif

View file

@ -0,0 +1,349 @@
/*****************************************************************************
* libvlc_vlm.h: libvlc_* new external API
*****************************************************************************
* Copyright (C) 1998-2008 VLC authors and VideoLAN
* $Id: 26e5cbb5ee7968a21520af0b8f553a4a117d4f99 $
*
* Authors: Clément Stenac <zorglub@videolan.org>
* Jean-Paul Saman <jpsaman _at_ m2x _dot_ nl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef LIBVLC_VLM_H
#define LIBVLC_VLM_H 1
/**
* \file
* This file defines libvlc_vlm_* external API
*/
# ifdef __cplusplus
extern "C" {
# endif
/*****************************************************************************
* VLM
*****************************************************************************/
/** \defgroup libvlc_vlm LibVLC VLM
* \ingroup libvlc
* @{
*/
/**
* Release the vlm instance related to the given libvlc_instance_t
*
* \param p_instance the instance
*/
LIBVLC_API void libvlc_vlm_release( libvlc_instance_t *p_instance );
/**
* Add a broadcast, with one input.
*
* \param p_instance the instance
* \param psz_name the name of the new broadcast
* \param psz_input the input MRL
* \param psz_output the output MRL (the parameter to the "sout" variable)
* \param i_options number of additional options
* \param ppsz_options additional options
* \param b_enabled boolean for enabling the new broadcast
* \param b_loop Should this broadcast be played in loop ?
* \return 0 on success, -1 on error
*/
LIBVLC_API int libvlc_vlm_add_broadcast( libvlc_instance_t *p_instance,
const char *psz_name, const char *psz_input,
const char *psz_output, int i_options,
const char * const* ppsz_options,
int b_enabled, int b_loop );
/**
* Add a vod, with one input.
*
* \param p_instance the instance
* \param psz_name the name of the new vod media
* \param psz_input the input MRL
* \param i_options number of additional options
* \param ppsz_options additional options
* \param b_enabled boolean for enabling the new vod
* \param psz_mux the muxer of the vod media
* \return 0 on success, -1 on error
*/
LIBVLC_API int libvlc_vlm_add_vod( libvlc_instance_t * p_instance,
const char *psz_name, const char *psz_input,
int i_options, const char * const* ppsz_options,
int b_enabled, const char *psz_mux );
/**
* Delete a media (VOD or broadcast).
*
* \param p_instance the instance
* \param psz_name the media to delete
* \return 0 on success, -1 on error
*/
LIBVLC_API int libvlc_vlm_del_media( libvlc_instance_t * p_instance,
const char *psz_name );
/**
* Enable or disable a media (VOD or broadcast).
*
* \param p_instance the instance
* \param psz_name the media to work on
* \param b_enabled the new status
* \return 0 on success, -1 on error
*/
LIBVLC_API int libvlc_vlm_set_enabled( libvlc_instance_t *p_instance,
const char *psz_name, int b_enabled );
/**
* Set the output for a media.
*
* \param p_instance the instance
* \param psz_name the media to work on
* \param psz_output the output MRL (the parameter to the "sout" variable)
* \return 0 on success, -1 on error
*/
LIBVLC_API int libvlc_vlm_set_output( libvlc_instance_t *p_instance,
const char *psz_name,
const char *psz_output );
/**
* Set a media's input MRL. This will delete all existing inputs and
* add the specified one.
*
* \param p_instance the instance
* \param psz_name the media to work on
* \param psz_input the input MRL
* \return 0 on success, -1 on error
*/
LIBVLC_API int libvlc_vlm_set_input( libvlc_instance_t *p_instance,
const char *psz_name,
const char *psz_input );
/**
* Add a media's input MRL. This will add the specified one.
*
* \param p_instance the instance
* \param psz_name the media to work on
* \param psz_input the input MRL
* \return 0 on success, -1 on error
*/
LIBVLC_API int libvlc_vlm_add_input( libvlc_instance_t *p_instance,
const char *psz_name,
const char *psz_input );
/**
* Set a media's loop status.
*
* \param p_instance the instance
* \param psz_name the media to work on
* \param b_loop the new status
* \return 0 on success, -1 on error
*/
LIBVLC_API int libvlc_vlm_set_loop( libvlc_instance_t *p_instance,
const char *psz_name,
int b_loop );
/**
* Set a media's vod muxer.
*
* \param p_instance the instance
* \param psz_name the media to work on
* \param psz_mux the new muxer
* \return 0 on success, -1 on error
*/
LIBVLC_API int libvlc_vlm_set_mux( libvlc_instance_t *p_instance,
const char *psz_name,
const char *psz_mux );
/**
* Edit the parameters of a media. This will delete all existing inputs and
* add the specified one.
*
* \param p_instance the instance
* \param psz_name the name of the new broadcast
* \param psz_input the input MRL
* \param psz_output the output MRL (the parameter to the "sout" variable)
* \param i_options number of additional options
* \param ppsz_options additional options
* \param b_enabled boolean for enabling the new broadcast
* \param b_loop Should this broadcast be played in loop ?
* \return 0 on success, -1 on error
*/
LIBVLC_API int libvlc_vlm_change_media( libvlc_instance_t *p_instance,
const char *psz_name, const char *psz_input,
const char *psz_output, int i_options,
const char * const *ppsz_options,
int b_enabled, int b_loop );
/**
* Play the named broadcast.
*
* \param p_instance the instance
* \param psz_name the name of the broadcast
* \return 0 on success, -1 on error
*/
LIBVLC_API int libvlc_vlm_play_media ( libvlc_instance_t *p_instance,
const char *psz_name );
/**
* Stop the named broadcast.
*
* \param p_instance the instance
* \param psz_name the name of the broadcast
* \return 0 on success, -1 on error
*/
LIBVLC_API int libvlc_vlm_stop_media ( libvlc_instance_t *p_instance,
const char *psz_name );
/**
* Pause the named broadcast.
*
* \param p_instance the instance
* \param psz_name the name of the broadcast
* \return 0 on success, -1 on error
*/
LIBVLC_API int libvlc_vlm_pause_media( libvlc_instance_t *p_instance,
const char *psz_name );
/**
* Seek in the named broadcast.
*
* \param p_instance the instance
* \param psz_name the name of the broadcast
* \param f_percentage the percentage to seek to
* \return 0 on success, -1 on error
*/
LIBVLC_API int libvlc_vlm_seek_media( libvlc_instance_t *p_instance,
const char *psz_name,
float f_percentage );
/**
* Return information about the named media as a JSON
* string representation.
*
* This function is mainly intended for debugging use,
* if you want programmatic access to the state of
* a vlm_media_instance_t, please use the corresponding
* libvlc_vlm_get_media_instance_xxx -functions.
* Currently there are no such functions available for
* vlm_media_t though.
*
* \param p_instance the instance
* \param psz_name the name of the media,
* if the name is an empty string, all media is described
* \return string with information about named media, or NULL on error
*/
LIBVLC_API const char* libvlc_vlm_show_media( libvlc_instance_t *p_instance,
const char *psz_name );
/**
* Get vlm_media instance position by name or instance id
*
* \param p_instance a libvlc instance
* \param psz_name name of vlm media instance
* \param i_instance instance id
* \return position as float or -1. on error
*/
LIBVLC_API float libvlc_vlm_get_media_instance_position( libvlc_instance_t *p_instance,
const char *psz_name,
int i_instance );
/**
* Get vlm_media instance time by name or instance id
*
* \param p_instance a libvlc instance
* \param psz_name name of vlm media instance
* \param i_instance instance id
* \return time as integer or -1 on error
*/
LIBVLC_API int libvlc_vlm_get_media_instance_time( libvlc_instance_t *p_instance,
const char *psz_name,
int i_instance );
/**
* Get vlm_media instance length by name or instance id
*
* \param p_instance a libvlc instance
* \param psz_name name of vlm media instance
* \param i_instance instance id
* \return length of media item or -1 on error
*/
LIBVLC_API int libvlc_vlm_get_media_instance_length( libvlc_instance_t *p_instance,
const char *psz_name,
int i_instance );
/**
* Get vlm_media instance playback rate by name or instance id
*
* \param p_instance a libvlc instance
* \param psz_name name of vlm media instance
* \param i_instance instance id
* \return playback rate or -1 on error
*/
LIBVLC_API int libvlc_vlm_get_media_instance_rate( libvlc_instance_t *p_instance,
const char *psz_name,
int i_instance );
#if 0
/**
* Get vlm_media instance title number by name or instance id
* \bug will always return 0
* \param p_instance a libvlc instance
* \param psz_name name of vlm media instance
* \param i_instance instance id
* \return title as number or -1 on error
*/
LIBVLC_API int libvlc_vlm_get_media_instance_title( libvlc_instance_t *,
const char *, int );
/**
* Get vlm_media instance chapter number by name or instance id
* \bug will always return 0
* \param p_instance a libvlc instance
* \param psz_name name of vlm media instance
* \param i_instance instance id
* \return chapter as number or -1 on error
*/
LIBVLC_API int libvlc_vlm_get_media_instance_chapter( libvlc_instance_t *,
const char *, int );
/**
* Is libvlc instance seekable ?
* \bug will always return 0
* \param p_instance a libvlc instance
* \param psz_name name of vlm media instance
* \param i_instance instance id
* \return 1 if seekable, 0 if not, -1 if media does not exist
*/
LIBVLC_API int libvlc_vlm_get_media_instance_seekable( libvlc_instance_t *,
const char *, int );
#endif
/**
* Get libvlc_event_manager from a vlm media.
* The p_event_manager is immutable, so you don't have to hold the lock
*
* \param p_instance a libvlc instance
* \return libvlc_event_manager
*/
LIBVLC_API libvlc_event_manager_t *
libvlc_vlm_get_event_manager( libvlc_instance_t *p_instance );
/** @} */
# ifdef __cplusplus
}
# endif
#endif /* <vlc/libvlc_vlm.h> */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,184 @@
/*****************************************************************************
* vlc_access.h: Access descriptor, queries and methods
*****************************************************************************
* Copyright (C) 1999-2006 VLC authors and VideoLAN
* $Id: df8cf1af98f0fe94a42fa9c402718d9a18bcfa7c $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_ACCESS_H
#define VLC_ACCESS_H 1
/**
* \file
* This file defines functions and definitions for access object
*/
#include <vlc_block.h>
/**
* \defgroup access Access
* @{
*/
enum access_query_e
{
/* capabilities */
ACCESS_CAN_SEEK, /* arg1= bool* cannot fail */
ACCESS_CAN_FASTSEEK, /* arg1= bool* cannot fail */
ACCESS_CAN_PAUSE, /* arg1= bool* cannot fail */
ACCESS_CAN_CONTROL_PACE,/* arg1= bool* cannot fail */
/* */
ACCESS_GET_PTS_DELAY = 0x101,/* arg1= int64_t* cannot fail */
/* */
ACCESS_GET_TITLE_INFO, /* arg1=input_title_t*** arg2=int* res=can fail */
/* Meta data */
ACCESS_GET_META, /* arg1= vlc_meta_t ** res=can fail */
/* */
ACCESS_GET_CONTENT_TYPE,/* arg1=char **ppsz_content_type res=can fail */
/* */
ACCESS_GET_SIGNAL, /* arg1=double *pf_quality, arg2=double *pf_strength res=can fail */
/* */
ACCESS_SET_PAUSE_STATE = 0x200, /* arg1= bool can fail */
/* */
ACCESS_SET_TITLE, /* arg1= int can fail */
ACCESS_SET_SEEKPOINT, /* arg1= int can fail */
/* Special mode for access/demux communication
* XXX: avoid to use it unless you can't */
ACCESS_SET_PRIVATE_ID_STATE = 0x1000, /* arg1= int i_private_data, bool b_selected res=can fail */
ACCESS_SET_PRIVATE_ID_CA, /* arg1= int i_program_number, uint16_t i_vpid, uint16_t i_apid1, uint16_t i_apid2, uint16_t i_apid3, uint8_t i_length, uint8_t *p_data */
ACCESS_GET_PRIVATE_ID_STATE, /* arg1=int i_private_data arg2=bool * res=can fail */
};
struct access_t
{
VLC_COMMON_MEMBERS
/* Module properties */
module_t *p_module;
/* Access name (empty if non forced) */
char *psz_access;
char *psz_location; /**< Location (URL with the scheme stripped) */
char *psz_filepath; /**< Local file path (if applicable) */
/* Access can fill this entry to force a demuxer
* XXX: fill it once you know for sure you will succeed
* (if you fail, this value won't be reseted */
char *psz_demux;
/* pf_read/pf_block is used to read data.
* XXX A access should set one and only one of them */
ssize_t (*pf_read) ( access_t *, uint8_t *, size_t ); /* Return -1 if no data yet, 0 if no more data, else real data read */
block_t *(*pf_block)( access_t * ); /* return a block of data in his 'natural' size, NULL if not yet data or eof */
/* Called for each seek.
* XXX can be null */
int (*pf_seek) ( access_t *, uint64_t ); /* can be null if can't seek */
/* Used to retreive and configure the access
* XXX mandatory. look at access_query_e to know what query you *have to* support */
int (*pf_control)( access_t *, int i_query, va_list args);
/* Access has to maintain them uptodate */
struct
{
unsigned int i_update; /* Access sets them on change,
Input removes them once take into account*/
uint64_t i_size; /* Write only for access, read only for input */
uint64_t i_pos; /* idem */
bool b_eof; /* idem */
int i_title; /* idem, start from 0 (could be menu) */
int i_seekpoint;/* idem, start from 0 */
} info;
access_sys_t *p_sys;
/* Weak link to parent input */
input_thread_t *p_input;
};
static inline int access_vaControl( access_t *p_access, int i_query, va_list args )
{
if( !p_access ) return VLC_EGENERIC;
return p_access->pf_control( p_access, i_query, args );
}
static inline int access_Control( access_t *p_access, int i_query, ... )
{
va_list args;
int i_result;
va_start( args, i_query );
i_result = access_vaControl( p_access, i_query, args );
va_end( args );
return i_result;
}
static inline void access_InitFields( access_t *p_a )
{
p_a->info.i_update = 0;
p_a->info.i_size = 0;
p_a->info.i_pos = 0;
p_a->info.b_eof = false;
p_a->info.i_title = 0;
p_a->info.i_seekpoint = 0;
}
/**
* This function will return the parent input of this access.
* It is retained. It can return NULL.
*/
VLC_API input_thread_t * access_GetParentInput( access_t *p_access ) VLC_USED;
#define ACCESS_SET_CALLBACKS( read, block, control, seek ) \
do { \
p_access->pf_read = (read); \
p_access->pf_block = (block); \
p_access->pf_control = (control); \
p_access->pf_seek = (seek); \
} while(0)
#define STANDARD_READ_ACCESS_INIT \
do { \
access_InitFields( p_access ); \
ACCESS_SET_CALLBACKS( Read, NULL, Control, Seek ); \
p_sys = p_access->p_sys = calloc( 1, sizeof( access_sys_t ) ); \
if( !p_sys ) return VLC_ENOMEM;\
} while(0);
#define STANDARD_BLOCK_ACCESS_INIT \
do { \
access_InitFields( p_access ); \
ACCESS_SET_CALLBACKS( NULL, Block, Control, Seek ); \
p_sys = p_access->p_sys = calloc( 1, sizeof( access_sys_t ) ); \
if( !p_sys ) return VLC_ENOMEM; \
} while(0);
/**
* @}
*/
#endif

View file

@ -0,0 +1,339 @@
/*****************************************************************************
* vlc_aout.h : audio output interface
*****************************************************************************
* Copyright (C) 2002-2011 VLC authors and VideoLAN
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_AOUT_H
#define VLC_AOUT_H 1
/**
* \file
* This file defines functions, structures and macros for audio output object
*/
/* Buffers which arrive in advance of more than AOUT_MAX_ADVANCE_TIME
* will be considered as bogus and be trashed */
#define AOUT_MAX_ADVANCE_TIME (AOUT_MAX_PREPARE_TIME + CLOCK_FREQ)
/* Buffers which arrive in advance of more than AOUT_MAX_PREPARE_TIME
* will cause the calling thread to sleep */
#define AOUT_MAX_PREPARE_TIME (2 * CLOCK_FREQ)
/* Buffers which arrive after pts - AOUT_MIN_PREPARE_TIME will be trashed
* to avoid too heavy resampling */
#define AOUT_MIN_PREPARE_TIME AOUT_MAX_PTS_ADVANCE
/* Tolerance values from EBU Recommendation 37 */
/** Maximum advance of actual audio playback time to coded PTS,
* above which downsampling will be performed */
#define AOUT_MAX_PTS_ADVANCE (CLOCK_FREQ / 25)
/** Maximum delay of actual audio playback time from coded PTS,
* above which upsampling will be performed */
#define AOUT_MAX_PTS_DELAY (3 * CLOCK_FREQ / 50)
/* Max acceptable resampling (in %) */
#define AOUT_MAX_RESAMPLING 10
#include "vlc_es.h"
#define AOUT_FMTS_IDENTICAL( p_first, p_second ) ( \
((p_first)->i_format == (p_second)->i_format) \
&& AOUT_FMTS_SIMILAR(p_first, p_second) )
/* Check if i_rate == i_rate and i_channels == i_channels */
#define AOUT_FMTS_SIMILAR( p_first, p_second ) ( \
((p_first)->i_rate == (p_second)->i_rate) \
&& ((p_first)->i_physical_channels == (p_second)->i_physical_channels)\
&& ((p_first)->i_original_channels == (p_second)->i_original_channels) )
#define AOUT_FMT_LINEAR( p_format ) \
(aout_BitsPerSample((p_format)->i_format) != 0)
#define VLC_CODEC_SPDIFL VLC_FOURCC('s','p','d','i')
#define VLC_CODEC_SPDIFB VLC_FOURCC('s','p','d','b')
#define AOUT_FMT_SPDIF( p_format ) \
( ((p_format)->i_format == VLC_CODEC_SPDIFL) \
|| ((p_format)->i_format == VLC_CODEC_SPDIFB) \
|| ((p_format)->i_format == VLC_CODEC_A52) \
|| ((p_format)->i_format == VLC_CODEC_DTS) )
/* Values used for the audio-channels object variable */
#define AOUT_VAR_CHAN_UNSET 0 /* must be zero */
#define AOUT_VAR_CHAN_STEREO 1
#define AOUT_VAR_CHAN_RSTEREO 2
#define AOUT_VAR_CHAN_LEFT 3
#define AOUT_VAR_CHAN_RIGHT 4
#define AOUT_VAR_CHAN_DOLBYS 5
/*****************************************************************************
* Main audio output structures
*****************************************************************************/
/* Size of a frame for S/PDIF output. */
#define AOUT_SPDIF_SIZE 6144
/* Number of samples in an A/52 frame. */
#define A52_FRAME_NB 1536
/* FIXME to remove once aout.h is cleaned a bit more */
#include <vlc_block.h>
/** Audio output object */
struct audio_output
{
VLC_COMMON_MEMBERS
struct aout_sys_t *sys; /**< Private data for callbacks */
int (*start)(audio_output_t *, audio_sample_format_t *fmt);
/**< Starts a new stream (mandatory, cannot be NULL).
* \param fmt input stream sample format upon entry,
* output stream sample format upon return [IN/OUT]
* \return VLC_SUCCESS on success, non-zero on failure
* \note No other stream may be already started when called.
*/
void (*stop)(audio_output_t *);
/**< Stops the existing stream (optional, may be NULL).
* \note A stream must have been started when called.
*/
int (*time_get)(audio_output_t *, mtime_t *delay);
/**< Estimates playback buffer latency (optional, may be NULL).
* \param delay pointer to the delay until the next sample to be written
* to the playback buffer is rendered [OUT]
* \return 0 on success, non-zero on failure or lack of data
* \note A stream must have been started when called.
*/
void (*play)(audio_output_t *, block_t *);
/**< Queues a block of samples for playback (mandatory, cannot be NULL).
* \note A stream must have been started when called.
*/
void (*pause)( audio_output_t *, bool pause, mtime_t date);
/**< Pauses or resumes playback (optional, may be NULL).
* \param pause pause if true, resume from pause if false
* \param date timestamp when the pause or resume was requested
* \note A stream must have been started when called.
*/
void (*flush)( audio_output_t *, bool wait);
/**< Flushes or drains the playback buffers (mandatory, cannot be NULL).
* \param wait true to wait for playback of pending buffers (drain),
* false to discard pending buffers (flush)
* \note A stream must have been started when called.
*/
int (*volume_set)(audio_output_t *, float volume);
/**< Changes playback volume (optional, may be NULL).
* \param volume requested volume (0. = mute, 1. = nominal)
* \note The volume is always a positive number.
* \warning A stream may or may not have been started when called.
*/
int (*mute_set)(audio_output_t *, bool mute);
/**< Changes muting (optinal, may be NULL).
* \param mute true to mute, false to unmute
* \warning A stream may or may not have been started when called.
*/
int (*device_select)(audio_output_t *, const char *id);
/**< Selects an audio output device (optional, may be NULL).
* \param id nul-terminated device unique identifier.
* \return 0 on success, non-zero on failure.
* \warning A stream may or may not have been started when called.
*/
struct {
void (*volume_report)(audio_output_t *, float);
void (*mute_report)(audio_output_t *, bool);
void (*policy_report)(audio_output_t *, bool);
void (*device_report)(audio_output_t *, const char *);
void (*hotplug_report)(audio_output_t *, const char *, const char *);
int (*gain_request)(audio_output_t *, float);
void (*restart_request)(audio_output_t *, unsigned);
} event;
};
/**
* It describes the audio channel order VLC expect.
*/
static const uint32_t pi_vlc_chan_order_wg4[] =
{
AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT,
AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT,
AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, AOUT_CHAN_REARCENTER,
AOUT_CHAN_CENTER, AOUT_CHAN_LFE, 0
};
#define AOUT_RESTART_FILTERS 1
#define AOUT_RESTART_OUTPUT 2
#define AOUT_RESTART_DECODER 4
/*****************************************************************************
* Prototypes
*****************************************************************************/
/**
* This function computes the reordering needed to go from pi_chan_order_in to
* pi_chan_order_out.
* If pi_chan_order_in or pi_chan_order_out is NULL, it will assume that vlc
* internal (WG4) order is requested.
*/
VLC_API unsigned aout_CheckChannelReorder( const uint32_t *, const uint32_t *,
uint32_t mask, uint8_t *table );
VLC_API void aout_ChannelReorder(void *, size_t, unsigned, const uint8_t *, vlc_fourcc_t);
VLC_API void aout_Interleave(void *dst, const void *const *planes,
unsigned samples, unsigned channels,
vlc_fourcc_t fourcc);
VLC_API void aout_Deinterleave(void *dst, const void *src, unsigned samples,
unsigned channels, vlc_fourcc_t fourcc);
/**
* This function will compute the extraction parameter into pi_selection to go
* from i_channels with their type given by pi_order_src[] into the order
* describe by pi_order_dst.
* It will also set :
* - *pi_channels as the number of channels that will be extracted which is
* lower (in case of non understood channels type) or equal to i_channels.
* - the layout of the channels (*pi_layout).
*
* It will return true if channel extraction is really needed, in which case
* aout_ChannelExtract must be used
*
* XXX It must be used when the source may have channel type not understood
* by VLC. In this case the channel type pi_order_src[] must be set to 0.
* XXX It must also be used if multiple channels have the same type.
*/
VLC_API bool aout_CheckChannelExtraction( int *pi_selection, uint32_t *pi_layout, int *pi_channels, const uint32_t pi_order_dst[AOUT_CHAN_MAX], const uint32_t *pi_order_src, int i_channels );
/**
* Do the actual channels extraction using the parameters created by
* aout_CheckChannelExtraction.
*
* XXX this function does not work in place (p_dst and p_src must not overlap).
* XXX Only 8, 16, 24, 32, 64 bits per sample are supported.
*/
VLC_API void aout_ChannelExtract( void *p_dst, int i_dst_channels, const void *p_src, int i_src_channels, int i_sample_count, const int *pi_selection, int i_bits_per_sample );
/* */
static inline unsigned aout_FormatNbChannels(const audio_sample_format_t *fmt)
{
return popcount(fmt->i_physical_channels);
}
VLC_API unsigned int aout_BitsPerSample( vlc_fourcc_t i_format ) VLC_USED;
VLC_API void aout_FormatPrepare( audio_sample_format_t * p_format );
VLC_API void aout_FormatPrint(vlc_object_t *, const char *,
const audio_sample_format_t *);
#define aout_FormatPrint(o, t, f) aout_FormatPrint(VLC_OBJECT(o), t, f)
VLC_API const char * aout_FormatPrintChannels( const audio_sample_format_t * ) VLC_USED;
VLC_API float aout_VolumeGet (audio_output_t *);
VLC_API int aout_VolumeSet (audio_output_t *, float);
VLC_API int aout_MuteGet (audio_output_t *);
VLC_API int aout_MuteSet (audio_output_t *, bool);
VLC_API char *aout_DeviceGet (audio_output_t *);
VLC_API int aout_DeviceSet (audio_output_t *, const char *);
VLC_API int aout_DevicesList (audio_output_t *, char ***, char ***);
/**
* Report change of configured audio volume to the core and UI.
*/
static inline void aout_VolumeReport(audio_output_t *aout, float volume)
{
aout->event.volume_report(aout, volume);
}
/**
* Report change of muted flag to the core and UI.
*/
static inline void aout_MuteReport(audio_output_t *aout, bool mute)
{
aout->event.mute_report(aout, mute);
}
/**
* Report audio policy status.
* \parm cork true to request a cork, false to undo any pending cork.
*/
static inline void aout_PolicyReport(audio_output_t *aout, bool cork)
{
aout->event.policy_report(aout, cork);
}
/**
* Report change of output device.
*/
static inline void aout_DeviceReport(audio_output_t *aout, const char *id)
{
aout->event.device_report(aout, id);
}
/**
* Report a device hot-plug event.
* @param id device ID
* @param name human-readable device name (NULL for hot unplug)
*/
static inline void aout_HotplugReport(audio_output_t *aout,
const char *id, const char *name)
{
aout->event.hotplug_report(aout, id, name);
}
/**
* Request a change of software audio amplification.
* \param gain linear amplitude gain (must be positive)
* \warning Values in excess 1.0 may cause overflow and distorsion.
*/
static inline int aout_GainRequest(audio_output_t *aout, float gain)
{
return aout->event.gain_request(aout, gain);
}
static inline void aout_RestartRequest(audio_output_t *aout, unsigned mode)
{
aout->event.restart_request(aout, mode);
}
static inline int aout_ChannelsRestart (vlc_object_t *obj, const char *varname,
vlc_value_t oldval, vlc_value_t newval, void *data)
{
audio_output_t *aout = (audio_output_t *)obj;
(void)varname; (void)oldval; (void)newval; (void)data;
aout_RestartRequest (aout, AOUT_RESTART_OUTPUT);
return 0;
}
/* Audio output filters */
typedef struct aout_filters aout_filters_t;
typedef struct aout_request_vout aout_request_vout_t;
VLC_API aout_filters_t *aout_FiltersNew(vlc_object_t *,
const audio_sample_format_t *,
const audio_sample_format_t *,
const aout_request_vout_t *) VLC_USED;
#define aout_FiltersNew(o,inf,outf,rv) \
aout_FiltersNew(VLC_OBJECT(o),inf,outf,rv)
VLC_API void aout_FiltersDelete(vlc_object_t *, aout_filters_t *);
#define aout_FiltersDelete(o,f) \
aout_FiltersDelete(VLC_OBJECT(o),f)
VLC_API bool aout_FiltersAdjustResampling(aout_filters_t *, int);
VLC_API block_t *aout_FiltersPlay(aout_filters_t *, block_t *, int rate);
VLC_API vout_thread_t * aout_filter_RequestVout( filter_t *, vout_thread_t *p_vout, video_format_t *p_fmt );
#endif /* VLC_AOUT_H */

View file

@ -0,0 +1,54 @@
/*****************************************************************************
* vlc_aout_volume.h: audio volume module
*****************************************************************************
* Copyright (C) 2002-2009 VLC authors and VideoLAN
* $Id: 051413ba105d5f7ee552679bf7fcd3a053db112c $
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
* Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_AOUT_MIXER_H
#define VLC_AOUT_MIXER_H 1
/**
* \file
* This file defines functions, structures and macros for audio output mixer object
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct audio_volume audio_volume_t;
/**
* Audio volume
*/
struct audio_volume
{
VLC_COMMON_MEMBERS
vlc_fourcc_t format; /**< Audio samples format */
void (*amplify)(audio_volume_t *, block_t *, float); /**< Amplifier */
};
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,622 @@
/*****************************************************************************
* vlc_arrays.h : Arrays and data structures handling
*****************************************************************************
* Copyright (C) 1999-2004 VLC authors and VideoLAN
* $Id: 7749140f10f5ce76339559b166d041854bb4a7da $
*
* Authors: Samuel Hocevar <sam@zoy.org>
* Clément Stenac <zorglub@videolan.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_ARRAYS_H_
#define VLC_ARRAYS_H_
/**
* \file
* This file defines functions, structures and macros for handling arrays in vlc
*/
/* realloc() that never fails *if* downsizing */
static inline void *realloc_down( void *ptr, size_t size )
{
void *ret = realloc( ptr, size );
return ret ? ret : ptr;
}
/**
* Simple dynamic array handling. Array is realloced at each insert/removal
*/
#define INSERT_ELEM( p_ar, i_oldsize, i_pos, elem ) \
do \
{ \
if( !(i_oldsize) ) (p_ar) = NULL; \
(p_ar) = realloc( p_ar, ((i_oldsize) + 1) * sizeof(*(p_ar)) ); \
if( !(p_ar) ) abort(); \
if( (i_oldsize) - (i_pos) ) \
{ \
memmove( (p_ar) + (i_pos) + 1, (p_ar) + (i_pos), \
((i_oldsize) - (i_pos)) * sizeof( *(p_ar) ) ); \
} \
(p_ar)[(i_pos)] = elem; \
(i_oldsize)++; \
} \
while( 0 )
#define REMOVE_ELEM( p_ar, i_size, i_pos ) \
do \
{ \
if( (i_size) - (i_pos) - 1 ) \
{ \
memmove( (p_ar) + (i_pos), \
(p_ar) + (i_pos) + 1, \
((i_size) - (i_pos) - 1) * sizeof( *(p_ar) ) ); \
} \
if( i_size > 1 ) \
(p_ar) = realloc_down( p_ar, ((i_size) - 1) * sizeof( *(p_ar) ) );\
else \
{ \
free( p_ar ); \
(p_ar) = NULL; \
} \
(i_size)--; \
} \
while( 0 )
#define TAB_INIT( count, tab ) \
do { \
(count) = 0; \
(tab) = NULL; \
} while(0)
#define TAB_CLEAN( count, tab ) \
do { \
free( tab ); \
(count)= 0; \
(tab)= NULL; \
} while(0)
#define TAB_APPEND_CAST( cast, count, tab, p ) \
do { \
if( (count) > 0 ) \
(tab) = cast realloc( tab, sizeof( void ** ) * ( (count) + 1 ) ); \
else \
(tab) = cast malloc( sizeof( void ** ) ); \
if( !(tab) ) abort(); \
(tab)[count] = (p); \
(count)++; \
} while(0)
#define TAB_APPEND( count, tab, p ) \
TAB_APPEND_CAST( , count, tab, p )
#define TAB_FIND( count, tab, p, idx ) \
do { \
for( (idx) = 0; (idx) < (count); (idx)++ ) \
if( (tab)[(idx)] == (p) ) \
break; \
if( (idx) >= (count) ) \
(idx) = -1; \
} while(0)
#define TAB_REMOVE( count, tab, p ) \
do { \
int i_index; \
TAB_FIND( count, tab, p, i_index ); \
if( i_index >= 0 ) \
{ \
if( (count) > 1 ) \
{ \
memmove( ((void**)(tab) + i_index), \
((void**)(tab) + i_index+1), \
( (count) - i_index - 1 ) * sizeof( void* ) );\
} \
(count)--; \
if( (count) == 0 ) \
{ \
free( tab ); \
(tab) = NULL; \
} \
} \
} while(0)
#define TAB_INSERT_CAST( cast, count, tab, p, index ) do { \
if( (count) > 0 ) \
(tab) = cast realloc( tab, sizeof( void ** ) * ( (count) + 1 ) ); \
else \
(tab) = cast malloc( sizeof( void ** ) ); \
if( !(tab) ) abort(); \
if( (count) - (index) > 0 ) \
memmove( (void**)(tab) + (index) + 1, \
(void**)(tab) + (index), \
((count) - (index)) * sizeof(*(tab)) );\
(tab)[(index)] = (p); \
(count)++; \
} while(0)
#define TAB_INSERT( count, tab, p, index ) \
TAB_INSERT_CAST( , count, tab, p, index )
/**
* Binary search in a sorted array. The key must be comparable by < and >
* \param entries array of entries
* \param count number of entries
* \param elem key to check within an entry (like .id, or ->i_id)
* \param zetype type of the key
* \param key value of the key
* \param answer index of answer within the array. -1 if not found
*/
#define BSEARCH( entries, count, elem, zetype, key, answer ) \
do { \
int low = 0, high = count - 1; \
answer = -1; \
while( low <= high ) {\
int mid = (low + high ) / 2; /* Just don't care about 2^30 tables */ \
zetype mid_val = entries[mid] elem;\
if( mid_val < key ) \
low = mid + 1; \
else if ( mid_val > key ) \
high = mid -1; \
else \
{ \
answer = mid; break; \
}\
} \
} while(0)
/************************************************************************
* Dynamic arrays with progressive allocation
************************************************************************/
/* Internal functions */
#define _ARRAY_ALLOC(array, newsize) { \
(array).i_alloc = newsize; \
(array).p_elems = realloc( (array).p_elems, (array).i_alloc * \
sizeof(*(array).p_elems) ); \
if( !(array).p_elems ) abort(); \
}
#define _ARRAY_GROW1(array) { \
if( (array).i_alloc < 10 ) \
_ARRAY_ALLOC(array, 10 ) \
else if( (array).i_alloc == (array).i_size ) \
_ARRAY_ALLOC(array, (int)(array.i_alloc * 1.5) ) \
}
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/* API */
#define DECL_ARRAY(type) struct { \
int i_alloc; \
int i_size; \
type *p_elems; \
}
#define TYPEDEF_ARRAY(type, name) typedef DECL_ARRAY(type) name;
#define ARRAY_INIT(array) \
do { \
(array).i_alloc = 0; \
(array).i_size = 0; \
(array).p_elems = NULL; \
} while(0)
#define ARRAY_RESET(array) \
do { \
(array).i_alloc = 0; \
(array).i_size = 0; \
free( (array).p_elems ); (array).p_elems = NULL; \
} while(0)
#define ARRAY_APPEND(array, elem) \
do { \
_ARRAY_GROW1(array); \
(array).p_elems[(array).i_size] = elem; \
(array).i_size++; \
} while(0)
#define ARRAY_INSERT(array,elem,pos) \
do { \
_ARRAY_GROW1(array); \
if( (array).i_size - pos ) { \
memmove( (array).p_elems + pos + 1, (array).p_elems + pos, \
((array).i_size-pos) * sizeof(*(array).p_elems) ); \
} \
(array).p_elems[pos] = elem; \
(array).i_size++; \
} while(0)
#define _ARRAY_SHRINK(array) { \
if( (array).i_size > 10 && (array).i_size < (int)((array).i_alloc / 1.5) ) { \
_ARRAY_ALLOC(array, (array).i_size + 5); \
} \
}
#define ARRAY_REMOVE(array,pos) \
do { \
if( (array).i_size - (pos) - 1 ) \
{ \
memmove( (array).p_elems + pos, (array).p_elems + pos + 1, \
( (array).i_size - pos - 1 ) *sizeof(*(array).p_elems) ); \
} \
(array).i_size--; \
_ARRAY_SHRINK(array); \
} while(0)
#define ARRAY_VAL(array, pos) array.p_elems[pos]
#define ARRAY_BSEARCH(array, elem, zetype, key, answer) \
BSEARCH( (array).p_elems, (array).i_size, elem, zetype, key, answer)
#define FOREACH_ARRAY( item, array ) { \
int fe_idx; \
for( fe_idx = 0 ; fe_idx < (array).i_size ; fe_idx++ ) \
{ \
item = (array).p_elems[fe_idx];
#define FOREACH_END() } }
/************************************************************************
* Dynamic arrays with progressive allocation (Preferred API)
************************************************************************/
typedef struct vlc_array_t
{
int i_count;
void ** pp_elems;
} vlc_array_t;
static inline void vlc_array_init( vlc_array_t * p_array )
{
memset( p_array, 0, sizeof(vlc_array_t) );
}
static inline void vlc_array_clear( vlc_array_t * p_array )
{
free( p_array->pp_elems );
memset( p_array, 0, sizeof(vlc_array_t) );
}
static inline vlc_array_t * vlc_array_new( void )
{
vlc_array_t * ret = (vlc_array_t *)malloc( sizeof(vlc_array_t) );
if( ret ) vlc_array_init( ret );
return ret;
}
static inline void vlc_array_destroy( vlc_array_t * p_array )
{
if( !p_array )
return;
vlc_array_clear( p_array );
free( p_array );
}
/* Read */
static inline int
vlc_array_count( vlc_array_t * p_array )
{
return p_array->i_count;
}
static inline void *
vlc_array_item_at_index( vlc_array_t * p_array, int i_index )
{
return p_array->pp_elems[i_index];
}
static inline int
vlc_array_index_of_item( vlc_array_t * p_array, void * item )
{
int i;
for( i = 0; i < p_array->i_count; i++)
{
if( p_array->pp_elems[i] == item )
return i;
}
return -1;
}
/* Write */
static inline void
vlc_array_insert( vlc_array_t * p_array, void * p_elem, int i_index )
{
TAB_INSERT_CAST( (void **), p_array->i_count, p_array->pp_elems, p_elem, i_index );
}
static inline void
vlc_array_append( vlc_array_t * p_array, void * p_elem )
{
vlc_array_insert( p_array, p_elem, p_array->i_count );
}
static inline void
vlc_array_remove( vlc_array_t * p_array, int i_index )
{
if( i_index >= 0 )
{
if( p_array->i_count > 1 )
{
memmove( p_array->pp_elems + i_index,
p_array->pp_elems + i_index+1,
( p_array->i_count - i_index - 1 ) * sizeof( void* ) );
}
p_array->i_count--;
if( p_array->i_count == 0 )
{
free( p_array->pp_elems );
p_array->pp_elems = NULL;
}
}
}
/************************************************************************
* Dictionaries
************************************************************************/
/* This function is not intended to be crypto-secure, we only want it to be
* fast and not suck too much. This one is pretty fast and did 0 collisions
* in wenglish's dictionary.
*/
static inline uint64_t DictHash( const char *psz_string, int hashsize )
{
uint64_t i_hash = 0;
if( psz_string )
{
while( *psz_string )
{
i_hash += *psz_string++;
i_hash += i_hash << 10;
i_hash ^= i_hash >> 8;
}
}
return i_hash % hashsize;
}
typedef struct vlc_dictionary_entry_t
{
char * psz_key;
void * p_value;
struct vlc_dictionary_entry_t * p_next;
} vlc_dictionary_entry_t;
typedef struct vlc_dictionary_t
{
int i_size;
vlc_dictionary_entry_t ** p_entries;
} vlc_dictionary_t;
static void * const kVLCDictionaryNotFound = NULL;
static inline void vlc_dictionary_init( vlc_dictionary_t * p_dict, int i_size )
{
p_dict->p_entries = NULL;
if( i_size > 0 )
{
p_dict->p_entries = (vlc_dictionary_entry_t **)calloc( i_size, sizeof(*p_dict->p_entries) );
if( !p_dict->p_entries )
i_size = 0;
}
p_dict->i_size = i_size;
}
static inline void vlc_dictionary_clear( vlc_dictionary_t * p_dict,
void ( * pf_free )( void * p_data, void * p_obj ),
void * p_obj )
{
if( p_dict->p_entries )
{
for( int i = 0; i < p_dict->i_size; i++ )
{
vlc_dictionary_entry_t * p_current, * p_next;
p_current = p_dict->p_entries[i];
while( p_current )
{
p_next = p_current->p_next;
if( pf_free != NULL )
( * pf_free )( p_current->p_value, p_obj );
free( p_current->psz_key );
free( p_current );
p_current = p_next;
}
}
free( p_dict->p_entries );
p_dict->p_entries = NULL;
}
p_dict->i_size = 0;
}
static inline int
vlc_dictionary_has_key( const vlc_dictionary_t * p_dict, const char * psz_key )
{
if( !p_dict->p_entries )
return 0;
int i_pos = DictHash( psz_key, p_dict->i_size );
return p_dict->p_entries[i_pos] != NULL;
}
static inline void *
vlc_dictionary_value_for_key( const vlc_dictionary_t * p_dict, const char * psz_key )
{
if( !p_dict->p_entries )
return kVLCDictionaryNotFound;
int i_pos = DictHash( psz_key, p_dict->i_size );
vlc_dictionary_entry_t * p_entry = p_dict->p_entries[i_pos];
if( !p_entry )
return kVLCDictionaryNotFound;
/* Make sure we return the right item. (Hash collision) */
do {
if( !strcmp( psz_key, p_entry->psz_key ) )
return p_entry->p_value;
p_entry = p_entry->p_next;
} while( p_entry );
return kVLCDictionaryNotFound;
}
static inline int
vlc_dictionary_keys_count( const vlc_dictionary_t * p_dict )
{
vlc_dictionary_entry_t * p_entry;
int i, count = 0;
if( !p_dict->p_entries )
return 0;
for( i = 0; i < p_dict->i_size; i++ )
{
for( p_entry = p_dict->p_entries[i]; p_entry; p_entry = p_entry->p_next ) count++;
}
return count;
}
static inline char **
vlc_dictionary_all_keys( const vlc_dictionary_t * p_dict )
{
vlc_dictionary_entry_t * p_entry;
char ** ppsz_ret;
int i, count = vlc_dictionary_keys_count( p_dict );
ppsz_ret = (char**)malloc(sizeof(char *) * (count + 1));
if( unlikely(!ppsz_ret) )
return NULL;
count = 0;
for( i = 0; i < p_dict->i_size; i++ )
{
for( p_entry = p_dict->p_entries[i]; p_entry; p_entry = p_entry->p_next )
ppsz_ret[count++] = strdup( p_entry->psz_key );
}
ppsz_ret[count] = NULL;
return ppsz_ret;
}
static inline void
__vlc_dictionary_insert( vlc_dictionary_t * p_dict, const char * psz_key,
void * p_value, bool rebuild )
{
if( !p_dict->p_entries )
vlc_dictionary_init( p_dict, 1 );
int i_pos = DictHash( psz_key, p_dict->i_size );
vlc_dictionary_entry_t * p_entry;
p_entry = (vlc_dictionary_entry_t *)malloc(sizeof(*p_entry));
p_entry->psz_key = strdup( psz_key );
p_entry->p_value = p_value;
p_entry->p_next = p_dict->p_entries[i_pos];
p_dict->p_entries[i_pos] = p_entry;
if( rebuild )
{
/* Count how many items there was */
int count;
for( count = 1; p_entry->p_next; count++ )
p_entry = p_entry->p_next;
if( count > 3 ) /* XXX: this need tuning */
{
/* Here it starts to be not good, rebuild a bigger dictionary */
struct vlc_dictionary_t new_dict;
int i_new_size = ( (p_dict->i_size+2) * 3) / 2; /* XXX: this need tuning */
int i;
vlc_dictionary_init( &new_dict, i_new_size );
for( i = 0; i < p_dict->i_size; i++ )
{
p_entry = p_dict->p_entries[i];
while( p_entry )
{
__vlc_dictionary_insert( &new_dict, p_entry->psz_key,
p_entry->p_value,
false /* To avoid multiple rebuild loop */);
p_entry = p_entry->p_next;
}
}
vlc_dictionary_clear( p_dict, NULL, NULL );
p_dict->i_size = new_dict.i_size;
p_dict->p_entries = new_dict.p_entries;
}
}
}
static inline void
vlc_dictionary_insert( vlc_dictionary_t * p_dict, const char * psz_key, void * p_value )
{
__vlc_dictionary_insert( p_dict, psz_key, p_value, true );
}
static inline void
vlc_dictionary_remove_value_for_key( const vlc_dictionary_t * p_dict, const char * psz_key,
void ( * pf_free )( void * p_data, void * p_obj ),
void * p_obj )
{
if( !p_dict->p_entries )
return;
int i_pos = DictHash( psz_key, p_dict->i_size );
vlc_dictionary_entry_t * p_entry = p_dict->p_entries[i_pos];
vlc_dictionary_entry_t * p_prev;
if( !p_entry )
return; /* Not found, nothing to do */
/* Hash collision */
p_prev = NULL;
do {
if( !strcmp( psz_key, p_entry->psz_key ) )
{
if( pf_free != NULL )
( * pf_free )( p_entry->p_value, p_obj );
if( !p_prev )
p_dict->p_entries[i_pos] = p_entry->p_next;
else
p_prev->p_next = p_entry->p_next;
free( p_entry->psz_key );
free( p_entry );
return;
}
p_prev = p_entry;
p_entry = p_entry->p_next;
} while( p_entry );
/* No key was found */
}
#ifdef __cplusplus
// C++ helpers
template <typename T>
void vlc_delete_all( T &container )
{
typename T::iterator it = container.begin();
while ( it != container.end() )
{
delete *it;
++it;
}
container.clear();
}
#endif
#endif

View file

@ -0,0 +1,30 @@
/*****************************************************************************
* vlc_art_finder.h
*****************************************************************************
* Copyright (C) 2009 Rémi Denis-Courmont
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_ART_FINDER_H
#define VLC_ART_FINDER_H 1
typedef struct art_finder_t
{
VLC_COMMON_MEMBERS
input_item_t *p_item;
} art_finder_t;
#endif

View file

@ -0,0 +1,383 @@
/*****************************************************************************
* vlc_atomic.h:
*****************************************************************************
* Copyright (C) 2010 Rémi Denis-Courmont
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_ATOMIC_H
# define VLC_ATOMIC_H
/**
* \file
* Atomic operations do not require locking, but they are not very powerful.
*/
# if !defined (__cplusplus) && (__STDC_VERSION__ >= 201112L) \
&& !defined (__STDC_NO_ATOMICS__)
/*** Native C11 atomics ***/
# include <stdatomic.h>
# else
# define ATOMIC_FLAG_INIT false
# define ATOMIC_VAR_INIT(value) (value)
# define atomic_init(obj, value) \
do { *(obj) = (value); } while(0)
# define kill_dependency(y) \
((void)0)
# define atomic_thread_fence(order) \
__sync_synchronize()
# define atomic_signal_fence(order) \
((void)0)
# define atomic_is_lock_free(obj) \
false
/* In principles, __sync_*() only supports int, long and long long and their
* unsigned equivalents, i.e. 4-bytes and 8-bytes types, although GCC also
* supports 1 and 2-bytes types. Some non-x86 architectures do not support
* 8-byte atomic types (or not efficiently). */
typedef bool atomic_flag;
typedef bool atomic_bool;
typedef char atomic_char;
typedef signed char atomic_schar;
typedef unsigned char atomic_uchar;
typedef short atomic_short;
typedef unsigned short atomic_ushort;
typedef int atomic_int;
typedef unsigned int atomic_uint;
typedef long atomic_long;
typedef unsigned long atomic_ulong;
typedef long long atomic_llong;
typedef unsigned long long atomic_ullong;
//typedef char16_t atomic_char16_t;
//typedef char32_t atomic_char32_t;
typedef wchar_t atomic_wchar_t;
typedef int_least8_t atomic_int_least8_t;
typedef uint_least8_t atomic_uint_least8_t;
typedef int_least16_t atomic_int_least16_t;
typedef uint_least16_t atomic_uint_least16_t;
typedef int_least32_t atomic_int_least32_t;
typedef uint_least32_t atomic_uint_least32_t;
typedef int_least64_t atomic_int_least64_t;
typedef uint_least64_t atomic_uint_least64_t;
typedef int_fast8_t atomic_int_fast8_t;
typedef uint_fast8_t atomic_uint_fast8_t;
typedef int_fast16_t atomic_int_fast16_t;
typedef uint_fast16_t atomic_uint_fast16_t;
typedef int_fast32_t atomic_int_fast32_t;
typedef uint_fast32_t atomic_uint_fast32_t;
typedef int_fast64_t atomic_int_fast64_t;
typedef uint_fast64_t atomic_uint_fast64_t;
typedef intptr_t atomic_intptr_t;
typedef uintptr_t atomic_uintptr_t;
typedef size_t atomic_size_t;
typedef ptrdiff_t atomic_ptrdiff_t;
typedef intmax_t atomic_intmax_t;
typedef uintmax_t atomic_uintmax_t;
# if defined (__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || (defined (__clang__) && (defined (__x86_64__) || defined (__i386__)))
/*** Intel/GCC atomics ***/
# define atomic_store(object,desired) \
do { \
*(object) = (desired); \
__sync_synchronize(); \
} while (0)
# define atomic_store_explicit(object,desired,order) \
atomic_store(object,desired)
# define atomic_load(object) \
(__sync_synchronize(), *(object))
# define atomic_load_explicit(object,order) \
atomic_load(object)
# define atomic_exchange(object,desired) \
({ \
typeof (object) _obj = (object); \
typeof (*object) _old; \
do \
_old = atomic_load(_obj); \
while (!__sync_bool_compare_and_swap(_obj, _old, (desired))); \
_old; \
})
# define atomic_exchange_explicit(object,desired,order) \
atomic_exchange(object,desired)
# define atomic_compare_exchange(object,expected,desired) \
({ \
typeof (object) _exp = (expected); \
typeof (*object) _old = *_exp; \
*_exp = __sync_val_compare_and_swap((object), _old, (desired)); \
*_exp == _old; \
})
# define atomic_compare_exchange_strong(object,expected,desired) \
atomic_compare_exchange(object, expected, desired)
# define atomic_compare_exchange_strong_explicit(object,expected,desired,order) \
atomic_compare_exchange_strong(object, expected, desired)
# define atomic_compare_exchange_weak(object,expected,desired) \
atomic_compare_exchange(object, expected, desired)
# define atomic_compare_exchange_weak_explicit(object,expected,desired,order) \
atomic_compare_exchange_weak(object, expected, desired)
# define atomic_fetch_add(object,operand) \
__sync_fetch_and_add(object, operand)
# define atomic_fetch_add_explicit(object,operand,order) \
atomic_fetch_add(object,operand)
# define atomic_fetch_sub(object,operand) \
__sync_fetch_and_sub(object, operand)
# define atomic_fetch_sub_explicit(object,operand,order) \
atomic_fetch_sub(object,operand)
# define atomic_fetch_or(object,operand) \
__sync_fetch_and_or(object, operand)
# define atomic_fetch_or_explicit(object,operand,order) \
atomic_fetch_or(object,operand)
# define atomic_fetch_xor(object,operand) \
__sync_fetch_and_sub(object, operand)
# define atomic_fetch_xor_explicit(object,operand,order) \
atomic_fetch_sub(object,operand)
# define atomic_fetch_and(object,operand) \
__sync_fetch_and_and(object, operand)
# define atomic_fetch_and_explicit(object,operand,order) \
atomic_fetch_and(object,operand)
# define atomic_flag_test_and_set(object) \
atomic_exchange(object, true)
# define atomic_flag_test_and_set_explicit(object,order) \
atomic_flag_test_and_set(object)
# define atomic_flag_clear(object) \
atomic_store(object, false)
# define atomic_flag_clear_explicit(object,order) \
atomic_flag_clear(object)
# elif defined (__GNUC__)
/*** No atomics ***/
# define atomic_store(object,desired) \
do { \
typeof (object) _obj = (object); \
typeof (*object) _des = (desired); \
vlc_global_lock(VLC_ATOMIC_MUTEX); \
*_obj = _des; \
vlc_global_unlock(VLC_ATOMIC_MUTEX); \
} while (0)
# define atomic_store_explicit(object,desired,order) \
atomic_store(object,desired)
# define atomic_load(object) \
({ \
typeof (object) _obj = (object); \
typeof (*object) _old; \
vlc_global_lock(VLC_ATOMIC_MUTEX); \
_old = *_obj; \
vlc_global_unlock(VLC_ATOMIC_MUTEX); \
_old; \
})
# define atomic_load_explicit(object,order) \
atomic_load(object)
# define atomic_exchange(object,desired) \
({ \
typeof (object) _obj = (object); \
typeof (*object) _des = (desired); \
typeof (*object) _old; \
vlc_global_lock(VLC_ATOMIC_MUTEX); \
_old = *_obj; \
*_obj = _des; \
vlc_global_unlock(VLC_ATOMIC_MUTEX); \
_old; \
})
# define atomic_exchange_explicit(object,desired,order) \
atomic_exchange(object,desired)
# define atomic_compare_exchange_strong(object,expected,desired) \
({ \
typeof (object) _obj = (object); \
typeof (object) _exp = (expected); \
typeof (*object) _des = (desired); \
bool ret; \
vlc_global_lock(VLC_ATOMIC_MUTEX); \
ret = *_obj == *_exp; \
if (ret) \
*_obj = _des; \
else \
*_exp = *_obj; \
vlc_global_unlock(VLC_ATOMIC_MUTEX); \
ret; \
})
# define atomic_compare_exchange_strong_explicit(object,expected,desired,order) \
atomic_compare_exchange_strong(object, expected, desired)
# define atomic_compare_exchange_weak(object,expected,desired) \
atomic_compare_exchange_strong(object, expected, desired)
# define atomic_compare_exchange_weak_explicit(object,expected,desired,order) \
atomic_compare_exchange_weak(object, expected, desired)
# define atomic_fetch_OP(object,desired,op) \
({ \
typeof (object) _obj = (object); \
typeof (*object) _des = (desired); \
typeof (*object) _old; \
vlc_global_lock(VLC_ATOMIC_MUTEX); \
_old = *_obj; \
*_obj = (*_obj) op (_des); \
vlc_global_unlock(VLC_ATOMIC_MUTEX); \
_old; \
})
# define atomic_fetch_add(object,operand) \
atomic_fetch_OP(object,operand,+)
# define atomic_fetch_add_explicit(object,operand,order) \
atomic_fetch_add(object,operand)
# define atomic_fetch_sub(object,operand) \
atomic_fetch_OP(object,operand,-)
# define atomic_fetch_sub_explicit(object,operand,order) \
atomic_fetch_sub(object,operand)
# define atomic_fetch_or(object,operand) \
atomic_fetch_OP(object,operand,|)
# define atomic_fetch_or_explicit(object,operand,order) \
atomic_fetch_or(object,operand)
# define atomic_fetch_xor(object,operand) \
atomic_fetch_OP(object,operand,^)
# define atomic_fetch_xor_explicit(object,operand,order) \
atomic_fetch_sub(object,operand)
# define atomic_fetch_and(object,operand) \
atomic_fetch_OP(object,operand,&)
# define atomic_fetch_and_explicit(object,operand,order) \
atomic_fetch_and(object,operand)
# define atomic_flag_test_and_set(object) \
atomic_exchange(object, true)
# define atomic_flag_test_and_set_explicit(object,order) \
atomic_flag_test_and_set(object)
# define atomic_flag_clear(object) \
atomic_store(object, false)
# define atomic_flag_clear_explicit(object,order) \
atomic_flag_clear(object)
# else
# error FIXME: implement atomic operations for this compiler.
# endif
# endif
/**
* Memory storage space for an atom. Never access it directly.
*/
typedef union
{
atomic_uintptr_t u;
} vlc_atomic_t;
/** Static initializer for \ref vlc_atomic_t */
# define VLC_ATOMIC_INIT(val) { (val) }
/* All functions return the atom value _after_ the operation. */
static inline uintptr_t vlc_atomic_get(vlc_atomic_t *atom)
{
return atomic_load(&atom->u);
}
static inline uintptr_t vlc_atomic_set(vlc_atomic_t *atom, uintptr_t v)
{
atomic_store(&atom->u, v);
return v;
}
static inline uintptr_t vlc_atomic_add(vlc_atomic_t *atom, uintptr_t v)
{
return atomic_fetch_add(&atom->u, v) + v;
}
static inline uintptr_t vlc_atomic_sub (vlc_atomic_t *atom, uintptr_t v)
{
return atomic_fetch_sub (&atom->u, v) - v;
}
static inline uintptr_t vlc_atomic_inc (vlc_atomic_t *atom)
{
return vlc_atomic_add (atom, 1);
}
static inline uintptr_t vlc_atomic_dec (vlc_atomic_t *atom)
{
return vlc_atomic_sub (atom, 1);
}
static inline uintptr_t vlc_atomic_swap(vlc_atomic_t *atom, uintptr_t v)
{
return atomic_exchange(&atom->u, v);
}
static inline uintptr_t vlc_atomic_compare_swap(vlc_atomic_t *atom,
uintptr_t u, uintptr_t v)
{
atomic_compare_exchange_strong(&atom->u, &u, v);
return u;
}
typedef atomic_uint_least32_t vlc_atomic_float;
/** Helper to retrieve a single precision from an atom. */
static inline float vlc_atomic_loadf(vlc_atomic_float *atom)
{
union { float f; uint32_t i; } u;
u.i = atomic_load(atom);
return u.f;
}
/** Helper to store a single precision into an atom. */
static inline void vlc_atomic_storef(vlc_atomic_float *atom, float f)
{
union { float f; uint32_t i; } u;
u.f = f;
atomic_store(atom, u.i);
}
#endif

View file

@ -0,0 +1,34 @@
/*****************************************************************************
* vlc_avcodec.h: VLC thread support for FFMPEG/libavcodec
*****************************************************************************
* Copyright (C) 2009-2010 Rémi Denis-Courmont
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_AVCODEC_H
# define VLC_AVCODEC_H 1
static inline void vlc_avcodec_lock (void)
{
vlc_global_lock (VLC_AVCODEC_MUTEX);
}
static inline void vlc_avcodec_unlock (void)
{
vlc_global_unlock (VLC_AVCODEC_MUTEX);
}
#endif

View file

@ -0,0 +1,197 @@
/*****************************************************************************
* vlc_bits.h : Bit handling helpers
*****************************************************************************
* Copyright (C) 2003 VLC authors and VideoLAN
* $Id: 6c2915138c768d9c49b6646dde6c711acf6eabef $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_BITS_H
#define VLC_BITS_H 1
/**
* \file
* This file defines functions, structures for handling streams of bits in vlc
*/
typedef struct bs_s
{
uint8_t *p_start;
uint8_t *p;
uint8_t *p_end;
ssize_t i_left; /* i_count number of available bits */
} bs_t;
static inline void bs_init( bs_t *s, const void *p_data, size_t i_data )
{
s->p_start = (void *)p_data;
s->p = s->p_start;
s->p_end = s->p_start + i_data;
s->i_left = 8;
}
static inline int bs_pos( const bs_t *s )
{
return( 8 * ( s->p - s->p_start ) + 8 - s->i_left );
}
static inline int bs_eof( const bs_t *s )
{
return( s->p >= s->p_end ? 1: 0 );
}
static inline uint32_t bs_read( bs_t *s, int i_count )
{
static const uint32_t i_mask[33] =
{ 0x00,
0x01, 0x03, 0x07, 0x0f,
0x1f, 0x3f, 0x7f, 0xff,
0x1ff, 0x3ff, 0x7ff, 0xfff,
0x1fff, 0x3fff, 0x7fff, 0xffff,
0x1ffff, 0x3ffff, 0x7ffff, 0xfffff,
0x1fffff, 0x3fffff, 0x7fffff, 0xffffff,
0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff,
0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff};
int i_shr;
uint32_t i_result = 0;
while( i_count > 0 )
{
if( s->p >= s->p_end )
{
break;
}
if( ( i_shr = s->i_left - i_count ) >= 0 )
{
/* more in the buffer than requested */
i_result |= ( *s->p >> i_shr )&i_mask[i_count];
s->i_left -= i_count;
if( s->i_left == 0 )
{
s->p++;
s->i_left = 8;
}
return( i_result );
}
else
{
/* less in the buffer than requested */
i_result |= (*s->p&i_mask[s->i_left]) << -i_shr;
i_count -= s->i_left;
s->p++;
s->i_left = 8;
}
}
return( i_result );
}
static inline uint32_t bs_read1( bs_t *s )
{
if( s->p < s->p_end )
{
unsigned int i_result;
s->i_left--;
i_result = ( *s->p >> s->i_left )&0x01;
if( s->i_left == 0 )
{
s->p++;
s->i_left = 8;
}
return i_result;
}
return 0;
}
static inline uint32_t bs_show( bs_t *s, int i_count )
{
bs_t s_tmp = *s;
return bs_read( &s_tmp, i_count );
}
static inline void bs_skip( bs_t *s, ssize_t i_count )
{
s->i_left -= i_count;
if( s->i_left <= 0 )
{
const int i_bytes = ( -s->i_left + 8 ) / 8;
s->p += i_bytes;
s->i_left += 8 * i_bytes;
}
}
static inline void bs_write( bs_t *s, int i_count, uint32_t i_bits )
{
while( i_count > 0 )
{
if( s->p >= s->p_end )
{
break;
}
i_count--;
if( ( i_bits >> i_count )&0x01 )
{
*s->p |= 1 << ( s->i_left - 1 );
}
else
{
*s->p &= ~( 1 << ( s->i_left - 1 ) );
}
s->i_left--;
if( s->i_left == 0 )
{
s->p++;
s->i_left = 8;
}
}
}
static inline void bs_align( bs_t *s )
{
if( s->i_left != 8 )
{
s->i_left = 8;
s->p++;
}
}
static inline void bs_align_0( bs_t *s )
{
if( s->i_left != 8 )
{
bs_write( s, s->i_left, 0 );
}
}
static inline void bs_align_1( bs_t *s )
{
while( s->i_left != 8 )
{
bs_write( s, 1, 1 );
}
}
#endif

View file

@ -0,0 +1,321 @@
/*****************************************************************************
* vlc_block.h: Data blocks management functions
*****************************************************************************
* Copyright (C) 2003 VLC authors and VideoLAN
* $Id: e56e4c7efa57d9ae644892f7e66bf98902f7ce14 $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_BLOCK_H
#define VLC_BLOCK_H 1
/**
* \file
* This file implements functions and structures to handle blocks of data in vlc
*
*/
#include <sys/types.h> /* for ssize_t */
/****************************************************************************
* block:
****************************************************************************
* - i_flags may not always be set (ie could be 0, even for a key frame
* it depends where you receive the buffer (before/after a packetizer
* and the demux/packetizer implementations.
* - i_dts/i_pts could be VLC_TS_INVALID, it means no pts/dts
* - i_length: length in microseond of the packet, can be null except in the
* sout where it is mandatory.
*
* - i_buffer number of valid data pointed by p_buffer
* you can freely decrease it but never increase it yourself
* (use block_Realloc)
* - p_buffer: pointer over datas. You should never overwrite it, you can
* only incremment it to skip datas, in others cases use block_Realloc
* (don't duplicate yourself in a bigger buffer, block_Realloc is
* optimised for preheader/postdatas increase)
****************************************************************************/
/** The content doesn't follow the last block, or is probably broken */
#define BLOCK_FLAG_DISCONTINUITY 0x0001
/** Intra frame */
#define BLOCK_FLAG_TYPE_I 0x0002
/** Inter frame with backward reference only */
#define BLOCK_FLAG_TYPE_P 0x0004
/** Inter frame with backward and forward reference */
#define BLOCK_FLAG_TYPE_B 0x0008
/** For inter frame when you don't know the real type */
#define BLOCK_FLAG_TYPE_PB 0x0010
/** Warn that this block is a header one */
#define BLOCK_FLAG_HEADER 0x0020
/** This is the last block of the frame */
#define BLOCK_FLAG_END_OF_FRAME 0x0040
/** This is not a key frame for bitrate shaping */
#define BLOCK_FLAG_NO_KEYFRAME 0x0080
/** This block contains the last part of a sequence */
#define BLOCK_FLAG_END_OF_SEQUENCE 0x0100
/** This block contains a clock reference */
#define BLOCK_FLAG_CLOCK 0x0200
/** This block is scrambled */
#define BLOCK_FLAG_SCRAMBLED 0x0400
/** This block has to be decoded but not be displayed */
#define BLOCK_FLAG_PREROLL 0x0800
/** This block is corrupted and/or there is data loss */
#define BLOCK_FLAG_CORRUPTED 0x1000
/** This block contains an interlaced picture with top field first */
#define BLOCK_FLAG_TOP_FIELD_FIRST 0x2000
/** This block contains an interlaced picture with bottom field first */
#define BLOCK_FLAG_BOTTOM_FIELD_FIRST 0x4000
/** This block contains an interlaced picture */
#define BLOCK_FLAG_INTERLACED_MASK \
(BLOCK_FLAG_TOP_FIELD_FIRST|BLOCK_FLAG_BOTTOM_FIELD_FIRST)
#define BLOCK_FLAG_TYPE_MASK \
(BLOCK_FLAG_TYPE_I|BLOCK_FLAG_TYPE_P|BLOCK_FLAG_TYPE_B|BLOCK_FLAG_TYPE_PB)
/* These are for input core private usage only */
#define BLOCK_FLAG_CORE_PRIVATE_MASK 0x00ff0000
#define BLOCK_FLAG_CORE_PRIVATE_SHIFT 16
/* These are for module private usage only */
#define BLOCK_FLAG_PRIVATE_MASK 0xff000000
#define BLOCK_FLAG_PRIVATE_SHIFT 24
typedef void (*block_free_t) (block_t *);
struct block_t
{
block_t *p_next;
uint8_t *p_buffer; /**< Payload start */
size_t i_buffer; /**< Payload length */
uint8_t *p_start; /**< Buffer start */
size_t i_size; /**< Buffer total size */
uint32_t i_flags;
unsigned i_nb_samples; /* Used for audio */
mtime_t i_pts;
mtime_t i_dts;
mtime_t i_length;
/* Rudimentary support for overloading block (de)allocation. */
block_free_t pf_release;
};
/****************************************************************************
* Blocks functions:
****************************************************************************
* - block_Alloc : create a new block with the requested size ( >= 0 ), return
* NULL for failure.
* - block_Release : release a block allocated with block_Alloc.
* - block_Realloc : realloc a block,
* i_pre: how many bytes to insert before body if > 0, else how many
* bytes of body to skip (the latter can be done without using
* block_Realloc i_buffer -= -i_pre, p_buffer += -i_pre as i_pre < 0)
* i_body (>= 0): the final size of the body (decreasing it can directly
* be done with i_buffer = i_body).
* with preheader and or body (increase
* and decrease are supported). Use it as it is optimised.
* - block_Duplicate : create a copy of a block.
****************************************************************************/
VLC_API void block_Init( block_t *, void *, size_t );
VLC_API block_t *block_Alloc( size_t ) VLC_USED VLC_MALLOC;
VLC_API block_t *block_Realloc( block_t *, ssize_t i_pre, size_t i_body ) VLC_USED;
static inline void block_CopyProperties( block_t *dst, block_t *src )
{
dst->i_flags = src->i_flags;
dst->i_nb_samples = src->i_nb_samples;
dst->i_dts = src->i_dts;
dst->i_pts = src->i_pts;
dst->i_length = src->i_length;
}
VLC_USED
static inline block_t *block_Duplicate( block_t *p_block )
{
block_t *p_dup = block_Alloc( p_block->i_buffer );
if( p_dup == NULL )
return NULL;
block_CopyProperties( p_dup, p_block );
memcpy( p_dup->p_buffer, p_block->p_buffer, p_block->i_buffer );
return p_dup;
}
static inline void block_Release( block_t *p_block )
{
p_block->pf_release( p_block );
}
VLC_API block_t *block_heap_Alloc(void *, size_t) VLC_USED VLC_MALLOC;
VLC_API block_t *block_mmap_Alloc(void *addr, size_t length) VLC_USED VLC_MALLOC;
VLC_API block_t * block_shm_Alloc(void *addr, size_t length) VLC_USED VLC_MALLOC;
VLC_API block_t *block_File(int fd) VLC_USED VLC_MALLOC;
VLC_API block_t *block_FilePath(const char *) VLC_USED VLC_MALLOC;
static inline void block_Cleanup (void *block)
{
block_Release ((block_t *)block);
}
#define block_cleanup_push( block ) vlc_cleanup_push (block_Cleanup, block)
/****************************************************************************
* Chains of blocks functions helper
****************************************************************************
* - block_ChainAppend : append a block to the last block of a chain. Try to
* avoid using with a lot of data as it's really slow, prefer
* block_ChainLastAppend, p_block can be NULL
* - block_ChainLastAppend : use a pointer over a pointer to the next blocks,
* and update it.
* - block_ChainRelease : release a chain of block
* - block_ChainExtract : extract data from a chain, return real bytes counts
* - block_ChainGather : gather a chain, free it and return one block.
****************************************************************************/
static inline void block_ChainAppend( block_t **pp_list, block_t *p_block )
{
if( *pp_list == NULL )
{
*pp_list = p_block;
}
else
{
block_t *p = *pp_list;
while( p->p_next ) p = p->p_next;
p->p_next = p_block;
}
}
static inline void block_ChainLastAppend( block_t ***ppp_last, block_t *p_block )
{
block_t *p_last = p_block;
**ppp_last = p_block;
while( p_last->p_next ) p_last = p_last->p_next;
*ppp_last = &p_last->p_next;
}
static inline void block_ChainRelease( block_t *p_block )
{
while( p_block )
{
block_t *p_next = p_block->p_next;
block_Release( p_block );
p_block = p_next;
}
}
static size_t block_ChainExtract( block_t *p_list, void *p_data, size_t i_max )
{
size_t i_total = 0;
uint8_t *p = (uint8_t*)p_data;
while( p_list && i_max )
{
size_t i_copy = __MIN( i_max, p_list->i_buffer );
memcpy( p, p_list->p_buffer, i_copy );
i_max -= i_copy;
i_total += i_copy;
p += i_copy;
p_list = p_list->p_next;
}
return i_total;
}
static inline void block_ChainProperties( block_t *p_list, int *pi_count, size_t *pi_size, mtime_t *pi_length )
{
size_t i_size = 0;
mtime_t i_length = 0;
int i_count = 0;
while( p_list )
{
i_size += p_list->i_buffer;
i_length += p_list->i_length;
i_count++;
p_list = p_list->p_next;
}
if( pi_size )
*pi_size = i_size;
if( pi_length )
*pi_length = i_length;
if( pi_count )
*pi_count = i_count;
}
static inline block_t *block_ChainGather( block_t *p_list )
{
size_t i_total = 0;
mtime_t i_length = 0;
block_t *g;
if( p_list->p_next == NULL )
return p_list; /* Already gathered */
block_ChainProperties( p_list, NULL, &i_total, &i_length );
g = block_Alloc( i_total );
block_ChainExtract( p_list, g->p_buffer, g->i_buffer );
g->i_flags = p_list->i_flags;
g->i_pts = p_list->i_pts;
g->i_dts = p_list->i_dts;
g->i_length = i_length;
/* free p_list */
block_ChainRelease( p_list );
return g;
}
/****************************************************************************
* Fifos of blocks.
****************************************************************************
* - block_FifoNew : create and init a new fifo
* - block_FifoRelease : destroy a fifo and free all blocks in it.
* - block_FifoPace : wait for a fifo to drain to a specified number of packets or total data size
* - block_FifoEmpty : free all blocks in a fifo
* - block_FifoPut : put a block
* - block_FifoGet : get a packet from the fifo (and wait if it is empty)
* - block_FifoShow : show the first packet of the fifo (and wait if
* needed), be carefull, you can use it ONLY if you are sure to be the
* only one getting data from the fifo.
* - block_FifoCount : how many packets are waiting in the fifo
*
* block_FifoGet and block_FifoShow are cancellation points.
****************************************************************************/
VLC_API block_fifo_t *block_FifoNew( void ) VLC_USED VLC_MALLOC;
VLC_API void block_FifoRelease( block_fifo_t * );
VLC_API void block_FifoPace( block_fifo_t *fifo, size_t max_depth, size_t max_size );
VLC_API void block_FifoEmpty( block_fifo_t * );
VLC_API size_t block_FifoPut( block_fifo_t *, block_t * );
void block_FifoWake( block_fifo_t * );
VLC_API block_t * block_FifoGet( block_fifo_t * ) VLC_USED;
VLC_API block_t * block_FifoShow( block_fifo_t * );
size_t block_FifoSize( const block_fifo_t *p_fifo ) VLC_USED;
VLC_API size_t block_FifoCount( const block_fifo_t *p_fifo ) VLC_USED;
#endif /* VLC_BLOCK_H */

View file

@ -0,0 +1,517 @@
/*****************************************************************************
* vlc_block_helper.h: Helper functions for data blocks management.
*****************************************************************************
* Copyright (C) 2003 VLC authors and VideoLAN
* $Id: fdd5fdbeafee1f296c157410ef3e69a7cf57d3e5 $
*
* Authors: Gildas Bazin <gbazin@netcourrier.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_BLOCK_HELPER_H
#define VLC_BLOCK_HELPER_H 1
#include <vlc_block.h>
typedef struct block_bytestream_t
{
block_t *p_chain; /**< byte stream head block */
block_t *p_block; /**< byte stream read pointer block */
size_t i_offset; /**< byte stream read pointer offset within block */
/* TODO? add tail pointer for faster push? */
} block_bytestream_t;
/*****************************************************************************
* block_bytestream_t management
*****************************************************************************/
static inline void block_BytestreamInit( block_bytestream_t *p_bytestream )
{
p_bytestream->p_chain = p_bytestream->p_block = NULL;
p_bytestream->i_offset = 0;
}
static inline void block_BytestreamRelease( block_bytestream_t *p_bytestream )
{
for( block_t *block = p_bytestream->p_chain; block != NULL; )
{
block_t *p_next = block->p_next;
block_Release( block );
block = p_next;
}
}
/**
* It flush all data (read and unread) from a block_bytestream_t.
*/
static inline void block_BytestreamEmpty( block_bytestream_t *p_bytestream )
{
block_BytestreamRelease( p_bytestream );
block_BytestreamInit( p_bytestream );
}
/**
* It flushes all already read data from a block_bytestream_t.
*/
static inline void block_BytestreamFlush( block_bytestream_t *p_bytestream )
{
block_t *block = p_bytestream->p_chain;
while( block != p_bytestream->p_block )
{
block_t *p_next = block->p_next;
block_Release( block );
block = p_next;
}
while( block != NULL && block->i_buffer == p_bytestream->i_offset )
{
block_t *p_next = block->p_next;
block_Release( block );
block = p_next;
p_bytestream->i_offset = 0;
}
p_bytestream->p_chain = p_bytestream->p_block = block;
}
static inline void block_BytestreamPush( block_bytestream_t *p_bytestream,
block_t *p_block )
{
block_ChainAppend( &p_bytestream->p_chain, p_block );
if( !p_bytestream->p_block ) p_bytestream->p_block = p_block;
}
VLC_USED
static inline block_t *block_BytestreamPop( block_bytestream_t *p_bytestream )
{
block_t *p_block;
block_BytestreamFlush( p_bytestream );
p_block = p_bytestream->p_block;
if( p_block == NULL )
{
return NULL;
}
else if( !p_block->p_next )
{
p_block->p_buffer += p_bytestream->i_offset;
p_block->i_buffer -= p_bytestream->i_offset;
p_bytestream->i_offset = 0;
p_bytestream->p_chain = p_bytestream->p_block = NULL;
return p_block;
}
while( p_block->p_next && p_block->p_next->p_next )
p_block = p_block->p_next;
block_t *p_block_old = p_block;
p_block = p_block->p_next;
p_block_old->p_next = NULL;
return p_block;
}
static inline int block_SkipByte( block_bytestream_t *p_bytestream )
{
/* Most common case first */
if( p_bytestream->p_block->i_buffer - p_bytestream->i_offset )
{
p_bytestream->i_offset++;
return VLC_SUCCESS;
}
else
{
block_t *p_block;
/* Less common case which is also slower */
for( p_block = p_bytestream->p_block->p_next;
p_block != NULL; p_block = p_block->p_next )
{
if( p_block->i_buffer )
{
p_bytestream->i_offset = 1;
p_bytestream->p_block = p_block;
return VLC_SUCCESS;
}
}
}
/* Not enough data, bail out */
return VLC_EGENERIC;
}
static inline int block_PeekByte( block_bytestream_t *p_bytestream,
uint8_t *p_data )
{
/* Most common case first */
if( p_bytestream->p_block->i_buffer - p_bytestream->i_offset )
{
*p_data = p_bytestream->p_block->p_buffer[p_bytestream->i_offset];
return VLC_SUCCESS;
}
else
{
block_t *p_block;
/* Less common case which is also slower */
for( p_block = p_bytestream->p_block->p_next;
p_block != NULL; p_block = p_block->p_next )
{
if( p_block->i_buffer )
{
*p_data = p_block->p_buffer[0];
return VLC_SUCCESS;
}
}
}
/* Not enough data, bail out */
return VLC_EGENERIC;
}
static inline int block_GetByte( block_bytestream_t *p_bytestream,
uint8_t *p_data )
{
/* Most common case first */
if( p_bytestream->p_block->i_buffer - p_bytestream->i_offset )
{
*p_data = p_bytestream->p_block->p_buffer[p_bytestream->i_offset];
p_bytestream->i_offset++;
return VLC_SUCCESS;
}
else
{
block_t *p_block;
/* Less common case which is also slower */
for( p_block = p_bytestream->p_block->p_next;
p_block != NULL; p_block = p_block->p_next )
{
if( p_block->i_buffer )
{
*p_data = p_block->p_buffer[0];
p_bytestream->i_offset = 1;
p_bytestream->p_block = p_block;
return VLC_SUCCESS;
}
}
}
/* Not enough data, bail out */
return VLC_EGENERIC;
}
static inline int block_WaitBytes( block_bytestream_t *p_bytestream,
size_t i_data )
{
block_t *p_block;
size_t i_offset, i_copy, i_size;
/* Check we have that much data */
i_offset = p_bytestream->i_offset;
i_size = i_data;
i_copy = 0;
for( p_block = p_bytestream->p_block;
p_block != NULL; p_block = p_block->p_next )
{
i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
i_size -= i_copy;
i_offset = 0;
if( !i_size ) break;
}
if( i_size )
{
/* Not enough data, bail out */
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
static inline int block_SkipBytes( block_bytestream_t *p_bytestream,
size_t i_data )
{
block_t *p_block;
size_t i_offset, i_copy;
/* Check we have that much data */
i_offset = p_bytestream->i_offset;
i_copy = 0;
for( p_block = p_bytestream->p_block;
p_block != NULL; p_block = p_block->p_next )
{
i_copy = __MIN( i_data, p_block->i_buffer - i_offset );
i_data -= i_copy;
if( !i_data ) break;
i_offset = 0;
}
if( i_data )
{
/* Not enough data, bail out */
return VLC_EGENERIC;
}
p_bytestream->p_block = p_block;
p_bytestream->i_offset = i_offset + i_copy;
return VLC_SUCCESS;
}
static inline int block_PeekBytes( block_bytestream_t *p_bytestream,
uint8_t *p_data, size_t i_data )
{
block_t *p_block;
size_t i_offset, i_copy, i_size;
/* Check we have that much data */
i_offset = p_bytestream->i_offset;
i_size = i_data;
i_copy = 0;
for( p_block = p_bytestream->p_block;
p_block != NULL; p_block = p_block->p_next )
{
i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
i_size -= i_copy;
i_offset = 0;
if( !i_size ) break;
}
if( i_size )
{
/* Not enough data, bail out */
return VLC_EGENERIC;
}
/* Copy the data */
i_offset = p_bytestream->i_offset;
i_size = i_data;
i_copy = 0;
for( p_block = p_bytestream->p_block;
p_block != NULL; p_block = p_block->p_next )
{
i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
i_size -= i_copy;
if( i_copy )
{
memcpy( p_data, p_block->p_buffer + i_offset, i_copy );
p_data += i_copy;
}
i_offset = 0;
if( !i_size ) break;
}
return VLC_SUCCESS;
}
static inline int block_GetBytes( block_bytestream_t *p_bytestream,
uint8_t *p_data, size_t i_data )
{
block_t *p_block;
size_t i_offset, i_copy, i_size;
/* Check we have that much data */
i_offset = p_bytestream->i_offset;
i_size = i_data;
i_copy = 0;
for( p_block = p_bytestream->p_block;
p_block != NULL; p_block = p_block->p_next )
{
i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
i_size -= i_copy;
i_offset = 0;
if( !i_size ) break;
}
if( i_size )
{
/* Not enough data, bail out */
return VLC_EGENERIC;
}
/* Copy the data */
i_offset = p_bytestream->i_offset;
i_size = i_data;
i_copy = 0;
for( p_block = p_bytestream->p_block;
p_block != NULL; p_block = p_block->p_next )
{
i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
i_size -= i_copy;
if( i_copy )
{
memcpy( p_data, p_block->p_buffer + i_offset, i_copy );
p_data += i_copy;
}
if( !i_size ) break;
i_offset = 0;
}
p_bytestream->p_block = p_block;
p_bytestream->i_offset = i_offset + i_copy;
return VLC_SUCCESS;
}
static inline int block_PeekOffsetBytes( block_bytestream_t *p_bytestream,
size_t i_peek_offset, uint8_t *p_data, size_t i_data )
{
block_t *p_block;
size_t i_offset, i_copy, i_size;
/* Check we have that much data */
i_offset = p_bytestream->i_offset;
i_size = i_data + i_peek_offset;
i_copy = 0;
for( p_block = p_bytestream->p_block;
p_block != NULL; p_block = p_block->p_next )
{
i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
i_size -= i_copy;
i_offset = 0;
if( !i_size ) break;
}
if( i_size )
{
/* Not enough data, bail out */
return VLC_EGENERIC;
}
/* Find the right place */
i_offset = p_bytestream->i_offset;
i_size = i_peek_offset;
i_copy = 0;
for( p_block = p_bytestream->p_block;
p_block != NULL; p_block = p_block->p_next )
{
i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
i_size -= i_copy;
if( !i_size ) break;
i_offset = 0;
}
/* Copy the data */
i_offset += i_copy;
i_size = i_data;
i_copy = 0;
for( ; p_block != NULL; p_block = p_block->p_next )
{
i_copy = __MIN( i_size, p_block->i_buffer - i_offset );
i_size -= i_copy;
if( i_copy )
{
memcpy( p_data, p_block->p_buffer + i_offset, i_copy );
p_data += i_copy;
}
i_offset = 0;
if( !i_size ) break;
}
return VLC_SUCCESS;
}
static inline int block_FindStartcodeFromOffset(
block_bytestream_t *p_bytestream, size_t *pi_offset,
const uint8_t *p_startcode, int i_startcode_length )
{
block_t *p_block, *p_block_backup = 0;
int i_size = 0;
size_t i_offset, i_offset_backup = 0;
int i_caller_offset_backup = 0, i_match;
/* Find the right place */
i_size = *pi_offset + p_bytestream->i_offset;
for( p_block = p_bytestream->p_block;
p_block != NULL; p_block = p_block->p_next )
{
i_size -= p_block->i_buffer;
if( i_size < 0 ) break;
}
if( i_size >= 0 )
{
/* Not enough data, bail out */
return VLC_EGENERIC;
}
/* Begin the search.
* We first look for an occurrence of the 1st startcode byte and
* if found, we do a more thorough check. */
i_size += p_block->i_buffer;
*pi_offset -= i_size;
i_match = 0;
for( ; p_block != NULL; p_block = p_block->p_next )
{
for( i_offset = i_size; i_offset < p_block->i_buffer; i_offset++ )
{
if( p_block->p_buffer[i_offset] == p_startcode[i_match] )
{
if( !i_match )
{
p_block_backup = p_block;
i_offset_backup = i_offset;
i_caller_offset_backup = *pi_offset;
}
if( i_match + 1 == i_startcode_length )
{
/* We have it */
*pi_offset += i_offset - i_match;
return VLC_SUCCESS;
}
i_match++;
}
else if ( i_match )
{
/* False positive */
p_block = p_block_backup;
i_offset = i_offset_backup;
*pi_offset = i_caller_offset_backup;
i_match = 0;
}
}
i_size = 0;
*pi_offset += i_offset;
}
*pi_offset -= i_match;
return VLC_EGENERIC;
}
#endif /* VLC_BLOCK_HELPER_H */

View file

@ -0,0 +1,211 @@
/*****************************************************************************
* vlc_charset.h: Unicode UTF-8 wrappers function
*****************************************************************************
* Copyright (C) 2003-2005 VLC authors and VideoLAN
* Copyright © 2005-2010 Rémi Denis-Courmont
* $Id: 4de57ab70f369c91338676f8ca154ecab427ca5e $
*
* Author: Rémi Denis-Courmont <rem # videolan,org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_CHARSET_H
#define VLC_CHARSET_H 1
/**
* \file
* This files handles locale conversions in vlc
*/
/* iconv wrappers (defined in src/extras/libc.c) */
typedef void *vlc_iconv_t;
VLC_API vlc_iconv_t vlc_iconv_open( const char *, const char * ) VLC_USED;
VLC_API size_t vlc_iconv( vlc_iconv_t, const char **, size_t *, char **, size_t * ) VLC_USED;
VLC_API int vlc_iconv_close( vlc_iconv_t );
#include <stdarg.h>
VLC_API int utf8_vfprintf( FILE *stream, const char *fmt, va_list ap );
VLC_API int utf8_fprintf( FILE *, const char *, ... ) VLC_FORMAT( 2, 3 );
VLC_API char * vlc_strcasestr(const char *, const char *) VLC_USED;
VLC_API char * EnsureUTF8( char * );
VLC_API const char * IsUTF8( const char * ) VLC_USED;
VLC_API char * FromCharset( const char *charset, const void *data, size_t data_size ) VLC_USED;
VLC_API void * ToCharset( const char *charset, const char *in, size_t *outsize ) VLC_USED;
#ifdef _WIN32
VLC_USED
static inline char *FromWide (const wchar_t *wide)
{
size_t len = WideCharToMultiByte (CP_UTF8, 0, wide, -1, NULL, 0, NULL, NULL);
if (len == 0)
return NULL;
char *out = (char *)malloc (len);
if (likely(out))
WideCharToMultiByte (CP_UTF8, 0, wide, -1, out, len, NULL, NULL);
return out;
}
VLC_USED
static inline wchar_t *ToWide (const char *utf8)
{
int len = MultiByteToWideChar (CP_UTF8, 0, utf8, -1, NULL, 0);
if (len == 0)
return NULL;
wchar_t *out = (wchar_t *)malloc (len * sizeof (wchar_t));
if (likely(out))
MultiByteToWideChar (CP_UTF8, 0, utf8, -1, out, len);
return out;
}
VLC_USED VLC_MALLOC
static inline char *ToCodePage (unsigned cp, const char *utf8)
{
wchar_t *wide = ToWide (utf8);
if (wide == NULL)
return NULL;
size_t len = WideCharToMultiByte (cp, 0, wide, -1, NULL, 0, NULL, NULL);
if (len == 0)
return NULL;
char *out = (char *)malloc (len);
if (likely(out != NULL))
WideCharToMultiByte (cp, 0, wide, -1, out, len, NULL, NULL);
free (wide);
return out;
}
VLC_USED VLC_MALLOC
static inline char *FromCodePage (unsigned cp, const char *mb)
{
int len = MultiByteToWideChar (cp, 0, mb, -1, NULL, 0);
if (len == 0)
return NULL;
wchar_t *wide = (wchar_t *)malloc (len * sizeof (wchar_t));
if (unlikely(wide == NULL))
return NULL;
MultiByteToWideChar (cp, 0, mb, -1, wide, len);
char *utf8 = FromWide (wide);
free (wide);
return utf8;
}
VLC_USED VLC_MALLOC
static inline char *FromANSI (const char *ansi)
{
return FromCodePage (GetACP (), ansi);
}
VLC_USED VLC_MALLOC
static inline char *ToANSI (const char *utf8)
{
return ToCodePage (GetACP (), utf8);
}
# ifdef UNICODE
# define FromT FromWide
# define ToT ToWide
# else
# define FromT FromANSI
# define ToT ToANSI
# endif
# define FromLocale FromANSI
# define ToLocale ToANSI
# define LocaleFree(s) free((char *)(s))
# define FromLocaleDup FromANSI
# define ToLocaleDup ToANSI
#elif defined(__OS2__)
VLC_USED static inline char *FromLocale (const char *locale)
{
return locale ? FromCharset ((char *)"", locale, strlen(locale)) : NULL;
}
VLC_USED static inline char *ToLocale (const char *utf8)
{
size_t outsize;
return utf8 ? (char *)ToCharset ("", utf8, &outsize) : NULL;
}
VLC_USED static inline void LocaleFree (const char *str)
{
free ((char *)str);
}
VLC_USED static inline char *FromLocaleDup (const char *locale)
{
return FromCharset ("", locale, strlen(locale));
}
VLC_USED static inline char *ToLocaleDup (const char *utf8)
{
size_t outsize;
return (char *)ToCharset ("", utf8, &outsize);
}
#else
# define FromLocale(l) (l)
# define ToLocale(u) (u)
# define LocaleFree(s) ((void)(s))
# define FromLocaleDup strdup
# define ToLocaleDup strdup
#endif
/**
* Converts a nul-terminated string from ISO-8859-1 to UTF-8.
*/
static inline char *FromLatin1 (const char *latin)
{
char *str = (char *)malloc (2 * strlen (latin) + 1), *utf8 = str;
unsigned char c;
if (str == NULL)
return NULL;
while ((c = *(latin++)) != '\0')
{
if (c >= 0x80)
{
*(utf8++) = 0xC0 | (c >> 6);
*(utf8++) = 0x80 | (c & 0x3F);
}
else
*(utf8++) = c;
}
*(utf8++) = '\0';
utf8 = (char *)realloc (str, utf8 - str);
return utf8 ? utf8 : str;
}
VLC_API double us_strtod( const char *, char ** ) VLC_USED;
VLC_API float us_strtof( const char *, char ** ) VLC_USED;
VLC_API double us_atof( const char * ) VLC_USED;
VLC_API int us_vasprintf( char **, const char *, va_list );
VLC_API int us_asprintf( char **, const char *, ... ) VLC_USED;
#endif

View file

@ -0,0 +1,246 @@
/*****************************************************************************
* vlc_codec.h: Definition of the decoder and encoder structures
*****************************************************************************
* Copyright (C) 1999-2003 VLC authors and VideoLAN
* $Id: b9a89d4cc714e3893187bf78605f3a82d5483478 $
*
* Authors: Gildas Bazin <gbazin@netcourrier.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_CODEC_H
#define VLC_CODEC_H 1
#include <vlc_block.h>
#include <vlc_es.h>
#include <vlc_picture.h>
#include <vlc_subpicture.h>
/**
* \file
* This file defines the structure and types used by decoders and encoders
*/
typedef struct decoder_owner_sys_t decoder_owner_sys_t;
/**
* \defgroup decoder Decoder
*
* The structure describing a decoder
*
* @{
*/
/*
* BIG FAT WARNING : the code relies in the first 4 members of filter_t
* and decoder_t to be the same, so if you have anything to add, do it
* at the end of the structure.
*/
struct decoder_t
{
VLC_COMMON_MEMBERS
/* Module properties */
module_t * p_module;
decoder_sys_t * p_sys;
/* Input format ie from demuxer (XXX: a lot of field could be invalid) */
es_format_t fmt_in;
/* Output format of decoder/packetizer */
es_format_t fmt_out;
/* Some decoders only accept packetized data (ie. not truncated) */
bool b_need_packetized;
/* Tell the decoder if it is allowed to drop frames */
bool b_pace_control;
/* */
picture_t * ( * pf_decode_video )( decoder_t *, block_t ** );
block_t * ( * pf_decode_audio )( decoder_t *, block_t ** );
subpicture_t * ( * pf_decode_sub) ( decoder_t *, block_t ** );
block_t * ( * pf_packetize ) ( decoder_t *, block_t ** );
/* Closed Caption (CEA 608/708) extraction.
* If set, it *may* be called after pf_decode_video/pf_packetize
* returned data. It should return CC for the pictures returned by the
* last pf_packetize/pf_decode_video call only,
* pb_present will be used to known which cc channel are present (but
* globaly, not necessary for the current packet */
block_t * ( * pf_get_cc ) ( decoder_t *, bool pb_present[4] );
/* Meta data at codec level
* The decoder owner set it back to NULL once it has retreived what it needs.
* The decoder owner is responsible of its release except when you overwrite it.
*/
vlc_meta_t *p_description;
/*
* Owner fields
* XXX You MUST not use them directly.
*/
/* Video output callbacks
* XXX use decoder_NewPicture/decoder_DeletePicture
* and decoder_LinkPicture/decoder_UnlinkPicture */
picture_t *(*pf_vout_buffer_new)( decoder_t * );
void (*pf_vout_buffer_del)( decoder_t *, picture_t * );
void (*pf_picture_link) ( decoder_t *, picture_t * );
void (*pf_picture_unlink) ( decoder_t *, picture_t * );
/**
* Number of extra (ie in addition to the DPB) picture buffers
* needed for decoding.
*/
int i_extra_picture_buffers;
/* Audio output callbacks
* XXX use decoder_NewAudioBuffer/decoder_DeleteAudioBuffer */
block_t *(*pf_aout_buffer_new)( decoder_t *, int );
/* SPU output callbacks
* XXX use decoder_NewSubpicture and decoder_DeleteSubpicture */
subpicture_t *(*pf_spu_buffer_new)( decoder_t *, const subpicture_updater_t * );
void (*pf_spu_buffer_del)( decoder_t *, subpicture_t * );
/* Input attachments
* XXX use decoder_GetInputAttachments */
int (*pf_get_attachments)( decoder_t *p_dec, input_attachment_t ***ppp_attachment, int *pi_attachment );
/* Display date
* XXX use decoder_GetDisplayDate */
mtime_t (*pf_get_display_date)( decoder_t *, mtime_t );
/* Display rate
* XXX use decoder_GetDisplayRate */
int (*pf_get_display_rate)( decoder_t * );
/* Private structure for the owner of the decoder */
decoder_owner_sys_t *p_owner;
bool b_error;
};
/**
* @}
*/
/**
* \defgroup encoder Encoder
*
* The structure describing a Encoder
*
* @{
*/
struct encoder_t
{
VLC_COMMON_MEMBERS
/* Module properties */
module_t * p_module;
encoder_sys_t * p_sys;
/* Properties of the input data fed to the encoder */
es_format_t fmt_in;
/* Properties of the output of the encoder */
es_format_t fmt_out;
block_t * ( * pf_encode_video )( encoder_t *, picture_t * );
block_t * ( * pf_encode_audio )( encoder_t *, block_t * );
block_t * ( * pf_encode_sub )( encoder_t *, subpicture_t * );
/* Common encoder options */
int i_threads; /* Number of threads to use during encoding */
int i_iframes; /* One I frame per i_iframes */
int i_bframes; /* One B frame per i_bframes */
int i_tolerance; /* Bitrate tolerance */
/* Encoder config */
config_chain_t *p_cfg;
};
/**
* @}
*/
/**
* This function will return a new picture usable by a decoder as an output
* buffer. You have to release it using decoder_DeletePicture or by returning
* it to the caller as a pf_decode_video return value.
*/
VLC_API picture_t * decoder_NewPicture( decoder_t * ) VLC_USED;
/**
* This function will release a picture create by decoder_NewPicture.
*/
VLC_API void decoder_DeletePicture( decoder_t *, picture_t *p_picture );
/**
* This function will increase the picture reference count.
* (picture_Hold is not usable.)
*/
VLC_API void decoder_LinkPicture( decoder_t *, picture_t * );
/**
* This function will decrease the picture reference count.
* (picture_Release is not usable.)
*/
VLC_API void decoder_UnlinkPicture( decoder_t *, picture_t * );
/**
* This function will return a new audio buffer usable by a decoder as an
* output buffer. You have to release it using decoder_DeleteAudioBuffer
* or by returning it to the caller as a pf_decode_audio return value.
*/
VLC_API block_t * decoder_NewAudioBuffer( decoder_t *, int i_size ) VLC_USED;
/**
* This function will return a new subpicture usable by a decoder as an output
* buffer. You have to release it using decoder_DeleteSubpicture or by returning
* it to the caller as a pf_decode_sub return value.
*/
VLC_API subpicture_t * decoder_NewSubpicture( decoder_t *, const subpicture_updater_t * ) VLC_USED;
/**
* This function will release a subpicture created by decoder_NewSubicture.
*/
VLC_API void decoder_DeleteSubpicture( decoder_t *, subpicture_t *p_subpicture );
/**
* This function gives all input attachments at once.
*
* You MUST release the returned values
*/
VLC_API int decoder_GetInputAttachments( decoder_t *, input_attachment_t ***ppp_attachment, int *pi_attachment );
/**
* This function converts a decoder timestamp into a display date comparable
* to mdate().
* You MUST use it *only* for gathering statistics about speed.
*/
VLC_API mtime_t decoder_GetDisplayDate( decoder_t *, mtime_t ) VLC_USED;
/**
* This function returns the current input rate.
* You MUST use it *only* for gathering statistics about speed.
*/
VLC_API int decoder_GetDisplayRate( decoder_t * ) VLC_USED;
#endif /* _VLC_CODEC_H */

Some files were not shown because too many files have changed in this diff Show more