Merge remote-tracking branch 'bay12-upstream/master' into development

This commit is contained in:
skull132
2016-10-07 00:58:39 +03:00
1594 changed files with 53994 additions and 48590 deletions
+31 -26
View File
@@ -11,6 +11,9 @@ mob/proc/airflow_stun()
if(!(status_flags & CANSTUN) && !(status_flags & CANWEAKEN))
src << "<span class='notice'>You stay upright as the air rushes past you.</span>"
return 0
if(buckled)
src << "<span class='notice'>Air suddenly rushes past you!</span>"
return 0
if(!lying)
src << "<span class='warning'>The sudden rush of air knocks you over!</span>"
Weaken(5)
@@ -24,7 +27,7 @@ mob/living/carbon/slime/airflow_stun()
mob/living/carbon/human/airflow_stun()
if(shoes)
if(shoes.flags & NOSLIP) return 0
if(shoes.item_flags & NOSLIP) return 0
..()
atom/movable/proc/check_airflow_movable(n)
@@ -62,6 +65,19 @@ obj/item/check_airflow_movable(n)
/atom/movable/var/tmp/airflow_time = 0
/atom/movable/var/tmp/last_airflow = 0
/atom/movable/proc/AirflowCanMove(n)
return 1
/mob/AirflowCanMove(n)
if(status_flags & GODMODE)
return 0
if(buckled)
return 0
var/obj/item/shoes = get_equipped_item(slot_shoes)
if(istype(shoes) && (shoes.item_flags & NOSLIP))
return 0
return 1
/atom/movable/proc/GotoAirflowDest(n)
if(!airflow_dest) return
if(airflow_speed < 0) return
@@ -69,18 +85,13 @@ obj/item/check_airflow_movable(n)
if(airflow_speed)
airflow_speed = n/max(get_dist(src,airflow_dest),1)
return
last_airflow = world.time
if(airflow_dest == loc)
step_away(src,loc)
if(!src.AirflowCanMove(n))
return
if(ismob(src))
if(src:status_flags & GODMODE)
return
if(istype(src, /mob/living/carbon/human))
if(src:buckled)
return
if(src:shoes && src:shoes.flags & NOSLIP)
return
src << "\red You are sucked away by airflow!"
src << "<span class='danger'>You are sucked away by airflow!</span>"
last_airflow = world.time
var/airflow_falloff = 9 - sqrt((x - airflow_dest.x) ** 2 + (y - airflow_dest.y) ** 2)
if(airflow_falloff < 1)
airflow_dest = null
@@ -116,8 +127,9 @@ obj/item/check_airflow_movable(n)
if(!istype(loc, /turf))
break
step_towards(src, src.airflow_dest)
if(ismob(src) && src:client)
src:client:move_delay = world.time + vsc.airflow_mob_slowdown
var/mob/M = src
if(istype(M) && M.client)
M.setMoveCooldown(vsc.airflow_mob_slowdown)
airflow_dest = null
airflow_speed = 0
airflow_time = 0
@@ -134,18 +146,11 @@ obj/item/check_airflow_movable(n)
return
if(airflow_dest == loc)
step_away(src,loc)
if(!src.AirflowCanMove(n))
return
if(ismob(src))
if(src:status_flags & GODMODE)
return
if(istype(src, /mob/living/carbon/human))
if(src:buckled)
return
if(src:shoes)
if(istype(src:shoes, /obj/item/clothing/shoes/magboots))
if(src:shoes.flags & NOSLIP)
return
src << "\red You are pushed away by airflow!"
last_airflow = world.time
src << "<span clas='danger'>You are pushed away by airflow!</span>"
last_airflow = world.time
var/airflow_falloff = 9 - sqrt((x - airflow_dest.x) ** 2 + (y - airflow_dest.y) ** 2)
if(airflow_falloff < 1)
airflow_dest = null
@@ -197,7 +202,7 @@ atom/movable/proc/airflow_hit(atom/A)
mob/airflow_hit(atom/A)
for(var/mob/M in hearers(src))
M.show_message("\red <B>\The [src] slams into \a [A]!</B>",1,"\red You hear a loud slam!",2)
M.show_message("<span class='danger'>\The [src] slams into \a [A]!</span>",1,"<span class='danger'>You hear a loud slam!</span>",2)
playsound(src.loc, "smash.ogg", 25, 1, -1)
var/weak_amt = istype(A,/obj/item) ? A:w_class : rand(1,5) //Heheheh
Weaken(weak_amt)
@@ -205,7 +210,7 @@ mob/airflow_hit(atom/A)
obj/airflow_hit(atom/A)
for(var/mob/M in hearers(src))
M.show_message("\red <B>\The [src] slams into \a [A]!</B>",1,"\red You hear a loud slam!",2)
M.show_message("<span class='danger'>\The [src] slams into \a [A]!</span>",1,"<span class='danger'>You hear a loud slam!</span>",2)
playsound(src.loc, "smash.ogg", 25, 1, -1)
. = ..()
@@ -215,7 +220,7 @@ obj/item/airflow_hit(atom/A)
mob/living/carbon/human/airflow_hit(atom/A)
// for(var/mob/M in hearers(src))
// M.show_message("\red <B>[src] slams into [A]!</B>",1,"\red You hear a loud slam!",2)
// M.show_message("<span class='danger'>[src] slams into [A]!</span>",1,"<span class='danger'>You hear a loud slam!</span>",2)
playsound(src.loc, "punch", 25, 1, -1)
if (prob(33))
loc:add_blood(src)
+2 -5
View File
@@ -1,7 +1,4 @@
/atom/var/pressure_resistance = ONE_ATMOSPHERE
/atom/proc/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0)
//Purpose: Determines if the object (or airflow) can pass this atom.
//Called by: Movement, airflow.
@@ -64,9 +61,9 @@ turf/c_airblock(turf/other)
#ifdef ZLEVELS
if(other.z != src.z)
if(other.z < src.z)
if(!istype(src, /turf/simulated/floor/open)) return BLOCKED
if(!istype(src, /turf/simulated/open)) return BLOCKED
else
if(!istype(other, /turf/simulated/floor/open)) return BLOCKED
if(!istype(other, /turf/simulated/open)) return BLOCKED
#endif
var/result = 0
+4 -4
View File
@@ -109,13 +109,13 @@ Class Procs:
simulated_turf_count++
S.update_air_properties()
admin_notice({"<span class='danger'>Geometry initialized in [round(0.1*(world.timeofday-start_time),0.1)] seconds.</b></span>
admin_notice({"<span class='danger'>Geometry initialized in [round(0.1*(world.timeofday-start_time),0.1)] seconds.</span>
<span class='info'>
Total Simulated Turfs: [simulated_turf_count]
Total Zones: [zones.len]
Total Edges: [edges.len]
Total Active Edges: [active_edges.len ? "<span class='danger'>[active_edges.len]</span>" : "None"]
Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]</font>
Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]
</span>"}, R_DEBUG)
@@ -187,7 +187,7 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
#ifdef ZASDBG
if(updated != updating.len)
tick_progress = "[updating.len - updated] tiles left unupdated."
world << "\red [tick_progress]"
world << "<span class='danger'>[tick_progress]</span>"
. = 0
#endif
@@ -372,4 +372,4 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
/datum/controller/air_system/proc/remove_edge(connection_edge/E)
edges.Remove(E)
if(!E.sleeping) active_edges.Remove(E)
if(!E.sleeping) active_edges.Remove(E)
+1 -148
View File
@@ -84,154 +84,7 @@ client/proc/Test_ZAS_Connection(var/turf/simulated/T as turf)
else
mob << "both turfs can merge."
/*zone/proc/DebugDisplay(client/client)
if(!istype(client))
return
if(!dbg_output)
dbg_output = 1 //Don't want to be spammed when someone investigates a zone...
if(!client.zone_debug_images)
client.zone_debug_images = list()
var/list/current_zone_images = list()
for(var/turf/T in contents)
current_zone_images += image('icons/misc/debug_group.dmi', T, null, TURF_LAYER)
for(var/turf/space/S in unsimulated_tiles)
current_zone_images += image('icons/misc/debug_space.dmi', S, null, TURF_LAYER)
client << "<u>Zone Air Contents</u>"
client << "Oxygen: [air.oxygen]"
client << "Nitrogen: [air.nitrogen]"
client << "Phoron: [air.phoron]"
client << "Carbon Dioxide: [air.carbon_dioxide]"
client << "Temperature: [air.temperature] K"
client << "Heat Energy: [air.temperature * air.heat_capacity()] J"
client << "Pressure: [air.return_pressure()] KPa"
client << ""
client << "Space Tiles: [length(unsimulated_tiles)]"
client << "Movable Objects: [length(movables())]"
client << "<u>Connections: [length(connections)]</u>"
for(var/connection/C in connections)
client << "\ref[C] [C.A] --> [C.B] [(C.indirect?"Open":"Closed")]"
current_zone_images += image('icons/misc/debug_connect.dmi', C.A, null, TURF_LAYER)
current_zone_images += image('icons/misc/debug_connect.dmi', C.B, null, TURF_LAYER)
client << "Connected Zones:"
for(var/zone/zone in connected_zones)
client << "\ref[zone] [zone] - [connected_zones[zone]] (Connected)"
for(var/zone/zone in closed_connection_zones)
client << "\ref[zone] [zone] - [closed_connection_zones[zone]] (Unconnected)"
for(var/C in connections)
if(!istype(C,/connection))
client << "[C] (Not Connection!)"
if(!client.zone_debug_images)
client.zone_debug_images = list()
client.zone_debug_images[src] = current_zone_images
client.images += client.zone_debug_images[src]
else
dbg_output = 0
client.images -= client.zone_debug_images[src]
client.zone_debug_images.Remove(src)
if(air_master)
for(var/zone/Z in air_master.zones)
if(Z.air == air && Z != src)
var/turf/zloc = pick(Z.contents)
client << "\red Illegal air datum shared by: [zloc.loc.name]"*/
/*client/proc/TestZASRebuild()
set category = "Debug"
// var/turf/turf = get_turf(mob)
var/zone/current_zone = mob.loc:zone
if(!current_zone)
src << "There is no zone there!"
return
var/list/current_adjacents = list()
var/list/overlays = list()
var/adjacent_id
var/lowest_id
var/list/identical_ids = list()
var/list/turfs = current_zone.contents.Copy()
var/current_identifier = 1
for(var/turf/simulated/current in turfs)
lowest_id = null
current_adjacents = list()
for(var/direction in cardinal)
var/turf/simulated/adjacent = get_step(current, direction)
if(!current.ZCanPass(adjacent))
continue
if(turfs.Find(adjacent))
current_adjacents += adjacent
adjacent_id = turfs[adjacent]
if(adjacent_id && (!lowest_id || adjacent_id < lowest_id))
lowest_id = adjacent_id
if(!lowest_id)
lowest_id = current_identifier++
identical_ids += lowest_id
overlays += image('icons/misc/debug_rebuild.dmi',, "[lowest_id]")
for(var/turf/simulated/adjacent in current_adjacents)
adjacent_id = turfs[adjacent]
if(adjacent_id != lowest_id)
if(adjacent_id)
adjacent.overlays -= overlays[adjacent_id]
identical_ids[adjacent_id] = lowest_id
turfs[adjacent] = lowest_id
adjacent.overlays += overlays[lowest_id]
sleep(5)
if(turfs[current])
current.overlays -= overlays[turfs[current]]
turfs[current] = lowest_id
current.overlays += overlays[lowest_id]
sleep(5)
var/list/final_arrangement = list()
for(var/turf/simulated/current in turfs)
current_identifier = identical_ids[turfs[current]]
current.overlays -= overlays[turfs[current]]
current.overlays += overlays[current_identifier]
sleep(5)
if( current_identifier > final_arrangement.len )
final_arrangement.len = current_identifier
final_arrangement[current_identifier] = list(current)
else
final_arrangement[current_identifier] += current
//lazy but fast
final_arrangement.Remove(null)
src << "There are [final_arrangement.len] unique segments."
for(var/turf/current in turfs)
current.overlays -= overlays
return final_arrangement*/
client/proc/ZASSettings()
set category = "Debug"
vsc.SetDefault(mob)
vsc.SetDefault(mob)
-59
View File
@@ -1,59 +0,0 @@
/xgm_gas/oxygen
id = "oxygen"
name = "Oxygen"
specific_heat = 20 // J/(mol*K)
molar_mass = 0.032 // kg/mol
flags = XGM_GAS_OXIDIZER
/xgm_gas/nitrogen
id = "nitrogen"
name = "Nitrogen"
specific_heat = 20 // J/(mol*K)
molar_mass = 0.028 // kg/mol
/xgm_gas/carbon_dioxide
id = "carbon_dioxide"
name = "Carbon Dioxide"
specific_heat = 30 // J/(mol*K)
molar_mass = 0.044 // kg/mol
/xgm_gas/phoron
id = "phoron"
name = "Phoron"
//Note that this has a significant impact on TTV yield.
//Because it is so high, any leftover phoron soaks up a lot of heat and drops the yield pressure.
specific_heat = 200 // J/(mol*K)
//Hypothetical group 14 (same as carbon), period 8 element.
//Using multiplicity rule, it's atomic number is 162
//and following a N/Z ratio of 1.5, the molar mass of a monatomic gas is:
molar_mass = 0.405 // kg/mol
tile_overlay = "phoron"
overlay_limit = 0.7
flags = XGM_GAS_FUEL | XGM_GAS_CONTAMINANT
/xgm_gas/volatile_fuel
id = "volatile_fuel"
name = "Volatile Fuel"
specific_heat = 253 // J/(mol*K) C8H18 gasoline. Isobaric, but good enough.
molar_mass = 0.114 // kg/mol. same.
flags = XGM_GAS_FUEL
/xgm_gas/sleeping_agent
id = "sleeping_agent"
name = "Sleeping Agent"
specific_heat = 40 // J/(mol*K)
molar_mass = 0.044 // kg/mol. N2O
tile_overlay = "sleeping_agent"
overlay_limit = 1
/xgm_gas/oxygen_agent_b
id = "oxygen_agent_b"
name = "Oxygen Agent-B" //what is this?
specific_heat = 300 // J/(mol*K)
molar_mass = 0.032 // kg/mol
+29 -24
View File
@@ -88,7 +88,7 @@ obj/var/contaminated = 0
if(vsc.plc.SKIN_BURNS)
if(!pl_head_protected() || !pl_suit_protected())
burn_skin(0.75)
if(prob(20)) src << "\red Your skin burns!"
if(prob(20)) src << "<span class='danger'>Your skin burns!</span>"
updatehealth()
//Burn eyes if exposed.
@@ -97,37 +97,37 @@ obj/var/contaminated = 0
if(!wear_mask)
burn_eyes()
else
if(!(wear_mask.flags & MASKCOVERSEYES))
if(!(wear_mask.body_parts_covered & EYES))
burn_eyes()
else
if(!(head.flags & HEADCOVERSEYES))
if(!(head.body_parts_covered & EYES))
if(!wear_mask)
burn_eyes()
else
if(!(wear_mask.flags & MASKCOVERSEYES))
if(!(wear_mask.body_parts_covered & EYES))
burn_eyes()
//Genetic Corruption
if(vsc.plc.GENETIC_CORRUPTION)
if(rand(1,10000) < vsc.plc.GENETIC_CORRUPTION)
randmutb(src)
src << "\red High levels of toxins cause you to spontaneously mutate."
src << "<span class='danger'>High levels of toxins cause you to spontaneously mutate!</span>"
domutcheck(src,null)
/mob/living/carbon/human/proc/burn_eyes()
//The proc that handles eye burning.
if(!species.has_organ["eyes"] || get_species() == "Vaurca")
return
//The proc that handles eye burning.
if(!species.has_organ["eyes"] || get_species() == "Vaurca")
return
var/obj/item/organ/eyes/E = internal_organs_by_name["eyes"]
if(E)
if(prob(20)) src << "\red Your eyes burn!"
E.damage += 2.5
eye_blurry = min(eye_blurry+1.5,50)
if (prob(max(0,E.damage - 15) + 1) &&!eye_blind)
src << "\red You are blinded!"
eye_blind += 20
var/obj/item/organ/eyes/E = internal_organs_by_name["eyes"]
if(E)
if(prob(20)) src << "<span class='danger'>Your eyes burn!</span>"
E.damage += 2.5
eye_blurry = min(eye_blurry+1.5,50)
if (prob(max(0,E.damage - 15) + 1) &&!eye_blind)
src << "<span class='danger'>You are blinded!</span>"
eye_blind += 20
/mob/living/carbon/human/proc/pl_head_protected()
//Checks if the head is adequately sealed.
@@ -135,19 +135,24 @@ obj/var/contaminated = 0
if(vsc.plc.PHORONGUARD_ONLY)
if(head.flags & PHORONGUARD)
return 1
else if(head.flags & HEADCOVERSEYES)
else if(head.body_parts_covered & EYES)
return 1
return 0
/mob/living/carbon/human/proc/pl_suit_protected()
//Checks if the suit is adequately sealed.
if(wear_suit)
if(vsc.plc.PHORONGUARD_ONLY)
if(wear_suit.flags & PHORONGUARD) return 1
else
if(wear_suit.flags_inv & HIDEJUMPSUIT) return 1
//should check HIDETAIL as well, but for the moment tails are not a part that can be damaged separately
return 0
var/coverage = 0
for(var/obj/item/protection in list(wear_suit, gloves, shoes))
if(!protection)
continue
if(vsc.plc.PHORONGUARD_ONLY && !(protection.flags & PHORONGUARD))
return 0
coverage |= protection.body_parts_covered
if(vsc.plc.PHORONGUARD_ONLY)
return 1
return BIT_TEST_ALL(coverage, UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS)
/mob/living/carbon/human/proc/suit_contamination()
//Runs over the things that can be contaminated and does so.
+2 -2
View File
@@ -169,7 +169,7 @@ var/global/vs_control/vsc = new
vars[ch] = vw
if(how == "Toggle")
newvar = (newvar?"ON":"OFF")
world << "\blue <b>[key_name(user)] changed the setting [display_description] to [newvar].</b>"
world << "<span class='notice'><b>[key_name(user)] changed the setting [display_description] to [newvar].</b></span>"
if(ch in plc.settings)
ChangeSettingsDialog(user,plc.settings)
else
@@ -322,7 +322,7 @@ var/global/vs_control/vsc = new
plc.N2O_HALLUCINATION = initial(plc.N2O_HALLUCINATION)
world << "\blue <b>[key_name(user)] changed the global phoron/ZAS settings to \"[def]\"</b>"
world << "<span class='notice'><b>[key_name(user)] changed the global phoron/ZAS settings to \"[def]\"</b></span>"
/pl_control/var/list/settings = list()
-468
View File
@@ -1,468 +0,0 @@
#define QUANTIZE(variable) (round(variable,0.0001))
/datum/gas_mixture
//Associative list of gas moles.
//Gases with 0 moles are not tracked and are pruned by update_values()
var/list/gas = list()
//Temperature in Kelvin of this gas mix.
var/temperature = 0
//Sum of all the gas moles in this mix. Updated by update_values()
var/total_moles = 0
//Volume of this mix.
var/volume = CELL_VOLUME
//Size of the group this gas_mixture is representing. 1 for singletons.
var/group_multiplier = 1
//List of active tile overlays for this gas_mixture. Updated by check_tile_graphic()
var/list/graphic = list()
/datum/gas_mixture/New(vol = CELL_VOLUME)
volume = vol
//Takes a gas string and the amount of moles to adjust by. Calls update_values() if update isn't 0.
/datum/gas_mixture/proc/adjust_gas(gasid, moles, update = 1)
if(moles == 0)
return
if (group_multiplier != 1)
gas[gasid] += moles/group_multiplier
else
gas[gasid] += moles
if(update)
update_values()
//Same as adjust_gas(), but takes a temperature which is mixed in with the gas.
/datum/gas_mixture/proc/adjust_gas_temp(gasid, moles, temp, update = 1)
if(moles == 0)
return
if(moles > 0 && abs(temperature - temp) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
var/self_heat_capacity = heat_capacity()
var/giver_heat_capacity = gas_data.specific_heat[gasid] * moles
var/combined_heat_capacity = giver_heat_capacity + self_heat_capacity
if(combined_heat_capacity != 0)
temperature = (temp * giver_heat_capacity + temperature * self_heat_capacity) / combined_heat_capacity
if (group_multiplier != 1)
gas[gasid] += moles/group_multiplier
else
gas[gasid] += moles
if(update)
update_values()
//Variadic version of adjust_gas(). Takes any number of gas and mole pairs and applies them.
/datum/gas_mixture/proc/adjust_multi()
ASSERT(!(args.len % 2))
for(var/i = 1; i < args.len; i += 2)
adjust_gas(args[i], args[i+1], update = 0)
update_values()
//Variadic version of adjust_gas_temp(). Takes any number of gas, mole and temperature associations and applies them.
/datum/gas_mixture/proc/adjust_multi_temp()
ASSERT(!(args.len % 3))
for(var/i = 1; i < args.len; i += 3)
adjust_gas_temp(args[i], args[i + 1], args[i + 2], update = 0)
update_values()
//Merges all the gas from another mixture into this one. Respects group_multipliers and adjusts temperature correctly.
//Does not modify giver in any way.
/datum/gas_mixture/proc/merge(const/datum/gas_mixture/giver)
if(!giver)
return
if(abs(temperature-giver.temperature)>MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
var/self_heat_capacity = heat_capacity()
var/giver_heat_capacity = giver.heat_capacity()
var/combined_heat_capacity = giver_heat_capacity + self_heat_capacity
if(combined_heat_capacity != 0)
temperature = (giver.temperature*giver_heat_capacity + temperature*self_heat_capacity)/combined_heat_capacity
if((group_multiplier != 1)||(giver.group_multiplier != 1))
for(var/g in giver.gas)
gas[g] += giver.gas[g] * giver.group_multiplier / group_multiplier
else
for(var/g in giver.gas)
gas[g] += giver.gas[g]
update_values()
/datum/gas_mixture/proc/equalize(datum/gas_mixture/sharer)
var/our_heatcap = heat_capacity()
var/share_heatcap = sharer.heat_capacity()
for(var/g in gas|sharer.gas)
var/comb = gas[g] + sharer.gas[g]
comb /= volume + sharer.volume
gas[g] = comb * volume
sharer.gas[g] = comb * sharer.volume
if(our_heatcap + share_heatcap)
temperature = ((temperature * our_heatcap) + (sharer.temperature * share_heatcap)) / (our_heatcap + share_heatcap)
sharer.temperature = temperature
return 1
//Returns the heat capacity of the gas mix based on the specific heat of the gases.
/datum/gas_mixture/proc/heat_capacity()
. = 0
for(var/g in gas)
. += gas_data.specific_heat[g] * gas[g]
. *= group_multiplier
//Adds or removes thermal energy. Returns the actual thermal energy change, as in the case of removing energy we can't go below TCMB.
/datum/gas_mixture/proc/add_thermal_energy(var/thermal_energy)
if (total_moles == 0)
return 0
var/heat_capacity = heat_capacity()
if (thermal_energy < 0)
if (temperature < TCMB)
return 0
var/thermal_energy_limit = -(temperature - TCMB)*heat_capacity //ensure temperature does not go below TCMB
thermal_energy = max( thermal_energy, thermal_energy_limit ) //thermal_energy and thermal_energy_limit are negative here.
temperature += thermal_energy/heat_capacity
return thermal_energy
//Returns the thermal energy change required to get to a new temperature
/datum/gas_mixture/proc/get_thermal_energy_change(var/new_temperature)
return heat_capacity()*(max(new_temperature, 0) - temperature)
//Technically vacuum doesn't have a specific entropy. Just use a really big number (infinity would be ideal) here so that it's easy to add gas to vacuum and hard to take gas out.
#define SPECIFIC_ENTROPY_VACUUM 150000
//Returns the ideal gas specific entropy of the whole mix. This is the entropy per mole of /mixed/ gas.
/datum/gas_mixture/proc/specific_entropy()
if (!gas.len || total_moles == 0)
return SPECIFIC_ENTROPY_VACUUM
. = 0
for(var/g in gas)
. += gas[g] * specific_entropy_gas(g)
. /= total_moles
/*
It's arguable whether this should even be called entropy anymore. It's more "based on" entropy than actually entropy now.
Returns the ideal gas specific entropy of a specific gas in the mix. This is the entropy due to that gas per mole of /that/ gas in the mixture, not the entropy due to that gas per mole of gas mixture.
For the purposes of SS13, the specific entropy is just a number that tells you how hard it is to move gas. You can replace this with whatever you want.
Just remember that returning a SMALL number == adding gas to this gas mix is HARD, taking gas away is EASY, and that returning a LARGE number means the opposite (so a vacuum should approach infinity).
So returning a constant/(partial pressure) would probably do what most players expect. Although the version I have implemented below is a bit more nuanced than simply 1/P in that it scales in a way
which is bit more realistic (natural log), and returns a fairly accurate entropy around room temperatures and pressures.
*/
/datum/gas_mixture/proc/specific_entropy_gas(var/gasid)
if (!(gasid in gas) || gas[gasid] == 0)
return SPECIFIC_ENTROPY_VACUUM //that gas isn't here
//group_multiplier gets divided out in volume/gas[gasid] - also, V/(m*T) = R/(partial pressure)
var/molar_mass = gas_data.molar_mass[gasid]
var/specific_heat = gas_data.specific_heat[gasid]
return R_IDEAL_GAS_EQUATION * ( log( (IDEAL_GAS_ENTROPY_CONSTANT*volume/(gas[gasid] * temperature)) * (molar_mass*specific_heat*temperature)**(2/3) + 1 ) + 15 )
//alternative, simpler equation
//var/partial_pressure = gas[gasid] * R_IDEAL_GAS_EQUATION * temperature / volume
//return R_IDEAL_GAS_EQUATION * ( log (1 + IDEAL_GAS_ENTROPY_CONSTANT/partial_pressure) + 20 )
//Updates the total_moles count and trims any empty gases.
/datum/gas_mixture/proc/update_values()
total_moles = 0
for(var/g in gas)
if(gas[g] <= 0)
gas -= g
else
total_moles += gas[g]
//Returns the pressure of the gas mix. Only accurate if there have been no gas modifications since update_values() has been called.
/datum/gas_mixture/proc/return_pressure()
if(volume)
return total_moles * R_IDEAL_GAS_EQUATION * temperature / volume
return 0
//Removes moles from the gas mixture and returns a gas_mixture containing the removed air.
/datum/gas_mixture/proc/remove(amount)
amount = min(amount, total_moles * group_multiplier) //Can not take more air than the gas mixture has!
if(amount <= 0)
return null
var/datum/gas_mixture/removed = new
for(var/g in gas)
removed.gas[g] = QUANTIZE((gas[g] / total_moles) * amount)
gas[g] -= removed.gas[g] / group_multiplier
removed.temperature = temperature
update_values()
removed.update_values()
return removed
//Removes a ratio of gas from the mixture and returns a gas_mixture containing the removed air.
/datum/gas_mixture/proc/remove_ratio(ratio, out_group_multiplier = 1)
if(ratio <= 0)
return null
out_group_multiplier = between(1, out_group_multiplier, group_multiplier)
ratio = min(ratio, 1)
var/datum/gas_mixture/removed = new
removed.group_multiplier = out_group_multiplier
for(var/g in gas)
removed.gas[g] = (gas[g] * ratio * group_multiplier / out_group_multiplier)
gas[g] = gas[g] * (1 - ratio)
removed.temperature = temperature
removed.volume = volume * group_multiplier / out_group_multiplier
update_values()
removed.update_values()
return removed
//Removes a volume of gas from the mixture and returns a gas_mixture containing the removed air with the given volume
/datum/gas_mixture/proc/remove_volume(removed_volume)
var/datum/gas_mixture/removed = remove_ratio(removed_volume/(volume*group_multiplier), 1)
removed.volume = removed_volume
return removed
//Removes moles from the gas mixture, limited by a given flag. Returns a gax_mixture containing the removed air.
/datum/gas_mixture/proc/remove_by_flag(flag, amount)
if(!flag || amount <= 0)
return
var/sum = 0
for(var/g in gas)
if(gas_data.flags[g] & flag)
sum += gas[g]
var/datum/gas_mixture/removed = new
for(var/g in gas)
if(gas_data.flags[g] & flag)
removed.gas[g] = QUANTIZE((gas[g] / sum) * amount)
gas[g] -= removed.gas[g] / group_multiplier
removed.temperature = temperature
update_values()
removed.update_values()
return removed
//Copies gas and temperature from another gas_mixture.
/datum/gas_mixture/proc/copy_from(const/datum/gas_mixture/sample)
gas = sample.gas.Copy()
temperature = sample.temperature
update_values()
return 1
//Checks if we are within acceptable range of another gas_mixture to suspend processing or merge.
/datum/gas_mixture/proc/compare(const/datum/gas_mixture/sample)
if(!sample) return 0
var/list/marked = list()
for(var/g in gas)
if((abs(gas[g] - sample.gas[g]) > MINIMUM_AIR_TO_SUSPEND) && \
((gas[g] < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.gas[g]) || \
(gas[g] > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.gas[g])))
return 0
marked[g] = 1
for(var/g in sample.gas)
if(!marked[g])
if((abs(gas[g] - sample.gas[g]) > MINIMUM_AIR_TO_SUSPEND) && \
((gas[g] < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.gas[g]) || \
(gas[g] > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.gas[g])))
return 0
if(total_moles > MINIMUM_AIR_TO_SUSPEND)
if((abs(temperature - sample.temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) && \
((temperature < (1 - MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND)*sample.temperature) || \
(temperature > (1 + MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND)*sample.temperature)))
return 0
return 1
/datum/gas_mixture/proc/react()
zburn(null, force_burn=0, no_check=0) //could probably just call zburn() here with no args but I like being explicit.
//Rechecks the gas_mixture and adjusts the graphic list if needed.
//Two lists can be passed by reference if you need know specifically which graphics were added and removed.
/datum/gas_mixture/proc/check_tile_graphic(list/graphic_add = null, list/graphic_remove = null)
for(var/g in gas_data.overlay_limit)
if(graphic.Find(gas_data.tile_overlay[g]))
//Overlay is already applied for this gas, check if it's still valid.
if(gas[g] <= gas_data.overlay_limit[g])
if(!graphic_remove)
graphic_remove = list()
graphic_remove += gas_data.tile_overlay[g]
else
//Overlay isn't applied for this gas, check if it's valid and needs to be added.
if(gas[g] > gas_data.overlay_limit[g])
if(!graphic_add)
graphic_add = list()
graphic_add += gas_data.tile_overlay[g]
. = 0
//Apply changes
if(graphic_add && graphic_add.len)
graphic += graphic_add
. = 1
if(graphic_remove && graphic_remove.len)
graphic -= graphic_remove
. = 1
//Simpler version of merge(), adjusts gas amounts directly and doesn't account for temperature or group_multiplier.
/datum/gas_mixture/proc/add(datum/gas_mixture/right_side)
for(var/g in right_side.gas)
gas[g] += right_side.gas[g]
update_values()
return 1
//Simpler version of remove(), adjusts gas amounts directly and doesn't account for group_multiplier.
/datum/gas_mixture/proc/subtract(datum/gas_mixture/right_side)
for(var/g in right_side.gas)
gas[g] -= right_side.gas[g]
update_values()
return 1
//Multiply all gas amounts by a factor.
/datum/gas_mixture/proc/multiply(factor)
for(var/g in gas)
gas[g] *= factor
update_values()
return 1
//Divide all gas amounts by a factor.
/datum/gas_mixture/proc/divide(factor)
for(var/g in gas)
gas[g] /= factor
update_values()
return 1
//Shares gas with another gas_mixture based on the amount of connecting tiles and a fixed lookup table.
/datum/gas_mixture/proc/share_ratio(datum/gas_mixture/other, connecting_tiles, share_size = null, one_way = 0)
var/static/list/sharing_lookup_table = list(0.30, 0.40, 0.48, 0.54, 0.60, 0.66)
//Shares a specific ratio of gas between mixtures using simple weighted averages.
var/ratio = sharing_lookup_table[6]
var/size = max(1, group_multiplier)
if(isnull(share_size)) share_size = max(1, other.group_multiplier)
var/full_heat_capacity = heat_capacity()
var/s_full_heat_capacity = other.heat_capacity()
var/list/avg_gas = list()
for(var/g in gas)
avg_gas[g] += gas[g] * size
for(var/g in other.gas)
avg_gas[g] += other.gas[g] * share_size
for(var/g in avg_gas)
avg_gas[g] /= (size + share_size)
var/temp_avg = 0
if(full_heat_capacity + s_full_heat_capacity)
temp_avg = (temperature * full_heat_capacity + other.temperature * s_full_heat_capacity) / (full_heat_capacity + s_full_heat_capacity)
//WOOT WOOT TOUCH THIS AND YOU ARE A RETARD.
if(sharing_lookup_table.len >= connecting_tiles) //6 or more interconnecting tiles will max at 42% of air moved per tick.
ratio = sharing_lookup_table[connecting_tiles]
//WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
for(var/g in avg_gas)
gas[g] = max(0, (gas[g] - avg_gas[g]) * (1 - ratio) + avg_gas[g])
if(!one_way)
other.gas[g] = max(0, (other.gas[g] - avg_gas[g]) * (1 - ratio) + avg_gas[g])
temperature = max(0, (temperature - temp_avg) * (1-ratio) + temp_avg)
if(!one_way)
other.temperature = max(0, (other.temperature - temp_avg) * (1-ratio) + temp_avg)
update_values()
other.update_values()
return compare(other)
//A wrapper around share_ratio for spacing gas at the same rate as if it were going into a large airless room.
/datum/gas_mixture/proc/share_space(datum/gas_mixture/unsim_air)
return share_ratio(unsim_air, unsim_air.group_multiplier, max(1, max(group_multiplier + 3, 1) + unsim_air.group_multiplier), one_way = 1)
//Equalizes a list of gas mixtures. Used for pipe networks.
/proc/equalize_gases(datum/gas_mixture/list/gases)
//Calculate totals from individual components
var/total_volume = 0
var/total_thermal_energy = 0
var/total_heat_capacity = 0
var/list/total_gas = list()
for(var/datum/gas_mixture/gasmix in gases)
total_volume += gasmix.volume
var/temp_heatcap = gasmix.heat_capacity()
total_thermal_energy += gasmix.temperature * temp_heatcap
total_heat_capacity += temp_heatcap
for(var/g in gasmix.gas)
total_gas[g] += gasmix.gas[g]
if(total_volume > 0)
var/datum/gas_mixture/combined = new(total_volume)
combined.gas = total_gas
//Calculate temperature
if(total_heat_capacity > 0)
combined.temperature = total_thermal_energy / total_heat_capacity
combined.update_values()
//Allow for reactions
combined.react()
//Average out the gases
for(var/g in combined.gas)
combined.gas[g] /= total_volume
//Update individual gas_mixtures
for(var/datum/gas_mixture/gasmix in gases)
gasmix.gas = combined.gas.Copy()
gasmix.temperature = combined.temperature
gasmix.multiply(gasmix.volume)
return 1
-46
View File
@@ -1,46 +0,0 @@
/var/xgm_gas_data/gas_data
/xgm_gas_data
//Simple list of all the gas IDs.
var/list/gases = list()
//The friendly, human-readable name for the gas.
var/list/name = list()
//Specific heat of the gas. Used for calculating heat capacity.
var/list/specific_heat = list()
//Molar mass of the gas. Used for calculating specific entropy.
var/list/molar_mass = list()
//Tile overlays. /images, created from references to 'icons/effects/tile_effects.dmi'
var/list/tile_overlay = list()
//Overlay limits. There must be at least this many moles for the overlay to appear.
var/list/overlay_limit = list()
//Flags.
var/list/flags = list()
/xgm_gas
var/id = ""
var/name = "Unnamed Gas"
var/specific_heat = 20 // J/(mol*K)
var/molar_mass = 0.032 // kg/mol
var/tile_overlay = null
var/overlay_limit = null
var/flags = 0
/hook/startup/proc/generateGasData()
gas_data = new
for(var/p in (typesof(/xgm_gas) - /xgm_gas))
var/xgm_gas/gas = new p
if(gas.id in gas_data.gases)
error("Duplicate gas id `[gas.id]` in `[p]`")
gas_data.gases += gas.id
gas_data.name[gas.id] = gas.name
gas_data.specific_heat[gas.id] = gas.specific_heat
gas_data.molar_mass[gas.id] = gas.molar_mass
if(gas.tile_overlay) gas_data.tile_overlay[gas.id] = image('icons/effects/tile_effects.dmi', gas.tile_overlay, FLY_LAYER)
if(gas.overlay_limit) gas_data.overlay_limit[gas.id] = gas.overlay_limit
gas_data.flags[gas.id] = gas.flags
return 1