Mod Unity AI Girl (AI 少女) - Modding Directory/Discussion Thread

EroShane

Active Member
Sep 7, 2019
815
965
Figured this would best serve the community plopped here if anyone is interested in collaborating some ideas with me...


For all those looking for a mod to add in more girls... Unfortunately the girls are not added in a static "item" sense... They are referred to as AgentActor [Heroine] in the programming (chara= Agent/Player/Merchant)... I found where they are added... The problem is they are tied to the repair and activation of their capsules [heroine_point]. Therefore, the only way we can add new girls would be:

A: place new capsules via decompressing the current game assets and assemblies, opening the module in Unity, duplicating the animated capsules with attached scripts, placing them in new locations on the map in Unity, then exporting the file package to be applied in the game's mods folder... (I wonder if the room at the back of the lab has the same animated capsules, deleting the box collider and model for that one collapsed capsule shouldn't be too hard to gain access to that back area -- provided it's not just a visual overlay and they are 3d objects in there -- if not we'd need to replace the capsules with the animated one outside, assign a new ID number, and duplicate them throughout that back room... should work in conventional theory)

B: Track down the script that is deleting the functionality of the add girl function in the existing capsules, which would more-or-less be gamebreaking in that you would be able to load infinite girls from the first capsule you find and repair. This would be easier in theory but would be broken in completed games and could possibly cause save corruption, and also would need to be used experimentally and not in excess since I would imagine if you blindly added a dozen girls then went to play there is a possibility the game would freeze completely. Since they are all going to be battling one another for your attention immediately after. We would probably have to add one, run around and test FPS drops, then save, then add another, lather rinse repeat.

C: Lastly, there may be a way to add a module with a UI overlay to simply spawn in an additional girl at your location using a simple location get script and then making it call the spawn of the new girl routine just kind of plopping her on top of you. That would also operate the same as the B: option in general...

I would feel best with A since that would require you to get to the end of the island, but it would also be more work with higher potential of causing deeper issues and breaking more stuff. However, the other two would likely make it easier to break the game in various ways... I dunno, just spitballing...

