Merge branch 'develop' into Bumber64-patch-2
						commit
						621ccef996
					
				@ -0,0 +1,361 @@
 | 
				
			|||||||
 | 
					--[[
 | 
				
			||||||
 | 
					custom-raw-tokens
 | 
				
			||||||
 | 
					Allows for reading custom tokens added to raws by mods
 | 
				
			||||||
 | 
					by Tachytaenius (wolfboyft)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Yes, non-vanilla raw tokens do quietly print errors into the error log but the error log gets filled with garbage anyway
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NOTE: This treats plant growths similarly to creature castes but there is no way to deselect a growth, so don't put a token you want to apply to a whole plant after any growth definitions
 | 
				
			||||||
 | 
					]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local _ENV = mkmodule("custom-raw-tokens")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local customRawTokensCache = {}
 | 
				
			||||||
 | 
					dfhack.onStateChange.customRawTokens = function(code)
 | 
				
			||||||
 | 
					    if code == SC_WORLD_UNLOADED then
 | 
				
			||||||
 | 
					        customRawTokensCache = {}
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function doToken(cacheTable, token, iter)
 | 
				
			||||||
 | 
					    local args, lenArgs = {}, 0
 | 
				
			||||||
 | 
					    for arg in iter do
 | 
				
			||||||
 | 
					        lenArgs = lenArgs + 1
 | 
				
			||||||
 | 
					        args[lenArgs] = arg
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    if lenArgs == 0 then
 | 
				
			||||||
 | 
					        cacheTable[token] = true
 | 
				
			||||||
 | 
					        return true
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        cacheTable[token] = args
 | 
				
			||||||
 | 
					        return table.unpack(args)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function getSubtype(item)
 | 
				
			||||||
 | 
					    if item:getSubtype() == -1 then return nil end -- number
 | 
				
			||||||
 | 
					    return dfhack.items.getSubtypeDef(item:getType(), item:getSubtype()) -- struct
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local rawStringsFieldNames = {
 | 
				
			||||||
 | 
					    [df.inorganic_raw] = "str",
 | 
				
			||||||
 | 
					    [df.plant_raw] = "raws",
 | 
				
			||||||
 | 
					    [df.creature_raw] = "raws",
 | 
				
			||||||
 | 
					    [df.itemdef_weaponst] = "raw_strings",
 | 
				
			||||||
 | 
					    [df.itemdef_trapcompst] = "raw_strings",
 | 
				
			||||||
 | 
					    [df.itemdef_toyst] = "raw_strings",
 | 
				
			||||||
 | 
					    [df.itemdef_toolst] = "raw_strings",
 | 
				
			||||||
 | 
					    [df.itemdef_instrumentst] = "raw_strings",
 | 
				
			||||||
 | 
					    [df.itemdef_armorst] = "raw_strings",
 | 
				
			||||||
 | 
					    [df.itemdef_ammost] = "raw_strings",
 | 
				
			||||||
 | 
					    [df.itemdef_siegeammost] = "raw_strings",
 | 
				
			||||||
 | 
					    [df.itemdef_glovesst] = "raw_strings",
 | 
				
			||||||
 | 
					    [df.itemdef_shoesst] = "raw_strings",
 | 
				
			||||||
 | 
					    [df.itemdef_shieldst] = "raw_strings",
 | 
				
			||||||
 | 
					    [df.itemdef_helmst] = "raw_strings",
 | 
				
			||||||
 | 
					    [df.itemdef_pantsst] = "raw_strings",
 | 
				
			||||||
 | 
					    [df.itemdef_foodst] = "raw_strings",
 | 
				
			||||||
 | 
					    [df.entity_raw] = "raws",
 | 
				
			||||||
 | 
					    [df.language_word] = "str",
 | 
				
			||||||
 | 
					    [df.language_symbol] = "str",
 | 
				
			||||||
 | 
					    [df.language_translation] = "str",
 | 
				
			||||||
 | 
					    [df.reaction] = "raw_strings",
 | 
				
			||||||
 | 
					    [df.interaction] = "str"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function getTokenCore(typeDefinition, token)
 | 
				
			||||||
 | 
					    -- Have we got a table for this item subtype/reaction/whatever?
 | 
				
			||||||
 | 
					    -- tostring is needed here because the same raceDefinition key won't give the same value every time
 | 
				
			||||||
 | 
					    local thisTypeDefCache = ensure_key(customRawTokensCache, tostring(typeDefinition))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -- Have we already extracted and stored this custom raw token for this type definition?
 | 
				
			||||||
 | 
					    local tokenData = thisTypeDefCache[token]
 | 
				
			||||||
 | 
					    if tokenData ~= nil then
 | 
				
			||||||
 | 
					        if type(tokenData) == "table" then
 | 
				
			||||||
 | 
					            return table.unpack(tokenData)
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            return tokenData
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -- Get data anew
 | 
				
			||||||
 | 
					    local success, dftype = pcall(function() return typeDefinition._type end)
 | 
				
			||||||
 | 
					    local rawStrings = typeDefinition[rawStringsFieldNames[dftype]]
 | 
				
			||||||
 | 
					    if not success or not rawStrings then
 | 
				
			||||||
 | 
					        error("Expected a raw type definition or instance in argument 1")
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local currentTokenIterator
 | 
				
			||||||
 | 
					    for _, rawString in ipairs(rawStrings) do -- e.g. "[CUSTOM_TOKEN:FOO:2]"
 | 
				
			||||||
 | 
					        local noBrackets = rawString.value:sub(2, -2)
 | 
				
			||||||
 | 
					        local iter = noBrackets:gmatch("[^:]*") -- iterate over all the text between colons between the brackets
 | 
				
			||||||
 | 
					        if token == iter() then
 | 
				
			||||||
 | 
					            currentTokenIterator = iter -- we return for last instance of token if multiple instances are present
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    if currentTokenIterator then
 | 
				
			||||||
 | 
					        return doToken(thisTypeDefCache, token, currentTokenIterator)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    -- Not present
 | 
				
			||||||
 | 
					    thisTypeDefCache[token] = false
 | 
				
			||||||
 | 
					    return false
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function getRaceCasteTokenCore(raceDefinition, casteNumber, token)
 | 
				
			||||||
 | 
					    -- Have we got tables for this race/caste pair?
 | 
				
			||||||
 | 
					    local thisRaceDefCache = ensure_key(customRawTokensCache, tostring(raceDefinition))
 | 
				
			||||||
 | 
					    local thisRaceDefCacheCaste = ensure_key(thisRaceDefCache, casteNumber)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -- Have we already extracted and stored this custom raw token for this race/caste pair?
 | 
				
			||||||
 | 
					    local tokenData = thisRaceDefCacheCaste[token]
 | 
				
			||||||
 | 
					    if tokenData ~= nil then
 | 
				
			||||||
 | 
					        if type(tokenData) == "table" then
 | 
				
			||||||
 | 
					            return table.unpack(tokenData)
 | 
				
			||||||
 | 
					        elseif tokenData == false and casteNumber ~= -1 then
 | 
				
			||||||
 | 
					            return getRaceCasteTokenCore(raceDefinition, -1, token)
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            return tokenData
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -- Get data anew. Here we have to track what caste is currently being written to
 | 
				
			||||||
 | 
					    local casteId, thisCasteActive
 | 
				
			||||||
 | 
					    if casteNumber ~= -1 then
 | 
				
			||||||
 | 
					        casteId = raceDefinition.caste[casteNumber].caste_id
 | 
				
			||||||
 | 
					        thisCasteActive = false
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        thisCasteActive = true
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local currentTokenIterator
 | 
				
			||||||
 | 
					    for _, rawString in ipairs(raceDefinition.raws) do
 | 
				
			||||||
 | 
					        local noBrackets = rawString.value:sub(2, -2)
 | 
				
			||||||
 | 
					        local iter = noBrackets:gmatch("[^:]*")
 | 
				
			||||||
 | 
					        local rawStringToken = iter()
 | 
				
			||||||
 | 
					        if rawStringToken == "CASTE" or rawStringToken == "SELECT_CASTE" or rawStringToken == "SELECT_ADDITIONAL_CASTE" or rawStringToken == "USE_CASTE" then
 | 
				
			||||||
 | 
					            local newCaste = iter()
 | 
				
			||||||
 | 
					            if newCaste then
 | 
				
			||||||
 | 
					                thisCasteActive = newCaste == casteId or rawStringToken == "SELECT_CASTE" and newCaste == "ALL"
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        elseif thisCasteActive and token == rawStringToken then
 | 
				
			||||||
 | 
					            currentTokenIterator = iter
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    if currentTokenIterator then
 | 
				
			||||||
 | 
					        return doToken(thisRaceDefCache, token, currentTokenIterator)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    thisRaceDefCacheCaste[token] = false
 | 
				
			||||||
 | 
					    if casteNumber == -1 then
 | 
				
			||||||
 | 
					        return false -- Don't get into an infinite loop!
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    -- Not present, try with no caste
 | 
				
			||||||
 | 
					    return getRaceCasteTokenCore(raceDefinition, -1, token)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function getPlantGrowthTokenCore(plantDefinition, growthNumber, token)
 | 
				
			||||||
 | 
					    -- Have we got tables for this plant/growth pair?
 | 
				
			||||||
 | 
					    local thisPlantDefCache = ensure_key(customRawTokensCache, tostring(plantDefinition))
 | 
				
			||||||
 | 
					    local thisPlantDefCacheGrowth = ensure_key(thisPlantDefCache, growthNumber)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -- Have we already extracted and stored this custom raw token for this plant/growth pair?
 | 
				
			||||||
 | 
					    local tokenData = thisPlantDefCacheGrowth[token]
 | 
				
			||||||
 | 
					    if tokenData ~= nil then
 | 
				
			||||||
 | 
					        if type(tokenData) == "table" then
 | 
				
			||||||
 | 
					            return table.unpack(tokenData)
 | 
				
			||||||
 | 
					        elseif tokenData == false and growthNumber ~= -1 then
 | 
				
			||||||
 | 
					            return getPlantGrowthTokenCore(plantDefinition, -1, token)
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            return tokenData
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -- Get data anew. Here we have to track what growth is currently being written to
 | 
				
			||||||
 | 
					    local growthId, thisGrowthActive
 | 
				
			||||||
 | 
					    if growthNumber ~= -1 then
 | 
				
			||||||
 | 
					        growthId = plantDefinition.growths[growthNumber].id
 | 
				
			||||||
 | 
					        thisGrowthActive = false
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        thisGrowthActive = true
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local currentTokenIterator
 | 
				
			||||||
 | 
					    for _, rawString in ipairs(plantDefinition.raws) do
 | 
				
			||||||
 | 
					        local noBrackets = rawString.value:sub(2, -2)
 | 
				
			||||||
 | 
					        local iter = noBrackets:gmatch("[^:]*")
 | 
				
			||||||
 | 
					        local rawStringToken = iter()
 | 
				
			||||||
 | 
					        if rawStringToken == "GROWTH" then
 | 
				
			||||||
 | 
					            local newGrowth = iter()
 | 
				
			||||||
 | 
					            if newGrowth then
 | 
				
			||||||
 | 
					                thisGrowthActive = newGrowth == growthId
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        elseif thisGrowthActive and token == rawStringToken then
 | 
				
			||||||
 | 
					            currentTokenIterator = iter
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    if currentTokenIterator then
 | 
				
			||||||
 | 
					        return doToken(thisPlantDefCache, token, currentTokenIterator)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    thisPlantDefCacheGrowth[token] = false
 | 
				
			||||||
 | 
					    if growthNumber == -1 then
 | 
				
			||||||
 | 
					        return false
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    return getPlantGrowthTokenCore(plantDefinition, -1, token)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--[[
 | 
				
			||||||
 | 
					Function signatures:
 | 
				
			||||||
 | 
					getToken(rawStruct, token)
 | 
				
			||||||
 | 
					getToken(rawStructInstance, token)
 | 
				
			||||||
 | 
					getToken(raceDefinition, casteNumber, token)
 | 
				
			||||||
 | 
					getToken(raceDefinition, casteString, token)
 | 
				
			||||||
 | 
					getToken(plantDefinition, growthNumber, token)
 | 
				
			||||||
 | 
					getToken(plantDefinition, growthString, token)
 | 
				
			||||||
 | 
					]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function getTokenArg1RaceDefinition(raceDefinition, b, c)
 | 
				
			||||||
 | 
					    local casteNumber, token
 | 
				
			||||||
 | 
					    if not c then
 | 
				
			||||||
 | 
					        -- 2 arguments
 | 
				
			||||||
 | 
					        casteNumber = -1
 | 
				
			||||||
 | 
					        assert(type(b) == "string", "Invalid argument 2 to getToken, must be a string")
 | 
				
			||||||
 | 
					        token = b
 | 
				
			||||||
 | 
					    elseif type(b) == "number" then
 | 
				
			||||||
 | 
					        -- 3 arguments, casteNumber
 | 
				
			||||||
 | 
					        assert(b == -1 or b < #raceDefinition.caste and math.floor(b) == b and b >= 0, "Invalid argument 2 to getToken, must be -1 or a caste name or number present in the creature raw")
 | 
				
			||||||
 | 
					        casteNumber = b
 | 
				
			||||||
 | 
					        assert(type(c) == "string", "Invalid argument 3 to getToken, must be a string")
 | 
				
			||||||
 | 
					        token = c
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        -- 3 arguments, casteString
 | 
				
			||||||
 | 
					        assert(type(b) == "string", "Invalid argument 2 to getToken, must be -1 or a caste name or number present in the creature raw")
 | 
				
			||||||
 | 
					        local casteString = b
 | 
				
			||||||
 | 
					        for i, v in ipairs(raceDefinition.caste) do
 | 
				
			||||||
 | 
					            if v.caste_id == casteString then
 | 
				
			||||||
 | 
					                casteNumber = i
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        assert(casteNumber, "Invalid argument 2 to getToken, caste name \"" .. casteString .. "\" not found")
 | 
				
			||||||
 | 
					        assert(type(c) == "string", "Invalid argument 3 to getToken, must be a string")
 | 
				
			||||||
 | 
					        token = c
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    return getRaceCasteTokenCore(raceDefinition, casteNumber, token)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function getTokenArg1PlantDefinition(plantDefinition, b, c)
 | 
				
			||||||
 | 
					    local growthNumber, token
 | 
				
			||||||
 | 
					    if not c then
 | 
				
			||||||
 | 
					        -- 2 arguments
 | 
				
			||||||
 | 
					        growthNumber = -1
 | 
				
			||||||
 | 
					        assert(type(b) == "string", "Invalid argument 2 to getToken, must be a string")
 | 
				
			||||||
 | 
					        token = b
 | 
				
			||||||
 | 
					    elseif type(b) == "number" then
 | 
				
			||||||
 | 
					        -- 3 arguments, growthNumber
 | 
				
			||||||
 | 
					        assert(b == -1 or b < #plantDefinition.growths and math.floor(b) == b and b >= 0, "Invalid argument 2 to getToken, must be -1 or a growth name or number present in the plant raw")
 | 
				
			||||||
 | 
					        growthNumber = b
 | 
				
			||||||
 | 
					        assert(type(c) == "string", "Invalid argument 3 to getToken, must be a string")
 | 
				
			||||||
 | 
					        token = c
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        -- 3 arguments, growthString
 | 
				
			||||||
 | 
					        assert(type(b) == "string", "Invalid argument 2 to getToken, must be -1 or a growth name or number present in the plant raw")
 | 
				
			||||||
 | 
					        local growthString = b
 | 
				
			||||||
 | 
					        for i, v in ipairs(plantDefinition.growths) do
 | 
				
			||||||
 | 
					            if v.id == growthString then
 | 
				
			||||||
 | 
					                growthNumber = i
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        assert(growthNumber, "Invalid argument 2 to getToken, growth name \"" .. growthString .. "\" not found")
 | 
				
			||||||
 | 
					        assert(type(c) == "string", "Invalid argument 3 to getToken, must be a string")
 | 
				
			||||||
 | 
					        token = c
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    return getPlantGrowthTokenCore(plantDefinition, growthNumber, token)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function getTokenArg1Else(userdata, token)
 | 
				
			||||||
 | 
					    assert(type(token) == "string", "Invalid argument 2 to getToken, must be a string")
 | 
				
			||||||
 | 
					    local rawStruct
 | 
				
			||||||
 | 
					    if df.is_instance(df.historical_entity, userdata) then
 | 
				
			||||||
 | 
					        rawStruct = userdata.entity_raw
 | 
				
			||||||
 | 
					    elseif df.is_instance(df.item, userdata) then
 | 
				
			||||||
 | 
					        rawStruct = getSubtype(userdata)
 | 
				
			||||||
 | 
					    elseif df.is_instance(df.job, userdata) then
 | 
				
			||||||
 | 
					        if job.job_type == df.job_type.CustomReaction then
 | 
				
			||||||
 | 
					            for i, v in ipairs(df.global.world.raws.reactions.reactions) do
 | 
				
			||||||
 | 
					                if job.reaction_name == v.code then
 | 
				
			||||||
 | 
					                    rawStruct = v
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    elseif df.is_instance(df.proj_itemst, userdata) then
 | 
				
			||||||
 | 
					        if not userdata.item then return false end
 | 
				
			||||||
 | 
					        if df.is_instance(df.item_plantst, userdata.item) or df.is_instance(df.item_plant_growthst, userdata.item) then
 | 
				
			||||||
 | 
					            -- use plant behaviour from getToken
 | 
				
			||||||
 | 
					            return getToken(userdata.item, token)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        rawStruct = userdata.item and userdata.item.subtype
 | 
				
			||||||
 | 
					    elseif df.is_instance(df.proj_unitst, userdata) then
 | 
				
			||||||
 | 
					        if not usertdata.unit then return false end
 | 
				
			||||||
 | 
					        -- special return so do tag here
 | 
				
			||||||
 | 
					        local unit = userdata.unit
 | 
				
			||||||
 | 
					        return getRaceCasteTokenCore(df.global.world.raws.creatures.all[unit.race], unit.caste, token)
 | 
				
			||||||
 | 
					    elseif df.is_instance(df.building_workshopst, userdata) or df.is_instance(df.building_furnacest, userdata) then
 | 
				
			||||||
 | 
					        rawStruct = df.building_def.find(userdata.custom_type)
 | 
				
			||||||
 | 
					    elseif df.is_instance(df.interaction_instance, userdata) then
 | 
				
			||||||
 | 
					        rawStruct = df.global.world.raws.interactions[userdata.interaction_id]
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        -- Assume raw struct *is* argument 1
 | 
				
			||||||
 | 
					        rawStruct = userdata
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    if not rawStruct then return false end
 | 
				
			||||||
 | 
					    return getTokenCore(rawStruct, token)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getToken(from, b, c)
 | 
				
			||||||
 | 
					    -- Argument processing
 | 
				
			||||||
 | 
					    assert(from and type(from) == "userdata", "Expected userdata for argument 1 to getToken")
 | 
				
			||||||
 | 
					    if df.is_instance(df.creature_raw, from) then
 | 
				
			||||||
 | 
					        -- Signatures from here:
 | 
				
			||||||
 | 
					        -- getToken(raceDefinition, casteNumber, token)
 | 
				
			||||||
 | 
					        -- getToken(raceDefinition, casteString, token)
 | 
				
			||||||
 | 
					        return getTokenArg1RaceDefinition(from, b, c)
 | 
				
			||||||
 | 
					    elseif df.is_instance(df.unit, from) then
 | 
				
			||||||
 | 
					        -- Signatures from here:
 | 
				
			||||||
 | 
					        -- getToken(rawStructInstance, token)
 | 
				
			||||||
 | 
					        assert(type(b) == "string", "Invalid argument 2 to getToken, must be a string")
 | 
				
			||||||
 | 
					        local unit, token = from, b
 | 
				
			||||||
 | 
					        return getRaceCasteTokenCore(df.global.world.raws.creatures.all[unit.race], unit.caste, token)
 | 
				
			||||||
 | 
					    elseif df.is_instance(df.plant_raw, from) then
 | 
				
			||||||
 | 
					        -- Signatures from here:
 | 
				
			||||||
 | 
					        -- getToken(plantDefinition, growthNumber, token)
 | 
				
			||||||
 | 
					        -- getToken(plantDefinition, growthString, token)
 | 
				
			||||||
 | 
					        return getTokenArg1PlantDefinition(from, b, c)
 | 
				
			||||||
 | 
					    elseif df.is_instance(df.plant, from) then
 | 
				
			||||||
 | 
					        -- Signatures from here:
 | 
				
			||||||
 | 
					        -- getToken(rawStructInstance, token)
 | 
				
			||||||
 | 
					        assert(type(b) == "string", "Invalid argument 2 to getToken, must be a string")
 | 
				
			||||||
 | 
					        local plantDefinition, plantGrowthNumber, token = df.global.world.raws.plants.all[from.material], -1, b
 | 
				
			||||||
 | 
					        return getPlantGrowthTokenCore(plantDefinition, plantGrowthNumber, token)
 | 
				
			||||||
 | 
					    elseif df.is_instance(df.item_plantst, from) then
 | 
				
			||||||
 | 
					        -- Signatures from here:
 | 
				
			||||||
 | 
					        -- getToken(rawStructInstance, token)
 | 
				
			||||||
 | 
					        local matInfo = dfhack.matinfo.decode(from)
 | 
				
			||||||
 | 
					        if matInfo.mode ~= "plant" then return false end
 | 
				
			||||||
 | 
					        assert(type(b) == "string", "Invalid argument 2 to getToken, must be a string")
 | 
				
			||||||
 | 
					        local plantDefinition, plantGrowthNumber, token = matInfo.plant, -1, b
 | 
				
			||||||
 | 
					        return getPlantGrowthTokenCore(plantDefinition, plantGrowthNumber, token)
 | 
				
			||||||
 | 
					    elseif df.is_instance(df.item_plant_growthst, from) then
 | 
				
			||||||
 | 
					        -- Signatures from here:
 | 
				
			||||||
 | 
					        -- getToken(rawStructInstance, token)
 | 
				
			||||||
 | 
					        local matInfo = dfhack.matinfo.decode(from)
 | 
				
			||||||
 | 
					        if matInfo.mode ~= "plant" then return false end
 | 
				
			||||||
 | 
					        assert(type(b) == "string", "Invalid argument 2 to getToken, must be a string")
 | 
				
			||||||
 | 
					        local plantDefinition, plantGrowthNumber, token = matInfo.plant, from.growth_print, b
 | 
				
			||||||
 | 
					        return getPlantGrowthTokenCore(plantDefinition, plantGrowthNumber, token)
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        -- Signatures from here:
 | 
				
			||||||
 | 
					        -- getToken(rawStruct, token)
 | 
				
			||||||
 | 
					        -- getToken(rawStructInstance, token)
 | 
				
			||||||
 | 
					        return getTokenArg1Else(from, b)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return _ENV
 | 
				
			||||||
@ -1 +1 @@
 | 
				
			|||||||
Subproject commit b8d48430aa13570a668464ce40294a589f1f0b1f
 | 
					Subproject commit 5fe90d06ebad4f9686545b7c961c2c07b8bcb799
 | 
				
			||||||
@ -1 +0,0 @@
 | 
				
			|||||||
repeat -name warn-starving -time 10 -timeUnits days -command [ warn-starving ]
 | 
					 | 
				
			||||||
@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					*
 | 
				
			||||||
 | 
					!.gitignore
 | 
				
			||||||
@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					// When displaying the names of partially-consumed items, show the percentage remaining
 | 
				
			||||||
 | 
					// Potentially useful for revealing why random pieces of cloth or thread aren't suitable for jobs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DEFINE_PARTIAL_ITEM_TWEAK(TYPE, DIM) \
 | 
				
			||||||
 | 
					struct partial_items_hook_##TYPE : df::item_##TYPE##st { \
 | 
				
			||||||
 | 
					    typedef df::item_##TYPE##st interpose_base; \
 | 
				
			||||||
 | 
					    DEFINE_VMETHOD_INTERPOSE(void, getItemDescription, (std::string *str, int8_t plurality)) \
 | 
				
			||||||
 | 
					    { \
 | 
				
			||||||
 | 
					        INTERPOSE_NEXT(getItemDescription)(str, plurality); \
 | 
				
			||||||
 | 
					        if (dimension != DIM) \
 | 
				
			||||||
 | 
					            str->append(stl_sprintf(" (%i%%)", std::max(1, dimension * 100 / DIM))); \
 | 
				
			||||||
 | 
					    } \
 | 
				
			||||||
 | 
					}; \
 | 
				
			||||||
 | 
					IMPLEMENT_VMETHOD_INTERPOSE(partial_items_hook_##TYPE, getItemDescription);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFINE_PARTIAL_ITEM_TWEAK(bar, 150)
 | 
				
			||||||
 | 
					DEFINE_PARTIAL_ITEM_TWEAK(drink, 150)
 | 
				
			||||||
 | 
					DEFINE_PARTIAL_ITEM_TWEAK(glob, 150)
 | 
				
			||||||
 | 
					DEFINE_PARTIAL_ITEM_TWEAK(liquid_misc, 150)
 | 
				
			||||||
 | 
					DEFINE_PARTIAL_ITEM_TWEAK(powder_misc, 150)
 | 
				
			||||||
 | 
					DEFINE_PARTIAL_ITEM_TWEAK(cloth, 10000)
 | 
				
			||||||
 | 
					DEFINE_PARTIAL_ITEM_TWEAK(sheet, 10000)
 | 
				
			||||||
 | 
					DEFINE_PARTIAL_ITEM_TWEAK(thread, 15000)
 | 
				
			||||||
@ -1 +1 @@
 | 
				
			|||||||
Subproject commit d9e390cd5509458bfbc0c1e2ecb01ae96bddc015
 | 
					Subproject commit 74f03c0e4a5a7818b7a1cbc3576ce2c3d30d3696
 | 
				
			||||||
@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					local rng = require('plugins.cxxrandom')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function test.cxxrandom_distributions()
 | 
				
			||||||
 | 
					    rng.normal_distribution:new(0,5)
 | 
				
			||||||
 | 
					    rng.real_distribution:new(-1,1)
 | 
				
			||||||
 | 
					    rng.int_distribution:new(-20,20)
 | 
				
			||||||
 | 
					    rng.bool_distribution:new(0.00000000001)
 | 
				
			||||||
 | 
					    rng.num_sequence:new(-1000,1000)
 | 
				
			||||||
 | 
					    -- no errors, no problem
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--[[
 | 
				
			||||||
 | 
					The below tests pass with their given seeds, if they begin failing
 | 
				
			||||||
 | 
					for a given platform, or all around, new seeds should be found.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note: these tests which assert RNG, are mere sanity checks
 | 
				
			||||||
 | 
					to ensure things haven't been severely broken by any changes
 | 
				
			||||||
 | 
					]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function test.cxxrandom_seed()
 | 
				
			||||||
 | 
					    local nd = rng.normal_distribution:new(0,500000)
 | 
				
			||||||
 | 
					    local e1 = rng.MakeNewEngine(1)
 | 
				
			||||||
 | 
					    local e2 = rng.MakeNewEngine(1)
 | 
				
			||||||
 | 
					    local e3 = rng.MakeNewEngine(2)
 | 
				
			||||||
 | 
					    local g1 = rng.crng:new(e1, true, nd)
 | 
				
			||||||
 | 
					    local g2 = rng.crng:new(e2, true, nd)
 | 
				
			||||||
 | 
					    local g3 = rng.crng:new(e3, true, nd)
 | 
				
			||||||
 | 
					    local v1 = g1:next()
 | 
				
			||||||
 | 
					    expect.eq(v1, g2:next())
 | 
				
			||||||
 | 
					    expect.ne(v1, g3:next())
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function test.cxxrandom_ranges()
 | 
				
			||||||
 | 
					    local e1 = rng.MakeNewEngine(1)
 | 
				
			||||||
 | 
					    local g1 = rng.crng:new(e1, true, rng.normal_distribution:new(0,1))
 | 
				
			||||||
 | 
					    local g2 = rng.crng:new(e1, true, rng.real_distribution:new(-5,5))
 | 
				
			||||||
 | 
					    local g3 = rng.crng:new(e1, true, rng.int_distribution:new(-5,5))
 | 
				
			||||||
 | 
					    local g4 = rng.crng:new(e1, true, rng.num_sequence:new(-5,5))
 | 
				
			||||||
 | 
					    for i = 1, 10 do
 | 
				
			||||||
 | 
					        local a = g1:next()
 | 
				
			||||||
 | 
					        local b = g2:next()
 | 
				
			||||||
 | 
					        local c = g3:next()
 | 
				
			||||||
 | 
					        local d = g4:next()
 | 
				
			||||||
 | 
					        expect.ge(a, -5)
 | 
				
			||||||
 | 
					        expect.ge(b, -5)
 | 
				
			||||||
 | 
					        expect.ge(c, -5)
 | 
				
			||||||
 | 
					        expect.ge(d, -5)
 | 
				
			||||||
 | 
					        expect.le(a, 5)
 | 
				
			||||||
 | 
					        expect.le(b, 5)
 | 
				
			||||||
 | 
					        expect.le(c, 5)
 | 
				
			||||||
 | 
					        expect.le(d, 5)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local gb = rng.crng:new(e1, true, rng.bool_distribution:new(0.00000000001))
 | 
				
			||||||
 | 
					    for i = 1, 10 do
 | 
				
			||||||
 | 
					        expect.false_(gb:next())
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function test.cxxrandom_exports()
 | 
				
			||||||
 | 
					    local id = rng.GenerateEngine(0)
 | 
				
			||||||
 | 
					    rng.NewSeed(id, 2022)
 | 
				
			||||||
 | 
					    expect.ge(rng.rollInt(id, 0, 1000), 0)
 | 
				
			||||||
 | 
					    expect.ge(rng.rollDouble(id, 0, 1), 0)
 | 
				
			||||||
 | 
					    expect.ge(rng.rollNormal(id, 5, 1), 0)
 | 
				
			||||||
 | 
					    expect.true_(rng.rollBool(id, 0.9999999999))
 | 
				
			||||||
 | 
					    local sid = rng.MakeNumSequence(0,8)
 | 
				
			||||||
 | 
					    rng.AddToSequence(sid, 9)
 | 
				
			||||||
 | 
					    rng.ShuffleSequence(sid, id)
 | 
				
			||||||
 | 
					    for i = 1, 10 do
 | 
				
			||||||
 | 
					        local v = rng.NextInSequence(sid)
 | 
				
			||||||
 | 
					        expect.ge(v, 0)
 | 
				
			||||||
 | 
					        expect.le(v, 9)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    rng.DestroyNumSequence(sid)
 | 
				
			||||||
 | 
					    rng.DestroyEngine(id)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
		Loading…
	
		Reference in New Issue