Merge branch 'funkin-mobile/main' of github.com:FunkinCrew/Funkin-secret into funkin-mobile/main

This commit is contained in:
KarimAkra
2025-04-20 22:09:59 +03:00
8 changed files with 210 additions and 62 deletions
+1 -1
Submodule art updated: 78dc310219...e5eafbad4a
+1 -1
View File
@@ -227,7 +227,7 @@
"name": "extension-iapcore",
"type": "git",
"dir": null,
"ref": "46a8b945fb6fbfbf3cff313e8110f62413ad286f",
"ref": "cb0195ff33903a28f483d2092a7420854ecdfdf5",
"url": "https://github.com/MAJigsaw77/extension-iapcore"
},
{
+10 -5
View File
@@ -493,11 +493,13 @@ class FunkinHitbox extends FlxTypedSpriteGroup<FunkinHint>
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<FunkinHint>
{
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<FunkinHint>
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<FunkinHint>
{
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
{
+124 -37
View File
@@ -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<String> = ['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<IAPProductDetails> = [];
public static var currentProductDetails:Array<IAPProductDetails> = [];
/**
* A static variable that holds an array of currently purchased for in-app purchases.
*/
public static var currentPurchased:Array<IAPPurchase> = [];
/**
* 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<IAPProductDetails>):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<IAPPurchase>):Void {
logMessage('IAP Query purchases responded with code "$code", $purchases');
IAPAndroid.onProductDetailsResponse.add(function(result:IAPResult, productDetails:Array<IAPProductDetails>):Void {
if (result.getResponseCode() == IAPResponseCode.OK) currentProductDetails = productDetails;
else
{
logMessage('Failed to fetch product details: "$result"');
}
});
IAPAndroid.onPurchasesUpdated.add(function(code:IAPResponseCode, purchases:Array<IAPPurchase>):Void {
logMessage('IAP Purchases updated response with code "$code", $purchases');
IAPAndroid.onQueryPurchasesResponse.add(function(result:IAPResult, purchases:Array<IAPPurchase>):Void {
if (result.getResponseCode() == IAPResponseCode.OK) handlePurchases(purchases);
else
{
logMessage('Failed to query purchases: "$result"');
}
});
IAPAndroid.onPurchasesUpdated.add(function(result:IAPResult, purchases:Array<IAPPurchase>):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,42 @@ 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<IAPPurchase>):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
@@ -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<Float>):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);
}
@@ -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<Float>):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();
}
+1 -1
View File
@@ -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();
+9 -6
View File
@@ -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<SaveScoreData> = Save.instance.getLevelScore(currentLevelId, currentDifficultyId);
highScore = levelScore?.score ?? 0;