initial commit

This commit is contained in:
Lilian Jónsdóttir 2020-07-28 21:30:33 -07:00
parent 28c957ffbb
commit 1600a513a8
5 changed files with 340 additions and 0 deletions

86
.gitignore vendored Normal file
View 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

View 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

View 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

View 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)

View 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