Thursday, December 13, 2018

Stats Editor for Feed and Grow Fish

Preface


Today we're going to build a CSV based stats editor for Feed and Grow Fish. This editor will allow you to trivially edit the stats using an external CSV file.

Can I see it in action?

Here's s companion video that shows off all of this in action:


Let's build a mod

Let's load Feed and Grow Fish into dnSpy so that we can begin to add the CSV editor into the game.

We need to get our code inserted into the right place.

Jump into your Assembly-CSharp.dll and click on the "FishSelectFilterButton" class which is within Assets.Code.UI.Extensions:


Within the FishSelectFitlerButton class you need to use the search functionality (control + f) to search for "NPCS". You are looking for the "LoadAll" function as seen below:


You need to click on the "LoadAll" orange text which will jump you over to that function. Upon clicking on it you should see something like this:


We need to edit this code. Right click anywhere in the page and select "Edit Class (C#)" as seen below:


Remove everything from within the "LoadAll" function (in other words, just the line that starts with return Resources.ConvertObjects), and replace it with the following block of code:

// Load in the resources that were requested
T[] array = Resources.ConvertObjects<T>(Resources.LoadAll(path, typeof(T)));

// Did we request the NPCS?
if (path == "NPCS")
{
 // Does a stats file already exist?
 if (File.Exists("stats.csv"))
 {
  // Yes a stats file exists

  // Read in the stats CSV file and split it up into an array with one line as each entry
  string[] array2 = File.ReadAllText("stats.csv").Replace("\"", "").Split(new char[]
  {
   '\n'
  });

  // Loop over each NPC
  foreach (LivingEntity livingEntity in array as LivingEntity[])
  {
   // Search the CSV for the NPC we are currently dealing with
   string[] array3 = null;
   string[] array4 = array2;
   // Loop over each line in the CSV
   for (int i = 0; i < array4.Length; i++)
   {
    // Split the line based on a comma
    string[] array5 = array4[i].Split(new char[]
    {
     ','
    });
    // Was this the fish we are currently dealing with?
    if (array5[0] == livingEntity.name)
    {
     // Yep, grab it, and stop looping
     array3 = array5;
     break;
    }
   }
   // Did we find the fish in our CSV?
   if (array3 != null)
   {
    // Yes we found the fish

    // Apply all of the properties from the CSV
    // Some are floats, some are ints, we are just parsing the CSV
    livingEntity.AttackSpeed = float.Parse(array3[1]);
    livingEntity.AdditiveMaxEggs = int.Parse(array3[2]);
    livingEntity.Anger = float.Parse(array3[3]);
    livingEntity.CurrentSpeed = float.Parse(array3[4]);
    livingEntity.DamageStat = float.Parse(array3[5]);
    livingEntity.DefaultFocus = float.Parse(array3[6]);
    livingEntity.EggsInside = int.Parse(array3[7]);
    livingEntity.Health = float.Parse(array3[8]);
    livingEntity.HealthStat = float.Parse(array3[9]);
    livingEntity.MaxTemp = float.Parse(array3[10]);
    livingEntity.MinTemp = float.Parse(array3[11]);
    livingEntity.MinSize = float.Parse(array3[12]);

    // Handle the mouth, just need to apply it correctly
    string a = array3[13];
    if (!(a == "Beak"))
    {
     if (!(a == "Teeth"))
     {
      if (a == "Empty")
      {
       livingEntity.Mouth = ActiveThing.MouthType.Empty;
      }
     }
     else
     {
      livingEntity.Mouth = ActiveThing.MouthType.Teeth;
     }
    }
    else
    {
     livingEntity.Mouth = ActiveThing.MouthType.Beak;
    }
    // Handle some more attributes:
    livingEntity.MovemSpeed = float.Parse(array3[14]);
    livingEntity.RoamStrenght = float.Parse(array3[15]);
    if (livingEntity is Fish)
    {
     (livingEntity as Fish).RotationSpeed = float.Parse(array3[16]);
    }
    // Handle the skin type
    a = array3[17];
    if (!(a == "Leather"))
    {
     if (!(a == "Plate"))
     {
      if (a == "Scale")
      {
       livingEntity.Skin = ActiveThing.SkinType.Scale;
      }
     }
     else
     {
      livingEntity.Skin = ActiveThing.SkinType.Plate;
     }
    }
    else
    {
     livingEntity.Skin = ActiveThing.SkinType.Leather;
    }
   }
  }
 }
 else
 {
  // No, a stats file doesn't exist

  // Create the header for the CSV file
  string text = "Name,";
  text += "AttackSpeed,";
  text += "AdditiveMaxEggs,";
  text += "Anger,";
  text += "CurrentSpeed,";
  text += "DamageStat,";
  text += "DefaultFocus,";
  text += "EggsInside,";
  text += "Health,";
  text += "HealthStat,";
  text += "MaxTemp,";
  text += "MinTemp,";
  text += "MinSize,";
  text += "Mouth,";
  text += "MovemSpeed,";
  text += "RoamStrenght,";
  text += "RotationSpeed,";
  text += "Skin";
  text += Environment.NewLine;

  // Loop over every entity that we loaded from above
  foreach (LivingEntity livingEntity2 in array as LivingEntity[])
  {
   // Add the fields into the CSV
   text = text + livingEntity2.name + ",";
   text = text + livingEntity2.AttackSpeed + ",";
   text = text + livingEntity2.AdditiveMaxEggs + ",";
   text = text + livingEntity2.Anger + ",";
   text = text + livingEntity2.CurrentSpeed + ",";
   text = text + livingEntity2.DamageStat + ",";
   text = text + livingEntity2.DefaultFocus + ",";
   text = text + livingEntity2.EggsInside + ",";
   text = text + livingEntity2.Health + ",";
   text = text + livingEntity2.HealthStat + ",";
   text = text + livingEntity2.MaxTemp + ",";
   text = text + livingEntity2.MinTemp + ",";
   text = text + livingEntity2.MinSize + ",";
   text = text + livingEntity2.Mouth + ",";
   text = text + livingEntity2.MovemSpeed + ",";
   text = text + livingEntity2.RoamStrenght + ",";

   // Are we dealing with a fish?
   if (livingEntity2 is Fish)
   {
    // Yep, we are dealing with a fish, allow rotation speed to be edited
    text = text + (livingEntity2 as Fish).RotationSpeed + ",";
   }
   else
   {
    // Nope, we are not dealing with a fish, this isnt applicable
    text += "N/A,";
   }

   // Add the remaining fields and a new line
   text += livingEntity2.Skin;
   text += Environment.NewLine;
  }
  
  // Save the CSV
  File.WriteAllText("stats.csv", text);
 }
}
return array;


