using System.Collections; using System.Collections.Generic; using UnityEngine; using Sirenix.OdinInspector; using System; using System.Linq; using UnityEngine.Assertions; using NodeCanvas.Framework; using KairoEngine.Core; using KairoEngine.Inventory; namespace KairoEngine.CharacterSystem { public class CharacterGenerator : MonoBehaviour { public static CharacterGenerator instance; public GameObject characterTemplatePrefab; [AssetList(Path = "/Data/Characters/ModelData/", AutoPopulate = true)] public List modelList; [AssetList(Path = "/Data/Characters/Attachments/", AutoPopulate = true)] public List attachmentList; void OnEnable() { GenericEvents.StartListening("GenerateCharacter", Create); } void OnDisable() { GenericEvents.StopListening("GenerateCharacter", Create); } void Awake() { if(instance == null) instance = this; else Destroy(this.gameObject); } public void Create(ScriptableObject scriptableObject) { CharacterData characterData = (CharacterData)scriptableObject; Create(characterData); } public static CharacterController Create(CharacterData data, Vector3 position, Quaternion rotation) { data.hasSpawned = true; data.position = position; data.rotation = rotation; return Create(data); } public static CharacterController Create(CharacterData data) { // Instantiate Template Character Prefab GameObject characterObj = GameObject.Instantiate(instance.characterTemplatePrefab, data.position, data.rotation); characterObj.name = "Character_" + data.characterName; CharacterController characterController = characterObj.GetComponentInChildren(); // Set Body Model ModelData modelData = FindModel(data.bodyModel); if(modelData == null) { Debug.LogError("No modelData found with name " + data.bodyModel, data); return null; } Material material = GetMaterialInModelData(modelData, data.bodyMaterial); SkinRigger skinRigger = characterObj.GetComponentInChildren(); if(skinRigger == null) Debug.LogError("Missing SkinRigger component", characterObj); else skinRigger.Setup(modelData, material); // Set Head Attachment if(data.headAttachment != "") { GameObject headAttachmentPrefab = FindAttachment(data.headAttachment); if(headAttachmentPrefab != null) GameObject.Instantiate(headAttachmentPrefab, characterController.head.transform); } // Set Face Attachment if(data.faceAttachment != "") { GameObject faceAttachmentPrefab = FindAttachment(data.faceAttachment); if(faceAttachmentPrefab != null) GameObject.Instantiate(faceAttachmentPrefab, characterController.head.transform); } // Set Name characterController.characterName = data.name; characterController.id = data.id; // Set Faction characterController.faction = data.faction; // Set details characterController.race = data.race; characterController.gender = data.gender; characterController.age = data.age; // Set Visuals characterController.bodyModelName = data.bodyModel; characterController.bodyMaterialName = data.bodyMaterial; characterController.headAttachmentName = data.headAttachment; characterController.faceAttachmentName = data.faceAttachment; // Set Hitpoints and armor characterController.damageController.SetHP(data.hitpoints); characterController.damageController.SetMaxHP(data.maxHitpoints); characterController.damageController.SetArmor(data.armor); // Set inventory items ItemContainer itemContainer = characterController.GetComponent(); for (int b = 0; b < data.itemList.Count; b++) { if(data.itemList[b].item.category == ItemType.firearm) { var originalFirearmRef = data.itemList[b] as ItemFirearmRef; if (originalFirearmRef == null) { ItemBaseFirearm itemBaseFirearm = (ItemBaseFirearm)data.itemList[b].item; ItemBaseAmmo itemBaseAmmo = (ItemBaseAmmo)itemBaseFirearm.ammoType; int ammo = itemBaseFirearm.ammoCapacity; ItemFirearmRef firearmRef = new ItemFirearmRef(itemBaseFirearm, data.itemList[b].quantity, itemBaseAmmo, ammo); itemContainer.AddItem(firearmRef); } else { ItemFirearmRef newFirearm = new ItemFirearmRef(data.itemList[b].item, data.itemList[b].quantity, originalFirearmRef.ammoType, originalFirearmRef.ammo, originalFirearmRef.accuracyModifier, originalFirearmRef.lastUsed); itemContainer.AddItem(newFirearm); } } else { ItemRef newItem = new ItemRef(data.itemList[b].item, data.itemList[b].quantity, data.itemList[b].lastUsed); itemContainer.AddItem(newItem); } } // Set Sensors if(characterController.visionSensor != null) characterController.visionSensor.enabled = data.vision; if(characterController.hearingSensor != null) characterController.hearingSensor.enabled = data.hearing; // Set death stance if(data.isDead) { characterController.Die(); SetBones(data.bones, characterController); } // Set Dialogue Blackboard blackboard = characterController.GetComponent(); if(blackboard !=null && data.dialogueTree != null) blackboard.SetVariableValue("dialogueTree", data.dialogueTree); return characterController; } public static ModelData FindModel(string title) { for (int i = 0; i < instance.modelList.Count; i++) { if(instance.modelList[i] != null) { if(instance.modelList[i].name == title) return instance.modelList[i]; } } return null; } public static Material GetMaterialInModelData(ModelData modelData, string materialName) { if(modelData != null) { for (int i = 0; i < modelData.materials.Count; i++) { if(modelData.materials[i].name == materialName) return modelData.materials[i]; } } return null; } public static GameObject FindAttachment(string title) { for (int i = 0; i < instance.attachmentList.Count; i++) { if(instance.attachmentList[i].name == title) return instance.attachmentList[i]; } return null; } public static void SetBones(List bones, CharacterController character) { int boneCount = 0; character.animator.enabled = false; Destroy(character.puppetMaster.gameObject); Transform[] transforms = character.transform.root.GetComponentsInChildren(); for (int i = 0; i < bones.Count; i++) { for (int a = 0; a < transforms.Length; a++) { if(bones[i].name == transforms[a].gameObject.name) { transforms[a].position = bones[i].position; transforms[a].rotation = bones[i].rotation; boneCount += 1; } } } Debug.Log($"Set up {boneCount}/{bones.Count} bones for {character.unique_name}"); } public static void UpdateBoneOrder(SkinnedMeshRenderer meshRenderer) { Transform[] children = meshRenderer.rootBone.GetComponentsInChildren(); Transform[] bones = new Transform[meshRenderer.bones.Length]; for (int boneOrder = 0; boneOrder < meshRenderer.bones.Length; boneOrder++) { bones[boneOrder] = Array.Find(children, c => c.name == meshRenderer.bones[boneOrder].name); } meshRenderer.bones = bones; } public static void UpdateBoneOrder2(SkinnedMeshRenderer rend) { //list of bones List tList = rend.bones.ToList(); //sort alphabetically tList.Sort(CompareTransform); //record bone index mappings (richardf advice) //build a Dictionary that records the old bone index => new bone index mappings, //then run through every vertex and just do boneIndexN = dict[boneIndexN] for each weight on each vertex. Dictionary remap = new Dictionary(); for (int i = 0; i < rend.bones.Length; i++) { remap[i] = tList.IndexOf(rend.bones[i]); } //remap bone weight indexes BoneWeight[] bw = rend.sharedMesh.boneWeights; for (int i = 0; i < bw.Length; i++) { bw[i].boneIndex0 = remap[bw[i].boneIndex0]; bw[i].boneIndex1 = remap[bw[i].boneIndex1]; bw[i].boneIndex2 = remap[bw[i].boneIndex2]; bw[i].boneIndex3 = remap[bw[i].boneIndex3]; } //remap bindposes Matrix4x4[] bp = new Matrix4x4[rend.sharedMesh.bindposes.Length]; for (int i = 0; i < bp.Length; i++) { var r = remap[i]; if(bp.Length > r && rend.sharedMesh.bindposes.Length > i) { bp[r] = rend.sharedMesh.bindposes[i]; } } //assign new data rend.bones = tList.ToArray(); rend.sharedMesh.boneWeights = bw; rend.sharedMesh.bindposes = bp; } private static int CompareTransform(Transform A, Transform B) { return A.name.CompareTo(B.name); } } }