I think easiest way to explain how the backgrounds are handled is by posting the script itself, as I have no idea how the process used is called
Im sure u will see it in a second. Backgrounds have names in CAPITAL. If it is not clear from script, I will ask the person who wrote it (edit: already did, just waiting for a reply)
Code: Select all
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using DaggerfallConnect.Arena2;
using DaggerfallConnect.Utility;
using DaggerfallWorkshop;
using DaggerfallWorkshop.Game;
using DaggerfallWorkshop.Game.Entity;
using DaggerfallWorkshop.Game.MagicAndEffects.MagicEffects;
using DaggerfallWorkshop.Game.Serialization;
using DaggerfallWorkshop.Game.UserInterface;
using DaggerfallWorkshop.Game.UserInterfaceWindows;
using DaggerfallWorkshop.Game.Utility.ModSupport;
using DaggerfallWorkshop.Game.Utility.ModSupport.ModSettings;
using DaggerfallWorkshop.Game.Weather;
using DaggerfallWorkshop.Utility;
using DaggerfallWorkshop.Utility.AssetInjection;
using UnityEngine;
namespace SpellcastStudios.TODBackgrounds
{
public class TODBackgrounds : MonoBehaviour
{
// MOD SETTINGS
static TODBackgrounds instance;
public static TODBackgrounds Instance { get { return instance ?? (instance = FindObjectOfType<TODBackgrounds>()); } }
public static Mod mod { get; private set; }
[SerializeField] private bool dreamGeoBackgrounds = true;
[SerializeField] private bool cityBackgrounds = true;
[SerializeField] private bool extraInteriors = true;
[SerializeField] private bool mainStoryDungeons = true;
[SerializeField] private bool dungeonTypes = true;
private string dreamGeoBG = "";
private int locationBG = 0;
private int locationPlayer = 0;
PlayerGPS playerGPS = GameManager.Instance.PlayerGPS;
private void LoadSettings()
{
var settings = mod.GetSettings();
dreamGeoBackgrounds = settings.GetBool("Options", "DreamGeoBackgrounds");
cityBackgrounds = settings.GetBool("Options", "CityBackgrounds");
extraInteriors = settings.GetBool("Options", "ExtraInteriors");
mainStoryDungeons = settings.GetBool("Options", "MainStoryDungeons");
dungeonTypes = settings.GetBool("Options", "DungeonTypes");
if (dreamGeoBackgrounds == false) cityBackgrounds = false;
}
//
void Update()
{
// Track player by storing the Map location ID
locationPlayer = (int)GameManager.Instance.PlayerGPS.CurrentMapID;
// Geographic Backgrounds DREAM version (only used when GeoBck is Enabled in DFU Settings)
if ((DaggerfallUnity.Settings.EnableGeographicBackgrounds) && (instance.dreamGeoBackgrounds))
{
// Check if the player is in a City (True only when in a Large Settlement ID=0 ; IsPlayerInTown check is nececeary!)
bool InCity = false;
bool CityBG = instance.cityBackgrounds;
if ((playerGPS.IsPlayerInTown(true)) && ((int)GameManager.Instance.PlayerGPS.CurrentLocationType == 0))
InCity = true;
else InCity = false;
// Set Backgrounds based on climate zones or city climate type (Temperate=01, Desert=02, Mountain=03)
switch (playerGPS.CurrentClimateIndex)
{
// Desert Regions
case (int)MapsFile.Climates.Desert:
case (int)MapsFile.Climates.Desert2:
{ if ((InCity) && (CityBG)) dreamGeoBG = "TOWN02.IMG"; else dreamGeoBG = "SCBG01I0.IMG"; } break;
//Subtropical Regions
case (int)MapsFile.Climates.Subtropical:
{ if ((InCity) && (CityBG)) dreamGeoBG = "TOWN02.IMG"; else dreamGeoBG = "SCBG06I0.IMG"; } break;
// Mountain Regions
case (int)MapsFile.Climates.Mountain:
case (int)MapsFile.Climates.MountainWoods:
{ if ((InCity) && (CityBG)) dreamGeoBG = "TOWN03.IMG"; else dreamGeoBG = "SCBG02I0.IMG"; } break;
// Rainforest Regions
case (int)MapsFile.Climates.Rainforest:
{ if ((InCity) && (CityBG)) dreamGeoBG = "TOWN01.IMG"; else dreamGeoBG = "SCBG05I0.IMG"; } break;
// Ocean Region (Player's Ship Exterior or Iliac Bay Region when player is at open sea)
case (int)MapsFile.Climates.Ocean:
{ if (((int)GameManager.Instance.PlayerGPS.CurrentLocationType == 14) && (GameManager.Instance.PlayerGPS.IsPlayerInLocationRect))
dreamGeoBG = "SHIP.IMG"; else dreamGeoBG = "OCEAN.IMG"; } break;
// Swamp Region
case (int)MapsFile.Climates.Swamp:
{ if ((InCity) && (CityBG)) dreamGeoBG = "TOWN02.IMG"; else dreamGeoBG = "SCBG07I0.IMG"; } break;
// Temperate Woodlands Regions
case (int)MapsFile.Climates.Woodlands:
{ if ((InCity) && (CityBG)) dreamGeoBG = "TOWN01.IMG"; else dreamGeoBG = "SCBG04I0.IMG"; } break;
// Haunted Woodlands and Hill Regions
case (int)MapsFile.Climates.HauntedWoodlands:
{ if ((InCity) && (CityBG)) dreamGeoBG = "TOWN03.IMG"; else dreamGeoBG = "SCBG00I0.IMG"; } break;
}
// Alternative Graveyard Background
if (((int)GameManager.Instance.PlayerGPS.CurrentLocationType == 12) && (GameManager.Instance.PlayerGPS.IsPlayerInLocationRect))
dreamGeoBG = "CEMETERY.IMG";
}
}
private class BackgroundScreen
{
//TODO: Use reflection instead of hardcoding it here
const int paperDollWidth = 110;
const int paperDollHeight = 184;
readonly DFSize backgroundFullSize = new DFSize(125, 198);
readonly Rect backgroundSubRect = new Rect(8, 7, paperDollWidth, paperDollHeight);
public UserInterfaceWindow window;
public PaperDoll paperDoll;
public Panel backgroundPanel;
public Texture2D lastSetBackground;
public List<string> lastTags;
public BackgroundScreen(UserInterfaceWindow window, PaperDoll paperDoll)
{
UpdateWindow(window, paperDoll);
}
public void UpdateWindow(UserInterfaceWindow window, PaperDoll paperDoll)
{
this.window = window;
this.paperDoll = paperDoll;
if (paperDoll != null)
this.backgroundPanel = (Panel)GetFieldValue("backgroundPanel", paperDoll);
}
private bool RequiresChange()
{
if (lastTags == null)
return false;
var newTags = GetTags();
for (int i = 0; i < lastTags.Count; i++)
{
if (newTags[i] != lastTags[i])
return true;
}
return false;
}
private List<string> GetTags()
{
string custom = "";
bool isPlayerInSnowyRegion = false;
switch (instance.playerGPS.CurrentClimateIndex)
{
case (int)MapsFile.Climates.Woodlands:
case (int)MapsFile.Climates.HauntedWoodlands:
case (int)MapsFile.Climates.MountainWoods:
case (int)MapsFile.Climates.Mountain:
isPlayerInSnowyRegion = true;
break;
}
if (PlayerIsInFactionBuilding(templeFactions))
custom = "_TEMPLE";
else if (GameManager.Instance.PlayerEnterExit.IsPlayerInsideDungeonCastle)
custom = "_CASTLE";
else if (GameManager.Instance.PlayerEnterExit.IsPlayerInsideDungeon)
custom = "_DUNGEON";
else if ((GameManager.Instance.PlayerEnterExit.IsPlayerInsideOpenShop) && (GameManager.Instance.IsPlayerInside))
custom = "_OPENSHOP";
else if ((GameManager.Instance.PlayerEnterExit.IsPlayerInsideTavern) && (GameManager.Instance.IsPlayerInside))
custom = "_TAVERN";
else if (GameManager.Instance.PlayerEnterExit.IsPlayerInside)
custom = "_BUILDING";
if ((instance.extraInteriors) && (GameManager.Instance.PlayerEnterExit.IsPlayerInside))
{
// Backgrounds for Guilds and Knightly Orders
if (PlayerIsInFactionBuilding(knightFactions)) custom = "_GUILDHALL"; // Fighters Guild & All Knightly Orders
if (PlayerIsInFactionBuilding(mageFactions)) custom = "_MAGEHALL"; // Mages Guild Unique BG
if (PlayerIsInFactionBuilding(crimeFactions)) custom = "_UNDERWORLD"; // Thieves Guild & Dark Brotherhood
// Extra Building Interior Backgrounds
switch ((int)GameManager.Instance.PlayerEnterExit.BuildingType)
{
case 0: custom = "_ALCHEMIST"; break; // Alchemist
case 2: custom = "_BLACKSMITH"; break; // Armor Shop
case 3: custom = "_BANK"; break; // Bank
case 5: custom = "_LIBRARY"; break; // Library
case 10: custom = "_LIBRARY"; break; // Bookseller
case 13: custom = "_BLACKSMITH"; break; // Weapon Shop
case 16: custom = "_PALACE"; break; // Non-Unique City Palace
}
// Player's Ship Interior (Exterior of the ship is shared with the ocean climate background)
if ((int)GameManager.Instance.PlayerGPS.CurrentLocationType == 14 ) custom = "_SHIP";
// Player House (Poor Home)
//if ((int)GameManager.Instance.PlayerGPS.CurrentLocationType == 11 ) custom = "_HOUSE";
// Player House (Wealthy Home)
//if ((int)GameManager.Instance.PlayerGPS.CurrentLocationType == 8 ) custom = "_MANOR";
}
// DREAM Dungeon Backgrounds
if (GameManager.Instance.PlayerEnterExit.IsPlayerInsideDungeon)
{
// New Backgrounds based on Dungeon Type
if (instance.dungeonTypes) switch ((int)GameManager.Instance.PlayerGPS.CurrentLocation.MapTableData.DungeonType)
{
case 0: custom = "_DUNGEON"; break; // Crypt
case 1: custom = "_ORC"; break; // Orc Stronghold
case 2: custom = "_HUMAN"; break; // Human Stronghold
case 3: custom = "_PRISON"; break; // Prison
case 4: custom = "_DTEMPLE"; break; // Desecrated Temple
case 5: custom = "_MINE"; break; // Mine
case 6: custom = "_CAVE"; break; // Natural Cave
case 7: custom = "_COVEN"; break; // Coven
case 8: custom = "_VAMPIRE"; break; // Vampire Haunt
case 9: custom = "_LABORATORY"; break; // Laboratory
case 10: custom = "_HARPY"; break; // Harpy Nest
case 11: custom = "_RCASTLE"; break; // Ruined Castle
case 12: custom = "_SPIDER"; break; // Spider Nest
case 13: custom = "_GIANT"; break; // Giant Stronghold
case 14: custom = "_DRAGON"; break; // Dragon's Den
case 15: custom = "_BARBARIAN"; break; // Barbarian Stronghold
case 16: custom = "_VOLCANIC"; break; // Volcanic Caves
case 17: custom = "_SCORPION"; break; // Scorpion Nest
case 18: custom = "_CEMETERY"; break; // Cemetery
}
// Main Story Unique Dungeon Backgrounds
if (instance.mainStoryDungeons) switch (GameManager.Instance.PlayerGPS.CurrentMapID)
{
case 187853213: custom = "_DUNGEON"; break; // Daggerfall/Privateer's Hold
case 630439035: custom = "_WAYREST"; break; // Wayrest/Wayrest
case 1291010263: custom = "_DAGGERFALL"; break; // Daggerfall/Daggerfall
case 6634853: custom = "_SENTINEL"; break; // Sentinel/Sentinel
case 19021260: custom = "_ORSINIUM"; break; // Orsinium Area/Orsinium
case 728811286: custom = "_SHEDUNGENT"; break; // Wrothgarian Mountains/Shedungent
case 701948302: custom = "_SBARROW"; break; // Dragontail Mountains/Scourg Barrow
case 83032363: custom = "_WOODBORNE"; break; // Wayrest/Woodborne Hall
case 1001: custom = "_CRUX"; break; // High Rock sea coast/Mantellan Crux
case 207828842: custom = "_TOMB"; break; // Menevia/Lysandus' Tomb
case 9570447: custom = "_NECRO"; break; // Daggerfall/Castle Necromoghan
case 2352284: custom = "_TRISTORE"; break; // Betony/Tristore Laboratory
case 336619236: custom = "_BLADES"; break; // Ykalon/Castle Llugwych
case 43196334: custom = "_TOWER"; break; // Isle of Balfiera/Direnni Tower
}
}
string weather = "";
var weatherType = GameManager.Instance.WeatherManager.PlayerWeather.WeatherType;
if ((DaggerfallUnity.Instance.WorldTime.Now.SeasonName == "Winter") && isPlayerInSnowyRegion)
weather = "_WINTER";
else if (weatherType == WeatherType.Rain || weatherType == WeatherType.Rain_Normal)
weather = "_RAIN";
else if (weatherType == WeatherType.Thunder)
weather = "_STORM";
else if (weatherType == WeatherType.Fog)
weather = "_FOG";
else if (weatherType == WeatherType.Overcast)
weather = "_OVERCAST";
string time = "";
if (DaggerfallUnity.Instance.WorldTime.DaggerfallDateTime.IsNight)
time = "_NIGHT";
return new List<string> { custom, weather, time };
}
public void UpdateCurrentTexture()
{
string vanillaName = null;
//Apply builtin racial effect names
vanillaName = TryGetVampirismTextureName();
vanillaName = TryGetLycanthropyTextureName();
//Get vanilla image string
if (vanillaName == null)
{
vanillaName = (string)RunMethod("GetPaperDollBackground", paperDoll, GameManager.Instance.PlayerEntity);
}
if (vanillaName == null)
{
Debug.LogError("Could not find vanilla background");
return;
}
string modVanillaName = vanillaName;
// Check if the Player is in the Beast Form and if not use Normal Backgrounds (IMPORTANT PART OF THE CODE)
if (GameManager.Instance.PlayerEntity.IsInBeastForm == false)
{
if (GameManager.Instance.IsPlayerInside) modVanillaName = "interior.IMG";
else if (instance.dreamGeoBackgrounds) modVanillaName = instance.dreamGeoBG;
}
//
Texture2D texture = TryFindTexture(modVanillaName, "");
var tags = GetTags();
for (int i = 0; i < tags.Count; i++)
{
for (int k = 0; k < tags.Count; k++)
{
if (i == k)
continue;
string tagi = tags[i];
string tagk = tags[k];
Debug.Log(string.Format("{0} {1}", tagi, tagk));
if (tagi == "" && tagk == "")
continue;
var nt = TryFindTexture(modVanillaName, tagi + tagk);
if (nt != null)
texture = nt;
}
}
if (texture == null)
texture = TryFindTexture(modVanillaName, tags[0]);
//Failed to find texture, so pick vanilla texture
if (texture == null)
texture = ImageReader.GetTexture(vanillaName, 0, 0, false);
//Apply to background panel
backgroundPanel.BackgroundTexture = ImageReader.GetSubTexture(texture, backgroundSubRect, backgroundFullSize);
backgroundPanel.Size = new Vector2(paperDollWidth, paperDollHeight);
//Save last update
lastSetBackground = backgroundPanel.BackgroundTexture;
lastTags = tags;
}
private Texture2D TryFindTexture(string vanillaName, string postfix)
{
//Note removal of IMG. this is to get around a DFU bug
var newTexture = Path.GetFileNameWithoutExtension(vanillaName) + postfix + Path.GetExtension(vanillaName);
Debug.Log("Trying to find " + newTexture);
Texture2D output;
if (TextureReplacement.TryImportImage(newTexture, false, out output))
return output;
return null;
}
//Hardcoded grab of varmpirism background
private string TryGetVampirismTextureName()
{
VampirismEffect racialOverride = GameManager.Instance.PlayerEffectManager.GetRacialOverrideEffect() as VampirismEffect;
if (racialOverride == null)
return null;
return "SCBG08I0.IMG";
}
//Hardcoded grab of lycanthropy background
private string TryGetLycanthropyTextureName()
{
const string werewolfBackground = "WOLF00I0.IMG";
const string wereboarBackground = "BOAR00I0.IMG";
LycanthropyEffect racialOverride = GameManager.Instance.PlayerEffectManager.GetRacialOverrideEffect() as LycanthropyEffect;
if (racialOverride == null)
return null;
// Do nothing if not transformed
if (!racialOverride.IsTransformed)
return null;
// Get source texture based on lycanthropy type
string filename;
switch (racialOverride.InfectionType)
{
case LycanthropyTypes.Werewolf:
filename = werewolfBackground;
break;
case LycanthropyTypes.Wereboar:
filename = wereboarBackground;
break;
default:
return null;
}
return filename;
}
public void Update()
{
if (window == null || !window.Enabled)
return;
if (backgroundPanel.BackgroundTexture != lastSetBackground || RequiresChange())
UpdateCurrentTexture();
}
private static bool PlayerIsInFactionBuilding(List<FactionFile.FactionIDs> factionIds)//, DaggerfallInterior interior)
{
var interior = GameManager.Instance.PlayerEnterExit.Interior;
if (interior == null)
return false;
return factionIds.Contains((FactionFile.FactionIDs)interior.BuildingData.FactionId);
}
}
private BackgroundScreen inventoryBackgroundScreen;
private BackgroundScreen characterSheetBackgroundScreen;
private BackgroundScreen tradeBackgroundScreen;
private UserInterfaceManager uiManager;
public static List<FactionFile.FactionIDs> templeFactions;
public static List<FactionFile.FactionIDs> knightFactions;
public static List<FactionFile.FactionIDs> mageFactions;
public static List<FactionFile.FactionIDs> crimeFactions;
public void Awake()
{
if (mod != null) LoadSettings();
templeFactions = new List<FactionFile.FactionIDs>
{
FactionFile.FactionIDs.The_Akatosh_Chantry,
FactionFile.FactionIDs.Akatosh,
FactionFile.FactionIDs.The_Order_of_Arkay,
FactionFile.FactionIDs.Arkay,
FactionFile.FactionIDs.The_House_of_Dibella,
FactionFile.FactionIDs.Dibella,
FactionFile.FactionIDs.The_Schools_of_Julianos,
FactionFile.FactionIDs.Julianos,
FactionFile.FactionIDs.The_Temple_of_Kynareth,
FactionFile.FactionIDs.Kynareth,
FactionFile.FactionIDs.The_Benevolence_of_Mara,
FactionFile.FactionIDs.Mara,
FactionFile.FactionIDs.The_Temple_of_Stendarr,
FactionFile.FactionIDs.Stendarr,
FactionFile.FactionIDs.The_Resolution_of_Zen,
FactionFile.FactionIDs.Zen
};
mageFactions = new List<FactionFile.FactionIDs>
{
FactionFile.FactionIDs.The_Mages_Guild
};
crimeFactions = new List<FactionFile.FactionIDs>
{
FactionFile.FactionIDs.The_Thieves_Guild,
FactionFile.FactionIDs.The_Dark_Brotherhood
};
knightFactions = new List<FactionFile.FactionIDs>
{
FactionFile.FactionIDs.The_Fighters_Guild,
FactionFile.FactionIDs.The_Host_of_the_Horn,
FactionFile.FactionIDs.The_Knights_of_the_Dragon,
FactionFile.FactionIDs.The_Knights_of_the_Flame,
FactionFile.FactionIDs.The_Knights_of_the_Hawk,
FactionFile.FactionIDs.The_Knights_of_the_Owl,
FactionFile.FactionIDs.The_Knights_of_the_Rose,
FactionFile.FactionIDs.The_Knights_of_the_Wheel,
FactionFile.FactionIDs.The_Order_of_the_Candle,
FactionFile.FactionIDs.The_Order_of_the_Raven,
FactionFile.FactionIDs.The_Order_of_the_Scarab
};
}
[Invoke(StateManager.StateTypes.Start, 0)]
public static void Init(InitParams initParams)
{
mod = initParams.Mod;
var go = new GameObject(mod.Title);
instance = go.AddComponent<TODBackgrounds>();
mod.IsReady = true;
}
private void Start()
{
var inventoryWindow = (DaggerfallInventoryWindow)GetFieldValue("dfInventoryWindow", DaggerfallUI.Instance);
var inventoryPaperDoll = (PaperDoll)GetFieldValue("paperDoll", inventoryWindow);
inventoryBackgroundScreen = new BackgroundScreen(inventoryWindow, inventoryPaperDoll);
var characterSheetWindow = (DaggerfallCharacterSheetWindow)GetFieldValue("dfCharacterSheetWindow", DaggerfallUI.Instance);
var characterSheetPaperDoll = (PaperDoll)GetFieldValue("characterPortrait", characterSheetWindow);
characterSheetBackgroundScreen = new BackgroundScreen(characterSheetWindow, characterSheetPaperDoll);
uiManager = (UserInterfaceManager)GetFieldValue("uiManager", DaggerfallUI.Instance);
uiManager.OnWindowChange += OnWindowChange;
//Create container for trade window
tradeBackgroundScreen = new BackgroundScreen(null, null);
}
//Detect when window changes and see if it's the trade menu
// IMPORTANT: This part of the code needs to be like this in order to add compatibility with mods that use dll files !!!
private void OnWindowChange(object sender, System.EventArgs e)
{
var tradeWindow = uiManager.TopWindow as DaggerfallTradeWindow;
var inventoryWindow = uiManager.TopWindow as DaggerfallInventoryWindow;
var characterSheetWindow = uiManager.TopWindow as DaggerfallCharacterSheetWindow;
if ((tradeWindow != null) && (tradeBackgroundScreen.window != tradeWindow))
{
var tradePaperDoll = (PaperDoll)GetFieldValue("paperDoll", tradeWindow);
tradeBackgroundScreen.UpdateWindow(tradeWindow, tradePaperDoll);
tradeBackgroundScreen.UpdateCurrentTexture();
}
// CAUTION: These functions add compatibility between DREAM GeoBG system, other mods and DFU Fast Travel system
if (((inventoryWindow != null) && (inventoryBackgroundScreen.window != inventoryWindow) && (tradeWindow == null)) || (locationBG != locationPlayer))
{
var inventoryPaperDoll = (PaperDoll)GetFieldValue("paperDoll", inventoryWindow);
inventoryBackgroundScreen.UpdateWindow(inventoryWindow, inventoryPaperDoll);
inventoryBackgroundScreen.UpdateCurrentTexture();
}
if (((characterSheetWindow != null) && (characterSheetBackgroundScreen.window != characterSheetWindow) && (tradeWindow == null)) || (locationBG != locationPlayer))
{
var characterSheetPaperDoll = (PaperDoll)GetFieldValue("characterPortrait", characterSheetWindow);
characterSheetBackgroundScreen.UpdateWindow(characterSheetWindow, characterSheetPaperDoll);
characterSheetBackgroundScreen.UpdateCurrentTexture();
}
// Save the last map location ID in which inv or char sheet is opened (used by functions above to fix fast travel issue)
if (locationBG != locationPlayer) locationBG = locationPlayer;
}
private void LateUpdate()
{
inventoryBackgroundScreen.Update();
characterSheetBackgroundScreen.Update();
tradeBackgroundScreen.Update();
}
private static object RunMethod(string method, object fromObject, params object[] parameters)
{
var flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
return (fromObject?.GetType()?.GetMethod(method, flags))?.Invoke(fromObject, parameters);
}
private static object GetFieldValue(string field, object fromObject)
{
var flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
return (fromObject?.GetType()?.GetField(field, flags))?.GetValue(fromObject);
}
}
}