Baldurwind is a mechanical total conversion for OpenMW. More features are planned, but in essence, Baldurwind's goal is to convert Morrowind into turn-based, party RPG!
The idea was inspired due to the sheer popularity of Baldur's Gate 3 (obviously) and some of my longstanding grievances with the combat system. There's not enough audiovisual feedback in typical Morrowind combat, so instead of removing hit chance (as I did in another mod), I designed some mechanics which enable the game to (in my opinion) more properly lean into that idea.
Beta Icons Restored and Reimagined provides an alternate glimpse into what might have been. It's based on the Beta skill icons, which you can find here for reference. However, the beta icons had problems of their own. For one, the rune backgrounds used obfuscated the art a lot for spell icons. Remember, these are displayed at 32x32px for the big spell icons and 16x16px for the small ones. Thus, I took the color palettes of the beta skill icons and extended them to the spell, attribute, and dynamic stat icons. The result is a full suite of character icon replacements (293 in total), best suited for use with Monochrome UI. See below for a preview of all the new icons!
CHIM 2090 is a modernized interpretation of the MWScript classic, CHIM Movement and Stamina. It has a hard requirement for OpenMW 0.50, due to extensive use of its onHit feature.
At its core, CHIM is a highly advanced damage scaing mod, taking many factors into account. There are four core systems in CHIM:
CHIM Climbing is my own long-awaited attempt at porting the movement mechanics from the classic CHIM mod I worked on many years ago into OpenMW-Lua. The climbing module is strictly related to, well, climbing!
This first version focuses exclusively on the concept of mantling, You can now climb and clamber over surfaces which you wouldn't have previously had access to.
The next major release of this mod will include a full Daggerfall-style climbing system, as was implemented in the original mod, but far less jank.
Uncle Crassius Doesn't Need A Map. Why Should You?
Crassified Navigation is a simple mod, but meant to be as cursed as possible. It's effectively an OpenMW port of the mod Get Lost, spawned by a user request in MMC. But it comes with an extra cursed twist, being that as much of the operative Lua code as possible is embedded directly into the mod's omwaddon.
Nevertheless, it works just as well and easily as anything else.
At a certain point in the mod's questline, the player might get into a fight with Fargoth. If they do, and they run away, Fargoth will send assassins after them until they come back to finish the job.
Fargoth's assassins can be of any (vanilla) race or gender, and will copy any random local NPC's inventory. You probably won't notice them! Until it's too late . . .
H3lp Yours3lf is a collection of scripting modules built for openmw. It contains multiple individual interfaces which authors can use to improve performance and ergonomics in their OpenMW-Lua scripts. Additionally, H3lp Yours3lf includes some helper functions for more exotic behaviors, such as detecting the context in which a given script is running.
Hawk3ye was inspired by Zoom To See. However, when eyeing it for my own personal setup, I found the mod was broken and wasn't really very satisfying overall. So, I refactored it for greater reliability and mod compatibility.
Two simple keybinds are added - Toggle Zoom and Hold Zoom. The recommended defaults are Z and Mouse Wheel Click, respectively. Zooming in uses a smooth, exponential curve to decrease/increase the camera FOV as needed.
If you just want to install the mod, read the Installation and Usage sections. For Lua scripters, view the table of contents for more details.
Paste the location you extracted it to into the form below, hit Generate Config, and paste the result into openmw.cfg.
Where would you like to install this mod?
# Hawk3ye Data Directories
data=C:/Games/OpenMW-Mods/Hawk3ye/.
# Hawk3ye Content Files
content=Hawk3ye.esp
Usage
Warning! Hawk3ye needs just a bit of setup before it will work in your game. Make sure to hit the Escape key, go into Settings -> Scripts -> Hawk3ye, and set bindings for hold/toggle as needed. If you want, you can also set a fixed duration for the Zoom to reach its lowest FOV, and a target FOV when zooming.
Hawk3ye for Modders
Hawk3ye uses events to trigger zooming, and takes advantage of OpenMW's more advanced input features to allow greater compatibility/interoperability. There also is an interface provided to check zooming state and better inform eventHandlers listening for Hawk3ye's zoom event.
Events
Hawk3ye uses one core event to control zooming - Hawk3yeToggle. It is provided a single argument which is a ZoomType (enum available in Hawk3ye's interface). To interrupt or prevent zooming, register a handler for this event and return false from that function.
ZoomType Specification
--- Used for determining what kind of state change occurred. This enum can also be re-used--- When emitting your own Hawk3yeToggle events.---@enum ZoomTypelocal ZoomState = { RESET = 1, DISABLE = 2, ENABLE = 3,}
Interface
Hawk3ye also offers an interface in Player scope, I.Hawk3ye.
The various fields available are as follows:
---@return boolean isZoomed Whether or not the zoom action is currently engaged. It cannot be overriddenisZoomed = function() return zoomActiveend,---@return boolean whether or not the player is allowed to zoom. Any menu being open prevents zoom, in addition to the world being paused.CanZoom = canZoom,--- A read-only copy of the ZoomType enum for use in eventHandlers---@type ZoomTypeZoomStates = ReadOnlyStates,
Settings
Hawk3ye's main settings group is a Player scoped storage section called SettingsHawk3ye. It contains the following keys and values:
1.enabled - Checkbox - Whether to use the mod's functions at all.
1.zoom_fov_degrees - Number - Target FOV when zooming. The maximum value is always 1 less than the FOV configured in the settings menu. Default is 42.
1.zoom_time - Number - Actual duration of the zoom effect from start to finish. Up to 2.5, default 0.75.
1.Hawk3yeToggle - inputBinding - Button/key used to toggle zoom. Suggested default is Z.
1.Hawk3yeHold - inputBinding - Button/key used to hold zoom. Suggested default is Mouse Wheel.
Credits
All code in this repository was generated by Dave Corley under the GPL3 License.
Please enjoy my content, hack away as you please, and respect the freedoms of your fellow creators in the meantime.
Luamics is a simple global script that adds a chance for any chest encountered in-game to be replaced by a mimic chest. Mimic chests will retain their original loot, and spawn at a rate of 5%. Chests which are scripted, owned, or have trader in their record id are ineligible to be converted to mimics, so the script should be relatively safe. Should be, that is.
Monochrome UI supplement is, well... a monochrome UI supplement.
Specifically, this mod replaces some of the built-in textures openmw provides and changes the default text color of Lua widgets. When Monochrome UI first released, there were no Lua widgets and replacing these built-in textures required invasive (and unsafe) modifications to one's game. Now, we can replace both much more easily.
Brought to You by the Love and Passion of Modding-OpenMW.com
Your music, just the way you want it. No compromises, no bullshit, with a focus on scalability and extreme attention to performance optimization.
I simply decided existing music solutions were not good enough after OpenMW's music system was dehardcoded. The underlying playlist mechanism used by OpenMW is very powerful - but due to a disagreement about playlist conflict resolution, the public API for music playlists was removed. Since that time, version 0.49 has straightforwardly not been living up to its true potential.
S3maphore fixes that. Why call it S3maphore?
In computing, a semaphore is a synchronization primitive designed to allow concurrent access to systems which may really only have one consumer at a time. This definition and application both date back to mid-nineteenth century, when the semaphore was first developed as a safety measure for signalling train drivers on railways.
This one is no different - S3maphore is a full replacement for OpenMW's builtin music handling that can be configured or permutated in any way you can imagine.
If you imagine one that it doesn't offer, let's fix that together.
S3maphore works through a system of playlists, similarly to Dynamic Music or MUSE. However, unlike both, S3maphore is an open-ended system, which allows playlist creators to define any conditions under which a playlist should run. For more details, view the Playlist Creation section.
If you just want to install the mod, stop here. For Lua scripters or playlist developers, view the table of contents for more details.
Paste the location you extracted it to into the form below, hit Generate Config, and paste the result into openmw.cfg.
Where would you like to install this mod?
# S3maphore Data Directories
# Required
data=C:/Games/OpenMW-Mods/S3maphore/00 Core
# Recommended
data=C:/Games/OpenMW-Mods/S3maphore/01 Tamriel Rebuilt Playlists
data=C:/Games/OpenMW-Mods/S3maphore/02 Project Cyrodiil Playlists
data=C:/Games/OpenMW-Mods/S3maphore/03 Muse Expansion Playlists
data=C:/Games/OpenMW-Mods/S3maphore/06 Songbook of the North
data=C:/Games/OpenMW-Mods/S3maphore/10 Inns and Taverns
# Starwind only!
data=C:/Games/OpenMW-Mods/S3maphore/07 Starwind Playlists
# S3maphore Content Files
content=S3maphore.esp
Compatibility With Other Music Mods
Dynamic Music and MUSE soundbanks are not natively supported. However, you can convert either to a S3maphore playlist in minutes by using this document.
Any mods which simply add to or replace music Morrowind already provides (EG, in the Explore, Special, or Battle folders) are natively compatible with zero extra work.
S3maphore ships a set of BAIN modules with Playlist Arrays which can be used in conjunction with tracks provided by other mods. Where appropriate, these modules are folder-based and will allow you to extend them yourself.
Make sure to install the playlist files and tracks for any S3maphore playlist arrays you install. In the settings menu, you can toggle S3maphore debug messages and the onscreen track/playlist name display.
All S3maphore playlists may ship a unique l10n context with the appropriate playlist/name strings for your preferred language. Presently, only English is supported, but translations are greatly welcomed!
Also in the settings menu, you can scroll through registered playlists and manually enable/disable them completely.
TIP: If you can't toggle a playlist on or off, that means S3maphore couldn't find any of the tracks this playlist has registered in your game.
Finally, pressing F8 will always skip the current track if one is playing - if the shift key is held when pressing F8, it will toggle music playback on or off entirely.
S3maphore for Modders
As mentioned before, S3maphore is an open-ended system for playlist management. It is so open-ended, in fact, that you do not need to use S3maphore's built-in playlist management at all. Other playlist-related mods or plugins could easily be built on top of S3maphore's internal interface and provided events. This section of the docs will describe how to make your own playlists and interact with S3maphore's public methods and mechanisms.
Playlist Creation
S3maphore's playlists are similar, but not identical, to existing solutions. There are some key differences, however:
Playlist Specification
S3maphore playlist files are known as Playlist Arrays, because that's what they are. Every playlist file can return any number of playlists inside of table it sends back out. Every playlist must have both a string id and a priority field. id is the name of the playlist which is used as a table key, and priority is a number between 1 and 1000 indicating when the playlist should override others or be overridden by others. More on playlist priority below. Optional fields include:
tracks - string[] - List of VFS paths to tracks to play. If using MUSE or Dynamic Music soundbanks as a base, the paths can be copied directly.
active - boolean - If false, this playlist will never be selected for playback. Defaults to true. See below for how to turn it on, if it's not actually enabled.
randomize - boolean - Whether to play tracks from this playlist in the actual written order or randomly. Defaults to false.
cycleTracks - boolean - Whether to continue repeating the playlist after it has finished. Defaults to true.
playOneTrack - boolean - If true, the playlist will only play a single track and then deactivate itself. It must be reactivated using either the setPlaylistActive event or interface function in I.S3maphore. Defaults to false.
isValidCallback - function(playback: Playback): boolean - A function which returns true/false to indicate whether or not a given playlist should run in this specific context. Only optional when using PlaylistPriority.Never
fadeOut - number - optional duration for the fadeOut time between tracks in a playlist.
silenceBetweenTracks - PlaylistSilenceParams - Parameters for determining how long, if at all, fake silence tracks are used in a playlist. See below for a more detailed description of the PlaylistSilenceParams type.
interruptMode - InterruptMode - This field determines whether or not a playlist may be interrupted by another, based on the archetypes used by vanilla playlists. Valid values are INTERRUPT.Me, INTERRUPT.Other, and INTERRUPT.Never.
fallback - PlaylistFallback - Selection of fallback tracks and playlists which can be used alongside this playlist. See below for details of the PlaylistFallback type.
exclusions - S3maphorePlaylistExclusions - Selection of tracks and sub-directories which will NOT be played by this particular list
S3maphore replaces OpenMW's builtin music script almost in its entirety. This means that conflicts between the two are mostly-impossible and limitations such as needing to manually set/know the duration of specific tracks is no longer necessary.
S3maphore playlists do not have strictly defined rules, instead relying on the isValidCallback to allow each playlist to define its own rules in a very open-ended way. Every isValidCallback is passed a playback struct, which is a table containing two more tables: state, and rules.
---@class PlaylistSilenceParams---@field min integer minimum possible duration for this silence track---@field max integer maximum possible duration for this silence track---@field chance number probablility that this playlist will use silence between tracks--- Example of how silenceparams may be used in a playlistsilenceBetweenTracks = { chance = 0.1, max = 30, min = 10,},--- Data type used to bridge one playlist into another, or to extend---@class PlaylistFallback---@field playlistChance number? optional float between 1 and 0 indicating the chance for a fallback playlist to be selected. If not present, the chance is always 50%---@field playlists string[]? array of fallback playlists from which to select tracks. No default values and not required.---@field tracks string[]? tracks to manually add to a given playlist. Used for folder-based playlists; not necessary for any othersfallback = { playlistChance = 0.25, playlists = { 'Explore', 'Tamriel Rebuilt - Port Telvannis', }, tracks = { 'explore/mx_explore_1.mp3', },}--- Special class for handling exterior grids.--- Used for special circumstances in which playlists should only run in *particular* exterior cells---@class S3maphoreCellGrid---@field x integer---@field y integergrid = { x = -2, y = -2, }---@class S3maphorePlaylistExclusions---@field playlists string[]? list of subdirectories to ignore when constructing a playlist. the `music/` prefix is inferred, so this field works the same way as playlist IDs.---@field tracks string[]? explicit list of tracks to ignore when constructing a playlist. the `music/` prefix is inferred, so this field works the same way as playlist IDs.exclusions = { tracks = { 'explore/nerevar_rising.mp3', }, playlists = { 'expore/subdir/ },}
PlaylistState Specification
---@class StaticList---@field recordIds string[] array of all unique static record ids in the current cell---@field contentFiles string[] array of all unique content files which placed statics in this cell---@class PlaylistState---@field self userdata the player actor---@field playlistTimeOfDay TimeMap the time of day for the current playlist---@field isInCombat boolean whether the player is in combat or not---@field cellIsExterior boolean whether the player is in an exterior cell or not (includes fake exteriors such as starwind)---@field cellName string lowercased name of the cell the player is in---@field cellId string engine-level identifier for cells. Should generally not be used in favor of cellNames as the only way to determine cell ids is to check in-engine using `cell.id`. It is made available in PlaylistState mostly for caching purposes, but may be used regardless.---@field combatTargets FightingActors a read-only table of combat targets, where keys are actor IDs and values are booleans indicating if the actor is currently fighting---@field staticList StaticList a list of all recordIds and content files placing objects in this cell. This is used in staticContentFile, staticMatch, and staticExact rules for playlists.---@field weather WeatherType a string indicating the current weather name. Updated by an internal mwscript in 0.49-compatible versions.---@field nearestRegion string? The current region the player is in. This is determined by either checking the current region of the player's current cell, OR, reading all load door's target cell's regions in the current cell. The first cell which is found to have a region will match and be assigned to the PlaylistState.---@field currentGrid S3maphoreCellGrid? The current exterior cell grid. Nil if not in an actual exterior.
As usual, if there are more state values you'd like the PlaylistState to offer, please do share! A generic-enough use case can and absolutely will make the cut.
rules is a table of functions that can have certain data passed into them to define when a S3maphore playlist should run. This is the real meat and potatoes of creating a S3maphorePlaylist. For exmaple:
WARNING:
Take note of the fact that the OuterRimCells table is declared and defined in an upper scope, outside of the isValidCallback function. This is crucial for S3maphore's lookup caching methods to work. Never inline a table constructor in your isValidCallbacks, or you will lose performance for no reason and trigger a memory leak. This is unavoidable on S3maphore's part, so it is paramount that you use it properly. NEVER make an isValidCallback that looks like this:
TIP: If you don't want to specify track names manually, just don't! If no tracks are listed for a playlist, its id field is used to determine a folder name in which a playlist's tracks will be looked for. See the builtin playlists for an easy example:
{ id = "Explore", priority = PlaylistPriority.Explore, randomize = true, isValidCallback = function() return not Playback.state.isInCombat and defaultPlaylistStates.ExploreActive end,}
Above is S3maphore's version of Morrowind's built-in Explore playlist. No tracks are listed, because it just randomly picks from Music/Explore, since Explore is the playlist id.
PlaylistRules Specification
--- Returns whether the current cell name matches a pattern rule. Checks disallowed patterns first------ Example usage:------ playlistRules.cellNameMatch { allowed = { 'mages', 'south wall', }, disallowed = { 'fighters', } }---@param patterns CellMatchPatternsfunction PlaylistRules.cellNameMatch(patterns)--- Returns whether or not the current cell exists in the cellNames map------ Example usage:------ playlistRules.cellNameExact { 'balmora, caius cosades\'s house' = true, 'balmora, guild of mages' = true, }---@param cellNames IDPresenceMap---@return booleanfunction PlaylistRules.cellNameExact(cellNames)--- Returns whether the player is currently in combat with any actor out of the input set--- the playlistState provided to each `isValidCallback` includes a `combatTargets` field which is meant to be used as the first argument------ Exmple usage:------ playlistRules.combatTarget { 'caius cosades' = true, }---@param validTargets IDPresenceMap---@return booleanfunction PlaylistRules.combatTargetExact(validTargets)--- Finds any nearby combat target whose name matches any one string of a set------ Example usage:------ playlist.rules.combatTargetMatch { 'jedi', 'sith', }---@param validTargetPatterns string[]---@return booleanfunction PlaylistRules.combatTargetMatch(validTargetPatterns)--- Checks the current cell's static list for whether--- an allowed static is present, or a disallowed one is present.--- Example usage:------ playlistRules.staticExact { 'furn_de_ex_bench_01' = true, 'ex_ashl_tent_01' = false, }---@param staticRules IDPresenceMap---@return boolean?function PlaylistRules.staticExact(staticRules)--- Checks the current cell's static list to see if it contains any object matching any of the input patterns--- WARNING: This is the most expensive possible playlist filter. It is only available in interior cells as S3maphore will not track statics in exterior cells.------ Example usage:------ playlistRules.staticMatch { 'cave', 'py', }------@param patterns string[]---@return boolean?function PlaylistRules.staticMatch(patterns)--- Returns whether or not a given cell contains statics matching the given content file array--- Automatically lowercases all input content file names!------ Example usage:------ playback.rules.staticContentFile { ['starwind enhanced.esm'] = true, }function PlaylistRules.staticContentFile(contentFiles)--- Checks whether the current gameHour matches a certain time of day or not------ Example usage:------ playlistRules.timeOfDay(8, 12)---@param minHour integer---@param maxHour integer---@return booleanfunction PlaylistRules.timeOfDay(minHour, maxHour)--- Return whether the current region matches a set------ Example usage:------ playlistRules.region { 'azura\'s coast region' = true, 'sheogorad region' = true, }---@param regionNames IDPresenceMap---@return booleanfunction PlaylistRules.region(regionNames)--- Rule for checking whether combat targets match a specific type. This can be for NPCs, or specific subtypes of creatures, such as undead, or daedric.--- Valid values are listed under the TargetType enum.--- NOTE: These are hashsets and only `true` is a valid value.--- Inputs must always be lowercased. Yes, really.--- --- Example Usage:--- --- playlistRules.combatTargetType { ['npc'] = true }--- playlistRules.combatTargetType { ['undead'] = true }---@param targetTypeRules CombatTargetTypeMatches---@return booleanfunction PlaylistRules.combatTargetType(targetTypeRules)--- Rule for checking if the player is fighting vampires of any type, or clan.--- To check specific vampire clans, use the faction rule.---@return booleanfunction PlaylistRules.fightingVampires()--- Sets a relative or absolute limit on combat target levels for triggering combat music.------ levelDifference rules may be relative or absolute, eg a multplier of the player's level or the actual difference in level.--- They may have a minimum and maximum threshold, although either is optional.--- Negative values indicate the player is stronger, whereas positive ones indicate the target is stronger.------ Example usage:------ This rule plays if the target's level is equal to or up to five levels bove the player's--- playlistRules.combatTargetLevelDifference { absolute = { min = 0, max = 5 } }------ This rule is valid if the target's level is within half or twice the player's level. EG if you're level 20, and the target is level 10, this rule matches.--- playlistRules.combatTargetLevelDifference { relative = { min = 0.5, max = 2.0 } }---@param levelRule LevelDifferenceMapfunction PlaylistRules.combatTargetLevelDifference(levelRule)--- Checks whether or not an actor meets a specific threshold for any of the three dynamic stats - health, fatigue, or magicka.--- Any combination of the three will work, and one may use a maximum and/or a minimum threshold------ Example usage:------ Rule is valid if an actor has MORE than 25% health--- playlistRules.dynamicStatThreshold { health = { min = 0.25 } }------ Rule is valid is an actor has LESS THAN 75% magicka.--- playlistRules.dynamicStatThreshold { magicka = { max = 0.75 } }---@param statThreshold StatThresholdMap decimal number encompassing how much health the target should have left in order for this playlist to be considered valid---@return booleanfunction PlaylistRules.dynamicStatThreshold(statThreshold)--- Rule for checking the rank of a target in the specified faction.--- Like any rule utilizing a LevelDifferenceMap, either min or max are optional, but *one* of the two is required.------ Example usage:------ playlistRules.combatTargetFaction { hlaalu = { min = 1 } }---@param factionRules NumericPresenceMapfunction PlaylistRules.combatTargetFaction(factionRules)--- Playlist rule for checking a specific journal state------ Example usage:------ playback.rules.journal { A1_V_VivecInformants = { min = 50, max = 55, }, }---@param journalDataMap NumericPresenceMap---@return booleanfunction PlaylistRules.journal(journalDataMap)--- Checks whether any combat target's classes matches one of a hashset--- ALWAYS LOWERCASE YOUR INPUTS!--- --- Example Usage:--- --- playlistRules.combatTargetClasses { ['guard'] = true, ['acrobat'] = true }---@param classes IDPresenceMap---@return booleanfunction PlaylistRules.combatTargetClass(classes)--- Rule used to check if a nearby merchant does, or doesn't, offer a specific service.--- Works on all nearby actors, and bails and returns true for the first actor whom matches all provided rules.--- Works best in locations where a single merchant is present - for cells where multiple actors may potentially offer the same service, like The Abecette,--- a cellNameMatch or cellNameExact rule may be more appropriate.--- Only accepts a limited range of inputs as defined by the `ServicesOffered` type.------ Example Usage:------ local services = { ["Armor"] = true, ['Repair'] = true, }--- playlistRules.localMerchantType(services)---@param services ServicesOffered---@return booleanfunction PlaylistRules.localMerchantType(services)--- Returns whether the current exterior cell is on a particular node of the grid------ Example usage:------ playlistRules.exteriorGrid { { x = -2, y = -3 } }---@param gridRules S3maphoreCellGrid[]function PlaylistRules.exteriorGrid(gridRules)
All of these functions may be chained, used, ignored, or even reimplemented by you, as long as your playlist's isValidCallback returns true when it's supposed to play and false|nil when it isn't.
NOTE:
While S3maphore doesn't require that you use its built-in callbacks, your life will be easier if you do. S3maphore caches as many of its lookups as it possibly can, so one particular cell for example, will only really run the necessary code for these callbacks if it's never happened before.
In practice, this means most of the performance hit from S3maphore occurs during cell transitions. Increased usage will also occur durng combat as a much larger segment of the total number of registered playlist is iterated.
Optimizing your playlists is important!
S3maphore works by calling every isValidCallback, for every playlist which is marked as active and has at least one track, every single frame. This is why the caching is necessary, and why it's important to keep your isValidCallbacks simple where you can.
Use S3maphore's open ended nature to your advantage, but please keep in mind that every playlist is running its callbacks every single frame. You are sharing not-a-lot of space with a lot of neighbors who are very much in a hurry.
If you do run into performance bottlenecks with your playlists, please let me know and I will do everything I can to help make your playlists functional. Later iterations of S3maphore are likely to rely on my helper library, H3lp Yours3lf, for additional optimizations!
Playlist Environment Specification
Starting from version 0.54 and onward, S3maphore provides a builtin environment to playlists which can be used to simplify the creation of playlists.
For example, a playlist which used to look like this:
local function crystalCityRule(playback) return playback.rules.cellNameExact(CrystalCityCells)endlocal PlaylistPriority = require 'doc.playlistPriority'---@type S3maphorePlaylist[]return { { id = 'MOMW Patches - Secrets of the Crystal City', priority = PlaylistPriority.CellExact, tracks = { 'music/aa22/tew_aa_3.mp3', }, isValidCallback = crystalCityRule, },}
May now be:
local function crystalCityRule() return Playback.rules.cellNameExact(CrystalCityCells)endlocal PlaylistPriority = require 'doc.playlistPriority'---@type S3maphorePlaylist[]return { { id = 'MOMW Patches - Secrets of the Crystal City', priority = PlaylistPriority.CellExact, tracks = { 'music/aa22/tew_aa_3.mp3', }, isValidCallback = crystalCityRule, },}
The idea is to provide a bunch of built-in variables and functions to playlists so that it is no longer necessary for every playlist to require the same things every other one does. S3maphore's built-in event handling functions and tilesets are provided through this environment, as well as openmw.interfaces, and many builtin lua functions you otherwise would not have access to.
The environment is easily extensible through the core.lua which creates it, so please feel free to request new functions here.
Full playlist environment specification:
---@class S3maphorePlaylistEnvlocal PlaylistEnvironment = { playSpecialTrack = MusicManager.playSpecialTrack, skipTrack = MusicManager.skipTrack, setPlaylistActive = MusicManager.setPlaylistActive, timeOfDay = MusicManager.playlistTimeOfDay, INTERRUPT = MusicManager.INTERRUPT, ---@type PlaylistPriority PlaylistPriority = require 'doc.playlistPriority', Tilesets = require 'doc.tilesets', Playback = Playback, ---@type table <string, any> I = I, require = require, math = math, string = string, ipairs = ipairs, pairs = pairs, --- Takes any number of paramaters and deep prints them, if debug logging is enabled ---@param ... any print = function(...) helpers.debugLog(helpers.deepToString({ ... }, 3)) end,}
The Special Playlist
The Special playlist is, well, a special case - it comes with no tracks, and is the ONLY playlist which is allowed to do so intentionally. Playing a special track using either the below events or interfaces will play the specified track, once, and then fall back to whatever playback behavior is contextually appropriate. This can be useful for playing things like boss music, or the levelup sting.
Playlist Localization
All S3maphore playlists natively support being localized, thanks to OpenMW's use of the l10n system. To provide localizations for a playlist, make a folder adjacent to your playlists folder, called l10n. Inside of that, make another folder called, S3maphoreTracks${PLAYLIST_ID}. For example, the builtin music playlists are in S3maphoreTracksExplore and S3maphoreTracksBattle. You'll see localized versions of track and playlist names onscreen as they change, if you have the BannerEnabled setting enabled.
Events
S3maphore includes a selection of Player events which indicate the changing of tracks and responds to some to modify its behavior.
S3maphoreSkipTrack - () - skips the current track
S3maphoreToggleMusic - ({ state: boolean }) - set whether music plays or not. Will immediately stop playback when disabling.
S3maphoreSetPlaylistActive - ({ playlist: string, state: boolean }) - Enables or disables a playlist
S3maphoreSpecialTrack - ({ trackPath: string, reason: S3maphoreTrackChangeReason }) - plays one specific track from the special playlist which will not be interrupted, but may be overridden by other playlists in the Special priority class.
S3maphoreTrackChanged - (S3maphoreStateChangeEventData) - Emitted whenever the currently playing track changes, due to the previous one being skipped, or a new playlist starting, or a new track from the same playlist starting.
S3maphoreMusicStopped - ({ reason: S3maphoreTrackChangeReason }) - Emitted when music stops, due to being disabled, no playlist being valid for this frame, or just the player dying.
S3maphoreStateChangeEventData Specification
---@class S3maphoreStateChangeEventData---@field playlistId string---@field trackName string VFS path of the track being played---@field reason S3maphoreStateChangeReason
See the interface documentation immediately below for the S3maphoreTrackChangeReason specification.
Interface
If you need more direct control over what S3maphore is doing, it also offers an interface in Player scope, I.S3maphore. This is especially useful for debugging S3maphore via the console, as almost all of its important playlist-related functionality is exposed through this interface. The S3maphoreSkipTrack, S3maphoreToggleMusic, S3maphoreSetPlaylistActive, and S3maphoreSpecialTrack events are all shorthands for calling functions in this interface.
The various functions available are as follows:
---@param trackPath string VFS path of the track to play---@param reason S3maphoreStateChangeReasonfunction S3maphore.playSpecialTrack(trackPath, reason)--- Returns a string listing all the currently registered playlists, mapped to their (descending) priority.--- Mostly intended to be used via the `luap` console.---@return stringfunction S3maphore.listPlaylistsByPriority()--- Disable or enable music playback (via setting, which persists through boot cycles)---@param enabled booleanfunction MusicManager.overrideMusicEnabled(enabled)--- Whether or not S3maphore's music playback is enabledfunction MusicManager.getEnabled()--- Returns a read-only array of all recognized playlist files (files with the .lua extension under the VFS directory, Playlists/ )---@return userdata playlistFilesfunction MusicManager.listPlaylistFiles()--- Returns a read-only list of read-only playlist structs for introspection. To modify playlists in any way, use other functions.function MusicManager.getRegisteredPlaylists()--- Returns a read-only copy of the current playlist, or nil---@return userdata? readOnlyPlaylistfunction MusicManager.getCurrentPlaylist()--- Returns l10n-localized playlist and track names for the current playlist. If a localization for the track does not exist, return nil---@return string? playlistName, string? trackNamefunction MusicManager.getCurrentTrackInfo()--- Returns the path of the currently playing track---@return string?function MusicManager.getCurrentTrack()--- initialize any missing playlist fields and assign track order for the playlist, and global registration order.---@param playlist S3maphorePlaylistfunction MusicManager.registerPlaylist(playlist)--- Used as the `reason` argument in S3maphore Events---@enum S3maphoreStateChangeReasonS3maphore.STATE = util.makeReadOnly { Died = 'DIED', Disabled = 'DSBL', NoPlaylist = 'NPLS', SpecialTrackPlaying = 'SPTR',},--- Meant to be used in conjunction with the output of MusicManager.playlistTimeOfDay OR PlaylistState.playlistTimeOfDay ---@enum TimeMapS3maphore.TIME_MAP = util.makeReadOnly { [0] = 'night', [1] = 'morning', [2] = 'afternoon', [3] = 'evening',}
Settings
S3maphore's main settings group is a Player scoped storage section called SettingsS3Music. It contains the following keys and values:
1.Enable Debug Messages - Checkbox - Whether or not to use verbose logging. Press F10 in-game or veiew openmw.log for more details. Every line with S3maphore log outputs will start with [ S3MAPHORE ]:
Enable Music - Checkbox - Whether or not S3maphore will play music at all, for the purposes of temporarily stopping playback to be managed by something else
Show Track Info - Checkbox - Whether or not the current playlist and track names will be shown when S3maphore changes songs.
Enable Combat Music - Checkbox - If this is disabled, combat music will never play. Added by request.
Finish Previous Track - Checkbox - Enabled by default, this setting prevents playlist changes with the same interrupt mode from interrupting music playback.
Allow Friendly Interior Playlist Override - Checkbox - disabled by default, this setting allows overriding the finish previous track setting, when entering a friendly interior, such as a tavern.
Allow Dungeon Playlist Override - Checkbox - enabled by default, overrides the finish previous track setting when entering a dungeon.
Allow Overworld Playlist Override - Checkbox - disabled by default, allows playlist changes while traversing exteriors to override one another. May be removed in a later version as this was the main reason Finish Previous Track was implemented in the first place.
Default Fade Duration - Number - Global fadeOut duration used between tracks if a playlist doesn't specify its own.
Enable Silence Tracks - Checkbox - Enabled by default, allows S3maphore to stop music playback for a specified amount of time between track changes in the same playlist.
Global Silence Chance - Number - Defaults to 0.15, this is the global chance for silence tracks to play. If a playlist specifies this value itself, that is used instead.
Explore Silence Min Duration - Number - Minimum duration for playback to stop between tracks, during Explore playlists. Randomly selected between this and the maximum value.
Explore Silence Max Duration - Number - Maximum duration for playback to stop between tracks, during Explore playlists. Randomly selected between this and the minimum value.
Battle Silence Min Duration - Number - Minimum duration for playback to stop between tracks, during Battle playlists. Randomly selected between this and the maximum value.
Battle Silence Max Duration - Number - Maximum duration for playback to stop between tracks, during Battle playlists. Randomly selected between this and the minimum value.
There also is another Player scoped storage section of note - S3maphoreActivePlaylistSettings. All playlists registered by S3maphore can have their active states set by other scripts by setting the key ${PLAYLISTNAME}Active to true or false. S3maphore will then automatically respond to this change and disable/enable the playlist accordingly. Every playlist can also be permanently toggled on or off in the settings menu manually.
Credits
All code in this repository was generated by Dave Corley under the GPL3 License.
Please enjoy my content, hack away as you please, and respect the freedoms of your fellow creators in the meantime.
All code was written by Dave Corley under the GPL3 license. Please enjoy my mod, hack away as you please, and respect the freedoms of your fellow modders and players in the meantime.
I pour my entire heart, soul, and talent into this community. If you appreciate my work, please, [please consider supporting me on Ko-Fi.](https://ko-fi.com/magicaldave)
I would do this full-time if I could only afford to.
S3ui is an early iteration at an attempt to replace OpenMW's user interface entirely, with the transmog mod as a foundation.
In retrospect, I have found the architecture of Transmog to be overall, a total disaster, and am not really sure if I want to continue developing this mod based on it. Still, this mod itself is distinct from Transmog and is thus available on the St4sh. Don't expect much at the moment, however.
The Starwind Community patch project was born as a fix for crashes we found after someone made a bunch of normal maps for everything, including Starwind. Technically, these crashes are an engine bug, but it's also because the meshes are broken as well. Thus, SW_CPP was born.
Over time as The Starwind Initiative matured, we collected lots of minor bug fixes we needed to implement for multiplayer and wanted to make sure they were available to all Starwind players. Everything built here is available in the Starwind-Builder repo, which is also used for deploying all content patches for TSI.
Community Patch Project also now includes a replacement file for Lua scripts to make their default layout colors look better. This means better compatibility with fancy script mods for Morrowind, basically. Also, a new and improved font, Oxanium-SemiBold has been added. Starwind's UI should look better than ever with CPP! Additionally, the galactic-basic font has been added as a replacement for daedric fonts.
Paste the location you extracted it to into the form below, hit Generate Config, and paste the result into openmw.cfg.
Where would you like to install this mod?
# Starwind Community Patch Project Data Directories
data=C:/Games/OpenMW-Mods/Starwind Community Patch Project/.
# Starwind Community Patch Project Content Files
content=Starwind Community Patch Project.omwaddon
Credits
Author: S3ctor
All code was written by Dave Corley under the GPL3 license.
Please enjoy my mod, hack away as you please, and respect the freedoms of your fellow modders and players in the meantime.
The Starwind Merged Plugin Project began as an effort to consolidate the Starwind ESM files, to eventually make them entirely indepentent of Morrowind.esm and friends. At this time, the merged plugin project primarily focuses on the vanilla ESM files, with additional content stripped out.
NOTE: Only use one of the two plugins, either the Singleplayer version or the Multiplayer one. They are meaningfully different.
WARNING
Please do not try to use the merged plugin project with any other Starwind mods. They are not presently compatible, but will be in the future!
History
Over time as The Starwind Initiative matured, we collected lots of minor bug fixes we needed to implement for multiplayer and wanted to make sure they were available to all Starwind players. Everything built here is available in the Starwind-Builder repo, which is also used for deploying all content patches for TSI.
Credits
Credits
All code in this repository was generated by Dave Corley under the GPL3 License.
Please enjoy my content, hack away as you please, and respect the freedoms of your fellow creators in the meantime.
Most of the changes and fixes in here were drawn from bug reports and other errata discovered by the TSI community. Thanks to all of you, as well <3.
With T4rgets, an enemy's health is displayed according to the color of the icon. Each enemy has five phases from full to wounded to dead, with the actual icon color mixing between the two nearest colors. This means you always know how healthy the enemy is in combat!
Target indicators also grow in size dynamically according to how far away your target is. Additionally, it is impossible to target enemies which are offscreen, and the mod does not use any raycasts unless the CheckLOS setting is enabled - what this means for you is no bad targets, and high performance (as much as can be expected from updating a UI element every frame, anyway).
T4rg3t5 comes with a full suite of 31 icons to use for lock-on indicators. Additionally, it's very easy to create new target lock icons for T4rg3t5 for your own mods or personal use.
Please make sure to assign a keybinding for T4rg3t5 to use, or the mod will be (mostly) useless.
Core Features
Smart Target Acquisition
Automatic Target Selection: Finds the nearest valid enemy in your field of view
Line-of-Sight Checking: Ensures targets are actually visible and not behind obstacles
Flick Switching: Quickly change targets by flicking the mouse left or right
Combat Auto-Lock: Automatically locks onto enemies who initiate combat with you
Visual Target Indicators
Dynamic Lock Icons: Customizable target markers that scale with distance
Health-Based Coloring: Icon color changes based on enemy health status:
Full Health (100%+): Primary color
Very Healthy (80-100%): Mix with full health color
Healthy (60-80%): Distinct healthy state
Wounded (40-60%): Noticeable wound indication
Very Wounded (20-40%): Critical condition warning
Dead/Dying (0-20%): Near death state
Hit Feedback: Icons "bounce" when you successfully hit locked targets
Distance Scaling: Icons grow/shrink based on target distance
Intelligent Automation
Auto-Facing: Character automatically turns to face locked targets
Weapon Awareness: Only allows locking when wielding weapons
Smart Target Management:
Automatically switches targets when current target dies
Breaks lock when targets move out of sight
Releases lock when sheathing weapons
For Modders
Events
Target locking always happens by way of sending an event. If you wish to modify this behavior somehow, you may create an eventHandler for the S3TargetLockOnto event. This will allow you to prevent target locking in some circumstances, change the target, do some specific behavior when a target is locked, etc. In the eventData is only the targeted actor, which is nil if the target lock has been broken for any reason.
Triggers
You can also engage target locking manually from any PLAYER script by calling input.activateTrigger('S3TargetLockOn'). This will either disable or enable locking as appropriate.
If you care to check whether a target is already selected or not, then use I.S3LockOn.Manager.getMarkerVisibility().
ProtectedTable
T4rg3t5 uses H3lp Yours3lf's protected table interface, which offers many conveniences - such as displaying all the associated setting values by simply trying to print the table. Try the following the console and explore for yourself:
luapI.S3LockOn.Manager
Making New Icons
Making new Icons for T4rg3t5 is dead simple. The recommended way is to use GIMP, but any image editor should be able to handle this task.
First, pick or create a source image.
Open it in GIMP
Use Colors -> Threshold to flatten all the colors to pure black/white. Make sure to adjust the values to find ones that fit your image.
Either delete the black pixels, or lighten them, using Colors -> Levels to increase the white levels of these pixels, so that the script's coloration works better.
Use Image -> Scale Image to resize the image to 128x128
Export as DDS without mipmaps OR compression to a subfolder, textures/s3/crosshair
While eventually Skyrim's literal format could be supported, this is most likely a long ways off and not necessarily practical for Morrowind modders. MTM attempts to bridge the gap, by making it easy to create and use tags for classifying objects - like, which cells are dungeons, or merchants, or distinguishing bandages from regular medical supplies, etc.
End users will need this as a dependency of other mods (hopefully), but it doesn't do much of anything in itself. Read on past the installation guide for developer's notes.
Using Tags
Tags are stored as yaml files under the subdirectory ModTags of any data= entry in openmw.cfg. When properly installed, Tagger includes a (hopefully growing) built-in set of tag definitions for the vanilla plugins, as well as some popular mods. Contributions to Tagger's built-in tags and associations are welcome and desired. The rest of this category will describe how to create tags and attach them to any object desired. This includes cells, quests, objects in the game world, or whatever other thing you can represent as a string in Lua.
Yaml Schema
Every Tagger definition file should have either or both of the following fields:
tags is a list of tag names to be used. It might look like this:
These will then be made available to other modders through the Tagger interface.
applied_tags is a map of tagged objects to the list of tags they actually use. Multiple mods may add as many tags as they wish to the same object as many times as they like. Tags may not be removed by design.
When Tagger is loaded, all tags are loaded immediately and made available in all script contexts. Tags are stored inside of a global storage section which only lives through the length of your play session.
This means that Tagger doesn't occupy unnecessary space in your save file, is always as up to date as it can be, and can be used by any object in the game. Also, note that it's not necessarily a requirement that tags are associated with a specific gameObject. You can apply tags to anything you can represent as a string in a yaml file, so feel free to make up your own associations, such as with quests or even specific MWScripts.
API
For scripters whom are not creating their own tag definitions but derivative scripts (you can do both!) there are mostly four values you'd care about.
Global scripts can access Tagger's features through I.TaggerG, whereas all other scripts can access them through I.TaggerL. Both the global list of tags and all applied tags are available as the values I.TaggerL.TagList or I.TaggerL.AppliedTags. Calls to these functions normalize your inputs, so don't worry about case sensitivity.
You can check if an object has a tag, or read its entire tag list like so:
I.TaggerL.objectHasTag(self, "NPCMainQuest")
I.TaggerL.objectTags(self)
Toolgun is a port of the eponymous swiss-army-revolver from Garry's Mod, purpose built for OpenMW. Fix floaters, rotate or scale objects to your liking, and then save them all back into a REAL mod you can share with the world after!
Paste the location you extracted it to into the form below, hit Generate Config, and paste the result into openmw.cfg.
Where would you like to install this mod?
# Toolgun Data Directories
data=C:/Games/OpenMW-Mods/Toolgun/.
# Toolgun Content Files
content=toolgun.omwaddon
With that out of the way...
It's TOOL TIME!
Toolgun offers a series of built-in tool modes for manipulating the game world around you. The entire table of tool modes is exposed through the interface toolTime, and modders may add their own tool modes through the member tools. In mode.lua, modders can find the base tool object for making their own.
Toolgun also offers a hand-built JSON serializer that players can use to make their own mods using it (almost) as simply as possible. Just go do some stuff with it, save your game, and hit F10. You'll have a single (possibly, extremely long) line of text you can copy into a new document. Do so, and name it whatever.json. Then you can feed that file into tes3conv (specifically, version 0.0.10), and now you've got a new mod.
How hard was that?
Usage
Please keep in mind that Toolgun requires OpenMW version 0.49, or at time of writing, a development build.
Linux users may (probably) find development builds through their respective package managers, or failing all else, check out JohnnyHostile's AppImages.
Controls
Many controls in toolgun are mode-specific. However, there are a small handful of (hardcoded, I am not changing this until default keybinds are properly implemented) controls which apply to all modes:
'X' - this increases the magnitude of your current edit mode. Eg, in scale mode, this will increase the amount by which the object's scale is increased.
'Z' - this decreases the magnitude of your current edit mode. Eg, in scale mode, this will decrease the amount by which the object's scale is decrease.
Ctrl + 'X', 'Y', or 'Z' - this will lock certain actions to a given axis. For example, if using the move submode, pressing Ctrl-Z will ensure the object only moves on the Z axis. Not applicable for every submode.
Ctrl + 'C' - This cycles between tool modes.
Just pressing C will allow switching between submodes of certain tools, such as clone.
Otherwise, just equip the toolgun and fire to activate it.
Built-in Tools
Scale - Use this to increase or decrease the size of objects. WARNING: Scaling an object below 0.5 or above 2.0 violates Bethesda's usage of the ESP format, and so, extreme object scales will not work when trying to save them with tes3conv. Sorry.
Delete - Self-explanatory.
However, if you press Shift+Z when using the delete submode, you can replace any object you remove accidentally.
Clone - Use this submode to copy or cut objects from the game world. Hold shift when shooting an object to delete the original and save it to your clipboard.
Switch submodes to go between copy and paste
Grab - As it says on the tin, use the grab submode to move objects around.
Free - No special constraints on object movement. Just drag the thing directly in front of your camera while left mouse is held down.
Axis - Use with the above mentioned axis-locking commands to restrict movement to a specific axis
Ground - Use this submode to drop objects onto the ground using a raycast.
Point - In this submode, you can bind two objects together. First, shoot wherever you want your object to go. Then, shoot the object you want to move. The second object will snap to whatever the first point you shot was, and you can continue from there.
Rotate - Just rotates objects around. Quite unwieldy when rotating on all three axes, so highly recommended to lock rotation to a specific axis.
Credits
Author: S3ctor
All code was written by S3ctor.
Facepunch owns the original Toolgun mesh.
SFX are sourced from Valve in accordance with their mod content policy for non-commercial products.
The Transmog Menu is originally derived from Cosmetic Overrides for MWSE. However, that mod does all its work in an MCM, and I wanted to go all-out here.
Transmog Menu is much closer to a full reimplementation of the Morrowind inventory menu. It does as much as the API is presently capable of in UI terms in as modular a way as possible, constituting just over three thousand lines of annotated code I hope can prove useful to someone else as well, covering subjects such as the new Actions/Triggers API, loads and loads of from-scratch UI, and OpenMW's native MCM
Transmog Menu isn't fully complete and I have broader goals for it, but right now I'm golfing it down as much as possible so that I can extend it more easily and hopefully make it more useful for other scripters on the way.
Usage
Transmog Menu requires requires OpenMW 0.49, or a development build as of this writing. Otherwise, it is completely standalone and should be compatible with the final released version of 0.49, whenever that happens.
Follow the installation instructions below, and that should get you started! Transmog menu tries to explain as much ingame as possible, but a few things bear mentioning here:
To 'mog two items together, just open the menu and click on two items.
You can preview items that would appear on your character visually by holding the spacebar. This binding does not use the Settings menu and is hardcoded. If you have a new item selected with a valid appearance, you'll preview it automatically.
Almost any two object types can be combined together. Mix and match stuff and get weird with it!
In general, objects will inherit the stats and properties of the top, or base, item, with the appearance of the new one in the bottom box. There are special cases for a few types of objects, however...
This will be explained ingame when the mod is first installed, but it also bears mentioning here. At this time, OpenMW does not allow you to use its settings menu and automatically have working default keybinds. Thus, you have to set the key bindings yourself or this mod will not work. I recommend the following defaults:
Rotate Right: Q Rotate Left: E Select: Enter/Return Open Transmog Menu: L
Paste the location you extracted it to into the form below, hit Generate Config, and paste the result into openmw.cfg.
Where would you like to install this mod?
# Transmog Menu Data Directories
data=C:/Games/OpenMW-Mods/Transmog Menu/.
# Transmog Menu Content Files
content=transmog.omwscripts
Credits
Author: S3ctor
All code was written by Dave Corley under the GPL3 license. Please enjoy my mod, hack away as you please, and respect the freedoms of your fellow modders and players in the meantime.
Alternatively, you may simply (manually) add the following entry to your openmw.cfg:
config=C:/the/location/of/ui_modes_slim
For example, if you have the ui_modes_slim folder in the same location as openmw.cfg, you can simply do this:
config=ui_modes_slim
This will ensure UI Modes Slim loads after any other mods in your config chain and also apply its settings for you automagically.
You may, of course, apply these settings manually via settings.cfg, but this is more tedious and fallible:
[Windows]inventory h = 0.896296inventory w = 0.833333inventory x = 0.00625inventory y = 0.0611111spells h = 0.828704spells w = 0.921354spells x = 0.0348958spells y = 0.0694444stats h = 0.827778stats w = 0.957292stats x = 0.0296875stats y = 0.0703704
Credits
- Originally written by [Petr Mikheev](https://gitlab.com/ptmikheev)