Monday, March 22, 2021

Spiritfarer and game exhaustion

Spiritfarer is a wonderful game. It's charming - the artstyle feels hand drawn and light and lively. The characters are emotive and huggable. Your travelling companion is a cat. You grow gardens and weave and cook and herd sheep and plant trees and catch fish. You play music and chop logs and smash rocks and glide around through the air and play with movement physics. I love it, it's a magical first-time experience game.

And I've never finished it.

I purchased Spiritfarer back in August of 2020, and played it for 1 or 2 hours each evening after work for about a month. I took it slow, as I was never the person for these kind of games, and had just gotten done seeing my girlfriend take a break from Animal Crossing for literally the entire month of July after playing it every day for 3 months, so I knew that the grind of playing a game every day could be killer. I stuck to only a couple of hours each evening. In that time with a couple hours each night, I was able to meet and send off Gwen, Summer, and Alice. I took a break from Spiritfarer around October - one evening I simply didn't turn it on like I had every other night for the past month or so, and my planned break of that single evening turned into days, into weeks - into months. I started up once more in January of this year, helping Atul find his Fried Chicken and started planning his dinner party (though he's still waiting for Gustav's Tuna Tataki before we can sit down to eat). I played again for 2 or 3 days before once again putting the game down, and I sadly haven't picked it up since.

Let me state that I do not think Spiritfarer is a bad game, it's just simply not my game. I played Spiritfarer for what I estimate is around 40 hours, and enjoyed it. It was not at all the kind of game I usually play, and frankly I'm surprised I stuck around for so long. I think the reasons I left the game mainly boil down to two things - uncertainty and exhaustion.

Let's touch on what I mean by uncertainty first. The beginning hours of the game are masterfully crafted. You meet Gwen and she takes you through the game's basic tutorial where you discover how things work and what kinds of things you will be expected to remember during your sessions. You learn about movement, arranging the boat, cooking food, spirit food likes and dislikes, spirit skills, and the basic "crafting" system. You meet Summer and learn about gardening and music and where to go when you need to find resources that are usually scarce. You learn more about Gwen as her backstory opens up, and you discover the locations relevant to her past and her journey. You send her off through the Everdoor. Maybe you cry a little.

And then - what now. The tutorial is done, the Spirit whom I've grown the closest too on my boat is now gone, and I'm left with Summer, Atul, Alice, and Astrid. Atul is easier since he will eat anything, but gone are the days where it was acceptable to just rotate between popcorn and coffee - now I need to actually start making food. Oh but, I've only got access to Coffee Beans, Corn, and basic thread, so if I want to start making foods that Summer and Alice will actually enjoy, I need to go find more materials. My first soft wall in the game that I've picked up as a way to de-stress - everyone wants something but nobody will tell me where to find it. I open up the map and no dice - I've visited all of the locations on my map already.

But wait, what about the dark zones on the map? What if I just go off into one of those zones and hope that I'll find the magical city of cabbage and fried chicken? Beats going around looking for crates in the hopes that one of them will have not-coffee-beans.

Looking back, I now realize that this exploration is exactly what I was meant to and expected to do. The game wanted me to venture off into the far unknowns and organically discover new places and new things. I did, and I enjoyed it! But, up until that point for me the destination had been mostly laid out and setup for me, with the only obstacles on the way being making sure I had enough materials, or had shown mastery of the loom and the sawmill. I had never been required to find a new location, but rather had always been gently or forcefully guided towards that outcome. Suddenly with Gwen gone, the game had decided that I had made it to the big leagues after just one Everdoor trip, and now I had to play or go home.

Armed with my new mindset to go wherever until I was stopped by ice or rocks, I eventually succeeded in finding Bruce and Mickey, Giovanni, and Gustav. I also helped see Summer and Alice off through the Everdoor. It was at this point that the game took a major turn for me and I had to put it down for the first time. It was at this point that the game became more of a chore, and the chore became exhausting.

