Merge remote-tracking branch 'upstream/master' into rmv
@@ -0,0 +1,149 @@
|
||||
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
|
||||
"a" = (
|
||||
/obj/effect/turf_decal/stripes/red/corner{
|
||||
dir = 4
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/space)
|
||||
"b" = (
|
||||
/obj/effect/turf_decal/stripes/red/line{
|
||||
dir = 4
|
||||
},
|
||||
/obj/effect/turf_decal/arrows/white{
|
||||
dir = 8;
|
||||
pixel_x = 5;
|
||||
pixel_y = 10
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/space)
|
||||
"c" = (
|
||||
/obj/effect/turf_decal/stripes/red/line{
|
||||
dir = 4
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/space)
|
||||
"d" = (
|
||||
/obj/structure/cable/yellow,
|
||||
/obj/machinery/atmospherics/components/trinary/nuclear_reactor/cargo,
|
||||
/turf/open/floor/engine,
|
||||
/area/space)
|
||||
"e" = (
|
||||
/obj/effect/turf_decal/stripes/red/corner{
|
||||
dir = 1
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/space)
|
||||
"f" = (
|
||||
/obj/machinery/atmospherics/pipe/simple/cyan{
|
||||
dir = 4
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/space)
|
||||
"g" = (
|
||||
/obj/structure/cable/yellow{
|
||||
icon_state = "1-2"
|
||||
},
|
||||
/obj/machinery/atmospherics/pipe/simple/purple,
|
||||
/turf/open/floor/engine,
|
||||
/area/space)
|
||||
"h" = (
|
||||
/turf/open/floor/engine,
|
||||
/area/space)
|
||||
"s" = (
|
||||
/obj/effect/turf_decal/stripes/red/corner,
|
||||
/turf/open/floor/engine,
|
||||
/area/space)
|
||||
"t" = (
|
||||
/obj/effect/turf_decal/stripes/red/line{
|
||||
dir = 8
|
||||
},
|
||||
/obj/effect/turf_decal/arrows/white{
|
||||
dir = 8;
|
||||
pixel_x = 5;
|
||||
pixel_y = 10
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/space)
|
||||
"B" = (
|
||||
/obj/effect/turf_decal/stripes/red/corner{
|
||||
dir = 8
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/space)
|
||||
"G" = (
|
||||
/obj/machinery/atmospherics/pipe/simple/cyan{
|
||||
dir = 4
|
||||
},
|
||||
/turf/open/floor/plating,
|
||||
/area/space)
|
||||
"M" = (
|
||||
/obj/effect/turf_decal/stripes/red/line,
|
||||
/turf/open/floor/engine,
|
||||
/area/space)
|
||||
"O" = (
|
||||
/obj/machinery/atmospherics/pipe/simple/scrubbers{
|
||||
dir = 4
|
||||
},
|
||||
/turf/open/floor/plating,
|
||||
/area/space)
|
||||
"P" = (
|
||||
/obj/effect/turf_decal/stripes/red/line{
|
||||
dir = 8
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/space)
|
||||
"V" = (
|
||||
/obj/effect/turf_decal/stripes/red/line{
|
||||
dir = 1
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/space)
|
||||
"Y" = (
|
||||
/obj/structure/cable/yellow{
|
||||
icon_state = "0-2"
|
||||
},
|
||||
/obj/machinery/atmospherics/pipe/simple/purple,
|
||||
/turf/open/floor/plating,
|
||||
/area/space)
|
||||
"Z" = (
|
||||
/obj/machinery/atmospherics/pipe/simple/scrubbers{
|
||||
dir = 4
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/space)
|
||||
|
||||
(1,1,1) = {"
|
||||
s
|
||||
c
|
||||
O
|
||||
b
|
||||
a
|
||||
"}
|
||||
(2,1,1) = {"
|
||||
M
|
||||
h
|
||||
Z
|
||||
h
|
||||
V
|
||||
"}
|
||||
(3,1,1) = {"
|
||||
Y
|
||||
g
|
||||
d
|
||||
h
|
||||
V
|
||||
"}
|
||||
(4,1,1) = {"
|
||||
M
|
||||
h
|
||||
f
|
||||
h
|
||||
V
|
||||
"}
|
||||
(5,1,1) = {"
|
||||
B
|
||||
P
|
||||
G
|
||||
t
|
||||
e
|
||||
"}
|
||||
@@ -16,10 +16,14 @@
|
||||
#define CHANNEL_DIGEST 1009
|
||||
#define CHANNEL_PREYLOOP 1008
|
||||
|
||||
//Hyperstation Channels
|
||||
#define CHANNEL_REACTOR_ALERT 1007 // reactor sounds
|
||||
|
||||
|
||||
//THIS SHOULD ALWAYS BE THE LOWEST ONE!
|
||||
//KEEP IT UPDATED
|
||||
|
||||
#define CHANNEL_HIGHEST_AVAILABLE 1008 //CIT CHANGE - COMPENSATES FOR VORESOUND CHANNELS
|
||||
#define CHANNEL_HIGHEST_AVAILABLE 1006 //CIT CHANGE - COMPENSATES FOR VORESOUND CHANNELS
|
||||
|
||||
|
||||
#define SOUND_MINIMUM_PRESSURE 10
|
||||
|
||||
@@ -176,4 +176,48 @@ round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), ro
|
||||
offset = (y-1)*4
|
||||
for(x in 1 to 4)
|
||||
output[offset+x] = round(A[offset+1]*B[x] + A[offset+2]*B[x+4] + A[offset+3]*B[x+8] + A[offset+4]*B[x+12]+(y==5?B[x+16]:0), 0.001)
|
||||
return output
|
||||
return output
|
||||
|
||||
/atom/proc/shake_animation(var/intensity = 8) //Makes the object visibly shake
|
||||
var/initial_transform = new/matrix(transform)
|
||||
var/init_px = pixel_x
|
||||
var/shake_dir = pick(-1, 1)
|
||||
var/rotation = 2+soft_cap(intensity, 1, 1, 0.94)
|
||||
var/offset = 1+soft_cap(intensity*0.3, 1, 1, 0.8)
|
||||
var/time = 2+soft_cap(intensity*0.3, 2, 1, 0.92)
|
||||
animate(src, transform=turn(transform, rotation*shake_dir), pixel_x=init_px + offset*shake_dir, time=1)
|
||||
animate(transform=initial_transform, pixel_x=init_px, time=time, easing=ELASTIC_EASING)
|
||||
|
||||
/*
|
||||
This proc makes the input taper off above cap. But there's no absolute cutoff.
|
||||
Chunks of the input value above cap, are reduced more and more with each successive one and added to the output
|
||||
A higher input value always makes a higher output value. but the rate of growth slows
|
||||
*/
|
||||
/proc/soft_cap(var/input, var/cap = 0, var/groupsize = 1, var/groupmult = 0.9)
|
||||
|
||||
//The cap is a ringfenced amount. If we're below that, just return the input
|
||||
if (input <= cap)
|
||||
return input
|
||||
|
||||
var/output = 0
|
||||
var/buffer = 0
|
||||
var/power = 1//We increment this after each group, then apply it to the groupmult as a power
|
||||
|
||||
//Ok its above, so the cap is a safe amount, we move that to the output
|
||||
input -= cap
|
||||
output += cap
|
||||
|
||||
//Now we start moving groups from input to buffer
|
||||
|
||||
|
||||
while (input > 0)
|
||||
buffer = min(input, groupsize) //We take the groupsize, or all the input has left if its less
|
||||
input -= buffer
|
||||
|
||||
buffer *= groupmult**power //This reduces the group by the groupmult to the power of which index we're on.
|
||||
//This ensures that each successive group is reduced more than the previous one
|
||||
|
||||
output += buffer
|
||||
power++ //Transfer to output, increment power, repeat until the input pile is all used
|
||||
|
||||
return output
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
/proc/radiation_pulse(atom/source, intensity, range_modifier, log=FALSE, can_contaminate=TRUE)
|
||||
if(!SSradiation.can_fire)
|
||||
return
|
||||
if(istype(get_turf(source), /turf/open/pool))
|
||||
var/turf/open/pool/PL = get_turf(source)
|
||||
if(PL.filled == TRUE)
|
||||
intensity *= 0.15
|
||||
var/area/A = get_area(source)
|
||||
var/atom/nested_loc = source.loc
|
||||
var/spawn_waves = TRUE
|
||||
|
||||
@@ -537,6 +537,10 @@
|
||||
SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT)
|
||||
|
||||
/atom/proc/rad_act(strength)
|
||||
if(istype(get_turf(src), /turf/open/pool))
|
||||
var/turf/open/pool/PL = get_turf(src)
|
||||
if(PL.filled == TRUE)
|
||||
strength *= 0.15
|
||||
SEND_SIGNAL(src, COMSIG_ATOM_RAD_ACT, strength)
|
||||
|
||||
/atom/proc/narsie_act()
|
||||
@@ -894,4 +898,4 @@ Proc for attack log creation, because really why not
|
||||
if(href_list["statpanel_item_click"])
|
||||
// first of all make sure we valid
|
||||
var/mouseparams = list2params(paramslist)
|
||||
usr_client.Click(src, loc, null, mouseparams)
|
||||
usr_client.Click(src, loc, null, mouseparams)
|
||||
|
||||
@@ -13,19 +13,16 @@
|
||||
var/list/living_antags = list()
|
||||
var/list/dead_players = list()
|
||||
var/list/list_observers = list()
|
||||
|
||||
/datum/dynamic_ruleset/event/acceptable(population=0, threat=0)
|
||||
if(istype(controller, /datum/round_event_control))
|
||||
var/datum/round_event_control/R = controller
|
||||
if(R.map_blacklist.len && (SSmapping.config.map_file in R.map_blacklist)) //Apparently dynamic doesn't call a controller's canSpawnEvent :>
|
||||
return FALSE
|
||||
if(R.map_whitelist.len && !(SSmapping.config.map_file in R.map_whitelist))
|
||||
return FALSE
|
||||
return ..()
|
||||
var/list/map_blacklist = list() //Determines if a map, "BoxStation.dmm" for example, will spawn. Has to be case-sensitive
|
||||
var/list/map_whitelist = list() //Blacklist/Whitelist does not check round event controllers, they are separate vars
|
||||
|
||||
/datum/dynamic_ruleset/event/ready(forced = 0)
|
||||
if (!forced)
|
||||
var/job_check = 0
|
||||
if (map_blacklist.len && (SSmapping.config.map_file in map_blacklist))
|
||||
return FALSE
|
||||
if (map_whitelist.len && !(SSmapping.config.map_file in map_whitelist))
|
||||
return FALSE
|
||||
if (enemy_roles.len > 0)
|
||||
for (var/mob/M in mode.current_players[CURRENT_LIVING_PLAYERS])
|
||||
if (M.stat == DEAD)
|
||||
@@ -232,6 +229,7 @@
|
||||
//property_weights = list("extended" = -2)
|
||||
occurances_max = 2
|
||||
chaos_min = 1.5
|
||||
map_blacklist = list("LayeniaStation.dmm")
|
||||
|
||||
/datum/dynamic_ruleset/event/meteor_wave/ready()
|
||||
if(world.time-SSticker.round_start_time > 35 MINUTES && mode.threat_level > 40 && mode.threat >= 25 && prob(30))
|
||||
@@ -408,6 +406,7 @@
|
||||
//property_weights = list("extended" = 1)
|
||||
occurances_max = 3
|
||||
chaos_min = 0.5
|
||||
map_blacklist = list("LayeniaStation.dmm")
|
||||
|
||||
/datum/dynamic_ruleset/event/communications_blackout
|
||||
name = "Communications Blackout"
|
||||
@@ -471,6 +470,7 @@
|
||||
repeatable = TRUE
|
||||
//property_weights = list("extended" = 1)
|
||||
occurances_max = 3
|
||||
|
||||
|
||||
/datum/dynamic_ruleset/event/electrical_storm
|
||||
name = "Electrical Storm"
|
||||
@@ -544,6 +544,7 @@
|
||||
//property_weights = list("extended" = 1)
|
||||
occurances_max = 2
|
||||
chaos_min = 1.0
|
||||
map_blacklist = list("LayeniaStation.dmm")
|
||||
|
||||
/datum/dynamic_ruleset/event/swarmers
|
||||
name = "Swarmers"
|
||||
@@ -604,6 +605,7 @@
|
||||
repeatable_weight_decrease = 2
|
||||
chaos_min = 1.5
|
||||
var/atom/special_target
|
||||
map_blacklist = list("LayeniaStation.dmm")
|
||||
|
||||
/*
|
||||
/datum/dynamic_ruleset/event/immovable_rod/execute() //I do not know why this is necessary
|
||||
@@ -629,6 +631,7 @@
|
||||
cost = 10
|
||||
repeatable = TRUE
|
||||
occurances_max = 2
|
||||
map_blacklist = list("LayeniaStation.dmm")
|
||||
|
||||
/datum/dynamic_ruleset/event/high_priority_bounty
|
||||
name = "High Priority Bounty"
|
||||
@@ -723,6 +726,7 @@
|
||||
weight = 4
|
||||
repeatable_weight_decrease = 3
|
||||
occurances_max = 2
|
||||
map_blacklist = list("LayeniaStation.dmm")
|
||||
|
||||
/datum/dynamic_ruleset/event/sentience
|
||||
name = "Random Human-level Intelligence"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
S.update_icon()
|
||||
S.power_change()
|
||||
|
||||
var/list/skipped_areas = list(/area/engine/engineering, /area/engine/supermatter, /area/engine/atmospherics_engine, /area/ai_monitored/turret_protected/ai)
|
||||
var/list/skipped_areas = list(/area/engine/engineering, /area/engine/supermatter, /area/engine/atmospherics_engine, /area/engine/engineering/reactor_core, /area/engine/engineering/reactor_control, /area/ai_monitored/turret_protected/ai)
|
||||
|
||||
for(var/area/A in world)
|
||||
if( !A.requires_power || A.always_unpowered )
|
||||
|
||||
@@ -208,7 +208,7 @@
|
||||
/datum/pipeline/proc/return_air()
|
||||
. = other_airs + air
|
||||
if(null in .)
|
||||
stack_trace("[src] has one or more null gas mixtures, which may cause bugs. Null mixtures will not be considered in reconcile_air().")
|
||||
stack_trace("[src]([REF(src)]) has one or more null gas mixtures, which may cause bugs. Null mixtures will not be considered in reconcile_air().")
|
||||
return removeNullsFromList(.)
|
||||
|
||||
/datum/pipeline/proc/reconcile_air()
|
||||
|
||||
@@ -193,3 +193,39 @@
|
||||
contains = list(/obj/item/energy_harvester)
|
||||
crate_name = "energy harvesting module crate"
|
||||
crate_type = /obj/structure/closet/crate/secure/engineering
|
||||
|
||||
/datum/supply_pack/engine/fuel_rod
|
||||
name = "Uranium Fuel Rod crate"
|
||||
desc = "Two additional fuel rods for use in a reactor, requires CE access to open. Caution: Radioactive"
|
||||
cost = 3000
|
||||
access = ACCESS_CE
|
||||
contains = list(/obj/item/twohanded/required/fuel_rod,
|
||||
/obj/item/twohanded/required/fuel_rod)
|
||||
crate_name = "Uranium-235 Fuel Rod crate"
|
||||
crate_type = /obj/structure/closet/crate/secure/engineering
|
||||
dangerous = TRUE
|
||||
|
||||
/datum/supply_pack/engine/bananium_fuel_rod
|
||||
name = "Bananium Fuel Rod crate"
|
||||
desc = "Two fuel rods designed to utilize and multiply bananium in a reactor, requires CE access to open. Caution: Radioactive"
|
||||
cost = 4000
|
||||
access = ACCESS_CE // Nag your local CE
|
||||
contains = list(/obj/item/twohanded/required/fuel_rod/material/bananium,
|
||||
/obj/item/twohanded/required/fuel_rod/material/bananium)
|
||||
crate_name = "Bluespace Crystal Fuel Rod crate"
|
||||
crate_type = /obj/structure/closet/crate/secure/engineering
|
||||
dangerous = TRUE
|
||||
contraband = TRUE
|
||||
|
||||
/datum/supply_pack/engine/reactor
|
||||
name = "RMBK Nuclear Reactor Kit" // (not) a toy
|
||||
desc = "Contains a reactor beacon and 3 reactor consoles. Uranium rods not included."
|
||||
cost = 12000
|
||||
access = ACCESS_CE
|
||||
contains = list(/obj/item/survivalcapsule/reactor,
|
||||
/obj/machinery/computer/reactor/control_rods/cargo,
|
||||
/obj/machinery/computer/reactor/stats/cargo,
|
||||
/obj/machinery/computer/reactor/fuel_rods/cargo)
|
||||
crate_name = "Build Your Own Reactor Kit"
|
||||
crate_type = /obj/structure/closet/crate/secure/engineering
|
||||
dangerous = TRUE
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
hard_drive.store_file(new/datum/computer_file/program/power_monitor())
|
||||
hard_drive.store_file(new/datum/computer_file/program/alarm_monitor())
|
||||
hard_drive.store_file(new/datum/computer_file/program/supermatter_monitor())
|
||||
hard_drive.store_file(new/datum/computer_file/program/nuclear_monitor())
|
||||
|
||||
// ===== RESEARCH CONSOLE =====
|
||||
/obj/machinery/modular_computer/console/preset/research
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/datum/export/plutonium_rod
|
||||
cost = 20000
|
||||
unit_name = "Plutonium Rod"
|
||||
export_types = list(/obj/item/twohanded/required/fuel_rod/plutonium)
|
||||
@@ -0,0 +1,8 @@
|
||||
// yes, I COULD make it a seperate object from the surv capsule but the code needed is indeticle soo...
|
||||
|
||||
/obj/item/survivalcapsule/reactor // the not-so-survival capsule
|
||||
name = "RMBK Reactor Beacon"
|
||||
desc = "A special bluespace beacon designed to implement a reactor into the hull of the ship or station that it is activated on."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "beacon"
|
||||
template_id = "reactor"
|
||||
@@ -0,0 +1,7 @@
|
||||
// yes, I COULD make it a seperate object from the surv capsule but the code needed is indeticle soo...
|
||||
|
||||
/datum/map_template/shelter/reactor
|
||||
name = "RBMK Reactor"
|
||||
shelter_id = "reactor"
|
||||
description = "A reactor core, coolant and moderator loop not included."
|
||||
mappath = "_maps/templates/reactor_1.dmm"
|
||||
@@ -0,0 +1,229 @@
|
||||
/obj/item/twohanded/required/fuel_rod
|
||||
var/rad_strength = 500
|
||||
var/half_life = 2000 // how many depletion ticks are needed to half the fuel_power (1 tick = 1 second)
|
||||
var/time_created = 0
|
||||
var/og_fuel_power = 0.20 //the original fuel power value
|
||||
var/process = FALSE
|
||||
// The depletion where depletion_final() will be called (and does something)
|
||||
var/depletion_threshold = 100
|
||||
// How fast this rod will deplete
|
||||
var/depletion_speed_modifier = 1
|
||||
var/depleted_final = FALSE // depletion_final should run only once
|
||||
var/depletion_conversion_type = "plutonium"
|
||||
|
||||
/obj/item/twohanded/required/fuel_rod/Initialize()
|
||||
. = ..()
|
||||
time_created = world.time
|
||||
AddComponent(/datum/component/radioactive, rad_strength, src) // This should be temporary for it won't make rads go lower than 350
|
||||
if(process)
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/twohanded/required/fuel_rod/Destroy()
|
||||
if(process)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
var/obj/machinery/atmospherics/components/trinary/nuclear_reactor/N = loc
|
||||
if(istype(N))
|
||||
N.fuel_rods -= src
|
||||
. = ..()
|
||||
|
||||
// This proc will try to convert your fuel rod if you don't override this proc
|
||||
// So, ideally, you should write an override of this for every fuel rod you want to create
|
||||
/obj/item/twohanded/required/fuel_rod/proc/depletion_final(result_rod = "")
|
||||
if(result_rod)
|
||||
var/obj/machinery/atmospherics/components/trinary/nuclear_reactor/N = loc
|
||||
// Rod conversion is moot when you can't find the reactor
|
||||
if(istype(N))
|
||||
var/obj/item/twohanded/required/fuel_rod/R
|
||||
|
||||
// You can add your own depletion scheme and not override this proc if you are going to convert a fuel rod into another type
|
||||
switch(result_rod)
|
||||
if("plutonium")
|
||||
R = new /obj/item/twohanded/required/fuel_rod/plutonium(loc)
|
||||
R.depletion = depletion
|
||||
if("depleted")
|
||||
if(fuel_power < 10)
|
||||
fuel_power = 0
|
||||
playsound(loc, 'sound/effects/supermatter.ogg', 100, TRUE)
|
||||
R = new /obj/item/twohanded/required/fuel_rod/depleted(loc)
|
||||
R.depletion = depletion
|
||||
|
||||
// Finalization of conversion
|
||||
if(istype(R))
|
||||
N.fuel_rods += R
|
||||
qdel(src)
|
||||
else
|
||||
depleted_final = FALSE // Maybe try again later?
|
||||
|
||||
/obj/item/twohanded/required/fuel_rod/deplete(amount=0.035) // override for the one in rmbk.dm
|
||||
depletion += amount * depletion_speed_modifier
|
||||
if(depletion >= depletion_threshold && !depleted_final)
|
||||
depleted_final = TRUE
|
||||
depletion_final(depletion_conversion_type)
|
||||
|
||||
/obj/item/twohanded/required/fuel_rod/plutonium
|
||||
fuel_power = 0.20
|
||||
name = "Plutonium-239 Fuel Rod"
|
||||
desc = "A highly energetic titanium sheathed rod containing a sizeable measure of weapons grade plutonium, it's highly efficient as nuclear fuel, but will cause the reaction to get out of control if not properly utilised."
|
||||
icon_state = "inferior"
|
||||
rad_strength = 1500
|
||||
process = TRUE // for half life code
|
||||
depletion_threshold = 300
|
||||
depletion_conversion_type = "depleted"
|
||||
|
||||
/obj/item/twohanded/required/fuel_rod/plutonium/process()
|
||||
fuel_power = og_fuel_power * 0.5**((world.time - time_created) / half_life SECONDS) // halves the fuel power every half life (33 minutes)
|
||||
|
||||
/obj/item/twohanded/required/fuel_rod/depleted
|
||||
name = "Depleted Fuel Rod"
|
||||
desc = "A highly radioactive fuel rod which has expended most of it's useful energy."
|
||||
icon_state = "normal"
|
||||
rad_strength = 6000 // smelly
|
||||
depletion_conversion_type = "" // It means that it won't turn into anything
|
||||
|
||||
// Master type for material optional (or requiring, wyci) and/or producing rods
|
||||
/obj/item/twohanded/required/fuel_rod/material
|
||||
// Whether the rod has been harvested. Should be set in expend().
|
||||
var/expended = FALSE
|
||||
// The material that will be inserted and then multiplied (or not). Should be some sort of /obj/item/stack
|
||||
var/material_type
|
||||
// The name of material that'll be used for texts
|
||||
var/material_name
|
||||
var/material_name_singular
|
||||
var/initial_amount = 0
|
||||
// The maximum amount of material the rod can hold
|
||||
var/max_initial_amount = 10
|
||||
var/grown_amount = 0
|
||||
// The multiplier for growth. 1 for the same 2 for double etc etc
|
||||
var/multiplier = 2
|
||||
// After this depletion, you won't be able to add new materials
|
||||
var/material_input_deadline = 25
|
||||
// Material fuel rods generally don't get converted into another fuel object
|
||||
depletion_conversion_type = ""
|
||||
|
||||
// Called when the rod is fully harvested
|
||||
/obj/item/twohanded/required/fuel_rod/material/proc/expend()
|
||||
expended = TRUE
|
||||
|
||||
// Basic checks for material rods
|
||||
/obj/item/twohanded/required/fuel_rod/material/proc/check_material_input(mob/user)
|
||||
if(depletion >= material_input_deadline)
|
||||
to_chat(user, "<span class='warning'>The sample slots have sealed themselves shut, it's too late to add [material_name] now!</span>") // no cheesing in crystals at 100%
|
||||
return FALSE
|
||||
if(expended)
|
||||
to_chat(user, "<span class='warning'>\The [src]'s material slots have already been used.</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
// The actual growth
|
||||
/obj/item/twohanded/required/fuel_rod/material/depletion_final(result_rod)
|
||||
if(result_rod)
|
||||
..() // So if you put anything into depletion_conversion_type then your fuel rod will be converted (or not) and *won't grow*
|
||||
else
|
||||
grown_amount = initial_amount * multiplier
|
||||
|
||||
/obj/item/twohanded/required/fuel_rod/material/attackby(obj/item/W, mob/user, params)
|
||||
var/obj/item/stack/M = W
|
||||
if(istype(M, material_type))
|
||||
if(!check_material_input(user))
|
||||
return
|
||||
if(initial_amount < max_initial_amount)
|
||||
var/adding = min((max_initial_amount - initial_amount), M.amount)
|
||||
M.amount -= adding
|
||||
initial_amount += adding
|
||||
if (adding == 1)
|
||||
to_chat(user, "<span class='notice'>You insert [adding] [material_name_singular] into \the [src].</span>")
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You insert [adding] [material_name] into \the [src].</span>")
|
||||
M.zero_amount()
|
||||
else
|
||||
to_chat(user, "<span class='warning'>\The [src]'s material slots are full!</span>")
|
||||
return
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/twohanded/required/fuel_rod/material/attack_self(mob/user)
|
||||
if(expended)
|
||||
to_chat(user, "<span class='notice'>You have already removed [material_name] from \the [src].</span>")
|
||||
return
|
||||
|
||||
if(depleted_final)
|
||||
new material_type(user.loc, grown_amount)
|
||||
if (grown_amount == 1)
|
||||
to_chat(user, "<span class='notice'>You harvest [grown_amount] [material_name_singular] from \the [src].</span>") // Unlikely
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You harvest [grown_amount] [material_name] from \the [src].</span>")
|
||||
grown_amount = 0
|
||||
expend()
|
||||
else if(depletion)
|
||||
to_chat(user, "<span class='warning'>\The [src] has not fissiled enough to fully grow the sample. The progress bar shows it is [min(depletion/depletion_threshold*100,100)]% complete.</span>")
|
||||
else if(initial_amount)
|
||||
new material_type(user.loc, initial_amount)
|
||||
if (initial_amount == 1)
|
||||
to_chat(user, "<span class='notice'>You remove [initial_amount] [material_name_singular] from \the [src].</span>")
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You remove [initial_amount] [material_name] from \the [src].</span>")
|
||||
initial_amount = 0
|
||||
|
||||
/obj/item/twohanded/required/fuel_rod/material/examine(mob/user)
|
||||
. = ..()
|
||||
if(expended)
|
||||
. += "<span class='warning'>The material slots have been slagged by the extreme heat, you can't grow [material_name] in this rod again...</span>"
|
||||
return
|
||||
else if(depleted_final)
|
||||
if(grown_amount == 1)
|
||||
. += "<span class='warning'>This fuel rod's [material_name] are now fully grown, and it currently bears [grown_amount] harvestable [material_name_singular].</span>"
|
||||
else
|
||||
. += "<span class='warning'>This fuel rod's [material_name] are now fully grown, and it currently bears [grown_amount] harvestable [material_name].</span>"
|
||||
return
|
||||
if(depletion)
|
||||
. += "<span class='danger'>The sample is [min(depletion/depletion_threshold*100,100)]% fissiled.</span>"
|
||||
. += "<span class='disarm'>[initial_amount]/[max_initial_amount] of the slots for [material_name] are full.</span>"
|
||||
|
||||
/obj/item/twohanded/required/fuel_rod/material/telecrystal
|
||||
name = "Telecrystal Fuel Rod"
|
||||
desc = "A disguised titanium sheathed rod containing several small slots infused with uranium dioxide. Permits the insertion of telecrystals to grow more. Fissiles much faster than its standard counterpart"
|
||||
icon_state = "telecrystal"
|
||||
fuel_power = 0.30 // twice as powerful as a normal rod, you're going to need some engineering autism if you plan to mass produce TC
|
||||
depletion_speed_modifier = 3 // headstart, otherwise it takes two hours
|
||||
rad_strength = 1500
|
||||
max_initial_amount = 8
|
||||
multiplier = 3
|
||||
material_type = /obj/item/stack/telecrystal
|
||||
material_name = "telecrystals"
|
||||
material_name_singular = "telecrystal"
|
||||
|
||||
/obj/item/twohanded/required/fuel_rod/material/telecrystal/depletion_final(result_rod)
|
||||
..()
|
||||
if(!result_rod)
|
||||
fuel_power = 0.60 // thrice as powerful as plutonium, you'll want to get this one out quick!
|
||||
name = "Exhausted Telecrystal Fuel Rod"
|
||||
desc = "A highly energetic, disguised titanium sheathed rod containing a number of slots filled with greatly expanded telecrystals which can be removed by hand. It's extremely efficient as nuclear fuel, but will cause the reaction to get out of control if not properly utilised."
|
||||
icon_state = "telecrystal_used"
|
||||
AddComponent(/datum/component/radioactive, 3000, src)
|
||||
|
||||
/obj/item/twohanded/required/fuel_rod/material/bananium
|
||||
name = "Bananium Fuel Rod"
|
||||
desc = "A hilarious heavy-duty fuel rod which fissiles a bit slower than its cowardly counterparts. However, its cutting-edge cosmic clown technology allows rooms for extraordinarily exhilarating extraterrestrial element called bananium to menacingly multiply."
|
||||
icon_state = "bananium"
|
||||
fuel_power = 0.15
|
||||
depletion_speed_modifier = 3
|
||||
rad_strength = 350
|
||||
max_initial_amount = 10
|
||||
multiplier = 3
|
||||
material_type = /obj/item/stack/sheet/mineral/bananium
|
||||
material_name = "sheets of bananium"
|
||||
material_name_singular = "sheet of bananium"
|
||||
|
||||
/obj/item/twohanded/required/fuel_rod/material/bananium/deplete(amount=0.035)
|
||||
..()
|
||||
if(initial_amount == max_initial_amount && prob(10))
|
||||
playsound(src, pick('sound/items/bikehorn.ogg', 'yogstation/sound/misc/bikehorn_creepy.ogg'), 50) // HONK
|
||||
|
||||
/obj/item/twohanded/required/fuel_rod/material/bananium/depletion_final(result_rod)
|
||||
..()
|
||||
if(!result_rod)
|
||||
fuel_power = 0.3 // Be warned
|
||||
name = "Fully Grown Bananium Fuel Rod"
|
||||
desc = "A hilarious heavy-duty fuel rod which fissiles a bit slower than it cowardly counterparts. Its greatly grimacing grwoth stage is now over, and bananium outgrowth hums as if it's blatantly honking bike horns."
|
||||
icon_state = "bananium_used"
|
||||
AddComponent(/datum/component/radioactive, 1250, src)
|
||||
@@ -0,0 +1,31 @@
|
||||
// modular shitcode but it works:tm:
|
||||
|
||||
/obj/machinery/atmospherics/components/trinary/nuclear_reactor/multitool_act(mob/living/user, obj/item/multitool/I)
|
||||
if(istype(I))
|
||||
to_chat(user, "<span class='notice'>You add \the [src]'s ID into the multitool's buffer.</span>")
|
||||
I.buffer = src.id
|
||||
return TRUE
|
||||
/obj/machinery/computer/reactor/multitool_act(mob/living/user, obj/item/multitool/I)
|
||||
if(istype(I))
|
||||
to_chat(user, "<span class='notice'>You add the reactor's ID to \the [src]>")
|
||||
src.id = I.buffer
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/atmospherics/components/trinary/nuclear_reactor/cargo // easier on the brain
|
||||
|
||||
/obj/machinery/atmospherics/components/trinary/nuclear_reactor/cargo/New()
|
||||
id = rand(1, 1000000) // cmon, what are the chances?
|
||||
|
||||
// Cargo varients can be wrenched down and don't start linked to the default RMBK reactor
|
||||
|
||||
/obj/machinery/computer/reactor/control_rods/cargo
|
||||
anchored = FALSE
|
||||
id = null
|
||||
|
||||
/obj/machinery/computer/reactor/stats/cargo
|
||||
anchored = FALSE
|
||||
id = null
|
||||
|
||||
/obj/machinery/computer/reactor/fuel_rods/cargo
|
||||
anchored = FALSE
|
||||
id = null
|
||||
@@ -0,0 +1,8 @@
|
||||
/datum/uplink_item/device_tools/tc_rod
|
||||
name = "Telecrystal Fuel Rod"
|
||||
desc = "This special fuel rod has eight material slots that can be inserted with telecrystals, \
|
||||
once the rod has been fully depleted, you will be able to harvest the extra telecrystals. \
|
||||
Please note: This Rod fissiles much faster than it's regular counterpart, it doesn't take \
|
||||
much to overload the reactor with these..."
|
||||
item = /obj/item/twohanded/required/fuel_rod/material/telecrystal
|
||||
cost = 7
|
||||
@@ -162,6 +162,9 @@
|
||||
C.adjustArousalLoss(20)
|
||||
M.adjustArousalLoss(20)
|
||||
M.do_jitter_animation() //make your partner shake too!
|
||||
if (C.getArousalLoss() >= 100 && ishuman(C) && C.has_dna())
|
||||
var/mob/living/carbon/human/O = C
|
||||
O.mob_climax_partner(P, M, FALSE, TRUE, FALSE, TRUE) //climax with their partner remotely, and impreg because people keep asking!
|
||||
if(option == "Lick")
|
||||
to_chat(M, "<span class='love'>You feel a tongue lick you through the portal against your vagina.</span>")
|
||||
M.adjustArousalLoss(10)
|
||||
@@ -169,9 +172,6 @@
|
||||
to_chat(M, "<span class='love'>You feel someone touching your vagina through the portal.</span>")
|
||||
M.adjustArousalLoss(5)
|
||||
|
||||
if (C.getArousalLoss() >= 100 && ishuman(C) && C.has_dna())
|
||||
var/mob/living/carbon/human/O = C
|
||||
O.mob_climax_partner(P, M, FALSE, TRUE, FALSE, TRUE) //climax with their partner remotely, and impreg because people keep asking!
|
||||
return
|
||||
..()
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
@@ -390,9 +390,11 @@
|
||||
if(G.can_climax)
|
||||
setArousalLoss(min_arousal)
|
||||
|
||||
else //knots and other non-spilling orgasms
|
||||
else //knots, portal fleshlights, and other non-spilling orgasms
|
||||
if(!cover)
|
||||
if(do_after(src, mb_time, target = src) && in_range(src, L))
|
||||
if(!remote && !in_range(src, L))
|
||||
return
|
||||
if(do_after(src, mb_time, target = src))
|
||||
var/obj/item/organ/genital/penis/P = G
|
||||
if (P.condom)//condomed.
|
||||
src.condomclimax()
|
||||
|
||||
@@ -3072,6 +3072,7 @@
|
||||
#include "hyperstation\code\modules\antagonists\wendigo\mob\update_icons.dm"
|
||||
#include "hyperstation\code\modules\antagonists\werewolf\werewolf.dm"
|
||||
#include "hyperstation\code\modules\arousal\arousalhud.dm"
|
||||
#include "hyperstation\code\modules\cargo\exports\engineering.dm"
|
||||
#include "hyperstation\code\modules\cargo\exports\sweatshop.dm"
|
||||
#include "hyperstation\code\modules\cargo\packs\misc.dm"
|
||||
#include "hyperstation\code\modules\cargo\sweatshop\metal.dm"
|
||||
@@ -3089,9 +3090,14 @@
|
||||
#include "hyperstation\code\modules\crafting\recipes.dm"
|
||||
#include "hyperstation\code\modules\economy\account.dm"
|
||||
#include "hyperstation\code\modules\integrated_electronics\input.dm"
|
||||
#include "hyperstation\code\modules\mining\shelters.dm"
|
||||
#include "hyperstation\code\modules\mining\equipment\survival_pod.dm"
|
||||
#include "hyperstation\code\modules\mob\mob_helpers.dm"
|
||||
#include "hyperstation\code\modules\mob\living\status_indicators.dm"
|
||||
#include "hyperstation\code\modules\patreon\patreon.dm"
|
||||
#include "hyperstation\code\modules\power\reactor\fuel_rods.dm"
|
||||
#include "hyperstation\code\modules\power\reactor\rbmk.dm"
|
||||
#include "hyperstation\code\modules\power\reactor\reactor_cargo.dm"
|
||||
#include "hyperstation\code\modules\reagents\chemistry\reagents\food_reagents.dm"
|
||||
#include "hyperstation\code\modules\reagents\chemistry\reagents\hydroponics_reactions.dm"
|
||||
#include "hyperstation\code\modules\resize\resize_action.dm"
|
||||
@@ -3099,6 +3105,7 @@
|
||||
#include "hyperstation\code\modules\resize\sizechems.dm"
|
||||
#include "hyperstation\code\modules\resize\sizegun.dm"
|
||||
#include "hyperstation\code\modules\surgery\organs\augments_arms.dm"
|
||||
#include "hyperstation\code\modules\uplink\uplink_items.dm"
|
||||
#include "hyperstation\code\obj\ashtray.dm"
|
||||
#include "hyperstation\code\obj\bluespace sewing kit.dm"
|
||||
#include "hyperstation\code\obj\condom.dm"
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import { map, sortBy } from 'common/collections';
|
||||
import { flow } from 'common/fp';
|
||||
import { toFixed } from 'common/math';
|
||||
import { pureComponentHooks } from 'common/react';
|
||||
import { Component, Fragment } from 'inferno';
|
||||
import { Box, Button, Chart, ColorBox, Flex, Icon, LabeledList, ProgressBar, Section, Table } from '../components';
|
||||
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
|
||||
|
||||
export const NtosRbmkStats = props => {
|
||||
const { state } = props;
|
||||
const { act, data } = useBackend(props);
|
||||
const powerData = data.powerData.map((value, i) => [i, value]);
|
||||
const psiData = data.psiData.map((value, i) => [i, value]);
|
||||
const tempInputData = data.tempInputData.map((value, i) => [i, value]);
|
||||
const tempOutputdata = data.tempOutputdata.map((value, i) => [i, value]);
|
||||
return (
|
||||
<Section title="Reactor Management">
|
||||
<Section title="Legend:" buttons={
|
||||
<Button
|
||||
icon="search"
|
||||
onClick={() => act('swap_reactor')}
|
||||
content="Change Reactor" />
|
||||
}>
|
||||
Reactor Power (%):
|
||||
<ProgressBar
|
||||
value={data.power}
|
||||
minValue={0}
|
||||
maxValue={100}
|
||||
color="yellow" />
|
||||
<br />
|
||||
Reactor Pressure (PSI):
|
||||
<ProgressBar
|
||||
value={data.psi}
|
||||
minValue={0}
|
||||
maxValue={2000}
|
||||
color="white" >
|
||||
{data.psi} PSI
|
||||
</ProgressBar>
|
||||
Coolant temperature (°C):
|
||||
<ProgressBar
|
||||
value={data.coolantInput}
|
||||
minValue={-273.15}
|
||||
maxValue={1227}
|
||||
color="blue">
|
||||
{data.coolantInput} °C
|
||||
</ProgressBar>
|
||||
Outlet temperature (°C):
|
||||
<ProgressBar
|
||||
value={data.coolantOutput}
|
||||
minValue={-273.15}
|
||||
maxValue={1227}
|
||||
color="bad">
|
||||
{data.coolantOutput} °C
|
||||
</ProgressBar>
|
||||
</Section>
|
||||
<Section title="Reactor Statistics:" height="200px">
|
||||
<Chart.Line
|
||||
fillPositionedParent
|
||||
data={powerData}
|
||||
rangeX={[0, powerData.length - 1]}
|
||||
rangeY={[0, 1500]}
|
||||
strokeColor="rgba(255, 215,0, 1)"
|
||||
fillColor="rgba(255, 215, 0, 0.1)" />
|
||||
<Chart.Line
|
||||
fillPositionedParent
|
||||
data={psiData}
|
||||
rangeX={[0, psiData.length - 1]}
|
||||
rangeY={[0, 1500]}
|
||||
strokeColor="rgba(255,250,250, 1)"
|
||||
fillColor="rgba(255,250,250, 0.1)" />
|
||||
<Chart.Line
|
||||
fillPositionedParent
|
||||
data={tempInputData}
|
||||
rangeX={[0, tempInputData.length - 1]}
|
||||
rangeY={[-273.15, 1227]}
|
||||
strokeColor="rgba(127, 179, 255 , 1)"
|
||||
fillColor="rgba(127, 179, 255 , 0.1)" />
|
||||
<Chart.Line
|
||||
fillPositionedParent
|
||||
data={tempOutputdata}
|
||||
rangeX={[0, tempOutputdata.length - 1]}
|
||||
rangeY={[-273.15, 1227]}
|
||||
strokeColor="rgba(255, 0, 0 , 1)"
|
||||
fillColor="rgba(255, 0, 0 , 0.1)" />
|
||||
</Section>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Section, ProgressBar, LabeledList, NumberInput } from '../components';
|
||||
|
||||
export const RbmkControlRods = props => {
|
||||
const { state } = props;
|
||||
const { act, data } = useBackend(props);
|
||||
const control_rods = data.control_rods;
|
||||
const k = data.k;
|
||||
const desiredK = data.desiredK;
|
||||
return (
|
||||
<Section title="Control Rod Management:">
|
||||
Control Rod Insertion:
|
||||
<ProgressBar
|
||||
value={(control_rods / 100 * 100) * 0.01}
|
||||
ranges={{
|
||||
good: [0.7, Infinity],
|
||||
average: [0.4, 0.7],
|
||||
bad: [-Infinity, 0.4],
|
||||
}} />
|
||||
<br />
|
||||
Neutrons per generation (K):
|
||||
<br />
|
||||
<ProgressBar
|
||||
value={(k / 3 * 100) * 0.01}
|
||||
ranges={{
|
||||
good: [-Infinity, 0.4],
|
||||
average: [0.4, 0.6],
|
||||
bad: [0.6, Infinity],
|
||||
}}>
|
||||
{k}
|
||||
</ProgressBar>
|
||||
<br />
|
||||
Target criticality:
|
||||
<br />
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Target">
|
||||
<NumberInput
|
||||
animated
|
||||
value={desiredK}
|
||||
unit="k"
|
||||
width="125px"
|
||||
minValue={0}
|
||||
maxValue={3}
|
||||
step={0.1}
|
||||
onChange={(e, value) => act('input', {
|
||||
target: value,
|
||||
})} />
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
import { map, sortBy } from 'common/collections';
|
||||
import { flow } from 'common/fp';
|
||||
import { toFixed } from 'common/math';
|
||||
import { pureComponentHooks } from 'common/react';
|
||||
import { Component, Fragment } from 'inferno';
|
||||
import { Box, Button, Chart, ColorBox, Flex, Icon, LabeledList, ProgressBar, Section, Table } from '../components';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
|
||||
export const RbmkStats = props => {
|
||||
const { state } = props;
|
||||
const { act, data } = useBackend(props);
|
||||
const powerData = data.powerData.map((value, i) => [i, value]);
|
||||
const psiData = data.psiData.map((value, i) => [i, value]);
|
||||
const tempInputData = data.tempInputData.map((value, i) => [i, value]);
|
||||
const tempOutputdata = data.tempOutputdata.map((value, i) => [i, value]);
|
||||
return (
|
||||
<Section title="RBMK Stats:">
|
||||
<Section title="Legend:">
|
||||
Reactor Power (%):
|
||||
<ProgressBar
|
||||
value={data.power}
|
||||
minValue={0}
|
||||
maxValue={100}
|
||||
color="yellow" />
|
||||
<br />
|
||||
Reactor Pressure (PSI):
|
||||
<ProgressBar
|
||||
value={data.psi}
|
||||
minValue={0}
|
||||
maxValue={2000}
|
||||
color="white" >
|
||||
{data.psi} PSI
|
||||
</ProgressBar>
|
||||
Coolant temperature (°C):
|
||||
<ProgressBar
|
||||
value={data.coolantInput}
|
||||
minValue={-273.15}
|
||||
maxValue={1227}
|
||||
color="blue">
|
||||
{data.coolantInput} °C
|
||||
</ProgressBar>
|
||||
Outlet temperature (°C):
|
||||
<ProgressBar
|
||||
value={data.coolantOutput}
|
||||
minValue={-273.15}
|
||||
maxValue={1227}
|
||||
color="bad">
|
||||
{data.coolantOutput} °C
|
||||
</ProgressBar>
|
||||
</Section>
|
||||
<Section title="Reactor Statistics:" height="200px">
|
||||
<Chart.Line
|
||||
fillPositionedParent
|
||||
data={powerData}
|
||||
rangeX={[0, powerData.length - 1]}
|
||||
rangeY={[0, 1500]}
|
||||
strokeColor="rgba(255, 215,0, 1)"
|
||||
fillColor="rgba(255, 215, 0, 0.1)" />
|
||||
<Chart.Line
|
||||
fillPositionedParent
|
||||
data={psiData}
|
||||
rangeX={[0, psiData.length - 1]}
|
||||
rangeY={[0, 1500]}
|
||||
strokeColor="rgba(255,250,250, 1)"
|
||||
fillColor="rgba(255,250,250, 0.1)" />
|
||||
<Chart.Line
|
||||
fillPositionedParent
|
||||
data={tempInputData}
|
||||
rangeX={[0, tempInputData.length - 1]}
|
||||
rangeY={[-273.15, 1227]}
|
||||
strokeColor="rgba(127, 179, 255 , 1)"
|
||||
fillColor="rgba(127, 179, 255 , 0.1)" />
|
||||
<Chart.Line
|
||||
fillPositionedParent
|
||||
data={tempOutputdata}
|
||||
rangeX={[0, tempOutputdata.length - 1]}
|
||||
rangeY={[-273.15, 1227]}
|
||||
strokeColor="rgba(255, 0, 0 , 1)"
|
||||
fillColor="rgba(255, 0, 0 , 0.1)" />
|
||||
</Section>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
import { classes } from 'common/react';
|
||||
import { IS_IE8 } from '../byond';
|
||||
|
||||
/**
|
||||
* Brings Layout__content DOM element back to focus.
|
||||
*
|
||||
* Commonly used to keep the content scrollable in IE.
|
||||
*/
|
||||
export const refocusLayout = () => {
|
||||
// IE8: Focus method is seemingly fucked.
|
||||
if (IS_IE8) {
|
||||
return;
|
||||
}
|
||||
const element = document.getElementById('Layout__content');
|
||||
if (element) {
|
||||
element.focus();
|
||||
}
|
||||
};
|
||||
|
||||
export const Layout = props => {
|
||||
const {
|
||||
className,
|
||||
theme = 'nanotrasen',
|
||||
children,
|
||||
} = props;
|
||||
return (
|
||||
<div className={'theme-' + theme}>
|
||||
<div
|
||||
className={classes([
|
||||
'Layout',
|
||||
className,
|
||||
])}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const LayoutContent = props => {
|
||||
const {
|
||||
className,
|
||||
scrollable,
|
||||
children,
|
||||
} = props;
|
||||
return (
|
||||
<div
|
||||
id="Layout__content"
|
||||
className={classes([
|
||||
'Layout__content',
|
||||
scrollable && 'Layout__content--scrollable',
|
||||
className,
|
||||
])}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Layout.Content = LayoutContent;
|
||||
@@ -0,0 +1,126 @@
|
||||
import { useBackend } from '../backend';
|
||||
import { Box, Button } from '../components';
|
||||
import { refocusLayout } from './Layout';
|
||||
import { Window } from './Window';
|
||||
|
||||
export const NtosWindow = (props, context) => {
|
||||
const {
|
||||
resizable,
|
||||
theme = 'ntos',
|
||||
children,
|
||||
} = props;
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
PC_batteryicon,
|
||||
PC_showbatteryicon,
|
||||
PC_batterypercent,
|
||||
PC_ntneticon,
|
||||
PC_apclinkicon,
|
||||
PC_stationtime,
|
||||
PC_programheaders = [],
|
||||
PC_showexitprogram,
|
||||
} = data;
|
||||
return (
|
||||
<Window
|
||||
theme={theme}
|
||||
resizable={resizable}>
|
||||
<div className="NtosWindow">
|
||||
<div
|
||||
className="NtosWindow__header NtosHeader"
|
||||
onMouseDown={() => {
|
||||
refocusLayout();
|
||||
}}>
|
||||
<div className="NtosHeader__left">
|
||||
<Box inline bold mr={2}>
|
||||
{PC_stationtime}
|
||||
</Box>
|
||||
<Box inline italic mr={2} opacity={0.33}>
|
||||
NtOS
|
||||
</Box>
|
||||
</div>
|
||||
<div className="NtosHeader__right">
|
||||
{PC_programheaders.map(header => (
|
||||
<Box key={header.icon} inline mr={1}>
|
||||
<img
|
||||
className="NtosHeader__icon"
|
||||
src={header.icon} />
|
||||
</Box>
|
||||
))}
|
||||
<Box inline>
|
||||
{PC_ntneticon && (
|
||||
<img
|
||||
className="NtosHeader__icon"
|
||||
src={PC_ntneticon} />
|
||||
)}
|
||||
</Box>
|
||||
{!!PC_showbatteryicon && PC_batteryicon && (
|
||||
<Box inline mr={1}>
|
||||
{PC_batteryicon && (
|
||||
<img
|
||||
className="NtosHeader__icon"
|
||||
src={PC_batteryicon} />
|
||||
)}
|
||||
{PC_batterypercent && (
|
||||
PC_batterypercent
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
{PC_apclinkicon && (
|
||||
<Box inline mr={1}>
|
||||
<img
|
||||
className="NtosHeader__icon"
|
||||
src={PC_apclinkicon} />
|
||||
</Box>
|
||||
)}
|
||||
{!!PC_showexitprogram && (
|
||||
<Button
|
||||
width="26px"
|
||||
lineHeight="22px"
|
||||
textAlign="center"
|
||||
color="transparent"
|
||||
icon="window-minimize-o"
|
||||
tooltip="Minimize"
|
||||
tooltipPosition="bottom"
|
||||
onClick={() => act('PC_minimize')} />
|
||||
)}
|
||||
{!!PC_showexitprogram && (
|
||||
<Button
|
||||
mr="-3px"
|
||||
width="26px"
|
||||
lineHeight="22px"
|
||||
textAlign="center"
|
||||
color="transparent"
|
||||
icon="window-close-o"
|
||||
tooltip="Close"
|
||||
tooltipPosition="bottom-left"
|
||||
onClick={() => act('PC_exit')} />
|
||||
)}
|
||||
{!PC_showexitprogram && (
|
||||
<Button
|
||||
mr="-3px"
|
||||
width="26px"
|
||||
lineHeight="22px"
|
||||
textAlign="center"
|
||||
color="transparent"
|
||||
icon="power-off"
|
||||
tooltip="Power off"
|
||||
tooltipPosition="bottom-left"
|
||||
onClick={() => act('PC_shutdown')} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const NtosWindowContent = props => {
|
||||
return (
|
||||
<div className="NtosWindow__content">
|
||||
<Window.Content {...props} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
NtosWindow.Content = NtosWindowContent;
|
||||
@@ -0,0 +1,142 @@
|
||||
import { classes } from 'common/react';
|
||||
import { decodeHtmlEntities, toTitleCase } from 'common/string';
|
||||
import { Component, Fragment } from 'inferno';
|
||||
import { useBackend } from '../backend';
|
||||
import { IS_IE8, runCommand, winset } from '../byond';
|
||||
import { Box, Icon } from '../components';
|
||||
import { UI_DISABLED, UI_INTERACTIVE, UI_UPDATE } from '../constants';
|
||||
import { dragStartHandler, resizeStartHandler } from '../drag';
|
||||
import { releaseHeldKeys } from '../hotkeys';
|
||||
import { createLogger } from '../logging';
|
||||
import { Layout, refocusLayout } from './Layout';
|
||||
|
||||
const logger = createLogger('Window');
|
||||
|
||||
export class Window extends Component {
|
||||
componentDidMount() {
|
||||
refocusLayout();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
resizable,
|
||||
theme,
|
||||
children,
|
||||
} = this.props;
|
||||
const {
|
||||
config,
|
||||
debugLayout,
|
||||
} = useBackend(this.context);
|
||||
// Determine when to show dimmer
|
||||
const showDimmer = config.observer
|
||||
? config.status < UI_DISABLED
|
||||
: config.status < UI_INTERACTIVE;
|
||||
return (
|
||||
<Layout
|
||||
className="Window"
|
||||
theme={theme}>
|
||||
<TitleBar
|
||||
className="Window__titleBar"
|
||||
title={decodeHtmlEntities(config.title)}
|
||||
status={config.status}
|
||||
fancy={config.fancy}
|
||||
onDragStart={dragStartHandler}
|
||||
onClose={() => {
|
||||
logger.log('pressed close');
|
||||
releaseHeldKeys();
|
||||
winset(config.window, 'is-visible', false);
|
||||
runCommand(`uiclose ${config.ref}`);
|
||||
}} />
|
||||
<div
|
||||
className={classes([
|
||||
'Window__rest',
|
||||
debugLayout && 'debug-layout',
|
||||
])}>
|
||||
{children}
|
||||
{showDimmer && (
|
||||
<div className="Window__dimmer" />
|
||||
)}
|
||||
</div>
|
||||
{config.fancy && resizable && (
|
||||
<Fragment>
|
||||
<div className="Window__resizeHandle__e"
|
||||
onMousedown={resizeStartHandler(1, 0)} />
|
||||
<div className="Window__resizeHandle__s"
|
||||
onMousedown={resizeStartHandler(0, 1)} />
|
||||
<div className="Window__resizeHandle__se"
|
||||
onMousedown={resizeStartHandler(1, 1)} />
|
||||
</Fragment>
|
||||
)}
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const WindowContent = props => {
|
||||
const { scrollable, children } = props;
|
||||
// A bit lazy to actually write styles for it,
|
||||
// so we simply include a Box with margins.
|
||||
return (
|
||||
<Layout.Content
|
||||
scrollable={scrollable}>
|
||||
<Box m={1}>
|
||||
{children}
|
||||
</Box>
|
||||
</Layout.Content>
|
||||
);
|
||||
};
|
||||
|
||||
Window.Content = WindowContent;
|
||||
|
||||
const statusToColor = status => {
|
||||
switch (status) {
|
||||
case UI_INTERACTIVE:
|
||||
return 'good';
|
||||
case UI_UPDATE:
|
||||
return 'average';
|
||||
case UI_DISABLED:
|
||||
default:
|
||||
return 'bad';
|
||||
}
|
||||
};
|
||||
|
||||
const TitleBar = props => {
|
||||
const {
|
||||
className,
|
||||
title,
|
||||
status,
|
||||
fancy,
|
||||
onDragStart,
|
||||
onClose,
|
||||
} = props;
|
||||
return (
|
||||
<div
|
||||
className={classes([
|
||||
'TitleBar',
|
||||
className,
|
||||
])}>
|
||||
<Icon
|
||||
className="TitleBar__statusIcon"
|
||||
color={statusToColor(status)}
|
||||
name="eye" />
|
||||
<div className="TitleBar__title">
|
||||
{title === title.toLowerCase()
|
||||
? toTitleCase(title)
|
||||
: title}
|
||||
</div>
|
||||
<div
|
||||
className="TitleBar__dragZone"
|
||||
onMousedown={e => fancy && onDragStart(e)} />
|
||||
{!!fancy && (
|
||||
<div
|
||||
className="TitleBar__close TitleBar__clickable"
|
||||
// IE8: Synthetic onClick event doesn't work on IE8.
|
||||
// IE8: Use a plain character instead of a unicode symbol.
|
||||
// eslint-disable-next-line react/no-unknown-property
|
||||
onclick={onClose}>
|
||||
{IS_IE8 ? 'x' : '×'}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export { Layout, refocusLayout } from './Layout';
|
||||
export { NtosWindow } from './NtosWindow';
|
||||
export { Window } from './Window';
|
||||
@@ -60,6 +60,9 @@ import { NtosNetChat } from './interfaces/NtosNetChat';
|
||||
import { NtosNetDownloader } from './interfaces/NtosNetDownloader';
|
||||
import { NtosSupermatterMonitor } from './interfaces/NtosSupermatterMonitor';
|
||||
import { NtosWrapper } from './interfaces/NtosWrapper';
|
||||
import { NtosRbmkStats } from './interfaces/NtosRbmkStats';
|
||||
import { RbmkControlRods } from './interfaces/RbmkControlRods';
|
||||
import { RbmkStats } from './interfaces/RbmkStats';
|
||||
import { NuclearBomb } from './interfaces/NuclearBomb';
|
||||
import { OperatingComputer } from './interfaces/OperatingComputer';
|
||||
import { OreBox } from './interfaces/OreBox';
|
||||
@@ -370,6 +373,24 @@ const ROUTES = {
|
||||
scrollable: true,
|
||||
theme: 'ntos',
|
||||
},
|
||||
ntosrbmkstats: {
|
||||
component: () => NtosRbmkStats,
|
||||
wrapper: () => NtosWrapper,
|
||||
scrollable: true,
|
||||
theme: 'ntos',
|
||||
},
|
||||
rbmkcontrolrods: {
|
||||
component: () => RbmkControlRods,
|
||||
wrapper: () => NtosWrapper,
|
||||
scrollable: true,
|
||||
theme: 'ntos',
|
||||
},
|
||||
rbmkstats: {
|
||||
component: () => RbmkStats,
|
||||
wrapper: () => NtosWrapper,
|
||||
scrollable: true,
|
||||
theme: 'ntos',
|
||||
},
|
||||
nuclear_bomb: {
|
||||
component: () => NuclearBomb,
|
||||
scrollable: false,
|
||||
|
||||