mirror of
https://github.com/goonstation/goonstation-2016.git
synced 2026-05-18 22:49:01 +01:00
dc4217b498
this is all very alpha please don't hate me too much if i fucked it up
438 lines
18 KiB
Plaintext
438 lines
18 KiB
Plaintext
var/global/list/material_cache = list()
|
|
/atom/var/datum/material/material = null
|
|
|
|
//This contains the names of the trigger lists on materials. Required for copying materials. Remember to keep this updated if you add new triggers.
|
|
var/global/list/triggerVars = list("triggersFail", "triggersTemp", "triggersChem", "triggersPickup", "triggersDrop", "triggersExp", "triggersOnAdd", "triggersOnLife", "triggersOnAttack", "triggersOnAttacked", "triggersOnEntered")
|
|
|
|
//Returns one of the base materials by id.
|
|
/proc/getCachedMaterial(var/mat)
|
|
if(!istext(mat) || !length(mat)) return null
|
|
if(!material_cache.len) buildMaterialCache()
|
|
if(material_cache.Find(mat)) return copyMaterial(material_cache[mat])
|
|
return null
|
|
|
|
/proc/mergeProperties(var/list/l1, var/list/l2, var/bias=0.5)
|
|
if(!l1) CRASH("SOME IDIOT TRIED TO MERGE LISTS WITHOUT PASSING LISTS TO MERGEPROPERTIES")
|
|
var/oBias = 1 - bias
|
|
|
|
var/list/merged = list()
|
|
|
|
for(var/o in l1)
|
|
merged.Add(o)
|
|
var/datum/material_property/Base = l1[o]
|
|
var/datum/material_property/newProp = new/datum/material_property()
|
|
merged[o] = newProp
|
|
newProp.name = o
|
|
newProp.value_max = Base.value_max
|
|
newProp.value_min = Base.value_min
|
|
newProp.value = Base.value
|
|
|
|
if(l2)
|
|
for(var/x in l2)
|
|
if(merged.Find(x))
|
|
var/datum/material_property/Base = l2[x]
|
|
var/datum/material_property/Modded = merged[x]
|
|
Modded.value_min = round(Modded.value_min * oBias + Base.value_min * bias)
|
|
Modded.value_max = round(Modded.value_max * oBias + Base.value_max * bias)
|
|
Modded.value = round(Modded.value * oBias + Base.value * bias)
|
|
else
|
|
var/datum/material_property/Base = l2[x]
|
|
var/datum/material_property/newProp = new/datum/material_property()
|
|
merged[x] = newProp
|
|
newProp.name = x
|
|
newProp.value_max = Base.value_max
|
|
newProp.value_min = Base.value_min
|
|
newProp.value = Base.value
|
|
return merged
|
|
|
|
//Returns a copy of a given material.
|
|
/proc/copyMaterial(var/datum/material/base)
|
|
if(!base || !istype(base, /datum/material))
|
|
var/datum/material/M = new/datum/material()
|
|
return M
|
|
else
|
|
var/datum/material/M = new base.type ()
|
|
|
|
var/list/copyProps = mergeProperties(base.properties)
|
|
M.properties = copyProps
|
|
|
|
for(var/X in base.vars)
|
|
if(X == "type" || X == "parent_type" || X == "tag" || X == "vars" || X == "properties") continue
|
|
|
|
if(X in triggerVars)
|
|
M.vars[X] = getFusedTriggers(base.vars[X], list()) //Pass in an empty list to basically copy the first one.
|
|
else
|
|
if(M.vars.Find(X))
|
|
if(istype(base.vars[X],/list))
|
|
var/list/oldList = base.vars[X]
|
|
M.vars[X] = oldList.Copy()
|
|
else
|
|
M.vars[X] = base.vars[X]
|
|
return M
|
|
return null
|
|
|
|
//Simply removes a material from an object.
|
|
/atom/proc/removeMaterial()
|
|
if(src.mat_changename)
|
|
src.remove_prefixes(99)
|
|
src.remove_suffixes(99)
|
|
src.UpdateName()
|
|
//src.name = initial(src.name)
|
|
|
|
if(src.mat_changedesc)
|
|
src.desc = initial(src.desc)
|
|
|
|
src.alpha = initial(src.alpha)
|
|
src.color = initial(src.color)
|
|
|
|
src.UpdateOverlays(null, "material")
|
|
|
|
src.material = null
|
|
return
|
|
|
|
//Time for some super verbose proc names.
|
|
/proc/get_material_trait_desc(var/datum/material/mat1)
|
|
var/string = ""
|
|
var/list/allTriggers = (mat1.triggersFail + mat1.triggersTemp + mat1.triggersChem + mat1.triggersPickup + mat1.triggersDrop + mat1.triggersExp + mat1.triggersOnAdd + mat1.triggersOnLife + mat1.triggersOnAttack + mat1.triggersOnAttacked + mat1.triggersOnEntered)
|
|
for(var/datum/materialProc/P in allTriggers)
|
|
if(length(P.desc))
|
|
if(length(string))
|
|
if(!findtext(string,P.desc))
|
|
string += " " + P.desc
|
|
else
|
|
string = P.desc
|
|
return string
|
|
|
|
//Sets the material of an object. PLEASE USE THIS TO SET MATERIALS UNLESS YOU KNOW WHAT YOU'RE DOING.
|
|
/atom/proc/setMaterial(var/datum/material/mat1)
|
|
if(!mat1 ||!istype(mat1, /datum/material)) return
|
|
var/traitDesc = get_material_trait_desc(mat1)
|
|
var/strPrefix = jointext(mat1.prefixes, " ")
|
|
var/strSuffix = jointext(mat1.suffixes, " ")
|
|
// Setting mat_changename/desc == 0 is almost as conspicuous as having "good gold chameleon bombs".
|
|
// Real_desc/name is a substandard implementation, but it will prevent some midas touch exploits (Convair880).
|
|
if (src.mat_changename) // now that I've cleaned up the name prefix/suffix stuff, let's try this, in the hopes that eveything that SHOULD override UpdateName() actually DOES - Haine
|
|
src.remove_prefixes(99) // janky but better than ending up with uhh
|
|
src.remove_suffixes(99) // "This is a good gold good gold good gold good gold good gold good gold good gold good gold good gold good gold bedsheet."
|
|
src.name_prefix(strPrefix ? strPrefix : "")
|
|
src.name_prefix(length(getQualityName(mat1.quality)) ? getQualityName(mat1.quality) : "")
|
|
src.name_prefix(mat1.name ? mat1.name : "")
|
|
src.name_suffix(strSuffix ? "of [strSuffix]" : "")
|
|
src.UpdateName()
|
|
/* if (istype(src, /obj))
|
|
var/obj/O = src
|
|
O.name = "[strPrefix ? strPrefix + " ":""][length(getQualityName(mat1.quality)) ? getQualityName(mat1.quality) + " ":""][mat1.name ? mat1.name + " ":""][!isnull(O.real_name) ? "[O.real_name]" : "[initial(O.name)]"][strSuffix ? (" of " + strSuffix) : ""]"
|
|
else
|
|
src.name = "[strPrefix ? strPrefix + " ":""][length(getQualityName(mat1.quality)) ? getQualityName(mat1.quality) + " ":""][mat1.name ? mat1.name + " ":""][initial(src.name)][strSuffix ? (" of " + strSuffix) : ""]"
|
|
*/
|
|
if (src.mat_changedesc)
|
|
if (istype(src, /obj))
|
|
var/obj/O2 = src
|
|
O2.desc = "[!isnull(O2.real_desc) ? "[O2.real_desc]" : "[initial(O2.desc)]"] It is made of [mat1.name].[length(traitDesc) ? " " + traitDesc : ""]"
|
|
else
|
|
src.desc = "[initial(src.desc)] It is made of [mat1.name].[length(traitDesc) ? " " + traitDesc : ""]"
|
|
if(mat1.texture)
|
|
src.setTexture(mat1.texture, mat1.texture_blend, "material")
|
|
src.alpha = mat1.alpha
|
|
src.color = mat1.color
|
|
src.material = mat1
|
|
mat1.owner = src
|
|
mat1.triggerOnAdd(src)
|
|
src.onMaterialChanged()
|
|
return
|
|
|
|
/proc/getProcessedMaterialForm(var/datum/material/MAT)
|
|
if (!istype(MAT))
|
|
return /obj/item/material_piece // just in case
|
|
|
|
// higher on this list means higher priority, be careful with it!
|
|
if (MAT.material_flags & MATERIAL_CRYSTAL)
|
|
return /obj/item/material_piece/block
|
|
if (MAT.material_flags & MATERIAL_METAL)
|
|
return /obj/item/material_piece
|
|
if (MAT.material_flags & MATERIAL_ORGANIC)
|
|
return /obj/item/material_piece/wad
|
|
if (MAT.material_flags & MATERIAL_CLOTH)
|
|
return /obj/item/material_piece/cloth
|
|
if (MAT.material_flags & MATERIAL_RUBBER)
|
|
return /obj/item/material_piece/block
|
|
if (MAT.material_flags & MATERIAL_ENERGY)
|
|
return /obj/item/material_piece/sphere
|
|
|
|
return /obj/item/material_piece
|
|
|
|
//Increases generations on material triggers and handles removal if over the generation cap.
|
|
/proc/handleTriggerGenerations(var/list/toDo)
|
|
for(var/datum/materialProc/current in toDo)
|
|
if(current.max_generations != -1 && (toDo[current] + 1) > current.max_generations) toDo.Remove(current)
|
|
else toDo[current] = (toDo[current] + 1)
|
|
return toDo
|
|
|
|
//Fuses two material trigger lists.
|
|
/proc/getFusedTriggers(var/list/L1 , var/list/L2)
|
|
var/list/newList = list()
|
|
for(var/datum/materialProc/toCopy in L1) //Copy list 1 with new instances of trigger datum.
|
|
var/datum/materialProc/P = new toCopy.type()
|
|
newList.Add(P) //Add new instance of datum
|
|
newList[P] = L1[toCopy] //Set generation
|
|
for(var/varCopy in toCopy.vars)
|
|
if(varCopy == "type" || varCopy == "id" || varCopy == "parent_type" || varCopy == "tag" || varCopy == "vars") continue
|
|
if(!issaved(toCopy.vars[varCopy])) continue
|
|
P.vars[varCopy] = toCopy.vars[varCopy]
|
|
|
|
for(var/datum/materialProc/A in L2) //Go through second list
|
|
if((locate(A.type) in newList)) //We already have that trigger type from the other list
|
|
var/datum/materialProc/existing = (locate(A.type) in newList) //Get the trigger datum from the other list
|
|
if(newList[existing] <= L2[A]) continue //If the generation of the datum that already exists is lower or equal to the new one, leave it alone.
|
|
else newList[existing] = L2[A] //Otherwise set the generation to the generation of the second copy because it is lower.
|
|
else //Trigger type isnt in the list yet.
|
|
var/datum/materialProc/newProc = new A.type() //Create a new instance
|
|
newList.Add(newProc) //Add to list
|
|
newList[newProc] = L2[A] //Set generation
|
|
for(var/varCopy in A.vars)
|
|
if(varCopy == "type" || varCopy == "id" || varCopy == "parent_type" || varCopy == "tag" || varCopy == "vars") continue
|
|
if(!issaved(A.vars[varCopy])) continue
|
|
newProc.vars[varCopy] = A.vars[varCopy]
|
|
return newList
|
|
|
|
/proc/getFusedMaterial(var/datum/material/mat1,var/datum/material/mat2)
|
|
//Merges two materials and returns result as new material.
|
|
return getInterpolatedMaterial(mat1, mat2, 0.5)
|
|
|
|
/proc/getInterpolatedMaterial(var/datum/material/mat1,var/datum/material/mat2,var/t)
|
|
var/ot = 1 - t
|
|
var/datum/material/newMat = new()
|
|
|
|
newMat.quality = round(mat1.quality * ot + mat2.quality * t)
|
|
|
|
newMat.prefixes = (mat1.prefixes | mat2.prefixes)
|
|
newMat.suffixes = (mat1.suffixes | mat2.suffixes)
|
|
|
|
//newMat.name = getInterpolatedName(mat1.name, mat2.name, t)
|
|
newMat.name = getInterpolatedName(mat1.name, mat2.name, 0.5)
|
|
newMat.mat_id = "([mat1.mat_id]+[mat2.mat_id])"
|
|
newMat.alpha = round(mat1.alpha * ot + mat2.alpha * t)
|
|
newMat.color = rgb(round(GetRedPart(mat1.color) * ot + GetRedPart(mat2.color) * t), round(GetGreenPart(mat1.color) * ot + GetGreenPart(mat2.color) * t), round(GetBluePart(mat1.color) * ot + GetBluePart(mat2.color) * t))
|
|
newMat.properties = mergeProperties(mat1.properties, mat2.properties, t)
|
|
//--
|
|
newMat.triggersFail = getFusedTriggers(mat1.triggersFail, mat2.triggersFail)
|
|
newMat.triggersTemp = getFusedTriggers(mat1.triggersTemp, mat2.triggersTemp)
|
|
newMat.triggersChem = getFusedTriggers(mat1.triggersChem, mat2.triggersChem)
|
|
newMat.triggersPickup = getFusedTriggers(mat1.triggersPickup, mat2.triggersPickup)
|
|
newMat.triggersDrop = getFusedTriggers(mat1.triggersDrop, mat2.triggersDrop)
|
|
newMat.triggersExp = getFusedTriggers(mat1.triggersExp, mat2.triggersExp)
|
|
newMat.triggersOnAdd = getFusedTriggers(mat1.triggersOnAdd, mat2.triggersOnAdd)
|
|
newMat.triggersOnLife = getFusedTriggers(mat1.triggersOnLife, mat2.triggersOnLife)
|
|
newMat.triggersOnAttack = getFusedTriggers(mat1.triggersOnAttack, mat2.triggersOnAttack)
|
|
newMat.triggersOnAttacked = getFusedTriggers(mat1.triggersOnAttacked, mat2.triggersOnAttacked)
|
|
newMat.triggersOnEntered = getFusedTriggers(mat1.triggersOnEntered, mat2.triggersOnEntered)
|
|
|
|
handleTriggerGenerations(newMat.triggersFail)
|
|
handleTriggerGenerations(newMat.triggersTemp)
|
|
handleTriggerGenerations(newMat.triggersChem)
|
|
handleTriggerGenerations(newMat.triggersPickup)
|
|
handleTriggerGenerations(newMat.triggersDrop)
|
|
handleTriggerGenerations(newMat.triggersExp)
|
|
handleTriggerGenerations(newMat.triggersOnAdd)
|
|
handleTriggerGenerations(newMat.triggersOnLife)
|
|
handleTriggerGenerations(newMat.triggersOnAttack)
|
|
handleTriggerGenerations(newMat.triggersOnAttacked)
|
|
handleTriggerGenerations(newMat.triggersOnEntered)
|
|
//--
|
|
|
|
//Texture merging. SUPER DUPER UGLY AAAAH
|
|
if(mat2.texture && !mat1.texture)
|
|
newMat.texture = mat2.texture
|
|
newMat.texture_blend = mat2.texture_blend
|
|
else if (mat1.texture && !mat2.texture)
|
|
newMat.texture = mat2.texture
|
|
newMat.texture_blend = mat1.texture_blend
|
|
else if (mat1.texture && mat2.texture)
|
|
if(mat1.generation == mat2.generation)
|
|
//Mat1 has higher priority in this case. Optional: implement some shitty blended texture thing. probably a bad idea.
|
|
newMat.texture = mat1.texture
|
|
newMat.texture_blend = mat1.texture_blend
|
|
else
|
|
if(mat1.generation < mat2.generation)
|
|
newMat.texture = mat1.texture
|
|
newMat.texture_blend = mat1.texture_blend
|
|
else
|
|
newMat.texture = mat2.texture
|
|
newMat.texture_blend = mat2.texture_blend
|
|
//
|
|
|
|
//This is sub-optimal and only used because im dumb
|
|
if(mat1.material_flags & MATERIAL_CRYSTAL || mat2.material_flags & MATERIAL_CRYSTAL) newMat.material_flags |= MATERIAL_CRYSTAL
|
|
if(mat1.material_flags & MATERIAL_METAL || mat2.material_flags & MATERIAL_METAL) newMat.material_flags |= MATERIAL_METAL
|
|
if(mat1.material_flags & MATERIAL_CLOTH || mat2.material_flags & MATERIAL_CLOTH) newMat.material_flags |= MATERIAL_CLOTH
|
|
if(mat1.material_flags & MATERIAL_ORGANIC || mat2.material_flags & MATERIAL_ORGANIC) newMat.material_flags |= MATERIAL_ORGANIC
|
|
if(mat1.material_flags & MATERIAL_ENERGY || mat2.material_flags & MATERIAL_ENERGY) newMat.material_flags |= MATERIAL_ENERGY
|
|
if(mat1.material_flags & MATERIAL_RUBBER || mat2.material_flags & MATERIAL_RUBBER) newMat.material_flags |= MATERIAL_RUBBER
|
|
|
|
newMat.parent_materials.Add(mat1)
|
|
newMat.parent_materials.Add(mat2)
|
|
|
|
return newMat
|
|
|
|
//Merges two material names into one.
|
|
/proc/getInterpolatedName(var/mat1, var/mat2, var/t)
|
|
var/ot = 1 - t
|
|
var/part1 = copytext(mat1, 1, round((length(mat1) * ot) + 0.5))
|
|
var/part2 = copytext(mat2, round((length(mat2) * ot) + 0.5), 0)
|
|
return capitalize(ckey("[part1][part2]"))
|
|
|
|
//Returns a string for when a material fail or breaks depending on its material flags.
|
|
/proc/getMatFailString(var/flag)
|
|
if(flag & MATERIAL_METAL && flag & MATERIAL_CRYSTAL && flag & MATERIAL_CLOTH)
|
|
return "frays apart into worthless dusty fibers"
|
|
if(flag & MATERIAL_METAL && flag & MATERIAL_CRYSTAL)
|
|
return "cracks and shatters into unworkable dust"
|
|
if(flag & MATERIAL_CLOTH && flag & MATERIAL_CRYSTAL)
|
|
return "shatters into useless brittle fibers"
|
|
if(flag & MATERIAL_ENERGY && flag & MATERIAL_CRYSTAL)
|
|
return "violently disintegrates into vapor"
|
|
if(flag & MATERIAL_ENERGY && flag & MATERIAL_METAL)
|
|
return "shines brightly before self-vaporizing"
|
|
if(flag & MATERIAL_ENERGY && flag & MATERIAL_CLOTH)
|
|
return "bursts into flames and is gone almost instantly"
|
|
if(flag & MATERIAL_ENERGY && flag & MATERIAL_ORGANIC)
|
|
return "catches on fire and rapidly burns to ash"
|
|
if(flag & MATERIAL_ORGANIC)
|
|
return "crumbles into worthless slime"
|
|
if(flag & MATERIAL_CRYSTAL)
|
|
return "shatters to dust and blows away"
|
|
if(flag & MATERIAL_METAL)
|
|
return "disintegrates into useless flakes"
|
|
if(flag & MATERIAL_CLOTH)
|
|
return "frays apart into useless strands"
|
|
if(flag & MATERIAL_ENERGY)
|
|
return "suddenly vanishes into nothingness"
|
|
if(flag & MATERIAL_RUBBER)
|
|
return "melts into an unworkable pile of slop"
|
|
return "comes apart"
|
|
|
|
//Translates a material flag into a string.
|
|
/proc/getMatFlagString(var/flag)
|
|
switch(flag)
|
|
if(MATERIAL_CRYSTAL)
|
|
return "Crystal"
|
|
if(MATERIAL_METAL)
|
|
return "Metal"
|
|
if(MATERIAL_CLOTH)
|
|
return "Fabric"
|
|
if(MATERIAL_ORGANIC)
|
|
return "Organic Matter"
|
|
if(MATERIAL_ENERGY)
|
|
return "Energy Source"
|
|
if(MATERIAL_RUBBER)
|
|
return "Rubber"
|
|
else
|
|
return "Unknown"
|
|
return
|
|
|
|
//Simply returns a string for a given quality. Used as prefix for objects.
|
|
/proc/getQualityName(var/quality)
|
|
switch(quality)
|
|
if(-INFINITY to -101)
|
|
return "useless"
|
|
if(-100 to -91)
|
|
return "atrocious"
|
|
if(-90 to -81)
|
|
return "wretched"
|
|
if(-80 to -71)
|
|
return "crap"
|
|
if(-70 to -61)
|
|
return "awful"
|
|
if(-60 to -51)
|
|
return "terrible"
|
|
if(-50 to -41)
|
|
return "bad"
|
|
if(-40 to -31)
|
|
return "shabby"
|
|
if(-30 to -21)
|
|
return "mediocre"
|
|
if(-20 to -11)
|
|
return "low-quality"
|
|
if(-10 to -1)
|
|
return "poor"
|
|
if(0)
|
|
return ""
|
|
if(1 to 10)
|
|
return "decent"
|
|
if(11 to 20)
|
|
return "fine"
|
|
if(21 to 30)
|
|
return "good"
|
|
if(31 to 40)
|
|
return "great"
|
|
if(41 to 50)
|
|
return "high-quality"
|
|
if(51 to 60)
|
|
return "excellent"
|
|
if(61 to 70)
|
|
return "superb"
|
|
if(71 to 80)
|
|
return "incredible"
|
|
if(81 to 90)
|
|
return "amazing"
|
|
if(91 to 100)
|
|
return "supreme"
|
|
if(101 to INFINITY)
|
|
return "perfect"
|
|
else
|
|
return "odd"
|
|
return
|
|
|
|
//Checks if a material matches a recipe and returns the recipe if a match is found. returns null if nothing matches it.
|
|
/proc/matchesMaterialRecipe(var/datum/material/M)
|
|
for(var/datum/material_recipe/R in materialRecipes)
|
|
if(R.validate(M)) return R
|
|
return null
|
|
|
|
//Searches the parent materials of the given material, up to a given generation, for an id.
|
|
//Useful if you want to figure out if a given material was used in the making of another material.
|
|
//Keep in mind that this can be expensive so use it only when you have to.
|
|
/proc/hasParentMaterial(var/datum/material/M, var/search_id, var/max_generations = 3)
|
|
return searchMatTree(M, search_id, 0, max_generations)
|
|
|
|
/proc/searchMatTree(var/datum/material/M, var/id, var/current_depth, var/max_depth = 3)
|
|
if(!M || !id) return null
|
|
if(!M.parent_materials.len) return null
|
|
for(var/datum/material/CM in M.parent_materials)
|
|
if(CM.mat_id == id) return CM
|
|
if(current_depth + 1 <= max_depth)
|
|
var/temp = searchMatTree(CM, id, current_depth + 1, max_depth)
|
|
if(temp) return temp
|
|
return null
|
|
|
|
//Yes hello apparently we need a proc for this because theres a million types of different wires and cables.
|
|
/proc/applyCableMaterials(var/atom/C, var/datum/material/insulator, var/datum/material/conductor)
|
|
if(!conductor) return // silly
|
|
|
|
if(istype(C, /obj/cable))
|
|
var/obj/cable/cable = C
|
|
cable.insulator = insulator
|
|
cable.conductor = conductor
|
|
|
|
if (cable.insulator)
|
|
cable.setMaterial(copyMaterial(cable.insulator))
|
|
cable.name = "[cable.insulator.name]-insulated [cable.conductor.name]-cable"
|
|
cable.color = cable.insulator.color
|
|
else
|
|
cable.setMaterial(copyMaterial(cable.conductor))
|
|
cable.name = "uninsulated [cable.conductor.name]-cable"
|
|
cable.color = cable.conductor.color
|
|
|
|
if(istype(C, /obj/item/cable_coil))
|
|
var/obj/item/cable_coil/coil = C
|
|
|
|
coil.insulator = insulator
|
|
coil.conductor = conductor
|
|
|
|
if (coil.insulator)
|
|
coil.setMaterial(copyMaterial(coil.insulator))
|
|
coil.color = coil.insulator.color
|
|
else
|
|
coil.setMaterial(copyMaterial(coil.conductor))
|
|
coil.color = coil.conductor.color
|
|
coil.updateName() |