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
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 489c0be5e4cc42a19b0eb58260774491
timeCreated: 1725704450
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 00d9b8da9fd9484f96600e76d664e74c
timeCreated: 1709532883
@@ -0,0 +1,56 @@
// // ©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;
namespace WordsToolkit.Scripts.Services.Ads.AdUnits
{
public class AdLifecycleManager : IAdLifecycleManager
{
private readonly AdsHandlerBase _adsHandler;
public AdLifecycleManager(AdsHandlerBase adsHandler)
{
_adsHandler = adsHandler;
}
public void Load(AdUnit adUnit)
{
_adsHandler?.Load(adUnit);
}
public void Show(AdUnit adUnit)
{
_adsHandler?.Show(adUnit);
}
public void Hide(AdUnit adUnit)
{
_adsHandler?.Hide(adUnit);
}
public bool IsAvailable(AdUnit adUnit)
{
return _adsHandler != null && (_adsHandler.IsAvailable(adUnit) || adUnit.Loaded);
}
public void Complete(AdUnit adUnit)
{
adUnit.OnShown?.Invoke(adUnit.PlacementId);
}
public void Initialize(AdUnit adUnit)
{
adUnit.OnInitialized?.Invoke(adUnit.PlacementId);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 572fd5f113dad43b6b492120fbd965ae
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,22 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEngine;
namespace WordsToolkit.Scripts.Services.Ads.AdUnits
{
[CreateAssetMenu(fileName = "AdReference", menuName ="WordConnectGameToolkit/Ads/AdReference")]
public class AdReference : ScriptableObject
{
public EAdType adType;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8151ae330add46d49f409e031f774236
timeCreated: 1709625749
@@ -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 System;
namespace WordsToolkit.Scripts.Services.Ads.AdUnits
{
public class AdUnit
{
public string PlacementId { get; }
public AdReference AdReference { get; }
public bool Loaded { get; set; }
public Action<string> OnShown { get; set; }
public Action<string> OnInitialized { get; set; }
public AdUnit(string placementId, AdReference adReference)
{
PlacementId = placementId;
AdReference = adReference;
}
public AdsHandlerBase AdsHandler { get; set; }
public void Complete()
{
OnShown?.Invoke(PlacementId);
}
public void Initialized()
{
OnInitialized?.Invoke(PlacementId);
}
public void Load()
{
AdsHandler?.Load(this);
}
public void Show()
{
AdsHandler?.Show(this);
}
public bool IsAvailable()
{
return AdsHandler != null && (AdsHandler.IsAvailable(this) || Loaded);
}
public void Hide()
{
AdsHandler?.Hide(this);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bd76f366cfc84bfab6c025eb8ebc1b5e
timeCreated: 1709541000
@@ -0,0 +1,28 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEngine;
namespace WordsToolkit.Scripts.Services.Ads.AdUnits
{
public abstract class AdsHandlerBase : ScriptableObject
{
public abstract void Init(string _id, bool adSettingTestMode, IAdsListener listener);
public abstract void Show(AdUnit adUnit);
public abstract void Load(AdUnit adUnit);
/// set false if adapter doesn't have availability method
public abstract bool IsAvailable(AdUnit adUnit);
public abstract void Hide(AdUnit adUnit);
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f71ea344e49b486dbea6a85fb3422111
timeCreated: 1709485645
@@ -0,0 +1,21 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
namespace WordsToolkit.Scripts.Services.Ads.AdUnits
{
public enum EAdType
{
Banner,
Interstitial,
Rewarded
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0b34f9ec5c8244b79a5b7eca99ddaee8
timeCreated: 1709539401
@@ -0,0 +1,26 @@
// // ©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;
namespace WordsToolkit.Scripts.Services.Ads.AdUnits
{
public interface IAdLifecycleManager
{
void Load(AdUnit adUnit);
void Show(AdUnit adUnit);
void Hide(AdUnit adUnit);
bool IsAvailable(AdUnit adUnit);
void Complete(AdUnit adUnit);
void Initialize(AdUnit adUnit);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 15053b0564ace4fcdae31ceeb36d5e23
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,27 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
namespace WordsToolkit.Scripts.Services.Ads.AdUnits
{
public interface IAdsListener
{
void Show(AdUnit adUnit);
void OnAdsInitialized();
void OnAdsLoaded(string placementId);
void OnAdsLoadFailed();
void OnAdsShowFailed();
void OnAdsShowStart();
void OnAdsShowClick();
void OnAdsShowComplete();
void OnInitFailed();
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1c85c74df67b4b7a928492e027e61311
timeCreated: 1709539029
@@ -0,0 +1,94 @@
// // ©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 UnityEngine;
using WordsToolkit.Scripts.Services.Ads.AdUnits;
namespace WordsToolkit.Scripts.Services.Ads
{
public class AdsListener : IAdsListener
{
private readonly List<AdUnit> adUnits;
private readonly Dictionary<AdUnit, IAdLifecycleManager> lifecycleManagers;
private bool available;
private AdUnit _adUnit;
public AdsListener(List<AdUnit> adUnits, Dictionary<AdUnit, IAdLifecycleManager> lifecycleManagers)
{
this.adUnits = adUnits;
this.lifecycleManagers = lifecycleManagers;
}
public void Show(AdUnit adUnit)
{
Debug.Log("Show ad " + adUnit.PlacementId);
_adUnit = adUnit;
}
public void OnAdsInitialized()
{
Debug.Log("Ads initialized");
foreach (var adUnit in adUnits)
{
lifecycleManagers[adUnit].Initialize(adUnit);
}
}
public void OnAdsLoaded(string placementId)
{
foreach (var adUnit in adUnits)
{
if (adUnit.PlacementId == placementId)
{
adUnit.Loaded = true;
}
}
}
public void OnInitFailed()
{
Debug.Log("Ads init failed, check assigned ad handler");
}
public void OnAdsLoadFailed()
{
}
public void OnAdsShowFailed()
{
if (_adUnit != null)
{
_adUnit.Loaded = false;
}
_adUnit = null;
}
public void OnAdsShowStart()
{
}
public void OnAdsShowClick()
{
}
public void OnAdsShowComplete()
{
if (_adUnit != null)
{
lifecycleManagers[_adUnit].Complete(_adUnit);
_adUnit = null;
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5daba387c7ef452b807b7ca49155e6d8
timeCreated: 1709532901
@@ -0,0 +1,28 @@
{
"name": "CandySmith.Ads",
"rootNamespace": "",
"references": [
"GUID:ff6de30450f3748908ff580f6ec64d9a",
"GUID:760a4c7888534400e882b82c5b3fba06"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.ads",
"expression": "",
"define": "UNITY_ADS"
},
{
"name": "com.unity.services.levelplay",
"expression": "",
"define": "IRONSOURCE"
}
],
"noEngineReferences": false
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 00dd4a7ac8c24c898083910c81898ecc
timeCreated: 1709468561
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1f47475dce324490b55feb233fab1d19
timeCreated: 1709621319
@@ -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 UnityEngine;
using WordsToolkit.Scripts.Services.Ads.AdUnits;
#if ADMOB
using GoogleMobileAds.Api;
#endif
namespace WordsToolkit.Scripts.Services.Ads.Networks
{
[CreateAssetMenu(fileName = "AdmobBannerHandler", menuName ="WordConnectGameToolkit/Ads/AdmobBannerHandler")]
public class AdmobBannerHandler : AdsHandlerBase
{
private IAdsListener _listener;
#if ADMOB
private BannerView _bannerView;
#endif
private bool _isInitialized = false;
private bool _isBannerLoaded = false;
public override void Init(string _id, bool adSettingTestMode, IAdsListener listener)
{
#if ADMOB
_listener = listener;
_isInitialized = true;
Debug.Log("AdMob Banner Handler initialized.");
_listener?.OnAdsInitialized();
#endif
}
public override void Show(AdUnit adUnit)
{
#if ADMOB
_listener?.Show(adUnit);
_bannerView?.Show();
Debug.Log("Showing banner ad.");
#endif
}
public override void Load(AdUnit adUnit)
{
#if ADMOB
if (!_isInitialized)
{
Debug.LogError("AdMob Banner Handler is not initialized.");
return;
}
// Destroy any existing banner before creating a new one
if (_bannerView != null)
{
_bannerView.Destroy();
}
_bannerView = new BannerView(adUnit.PlacementId, AdSize.Banner, AdPosition.Bottom);
// Add event handlers
_bannerView.OnBannerAdLoaded += () =>
{
_isBannerLoaded = true;
Debug.Log("Banner ad loaded successfully.");
_listener?.OnAdsLoaded(adUnit.PlacementId);
};
_bannerView.OnBannerAdLoadFailed += (LoadAdError error) =>
{
_isBannerLoaded = false;
Debug.LogError($"Banner ad failed to load with error : {error.GetMessage()}");
_listener?.OnAdsLoadFailed();
};
_bannerView.OnAdPaid += (AdValue adValue) => { Debug.Log($"Banner ad paid {adValue.Value} {adValue.CurrencyCode}"); };
_bannerView.OnAdClicked += () =>
{
Debug.Log("Banner ad clicked.");
_listener?.OnAdsShowClick();
};
_bannerView.OnAdImpressionRecorded += () =>
{
Debug.Log("Banner ad impression recorded.");
_listener?.OnAdsShowStart();
};
// Create an empty ad request
AdRequest adRequest = new AdRequest();
// Load the banner with the request
_bannerView.LoadAd(adRequest);
Debug.Log("Requested banner ad load.");
#endif
}
public override bool IsAvailable(AdUnit adUnit)
{
#if ADMOB
return _isInitialized && _isBannerLoaded;
#else
return false;
#endif
}
public override void Hide(AdUnit adUnit)
{
#if ADMOB
if (_bannerView != null)
{
_bannerView.Hide();
Debug.Log("Banner ad hidden.");
}
#endif
}
public void DestroyBanner()
{
#if ADMOB
if (_bannerView != null)
{
_bannerView.Destroy();
_bannerView = null;
_isBannerLoaded = false;
Debug.Log("Banner ad destroyed.");
}
#endif
}
private void OnDisable()
{
DestroyBanner();
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d3606b19b7f348cea65dc3082d89cafd
timeCreated: 1726566442
@@ -0,0 +1,163 @@
// // ©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.Services.Ads.AdUnits;
#if ADMOB
using GoogleMobileAds.Api;
#endif
namespace WordsToolkit.Scripts.Services.Ads.Networks
{
[CreateAssetMenu(fileName = "AdmobHandler", menuName ="WordConnectGameToolkit/Ads/AdmobHandler")]
public class AdmobHandler : AdsHandlerBase
{
private IAdsListener _listener;
#if ADMOB
private InterstitialAd _interstitialAd;
private RewardedAd _rewardedAd;
#endif
public override void Init(string _id, bool adSettingTestMode, IAdsListener listener)
{
#if ADMOB
_listener = listener;
MobileAds.RaiseAdEventsOnUnityMainThread = true;
MobileAds.Initialize(initstatus =>
{
if (initstatus == null)
{
Debug.LogError("Google Mobile Ads initialization failed.");
return;
}
// If you use mediation, you can check the status of each adapter.
var adapterStatusMap = initstatus.getAdapterStatusMap();
if (adapterStatusMap != null)
{
foreach (var item in adapterStatusMap)
{
Debug.Log(string.Format("Adapter {0} is {1}",
item.Key,
item.Value.InitializationState));
}
}
Debug.Log("Google Mobile Ads initialization complete.");
_listener?.OnAdsInitialized();
});
#endif
}
public override void Show(AdUnit adUnit)
{
#if ADMOB
_listener?.Show(adUnit);
if (adUnit.AdReference.adType == EAdType.Interstitial)
{
if (_interstitialAd != null && _interstitialAd.CanShowAd())
{
Debug.Log("Showing interstitial ad.");
_interstitialAd.Show();
}
else
{
Debug.LogError("Interstitial ad is not ready yet.");
}
}
else if (adUnit.AdReference.adType == EAdType.Rewarded)
{
if (_rewardedAd != null && _rewardedAd.CanShowAd())
{
_rewardedAd.Show(reward =>
{
Debug.Log(string.Format("Rewarded ad granted a reward: {0} {1}",
reward.Amount,
reward.Type));
});
}
else
{
Debug.LogError("Rewarded ad is not ready yet.");
}
}
#endif
}
public override void Load(AdUnit adUnit)
{
#if ADMOB
var adRequest = new AdRequest();
if (adUnit.AdReference.adType == EAdType.Interstitial)
{
InterstitialAd.Load(adUnit.PlacementId, adRequest, (ad, error) =>
{
if (error != null)
{
Debug.LogError("Interstitial ad failed to load an ad with error : " + error);
return;
}
if (ad == null)
{
Debug.LogError("Unexpected error: Interstitial load event fired with null ad and null error.");
return;
}
_interstitialAd = ad;
Debug.Log("Interstitial ad loaded with response : " + ad.GetResponseInfo());
ad.OnAdFullScreenContentClosed += () => _listener.OnAdsShowComplete();
_listener?.OnAdsLoaded(adUnit.PlacementId);
});
}
else if (adUnit.AdReference.adType == EAdType.Rewarded)
{
RewardedAd.Load(adUnit.PlacementId, adRequest, (ad, error) =>
{
if (error != null)
{
Debug.LogError("Rewarded ad failed to load an ad with error : " + error);
return;
}
if (ad == null)
{
Debug.LogError("Unexpected error: Rewarded load event fired with null ad and null error.");
return;
}
_rewardedAd = ad;
ad.OnAdFullScreenContentClosed += () => _listener.OnAdsShowComplete();
Debug.Log("Rewarded ad loaded with response : " + ad.GetResponseInfo());
_listener?.OnAdsLoaded(adUnit.PlacementId);
});
}
#endif
}
public override bool IsAvailable(AdUnit adUnit)
{
return false;
}
public override void Hide(AdUnit adUnit)
{
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 00a645503e7646baac86d72a0b411f9e
timeCreated: 1709647067
@@ -0,0 +1,149 @@
// // ©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.Services.Ads.AdUnits;
namespace WordsToolkit.Scripts.Services.Ads.Networks
{
[CreateAssetMenu(fileName = "IronsourceAdsHandler", menuName ="WordConnectGameToolkit/Ads/IronsourceAdsHandler")]
public class IronsourceAdsHandler : AdsHandlerBase
{
private IAdsListener _listener;
private void Init(string _id)
{
#if IRONSOURCE
IronSource.Agent.setManualLoadRewardedVideo(true);
IronSource.Agent.validateIntegration();
IronSource.Agent.init(_id);
#endif
}
private void SetListener(IAdsListener listener)
{
_listener = listener;
Debug.Log(_listener);
#if IRONSOURCE
//Add Rewarded Video Events
IronSourceInterstitialEvents.onAdReadyEvent += OnInterstitialAdReady;
IronSourceInterstitialEvents.onAdLoadFailedEvent += InterstitialAdLoadFailedEvent;
IronSourceRewardedVideoEvents.onAdReadyEvent += OnRewardedVideoAdReady;
IronSourceRewardedVideoEvents.onAdLoadFailedEvent += RewardedVideoAdShowFailedEvent;
IronSourceRewardedVideoEvents.onAdRewardedEvent += Rewardeded;
IronSourceEvents.onSdkInitializationCompletedEvent += SdkInitializationCompletedEvent;
#endif
}
#if IRONSOURCE
private void Rewardeded(IronSourcePlacement obj, IronSourceAdInfo ironSourceAdInfo)
{
Debug.Log("Ironsource Rewardeded");
_listener?.OnAdsShowComplete();
}
private void SdkInitializationCompletedEvent()
{
Debug.Log("Ironsource SdkInitializationCompletedEvent");
_listener?.OnAdsInitialized();
}
private void InterstitialAdLoadFailedEvent(IronSourceError obj)
{
Debug.Log("Ironsource InterstitialAdLoadFailedEvent " + obj.getCode() + " " + obj.getDescription());
_listener?.OnAdsLoadFailed();
}
private void RewardedVideoAdShowFailedEvent(IronSourceError obj)
{
Debug.Log("1" + obj.getCode());
Debug.Log("2" + obj.getDescription());
Debug.Log("Ironsource RewardedVideoAdShowFailedEvent " + obj.getCode() + " " + obj.getDescription());
Debug.Log(_listener);
_listener?.OnAdsShowFailed();
}
private void OnRewardedVideoAdReady(IronSourceAdInfo obj)
{
Debug.Log("Ironsource OnRewardedVideoAdReady");
_listener?.OnAdsLoaded(obj.instanceId);
}
private void OnInterstitialAdReady(IronSourceAdInfo obj)
{
Debug.Log("Ironsource OnInterstitialAdReady");
_listener?.OnAdsLoaded(obj.instanceId);
}
#endif
public override void Init(string _id, bool adSettingTestMode, IAdsListener listener)
{
Debug.Log("Ironsource Init");
Init(_id);
Debug.Log("Ironsource SetListener");
SetListener(listener);
}
public override void Show(AdUnit adUnit)
{
#if IRONSOURCE
if (adUnit.AdReference.adType == EAdType.Interstitial)
{
IronSource.Agent.showInterstitial();
}
else if (adUnit.AdReference.adType == EAdType.Rewarded)
{
IronSource.Agent.showRewardedVideo();
}
_listener?.Show(adUnit);
#endif
}
public override void Load(AdUnit adUnit)
{
#if IRONSOURCE
if (adUnit.AdReference.adType == EAdType.Interstitial)
{
IronSource.Agent.loadInterstitial();
}
else if (adUnit.AdReference.adType == EAdType.Rewarded)
{
IronSource.Agent.loadRewardedVideo();
}
#endif
}
public override bool IsAvailable(AdUnit adUnit)
{
#if IRONSOURCE
if (adUnit.AdReference.adType == EAdType.Interstitial)
{
return IronSource.Agent.isInterstitialReady();
}
if (adUnit.AdReference.adType == EAdType.Rewarded)
{
return IronSource.Agent.isRewardedVideoAvailable();
}
#endif
return false;
}
public override void Hide(AdUnit adUnit)
{
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c8e3881626924945a6256e7be23a89b5
timeCreated: 1709623544
@@ -0,0 +1,124 @@
// // ©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.Services.Ads.AdUnits;
namespace WordsToolkit.Scripts.Services.Ads.Networks
{
[CreateAssetMenu(fileName = "IronsourceBannerHandler", menuName ="WordConnectGameToolkit/Ads/IronsourceBannerHandler")]
public class IronsourceBannerHandler : AdsHandlerBase
{
private IAdsListener _listener;
private void Init(string _id)
{
#if IRONSOURCE
IronSource.Agent.validateIntegration();
IronSource.Agent.init(_id);
#endif
}
private void SetListener(IAdsListener listener)
{
_listener = listener;
#if IRONSOURCE
IronSourceBannerEvents.onAdLoadedEvent += BannerAdLoadedEvent;
IronSourceBannerEvents.onAdLoadFailedEvent += BannerAdLoadFailedEvent;
IronSourceBannerEvents.onAdClickedEvent += BannerAdClickedEvent;
IronSourceBannerEvents.onAdScreenPresentedEvent += BannerAdScreenPresentedEvent;
IronSourceBannerEvents.onAdScreenDismissedEvent += BannerAdScreenDismissedEvent;
IronSourceBannerEvents.onAdLeftApplicationEvent += BannerAdLeftApplicationEvent;
#endif
}
#if IRONSOURCE
private void BannerAdLoadedEvent(IronSourceAdInfo adInfo)
{
Debug.Log("IronSource Banner ad loaded");
_listener?.OnAdsLoaded(adInfo.instanceId);
}
private void BannerAdLoadFailedEvent(IronSourceError error)
{
Debug.Log($"IronSource Banner ad load failed. Error: {error.getCode()} - {error.getDescription()}");
_listener?.OnAdsLoadFailed();
}
private void BannerAdClickedEvent(IronSourceAdInfo adInfo)
{
Debug.Log("IronSource Banner ad clicked");
}
private void BannerAdScreenPresentedEvent(IronSourceAdInfo adInfo)
{
Debug.Log("IronSource Banner ad screen presented");
}
private void BannerAdScreenDismissedEvent(IronSourceAdInfo adInfo)
{
Debug.Log("IronSource Banner ad screen dismissed");
}
private void BannerAdLeftApplicationEvent(IronSourceAdInfo adInfo)
{
Debug.Log("IronSource Banner ad caused app to leave");
}
#endif
public override void Init(string _id, bool adSettingTestMode, IAdsListener listener)
{
Debug.Log("IronSource Banner Init");
Init(_id);
SetListener(listener);
}
public override void Show(AdUnit adUnit)
{
#if IRONSOURCE
if (adUnit.AdReference.adType == EAdType.Banner)
{
IronSource.Agent.displayBanner();
_listener?.Show(adUnit);
}
#endif
}
public override void Load(AdUnit adUnit)
{
#if IRONSOURCE
if (adUnit.AdReference.adType == EAdType.Banner)
{
IronSourceBannerSize bannerSize = IronSourceBannerSize.BANNER;
IronSource.Agent.loadBanner(bannerSize, IronSourceBannerPosition.BOTTOM);
}
#endif
}
public override bool IsAvailable(AdUnit adUnit)
{
// IronSource doesn't provide a direct method to check if a banner is available
// You might want to implement your own logic to track banner availability
return true;
}
public override void Hide(AdUnit adUnit)
{
#if IRONSOURCE
if (adUnit.AdReference.adType == EAdType.Banner)
{
IronSource.Agent.hideBanner();
}
#endif
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 229059528153459db318609c350eea5f
timeCreated: 1726721377
@@ -0,0 +1,120 @@
// // ©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.Services.Ads.AdUnits;
namespace WordsToolkit.Scripts.Services.Ads.Networks
{
#if UNITY_ADS
using UnityEngine.Advertisements;
[CreateAssetMenu(fileName = "UnityAdsHandler", menuName ="WordConnectGameToolkit/Ads/UnityAdsHandler")]
public class UnityAdsHandler : AdsHandlerBase, IUnityAdsInitializationListener, IUnityAdsLoadListener, IUnityAdsShowListener
{
private bool rewardedLoaded;
private bool initialized;
private IAdsListener listener;
#region IAdsShowable Implementations
public override void Init(string _id, bool adSettingTestMode, IAdsListener listener)
{
Advertisement.Initialize(_id, adSettingTestMode, this);
this.listener = listener;
}
public override void Show(AdUnit adUnit)
{
Advertisement.Show(adUnit.PlacementId, this);
listener.Show(adUnit);
}
public override void Load(AdUnit adUnit)
{
Advertisement.Load(adUnit.PlacementId, this);
}
public override bool IsAvailable(AdUnit adUnit)
{
return false;
}
public override void Hide(AdUnit adUnit)
{
}
#endregion
#region Interface Implementations
public void OnInitializationComplete()
{
DebugLog("Init Success");
listener.OnAdsInitialized();
}
public void OnInitializationFailed(UnityAdsInitializationError error, string message)
{
DebugLog($"Init Failed: [{error}]: {message}");
listener.OnInitFailed();
}
public void OnUnityAdsAdLoaded(string placementId)
{
DebugLog($"Load Success: {placementId}");
listener.OnAdsLoaded(placementId);
}
public void OnUnityAdsFailedToLoad(string placementId, UnityAdsLoadError error, string message)
{
DebugLog($"Load Failed: [{error}:{placementId}] {message}");
listener.OnAdsLoadFailed();
}
public void OnUnityAdsShowFailure(string placementId, UnityAdsShowError error, string message)
{
DebugLog($"OnUnityAdsShowFailure: [{error}]: {message}");
listener.OnAdsShowFailed();
}
public void OnUnityAdsShowStart(string placementId)
{
DebugLog($"OnUnityAdsShowStart: {placementId}");
listener.OnAdsShowStart();
}
public void OnUnityAdsShowClick(string placementId)
{
DebugLog($"OnUnityAdsShowClick: {placementId}");
listener.OnAdsShowClick();
}
public void OnUnityAdsShowComplete(string placementId, UnityAdsShowCompletionState showCompletionState)
{
if (showCompletionState == UnityAdsShowCompletionState.COMPLETED)
{
DebugLog($"OnUnityAdsShowComplete: {placementId}");
listener.OnAdsShowComplete();
}
}
//wrapper around debug.log to allow broadcasting log strings to the UI
private void DebugLog(string msg)
{
Debug.Log(msg);
}
#endregion
}
#endif
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d639e25c5eb64e8689f8fa30e7839258
timeCreated: 1709468744
@@ -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 WordsToolkit.Scripts.Services.Ads.AdUnits;
namespace WordsToolkit.Scripts.Services.Ads.Networks
{
#if UNITY_ADS
using UnityEngine.Advertisements;
[CreateAssetMenu(fileName = "UnityBannerAdsHandler", menuName ="WordConnectGameToolkit/Ads/UnityBannerAdsHandler")]
public class UnityBannerAdsHandler : AdsHandlerBase
{
private bool initialized;
private IAdsListener listener;
private readonly BannerPosition bannerPosition = BannerPosition.BOTTOM_CENTER;
public override void Init(string _id, bool adSettingTestMode, IAdsListener listener)
{
this.listener = listener;
initialized = true;
}
public override void Show(AdUnit adUnit)
{
if (!initialized)
{
DebugLog("Unity Ads not initialized. Cannot show banner.");
return;
}
Advertisement.Banner.SetPosition(bannerPosition);
Advertisement.Banner.Show(adUnit.PlacementId, new BannerOptions
{
clickCallback = OnBannerClicked,
hideCallback = OnBannerHidden,
showCallback = OnBannerShown
});
listener?.Show(adUnit);
}
public override void Load(AdUnit adUnit)
{
if (!initialized)
{
DebugLog("Unity Ads not initialized. Cannot load banner.");
return;
}
Advertisement.Banner.Load(adUnit.PlacementId, new BannerLoadOptions
{
loadCallback = () => OnBannerLoaded(adUnit.PlacementId),
errorCallback = OnBannerError
});
}
public override bool IsAvailable(AdUnit adUnit)
{
return initialized && Advertisement.Banner.isLoaded;
}
public override void Hide(AdUnit adUnit)
{
Advertisement.Banner.Hide();
}
private void OnBannerLoaded(string placementId)
{
DebugLog($"Banner loaded: {placementId}");
listener?.OnAdsLoaded(placementId);
}
private void OnBannerError(string message)
{
DebugLog($"Banner load error: {message}");
listener?.OnAdsLoadFailed();
}
private void OnBannerClicked()
{
DebugLog("Banner clicked");
listener?.OnAdsShowClick();
}
private void OnBannerShown()
{
DebugLog("Banner shown");
listener?.OnAdsShowStart();
}
private void OnBannerHidden()
{
DebugLog("Banner hidden");
}
private void DebugLog(string msg)
{
Debug.Log($"[UnityBannerAdsHandler] {msg}");
}
}
#endif
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 60d7b8cc66824f168db82afe8c22a4b4
timeCreated: 1726563264
@@ -0,0 +1,197 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System;
using System.Collections.Generic;
using UnityEngine;
using VContainer;
using WordsToolkit.Scripts.Popups;
using WordsToolkit.Scripts.Services.Ads;
using WordsToolkit.Scripts.Services.Ads.AdUnits;
using WordsToolkit.Scripts.Services.IAP;
using WordsToolkit.Scripts.Settings;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.Services
{
public class AdsManager : MonoBehaviour, IAdsManager
{
private readonly List<AdSetting> adList = new();
private readonly List<AdUnit> adUnits = new();
private readonly Dictionary<AdUnit, IAdLifecycleManager> lifecycleManagers = new();
private EPlatforms platforms;
[Inject]
private GameSettings gameSettings;
[Inject]
private IIAPManager iapManager;
[SerializeField]
private ProductID noAdsProduct;
private void Awake()
{
if (!gameSettings.enableAds)
{
return;
}
platforms = GetPlatform();
var adElements = Resources.Load<AdsSettings>("Settings/AdsSettings").adProfiles;
foreach (var t in adElements)
{
if (t.platforms == platforms && t.enable)
{
if (Application.isEditor && !t.testInEditor)
{
continue;
}
adList.Add(t);
foreach (var adElement in t.adElements)
{
var adUnit = new AdUnit(adElement.placementId, adElement.adReference);
var lifecycleManager = new AdLifecycleManager(t.adsHandler);
lifecycleManagers[adUnit] = lifecycleManager;
adUnits.Add(adUnit);
if (adUnit.AdReference.adType == EAdType.Banner && !IsNoAdsPurchased())
{
lifecycleManager.Show(adUnit);
}
}
t.adsHandler?.Init(t.appId, false, new AdsListener(adUnits, lifecycleManagers));
}
}
}
private void Start()
{
if(IsNoAdsPurchased())
RemoveAds();
}
private void OnEnable()
{
Popup.OnOpenPopup += OnOpenPopup;
Popup.OnClosePopup += OnClosePopup;
}
private void OnDisable()
{
Popup.OnOpenPopup -= OnOpenPopup;
Popup.OnClosePopup -= OnClosePopup;
}
private EPlatforms GetPlatform()
{
#if UNITY_ANDROID
return EPlatforms.Android;
#elif UNITY_IOS
return EPlatforms.IOS;
#elif UNITY_WEBGL
return EPlatforms.WebGL;
#else
return EPlatforms.Windows;
#endif
}
private void OnOpenPopup(Popup popup)
{
OnPopupTrigger(popup, true);
}
private void OnClosePopup(Popup popup)
{
OnPopupTrigger(popup, false);
}
private void OnPopupTrigger(Popup popup, bool open)
{
if (IsNoAdsPurchased())
{
return;
}
foreach (var ad in adList)
{
foreach (var adElement in ad.adElements)
{
var adUnit = adUnits.Find(i => i.AdReference == adElement.adReference);
if (!lifecycleManagers[adUnit].IsAvailable(adUnit))
{
lifecycleManagers[adUnit].Load(adUnit);
continue;
}
if (((open && adElement.popup.showOnOpen) || (!open && adElement.popup.showOnClose)) && popup.GetType() == adElement.popup.popup.GetType())
{
lifecycleManagers[adUnit].Show(adUnit);
lifecycleManagers[adUnit].Load(adUnit);
return;
}
}
}
}
public bool IsNoAdsPurchased()
{
return !gameSettings.enableAds || iapManager.IsProductPurchased(noAdsProduct.ID);
}
public void ShowAdByType(AdReference adRef, Action<string> shown)
{
if (!gameSettings.enableAds)
{
shown?.Invoke(null);
return;
}
foreach (var adUnit in adUnits)
{
if (adUnit.AdReference == adRef && lifecycleManagers[adUnit].IsAvailable(adUnit))
{
adUnit.OnShown = shown;
lifecycleManagers[adUnit].Show(adUnit);
lifecycleManagers[adUnit].Load(adUnit);
return;
}
}
}
public bool IsRewardedAvailable(AdReference adRef)
{
foreach (var adUnit in adUnits)
{
if (adUnit.AdReference == adRef)
{
return lifecycleManagers[adUnit].IsAvailable(adUnit);
}
}
return false;
}
public void RemoveAds()
{
foreach (var adUnit in adUnits)
{
if (adUnit.AdReference.adType == EAdType.Banner)
{
lifecycleManagers[adUnit].Hide(adUnit);
}
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 637f18f981c24474860234607bd24517
timeCreated: 1725705449
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 171feb9694d164721abc7abbf74d41ed
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace WordsToolkit.Scripts.Services.BannedWords
{
[Serializable]
public class LanguageBannedWords
{
public string languageCode;
public List<string> bannedWords = new List<string>();
}
public class BannedWordsConfiguration : ScriptableObject
{
public List<LanguageBannedWords> bannedWordsByLanguage = new List<LanguageBannedWords>();
public List<string> GetBannedWords(string languageCode)
{
var languageData = bannedWordsByLanguage.Find(x => x.languageCode == languageCode);
return languageData?.bannedWords ?? new List<string>();
}
public void AddBannedWord(string word, string languageCode)
{
var languageData = bannedWordsByLanguage.Find(x => x.languageCode == languageCode);
if (languageData == null)
{
languageData = new LanguageBannedWords { languageCode = languageCode };
bannedWordsByLanguage.Add(languageData);
}
if (!languageData.bannedWords.Contains(word))
{
languageData.bannedWords.Add(word);
}
}
public void RemoveBannedWord(string word, string languageCode)
{
var languageData = bannedWordsByLanguage.Find(x => x.languageCode == languageCode);
languageData?.bannedWords.Remove(word);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 889b087cffdd548cc9d3e4d3adefdebc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,55 @@
using System.Collections.Generic;
using UnityEngine;
using VContainer;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace WordsToolkit.Scripts.Services.BannedWords
{
public class BannedWordsService : IBannedWordsService
{
private readonly BannedWordsConfiguration configuration;
[Inject]
public BannedWordsService(BannedWordsConfiguration configuration)
{
this.configuration = configuration;
}
public List<string> GetBannedWords(string languageCode)
{
return configuration.GetBannedWords(languageCode);
}
public bool IsWordBanned(string word, string languageCode)
{
var bannedWords = GetBannedWords(languageCode);
return bannedWords.Contains(word.ToLower());
}
public void AddBannedWord(string word, string languageCode)
{
configuration.AddBannedWord(word.ToLower(), languageCode);
SaveConfiguration();
}
public void RemoveBannedWord(string word, string languageCode)
{
configuration.RemoveBannedWord(word.ToLower(), languageCode);
SaveConfiguration();
}
private void SaveConfiguration()
{
#if UNITY_EDITOR
if (configuration != null)
{
EditorUtility.SetDirty(configuration);
AssetDatabase.SaveAssets();
Debug.Log($"Saved BannedWordsConfiguration");
}
#endif
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cebba6a642ce346a4a2045f00d26aee9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,12 @@
using System.Collections.Generic;
namespace WordsToolkit.Scripts.Services.BannedWords
{
public interface IBannedWordsService
{
List<string> GetBannedWords(string languageCode);
bool IsWordBanned(string word, string languageCode);
void AddBannedWord(string word, string languageCode);
void RemoveBannedWord(string word, string languageCode);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0bac5fa11daa34c1494f453f2252f6e6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e31cbafb896a48938cd7ce4655db5ffb
timeCreated: 1709286754
@@ -0,0 +1,37 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEngine;
using WordsToolkit.Scripts.Popups;
using VContainer;
namespace WordsToolkit.Scripts.Services.GDPR
{
public class GDPRPopupManager : MonoBehaviour
{
[Inject]
private MenuManager menuManager;
private void Awake()
{
Invoke(nameof(ShowGDPR), 2);
}
private void ShowGDPR()
{
if (PlayerPrefs.GetInt("npa", -1) == -1)
{
menuManager.ShowPopup<Popups.GDPR>();
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 525a5e24238d5f54483b19d5f314b140
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7fb87bc4c8714ad38cefc8402d562a17
timeCreated: 1709286765
@@ -0,0 +1,26 @@
{
"name": "CandySmith.IAP",
"rootNamespace": "",
"references": [
"GUID:60bfecf5cb232594891bc622f40d6bed",
"GUID:08d1c582746949b40ba6a45cdb776bdf",
"GUID:fe25561d224ed4743af4c60938a59d0b",
"GUID:70ea675efa2644cef98c7ece24158333",
"GUID:b0214a6008ed146ff8f122a6a9c2f6cc"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.purchasing",
"expression": "",
"define": "UNITY_PURCHASING"
}
],
"noEngineReferences": false
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: ac6e78962cfc743b9a5fc5f5a808aa72
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,258 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Purchasing;
using UnityEngine.Purchasing.Extension;
namespace WordsToolkit.Scripts.Services.IAP
{
#if UNITY_PURCHASING
public class IAPController : IDetailedStoreListener, IIAPService
{
private static IStoreController storeController;
private IExtensionProvider extensionProvider;
public static event Action<string> OnSuccessfulPurchase;
public static event Action<bool, List<string>> OnRestorePurchasesFinished;
public void InitializePurchasing(IEnumerable<(string productId, ProductTypeWrapper.ProductType productType)> products)
{
if (IsInitialized())
{
return;
}
var standardPurchasingModule = StandardPurchasingModule.Instance();
// #if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WEBGL
// standardPurchasingModule.useFakeStoreUIMode = FakeStoreUIMode.StandardUser;
// standardPurchasingModule.useFakeStoreAlways = true;
// #endif
var builder = ConfigurationBuilder.Instance(standardPurchasingModule);
foreach (var (productId, productType) in products)
{
builder.AddProduct(productId, ProductTypeWrapper.GetProductType(productType));
}
UnityPurchasing.Initialize(this, builder);
}
public bool IsInitialized()
{
return storeController != null;
}
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
extensionProvider = extensions;
storeController = controller;
RestorePurchases((success, restoredProducts) =>
{
if (success)
{
Debug.Log($"Restore purchases succeeded. Restored products: {string.Join(", ", restoredProducts)}");
foreach (var productId in restoredProducts)
{
MarkProductAsPurchased(productId);
}
}
});
}
public void Restore(Action<bool, List<string>> action)
{
RestorePurchases((success, restoredProducts) =>
{
if (success)
{
action?.Invoke(true, restoredProducts);
Debug.Log($"Restore purchases succeeded. Restored products: {string.Join(", ", restoredProducts)}");
foreach (var productId in restoredProducts)
{
MarkProductAsPurchased(productId);
}
}
});
}
private void MarkProductAsPurchased(string productId)
{
PlayerPrefs.SetInt("Purchased_" + productId, 1);
PlayerPrefs.Save();
}
public bool IsProductPurchased(string productId)
{
return PlayerPrefs.GetInt("Purchased_" + productId, 0) == 1;
}
public void BuyProduct(string productId)
{
try
{
if (IsInitialized())
{
var product = storeController.products.WithID(productId);
if (product != null && product.availableToPurchase)
{
Debug.Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));
storeController.InitiatePurchase(product);
}
else
{
Debug.Log($"BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase {productId}");
}
}
else
{
Debug.Log("BuyProductID FAIL. Not initialized.");
}
}
catch (Exception e)
{
Debug.Log("BuyProductID: FAIL. Exception during purchase. " + e);
}
}
public decimal GetProductLocalizedPrice(string productId)
{
if (IsInitialized())
{
var product = storeController.products.WithID(productId);
if (product != null)
{
return product.metadata.localizedPrice;
}
}
return 0m;
}
public string GetProductLocalizedPriceString(string productId)
{
if (IsInitialized())
{
var product = storeController.products.WithID(productId);
if (product != null)
{
return product.metadata.localizedPriceString;
}
}
return string.Empty;
}
public void OnPurchaseFailed(Product product, PurchaseFailureDescription failureDescription)
{
Debug.Log("OnPurchaseFailed: FAIL. Product: " + product.definition.id + " PurchaseFailureDescription: " + failureDescription);
}
public void OnPurchaseFailed(Product i, PurchaseFailureReason p)
{
Debug.Log($"OnPurchaseFailed: FAIL. Product: '{i.definition.id}', PurchaseFailureReason: {p}");
}
public void OnInitializeFailed(InitializationFailureReason reason)
{
Debug.Log("OnInitializeFailed InitializationFailureReason:" + reason);
}
public void OnInitializeFailed(InitializationFailureReason error, string message)
{
Debug.Log("OnInitializeFailed InitializationFailureReason:" + error + " message: " + message);
}
public void RestorePurchases(Action<bool, List<string>> onRestore)
{
if (!IsInitialized())
{
Debug.Log("RestorePurchases FAIL. Not initialized.");
onRestore(false, new List<string>());
return;
}
var restoredProducts = new List<string>();
Action<bool> restoreCallback = success =>
{
if (success)
{
foreach (var product in storeController.products.all)
{
if (product.hasReceipt)
{
restoredProducts.Add(product.definition.id);
}
}
}
Debug.Log($"RestorePurchases finished. Success: {success}, Restored products: {string.Join(", ", restoredProducts)}");
onRestore(success, restoredProducts);
OnRestorePurchasesFinished?.Invoke(success, restoredProducts);
};
if (Application.platform == RuntimePlatform.IPhonePlayer ||
Application.platform == RuntimePlatform.OSXPlayer)
{
Debug.Log("RestorePurchases started ...");
var apple = extensionProvider.GetExtension<IAppleExtensions>();
apple.RestoreTransactions((result, message) =>
{
Debug.Log("RestorePurchases continuing: " + message);
restoreCallback(result);
});
}
else if (Application.platform == RuntimePlatform.Android)
{
var googlePlayStoreExtensions = extensionProvider.GetExtension<IGooglePlayStoreExtensions>();
googlePlayStoreExtensions.RestoreTransactions((success, message) =>
{
if (success)
{
Debug.Log("Transactions restored successfully: " + message);
}
else
{
Debug.LogError("Failed to restore transactions: " + message);
}
restoreCallback(success);
});
}
else
{
Debug.Log("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform);
restoreCallback(false);
}
}
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
{
Debug.Log($"ProcessPurchase: PASS. Product: '{args.purchasedProduct.definition.id}'");
if (args.purchasedProduct.definition.type == ProductType.NonConsumable)
{
MarkProductAsPurchased(args.purchasedProduct.definition.id);
}
OnSuccessfulPurchase?.Invoke(args.purchasedProduct.definition.id);
return PurchaseProcessingResult.Complete;
}
}
#endif
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 52d5d6d92bf64249bacbd9aca992fd28
timeCreated: 1709972098
@@ -0,0 +1,123 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using VContainer;
#if UNITY_PURCHASING
#endif
namespace WordsToolkit.Scripts.Services.IAP
{
public class IAPManager : MonoBehaviour, IIAPManager
{
private IIAPService iapController;
[Inject]
public void Construct(IIAPService iapService)
{
iapController = iapService;
}
public async Task InitializePurchasing(IEnumerable<(string productId, ProductTypeWrapper.ProductType productType)> products)
{
#if UNITY_PURCHASING
if (iapController is IAPController controller)
{
controller.InitializePurchasing(products);
while (!controller.IsInitialized())
{
await Task.Delay(100);
}
}
#endif
}
public void SubscribeToPurchaseEvent(Action<string> purchaseHandler)
{
#if UNITY_PURCHASING
IAPController.OnSuccessfulPurchase += purchaseHandler;
#endif
}
public void UnsubscribeFromPurchaseEvent(Action<string> purchaseHandler)
{
#if UNITY_PURCHASING
IAPController.OnSuccessfulPurchase -= purchaseHandler;
#endif
}
public void BuyProduct(string productId)
{
iapController.BuyProduct(productId);
}
public decimal GetProductLocalizedPrice(string productId)
{
return iapController.GetProductLocalizedPrice(productId);
}
public string GetProductLocalizedPriceString(string productId)
{
return iapController.GetProductLocalizedPriceString(productId);
}
public bool IsProductPurchased(string productId)
{
#if UNITY_PURCHASING
if (iapController is IAPController controller)
{
return controller.IsProductPurchased(productId);
}
#endif
return false;
}
public void RestorePurchases(Action<bool, List<string>> action)
{
#if UNITY_PURCHASING
if (iapController is IAPController controller)
{
controller.Restore(action);
}
#endif
}
}
public class DummyIAPService : IIAPService
{
public void InitializePurchasing(IEnumerable<(string productId, ProductTypeWrapper.ProductType productType)> products)
{
}
public void BuyProduct(string productId)
{
}
public decimal GetProductLocalizedPrice(string productId)
{
return 0m;
}
public string GetProductLocalizedPriceString(string productId)
{
return string.Empty;
}
public bool IsInitialized()
{
return true;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4ff980a1000879c44b057af715d834ae
timeCreated: 1458726993
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace WordsToolkit.Scripts.Services.IAP
{
public interface IIAPManager
{
Task InitializePurchasing(IEnumerable<(string productId, ProductTypeWrapper.ProductType productType)> products);
void BuyProduct(string productId);
decimal GetProductLocalizedPrice(string productId);
string GetProductLocalizedPriceString(string productId);
bool IsProductPurchased(string productId);
void RestorePurchases(Action<bool, List<string>> action);
void SubscribeToPurchaseEvent(Action<string> purchaseHandler);
void UnsubscribeFromPurchaseEvent(Action<string> purchaseHandler);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b9df0059ab0544edf813589961dc11d2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,25 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System.Collections.Generic;
namespace WordsToolkit.Scripts.Services.IAP
{
public interface IIAPService
{
public void InitializePurchasing(IEnumerable<(string productId, ProductTypeWrapper.ProductType productType)> products);
void BuyProduct(string productId);
decimal GetProductLocalizedPrice(string productId);
string GetProductLocalizedPriceString(string productId);
bool IsInitialized();
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ae2bec11927d49c69e8bb913a0a3c7be
timeCreated: 1709976367
@@ -0,0 +1,10 @@
using System;
using System.Threading.Tasks;
namespace WordsToolkit.Scripts.Services.IAP
{
public interface IInitializeGamingServices
{
Task Initialize(Action onSuccess, Action<string> onError);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1a3f2bd448f774ee89a9b4ede6eebcc2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,41 @@
// // ©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.Threading.Tasks;
using Unity.Services.Core;
using Unity.Services.Core.Environments;
using UnityEngine;
using VContainer;
namespace WordsToolkit.Scripts.Services.IAP
{
public class InitializeGamingServices : MonoBehaviour, IInitializeGamingServices
{
private const string k_Environment = "production";
public async Task Initialize(Action onSuccess, Action<string> onError)
{
#if UNITY_PURCHASING
try
{
var options = new InitializationOptions().SetEnvironmentName(k_Environment);
await UnityServices.InitializeAsync(options).ContinueWith(task => onSuccess());
}
catch (Exception exception)
{
onError(exception.Message);
}
#endif
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d070cf48df2d4d3db819987c544aa328
timeCreated: 1650900975
@@ -0,0 +1,37 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEngine;
namespace WordsToolkit.Scripts.Services.IAP
{
[CreateAssetMenu(fileName = "ProductID", menuName = "WordConnectGameToolkit/IAP/ProductID", order = 0)]
public class ProductID : ScriptableObject
{
public ProductTypeWrapper.ProductType productType;
public string androidId;
public string iosId;
public string ID {
get {
if (Application.platform == RuntimePlatform.Android) {
return androidId;
} else if (Application.platform == RuntimePlatform.IPhonePlayer) {
return iosId;
} else {
return androidId; // existing 'id' field
}
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c45bac7a3df748a6935182751a4577f6
timeCreated: 1725686838
@@ -0,0 +1,40 @@
// // ©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.Services.IAP
{
/// <summary>
/// Wrapper for the ProductType in case the Unity Purchasing system is not available.
/// </summary>
public class ProductTypeWrapper
{
// Enum definition to be used when the purchasing library is not available.
public enum ProductType
{
Consumable,
NonConsumable,
Subscription
}
#if UNITY_PURCHASING
public static UnityEngine.Purchasing.ProductType GetProductType(ProductType productType)
{
return (UnityEngine.Purchasing.ProductType)(int)productType;
}
#else
public static ProductType GetProductType(ProductType productType)
{
return productType;
}
#endif
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 39ed2a1121a6459a920f914695f83c34
timeCreated: 1726630627
@@ -0,0 +1,25 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System;
using WordsToolkit.Scripts.Services.Ads.AdUnits;
namespace WordsToolkit.Scripts.Services
{
public interface IAdsManager
{
void ShowAdByType(AdReference adRef, Action<string> shown);
bool IsRewardedAvailable(AdReference adRef);
void RemoveAds();
bool IsNoAdsPurchased();
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9da3f58fd19d483ba8593b80693a4cec
timeCreated: 1744905563
@@ -0,0 +1,124 @@
using System.Collections.Generic;
using UnityEngine;
using VContainer;
using VContainer.Unity;
using WordsToolkit.Scripts.Enums;
using WordsToolkit.Scripts.Levels;
using WordsToolkit.Scripts.Localization;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.Services
{
public interface ILanguageService
{
string GetCurrentLanguageCode();
LanguageConfiguration.LanguageInfo GetCurrentLanguageInfo();
List<LanguageConfiguration.LanguageInfo> GetEnabledLanguages();
void SetLanguage(string languageCode);
bool HasMultipleEnabledLanguages();
void InitializeDefaultLanguage();
}
public class LanguageService : ILanguageService, IInitializable
{
private const string LANGUAGE_PREF_KEY = "SelectedLanguage";
private readonly IObjectResolver container;
private LanguageConfiguration languageConfiguration;
private string currentLanguageCode;
public LanguageService(IObjectResolver container)
{
this.container = container;
}
public void Initialize()
{
languageConfiguration = container.Resolve<LanguageConfiguration>();
InitializeDefaultLanguage();
}
public void InitializeDefaultLanguage()
{
currentLanguageCode = GetSavedOrDefaultLanguage();
ApplyLanguage(currentLanguageCode);
}
public string GetCurrentLanguageCode()
{
if (string.IsNullOrEmpty(currentLanguageCode))
{
currentLanguageCode = GetSavedOrDefaultLanguage();
}
return currentLanguageCode;
}
public LanguageConfiguration.LanguageInfo GetCurrentLanguageInfo()
{
if (languageConfiguration == null) return null;
var languageCode = GetCurrentLanguageCode();
return languageConfiguration.GetLanguageInfo(languageCode);
}
public List<LanguageConfiguration.LanguageInfo> GetEnabledLanguages()
{
if (languageConfiguration == null) return new List<LanguageConfiguration.LanguageInfo>();
return languageConfiguration.GetEnabledLanguages();
}
public void SetLanguage(string languageCode)
{
if (languageConfiguration == null) return;
var languageInfo = languageConfiguration.GetLanguageInfo(languageCode);
if (languageInfo != null && languageInfo.enabledByDefault)
{
currentLanguageCode = languageCode;
PlayerPrefs.SetString(LANGUAGE_PREF_KEY, languageCode);
PlayerPrefs.Save();
ApplyLanguage(languageCode);
EventManager.GetEvent<string>(EGameEvent.LanguageChanged).Invoke(languageCode);
}
}
public bool HasMultipleEnabledLanguages()
{
return GetEnabledLanguages().Count > 1;
}
private string GetSavedOrDefaultLanguage()
{
if (languageConfiguration == null)
{
Debug.LogWarning("LanguageService: LanguageConfiguration is null, using 'en'");
return "en";
}
var enabledLanguages = languageConfiguration.GetEnabledLanguages();
Debug.Log($"LanguageService: Found {enabledLanguages.Count} enabled languages");
// If only one language is enabled, use it regardless of saved preference
if (enabledLanguages.Count == 1)
{
Debug.Log($"LanguageService: Using single enabled language: {enabledLanguages[0].code}");
return enabledLanguages[0].code;
}
// If multiple languages are enabled, use saved preference or default
var defaultLang = languageConfiguration.defaultLanguage ?? "en";
var selectedLang = PlayerPrefs.GetString(LANGUAGE_PREF_KEY, defaultLang);
Debug.Log($"LanguageService: Using selected/default language: {selectedLang} (default: {defaultLang})");
return selectedLang;
}
private void ApplyLanguage(string languageCode)
{
if (languageConfiguration == null) return;
var languageInfo = languageConfiguration.GetLanguageInfo(languageCode);
if (languageInfo?.localizationBase != null)
{
LocalizationManager.instance.LoadLanguageFromBase(languageInfo.localizationBase);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f5edc9f7c45834c179b8d8f83ee86589
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: