Merge branch 'master' into upstream-merge-31621
@@ -18,7 +18,7 @@ If you want to contribute the first thing you'll need to do is [set up Git](http
|
||||
|
||||
We have a [list of guides on the wiki](http://www.tgstation13.org/wiki/index.php/Guides#Development_and_Contribution_Guides) that will help you get started contributing to /tg/station with Git and Dream Maker. For beginners, it is recommended you work on small projects like bugfixes at first. If you need help learning to program in BYOND, check out this [repository of resources](http://www.byond.com/developer/articles/resources).
|
||||
|
||||
There is an open list of approachable issues for [your inspiration here](https://github.com/tgstation/-tg-station/issues?q=is%3Aopen+is%3Aissue+label%3A%22Easy+Fix%22).
|
||||
There is an open list of approachable issues for [your inspiration here](https://github.com/tgstation/-tg-station/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+First+Issue%22).
|
||||
|
||||
You can of course, as always, ask for help at [#coderbus](irc://irc.rizon.net/coderbus) on irc.rizon.net. We're just here to have fun and help out, so please don't expect professional support.
|
||||
|
||||
|
||||
@@ -47786,6 +47786,7 @@
|
||||
pixel_y = 4
|
||||
},
|
||||
/obj/item/hand_tele,
|
||||
/obj/item/melee/chainofcommand,
|
||||
/turf/open/floor/plasteel/grimy,
|
||||
/area/crew_quarters/heads/captain)
|
||||
"bQC" = (
|
||||
|
||||
@@ -141,6 +141,7 @@
|
||||
#define ADMIN_PUNISHMENT_BRAINDAMAGE "Brain damage"
|
||||
#define ADMIN_PUNISHMENT_GIB "Gib"
|
||||
#define ADMIN_PUNISHMENT_BSA "Bluespace Artillery Device"
|
||||
#define ADMIN_PUNISHMENT_FIREBALL "Fireball"
|
||||
|
||||
#define AHELP_ACTIVE 1
|
||||
#define AHELP_CLOSED 2
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#define SPACE_HEAT_TRANSFER_COEFFICIENT 0.2 //a hack to partly simulate radiative heat
|
||||
#define OPEN_HEAT_TRANSFER_COEFFICIENT 0.4
|
||||
#define WINDOW_HEAT_TRANSFER_COEFFICIENT 0.1 //a hack for now
|
||||
#define HEAT_CAPACITY_VACUUM 7000 //a hack to help make vacuums "cold", sacrificing realism for gameplay
|
||||
//Must be between 0 and 1. Values closer to 1 equalize temperature faster
|
||||
//Should not exceed 0.4 else strange heat flow occur
|
||||
#define FIRE_MINIMUM_TEMPERATURE_TO_SPREAD 150+T0C
|
||||
|
||||
@@ -42,9 +42,10 @@ Last space-z level = empty
|
||||
#define ZLEVEL_LAVALAND 5
|
||||
#define ZLEVEL_CITYOFCOGS 6
|
||||
#define ZLEVEL_EMPTY_SPACE 12
|
||||
#define ZLEVEL_TRANSIT 11
|
||||
//Unless you modify it in map config should be equal to ZLEVEL_SPACEMAX
|
||||
#define ZLEVEL_TRANSIT 13
|
||||
|
||||
#define ZLEVEL_SPACEMIN 3
|
||||
#define ZLEVEL_SPACEMAX 12
|
||||
#define ZLEVEL_SPACEMAX 13
|
||||
|
||||
#define SPACERUIN_MAP_EDGE_PAD 15
|
||||
@@ -26,7 +26,7 @@
|
||||
var/require_comms_key = FALSE
|
||||
|
||||
/datum/world_topic/proc/TryRun(list/input)
|
||||
key_valid = !config || CONFIG_GET(string/comms_key) != input["key"]
|
||||
key_valid = config && (CONFIG_GET(string/comms_key) == input["key"])
|
||||
if(require_comms_key && !key_valid)
|
||||
return "Bad Key"
|
||||
input -= "key"
|
||||
@@ -124,12 +124,8 @@
|
||||
return NC.Run(input["sender"], input["namecheck"])
|
||||
|
||||
/datum/world_topic/adminwho
|
||||
<<<<<<< HEAD
|
||||
keyword = "namecheck"
|
||||
=======
|
||||
keyword = "adminwho"
|
||||
require_comms_key = TRUE
|
||||
>>>>>>> 9a91eeb... Add require_comms_key = TRUE to some topic that need it
|
||||
|
||||
/datum/world_topic/adminwho/Run(list/input)
|
||||
return ircadminwho()
|
||||
|
||||
@@ -450,7 +450,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
|
||||
if(!L.client.played)
|
||||
SEND_SOUND(L, sound(sound, repeat = 0, wait = 0, volume = 25, channel = CHANNEL_AMBIENCE))
|
||||
L.client.played = TRUE
|
||||
addtimer(CALLBACK(L.client, /client/proc/ResetAmbiencePlayed), 600)
|
||||
addtimer(CALLBACK(L.client, /client/proc/ResetAmbiencePlayed), 600)
|
||||
|
||||
/client/proc/ResetAmbiencePlayed()
|
||||
played = FALSE
|
||||
|
||||
@@ -31,8 +31,5 @@
|
||||
if(cell && (cell.charge / cell.maxcharge > COG_MAX_SIPHON_THRESHOLD))
|
||||
cell.use(1)
|
||||
adjust_clockwork_power(1) //Power is shared, so only do it once; this runs very quickly so it's about 1W/second
|
||||
if(prob(1))
|
||||
playsound(apc, 'sound/machines/clockcult/steam_whoosh.ogg', 10, TRUE)
|
||||
new/obj/effect/temp_visual/steam(get_turf(apc), pick(GLOB.cardinals))
|
||||
|
||||
#undef COG_MAX_SIPHON_THRESHOLD
|
||||
|
||||
@@ -231,6 +231,13 @@
|
||||
quickbind = TRUE
|
||||
quickbind_desc = "Creates a stargazer, which generates power when near starlight."
|
||||
|
||||
/datum/clockwork_scripture/create_object/stargazer/check_special_requirements()
|
||||
var/area/A = get_area(invoker)
|
||||
if(A.outdoors || A.map_name == "Space" || !A.blob_allowed)
|
||||
to_chat(invoker, "<span class='danger'>Stargazers can't be built off-station.</span>")
|
||||
return
|
||||
return TRUE
|
||||
|
||||
|
||||
//Integration Cog: Creates an integration cog that can be inserted into APCs to passively siphon power.
|
||||
/datum/clockwork_scripture/create_object/integration_cog
|
||||
|
||||
@@ -50,6 +50,10 @@
|
||||
if(isspaceturf(T))
|
||||
has_starlight = TRUE
|
||||
break
|
||||
if(has_starlight && anchored)
|
||||
var/area/A = get_area(src)
|
||||
if(A.outdoors || A.map_name == "Space" || !A.blob_allowed)
|
||||
has_starlight = FALSE
|
||||
if(old_status != has_starlight)
|
||||
if(has_starlight)
|
||||
visible_message("<span class='nzcrentr_small'>[src] hums and shines brilliantly!</span>")
|
||||
|
||||
@@ -67,6 +67,8 @@
|
||||
push_over()
|
||||
|
||||
/obj/item/cardboard_cutout/bullet_act(obj/item/projectile/P)
|
||||
if(istype(P, /obj/item/projectile/bullet/reusable))
|
||||
P.on_hit(src, 0)
|
||||
visible_message("<span class='danger'>[src] has been hit by [P]!</span>")
|
||||
playsound(src, 'sound/weapons/slice.ogg', 50, 1)
|
||||
if(prob(P.damage))
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
name = "plastic explosive"
|
||||
desc = "Used to put holes in specific areas without too much extra hole."
|
||||
icon_state = "plastic-explosive0"
|
||||
item_state = "plasticx"
|
||||
item_state = "plastic-explosive"
|
||||
lefthand_file = 'icons/mob/inhands/weapons/bombs_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/weapons/bombs_righthand.dmi'
|
||||
flags_1 = NOBLUDGEON_1
|
||||
flags_2 = NO_EMP_WIRES_2
|
||||
det_time = 10
|
||||
display_timer = 0
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
origin_tech = "syndicate=1"
|
||||
var/atom/target = null
|
||||
var/mutable_appearance/plastic_overlay
|
||||
var/obj/item/device/assembly_holder/nadeassembly = null
|
||||
@@ -16,7 +20,7 @@
|
||||
var/boom_sizes = list(0, 0, 3)
|
||||
|
||||
/obj/item/grenade/plastic/New()
|
||||
plastic_overlay = mutable_appearance(icon, "[item_state]2")
|
||||
plastic_overlay = mutable_appearance(icon, "[item_state]2", HIGH_OBJ_LAYER)
|
||||
..()
|
||||
|
||||
/obj/item/grenade/plastic/Destroy()
|
||||
@@ -112,7 +116,7 @@
|
||||
message_admins("[ADMIN_LOOKUPFLW(user)] planted [name] on [target.name] at [ADMIN_COORDJMP(target)] with [det_time] second fuse",0,1)
|
||||
log_game("[key_name(user)] planted [name] on [target.name] at [COORD(src)] with [det_time] second fuse")
|
||||
|
||||
target.add_overlay(plastic_overlay, 1)
|
||||
target.add_overlay(plastic_overlay, TRUE)
|
||||
if(!nadeassembly)
|
||||
to_chat(user, "<span class='notice'>You plant the bomb. Timer counting down from [det_time].</span>")
|
||||
addtimer(CALLBACK(src, .proc/prime), det_time*10)
|
||||
@@ -154,21 +158,12 @@
|
||||
name = "C4"
|
||||
desc = "Used to put holes in specific areas without too much extra hole. A saboteur's favorite."
|
||||
gender = PLURAL
|
||||
icon = 'icons/obj/grenade.dmi'
|
||||
icon_state = "plastic-explosive0"
|
||||
item_state = "plasticx"
|
||||
lefthand_file = 'icons/mob/inhands/weapons/bombs_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/weapons/bombs_righthand.dmi'
|
||||
flags_1 = NOBLUDGEON_1
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
origin_tech = "syndicate=1"
|
||||
var/timer = 10
|
||||
var/open_panel = 0
|
||||
|
||||
/obj/item/grenade/plastic/c4/New()
|
||||
wires = new /datum/wires/explosive/c4(src)
|
||||
..()
|
||||
plastic_overlay = mutable_appearance(icon, "plastic-explosive2")
|
||||
|
||||
/obj/item/grenade/plastic/c4/Destroy()
|
||||
qdel(wires)
|
||||
@@ -234,14 +229,14 @@
|
||||
if(!user.temporarilyRemoveItemFromInventory(src))
|
||||
return
|
||||
src.target = AM
|
||||
forceMove(null)
|
||||
moveToNullspace()
|
||||
|
||||
var/message = "[ADMIN_LOOKUPFLW(user)] planted [name] on [target.name] at [ADMIN_COORDJMP(target)] with [timer] second fuse"
|
||||
GLOB.bombers += message
|
||||
message_admins(message,0,1)
|
||||
log_game("[key_name(user)] planted [name] on [target.name] at [COORD(target)] with [timer] second fuse")
|
||||
|
||||
target.add_overlay(plastic_overlay, 1)
|
||||
target.add_overlay(plastic_overlay, TRUE)
|
||||
to_chat(user, "<span class='notice'>You plant the bomb. Timer counting down from [timer].</span>")
|
||||
addtimer(CALLBACK(src, .proc/explode), timer * 10)
|
||||
|
||||
@@ -272,5 +267,6 @@
|
||||
desc = "A shaped high-explosive breaching charge. Designed to ensure user safety and wall nonsafety."
|
||||
icon_state = "plasticx40"
|
||||
item_state = "plasticx4"
|
||||
gender = PLURAL
|
||||
directional = TRUE
|
||||
boom_sizes = list(0, 2, 5)
|
||||
|
||||
@@ -65,10 +65,12 @@
|
||||
playsound(src.loc, 'sound/items/bikehorn.ogg', 50, 1)
|
||||
|
||||
/obj/item/target/bullet_act(obj/item/projectile/P)
|
||||
if(istype(P, /obj/item/projectile/bullet/reusable)) // If it's a foam dart, don't bother with any of this other shit
|
||||
return P.on_hit(src, 0)
|
||||
var/p_x = P.p_x + pick(0,0,0,0,0,-1,1) // really ugly way of coding "sometimes offset P.p_x!"
|
||||
var/p_y = P.p_y + pick(0,0,0,0,0,-1,1)
|
||||
var/decaltype = DECALTYPE_SCORCH
|
||||
if(istype(/obj/item/projectile/bullet, P))
|
||||
if(istype(P, /obj/item/projectile/bullet))
|
||||
decaltype = DECALTYPE_BULLET
|
||||
var/icon/C = icon(icon,icon_state)
|
||||
if(C.GetPixel(p_x, p_y) && P.original == src && overlays.len <= 35) // if the located pixel isn't blank (null)
|
||||
@@ -92,4 +94,4 @@
|
||||
return -1
|
||||
|
||||
#undef DECALTYPE_SCORCH
|
||||
#undef DECALTYPE_BULLET
|
||||
#undef DECALTYPE_BULLET
|
||||
|
||||
@@ -121,7 +121,7 @@ GLOBAL_PROTECT(security_mode)
|
||||
var/datum/world_topic/handler
|
||||
for(var/I in topic_handlers)
|
||||
if(input[I])
|
||||
handler = I
|
||||
handler = topic_handlers[I]
|
||||
break
|
||||
|
||||
if((!handler || initial(handler.log)) && config && CONFIG_GET(flag/log_world_topic))
|
||||
@@ -133,7 +133,7 @@ GLOBAL_PROTECT(security_mode)
|
||||
return
|
||||
|
||||
handler = new handler()
|
||||
return handler.Run(input)
|
||||
return handler.TryRun(input)
|
||||
|
||||
/world/proc/AnnouncePR(announcement, list/payload)
|
||||
var/static/list/PRcounts = list() //PR id -> number of times announced this round
|
||||
|
||||
@@ -1166,7 +1166,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
|
||||
if(!holder)
|
||||
return
|
||||
|
||||
var/list/punishment_list = list(ADMIN_PUNISHMENT_LIGHTNING, ADMIN_PUNISHMENT_BRAINDAMAGE, ADMIN_PUNISHMENT_GIB, ADMIN_PUNISHMENT_BSA)
|
||||
var/list/punishment_list = list(ADMIN_PUNISHMENT_LIGHTNING, ADMIN_PUNISHMENT_BRAINDAMAGE, ADMIN_PUNISHMENT_GIB, ADMIN_PUNISHMENT_BSA, ADMIN_PUNISHMENT_FIREBALL)
|
||||
|
||||
var/punishment = input("Choose a punishment", "DIVINE SMITING") as null|anything in punishment_list
|
||||
|
||||
@@ -1186,6 +1186,8 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
|
||||
target.gib(FALSE)
|
||||
if(ADMIN_PUNISHMENT_BSA)
|
||||
bluespace_artillery(target)
|
||||
if(ADMIN_PUNISHMENT_FIREBALL)
|
||||
new /obj/effect/temp_visual/target(get_turf(target))
|
||||
|
||||
var/msg = "[key_name_admin(usr)] punished [key_name_admin(target)] with [punishment]."
|
||||
message_admins(msg)
|
||||
@@ -1253,4 +1255,4 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
|
||||
to_chat(usr, "<span class='danger'>ERROR: Unable to update player flags. Please check logs.</span>")
|
||||
else
|
||||
message_admins("[key_name_admin(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name_admin(C)]")
|
||||
log_admin("[key_name(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name(C)]")
|
||||
log_admin("[key_name(usr)] has [newstate ? "activated" : "deactivated"] job exp exempt status on [key_name(C)]")
|
||||
|
||||
@@ -71,6 +71,9 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
|
||||
for(var/id in cached_gases)
|
||||
var/gas_data = cached_gases[id]
|
||||
. += gas_data[MOLES] * gas_data[GAS_META][META_GAS_SPECIFIC_HEAT]
|
||||
if(!.) //if no heat capacity, we're a vacuum - things get weird like this but this hack sorta works
|
||||
. += HEAT_CAPACITY_VACUUM
|
||||
|
||||
|
||||
/datum/gas_mixture/proc/heat_capacity_archived() //joules per kelvin
|
||||
var/list/cached_gases = gases
|
||||
|
||||
@@ -16,7 +16,7 @@ GLOBAL_LIST_INIT(hardcoded_gases, list(/datum/gas/oxygen, /datum/gas/nitrogen, /
|
||||
.[gas_path] = gas_info
|
||||
|
||||
/proc/gas_id2path(id)
|
||||
var/meta_gas = meta_gas_list()
|
||||
var/list/meta_gas = GLOB.meta_gas_info
|
||||
for(var/path in meta_gas)
|
||||
if(meta_gas[path][META_GAS_ID] == id)
|
||||
return path
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
initial_temperature = TCMB
|
||||
|
||||
/datum/gas_mixture/immutable/space/heat_capacity()
|
||||
return 7000
|
||||
return HEAT_CAPACITY_VACUUM
|
||||
|
||||
/datum/gas_mixture/immutable/space/remove()
|
||||
return copy() //we're always empty, so we can just return a copy.
|
||||
|
||||
@@ -217,7 +217,9 @@
|
||||
|
||||
if(abs(temperature_delta) > 1)
|
||||
var/air_heat_capacity = air1.heat_capacity()
|
||||
var/heat = ((1 - cold_protection) * 0.1 + conduction_coefficient) * temperature_delta * (1 / air_heat_capacity + 1 / heat_capacity)
|
||||
|
||||
var/heat = ((1 - cold_protection) * 0.1 + conduction_coefficient) * temperature_delta * (air_heat_capacity * heat_capacity / (air_heat_capacity + heat_capacity))
|
||||
|
||||
air1.temperature = max(air1.temperature - heat / air_heat_capacity, TCMB)
|
||||
mob_occupant.bodytemperature = max(mob_occupant.bodytemperature + heat / heat_capacity, TCMB)
|
||||
|
||||
|
||||
@@ -273,7 +273,7 @@
|
||||
return ..()
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/obj_break(damage_flag)
|
||||
if((flags_1 & BROKEN) || (flags_1 & NODECONSTRUCT_1))
|
||||
if((stat & BROKEN) || (flags_1 & NODECONSTRUCT_1))
|
||||
return
|
||||
canister_break()
|
||||
|
||||
|
||||
@@ -663,6 +663,7 @@ GLOBAL_LIST(external_rsc_urls)
|
||||
return FALSE
|
||||
if ("key")
|
||||
return FALSE
|
||||
. = ..()
|
||||
|
||||
|
||||
/client/proc/change_view(new_size)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
S.remove_from_storage(O, src) //This will move the item to this item's contents
|
||||
to_chat(user, "<span class='notice'>You empty the ore in [S] into \the [src].</span>")
|
||||
else if(istype(W, /obj/item/crowbar))
|
||||
playsound(loc, W.usesound, 50, 1)
|
||||
playsound(src, W.usesound, 50, 1)
|
||||
var/obj/item/crowbar/C = W
|
||||
if(do_after(user, 50*C.toolspeed, target = src))
|
||||
user.visible_message("[user] pries \the [src] apart.", "<span class='notice'>You pry apart \the [src].</span>", "<span class='italics'>You hear splitting wood.</span>")
|
||||
@@ -51,8 +51,16 @@
|
||||
user << browse(dat, "window=orebox")
|
||||
|
||||
/obj/structure/ore_box/proc/dump_box_contents()
|
||||
for(var/obj/item/ore/O in contents)
|
||||
O.forceMove(loc)
|
||||
var/drop = drop_location()
|
||||
for(var/obj/item/ore/O in src)
|
||||
if(QDELETED(O))
|
||||
continue
|
||||
if(QDELETED(src))
|
||||
break
|
||||
O.forceMove(drop)
|
||||
if(TICK_CHECK)
|
||||
stoplag()
|
||||
drop = drop_location()
|
||||
|
||||
/obj/structure/ore_box/Topic(href, href_list)
|
||||
if(..())
|
||||
@@ -64,10 +72,9 @@
|
||||
src.add_fingerprint(usr)
|
||||
if(href_list["removeall"])
|
||||
dump_box_contents()
|
||||
to_chat(usr, "<span class='notice'>You empty the box.</span>")
|
||||
to_chat(usr, "<span class='notice'>You open the release hatch on the box..</span>")
|
||||
updateUsrDialog()
|
||||
|
||||
|
||||
/obj/structure/ore_box/deconstruct(disassembled = TRUE, mob/user)
|
||||
var/obj/item/stack/sheet/mineral/wood/WD = new (loc, 4)
|
||||
if(user)
|
||||
|
||||
@@ -110,6 +110,8 @@
|
||||
if(obscuredTurfs[t])
|
||||
if(!t.obscured)
|
||||
t.obscured = image('icons/effects/cameravis.dmi', t, null, LIGHTING_LAYER+1)
|
||||
t.obscured.pixel_x = -t.pixel_x
|
||||
t.obscured.pixel_y = -t.pixel_y
|
||||
t.obscured.plane = LIGHTING_PLANE+1
|
||||
obscured += t.obscured
|
||||
for(var/eye in seenby)
|
||||
@@ -165,6 +167,8 @@
|
||||
var/turf/t = turf
|
||||
if(!t.obscured)
|
||||
t.obscured = image('icons/effects/cameravis.dmi', t, null, LIGHTING_LAYER+1)
|
||||
t.obscured.pixel_x = -t.pixel_x
|
||||
t.obscured.pixel_y = -t.pixel_y
|
||||
t.obscured.plane = LIGHTING_PLANE+1
|
||||
obscured += t.obscured
|
||||
|
||||
|
||||
@@ -190,11 +190,17 @@
|
||||
break_light_tube(1)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/light/built
|
||||
icon_state = "tube-empty"
|
||||
|
||||
/obj/machinery/light/built/New()
|
||||
status = LIGHT_EMPTY
|
||||
update(0)
|
||||
..()
|
||||
|
||||
/obj/machinery/light/small/built
|
||||
icon_state = "bulb-empty"
|
||||
|
||||
/obj/machinery/light/small/built/New()
|
||||
status = LIGHT_EMPTY
|
||||
update(0)
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
if("power")
|
||||
on = !on
|
||||
if(on)
|
||||
message_admins("[key_name_admin(usr)] activated a smoke machine that contains [english_list(reagents.reagent_list)] at [ADMIN_COORDJMP(src)].")
|
||||
log_admin("[key_name(usr)] activated a smoke machine that contains [english_list(reagents.reagent_list)] at [COORD(src)].")
|
||||
add_logs(usr, src, "has activated [src] which contains [english_list(reagents.reagent_list)].")
|
||||
if("goScreen")
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
name = "conveyor belt"
|
||||
desc = "A conveyor belt."
|
||||
anchored = TRUE
|
||||
layer = BELOW_OPEN_DOOR_LAYER
|
||||
var/operating = FALSE // 1 if running forward, -1 if backwards, 0 if off
|
||||
var/operable = 1 // true if can operate (no broken segments in this belt run)
|
||||
var/forwards // this is the default (forward) direction, set by the map dir
|
||||
|
||||
@@ -85,14 +85,15 @@ All ShuttleMove procs go here
|
||||
if(rotation)
|
||||
shuttleRotate(rotation) //see shuttle_rotate.dm
|
||||
loc = newT
|
||||
if(length(client_mobs_in_contents))
|
||||
update_parallax_contents()
|
||||
return TRUE
|
||||
|
||||
// Called on atoms after everything has been moved
|
||||
/atom/movable/proc/afterShuttleMove(list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir)
|
||||
if(light)
|
||||
update_light()
|
||||
|
||||
update_parallax_contents()
|
||||
|
||||
return TRUE
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
author: "CitadelStationBot"
|
||||
delete-after: True
|
||||
changes:
|
||||
- bugfix: "AI eye camera static is now correctly positioned on Lavaland."
|
||||
@@ -0,0 +1,4 @@
|
||||
author: "WJohnston"
|
||||
delete-after: True
|
||||
changes:
|
||||
- imageadd: "Black carpets no longer have an ugly periodic black line in them. Regular catwalks are now totally opaque so you won't accidentally click on space when trying to place wires or tiles on them."
|
||||
@@ -0,0 +1,5 @@
|
||||
author: "ShizCalev"
|
||||
delete-after: True
|
||||
changes:
|
||||
- bugfix: "C4 will no longer appear underneath objects on walls."
|
||||
- bugfix: "Plastic explosives will no longer be invisible after being planted."
|
||||
@@ -0,0 +1,4 @@
|
||||
author: "Naksu"
|
||||
delete-after: True
|
||||
changes:
|
||||
- bugfix: "Reusable projectiles such as foam darts no longer get deleted if they're shot at a shooting range target or a cardboard cutout."
|
||||
@@ -0,0 +1,4 @@
|
||||
author: "ninjanomnom"
|
||||
delete-after: True
|
||||
changes:
|
||||
- bugfix: "Mobs that didn't move during shuttle launch would not have their parallax updated. This is fixed now."
|
||||
@@ -0,0 +1,5 @@
|
||||
author: "Xhuis"
|
||||
delete-after: True
|
||||
changes:
|
||||
- balance: "Stargazers can no longer be built off-station, even in new areas."
|
||||
- balance: "Integration cogs no longer emit sounds and steam visuals when active."
|
||||
|
Before Width: | Height: | Size: 733 B After Width: | Height: | Size: 743 B |
|
Before Width: | Height: | Size: 749 B After Width: | Height: | Size: 759 B |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
//Tool for web based TGUI compilation
|
||||
//by Cyberboss
|
||||
|
||||
//RESTRICTIONS
|
||||
//Restricted to Windows for now because it uses mklink cmd function
|
||||
|
||||
//INSTALLATION
|
||||
//Setup PHP
|
||||
//Place this file in it's own directory and rewrite all requests to the directory to this file
|
||||
//add extension=php_fileinfo.dll to php.ini
|
||||
//ensure fastcgi.impersonate is set to 0 in php.ini
|
||||
//clone a tgui repository and place next to this file
|
||||
//run install_dependencies.bat
|
||||
//MOVE (not copy) the node_modules folder next to this file
|
||||
try{
|
||||
//CONFIG
|
||||
$repo_dir = 'tgstation';
|
||||
$path_to_tgui_from_repo = '/tgui';
|
||||
$full_path_to_gulp = 'C:/Users/Cyberboss/AppData/Roaming/npm/gulp'; //this needs to be read/executable by the PHP app pool
|
||||
$max_number_of_uploads = 20;
|
||||
//END CONFIG
|
||||
|
||||
function getGitRevision()
|
||||
{
|
||||
global $tgdir;
|
||||
$rev = trim(file_get_contents($tgdir . '/.git/HEAD'));
|
||||
|
||||
if (substr($rev, 0, 4) == 'ref:') {
|
||||
$tmp = explode('/', $rev);
|
||||
$ref = end($tmp);
|
||||
$rev = trim(file_get_contents($tgdir . "/.git/refs/heads/{$ref}"));
|
||||
}
|
||||
|
||||
return $rev;
|
||||
}
|
||||
|
||||
function extrapolate_git_url(){
|
||||
global $tgdir;
|
||||
$config = file($tgdir . '/.git/config', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
foreach($config as $line)
|
||||
if(strpos($line, 'url = ') !== false)
|
||||
return trim(explode('=', $line)[1]);
|
||||
}
|
||||
|
||||
function download_file($path){
|
||||
header('Content-type: application/zip');
|
||||
header('Content-Disposition: attachment; filename=' . basename($path));
|
||||
header('Content-length: ' . filesize($path));
|
||||
header('Pragma: no-cache');
|
||||
header('Expires: 0');
|
||||
readfile($path);
|
||||
}
|
||||
|
||||
function recurse_copy($src,$dst) {
|
||||
$dir = opendir($src);
|
||||
@mkdir($dst);
|
||||
while(false !== ( $file = readdir($dir)) ) {
|
||||
if (( $file != '.' ) && ( $file != '..' )) {
|
||||
if ( is_dir($src . '/' . $file) ) {
|
||||
recurse_copy($src . '/' . $file,$dst . '/' . $file);
|
||||
}
|
||||
else {
|
||||
copy($src . '/' . $file,$dst . '/' . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($dir);
|
||||
}
|
||||
|
||||
function rrmdir($dir) {
|
||||
if (is_dir($dir)) {
|
||||
$objects = scandir($dir);
|
||||
foreach ($objects as $object) {
|
||||
if ($object != "." && $object != "..") {
|
||||
if (is_dir($dir."/".$object))
|
||||
rrmdir($dir."/".$object);
|
||||
else
|
||||
unlink($dir."/".$object);
|
||||
}
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
|
||||
function update_git(){
|
||||
global $tgdir;
|
||||
shell_exec('cd ' . $tgdir . ' && git pull');
|
||||
}
|
||||
|
||||
$full_path_to_gulp = str_replace('/', '\\', $full_path_to_gulp);
|
||||
$parent_dir = str_replace('\\', '/', realpath(dirname(__FILE__)));
|
||||
$tgdir = $parent_dir . '/' . $repo_dir;
|
||||
|
||||
$revision = getGitRevision();
|
||||
$git_base = extrapolate_git_url();
|
||||
if($git_base)
|
||||
$commit_url = $git_base . '/commit/' . $revision;
|
||||
else
|
||||
$error = 'Unable to determine github URL!';
|
||||
|
||||
if($_SERVER['REQUEST_METHOD'] === 'POST'){
|
||||
$updated_git = isset($_POST['pull']);
|
||||
if($updated_git){
|
||||
update_git();
|
||||
$revision = getGitRevision();
|
||||
}
|
||||
$good_files = array();
|
||||
$finfo = new finfo(FILEINFO_MIME_TYPE);
|
||||
foreach($_FILES as $F){
|
||||
$name = $F['name'];
|
||||
if(isset($F['error']) && !is_array($F['error']) && $F['error'] == UPLOAD_ERR_OK && $F['size'] > 0 && strpos($name, '\\') == false && strpos($name, '/') == false){
|
||||
$ext = pathinfo($name, PATHINFO_EXTENSION);
|
||||
$mime = $finfo->file($F['tmp_name']);
|
||||
if($ext == 'ract' && $mime == 'text/plain')
|
||||
$good_files[] = $F;
|
||||
}
|
||||
}
|
||||
$the_count = count($good_files);
|
||||
if($the_count > 0 && $the_count < $max_number_of_uploads){
|
||||
$tgtgui_path = $tgdir . $path_to_tgui_from_repo;
|
||||
$requests_dir = $parent_dir . '/requests';
|
||||
if(!is_dir($requests_dir))
|
||||
mkdir($requests_dir);
|
||||
$target_path = str_replace('\\', '/', tempnam($requests_dir, 'tgui'));
|
||||
unlink($target_path);
|
||||
recurse_copy($tgtgui_path, $target_path);
|
||||
$parent_node = $parent_dir . '/node_modules';
|
||||
$target_node = $target_path . '/node_modules';
|
||||
exec('mklink /j "' . str_replace('/', '\\', $target_node) . '" "' . str_replace('/', '\\', $parent_node) . '"');
|
||||
|
||||
//now copy the uploads to the thing
|
||||
$target_interfaces = $target_path . '/src/interfaces/';
|
||||
foreach($good_files as $F){
|
||||
$target_name = $target_interfaces . $F['name'];
|
||||
if(file_exists($target_name))
|
||||
unlink($target_name); //remove the file
|
||||
move_uploaded_file($F['tmp_name'], $target_name);
|
||||
}
|
||||
|
||||
//compile
|
||||
$command = '"' . $full_path_to_gulp . '" --cwd "' . str_replace('/', '\\', $target_path) . '" --min 2>&1';
|
||||
$output = shell_exec($command);
|
||||
|
||||
$zip = new ZipArchive();
|
||||
$zippath = $target_path . '/TGUI.zip';
|
||||
if($zip->open($zippath, ZipArchive::CREATE) == TRUE){
|
||||
$zip->addFile($target_path . '/assets/tgui.css', 'tgui.css');
|
||||
$zip->addFile($target_path . '/assets/tgui.js', 'tgui.js');
|
||||
$zip->addFromString('gulp_output.txt', $output);
|
||||
$zip->close();
|
||||
download_file($zippath);
|
||||
}
|
||||
else
|
||||
$error = 'Unable to create output zipfile!';
|
||||
exec('rmdir "' . str_replace('/', '\\', $target_node) . '"'); //improtant
|
||||
rrmdir($target_path);
|
||||
}
|
||||
else if(!$updated_git)
|
||||
throw new RuntimeException('No valid files uploaded!');
|
||||
}
|
||||
}
|
||||
catch(Exception $e){
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>TGUI .ract Compiler</title>
|
||||
</head>
|
||||
<body>
|
||||
<?php if(isset($error)) echo '<h5><font color="red">An error occured:</font> ' . $error . '</h5><br><br>'; ?>
|
||||
<h1>Upload up to <?php echo $max_number_of_uploads; ?> .ract files<h2>
|
||||
<h4>Based off revision: <?php echo isset($commit_url) ? '<a href="' . $commit_url . '">' . $revision . '</a>' : $revision; ?>
|
||||
<form id='file_form' action = '' method = 'post' enctype='multipart/form-data'>
|
||||
<input type='checkbox' name='pull'>Update to latest revision (don't use this unless you have to)<br>
|
||||
<div id='files_field'>
|
||||
<input name='f1' type='file'><br>
|
||||
</div>
|
||||
<button id='add_more'>Add Another File</button>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
</body>
|
||||
<script type='text/javascript' src='https://code.jquery.com/jquery-3.2.1.min.js'></script>
|
||||
<script type='text/javascript'>
|
||||
$(function(){
|
||||
var next_id = 2;
|
||||
$("#add_more").click(function(e){
|
||||
$("#files_field").append("<input name='f" + next_id + "' type='file'><br>");
|
||||
++next_id;
|
||||
if(next_id > <?php echo $max_number_of_uploads; ?>)
|
||||
$("#add_more").remove();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||