//the em field is where the fun happens /* Deuterium-deuterium fusion : 40 x 10^7 K Deuterium-tritium fusion: 4.5 x 10^7 K */ //#DEFINE MAX_STORED_ENERGY (held_plasma[GAS_PLASMA] * held_plasma[GAS_PLASMA] * SPECIFIC_HEAT_TOXIN) /obj/effect/rust_em_field name = "EM Field" desc = "A coruscating, barely visible field of energy. It is shaped like a slightly flattened torus." icon = 'icons/obj/machines/rust.dmi' icon_state = "emfield_s1" alpha = 50 var/major_radius = 0 //longer radius in meters = field_strength * 0.21875, max = 8.75 var/minor_radius = 0 //shorter radius in meters = field_strength * 0.2125, max = 8.625 var/size = 1 //diameter in tiles var/volume_covered = 0 //atmospheric volume covered var/obj/machinery/power/rust_core/owned_core var/list/dormant_reactant_quantities = new layer = ABOVE_OBJ_LAYER var/energy = 0 var/mega_energy = 0 var/radiation = 0 var/frequency = 1 var/field_strength = 0.01 //in teslas, max is 50T var/obj/machinery/rust/rad_source/radiator var/datum/gas_mixture/held_plasma = new var/particle_catchers[13] var/emp_overload = 0 /obj/effect/rust_em_field/New(loc, var/obj/machinery/power/rust_core/new_owned_core) ..() //create radiator for(var/obj/machinery/rust/rad_source/rad in range(0)) radiator = rad if(!radiator) radiator = new() owned_core = new_owned_core if(!owned_core) qdel(src) //create the gimmicky things to handle field collisions var/obj/effect/rust_particle_catcher/catcher catcher = new (locate(src.x,src.y,src.z)) catcher.parent = src catcher.SetSize(1) particle_catchers.Add(catcher) catcher = new (locate(src.x-1,src.y,src.z)) catcher.parent = src catcher.SetSize(3) particle_catchers.Add(catcher) catcher = new (locate(src.x+1,src.y,src.z)) catcher.parent = src catcher.SetSize(3) particle_catchers.Add(catcher) catcher = new (locate(src.x,src.y+1,src.z)) catcher.parent = src catcher.SetSize(3) particle_catchers.Add(catcher) catcher = new (locate(src.x,src.y-1,src.z)) catcher.parent = src catcher.SetSize(3) particle_catchers.Add(catcher) catcher = new (locate(src.x-2,src.y,src.z)) catcher.parent = src catcher.SetSize(5) particle_catchers.Add(catcher) catcher = new (locate(src.x+2,src.y,src.z)) catcher.parent = src catcher.SetSize(5) particle_catchers.Add(catcher) catcher = new (locate(src.x,src.y+2,src.z)) catcher.parent = src catcher.SetSize(5) particle_catchers.Add(catcher) catcher = new (locate(src.x,src.y-2,src.z)) catcher.parent = src catcher.SetSize(5) particle_catchers.Add(catcher) catcher = new (locate(src.x-3,src.y,src.z)) catcher.parent = src catcher.SetSize(7) particle_catchers.Add(catcher) catcher = new (locate(src.x+3,src.y,src.z)) catcher.parent = src catcher.SetSize(7) particle_catchers.Add(catcher) catcher = new (locate(src.x,src.y+3,src.z)) catcher.parent = src catcher.SetSize(7) particle_catchers.Add(catcher) catcher = new (locate(src.x,src.y-3,src.z)) catcher.parent = src catcher.SetSize(7) particle_catchers.Add(catcher) //init values major_radius = field_strength * 0.21875// max = 8.75 minor_radius = field_strength * 0.2125// max = 8.625 volume_covered = PI * major_radius * minor_radius * 2.5 * 2.5 * 1000 processing_objects.Add(src) /obj/effect/rust_em_field/process() //make sure the field generator is still intact if(!owned_core) qdel(src) //handle radiation if(!radiator) radiator = new /obj/machinery/rust/rad_source() radiator.mega_energy += radiation radiator.source_alive++ radiation = 0 //update values var/transfer_ratio = field_strength / 50 //higher field strength will result in faster plasma aggregation major_radius = field_strength * 0.21875// max = 8.75m minor_radius = field_strength * 0.2125// max = 8.625m volume_covered = PI * major_radius * minor_radius * 2.5 * 2.5 * 2.5 * 7 * 7 * transfer_ratio //one tile = 2.5m*2.5m*2.5m //add plasma from the surrounding environment var/datum/gas_mixture/environment = loc.return_air() //hack in some stuff to remove plasma from the air because SCIENCE //the amount of plasma pulled in each update is relative to the field strength, with 50T (max field strength) = 100% of area covered by the field //at minimum strength, 0.25% of the field volume is pulled in per update (?) //have a max of 1000 moles suspended if(held_plasma[GAS_PLASMA] < transfer_ratio * 1000) var/moles_covered = environment.return_pressure()*volume_covered/(environment.temperature * R_IDEAL_GAS_EQUATION) // to_chat(world, "moles_covered: [moles_covered]") // //Since nearly every mathematical expression in this file is wrong in some way, it actually relied on a bug in ZAS to not cause ridiculous nonsense. //That bug got fixed, and ridiculous nonsense was caused. //The min() in the following line of code simulates that bug, because god is dead. //Obviously the correct solution would be to fix all the errors in here, but that would involve rebalancing every magic number in this file. var/datum/gas_mixture/gas_covered = environment.remove(min(moles_covered, environment.molar_density() * CELL_VOLUME)) var/datum/gas_mixture/plasma_captured = new() // plasma_captured.temperature = gas_covered.temperature plasma_captured.adjust_gas(GAS_PLASMA, round(gas_covered[GAS_PLASMA] * transfer_ratio)) // gas_covered.subtract(plasma_captured) // held_plasma.merge(plasma_captured) // environment.merge(gas_covered) //let the particles inside the field react React() //forcibly radiate any excess energy /*var/energy_max = transfer_ratio * 100000 if(mega_energy > energy_max) var/energy_lost = rand( 1.5 * (mega_energy - energy_max), 2.5 * (mega_energy - energy_max) ) mega_energy -= energy_lost radiation += energy_lost*/ //change held plasma temp according to energy levels //SPECIFIC_HEAT_TOXIN if(mega_energy > 0 && held_plasma[GAS_PLASMA]) var/heat_capacity = held_plasma.heat_capacity()//200 * number of plasma moles if(heat_capacity > 0.0003) //formerly MINIMUM_HEAT_CAPACITY held_plasma.temperature = (heat_capacity + mega_energy * 35000)/heat_capacity //if there is too much plasma in the field, lose some /*if( held_plasma[GAS_PLASMA] > (MOLES_CELLSTANDARD * 7) * (50 / field_strength) ) Loseplasma()*/ if(held_plasma[GAS_PLASMA] > 1) //lose a random amount of plasma back into the air, increased by the field strength (want to switch this over to frequency eventually) var/loss_ratio = rand() * (0.05 + (0.05 * 50 / field_strength)) // to_chat(world, "lost [loss_ratio*100]% of held plasma") // var/datum/gas_mixture/plasma_lost = new plasma_lost.temperature = held_plasma.temperature // plasma_lost.adjust_gas(GAS_PLASMA, held_plasma[GAS_PLASMA] * loss_ratio) held_plasma.subtract(plasma_lost) // environment.merge(plasma_lost) radiation += loss_ratio * mega_energy * 0.1 mega_energy -= loss_ratio * mega_energy * 0.1 else held_plasma.multiply(0) //Pretty sure this is equivalent but this thing is so fucked up already I don't even care enough to make sure //handle some reactants formatting for(var/reactant in dormant_reactant_quantities) var/amount = dormant_reactant_quantities[reactant] if(amount < 1) dormant_reactant_quantities.Remove(reactant) else if(amount >= 1000000) var/radiate = rand(3 * amount / 4, amount / 4) dormant_reactant_quantities[reactant] -= radiate radiation += radiate return 1 /obj/effect/rust_em_field/proc/ChangeFieldStrength(var/new_strength) var/calc_size = 1 emp_overload = 0 if(new_strength <= 50) calc_size = 1 else if(new_strength <= 200) calc_size = 3 else if(new_strength <= 500) calc_size = 5 else calc_size = 7 if(new_strength > 900) emp_overload = 1 // field_strength = new_strength change_size(calc_size) /obj/effect/rust_em_field/proc/ChangeFieldFrequency(var/new_frequency) frequency = new_frequency /obj/effect/rust_em_field/proc/AddEnergy(var/a_energy, var/a_mega_energy, var/a_frequency) var/energy_loss_ratio = 0 if(a_frequency != src.frequency) energy_loss_ratio = 1 / abs(a_frequency - src.frequency) energy += a_energy - a_energy * energy_loss_ratio mega_energy += a_mega_energy - a_mega_energy * energy_loss_ratio while(energy > 100000) energy -= 100000 mega_energy += 0.1 /obj/effect/rust_em_field/proc/AddParticles(var/name, var/quantity = 1) if(name in dormant_reactant_quantities) dormant_reactant_quantities[name] += quantity else if(name != "proton" && name != "electron" && name != "neutron") dormant_reactant_quantities.Add(name) dormant_reactant_quantities[name] = quantity /obj/effect/rust_em_field/proc/RadiateAll(var/ratio_lost = 1) for(var/particle in dormant_reactant_quantities) radiation += dormant_reactant_quantities[particle] dormant_reactant_quantities.Remove(particle) radiation += mega_energy mega_energy = 0 //lose all held plasma back into the air var/datum/gas_mixture/environment = loc.return_air() environment.merge(held_plasma) /obj/effect/rust_em_field/proc/change_size(var/newsize = 1) // var/changed = 0 switch(newsize) if(1) size = 1 icon = 'icons/obj/machines/rust.dmi' icon_state = "emfield_s1" pixel_x = 0 pixel_y = 0 // changed = 1 if(3) size = 3 icon = 'icons/effects/96x96.dmi' icon_state = "emfield_s3" pixel_x = -32 * PIXEL_MULTIPLIER pixel_y = -32 * PIXEL_MULTIPLIER // changed = 3 if(5) size = 5 icon = 'icons/effects/160x160.dmi' icon_state = "emfield_s5" pixel_x = -64 * PIXEL_MULTIPLIER pixel_y = -64 * PIXEL_MULTIPLIER // changed = 5 if(7) size = 7 icon = 'icons/effects/224x224.dmi' icon_state = "emfield_s7" pixel_x = -96 * PIXEL_MULTIPLIER pixel_y = -96 * PIXEL_MULTIPLIER // changed = 7 for(var/obj/effect/rust_particle_catcher/catcher in particle_catchers) catcher.UpdateSize() return changed //the !!fun!! part /obj/effect/rust_em_field/proc/React() //loop through the reactants in random order var/list/reactants_reacting_pool = dormant_reactant_quantities.Copy() /* for(var/reagent in dormant_reactant_quantities) to_chat(world, " before: [reagent]: [dormant_reactant_quantities[reagent]]") */ //cant have any reactions if there aren't any reactants present if(reactants_reacting_pool.len) //determine a random amount to actually react this cycle, and remove it from the standard pool //this is a hack, and quite nonrealistic :( for(var/reactant in reactants_reacting_pool) reactants_reacting_pool[reactant] = rand(0,reactants_reacting_pool[reactant]) dormant_reactant_quantities[reactant] -= reactants_reacting_pool[reactant] if(!reactants_reacting_pool[reactant]) reactants_reacting_pool -= reactant //loop through all the reacting reagents, picking out random reactions for them var/list/produced_reactants = new/list var/list/primary_reactant_pool = reactants_reacting_pool.Copy() while(primary_reactant_pool.len) //pick one of the unprocessed reacting reagents randomly var/cur_primary_reactant = pick(primary_reactant_pool) primary_reactant_pool.Remove(cur_primary_reactant) // to_chat(world, "primary reactant chosen: [cur_primary_reactant]") //grab all the possible reactants to have a reaction with var/list/possible_secondary_reactants = reactants_reacting_pool.Copy() //if there is only one of a particular reactant, then it can not react with itself so remove it possible_secondary_reactants[cur_primary_reactant] -= 1 if(possible_secondary_reactants[cur_primary_reactant] < 1) possible_secondary_reactants.Remove(cur_primary_reactant) //loop through and work out all the possible reactions var/list/possible_reactions = new/list for(var/cur_secondary_reactant in possible_secondary_reactants) if(possible_secondary_reactants[cur_secondary_reactant] < 1) continue var/datum/fusion_reaction/cur_reaction = get_fusion_reaction(cur_primary_reactant, cur_secondary_reactant) if(cur_reaction) // to_chat(world, "secondary reactant: [cur_secondary_reactant], [reaction_products.len]") possible_reactions.Add(cur_reaction) //if there are no possible reactions here, abandon this primary reactant and move on if(!possible_reactions.len) // to_chat(world, "no reactions") continue //split up the reacting atoms between the possible reactions while(possible_reactions.len) //pick a random substance to react with var/datum/fusion_reaction/cur_reaction = pick(possible_reactions) possible_reactions.Remove(cur_reaction) //set the randmax to be the lower of the two involved reactants var/max_num_reactants = reactants_reacting_pool[cur_reaction.primary_reactant] > reactants_reacting_pool[cur_reaction.secondary_reactant] ? \ reactants_reacting_pool[cur_reaction.secondary_reactant] : reactants_reacting_pool[cur_reaction.primary_reactant] if(max_num_reactants < 1) continue //make sure we have enough energy if(mega_energy < max_num_reactants * cur_reaction.energy_consumption) max_num_reactants = round(mega_energy / cur_reaction.energy_consumption) if(max_num_reactants < 1) continue //randomly determined amount to react var/amount_reacting = rand(1, max_num_reactants) //removing the reacting substances from the list of substances that are primed to react this cycle //if there aren't enough of that substance (there should be) then modify the reactant amounts accordingly if( reactants_reacting_pool[cur_reaction.primary_reactant] - amount_reacting >= 0 ) reactants_reacting_pool[cur_reaction.primary_reactant] -= amount_reacting else amount_reacting = reactants_reacting_pool[cur_reaction.primary_reactant] reactants_reacting_pool[cur_reaction.primary_reactant] = 0 //same again for secondary reactant if( reactants_reacting_pool[cur_reaction.secondary_reactant] - amount_reacting >= 0 ) reactants_reacting_pool[cur_reaction.secondary_reactant] -= amount_reacting else reactants_reacting_pool[cur_reaction.primary_reactant] += amount_reacting - reactants_reacting_pool[cur_reaction.primary_reactant] amount_reacting = reactants_reacting_pool[cur_reaction.secondary_reactant] reactants_reacting_pool[cur_reaction.secondary_reactant] = 0 //remove the consumed energy mega_energy -= max_num_reactants * cur_reaction.energy_consumption //add any produced energy mega_energy += max_num_reactants * cur_reaction.energy_production //add any produced radiation radiation += max_num_reactants * cur_reaction.radiation //create the reaction products for(var/reactant in cur_reaction.products) var/success = 0 for(var/check_reactant in produced_reactants) if(check_reactant == reactant) produced_reactants[reactant] += cur_reaction.products[reactant] * amount_reacting success = 1 break if(!success) produced_reactants[reactant] = cur_reaction.products[reactant] * amount_reacting //this reaction is done, and can't be repeated this sub-cycle possible_reactions.Remove(cur_reaction.secondary_reactant) // /*if(new_radiation) if(!radiating) radiating = 1 PeriodicRadiate()*/ //loop through the newly produced reactants and add them to the pool //var/list/neutronic_radiation = new //var/list/protonic_radiation = new for(var/reactant in produced_reactants) AddParticles(reactant, produced_reactants[reactant]) // to_chat(world, "produced: [reactant], [dormant_reactant_quantities[reactant]]") //check whether there are reactants left, and add them back to the pool for(var/reactant in reactants_reacting_pool) AddParticles(reactant, reactants_reacting_pool[reactant]) // to_chat(world, "retained: [reactant], [reactants_reacting_pool[reactant]]") /obj/effect/rust_em_field/Destroy() //radiate everything in one giant burst for(var/obj/effect/rust_particle_catcher/catcher in particle_catchers) qdel(catcher) owned_core.owned_field = null owned_core = null RadiateAll() processing_objects.Remove(src) . = ..() /obj/effect/rust_em_field/bullet_act(var/obj/item/projectile/Proj) if(Proj.flag != "bullet") AddEnergy(Proj.damage * 100, 0, 1) update_icon() return 0