start of update

This commit is contained in:
Ell 2023-02-11 10:16:42 +01:00
parent d4bd862b04
commit f7bc8738d1
38 changed files with 2001 additions and 3398 deletions

View file

@ -0,0 +1,36 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-mgcb": {
"version": "3.8.1.303",
"commands": [
"mgcb"
]
},
"dotnet-mgcb-editor": {
"version": "3.8.1.303",
"commands": [
"mgcb-editor"
]
},
"dotnet-mgcb-editor-linux": {
"version": "3.8.1.303",
"commands": [
"mgcb-editor-linux"
]
},
"dotnet-mgcb-editor-windows": {
"version": "3.8.1.303",
"commands": [
"mgcb-editor-windows"
]
},
"dotnet-mgcb-editor-mac": {
"version": "3.8.1.303",
"commands": [
"mgcb-editor-mac"
]
}
}
}

View file

@ -1,95 +1,95 @@
using System;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Gms.Common;
using Android.Gms.Games;
using Android.OS;
using Android.Views;
using Android.Widget;
using GameAnalyticsSDK;
using Java.Lang;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using MLEM.Extensions;
using MLEM.Misc;
using TouchyTickets;
using static Android.Views.SystemUiFlags;
using static Android.Views.ViewGroup.LayoutParams;
using static Android.Views.ViewGroup;
using Uri = Android.Net.Uri;
namespace Android {
[Activity(
Label = "@string/app_name",
MainLauncher = true,
Icon = "@drawable/icon",
AlwaysRetainTaskState = true,
LaunchMode = LaunchMode.SingleInstance,
ScreenOrientation = ScreenOrientation.UserPortrait,
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden | ConfigChanges.ScreenSize
)]
public class Activity1 : AndroidGameActivity {
namespace Android;
private GameImpl game;
private AndroidPlatform platform;
private LinearLayout mainView;
[Activity(
Label = "@string/app_name",
MainLauncher = true,
Icon = "@drawable/icon",
AlwaysRetainTaskState = true,
LaunchMode = LaunchMode.SingleInstance,
ScreenOrientation = ScreenOrientation.UserPortrait,
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden | ConfigChanges.ScreenSize
)]
public class Activity1 : AndroidGameActivity {
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
private GameImpl game;
private AndroidPlatform platform;
private LinearLayout mainView;
// ad layout
var adLayout = new LinearLayout(this) {Orientation = Orientation.Vertical};
adLayout.SetGravity(GravityFlags.Bottom);
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
// set up the game
MlemPlatform.Current = new MlemPlatform.Mobile(KeyboardInput.Show, l => this.StartActivity(new Intent(Intent.ActionView, Uri.Parse(l))));
this.platform = new AndroidPlatform(this, adLayout);
this.game = new GameImpl(this.platform);
this.game.GraphicsDeviceManager.ResetWidthAndHeight(this.game.Window);
this.game.GraphicsDeviceManager.IsFullScreen = true;
this.game.OnLoadContent += game => game.InputHandler.HandleMouse = false;
// ad layout
var adLayout = new LinearLayout(this) {Orientation = Orientation.Vertical};
adLayout.SetGravity(GravityFlags.Bottom);
var gameView = this.game.Services.GetService(typeof(View)) as View;
gameView.LayoutChange += (o, args) => {
// force the game size to update when the ad size changes
this.game.GraphicsDeviceManager.PreferredBackBufferWidth = args.Right - args.Left;
this.game.GraphicsDeviceManager.PreferredBackBufferHeight = args.Bottom - args.Top;
this.game.GraphicsDeviceManager.ApplyChanges();
};
// set up the game
MlemPlatform.Current = new MlemPlatform.Mobile(KeyboardInput.Show, l => this.StartActivity(new Intent(Intent.ActionView, Uri.Parse(l))));
this.platform = new AndroidPlatform(this, adLayout);
this.game = new GameImpl(this.platform);
this.game.GraphicsDeviceManager.ResetWidthAndHeight(this.game.Window);
this.game.GraphicsDeviceManager.IsFullScreen = true;
this.game.OnLoadContent += game => game.InputHandler.HandleMouse = false;
// don't render under notches
if (Build.VERSION.SdkInt >= BuildVersionCodes.P)
this.Window.Attributes.LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.Never;
var gameView = this.game.Services.GetService(typeof(View)) as View;
gameView.LayoutChange += (_, args) => {
// force the game size to update when the ad size changes
this.game.GraphicsDeviceManager.PreferredBackBufferWidth = args.Right - args.Left;
this.game.GraphicsDeviceManager.PreferredBackBufferHeight = args.Bottom - args.Top;
this.game.GraphicsDeviceManager.ApplyChanges();
};
// total layout that is displayed
this.mainView = new LinearLayout(this) {Orientation = Orientation.Vertical};
this.mainView.LayoutParameters = new LinearLayout.LayoutParams(MatchParent, MatchParent);
this.mainView.AddView(gameView);
// height of 0 but high weight causes this element so scale based on the ad's height
gameView.LayoutParameters = new LinearLayout.LayoutParams(MatchParent, 0, 1);
this.mainView.AddView(adLayout);
adLayout.LayoutParameters = new LinearLayout.LayoutParams(MatchParent, WrapContent);
this.SetContentView(this.mainView);
// don't render under notches
if (Build.VERSION.SdkInt >= BuildVersionCodes.P)
this.Window.Attributes.LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.Never;
this.game.Run();
}
protected override void OnActivityResult(int requestCode, Result result, Intent data) {
base.OnActivityResult(requestCode, result, data);
// Connect again after logging in to Google Play game services, but only if we haven't tried yet
try {
if (requestCode == AndroidPlatform.GooglePlayLoginRequest && (int) result != GamesActivityResultCodes.ResultSignInFailed)
this.platform.GoogleApi.Connect();
} catch (Exception e) {
GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "OnActivityResult " + e);
}
}
public override void OnWindowFocusChanged(bool hasFocus) {
base.OnWindowFocusChanged(hasFocus);
// hide the status bar
if (hasFocus)
this.Window.DecorView.SystemUiVisibility = (StatusBarVisibility) (ImmersiveSticky | LayoutStable | LayoutHideNavigation | LayoutFullscreen | HideNavigation | Fullscreen);
}
// total layout that is displayed
this.mainView = new LinearLayout(this) {Orientation = Orientation.Vertical};
this.mainView.LayoutParameters = new LinearLayout.LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent);
this.mainView.AddView(gameView);
// height of 0 but high weight causes this element so scale based on the ad's height
gameView.LayoutParameters = new LinearLayout.LayoutParams(LayoutParams.MatchParent, 0, 1);
this.mainView.AddView(adLayout);
adLayout.LayoutParameters = new LinearLayout.LayoutParams(LayoutParams.MatchParent, LayoutParams.WrapContent);
this.SetContentView(this.mainView);
this.game.Run();
}
protected override void OnActivityResult(int requestCode, Result result, Intent data) {
base.OnActivityResult(requestCode, result, data);
// Connect again after logging in to Google Play game services, but only if we haven't tried yet
try {
if (requestCode == AndroidPlatform.GooglePlayLoginRequest && (int) result != GamesActivityResultCodes.ResultSignInFailed)
this.platform.GoogleApi.Connect();
} catch (Exception e) {
GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "OnActivityResult " + e);
}
}
public override void OnWindowFocusChanged(bool hasFocus) {
base.OnWindowFocusChanged(hasFocus);
#pragma warning disable CS0618
// hide the status bar
if (hasFocus)
this.Window.DecorView.SystemUiVisibility = (StatusBarVisibility) (SystemUiFlags.ImmersiveSticky | SystemUiFlags.LayoutStable | SystemUiFlags.LayoutHideNavigation | SystemUiFlags.LayoutFullscreen | SystemUiFlags.HideNavigation | SystemUiFlags.Fullscreen);
#pragma warning restore CS0618
}
}