This is the LoadAgents script that utilizes the capsules:
C:
private IEnumerator LoadAgents(WorldData profile, bool existsBackup)
    {
        DefinePack define = Singleton<Manager.Resources>.Instance.DefinePack;
        AgentProfile agentProfile = Singleton<Manager.Resources>.Instance.AgentProfile;
        string bundlePath = define.ABPaths.ActorPrefab;
        string manifest = define.ABManifests.Default;
        GameObject agentPrefab = CommonLib.LoadAsset<GameObject>(bundlePath, "Agent", clone: false, manifest);
        if (!MapScene.AssetBundlePaths.Exists((UnityEx.ValueTuple<string, string> x) => x.Item1 == bundlePath && x.Item2 == manifest))
        {
            MapScene.AssetBundlePaths.Add(new UnityEx.ValueTuple<string, string>(bundlePath, manifest));
        }
        int[] chunkKeys = ChunkTable.Keys.ToArray();
        int maxCharaNum = Config.GraphicData.MaxCharaNum;
        bool[] charasEnter = Config.GraphicData.CharasEntry;
        for (int i = 0; i < profile.AgentTable.Count; i++)
        {
            if (!charasEnter[i] && (!TutorialMode || i != 0))
            {
                continue;
            }
            AgentData data = profile.AgentTable[i];
            if (!data.OpenState || data.CharaFileName.IsNullOrEmpty())
            {
                continue;
            }
            GameObject agentObj = UnityEngine.Object.Instantiate(agentPrefab, ActorRoot);
            agentObj.name = string.Format("Heroine_{0}", i.ToString("00"));
            AgentActor agent = agentObj.GetComponent<AgentActor>();
            agent.ID = i;
            agent.AgentData = data;
            data.param.Bind(agent);
            RegisterActor(i, agent);
            RegisterAgent(i, agent);
            if (existsBackup)
            {
                if (!TutorialMode)
                {
                    if (agentProfile.BlackListInSaveAndLoad.Contains(data.ModeType))
                    {
                        data.ModeType = Desire.ActionType.Normal;
                    }
                    if (data.ModeType == Desire.ActionType.Date || data.ModeType == Desire.ActionType.Onbu)
                    {
                        data.ModeType = Desire.ActionType.Normal;
                    }
                }
                agent.Mode = data.ModeType;
                agent.PrevMode = data.PrevMode;
                if (i == 0 && TutorialMode)
                {
                    AgentActor agentActor = agent;
                    Desire.ActionType actionType2 = data.ModeType = Desire.ActionType.Idle;
                    actionType2 = actionType2;
                    agent.PrevActionMode = actionType2;
                    actionType2 = actionType2;
                    agent.PrevMode = actionType2;
                    agentActor.Mode = actionType2;
                    agent.PrevActionMode = Desire.ActionType.Idle;
                    agent.TutorialType = data.TutorialModeType;
                    agent.TutorialMode = true;
                    TutorialAgent = agent;
                    agent.CreateTutorialBehaviorResources();
                }
                Vector3 pos = data.Position;
                (from _ in Observable.EveryUpdate()
                    where agent.NavMeshAgent.enabled
                    select _).TakeWhile((long _) => !agent.NavMeshAgent.isOnNavMesh).Subscribe(delegate
                {
                    agent.NavMeshAgent.Warp(pos);
                });
                agent.Rotation = data.Rotation;
                ChunkTable.TryGetValue(data.ChunkID, out Chunk value);
                MapArea[] mapAreas = value.MapAreas;
                foreach (MapArea mapArea in mapAreas)
                {
                    if (mapArea.AreaID == data.AreaID)
                    {
                        agent.MapArea = mapArea;
                    }
                }
            }
            else
            {
                StoryPoint storyPoint = GetStoryPoint(0);
                if (TutorialMode && i == 0 && storyPoint != null)
                {
                    AgentActor agentActor2 = agent;
                    Desire.ActionType actionType2 = data.ModeType = Desire.ActionType.Idle;
                    actionType2 = actionType2;
                    agent.PrevActionMode = actionType2;
                    actionType2 = actionType2;
                    agent.PrevMode = actionType2;
                    agentActor2.Mode = actionType2;
                    AIProject.Definitions.Tutorial.ActionType actionType6 = agent.TutorialType = (data.TutorialModeType = AIProject.Definitions.Tutorial.ActionType.Idle);
                    agent.MapArea = storyPoint.OwnerArea;
                    Transform transform = storyPoint.transform.FindLoop("heroine_point").transform;
                    AgentActor agentActor3 = agent;
                    Vector3? vector = transform?.position;
                    agentActor3.Position = ((!vector.HasValue) ? (storyPoint.Position + Player.Rotation * storyPoint.Forward * 3f) : vector.Value);
                    AgentActor agentActor4 = agent;
                    Quaternion? quaternion = transform?.rotation;
                    agentActor4.Rotation = ((!quaternion.HasValue) ? storyPoint.Rotation : quaternion.Value);
                    agent.TutorialMode = true;
                    TutorialAgent = agent;
                    agent.CreateTutorialBehaviorResources();
                }
                else
                {
                    int key = 0;
                    ChunkTable.TryGetValue(key, out Chunk value2);
                    List<Waypoint> waypoints = value2.MapAreas[0].Waypoints;
                    List<Waypoint> list = waypoints.FindAll(delegate(Waypoint x)
                    {
                        Vector3 position = Player.Position;
                        float num = Vector3.Distance(x.transform.position, position);
                        return num > 50f;
                    });
                    if (Debug.isDebugBuild)
                    {
                        Debug.Log($"近距離で除外されたウェイポイントの数 = {waypoints.Count - list.Count}");
                    }
                    Waypoint element = list.GetElement(UnityEngine.Random.Range(0, list.Count));
                    agent.Position = element.transform.position;
                    agent.MapArea = value2.MapAreas[0];
                }
            }
            agent.Relocate();
            agentObj.SetActiveSafe(isActive: true);
            yield return agent.LoadAsync();
            if (!existsBackup)
            {
                agent.SetDefaultImmoral();
                foreach (KeyValuePair<int, float> item in agent.ChaControl.fileGameInfo.desireDefVal)
                {
                    agent.AgentData.DesireTable[item.Key] = item.Value;
                }
            }
            yield return null;
        }
        yield return null;

public void LoadAgentTargetActionPoint()
    {
        foreach (KeyValuePair<int, AgentActor> agent in _agentTable)
        {
            ActionPoint actionPoint = PointAgent.ActionPoints.FirstOrDefault((ActionPoint x) => x.RegisterID == agent.Value.AgentData.CurrentActionPointID);
            if (actionPoint == null)
            {
                actionPoint = PointAgent.AppendActionPoints.Find((ActionPoint x) => x.RegisterID == agent.Value.AgentData.CurrentActionPointID);
            }
            if (actionPoint != null && !actionPoint.AgentEventType.Contains(AIProject.EventType.Move) && !actionPoint.AgentEventType.Contains(AIProject.EventType.DoorOpen))
            {
                agent.Value.TargetInSightActionPoint = actionPoint;
            }
        }
    }

    public void LoadAgentTargetActor()
    {
        foreach (KeyValuePair<int, AgentActor> item in _agentTable)
        {
            if (_actorTable.TryGetValue(item.Value.AgentData.ActionTargetID, out Actor value))
            {
                item.Value.TargetInSightActor = value;
            }
        }
    }
    }
This seems to be the script that is activated to actually spawn the girl into the world:

Code:
public AgentActor AddAgent(int id, AgentData agentData)
    {
        if (_agentPrefab == null)
        {
            DefinePack definePack = Singleton<Manager.Resources>.Instance.DefinePack;
            string bundlePath = definePack.ABPaths.ActorPrefab;
            string manifest = definePack.ABManifests.Default;
            _agentPrefab = CommonLib.LoadAsset<GameObject>(bundlePath, "Agent", clone: false, manifest);
            if (!MapScene.AssetBundlePaths.Exists((UnityEx.ValueTuple<string, string> x) => x.Item1 == bundlePath && x.Item2 == manifest))
            {
                MapScene.AssetBundlePaths.Add(new UnityEx.ValueTuple<string, string>(bundlePath, manifest));
            }
        }
        int[] array = ChunkTable.Keys.ToArray();
        GameObject gameObject = UnityEngine.Object.Instantiate(_agentPrefab, ActorRoot);
        gameObject.name = string.Format("Heroine_{0}", id.ToString("00"));
        AgentActor component = gameObject.GetComponent<AgentActor>();
        component.ID = id;
        component.AgentData = agentData;
        if (Singleton<Manager.Resources>.Instance.AgentProfile.DefaultAreaIDTable.TryGetValue(id, out int value))
        {
            int num3 = component.AreaID = (agentData.AreaID = value);
            foreach (KeyValuePair<int, Chunk> item in ChunkTable)
            {
                MapArea[] mapAreas = item.Value.MapAreas;
                foreach (MapArea mapArea in mapAreas)
                {
                    if (mapArea.AreaID == value)
                    {
                        component.MapArea = mapArea;
                    }
                }
            }
        }
        agentData.param.Bind(component);
        RegisterActor(id, component);
        RegisterAgent(id, component);
        if (Singleton<AnimalManager>.IsInstance())
        {
            Singleton<AnimalManager>.Instance.AddTargetAnimals(component);
        }
        component.Relocate();
        gameObject.SetActiveSafe(isActive: true);
        component.Load();
        return component;
    }
