Jump to content

Recommended Posts

The only thing I can think of is the components of a GameObject, like

		ElementConverter elementConverter = go.AddComponent<ElementConverter>();
		elementConverter.consumedElements = new ElementConverter.ConsumedElement[]
		{
			new ElementConverter.ConsumedElement(new Tag("Algae"), 0.0300000012f),
			new ElementConverter.ConsumedElement(new Tag("Water"), 0.3f)
		};
		elementConverter.outputElements = new ElementConverter.OutputElement[]
		{
			new ElementConverter.OutputElement(0.0400000028f, SimHashes.Oxygen, 303.15f, false, false, 0f, 1f, 1f, 255, 0)
		};

Interesting components to alter: Storage, ManualDeliveryKG, ElementConverter, ElementConsumer, PassiveElementConsumer, EnergyGenerator, ElementEmitter, ElementDropper.

I might have forgotten some, but even just partial control of those can allow a whole lot of modded buildings.

Maybe some interface to mod critters, if nothing else then input and output elements as well as converted mass and how often it will convert. Plants could also be interesting to add.

Link to comment
Share on other sites


  public static GameObject CreateDrecko(
    string id,
    string name,
    string desc,
    string anim_file,
    bool is_baby)
  {
    GameObject wildCreature = EntityTemplates.ExtendEntityToWildCreature(BaseDreckoConfig.BaseDrecko(id, name, desc, anim_file, "DreckoBaseTrait", is_baby, "fbr_", 308.15f, 363.15f), DreckoTuning.PEN_SIZE_PER_CREATURE, 150f);
    Trait trait = Db.Get().CreateTrait("DreckoBaseTrait", name, name, (string) null, false, (ChoreGroup[]) null, true, true);
    trait.Add(new AttributeModifier(Db.Get().Amounts.Calories.maxAttribute.Id, DreckoTuning.STANDARD_STOMACH_SIZE, name, false, false, true));
    trait.Add(new AttributeModifier(Db.Get().Amounts.Calories.deltaAttribute.Id, (float) (-(double) DreckoTuning.STANDARD_CALORIES_PER_CYCLE / 600.0), name, false, false, true));
    trait.Add(new AttributeModifier(Db.Get().Amounts.HitPoints.maxAttribute.Id, 25f, name, false, false, true));
    trait.Add(new AttributeModifier(Db.Get().Amounts.Age.maxAttribute.Id, 150f, name, false, false, true));
    Diet diet = new Diet(new Diet.Info[1]
    {
      new Diet.Info(new HashSet<Tag>()
      {
        "SpiceVine".ToTag(),
        SwampLilyConfig.ID.ToTag(),
        "BasicSingleHarvestPlant".ToTag()
      }, DreckoConfig.POOP_ELEMENT, DreckoConfig.CALORIES_PER_DAY_OF_PLANT_EATEN, DreckoConfig.KG_POOP_PER_DAY_OF_PLANT, (string) null, 0.0f, false)
    });
    CreatureCalorieMonitor.Def def1 = wildCreature.AddOrGetDef<CreatureCalorieMonitor.Def>();
    def1.diet = diet;
    def1.minPoopSizeInCalories = DreckoConfig.MIN_POOP_SIZE_IN_CALORIES;
    wildCreature.AddOrGetDef<SolidConsumerMonitor.Def>().diet = diet;
    ScaleGrowthMonitor.Def def2 = wildCreature.AddOrGetDef<ScaleGrowthMonitor.Def>();
    def2.defaultGrowthRate = (float) (1.0 / (double) DreckoConfig.SCALE_GROWTH_TIME_IN_CYCLES / 600.0);
    def2.dropMass = DreckoConfig.FIBER_PER_CYCLE * DreckoConfig.SCALE_GROWTH_TIME_IN_CYCLES;
    def2.itemDroppedOnShear = DreckoConfig.EMIT_ELEMENT;
    def2.levelCount = 6;
    def2.targetAtmosphere = SimHashes.Hydrogen;
    return wildCreature;
  }

I registered the building components you mentioned now. However i have not tested them.

DreckoConfig looks like this any idea where wildCreature and trait will be stored to access them?

Link to comment
Share on other sites

On 7/12/2019 at 4:57 AM, Rainbowdesign said:

DreckoConfig looks like this any idea where wildCreature and trait will be stored to access them?

