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,118 @@
// // ©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;
namespace WordsToolkit.Scripts.GUI
{
[RequireComponent(typeof(RectTransform))]
public class AnimateTransform : MonoBehaviour
{
public Move position;
public Rotate rotation;
public Scale scale;
public Size sizeDelta;
private RectTransform rectTransform;
private void OnEnable()
{
rectTransform = GetComponent<RectTransform>();
if (position.targetValue != Vector3.zero)
{
rectTransform.anchoredPosition = position.startValue;
position.Animate(rectTransform);
}
if (rotation.targetValue != Vector3.zero)
{
rectTransform.eulerAngles = rotation.startValue;
rotation.Animate(rectTransform);
}
if (scale.targetValue != Vector3.zero)
{
rectTransform.localScale = scale.startValue;
scale.Animate(rectTransform);
}
if (sizeDelta.targetValue != Vector3.zero)
{
rectTransform.sizeDelta = sizeDelta.startValue;
sizeDelta.Animate(rectTransform);
}
}
private void OnDisable()
{
rectTransform.DOKill();
}
}
[Serializable]
public abstract class TweenAnimationParameters
{
public Vector3 startValue;
public Vector3 targetValue;
public float duration = 1f;
public float delay;
public Ease easeType = Ease.Linear;
public bool loop;
public LoopType loopType;
public void Animate(RectTransform rectTransform)
{
GetTween(rectTransform).SetEase(easeType).SetDelay(delay).SetLoops(loop ? -1 : 1, loopType);
}
protected abstract Tweener GetTween(RectTransform rectTransform);
}
[Serializable]
public class Move : TweenAnimationParameters
{
protected override Tweener GetTween(RectTransform rectTransform)
{
return rectTransform.DOLocalMove(targetValue, duration);
}
}
[Serializable]
public class Rotate : TweenAnimationParameters
{
protected override Tweener GetTween(RectTransform rectTransform)
{
return rectTransform.DOLocalRotate(targetValue, duration);
}
}
[Serializable]
public class Scale : TweenAnimationParameters
{
protected override Tweener GetTween(RectTransform rectTransform)
{
return rectTransform.DOScale(targetValue, duration);
}
}
[Serializable]
public class Size : TweenAnimationParameters
{
protected override Tweener GetTween(RectTransform rectTransform)
{
return rectTransform.DOSizeDelta(targetValue, duration);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cadee4a7f70a470daf9c57afd1455652
timeCreated: 1692895718

View File

@ -0,0 +1,84 @@
// // ©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 VContainer;
using WordsToolkit.Scripts.Gameplay.Managers;
using WordsToolkit.Scripts.Infrastructure.Service;
using WordsToolkit.Scripts.Levels;
using WordsToolkit.Scripts.Popups;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.GUI
{
public class BackgroundChanger : MonoBehaviour
{
[SerializeField]
private Image image;
private ILevelLoaderService levelLoaderService;
private StateManager stateManager;
[SerializeField]
private Sprite mainBackground;
[Inject]
public void Construct(ILevelLoaderService levelLoaderService, StateManager stateManager)
{
this.levelLoaderService = levelLoaderService;
this.levelLoaderService.OnLevelLoaded += OnLevelLoaded;
SceneLoader.OnGameStart += SetBack;
this.stateManager = stateManager;
}
private void OnEnable()
{
stateManager.OnStateChanged.AddListener(OnStateChanged);
}
private void OnStateChanged(EScreenStates arg0)
{
if (arg0 == EScreenStates.Game)
{
SetBack();
}else if (arg0 == EScreenStates.MainMenu)
{
image.sprite = mainBackground;
}
}
private void SetBack()
{
image.sprite = GameDataManager.GetLevel().background;
}
private void OnDestroy()
{
if (levelLoaderService != null)
{
levelLoaderService.OnLevelLoaded -= OnLevelLoaded;
SceneLoader.OnGameStart -= SetBack;
stateManager.OnStateChanged.RemoveListener(OnStateChanged);
}
}
private void OnLevelLoaded(Level level)
{
image.sprite = level.background;
}
public void SetBackground(Sprite bg)
{
image.sprite = bg;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 59912d2c0926422a8517e192d9ecc7f6
timeCreated: 1730130790

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 716a5963d39e49cfad01e43eff89511f
timeCreated: 1748674096

View File

@ -0,0 +1,69 @@
// // ©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 UnityEngine;
using VContainer;
using WordsToolkit.Scripts.Data;
using WordsToolkit.Scripts.Gameplay.Managers;
using WordsToolkit.Scripts.Settings;
namespace WordsToolkit.Scripts.GUI.Buttons
{
public class BaseGUIButton : CustomButton, IFadeable
{
protected LevelManager levelManager;
protected GameSettings gameSettings;
protected ResourceManager resourceManager;
protected ButtonViewController buttonViewController;
public RectTransform rectTransform;
public Vector2 savePosition;
public Vector2 targetPosition;
[Inject]
public void Construct(LevelManager levelManager, GameSettings gameSettings, ResourceManager resourceManager, ButtonViewController buttonViewController)
{
this.gameSettings = gameSettings;
this.levelManager = levelManager;
this.resourceManager = resourceManager;
this.buttonViewController = buttonViewController;
this.buttonViewController.RegisterButton(this);
}
public void Hide()
{
animator.enabled = false;
rectTransform.DOAnchorPos( targetPosition, 0.5f);
}
public void InstantHide()
{
rectTransform.anchoredPosition = targetPosition;
}
public void HideForWin()
{
Hide();
}
public void Show()
{
animator.enabled = true;
rectTransform.DOAnchorPos(savePosition, 0.5f).OnComplete(ShowCallback);
}
protected virtual void ShowCallback()
{
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: eae64b3976cb4849bb325653a674bfd0
timeCreated: 1748758607

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b4baa2b47c0445f7837eea134618de67
timeCreated: 1748527950

View File

@ -0,0 +1,139 @@
// // ©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 DG.Tweening;
using TMPro;
using UnityEngine;
using WordsToolkit.Scripts.Data;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.GUI.Buttons.Boosts
{
public abstract class BaseBoostButton : BaseGUIButton
{
public ResourceObject resourceToPay;
public ResourceObject resourseToHoldBoost;
public TextMeshProUGUI price;
public TextMeshProUGUI countText;
public GameObject priceObject;
public GameObject countTextObject;
protected int count;
[SerializeField]
private ParticleSystem waves;
private CanvasGroup canvasGroup;
private bool isActive;
protected override void OnEnable()
{
base.OnEnable();
if (!Application.isPlaying)
{
return;
}
InitializePrice();
UpdatePriceDisplay();
onClick.AddListener(OnClick);
var main = waves.main;
main.prewarm = true; // Start particles in grown state
main.loop = true;
waves.Stop(); // Ensure it's not auto-playing
}
protected override void OnDisable()
{
base.OnDisable();
onClick.RemoveListener(OnClick);
}
protected abstract void InitializePrice();
public virtual void UpdatePriceDisplay()
{
// If we have resources in the hold boost, show that count
if (resourseToHoldBoost != null && resourseToHoldBoost.GetValue() > 0)
{
countTextObject.gameObject.SetActive(true);
priceObject.gameObject.SetActive(false);
countText.text = resourseToHoldBoost.GetValue().ToString();
}
else
{
countTextObject.gameObject.SetActive(false);
priceObject.gameObject.SetActive(true);
price.text = count.ToString();
}
}
protected void OnClick()
{
if (isActive)
{
Refund();
DeactivateBoost();
return;
}
if (resourceManager.Consume(resourseToHoldBoost, 1))
{
ActivateBoost();
}
// If not, consume from the regular resource
else if (resourceManager.ConsumeWithEffects(resourceToPay, count))
{
resourseToHoldBoost.Add(gameSettings.countOfBoostsToBuy);
UpdatePriceDisplay();
}
}
private void Refund()
{
resourseToHoldBoost.Add(1);
UpdatePriceDisplay();
}
protected virtual void ActivateBoost(bool hideButtons = true)
{
UpdatePriceDisplay();
if(hideButtons)
buttonViewController.HideOtherButtons(this);
PulseAnimation();
waves.Play();
isActive = true;
priceObject.SetActive(false);
countTextObject.SetActive(false);
}
protected virtual void DeactivateBoost()
{
isActive = false;
buttonViewController.ShowButtons();
waves.Clear();
waves.Stop();
DOTween.Complete(transform);
DOTween.Kill(transform);
transform.localScale = Vector3.one;
UpdatePriceDisplay();
}
private void PulseAnimation()
{
animator.enabled = false;
transform.DOScale(Vector3.one * 0.9f, 0.5f)
.SetLoops(-1, LoopType.Yoyo)
.SetEase(Ease.InOutSine);
}
}
}

View File

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

View File

@ -0,0 +1,49 @@
// // ©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.Gameplay;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.GUI.Buttons.Boosts
{
public class HammerBoostButton : BaseBoostButton
{
protected override void InitializePrice()
{
count = gameSettings.hammerBoostPrice;
UpdatePriceDisplay();
}
protected override void ActivateBoost(bool hideButtons = true)
{
base.ActivateBoost(hideButtons);
if (levelManager != null && !levelManager.hammerMode)
{
levelManager.hammerMode = true;
EventManager.GetEvent<Tile>(EGameEvent.TileSelected).Subscribe(OnTileSelected);
}
}
private void OnTileSelected(Tile obj)
{
DeactivateBoost();
}
protected override void DeactivateBoost()
{
levelManager.hammerMode = false;
EventManager.GetEvent<Tile>(EGameEvent.TileSelected).Unsubscribe(OnTileSelected);
base.DeactivateBoost();
}
}
}

View File

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

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.Gameplay.Managers;
namespace WordsToolkit.Scripts.GUI.Buttons.Boosts
{
public class TipBoostButton : BaseBoostButton
{
protected override void InitializePrice()
{
count = gameSettings.hintBoostPrice;
UpdatePriceDisplay();
}
protected override void ActivateBoost(bool hideButtons = true)
{
base.ActivateBoost(false);
FindObjectOfType<FieldManager>().OpenRandomTile();
DeactivateBoost();
}
protected override void DeactivateBoost()
{
base.DeactivateBoost();
}
}
}

View File

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

View File

@ -0,0 +1,88 @@
// // ©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;
namespace WordsToolkit.Scripts.GUI.Buttons
{
public interface IShowable
{
void Show();
}
public interface IHideable
{
void Hide();
void InstantHide();
}
public interface IHideableForWin
{
void HideForWin();
}
public interface IFadeable : IShowable, IHideable{}
public class ButtonViewController
{
private HashSet<IShowable> buttons;
public void RegisterButton(IShowable button)
{
if (buttons == null)
{
buttons = new HashSet<IShowable>();
}
buttons.Add(button);
if (button is IHideable hideable)
{
hideable.Hide();
}
}
public void HideOtherButtons(IShowable except)
{
foreach (var button in buttons)
{
if (!ReferenceEquals(button, except) && button is IHideable hideable)
{
hideable.Hide();
}
}
}
public void ShowButtons()
{
foreach (var button in buttons)
{
button.Show();
}
}
public void HideAllForWin()
{
if (buttons == null) return;
foreach (var button in buttons)
{
if (button is IHideableForWin buttonForWin)
{
buttonForWin.HideForWin();
}
else if(button is IHideable hideable)
{
hideable.Hide();
}
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 717db02c9e994b8ea966b55ece00dc54
timeCreated: 1748601128

View File

@ -0,0 +1,116 @@
// // ©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;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using VContainer;
using WordsToolkit.Scripts.Audio;
using WordsToolkit.Scripts.Enums;
using WordsToolkit.Scripts.System;
using WordsToolkit.Scripts.System.Haptic;
namespace WordsToolkit.Scripts.GUI.Buttons
{
[RequireComponent(typeof(Animator))]
public class CustomButton : Button
{
public AudioClip overrideClickSound;
public RuntimeAnimatorController overrideAnimatorController;
private bool isClicked;
private readonly float cooldownTime = .5f; // Cooldown time in seconds
public new ButtonClickedEvent onClick;
private new Animator animator;
public bool noSound;
private static bool blockInput;
public static CustomButton latestClickedButton;
private IAudioService audioService;
[Inject]
public void Construct(IAudioService audioService)
{
this.audioService = audioService;
}
protected override void OnEnable()
{
isClicked = false;
// run only in runtime
if (Application.isEditor)
{
return;
}
base.OnEnable();
animator = GetComponent<Animator>();
if (overrideAnimatorController != null)
{
animator.runtimeAnimatorController = overrideAnimatorController;
}
}
public override void OnPointerClick(PointerEventData eventData)
{
if (blockInput || isClicked || !interactable)
{
return;
}
if (transition != Transition.Animation)
{
Pressed();
}
isClicked = true;
if(!noSound)
audioService.PlayClick(overrideClickSound);
HapticFeedback.TriggerHapticFeedback(HapticFeedback.HapticForce.Light);
// Start cooldown
if (gameObject.activeInHierarchy)
{
StartCoroutine(Cooldown());
}
base.OnPointerClick(eventData);
}
public void Pressed()
{
if (blockInput || !interactable)
{
return;
}
latestClickedButton = this;
onClick?.Invoke();
EventManager.GetEvent<CustomButton>(EGameEvent.ButtonClicked).Invoke(this);
}
private IEnumerator Cooldown()
{
yield return new WaitForSeconds(cooldownTime);
isClicked = false;
}
private bool IsAnimationPlaying()
{
var stateInfo = animator.GetCurrentAnimatorStateInfo(0);
return stateInfo.loop || stateInfo.normalizedTime < 1;
}
public static void BlockInput(bool block)
{
blockInput = block;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c9659348afa04bc6bb30eb6900f94b65
timeCreated: 1725694504

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 UnityEngine;
using System.Collections;
namespace WordsToolkit.Scripts.GUI.Buttons
{
public class ExtraWordsButton : BaseGUIButton
{
private Coroutine occasionalPulseCoroutine;
private bool pulseEnabled;
private bool animated;
public void PulseAnimation(bool b)
{
pulseEnabled = b;
if (b)
{
animator.Play($"PulseLoop");
}
else
{
animator.SetTrigger($"Pulse");
}
}
protected override void ShowCallback()
{
base.ShowCallback();
if (pulseEnabled)
{
PulseAnimation(true);
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2c3f8f7c7b8644ec9c726828e284a7dd
timeCreated: 1748687127

View File

@ -0,0 +1,208 @@
// // ©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 UnityEngine.UI;
using VContainer;
using WordsToolkit.Scripts.Data;
using WordsToolkit.Scripts.Enums;
using WordsToolkit.Scripts.Popups;
using WordsToolkit.Scripts.Settings;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.GUI.Buttons
{
public class GiftButton : BaseGUIButton
{
private const string SAVE_KEY = "GiftButton_CollectedItems";
[Header("Counter Settings")]
[SerializeField] private TextMeshProUGUI counterText;
[SerializeField] private GameObject counterContainer;
[SerializeField] private int collectedItems = 0;
[Header("Visual Feedback")]
[SerializeField] private float pulseScale = 1.2f;
[SerializeField] private float pulseDuration = 0.3f;
[Header("Resources")]
[SerializeField] private ResourceObject resource;
[Tooltip("Optional override for gems awarded per gift (if not set, uses value from GameSettings)")]
[SerializeField] private int gemsPerGift = 0;
[SerializeField] private TextMeshProUGUI gemsValueText;
[SerializeField] private GameObject gemsLabelObject;
[Header("Visual Appearance")]
[SerializeField] private Image mainBoxSprite;
[SerializeField] private Sprite lightModeSprite;
[SerializeField] private Sprite darkModeSprite;
[Inject]
private MenuManager menuManager;
[SerializeField]
private Popup giftPopup;
[Inject]
private GameManager gameManager;
private bool isActive;
protected override void OnEnable()
{
base.OnEnable();
EventManager.GetEvent(EGameEvent.SpecialItemCollected).Subscribe(OnSpecialItemCollected);
onClick.AddListener(ConsumeResource);
}
protected override void OnDisable()
{
base.OnDisable();
EventManager.GetEvent(EGameEvent.SpecialItemCollected).Unsubscribe(OnSpecialItemCollected);
onClick.RemoveListener(ConsumeResource);
}
protected override void Start()
{
base.Start();
if(!Application.isPlaying)
{
return;
}
// Load saved collected items
collectedItems = PlayerPrefs.GetInt(SAVE_KEY, 0);
UpdateCounterDisplay();
UpdateGemsValueDisplay();
UpdateState();
}
/// <summary>
/// Updates the displayed gems value
/// </summary>
private void UpdateGemsValueDisplay()
{
if (gemsValueText != null)
{
int gemsAmount = gemsPerGift > 0 ?
gemsPerGift :
gameSettings.gemsForGift;
gemsValueText.text = gemsAmount.ToString();
}
// Update visibility of gems label
if (gemsLabelObject != null)
{
gemsLabelObject.SetActive(collectedItems > 0);
}
else if (gemsValueText != null)
{
// If no specific label object is assigned, control the text object directly
gemsValueText.gameObject.SetActive(collectedItems > 0);
}
}
/// <summary>
/// Updates the visual state of the button based on collection status
/// </summary>
private void UpdateState()
{
isActive = collectedItems > 0;
interactable = isActive;
}
/// <summary>
/// Increments the counter when a special item is collected
/// </summary>
public void CollectItem()
{
collectedItems++;
// Save the updated count
PlayerPrefs.SetInt(SAVE_KEY, collectedItems);
PlayerPrefs.Save();
UpdateCounterDisplay();
UpdateGemsValueDisplay();
UpdateState();
PlayCollectionFeedback();
}
/// <summary>
/// Updates the counter UI text
/// </summary>
private void UpdateCounterDisplay()
{
if (counterText != null)
{
counterText.text = collectedItems.ToString();
}
// Show counter only if we've collected items
if (counterContainer != null)
{
counterContainer.SetActive(collectedItems > 0);
}
}
private void ConsumeResource()
{
// Get the gems amount from GameSettings or use the override if set
int gemsAmount = gemsPerGift > 0 ?
gemsPerGift :
gameSettings.gemsForGift;
// Attempt to consume the resource and add gems
if (resource != null && resourceManager.ConsumeWithEffects(resource,gemsAmount))
{
// Decrease the counter
collectedItems--;
// Save the updated count
PlayerPrefs.SetInt(SAVE_KEY, collectedItems);
PlayerPrefs.Save();
// Update the display
UpdateCounterDisplay();
UpdateGemsValueDisplay();
UpdateState();
menuManager.ShowPopup(giftPopup);
}
}
/// <summary>
/// Play visual feedback when item is collected
/// </summary>
private void PlayCollectionFeedback()
{
// Simple pulse animation
mainBoxSprite.transform.DOScale(pulseScale, pulseDuration / 2)
.SetEase(Ease.OutQuad)
.OnComplete(() => {
mainBoxSprite.transform.DOScale(1f, pulseDuration / 2).SetEase(Ease.InQuad);
});
}
// Event handler for when a special item is collected
private void OnSpecialItemCollected()
{
CollectItem();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4d3677e4b5b644088ad9a552da7f8f1b
timeCreated: 1742478154

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 UnityEngine;
namespace WordsToolkit.Scripts.GUI
{
public class EffectDestroyer : MonoBehaviour
{
public void DestroyEffect()
{
Destroy(gameObject);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d70fddf960a6480eaa44d7e72f094f6a
timeCreated: 1742997767

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 17f6ba929b9641c6800767ce10891c98
timeCreated: 1742321438

View File

@ -0,0 +1,172 @@
// // ©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 TMPro;
using UnityEngine;
using WordsToolkit.Scripts.Enums;
using WordsToolkit.Scripts.Gameplay.Managers;
using WordsToolkit.Scripts.Levels;
using WordsToolkit.Scripts.System;
using WordsToolkit.Scripts.Settings;
using VContainer;
using WordsToolkit.Scripts.NLP;
namespace WordsToolkit.Scripts.GUI.ExtraWordBar
{
// Abstract base class for progress bars
public abstract class BaseExtraWordsProgressBar : MonoBehaviour
{
[Tooltip("Optional text display for progress")]
public TextMeshProUGUI progressText;
[Tooltip("Format string for progress text. Use {0} for current, {1} for target")]
public string textFormat = "{0}/{1}";
// Singleton instance for easy access from other scripts
public static BaseExtraWordsProgressBar Instance { get; private set; }
// Protected field for max value that can be set by derived classes
protected float maxValue = 1f;
private LevelManager levelManager;
private GameManager gameManager;
private GameSettings gameSettings;
private ICustomWordRepository wordRepository;
[Inject]
public void Construct(LevelManager levelManager, GameManager gameManager, GameSettings gameSettings, ICustomWordRepository wordRepository)
{
this.levelManager = levelManager;
this.gameManager = gameManager;
this.gameSettings = gameSettings;
this.wordRepository = wordRepository;
}
protected virtual void Awake()
{
Instance = this;
}
protected virtual void Start()
{
UpdateMaxValueFromLevel();
}
protected virtual void OnEnable()
{
EventManager.GetEvent<Level>(EGameEvent.LevelLoaded).Subscribe(OnLevelLoaded);
// Make sure to update max value when enabled
UpdateMaxValueFromLevel();
UpdateProgressBar();
}
private void OnExtraWordClaimed()
{
UpdateProgressBar();
}
private void OnLevelLoaded(Level level)
{
EventManager.GetEvent<string>(EGameEvent.ExtraWordFound).Subscribe(OnExtraWordFound);
EventManager.GetEvent(EGameEvent.ExtraWordClaimed).Subscribe(OnExtraWordClaimed);
}
protected virtual void OnDisable()
{
EventManager.GetEvent<string>(EGameEvent.ExtraWordFound).Unsubscribe(OnExtraWordFound);
EventManager.GetEvent(EGameEvent.ExtraWordClaimed).Unsubscribe(OnExtraWordClaimed);
EventManager.GetEvent<Level>(EGameEvent.LevelLoaded).Unsubscribe(OnLevelLoaded);
}
protected virtual void OnExtraWordFound(string word)
{
// Update the progress bar when a new extra word is found
UpdateProgressBar();
}
public virtual void UpdateProgressBar()
{
if (levelManager == null)
return;
// Get the current counts
int targetExtraWords = GetTargetExtraWordsCount();
int currentExtraWords = GetCurrentExtraWordsCount();
var currentValue = Mathf.Min(targetExtraWords, PlayerPrefs.GetInt("ExtraWordsCollected"));
// Calculate the normalized progress value (0-1)
float normalizedProgress = CalculateProgress(currentValue, targetExtraWords);
// Update the specific UI component (each implementation can scale as needed)
UpdateProgressDisplay(normalizedProgress);
// Update the text if available
UpdateTextDisplay(currentValue, targetExtraWords);
}
// Abstract method that must be implemented by derived classes
protected abstract void UpdateProgressDisplay(float progress);
// New method to update max value from level data
protected virtual void UpdateMaxValueFromLevel()
{
int targetExtraWords = GetTargetExtraWordsCount();
SetMaxValue(targetExtraWords);
}
// Method to set the max value for this progress bar
public virtual void SetMaxValue(float value)
{
if (value <= 0)
value = 1f; // Ensure we don't have invalid values
maxValue = value;
// Call implementation-specific method to apply the new max value
ApplyMaxValue();
}
// Virtual method that derived classes will override to apply max value to their specific component
protected virtual void ApplyMaxValue()
{
// Base implementation does nothing
}
// Gets the target extra words count from the current level's group or fallback to game settings
protected int GetTargetExtraWordsCount()
{
var currentLevelGroup = GameDataManager.GetLevel().GetGroup();
return Mathf.Max(1, currentLevelGroup.targetExtraWords); // Ensure it's at least 1 to prevent division by zero
}
// Gets the current count of extra words found
protected int GetCurrentExtraWordsCount()
{
return wordRepository.GetExtraWordsCount();
}
// Calculate normalized progress as a value between 0 and 1
protected float CalculateProgress(int current, int target)
{
return Mathf.Clamp01((float)current / target);
}
// Updates the text display if available
protected void UpdateTextDisplay(int currentValue, int targetValue)
{
if (progressText != null)
{
progressText.text = string.Format(textFormat, currentValue, targetValue);
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c1c50d302d02427d9efaed58b590f731
timeCreated: 1742318054

View File

@ -0,0 +1,63 @@
// // ©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.GUI.Buttons;
namespace WordsToolkit.Scripts.GUI.ExtraWordBar
{
public class ImageExtraWordsProgressBar : BaseExtraWordsProgressBar
{
[Tooltip("Image component used as progress bar")]
public Image progressBar;
[Tooltip("Speed of the fill animation (higher is faster)")]
[Range(1f, 10f)]
public float interpolationSpeed = 5f;
// Current displayed fill amount (for interpolation)
private float _currentFill;
// Target fill amount we're interpolating toward
private float _targetFill;
[SerializeField]
private ExtraWordsButton extraWordsButton;
protected override void UpdateProgressDisplay(float progress)
{
if (progressBar != null)
{
// Set the target fill amount instead of directly setting fillAmount
_targetFill = progress;
extraWordsButton.PulseAnimation(progress == 1);
}
}
private void Update()
{
// Smoothly interpolate the current fill amount towards the target
if (progressBar != null && !Mathf.Approximately(_currentFill, _targetFill))
{
_currentFill = Mathf.Lerp(_currentFill, _targetFill, Time.deltaTime * interpolationSpeed);
// If we're very close to the target, snap to it
if (Mathf.Abs(_currentFill - _targetFill) < 0.005f)
{
_currentFill = _targetFill;
}
progressBar.fillAmount = _currentFill;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: afb2b6c16a9f44c5be59528504d23ca6
timeCreated: 1742321446

View File

@ -0,0 +1,51 @@
// // ©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.GUI.ExtraWordBar
{
public class SliderExtraWordsProgressBar : BaseExtraWordsProgressBar
{
[Tooltip("Slider component used as progress bar")]
public Slider progressSlider;
protected override void Start()
{
base.Start();
// Configure the slider's min value
if (progressSlider != null)
{
progressSlider.minValue = 0;
}
}
protected override void UpdateProgressDisplay(float progress)
{
if (progressSlider != null)
{
progressSlider.value = PlayerPrefs.GetInt("ExtraWordsCollected");
}
}
// Apply max value changes to the slider component
protected override void ApplyMaxValue()
{
if (progressSlider != null)
{
progressSlider.maxValue = maxValue;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0cb7c2a9625d4e8b964c8d627dfe1e43
timeCreated: 1742321457

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.GUI
{
public class FXCanvas : MonoBehaviour
{
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b6413f9cec49418a89c0595be4a8f0e2
timeCreated: 1730275156

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 31b6dae04c644f229860c8c3d0ca1275
timeCreated: 1725686154

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 System;
using DG.Tweening;
using UnityEngine;
using WordsToolkit.Scripts.Gameplay.Pool;
using Random = UnityEngine.Random;
namespace WordsToolkit.Scripts.GUI.Labels
{
public class CurrencyLabelAnimation : LabelAnim, ILabelAnimation
{
public void Animate(GameObject sourceObject, Vector3 startPosition, string rewardDataCount, AudioClip sound, Action callback)
{
var count = 0;
var animateCount = 4;
var targetPosition = targetTransform.transform.position;
// if (coinsTextPrefab != null)
// {
// PopupText(startPosition, rewardDataCount);
// }
if (targetTransform)
{
for (var i = 0; i < animateCount; i++)
{
var item = GetAnimatedObjectSource(sourceObject);
var random = .5f;
item.transform.position = startPosition + new Vector3(Random.Range(-random, random), Random.Range(-random, random));
var randomStartDelay = i==0? 0 : Random.Range(0f, 0.5f);
StartAnim(item.transform, targetPosition, randomStartDelay, () =>
{
animatedObjectSource.ReleaseObject(item);
if (doPunchScale == null || !doPunchScale.IsPlaying())
{
var punchScale = transform.DOPunchScale(Vector3.one * 0.2f, 0.2f);
punchScale.OnComplete(() => { doPunchScale = null; });
doPunchScale = punchScale;
}
if (fxPrefab != null)
{
var fx = PoolObject.GetObject(fxPrefab, targetPosition);
fx.transform.localScale = Vector3.one;
fx.transform.position = targetPosition;
DOVirtual.DelayedCall(1f, () => { PoolObject.Return(fx); });
}
if (count == 0)
{
_audioService.PlaySound(sound);
}
count++;
if (count == animateCount)
{
transform.localScale = Vector3.one;
callback?.Invoke();
DOVirtual.DelayedCall(.5f, () => { DOTween.Kill(gameObject); });
}
PoolObject.Return(item);
});
}
}
}
private void StartAnim(Transform targetTransform, Vector3 targetPos, float randomStartDelay, Action callback = null)
{
var sequence = DOTween.Sequence();
targetTransform.localScale = Vector3.zero;
var _scaleTween = targetTransform.DOScale(Vector3.one * 1f, .5f)
.SetEase(Ease.OutBack)
.SetDelay(randomStartDelay);
sequence.Append(_scaleTween);
var _rotationTween = targetTransform.DORotate(new Vector3(0, 0, Random.Range(0, 360)), .3f)
.SetEase(Ease.Linear)
.SetLoops(1, LoopType.Incremental);
sequence.Join(_rotationTween);
var _movementTween = targetTransform.DOMove(targetPos, .3f)
.SetEase(Ease.Linear)
.OnComplete(() =>
{
callback?.Invoke();
targetTransform.gameObject.SetActive(false);
});
sequence.Join(_movementTween);
sequence.Play();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 70fae662836044c79412a304f4a5d3f3
timeCreated: 1748447122

View File

@ -0,0 +1,104 @@
// // ©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 WordsToolkit.Scripts.Gameplay.Pool;
using Random = UnityEngine.Random;
namespace WordsToolkit.Scripts.GUI.Labels
{
public class GemsLabelAnimation : LabelAnim, ILabelAnimation
{
public void Animate(GameObject sourceObject, Vector3 startPosition, string rewardDataCount, AudioClip sound, Action callback)
{
var count = 0;
var animateCount = 4;
var targetPosition = targetTransform.transform.position;
// if (coinsTextPrefab != null)
// {
// PopupText(startPosition, rewardDataCount);
// }
if (targetTransform)
{
for (var i = 0; i < animateCount; i++)
{
var item = GetAnimatedObjectSource(sourceObject);
var random = .5f;
item.transform.position = startPosition + new Vector3(Random.Range(-random, random), Random.Range(-random, random));
var randomStartDelay = i==0? 0 : Random.Range(0f, 0.5f);
if (count == 0)
{
_audioService.PlayDelayed(sound, 1.0f);
}
StartAnim(item.transform, targetPosition, randomStartDelay, () =>
{
animatedObjectSource.ReleaseObject(item);
if (doPunchScale == null || !doPunchScale.IsPlaying())
{
var punchScale = transform.DOPunchScale(Vector3.one * 0.2f, 0.2f);
punchScale.OnComplete(() => { doPunchScale = null; });
doPunchScale = punchScale;
}
if (fxPrefab != null)
{
var fx = PoolObject.GetObject(fxPrefab, targetPosition);
fx.transform.localScale = Vector3.one;
fx.transform.position = targetPosition;
DOVirtual.DelayedCall(1f, () => { PoolObject.Return(fx); });
}
count++;
if (count == animateCount)
{
transform.localScale = Vector3.one;
callback?.Invoke();
DOVirtual.DelayedCall(.5f, () => { DOTween.Kill(gameObject); });
}
PoolObject.Return(item);
});
}
}
}
private void StartAnim(Transform targetTransform, Vector3 targetPos, float randomStartDelay, Action callback = null)
{
var sequence = DOTween.Sequence();
targetTransform.localScale = Vector3.zero;
var _scaleTween = targetTransform.DOScale(Vector3.one * 1f, .5f)
.SetEase(Ease.OutBack)
.SetDelay(randomStartDelay);
sequence.Append(_scaleTween);
var _rotationTween = targetTransform.DORotate(new Vector3(0, 0, Random.Range(0, 360)), .3f)
.SetEase(Ease.Linear)
.SetLoops(1, LoopType.Incremental);
sequence.Join(_rotationTween);
var _movementTween = targetTransform.DOMove(targetPos, 0.5f)
.SetEase(Ease.Linear)
.OnComplete(() =>
{
callback?.Invoke();
targetTransform.gameObject.SetActive(false);
});
sequence.Append(_movementTween);
sequence.Play();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5011c5819a984dbeac491d82a8128433
timeCreated: 1749031939

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.GUI.Labels
{
public class HammerLabelAnimation : IconLabel
{
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 339d208ff02448daae7bbd7af2000c48
timeCreated: 1748439618

View File

@ -0,0 +1,68 @@
// // ©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.Pool;
namespace WordsToolkit.Scripts.GUI.Labels
{
public interface IAnimationSource
{
GameObject GetAnimatedObject();
void ReleaseObject(GameObject animatedObject);
}
public class PooledAnimationSource : IAnimationSource
{
private readonly ObjectPool<GameObject> _pool;
public PooledAnimationSource(GameObject prefab, Transform transform)
{
_pool = new ObjectPool<GameObject>(
() => Object.Instantiate(prefab, transform),
animatedObject => animatedObject.SetActive(true),
animatedObject => animatedObject.SetActive(false),
Object.Destroy
);
}
public GameObject GetAnimatedObject()
{
return _pool.Get();
}
public void ReleaseObject(GameObject animatedObject)
{
_pool.Release(animatedObject);
}
}
public class SingleObjectAnimationSource : IAnimationSource
{
private readonly GameObject _animatedObject;
public SingleObjectAnimationSource(GameObject animatedObject)
{
_animatedObject = animatedObject;
}
public GameObject GetAnimatedObject()
{
return _animatedObject;
}
public void ReleaseObject(GameObject animatedObject)
{
animatedObject.SetActive(false);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ad866880d5bd4e2a8c3bfe3bfb298d9c
timeCreated: 1748509775

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.
using System;
using UnityEngine;
using WordsToolkit.Scripts.Popups.RewardsGift;
namespace WordsToolkit.Scripts.GUI.Labels
{
public interface ILabelAnimation
{
void Animate(GameObject sourceObject, Vector3 startPosition, string rewardDataCount, AudioClip sound, Action callback);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f7324cbdb7e04d8784e7b074ce95de5f
timeCreated: 1748447755

View File

@ -0,0 +1,60 @@
// // ©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 WordsToolkit.Scripts.Gameplay.Pool;
using WordsToolkit.Scripts.Popups.RewardsGift;
namespace WordsToolkit.Scripts.GUI.Labels
{
public class IconLabel : LabelAnim, ILabelAnimation
{
public void Animate(GameObject sourceObject, Vector3 startPosition, string rewardDataCount, AudioClip sound, Action callback)
{
sourceObject.GetComponent<GiftBase>().HideText();
var animatedObject = GetAnimatedObjectSource(sourceObject);
Vector3 targetPos = targetTransform.position;
animatedObject.transform.position = startPosition;
var sequence = DOTween.Sequence();
var _decreaseScaleTween = animatedObject.transform.DOScale(Vector3.one * 0.1f, .3f)
.SetEase(Ease.Linear);
sequence.Join(_decreaseScaleTween);
var _movementTween = animatedObject.transform.DOMove(targetPos, .3f)
.SetEase(Ease.Linear);
sequence.Join(_movementTween);
// bounce after movement
var _bounceTween = animatedObject.transform.DOScale(Vector3.one * .3f, .4f)
.SetEase(Ease.OutBack)
.OnComplete(() =>
{
_audioService.PlaySoundExclusive(sound);
// play fx
if (fxPrefab != null)
{
var fx = PoolObject.GetObject(fxPrefab, targetPos);
fx.transform.position = targetPos;
DOVirtual.DelayedCall(1f, () => { PoolObject.Return(fx); });
}
Release(animatedObject);
callback?.Invoke();
OnAnimationComplete?.Invoke();
});
sequence.Append(_bounceTween);
sequence.Play();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cf51bcfa784c4a1ba25080cb5268d346
timeCreated: 1748519904

View File

@ -0,0 +1,69 @@
// // ©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 UnityEngine.Events;
using VContainer;
using WordsToolkit.Scripts.Audio;
using WordsToolkit.Scripts.Data;
using WordsToolkit.Scripts.Gameplay.Pool;
namespace WordsToolkit.Scripts.GUI.Labels
{
public class LabelAnim : MonoBehaviour
{
public GameObject prefab;
public Transform targetTransform;
public ResourceObject associatedResource;
[SerializeField]
protected GameObject fxPrefab;
[SerializeField]
protected TextMeshProUGUI coinsTextPrefab;
protected Tweener doPunchScale;
[Inject]
protected IAudioService _audioService;
protected IAnimationSource animatedObjectSource;
[SerializeField]
protected UnityEvent OnAnimationComplete;
protected GameObject GetAnimatedObjectSource(GameObject o)
{
animatedObjectSource ??= new PooledAnimationSource(o??prefab, transform);
return animatedObjectSource.GetAnimatedObject();
}
protected void Release(GameObject o)
{
animatedObjectSource?.ReleaseObject(o);
}
protected void PopupText(Vector3 transformPosition, string rewardDataCount)
{
var coinsText = PoolObject.GetObject(coinsTextPrefab.gameObject, transformPosition).GetComponent<TextMeshProUGUI>();
coinsText.transform.position = transformPosition;
coinsText.text = rewardDataCount;
coinsText.alpha = 0;
var sequence = DOTween.Sequence();
sequence.Append(coinsText.DOFade(1, 0.2f));
sequence.Join(coinsText.transform.DOMoveY(coinsText.transform.position.y + .5f, .5f)).OnComplete(() => { PoolObject.Return(coinsText.gameObject); });
sequence.Append(coinsText.DOFade(0, 0.2f));
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9a33679f003442d482254fce1279dc4e
timeCreated: 1722963833

View File

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

View File

@ -0,0 +1,50 @@
// // ©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 WordsToolkit.Scripts.Data;
using Object = UnityEngine.Object;
namespace WordsToolkit.Scripts.GUI.Labels
{
public static class ResourceAnimationController
{
public static void AnimateForResource(ResourceObject resourceObject, GameObject animatedObject, Vector3 startPosition, string rewardDataCount, AudioClip sound,
Action callback)
{
var label = FindLabelForResource(resourceObject);
if (label != null)
{
label.Animate(animatedObject, startPosition, rewardDataCount, sound, callback);
}
else
{
callback?.Invoke();
}
}
private static ILabelAnimation FindLabelForResource(ResourceObject resourceObject)
{
var allLabels = Object.FindObjectsOfType<LabelAnim>();
foreach (var label in allLabels)
{
if (label.associatedResource == resourceObject)
{
return label as ILabelAnimation;
}
}
return null;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a27ac2d882a446c38e9418f664e5238e
timeCreated: 1748412719

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 TMPro;
using UnityEngine;
using WordsToolkit.Scripts.Data;
namespace WordsToolkit.Scripts.GUI.Labels
{
public class ResourceLabel : MonoBehaviour
{
public ResourceObject resourceObject;
[SerializeField]
private TextMeshProUGUI text;
protected virtual void OnEnable()
{
resourceObject.OnResourceUpdate += UpdateValue;
UpdateValue(resourceObject.GetValue());
}
protected virtual void OnDisable()
{
resourceObject.OnResourceUpdate -= UpdateValue;
}
protected virtual void UpdateValue(int count)
{
text.text = count.ToString();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c79bf792c0d841e59f4e9ce0e4f13cc8
timeCreated: 1716450501

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.GUI.Labels
{
public class TipLabelAnimation : IconLabel
{
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 996433fa960e41578e8420db55d6317f
timeCreated: 1748439593

View File

@ -0,0 +1,75 @@
// // ©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 VContainer;
using WordsToolkit.Scripts.Enums;
using WordsToolkit.Scripts.Gameplay.Managers;
using WordsToolkit.Scripts.GUI.Buttons;
using WordsToolkit.Scripts.Levels;
using WordsToolkit.Scripts.Localization;
using WordsToolkit.Scripts.Popups;
using WordsToolkit.Scripts.Services;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.GUI
{
public class LanguageSelectionButton : MonoBehaviour
{
public TextMeshProUGUI languageText;
public CustomButton button;
[Inject]
private StateManager stateManager;
[Inject]
private MenuManager menuManager;
[Inject]
private ILanguageService languageService;
private void OnEnable()
{
button.onClick.AddListener(OnClick);
languageText.text = LocalizationManager.instance.GetLocalizedCurrentLanguage();
EventManager.GetEvent<string>(EGameEvent.LanguageChanged).Subscribe(OnLanguageChanged);
}
private void OnDisable()
{
button.onClick.RemoveListener(OnClick);
EventManager.GetEvent<string>(EGameEvent.LanguageChanged).Unsubscribe(OnLanguageChanged);
}
private void OnLanguageChanged(string obj)
{
UpdateText();
}
private void UpdateText()
{
languageText.text = LocalizationManager.instance.GetLocalizedCurrentLanguage();
}
private void OnClick()
{
ShowLanguageSelector();
}
public void ShowLanguageSelector()
{
menuManager.CloseAllPopups();
menuManager.ShowPopup<LanguageSelectionGame>(null, result =>
{
UpdateText();
});
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ce23d940581c42ddb21ae9b4c053a1fb
timeCreated: 1742713425

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 TMPro;
using UnityEngine;
namespace WordsToolkit.Scripts.GUI
{
public class LanguageSelectionElement : MonoBehaviour
{
[SerializeField]
private TextMeshProUGUI languageName;
[SerializeField]
private GameObject checkMark;
[SerializeField]
private GameObject selectedBackground;
private void OnEnable()
{
SetCheckMarkActive(false);
}
public void SetLanguageName(string name)
{
languageName.text = name;
}
public void SetCheckMarkActive(bool isActive)
{
checkMark.SetActive(isActive);
selectedBackground.SetActive(isActive);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 34f6491ebc5f45a48522da26ec0beb4d
timeCreated: 1742718301

View File

@ -0,0 +1,191 @@
// // ©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 TMPro;
using UnityEngine;
using UnityEngine.UI;
using VContainer;
using VContainer.Unity;
using WordsToolkit.Scripts.Enums;
using WordsToolkit.Scripts.GUI.Buttons;
using WordsToolkit.Scripts.Levels;
using WordsToolkit.Scripts.Localization;
using WordsToolkit.Scripts.Popups;
using WordsToolkit.Scripts.Services;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.GUI
{
public class LanguageSelectionGame : Popup
{
private List<LanguageConfiguration.LanguageInfo> languages;
[SerializeField] private LanguageSelectionElement languageButtonPrefab;
[SerializeField] private Transform languageButtonContainer;
[SerializeField] private ScrollRect scrollView;
[SerializeField] private TextMeshProUGUI selectedLanguageText;
public CurtureTuple[] extraLanguages;
[SerializeField]
private LanguageConfiguration languageConfiguration;
[Inject]
private ILanguageService languageService;
private Button[] languageButtons;
private int currentSelectedIndex = -1;
private void Start()
{
// Check for layout component on container
if (languageButtonContainer.GetComponent<LayoutGroup>() == null)
{
Debug.LogWarning("No LayoutGroup component found on languageButtonContainer. Elements may overlap.");
}
// Get enabled languages from configuration
languages = languageConfiguration.GetEnabledLanguages();
// Add extra languages if configured
if (extraLanguages != null && extraLanguages.Length > 0)
{
foreach (var extraLang in extraLanguages)
{
var langInfo = languageConfiguration.GetLanguageInfo(extraLang.culture);
if (langInfo != null && !languages.Contains(langInfo))
{
languages.Add(langInfo);
}
}
}
LocalizationManager.instance.InitializeLocalization();
PopulateLanguageButtons();
}
private void PopulateLanguageButtons()
{
// Clear existing buttons if any
foreach (Transform child in languageButtonContainer)
{
Destroy(child.gameObject);
}
var currentLanguageCode = languageService.GetCurrentLanguageCode();
currentSelectedIndex = languages.FindIndex(l => l.code == currentLanguageCode);
languageButtons = new Button[languages.Count];
for (int i = 0; i < languages.Count; i++)
{
var buttonObj = _container.Instantiate(languageButtonPrefab, languageButtonContainer);
var button = buttonObj.GetComponent<CustomButton>();
buttonObj.SetLanguageName(languages[i].localizedName.ToUpper());
// Ensure proper positioning in layout
RectTransform rectTransform = buttonObj.GetComponent<RectTransform>();
rectTransform.localScale = Vector3.one;
int index = i; // Capture the index for the lambda
button.onClick.AddListener(() =>
{
SelectLanguage(index);
});
languageButtons[i] = button;
// if the language is selected, set the checkmark active
buttonObj.SetCheckMarkActive(i == currentSelectedIndex);
}
// Force layout rebuild
LayoutRebuilder.ForceRebuildLayoutImmediate(languageButtonContainer.GetComponent<RectTransform>());
Canvas.ForceUpdateCanvases();
// Make sure the content size fitter is updated if present
if (languageButtonContainer.GetComponent<ContentSizeFitter>() != null)
{
languageButtonContainer.GetComponent<ContentSizeFitter>().SetLayoutVertical();
}
}
private void SelectLanguage(int index)
{
if (index < 0 || index >= languages.Count)
return;
// First deactivate all checkmarks
for (int i = 0; i < languageButtons.Length; i++)
{
if (languageButtons[i].GetComponent<LanguageSelectionElement>() != null)
{
languageButtons[i].GetComponent<LanguageSelectionElement>().SetCheckMarkActive(false);
}
}
// Then activate only the selected one
if (languageButtons[index].GetComponent<LanguageSelectionElement>() != null)
{
languageButtons[index].GetComponent<LanguageSelectionElement>().SetCheckMarkActive(true);
}
// Update visual selection
if (currentSelectedIndex >= 0 && currentSelectedIndex < languageButtons.Length)
{
// Reset previous selection visual if needed
ColorBlock colors = languageButtons[currentSelectedIndex].colors;
colors.normalColor = Color.white;
languageButtons[currentSelectedIndex].colors = colors;
}
currentSelectedIndex = index;
// Scroll to the selected button
Canvas.ForceUpdateCanvases();
var selectedLanguage = languages[index];
// Use LanguageService to set the language
languageService.SetLanguage(selectedLanguage.code);
// find all objects of LocalizationTextMeshProUGUI
var localizationTextObjects = FindObjectsOfType<LocalizedTextMeshProUGUI>();
foreach (var localizationText in localizationTextObjects)
{
localizationText.UpdateText();
}
}
public void OnChangeLanguage()
{
if (currentSelectedIndex >= 0)
{
var selectedLanguage = languages[currentSelectedIndex];
if (selectedLanguage.localizationBase != null)
{
LocalizationManager.instance.LoadLanguageFromBase(selectedLanguage.localizationBase);
}
}
}
private LanguageConfiguration.LanguageInfo GetSelectedLanguage()
{
return languages[currentSelectedIndex];
}
}
[Serializable]
public struct CurtureTuple
{
public string culture;
public string name;
}
}

View File

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

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 DG.Tweening;
using UnityEngine;
using WordsToolkit.Scripts.Popups;
using WordsToolkit.Scripts.System;
using VContainer;
using WordsToolkit.Scripts.GUI.Buttons;
using WordsToolkit.Scripts.Services;
namespace WordsToolkit.Scripts.GUI
{
public class NoAdsButton : CustomButton
{
[Inject]
private MenuManager menuManager;
[Inject]
private GameManager gameManager;
[Inject]
private IAdsManager adsManager;
protected override void OnEnable()
{
base.OnEnable();
if (adsManager != null && adsManager.IsNoAdsPurchased())
{
gameObject.SetActive(false);
}
onClick.AddListener(() =>
{
menuManager.CloseAllPopups();
DOVirtual.DelayedCall(0.5f, () =>
{
gameManager.RemoveAds();
});
});
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 38cf47ea73764d40996e1d8445fdec1c
timeCreated: 1726470327

View File

@ -0,0 +1,33 @@
// // ©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 UnityEngine;
using UnityEngine.UI;
namespace WordsToolkit.Scripts.GUI
{
public class OutlineField : MonoBehaviour
{
[SerializeField]
private Image imageToFade;
[SerializeField]
private Image imageToFade2;
private void Start()
{
imageToFade.DOFade(.5f, .5f).SetLoops(-1, LoopType.Yoyo);
imageToFade2.DOFade(.5f, .5f).SetLoops(-1, LoopType.Yoyo);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 68b5702063f7452fb8347a9e17336c1c
timeCreated: 1728481029

View File

@ -0,0 +1,76 @@
// // ©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.GUI
{
[ExecuteAlways]
public class SafeArea : MonoBehaviour
{
[SerializeField]
private RectTransform rectCanvasTransform;
private Rect lastSafeArea = Rect.zero;
private Vector2 lastScreenSize = Vector2.zero;
private void Awake()
{
ApplySafeArea();
}
private void OnRectTransformDimensionsChange()
{
ApplySafeArea();
}
#if UNITY_EDITOR
private void OnValidate()
{
ApplySafeArea();
}
#endif
private void ApplySafeArea()
{
if (rectCanvasTransform == null)
{
return;
}
var safeArea = Screen.safeArea;
var screenSize = new Vector2(Screen.width, Screen.height);
if (safeArea != lastSafeArea || screenSize != lastScreenSize)
{
lastSafeArea = safeArea;
lastScreenSize = screenSize;
var anchorMin = safeArea.position;
var anchorMax = safeArea.position + safeArea.size;
if (screenSize.x == 0 || screenSize.y == 0)
{
Debug.LogError($"Screen size is zero: {screenSize}");
return;
}
anchorMin.x /= screenSize.x;
anchorMin.y /= screenSize.y;
anchorMax.x /= screenSize.x;
anchorMax.y /= screenSize.y;
rectCanvasTransform.anchorMin = anchorMin;
rectCanvasTransform.anchorMax = anchorMax;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a662ae09fc7e48e38df93e3acf55ed59
timeCreated: 1725705596

View File

@ -0,0 +1,192 @@
using DG.Tweening;
using TMPro;
using UnityEngine;
using VContainer;
using WordsToolkit.Scripts.Audio;
using WordsToolkit.Scripts.Gameplay.Managers;
using WordsToolkit.Scripts.Utils;
using WordsToolkit.Scripts.System;
using WordsToolkit.Scripts.Enums;
namespace WordsToolkit.Scripts.GUI
{
[RequireComponent(typeof(TextMeshProUGUI))]
public class TimerDisplay : MonoBehaviour
{
private TextMeshProUGUI timerText;
[Inject]
private LevelManager levelManager;
[Inject]
private IAudioService audioService;
private CanvasGroup canvasGroup;
private Color originalTextColor;
[SerializeField]
private AudioClip alertSound;
private Sequence bounceSequence;
private Sequence fadeSequence;
private bool isWarningActive;
private bool isVisible = true;
private void Awake()
{
timerText = GetComponent<TextMeshProUGUI>();
canvasGroup = GetComponent<CanvasGroup>();
// Add canvas group if it doesn't exist
if (canvasGroup == null)
canvasGroup = gameObject.AddComponent<CanvasGroup>();
}
private void Start()
{
originalTextColor = timerText.color;
EventManager.OnGameStateChanged += OnGameStateChanged;
UpdateVisibility();
}
private void OnDestroy()
{
EventManager.OnGameStateChanged -= OnGameStateChanged;
if (bounceSequence != null)
{
bounceSequence.Kill();
bounceSequence = null;
}
if (fadeSequence != null)
{
fadeSequence.Kill();
fadeSequence = null;
}
}
private void Update()
{
if (levelManager != null && isVisible)
{
float timeToDisplay;
bool isTimerActive = levelManager.HasTimer;
// Update timer text - display as countdown
if (levelManager.HasTimer && levelManager.TimerLimit > 0)
{
// Calculate remaining time
float remainingTime = Mathf.Max(0, levelManager.TimerLimit - levelManager.GameTime);
timerText.text = TimeUtils.GetTimeString(remainingTime);
timeToDisplay = remainingTime;
}
else
{
// For timers without limits, just show elapsed time
timerText.text = TimeUtils.GetTimeString(levelManager.GameTime);
timeToDisplay = levelManager.GameTime;
}
if (timeToDisplay <= 10f && !isWarningActive && isTimerActive)
{
StartWarningEffect();
}
else if (timeToDisplay > 10f && isWarningActive)
{
StopWarningEffect();
}
}
}
private void OnGameStateChanged(EGameState newState)
{
UpdateVisibility();
}
private void UpdateVisibility()
{
bool shouldBeVisible = levelManager.HasTimer && EventManager.GameStatus <= EGameState.Playing;
if (shouldBeVisible && !isVisible)
{
ShowTimer();
}
else if (!shouldBeVisible && isVisible)
{
HideTimer();
}
}
private void ShowTimer()
{
if (fadeSequence != null)
{
fadeSequence.Kill();
fadeSequence = null;
}
isVisible = true;
canvasGroup.interactable = true;
canvasGroup.blocksRaycasts = true;
fadeSequence = DOTween.Sequence();
fadeSequence.Append(canvasGroup.DOFade(1f, 0.3f));
}
private void HideTimer()
{
if (fadeSequence != null)
{
fadeSequence.Kill();
fadeSequence = null;
}
isVisible = false;
fadeSequence = DOTween.Sequence();
fadeSequence.Append(canvasGroup.DOFade(0f, 0.5f));
fadeSequence.OnComplete(() =>
{
canvasGroup.interactable = false;
canvasGroup.blocksRaycasts = false;
});
}
private void StartWarningEffect()
{
if (timerText == null) return;
if (alertSound != null)
audioService.PlaySound(alertSound);
isWarningActive = true;
originalTextColor = timerText.color;
if (bounceSequence != null)
{
bounceSequence.Kill();
bounceSequence = null;
}
bounceSequence = DOTween.Sequence();
bounceSequence.Append(timerText.transform.DOScale(1.2f, 0.3f));
bounceSequence.Append(timerText.transform.DOScale(1f, 0.3f));
timerText.color = new Color32(0xF6, 0x42, 0x8E, 0xFF);
bounceSequence.SetLoops(-1);
}
private void StopWarningEffect()
{
if (timerText == null) return;
isWarningActive = false;
if (bounceSequence != null)
{
bounceSequence.Kill();
bounceSequence = null;
}
timerText.transform.localScale = Vector3.one;
timerText.color = originalTextColor;
}
}
}

View File

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

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e2ae417f18304616bd2f3c81783575b2
timeCreated: 1748527886

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.GUI.Tutorials
{
public class TutorialExtraWords : TutorialPopupUI
{
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e7d3fb59d92c40efa3b8e25774fd7ed2
timeCreated: 1748240603

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 UnityEngine;
using WordsToolkit.Scripts.Localization;
using WordsToolkit.Scripts.Popups;
using WordsToolkit.Scripts.Settings;
namespace WordsToolkit.Scripts.GUI.Tutorials
{
public class TutorialPopupBase : Popup
{
[SerializeField]
private LocalizedTextMeshProUGUI text;
protected TutorialSettingsData tutorialData;
protected GameObject[] GetObjectsOfTagsToShow()
{
if (tagsToShow == null || tagsToShow.Length == 0)
return null;
var tags = new GameObject[tagsToShow.Length];
for (int i = 0; i < tagsToShow.Length; i++)
{
var t = GameObject.FindGameObjectWithTag(tagsToShow[i]);
if (t == null)
Debug.LogError($"TutorialPopupBase: GetTagsToShow: {tagsToShow[i]} found: {t != null}");
if (t != null)
{
tags[i] = t;
}
}
return tags;
}
public void SetTitle(string getText)
{
if (text != null)
{
text.text = getText;
}
}
public void SetData(TutorialSettingsData tutorialData)
{
this.tutorialData = tutorialData;
ShowTags(tutorialData.tagsToShow);
}
public TutorialSettingsData GetData()
{
return tutorialData;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 62879301cce14e229585844c2d2e1d5e
timeCreated: 1748157756

View File

@ -0,0 +1,45 @@
// // ©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 DG.Tweening;
using UnityEngine;
using VContainer;
using WordsToolkit.Scripts.Enums;
using WordsToolkit.Scripts.Gameplay;
using WordsToolkit.Scripts.Gameplay.Managers;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.GUI.Tutorials
{
public class TutorialPopupGame : TutorialWordSubstitution
{
public override void AfterShowAnimation()
{
base.AfterShowAnimation();
var words = levelManager.GetCurrentLevel().GetWords(gameManager.language);
ReplaceWordForTutorial(words[0]);
SelectWordAnimation(words[0]);
var tiles = fieldManager.GetTilesWord(words[0]);
if (tiles != null && tiles.Count > 0)
{
foreach (var tile in tiles)
{
if (tile != null)
{
MakeObjectVisible(tile.gameObject);
}
}
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 71d0ca3b60b74d44bcff8d7f94a91ac8
timeCreated: 1748156902

View File

@ -0,0 +1,95 @@
// // ©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 System.Collections;
namespace WordsToolkit.Scripts.GUI.Tutorials
{
public class TutorialPopupUI : TutorialPopupBase
{
[SerializeField]
protected GameObject arrow;
protected Coroutine arrowAnimation;
protected GameObject targetObject;
protected float arrowOffset = 1.0f;
public override void AfterShowAnimation()
{
base.AfterShowAnimation();
targetObject = GetObjectsOfTagsToShow()[0];
if (targetObject != null && arrow != null)
{
Vector3 targetPos = targetObject.transform.position;
if (targetPos.x < 0)
{
arrow.transform.localScale = new Vector3(-1, 1, 1);
arrowOffset = 1.0f;
}
else
{
arrow.transform.localScale = new Vector3(1, 1, 1);
arrowOffset = -1.0f;
}
UpdateArrowPosition();
arrow.SetActive(true);
arrowAnimation = StartCoroutine(AnimateArrow());
}
}
protected void UpdateArrowPosition()
{
if (targetObject != null && arrow != null)
{
Vector3 targetPos = targetObject.transform.position;
arrow.transform.position = new Vector3(targetPos.x + arrowOffset, targetPos.y, targetPos.z);
}
}
protected IEnumerator AnimateArrow()
{
float time = 0f;
float wobbleAmount = 0.1f;
while (true)
{
time += Time.deltaTime * 2f; // Complete one cycle in 0.5 seconds
UpdateArrowPosition();
// Add small wobble animation
float wobble = Mathf.PingPong(time, 1f) * wobbleAmount;
Vector3 currentPos = arrow.transform.position;
arrow.transform.position = new Vector3(currentPos.x + (arrowOffset > 0 ? wobble : -wobble), currentPos.y, currentPos.z);
yield return null;
}
}
public override void AfterHideAnimation()
{
base.AfterHideAnimation();
if (arrow != null)
{
arrow.SetActive(false);
if (arrowAnimation != null)
{
StopCoroutine(arrowAnimation);
arrowAnimation = null;
}
}
targetObject = null;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2fb722bb3ee846c9b50fb4fdd88276d1
timeCreated: 1748086797

View File

@ -0,0 +1,36 @@
// // ©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.GUI.Tutorials
{
public class TutorialRedGem : TutorialWordSubstitution
{
public override void AfterShowAnimation()
{
base.AfterShowAnimation();
var tiles = fieldManager.GetTilesWordWithSpecialItems(out var word);
ReplaceWordForTutorial(word);
SelectWordAnimation(word);
// make tiles above UI
if (tiles != null && tiles.Count > 0)
{
foreach (var tile in tiles)
{
if (tile != null)
{
MakeObjectVisible(tile.gameObject);
}
}
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c69baf91b6764c6d9e5a061a9ca87959
timeCreated: 1748266655

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 UnityEngine;
namespace WordsToolkit.Scripts.GUI.Tutorials
{
public class TutorialTime : TutorialPopupUI
{
public override void AfterShowAnimation()
{
base.AfterShowAnimation();
targetObject = GetObjectsOfTagsToShow()[0];
if (targetObject != null && arrow != null)
{
UpdateArrowPosition();
arrow.SetActive(true);
arrowAnimation = StartCoroutine(AnimateArrow());
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 08a2702beacc4f3eae33f19c8558a0c1
timeCreated: 1750149586

View File

@ -0,0 +1,88 @@
// // ©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 DG.Tweening;
using UnityEngine;
using VContainer;
using WordsToolkit.Scripts.Enums;
using WordsToolkit.Scripts.Gameplay;
using WordsToolkit.Scripts.Gameplay.Managers;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.GUI.Tutorials
{
public class TutorialWordSubstitution : TutorialPopupBase
{
[Inject]
protected LevelManager levelManager;
[Inject]
protected GameManager gameManager;
[SerializeField]
private GameObject hand;
protected void ReplaceWordForTutorial(string word)
{
localizationManager.SetPairPlaceholder("word", word.ToUpper());
SetTitle(localizationManager.GetText(tutorialData.kind.ToString(), "Tutorial"));
}
protected void SelectWordAnimation(string wordForTutorial)
{
var selection = FindObjectOfType<WordSelectionManager>();
var letters = selection.GetLetters(wordForTutorial);
AnimateHandToLetters(letters);
EventManager.GetEvent<string>(EGameEvent.WordOpened).Subscribe(OnWordOpened);
}
private void OnWordOpened(string obj)
{
EventManager.GetEvent<string>(EGameEvent.WordOpened).Unsubscribe(OnWordOpened);
Close();
}
private void OnDestroy()
{
if (hand != null)
{
hand.SetActive(false);
hand.transform.DOKill();
}
}
private void AnimateHandToLetters(List<LetterButton> letters)
{
var path = new List<Vector3>();
foreach (var letter in letters)
{
// Skip null letters (in case a word can't be formed with available letters)
if (letter != null)
{
path.Add(letter.transform.position + new Vector3(.5f, -0.8f, 0)); // Adjusting position to avoid overlap with letter
}
}
if (path.Count > 0)
{
if (hand != null)
{
hand.SetActive(true);
hand.transform.position = path[0];
hand.transform.DOPath(path.ToArray(), 2f, PathType.CatmullRom)
.SetEase(Ease.InOutSine)
.SetLoops(-1, LoopType.Restart);
}
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 47831d486a4f4d869ae4809e05afb516
timeCreated: 1748532563

View File

@ -0,0 +1,111 @@
// // ©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.GUI
{
[RequireComponent(typeof(CanvasRenderer))]
public class UILineRenderer : MaskableGraphic
{
public Vector2[] _points;
public Vector2[] points
{
get => _points;
set
{
_points = value;
SetVerticesDirty(); // Tell Unity to redraw the UI component
}
}
public float thickness = 10f;
public bool center = true;
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
if (_points == null || _points.Length < 2)
return;
for (int i = 0; i < _points.Length-1; i++)
{
// Create a line segment between the next two points
CreateLineSegment(_points[i], _points[i+1], vh);
int index = i * 5;
// Add the line segment to the triangles array
vh.AddTriangle(index, index+1, index+3);
vh.AddTriangle(index+3, index+2, index);
// These two triangles create the beveled edges
// between line segments using the end point of
// the last line segment and the start points of this one
if (i != 0)
{
vh.AddTriangle(index, index-1, index-3);
vh.AddTriangle(index+1, index-1, index-2);
}
}
}
/// <summary>
/// Creates a rect from two points that acts as a line segment
/// </summary>
/// <param name="point1">The starting point of the segment</param>
/// <param name="point2">The endint point of the segment</param>
/// <param name="vh">The vertex helper that the segment is added to</param>
private void CreateLineSegment(Vector3 point1, Vector3 point2, VertexHelper vh)
{
Vector3 offset = center ? (rectTransform.sizeDelta / 2) : Vector2.zero;
// Create vertex template
UIVertex vertex = UIVertex.simpleVert;
vertex.color = color;
// Create the start of the segment
Quaternion point1Rotation = Quaternion.Euler(0, 0, RotatePointTowards(point1, point2) + 90);
vertex.position = point1Rotation * new Vector3(-thickness / 2, 0);
vertex.position += point1 - offset;
vh.AddVert(vertex);
vertex.position = point1Rotation * new Vector3(thickness / 2, 0);
vertex.position += point1 - offset;
vh.AddVert(vertex);
// Create the end of the segment
Quaternion point2Rotation = Quaternion.Euler(0, 0, RotatePointTowards(point2, point1) - 90);
vertex.position = point2Rotation * new Vector3(-thickness / 2, 0);
vertex.position += point2 - offset;
vh.AddVert(vertex);
vertex.position = point2Rotation * new Vector3(thickness / 2, 0);
vertex.position += point2 - offset;
vh.AddVert(vertex);
// Also add the end point
vertex.position = point2 - offset;
vh.AddVert(vertex);
}
/// <summary>
/// Gets the angle that a vertex needs to rotate to face target vertex
/// </summary>
/// <param name="vertex">The vertex being rotated</param>
/// <param name="target">The vertex to rotate towards</param>
/// <returns>The angle required to rotate vertex towards target</returns>
private float RotatePointTowards(Vector2 vertex, Vector2 target)
{
return (float)(Mathf.Atan2(target.y - vertex.y, target.x - vertex.x) * (180 / Mathf.PI));
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6dedae9574fd47d291a2f9012a1ec9e1
timeCreated: 1748193767

View File

@ -0,0 +1,166 @@
// // ©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 WordsToolkit.Scripts.Gameplay.Managers;
using WordsToolkit.Scripts.Popups;
using UnityEngine.UI;
using VContainer;
using WordsToolkit.Scripts.Enums;
using WordsToolkit.Scripts.GUI.Buttons;
using WordsToolkit.Scripts.Levels;
using WordsToolkit.Scripts.Settings;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.GUI
{
public class UIManager : MonoBehaviour
{
[Inject]
private MenuManager menuManager;
[Inject]
private IObjectResolver resolver;
[Inject]
private GameSettings gameSettings;
[Inject]
private FieldManager fieldManager;
public ButtonPopup[] buttonsToOpenPopups;
[SerializeField] private CustomButton openRandomTileButton;
[SerializeField] private CustomButton openSelectedTileButton;
[SerializeField] private CustomButton giftButton;
[SerializeField] private TimerDisplay timerDisplay;
private LevelManager levelManager;
private const string GIFT_BUTTON_SHOWN_KEY = "GiftButtonShown";
private void Awake()
{
foreach (var buttonPopup in buttonsToOpenPopups)
{
resolver.Inject(buttonPopup);
buttonPopup.Init();
}
// Initialize gift button visibility
if (giftButton != null)
{
giftButton.gameObject.SetActive(false);
}
}
private void SaveGiftButtonState(bool shown)
{
PlayerPrefs.SetInt(GIFT_BUTTON_SHOWN_KEY, shown ? 1 : 0);
PlayerPrefs.Save();
}
private bool LoadGiftButtonState()
{
return PlayerPrefs.GetInt(GIFT_BUTTON_SHOWN_KEY, 0) == 1;
}
private void UpdateGiftButtonVisibility(bool hasSpecialItems)
{
if (giftButton != null)
{
bool shouldShow = hasSpecialItems || LoadGiftButtonState();
giftButton.gameObject.SetActive(shouldShow);
if (shouldShow)
{
SaveGiftButtonState(true);
}
}
}
private void OnEnable()
{
EventManager.GetEvent<Level>(EGameEvent.LevelLoaded).Subscribe(OnLevelLoaded);
EventManager.OnGameStateChanged += HandleGameStateChange;
if (fieldManager != null && giftButton != null)
{
UpdateGiftButtonVisibility(fieldManager.HasSpecialItems());
}
}
private void OnDisable()
{
EventManager.GetEvent<Level>(EGameEvent.LevelLoaded).Unsubscribe(OnLevelLoaded);
EventManager.OnGameStateChanged -= HandleGameStateChange;
}
private void OnLevelLoaded(Level obj)
{
foreach (var boost in gameSettings.boostLevels)
{
if(openRandomTileButton.CompareTag(boost.tag))
{
openRandomTileButton.gameObject.SetActive(boost.level <= obj.number);
}
if(openSelectedTileButton.CompareTag(boost.tag))
{
openSelectedTileButton.gameObject.SetActive(boost.level <= obj.number);
}
}
// Check for special items and update gift button visibility
if (giftButton != null)
{
UpdateGiftButtonVisibility(fieldManager.HasSpecialItems());
}
}
private void HandleGameStateChange(EGameState newState)
{
UpdateButtonInteractability(newState);
}
private void UpdateButtonInteractability(EGameState gameState)
{
if (buttonsToOpenPopups == null) return;
bool isInteractable = gameState == EGameState.Playing;
foreach (var buttonPopup in buttonsToOpenPopups)
{
if (buttonPopup.button != null)
{
buttonPopup.button.interactable = isInteractable;
}
}
}
}
[Serializable]
public class ButtonPopup
{
public CustomButton button;
public Popup popupRef;
public Button.ButtonClickedEvent onClick => button.onClick;
[Inject]
private MenuManager menuManager;
public void Init()
{
button?.onClick.AddListener(() =>
{
if (popupRef)
{
this.menuManager.ShowPopup(popupRef);
}
});
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b46e327667804829b444b6866d5d5e8e
timeCreated: 1727667184

View File

@ -0,0 +1,86 @@
// // ©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 DG.Tweening;
using UnityEngine.UI;
namespace WordsToolkit.Scripts.GUI
{
public class WordBubble : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI wordText;
public float shakeDuration = 0.5f;
public float shakeMagnitude = 0.1f;
public int vibratoCount = 10;
public float randomness = 90f;
public float destroyDelay = 0.2f;
private RectTransform _rectTransform;
private ContentSizeFitter _contentSizeFitter;
private void Awake()
{
_rectTransform = GetComponent<RectTransform>();
_contentSizeFitter = GetComponent<ContentSizeFitter>();
}
public void SetWord(string word)
{
wordText.text = word;
// Force content size fitter and layout update
if (_contentSizeFitter != null)
{
Canvas.ForceUpdateCanvases();
_contentSizeFitter.SetLayoutHorizontal();
_contentSizeFitter.SetLayoutVertical();
LayoutRebuilder.ForceRebuildLayoutImmediate(_rectTransform);
}
Shake();
}
private void Shake()
{
if (_rectTransform != null)
{
// Create sequence for shake and fade out
Sequence sequence = DOTween.Sequence();
// Add shake animation
sequence.Append(_rectTransform.DOShakeAnchorPos(
duration: shakeDuration,
strength: shakeMagnitude * 100f,
vibrato: vibratoCount,
randomness: randomness,
fadeOut: true
));
// Add fade out and destroy
sequence.AppendInterval(destroyDelay);
sequence.Append(wordText.DOFade(0, 0.2f));
sequence.OnComplete(() => {
Destroy(gameObject);
});
}
}
private void OnDisable()
{
_rectTransform?.DOKill();
wordText?.DOKill();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 23031a4694394b68b71a3e296c5a4e55
timeCreated: 1743012191