Then these are used to unload the existing girls from the world:

Code:
public void RemoveAgent(AgentActor agent)
    {
        agent.DisableBehavior();
        agent.BehaviorResources.DisableAllBehaviors();
        foreach (KeyValuePair<int, Dictionary<int, UnityEx.ValueTuple<AudioSource, FadePlayer>>> item in agent.Animation.ActionLoopSETable)
        {
            foreach (KeyValuePair<int, UnityEx.ValueTuple<AudioSource, FadePlayer>> item2 in item.Value)
            {
                UnityEx.ValueTuple<AudioSource, FadePlayer> value = item2.Value;
                if (!(value.Item2 == null))
                {
                    UnityEx.ValueTuple<AudioSource, FadePlayer> value2 = item2.Value;
                    value2.Item2.Stop(0f);
                }
            }
        }
        agent.Animation.ActionLoopSETable.Clear();
        Player.PlayerController.CommandArea.RemoveCommandableObject(agent);
        UnregisterAgent(agent.ID);
        UnregisterActor(agent.ID);
        foreach (KeyValuePair<int, AgentActor> item3 in AgentTable)
        {
            if (!(item3.Value == agent))
            {
                item3.Value.RemoveActor(agent);
            }
        }
        Singleton<Character>.Instance.DeleteChara(agent.ChaControl);
        UnityEngine.Object.Destroy(agent.gameObject);
    }
These are found at:
Assembly-CSharp.dll>Manager>Map>Line 2580

Attached a full version of the map.xml file if anyone wants to look at the whole thing and throw about some ideas...
 
  • Like
Reactions: mepew

EroShane

Active Member
Sep 7, 2019
815
965
Hah! Found it! Wonder if simply tweaking this will make it work...

IN: Assembly-CSharp>ConfigScene>GraphicSystem>Line 9


C:
// ConfigScene.GraphicSystem
using ConfigScene;
using System;
using System.Xml;
using UnityEngine;

public class GraphicSystem : BaseSystem
{
    public const int MAX_CHARA_NUM = 4;

    public const byte CHARA_GRAPHIC_QUALITY = 0;

    public const byte MAP_GRAPHIC_QUALITY = 0;

    public bool SelfShadow = true;

    public bool Bloom = true;

    public bool SSAO = true;

    public bool SSR = true;

    public bool DepthOfField = true;

    public bool Atmospheric = true;

    public bool Vignette = true;

    public bool Rain = true;

    public byte CharaGraphicQuality;

    public byte MapGraphicQuality;

    public bool FaceLight;

    public bool AmbientLight = true;

    public bool Shield = true;

    public bool SimpleBody;

    public Color SilhouetteColor = Color.blue;

    public Color BackColor = Color.black;

    public int MaxCharaNum = 4;

    public bool[] CharasEntry = new bool[4];

    public GraphicSystem(string elementName)
        : base(elementName)
    {
    }

    public override void Init()
    {
        SelfShadow = true;
        Bloom = true;
        SSAO = true;
        SSR = true;
        DepthOfField = true;
        Atmospheric = true;
        Vignette = true;
        Rain = true;
        CharaGraphicQuality = 0;
        MapGraphicQuality = 0;
        FaceLight = false;
        AmbientLight = true;
        Shield = true;
        SimpleBody = false;
        SilhouetteColor = Color.blue;
        BackColor = Color.black;
        MaxCharaNum = 4;
        for (int i = 0; i < 4; i++)
        {
            CharasEntry[i] = true;
        }
    }