View file

@ -1,94 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{410C0262-131C-4D0E-910D-D01B4F7143E0}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Android</RootNamespace>
<AssemblyName>Android</AssemblyName>
<FileAlignment>512</FileAlignment>
<AndroidApplication>true</AndroidApplication>
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<AndroidResgenClass>Resource</AndroidResgenClass>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidStoreUncompressedFileExtensions>.m4a</AndroidStoreUncompressedFileExtensions>
<TargetFrameworkVersion>v9.0</TargetFrameworkVersion>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
<AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\</OutputPath>
<DefineConstants>DEBUG;TRACE;ANDROID</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
<AndroidLinkMode>None</AndroidLinkMode>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\</OutputPath>
<DefineConstants>TRACE;ANDROID</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
<AotAssemblies>false</AotAssemblies>
<EnableLLVM>false</EnableLLVM>
<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
<BundleAssemblies>false</BundleAssemblies>
<MandroidI18n />
<AndroidPackageFormat>aab</AndroidPackageFormat>
<AndroidUseAapt2>true</AndroidUseAapt2>
<AndroidCreatePackagePerAbi>false</AndroidCreatePackagePerAbi>
<TargetFramework>net6.0-android</TargetFramework>
<SupportedOSPlatformVersion>31</SupportedOSPlatformVersion>
<OutputType>Exe</OutputType>
<ApplicationId>de.ellpeck.touchytickets</ApplicationId>
<ApplicationVersion>1.2.1</ApplicationVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml" />
<Reference Include="Mono.Android" />
<ProjectReference Include="..\TouchyTickets\TouchyTickets.csproj"/>
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303"/>
<PackageReference Include="MonoGame.Framework.Android" Version="3.8.1.303"/>
<PackageReference Include="Contentless" Version="3.0.7"/>
<PackageReference Include="GameAnalytics.Xamarin.SDK" Version="5.2.5"/>
<PackageReference Include="Xamarin.GooglePlayServices.Games" Version="123.1.0.1"/>
</ItemGroup>
<ItemGroup>
<Compile Include="Activity1.cs" />
<Compile Include="AndroidPlatform.cs" />
<Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<MonoGameContentReference Include="..\TouchyTickets\Content\Content.mgcb"/>
<None Include="..\TouchyTickets\Content\*\**">
<Link>Content/%(RecursiveDir)%(Filename)%(Extension)</Link>
</None>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\Drawable\Icon.png" />
<AndroidResource Include="Resources\Values\Strings.xml" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
<None Include="..\TouchyTickets\Content\*\**" />
<MonoGameContentReference Include="..\TouchyTickets\Content\Content.mgcb">
<Link>Content\Content.mgcb</Link>
</MonoGameContentReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Contentless" Version="3.0.6" />
<PackageReference Include="GameAnalytics.Xamarin.SDK" Version="5.2.3" />
<PackageReference Include="MonoGame.Content.Builder" Version="3.7.0.9" />
<PackageReference Include="MonoGame.Framework.Android" Version="3.8.0.1641" />
<PackageReference Include="Xamarin.GooglePlayServices.Games" Version="121.0.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TouchyTickets\TouchyTickets.csproj">
<Project>{3df7ae69-f3f0-461a-be98-f31eb576b5e2}</Project>
<Name>TouchyTickets</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<Target Name="RestoreDotnetTools" BeforeTargets="Restore">
<Message Text="Restoring dotnet tools" Importance="High"/>
<Exec Command="dotnet tool restore"/>
</Target>
</Project>

View file

@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.ellpeck.touchytickets" android:installLocation="auto"
android:versionCode="121" android:versionName="1.2.1">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.ellpeck.touchytickets"
android:installLocation="auto" android:versionCode="121" android:versionName="1.2.1">
<uses-sdk android:minSdkVersion="23" android:targetSdkVersion="31"/>
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
<application android:label="Touchy Tickets" android:resizeableActivity="true">
<meta-data android:name="com.google.android.gms.games.APP_ID" android:value="\u003169609944700" />
<meta-data android:name="com.google.android.gms.games.APP_ID" android:value="\u003169609944700"/>
</application>
<permission android:name="ACCESS_NETWORK_STATE"/>
<permission android:name="INTERNET"/>

View file

