From 842d1a72a4b00924a21819fa578f34ec46a08159 Mon Sep 17 00:00:00 2001 From: sector-a <82838084+sector-a@users.noreply.github.com> Date: Sat, 19 Apr 2025 23:11:58 +0300 Subject: [PATCH 1/7] DialogueBox tweaks for fullscreen --- .../play/cutscene/dialogue/DialogueBox.hx | 44 ++++++++++++++++--- .../funkin/play/cutscene/dialogue/Speaker.hx | 31 +++++++++++-- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/source/funkin/play/cutscene/dialogue/DialogueBox.hx b/source/funkin/play/cutscene/dialogue/DialogueBox.hx index 2d7b3b9d5..90d4d6a22 100644 --- a/source/funkin/play/cutscene/dialogue/DialogueBox.hx +++ b/source/funkin/play/cutscene/dialogue/DialogueBox.hx @@ -61,8 +61,15 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple var xDiff:Float = value[0] - globalOffsets[0]; var yDiff:Float = value[1] - globalOffsets[1]; - this.x += xDiff * FullScreenScaleMode.windowScale.x; - this.y += yDiff * FullScreenScaleMode.windowScale.y; + this.x += xDiff; + this.y += yDiff; + + if (FullScreenScaleMode.windowScale.x != 1) + { + this.x *= fullscreenScale; + this.y = this.y * fullscreenScale + (-100 * fullscreenScale); + } + return globalOffsets = value; } @@ -90,6 +97,16 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple return this.speed; } + /** + * A value used for scaling object's parameters on mobile. + */ + var fullscreenScale(get, never):Float; + + function get_fullscreenScale():Float + { + return FullScreenScaleMode.windowScale.x - 0.05; + } + public function new(id:String) { super(); @@ -190,8 +207,14 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple public function setScale(scale:Null):Void { if (scale == null) scale = 1.0; - this.boxSprite.scale.x = scale * FullScreenScaleMode.windowScale.x; - this.boxSprite.scale.y = scale * FullScreenScaleMode.windowScale.y; + + if (FullScreenScaleMode.windowScale.x != 1) + { + scale *= fullscreenScale; + } + + this.boxSprite.scale.x = scale; + this.boxSprite.scale.y = scale; this.boxSprite.updateHitbox(); } @@ -275,7 +298,7 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple function loadText():Void { textDisplay = new FlxTypeText(0, 0, 300, '', 32); - textDisplay.fieldWidth = _data.text.width * FullScreenScaleMode.windowScale.x; + textDisplay.fieldWidth = _data.text.width; textDisplay.setFormat(_data.text.fontFamily, _data.text.size, FlxColor.fromString(_data.text.color), LEFT, SHADOW, FlxColor.fromString(_data.text.shadowColor ?? '#00000000'), false); textDisplay.borderSize = _data.text.shadowWidth ?? 2; @@ -284,8 +307,15 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple textDisplay.completeCallback = onTypingComplete; - textDisplay.x += _data.text.offsets[0] * FullScreenScaleMode.windowScale.x; - textDisplay.y += _data.text.offsets[1] * FullScreenScaleMode.windowScale.y; + textDisplay.x += _data.text.offsets[0]; + textDisplay.y += _data.text.offsets[1]; + + if (FullScreenScaleMode.windowScale.x != 1) + { + textDisplay.fieldWidth *= fullscreenScale; + textDisplay.x *= fullscreenScale; + textDisplay.y *= fullscreenScale; + } add(textDisplay); } diff --git a/source/funkin/play/cutscene/dialogue/Speaker.hx b/source/funkin/play/cutscene/dialogue/Speaker.hx index fb31e7f4d..d47914fce 100644 --- a/source/funkin/play/cutscene/dialogue/Speaker.hx +++ b/source/funkin/play/cutscene/dialogue/Speaker.hx @@ -64,11 +64,28 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRe var xDiff:Float = value[0] - globalOffsets[0]; var yDiff:Float = value[1] - globalOffsets[1]; - this.x += xDiff * FullScreenScaleMode.windowScale.x; - this.y += yDiff * FullScreenScaleMode.windowScale.y; + this.x += xDiff; + this.y += yDiff; + + if (FullScreenScaleMode.windowScale.x != 1) + { + this.x *= fullscreenScale; + this.y = this.y * fullscreenScale + (-100 * fullscreenScale); + } + return globalOffsets = value; } + /** + * A value used for scaling object's parameters on mobile. + */ + var fullscreenScale(get, never):Float; + + function get_fullscreenScale():Float + { + return FullScreenScaleMode.windowScale.x - 0.05; + } + public function new(id:String) { super(); @@ -152,8 +169,14 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRe public function setScale(scale:Null):Void { if (scale == null) scale = 1.0; - this.scale.x = scale * FullScreenScaleMode.windowScale.y; - this.scale.y = scale * FullScreenScaleMode.windowScale.y; + + if (FullScreenScaleMode.windowScale.x != 1) + { + scale *= fullscreenScale; + } + + this.scale.x = scale; + this.scale.y = scale; this.updateHitbox(); } From cc7def517823032de6a1dd983b2bb3b0ad7bc82c Mon Sep 17 00:00:00 2001 From: MAJigsaw77 <77043862+MAJigsaw77@users.noreply.github.com> Date: Sun, 20 Apr 2025 00:39:14 +0300 Subject: [PATCH 2/7] Somewhat finished the IAP implementation. --- hmm.json | 2 +- .../funkin/mobile/util/InAppPurchasesUtil.hx | 155 +++++++++++++----- 2 files changed, 119 insertions(+), 38 deletions(-) diff --git a/hmm.json b/hmm.json index 4e3414219..ca6b0ae36 100644 --- a/hmm.json +++ b/hmm.json @@ -227,7 +227,7 @@ "name": "extension-iapcore", "type": "git", "dir": null, - "ref": "46a8b945fb6fbfbf3cff313e8110f62413ad286f", + "ref": "cb0195ff33903a28f483d2092a7420854ecdfdf5", "url": "https://github.com/MAJigsaw77/extension-iapcore" }, { diff --git a/source/funkin/mobile/util/InAppPurchasesUtil.hx b/source/funkin/mobile/util/InAppPurchasesUtil.hx index d4f8cab26..c0d8dc3c1 100644 --- a/source/funkin/mobile/util/InAppPurchasesUtil.hx +++ b/source/funkin/mobile/util/InAppPurchasesUtil.hx @@ -4,13 +4,12 @@ package funkin.mobile.util; import extension.iapcore.android.IAPAndroid; import extension.iapcore.android.IAPProductDetails; import extension.iapcore.android.IAPPurchase; +import extension.iapcore.android.IAPPurchaseState; import extension.iapcore.android.IAPResponseCode; +import extension.iapcore.android.IAPResult; /** - * Utility class for managing in-app purchases in a mobile application. - * This class provides functions to initialize the in-app purchase system, - * handle purchase events, and manage the purchase flow for different types - * of in-app products, including consumables and non-consumables. + * Utility class for managing in-app purchases. */ class InAppPurchasesUtil { @@ -19,10 +18,25 @@ class InAppPurchasesUtil */ static final FETCH_PRODUCT_DETAILS_IDS:Array = ['test_product_0']; + /** + * The maximum number of attempts to reconnect in case of a failure. + */ + static final MAX_RECONNECT_ATTEMPTS:Int = 3; + /** * A static variable that holds an array of currently loaded product details for in-app purchases. */ - public static var currentLoadedProductDetails:Array = []; + public static var currentProductDetails:Array = []; + + /** + * A static variable that holds an array of currently purchased for in-app purchases. + */ + public static var currentPurchased:Array = []; + + /** + * A static variable to track the number of attempts made to reconnect. + */ + static var reconnectAttempts:Int = 0; /** * Initializes the in-app purchases utility. @@ -33,42 +47,67 @@ class InAppPurchasesUtil logMessage(message); }); - IAPAndroid.onBillingSetupFinished.add(function(code:IAPResponseCode, message:String):Void { - if (message != null && message.length > 0) logMessage('IAP Setup finished with code "$code" "$message"!'); - else + IAPAndroid.onBillingSetupFinished.add(function(result:IAPResult):Void { + if (result.getResponseCode() != IAPResponseCode.OK) { - logMessage('IAP Setup finished with code "$code"!'); + logMessage('Billing setup failed "$result"!'); + return; } - if (code == IAPResponseCode.OK) - { - IAPAndroid.queryPurchases(); + reconnectAttempts = 0; - IAPAndroid.queryProductDetails(FETCH_PRODUCT_DETAILS_IDS); - } + IAPAndroid.queryPurchases(); + + IAPAndroid.queryProductDetails(FETCH_PRODUCT_DETAILS_IDS); }); IAPAndroid.onBillingServiceDisconnected.add(function():Void { - logMessage("IAP Billing service disconnected!"); - }); + logMessage("Billing service disconnected!"); - IAPAndroid.onProductDetailsResponse.add(function(code:IAPResponseCode, productDetails:Array):Void { - logMessage('IAP Product details responded with code "$code", $productDetails'); - - currentLoadedProductDetails = productDetails; - - for (productDetails in currentLoadedProductDetails) + if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) { - logMessage('IAP Product details with title: ${productDetails.getTitle()}'); + reconnectAttempts++; + + logMessage('Attempting to reconnect... ($reconnectAttempts/$MAX_RECONNECT_ATTEMPTS)'); + + IAPAndroid.startConnection(); + } + else + { + logMessage('Max reconnect attempts reached.'); } }); - IAPAndroid.onQueryPurchasesResponse.add(function(code:IAPResponseCode, purchases:Array):Void { - logMessage('IAP Query purchases responded with code "$code", $purchases'); + IAPAndroid.onProductDetailsResponse.add(function(result:IAPResult, productDetails:Array):Void { + if (result.getResponseCode() == IAPResponseCode.OK) currentProductDetails = productDetails; + else + { + logMessage('Failed to fetch product details: "$result"'); + } }); - IAPAndroid.onPurchasesUpdated.add(function(code:IAPResponseCode, purchases:Array):Void { - logMessage('IAP Purchases updated response with code "$code", $purchases'); + IAPAndroid.onQueryPurchasesResponse.add(function(result:IAPResult, purchases:Array):Void { + if (result.getResponseCode() == IAPResponseCode.OK) handlePurchases(purchases); + else + { + logMessage('Failed to query purchases: "$result"'); + } + }); + + IAPAndroid.onPurchasesUpdated.add(function(result:IAPResult, purchases:Array):Void { + if (result.getResponseCode() == IAPResponseCode.OK) handlePurchases(purchases); + else + { + logMessage('Failed to update purchases: "$result"'); + } + }); + + IAPAndroid.onAcknowledgePurchaseResponse.add(function(result:IAPResult):Void { + if (result.getResponseCode() == IAPResponseCode.OK) logMessage('Purchase acknowledged successfully!'); + else + { + logMessage('Failed to acknowledge purchase: $result'); + } }); IAPAndroid.init(); @@ -83,23 +122,36 @@ class InAppPurchasesUtil */ public static function purchase(id:String):Void { - for (product in currentLoadedProductDetails) + for (product in currentProductDetails) { if (product.getProductId() == id) { - logMessage('Lunching purchase flow for ${product.getTitle()}'); - IAPAndroid.launchPurchaseFlow(product); - return; } - else - { - logMessage('Current ${product.getProductId()} doesnt match ${id}'); - } } - logMessage("Didn\'t Found Product Details for ID: " + id); + logMessage("Didn't find product details for ID: " + id); + } + + /** + * Checks if the specified product ID is already purchased. + * + * @param id The product ID to check. + * + * @return `true` if the product is already purchased and acknowledged, false otherwise. + */ + public static function isPurchased(id:String):Bool + { + for (purchase in currentPurchased) + { + if (purchase.getProducts().contains(id)) + { + return true; + } + } + + return false; } @:noCompletion @@ -109,7 +161,36 @@ class InAppPurchasesUtil android.widget.Toast.makeText(message, android.widget.Toast.LENGTH_SHORT); #end - trace(message); + Sys.println(message); + } + + @:noCompletion + private static function handlePurchases(purchases:Array):Void + { + for (purchase in purchases) + { + if (purchase.getPurchaseState() == IAPPurchaseState.PURCHASED) + { + if (!purchase.isAcknowledged()) IAPAndroid.acknowledgePurchase(purchase.getPurchaseToken()); + + var alreadyTracked:Bool = false; + + for (existing in currentPurchased) + { + if (existing.getPurchaseToken() == purchase.getPurchaseToken()) + { + alreadyTracked = true; + break; + } + } + + if (!alreadyTracked) currentPurchased.push(purchase); + } + else + { + logMessage('Purchase not completed: ${purchase.getPurchaseState()}'); + } + } } } #end From 3cc189869bc564ecdc83d72eaa865b5cfe55d227 Mon Sep 17 00:00:00 2001 From: MAJigsaw77 <77043862+MAJigsaw77@users.noreply.github.com> Date: Sun, 20 Apr 2025 10:39:47 +0300 Subject: [PATCH 3/7] Some formatting on `InAppPurchasesUtil`. --- source/funkin/mobile/util/InAppPurchasesUtil.hx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/source/funkin/mobile/util/InAppPurchasesUtil.hx b/source/funkin/mobile/util/InAppPurchasesUtil.hx index c0d8dc3c1..1e1b9156c 100644 --- a/source/funkin/mobile/util/InAppPurchasesUtil.hx +++ b/source/funkin/mobile/util/InAppPurchasesUtil.hx @@ -171,7 +171,10 @@ class InAppPurchasesUtil { if (purchase.getPurchaseState() == IAPPurchaseState.PURCHASED) { - if (!purchase.isAcknowledged()) IAPAndroid.acknowledgePurchase(purchase.getPurchaseToken()); + if (!purchase.isAcknowledged()) + { + IAPAndroid.acknowledgePurchase(purchase.getPurchaseToken()); + } var alreadyTracked:Bool = false; @@ -184,7 +187,10 @@ class InAppPurchasesUtil } } - if (!alreadyTracked) currentPurchased.push(purchase); + if (!alreadyTracked) + { + currentPurchased.push(purchase); + } } else { From 324ef37505a7165cf3b5d313a0c7d13777dd7c33 Mon Sep 17 00:00:00 2001 From: MAJigsaw77 <77043862+MAJigsaw77@users.noreply.github.com> Date: Sun, 20 Apr 2025 10:40:44 +0300 Subject: [PATCH 4/7] Follow the notch possition on `StoryMenuState`. --- source/funkin/ui/story/StoryMenuState.hx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 46e9aa6c7..7d0ee216a 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -176,18 +176,19 @@ class StoryMenuState extends MusicBeatState updateProps(); - tracklistText = new FlxText(FlxG.width * 0.05, levelBackground.x + levelBackground.height + 100, 0, "Tracks", 32); + tracklistText = new FlxText(funkin.ui.FullScreenScaleMode.gameNotchSize.x + (FlxG.width * 0.05), levelBackground.x + levelBackground.height + 100, 0, + "Tracks", 32); tracklistText.setFormat('VCR OSD Mono', 32); tracklistText.alignment = CENTER; tracklistText.color = 0xFFE55777; add(tracklistText); - scoreText = new FlxText(10, 10, 0, 'HIGH SCORE: 42069420'); + scoreText = new FlxText(funkin.ui.FullScreenScaleMode.gameNotchSize.x + 10, 10, 0, 'HIGH SCORE: 42069420'); scoreText.setFormat('VCR OSD Mono', 32); scoreText.zIndex = 1000; add(scoreText); - levelTitleText = new FlxText(FlxG.width * 0.7, 10, 0, 'LEVEL 1'); + levelTitleText = new FlxText((FlxG.width * 0.7) - funkin.ui.FullScreenScaleMode.gameNotchSize.x, 10, 0, 'LEVEL 1'); levelTitleText.setFormat('VCR OSD Mono', 32, FlxColor.WHITE, RIGHT); levelTitleText.alpha = 0.7; levelTitleText.zIndex = 1000; @@ -195,7 +196,7 @@ class StoryMenuState extends MusicBeatState buildLevelTitles(); - leftDifficultyArrow = new FlxSprite(FlxG.width - 410, 480); + leftDifficultyArrow = new FlxSprite((FlxG.width - 410) - funkin.ui.FullScreenScaleMode.gameNotchSize.x, 480); leftDifficultyArrow.frames = Paths.getSparrowAtlas('storymenu/ui/arrows'); leftDifficultyArrow.animation.addByPrefix('idle', 'leftIdle0'); leftDifficultyArrow.animation.addByPrefix('press', 'leftConfirm0'); @@ -205,7 +206,7 @@ class StoryMenuState extends MusicBeatState buildDifficultySprite(Constants.DEFAULT_DIFFICULTY); buildDifficultySprite(); - rightDifficultyArrow = new FlxSprite(FlxG.width - 35, leftDifficultyArrow.y); + rightDifficultyArrow = new FlxSprite((FlxG.width - 35) - funkin.ui.FullScreenScaleMode.gameNotchSize.x, leftDifficultyArrow.y); rightDifficultyArrow.frames = leftDifficultyArrow.frames; rightDifficultyArrow.animation.addByPrefix('idle', 'rightIdle0'); rightDifficultyArrow.animation.addByPrefix('press', 'rightConfirm0'); @@ -325,7 +326,8 @@ class StoryMenuState extends MusicBeatState scoreText.text = 'LEVEL SCORE: ${Math.round(highScoreLerp)}'; levelTitleText.text = currentLevel.getTitle(); - levelTitleText.x = FlxG.width - (levelTitleText.width + 10); // Right align. + + levelTitleText.x = FlxG.width - (levelTitleText.width + 10) - funkin.ui.FullScreenScaleMode.gameNotchSize.x; // Right align. handleKeyPresses(); @@ -680,6 +682,7 @@ class StoryMenuState extends MusicBeatState tracklistText.screenCenter(X); tracklistText.x -= FlxG.width * 0.35; + tracklistText.x += funkin.ui.FullScreenScaleMode.gameNotchSize.x; var levelScore:Null = Save.instance.getLevelScore(currentLevelId, currentDifficultyId); highScore = levelScore?.score ?? 0; From 94cd22e243f97c399c39008fa7ef10da484de3c0 Mon Sep 17 00:00:00 2001 From: MAJigsaw77 <77043862+MAJigsaw77@users.noreply.github.com> Date: Sun, 20 Apr 2025 10:41:09 +0300 Subject: [PATCH 5/7] Follow the notch possition on `CreditsState`. --- source/funkin/ui/credits/CreditsState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/ui/credits/CreditsState.hx b/source/funkin/ui/credits/CreditsState.hx index 775336431..c7f4cdd5f 100644 --- a/source/funkin/ui/credits/CreditsState.hx +++ b/source/funkin/ui/credits/CreditsState.hx @@ -102,7 +102,7 @@ class CreditsState extends MusicBeatState // TODO: Once we need to display Kickstarter backers, // make this use a recycled pool so we don't kill peformance. creditsGroup = new FlxSpriteGroup(); - creditsGroup.x = SCREEN_PAD; + creditsGroup.x = funkin.ui.FullScreenScaleMode.gameNotchSize.x + SCREEN_PAD; creditsGroup.y = STARTING_HEIGHT; buildCreditsGroup(); From 647b69bb0c27df25c20ac852eee416b09b1fc213 Mon Sep 17 00:00:00 2001 From: MAJigsaw77 <77043862+MAJigsaw77@users.noreply.github.com> Date: Sun, 20 Apr 2025 10:42:31 +0300 Subject: [PATCH 6/7] Use `gradients` on labels. --- source/funkin/mobile/ui/FunkinHitbox.hx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/source/funkin/mobile/ui/FunkinHitbox.hx b/source/funkin/mobile/ui/FunkinHitbox.hx index 52f763c57..5c7c4994d 100644 --- a/source/funkin/mobile/ui/FunkinHitbox.hx +++ b/source/funkin/mobile/ui/FunkinHitbox.hx @@ -493,11 +493,13 @@ class FunkinHitbox extends FlxTypedSpriteGroup hint.centerOrigin(); hint.centerOffsets(); }); + hint.onUp.add(() -> { hint.animation.play('static', true); hint.centerOrigin(); hint.centerOffsets(); }); + hint.onOut.add(() -> { hint.animation.play('static', true); hint.centerOrigin(); @@ -526,7 +528,7 @@ class FunkinHitbox extends FlxTypedSpriteGroup { final matrix:Matrix = new Matrix(); matrix.createGradientBox(width, height, 0, 0, 0); - shape.graphics.beginGradientFill(RADIAL, [baseColor.to24Bit(), baseColor.to24Bit()], [0, 1], [60, 255], matrix, PAD, RGB, 0); + shape.graphics.beginGradientFill(RADIAL, [baseColor.to24Bit(), baseColor.to24Bit()], [0, baseColor.alphaFloat], [60, 255], matrix, PAD, RGB, 0); } else { @@ -544,16 +546,19 @@ class FunkinHitbox extends FlxTypedSpriteGroup function createHintLaneLabelGraphic(width:Int, height:Int, labelHeight:Int, baseColor:FlxColor = 0xFFFFFFFF):FlxGraphic { final shape:Shape = new Shape(); - shape.graphics.beginFill(0, 0); shape.graphics.drawRect(0, 0, width, height); shape.graphics.endFill(); - shape.graphics.beginFill(baseColor.to24Bit(), baseColor.alphaFloat); + final matrix:Matrix = new Matrix(); + matrix.createGradientBox(width, labelHeight, Math.PI / 2, 0, 0); + shape.graphics.beginGradientFill(LINEAR, [baseColor.to24Bit(), baseColor.to24Bit()], [baseColor.alphaFloat, 0], [0, 255], matrix); shape.graphics.drawRect(0, 0, width, labelHeight); shape.graphics.endFill(); - shape.graphics.beginFill(baseColor.to24Bit(), baseColor.alphaFloat); + final matrix:Matrix = new Matrix(); + matrix.createGradientBox(width, labelHeight, Math.PI / 2, 0, height - labelHeight); + shape.graphics.beginGradientFill(LINEAR, [baseColor.to24Bit(), baseColor.to24Bit()], [0, baseColor.alphaFloat], [0, 255], matrix); shape.graphics.drawRect(0, height - labelHeight, width, labelHeight); shape.graphics.endFill(); @@ -578,7 +583,7 @@ class FunkinHitbox extends FlxTypedSpriteGroup { final matrix:Matrix = new Matrix(); matrix.createGradientBox(width, height, 0, 0, 0); - shape.graphics.beginGradientFill(RADIAL, [baseColor.to24Bit(), baseColor.to24Bit()], [0, 1], [60, 255], matrix, PAD, RGB, 0); + shape.graphics.beginGradientFill(RADIAL, [baseColor.to24Bit(), baseColor.to24Bit()], [0, baseColor.alphaFloat], [60, 255], matrix, PAD, RGB, 0); } else { From e0e660f35700f3207253381cc949f0cde6711769 Mon Sep 17 00:00:00 2001 From: MoonDroid <81515012+moondroidcoder@users.noreply.github.com> Date: Sun, 20 Apr 2025 21:09:11 +0700 Subject: [PATCH 7/7] Update art ref. --- art | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/art b/art index 78dc31021..e5eafbad4 160000 --- a/art +++ b/art @@ -1 +1 @@ -Subproject commit 78dc310219370144719b4eeef9b3b511c5a44532 +Subproject commit e5eafbad4ae15627130ffc21077106a85b144e48