Let me briefly speak on one point where I believe the game - as a game - does fall a little flat. The opening hours are supreme - there is no other feeling quite like picking up Spiritfarer for the first time. But as the game continues on and characters come and go, one thing I began to notice was that as I moved further and further into the game, the characters became less and less engaging. I sympathized with Gwen and was patient enough to wait for Summer and Atul, but my initial reaction to meeting Astrid and Gustav and Bruce were not those of patience. I didn't resonate with these later characters as much, and as a result never actually cracked their hard shells enough to get to the meat of what their stories were. Instead of the game building up more and more of it's wealth of characters and ending with a bang, it was beginning to fizzle out before it had finished. When I left the game, I was in the middle of Astrid and Giovanni's infidelity, and had just gotten done building homes for Gustav and Bruce, and I just couldn't be bothered.

I liked Gwen, really liked her. She had personality, she helped me through the first parts of the game - held my hand so to speak. Normally I'm not for that kind of thing in a game, but in Spiritfarer? In a game which I had hoped would be relaxing and stress free? In a game with wonderful music that allows you to hug all the characters? Heck yes I would want someone to help me along with a light suggestion of, at least, what to do next.

I liked Summer too, though to a lesser degree than Gwen. She and I never quite clicked the same way things were with Gwen. Alice was even less so - though I empathized with her story and how tough it became for her in the end, we never quite hit it off. It seems that I was opening up less and less to the later characters. In a strange way, Spiritfarer had burned me in a way I never thought possible.

I was afraid to become engrossed in these new relationships with my other characters because, after Gwen left, there was a hole that was left unfilled. In both a figurative emotional sense, as one of the characters I had come to enjoy was gone forever, but also in the literal in-game sense - as her house was left on the boat but abandoned, unable to be removed or re-occupied. As the exact opposite of the principal goal of the game, I had not learned to cope with loss, and I had not learned to let go - rather - I had become more fearful of the idea than ever before. If I let go of Gwen, who was my favorite, and was miserable, what would happen when Summer and Alice and Atul left. I'd be stuck with Astrid, and Giovanni, ugh, or even Gustav. Or Stanley.

So progress would damn me, as I would be left with only characters I genuinely would not want to be on a boat with. But a lack of progress would also damn me just the same. Astrid would constantly complain for me to see what Giovanni was up to. Atul would keep asking for Fried Chicken and Pork Chops. My days in the game went from exploration and wonder into the very planned and carefully calculated game of optimally watering plants, picking crops, crushing seeds and rocks, smelting ores, travelling to the next city or event location, and cooking in preparation of whose favorite dish I would be making that evening. Spiritfarer became a stressful experience, just because there was always so much micro-managing to do. And to do it for characters that I wasn't really meshing with didn't help.

Let me put it out there, that I know Stanley had a troubled past for a mushroom-thing, but boy do I despise him as a character. He shows up, basically out of nowhere, and immediately starts demanding resources and consuming space. I was not prepared at all for Stanley. With the other characters, I could at least decide to speak with them once I knew I had space on the boat, or enough food to last a couple nights, but Stanley literally pops out of the ground and then suddenly demands food and takes over my guest house. Not cool Stanley.

This large amount of micro-managing took a once relaxing and genuinely enjoyable game into a chore combined with the fun of optimizing my route around the ship and the arrangement of my buildings for the shortest path with the least button presses. Yeah. That was how I survived what had otherwise become the monotony of watering crops and smashing seeds and cutting logs. Optimizing.

I saw off Gwen in August, Summer in September and Alice in October. I was so burned out that I put the game down in October and didn't pick it up again until January. In January I saw off Atul, and then returned to a ship with Giovanni, Gustav, Astrid, Bruce, and Stanley. I looked at my fields of crops that needed watering, and my sheep that needed trimming and my food that needed cooking. I looked at my characters demanding simple food, but also exotic food, but also nothing with veggies or fruit. I looked at the quest I had to go to a town which was locked behind an ice-walled zone because I didn't have a big enough ship. I needed metals I had never heard of from places I had never been.

I put the game down and haven't picked it up since. I probably will never pick it up again.

I did not dislike Spiritfarer, on the contrary, I loved it and would actually recommend it to people who are more easily swept up in these kinds of micromanage/simulator/farming style games. It's not for me, but it is for somebody. I'm just exhausted and burned out from playing it too much - and if I have to wait for the slow animation of that tiny bird turning that huge bull around before the dialog box pops up one more time - I'm gonna lose it.



