Skip to content
ryobg edited this page Feb 15, 2018 · 4 revisions

Simple example

Suppose you want to store some actor related information (let it be the Player followers and their mood):

function storeFolloverMood(form follower, float mood)
    -- function creates "followers" storage and then creates
    -- (follower, entry) associations
    JFormDB.setFlt(follower, ".followers.mood", mood)
endfunction

float function followerMood(form follower)
    -- fetch follower mood
    return JFormDB.getFlt(follower, ".followers.mood")
endfunction

; method that gets called once user uninstalls your mod
function modUninstallMethod()
    --  destroy association to not pollute game save and precious RAM
    JDB.setObj("followers", 0)
endfunction

Config reading

You wish to have all your mod config values to be stored somewhere (for example in "Data/preset.txt" file) so you could easy adjust them all by editing the file. Or you do not wish to hardcode all these values. A JSON formatted file contains the following information:

{
    "classicPreset" : {
        "campfileLighting" : "Automatic",
        "exposureRate" : 1.0,
        "frigidWaterLethal" : 1,
        "exposureIsLethal" : 1,
        "axeDurability" : 1
    },
    "winterHorkerPreset" : {
        "campfileLighting" : "nonAutomatic",
        "exposureRate" : 0.5,
        "frigidWaterLethal" : 0,
        "exposureIsLethal" : 0,
        "axeDurability" : 0
    }
}

A root map containing two maps (two standard presets your mod provides) - classicPreset and winterHorkerPreset.

Then a config file reading may look like:

-- let it be .classicPreset or .winterHorkerPreset string
string currentPreset

-- use function each time you need re-read preset from a file
function parseConfig()
    -- that’s all. presets are already in Skyrim
    -- readFromFile returns root map container
    -- it may return zero if file not exist or it can not be parsed (not JSON format or you have accidentally added extra coma)
    int config = JValue.readFromFile("Data/preset.txt")
    -- put config into DB - associate key and config
    JDB.setObj("frostfall", config)
    currentPreset = ".classicPreset"
endfunction

bool function axeDurabilityEnabled()
    -- solveInt like any solve* function tries to find (solve) value for given path
    -- current path is ".frostfall.classicPreset.axeDurability"
    return JDB.solveInt(".frostfall" + currentPreset + ".axeDurability") != 0
endfunction

string function lightingType()
    return JDB.solveStr(".frostfall" + currentPreset + ".campfileLighting")
endfunction

Facial expressions

This is a script that smoothly changes Players expression from a one to another. It is simple script, so there are two states only, a scale describes the emotional state, where 0.0 is the first emotional state, 1.0 is the second. Anything in between is mix of both states.

This is achieved by modifying the model bone scales. The need configuration data is imported from a file Data/emotionInterpolation.txt:

[
    ["hkPhoneme:f:DialogueFear", 0, -0.33],
    ["hkPhoneme:f:DialogueHappy", -0.133, -0.3],
    ["hkPhoneme:f:DialogueSad", 0, 0.433],
    ["hkPhonemes:f:LookLeft", -0.2, 0.0]
]

What you see here is one array that contains 4 sub-arrays and each sub-array contains bone name, minimum and maximum scales. Then a script may look like:

-- retrieve scale info
int getScaleInfo()
  int info = JDB.solveObj(".emotionInterpolation")
  if !info
    info = JValue.readFromFile("Data/emotionInterpolation.txt")
    -- cache emotionInterpolation data, so the next time we won't read it again
    JDB.setObj("emotionInterpolation", info)
  endif
  return info
endfunction

function setEmotionScale(float scale)
    objectreference plr = GetTargetActor()
    
    -- retrieve config
    int config = getScaleInfo()
    
    -- iterate over array & calculate bone scale
    int i = JArray.count(config)
    while(i > 0)
        i -= 1
        -- fetch sub-array. it can be ["NPC Head [Head]", 0, -0.33] for instance
        int data = JArray.getObj(config, i)
        float nodeScale = 1.0 + JArray.getFlt(data,1) + (JArray.getFlt(data,2) - JArray.getFlt(data,1)) * scale
        NetImmerse.SetNodeScale(plr, JArray.getStr(data, 0), nodeScale, False)
    endWhile
endfunction

-- method that gets called once user uninstalls it via MCM
function modUninstallMethod()
    -- remove the emotionInterpolation info to not pollute a save file
    JDB.setObj("emotionInterpolation", 0)
endfunction

Followers example

Similar to the first example, but now you need to store one more value - anger and list of victims (both are per-actor data). Also you have decided to not associate followers with the JDB database.

We will store all per-actor information in the following structure:

{
    "mood": 0,
    "anger": 0,
    "victims": []
}

Here you can see a map that contains 3 key-value associations: mood and anger (both values are zeros initially) and an empty array of victims: "victims": [].

Actual code:

function storeFollowerMood(form follower, float mood)
    -- get follower entry to write into it
    int entry = getActorEntry(follower)
    -- write mood into follower entry
    JValue.solveFltSetter(entry, ".mood", mood)
endfunction

function addFollowerVictim(form follower, form victim)
    -- get follower entry to write into it AND then get victims array
    int victims = JValue.solveObj(getActorEntry(follower), ".victims")
    -- add victim into array
    JArray.addForm(victims, victim)
endfunction

float function followerMood(form follower)
    -- get follower entry AND fetch mood
    return JValue.solveFlt(getActorEntry(follower), ".mood")
endfunction

float function followerAnger(form follower)
    return JValue.solveFlt(getActorEntry(follower), ".anger")
endfunction

-- find (or create new if not found) per-actor information containing mood, anger and array of victims
int function getActorEntry(form actor)
    int entry = JFormMap.getObj(self.followers, follower)
    -- if no entry found - create new from prototype-string
    if !entry
        entry = JValue.objectWithPrototype("{ \"mood\": 0, \"anger\": 0, \"victims\": [] }")
        JFormMap.setObj(self.followers, follower, entry)
    endif

    return entry
endfunction

-- property hides all black magick - retains & releases object
-- see 'Object lifetime management rules' section for more of it
int property followers hidden
    int function get()
        return _followers
    endFunction
    function set(int value)
        -- retainAndRelease releases previous _followers object
        -- and owns (retains) a new
        _followers = JValue.releaseAndRetain(_followers, value, "<myAwesomeMod>")
    endFunction
endProperty

int _followers = 0

-- initial setup function where you usually do the things once mod gets installed
function modSetupMethod()
    -- create and retain JFormMap container
    self.followers = JFormMap.object()
endfunction

-- method that gets called once user uninstalls it via MCM
function modUninstallMethod()
    -- release followers container to not pollute game save
    self.followers = 0
endfunction