@ -1,149 +1,145 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Android.App;
using Android.Content;
using Android.Gms.Common;
using Android.Gms.Common.Apis;
using Android.Gms.Extensions;
using Android.Gms.Games;
using Android.Gms.Games.Achievement;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Coroutine;
using GameAnalyticsSDK;
using GameAnalyticsSDK.Utilities;
using Java.Lang;
using TouchyTickets;
using Achievement = TouchyTickets.Achievement;
using Uri = Android.Net.Uri;
namespace Android {
public class AndroidPlatform : Platform {
namespace Android;
public const int GooglePlayLoginRequest = 9001;
public const int ShowAchievementsRequest = 9002;
public class AndroidPlatform : Platform {
private static readonly Dictionary<string, string> AchievementIds = new Dictionary<string, string> {
{"1Stars", "CgkI_Lyp7PcEEAIQAw"},
{"10Stars", "CgkI_Lyp7PcEEAIQBA"},
{"100Stars", "CgkI_Lyp7PcEEAIQBQ"},
{"FullMap", "CgkI_Lyp7PcEEAIQBg"},
{"OnlySmallRides", "CgkI_Lyp7PcEEAIQBw"},
{"OnlyRelaxedRides", "CgkI_Lyp7PcEEAIQCA"},
{"OnlyWalkingRides", "CgkI_Lyp7PcEEAIQCQ"},
{"OnlyNonTechnologyRides", "CgkI_Lyp7PcEEAIQCg"},
{"100Modifiers", "CgkI_Lyp7PcEEAIQCw"},
{"500Modifiers", "CgkI_Lyp7PcEEAIQDA"},
{"1000Modifiers", "CgkI_Lyp7PcEEAIQDQ"},
{"5000Modifiers", "CgkI_Lyp7PcEEAIQDg"},
{"1ExpTickets", "CgkI_Lyp7PcEEAIQDw"},
{"2ExpTickets", "CgkI_Lyp7PcEEAIQEA"},
{"3ExpTickets", "CgkI_Lyp7PcEEAIQEQ"},
{"4ExpTickets", "CgkI_Lyp7PcEEAIQEg"},
{"5ExpTickets", "CgkI_Lyp7PcEEAIQEw"},
{"6ExpTickets", "CgkI_Lyp7PcEEAIQFA"},
{"7ExpTickets", "CgkI_Lyp7PcEEAIQFQ"},
{"8ExpTickets", "CgkI_Lyp7PcEEAIQFg"},
{"9ExpTickets", "CgkI_Lyp7PcEEAIQFw"},
{"10ExpTickets", "CgkI_Lyp7PcEEAIQGA"}
};
public const int GooglePlayLoginRequest = 9001;
public const int ShowAchievementsRequest = 9002;
private readonly Activity activity;
private readonly LinearLayout adLayout;
public GoogleApiClient GoogleApi { get; private set; }
private static readonly Dictionary<string, string> AchievementIds = new() {
{"1Stars", "CgkI_Lyp7PcEEAIQAw"},
{"10Stars", "CgkI_Lyp7PcEEAIQBA"},
{"100Stars", "CgkI_Lyp7PcEEAIQBQ"},
{"FullMap", "CgkI_Lyp7PcEEAIQBg"},
{"OnlySmallRides", "CgkI_Lyp7PcEEAIQBw"},
{"OnlyRelaxedRides", "CgkI_Lyp7PcEEAIQCA"},
{"OnlyWalkingRides", "CgkI_Lyp7PcEEAIQCQ"},
{"OnlyNonTechnologyRides", "CgkI_Lyp7PcEEAIQCg"},
{"100Modifiers", "CgkI_Lyp7PcEEAIQCw"},
{"500Modifiers", "CgkI_Lyp7PcEEAIQDA"},
{"1000Modifiers", "CgkI_Lyp7PcEEAIQDQ"},
{"5000Modifiers", "CgkI_Lyp7PcEEAIQDg"},
{"1ExpTickets", "CgkI_Lyp7PcEEAIQDw"},
{"2ExpTickets", "CgkI_Lyp7PcEEAIQEA"},
{"3ExpTickets", "CgkI_Lyp7PcEEAIQEQ"},
{"4ExpTickets", "CgkI_Lyp7PcEEAIQEg"},
{"5ExpTickets", "CgkI_Lyp7PcEEAIQEw"},
{"6ExpTickets", "CgkI_Lyp7PcEEAIQFA"},
{"7ExpTickets", "CgkI_Lyp7PcEEAIQFQ"},
{"8ExpTickets", "CgkI_Lyp7PcEEAIQFg"},
{"9ExpTickets", "CgkI_Lyp7PcEEAIQFw"},
{"10ExpTickets", "CgkI_Lyp7PcEEAIQGA"}
};
public AndroidPlatform(Activity activity, LinearLayout adLayout) {
this.activity = activity;
this.adLayout = adLayout;
}
public override void SetupOnlineInteractions(Dictionary<string, object> analyticsJson) {
// Analytics
GameAnalytics.SetAutoDetectAppVersion(true);
GameAnalytics.Initialize(this.activity, GA_MiniJSON.Serialize(new Hashtable(analyticsJson)));
AndroidEnvironment.UnhandledExceptionRaiser += (o, args) => GameAnalytics.NewErrorEvent(GAErrorSeverity.Critical, args.Exception.ToString());
// TODO fix ads
// Ads
/*try {
var ad = new AdView(this.activity) {
AdUnitId = "ca-app-pub-5754829579653773/7841535920",
AdSize = AdSize.SmartBanner
};
ad.LoadAd(new AdRequest.Builder()
.AddTestDevice("14B965C6457E17D2808061ADF7E34923")
.Build());
this.adLayout.AddView(ad);
} catch (Exception e) {
GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "Ads " + e);
}*/
// TODO fix google play game services
/*// Google Play game services
try {
this.GoogleApi = new GoogleApiClient.Builder(this.activity)
.AddApi(GamesClass.API)
.AddScope(GamesClass.ScopeGames)
.AddOnConnectionFailedListener(res => {
if (res.HasResolution) {
res.StartResolutionForResult(this.activity, GooglePlayLoginRequest);
} else {
throw new GoogleApiClientConnectionException(res);
}
})
.Build();
this.GoogleApi.Connect();
} catch (Exception e) {
GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "GoogleApiClient " + e);
}*/
#if DEBUG
// Sanity check to ensure that all achievements are mapped
foreach (var achievement in Achievement.Achievements.Values) {
var _ = AchievementIds[achievement.Name];
}
#endif
}
public override void AddResourceEvent(bool sink, string currency, float amount, string itemType, string itemId) {
GameAnalytics.NewResourceEvent(sink ? GAResourceFlowType.Sink : GAResourceFlowType.Source, currency, amount, itemType, itemId);
}
public override void SetKeepScreenOn(bool keep) {
if (keep) {
this.activity.Window.AddFlags(WindowManagerFlags.KeepScreenOn);
} else {
this.activity.Window.ClearFlags(WindowManagerFlags.KeepScreenOn);
}
}
public override void OpenRateLink() {
this.activity.StartActivity(new Intent(Intent.ActionView, Uri.Parse("https://play.google.com/store/apps/details?id=de.ellpeck.touchytickets")));
}
public override bool GainAchievement(Achievement achievement) {
try {
if (this.GoogleApi != null && this.GoogleApi.IsConnected) {
GamesClass.Achievements.Unlock(this.GoogleApi, AchievementIds[achievement.Name]);
return true;
}
} catch (Exception e) {
GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "GainAchievement " + e);
}
return false;
}
public override void ShowAchievements() {
try {
if (this.GoogleApi == null || !this.GoogleApi.IsConnected)
return;
var intent = GamesClass.Achievements.GetAchievementsIntent(this.GoogleApi);
this.activity.StartActivityForResult(intent, ShowAchievementsRequest);
} catch (Exception e) {
GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "ShowAchievements " + e);
}
}
private readonly Activity activity;
private readonly LinearLayout adLayout;
public GoogleApiClient GoogleApi { get; private set; }
public AndroidPlatform(Activity activity, LinearLayout adLayout) {
this.activity = activity;
this.adLayout = adLayout;
}
public override void SetupOnlineInteractions(Dictionary<string, object> analyticsJson) {
// Analytics
GameAnalytics.SetAutoDetectAppVersion(true);
GameAnalytics.Initialize(this.activity, GA_MiniJSON.Serialize(new Hashtable(analyticsJson)));
AndroidEnvironment.UnhandledExceptionRaiser += (_, args) => GameAnalytics.NewErrorEvent(GAErrorSeverity.Critical, args.Exception.ToString());
// TODO fix ads
// Ads
/*try {
var ad = new AdView(this.activity) {
AdUnitId = "ca-app-pub-5754829579653773/7841535920",
AdSize = AdSize.SmartBanner
};
ad.LoadAd(new AdRequest.Builder()
.AddTestDevice("14B965C6457E17D2808061ADF7E34923")
.Build());
this.adLayout.AddView(ad);
} catch (Exception e) {
GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "Ads " + e);
}*/
// TODO fix google play game services
/*// Google Play game services
try {
this.GoogleApi = new GoogleApiClient.Builder(this.activity)
.AddApi(GamesClass.API)
.AddScope(GamesClass.ScopeGames)
.AddOnConnectionFailedListener(res => {
if (res.HasResolution) {
res.StartResolutionForResult(this.activity, GooglePlayLoginRequest);
} else {
throw new GoogleApiClientConnectionException(res);
}
})
.Build();
this.GoogleApi.Connect();
} catch (Exception e) {
GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "GoogleApiClient " + e);
}*/
#if DEBUG
// Sanity check to ensure that all achievements are mapped
foreach (var achievement in Achievement.Achievements.Values) {
var _ = AndroidPlatform.AchievementIds[achievement.Name];
}
#endif
}
public override void AddResourceEvent(bool sink, string currency, float amount, string itemType, string itemId) {
GameAnalytics.NewResourceEvent(sink ? GAResourceFlowType.Sink : GAResourceFlowType.Source, currency, amount, itemType, itemId);
}
public override void SetKeepScreenOn(bool keep) {
if (keep) {
this.activity.Window.AddFlags(WindowManagerFlags.KeepScreenOn);
} else {
this.activity.Window.ClearFlags(WindowManagerFlags.KeepScreenOn);
}
}
public override void OpenRateLink() {
this.activity.StartActivity(new Intent(Intent.ActionView, Uri.Parse("https://play.google.com/store/apps/details?id=de.ellpeck.touchytickets")));
}
public override bool GainAchievement(Achievement achievement) {
try {
if (this.GoogleApi != null && this.GoogleApi.IsConnected) {
GamesClass.Achievements.Unlock(this.GoogleApi, AndroidPlatform.AchievementIds[achievement.Name]);
return true;
}
} catch (Exception e) {
GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "GainAchievement " + e);
}
return false;
}
public override void ShowAchievements() {
try {
if (this.GoogleApi == null || !this.GoogleApi.IsConnected)
return;
var intent = GamesClass.Achievements.GetAchievementsIntent(this.GoogleApi);
this.activity.StartActivityForResult(intent, AndroidPlatform.ShowAchievementsRequest);
} catch (Exception e) {
GameAnalytics.NewErrorEvent(GAErrorSeverity.Error, "ShowAchievements " + e);
}
}
}

