Skip to content

Commit

Permalink
Improve menu flow, add multi-choice locations for mage/fighters guild…
Browse files Browse the repository at this point in the history
… quests and more
  • Loading branch information
jhaakma committed Sep 1, 2024
1 parent c65be34 commit 65acacd
Show file tree
Hide file tree
Showing 13 changed files with 714 additions and 176 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@

local common = require("mer.chargenScenarios.common")
local Controls = require("mer.chargenScenarios.util.Controls")


local logger = require("mer.chargenScenarios.ScenarioBuilder.luaLogger").new{
outputFile = "ChargenScenarioClutter.txt",
}

local luaClutterTemplate = [[
{ --${name}
ids = {"${ids}"},
position = {${posx}, ${posy}, ${posz}},
orientation = {${orz}},
cell = "${cell}",
scale = ${scale},
},
]]

local function addClutter(clutterToAdd)
local clutterString = luaClutterTemplate:
gsub("${name}", clutterToAdd.name):
gsub("${id}", clutterToAdd.id):
gsub("${posx}", clutterToAdd.position[1]):
gsub("${posy}", clutterToAdd.position[2]):
gsub("${posz}", clutterToAdd.position[3]):
gsub("${orz}", table.concat(clutterToAdd.orientation, ", ")):
gsub("${cell}", clutterToAdd.cell.id):
gsub("${scale}", clutterToAdd.scale)
logger:info("\n" .. clutterString)
end

---@param target tes3reference
local function registerClutter(target)
local clutter = {
name = target.object.name,
id = target.object.id,
position = {
math.floor(target.position.x),
math.floor(target.position.y),
math.floor(target.position.z),
},
orientation = {
math.floor(target.orientation.x),
math.floor(target.orientation.y),
math.floor(target.orientation.z),
},
cell = target.cell,
scale = string.format("%.2d", target.scale)
}
addClutter(clutter)
end

---@param e keyDownEventData
local function onKeyDown(e)
if common.config.mcm.registerClutterEnabled then
if Controls.isKeyPressed(e, common.config.mcm.registerClutterHotKey) then
local result = tes3.rayTest{
position = tes3.getPlayerEyePosition(),
direction = tes3.getPlayerEyeVector(),
ignore = { tes3.player },
maxDistance = tes3.getPlayerActivationDistance()
}
if not (result and result.reference) then
tes3.messageBox("No reference found.")
return
end
local target = result.reference

tes3ui.showMessageMenu{
message = string.format("Register %s as clutter?", target.object.name),
buttons = {
{
text = "Yes",
callback = function()
registerClutter(target)
end
},
{
text = "Cancel"
}
}
}
end
end
end
event.register("keyDown", onKeyDown)

Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
To use this
]]
local common = require('mer.chargenScenarios.common')
local logger = common.createLogger("registerLocations")
local Controls = require("mer.chargenScenarios.util.Controls")
local Location = require("mer.chargenScenarios.component.Location")

local logger = require("mer.chargenScenarios.ScenarioBuilder.luaLogger").new{
outputFile = "chargenScenariosLocations.txt",
}