Code stuff soon, stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

Tuesday, March 16, 2021

Libraries and You

It should come as no surprise to you at this point that JCenter will be shutting down. To prepare for the future where PYDroid libraries will no longer be hosted on JCenter, I am preparing to release the next and all future versions of pyamsoft Android libraries via JitPack

In order to prepare for this change, you will need to update the artifact ID for the next release. Instead of releasing under the usual com.pyamsoft.pydroid or similar artifact groups, it will be released under com.github.pyamsoft.pydroid. The same transition goes for Cachify and Highlander as well, which will be getting a new com.github prefixed artifact group. This will not change the package name.

With this change comes some important news about the next version of PYDroid, which will be a new major version - 23.

A major change is coming for how UiViewModel classes work with regards to creating a component. Before, you would have created the following kind of ViewModel





Notice how the ViewModel is strictly tied to one kind of ViewEvent, and knows how to handle all of those ViewEvent messages itself? This was largely nonsensical - as a ViewModel would not be connected to a ViewEvent publisher without wiring it up to the Component mindset via createComponent, which means that there would be a UiController present. This also led to issues that made it difficult to re-use a ViewModel with only a partial set of UiViews, as there would be ViewEvents which, in the context of the Component used, would never be called, but the ViewModel required handling it anyway, which was a code smell.

The new way in version 23 and onward will significantly change the ViewModel and createComponent calls in a few very important ways. First is that the UiViewModel no longer requires a strict ViewEvent type to be provided as part of the class signature, and as a result, will no longer support the handleViewEvent method.



The second significant change is that when creating a component via the createComponent function, a controller parameter is now explicitly required. The controller can be anything that inherits the UiController<C : UiControllerEvent> interface. You can either have your Android Controller class (Activity or Fragment for example) inherit this interface, or use the convenient inline method newUiController which creates an anonymous controller. The inline method is useful when a single Android Controller class handles different UiControllerEvent messages.

By changing component creation in this way, the UiController now correctly drives all interactions between its individual layers. A UiView publishes a ViewEvent to the Controller, which handles it in the trailing lambda of createComponent. In many cases, this ViewEvent will be call to a UiViewModel method, which can set UiState, or publish a ControllerEvent. The ControllerEvent is then handled by the controller passed into the createComponent method. This allows you to bind the same ViewModel to multiple different ViewEvent types, since the ViewEvent class type used in the trailing lambda will be decided by the kinds of UiViews that you pass to the function.




PYDroid 23 still has a ways to go before it is ready for release, but keep in mind that these changes will be coming soon. Because it is a major code base overhaul, this release will not be backwards compatible, so you may have to invest some time to migrate apps over to the new version. I was able to migrate all of my applications in a single day, though your time spent may change based on the size of your application. If you want to get a head start, you can pull a SNAPSHOT version off of JitPack.


========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

Wednesday, February 24, 2021

pydroid

A small update has been released for PYDroid bringing it to 22.6.1.

The only API change in this release is hopefully minor - it changes setState functions to allow for the stateChange lambda to be a suspending function. For Kotlin users there should be basically zero code change. For Java users, this will change the function signature because in Java suspending functions are implemented as Continuation resume callbacks. While this change may be slightly more painful for Java, as Android continues moving to a Kotlin first ecosystem modern codebases must be ready to follow. PYDroid is Kotlin first and Java friendly, but I cannot make Java compatibility guarantees as I largely do not use Java anymore.

If the change to suspending state change callbacks causes issues, please file a bug report and let me know.

Stay tuned! 

Saturday, February 20, 2021

Quick updates

A quick patch release was pushed for FridgeFriend that brings it to version 32. This fixes a bad buggy in the Group detail screen where the list of Items would normally be present. A bug was causing this list to be blank until at least one letter was inputted into the Search box - the recent update will fix this issue. Please update as soon as possible to get the latest fix!

An update to the Discord stock bot was released that brings very basic support for the nearest weekly options that are close to or in the money. Unfortunately, the options info is still incomplete, as I would like it to at least be able to list monthly or leap options, as well as provide delta info. I will continue working on this to make it more complete, but basic support is there for now.