View file

@ -1,17 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Android.App;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Touchy Tickets")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Touchy Tickets")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Touchy Tickets</string>
<string name="app_name">Touchy Tickets</string>
</resources>

View file

@ -4,8 +4,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TouchyTickets", "TouchyTick
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Android", "Android\Android.csproj", "{410C0262-131C-4D0E-910D-D01B4F7143E0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS", "iOS\iOS.csproj", "{CA7AB65C-57DE-412C-AF42-E7E6EDDF2D5F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -20,9 +18,5 @@ Global
{410C0262-131C-4D0E-910D-D01B4F7143E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{410C0262-131C-4D0E-910D-D01B4F7143E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{410C0262-131C-4D0E-910D-D01B4F7143E0}.Release|Any CPU.Build.0 = Release|Any CPU
{CA7AB65C-57DE-412C-AF42-E7E6EDDF2D5F}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator
{CA7AB65C-57DE-412C-AF42-E7E6EDDF2D5F}.Debug|Any CPU.Build.0 = Debug|iPhoneSimulator
{CA7AB65C-57DE-412C-AF42-E7E6EDDF2D5F}.Release|Any CPU.ActiveCfg = Release|iPhoneSimulator
{CA7AB65C-57DE-412C-AF42-E7E6EDDF2D5F}.Release|Any CPU.Build.0 = Release|iPhoneSimulator
EndGlobalSection
EndGlobal

View file

@ -5,56 +5,56 @@ using System.Numerics;
using Microsoft.Xna.Framework;
using TouchyTickets.Attractions;
namespace TouchyTickets {
public class Achievement {
namespace TouchyTickets;
public static readonly Dictionary<string, Achievement> Achievements = new Dictionary<string, Achievement>();
public class Achievement {
static Achievement() {
foreach (var amount in new[] {1, 10, 100})
Register(new Achievement($"{amount}Stars", g => g.Stars >= amount));
Register(new Achievement("FullMap", g => {
for (var x = 0; x < g.Map.Width; x++) {
for (var y = 0; y < g.Map.Height; y++) {
if (g.Map.GetAttractionAt(new Point(x, y)) == null)
return false;
}
public static readonly Dictionary<string, Achievement> Achievements = new();
static Achievement() {
foreach (var amount in new[] {1, 10, 100})
Achievement.Register(new Achievement($"{amount}Stars", g => g.Stars >= amount));
Achievement.Register(new Achievement("FullMap", g => {
for (var x = 0; x < g.Map.Width; x++) {
for (var y = 0; y < g.Map.Height; y++) {
if (g.Map.GetAttractionAt(new Point(x, y)) == null)
return false;
}
return true;
}));
foreach (var flag in new[] {AttractionFlags.Small, AttractionFlags.Relaxed, AttractionFlags.Walking, AttractionFlags.NonTechnology})
Register(new Achievement($"Only{flag}Rides", g => g.Map.GetAttractionAmount(null) >= 100 && g.Map.GetAttractions().All(a => a.Item2.Type.Flags.HasFlag(flag))));
foreach (var amount in new[] {100, 500, 1000, 5000})
Register(new Achievement($"{amount}Modifiers", g => g.Map.GetAttractionAmount(null) > 0 && g.Map.GetAttractions().All(a => a.Item2.GetModifierAmount(null) >= amount)));
for (var i = 1; i <= 10; i++) {
var amount = BigInteger.Pow(1000, i + 1);
Register(new Achievement($"{i}ExpTickets", g => g.Tickets >= amount));
}
return true;
}));
foreach (var flag in new[] {AttractionFlags.Small, AttractionFlags.Relaxed, AttractionFlags.Walking, AttractionFlags.NonTechnology})
Achievement.Register(new Achievement($"Only{flag}Rides", g => g.Map.GetAttractionAmount(null) >= 100 && g.Map.GetAttractions().All(a => a.Item2.Type.Flags.HasFlag(flag))));
foreach (var amount in new[] {100, 500, 1000, 5000})
Achievement.Register(new Achievement($"{amount}Modifiers", g => g.Map.GetAttractionAmount(null) > 0 && g.Map.GetAttractions().All(a => a.Item2.GetModifierAmount(null) >= amount)));
for (var i = 1; i <= 10; i++) {
var amount = BigInteger.Pow(1000, i + 1);
Achievement.Register(new Achievement($"{i}ExpTickets", g => g.Tickets >= amount));
}
public readonly string Name;
private readonly Func<GameImpl, bool> condition;
// this value doesn't save between game runs, since achievements
// are only displayed inside of the respective stores anyway
private bool unlocked;
public Achievement(string name, Func<GameImpl, bool> condition) {
this.Name = name;
this.condition = condition;
}
public void Update() {
if (this.unlocked)
return;
if (!this.condition.Invoke(GameImpl.Instance))
return;
if (GameImpl.Instance.Platform.GainAchievement(this))
this.unlocked = true;
}
public static void Register(Achievement achievement) {
Achievements.Add(achievement.Name, achievement);
}
}
public readonly string Name;
private readonly Func<GameImpl, bool> condition;
// this value doesn't save between game runs, since achievements
// are only displayed inside of the respective stores anyway
private bool unlocked;
public Achievement(string name, Func<GameImpl, bool> condition) {
this.Name = name;
this.condition = condition;
}
public void Update() {
if (this.unlocked)
return;
if (!this.condition.Invoke(GameImpl.Instance))
return;
if (GameImpl.Instance.Platform.GainAchievement(this))
this.unlocked = true;
}
public static void Register(Achievement achievement) {
Achievement.Achievements.Add(achievement.Name, achievement);
}
}

View file

@ -5,34 +5,34 @@ using MLEM.Font;
using MLEM.Startup;
using MLEM.Textures;
namespace TouchyTickets {
public static class Assets {
namespace TouchyTickets;
public static UniformTextureAtlas TilesTexture { get; private set; }
public static UniformTextureAtlas AttractionTexture { get; private set; }
public static UniformTextureAtlas UiTexture { get; private set; }
public static class Assets {
public static SoundEffect ClickSound { get; private set; }
public static SoundEffect PlaceSound { get; private set; }
public static SoundEffect BuySound { get; private set; }
public static UniformTextureAtlas TilesTexture { get; private set; }
public static UniformTextureAtlas AttractionTexture { get; private set; }
public static UniformTextureAtlas UiTexture { get; private set; }
public static Vector2 TileSize { get; private set; }
public static GenericFont Font { get; private set; }
public static GenericFont MonospacedFont { get; private set; }
public static SoundEffect ClickSound { get; private set; }
public static SoundEffect PlaceSound { get; private set; }
public static SoundEffect BuySound { get; private set; }
public static void Load() {
TilesTexture = new UniformTextureAtlas(MlemGame.LoadContent<Texture2D>("Textures/Tiles"), 16, 16);
AttractionTexture = new UniformTextureAtlas(MlemGame.LoadContent<Texture2D>("Textures/Attractions"), 16, 16);
UiTexture = new UniformTextureAtlas(MlemGame.LoadContent<Texture2D>("Textures/Ui"), 16, 16);
public static Vector2 TileSize { get; private set; }
public static GenericFont Font { get; private set; }
public static GenericFont MonospacedFont { get; private set; }
ClickSound = MlemGame.LoadContent<SoundEffect>("Sounds/Click");
PlaceSound = MlemGame.LoadContent<SoundEffect>("Sounds/Place");
BuySound = MlemGame.LoadContent<SoundEffect>("Sounds/StarBuy");
public static void Load() {
Assets.TilesTexture = new UniformTextureAtlas(MlemGame.LoadContent<Texture2D>("Textures/Tiles"), 16, 16);
Assets.AttractionTexture = new UniformTextureAtlas(MlemGame.LoadContent<Texture2D>("Textures/Attractions"), 16, 16);
Assets.UiTexture = new UniformTextureAtlas(MlemGame.LoadContent<Texture2D>("Textures/Ui"), 16, 16);
TileSize = new Vector2(AttractionTexture.RegionWidth, AttractionTexture.RegionHeight);
Font = new GenericSpriteFont(MlemGame.LoadContent<SpriteFont>("Fonts/" + Localization.Get("Font")));
MonospacedFont = new GenericSpriteFont(MlemGame.LoadContent<SpriteFont>("Fonts/Monospaced"));
}
Assets.ClickSound = MlemGame.LoadContent<SoundEffect>("Sounds/Click");
Assets.PlaceSound = MlemGame.LoadContent<SoundEffect>("Sounds/Place");
Assets.BuySound = MlemGame.LoadContent<SoundEffect>("Sounds/StarBuy");
Assets.TileSize = new Vector2(Assets.AttractionTexture.RegionWidth, Assets.AttractionTexture.RegionHeight);
Assets.Font = new GenericSpriteFont(MlemGame.LoadContent<SpriteFont>("Fonts/" + Localization.Get("Font")));
Assets.MonospacedFont = new GenericSpriteFont(MlemGame.LoadContent<SpriteFont>("Fonts/Monospaced"));
}
}

View file

@ -5,107 +5,104 @@ using System.Numerics;
using System.Runtime.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MLEM.Extensions;
using MLEM.Misc;
using MLEM.Startup;
using MLEM.Textures;
using TouchyTickets.Upgrades;
using static TouchyTickets.Attractions.AttractionFlags;
using static TouchyTickets.Attractions.AttractionType;
using Vector2 = Microsoft.Xna.Framework.Vector2;
namespace TouchyTickets.Attractions {
[DataContract]
public class Attraction {
namespace TouchyTickets.Attractions;
[DataMember]
public readonly List<ActiveModifier> Modifiers = new List<ActiveModifier>();
[DataMember]
public readonly AttractionType Type;
[DataMember]
private double ticketPercentage;
private float animationSizeModifier;
[DataContract]
public class Attraction {
public Attraction(AttractionType type) {
this.Type = type;
}
public double Update(TimeSpan passed, ParkMap map, Point position) {
var genRate = this.GetGenerationRate(map, position);
// apply generation rate to ticket amount
this.ticketPercentage += genRate * passed.TotalSeconds;
var total = (BigInteger) this.ticketPercentage;
if (total > 0) {
GameImpl.Instance.Tickets += total;
this.ticketPercentage -= (double) total;
}
// animation stuff
if (this.animationSizeModifier > 0)
this.animationSizeModifier = Math.Max(this.animationSizeModifier - 0.2F, 0);
// return the generation rate per second
return genRate;
}
public void Draw(SpriteBatch batch, Vector2 position, float alpha, float scale) {
var drawScale = scale;
if (this.animationSizeModifier > 0)
drawScale += (float) Math.Sin(this.animationSizeModifier) * 0.05F;
var tex = Assets.AttractionTexture[this.Type.TextureRegion];
var center = tex.Size.ToVector2() / 2;
batch.Draw(tex, position + center * scale, Color.White * alpha, 0, center, drawScale, SpriteEffects.None, 0);
}
public double GetGenerationRate(ParkMap map, Point position) {
var genRate = this.Type.GetGenerationRate();
// apply attraction modifiers
var mod = 1D;
foreach (var modifier in this.Modifiers)
mod += Math.Pow(modifier.Modifier.Multiplier, modifier.Amount) - 1;
genRate *= mod;
// apply star upgrades
foreach (var upgrade in Upgrade.Upgrades.Values.OfType<NeighborModifierUpgrade>())
genRate *= upgrade.GetCurrentMultiplier(this, map, position);
return genRate;
}
public void ApplyModifier(AttractionModifier modifier) {
// increase the amount of existing modifiers
foreach (var mod in this.Modifiers) {
if (mod.Modifier == modifier) {
mod.Amount++;
return;
}
}
// or add a new modifier
this.Modifiers.Add(new ActiveModifier(modifier, 1));
}
public int GetModifierAmount(AttractionModifier modifier) {
return this.Modifiers.Where(m => modifier == null || m.Modifier == modifier).Sum(m => m.Amount);
}
public BigInteger GetModifierPrice(AttractionModifier modifier) {
var amount = this.GetModifierAmount(modifier);
return (BigInteger) Math.Ceiling(modifier.InitialPrice * Math.Pow(1 + 0.45F, amount));
}
public void Wobble() {
this.animationSizeModifier = MathHelper.Pi;
}
public IEnumerable<Attraction> GetSurrounding(ParkMap map, Point position, AttractionType type) {
foreach (var tile in this.Type.GetCoveredTiles()) {
foreach (var dir in Direction2Helper.Adjacent) {
var other = map.GetAttractionAt(position + tile + dir.Offset());
if (other != null && other != this && other.Type == type)
yield return other;
}
}
}
[DataMember]
public readonly List<ActiveModifier> Modifiers = new();
[DataMember]
public readonly AttractionType Type;
[DataMember]
private double ticketPercentage;
private float animationSizeModifier;
public Attraction(AttractionType type) {
this.Type = type;
}
public double Update(TimeSpan passed, ParkMap map, Point position) {
var genRate = this.GetGenerationRate(map, position);
// apply generation rate to ticket amount
this.ticketPercentage += genRate * passed.TotalSeconds;
var total = (BigInteger) this.ticketPercentage;
if (total > 0) {
GameImpl.Instance.Tickets += total;
this.ticketPercentage -= (double) total;
}
// animation stuff
if (this.animationSizeModifier > 0)
this.animationSizeModifier = Math.Max(this.animationSizeModifier - 0.2F, 0);
// return the generation rate per second
return genRate;
}
public void Draw(SpriteBatch batch, Vector2 position, float alpha, float scale) {
var drawScale = scale;
if (this.animationSizeModifier > 0)
drawScale += (float) Math.Sin(this.animationSizeModifier) * 0.05F;
var tex = Assets.AttractionTexture[this.Type.TextureRegion];
var center = tex.Size.ToVector2() / 2;
batch.Draw(tex, position + center * scale, Color.White * alpha, 0, center, drawScale, SpriteEffects.None, 0);
}
public double GetGenerationRate(ParkMap map, Point position) {
var genRate = this.Type.GetGenerationRate();
// apply attraction modifiers
var mod = 1D;
foreach (var modifier in this.Modifiers)
mod += Math.Pow(modifier.Modifier.Multiplier, modifier.Amount) - 1;
genRate *= mod;
// apply star upgrades
foreach (var upgrade in Upgrade.Upgrades.Values.OfType<NeighborModifierUpgrade>())
genRate *= upgrade.GetCurrentMultiplier(this, map, position);
return genRate;
}
public void ApplyModifier(AttractionModifier modifier) {
// increase the amount of existing modifiers
foreach (var mod in this.Modifiers) {
if (mod.Modifier == modifier) {
mod.Amount++;
return;
}
}
// or add a new modifier
this.Modifiers.Add(new ActiveModifier(modifier, 1));
}
public int GetModifierAmount(AttractionModifier modifier) {
return this.Modifiers.Where(m => modifier == null || m.Modifier == modifier).Sum(m => m.Amount);
}
public BigInteger GetModifierPrice(AttractionModifier modifier) {
var amount = this.GetModifierAmount(modifier);
return (BigInteger) Math.Ceiling(modifier.InitialPrice * Math.Pow(1 + 0.45F, amount));
}
public void Wobble() {
this.animationSizeModifier = MathHelper.Pi;
}
public IEnumerable<Attraction> GetSurrounding(ParkMap map, Point position, AttractionType type) {
foreach (var tile in this.Type.GetCoveredTiles()) {
foreach (var dir in Direction2Helper.Adjacent) {
var other = map.GetAttractionAt(position + tile + dir.Offset());
if (other != null && other != this && other.Type == type)
yield return other;
}
}
}
}

View file

@ -1,18 +1,18 @@
using System;
namespace TouchyTickets.Attractions {
[Flags]
public enum AttractionFlags {
namespace TouchyTickets.Attractions;
// base flags
None = 0,
Relaxed = 1,
Cars = 2,
Walking = 4,
FastCars = 8,
NonTechnology = 16,
Small = 32,
All = ~0
[Flags]
public enum AttractionFlags {
// base flags
None = 0,
Relaxed = 1,
Cars = 2,
Walking = 4,
FastCars = 8,
NonTechnology = 16,
Small = 32,
All = ~0
}
}

View file

@ -2,85 +2,84 @@ using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using Microsoft.Xna.Framework;
using MLEM.Textures;
using Newtonsoft.Json;
using static TouchyTickets.Attractions.AttractionFlags;
namespace TouchyTickets.Attractions {
[JsonConverter(typeof(Converter))]
public class AttractionModifier {
namespace TouchyTickets.Attractions;
public static readonly Dictionary<string, AttractionModifier> Modifiers = new Dictionary<string, AttractionModifier>();
[JsonConverter(typeof(Converter))]
public class AttractionModifier {
static AttractionModifier() {
Register(new AttractionModifier("Lubricant", 200, Cars | FastCars, 1.02F, new Point(0, 4)));
Register(new AttractionModifier("LouderMusic", 500, Relaxed, 1.03F, new Point(2, 4)));
Register(new AttractionModifier("SmallAds", 800, Small, 1.35F, new Point(5, 4)));
Register(new AttractionModifier("LongerQueue", 1000, All, 1.06F, new Point(1, 4)));
Register(new AttractionModifier("Bouncer", 1500, Walking, 1.2F, new Point(3, 4)));
Register(new AttractionModifier("OnRideCameras", 2500, FastCars, 1.1F, new Point(4, 4)));
public static readonly Dictionary<string, AttractionModifier> Modifiers = new();
static AttractionModifier() {
AttractionModifier.Register(new AttractionModifier("Lubricant", 200, AttractionFlags.Cars | AttractionFlags.FastCars, 1.02F, new Point(0, 4)));
AttractionModifier.Register(new AttractionModifier("LouderMusic", 500, AttractionFlags.Relaxed, 1.03F, new Point(2, 4)));
AttractionModifier.Register(new AttractionModifier("SmallAds", 800, AttractionFlags.Small, 1.35F, new Point(5, 4)));
AttractionModifier.Register(new AttractionModifier("LongerQueue", 1000, AttractionFlags.All, 1.06F, new Point(1, 4)));
AttractionModifier.Register(new AttractionModifier("Bouncer", 1500, AttractionFlags.Walking, 1.2F, new Point(3, 4)));
AttractionModifier.Register(new AttractionModifier("OnRideCameras", 2500, AttractionFlags.FastCars, 1.1F, new Point(4, 4)));
}
public readonly string Name;
public readonly long InitialPrice;
public readonly Point Texture;
public readonly float Multiplier;
private readonly AttractionFlags affectedFlags;
public AttractionModifier(string name, long initialPrice, AttractionFlags affectedFlags, float multiplier, Point texture) {
this.Name = name;
this.InitialPrice = initialPrice;
this.affectedFlags = affectedFlags;
this.Texture = texture;
this.Multiplier = multiplier;
}
public bool IsAffected(Attraction attraction) {
return (attraction.Type.Flags & this.affectedFlags) != 0;
}
public bool Buy(Attraction attraction) {
var price = attraction.GetModifierPrice(this);
if (GameImpl.Instance.Tickets < price)
return false;
GameImpl.Instance.Tickets -= price;
GameImpl.Instance.Platform.AddResourceEvent(true, "Tickets", (float) price, "Modifier", this.Name);
attraction.ApplyModifier(this);
return true;
}
private static AttractionModifier Register(AttractionModifier type) {
AttractionModifier.Modifiers.Add(type.Name, type);
return type;
}
public class Converter : JsonConverter<AttractionModifier> {
public override void WriteJson(JsonWriter writer, AttractionModifier value, JsonSerializer serializer) {
if (value != null)
writer.WriteValue(value.Name);
}
public readonly string Name;
public readonly long InitialPrice;
public readonly Point Texture;
public readonly float Multiplier;
private readonly AttractionFlags affectedFlags;
public AttractionModifier(string name, long initialPrice, AttractionFlags affectedFlags, float multiplier, Point texture) {
this.Name = name;
this.InitialPrice = initialPrice;
this.affectedFlags = affectedFlags;
this.Texture = texture;
this.Multiplier = multiplier;
}
public bool IsAffected(Attraction attraction) {
return (attraction.Type.Flags & this.affectedFlags) != 0;
}
public bool Buy(Attraction attraction) {
var price = attraction.GetModifierPrice(this);
if (GameImpl.Instance.Tickets < price)
return false;
GameImpl.Instance.Tickets -= price;
GameImpl.Instance.Platform.AddResourceEvent(true, "Tickets", (float) price, "Modifier", this.Name);
attraction.ApplyModifier(this);
return true;
}
private static AttractionModifier Register(AttractionModifier type) {
Modifiers.Add(type.Name, type);
return type;
}
public class Converter : JsonConverter<AttractionModifier> {
public override void WriteJson(JsonWriter writer, AttractionModifier value, JsonSerializer serializer) {
if (value != null)
writer.WriteValue(value.Name);
}
public override AttractionModifier ReadJson(JsonReader reader, Type objectType, AttractionModifier existingValue, bool hasExistingValue, JsonSerializer serializer) {
return reader.Value != null ? Modifiers[reader.Value.ToString()] : null;
}
public override AttractionModifier ReadJson(JsonReader reader, Type objectType, AttractionModifier existingValue, bool hasExistingValue, JsonSerializer serializer) {
return reader.Value != null ? AttractionModifier.Modifiers[reader.Value.ToString()] : null;
}
}
[DataContract]
public class ActiveModifier {
}
[DataMember]
public readonly AttractionModifier Modifier;
[DataMember]
public int Amount;
[DataContract]
public class ActiveModifier {
public ActiveModifier(AttractionModifier modifier, int amount) {
this.Modifier = modifier;
this.Amount = amount;
}
[DataMember]
public readonly AttractionModifier Modifier;
[DataMember]
public int Amount;
public ActiveModifier(AttractionModifier modifier, int amount) {
this.Modifier = modifier;
this.Amount = amount;
}
}

View file

@ -2,98 +2,97 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using MLEM.Textures;
using Newtonsoft.Json;
using TouchyTickets.Upgrades;
using static TouchyTickets.Attractions.AttractionFlags;
namespace TouchyTickets.Attractions {
[JsonConverter(typeof(Converter))]
public class AttractionType {
namespace TouchyTickets.Attractions;
public static readonly Dictionary<string, AttractionType> Attractions = new Dictionary<string, AttractionType>();
public static readonly AttractionType Carousel = Register(new AttractionType("Carousel", RectArea(1, 1), new Rectangle(0, 0, 1, 1), 0.5F, 50, Relaxed | Cars | Small));
public static readonly AttractionType MirrorHouse = Register(new AttractionType("MirrorHouse", RectArea(1, 1), new Rectangle(3, 0, 1, 1), 1, 150, Relaxed | Walking | NonTechnology | Small));
public static readonly AttractionType FoodCourt = Register(new AttractionType("FoodCourt", RectArea(2, 1), new Rectangle(1, 0, 2, 1), 2F, 300, None));
public static readonly AttractionType SpiralSlide = Register(new AttractionType("SpiralSlide", RectArea(1, 2), new Rectangle(5, 0, 1, 2), 4, 1200, Relaxed | Walking));
public static readonly AttractionType HedgeMaze = Register(new AttractionType("HedgeMaze", RectArea(2, 2), new Rectangle(3, 3, 2, 2), 8, 2500, Relaxed | Walking | NonTechnology));
public static readonly AttractionType FerrisWheel = Register(new AttractionType("FerrisWheel", RectArea(2, 2), new Rectangle(0, 1, 2, 2), 12, 4000, Relaxed | Cars));
public static readonly AttractionType FreefallCoaster = Register(new AttractionType("FreefallCoaster", new[,] {{true, false, true}, {true, true, true}}, new Rectangle(6, 0, 3, 2), 24, 8000, FastCars));
public static readonly AttractionType HauntedHouse = Register(new AttractionType("HauntedHouse", RectArea(2, 2), new Rectangle(3, 5, 2, 2), 30, 12000, FastCars));
public static readonly AttractionType GoKarts = Register(new AttractionType("GoKarts", RectArea(2, 2), new Rectangle(5, 2, 2, 2), 50, 24000, Cars | Relaxed));
public static readonly AttractionType MiniGolf = Register(new AttractionType("MiniGolf", RectArea(2, 3), new Rectangle(9, 0, 2, 3), 75, 35000, Relaxed | Walking | NonTechnology));
public static readonly AttractionType WildMouse = Register(new AttractionType("WildMouse", RectArea(3, 2), new Rectangle(2, 1, 3, 2), 100, 60000, FastCars));
public static readonly AttractionType LogFlume = Register(new AttractionType("LogFlume", new[,] {{true, true, false}, {true, true, true}}, new Rectangle(0, 3, 3, 2), 160, 90000, FastCars));
public static readonly AttractionType HeartlineTwister = Register(new AttractionType("HeartlineTwister", RectArea(4, 2), new Rectangle(5, 4, 4, 2), 250, 150000, FastCars));
public static readonly AttractionType WoodCoaster = Register(new AttractionType("WoodCoaster", RectArea(3, 3), new Rectangle(0, 5, 3, 3), 300, 215000, FastCars));
public static readonly AttractionType SafariZone = Register(new AttractionType("SafariZone", RectArea(5, 3), new Rectangle(11, 0, 5, 3), 600, 750000, Relaxed | Walking | NonTechnology));
[JsonConverter(typeof(Converter))]
public class AttractionType {
public readonly string Name;
public readonly bool[,] Area;
public int Width => this.Area.GetLength(1);
public int Height => this.Area.GetLength(0);
public readonly Rectangle TextureRegion;
private readonly float generationPerSecond;
public readonly long InitialPrice;
public readonly AttractionFlags Flags;
public static readonly Dictionary<string, AttractionType> Attractions = new();
public static readonly AttractionType Carousel = AttractionType.Register(new AttractionType("Carousel", AttractionType.RectArea(1, 1), new Rectangle(0, 0, 1, 1), 0.5F, 50, AttractionFlags.Relaxed | AttractionFlags.Cars | AttractionFlags.Small));
public static readonly AttractionType MirrorHouse = AttractionType.Register(new AttractionType("MirrorHouse", AttractionType.RectArea(1, 1), new Rectangle(3, 0, 1, 1), 1, 150, AttractionFlags.Relaxed | AttractionFlags.Walking | AttractionFlags.NonTechnology | AttractionFlags.Small));
public static readonly AttractionType FoodCourt = AttractionType.Register(new AttractionType("FoodCourt", AttractionType.RectArea(2, 1), new Rectangle(1, 0, 2, 1), 2F, 300, AttractionFlags.None));
public static readonly AttractionType SpiralSlide = AttractionType.Register(new AttractionType("SpiralSlide", AttractionType.RectArea(1, 2), new Rectangle(5, 0, 1, 2), 4, 1200, AttractionFlags.Relaxed | AttractionFlags.Walking));
public static readonly AttractionType HedgeMaze = AttractionType.Register(new AttractionType("HedgeMaze", AttractionType.RectArea(2, 2), new Rectangle(3, 3, 2, 2), 8, 2500, AttractionFlags.Relaxed | AttractionFlags.Walking | AttractionFlags.NonTechnology));
public static readonly AttractionType FerrisWheel = AttractionType.Register(new AttractionType("FerrisWheel", AttractionType.RectArea(2, 2), new Rectangle(0, 1, 2, 2), 12, 4000, AttractionFlags.Relaxed | AttractionFlags.Cars));
public static readonly AttractionType FreefallCoaster = AttractionType.Register(new AttractionType("FreefallCoaster", new[,] {{true, false, true}, {true, true, true}}, new Rectangle(6, 0, 3, 2), 24, 8000, AttractionFlags.FastCars));
public static readonly AttractionType HauntedHouse = AttractionType.Register(new AttractionType("HauntedHouse", AttractionType.RectArea(2, 2), new Rectangle(3, 5, 2, 2), 30, 12000, AttractionFlags.FastCars));
public static readonly AttractionType GoKarts = AttractionType.Register(new AttractionType("GoKarts", AttractionType.RectArea(2, 2), new Rectangle(5, 2, 2, 2), 50, 24000, AttractionFlags.Cars | AttractionFlags.Relaxed));
public static readonly AttractionType MiniGolf = AttractionType.Register(new AttractionType("MiniGolf", AttractionType.RectArea(2, 3), new Rectangle(9, 0, 2, 3), 75, 35000, AttractionFlags.Relaxed | AttractionFlags.Walking | AttractionFlags.NonTechnology));
public static readonly AttractionType WildMouse = AttractionType.Register(new AttractionType("WildMouse", AttractionType.RectArea(3, 2), new Rectangle(2, 1, 3, 2), 100, 60000, AttractionFlags.FastCars));
public static readonly AttractionType LogFlume = AttractionType.Register(new AttractionType("LogFlume", new[,] {{true, true, false}, {true, true, true}}, new Rectangle(0, 3, 3, 2), 160, 90000, AttractionFlags.FastCars));
public static readonly AttractionType HeartlineTwister = AttractionType.Register(new AttractionType("HeartlineTwister", AttractionType.RectArea(4, 2), new Rectangle(5, 4, 4, 2), 250, 150000, AttractionFlags.FastCars));
public static readonly AttractionType WoodCoaster = AttractionType.Register(new AttractionType("WoodCoaster", AttractionType.RectArea(3, 3), new Rectangle(0, 5, 3, 3), 300, 215000, AttractionFlags.FastCars));
public static readonly AttractionType SafariZone = AttractionType.Register(new AttractionType("SafariZone", AttractionType.RectArea(5, 3), new Rectangle(11, 0, 5, 3), 600, 750000, AttractionFlags.Relaxed | AttractionFlags.Walking | AttractionFlags.NonTechnology));
public AttractionType(string name, bool[,] area, Rectangle textureRegion, float generationPerSecond, long initialPrice, AttractionFlags flags) {
this.Name = name;
this.Area = area;
this.TextureRegion = textureRegion;
this.generationPerSecond = generationPerSecond;
this.InitialPrice = initialPrice;
this.Flags = flags;
}
public readonly string Name;
public readonly bool[,] Area;
public int Width => this.Area.GetLength(1);
public int Height => this.Area.GetLength(0);
public readonly Rectangle TextureRegion;
private readonly float generationPerSecond;
public readonly long InitialPrice;
public readonly AttractionFlags Flags;
public Attraction Create() {
return new Attraction(this);
}
public AttractionType(string name, bool[,] area, Rectangle textureRegion, float generationPerSecond, long initialPrice, AttractionFlags flags) {
this.Name = name;
this.Area = area;
this.TextureRegion = textureRegion;
this.generationPerSecond = generationPerSecond;
this.InitialPrice = initialPrice;
this.Flags = flags;
}
public double GetGenerationRate() {
var genRate = this.generationPerSecond;
foreach (var upgrade in Upgrade.Upgrades.Values.OfType<ModifierUpgrade>())
genRate *= upgrade.GetCurrentMultiplier(this);
return genRate;
}
public Attraction Create() {
return new Attraction(this);
}
public IEnumerable<Point> GetCoveredTiles() {
for (var x = 0; x < this.Width; x++) {
for (var y = 0; y < this.Height; y++) {
if (this.Area[y, x])
yield return new Point(x, y);
}
public double GetGenerationRate() {
var genRate = this.generationPerSecond;
foreach (var upgrade in Upgrade.Upgrades.Values.OfType<ModifierUpgrade>())
genRate *= upgrade.GetCurrentMultiplier(this);
return genRate;
}
public IEnumerable<Point> GetCoveredTiles() {
for (var x = 0; x < this.Width; x++) {
for (var y = 0; y < this.Height; y++) {
if (this.Area[y, x])
yield return new Point(x, y);