diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 671d65d..4b1b85c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,9 +5,9 @@ name: Build Game on: # Triggers the workflow on push or pull request events but only for the master branch push: - branches: [ main, experimental ] + branches: [main, experimental] pull_request: - branches: [ main, experimental ] + branches: [main, experimental] # For release builds workflow_call: @@ -41,10 +41,13 @@ jobs: sudo apt-get install libvlccore-dev haxelib setup ~/haxelib haxelib install hxcpp > /dev/null --quiet + haxelib git systools https://github.com/haya3218/retools haxelib install format > nul haxe -cp ./setup -D analyzer-optimize -main Main --interp - name: Skip SScript setup mode run: echo 'oy9:showMacroty8:loopCosti25y10:includeAllfg' >> ~/settings.cocoa + - name: Rebuild systools + run: lime rebuild systools linux - name: Create Version Tag run: echo "${{github.run_id}}" > VERSION - name: Compile @@ -53,7 +56,7 @@ jobs: uses: actions/upload-artifact@v2.2.4 with: name: linuxBuild - path: 'export/release/linux/bin' + path: "export/release/linux/bin" buildWindows: runs-on: windows-latest @@ -69,12 +72,15 @@ jobs: run: | haxelib setup C:/haxelib haxelib install hxcpp > /dev/null --quiet + haxelib git systools https://github.com/haya3218/retools haxelib install format > nul haxe -cp ./setup -D analyzer-optimize -main Main --interp shell: cmd - name: Skip SScript setup mode run: echo 'oy9:showMacroty8:loopCosti25y10:includeAllfg' >> %USERPROFILE%/settings.cocoa shell: cmd + - name: Rebuild systools + run: lime rebuild systools windows - name: Create Version Tag run: echo "${{github.run_id}}" > VERSION - name: Compile @@ -99,10 +105,13 @@ jobs: run: | haxelib setup ~/haxelib haxelib install hxcpp > /dev/null --quiet + haxelib git systools https://github.com/haya3218/retools haxelib install format > nul haxe -cp ./setup -D analyzer-optimize -main Main --interp - name: Skip SScript setup mode run: echo 'oy9:showMacroty8:loopCosti25y10:includeAllfg' >> ~/settings.cocoa + - name: Rebuild systools + run: lime rebuild systools mac - name: Create Version Tag run: echo "${{github.run_id}}" > VERSION - name: Compile diff --git a/Project.xml b/Project.xml index f53ee6e..b6c55f2 100644 --- a/Project.xml +++ b/Project.xml @@ -2,7 +2,12 @@ - + + + + + + @@ -34,6 +39,10 @@ + + + @@ -90,16 +99,15 @@ + + + - - - - @@ -109,7 +117,7 @@ - + @@ -118,6 +126,7 @@ + diff --git a/source/extraflixel/FlxSprite3D.hx b/source/extraflixel/FlxSprite3D.hx new file mode 100644 index 0000000..f4b4fe6 --- /dev/null +++ b/source/extraflixel/FlxSprite3D.hx @@ -0,0 +1,156 @@ +package extraflixel; + +import flixel.math.FlxPoint; +import flixel.math.FlxAngle; +import math.VectorHelpers; +import math.Vector3; +import flixel.graphics.frames.FlxFrame.FlxFrameType; +import openfl.Vector; +import openfl.geom.ColorTransform; +import openfl.display.Shader; +import flixel.system.FlxAssets.FlxShader; + +class FlxSprite3D extends FlxSprite { + public var z:Float = 0; + + public var yaw:Float = 0; + public var pitch:Float = 0; + @:isVar + public var roll(get, set):Float = 0; + + function get_roll() return angle; + + function set_roll(val:Float) return angle = val; + + override public function draw():Void + { + checkEmptyFrame(); + + if(alpha == 0 || _frame.type == FlxFrameType.EMPTY) return; + + if(dirty) // rarely + calcFrame(useFramePixels); + + // TODO: take origin into consideration properly + var wid = frameWidth; + var hei = frameHeight; + + var halfW = wid * 0.5; + var halfH = hei * 0.5; + + var camPos = new Vector3(0, 0, 1280); + var camOrigin = new Vector3(FlxG.width / 2, FlxG.height / 2); // vertex origin + + // TODO: take origin into account properly without this bandaid fix vv + + var bandaidOrigin = FlxPoint.weak(); + bandaidOrigin.set(origin.x - halfW, origin.y - halfH); + for (camera in cameras) + { + if(!camera.visible || !camera.exists || camera.canvas == null || camera.canvas.graphics == null) + continue; + + var quad = [ + new Vector3(-halfW, -halfH, 0), + new Vector3(halfW, -halfH, 0), + new Vector3(-halfW, halfH, 0), + new Vector3(halfW, halfH, 0) + ]; + + getScreenPosition(_point, camera).subtractPoint(offset); + //_point.add(bandaidOrigin.x, bandaidOrigin.y); + var pos = new Vector3(_point.x, _point.y, z); + + for (idx => vert in quad) + { + if(flipX) vert.x *= -1; + if(flipY) vert.y *= -1; + vert.x -= bandaidOrigin.x; + vert.y -= bandaidOrigin.y; + vert.x *= scale.x; + vert.y *= scale.y; + + var vert = VectorHelpers.rotateV3(vert, FlxAngle.TO_RAD * pitch, FlxAngle.TO_RAD * yaw, FlxAngle.TO_RAD * roll); + var originMod = vert.add(pos).subtract(camOrigin); + var projected = VectorHelpers.project(originMod.subtract(camPos)); + vert = projected.subtract(pos).add(camOrigin); // puts the vertex back to default pos + + vert.x += bandaidOrigin.x; + vert.y += bandaidOrigin.y; + + quad[idx] = vert; + + } + var frameRect = frame.frame; + var sourceBitmap = graphic.bitmap; + + var leftUV = frameRect.left / sourceBitmap.width; + var rightUV = frameRect.right / sourceBitmap.width; + var topUV = frameRect.top / sourceBitmap.height; + var bottomUV = frameRect.bottom / sourceBitmap.height; + + // order should be LT, RT, RB, LT, LB, RB + // R is right L is left T is top B is bottom + // order matters! so LT is left, top because they're represented as x, y + var vertices = new Vector(12, false, [ + quad[0].x, quad[0].y, + quad[1].x, quad[1].y, + quad[3].x, quad[3].y, + + quad[0].x, quad[0].y, + quad[2].x, quad[2].y, + quad[3].x, quad[3].y + ]); + + var uvData = new Vector(12, false, [ + leftUV, topUV, + rightUV, topUV, + rightUV, bottomUV, + + leftUV, topUV, + leftUV, bottomUV, + rightUV, bottomUV, + ]); + + var shader = this.shader != null ? this.shader : new FlxShader(); + if (this.shader != shader) + this.shader = shader; + + shader.bitmap.input = graphic.bitmap; + shader.bitmap.filter = antialiasing ? LINEAR : NEAREST; + + + var transforms:Array = []; + var transfarm:ColorTransform = new ColorTransform(); + transfarm.redMultiplier = colorTransform.redMultiplier; + transfarm.greenMultiplier = colorTransform.greenMultiplier; + transfarm.blueMultiplier = colorTransform.blueMultiplier; + transfarm.redOffset = colorTransform.redOffset; + transfarm.greenOffset = colorTransform.greenOffset; + transfarm.blueOffset = colorTransform.blueOffset; + transfarm.alphaOffset = colorTransform.alphaOffset; + transfarm.alphaMultiplier = colorTransform.alphaMultiplier * camera.alpha; + + for (n in 0...Std.int(vertices.length / 3)) transforms.push(transfarm); + + + var indices = new Vector(vertices.length, false, cast [for (i in 0...vertices.length) i]); + var drawItem = camera.startTrianglesBatch(graphic, antialiasing, true, null, true, shader); + + @:privateAccess + { + drawItem.addTrianglesColorArray(vertices, indices, uvData, null, _point, camera._bounds, transforms); + } + + #if FLX_DEBUG + FlxBasic.visibleCount++; + #end + } + bandaidOrigin.putWeak(); + + + #if FLX_DEBUG + if(FlxG.debugger.drawDebug) drawDebug(); + #end + } +} \ No newline at end of file diff --git a/source/math/Vector3.hx b/source/math/Vector3.hx new file mode 100644 index 0000000..11fbd0a --- /dev/null +++ b/source/math/Vector3.hx @@ -0,0 +1,335 @@ +package math; + +// modified from lime.math.Vector4 + +/** + `Vector3` is a vector suitable for three-dimensional + math, containing (x, y, z) components +**/ +#if !lime_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +class Vector3 +{ + /** + A constant representing the x axis (1, 0, 0) + **/ + public static var X_AXIS(get, never):Vector3; + + /** + A constant representing the y axis (0, 1, 0) + **/ + public static var Y_AXIS(get, never):Vector3; + + /** + A constant representing the z axis (0, 0, 1) + **/ + public static var Z_AXIS(get, never):Vector3; + + /** + Get the length of this vector + **/ + public var length(get, never):Float; + + /** + Get the squared length of this vector + (avoiding the use of `Math.sqrt` for faster + performance) + **/ + public var lengthSquared(get, never):Float; + + /** + The x component value + **/ + public var x:Float; + + /** + The y component value + **/ + public var y:Float; + + /** + The z component value + **/ + public var z:Float; + + /** + Creates a new `Vector3` instance + @param x (Optional) An initial x value (default is 0) + @param y (Optional) An initial y value (default is 0) + @param z (Optional) An initial z value (default is 0) + **/ + public function new(x:Float = 0., y:Float = 0., z:Float = 0.) + { + this.x = x; + this.y = y; + this.z = z; + } + + /** + Adds two `Vector3` instances together and returns the result + @param a A `Vector3` instance to add to the current one + @param result (Optional) A `Vector3` instance to store the result + @return A `Vector3` instance with the added value + **/ + public inline function add(a:Vector3, result:Vector3 = null):Vector3 + { + if (result == null) result = new Vector3(); + result.setTo(this.x + a.x, this.y + a.y, this.z + a.z); + return result; + } + + /** + Calculates the angle between two `Vector3` coordinates + @param a A `Vector3` instance + @param b A second `Vector3` instance + @return The calculated angle + **/ + public static inline function angleBetween(a:Vector3, b:Vector3):Float + { + var a0 = a.clone(); + a0.normalize(); + var b0 = b.clone(); + b0.normalize(); + + return Math.acos(a0.dotProduct(b0)); + } + + /** + Creates a new `Vector3` instance with the same values as the current one + @return A new `Vector3` instance with the same values + **/ + public inline function clone():Vector3 + { + return new Vector3(x, y, z); + } + + /** + Creates a new `Vector3` instance linearly interpolated between this Vector3 and the given goal by the given alpha + @param goal A `Vector3` instance to interpolate towards + @param alpha How far the interpolation is + @return A `Vector3 instance linearly interpolated` + **/ + + //https://gamedev.stackexchange.com/questions/18615/how-do-i-linearly-interpolate-between-two-vectors + public function lerp(goal:Vector3, alpha:Float):Vector3{ + return new Vector3( + alpha*goal.x + x*(1-alpha), + alpha*goal.y + y*(1-alpha), + alpha*goal.z + z*(1-alpha) + ); + } + + + /** + Copies the x, y and z component values of another `Vector3` instance + @param sourceVector3 A `Vector3` instance to copy from + **/ + public inline function copyFrom(sourceVector3:Vector3):Void + { + x = sourceVector3.x; + y = sourceVector3.y; + z = sourceVector3.z; + } + + /** + Performs vector multiplication between this vector and another `Vector3` instance + @param a A `Vector3` instance to multiply by + @param result (Optional) A `Vector3` to use for the result + @return A `Vector3` instance with the result + **/ + public inline function crossProduct(a:Vector3, result:Vector3 = null):Vector3 + { + if (result == null) result = new Vector3(); + result.setTo(y * a.z - z * a.y, z * a.x - x * a.z, x * a.y - y * a.x); + return result; + } + + /** + Decrements the x, y and z component values by those in another `Vector3` instance + @param a A `Vector3` instance to decrement the current vector by + **/ + public inline function decrementBy(a:Vector3):Void + { + x -= a.x; + y -= a.y; + z -= a.z; + } + + /** + Calculates the distance between two vectors + @param pt1 A `Vector3` instance + @param pt2 A second `Vector3` instance + @return The distance between each vector + **/ + public inline static function distance(pt1:Vector3, pt2:Vector3):Float + { + var x = pt2.x - pt1.x; + var y = pt2.y - pt1.y; + var z = pt2.z - pt1.z; + + return Math.sqrt(x * x + y * y + z * z); + } + + /** + Calculates the squared distance between two vectors, + (avoids the use of `Math.sqrt` for faster performance) + @param pt1 A `Vector3` instance + @param pt2 A second `Vector3` instance + @return The square of the distance between each vector + **/ + public inline static function distanceSquared(pt1:Vector3, pt2:Vector3):Float + { + var x = pt2.x - pt1.x; + var y = pt2.y - pt1.y; + var z = pt2.z - pt1.z; + + return x * x + y * y + z * z; + } + + /** + Calculates the dot product of the current vector with another `Vector3` instance + @param a A `Vector3` instance to use in the dot product + @return The calculated dot product value + **/ + public inline function dotProduct(a:Vector3):Float + { + return x * a.x + y * a.y + z * a.z; + } + + /** + Whether two `Vector3` instances have equal component values. + + Comparing the w component value is optional. + @param toCompare A `Vector3` instance to compare against + @return Whether both instances have equal values + **/ + public inline function equals(toCompare:Vector3):Bool + { + return x == toCompare.x && y == toCompare.y && z == toCompare.z; + } + + /** + Increments the x, y and z component values by those in a second `Vector3` instance + @param a A `Vector3` instance to increment the current vector by + **/ + public inline function incrementBy(a:Vector3):Void + { + x += a.x; + y += a.y; + z += a.z; + } + + /** + Whether two `Vector3` instances have nearly equal component values. + Comparison is performed within a given tolerance value. + @param toCompare A `Vector3` instance to compare against + @param tolerance A floating point value determining how near the values must be to be considered near equal + @return Whether both instances have equal values, within the given tolerance + **/ + public inline function nearEquals(toCompare:Vector3, tolerance:Float):Bool + { + return Math.abs(x - toCompare.x) < tolerance + && Math.abs(y - toCompare.y) < tolerance + && Math.abs(z - toCompare.z) < tolerance; + } + + /** + Negates the x, y and z values of the current vector + (multiplying each value by -1) + **/ + public inline function negate():Void + { + x *= -1; + y *= -1; + z *= -1; + } + + /** + Divides the x, y and z component values by the + length of the vector + **/ + public inline function normalize():Float + { + var l = length; + + if (l != 0) + { + x /= l; + y /= l; + z /= l; + } + + return l; + } + + /** + Scales the x, y and z component values by a scale value + @param s The amount of scale to apply + **/ + public inline function scaleBy(s:Float):Void + { + x *= s; + y *= s; + z *= s; + } + + /** + Sets the x, y and z component values + @param xa An x value + @param ya A y value + @param za A z value + **/ + public inline function setTo(xa:Float, ya:Float, za:Float):Void + { + x = xa; + y = ya; + z = za; + } + + /** + Subtracts the values of a second `Vector3` instance + from the current one + @param a A second `Vector3` instance to substract + @param result (Optional) A `Vector3` instance to store the result + @return A `Vector3` instance containing the subtracted value + **/ + public inline function subtract(a:Vector3, result:Vector3 = null):Vector3 + { + if (result == null) result = new Vector3(); + result.setTo(x - a.x, y - a.y, z - a.z); + return result; + } + + @:dox(hide) public inline function toString():String + { + return "Vector3(" + x + ", " + y + ", " + z + ")"; + } + + // Getters & Setters + @:noCompletion private inline function get_length():Float + { + return Math.sqrt(x * x + y * y + z * z); + } + + @:noCompletion private inline function get_lengthSquared():Float + { + return x * x + y * y + z * z; + } + + private inline static function get_X_AXIS():Vector3 + { + return new Vector3(1, 0, 0); + } + + private inline static function get_Y_AXIS():Vector3 + { + return new Vector3(0, 1, 0); + } + + private inline static function get_Z_AXIS():Vector3 + { + return new Vector3(0, 0, 1); + } +} \ No newline at end of file diff --git a/source/math/VectorHelpers.hx b/source/math/VectorHelpers.hx new file mode 100644 index 0000000..a082790 --- /dev/null +++ b/source/math/VectorHelpers.hx @@ -0,0 +1,53 @@ +package math; + +import flixel.math.FlxMath; +import math.Vector3; + +class VectorHelpers { + static var near = 0; + static var far = 2; + + static function FastTan(rad:Float) // thanks schmoovin + { + return FlxMath.fastSin(rad) / FlxMath.fastCos(rad); + } + + // thanks schmoovin' + public static function rotateV3(vec:Vector3, xA:Float, yA:Float, zA:Float):Vector3 + { + var rotateZ = CoolUtil.rotate(vec.x, vec.y, zA); + var rotateY = CoolUtil.rotate(rotateZ.x, vec.z, yA); + var rotateX = CoolUtil.rotate(rotateY.y, rotateZ.y, xA); + var returnedVector = new Vector3(rotateY.x, rotateX.y, rotateX.x); + + rotateZ.putWeak(); + rotateX.putWeak(); + rotateY.putWeak(); + + return returnedVector; + } + + public static function project(pos:Vector3):Vector3 + { + var oX = pos.x; + var oY = pos.y; + + var aspect = 1; + + var shit = pos.z / 1280; + if (shit > 0) + shit = 0; + + var fov = (Math.PI / 2); + var ta = FastTan(fov / 2); + var x = oX * aspect / ta; + var y = oY / ta; + var a = (near + far) / (near - far); + var b = 2 * near * far / (near - far); + var z = (a * shit + b); + var returnedVector = new Vector3(x / z, y / z, z); + + return returnedVector; + } + +} \ No newline at end of file diff --git a/source/openfl/display/FPS.hx b/source/openfl/display/FPS.hx index bb0bf5b..4c22dd4 100644 --- a/source/openfl/display/FPS.hx +++ b/source/openfl/display/FPS.hx @@ -1,4 +1,3 @@ - package openfl.display; import haxe.Timer; @@ -99,7 +98,11 @@ class FPS extends TextField #end text += '\nPsych Engine 0.7.2 [CUSTOM BUILD]'; + #if STORY_EDITION + text += '\nFNF Vs. Foxa 3.0: Story Edition'; + #else text += '\nFNF Vs. Foxa 3.0'; + #end if (text != null || text != '') {if(Main.fpsVar != null) Main.fpsVar.visible = true;} diff --git a/source/states/LoadingState.hx b/source/states/LoadingState.hx index 6c5829e..0ca4cd0 100644 --- a/source/states/LoadingState.hx +++ b/source/states/LoadingState.hx @@ -75,6 +75,8 @@ class LoadingState extends MusicBeatState "potato\nwaterslide", "GingerBrave\nMore like", "Foxa hates Twilight Sparkle :D", + "I just wanna warn you, we're always fashionably early.", + "Please stand by.", "I love to smash my keyboard.", "There might be someone out there that's thinking about making a mod about you.\nKeep that in mind.", "Funkin' Forever.", diff --git a/source/states/editors/CharacterEditorState.hx b/source/states/editors/CharacterEditorState.hx index b412cec..a4ad825 100644 --- a/source/states/editors/CharacterEditorState.hx +++ b/source/states/editors/CharacterEditorState.hx @@ -49,11 +49,14 @@ class CharacterEditorState extends MusicBeatState var UI_box:FlxUITabMenu; var UI_characterbox:FlxUITabMenu; + var testMode:Bool = false; + private var camEditor:FlxCamera; private var camHUD:FlxCamera; private var camMenu:FlxCamera; var changeBGbutton:FlxButton; + var testModeButton:FlxButton; var leHealthIcon:HealthIcon; var characterList:Array = []; @@ -90,7 +93,13 @@ class CharacterEditorState extends MusicBeatState cameraFollowPointer.color = FlxColor.WHITE; add(cameraFollowPointer); - changeBGbutton = new FlxButton(FlxG.width - 360, 25, "", function() + testModeButton = new FlxButton(FlxG.width - 360, 25, "Test OFF", () -> + { + testMode = !testMode; + testModeButton.text = (testMode = !testMode) ? "Test ON" : "Test OFF"; + }); + testModeButton.cameras = [camMenu]; + changeBGbutton = new FlxButton(FlxG.width - 360, 25, "", () -> { onPixelBG = !onPixelBG; reloadBGs(); @@ -175,6 +184,7 @@ class CharacterEditorState extends MusicBeatState add(UI_characterbox); add(UI_box); add(changeBGbutton); + add(testModeButton); //addOffsetsUI(); addSettingsUI(); @@ -1084,8 +1094,14 @@ class CharacterEditorState extends MusicBeatState } ClientPrefs.toggleVolumeKeys(true); - if(!charDropDown.dropPanel.visible) { - if (FlxG.keys.justPressed.ESCAPE) { + if(testMode) { + if(controls.NOTE_LEFT_P) char.playAnim("singLEFT", true); + if(controls.NOTE_RIGHT_P)char.playAnim("singRIGHT", true); + if (controls.NOTE_DOWN_P)char.playAnim("singDOWN", true); + if(controls.NOTE_UP_P) char.playAnim("singUP", true); + } + else if(!charDropDown.dropPanel.visible) { + if(FlxG.keys.justPressed.ESCAPE) { if(goToPlayState) { MusicBeatState.switchState(new PlayState()); } else {