An update to bubblewrap-wrap has been pushed which brings better isolation for user-specific runtime directories.

Stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

Monday, February 15, 2021

new stuff

Some quick updates

My nameless discord stock bot has gained basic support for displaying the upcoming weekly options chain for a given symbol.

ZapTorch has been updated with support for toggling on and off the various double click volume commands, so you can use some or all of the different volume commands.

FridgeFriend has been updated with simple bugfixes and a change to the search screen (although this one is sitting in Google review limbo)

Stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

Monday, January 25, 2021

New FridgeFriend

A new version of FridgeFriend has been released which includes some major changes!

First is the removal of the Map screen. This screen was originally envisioned to let you mark store locations on your device so that FridgeFriend could notify you when you were close by via geofencing. It was intended to be a hands off operation, so you wouldn't need the app open and could be, for example, driving around in the car and the app would notify you as you passed by a store. Unfortunately, the Google API and Policy changes with the new Android version made this feature much less useful - as you had to have the app open for it to remind you about nearby stores - and with the change in how society functions due to the pandemic, most people are not out and about anymore (and if they are, they usually have decided in advance to go to the store already). As such, the feature rarely saw any use and was not very useful when it did, so it has been dropped. A side effect of this is that FridgeFriend now requires zero permissions.

Instead, a global search screen has been added. Now when searching on this page, Items from any Group will be returned to you, allowing for a higher level birds-eye view over all of the things in your fridge. This way, even if you have items spread out among multiple Groups, you will be able to manage them all here as if they were in one large "super group".

The update has been published and should be rolling out to your device soon!

Stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

New pydroid

PYDroid version 22.6.0 has been released.

This officially removes the old deprecated methods, like saved instance support on plain UiViewModels.
This is largely the same release as 22.5.4 just with deprecated methods formally removed, but there is one change that can impact your application's behavior.

The setState function used to take an optional parameter andThen which would run only when the state was actually changed. This behavior has been modified, and the andThen callback will now run after every setState operation, regardless if state actually changes or not.

Stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

Monday, January 18, 2021

More state less bugs

PYDroid 22.5.4 follows quickly on the heels of the last release and brings some changes and some fixes that are pretty important, if I do say so myself.

The first is the removal of the SwipeRefreshLayout.refreshing(Boolean) extension function. This function was originally created to work around a bug in the SwipeRefreshLayout library by posting to the setRefreshing function, but later versions of that library fixed the bug so the extension function is no longer needed.

Another important change is to the setState mechanism on ViewModel and StateModel classes. The setState function used to open its own CoroutineScope which could not be cancelled except by cancelling the entire scope. This API still exists but may become deprecated in the near future in favor of the new APIs, which are extensions with the same signature but on the CoroutineScope object. As this is only a bugfix release, the old functions will stick around for the future, but may be removed in the next minor release.

Please upgrade to 22.5.4 when possible.

Stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

Thursday, January 14, 2021

pydroid and state saving

PYDroid has received an update to version 22.5.0! Major changes include new support for the androidx.savedstate module in UiViewModels! This support is optional.

UiViewModel will be deprecating the doOnRestoreState and doOnSaveState hooks. In the future, the UiViewModel class will no longer support instance state saving. For ViewModels wishing to use and write instance state, the new UiSavedStateViewModel class should be used (long names - I know - what is this - Java?). The UiSavedStateViewModel class takes in it's constructor an additional UiSavedState parameter, which is a simple abstraction over the SavedStateHandle class from androidx. You can access the UiSavedState via the restoreSavedState, putSavedState and removeSavedState functions.

Currently the UiSavedStateViewModel class is seperated from the UiViewModel class, but once the saved state migration is complete in a future version, the saved state view model will extend from the normal view model as a base class. Consumer API will not change, but ABI compatibility will not be maintained once this happens.

To facilitate these new ViewModels, a new factory has been created, called SavedStateViewModelFactory. To create simple factories, the new inline functions createViewModelFactory and createSavedStateViewModelFactory can be used to create simple single-value factories so that your custom ViewModels can play nice with Android's ViewModelStore system.

