- Introduce new saga rail commands for species frame auditing and sweeping - Implement retry logic for capturing inactive live frames in the rail - Refactor face resolution logic to prioritize live frames over species icons - Update harness scenarios to validate new rail functionality and performance - Enhance documentation to clarify live capture behavior and fallback handling
816 lines
23 KiB
C#
816 lines
23 KiB
C#
using System;
|
|
using UnityEngine;
|
|
|
|
namespace IdleSpectator;
|
|
|
|
/// <summary>
|
|
/// Loads WorldBox ui/Icons sprites for HUD chrome.
|
|
/// </summary>
|
|
public static class HudIcons
|
|
{
|
|
public static Sprite FromUiIcon(string iconName)
|
|
{
|
|
if (string.IsNullOrEmpty(iconName))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
Sprite sprite = SpriteTextureLoader.getSprite("ui/Icons/" + iconName);
|
|
if (sprite != null)
|
|
{
|
|
return sprite;
|
|
}
|
|
|
|
// Some paths are lower-case in older content.
|
|
return SpriteTextureLoader.getSprite("ui/icons/" + iconName);
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static Sprite FromAsset(ActorAsset asset)
|
|
{
|
|
if (asset == null)
|
|
{
|
|
return FromUiIcon("iconQuestionMark");
|
|
}
|
|
|
|
try
|
|
{
|
|
Sprite sprite = asset.getSpriteIcon();
|
|
if (sprite != null)
|
|
{
|
|
return sprite;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// fall through
|
|
}
|
|
|
|
try
|
|
{
|
|
if (!string.IsNullOrEmpty(asset.icon))
|
|
{
|
|
return FromUiIcon(asset.icon);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
return FromUiIcon("iconQuestionMark");
|
|
}
|
|
|
|
public static Sprite FromActor(Actor actor)
|
|
{
|
|
if (actor == null)
|
|
{
|
|
return FromUiIcon("iconQuestionMark");
|
|
}
|
|
|
|
return FromAsset(actor.asset);
|
|
}
|
|
|
|
public static Sprite FromSpeciesId(string speciesId)
|
|
{
|
|
if (string.IsNullOrEmpty(speciesId))
|
|
{
|
|
return FromUiIcon("iconQuestionMark");
|
|
}
|
|
|
|
try
|
|
{
|
|
ActorAsset asset = AssetManager.actor_library?.get(speciesId);
|
|
if (asset != null)
|
|
{
|
|
return FromAsset(asset);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// fall through
|
|
}
|
|
|
|
return FromUiIcon("iconQuestionMark");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Live unit sprite as shown in inspect UI (phenotype/kingdom tint), not the species icon.
|
|
/// </summary>
|
|
public static Sprite FromActorLive(Actor actor)
|
|
{
|
|
if (actor == null || !actor.isAlive())
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
Sprite main = actor.calculateMainSprite();
|
|
if (main == null)
|
|
{
|
|
return FromActor(actor);
|
|
}
|
|
|
|
Sprite ui = DynamicActorSpriteCreatorUI.getUnitSpriteForUI(actor, main);
|
|
if (ui != null)
|
|
{
|
|
return ui;
|
|
}
|
|
|
|
if (actor.hasColoredSprite())
|
|
{
|
|
Sprite colored = actor.calculateColoredSprite(main);
|
|
if (colored != null)
|
|
{
|
|
return colored;
|
|
}
|
|
}
|
|
|
|
return main;
|
|
}
|
|
catch
|
|
{
|
|
return FromActor(actor);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Actor-specific saga-rail frame only. Returns null for eggs, dead actors, or
|
|
/// unusable render frames; callers decide whether to display a temporary fallback.
|
|
/// </summary>
|
|
public static Sprite FromActorLiveRailFace(Actor actor)
|
|
{
|
|
if (actor == null || !actor.isAlive())
|
|
{
|
|
return null;
|
|
}
|
|
|
|
bool egg = false;
|
|
try
|
|
{
|
|
egg = actor.isEgg();
|
|
}
|
|
catch
|
|
{
|
|
egg = false;
|
|
}
|
|
|
|
if (egg)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
Sprite main = actor.calculateMainSprite();
|
|
if (main == null || LooksLikeEggSprite(main))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
Sprite speciesIcon = FromActor(actor);
|
|
bool civilized = actor.asset != null && actor.asset.civ;
|
|
|
|
Sprite colored = null;
|
|
try
|
|
{
|
|
// Some creatures report hasColoredSprite=false even though this returns the
|
|
// palette-resolved live frame (cat: walk_0 -> gen_walk_0).
|
|
colored = actor.calculateColoredSprite(main);
|
|
}
|
|
catch
|
|
{
|
|
colored = null;
|
|
}
|
|
|
|
Sprite ui = null;
|
|
if (civilized)
|
|
{
|
|
try
|
|
{
|
|
ui = DynamicActorSpriteCreatorUI.getUnitSpriteForUI(actor, main);
|
|
}
|
|
catch
|
|
{
|
|
ui = null;
|
|
}
|
|
}
|
|
|
|
// Civ units benefit from the combined inspect frame (body/head/equipment).
|
|
if (civilized
|
|
&& IsUsableRailFace(ui)
|
|
&& !SameSpriteSource(ui, speciesIcon))
|
|
{
|
|
return ui;
|
|
}
|
|
|
|
// Creature palette generation is the closest single-frame equivalent to the
|
|
// live renderer. It must precede the raw atlas frame or palette-key colors leak.
|
|
if (IsUsableRailFace(colored)
|
|
&& !LooksLikeEggSprite(colored)
|
|
&& !SameSpriteSource(colored, speciesIcon))
|
|
{
|
|
return colored;
|
|
}
|
|
|
|
if (!civilized
|
|
&& IsUsableRailFace(main)
|
|
&& !SameSpriteSource(main, speciesIcon))
|
|
{
|
|
return main;
|
|
}
|
|
|
|
// The UI helper is allowed to synthesize a copy of the asset portrait for
|
|
// creatures. Source-region comparison cannot detect that copy, so never use
|
|
// it as a non-civ "live" frame. Their main/colored animation is authoritative.
|
|
if (civilized
|
|
&& IsUsableRailFace(ui)
|
|
&& !SameSpriteSource(ui, speciesIcon))
|
|
{
|
|
return ui;
|
|
}
|
|
|
|
return civilized
|
|
&& IsUsableRailFace(main)
|
|
&& !SameSpriteSource(main, speciesIcon)
|
|
? main
|
|
: null;
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Readable saga-rail face: actor-specific live frame when available, else species icon.
|
|
/// The combined helper is for non-caching presentation such as Cast cells.
|
|
/// </summary>
|
|
public static Sprite FromActorRailFace(Actor actor, string speciesFallbackId = null)
|
|
{
|
|
Sprite live = FromActorLiveRailFace(actor);
|
|
if (IsUsableRailFace(live))
|
|
{
|
|
return live;
|
|
}
|
|
|
|
string species = speciesFallbackId;
|
|
if (string.IsNullOrEmpty(species) && actor?.asset != null)
|
|
{
|
|
species = actor.asset.id;
|
|
}
|
|
|
|
return FromSpeciesId(species);
|
|
}
|
|
|
|
public static bool IsUsableRailFace(Sprite sprite)
|
|
{
|
|
if (sprite == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Actor animation frames are not square. Foxes and other long-bodied creatures
|
|
// legitimately use 5-7px-tall frames, so a per-axis 8px floor rejects the live
|
|
// animation and makes callers fall back to the standing species portrait.
|
|
float w = sprite.rect.width;
|
|
float h = sprite.rect.height;
|
|
if (w < 2f || h < 2f || (w * h) < 4f)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (sprite.texture == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>True when two sprites point at the same atlas region, even as different objects.</summary>
|
|
public static bool SameSpriteSource(Sprite a, Sprite b)
|
|
{
|
|
if (a == null || b == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(a, b))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
try
|
|
{
|
|
Rect ar = a.rect;
|
|
Rect br = b.rect;
|
|
return a.texture == b.texture
|
|
&& Mathf.Abs(ar.x - br.x) < 0.1f
|
|
&& Mathf.Abs(ar.y - br.y) < 0.1f
|
|
&& Mathf.Abs(ar.width - br.width) < 0.1f
|
|
&& Mathf.Abs(ar.height - br.height) < 0.1f;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>Harness diagnostic for WorldBox's competing actor-frame sources.</summary>
|
|
public static string DescribeActorRailSources(Actor actor)
|
|
{
|
|
if (actor == null || !actor.isAlive())
|
|
{
|
|
return "actor=none";
|
|
}
|
|
|
|
try
|
|
{
|
|
Sprite species = FromActor(actor);
|
|
Sprite main = actor.calculateMainSprite();
|
|
Sprite colored = null;
|
|
try
|
|
{
|
|
colored = main != null ? actor.calculateColoredSprite(main) : null;
|
|
}
|
|
catch
|
|
{
|
|
colored = null;
|
|
}
|
|
|
|
Sprite ui = null;
|
|
try
|
|
{
|
|
ui = main != null
|
|
? DynamicActorSpriteCreatorUI.getUnitSpriteForUI(actor, main)
|
|
: null;
|
|
}
|
|
catch
|
|
{
|
|
ui = null;
|
|
}
|
|
|
|
return "main=" + DescribeSprite(main, species)
|
|
+ " colored=" + DescribeSprite(colored, species)
|
|
+ " ui=" + DescribeSprite(ui, species)
|
|
+ " species=" + DescribeSprite(species, species);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return "sources_error=" + ex.GetType().Name;
|
|
}
|
|
}
|
|
|
|
/// <summary>Harness: retained frame matches the actor's palette/UI-generated live frame.</summary>
|
|
public static bool MatchesGeneratedActorFrame(Actor actor, Sprite retained)
|
|
{
|
|
if (actor == null || retained == null || !actor.isAlive())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
Sprite main = actor.calculateMainSprite();
|
|
if (main == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Sprite colored = null;
|
|
Sprite ui = null;
|
|
try
|
|
{
|
|
colored = actor.calculateColoredSprite(main);
|
|
}
|
|
catch
|
|
{
|
|
colored = null;
|
|
}
|
|
|
|
try
|
|
{
|
|
ui = DynamicActorSpriteCreatorUI.getUnitSpriteForUI(actor, main);
|
|
}
|
|
catch
|
|
{
|
|
ui = null;
|
|
}
|
|
|
|
return (IsUsableRailFace(colored) && SameSpriteSource(retained, colored))
|
|
|| (IsUsableRailFace(main) && SameSpriteSource(retained, main))
|
|
|| (IsUsableRailFace(ui) && SameSpriteSource(retained, ui));
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static string DescribeSprite(Sprite sprite, Sprite species)
|
|
{
|
|
if (sprite == null)
|
|
{
|
|
return "none";
|
|
}
|
|
|
|
try
|
|
{
|
|
return (sprite.name ?? "?")
|
|
+ ":" + sprite.rect.width.ToString("0")
|
|
+ "x" + sprite.rect.height.ToString("0")
|
|
+ (SameSpriteSource(sprite, species) ? ":species" : ":distinct");
|
|
}
|
|
catch
|
|
{
|
|
return "invalid";
|
|
}
|
|
}
|
|
|
|
private static bool LooksLikeEggSprite(Sprite sprite)
|
|
{
|
|
if (sprite == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
string n = sprite.name ?? "";
|
|
return n.IndexOf("egg", StringComparison.OrdinalIgnoreCase) >= 0;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static Sprite ForChronicleKind(ChronicleKind kind)
|
|
{
|
|
switch (kind)
|
|
{
|
|
case ChronicleKind.Death:
|
|
return FromUiIcon("iconDead") ?? FromUiIcon("iconSkulls");
|
|
case ChronicleKind.Kill:
|
|
return FromUiIcon("iconKills") ?? FromUiIcon("iconAttack");
|
|
case ChronicleKind.Lover:
|
|
return FromUiIcon("iconArrowLover");
|
|
case ChronicleKind.Friend:
|
|
return FromUiIcon("iconFavoriteStar");
|
|
case ChronicleKind.World:
|
|
return FromUiIcon("iconWar") ?? FromUiIcon("iconBooks");
|
|
case ChronicleKind.AgeChapter:
|
|
return FromUiIcon("iconAge") ?? FromUiIcon("iconClock");
|
|
default:
|
|
return FromUiIcon("iconClock");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Activity row icon from a task id, Beh* key, or milestone_* key.
|
|
/// </summary>
|
|
public static Sprite ForActivityKey(string key)
|
|
{
|
|
if (string.IsNullOrEmpty(key))
|
|
{
|
|
return Task() ?? FromUiIcon("iconClock");
|
|
}
|
|
|
|
string k = key.ToLowerInvariant();
|
|
if (k == "milestone_kill")
|
|
{
|
|
return ForChronicleKind(ChronicleKind.Kill);
|
|
}
|
|
|
|
if (k == "milestone_death")
|
|
{
|
|
return ForChronicleKind(ChronicleKind.Death);
|
|
}
|
|
|
|
if (k == "milestone_lover")
|
|
{
|
|
return ForChronicleKind(ChronicleKind.Lover);
|
|
}
|
|
|
|
if (k == "milestone_friend")
|
|
{
|
|
return ForChronicleKind(ChronicleKind.Friend);
|
|
}
|
|
|
|
if (ActivityStatusProse.TryParseTaskKey(key, out string statusId, out _))
|
|
{
|
|
return FromStatusId(statusId)
|
|
?? FromUiIcon("iconConfused")
|
|
?? Task()
|
|
?? FromUiIcon("iconClock");
|
|
}
|
|
|
|
if (ActivityHappinessProse.TryParseTaskKey(key, out string happinessId))
|
|
{
|
|
return FromHappinessId(happinessId)
|
|
?? FromUiIcon("iconHappy")
|
|
?? Task()
|
|
?? FromUiIcon("iconClock");
|
|
}
|
|
|
|
string verb = ActivityVerbMap.Resolve(key);
|
|
if (string.IsNullOrEmpty(verb))
|
|
{
|
|
verb = k;
|
|
}
|
|
|
|
switch (verb)
|
|
{
|
|
case "fight":
|
|
case "warrior":
|
|
return FromUiIcon("iconAttack") ?? FromUiIcon("iconKills") ?? Task();
|
|
case "hunt":
|
|
return FromUiIcon("iconKills") ?? FromUiIcon("iconAttack") ?? Task();
|
|
case "lover":
|
|
return FromUiIcon("iconArrowLover") ?? Task();
|
|
case "social":
|
|
case "cry":
|
|
case "swear":
|
|
return FromUiIcon("iconTalk") ?? FromUiIcon("iconSpeech") ?? FromUiIcon("iconSocial")
|
|
?? FromUiIcon("iconArrowLover") ?? Task();
|
|
case "eat":
|
|
return FromUiIcon("iconFood") ?? FromUiIcon("iconHunger") ?? Task();
|
|
case "sleep":
|
|
case "dream":
|
|
return FromUiIcon("iconSleep") ?? FromUiIcon("iconRest") ?? FromUiIcon("iconClock") ?? Task();
|
|
case "farm":
|
|
return FromUiIcon("iconFarm") ?? FromUiIcon("iconCrops") ?? FromUiIcon("iconWheat") ?? Task();
|
|
case "work":
|
|
case "haul":
|
|
return FromUiIcon("iconShowTasks") ?? FromUiIcon("iconHammer") ?? FromUiIcon("iconWalker") ?? Task();
|
|
case "fire":
|
|
case "ignite":
|
|
case "extinguish":
|
|
return FromUiIcon("iconFire") ?? FromUiIcon("iconLava") ?? Task();
|
|
case "flee":
|
|
return FromUiIcon("iconFlee") ?? FromUiIcon("iconRun") ?? FromUiIcon("iconDanger")
|
|
?? FromUiIcon("iconAttack") ?? Task();
|
|
case "play":
|
|
case "laugh":
|
|
case "sing":
|
|
return FromUiIcon("iconHappy") ?? FromUiIcon("iconFun") ?? FromUiIcon("iconMusic")
|
|
?? FromUiIcon("iconFavoriteStar") ?? Task();
|
|
case "read":
|
|
return FromUiIcon("iconBooks") ?? FromUiIcon("iconBook") ?? Task();
|
|
case "heal":
|
|
case "recharge":
|
|
return FromUiIcon("iconHealth") ?? FromUiIcon("iconHeal") ?? FromUiIcon("iconHeart") ?? Task();
|
|
case "pollinate":
|
|
return FromUiIcon("iconFlower") ?? FromUiIcon("iconBee") ?? FromUiIcon("iconNature") ?? Task();
|
|
case "fish":
|
|
return FromUiIcon("iconFish") ?? FromUiIcon("iconFishing") ?? FromUiIcon("iconWater") ?? Task();
|
|
case "trade":
|
|
return FromUiIcon("iconTrade") ?? FromUiIcon("iconCoin") ?? FromUiIcon("iconGold") ?? Task();
|
|
case "settle":
|
|
case "group":
|
|
return FromUiIcon("iconCity") ?? FromUiIcon("iconHome") ?? FromUiIcon("iconKingdom") ?? Task();
|
|
case "relieve":
|
|
return FromUiIcon("iconPoop") ?? FromUiIcon("iconToilet") ?? Task();
|
|
case "confused":
|
|
case "strange_urge":
|
|
case "possessed":
|
|
return FromUiIcon("iconConfused") ?? FromUiIcon("iconMadness") ?? FromUiIcon("iconPossessed")
|
|
?? FromUiIcon("iconQuestionMark") ?? Task();
|
|
case "steal":
|
|
case "loot":
|
|
return FromUiIcon("iconSteal") ?? FromUiIcon("iconLoot") ?? FromUiIcon("iconBag") ?? Task();
|
|
case "harvest_life":
|
|
case "gather_life":
|
|
return FromUiIcon("iconSoulHarvested") ?? FromUiIcon("iconSoul") ?? FromUiIcon("iconSkulls")
|
|
?? FromUiIcon("iconDead") ?? Task();
|
|
case "move":
|
|
return FromUiIcon("iconWalker") ?? FromUiIcon("iconShowTasks") ?? Task();
|
|
case "wait":
|
|
return FromUiIcon("iconClock") ?? Task();
|
|
default:
|
|
return Task() ?? FromUiIcon("iconClock");
|
|
}
|
|
}
|
|
|
|
public static Sprite ForDeathManner(DeathManner manner)
|
|
{
|
|
switch (manner)
|
|
{
|
|
case DeathManner.Slain:
|
|
return FromUiIcon("iconKills") ?? FromUiIcon("iconAttack") ?? ForChronicleKind(ChronicleKind.Death);
|
|
case DeathManner.OldAge:
|
|
return FromUiIcon("iconAge") ?? FromUiIcon("iconClock") ?? ForChronicleKind(ChronicleKind.Death);
|
|
case DeathManner.Starvation:
|
|
return FromUiIcon("iconHunger") ?? FromUiIcon("iconFood") ?? ForChronicleKind(ChronicleKind.Death);
|
|
case DeathManner.Drowned:
|
|
return FromUiIcon("iconRain") ?? FromUiIcon("iconWater") ?? ForChronicleKind(ChronicleKind.Death);
|
|
case DeathManner.Burned:
|
|
return FromUiIcon("iconFire") ?? FromUiIcon("iconLava") ?? ForChronicleKind(ChronicleKind.Death);
|
|
case DeathManner.Plague:
|
|
return FromUiIcon("iconPlague") ?? FromUiIcon("iconDisease") ?? ForChronicleKind(ChronicleKind.Death);
|
|
case DeathManner.Divine:
|
|
return FromUiIcon("iconDivineLight") ?? FromUiIcon("iconGodFinger") ?? ForChronicleKind(ChronicleKind.Death);
|
|
case DeathManner.Accident:
|
|
return FromUiIcon("iconBomb") ?? FromUiIcon("iconExplosion") ?? ForChronicleKind(ChronicleKind.Death);
|
|
default:
|
|
return ForChronicleKind(ChronicleKind.Death);
|
|
}
|
|
}
|
|
|
|
public static Sprite Favorite() =>
|
|
FromUiIcon("iconFavorite") ?? FromUiIcon("iconFavoriteStar") ?? FromUiIcon("iconStar");
|
|
|
|
public static Sprite ExpandHistory() =>
|
|
FromUiIcon("iconBooks") ?? FromUiIcon("iconList") ?? FromUiIcon("iconClock");
|
|
|
|
public static Sprite Age() => FromUiIcon("iconAge");
|
|
|
|
public static Sprite Level() => FromUiIcon("iconLevels");
|
|
|
|
public static Sprite Kills() => FromUiIcon("iconKills") ?? FromUiIcon("iconAttack");
|
|
|
|
public static Sprite Task() => FromUiIcon("iconShowTasks") ?? FromUiIcon("iconWalker");
|
|
|
|
public static Sprite SexMale() => FromUiIcon("IconMale");
|
|
|
|
public static Sprite SexFemale() => FromUiIcon("IconFemale");
|
|
|
|
public static Sprite FromTrait(ActorTrait trait)
|
|
{
|
|
if (trait == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
Sprite sprite = trait.getSprite();
|
|
if (sprite != null)
|
|
{
|
|
return sprite;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// fall through
|
|
}
|
|
|
|
try
|
|
{
|
|
if (!string.IsNullOrEmpty(trait.path_icon))
|
|
{
|
|
return SpriteTextureLoader.getSprite(trait.path_icon);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static Sprite FromStatusId(string statusId)
|
|
{
|
|
if (string.IsNullOrEmpty(statusId))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
StatusAsset asset = AssetManager.status?.get(statusId.Trim());
|
|
if (asset == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return FromStatus(asset);
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static Sprite FromStatus(StatusAsset status)
|
|
{
|
|
if (status == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
Sprite sprite = status.getSprite();
|
|
if (sprite != null)
|
|
{
|
|
return sprite;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// fall through
|
|
}
|
|
|
|
try
|
|
{
|
|
if (!string.IsNullOrEmpty(status.path_icon))
|
|
{
|
|
return SpriteTextureLoader.getSprite(status.path_icon);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static Sprite FromHappinessId(string effectId)
|
|
{
|
|
if (string.IsNullOrEmpty(effectId))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
HappinessAsset asset = AssetManager.happiness_library?.get(effectId.Trim());
|
|
if (asset == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return FromHappiness(asset);
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static Sprite FromHappiness(HappinessAsset asset)
|
|
{
|
|
if (asset == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
Sprite sprite = asset.getSprite();
|
|
if (sprite != null)
|
|
{
|
|
return sprite;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// fall through
|
|
}
|
|
|
|
try
|
|
{
|
|
if (!string.IsNullOrEmpty(asset.path_icon))
|
|
{
|
|
return SpriteTextureLoader.getSprite(asset.path_icon);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static void Apply(UnityEngine.UI.Image image, Sprite sprite, bool preserveAspect = true)
|
|
{
|
|
if (image == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
image.sprite = sprite;
|
|
image.enabled = sprite != null;
|
|
image.preserveAspect = preserveAspect;
|
|
image.color = Color.white;
|
|
}
|
|
}
|