local luaLocationTemplate = [[
["${id}"] = {
{ --${name}
position = {${posx}, ${posy}, ${posz}},
orientation =${orz},
cell = "${cell}"
Expand All @@ -17,19 +18,18 @@ local luaLocationTemplate = [[


local function addLocation(locationToAdd, name)
logger:info("Location: ")
local locationString = luaLocationTemplate:
gsub("${id}", name):
gsub("${name}", name):
gsub("${posx}", locationToAdd.position[1]):
gsub("${posy}", locationToAdd.position[2]):
gsub("${posz}", locationToAdd.position[3]):
gsub("${orz}", locationToAdd.orientation[3])
gsub("${orz}", locationToAdd.orientation)
if tes3.player.cell.isInterior then
locationString = locationString:gsub("${cell}", locationToAdd.cell.id)
else
locationString = locationString:gsub("${cell}", "nil")
end
mwse.log(locationString)
logger:info("\n" .. locationString)
end

local function registerLocation()
Expand All @@ -39,11 +39,7 @@ local function registerLocation()
math.floor(tes3.player.position.y),
math.floor(tes3.player.position.z),
},
orientation = {
math.floor(tes3.player.orientation.x),
math.floor(tes3.player.orientation.y),
math.floor(tes3.player.orientation.z),
},
orientation = math.floor(tes3.player.orientation.z),
cell = tes3.player.cell
}
timer.delayOneFrame(function()
Expand All @@ -53,8 +49,8 @@ local function registerLocation()
menu.alignX = 0.5 ---@diagnostic disable-line
menu.alignY = 0 ---@diagnostic disable-line
menu.autoHeight = true
local t = { name = ""}
mwse.mcm.createTextField(
local t = { name = tes3.player.cell.name }
local textField = mwse.mcm.createTextField(
menu,
{
label = "Enter name of location:",
Expand All @@ -63,43 +59,13 @@ local function registerLocation()
table = t
},
callback = function()
if Location.get(t.name) then
tes3ui.showMessageMenu{
message = "Location with this id already exists.",
buttons = {
{
text = "Overwrite",
callback = function()
addLocation(location, t.name)
tes3ui.leaveMenuMode()
tes3ui.findMenu(menuId):destroy()
end
},
{
text = "Rename",
callback = function()
tes3ui.leaveMenuMode()
tes3ui.findMenu(menuId):destroy()
registerLocation()
end,
},
{
text = "Cancel",
callback = function()
tes3ui.leaveMenuMode()
tes3ui.findMenu(menuId):destroy()
end
}
}
}
else
addLocation(location, t.name)
tes3ui.leaveMenuMode()
tes3ui.findMenu(menuId):destroy()
end
addLocation(location, t.name)
tes3ui.leaveMenuMode()
tes3ui.findMenu(menuId):destroy()
end
}
)
tes3ui.acquireTextInput(textField.elements.inputField)
tes3ui.enterMenuMode(menuId)
end)
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--[[
Logs lua code templates to a custom file
]]

local luaLogger = {}


---@param params { outputFile: string }
function luaLogger.new(params)
local self = {
outputFile = io.open(params.outputFile, "w"),
}

function self:info(output)
if self.outputFile then
self.outputFile:write(output .. "\n")
self.outputFile:flush()
else
print(output)
end
end

return self
end

return luaLogger
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
--Controllers for the scenario builder
require('mer.chargenScenarios.ScenarioBuilder.controllers.registerLocations')
require('mer.chargenScenarios.ScenarioBuilder.controllers.registerClutter')
--MCM
local mcm = require('mer.chargenScenarios.ScenarioBuilder.mcm')
event.register("modConfigReady", mcm.registerModConfig)
26 changes: 20 additions & 6 deletions Data Files/MWSE/mods/mer/chargenScenarios/component/ItemPick.lua
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,26 @@ function ItemPick:giveToPlayer()
count = 1,
playSound = false,
}
if pickedItem.enchantCapacity then
local isBoots = pickedItem.objectType == tes3.objectType.armor and pickedItem.slot == tes3.armorSlot.boots
local isShoes = pickedItem.objectType == tes3.objectType.clothing and pickedItem.slot == tes3.clothingSlot.shoes
local isHelmet = pickedItem.objectType == tes3.objectType.armor and pickedItem.slot == tes3.armorSlot.helmet
local blockBeast = tes3.player.object.race.isBeast and (isBoots or isShoes or isHelmet)
if not blockBeast then

local equippableTypes = {
[tes3.objectType.armor] = true,
[tes3.objectType.clothing] = true,
[tes3.objectType.weapon] = true,
}

if equippableTypes[pickedItem.objectType] then
local beastBlocked = {
[tes3.objectType.armor] = {
[tes3.armorSlot.boots] = true,
[tes3.armorSlot.helmet] = true,
},
[tes3.objectType.clothing] = {
[tes3.clothingSlot.shoes] = true,
}
}
local blockedForBeasts = beastBlocked[pickedItem.objectType]
and beastBlocked[pickedItem.objectType][pickedItem.slot]
if not (tes3.player.object.race.isBeast and blockedForBeasts) then
tes3.equip{
item = pickedItem,
reference = tes3.player,
Expand Down
16 changes: 16 additions & 0 deletions Data Files/MWSE/mods/mer/chargenScenarios/component/Location.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---@class (exact) ChargenScenariosLocationInput
---@field name string? @The name of the location, required for scenarios where you can choose the location
---@field position table<number, number> @The position where the player will be spawned
---@field orientation number @The orientation where the player will be spawned
---@field cell? string @The cell where the player will be spawned. Nil for exteriors
Expand Down Expand Up @@ -58,6 +59,7 @@ function Location:new(data)

---@type ChargenScenariosLocation
local location = {
name = data.name,
position = data.position,
orientation = data.orientation,
cell = data.cell,
Expand Down Expand Up @@ -106,5 +108,19 @@ function Location.doClutter(self)
end
end

--Checks name, then cell, then region
function Location:getName()
if self.name then
return self.name
end
if self.cell then
return self.cell
end
local cell = tes3.getCell{ id = self.cell }
if cell and cell.region then
return cell.region.name
end
return "Unknown Location"
end

return Location
57 changes: 34 additions & 23 deletions Data Files/MWSE/mods/mer/chargenScenarios/component/Scenario.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,30 @@ local Clutter = require("mer.chargenScenarios.component.Clutter")
local ClutterList = require("mer.chargenScenarios.component.ClutterList")

---@class ChargenScenariosScenarioInput
---@field name string @The name of the Scenario. Will be displayed in the scenario selection menu.
---@field description string @The description of the Scenario. Will be displayed in the scenario selection menu.
---@field location nil|string|ChargenScenariosLocationInput @The location of the scenario. If used instead of 'locations', this location will be used for the scenario.
---@field locations nil|string[]|ChargenScenariosLocationInput[] @A list of locations. If used instead of 'location', one from this list will be randomly selected for the scenario.
---@field items nil|ChargenScenariosItemPickInput[] @A list of items that will be added to the player's inventory.
---@field spells nil|ChargenScenariosSpellPickInput[] @A list of spells that will be added to the player
---@field requirements nil|ChargenScenariosRequirementsInput @The requirements that need to be met for this scenario to be used.
---@field clutter nil|string|ChargenScenariosClutterInput[] @The clutter for the location. Can be a list of clutter data or a cluterList ID
---@field onStart nil|fun(self: ChargenScenariosScenario) @Callback triggered when a scenario starts.
---@field doVanillaChargen nil|boolean @If true, the vanilla chargen will be used.
---@field id string A unique ID for the scenario
---@field name string The name of the Scenario. Will be displayed in the scenario selection menu.
---@field description string The description of the Scenario. Will be displayed in the scenario selection menu.
---@field location nil|string|ChargenScenariosLocationInput The location of the scenario. If used instead of 'locations', this location will be used for the scenario.
---@field locations nil|string[]|ChargenScenariosLocationInput[] A list of locations. If used instead of 'location', one from this list will be randomly selected for the scenario.
---@field items nil|ChargenScenariosItemPickInput[] A list of items that will be added to the player's inventory.
---@field spells nil|ChargenScenariosSpellPickInput[] A list of spells that will be added to the player
---@field requirements nil|ChargenScenariosRequirementsInput The requirements that need to be met for this scenario to be used.
---@field clutter nil|string|ChargenScenariosClutterInput[] The clutter for the location. Can be a list of clutter data or a cluterList ID
---@field onStart nil|fun(self: ChargenScenariosScenario) Callback triggered when a scenario starts.


---@class (exact) ChargenScenariosScenario
---@field name string @the name of the scenario
---@field description string @the description of the scenario
---@field requirements ChargenScenariosRequirements @the requirements for the scenario
---@field locations ChargenScenariosLocation[] @the list of locations for the scenario
---@field itemList? ChargenScenariosItemList @the list of items for the scenario
---@field spellList? ChargenScenariosSpellList @the list of spells given to the player for this scenario. May include abilities, diseases etc
---@field clutterList? ChargenScenariosClutterList @the clutter for the location
---@field onStart? fun(self: ChargenScenariosScenario) @Callback triggered when a scenario starts.
---@field doVanillaChargen boolean @whether or not to do the vanilla chargen.
---@field decidedLocation? ChargenScenariosLocation @the location that was decided for this scenario
---@field registeredScenarios table<string, ChargenScenariosScenario> @the list of registered scenarios
---@field id string A unique ID for hte scenario
---@field name string the name of the scenario
---@field description string the description of the scenario
---@field requirements ChargenScenariosRequirements the requirements for the scenario
---@field locations ChargenScenariosLocation[] the list of locations for the scenario
---@field itemList? ChargenScenariosItemList the list of items for the scenario
---@field spellList? ChargenScenariosSpellList the list of spells given to the player for this scenario. May include abilities, diseases etc
---@field clutterList? ChargenScenariosClutterList the clutter for the location
---@field onStart? fun(self: ChargenScenariosScenario) Callback triggered when a scenario starts.
---@field decidedLocation? ChargenScenariosLocation the index of the location that was decided for this scenario
---@field registeredScenarios table<string, ChargenScenariosScenario> the list of registered scenarios
local Scenario = {
registeredScenarios = {},
}
Expand All @@ -56,6 +57,7 @@ function Scenario:new(data)
end

local scenario = {
id = data.id,
name = data.name,
description = data.description,
requirements = Requirements:new(data.requirements),
Expand All @@ -64,7 +66,6 @@ function Scenario:new(data)
spellList = data.spells and SpellList:new(data.spells),
clutterList = clutter,
onStart = data.onStart,
doVanillaChargen = data.doVanillaChargen,
}
--Create scenario
setmetatable(scenario, { __index = Scenario })
Expand All @@ -82,7 +83,7 @@ end
function Scenario:register(data)
local scenario = self:new(data)
logger:debug("Adding %s to scenario list", scenario.name)
Scenario.registeredScenarios[scenario.name] = scenario
Scenario.registeredScenarios[scenario.id] = scenario
return scenario
end

Expand Down Expand Up @@ -117,6 +118,16 @@ function Scenario:getStartingLocation()
return self.decidedLocation
end

function Scenario:getValidLocations()
local validLocations = {}
for _, location in pairs(self.locations) do
if location:checkRequirements() then
table.insert(validLocations, location)
end
end
return validLocations
end

--- Move the player to the starting location
function Scenario:moveToLocation()
if not self.locations then
Expand Down
Loading

0 comments on commit 65acacd

Please sign in to comment.