Along with these changes, the UiBundleReader and UiBundleWriter interfaces have been deprecated. For library consumers, this change is largely unimportant - but one thing that will affect consumers is that the UiBundleReader methods have been reduced to just the get(String): T? method. All other methods in the future will be removed. UiView consumer code will be largely unchanged, as long as all reader access is changed to only use get(String): T?.

The UiRender interface has removed support for the distinct function as it did not make any sense in the given context, and has renamed the distinctBy function to mapChanged to better convey how that function is used. The asUiRender extension function has been changed from being an extension on Any to being an extension on UiViewState only, so that random objects cannot be turned into renderers unintentionally.

PYDroid ui has been updated to use these new systems and now lazily initializes all of it's components, which should reduce the start up time cost. An optimization has been made to coroutine scope creation which should avoid a large time penalty during debug mode builds. In the future, support will begin for using UiViews as composable building blocks, but this will not be at least until compose-ui enters an API beta with stability guarantees.

Finally, some functions are exposed to a Java consumer that were intended to be internal to the library only. These functions have since been moved into an Internals class. You should not use these functions as a PYDroid library consumer - if you do, there is no support and functions can be radically changed or removed without warning. Don't use anything labelled internal, if you are able to access it, it is a mistake.

The bintray official release will be going up later today, stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================

Wednesday, January 13, 2021

Monster Hunter and being Prepared

Monster Hunter is a series about being prepared. While not a series for everyone, for those who do enjoy it, it can be one of the single most addicting things they play. The game rewards a player being prepared for any number of situations. Let's explore briefly how being prepared is rewarded in the latest entry of the series, Monster Hunter: Rise.

Note: These are impressions gathered from playing the Switch demo of the game in a multiplayer context. I am a beginner, as you will soon figure out.

Monster Hunter is for the patient gamer. The game almost downright requires that you study and practice before going off on a hunt. Before you hunt, you have to pick a weapon class. Before you pick a weapon class, you need to know how each performs to see what you like and don't like.

Before you can hunt, you need to go into weapon training. You need to pick a weapon and start trying it out - and not just pressing random buttons either. Monster Hunter is a slow game. If you start an attack, you need to already know in your head how you plan to end your attack, and you need to know when you are committed to finishing an attack animation for large damage, and when you need to stop the attack and dodge. To be prepared to train, you need to first read the weapon combos and understand what the "goal" of a weapon combo is.

Take for example, the Charge Blade. The "goal" of this weapon in it's normal usage is to unleash a fully charged Amped Element Discharge attack. In order to do that, you'll need to build charge. In order to do that, you'll need to land the standard sword combo first, which is X, X, X, then follow it up with ZR so that you do not enter the slower Axe mode before you are prepared with charged Vials. But you need to also not push ZR too early, or you will bring up your shield and enter Guard, which is not what you want to do. Once you have charge, you need to store your charge by switching to Sword mode and pressing ZR + A, which will store one set of Vials. Rinse and repeat this until you have two charged sets of Vials, and then you can enter Axe mode and release a fully charged Amped Element Discharge attack.

But when should you release your big attack that you've taken so much time to build up? Not when the monster is moving around obviously. You need to first knock the monster down and make it so that it is stunned for long enough for you to get the fully charged attack off. In order to do that, you need to understand it's weak points and strike them repeatedly.

Now that you are prepared, and have on where to hit the monster and when to use your big attacks, you need to hunt the monster. You can't kill what you can't find.

So you have to prepare to fight by finding clues around the map that can help lead you to the monster. Droppings, skeletons and remains, tracks - all of these are clues that can help build an understanding of what monster you are looking at, and how to find it.

In order to prepare to track the monster, you need to also prepare properly for the excursion. Eating the right food, gathering the right equipment, heck, even being the right class can help you better prepare for the hour long quest ahead.

All of these things stem back to the core gameplay focus - being prepared. The game rewards you for being patient and planning out all you can about your encounters. By being so rehearsed, you will be able to keep up when the game suddenly changes your quest in the middle by throwing in a second monster or a bigger monster, or replacing your quest target with a different monster entirely.

And of course, you need to be prepared to play with other people. Monster Hunter is a lot more fun with friends.

Code someday, stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================