It should look something like this:


Let's scroll to the very top of the code, and replace the entire section that has "using xxx" with the following:

using System;
using System.IO;
using System.Runtime.CompilerServices;
using Assets.Code;
using Assets.Code.Things;
using UnityEngineInternal;


This will end up looking something like this:


The final thing we need to do as add some assembly references. Click the add assembly reference in the bottom left hand corner of the editor:


This will open a browse to window. You need to add the "Assembly-CSharp.dll" and "UnityEngine.Networking.dll", this can be done using the button twice:


Finally press the compile button in the lower right hand corner of the code editor.

We can now save our changes. Select "File" and then "Save All...", and then press "OK" to override the DLL and save our changes.

How do I use the mod?

With the mod installed, you need to launch the game, load a map, and access the menu where you select a fish.

You can now close the game and there will be a stats.csv file in the root directory of your Feed and Grow Fish game.

Edit the stats.csv file using Microsoft Excel or any other text editor, save it, and make sure to close the editor. If you don't close the editor then Feed and Grow Fish may error out and not let you select any fish.

All of the stat changed you made will be applied to the fish.


Once again, if you have any questions, let us know below, or on the video.

7 comments:

  1. wheres the code?

    ReplyDelete
  2. It's in line, like, the ugly green / grey lines are the code.

    This is our new tutorial, which is much easier to install: https://youtu.be/oyf1mxH66QY

    ReplyDelete
  3. What the hell is up with this dnSpy software. I opened it several times to look at .dll files and then one time, when it was opening a file I must have hit a key and now the entire expandable view is gone. I have gone through and toggled everything on the view tab and hit every key stroke that is listed in every menu. Closed the app, then reopened it, deleted the entire thing and put it back and even deleted the computers temp files and it's still stuck in this view with tree view on the left side. On the view menu there is Collapse TreeView Nodes but no expand TreeView nodes and toggling it does nothing. Never seen something so difficult to return to the default view.

    ReplyDelete
    Replies
    1. Edit, stuck on view with NO TreeView on the left side.

      Delete
    2. Can you share a screenshot of what you see? Upload to imgur.com if you have no where to upload the screenshot.

      Delete
  4. I edited my CSV file to power up a Bibos. (Not crazy overpowered). When I start the game the stats all appear to have been applied but the "eat" function doesn't work. I can kill anything I want I just can't eat it and gain XP. Any idea as to what's going on?

    ReplyDelete
    Replies
    1. This is no longer suppported.

      We will look at porting this mod to our new mod launcher soon -- https://AzzaMods.com

      Delete

Note: Only a member of this blog may post a comment.