Db.Get() provides you with a singleton instance of Db : EntityModifierSet : ModifierSet.

It makes use of ModifierSet.CreateTrait to create and manage TraitGroup, which in turn contains the traits. How to use this correctly will likely require a bit more digging.

 

I figured out how to list the creatures... well sort of. It's not a clean list, but they are in EntityConfigManager.LoadGeneratedEntities. It calls RegisterEntity for every single IEntityConfig.

// EntityConfigManager
public void RegisterEntity(IEntityConfig config)
{
	GameObject gameObject = config.CreatePrefab();
	KPrefabID component = gameObject.GetComponent<KPrefabID>();
	component.prefabInitFn += new KPrefabID.PrefabFn(config.OnPrefabInit);
	component.prefabSpawnFn += new KPrefabID.PrefabFn(config.OnSpawn);
	Assets.AddPrefab(component);
}

This means they all end up in a public static list called Assets.Prefabs.

Link to comment
Share on other sites

Thankyou i will look into it as soon i am back modding Lua.

I wonder if it would be possible to clone a building and then add different attributes with a script.

The main problem would be the new class i think.

 

till then i wonder why do i get a
NullReferenceException: Object reference not set to an instance of an object  where __instance seems to be null not sure whats the problem with harmony there:

 


    [HarmonyPatch(typeof(AutoMiner), "ValidDigCell"), ]
    public class __isValidDigCell
    {
        public static bool Prefix(AutoMiner __instance,int cell )
        {
            int width = __instance.width;// Traverse.Create(__instance).Field("width").GetValue<int>();
            int height = __instance.height;// Traverse.Create(__instance).Field("height").GetValue<int>();
            Debug.Log(" width " + width + " " + height);

            if (width == 60&& height == 8) return Grid.Solid[cell] && !Grid.Foundation[cell];
            else return Grid.Solid[cell] && !Grid.Foundation[cell] && Grid.Element[cell].hardness < (byte)150;
        }
    }

 

Link to comment
Share on other sites

1 hour ago, Rainbowdesign said:

NullReferenceException: Object reference not set to an instance of an object  where __instance seems to be null not sure whats the problem with harmony there:

The problem is ValidDigCell is static. This means it's called without an instance, which in turn will make __instance null.

1 hour ago, Rainbowdesign said:

I wonder if it would be possible to clone a building and then add different attributes with a script.

It should be. Buildings are added with:

BuildingConfigManager.Instance.RegisterBuilding(obj as IBuildingConfig);

This will then call the methods in IBuildingConfig to set up the building. You will have to allocate one instance of IBuildingConfig for each call (each instance is stored in a list), but it doesn't look like there is a rule against multiple instances of the same class.

This means there should be a lua command for creating an instance and call RegisterBuilding. Since it's your class, you can add code in each of the affected methods to go back to lua to figure out how to set up data in each method. Remember you can add data to your class instance, like a reference to lua or whatever you want. However since it stores the instance you shouldn't bloat what you store.

The call location for adding building would likely be a postfix to GeneratedBuildings.LoadGeneratedBuildings.

Link to comment
Share on other sites

22 hours ago, Nightinggale said:

(each instance is stored in a list)

I take that back. I did further research and it's actually a dictionary. It will use the IBuildingConfig as key and the resulting BuildingDef as value. This means each instance of IBuildingConfig needs to be unique. This means you need a variable in your config class, which has a unique value. The simplest approach I can think of would be an int. The values for it can then come from a counter. That should prevent non-unique instances.

All it's used for is this:

// BuildingConfigManager
public void ConfigurePost()
{
	foreach (KeyValuePair<IBuildingConfig, BuildingDef> current in this.configTable)
	{
		current.Key.ConfigurePost(current.Value);
	}
}

Another issue to look out for is the ID (first argument) in CreateBuildingDef. That one too needs to be unique. You can test for existing IDs with the static method TagManager.GetProperName(Tag tag). If it returns NULL, then the ID is unused. That should be useful for detecting errors, which then have to be displayed to the user.

At the same time that string is used as building identifier in savegames, meaning changing it will mess up savegames. I currently have no good solution to this other than using some naming convention. Personally I use the prefix "Nightinggale." since that's unlikely to ever show up in anything from either Klei or other mods. What the string is doesn't matter as long as it is unique.

