initial commit
This commit is contained in:
parent
28c957ffbb
commit
1600a513a8
5 changed files with 340 additions and 0 deletions
86
.gitignore
vendored
Normal file
86
.gitignore
vendored
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
|
||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/lua,visualstudiocode,windows
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=lua,visualstudiocode,windows
|
||||||
|
|
||||||
|
### Lua ###
|
||||||
|
# Compiled Lua sources
|
||||||
|
luac.out
|
||||||
|
|
||||||
|
# luarocks build files
|
||||||
|
*.src.rock
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
|
||||||
|
# Object files
|
||||||
|
*.o
|
||||||
|
*.os
|
||||||
|
*.ko
|
||||||
|
*.obj
|
||||||
|
*.elf
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
*.lib
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lo
|
||||||
|
*.def
|
||||||
|
*.exp
|
||||||
|
|
||||||
|
# Shared objects (inc. Windows DLLs)
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
*.i*86
|
||||||
|
*.x86_64
|
||||||
|
*.hex
|
||||||
|
|
||||||
|
|
||||||
|
### VisualStudioCode ###
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
### VisualStudioCode Patch ###
|
||||||
|
# Ignore all local history of files
|
||||||
|
.history
|
||||||
|
|
||||||
|
### Windows ###
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
Thumbs.db:encryptable
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/lua,visualstudiocode,windows
|
15
MWSE/mods/celediel/DoorRandomizer/common.lua
Normal file
15
MWSE/mods/celediel/DoorRandomizer/common.lua
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
local this = {}
|
||||||
|
|
||||||
|
this.modName = "Door Randomizer" -- or something
|
||||||
|
this.author = "Celediel"
|
||||||
|
this.version = "0.0.1"
|
||||||
|
this.modInfo = "When a cell-change door is activated, its destination is randomized, based on various configuration options"
|
||||||
|
this.configPath = string.gsub(this.modName, "%s+", "")
|
||||||
|
this.cellTypes = {
|
||||||
|
interior = 0,
|
||||||
|
exterior = 1,
|
||||||
|
both = 2,
|
||||||
|
match = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
21
MWSE/mods/celediel/DoorRandomizer/config.lua
Normal file
21
MWSE/mods/celediel/DoorRandomizer/config.lua
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
local common = require("celediel.DoorRandomizer.common")
|
||||||
|
|
||||||
|
local defaultConfig = {
|
||||||
|
randomizeChance = 50,
|
||||||
|
interiorExterior = common.cellTypes.both,
|
||||||
|
wildernessCells = true,
|
||||||
|
needDoor = true,
|
||||||
|
debug = false,
|
||||||
|
ignoredCells = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
local currentConfig
|
||||||
|
|
||||||
|
local this = {}
|
||||||
|
|
||||||
|
function this.getConfig()
|
||||||
|
currentConfig = currentConfig or mwse.loadConfig(common.configPath, defaultConfig)
|
||||||
|
return currentConfig
|
||||||
|
end
|
||||||
|
|
||||||
|
return this
|
144
MWSE/mods/celediel/DoorRandomizer/main.lua
Normal file
144
MWSE/mods/celediel/DoorRandomizer/main.lua
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
-- pick a destination cell at random when activating a door
|
||||||
|
local common = require("celediel.DoorRandomizer.common")
|
||||||
|
local config = require("celediel.DoorRandomizer.config").getConfig()
|
||||||
|
|
||||||
|
local cells = {}
|
||||||
|
|
||||||
|
-- {{{ helper functions
|
||||||
|
local function log(...) if config.debug then mwse.log("[%s] %s", common.modName, string.format(...)) end end
|
||||||
|
|
||||||
|
local function isZero(spot)
|
||||||
|
return spot.position.x == 0 and
|
||||||
|
spot.position.y == 0 and
|
||||||
|
spot.position.z == 0 and
|
||||||
|
spot.orientation.x == 0 and
|
||||||
|
spot.orientation.y == 0 and
|
||||||
|
spot.orientation.z == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local function isTypeMatch(ogCell, chosenCell)
|
||||||
|
return ((ogCell.isInterior and ogCell.behavesAsExterior) or not ogCell.isInterior) ==
|
||||||
|
((chosenCell.isInterior and chosenCell.behavesAsExterior) or not chosenCell.isInterior)
|
||||||
|
end
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
-- {{{ cell and position/orientation picking
|
||||||
|
local function pickSpot(cell)
|
||||||
|
-- this will be the position/orientation destination of every door that puts the player into the above chosen cell
|
||||||
|
local spots = {}
|
||||||
|
local spot
|
||||||
|
|
||||||
|
-- peek through doors in that cell to pick a position/orientation for the player
|
||||||
|
for cellDoor in cell:iterateReferences(tes3.objectType.door) do
|
||||||
|
if cellDoor.destination then -- only cell change doors
|
||||||
|
-- loop through doors in THAT cell to find the door that led us there in the first place
|
||||||
|
log("Looking through door %s in %s leading to %s", cellDoor.name or cellDoor.id, cell.id, cellDoor.destination.cell.id)
|
||||||
|
for innerDoor in cellDoor.destination.cell:iterateReferences(tes3.objectType.door) do
|
||||||
|
if innerDoor.destination and innerDoor.destination.cell.id == cell.id then
|
||||||
|
-- found the door, now add where that door puts a player to our table
|
||||||
|
log("Found a door in %s leading to %s with starting position:(%s) and orientation:(%s)",
|
||||||
|
cellDoor.destination.cell.id, innerDoor.destination.cell.id,
|
||||||
|
innerDoor.destination.marker.position, innerDoor.destination.marker.orientation)
|
||||||
|
-- comment this out and set config.needDoor = true to cause infinite recursion
|
||||||
|
table.insert(spots, {
|
||||||
|
position = innerDoor.destination.marker.position,
|
||||||
|
orientation = innerDoor.destination.marker.orientation
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #spots > 0 then
|
||||||
|
log("There %s %s spot%s in %s", #spots > 1 and "were" or "was", #spots, #spots > 1 and "s" or "", cell.id)
|
||||||
|
spot = table.choice(spots)
|
||||||
|
else
|
||||||
|
-- if we don't find any then use 0,0,0, which can be really bad in some cells
|
||||||
|
spot = {position = tes3vector3.new(0, 0, 0), orientation = tes3vector3.new(0, 0, 0)}
|
||||||
|
end
|
||||||
|
|
||||||
|
return spot
|
||||||
|
end
|
||||||
|
|
||||||
|
local function pickCell(ogDest)
|
||||||
|
local picked = table.choice(cells)
|
||||||
|
local cell = tes3.getCell({id = picked})
|
||||||
|
|
||||||
|
if not config.wildernessCells and not cell.name then
|
||||||
|
log("%s is wilderness, trying again...", cell.id)
|
||||||
|
cell = pickCell(ogDest)
|
||||||
|
end
|
||||||
|
|
||||||
|
if config.ignoredCells[cell.id] then
|
||||||
|
log("%s is ignored cell, trying again...", cell.id)
|
||||||
|
cell = pickCell(ogDest)
|
||||||
|
end
|
||||||
|
|
||||||
|
if config.interiorExterior == common.cellTypes.exterior and cell.isInterior and not cell.behavesAsExterior then
|
||||||
|
log("%s is interior, trying again...", cell.id)
|
||||||
|
cell = pickCell(ogDest)
|
||||||
|
elseif config.interiorExterior == common.cellTypes.interior and (not cell.isInterior or cell.behavesAsExterior) then
|
||||||
|
log("%s is exterior, trying again...", cell.id)
|
||||||
|
cell = pickCell(ogDest)
|
||||||
|
elseif config.interiorExterior == common.cellTypes.match and not isTypeMatch(ogDest, cell) then
|
||||||
|
log("%s and %s aren't same cell type, trying again...", ogDest.id, cell.id)
|
||||||
|
cell = pickCell(ogDest)
|
||||||
|
end
|
||||||
|
|
||||||
|
return cell
|
||||||
|
end
|
||||||
|
|
||||||
|
local function pickCellAndSpot(ogDest)
|
||||||
|
local cell = pickCell(ogDest)
|
||||||
|
log("finally settled on %s, now picking position/orientation", cell.id)
|
||||||
|
|
||||||
|
local spot = pickSpot(cell)
|
||||||
|
|
||||||
|
if config.needDoor and isZero(spot) then
|
||||||
|
log("No good spots in %s, starting over!", cell.id)
|
||||||
|
cell, spot = pickCellAndSpot(ogDest)
|
||||||
|
end
|
||||||
|
|
||||||
|
return cell, spot
|
||||||
|
end
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
-- {{{ event functions
|
||||||
|
local function onActivate(e)
|
||||||
|
-- only do stuff on cell change doors
|
||||||
|
if e.activator ~= tes3.player and e.target.objectType ~= tes3.objectType.door then return end
|
||||||
|
|
||||||
|
local door = e.target
|
||||||
|
|
||||||
|
-- don't randomize locked doors, or: only randomize when a cell change event happens
|
||||||
|
if door.destination and not tes3.getLocked({reference = door}) then
|
||||||
|
local roll = math.random(0, 100)
|
||||||
|
local rollStr = "Randomize Roll: %s "
|
||||||
|
if config.randomizeChance < roll then
|
||||||
|
log(rollStr .. "> %s, not randomizing", roll, config.randomizeChance)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
log(rollStr .. "< %s, randomizing!", roll, config.randomizeChance)
|
||||||
|
|
||||||
|
log("Picking initial cell...")
|
||||||
|
local cell, spot = pickCellAndSpot(door.destination.cell)
|
||||||
|
|
||||||
|
log("Picked %s at (%s) facing (%s)", cell.id, spot.position, spot.orientation)
|
||||||
|
|
||||||
|
-- set the door's destination to the picked cell and position/orientation
|
||||||
|
tes3.setDestination({reference = door, cell = cell, position = spot.position, orientation = spot.orientation})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function onInitialized(e)
|
||||||
|
for cell, _ in pairs(tes3.dataHandler.nonDynamicData.cells) do table.insert(cells, cell) end
|
||||||
|
|
||||||
|
log("found %s cells", #cells)
|
||||||
|
|
||||||
|
event.register("activate", onActivate)
|
||||||
|
end
|
||||||
|
-- }}}
|
||||||
|
|
||||||
|
event.register("initialized", onInitialized)
|
||||||
|
event.register("modConfigReady", function() mwse.mcm.register(require("celediel.DoorRandomizer.mcm")) end)
|
74
MWSE/mods/celediel/DoorRandomizer/mcm.lua
Normal file
74
MWSE/mods/celediel/DoorRandomizer/mcm.lua
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
local common = require("celediel.DoorRandomizer.common")
|
||||||
|
local config = require("celediel.DoorRandomizer.config").getConfig()
|
||||||
|
|
||||||
|
local function createTableVar(id)
|
||||||
|
return mwse.mcm.createTableVariable({id = id, table = config})
|
||||||
|
end
|
||||||
|
|
||||||
|
local template = mwse.mcm.createTemplate({name = common.modName})
|
||||||
|
template:saveOnClose(common.configPath, config)
|
||||||
|
|
||||||
|
local page = template:createSideBarPage({
|
||||||
|
label = "Main Options",
|
||||||
|
description = string.format("%s v%s by %s\n\n%s\n\n", common.modName, common.version, common.author, common.modInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
local category = page:createCategory(common.modName)
|
||||||
|
|
||||||
|
category:createYesNoButton({
|
||||||
|
label = "Pick wilderness cells?",
|
||||||
|
variable = createTableVar("wildernessCells")
|
||||||
|
})
|
||||||
|
|
||||||
|
category:createYesNoButton({
|
||||||
|
label = "Pick only cells that place the player at doors?",
|
||||||
|
variable = createTableVar("needDoor")
|
||||||
|
})
|
||||||
|
|
||||||
|
category:createDropdown({
|
||||||
|
label = "Pick interior or exterior cells only",
|
||||||
|
options = {
|
||||||
|
{label = "Both", value = common.cellTypes.both},
|
||||||
|
{label = "Interior Only", value = common.cellTypes.interior},
|
||||||
|
{label = "Exterior Only", value = common.cellTypes.exterior},
|
||||||
|
{label = "Match Original Destination", value = common.cellTypes.match}
|
||||||
|
},
|
||||||
|
defaultSetting = common.cellTypes.both,
|
||||||
|
variable = createTableVar("interiorExterior")
|
||||||
|
})
|
||||||
|
|
||||||
|
category:createSlider({
|
||||||
|
label = "Randomize Chance",
|
||||||
|
min = 0,
|
||||||
|
max = 100,
|
||||||
|
step = 1,
|
||||||
|
jump = 2,
|
||||||
|
variable = createTableVar("randomizeChance")
|
||||||
|
})
|
||||||
|
|
||||||
|
category:createYesNoButton({
|
||||||
|
label = "Debug logging",
|
||||||
|
variable = createTableVar("debug")
|
||||||
|
})
|
||||||
|
|
||||||
|
template:createExclusionsPage({
|
||||||
|
label = "Ignored cells",
|
||||||
|
description = "These cells will not even be considered when randomizing.",
|
||||||
|
showAllBlocked = false,
|
||||||
|
variable = createTableVar("ignoredCells"),
|
||||||
|
filters = {
|
||||||
|
{
|
||||||
|
label = "Cells",
|
||||||
|
callback = function()
|
||||||
|
local cells = {}
|
||||||
|
for cell, _ in pairs(tes3.dataHandler.nonDynamicData.cells) do
|
||||||
|
table.insert(cells, cell)
|
||||||
|
end
|
||||||
|
return cells
|
||||||
|
end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return template
|
||||||
|
|
Loading…
Add table
Reference in a new issue