// Copyright (c) 2024 Synty Studios Limited. All rights reserved. // // Use of this software is subject to the terms and conditions of the Synty Studios End User Licence Agreement (EULA) // available at: https://syntystore.com/pages/end-user-licence-agreement // // For additional details, see the LICENSE.MD file bundled with this software. using SQLite; using Synty.SidekickCharacters.Enums; using System; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEngine; namespace Synty.SidekickCharacters.Database.DTO { [Table("sk_part")] public class SidekickPart { private SidekickSpecies _species; [PrimaryKey, AutoIncrement, Column("id")] public int ID { get; set; } [Column("ptr_species")] public int PtrSpecies { get; set; } [Column("type")] public CharacterPartType Type { get; set; } [Column("part_group")] public PartGroup PartGroup { get; set; } [Column("name")] public string Name { get; set; } [Column("part_file_name")] public string FileName { get; set; } [Column("part_location")] public string Location { get; set; } [Column("uses_wrap")] public bool UsesWrap { get; set; } [Column("file_exists")] public bool FileExists { get; set; } [Ignore] public SidekickSpecies Species { get => _species; set { _species = value; PtrSpecies = value.ID; } } /// /// Gets a specific Preset Part by its database ID. /// /// The Database Manager to use. /// The id of the required Preset Part. /// The specific Preset Part if it exists; otherwise null. public static SidekickPart GetByID(DatabaseManager dbManager, int id) { SidekickPart part = dbManager.GetCurrentDbConnection().Find(id); Decorate(dbManager, part); return part; } /// /// Gets a list of all the parts in the database. /// /// The Database Manager to use. /// A list of all parts in the database. public static List GetAll(DatabaseManager dbManager) { List parts = dbManager.GetCurrentDbConnection().Table().ToList(); foreach (SidekickPart part in parts) { Decorate(dbManager, part); } return parts; } /// /// Gets a list of all the parts in the database for a given Character Part Type. /// /// The Database Manager to use. /// The Character Part Type to get all the parts for. /// A list of all parts for the given Character Part Type in the database. public static List GetAllForPartType(DatabaseManager dbManager, CharacterPartType partType) { List parts = dbManager.GetCurrentDbConnection().Table().Where(part => part.Type == partType).ToList(); foreach (SidekickPart part in parts) { Decorate(dbManager, part); } return parts; } /// /// Gets a list of all the parts in the database for a given Character Part Type. /// /// The Database Manager to use. /// The Character Part Type to get all the parts for. /// Whether to include parts that have a file in the project or not. /// A list of all parts for the given Character Part Type in the database. public static List GetAllForSpecies(DatabaseManager dbManager, SidekickSpecies species, bool onlyPartsWithFile = true) { List parts = new List(); if (onlyPartsWithFile) { parts = dbManager.GetCurrentDbConnection().Table().Where(part => part.PtrSpecies == species.ID && part.FileExists == onlyPartsWithFile).ToList(); } else { parts = dbManager.GetCurrentDbConnection().Table().Where(part => part.PtrSpecies == species.ID).ToList(); } foreach (SidekickPart part in parts) { Decorate(dbManager, part); } return parts; } /// /// Get the part in the database for the given part file name. /// /// The Database Manager to use. /// The file name to get the part for. /// A part in the database for the given part file name if it exists; otherwise null public static SidekickPart GetByPartFileName(DatabaseManager dbManager, string fileName) { SidekickPart part = dbManager.GetCurrentDbConnection().Table().FirstOrDefault(part => part.FileName == fileName); Decorate(dbManager, part); return part; } /// /// Gets all the base parts from the database. /// /// The Database Manager to use. /// A list of all the base parts from the database. public static List GetBaseParts(DatabaseManager dbManager) { List parts = dbManager.GetCurrentDbConnection().Table().Where(part => part.FileName.Contains("_BASE_")).ToList(); foreach (SidekickPart part in parts) { Decorate(dbManager, part); } return parts; } /// /// Search for a part in the database where the part name or filename match the given term. /// /// The Database Manager to use. /// The term to search for the part with. /// A part in the database for that matches the search term if it exists; otherwise null public static SidekickPart SearchForByName(DatabaseManager dbManager, string partName) { SidekickPart part = dbManager.GetCurrentDbConnection().Table().FirstOrDefault(part => part.Name == partName || part .FileName.Contains(partName)); Decorate(dbManager, part); return part; } /// /// Checks to see if the provided part name is unique. /// /// The Database Manager to use. /// The part name to check and see if it is unique. /// True if no part exists in the database with given name; otherwise false. public static bool IsPartNameUnique(DatabaseManager dbManager, string partName) { SidekickPart part = dbManager.GetCurrentDbConnection().Table().FirstOrDefault(part => part.Name == partName); return part == null; } /// /// Gets the species for a specific part. /// TODO: get from the DB when data is populated. /// /// The list of all species to return a populated species object from. /// The name of the part to get the species for. /// The species the part belongs to. public static SidekickSpecies GetSpeciesForPart(List allSpecies, string partName) { string shortcode = partName.Split('_').Last().Substring(0, 2); SidekickSpecies selectedSpecies = allSpecies[0]; foreach (SidekickSpecies species in allSpecies) { if (string.Equals(shortcode, species.Code, StringComparison.CurrentCultureIgnoreCase)) { selectedSpecies = species; } } return selectedSpecies; } /// /// Updates all of the given parts in the DB. This is an Update only, and will not insert new objects. /// /// The database manager to use. /// The parts to update public static void UpdateAll(DatabaseManager dbManager, List parts) { dbManager.GetCurrentDbConnection().UpdateAll(parts, false); } /// /// Ensures that the given part has its nice DTO class properties set /// /// The Database Manager to use. /// The part to decorate private static void Decorate(DatabaseManager dbManager, SidekickPart part) { if (part != null) { part.Location = DatabaseManager.NormalizeLegacyAssetPath(part.Location); if (part.Species == null && part.PtrSpecies >= 0) { part.Species = SidekickSpecies.GetByID(dbManager, part.PtrSpecies); } } } /// /// Updates or Inserts this item in the Database. /// /// The database manager to use. public int Save(DatabaseManager dbManager) { if (ID <= 0) { dbManager.GetCurrentDbConnection().Insert(this); // in theory this could return a different ID, but in practice it's highly unlikely ID = (int) SQLite3.LastInsertRowid(dbManager.GetCurrentDbConnection().Handle); } dbManager.GetCurrentDbConnection().Update(this); return ID; } /// /// Gets the image associated with this part. /// /// The database manager to use. /// The image associated with this part. public SidekickPartImage GetImageForPart(DatabaseManager dbManager) { return SidekickPartImage.GetByPart(dbManager, this); } /// /// Deletes this item from the database /// /// The database manager to use. public void Delete(DatabaseManager dbManager) { foreach (SidekickPartFilterRow row in SidekickPartFilterRow.GetAllForPart(dbManager, this)) { row.Delete(dbManager); } foreach (SidekickPartPresetRow row in SidekickPartPresetRow.GetAllByPart(dbManager, this)) { row.Delete(dbManager); } foreach (SidekickPartSpeciesLink link in SidekickPartSpeciesLink.GetAllForPart(dbManager, this)) { link.Delete(dbManager); } SidekickPartImage image = SidekickPartImage.GetByPart(dbManager, this); image?.Delete(dbManager); dbManager.GetCurrentDbConnection().Delete(ID); } /// /// Gets the GameObject model of this part. /// /// A GameObject with the part model public GameObject GetPartModel() { string resource = GetResourcePath(Location); return Resources.Load(resource); } /// /// Checks if the file for this part exists. /// /// True if the file is available; otherwise false public bool IsFileAvailable() { FileExists = File.Exists(Location); return FileExists; } /// /// Gets a resource path for using with Resources.Load() from a full path. /// /// The full path to get the resource path from. /// The resource path. private string GetResourcePath(string fullPath) { int startIndex = fullPath.IndexOf("Resources", StringComparison.Ordinal) + 10; string resourcePath = fullPath.Substring(startIndex, fullPath.Length - startIndex); return Path.Combine(Path.GetDirectoryName(resourcePath)?? "", Path.GetFileNameWithoutExtension(resourcePath)); } /// public override bool Equals(object obj) { SidekickPart part = (SidekickPart) obj; if (ID > 0 && part?.ID > 0) { return ID == part?.ID; } return Name.Equals(part?.Name); } /// public override int GetHashCode() { HashCode hashCode = new HashCode(); hashCode.Add(_species); hashCode.Add(ID); hashCode.Add(PtrSpecies); hashCode.Add((int) Type); hashCode.Add((int) PartGroup); hashCode.Add(Name); hashCode.Add(FileName); hashCode.Add(Location); hashCode.Add(UsesWrap); hashCode.Add(FileExists); return hashCode.ToHashCode(); } } }