Link to comment
Share on other sites

I started with it now, but i really have a problem i cannot find anything about lua and types.

 

As example if i want to get a component i cannot do .FindOrAddComponent<Storage>() Its not lua syntax (well of course it does not work)

 


    go = GetGameObject("Ladder")
    go.FindOrAddComponent<Storage>()
end
[16:40:51.130] [1] [INFO] [LuaError] chunk_1:(9,22-23): unexpected symbol near '<'

 

Also i have no idea how to return the type. I started with this as example and it did lead nowhere too:

 


        public static Type GetComponent(GameObject prefab_id, string typename)
        {
            //prefab_id.AddOrGet< >(prefab_id);
            Type t = Type.GetType(typename);
            return t;
        }

 

If i dont find a way i have to make a function for each component.

I started with AddBuilding now.

The biggest problem for tomorrow will be to replace this properly mostlikely with reflections and activator:

IBuildingConfig cloneb = new LadderConfig();

 


        static public bool AddBuilding(string buildingname, string clonename)
        {
            if (TagManager.GetProperName(clonename) != null)
            {
                ScriptingCore.DebugLogError(" The building cannot be cloned, the name " + clonename + " is already used.");


                return false;
            }
            IBuildingConfig clonebuildingconfig = null;
            BuildingDef clonebuildingdef = null;
            foreach (var i in configTable)
            {
                if (i.Value.PrefabID == buildingname)
                {
                    clonebuildingconfig = i.Key;
                    clonebuildingdef = i.Value;
                }
            }
            if (clonebuildingdef == null)
            {
                ScriptingCore.DebugLogError(" The configTable did not contain, the name " + clonename + ".");
                return false;
            }

            IBuildingConfig cloneb = new LadderConfig();
            BuildingConfigManager.Instance.RegisterBuilding(cloneb);
            configTable[cloneb] = clonebuildingdef;
            return true;
        }
Link to comment
Share on other sites

IBuildingConfig is an interface class. This means you can't make an instance of the class and it can't contain data. All it has is a list of methods, but no code in those methods. It's just a list of what can be called and nothing else.

What it is used for is to allow multiple other classes to tell that they can be called with those methods. Those classes doesn't have to have anything in common other than that they have the methods in question. This means you have to make a building class, which inherits IBuildingConfig and then you do what you have to do with your own class.

This is all C#. The question is how to connect this to lua.

One idea could be to make a singleton access class. Lua can call it with init (resets everything to remove any trace of the previous building) and insert (calls RegisterBuilding). This would be two lua commands with no arguments.

The issue is that once insert is called, your building class methods will be called and they have to deliver your building data. This means the C# methods needs to request data from lua, either directly or indirectly.

What I think would be the easiest approach is to add lua set commands and make lua call those set commands between init and insert. The access class will then store the data from the set commands. This means when RegisterBuilding calls your building class methods, those methods don't need lua data, they need data from your singleton access class in C#. In fact your building class methods can be reduced to a single call back to the access class to provide the arguments and then it returns the return values.

Link to comment
Share on other sites

I understand IBuildingConfig is an interface class.

What is irritating me is that they use it as dictionary key - as an interface you cannot make instances so what is the key at all if not the instance and why not use a string as key and have the other part access able inside the value.

 

My idea is: i search with c# the Building, make a copy of it and let the user manipulate the copy that way they really dont need to insert ALL the values if they want to do something similar.

 

I have however no idea how to find that class from lua just with a string and then i dont know how it is registered properly.

Especially since i cannot use types from lua.

Link to comment
Share on other sites

9 minutes ago, Rainbowdesign said:

What is irritating me is that they use it as dictionary key - as an interface you cannot make instances so what is the key at all if not the instance and why not use a string as key and have the other part access able inside the value.

Now that I think about it, what happens if there are non-unique keys? As far as I can tell, the content in the dictionary will overwrite the last building using the same IBuildingConfig class, but what will break if that happens? Not much. All I can see is it will skip the call to IBuildingConfig.ConfigurePost. If you design the code to not need calls to that method, then skipping it for some buildings will not actually do anything.

We don't know for sure what happens without trying it, but I don't think the dictionary is a huge problem.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

Please be aware that the content of this thread may be outdated and no longer applicable.

×
  • Create New...