Initial commit: Unity WordConnect project

This commit is contained in:
2025-08-01 19:12:05 +08:00
commit f14db75802
3503 changed files with 448337 additions and 0 deletions

View File

@ -0,0 +1,78 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.UI;
using WordsToolkit.Scripts.Audio;
namespace WordsToolkit.Scripts.Popups
{
public class AudioSettingsUI : MonoBehaviour
{
[SerializeField]
private Slider musicButton;
[SerializeField]
private Slider soundButton;
[SerializeField]
private AudioMixer mixer;
[SerializeField]
private string musicParameter = "musicVolume";
[SerializeField]
private string soundParameter = "soundVolume";
private void Start()
{
musicButton.onValueChanged.AddListener(ToggleMusic);
soundButton.onValueChanged.AddListener(ToggleSound);
OnEnable();
}
private void OnEnable()
{
UpdateButtonState("Music", musicParameter);
UpdateButtonState("Sound", soundParameter);
}
private void UpdateButtonState(string playerPrefKey, string volumeParameter)
{
var enabledState = PlayerPrefs.GetInt(playerPrefKey, 1) != 0f;
float volumeValue = enabledState ? 0 : -80;
mixer.SetFloat(volumeParameter, volumeValue);
if (playerPrefKey == "Sound")
{
soundButton.value = enabledState ? 1 : 0;
}
else
{
musicButton.value = enabledState ? 1 : 0;
}
}
private void ToggleMusic(float arg0)
{
PlayerPrefs.SetInt("Music", (int)arg0);
OnEnable();
}
private void ToggleSound(float arg0)
{
PlayerPrefs.SetInt("Sound", (int)arg0);
OnEnable();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4884d074b935462cab770ad5d3b1e12d
timeCreated: 1725698039

View File

@ -0,0 +1,32 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System.Collections;
using UnityEngine;
namespace WordsToolkit.Scripts.Popups
{
public class Banner : Popup
{
public override void AfterShowAnimation()
{
base.AfterShowAnimation();
StartCoroutine(Wait());
}
protected virtual IEnumerator Wait()
{
yield return new WaitForSeconds(1.8f);
Close();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0842f95857d34b26adc08a0a1fd9f297
timeCreated: 1729076105

View File

@ -0,0 +1,98 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System.Linq;
using UnityEngine;
using UnityEngine.Serialization;
using WordsToolkit.Scripts.Audio;
using WordsToolkit.Scripts.Data;
using WordsToolkit.Scripts.Enums;
using WordsToolkit.Scripts.GUI;
using WordsToolkit.Scripts.GUI.Labels;
using WordsToolkit.Scripts.Services;
using WordsToolkit.Scripts.Services.IAP;
using WordsToolkit.Scripts.Settings;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.Popups
{
public class CoinsShop : PopupWithCurrencyLabel
{
public ItemPurchase[] packs;
private CoinsShopSettings shopSettings;
public ProductID noAdsProduct;
[SerializeField]
private AudioClip coinsSound;
private void OnEnable()
{
shopSettings = Resources.Load<CoinsShopSettings>("Settings/CoinsShopSettings");
foreach (var itemPurchase in packs)
{
if (shopSettings.coinsProducts.Count > 0)
{
var productID = itemPurchase.productID;
if (shopSettings.coinsProducts.TryToGetPair(kvp => kvp.Key == productID, out var settingsShopItem))
{
itemPurchase.Init(settingsShopItem, iapManager);
}
}
}
EventManager.GetEvent<string>(EGameEvent.PurchaseSucceeded).Subscribe(PurchaseSucceded);
}
private void OnDisable()
{
EventManager.GetEvent<string>(EGameEvent.PurchaseSucceeded).Unsubscribe(PurchaseSucceded);
}
private void PurchaseSucceded(string id)
{
var shopItem = packs.First(i => i.productID.ID == id);
var count = shopItem.settingsShopItem.Value;
shopItem.resource.AddAnimated(count, shopItem.BuyItemButton.transform.position, animationSourceObject: null, callback: () =>
{
GetComponentInParent<Popup>().CloseDelay();
});
// If the item is non-consumable, mark it as purchased
if (shopItem.productID.productType == ProductTypeWrapper.ProductType.NonConsumable)
{
PlayerPrefs.SetInt("Purchased_" + id, 1);
PlayerPrefs.Save();
// Disable the button for this item
var pack = shopItem;
if (pack.BuyItemButton != null)
{
pack.BuyItemButton.interactable = false;
}
}
if (id == noAdsProduct.ID)
{
adsManager.RemoveAds();
Close();
}
}
public void BuyCoins(string id)
{
// StopInteration();
#if UNITY_WEBGL
gameManager.PurchaseSucceeded(id);
#else
iapManager.BuyProduct(id);
#endif
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0a33f6f1229248adabe29300449199f1
timeCreated: 1725686485

View File

@ -0,0 +1,43 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using WordsToolkit.Scripts.GUI.Buttons;
namespace WordsToolkit.Scripts.Popups
{
public class ComingSoon : Popup
{
public CustomButton mainMenuButton;
protected override void Awake()
{
base.Awake();
if (mainMenuButton != null)
{
mainMenuButton.onClick.AddListener(() =>
{
gameManager.MainMenu();
Close();
});
}
if (closeButton != null)
{
closeButton.onClick.AddListener(() =>
{
gameManager.MainMenu();
Close();
});
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7d297a58e25c40c59b0c113a9cdc59e7
timeCreated: 1753958487

View File

@ -0,0 +1,44 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using WordsToolkit.Scripts.GUI;
using WordsToolkit.Scripts.GUI.Buttons;
namespace WordsToolkit.Scripts.Popups
{
public class Confirmation : Popup
{
public CustomButton yesButton;
private void OnEnable()
{
yesButton.onClick.AddListener(Yes);
closeButton.onClick.AddListener(No);
}
private void No()
{
StopInteration();
result = EPopupResult.No;
Close();
}
private void Yes()
{
StopInteration();
result = EPopupResult.Yes;
Close();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 683c1c93877244efba6c4bddd411a3bb
timeCreated: 1725698320

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9a5d84ad0b57045aa840b42147a0e766
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,217 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System;
using System.Linq;
using UnityEngine;
using WordsToolkit.Scripts.Settings;
namespace WordsToolkit.Scripts.Popups.Daily
{
public class DailyBonus : PopupWithCurrencyLabel
{
[SerializeField]
public DayHandle[] dayHandles;
// Instance of the custom scriptable object that stores daily bonus settings
private DailyBonusSettings settings;
private int rewardStreak;
public DayHandle[] daysPrefabs;
public Transform dayHandlesParent;
private int days;
// This method is automatically called when the script becomes enabled
public void OnEnable()
{
// Load daily bonus settings
settings = LoadSettings("Settings/DailyBonusSettings");
days = settings.rewards.Length;
// Update the reward streak count and store it
rewardStreak = UpdateRewardStreak();
// Update each day handle based on the current reward streak
UpdateDayHandles(rewardStreak);
}
// Loads and returns daily bonus settings stored at the specified path
public DailyBonusSettings LoadSettings(string path)
{
return Resources.Load<DailyBonusSettings>(path);
}
// Checks the last reward date and the current date
// to determine and update the reward streak
public int UpdateRewardStreak()
{
var today = DateTime.Today;
var lastRewardDate = DateTime.Parse(PlayerPrefs.GetString("DailyBonusDay", today.Subtract(TimeSpan.FromDays(1)).ToString()));
if (today > lastRewardDate)
{
var rewardStreak = GetRewardStreak() + 1;
PlayerPrefs.SetString("DailyBonusDay", today.ToString());
PlayerPrefs.SetInt("RewardStreak", rewardStreak = (int)Mathf.Repeat(rewardStreak, dayHandles.Length));
return rewardStreak;
}
return GetRewardStreak();
}
// Updates the status of each day handle in the scene
// according to the current reward streak
public void UpdateDayHandles(int rewardStreak)
{
dayHandles = new DayHandle[days];
for (var i = 0; i < days; i++)
{
var status = i < rewardStreak ? EDailyStatus.passed : i == rewardStreak ? EDailyStatus.current : EDailyStatus.locked;
var dayHandle = Instantiate(daysPrefabs[(int)status], dayHandlesParent);
dayHandles[i] = dayHandle;
dayHandle.name = $"Day_{i + 1}";
dayHandle.SetDay(i + 1, settings.rewards[i]);
// Set status after setting the day to ensure proper icon display
dayHandle.SetStatus(status);
}
}
// Gets and returns the reward streak count from player preferences
public int GetRewardStreak()
{
return PlayerPrefs.GetInt("RewardStreak", -1);
}
public override void Close()
{
StopInteration();
closeButton.interactable = false;
var resource = dayHandles[rewardStreak].RewardData.resource;
var dayHandle = dayHandles.First(i => i.DailyStatus == EDailyStatus.current);
resource.AddAnimated(dayHandles[rewardStreak].RewardData.count, dayHandle.transform.position, animationSourceObject: null, callback: () =>
{
base.Close();
});
}
#if UNITY_EDITOR
[ContextMenu("Test - Reset Daily Bonus")]
private void TestResetDailyBonus()
{
CleanupDayHandles();
PlayerPrefs.DeleteKey("DailyBonusDay");
PlayerPrefs.DeleteKey("RewardStreak");
Debug.Log("Daily bonus data reset!");
if (Application.isPlaying)
{
OnEnable();
}
}
[ContextMenu("Test - Set Day 1")]
private void TestSetDay1()
{
SetTestDay(0);
}
[ContextMenu("Test - Set Day 2")]
private void TestSetDay2()
{
SetTestDay(1);
}
[ContextMenu("Test - Set Day 3")]
private void TestSetDay3()
{
SetTestDay(2);
}
[ContextMenu("Test - Set Day 4")]
private void TestSetDay4()
{
SetTestDay(3);
}
[ContextMenu("Test - Set Day 5")]
private void TestSetDay5()
{
SetTestDay(4);
}
[ContextMenu("Test - Set Day 6")]
private void TestSetDay6()
{
SetTestDay(5);
}
[ContextMenu("Test - Set Day 7")]
private void TestSetDay7()
{
SetTestDay(6);
}
private void SetTestDay(int day)
{
if (settings == null)
{
settings = LoadSettings("Settings/DailyBonusSettings");
}
if (settings != null && day < settings.rewards.Length)
{
// Clean up existing day handles before setting new ones
CleanupDayHandles();
PlayerPrefs.SetInt("RewardStreak", day);
PlayerPrefs.SetString("DailyBonusDay", DateTime.Today.ToString());
Debug.Log($"Set daily bonus to day {day + 1}");
if (Application.isPlaying)
{
OnEnable();
}
}
else
{
Debug.LogWarning($"Cannot set day {day + 1}. Settings not found or day out of range.");
}
}
private void CleanupDayHandles()
{
if (dayHandles != null)
{
for (int i = 0; i < dayHandles.Length; i++)
{
if (dayHandles[i] != null)
{
if (Application.isPlaying)
{
Destroy(dayHandles[i].gameObject);
}
else
{
DestroyImmediate(dayHandles[i].gameObject);
}
}
}
dayHandles = null;
}
}
#endif
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d11f98927948741d98f1a26c78ec810a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,65 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using WordsToolkit.Scripts.Settings;
namespace WordsToolkit.Scripts.Popups.Daily
{
public class DayHandle : MonoBehaviour
{
[SerializeField]
private TextMeshProUGUI dayText;
[SerializeField]
private TextMeshProUGUI coinsCountText;
[SerializeField] private GameObject rewardIcon;
[SerializeField]
private GameObject lights;
public EDailyStatus DailyStatus { get; private set; }
public RewardSetting RewardData { get; set; }
public void SetDay(int day, RewardSetting rewardSetting)
{
dayText.text = dayText.text + " " + day;
coinsCountText.text = rewardSetting.count.ToString();
RewardData = rewardSetting;
var image = rewardIcon.GetComponent<Image>();
image.sprite = rewardSetting.icon;
image.SetNativeSize();
lights.SetActive(rewardSetting.isLight);
}
public void SetStatus(EDailyStatus eDailyStatus)
{
DailyStatus = eDailyStatus;
// Update the reward icon based on status
if (eDailyStatus == EDailyStatus.locked && RewardData != null && RewardData.iconGreyed != null)
{
rewardIcon.GetComponent<Image>().sprite = RewardData.iconGreyed;
}
var list = GetComponentsInChildren<DayToggle>();
foreach (var dayToggle in list)
{
dayToggle.SetStatus(eDailyStatus);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6e7ff1621e23d4d8ba89656620d4c7de
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,42 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEngine;
using UnityEngine.UI;
namespace WordsToolkit.Scripts.Popups.Daily
{
public class DayToggle : MonoBehaviour
{
public Image panelImage;
public Color colorCurrent;
[SerializeField]
public GameObject current;
public void SetStatus(EDailyStatus eDailyStatus)
{
if (eDailyStatus == EDailyStatus.current)
{
current.SetActive(true);
panelImage.color = colorCurrent;
}
}
}
public enum EDailyStatus
{
locked = 0,
passed = 1,
current = 2
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d42cb9a76f3954136ac19f1d0c1f54e5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,23 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
namespace WordsToolkit.Scripts.Popups
{
public enum EPopupResult
{
Yes,
No,
Continue,
Cancel,
Restart
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b1521b5741b84f0b88aac61775304725
timeCreated: 1725685884

View File

@ -0,0 +1,336 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using WordsToolkit.Scripts.Data;
using WordsToolkit.Scripts.Enums;
using WordsToolkit.Scripts.GUI.Buttons;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.Popups
{
public class ExtraWords : Popup
{
[Header("Word Display")]
// Format for displaying each word in the list
[Tooltip("Format for each word in the list. Use {0} for the word.")]
public string wordFormat = "• {0}";
// Separator between words
[Tooltip("String to separate words in the list")]
public string separator = "\n";
[Header("Font Size")]
[Tooltip("Fixed font size for all text")]
[Range(8, 72)]
public float fixedFontSize = 60f;
[Tooltip("Enable font size adjustment to fit container")]
public bool autoFitContainer = true;
[Header("Columns")]
[Tooltip("List of pre-existing TextMeshPro components to use as columns")]
public TextMeshProUGUI[] columnTextObjects;
[Tooltip("Maximum lines per column (0 = unlimited)")]
[Range(0, 30)]
public int maxLinesPerColumn = 6;
[Tooltip("Line spacing multiplier")]
[Range(0.5f, 2f)]
public float lineSpacingMultiplier = 1f;
[Header("Rewards")]
[Tooltip("Button to claim gems for found extra words")]
public CustomButton claimButton;
[Tooltip("Text to show total gems to be claimed")]
public TextMeshProUGUI rewardText;
[Tooltip("Reference to the Gems resource")]
public ResourceObject gemsResource;
private bool hasClaimedRewards = false;
[SerializeField]
private Transform startAnimationTransform;
protected void OnEnable()
{
// Configure text components on enable
SetupTextComponents();
UpdateExtraWordsDisplay();
SetupClaimButton();
}
private void SetupTextComponents()
{
// Apply settings to existing text components
if (columnTextObjects == null || columnTextObjects.Length == 0)
return;
foreach (var text in columnTextObjects)
{
if (text == null)
continue;
// Configure text formatting
text.enableWordWrapping = true;
text.overflowMode = TextOverflowModes.Ellipsis;
// Set font size with auto-fitting options
if (autoFitContainer)
{
text.enableAutoSizing = true;
text.fontSizeMax = fixedFontSize;
text.fontSizeMin = fixedFontSize * 0.5f;
}
else
{
text.fontSize = fixedFontSize;
text.enableAutoSizing = false;
}
// Clear any existing content
text.text = string.Empty;
// Hide all initially
text.gameObject.SetActive(false);
}
}
private void SetupClaimButton()
{
if (claimButton != null)
{
// Reset claim state
hasClaimedRewards = false;
// Setup button click handler
claimButton.onClick.RemoveAllListeners();
claimButton.onClick.AddListener(ClaimExtraWordRewards);
// Get words list
List<string> words = GetWordsList();
// Calculate total reward
int totalGems = gameSettings.gemsForExtraWords;
// Show total reward or hide button if no rewards
int targetExtraWords = GetTargetExtraWordsFromGroup();
if (PlayerPrefs.GetInt("ExtraWordsCollected",0) >= targetExtraWords)
{
claimButton.gameObject.SetActive(true);
// Update reward text if available
if (rewardText != null)
{
rewardText.text = totalGems.ToString();
}
}
else
{
claimButton.gameObject.SetActive(false);
}
}
}
private void ClaimExtraWordRewards()
{
if (hasClaimedRewards)
return;
int totalGems = gameSettings.gemsForExtraWords;
if (totalGems <= 0)
return;
// Mark as claimed
hasClaimedRewards = true;
PlayerPrefs.SetInt("ExtraWordsCollected", 0); // Reset count after claiming
EventManager.GetEvent(EGameEvent.ExtraWordClaimed).Invoke();
// Use manually assigned reference or fall back to ResourceManager if not assigned
var gems = gemsResource != null ? gemsResource : resourceManager.GetResource("Gems");
// Add reward with animation
if (gems != null && claimButton != null)
{
gems.AddAnimated( totalGems, startAnimationTransform.position, animationSourceObject: null, callback: () => {
claimButton.gameObject.SetActive(false);
Close();
});
}
}
#if UNITY_EDITOR
private void Update()
{
// F5 to add a test word
if (Keyboard.current != null && Keyboard.current.f5Key.wasPressedThisFrame)
{
AddTestWord();
}
}
#endif
// Context menu item for testing reward claim in the Unity editor
[ContextMenu("Test Reward Claim")]
private void TestRewardClaim()
{
// Reset claim state to ensure we can claim again
hasClaimedRewards = false;
// Make sure the claim button is visible
if (claimButton != null)
claimButton.gameObject.SetActive(true);
// Set up reward text if available
if (rewardText != null)
{
int testAmount = gameSettings.gemsForExtraWords;
rewardText.text = testAmount.ToString();
}
// Call the claim method to test the full reward process
ClaimExtraWordRewards();
}
// Context menu item for adding a test word
[ContextMenu("Add Test Word")]
private void AddTestWord()
{
if (customWordRepository != null)
{
customWordRepository.AddExtraWord("blablabla " + Random.Range(1, 1000));
UpdateExtraWordsDisplay();
SetupClaimButton();
}
}
// Updates the text components with current extra words
public void UpdateExtraWordsDisplay()
{
// Get word list from game
List<string> words = GetWordsList();
// Early exit if no columns are assigned
if (columnTextObjects == null || columnTextObjects.Length == 0)
return;
// If no words found, display message in first column
if (words.Count == 0)
{
return;
}
// Otherwise, distribute words among columns
DistributeWordsToColumns(words);
}
private void DistributeWordsToColumns(List<string> words)
{
// Count valid columns
int validColumnCount = 0;
foreach (var col in columnTextObjects)
{
if (col != null)
validColumnCount++;
}
if (validColumnCount == 0)
return;
int currentColumn = 0;
int wordIndex = 0;
// First, disable all columns
foreach (var col in columnTextObjects)
{
if (col != null)
col.gameObject.SetActive(false);
}
while (wordIndex < words.Count && currentColumn < columnTextObjects.Length)
{
TextMeshProUGUI col = columnTextObjects[currentColumn];
if (col == null)
{
currentColumn++;
continue;
}
// Show this column
col.gameObject.SetActive(true);
// Build text for this column
StringBuilder sb = new StringBuilder();
int wordCount = 0;
bool columnIsFull = false;
// Fill this column until max lines or out of words
while (!columnIsFull && wordIndex < words.Count)
{
// Check if we've reached the max lines for this column
if (maxLinesPerColumn > 0 && wordCount >= maxLinesPerColumn)
{
columnIsFull = true;
break;
}
if (wordCount > 0)
sb.Append(separator);
sb.Append(string.Format(wordFormat, words[wordIndex]));
wordIndex++;
wordCount++;
}
// Set text content
col.text = sb.ToString();
// Move to next column if we have more words or the current column is full
if (wordIndex < words.Count || columnIsFull)
{
currentColumn++;
}
}
}
// Gets the list of extra words from the level manager
private List<string> GetWordsList()
{
if (levelManager != null)
{
List<string> words = customWordRepository.GetExtraWords().Where(word => word != null).ToList();
if (words.Count > 24)
{
words = words.Skip(Mathf.Max(0, words.Count - 24)).ToList();
}
return words ?? new List<string>();
}
return new List<string>();
}
// Helper method to get target extra words from the current level's group or fallback to game settings
private int GetTargetExtraWordsFromGroup()
{
var currentLevelGroup = GameDataManager.GetLevel().GetGroup();
return Mathf.Max(1, currentLevelGroup.targetExtraWords); // Ensure it's at least 1
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 14d648b82c79410bac1c9a4e6e811118
timeCreated: 1742316222

View File

@ -0,0 +1,47 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System;
using DG.Tweening;
using UnityEngine;
using UnityEngine.UI;
namespace WordsToolkit.Scripts.Popups
{
public class Fader : MonoBehaviour
{
public Image fader;
private readonly float fadeTime = .3f;
private readonly float maxValue = .993f;
public bool IsFaded()
{
return fader.color.a >= 0;
}
public void FadeIn(float fadeAlpha, Action action = null)
{
fader.gameObject.SetActive(true);
fader.DOFade(fadeAlpha, fadeTime).OnComplete(() => action?.Invoke());
}
public void FadeOut()
{
fader.DOFade(0, fadeTime).OnComplete(() => fader.gameObject.SetActive(false));
}
public void FadeAfterLoadingScene()
{
FadeOut();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ef3c4932b35844e8985afcacda4c590d
timeCreated: 1725685718

View File

@ -0,0 +1,37 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using WordsToolkit.Scripts.GUI;
using WordsToolkit.Scripts.GUI.Buttons;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.Popups
{
public class Failed : Popup
{
public CustomButton retryButton;
protected virtual void OnEnable()
{
retryButton.onClick.AddListener(Retry);
closeButton.onClick.AddListener(() => gameManager.MainMenu());
}
private void Retry()
{
StopInteration();
gameManager.RestartLevel();
Close();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b60a316fdb72486ea91097dbef9cbb9f
timeCreated: 1729248308

View File

@ -0,0 +1,64 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using WordsToolkit.Scripts.Levels;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.Popups
{
public class Finish : Popup
{
public TextMeshProUGUI title;
public TextMeshProUGUI description;
private Level currentLevel;
[SerializeField]
private AudioClip swish;
[SerializeField]
private AudioClip cheers;
[SerializeField]
private Image background;
private void OnEnable()
{
currentLevel = levelManager.GetCurrentLevel();
SetFinishText(currentLevel.GetTitle(gameManager.language),
currentLevel.GetText(gameManager.language));
background.sprite = levelManager.GetCurrentLevel().background;
}
private void SetFinishText(string titleText, string descriptionText)
{
if (titleText != "")
{
title.text = titleText;
}
if (descriptionText != "")
{
description.text = descriptionText;
}
}
public override void ShowAnimationSound()
{
base.ShowAnimationSound();
audioService.PlaySound(cheers);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 80d870e7714d48b4a86792fac4b3052c
timeCreated: 1747906864

View File

@ -0,0 +1,37 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEngine;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.Popups
{
public class GDPR : Popup
{
public void OnUserClickAccept()
{
PlayerPrefs.SetInt("npa", 0);
Close();
}
public void OnUserClickCancel()
{
PlayerPrefs.SetInt("npa", 1);
Close();
}
public void OnUserClickPrivacyPolicy()
{
Application.OpenURL(gameSettings.privacyPolicyUrl);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6c02172637514729966e9f5cd6078d9e
timeCreated: 1725685718

View File

@ -0,0 +1,27 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEngine;
using UnityEngine.Events;
namespace WordsToolkit.Scripts.Popups
{
public class GiftClaimTrigger : MonoBehaviour
{
public UnityEvent onOpened;
public void OnBoxOpened()
{
onOpened?.Invoke();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 17df131c94bc437289b191d79b801cb5
timeCreated: 1748338463

View File

@ -0,0 +1,181 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using DG.Tweening;
using UnityEngine;
using VContainer;
using WordsToolkit.Scripts.GUI;
using WordsToolkit.Scripts.GUI.Buttons;
using WordsToolkit.Scripts.Popups.RewardsGift;
using WordsToolkit.Scripts.Settings;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.Popups
{
public class GiftPopup : Popup
{
[SerializeField] private CustomButton claimButton;
[SerializeField] private Animator boxAnimator;
[Inject] private GiftsSettings giftsSettings;
private List<Transform> giftsPositionsSelected;
[SerializeField] private Transform startTransform;
[SerializeField] private Transform targetPosition;
private bool opened;
private List<GiftDataObject> giftsToClaim;
[SerializeField]
private AudioClip openBoxSound;
[SerializeField]
private Transform transformParent;
private void OnEnable()
{
claimButton.onClick.AddListener(OnClaimButtonClicked);
}
private void OnDestroy()
{
claimButton.onClick.RemoveListener(OnClaimButtonClicked);
}
private void OnClaimButtonClicked()
{
if (!opened)
{
audioService.PlayDelayed(openBoxSound, 1.0f);
boxAnimator.Play($"OpenBox");
}
else
StartCoroutine(ClaimGifts());
}
private IEnumerator ClaimGifts()
{
for (var index = 0; index < giftsToClaim.Count; index++)
{
var giftData = giftsToClaim[index];
giftData.giftInstance.Animate();
var giftInstanceGameObject = giftData.giftData.resourceObject.name != "Coins" ? giftData.giftInstance.gameObject : null;
giftData.giftData.resourceObject.AddAnimated(giftData.giftData.giftCount, giftData.giftInstance.transform.position, giftInstanceGameObject, () => { CloseDelay(); });
if (index > 0 && index < giftsToClaim.Count - 1)
{
yield return new WaitForSeconds(Random.Range(0.1f, 0.3f));
}
}
}
public void OnBoxOpened()
{
giftsToClaim = new List<GiftDataObject>();
foreach(var giftData in giftsSettings.gifts)
{
var firstOrDefault = gameSettings.boostLevels.FirstOrDefault(i=> i.tag == giftData.tagUIElement);
if(firstOrDefault != null && firstOrDefault!.level > GameDataManager.GetLevelNum())
{
break;
}
if(Random.value < giftData.giftChance/100f)
{
giftsToClaim.Add(new GiftDataObject
{
giftData = giftData,
giftInstance = null // Will be instantiated later
});
}
}
// If no gifts were added, force add the first gift
if(giftsToClaim.Count == 0 && giftsSettings.gifts.Length > 0)
{
giftsToClaim.Add( new GiftDataObject
{
giftData = giftsSettings.gifts[Random.Range(0, giftsSettings.gifts.Length)],
giftInstance = null // Will be instantiated later
});
}
for (var i = 0; i < giftsToClaim.Count; i++)
{
var giftData = giftsToClaim[i].giftData;
var giftPrefab = giftData.giftPrefab;
var giftInstance = Instantiate(giftPrefab, transformParent);
giftsToClaim[i].giftInstance = giftInstance;
giftInstance.transform.position = startTransform.position;
AnimateGift(giftInstance, i, giftsToClaim.Count, giftData);
giftInstance.SetCount(giftData.giftCount);
}
}
private void AnimateGift(GiftBase giftInstance, int i, int giftsCount, GiftData giftData)
{
giftInstance.transform.DOMove(GetGiftPosition(i, giftsCount), 0.5f).OnComplete(()=>OnCompleteAnimation(giftData));
}
private void OnCompleteAnimation(GiftData giftData)
{
opened = true;
var list = ShowTags(new[] { giftData.tagUIElement });
foreach (var tagObject in list)
{
var showable = tagObject.GetComponent<IFadeable>();
showable?.InstantHide();
showable?.Show();
var customButton = tagObject.GetComponent<CustomButton>();
if (customButton)
{
customButton.interactable = false;
}
}
}
private Vector3 GetGiftPosition(int index, int totalCount)
{
// check even or odd total count
if(totalCount % 2 == 0)
{
float radius = Vector3.Distance(targetPosition.position, startTransform.position)/1.2f;
float sign = (index == 1) ? 1f : -1f;
float angleInRadians = sign * 30f * Mathf.Deg2Rad;
float x = startTransform.position.x - (radius * Mathf.Sin(angleInRadians));
float y = startTransform.position.y + (radius * Mathf.Cos(angleInRadians));
return new Vector3(x, y, targetPosition.position.z);
}
// odd count
if (index == 0)
{
return targetPosition.position;
}
else if (index == 1 || index == 2)
{
float radius = Vector3.Distance(targetPosition.position, startTransform.position)/1.2f;
float sign = (index == 1) ? 1f : -1f;
float angleInRadians = sign * 50f * Mathf.Deg2Rad;
float x = startTransform.position.x - (radius * Mathf.Sin(angleInRadians));
float y = startTransform.position.y + (radius * Mathf.Cos(angleInRadians));
return new Vector3(x, y, targetPosition.position.z);
}
return targetPosition.position; // fallback
}
}
class GiftDataObject
{
public GiftBase giftInstance;
public GiftData giftData;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5db756b0b4a24c0bbde69ee4a65ec234
timeCreated: 1748331316

View File

@ -0,0 +1,12 @@
using UnityEngine;
using System;
namespace WordsToolkit.Scripts.Popups
{
public interface IPopupFactory
{
Popup CreatePopup(string pathWithType, Transform parent);
T CreatePopup<T>(Transform parent) where T : Popup;
Popup CreatePopup(Popup popupPrefab, Transform parent);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 677dfb691e6c64ee4b8c84b1a9acb6de
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
namespace WordsToolkit.Scripts.Popups
{
public interface IPopupStack
{
void Push(Popup popup);
void Pop();
Popup Peek();
bool IsEmpty();
void Clear();
int Count { get; }
event Action<Popup> OnPopupPushed;
event Action<Popup> OnPopupPopped;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a1ad40a22680a410287838c0abd94a0b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,82 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using WordsToolkit.Scripts.Data;
using WordsToolkit.Scripts.GUI;
using WordsToolkit.Scripts.GUI.Buttons;
using WordsToolkit.Scripts.Services.IAP;
namespace WordsToolkit.Scripts.Popups
{
public class ItemPurchase : MonoBehaviour
{
public CustomButton BuyItemButton;
public TextMeshProUGUI price;
public TextMeshProUGUI count;
public TextMeshProUGUI discountPercent;
[HideInInspector]
public KeyValuePair<ProductID, int> settingsShopItem;
public ProductID productID;
[SerializeField]
public ResourceObject resource;
private IIAPManager iapManager;
public void Init(KeyValuePair<ProductID, int> settingsShopItem, IIAPManager iapManager)
{
this.settingsShopItem = settingsShopItem;
productID = settingsShopItem.Key;
if (count != null)
{
count.text = settingsShopItem.Value.ToString();
}
if (productID != null && price != null)
{
var priceValue = iapManager.GetProductLocalizedPrice(productID.ID);
if (priceValue > 0.01m)
{
price.text = iapManager.GetProductLocalizedPriceString(productID.ID);
}
}
BuyItemButton?.onClick.AddListener(BuyCoins);
// Check if non-consumable item is already purchased
if (productID != null && productID.productType == ProductTypeWrapper.ProductType.NonConsumable &&
PlayerPrefs.GetInt("Purchased_" + productID.ID, 0) == 1)
{
gameObject.SetActive(false);
}
}
private void OnEnable()
{
// Removed old initialization code since it's now in Init
}
private void BuyCoins()
{
if (productID != null)
{
GetComponentInParent<CoinsShop>().BuyCoins(productID.ID);
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3375876541784a8f854c2536ec5cd514
timeCreated: 1725686495

View File

@ -0,0 +1,25 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
namespace WordsToolkit.Scripts.Popups
{
public class Loading : Popup
{
public override void CloseAnimationSound()
{
}
public override void Hide()
{
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3463917881594f2688a0688426106d74
timeCreated: 1725687066

View File

@ -0,0 +1,239 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using VContainer;
using WordsToolkit.Scripts.Audio;
using WordsToolkit.Scripts.Data;
using WordsToolkit.Scripts.GUI;
using WordsToolkit.Scripts.GUI.Buttons;
using WordsToolkit.Scripts.GUI.Labels;
using WordsToolkit.Scripts.Popups.Reward;
using WordsToolkit.Scripts.Settings;
using WordsToolkit.Scripts.System;
using Random = UnityEngine.Random;
namespace WordsToolkit.Scripts.Popups
{
public class LuckySpin : PopupWithCurrencyLabel
{
public float velocity;
public float stoptime;
[SerializeField]
private GameObject spin;
[SerializeField]
private List<Image> lights = new();
public CustomButton freeSpinButton;
public CustomButton rewardedAdButton;
public RewardSettingSpin[] spinRewards;
public List<RewardVisual> rewards = new();
private SpinSettings spinSettings;
private Rigidbody2D rb;
private bool isSpinning;
private int previousRotationMarker;
private const string LastFreeSpinTimeKey = "LastFreeSpinTime";
[SerializeField]
private float minVelocityMultiplier = 0.5f;
[SerializeField]
private float maxVelocityMultiplier = 2.5f;
[SerializeField]
private float additionalRandomFactor = 0.2f;
[Inject]
private SpinSettings luckySpinSettings;
[SerializeField]
private AudioClip luckySpin;
[SerializeField]
private AudioClip applause;
private void OnEnable()
{
rb = spin.GetComponent<Rigidbody2D>();
freeSpinButton.onClick.AddListener(FreeSpin);
UpdateButtonVisibility();
spinSettings = luckySpinSettings;
DefineRewards(spinSettings.rewards);
StartCoroutine(SwitchLightsAlpha());
}
private void UpdateButtonVisibility()
{
var canFreeSpin = CanUseFreeSpinToday();
freeSpinButton.gameObject.SetActive(canFreeSpin);
rewardedAdButton.gameObject.SetActive(!canFreeSpin);
}
private void SetButtonsVisibility(bool visible)
{
freeSpinButton.gameObject.SetActive(visible);
rewardedAdButton.gameObject.SetActive(visible);
}
private bool CanUseFreeSpinToday()
{
if (!PlayerPrefs.HasKey(LastFreeSpinTimeKey))
{
return true;
}
var lastFreeSpinTimeStr = PlayerPrefs.GetString(LastFreeSpinTimeKey);
var lastFreeSpinTime = DateTime.Parse(lastFreeSpinTimeStr);
return DateTime.Now.Date > lastFreeSpinTime.Date;
}
private void FreeSpin()
{
PlayerPrefs.SetString(LastFreeSpinTimeKey, DateTime.Now.ToString("o"));
Spin();
}
private IEnumerator SwitchLightsAlpha()
{
const float maxSpeed = 100;
while (true)
{
var speedRatio = Mathf.Abs(rb.angularVelocity) / maxSpeed; // Ratio of the current speed to the maximum speed
speedRatio = Mathf.Min(speedRatio, .9f);
var delay = 1f - speedRatio; // Higher speed -> smaller delay
yield return new WaitForSeconds(delay);
foreach (var light in lights)
{
light.color = new Color(light.color.r, light.color.g, light.color.b, light.color.a == 0 ? 1 : 0);
}
}
}
public void DefineRewards(RewardSettingSpin[] spinRewards)
{
this.spinRewards = spinRewards;
foreach (var reward in spinRewards)
{
var obj = Instantiate(reward.rewardVisualPrefab, spin.transform);
//rotate to 360/number of rewards
obj.transform.localPosition += new Vector3(0, 20, 0);
obj.transform.RotateAround(spin.transform.position, Vector3.forward, 360f / spinRewards.Length * obj.transform.GetSiblingIndex());
obj.SetCount(reward.count);
rewards.Add(obj);
}
}
public void Spin()
{
StartCoroutine(StartSpin());
}
private IEnumerator StartSpin()
{
// buttons interaction
closeButton.interactable = false;
freeSpinButton.interactable = false;
rewardedAdButton.interactable = false;
//hide buttons
SetButtonsVisibility(false);
var randomVelocity = CalculateRandomVelocity();
float timeElapsed = 0;
isSpinning = true;
previousRotationMarker = Mathf.FloorToInt(spin.transform.eulerAngles.z / 25);
while (timeElapsed < stoptime)
{
var appliedTorque = Mathf.Lerp(0, randomVelocity, timeElapsed / stoptime);
rb.AddTorque(appliedTorque);
timeElapsed += Time.deltaTime;
yield return new WaitForFixedUpdate();
}
rb.angularDrag *= 100;
yield return new WaitWhile(() => rb.angularVelocity != 0);
isSpinning = false;
CheckReward(GetWinReward());
}
private float CalculateRandomVelocity()
{
var baseMultiplier = Random.Range(minVelocityMultiplier, maxVelocityMultiplier);
var additionalRandomness = Random.Range(-additionalRandomFactor, additionalRandomFactor);
return velocity * (baseMultiplier + additionalRandomness);
}
private void Update()
{
if (isSpinning)
{
CheckPlaySound();
}
}
private void CheckPlaySound()
{
var currentZRotation = spin.transform.eulerAngles.z;
var currentTenDegreeMarker = Mathf.FloorToInt(currentZRotation / 25);
if (currentTenDegreeMarker != previousRotationMarker)
{
audioService.PlaySound(luckySpin);
previousRotationMarker = currentTenDegreeMarker;
}
}
private int GetWinReward()
{
audioService.PlaySound( applause);
var highestYIndex = 0; // Start with first item's index
var highestY = rewards[0].transform.position.y; // and its 'y' position
for (var i = 1; i < rewards.Count; i++)
{
// If current item's 'y' position is higher
if (rewards[i].transform.position.y > highestY)
{
highestY = rewards[i].transform.position.y;
highestYIndex = i;
}
}
return highestYIndex;
}
private void CheckReward(int rewardIndex)
{
var rewardSettingSpin = spinRewards[rewardIndex];
var rewardVisual = rewards[rewardIndex];
var _resource = rewardSettingSpin.resource;
var iconPos = rewardVisual.transform;
var _count = rewardSettingSpin.count;
_resource.AddAnimated(_count, iconPos.position, animationSourceObject: null, callback: () =>
{
Close();
});
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8722eb05d50246be8189fdbf4f9d9709
timeCreated: 1691983492

View File

@ -0,0 +1,93 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System;
using UnityEngine;
using UnityEngine.UI;
using WordsToolkit.Scripts.GUI;
using WordsToolkit.Scripts.Levels;
using WordsToolkit.Scripts.System;
using VContainer;
using WordsToolkit.Scripts.GUI.Buttons;
using WordsToolkit.Scripts.Settings;
namespace WordsToolkit.Scripts.Popups
{
public class MainMenu : Popup
{
public CustomButton settingsButton;
public CustomButton luckySpin;
public Button playButton;
[SerializeField]
private GameObject freeSpinMarker;
[SerializeField]
private Image background;
public Action OnAnimationEnded;
private const string LastFreeSpinTimeKey = "LastFreeSpinTime";
private void Start()
{
settingsButton.onClick.AddListener(SettingsButtonClicked);
luckySpin.onClick.AddListener(LuckySpinButtonClicked);
UpdateFreeSpinMarker();
var levelsCount = Resources.LoadAll<Level>("Levels").Length;
luckySpin.gameObject.SetActive(gameSettings.enableLuckySpin);
playButton.onClick.AddListener(PlayButtonClicked);
}
private void PlayButtonClicked()
{
menuManager.ShowPopup<MenuPlay>();
}
private bool CanUseFreeSpinToday()
{
if (!PlayerPrefs.HasKey(LastFreeSpinTimeKey))
{
return true;
}
var lastFreeSpinTimeStr = PlayerPrefs.GetString(LastFreeSpinTimeKey);
var lastFreeSpinTime = DateTime.Parse(lastFreeSpinTimeStr);
return DateTime.Now.Date > lastFreeSpinTime.Date;
}
private void UpdateFreeSpinMarker()
{
var isFreeSpinAvailable = CanUseFreeSpinToday();
if (freeSpinMarker != null)
{
freeSpinMarker.SetActive(isFreeSpinAvailable);
}
}
private void SettingsButtonClicked()
{
menuManager.ShowPopup<Settings>();
}
private void LuckySpinButtonClicked()
{
menuManager.ShowPopup<LuckySpin>(null, _ => UpdateFreeSpinMarker());
}
public void OnAnimationEnd()
{
OnAnimationEnded?.Invoke();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 99f1ec34f0834cd1a917d610fb019478
timeCreated: 1725696566

View File

@ -0,0 +1,202 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
using WordsToolkit.Scripts.System;
using VContainer;
namespace WordsToolkit.Scripts.Popups
{
public class MenuManager : MonoBehaviour
{
public Fader fader;
private List<Popup> popupStack = new();
[SerializeField]
private Canvas canvas;
[Inject]
private IPopupFactory popupFactory;
private void Awake()
{
DontDestroyOnLoad(this);
}
private void OnEnable()
{
Popup.OnClosePopup += ClosePopup;
Popup.OnBeforeCloseAction += OnBeforeCloseAction;
SceneManager.activeSceneChanged += OnSceneLoaded;
}
private void OnBeforeCloseAction(Popup popup)
{
if (fader != null && popupStack.Count == 1)
{
fader.FadeOut();
}
}
private void OnSceneLoaded(Scene scene, Scene scene1)
{
if (canvas == null && this != null)
{
canvas = GetComponent<Canvas>();
}
canvas.worldCamera = Camera.main;
}
private void OnDisable()
{
Popup.OnClosePopup -= ClosePopup;
SceneManager.activeSceneChanged -= OnSceneLoaded;
Popup.OnBeforeCloseAction -= OnBeforeCloseAction;
}
public T ShowPopup<T>(Action onShow = null, Action<EPopupResult> onClose = null) where T : Popup
{
if (popupStack.OfType<T>().Any())
{
return popupStack.OfType<T>().First();
}
var popup = popupFactory.CreatePopup<T>(transform);
return (T)ShowPopupInternal(popup, onShow, onClose);
}
public Popup ShowPopup(string pathWithType, Action onShow = null, Action<EPopupResult> onClose = null)
{
if (popupStack.Any(p => p.GetType().Name == pathWithType.Split('/').Last()))
{
return popupStack.First(p => p.GetType().Name == pathWithType.Split('/').Last());
}
var popup = popupFactory.CreatePopup(pathWithType, transform);
return ShowPopupInternal(popup, onShow, onClose);
}
public Popup ShowPopup(Popup popupPrefab, Action onShow = null, Action<EPopupResult> onClose = null)
{
if (popupStack.Any(p => p.GetType() == popupPrefab.GetType()))
{
return popupStack.First(p => p.GetType() == popupPrefab.GetType());
}
var popup = popupFactory.CreatePopup(popupPrefab, transform);
return ShowPopupInternal(popup, onShow, onClose);
}
private Popup ShowPopupInternal(Popup popup, Action onShow = null, Action<EPopupResult> onClose = null)
{
if (popupStack.Count > 0)
{
popupStack.Last().Hide();
}
popupStack.Add(popup);
popup.Show<Popup>(onShow, onClose);
if (fader != null && popupStack.Count > 0 && popup.fade)
{
fader.transform.SetSiblingIndex(popup.transform.GetSiblingIndex() - 1);
fader.FadeIn(popup.fadeAlpha);
}
return popup;
}
private void ClosePopup(Popup popupClose)
{
if (popupStack.Count > 0)
{
popupStack.Remove(popupClose);
if (popupStack.Count > 0)
{
var popup = popupStack.Last();
var siblingIndex = popup.transform.GetSiblingIndex() - 1;
siblingIndex = Mathf.Clamp(siblingIndex, 0, transform.childCount - 1);
fader.transform.SetSiblingIndex(siblingIndex);
popup.OnActivate();
}
}
}
public void ShowPurchased(GameObject imagePrefab, string boostName)
{
var menu = ShowPopup<PurchasedMenu>();
menu.GetComponent<PurchasedMenu>().SetIconSprite(imagePrefab, boostName);
}
private void Update()
{
if (Application.platform != RuntimePlatform.IPhonePlayer)
{
if (Keyboard.current[Key.Escape].wasReleasedThisFrame)
{
if (popupStack is { Count: > 0 })
{
var closeButton = popupStack.Last().closeButton;
if (closeButton != null)
{
closeButton.onClick?.Invoke();
}
}
}
}
}
public T GetPopupOpened<T>() where T : Popup
{
foreach (var popup in popupStack)
{
if (popup.GetType() == typeof(T))
{
return (T)popup;
}
}
return null;
}
public void CloseAllPopups()
{
for (var i = 0; i < popupStack.Count; i++)
{
var popup = popupStack[i];
popup.Close();
}
popupStack.Clear();
}
public bool IsAnyPopupOpened()
{
return popupStack.Count > 0;
}
public Popup GetLastPopup()
{
return popupStack.Last();
}
public MainMenu GetMainMenu()
{
return FindObjectOfType<MainMenu>();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 168b675c084949949d771ac92e62757a
timeCreated: 1725686357

View File

@ -0,0 +1,197 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using WordsToolkit.Scripts.Levels;
using WordsToolkit.Scripts.System;
using VContainer;
using WordsToolkit.Scripts.Gameplay.Managers;
using WordsToolkit.Scripts.GUI;
namespace WordsToolkit.Scripts.Popups
{
public class MenuPlay : Popup
{
public Image[] backgroundImages;
public Slider scrollBar;
public TextMeshProUGUI counter;
public Button play;
private LevelGroup currentGroup;
private Level currentLevel;
[Inject]
private SceneLoader sceneLoader;
[Inject]
private BackgroundChanger backgroundChanger;
[SerializeField]
private GameObject hardLabel;
private void OnEnable()
{
stateManager.HideMain();
play.onClick.AddListener(Play);
// Get the current level from GameDataManager
currentLevel = GameDataManager.GetLevel();
// If current level is null, try to find previous level
if (currentLevel == null)
{
TryLoadPreviousLevel();
}
currentGroup = currentLevel?.GetGroup();
// Try to find a background sprite following the hierarchy
Sprite backgroundToUse = GetBackgroundFromHierarchy();
backgroundChanger.SetBackground(backgroundToUse);
if (backgroundToUse != null && backgroundImages.Length > 0)
{
// Set sprite for all background images
foreach (var bg in backgroundImages)
{
bg.sprite = backgroundToUse;
}
}
// Update progress UI elements
UpdateProgressUI();
}
public override void AfterShowAnimation()
{
base.AfterShowAnimation();
if (currentLevel.isHardLevel)
{
hardLabel.SetActive(true); hardLabel.GetComponent<Animator>().Play("HardLabel");
}
}
private void TryLoadPreviousLevel()
{
Debug.LogWarning("Current level is null, trying to load previous level");
// Get current level number
int currentLevelNum = GameDataManager.GetLevelNum();
// Try to find a valid previous level
int previousLevel = currentLevelNum - 1;
while (previousLevel > 0)
{
GameDataManager.SetLevelNum(previousLevel);
Level levelData = GameDataManager.GetLevel();
if (levelData != null)
{
currentLevel = levelData;
Debug.Log($"Loaded previous level: {previousLevel}");
break;
}
previousLevel--;
}
// If we still couldn't find a valid level, log an error
if (currentLevel == null)
{
Debug.LogError("Could not find any valid level to load");
}
}
private void Play()
{
// Make sure we have a valid level to play
if (currentLevel == null)
{
Debug.LogWarning("Attempting to play with null level, trying to load previous level");
TryLoadPreviousLevel();
// If still null, we can't play
if (currentLevel == null)
{
Debug.LogError("No valid level to play");
return;
}
}
GameDataManager.SetLevel(currentLevel);
sceneLoader.StartGameScene();
Close();
}
private void UpdateProgressUI()
{
if (counter != null && currentLevel != null)
{
// Check if we have a valid group
if (currentGroup == null || currentGroup.levels == null)
{
counter.text = "0/0";
scrollBar.minValue = 0;
scrollBar.maxValue = 1;
scrollBar.value = 0;
return;
}
// Get the total number of levels in the group
int totalLevels = currentGroup.levels.Count;
// Find the index of current level in the group
int currentIndex = currentGroup.levels.IndexOf(currentLevel);
// If level is not found in the group, set index to 0
if (currentIndex < 0)
{
currentIndex = 0;
}
// Update counter and scrollbar
counter.text = $"{currentIndex}/{totalLevels}";
scrollBar.minValue = 0;
scrollBar.maxValue = totalLevels;
scrollBar.value = currentIndex;
}
}
private Sprite GetBackgroundFromHierarchy()
{
if (currentGroup == null) return null;
// Start from the top-most parent
LevelGroup current = currentGroup;
while (current.parentGroup != null)
{
if (current.parentGroup.background != null)
{
return current.parentGroup.background;
}
current = current.parentGroup;
}
// Then check current group
if (currentGroup.background != null)
{
return currentGroup.background;
}
// Finally check the level itself
if (currentLevel != null && currentLevel.background != null)
{
return currentLevel.background;
}
return null;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3ab97f16234641e39222de5d96eb8d74
timeCreated: 1742810007

View File

@ -0,0 +1,53 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using WordsToolkit.Scripts.Enums;
using WordsToolkit.Scripts.GUI;
using WordsToolkit.Scripts.GUI.Buttons;
using WordsToolkit.Scripts.Services;
using WordsToolkit.Scripts.Services.IAP;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.Popups
{
public class NoAds : Popup
{
public CustomButton removeAdsButton;
public ProductID productID;
private void OnEnable()
{
removeAdsButton.onClick.AddListener(RemoveAds);
EventManager.GetEvent<string>(EGameEvent.PurchaseSucceeded).Subscribe(PurchaseSucceeded);
}
protected override void OnDisable()
{
base.OnDisable();
EventManager.GetEvent<string>(EGameEvent.PurchaseSucceeded).Unsubscribe(PurchaseSucceeded);
}
private void PurchaseSucceeded(string obj)
{
if (obj == productID.ID)
{
adsManager.RemoveAds();
Close();
}
}
private void RemoveAds()
{
iapManager.BuyProduct(productID.ID);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: aa1ef191b5a343e9937c2ee97a24b843
timeCreated: 1726157597

View File

@ -0,0 +1,18 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
namespace WordsToolkit.Scripts.Popups
{
public class NoAdsItemPurchase : ItemPurchase
{
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ecf8678021564e0c8ccdb00b43162f58
timeCreated: 1742453759

View File

@ -0,0 +1,258 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using DG.Tweening;
using UnityEngine;
using UnityEngine.UI;
using VContainer;
using WordsToolkit.Scripts.Audio;
using WordsToolkit.Scripts.Data;
using WordsToolkit.Scripts.Gameplay.Managers;
using WordsToolkit.Scripts.GUI;
using WordsToolkit.Scripts.GUI.Buttons;
using WordsToolkit.Scripts.Localization;
using WordsToolkit.Scripts.NLP;
using WordsToolkit.Scripts.Services;
using WordsToolkit.Scripts.Services.IAP;
using WordsToolkit.Scripts.Settings;
using WordsToolkit.Scripts.System;
using WordsToolkit.Scripts.Utils;
namespace WordsToolkit.Scripts.Popups
{
[RequireComponent(typeof(Animator), typeof(CanvasGroup))]
public class Popup : MonoBehaviour
{
public bool fade = true;
private Animator animator;
public CustomButton closeButton;
private CanvasGroup canvasGroup;
public Action OnShowAction;
public Action<EPopupResult> OnCloseAction;
protected EPopupResult result;
protected GameManager gameManager;
protected MenuManager menuManager;
protected LevelManager levelManager;
protected StateManager stateManager;
protected GameSettings gameSettings;
protected IAdsManager adsManager;
protected IIAPManager iapManager;
protected IAudioService audioService;
protected ResourceManager resourceManager;
public delegate void PopupEvents(Popup popup);
public static event PopupEvents OnOpenPopup;
public static event PopupEvents OnClosePopup;
public static event PopupEvents OnBeforeCloseAction;
protected IObjectResolver _container;
[TagFieldUI, SerializeField]
public string[] tagsToShow;
private Dictionary<Transform, Transform> _tagsToShowDic = new Dictionary<Transform, Transform>();
[SerializeField]
private bool isPopupAboveTags;
protected ILocalizationService localizationManager;
protected FieldManager fieldManager;
protected ICustomWordRepository customWordRepository;
[SerializeField]
public float fadeAlpha = 1;
[SerializeField]
private AudioClip appearSound;
[SerializeField]
private AudioClip disappearSound;
[Inject]
public void Construct(GameManager gameManager, MenuManager menuManager, LevelManager levelManager, StateManager stateManager, GameSettings gameSettings, IAdsManager adsManager, IIAPManager iapManager, IAudioService audioService, ResourceManager resourceManager, IObjectResolver container, ILocalizationService localizationManager, FieldManager fieldManager, ICustomWordRepository customWordRepository)
{
this.gameManager = gameManager;
this.menuManager = menuManager;
this.levelManager = levelManager;
this.stateManager = stateManager;
this.gameSettings = gameSettings;
this.adsManager = adsManager;
this.iapManager = iapManager;
this.audioService = audioService;
this.resourceManager = resourceManager;
_container = container;
this.localizationManager = localizationManager;
this.fieldManager = fieldManager;
this.customWordRepository = customWordRepository;
}
protected virtual void Awake()
{
animator = GetComponent<Animator>();
canvasGroup = GetComponent<CanvasGroup>();
if (closeButton != null)
{
closeButton.onClick.AddListener(Close);
}
}
public void Show<T>(Action onShow = null, Action<EPopupResult> onClose = null)
{
if (onShow != null)
{
OnShowAction = onShow;
}
if (onClose != null)
{
OnCloseAction = onClose;
}
OnOpenPopup?.Invoke(this);
PlayShowAnimation();
OnActivate();
}
public List<GameObject> ShowTags(string[] tutorialDataTagsToShow)
{
List<GameObject> lObjects = new List<GameObject>();
tagsToShow = tutorialDataTagsToShow;
foreach (var t in tutorialDataTagsToShow)
{
var tagObject = GameObject.FindGameObjectsWithTag(t).FirstOrDefault(i=>i.gameObject.activeSelf);
if (tagObject)
{
lObjects.Add(tagObject);
MakeObjectVisible(tagObject);
}
}
if (isPopupAboveTags)
{
var canvas = transform.AddComponentIfNotExists<Canvas>();
canvas.overrideSorting = true;
canvas.sortingLayerID = SortingLayer.NameToID("UI");
canvas.sortingOrder = 5; // Ensure popup is above tags
}
return lObjects;
}
public void MakeObjectVisible(GameObject tagObject, int sortingOrder = 4)
{
_tagsToShowDic.TryAdd(tagObject.transform, tagObject.transform.parent);
var canvas = tagObject.AddComponentIfNotExists<Canvas>();
if (canvas != null && canvas.sortingLayerID == SortingLayer.NameToID("UI"))
return;
canvas.overrideSorting = true;
canvas.sortingLayerID = SortingLayer.NameToID("UI");
canvas.sortingOrder = sortingOrder;
tagObject.AddComponentIfNotExists<GraphicRaycaster>();
}
private void PlayShowAnimation()
{
if (animator != null)
{
animator.Play("popup_show");
}
}
public virtual void ShowAnimationSound()
{
audioService.PlaySound(appearSound);
}
public virtual void AfterShowAnimation()
{
OnShowAction?.Invoke();
}
public virtual void CloseAnimationSound()
{
audioService.PlaySound(disappearSound);
}
public virtual void Close()
{
if (closeButton)
{
closeButton.interactable = false;
}
if (canvasGroup != null)
{
canvasGroup.interactable = false;
}
OnBeforeCloseAction?.Invoke(this);
if (animator != null)
{
animator.Play("popup_hide");
}
HideTags();
}
private void HideTags()
{
foreach (var t in _tagsToShowDic)
{
if (!t.Key || !t.Key.gameObject)
continue;
var customButton = t.Key?.gameObject?.GetComponent<CustomButton>();
if (customButton)
{
customButton.interactable = true;
}
Destroy(t.Key.GetComponent<GraphicRaycaster>());
Destroy(t.Key.GetComponent<Canvas>());
}
_tagsToShowDic.Clear();
}
public virtual void AfterHideAnimation()
{
OnClosePopup?.Invoke(this);
OnCloseAction?.Invoke(result);
Destroy(gameObject, .5f);
}
protected virtual void OnDisable()
{
DOTween.Kill(gameObject);
}
public void OnActivate()
{
ShowTags(tagsToShow);
canvasGroup.interactable = true;
canvasGroup.DOFade(1, 0.1f);
}
public virtual void Hide()
{
canvasGroup.interactable = false;
canvasGroup.DOFade(0, 0.5f);
}
public void CloseDelay()
{
Invoke(nameof(Close), 0.5f);
}
protected void StopInteration()
{
canvasGroup.interactable = false;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c25b7a112efc4b81997a0dac8488b2c9
timeCreated: 1725685853

View File

@ -0,0 +1,38 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEngine.SceneManagement;
using WordsToolkit.Scripts.Audio;
namespace WordsToolkit.Scripts.Popups
{
public class PopupWithCurrencyLabel : Popup
{
private UIPanel _panel;
public override void AfterShowAnimation()
{
_panel = FindObjectOfType<UIPanel>(true);
_panel?.gameObject.SetActive(true);
base.AfterShowAnimation();
}
public override void Close()
{
base.Close();
if (SceneManager.GetActiveScene().name != "main")
{
_panel?.gameObject.SetActive(false);
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: abb10cf834b04599b175e39f80b10ae3
timeCreated: 1725685718

View File

@ -0,0 +1,89 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using DG.Tweening;
using TMPro;
using UnityEngine;
using WordsToolkit.Scripts.Audio;
using WordsToolkit.Scripts.Data;
using WordsToolkit.Scripts.Enums;
using WordsToolkit.Scripts.GUI;
using WordsToolkit.Scripts.GUI.Buttons;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.Popups
{
public class PreFailed : PopupWithCurrencyLabel
{
public TextMeshProUGUI continuePrice;
public TextMeshProUGUI timerText;
public CustomButton continueButton;
public CustomButton rewardButton;
public CustomButton againButton;
private int price;
[SerializeField]
private AudioClip warningTime;
[SerializeField]
private AudioClip failedSound;
private void OnEnable()
{
price = gameSettings.continuePrice;
continuePrice.text = price.ToString();
continueButton.onClick.AddListener(Continue);
closeButton.onClick.AddListener(Cancel);
timerText.text = "<color=#FFF76B> +" + gameSettings.continueTime + "</color> sec";
audioService.PlaySound(warningTime);
rewardButton.gameObject.SetActive(gameSettings.enableAds);
againButton.onClick.AddListener(Again);
}
private void Cancel()
{
result = EPopupResult.Cancel;
audioService.PlaySound(failedSound);
Close();
}
public override void Close()
{
base.Close();
}
private void Again()
{
gameManager.RestartLevel();
Close();
}
private void Continue()
{
var coinsResource = resourceManager.GetResource("Coins");
if (resourceManager.ConsumeWithEffects(coinsResource, price))
{
StopInteration();
DOVirtual.DelayedCall(0.5f, ContinueGame);
}
}
public void ContinueGame()
{
result = EPopupResult.Continue;
EventManager.GameStatus = EGameState.Playing;
Close();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 00e91d4ae82247a8996b0a7167979103
timeCreated: 1727773522

View File

@ -0,0 +1,18 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
namespace WordsToolkit.Scripts.Popups
{
public class PrePlay : Banner
{
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b94c41c20da245d087d7f6f49e086dc1
timeCreated: 1729181797

View File

@ -0,0 +1,28 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace WordsToolkit.Scripts.Popups
{
public class PreWin : Banner
{
protected override IEnumerator Wait()
{
audioService.PlayWin();
yield return new WaitForSeconds(.5f);
Close();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 164829b7963d44e38d8fa37253d80a5c
timeCreated: 1729061890

View File

@ -0,0 +1,32 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using TMPro;
using UnityEngine;
namespace WordsToolkit.Scripts.Popups
{
public class PurchasedMenu : Popup
{
[SerializeField]
private Transform icon;
[SerializeField]
private TextMeshProUGUI text;
public void SetIconSprite(GameObject imagePrefab, string boostName)
{
Instantiate(imagePrefab, icon);
text.text = "You got " + boostName + " boost";
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b6d0cb9b75bd454297d110789b964b5e
timeCreated: 1725686465

View File

@ -0,0 +1,28 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEngine;
using WordsToolkit.Scripts.GUI;
using WordsToolkit.Scripts.GUI.Buttons;
namespace WordsToolkit.Scripts.Popups
{
public class Quit : PopupWithCurrencyLabel
{
public CustomButton yes;
private void OnEnable()
{
yes.onClick.AddListener(Application.Quit);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1627a1f4ce114cc486cc853f8a946e12
timeCreated: 1726165082

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 609aa55a9fc44ea4b313194a00028fbd
timeCreated: 1726114462

View File

@ -0,0 +1,47 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using TMPro;
using UnityEngine;
using WordsToolkit.Scripts.Data;
using WordsToolkit.Scripts.GUI.Labels;
using WordsToolkit.Scripts.Settings;
namespace WordsToolkit.Scripts.Popups.Reward
{
public class RewardPopup : PopupWithCurrencyLabel
{
public Transform iconPos;
private int _count;
private ResourceObject _resource;
private RewardSettingSpin rewardVisual;
public TextMeshProUGUI countText;
public void SetReward(RewardSettingSpin rewardVisual)
{
this.rewardVisual = rewardVisual;
_count = rewardVisual.count;
countText.text = _count.ToString();
_resource = rewardVisual.resource;
}
public override void Close()
{
StopInteration();
_resource.AddAnimated(_count, iconPos.position, animationSourceObject: null, callback: () =>
{
base.Close();
});
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f9adedea92f702d4995617ce27d3831d
timeCreated: 1458824059
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,27 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using TMPro;
using UnityEngine;
namespace WordsToolkit.Scripts.Popups.Reward
{
public class RewardVisual : MonoBehaviour
{
public TextMeshProUGUI countText;
public void SetCount(int count)
{
countText.text = count.ToString();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 119251b6ae834c88a639a83ccec882d7
timeCreated: 1691995253

View File

@ -0,0 +1,21 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using WordsToolkit.Scripts.GUI;
using WordsToolkit.Scripts.GUI.Buttons;
namespace WordsToolkit.Scripts.Popups.Reward
{
public class RewardedButton : CustomButton
{
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9b56a70df4a6f465ea1e315972ed2f33
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,58 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEngine;
using UnityEngine.Events;
using VContainer;
using WordsToolkit.Scripts.GUI;
using WordsToolkit.Scripts.GUI.Buttons;
using WordsToolkit.Scripts.Services;
using WordsToolkit.Scripts.Services.Ads.AdUnits;
namespace WordsToolkit.Scripts.Popups.Reward
{
public class RewardedButtonHandler : MonoBehaviour
{
[SerializeField]
private AdReference adReference;
[SerializeField]
private CustomButton rewardedButton;
[SerializeField]
private UnityEvent onRewardedAdComplete;
[SerializeField]
private UnityEvent onRewardedShow;
[Inject]
private IAdsManager adsManager;
private void Awake()
{
rewardedButton.onClick.AddListener(ShowRewardedAd);
}
private void ShowRewardedAd()
{
if (adsManager.IsRewardedAvailable(adReference))
{
onRewardedShow?.Invoke();
adsManager.ShowAdByType(adReference, _ => onRewardedAdComplete?.Invoke());
}
else
{
Debug.Log("Rewarded ad is not available");
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dece0aac2cf9463587dade7a67633f5d
timeCreated: 1726040785

View File

@ -0,0 +1,48 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEngine;
using WordsToolkit.Scripts.Popups;
using VContainer;
using WordsToolkit.Scripts.GUI;
using WordsToolkit.Scripts.GUI.Buttons;
namespace WordsToolkit.Scripts.Popups.Reward
{
public class SpinOpenButton : MonoBehaviour
{
[Inject]
private MenuManager menuManager;
[SerializeField]
private CustomButton spinButton;
[SerializeField]
private GameObject freeSpinLabel;
private void OnEnable()
{
spinButton.onClick.AddListener(ShowLuckySpin);
CheckFree();
}
private void CheckFree()
{
// freeSpinLabel.SetActive(PlayerPrefs.GetInt("FreeSpin", 0) == 0);
}
public void ShowLuckySpin()
{
menuManager.ShowPopup<LuckySpin>(null, x => CheckFree());
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b2dffeea3b8d4ba384015d8920b5f08b
timeCreated: 1692033837

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7a4e60ed8f034a82937ff13c7ec87e83
timeCreated: 1748331848

View File

@ -0,0 +1,24 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using DG.Tweening;
namespace WordsToolkit.Scripts.Popups.RewardsGift
{
public class CoinsGift : GiftBase
{
public override void Animate()
{
gameObject.SetActive(false);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a4da337610604b04aa326a740e821066
timeCreated: 1748332650

View File

@ -0,0 +1,37 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using TMPro;
using UnityEngine;
namespace WordsToolkit.Scripts.Popups.RewardsGift
{
public class GiftBase : MonoBehaviour
{
public TextMeshProUGUI amountText;
public virtual void Animate()
{
}
public void SetCount(int giftDataGiftCount)
{
amountText.text = giftDataGiftCount.ToString();
}
public void HideText()
{
amountText.gameObject.SetActive(false);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fe09d988ff694223be60881b378313e8
timeCreated: 1748331871

View File

@ -0,0 +1,22 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
namespace WordsToolkit.Scripts.Popups.RewardsGift
{
public class HammerGift : GiftBase
{
public override void Animate()
{
gameObject.SetActive(false);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2e29de9241dd4db8b6feb91aa18d850c
timeCreated: 1748523167

View File

@ -0,0 +1,22 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
namespace WordsToolkit.Scripts.Popups.RewardsGift
{
public class TipGift : GiftBase
{
public override void Animate()
{
gameObject.SetActive(false);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3808146b8c4d49a3ac5297caf2ad1ecb
timeCreated: 1748331948

View File

@ -0,0 +1,66 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System;
using UnityEngine;
using UnityEngine.SceneManagement;
using WordsToolkit.Scripts.Gameplay.Managers;
using WordsToolkit.Scripts.Levels;
using WordsToolkit.Scripts.System;
using VContainer;
namespace WordsToolkit.Scripts.Popups
{
public class SceneLoader : MonoBehaviour
{
public static Action<Scene> OnSceneLoadedCallback;
public static event Action OnGameStart;
private Loading loading;
private Scene previouseScene;
[Inject]
private StateManager stateManager;
private void Start()
{
CheckEvent(SceneManager.GetActiveScene());
}
public void StartGameScene()
{
// Let GameDataManager.GetLevel() handle loading the appropriate level
// This ensures consistent behavior with our level selection logic
Level level = GameDataManager.GetLevel();
if (level != null)
{
GameDataManager.SetLevel(level);
}
OnGameStart?.Invoke();
stateManager.CurrentState = EScreenStates.Game;
}
public void GoMain()
{
stateManager.CurrentState = EScreenStates.MainMenu;
}
private void CheckEvent(Scene scene)
{
if (previouseScene != scene)
{
OnSceneLoadedCallback?.Invoke(scene);
previouseScene = scene;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b3d805f2764540d59695480d886c6756
timeCreated: 1725686397

View File

@ -0,0 +1,105 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEngine;
using UnityEngine.UI;
using WordsToolkit.Scripts.Enums;
using WordsToolkit.Scripts.Gameplay.Managers;
using WordsToolkit.Scripts.GUI;
using WordsToolkit.Scripts.System;
using VContainer;
using WordsToolkit.Scripts.GUI.Buttons;
namespace WordsToolkit.Scripts.Popups
{
public class Settings : PopupWithCurrencyLabel
{
[SerializeField]
private CustomButton privacypolicy;
[SerializeField]
private Button restorePurchase;
[SerializeField]
private Slider vibrationSlider;
private const string VibrationPrefKey = "VibrationLevel";
protected virtual void OnEnable()
{
privacypolicy?.onClick.AddListener(PrivacyPolicy);
LoadVibrationLevel();
vibrationSlider.onValueChanged.AddListener(SaveVibrationLevel);
closeButton.onClick.RemoveAllListeners();
closeButton.onClick.AddListener(BackToGame);
restorePurchase?.onClick.AddListener(RestorePurchase);
restorePurchase?.gameObject.SetActive(gameSettings.enableInApps);
}
private void RestorePurchase()
{
gameManager.RestorePurchases(((b, list) =>
{
if (b)
Close();
}));
}
private void BackToGame()
{
DisablePause();
Close();
}
protected override void OnDisable()
{
base.OnDisable();
vibrationSlider.onValueChanged.RemoveListener(SaveVibrationLevel);
}
private void SaveVibrationLevel(float value)
{
PlayerPrefs.SetFloat(VibrationPrefKey, value);
PlayerPrefs.Save();
}
private void LoadVibrationLevel()
{
if (PlayerPrefs.HasKey(VibrationPrefKey))
{
vibrationSlider.value = PlayerPrefs.GetFloat(VibrationPrefKey);
}
else
{
vibrationSlider.value = 1.0f;
SaveVibrationLevel(1.0f);
}
}
private void PrivacyPolicy()
{
StopInteration();
DisablePause();
menuManager.ShowPopup<GDPR>();
Close();
}
private void DisablePause()
{
if (stateManager.CurrentState == EScreenStates.Game)
{
EventManager.GameStatus = EGameState.Playing;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 03864e15af6e454d993b1385ee634955
timeCreated: 1726163156

View File

@ -0,0 +1,43 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEngine;
using WordsToolkit.Scripts.GUI.Buttons;
namespace WordsToolkit.Scripts.Popups
{
public class SettingsGame : Settings
{
[SerializeField]
private CustomButton homeButton;
protected override void OnEnable()
{
base.OnEnable();
homeButton.onClick.AddListener(OnHomeButtonClicked);
}
protected override void OnDisable()
{
base.OnDisable();
homeButton.onClick.RemoveListener(OnHomeButtonClicked);
}
private void OnHomeButtonClicked()
{
if (gameManager != null)
{
gameManager.MainMenu();
Close();
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 997e0362973d404c9b4f90a01990a019
timeCreated: 1749453629

View File

@ -0,0 +1,20 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEngine;
namespace WordsToolkit.Scripts.Popups
{
public class UIPanel : MonoBehaviour
{
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 34530b51ab774051937fc1860ba68b5f
timeCreated: 1726331373

View File

@ -0,0 +1,43 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using WordsToolkit.Scripts.GUI;
using WordsToolkit.Scripts.GUI.Buttons;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.Popups
{
public class Win : Popup
{
public CustomButton nextLevelButton;
protected override void Awake()
{
base.Awake();
nextLevelButton.onClick.AddListener(() =>
{
StopInteration();
if (GameDataManager.HasMoreLevels())
{
gameManager.NextLevel();
}
else
{
gameManager.MainMenu();
}
Close();
});
closeButton.onClick.AddListener(() => gameManager.MainMenu());
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 93f042b170a34f38b0db1e8dfad45492
timeCreated: 1725794899