    public override void Read(string rootName, XmlDocument xml)
    {
        try
        {
            XmlNodeList xmlNodeList = null;
            string text = rootName + "/" + elementName + "/";
            xmlNodeList = xml.SelectNodes(text + "SelfShadow");
            if (xmlNodeList != null)
            {
                XmlElement xmlElement = xmlNodeList.Item(0) as XmlElement;
                if (xmlElement != null)
                {
                    SelfShadow = (bool)BaseSystem.Cast(xmlElement.InnerText, SelfShadow.GetType());
                }
            }
            xmlNodeList = xml.SelectNodes(text + "Bloom");
            if (xmlNodeList != null)
            {
                XmlElement xmlElement2 = xmlNodeList.Item(0) as XmlElement;
                if (xmlElement2 != null)
                {
                    Bloom = (bool)BaseSystem.Cast(xmlElement2.InnerText, Bloom.GetType());
                }
            }
            xmlNodeList = xml.SelectNodes(text + "SSAO");
            if (xmlNodeList != null)
            {
                XmlElement xmlElement3 = xmlNodeList.Item(0) as XmlElement;
                if (xmlElement3 != null)
                {
                    SSAO = (bool)BaseSystem.Cast(xmlElement3.InnerText, SSAO.GetType());
                }
            }
            xmlNodeList = xml.SelectNodes(text + "SSR");
            if (xmlNodeList != null)
            {
                XmlElement xmlElement4 = xmlNodeList.Item(0) as XmlElement;
                if (xmlElement4 != null)
                {
                    SSR = (bool)BaseSystem.Cast(xmlElement4.InnerText, SSR.GetType());
                }
            }
            xmlNodeList = xml.SelectNodes(text + "DepthOfField");
            if (xmlNodeList != null)
            {
                XmlElement xmlElement5 = xmlNodeList.Item(0) as XmlElement;
                if (xmlElement5 != null)
                {
                    DepthOfField = (bool)BaseSystem.Cast(xmlElement5.InnerText, DepthOfField.GetType());
                }
            }
            xmlNodeList = xml.SelectNodes(text + "Atmospheric");
            if (xmlNodeList != null)
            {
                XmlElement xmlElement6 = xmlNodeList.Item(0) as XmlElement;
                if (xmlElement6 != null)
                {
                    Atmospheric = (bool)BaseSystem.Cast(xmlElement6.InnerText, Atmospheric.GetType());
                }
            }
            xmlNodeList = xml.SelectNodes(text + "Vignette");
            if (xmlNodeList != null)
            {
                XmlElement xmlElement7 = xmlNodeList.Item(0) as XmlElement;
                if (xmlElement7 != null)
                {
                    Vignette = (bool)BaseSystem.Cast(xmlElement7.InnerText, Vignette.GetType());
                }
            }
            xmlNodeList = xml.SelectNodes(text + "Rain");
            if (xmlNodeList != null)
            {
                XmlElement xmlElement8 = xmlNodeList.Item(0) as XmlElement;
                if (xmlElement8 != null)
                {
                    Rain = (bool)BaseSystem.Cast(xmlElement8.InnerText, Rain.GetType());
                }
            }
            xmlNodeList = xml.SelectNodes(text + "CharaGraphicQuality");
            if (xmlNodeList != null)
            {
                XmlElement xmlElement9 = xmlNodeList.Item(0) as XmlElement;
                if (xmlElement9 != null)
                {
                    CharaGraphicQuality = (byte)BaseSystem.Cast(xmlElement9.InnerText, CharaGraphicQuality.GetType());
                }
            }
            xmlNodeList = xml.SelectNodes(text + "MapGraphicQuality");
            if (xmlNodeList != null)
            {
                XmlElement xmlElement10 = xmlNodeList.Item(0) as XmlElement;
                if (xmlElement10 != null)
                {
                    MapGraphicQuality = (byte)BaseSystem.Cast(xmlElement10.InnerText, MapGraphicQuality.GetType());
                }
            }
            xmlNodeList = xml.SelectNodes(text + "FaceLight");
            if (xmlNodeList != null)
            {
                XmlElement xmlElement11 = xmlNodeList.Item(0) as XmlElement;
                if (xmlElement11 != null)
                {
                    FaceLight = (bool)BaseSystem.Cast(xmlElement11.InnerText, FaceLight.GetType());
                }
            }
            xmlNodeList = xml.SelectNodes(text + "AmbientLight");
            if (xmlNodeList != null)
            {
                XmlElement xmlElement12 = xmlNodeList.Item(0) as XmlElement;
                if (xmlElement12 != null)
                {
                    AmbientLight = (bool)BaseSystem.Cast(xmlElement12.InnerText, AmbientLight.GetType());
                }
            }
            xmlNodeList = xml.SelectNodes(text + "Shield");
            if (xmlNodeList != null)
            {
                XmlElement xmlElement13 = xmlNodeList.Item(0) as XmlElement;
                if (xmlElement13 != null)
                {
                    Shield = (bool)BaseSystem.Cast(xmlElement13.InnerText, Shield.GetType());
                }
            }
            xmlNodeList = xml.SelectNodes(text + "SimpleBody");
            if (xmlNodeList != null)
            {
                XmlElement xmlElement14 = xmlNodeList.Item(0) as XmlElement;
                if (xmlElement14 != null)
                {
                    SimpleBody = (bool)BaseSystem.Cast(xmlElement14.InnerText, SimpleBody.GetType());
                }
            }
            xmlNodeList = xml.SelectNodes(text + "SilhouetteColor");
            if (xmlNodeList != null)
            {
                XmlElement xmlElement15 = xmlNodeList.Item(0) as XmlElement;
                if (xmlElement15 != null)
                {
                    SilhouetteColor = (Color)BaseSystem.Cast(xmlElement15.InnerText, SilhouetteColor.GetType());
                }
            }
            xmlNodeList = xml.SelectNodes(text + "BackColor");
            if (xmlNodeList != null)
            {
                XmlElement xmlElement16 = xmlNodeList.Item(0) as XmlElement;
                if (xmlElement16 != null)
                {
                    BackColor = (Color)BaseSystem.Cast(xmlElement16.InnerText, BackColor.GetType());
                }
            }
            xmlNodeList = xml.SelectNodes(text + "MaxCharaNum");
            if (xmlNodeList != null)
            {
                XmlElement xmlElement17 = xmlNodeList.Item(0) as XmlElement;
                if (xmlElement17 != null)
                {
                    MaxCharaNum = (int)BaseSystem.Cast(xmlElement17.InnerText, MaxCharaNum.GetType());
                }
            }
            for (int i = 0; i < 4; i++)
            {
                xmlNodeList = xml.SelectNodes(text + "CharasEntry" + i);
                if (xmlNodeList != null)
                {
                    XmlElement xmlElement18 = xmlNodeList.Item(0) as XmlElement;
                    if (xmlElement18 != null)
                    {
                        CharasEntry[i] = (bool)BaseSystem.Cast(xmlElement18.InnerText, CharasEntry[i].GetType());
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Debug.LogError(ex.Message);
        }
    }

    public override void Write(XmlWriter writer)
    {
        writer.WriteStartElement(elementName);
        writer.WriteStartElement("SelfShadow");
        writer.WriteValue(BaseSystem.ConvertString(SelfShadow));
        writer.WriteEndElement();
        writer.WriteStartElement("Bloom");
        writer.WriteValue(BaseSystem.ConvertString(Bloom));
        writer.WriteEndElement();
        writer.WriteStartElement("SSAO");
        writer.WriteValue(BaseSystem.ConvertString(SSAO));
        writer.WriteEndElement();
        writer.WriteStartElement("SSR");
        writer.WriteValue(BaseSystem.ConvertString(SSR));
        writer.WriteEndElement();
        writer.WriteStartElement("DepthOfField");
        writer.WriteValue(BaseSystem.ConvertString(DepthOfField));
        writer.WriteEndElement();
        writer.WriteStartElement("Atmospheric");
        writer.WriteValue(BaseSystem.ConvertString(Atmospheric));
        writer.WriteEndElement();
        writer.WriteStartElement("Vignette");
        writer.WriteValue(BaseSystem.ConvertString(Vignette));
        writer.WriteEndElement();
        writer.WriteStartElement("Rain");
        writer.WriteValue(BaseSystem.ConvertString(Rain));
        writer.WriteEndElement();
        writer.WriteStartElement("CharaGraphicQuality");
        writer.WriteValue(BaseSystem.ConvertString(CharaGraphicQuality));
        writer.WriteEndElement();
        writer.WriteStartElement("MapGraphicQuality");
        writer.WriteValue(BaseSystem.ConvertString(MapGraphicQuality));
        writer.WriteEndElement();
        writer.WriteStartElement("FaceLight");
        writer.WriteValue(BaseSystem.ConvertString(FaceLight));
        writer.WriteEndElement();
        writer.WriteStartElement("AmbientLight");
        writer.WriteValue(BaseSystem.ConvertString(AmbientLight));
        writer.WriteEndElement();
        writer.WriteStartElement("Shield");
        writer.WriteValue(BaseSystem.ConvertString(Shield));
        writer.WriteEndElement();
        writer.WriteStartElement("SimpleBody");
        writer.WriteValue(BaseSystem.ConvertString(SimpleBody));
        writer.WriteEndElement();
        writer.WriteStartElement("SilhouetteColor");
        writer.WriteValue(BaseSystem.ConvertString(SilhouetteColor));
        writer.WriteEndElement();
        writer.WriteStartElement("BackColor");
        writer.WriteValue(BaseSystem.ConvertString(BackColor));
        writer.WriteEndElement();
        writer.WriteStartElement("MaxCharaNum");
        writer.WriteValue(BaseSystem.ConvertString(MaxCharaNum));
        writer.WriteEndElement();
        for (int i = 0; i < 4; i++)
        {
            string localName = "CharasEntry" + i;
            writer.WriteStartElement(localName);
            writer.WriteValue(BaseSystem.ConvertString(CharasEntry[i]));
            writer.WriteEndElement();
        }
        writer.WriteEndElement();
    }
}
 

huntershelby

New Member
Nov 4, 2019
2
0
Figured this would best serve the community plopped here if anyone is interested in collaborating some ideas with me...


For all those looking for a mod to add in more girls... Unfortunately the girls are not added in a static "item" sense... They are referred to as AgentActor [Heroine] in the programming (chara= Agent/Player/Merchant)... I found where they are added... The problem is they are tied to the repair and activation of their capsules [heroine_point]. Therefore, the only way we can add new girls would be:

A: place new capsules via decompressing the current game assets and assemblies, opening the module in Unity, duplicating the animated capsules with attached scripts, placing them in new locations on the map in Unity, then exporting the file package to be applied in the game's mods folder... (I wonder if the room at the back of the lab has the same animated capsules, deleting the box collider and model for that one collapsed capsule shouldn't be too hard to gain access to that back area -- provided it's not just a visual overlay and they are 3d objects in there -- if not we'd need to replace the capsules with the animated one outside, assign a new ID number, and duplicate them throughout that back room... should work in conventional theory)

B: Track down the script that is deleting the functionality of the add girl function in the existing capsules, which would more-or-less be gamebreaking in that you would be able to load infinite girls from the first capsule you find and repair. This would be easier in theory but would be broken in completed games and could possibly cause save corruption, and also would need to be used experimentally and not in excess since I would imagine if you blindly added a dozen girls then went to play there is a possibility the game would freeze completely. Since they are all going to be battling one another for your attention immediately after. We would probably have to add one, run around and test FPS drops, then save, then add another, lather rinse repeat.

C: Lastly, there may be a way to add a module with a UI overlay to simply spawn in an additional girl at your location using a simple location get script and then making it call the spawn of the new girl routine just kind of plopping her on top of you. That would also operate the same as the B: option in general...

I would feel best with A since that would require you to get to the end of the island, but it would also be more work with higher potential of causing deeper issues and breaking more stuff. However, the other two would likely make it easier to break the game in various ways... I dunno, just spitballing...

This is the LoadAgents script that utilizes the capsules:
C:
private IEnumerator LoadAgents(WorldData profile, bool existsBackup)
    {
        DefinePack define = Singleton<Manager.Resources>.Instance.DefinePack;
        AgentProfile agentProfile = Singleton<Manager.Resources>.Instance.AgentProfile;
        string bundlePath = define.ABPaths.ActorPrefab;
        string manifest = define.ABManifests.Default;
        GameObject agentPrefab = CommonLib.LoadAsset<GameObject>(bundlePath, "Agent", clone: false, manifest);
        if (!MapScene.AssetBundlePaths.Exists((UnityEx.ValueTuple<string, string> x) => x.Item1 == bundlePath && x.Item2 == manifest))
        {
            MapScene.AssetBundlePaths.Add(new UnityEx.ValueTuple<string, string>(bundlePath, manifest));
        }
        int[] chunkKeys = ChunkTable.Keys.ToArray();
        int maxCharaNum = Config.GraphicData.MaxCharaNum;
        bool[] charasEnter = Config.GraphicData.CharasEntry;
        for (int i = 0; i < profile.AgentTable.Count; i++)
        {
            if (!charasEnter[i] && (!TutorialMode || i != 0))
            {
                continue;
            }
            AgentData data = profile.AgentTable[i];
            if (!data.OpenState || data.CharaFileName.IsNullOrEmpty())
            {
                continue;
            }
            GameObject agentObj = UnityEngine.Object.Instantiate(agentPrefab, ActorRoot);
            agentObj.name = string.Format("Heroine_{0}", i.ToString("00"));
            AgentActor agent = agentObj.GetComponent<AgentActor>();
            agent.ID = i;
            agent.AgentData = data;
            data.param.Bind(agent);
            RegisterActor(i, agent);
            RegisterAgent(i, agent);
            if (existsBackup)
            {
                if (!TutorialMode)
                {
                    if (agentProfile.BlackListInSaveAndLoad.Contains(data.ModeType))
                    {
                        data.ModeType = Desire.ActionType.Normal;
                    }
                    if (data.ModeType == Desire.ActionType.Date || data.ModeType == Desire.ActionType.Onbu)
                    {
                        data.ModeType = Desire.ActionType.Normal;
                    }
                }
                agent.Mode = data.ModeType;
                agent.PrevMode = data.PrevMode;
                if (i == 0 && TutorialMode)
                {
                    AgentActor agentActor = agent;
                    Desire.ActionType actionType2 = data.ModeType = Desire.ActionType.Idle;
                    actionType2 = actionType2;
                    agent.PrevActionMode = actionType2;
                    actionType2 = actionType2;
                    agent.PrevMode = actionType2;
                    agentActor.Mode = actionType2;
                    agent.PrevActionMode = Desire.ActionType.Idle;
                    agent.TutorialType = data.TutorialModeType;
                    agent.TutorialMode = true;
                    TutorialAgent = agent;
                    agent.CreateTutorialBehaviorResources();
                }
                Vector3 pos = data.Position;
                (from _ in Observable.EveryUpdate()
                    where agent.NavMeshAgent.enabled
                    select _).TakeWhile((long _) => !agent.NavMeshAgent.isOnNavMesh).Subscribe(delegate
                {
                    agent.NavMeshAgent.Warp(pos);
                });
                agent.Rotation = data.Rotation;
                ChunkTable.TryGetValue(data.ChunkID, out Chunk value);
                MapArea[] mapAreas = value.MapAreas;
                foreach (MapArea mapArea in mapAreas)
                {
                    if (mapArea.AreaID == data.AreaID)
                    {
                        agent.MapArea = mapArea;
                    }
                }
            }
            else
            {
                StoryPoint storyPoint = GetStoryPoint(0);
                if (TutorialMode && i == 0 && storyPoint != null)
                {
                    AgentActor agentActor2 = agent;
                    Desire.ActionType actionType2 = data.ModeType = Desire.ActionType.Idle;
                    actionType2 = actionType2;
                    agent.PrevActionMode = actionType2;
                    actionType2 = actionType2;
                    agent.PrevMode = actionType2;
                    agentActor2.Mode = actionType2;
                    AIProject.Definitions.Tutorial.ActionType actionType6 = agent.TutorialType = (data.TutorialModeType = AIProject.Definitions.Tutorial.ActionType.Idle);
                    agent.MapArea = storyPoint.OwnerArea;
                    Transform transform = storyPoint.transform.FindLoop("heroine_point").transform;
                    AgentActor agentActor3 = agent;
                    Vector3? vector = transform?.position;
                    agentActor3.Position = ((!vector.HasValue) ? (storyPoint.Position + Player.Rotation * storyPoint.Forward * 3f) : vector.Value);
                    AgentActor agentActor4 = agent;
                    Quaternion? quaternion = transform?.rotation;
                    agentActor4.Rotation = ((!quaternion.HasValue) ? storyPoint.Rotation : quaternion.Value);
                    agent.TutorialMode = true;
                    TutorialAgent = agent;
                    agent.CreateTutorialBehaviorResources();
                }
                else
                {
                    int key = 0;
                    ChunkTable.TryGetValue(key, out Chunk value2);
                    List<Waypoint> waypoints = value2.MapAreas[0].Waypoints;
                    List<Waypoint> list = waypoints.FindAll(delegate(Waypoint x)
                    {
                        Vector3 position = Player.Position;
                        float num = Vector3.Distance(x.transform.position, position);
                        return num > 50f;
                    });
                    if (Debug.isDebugBuild)
                    {
                        Debug.Log($"近距離で除外されたウェイポイントの数 = {waypoints.Count - list.Count}");
                    }
                    Waypoint element = list.GetElement(UnityEngine.Random.Range(0, list.Count));
                    agent.Position = element.transform.position;
                    agent.MapArea = value2.MapAreas[0];
                }
            }
            agent.Relocate();
            agentObj.SetActiveSafe(isActive: true);
            yield return agent.LoadAsync();
            if (!existsBackup)
            {
                agent.SetDefaultImmoral();
                foreach (KeyValuePair<int, float> item in agent.ChaControl.fileGameInfo.desireDefVal)
                {
                    agent.AgentData.DesireTable[item.Key] = item.Value;
                }
            }
            yield return null;
        }
        yield return null;

public void LoadAgentTargetActionPoint()
    {
        foreach (KeyValuePair<int, AgentActor> agent in _agentTable)
        {
            ActionPoint actionPoint = PointAgent.ActionPoints.FirstOrDefault((ActionPoint x) => x.RegisterID == agent.Value.AgentData.CurrentActionPointID);
            if (actionPoint == null)
            {
                actionPoint = PointAgent.AppendActionPoints.Find((ActionPoint x) => x.RegisterID == agent.Value.AgentData.CurrentActionPointID);
            }
            if (actionPoint != null && !actionPoint.AgentEventType.Contains(AIProject.EventType.Move) && !actionPoint.AgentEventType.Contains(AIProject.EventType.DoorOpen))
            {
                agent.Value.TargetInSightActionPoint = actionPoint;
            }
        }
    }

    public void LoadAgentTargetActor()
    {
        foreach (KeyValuePair<int, AgentActor> item in _agentTable)
        {
            if (_actorTable.TryGetValue(item.Value.AgentData.ActionTargetID, out Actor value))
            {
                item.Value.TargetInSightActor = value;
            }
        }
    }
    }
This seems to be the script that is activated to actually spawn the girl into the world:

Code:
public AgentActor AddAgent(int id, AgentData agentData)
    {
        if (_agentPrefab == null)
        {
            DefinePack definePack = Singleton<Manager.Resources>.Instance.DefinePack;
            string bundlePath = definePack.ABPaths.ActorPrefab;
            string manifest = definePack.ABManifests.Default;
            _agentPrefab = CommonLib.LoadAsset<GameObject>(bundlePath, "Agent", clone: false, manifest);
            if (!MapScene.AssetBundlePaths.Exists((UnityEx.ValueTuple<string, string> x) => x.Item1 == bundlePath && x.Item2 == manifest))
            {
                MapScene.AssetBundlePaths.Add(new UnityEx.ValueTuple<string, string>(bundlePath, manifest));
            }
        }
        int[] array = ChunkTable.Keys.ToArray();
        GameObject gameObject = UnityEngine.Object.Instantiate(_agentPrefab, ActorRoot);
        gameObject.name = string.Format("Heroine_{0}", id.ToString("00"));
        AgentActor component = gameObject.GetComponent<AgentActor>();
        component.ID = id;
        component.AgentData = agentData;
        if (Singleton<Manager.Resources>.Instance.AgentProfile.DefaultAreaIDTable.TryGetValue(id, out int value))
        {
            int num3 = component.AreaID = (agentData.AreaID = value);
            foreach (KeyValuePair<int, Chunk> item in ChunkTable)
            {
                MapArea[] mapAreas = item.Value.MapAreas;
                foreach (MapArea mapArea in mapAreas)
                {
                    if (mapArea.AreaID == value)
                    {
                        component.MapArea = mapArea;
                    }
                }
            }
        }
        agentData.param.Bind(component);
        RegisterActor(id, component);
        RegisterAgent(id, component);
        if (Singleton<AnimalManager>.IsInstance())
        {
            Singleton<AnimalManager>.Instance.AddTargetAnimals(component);
        }
        component.Relocate();
        gameObject.SetActiveSafe(isActive: true);
        component.Load();
        return component;
    }
Then these are used to unload the existing girls from the world:

Code:
public void RemoveAgent(AgentActor agent)
    {
        agent.DisableBehavior();
        agent.BehaviorResources.DisableAllBehaviors();
        foreach (KeyValuePair<int, Dictionary<int, UnityEx.ValueTuple<AudioSource, FadePlayer>>> item in agent.Animation.ActionLoopSETable)
        {
            foreach (KeyValuePair<int, UnityEx.ValueTuple<AudioSource, FadePlayer>> item2 in item.Value)
            {
                UnityEx.ValueTuple<AudioSource, FadePlayer> value = item2.Value;
                if (!(value.Item2 == null))
                {
                    UnityEx.ValueTuple<AudioSource, FadePlayer> value2 = item2.Value;
                    value2.Item2.Stop(0f);
                }
            }
        }
        agent.Animation.ActionLoopSETable.Clear();
        Player.PlayerController.CommandArea.RemoveCommandableObject(agent);
        UnregisterAgent(agent.ID);
        UnregisterActor(agent.ID);
        foreach (KeyValuePair<int, AgentActor> item3 in AgentTable)
        {
            if (!(item3.Value == agent))
            {
                item3.Value.RemoveActor(agent);
            }
        }
        Singleton<Character>.Instance.DeleteChara(agent.ChaControl);
        UnityEngine.Object.Destroy(agent.gameObject);
    }
These are found at:
Assembly-CSharp.dll>Manager>Map>Line 2580

Attached a full version of the map.xml file if anyone wants to look at the whole thing and throw about some ideas...
Strongly wish someone could make a new edited map file using solution A by removing that broken door behind the lab, making it possible to add girls "in a regular way".

Also, strongly wish someone could make a plugin or script to provide male's and femal's POV during the H scene.
 

huntershelby

New Member
Nov 4, 2019
2
0
And does someone know what plugin can disable the area limit of build mode, so that I can place my house anywhere on the first island?
 

EroShane

Active Member
Sep 7, 2019
815
965
Okay, so I figured out how to extract the Assembly-CSharp.dll and using VisualStudio editing the MAX_CHARA_NUM = 4 to 9 (just to add one or two to test) then saving... However, the game loaded to the menu screen then the screen went black with the cursor. I checked what was messed up and found I cannot repack the dll with the correct metadata.

Any ideas? Anyone know a good dll packer for modding purposes? Or a way to get the modded file to load while extracted?
 

27X

Active Member
Oct 30, 2019
516
442
Okay, so I figured out how to extract the Assembly-CSharp.dll and using VisualStudio editing the MAX_CHARA_NUM = 4 to 9 (just to add one or two to test) then saving... However, the game loaded to the menu screen then the screen went black with the cursor. I checked what was messed up and found I cannot repack the dll with the correct metadata.

Any ideas? Anyone know a good dll packer for modding purposes? Or a way to get the modded file to load while extracted?
Winmerge.

The other thing that's going to be a huge pain is the amount of AI gambitting. Each girls uses sliding arrays of other arrays for behavior and the animations tied to those behaviors. You've already seen with dark matter versus fishing how that system can literally break already and cause endless loops in girls by themselves. Now add in the behaviors for four different girls without breaking, and then just go ahead and double that, or triple it.

Even though Illusion themsevles are probably thinking about adding more girls themselves it's probably not going to end well, and we haven't even covered the game will be rendering each of those girls all the time whether you can see them or not because Illusion+Unity. The reason shadows and AA cause such a huge hit is because there are zero rendering portals or occlusion in the game, save for the two islands.

Adding more strain to that is not going to be kind to the performance of a game that can barely do 60fps at 4K with a 2080, nevermind lighting and higher poly mods.
 
  • Like
Reactions: EroShane

JKerman

Newbie
Nov 1, 2019
28
6
Any news on vr status? It's in Unity, so repurposing an old vr mod wouldn't take that long, right?
 

iSoulfire

New Member
Aug 9, 2018
6
1
What about making a furniture item that looks and works like the capsule? Would that be possible or helpful for the sollution? This way you don't need to mod the island itself, since you could place the capsule anywhere in a base area.
 

bloodsuga

Newbie
Oct 23, 2019
54
29
AI Uncensor Male + [Pixler] AI Uncensor v2.0

I don't need credits for this!



ps: you won't be able to change skin color of the dick
Only works during character creation, in the game it turns black lol.
 

Jet4

New Member
Oct 28, 2019
14
5
Downloaded a bunch of mods, installed them as described but non seem te work, what am i missing here?
Not eventhe simple clothing mods from Roy work, wich is pretty much drag n drop into the game folder..?
Help! >.<''
 

27X

Active Member
Oct 30, 2019
516
442
Downloaded a bunch of mods, installed them as described but non seem te work, what am i missing here?
Not eventhe simple clothing mods from Roy work, wich is pretty much drag n drop into the game folder..?
Help! >.<''
down the better repack, look where the mods are located, that's where mods are supposed to go
 
  • Like
Reactions: Jet4

EroShane

Active Member
Sep 7, 2019
815
965
Winmerge.

The other thing that's going to be a huge pain is the amount of AI gambitting. Each girls uses sliding arrays of other arrays for behavior and the animations tied to those behaviors. You've already seen with dark matter versus fishing how that system can literally break already and cause endless loops in girls by themselves. Now add in the behaviors for four different girls without breaking, and then just go ahead and double that, or triple it.

Even though Illusion themsevles are probably thinking about adding more girls themselves it's probably not going to end well, and we haven't even covered the game will be rendering each of those girls all the time whether you can see them or not because Illusion+Unity. The reason shadows and AA cause such a huge hit is because there are zero rendering portals or occlusion in the game, save for the two islands.

Adding more strain to that is not going to be kind to the performance of a game that can barely do 60fps at 4K with a 2080, nevermind lighting and higher poly mods.
Going to check out that Winmerge now...

And yeah, I paged through all the camera controls, effects, settings, and post-processing effects and they WAY overdid that stuff... If you stand at the center of the island map (think where the tree bridge is between Shan's and the Forest Base, and do a 360, you are literally loading the entire map to the very edges, like 100 meters of water and dead distant islands, with a ton of random plants and crap on the ocean floor, all the girls, and everything you can and cannot see... The entire game is being rendered as the sun refreshes every 3 seconds or so then slides slighting in that direction endlessly... Yeah... I know what they were trying, as I went with a sky sphere in my game vs a traditional skybox as well. It is killer on processing.

Also, I was plotting to make a little script to cull distant views and shorten the overall view distance, pull the focus point back from 30m to 10m, the point of focus shouldn't be 100ft away. I also noticed a Unity noob mistake, they set the near distance of the camera to 0.1m, when it should be like 0.3-0.5 typically (suggested minimum of 0.3 as per Unity), especially if your height is 0.75, since you have no need of seeing your feet or between your legs... LOL! Also, the 0.1m near view is know to cause render conflicts and is noted in the core documentation for the utility pack first person camera rig... The current field of view is too long for how small the total map area is (they accounted for this by slowing movement to a crawl) -- and the Unity build is still experimental as it is, so you clearly have total Unity noobs on an intermediate to extremely advanced Unity build... LOL!

I already passed my classes on camera handling and post-processing... LOL! You know there is a hard coded chromatic aberration in the post processing script that I don't even see in the game at all? Unless they hid it behind the darker edges of the vignette, in which case, what is the purpose? Either way, it's there and it's running for nothing. The "rain draw effects" are a joke, they just have the game swap in additional metallic/smoothness maps with recalculated values to make the ground look "wet-ish," or covered in plastic wrap. LOL! There are a ton of small tweaks that can be made to clean it up... Which would free up a lot of processing strength... BUT, those ultra-high poly characters with their huge load files and that, dare I say, actual AI, dysfunctional as it may be, are still going to cause drops because they are massive resource hogs. The landslide of calculations the girls make in the earlier game is horrid, they actually get better once you get a few skills above 1000 though, ironically, they seem to have learned what to do when they are overworked, sick, hungry, etc., and will appropriately chose without the rigmarole of background random die 100 rolls...

Anyway, thanks for the heads up, hoping I can pull this off -- I feel confident but I worry, every time I commit to doing something there is always a hitch. After work tonight, I have three days off to tool around... So wish me luck... LOL!