mirror of
https://github.com/PolarisSS13/Polaris.git
synced 2025-12-26 10:03:45 +00:00
Oops, I refactored Chemistry! (#8013)
Reorganizes Reagents dir structure Refactors reactions to /decl SSChemistry is no longer processing Optimization isn't so optimized Updates to distillery New decl repository methods Further tweaks
This commit is contained in:
26
code/modules/reagents/holder/distilling.dm
Normal file
26
code/modules/reagents/holder/distilling.dm
Normal file
@@ -0,0 +1,26 @@
|
||||
/datum/reagents/distilling/handle_reactions()
|
||||
if(QDELETED(my_atom))
|
||||
return FALSE
|
||||
if(my_atom.flags & NOREACT)
|
||||
return FALSE
|
||||
var/reaction_occurred
|
||||
var/list/eligible_reactions = list()
|
||||
var/list/effect_reactions = list()
|
||||
do
|
||||
reaction_occurred = FALSE
|
||||
for(var/i in reagent_list)
|
||||
var/datum/reagent/R = i
|
||||
if(SSchemistry.distilled_reactions_by_reagent[R.id])
|
||||
eligible_reactions |= SSchemistry.distilled_reactions_by_reagent[R.id]
|
||||
|
||||
for(var/i in eligible_reactions)
|
||||
var/decl/chemical_reaction/C = i
|
||||
if(C.can_happen(src) && C.process(src))
|
||||
effect_reactions |= C
|
||||
reaction_occurred = TRUE
|
||||
eligible_reactions.len = 0
|
||||
while(reaction_occurred)
|
||||
for(var/i in effect_reactions)
|
||||
var/decl/chemical_reaction/C = i
|
||||
C.post_reaction(src)
|
||||
update_total()
|
||||
519
code/modules/reagents/holder/holder.dm
Normal file
519
code/modules/reagents/holder/holder.dm
Normal file
@@ -0,0 +1,519 @@
|
||||
#define PROCESS_REACTION_ITER 5 //when processing a reaction, iterate this many times
|
||||
|
||||
/datum/reagents
|
||||
var/list/datum/reagent/reagent_list = list()
|
||||
var/total_volume = 0
|
||||
var/maximum_volume = 100
|
||||
var/atom/my_atom = null
|
||||
|
||||
/datum/reagents/New(var/max = 100, atom/A = null)
|
||||
..()
|
||||
maximum_volume = max
|
||||
my_atom = A
|
||||
|
||||
//I dislike having these here but map-objects are initialised before world/New() is called. >_>
|
||||
if(!SSchemistry.chemical_reagents)
|
||||
//Chemical Reagents - Initialises all /datum/reagent into a list indexed by reagent id
|
||||
var/paths = subtypesof(/datum/reagent)
|
||||
SSchemistry.chemical_reagents = list()
|
||||
for(var/path in paths)
|
||||
var/datum/reagent/D = new path()
|
||||
if(!D.name)
|
||||
continue
|
||||
SSchemistry.chemical_reagents[D.id] = D
|
||||
|
||||
/datum/reagents/Destroy()
|
||||
for(var/datum/reagent/R in reagent_list)
|
||||
qdel(R)
|
||||
reagent_list = null
|
||||
if(my_atom && my_atom.reagents == src)
|
||||
my_atom.reagents = null
|
||||
return ..()
|
||||
|
||||
/* Internal procs */
|
||||
|
||||
/datum/reagents/proc/get_free_space() // Returns free space.
|
||||
return maximum_volume - total_volume
|
||||
|
||||
/datum/reagents/proc/get_master_reagent() // Returns reference to the reagent with the biggest volume.
|
||||
var/the_reagent = null
|
||||
var/the_volume = 0
|
||||
|
||||
for(var/datum/reagent/A in reagent_list)
|
||||
if(A.volume > the_volume)
|
||||
the_volume = A.volume
|
||||
the_reagent = A
|
||||
|
||||
return the_reagent
|
||||
|
||||
/datum/reagents/proc/get_master_reagent_name() // Returns the name of the reagent with the biggest volume.
|
||||
var/the_name = null
|
||||
var/the_volume = 0
|
||||
for(var/datum/reagent/A in reagent_list)
|
||||
if(A.volume > the_volume)
|
||||
the_volume = A.volume
|
||||
the_name = A.name
|
||||
|
||||
return the_name
|
||||
|
||||
/datum/reagents/proc/get_master_reagent_id() // Returns the id of the reagent with the biggest volume.
|
||||
var/the_id = null
|
||||
var/the_volume = 0
|
||||
for(var/datum/reagent/A in reagent_list)
|
||||
if(A.volume > the_volume)
|
||||
the_volume = A.volume
|
||||
the_id = A.id
|
||||
|
||||
return the_id
|
||||
|
||||
/datum/reagents/proc/update_total() // Updates volume.
|
||||
total_volume = 0
|
||||
for(var/datum/reagent/R in reagent_list)
|
||||
if(R.volume < MINIMUM_CHEMICAL_VOLUME)
|
||||
del_reagent(R.id)
|
||||
else
|
||||
total_volume += R.volume
|
||||
return
|
||||
|
||||
/datum/reagents/proc/handle_reactions()
|
||||
if(QDELETED(my_atom))
|
||||
return FALSE
|
||||
if(my_atom.flags & NOREACT)
|
||||
return FALSE
|
||||
var/reaction_occurred
|
||||
var/list/eligible_reactions = list()
|
||||
var/list/effect_reactions = list()
|
||||
do
|
||||
reaction_occurred = FALSE
|
||||
for(var/i in reagent_list)
|
||||
var/datum/reagent/R = i
|
||||
if(SSchemistry.instant_reactions_by_reagent[R.id])
|
||||
eligible_reactions |= SSchemistry.instant_reactions_by_reagent[R.id]
|
||||
|
||||
for(var/i in eligible_reactions)
|
||||
var/decl/chemical_reaction/C = i
|
||||
if(C.can_happen(src) && C.process(src))
|
||||
effect_reactions |= C
|
||||
reaction_occurred = TRUE
|
||||
eligible_reactions.len = 0
|
||||
while(reaction_occurred)
|
||||
for(var/i in effect_reactions)
|
||||
var/decl/chemical_reaction/C = i
|
||||
C.post_reaction(src)
|
||||
update_total()
|
||||
|
||||
/* Holder-to-chemical */
|
||||
|
||||
/datum/reagents/proc/add_reagent(var/id, var/amount, var/data = null, var/safety = 0)
|
||||
if(!isnum(amount) || amount <= 0)
|
||||
return 0
|
||||
|
||||
update_total()
|
||||
amount = min(amount, get_free_space())
|
||||
|
||||
if(istype(my_atom,/obj/item/weapon/reagent_containers/food)) //The following code is targeted specifically at getting allergen reagents into food items, since for the most part they're not applied by default.
|
||||
var/list/add_reagents = list()
|
||||
var/totalnum = 0
|
||||
|
||||
for(var/item in data) //Try to find the ID
|
||||
var/add_reagent_id = null
|
||||
if(item in SSchemistry.chemical_reagents)
|
||||
add_reagent_id = item
|
||||
else if("[item]juice" in SSchemistry.chemical_reagents)
|
||||
add_reagent_id = "[item]juice"
|
||||
if(add_reagent_id) //If we did find it, add it to our list of reagents to add, and add the number to our total.
|
||||
add_reagents[add_reagent_id] += data[item]
|
||||
totalnum += data[item]
|
||||
|
||||
if(totalnum)
|
||||
var/multconst = amount/totalnum //We're going to add these extra reagents so that they share the ratio described, but only add up to 1x the existing amount at the most
|
||||
for(var/item in add_reagents)
|
||||
add_reagent(item,add_reagents[item]*multconst)
|
||||
|
||||
|
||||
|
||||
|
||||
for(var/datum/reagent/current in reagent_list)
|
||||
if(current.id == id)
|
||||
if(current.id == "blood")
|
||||
if(LAZYLEN(data) && !isnull(data["species"]) && !isnull(current.data["species"]) && data["species"] != current.data["species"]) // Species bloodtypes are already incompatible, this just stops it from mixing into the one already in a container.
|
||||
continue
|
||||
|
||||
current.volume += amount
|
||||
if(!isnull(data)) // For all we know, it could be zero or empty string and meaningful
|
||||
current.mix_data(data, amount)
|
||||
update_total()
|
||||
if(!safety)
|
||||
handle_reactions()
|
||||
if(my_atom)
|
||||
my_atom.on_reagent_change()
|
||||
return 1
|
||||
var/datum/reagent/D = SSchemistry.chemical_reagents[id]
|
||||
if(D)
|
||||
var/datum/reagent/R = new D.type()
|
||||
reagent_list += R
|
||||
R.holder = src
|
||||
R.volume = amount
|
||||
R.initialize_data(data)
|
||||
update_total()
|
||||
if(!safety)
|
||||
handle_reactions()
|
||||
if(my_atom)
|
||||
my_atom.on_reagent_change()
|
||||
return 1
|
||||
else
|
||||
crash_with("[my_atom] attempted to add a reagent called '[id]' which doesn't exist. ([usr])")
|
||||
return 0
|
||||
|
||||
/datum/reagents/proc/isolate_reagent(reagent)
|
||||
for(var/A in reagent_list)
|
||||
var/datum/reagent/R = A
|
||||
if(R.id != reagent)
|
||||
del_reagent(R.id)
|
||||
update_total()
|
||||
|
||||
/datum/reagents/proc/remove_reagent(var/id, var/amount, var/safety = 0)
|
||||
if(!isnum(amount))
|
||||
return 0
|
||||
for(var/datum/reagent/current in reagent_list)
|
||||
if(current.id == id)
|
||||
current.volume -= amount // It can go negative, but it doesn't matter
|
||||
update_total() // Because this proc will delete it then
|
||||
if(!safety)
|
||||
handle_reactions()
|
||||
if(my_atom)
|
||||
my_atom.on_reagent_change()
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/reagents/proc/del_reagent(var/id)
|
||||
for(var/datum/reagent/current in reagent_list)
|
||||
if (current.id == id)
|
||||
reagent_list -= current
|
||||
qdel(current)
|
||||
update_total()
|
||||
if(my_atom)
|
||||
my_atom.on_reagent_change()
|
||||
return 0
|
||||
|
||||
/datum/reagents/proc/has_reagent(var/id, var/amount = 0)
|
||||
for(var/datum/reagent/current in reagent_list)
|
||||
if(current.id == id)
|
||||
if(current.volume >= amount)
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
return 0
|
||||
|
||||
/datum/reagents/proc/has_any_reagent(var/list/check_reagents)
|
||||
for(var/datum/reagent/current in reagent_list)
|
||||
if(current.id in check_reagents)
|
||||
if(current.volume >= check_reagents[current.id])
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
return 0
|
||||
|
||||
/datum/reagents/proc/has_all_reagents(var/list/check_reagents)
|
||||
//this only works if check_reagents has no duplicate entries... hopefully okay since it expects an associative list
|
||||
var/missing = check_reagents.len
|
||||
for(var/datum/reagent/current in reagent_list)
|
||||
if(current.id in check_reagents)
|
||||
if(current.volume >= check_reagents[current.id])
|
||||
missing--
|
||||
return !missing
|
||||
|
||||
/datum/reagents/proc/clear_reagents()
|
||||
for(var/datum/reagent/current in reagent_list)
|
||||
del_reagent(current.id)
|
||||
return
|
||||
|
||||
/datum/reagents/proc/get_reagent_amount(var/id)
|
||||
for(var/datum/reagent/current in reagent_list)
|
||||
if(current.id == id)
|
||||
return current.volume
|
||||
return 0
|
||||
|
||||
/datum/reagents/proc/get_data(var/id)
|
||||
for(var/datum/reagent/current in reagent_list)
|
||||
if(current.id == id)
|
||||
return current.get_data()
|
||||
return 0
|
||||
|
||||
/datum/reagents/proc/get_reagents()
|
||||
. = list()
|
||||
for(var/datum/reagent/current in reagent_list)
|
||||
. += "[current.id] ([current.volume])"
|
||||
return english_list(., "EMPTY", "", ", ", ", ")
|
||||
|
||||
/* Holder-to-holder and similar procs */
|
||||
|
||||
/datum/reagents/proc/remove_any(var/amount = 1) // Removes up to [amount] of reagents from [src]. Returns actual amount removed.
|
||||
amount = min(amount, total_volume)
|
||||
|
||||
if(!amount)
|
||||
return
|
||||
|
||||
var/part = amount / total_volume
|
||||
|
||||
for(var/datum/reagent/current in reagent_list)
|
||||
var/amount_to_remove = current.volume * part
|
||||
remove_reagent(current.id, amount_to_remove, 1)
|
||||
|
||||
update_total()
|
||||
handle_reactions()
|
||||
return amount
|
||||
|
||||
/datum/reagents/proc/trans_to_holder(var/datum/reagents/target, var/amount = 1, var/multiplier = 1, var/copy = 0) // Transfers [amount] reagents from [src] to [target], multiplying them by [multiplier]. Returns actual amount removed from [src] (not amount transferred to [target]).
|
||||
if(!target || !istype(target))
|
||||
return
|
||||
|
||||
amount = max(0, min(amount, total_volume, target.get_free_space() / multiplier))
|
||||
|
||||
if(!amount)
|
||||
return
|
||||
|
||||
var/part = amount / total_volume
|
||||
|
||||
for(var/datum/reagent/current in reagent_list)
|
||||
var/amount_to_transfer = current.volume * part
|
||||
target.add_reagent(current.id, amount_to_transfer * multiplier, current.get_data(), safety = 1) // We don't react until everything is in place
|
||||
if(!copy)
|
||||
remove_reagent(current.id, amount_to_transfer, 1)
|
||||
|
||||
if(!copy)
|
||||
handle_reactions()
|
||||
target.handle_reactions()
|
||||
return amount
|
||||
|
||||
/* Holder-to-atom and similar procs */
|
||||
|
||||
//The general proc for applying reagents to things. This proc assumes the reagents are being applied externally,
|
||||
//not directly injected into the contents. It first calls touch, then the appropriate trans_to_*() or splash_mob().
|
||||
//If for some reason touch effects are bypassed (e.g. injecting stuff directly into a reagent container or person),
|
||||
//call the appropriate trans_to_*() proc.
|
||||
/datum/reagents/proc/trans_to(var/atom/target, var/amount = 1, var/multiplier = 1, var/copy = 0)
|
||||
touch(target) //First, handle mere touch effects
|
||||
|
||||
if(ismob(target))
|
||||
return splash_mob(target, amount, copy)
|
||||
if(isturf(target))
|
||||
return trans_to_turf(target, amount, multiplier, copy)
|
||||
if(isobj(target) && target.is_open_container())
|
||||
return trans_to_obj(target, amount, multiplier, copy)
|
||||
return 0
|
||||
|
||||
//Splashing reagents is messier than trans_to, the target's loc gets some of the reagents as well.
|
||||
/datum/reagents/proc/splash(var/atom/target, var/amount = 1, var/multiplier = 1, var/copy = 0, var/min_spill=0, var/max_spill=60)
|
||||
var/spill = 0
|
||||
if(!isturf(target) && target.loc)
|
||||
spill = amount*(rand(min_spill, max_spill)/100)
|
||||
amount -= spill
|
||||
if(spill)
|
||||
splash(target.loc, spill, multiplier, copy, min_spill, max_spill)
|
||||
|
||||
if(!trans_to(target, amount, multiplier, copy))
|
||||
touch(target, amount)
|
||||
|
||||
/datum/reagents/proc/trans_type_to(var/target, var/rtype, var/amount = 1)
|
||||
if (!target)
|
||||
return
|
||||
|
||||
var/datum/reagent/transfering_reagent = get_reagent(rtype)
|
||||
|
||||
if (istype(target, /atom))
|
||||
var/atom/A = target
|
||||
if (!A.reagents || !A.simulated)
|
||||
return
|
||||
|
||||
amount = min(amount, transfering_reagent.volume)
|
||||
|
||||
if(!amount)
|
||||
return
|
||||
|
||||
|
||||
var/datum/reagents/F = new /datum/reagents(amount)
|
||||
var/tmpdata = get_data(rtype)
|
||||
F.add_reagent(rtype, amount, tmpdata)
|
||||
remove_reagent(rtype, amount)
|
||||
|
||||
|
||||
if (istype(target, /atom))
|
||||
return F.trans_to(target, amount) // Let this proc check the atom's type
|
||||
else if (istype(target, /datum/reagents))
|
||||
return F.trans_to_holder(target, amount)
|
||||
|
||||
/datum/reagents/proc/trans_id_to(var/atom/target, var/id, var/amount = 1)
|
||||
if (!target || !target.reagents)
|
||||
return
|
||||
|
||||
amount = min(amount, get_reagent_amount(id))
|
||||
|
||||
if(!amount)
|
||||
return
|
||||
|
||||
var/datum/reagents/F = new /datum/reagents(amount)
|
||||
var/tmpdata = get_data(id)
|
||||
F.add_reagent(id, amount, tmpdata)
|
||||
remove_reagent(id, amount)
|
||||
|
||||
return F.trans_to(target, amount) // Let this proc check the atom's type
|
||||
|
||||
// When applying reagents to an atom externally, touch() is called to trigger any on-touch effects of the reagent.
|
||||
// This does not handle transferring reagents to things.
|
||||
// For example, splashing someone with water will get them wet and extinguish them if they are on fire,
|
||||
// even if they are wearing an impermeable suit that prevents the reagents from contacting the skin.
|
||||
/datum/reagents/proc/touch(var/atom/target, var/amount)
|
||||
if(ismob(target))
|
||||
touch_mob(target, amount)
|
||||
if(isturf(target))
|
||||
touch_turf(target, amount)
|
||||
if(isobj(target))
|
||||
touch_obj(target, amount)
|
||||
return
|
||||
|
||||
/datum/reagents/proc/touch_mob(var/mob/target)
|
||||
if(!target || !istype(target))
|
||||
return
|
||||
|
||||
for(var/datum/reagent/current in reagent_list)
|
||||
current.touch_mob(target, current.volume)
|
||||
|
||||
update_total()
|
||||
|
||||
/datum/reagents/proc/touch_turf(var/turf/target, var/amount)
|
||||
if(!target || !istype(target))
|
||||
return
|
||||
|
||||
for(var/datum/reagent/current in reagent_list)
|
||||
current.touch_turf(target, amount)
|
||||
|
||||
update_total()
|
||||
|
||||
/datum/reagents/proc/touch_obj(var/obj/target, var/amount)
|
||||
if(!target || !istype(target))
|
||||
return
|
||||
|
||||
for(var/datum/reagent/current in reagent_list)
|
||||
current.touch_obj(target, amount)
|
||||
|
||||
update_total()
|
||||
|
||||
// Attempts to place a reagent on the mob's skin.
|
||||
// Reagents are not guaranteed to transfer to the target.
|
||||
// Do not call this directly, call trans_to() instead.
|
||||
/datum/reagents/proc/splash_mob(var/mob/target, var/amount = 1, var/copy = 0)
|
||||
var/perm = 1
|
||||
if(isliving(target)) //will we ever even need to tranfer reagents to non-living mobs?
|
||||
var/mob/living/L = target
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
if(H.check_shields(0, null, null, null, "the spray") == 1) //If they block the spray, it does nothing.
|
||||
amount = 0
|
||||
perm = L.reagent_permeability()
|
||||
return trans_to_mob(target, amount, CHEM_TOUCH, perm, copy)
|
||||
|
||||
/datum/reagents/proc/trans_to_mob(var/mob/target, var/amount = 1, var/type = CHEM_BLOOD, var/multiplier = 1, var/copy = 0) // Transfer after checking into which holder...
|
||||
if(!target || !istype(target))
|
||||
return
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/C = target
|
||||
if(type == CHEM_BLOOD)
|
||||
var/datum/reagents/R = C.reagents
|
||||
return trans_to_holder(R, amount, multiplier, copy)
|
||||
if(type == CHEM_INGEST)
|
||||
var/datum/reagents/R = C.ingested
|
||||
return C.ingest(src, R, amount, multiplier, copy)
|
||||
if(type == CHEM_TOUCH)
|
||||
var/datum/reagents/R = C.touching
|
||||
return trans_to_holder(R, amount, multiplier, copy)
|
||||
else
|
||||
var/datum/reagents/R = new /datum/reagents(amount)
|
||||
. = trans_to_holder(R, amount, multiplier, copy)
|
||||
R.touch_mob(target)
|
||||
|
||||
/datum/reagents/proc/trans_to_turf(var/turf/target, var/amount = 1, var/multiplier = 1, var/copy = 0) // Turfs don't have any reagents (at least, for now). Just touch it.
|
||||
if(!target)
|
||||
return
|
||||
|
||||
var/datum/reagents/R = new /datum/reagents(amount * multiplier)
|
||||
. = trans_to_holder(R, amount, multiplier, copy)
|
||||
R.touch_turf(target, amount)
|
||||
return
|
||||
|
||||
/datum/reagents/proc/trans_to_obj(var/obj/target, var/amount = 1, var/multiplier = 1, var/copy = 0) // Objects may or may not; if they do, it's probably a beaker or something and we need to transfer properly; otherwise, just touch.
|
||||
if(!target)
|
||||
return
|
||||
|
||||
if(!target.reagents)
|
||||
var/datum/reagents/R = new /datum/reagents(amount * multiplier)
|
||||
. = trans_to_holder(R, amount, multiplier, copy)
|
||||
R.touch_obj(target, amount)
|
||||
return
|
||||
|
||||
return trans_to_holder(target.reagents, amount, multiplier, copy)
|
||||
|
||||
/* Atom reagent creation - use it all the time */
|
||||
|
||||
/atom/proc/create_reagents(var/max_vol, var/reagents_type = /datum/reagents)
|
||||
if(!ispath(reagents_type))
|
||||
reagents_type = /datum/reagents
|
||||
reagents = new reagents_type(max_vol, src)
|
||||
|
||||
// Aurora Cooking Port
|
||||
/datum/reagents/proc/get_reagent(var/id) // Returns reference to reagent matching passed ID
|
||||
for(var/datum/reagent/A in reagent_list)
|
||||
if (A.id == id)
|
||||
return A
|
||||
|
||||
return null
|
||||
|
||||
//Spreads the contents of this reagent holder all over the vicinity of the target turf.
|
||||
/datum/reagents/proc/splash_area(var/turf/epicentre, var/range = 3, var/portion = 1.0, var/multiplier = 1, var/copy = 0)
|
||||
var/list/things = dview(range, epicentre, INVISIBILITY_LIGHTING)
|
||||
var/list/turfs = list()
|
||||
for (var/turf/T in things)
|
||||
turfs += T
|
||||
if (!turfs.len)
|
||||
return//Nowhere to splash to, somehow
|
||||
//Create a temporary holder to hold all the amount that will be spread
|
||||
var/datum/reagents/R = new /datum/reagents(total_volume * portion * multiplier)
|
||||
trans_to_holder(R, total_volume * portion, multiplier, copy)
|
||||
//The exact amount that will be given to each turf
|
||||
var/turfportion = R.total_volume / turfs.len
|
||||
for (var/turf/T in turfs)
|
||||
var/datum/reagents/TR = new /datum/reagents(turfportion)
|
||||
R.trans_to_holder(TR, turfportion, 1, 0)
|
||||
TR.splash_turf(T)
|
||||
qdel(R)
|
||||
|
||||
|
||||
//Spreads the contents of this reagent holder all over the target turf, dividing among things in it.
|
||||
//50% is divided between mobs, 20% between objects, and whatever is left on the turf itself
|
||||
/datum/reagents/proc/splash_turf(var/turf/T, var/amount = null, var/multiplier = 1, var/copy = 0)
|
||||
if (isnull(amount))
|
||||
amount = total_volume
|
||||
else
|
||||
amount = min(amount, total_volume)
|
||||
if (amount <= 0)
|
||||
return
|
||||
var/list/mobs = list()
|
||||
for (var/mob/M in T)
|
||||
mobs += M
|
||||
var/list/objs = list()
|
||||
for (var/obj/O in T)
|
||||
objs += O
|
||||
if (objs.len)
|
||||
var/objportion = (amount * 0.2) / objs.len
|
||||
for (var/o in objs)
|
||||
var/obj/O = o
|
||||
trans_to(O, objportion, multiplier, copy)
|
||||
amount = min(amount, total_volume)
|
||||
if (mobs.len)
|
||||
var/mobportion = (amount * 0.5) / mobs.len
|
||||
for (var/m in mobs)
|
||||
var/mob/M = m
|
||||
trans_to(M, mobportion, multiplier, copy)
|
||||
trans_to(T, total_volume, multiplier, copy)
|
||||
if (total_volume <= 0)
|
||||
qdel(src)
|
||||
Reference in New Issue
Block a user