mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-03 21:42:06 +00:00
(code bounty) The tram is now unstoppably powerful. it cannot be stopped, it cannot be slowed, it cannot be reasoned with. YOU HAVE NO IDEA HOW READY YOU ARE (#66657)
ever see the tram take 10 milliseconds per movement to move 2100 objects? now you have https://user-images.githubusercontent.com/15794172/166198184-8bab93bd-f584-4269-9ed1-6aee746f8f3c.mp4 About The Pull Request fixes #66887 done for the code bounty posted by @MMMiracles to optimize the tram so that it can be sped up. the tram is now twice as fast, firing every tick instead of every 2 ticks. and is now around 10x cheaper to move. also adds support for multiz trams, as in trams that span multiple z levels. the tram on master takes around 10-15 milliseconds per movement with nothing on it other than its starting contents. why is this? because the tram is the canary in the coal mines when it comes to movement code, which is normally expensive as fuck. the tram does way more work than it needs to, and even finds new ways to slow the game down. I'll walk you through a few of the dumber things the tram currently does and how i fixed them. the tram, at absolute minimum, has to move 55 separate industrial_lift platforms once per movement. this means that the tram has to unregister its entered/exited signals 55 times when "the tram" as a singular object is only entering 5 new turfs and exiting 5 old turfs every movement, this means that each of the 55 platforms calculates their own destination turfs and checks their contents every movement. The biggest single optimization in this pr was that I made the tram into a single 5x11 multitile object and made it only do entering/exiting checks on the 5 new and 5 old turfs in each movement. way too many of the default tram contents are expensive to move for something that has to move a lot. fun fact, did you know that the walls on the tram have opacity? do you know what opacity does for movables? it makes them recalculate static lighting every time they move. did you know that the tram, this entire time, was taking JUST as much time spamming SSlighting updates as it was spending time in SStramprocess? well it is! now it doesnt do that, the walls are transparent. also, every window and every grille on the tram had the atmos_sensitive element applied to them which then added connect_loc to them, causing them to update signals every movement. that is also dumb and i got rid of that with snowflake overrides. Now we must take care to not add things that sneakily register to Moved() or the moved signal to the roundstart tram, because that is dumb, and the relative utility of simulating objects that should normally shatter due to heat and conduct heat from the atmosphere is far less than the cost of moving them, for this one object. all tram contents physically Entered() and Exited() their destination and old turfs every movement, even though because they are on a tram they literally do not interact with the turf, the tram does. also, any objects that use connect_loc or connect_loc behalf that are on the same point on the tram also interact with each other because of this. now all contents of the tram act as if theyre being abstract_move()'d to their destination so that (almost) nothing thats in the destination turf or the exit turf can react to the event of "something laying on the tram is moving over you". the rare things that DO need to know what is physically entering or exiting their turf regardless of whether theyre interacting with the ground can register to the abstract entered and exited signals which are now always sent. many of the things hooked into Moved(), whether it be overrides of Moved() itself, or handlers for the moved signal, add up to a LOT of processing time. especially for humans. now ive gotten rid of a lot of it, mostly for the tram but also for normal movement. i made footsteps (a significant portion of human movement cost) not do any work if the human themselves didnt do the movement. i optimized has_gravity() a fair amount, and then realized that since everything on the tram isnt changing momentum, i didnt actually need to check gravity for the purposes of drifting (newtonian_move() was taking a significant portion of the cost of movement at some points along the development process). so now it simply doesnt call newtonian_move() for movements that dont represent a change in momentum (by default all movements do). also i put effort into 1. better organizing tram/lift code so that most of it is inside of a dedicated modules folder instead of scattered around 5 generic folders and 2. moved a lot of behavior from lift platforms themselves into their lift_master_datum since ideally the platforms would just handle moving themselves, while any behavior involving the entire lift such as "move to destination" and "blow up" would be handled by the lift_master_datum. also https://user-images.githubusercontent.com/15794172/166220129-ff2ea344-442f-4e3e-94f0-ec58ab438563.mp4 multiz tram (this just adds the capability to map it like this, no tram does this) Actual Performance Differences to benchmark this, i added a world.Profile(PROFILER_START) and world.Profile(PROFILER_START) to the tram moving, so that it generates a profiler output of all tram movement without any unrelated procs being recorded (except for world.Profile() overhead). this made it a lot easier to quantify what was slowing down both the tram and movement in general. and i did 3 types of tests on both master and my branch. also i should note that i sped up the "master" tram test to move once per tick as well, simply because the normal movement speed seems unbearably slow now. so all recorded videos are done at twice the speed of the real tram on master. this doesnt affect the main thing i was trying to measure: cost for each movement. the first test was the base tram, containing only my player mob and the movables starting on the tram roundstart. on master, this takes around 13 milliseconds or so on my computer (which is pretty close to what it takes on the servers), on this branch, it takes between 0.9-1.3 milliseconds. ALSO in these benchmarks youll see that tram/proc/travel() will vary significantly between the master and optimized branches. this is 100% because there are 55 times more platforms moving on master compared to the master branch, and thus 55x more calls to this proc. every test was recorded with the exact same amount of distance moved here are the master and optimized benchmark text files: master master base tram.txt https://user-images.githubusercontent.com/15794172/166210149-f118683d-6f6d-4dfb-b9e4-14f17b26aad8.mp4 also this shows the increased SSlighting usage resulting from the tram on master spamming updates, which doesnt happen on the optimized branch optimized optimization base tram.txt https://user-images.githubusercontent.com/15794172/166206280-cd849aaa-ed3b-4e2f-b741-b8a5726091a9.mp4 the second test is meant to benchmark the best case scaling cost of moving objects, where nothing extra is registered to movement besides the bare minimum stuff on the /atom/movable level. Each of the open tiles of the tram had 1 bluespace rped filled with parts dumped onto it, to the point that the tram in total was moving 2100 objects. the vast majority of these objects did nothing special in movement so they serve as a good base case. only slightly off due to the rped's registering to movement. on master, this test takes over 100 milliseconds per movement master 2000 obj's.txt https://user-images.githubusercontent.com/15794172/166210560-f4de620d-7dc6-4dbd-8b61-4a48149af707.mp4 when optimized, about 10 milliseconds per movement https://user-images.githubusercontent.com/15794172/166208654-bc10086b-bbfc-49fa-9987-d7558109cc1d.mp4 optimization 2000 obj's.txt the third test is 300 humans spawned onto the tram, meant to test all the shit added on to movement cost for humans/carbons. in retrospect this test is actually way too biased in favor of my optimizations since the humans are all in only 3 tiles, so all 100 humans on a tile are reacting to the other 99 humans movements, which wouldnt be as bad if they were distributed across 20 tiles like in the second test. so dont read into this one too hard. on master, this test takes 200 milliseconds master 300 catgirls.txt when optimized, this takes about 13-14 milliseconds. optimization 300 catgirls on ram ranch.txt Why It's Good For The Game the tram is literally 10x cheaper to move. and the code is better organized. currently on master the tram is as fast as running speed, meaning it has no real relative utility compared to just running the tracks (except for the added safety of not having to risk being ran over by the tram). now the tram of which we have an entire map based around can be used to its full potential. also, has some fixes to things on the tram reacting to movement. for example on master if you are standing on a tram tile that contains a banana and the TRAM moves, you will slip if the banana was in that spot before you (not if you were there first however). this is because the banana has no concept of relative movement, you and it are in the same reference frame but the banana, which failed highschool physics, believes you to have moved onto it and thus subjected you to the humiliation of an unjust slipping. now since tram contents that dont register to abstract entered/exited cannot know about other tram contents on the same tile during a movement, this cannot happen. also, you no longer make footstep sounds when the tram moves you over a floor TODO mainly opened it now so i can create a stopping point and attend to my other now staling prs, we're at a state of functionality far enough to start testmerging it anyways. add a better way for admins to be notified of the tram overloading the server if someone purposefully stuffs it with as much shit as they can, and for admins to clear said shit. automatically slow down the tram if SStramprocess takes over like, 10 milliseconds complete. the tram still cant really check tick and yield without introducing logic holes, so making sure it doesnt take half of the tick every tick is important go over my code to catch dumb shit i forgot about, there always is for these kinds of refactors because im very messy remove the area based forced_gravity optimization its not worth figuring out why it doesnt work fix the inevitable merge conflict with master lol create an icon for the tram_tunnel area type i made so that objects on the tram dont have to enter and exit areas twice in a cross-station traversal add an easy way to vv tram lethality for mobs/things being hit by it. its an easy target in another thing i already wanted to do: a reinforced concept of shared variables from any particular tram platform and the entire tram itself. admins should be able to slow down the tram by vv'ing one platform and have it apply to the entire tram for example. Changelog cl balance: the tram is now twice as fast, pray it doesnt get any faster (it cant without raising world fps) performance: the tram is now about 10 times cheaper to move for the server add: mappers can now create trams with multiple z levels code: industrial_lift's now have more of their behavior pertaining to "the entire lift" being handled by their lift_master_datum as opposed to belonging to a random platform on the lift. /cl
This commit is contained in:
@@ -10,14 +10,6 @@
|
||||
},
|
||||
/turf/open/floor/mineral/plastitanium,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"av" = (
|
||||
/obj/effect/mapping_helpers/airlock/locked,
|
||||
/obj/machinery/door/airlock/vault{
|
||||
name = "Secured Door"
|
||||
},
|
||||
/obj/effect/mapping_helpers/airlock/access/all/away/generic3,
|
||||
/turf/open/floor/mineral/plastitanium,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility/secretroom)
|
||||
"az" = (
|
||||
/turf/open/floor/carpet/black,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
@@ -238,6 +230,33 @@
|
||||
/obj/structure/frame/computer,
|
||||
/turf/open/floor/plating/airless,
|
||||
/area/ruin/unpowered/no_grav)
|
||||
"gm" = (
|
||||
/obj/structure/fluff/tram_rail{
|
||||
dir = 1
|
||||
},
|
||||
/obj/structure/window/reinforced/survival_pod{
|
||||
dir = 4
|
||||
},
|
||||
/obj/structure/window/reinforced/survival_pod{
|
||||
dir = 1
|
||||
},
|
||||
/obj/structure/shuttle/engine/propulsion/in_wall/tram{
|
||||
dir = 4;
|
||||
pixel_x = 32
|
||||
},
|
||||
/obj/structure/industrial_lift/tram{
|
||||
icon_state = "titanium_purple"
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"gv" = (
|
||||
/obj/structure/fluff/tram_rail,
|
||||
/obj/machinery/door/window/survival_pod,
|
||||
/obj/structure/industrial_lift/tram{
|
||||
icon_state = "titanium_white"
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"gx" = (
|
||||
/obj/machinery/mineral/processing_unit{
|
||||
dir = 1
|
||||
@@ -320,19 +339,6 @@
|
||||
},
|
||||
/turf/open/floor/grass/fairy,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"hN" = (
|
||||
/obj/structure/fluff/tram_rail{
|
||||
dir = 1
|
||||
},
|
||||
/obj/structure/industrial_lift/tram{
|
||||
icon_state = "titanium_white";
|
||||
initial_id = "middle_part_hilbert"
|
||||
},
|
||||
/obj/machinery/door/window/survival_pod{
|
||||
dir = 1
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"hQ" = (
|
||||
/obj/machinery/door/puzzle/light{
|
||||
puzzle_id = "hilbert"
|
||||
@@ -384,6 +390,22 @@
|
||||
},
|
||||
/turf/open/floor/mineral/titanium/tiled/yellow,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"iS" = (
|
||||
/obj/effect/landmark/tram/middle_part/hilbert,
|
||||
/obj/machinery/light/floor{
|
||||
brightness = 2;
|
||||
bulb_colour = "#deefff";
|
||||
bulb_power = 0.6
|
||||
},
|
||||
/obj/machinery/computer/tram_controls{
|
||||
dir = 8;
|
||||
specific_lift_id = "tram_hilbert"
|
||||
},
|
||||
/obj/structure/industrial_lift/tram{
|
||||
icon_state = "titanium_white"
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"iY" = (
|
||||
/obj/machinery/mineral/processing_unit{
|
||||
dir = 1
|
||||
@@ -466,16 +488,6 @@
|
||||
/obj/item/plant_analyzer,
|
||||
/turf/open/floor/glass,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"kK" = (
|
||||
/obj/structure/industrial_lift/tram{
|
||||
icon_state = "titanium_white";
|
||||
initial_id = "middle_part_hilbert"
|
||||
},
|
||||
/obj/structure/window/reinforced/survival_pod{
|
||||
dir = 4
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"kO" = (
|
||||
/obj/effect/turf_decal/siding/thinplating_new/light,
|
||||
/turf/open/floor/mineral/titanium/tiled/white,
|
||||
@@ -662,24 +674,6 @@
|
||||
},
|
||||
/turf/open/floor/mineral/titanium/tiled/yellow,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"pE" = (
|
||||
/obj/effect/landmark/tram/middle_part/hilbert,
|
||||
/obj/structure/industrial_lift/tram/central{
|
||||
icon_state = "titanium_white";
|
||||
initial_id = "middle_part_hilbert";
|
||||
tram_id = "tram_hilbert"
|
||||
},
|
||||
/obj/machinery/computer/tram_controls{
|
||||
dir = 8;
|
||||
tram_id = "tram_hilbert"
|
||||
},
|
||||
/obj/machinery/light/floor{
|
||||
brightness = 2;
|
||||
bulb_colour = "#deefff";
|
||||
bulb_power = 0.6
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"pI" = (
|
||||
/obj/structure/chair/stool/bar/directional/south,
|
||||
/obj/effect/decal/cleanable/dirt/dust,
|
||||
@@ -940,6 +934,14 @@
|
||||
},
|
||||
/turf/open/floor/grass/fairy,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"vT" = (
|
||||
/obj/effect/mapping_helpers/airlock/locked,
|
||||
/obj/machinery/door/airlock/vault{
|
||||
name = "Secured Door"
|
||||
},
|
||||
/obj/effect/mapping_helpers/airlock/access/all/away/generic3,
|
||||
/turf/open/floor/mineral/plastitanium,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility/secretroom)
|
||||
"vZ" = (
|
||||
/obj/effect/turf_decal/siding/wood{
|
||||
dir = 4
|
||||
@@ -1008,6 +1010,21 @@
|
||||
},
|
||||
/turf/open/floor/mineral/titanium/tiled/yellow,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"xI" = (
|
||||
/obj/structure/fluff/tram_rail,
|
||||
/obj/structure/window/reinforced/survival_pod{
|
||||
dir = 4
|
||||
},
|
||||
/obj/structure/window/reinforced/survival_pod,
|
||||
/obj/structure/shuttle/engine/propulsion/in_wall/tram{
|
||||
dir = 4;
|
||||
pixel_x = 32
|
||||
},
|
||||
/obj/structure/industrial_lift/tram{
|
||||
icon_state = "titanium_purple"
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"xO" = (
|
||||
/obj/structure/shuttle/engine/propulsion/burst{
|
||||
anchored = 0;
|
||||
@@ -1524,6 +1541,16 @@
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"HN" = (
|
||||
/obj/structure/window/reinforced/survival_pod{
|
||||
dir = 4
|
||||
},
|
||||
/obj/structure/industrial_lift/tram{
|
||||
icon_state = "titanium_white"
|
||||
},
|
||||
/obj/effect/landmark/lift_id/hilbert,
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"Ic" = (
|
||||
/turf/open/floor/iron/stairs{
|
||||
dir = 4
|
||||
@@ -1604,6 +1631,21 @@
|
||||
},
|
||||
/turf/open/floor/mineral/titanium/tiled/white,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"Jd" = (
|
||||
/obj/structure/fluff/tram_rail,
|
||||
/obj/structure/window/reinforced/survival_pod{
|
||||
dir = 8
|
||||
},
|
||||
/obj/structure/window/reinforced/survival_pod,
|
||||
/obj/structure/shuttle/engine/propulsion/in_wall/tram{
|
||||
dir = 8;
|
||||
pixel_x = -32
|
||||
},
|
||||
/obj/structure/industrial_lift/tram{
|
||||
icon_state = "titanium_purple"
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"Jh" = (
|
||||
/obj/structure/fluff/tram_rail/anchor,
|
||||
/turf/open/floor/engine,
|
||||
@@ -1651,64 +1693,13 @@
|
||||
/obj/structure/barricade/wooden,
|
||||
/turf/open/floor/mineral/titanium/tiled/purple,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"Kv" = (
|
||||
/obj/structure/industrial_lift/tram{
|
||||
icon_state = "titanium_white";
|
||||
initial_id = "middle_part_hilbert"
|
||||
},
|
||||
/obj/structure/window/reinforced/survival_pod{
|
||||
dir = 8
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"KC" = (
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"KF" = (
|
||||
/obj/structure/fluff/tram_rail,
|
||||
/obj/structure/industrial_lift/tram{
|
||||
icon_state = "titanium_purple";
|
||||
initial_id = "middle_part_hilbert"
|
||||
},
|
||||
/obj/structure/shuttle/engine/propulsion/in_wall{
|
||||
dir = 8;
|
||||
pixel_x = -32
|
||||
},
|
||||
/obj/structure/window/reinforced/survival_pod{
|
||||
dir = 8
|
||||
},
|
||||
/obj/structure/window/reinforced/survival_pod,
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"KI" = (
|
||||
/obj/structure/fluff/tram_rail,
|
||||
/obj/structure/industrial_lift/tram{
|
||||
icon_state = "titanium_white";
|
||||
initial_id = "middle_part_hilbert"
|
||||
},
|
||||
/obj/machinery/door/window/survival_pod,
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"KJ" = (
|
||||
/obj/structure/fans/tiny,
|
||||
/turf/open/floor/mineral/plastitanium,
|
||||
/area/ruin/unpowered/no_grav)
|
||||
"KK" = (
|
||||
/obj/structure/fluff/tram_rail,
|
||||
/obj/structure/industrial_lift/tram{
|
||||
icon_state = "titanium_purple";
|
||||
initial_id = "middle_part_hilbert"
|
||||
},
|
||||
/obj/structure/shuttle/engine/propulsion/in_wall{
|
||||
dir = 4;
|
||||
pixel_x = 32
|
||||
},
|
||||
/obj/structure/window/reinforced/survival_pod{
|
||||
dir = 4
|
||||
},
|
||||
/obj/structure/window/reinforced/survival_pod,
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"KL" = (
|
||||
/obj/structure/chair/stool/bar/directional/north,
|
||||
/obj/effect/decal/cleanable/dirt/dust,
|
||||
@@ -1783,26 +1774,6 @@
|
||||
/obj/structure/chair/stool/bar/directional/west,
|
||||
/turf/open/floor/wood,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"MR" = (
|
||||
/obj/structure/fluff/tram_rail{
|
||||
dir = 1
|
||||
},
|
||||
/obj/structure/industrial_lift/tram{
|
||||
icon_state = "titanium_purple";
|
||||
initial_id = "middle_part_hilbert"
|
||||
},
|
||||
/obj/structure/shuttle/engine/propulsion/in_wall{
|
||||
dir = 8;
|
||||
pixel_x = -32
|
||||
},
|
||||
/obj/structure/window/reinforced/survival_pod{
|
||||
dir = 8
|
||||
},
|
||||
/obj/structure/window/reinforced/survival_pod{
|
||||
dir = 1
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"MX" = (
|
||||
/turf/closed/wall/mineral/titanium,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
@@ -1885,6 +1856,18 @@
|
||||
},
|
||||
/turf/open/floor/iron/grimy,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"Pw" = (
|
||||
/obj/structure/fluff/tram_rail{
|
||||
dir = 1
|
||||
},
|
||||
/obj/machinery/door/window/survival_pod{
|
||||
dir = 1
|
||||
},
|
||||
/obj/structure/industrial_lift/tram{
|
||||
icon_state = "titanium_white"
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"Px" = (
|
||||
/obj/machinery/door/window/left/directional/south,
|
||||
/turf/open/floor/iron/grimy,
|
||||
@@ -1938,6 +1921,15 @@
|
||||
},
|
||||
/turf/open/floor/wood,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"QS" = (
|
||||
/obj/structure/window/reinforced/survival_pod{
|
||||
dir = 8
|
||||
},
|
||||
/obj/structure/industrial_lift/tram{
|
||||
icon_state = "titanium_white"
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"QU" = (
|
||||
/obj/structure/railing/corner{
|
||||
dir = 1
|
||||
@@ -1990,26 +1982,6 @@
|
||||
},
|
||||
/turf/open/floor/mineral/titanium/tiled/white,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"Rz" = (
|
||||
/obj/structure/fluff/tram_rail{
|
||||
dir = 1
|
||||
},
|
||||
/obj/structure/industrial_lift/tram{
|
||||
icon_state = "titanium_purple";
|
||||
initial_id = "middle_part_hilbert"
|
||||
},
|
||||
/obj/structure/shuttle/engine/propulsion/in_wall{
|
||||
dir = 4;
|
||||
pixel_x = 32
|
||||
},
|
||||
/obj/structure/window/reinforced/survival_pod{
|
||||
dir = 4
|
||||
},
|
||||
/obj/structure/window/reinforced/survival_pod{
|
||||
dir = 1
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"RE" = (
|
||||
/obj/item/stack/cable_coil/cut,
|
||||
/turf/open/floor/plating,
|
||||
@@ -2357,6 +2329,25 @@
|
||||
/obj/structure/table/reinforced/rglass,
|
||||
/turf/open/floor/wood,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"Yt" = (
|
||||
/obj/structure/fluff/tram_rail{
|
||||
dir = 1
|
||||
},
|
||||
/obj/structure/window/reinforced/survival_pod{
|
||||
dir = 8
|
||||
},
|
||||
/obj/structure/window/reinforced/survival_pod{
|
||||
dir = 1
|
||||
},
|
||||
/obj/structure/shuttle/engine/propulsion/in_wall/tram{
|
||||
dir = 8;
|
||||
pixel_x = -32
|
||||
},
|
||||
/obj/structure/industrial_lift/tram{
|
||||
icon_state = "titanium_purple"
|
||||
},
|
||||
/turf/open/floor/engine,
|
||||
/area/ruin/space/has_grav/powered/hilbertresearchfacility)
|
||||
"Yv" = (
|
||||
/obj/item/kirbyplants/random,
|
||||
/obj/effect/turf_decal/siding/wood{
|
||||
@@ -3797,9 +3788,9 @@ wo
|
||||
Bo
|
||||
zu
|
||||
Xi
|
||||
MR
|
||||
Kv
|
||||
KF
|
||||
Yt
|
||||
QS
|
||||
Jd
|
||||
YG
|
||||
Sa
|
||||
Bo
|
||||
@@ -3836,9 +3827,9 @@ YL
|
||||
Fg
|
||||
zC
|
||||
Xi
|
||||
hN
|
||||
pE
|
||||
KI
|
||||
Pw
|
||||
iS
|
||||
gv
|
||||
YG
|
||||
WY
|
||||
UF
|
||||
@@ -3875,9 +3866,9 @@ La
|
||||
Bo
|
||||
Ax
|
||||
Xi
|
||||
Rz
|
||||
kK
|
||||
KK
|
||||
gm
|
||||
HN
|
||||
xI
|
||||
YG
|
||||
Sp
|
||||
Bo
|
||||
@@ -5214,7 +5205,7 @@ La
|
||||
La
|
||||
ru
|
||||
La
|
||||
av
|
||||
vT
|
||||
sj
|
||||
xc
|
||||
sj
|
||||
|
||||
@@ -3327,10 +3327,11 @@
|
||||
/turf/open/floor/plating/icemoon,
|
||||
/area/station/engineering/atmos)
|
||||
"bed" = (
|
||||
/obj/structure/industrial_lift{
|
||||
id = "publicElevator"
|
||||
},
|
||||
/obj/machinery/light/floor,
|
||||
/obj/structure/industrial_lift,
|
||||
/obj/effect/landmark/lift_id{
|
||||
specific_lift_id = "publicElevator"
|
||||
},
|
||||
/turf/open/openspace,
|
||||
/area/station/commons/storage/mining)
|
||||
"ben" = (
|
||||
@@ -26836,8 +26837,8 @@
|
||||
},
|
||||
/obj/effect/turf_decal/tile/neutral/fourcorners,
|
||||
/obj/effect/turf_decal/loading_area{
|
||||
dir = 1;
|
||||
color = "#439C1E"
|
||||
color = "#439C1E";
|
||||
dir = 1
|
||||
},
|
||||
/obj/effect/turf_decal/trimline/dark_red/warning{
|
||||
dir = 1
|
||||
@@ -33112,12 +33113,12 @@
|
||||
"kpG" = (
|
||||
/obj/structure/table/reinforced,
|
||||
/obj/item/hfr_box/body/waste_output{
|
||||
pixel_y = -1;
|
||||
pixel_x = 5
|
||||
pixel_x = 5;
|
||||
pixel_y = -1
|
||||
},
|
||||
/obj/item/hfr_box/body/moderator_input{
|
||||
pixel_y = 8;
|
||||
pixel_x = -5
|
||||
pixel_x = -5;
|
||||
pixel_y = 8
|
||||
},
|
||||
/obj/item/radio/intercom/directional/west,
|
||||
/turf/open/floor/iron/dark,
|
||||
@@ -48815,8 +48816,8 @@
|
||||
/obj/structure/table/reinforced,
|
||||
/obj/item/hfr_box/body/fuel_input,
|
||||
/obj/item/hfr_box/body/interface{
|
||||
pixel_y = 9;
|
||||
pixel_x = 6
|
||||
pixel_x = 6;
|
||||
pixel_y = 9
|
||||
},
|
||||
/turf/open/floor/iron/dark,
|
||||
/area/station/engineering/atmos/hfr_room)
|
||||
@@ -52505,8 +52506,8 @@
|
||||
"qqh" = (
|
||||
/obj/effect/turf_decal/tile/neutral/fourcorners,
|
||||
/obj/machinery/atmospherics/components/unary/portables_connector/visible{
|
||||
name = "Exfiltrate Port";
|
||||
dir = 8
|
||||
dir = 8;
|
||||
name = "Exfiltrate Port"
|
||||
},
|
||||
/turf/open/floor/iron/dark,
|
||||
/area/station/engineering/atmos/mix)
|
||||
@@ -60802,9 +60803,9 @@
|
||||
/area/station/maintenance/port/aft)
|
||||
"sVE" = (
|
||||
/obj/machinery/door/poddoor/shutters/preopen{
|
||||
dir = 8;
|
||||
id = "Atmospherics HFR Shutters";
|
||||
name = "Atmospherics HFR Shutters";
|
||||
dir = 8
|
||||
name = "Atmospherics HFR Shutters"
|
||||
},
|
||||
/obj/effect/turf_decal/trimline/yellow/warning{
|
||||
dir = 4
|
||||
@@ -71820,8 +71821,8 @@
|
||||
"wrE" = (
|
||||
/obj/structure/table/reinforced,
|
||||
/obj/item/stack/sheet/glass/fifty{
|
||||
pixel_y = 0;
|
||||
pixel_x = 2
|
||||
pixel_x = 2;
|
||||
pixel_y = 0
|
||||
},
|
||||
/obj/item/stack/sheet/iron/fifty,
|
||||
/obj/item/stack/cable_coil{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -45,6 +45,8 @@
|
||||
#define COMSIG_ATOM_SMOOTHED_ICON "atom_smoothed_icon"
|
||||
///from base of atom/Entered(): (atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
||||
#define COMSIG_ATOM_ENTERED "atom_entered"
|
||||
///from base of atom/movable/Moved(): (atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
||||
#define COMSIG_ATOM_ABSTRACT_ENTERED "atom_abstract_entered"
|
||||
/// Sent from the atom that just Entered src. From base of atom/Entered(): (/atom/destination, atom/old_loc, list/atom/old_locs)
|
||||
#define COMSIG_ATOM_ENTERING "atom_entering"
|
||||
///from base of atom/Exit(): (/atom/movable/leaving, direction)
|
||||
@@ -52,6 +54,8 @@
|
||||
#define COMPONENT_ATOM_BLOCK_EXIT (1<<0)
|
||||
///from base of atom/Exited(): (atom/movable/gone, direction)
|
||||
#define COMSIG_ATOM_EXITED "atom_exited"
|
||||
///from base of atom/movable/Moved(): (atom/movable/gone, direction)
|
||||
#define COMSIG_ATOM_ABSTRACT_EXITED "atom_abstract_exited"
|
||||
///from base of atom/Bumped(): (/atom/movable)
|
||||
#define COMSIG_ATOM_BUMPED "atom_bumped"
|
||||
///from base of atom/handle_atom_del(): (atom/deleted)
|
||||
|
||||
@@ -59,6 +59,9 @@
|
||||
/// from /obj/machinery/atmospherics/components/unary/cryo_cell/set_on(bool): (on)
|
||||
#define COMSIG_CRYO_SET_ON "cryo_set_on"
|
||||
|
||||
/// from /obj/proc/make_unfrozen()
|
||||
#define COMSIG_OBJ_UNFREEZE "obj_unfreeze"
|
||||
|
||||
// /obj/machinery/atmospherics/components/binary/valve signals
|
||||
|
||||
/// from /obj/machinery/atmospherics/components/binary/valve/toggle(): (on)
|
||||
|
||||
@@ -5,3 +5,5 @@
|
||||
///the client mobs channel of the important_recursive_contents list, everything in here will be a mob with an attached client
|
||||
///this is given to both a clients mob, and a clients eye, both point to the clients mob
|
||||
#define RECURSIVE_CONTENTS_CLIENT_MOBS "recursive_contents_client_mobs"
|
||||
///the parent of storage components currently shown to some client mob get this. gets removed when nothing is viewing the parent
|
||||
#define RECURSIVE_CONTENTS_ACTIVE_STORAGE "recursive_contents_active_storage"
|
||||
|
||||
20
code/__DEFINES/industrial_lift.dm
Normal file
20
code/__DEFINES/industrial_lift.dm
Normal file
@@ -0,0 +1,20 @@
|
||||
//Booleans in arguments are confusing, so I made them defines.
|
||||
///the lift's controls are currently locked from user input
|
||||
#define LIFT_PLATFORM_LOCKED 1
|
||||
///the lift's controls are currently unlocked so user's can direct it
|
||||
#define LIFT_PLATFORM_UNLOCKED 0
|
||||
|
||||
//lift_id's
|
||||
///basic lift_id, goes up and down
|
||||
#define BASIC_LIFT_ID "base"
|
||||
///tram lift_id, goes left and right or north and south. maybe one day be able to turn and go up/down as well
|
||||
#define TRAM_LIFT_ID "tram"
|
||||
///debug lift_id
|
||||
#define DEBUG_LIFT_ID "debug"
|
||||
|
||||
|
||||
//specific_lift_id's
|
||||
///the specific_lift_id of the main station tram landmark for tramstation that spawns roundstart.
|
||||
#define MAIN_STATION_TRAM "main station tram"
|
||||
///the specific_lift_id of the tram on the hilbert research station
|
||||
#define HILBERT_TRAM "tram_hilbert"
|
||||
@@ -9,7 +9,6 @@
|
||||
#define ON_BLUEPRINTS (1<<5) //Are we visible on the station blueprints at roundstart?
|
||||
#define UNIQUE_RENAME (1<<6) // can you customize the description/name of the thing?
|
||||
#define USES_TGUI (1<<7) //put on things that use tgui on ui_interact instead of custom/old UI.
|
||||
#define FROZEN (1<<8)
|
||||
#define BLOCK_Z_OUT_DOWN (1<<9) // Should this object block z falling from loc?
|
||||
#define BLOCK_Z_OUT_UP (1<<10) // Should this object block z uprise from loc?
|
||||
#define BLOCK_Z_IN_DOWN (1<<11) // Should this object block z falling from above?
|
||||
|
||||
@@ -488,6 +488,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
|
||||
#define TRAIT_AREA_SENSITIVE "area-sensitive"
|
||||
///every hearing sensitive atom has this trait
|
||||
#define TRAIT_HEARING_SENSITIVE "hearing_sensitive"
|
||||
///every object that is currently the active storage of some client mob has this trait
|
||||
#define TRAIT_ACTIVE_STORAGE "active_storage"
|
||||
|
||||
/// Climbable trait, given and taken by the climbable element when added or removed. Exists to be easily checked via HAS_TRAIT().
|
||||
#define TRAIT_CLIMBABLE "trait_climbable"
|
||||
@@ -895,5 +897,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
|
||||
/// Ignores body_parts_covered during the add_fingerprint() proc. Works both on the person and the item in the glove slot.
|
||||
#define TRAIT_FINGERPRINT_PASSTHROUGH "fingerprint_passthrough"
|
||||
|
||||
/// this object has been frozen
|
||||
#define TRAIT_FROZEN "frozen"
|
||||
|
||||
/// Currently fishing
|
||||
#define TRAIT_GONE_FISHING "fishing"
|
||||
|
||||
@@ -1086,23 +1086,9 @@ GLOBAL_LIST_EMPTY(friendly_animal_types)
|
||||
|
||||
GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0,0,0)))
|
||||
|
||||
/obj/proc/make_frozen_visual()
|
||||
// Used to make the frozen item visuals for Freon.
|
||||
if(resistance_flags & FREEZE_PROOF)
|
||||
return
|
||||
if(!(obj_flags & FROZEN))
|
||||
name = "frozen [name]"
|
||||
add_atom_colour(GLOB.freon_color_matrix, TEMPORARY_COLOUR_PRIORITY)
|
||||
alpha -= 25
|
||||
obj_flags |= FROZEN
|
||||
|
||||
//Assumes already frozed
|
||||
/obj/proc/make_unfrozen()
|
||||
if(obj_flags & FROZEN)
|
||||
name = replacetext(name, "frozen ", "")
|
||||
remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, GLOB.freon_color_matrix)
|
||||
alpha += 25
|
||||
obj_flags &= ~FROZEN
|
||||
SEND_SIGNAL(src, COMSIG_OBJ_UNFREEZE)
|
||||
|
||||
/// generates a filename for a given asset.
|
||||
/// like generate_asset_name(), except returns the rsc reference and the rsc file hash as well as the asset name (sans extension)
|
||||
|
||||
@@ -274,7 +274,6 @@ DEFINE_BITFIELD(obj_flags, list(
|
||||
"CAN_BE_HIT" = CAN_BE_HIT,
|
||||
"DANGEROUS_POSSESSION" = DANGEROUS_POSSESSION,
|
||||
"EMAGGED" = EMAGGED,
|
||||
"FROZEN" = FROZEN,
|
||||
"IN_USE" = IN_USE,
|
||||
"NO_BUILD" = NO_BUILD,
|
||||
"ON_BLUEPRINTS" = ON_BLUEPRINTS,
|
||||
|
||||
@@ -213,11 +213,13 @@
|
||||
for(var/atom/target in checking) // will filter out nulls
|
||||
if(closed[target] || isarea(target)) // avoid infinity situations
|
||||
continue
|
||||
closed[target] = TRUE
|
||||
|
||||
if(isturf(target) || isturf(target.loc) || (target in direct_access) || (ismovable(target) && target.flags_1 & IS_ONTOP_1)) //Directly accessible atoms
|
||||
if(Adjacent(target) || (tool && CheckToolReach(src, target, tool.reach))) //Adjacent or reaching attacks
|
||||
return TRUE
|
||||
|
||||
closed[target] = TRUE
|
||||
|
||||
if (!target.loc)
|
||||
continue
|
||||
|
||||
|
||||
@@ -528,18 +528,18 @@ SUBSYSTEM_DEF(explosions)
|
||||
var/base_shake_amount = sqrt(near_distance / (distance + 1))
|
||||
|
||||
if(distance <= round(near_distance + world.view - 2, 1)) // If you are close enough to see the effects of the explosion first-hand (ignoring walls)
|
||||
listener.playsound_local(epicenter, null, 100, TRUE, frequency, S = near_sound)
|
||||
listener.playsound_local(epicenter, null, 100, TRUE, frequency, sound_to_use = near_sound)
|
||||
if(base_shake_amount > 0)
|
||||
shake_camera(listener, NEAR_SHAKE_DURATION, clamp(base_shake_amount, 0, NEAR_SHAKE_CAP))
|
||||
|
||||
else if(distance < far_distance) // You can hear a far explosion if you are outside the blast radius. Small explosions shouldn't be heard throughout the station.
|
||||
var/far_volume = clamp(far_distance / 2, FAR_LOWER, FAR_UPPER)
|
||||
if(creaking)
|
||||
listener.playsound_local(epicenter, null, far_volume, TRUE, frequency, S = creaking_sound, distance_multiplier = 0)
|
||||
listener.playsound_local(epicenter, null, far_volume, TRUE, frequency, sound_to_use = creaking_sound, distance_multiplier = 0)
|
||||
else if(prob(FAR_SOUND_PROB)) // Sound variety during meteor storm/tesloose/other bad event
|
||||
listener.playsound_local(epicenter, null, far_volume, TRUE, frequency, S = far_sound, distance_multiplier = 0)
|
||||
listener.playsound_local(epicenter, null, far_volume, TRUE, frequency, sound_to_use = far_sound, distance_multiplier = 0)
|
||||
else
|
||||
listener.playsound_local(epicenter, null, far_volume, TRUE, frequency, S = echo_sound, distance_multiplier = 0)
|
||||
listener.playsound_local(epicenter, null, far_volume, TRUE, frequency, sound_to_use = echo_sound, distance_multiplier = 0)
|
||||
|
||||
if(base_shake_amount || quake_factor)
|
||||
base_shake_amount = max(base_shake_amount, quake_factor * 3, 0) // Devastating explosions rock the station and ground
|
||||
@@ -552,7 +552,7 @@ SUBSYSTEM_DEF(explosions)
|
||||
shake_camera(listener, FAR_SHAKE_DURATION, clamp(quake_factor / 4, 0, FAR_SHAKE_CAP))
|
||||
else
|
||||
echo_volume = 40
|
||||
listener.playsound_local(epicenter, null, echo_volume, TRUE, frequency, S = echo_sound, distance_multiplier = 0)
|
||||
listener.playsound_local(epicenter, null, echo_volume, TRUE, frequency, sound_to_use = echo_sound, distance_multiplier = 0)
|
||||
|
||||
if(creaking) // 5 seconds after the bang, the station begins to creak
|
||||
addtimer(CALLBACK(listener, /mob/proc/playsound_local, epicenter, null, rand(FREQ_LOWER, FREQ_UPPER), TRUE, frequency, null, null, FALSE, hull_creaking_sound, 0), CREAK_DELAY)
|
||||
|
||||
@@ -40,14 +40,21 @@ SUBSYSTEM_DEF(mapping)
|
||||
// Z-manager stuff
|
||||
var/station_start // should only be used for maploading-related tasks
|
||||
var/space_levels_so_far = 0
|
||||
///list of all the z level datums created representing the z levels in the world
|
||||
var/list/z_list
|
||||
///list of all z level datums in the order of their z (z level 1 is at index 1, etc.)
|
||||
var/list/datum/space_level/z_list
|
||||
///list of all z level indices that form multiz connections and whether theyre linked up or down
|
||||
///list of lists, inner lists are of the form: list("up or down link direction" = TRUE)
|
||||
var/list/multiz_levels = list()
|
||||
var/datum/space_level/transit
|
||||
var/datum/space_level/empty_space
|
||||
var/num_of_res_levels = 1
|
||||
/// True when in the process of adding a new Z-level, global locking
|
||||
var/adding_new_zlevel = FALSE
|
||||
|
||||
///shows the default gravity value for each z level. recalculated when gravity generators change.
|
||||
///associative list of the form: list("[z level num]" = max generator gravity in that z level OR the gravity level trait)
|
||||
var/list/gravity_by_z_level = list()
|
||||
|
||||
/datum/controller/subsystem/mapping/New()
|
||||
..()
|
||||
#ifdef FORCE_MAP
|
||||
@@ -101,8 +108,48 @@ SUBSYSTEM_DEF(mapping)
|
||||
generate_station_area_list()
|
||||
initialize_reserved_level(transit.z_value)
|
||||
SSticker.OnRoundstart(CALLBACK(src, .proc/spawn_maintenance_loot))
|
||||
generate_z_level_linkages()
|
||||
calculate_default_z_level_gravities()
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/mapping/proc/calculate_default_z_level_gravities()
|
||||
for(var/z_level in 1 to length(z_list))
|
||||
calculate_z_level_gravity(z_level)
|
||||
|
||||
/datum/controller/subsystem/mapping/proc/generate_z_level_linkages()
|
||||
for(var/z_level in 1 to length(z_list))
|
||||
generate_linkages_for_z_level(z_level)
|
||||
|
||||
/datum/controller/subsystem/mapping/proc/generate_linkages_for_z_level(z_level)
|
||||
if(!isnum(z_level) || z_level <= 0)
|
||||
return FALSE
|
||||
|
||||
if(multiz_levels.len < z_level)
|
||||
multiz_levels.len = z_level
|
||||
|
||||
var/linked_down = level_trait(z_level, ZTRAIT_DOWN)
|
||||
var/linked_up = level_trait(z_level, ZTRAIT_UP)
|
||||
multiz_levels[z_level] = list()
|
||||
if(linked_down)
|
||||
multiz_levels[z_level]["[DOWN]"] = TRUE
|
||||
if(linked_up)
|
||||
multiz_levels[z_level]["[UP]"] = TRUE
|
||||
|
||||
/datum/controller/subsystem/mapping/proc/calculate_z_level_gravity(z_level_number)
|
||||
if(!isnum(z_level_number) || z_level_number < 1)
|
||||
return FALSE
|
||||
|
||||
var/max_gravity = 0
|
||||
|
||||
for(var/obj/machinery/gravity_generator/main/grav_gen as anything in GLOB.gravity_generators["[z_level_number]"])
|
||||
max_gravity = max(grav_gen.setting, max_gravity)
|
||||
|
||||
max_gravity = max_gravity || level_trait(z_level_number, ZTRAIT_GRAVITY) || 0//just to make sure no nulls
|
||||
gravity_by_z_level["[z_level_number]"] = max_gravity
|
||||
return max_gravity
|
||||
|
||||
|
||||
/**
|
||||
* ##setup_ruins
|
||||
*
|
||||
@@ -215,6 +262,7 @@ Used by the AI doomsday and the self-destruct nuke.
|
||||
clearing_reserved_turfs = SSmapping.clearing_reserved_turfs
|
||||
|
||||
z_list = SSmapping.z_list
|
||||
multiz_levels = SSmapping.multiz_levels
|
||||
|
||||
#define INIT_ANNOUNCE(X) to_chat(world, span_boldannounce("[X]")); log_world(X)
|
||||
/datum/controller/subsystem/mapping/proc/LoadGroup(list/errorList, name, path, files, list/traits, list/default_traits, silent = FALSE)
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
PROCESSING_SUBSYSTEM_DEF(tramprocess)
|
||||
name = "Tram Process"
|
||||
wait = 1
|
||||
wait = 0.5
|
||||
/// only used on maps with trams, so only enabled by such.
|
||||
can_fire = FALSE
|
||||
|
||||
///how much time a tram can take per movement before we notify admins and slow down the tram. in milliseconds
|
||||
var/max_time = 15
|
||||
|
||||
///how many times the tram can move costing over max_time milliseconds before it gets slowed down
|
||||
var/max_exceeding_moves = 5
|
||||
|
||||
///how many times the tram can move costing less than half max_time milliseconds before we speed it back up again.
|
||||
///is only used if the tram has been slowed down for exceeding max_time
|
||||
var/max_cheap_moves = 5
|
||||
|
||||
@@ -171,53 +171,6 @@ SUBSYSTEM_DEF(spatial_grid)
|
||||
var/datum/spatial_grid_cell/cell = new(x, y, z_level.z_value)
|
||||
new_cell_grid[y] += cell
|
||||
|
||||
///creates number_to_generate new oranges_ear's and adds them to the subsystems list of ears.
|
||||
///i really fucking hope this never gets called after init :clueless:
|
||||
/datum/controller/subsystem/spatial_grid/proc/pregenerate_more_oranges_ears(number_to_generate)
|
||||
for(var/new_ear in 1 to number_to_generate)
|
||||
pregenerated_oranges_ears += new/mob/oranges_ear(null)
|
||||
|
||||
number_of_oranges_ears = length(pregenerated_oranges_ears)
|
||||
|
||||
///allocate one [/mob/oranges_ear] mob per turf containing atoms_that_need_ears and give them a reference to every listed atom in their turf.
|
||||
///if an oranges_ear is allocated to a turf that already has an oranges_ear then the second one fails to allocate (and gives the existing one the atom it was assigned to)
|
||||
/datum/controller/subsystem/spatial_grid/proc/assign_oranges_ears(list/atoms_that_need_ears)
|
||||
var/input_length = length(atoms_that_need_ears)
|
||||
|
||||
if(input_length > number_of_oranges_ears)
|
||||
stack_trace("somehow, for some reason, more than the preset generated number of oranges ears was requested. thats fucking [number_of_oranges_ears]. this is not good that should literally never happen")
|
||||
pregenerate_more_oranges_ears(input_length - number_of_oranges_ears)//im still gonna DO IT but ill complain about it
|
||||
|
||||
. = list()
|
||||
|
||||
///the next unallocated /mob/oranges_ear that we try to allocate to assigned_atom's turf
|
||||
var/mob/oranges_ear/current_ear
|
||||
///the next atom in atoms_that_need_ears an ear assigned to it
|
||||
var/atom/assigned_atom
|
||||
///the turf loc of the current assigned_atom. turfs are used to track oranges_ears already assigned to one location so we dont allocate more than one
|
||||
///because allocating more than one oranges_ear to a given loc wastes view iterations
|
||||
var/turf/turf_loc
|
||||
|
||||
for(var/current_ear_index in 1 to input_length)
|
||||
assigned_atom = atoms_that_need_ears[current_ear_index]
|
||||
|
||||
turf_loc = get_turf(assigned_atom)
|
||||
if(!turf_loc)
|
||||
continue
|
||||
|
||||
current_ear = pregenerated_oranges_ears[current_ear_index]
|
||||
|
||||
if(turf_loc.assigned_oranges_ear)
|
||||
turf_loc.assigned_oranges_ear.references += assigned_atom
|
||||
continue //if theres already an oranges_ear mob at assigned_movable's turf we give assigned_movable to it instead and dont allocate ourselves
|
||||
|
||||
current_ear.references += assigned_atom
|
||||
|
||||
current_ear.loc = turf_loc //normally this is bad, but since this is meant to be as fast as possible we literally just need to exist there for view() to see us
|
||||
turf_loc.assigned_oranges_ear = current_ear
|
||||
|
||||
. += current_ear
|
||||
|
||||
///adds cells to the grid for every z level when world.maxx or world.maxy is expanded after this subsystem is initialized. hopefully this is never needed.
|
||||
///because i never tested this.
|
||||
/datum/controller/subsystem/spatial_grid/proc/after_world_bounds_expanded(datum/controller/subsystem/processing/dcs/fucking_dcs, has_expanded_world_maxx, has_expanded_world_maxy)
|
||||
@@ -278,10 +231,6 @@ SUBSYSTEM_DEF(spatial_grid)
|
||||
|
||||
. = list()
|
||||
|
||||
//cache for sanic speeds
|
||||
var/cells_on_y_axis = src.cells_on_y_axis
|
||||
var/cells_on_x_axis = src.cells_on_x_axis
|
||||
|
||||
//technically THIS list only contains lists, but inside those lists are grid cell datums and we can go without a SINGLE var init if we do this
|
||||
var/list/datum/spatial_grid_cell/grid_level = grids_by_z_level[center_turf.z]
|
||||
|
||||
@@ -458,6 +407,8 @@ SUBSYSTEM_DEF(spatial_grid)
|
||||
GRID_CELL_SET(intersecting_cell.atmos_contents, new_target)
|
||||
SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_ENTERED(SPATIAL_GRID_CONTENTS_TYPE_ATMOS), new_target)
|
||||
|
||||
return intersecting_cell
|
||||
|
||||
/**
|
||||
* find the spatial map cell that target used to belong to, then remove the target (and sometimes it's important_recusive_contents) from it.
|
||||
* make sure to provide the turf old_target used to be "in"
|
||||
@@ -584,6 +535,53 @@ SUBSYSTEM_DEF(spatial_grid)
|
||||
message_admins(cell_coords)
|
||||
message_admins("[src] is supposed to only be contained in the cell at indexes ([real_cell.cell_x], [real_cell.cell_y], [real_cell.cell_z]). but is contained at the cells at [cell_coords]")
|
||||
|
||||
///creates number_to_generate new oranges_ear's and adds them to the subsystems list of ears.
|
||||
///i really fucking hope this never gets called after init :clueless:
|
||||
/datum/controller/subsystem/spatial_grid/proc/pregenerate_more_oranges_ears(number_to_generate)
|
||||
for(var/new_ear in 1 to number_to_generate)
|
||||
pregenerated_oranges_ears += new/mob/oranges_ear(null)
|
||||
|
||||
number_of_oranges_ears = length(pregenerated_oranges_ears)
|
||||
|
||||
///allocate one [/mob/oranges_ear] mob per turf containing atoms_that_need_ears and give them a reference to every listed atom in their turf.
|
||||
///if an oranges_ear is allocated to a turf that already has an oranges_ear then the second one fails to allocate (and gives the existing one the atom it was assigned to)
|
||||
/datum/controller/subsystem/spatial_grid/proc/assign_oranges_ears(list/atoms_that_need_ears)
|
||||
var/input_length = length(atoms_that_need_ears)
|
||||
|
||||
if(input_length > number_of_oranges_ears)
|
||||
stack_trace("somehow, for some reason, more than the preset generated number of oranges ears was requested. thats fucking [number_of_oranges_ears]. this is not good that should literally never happen")
|
||||
pregenerate_more_oranges_ears(input_length - number_of_oranges_ears)//im still gonna DO IT but ill complain about it
|
||||
|
||||
. = list()
|
||||
|
||||
///the next unallocated /mob/oranges_ear that we try to allocate to assigned_atom's turf
|
||||
var/mob/oranges_ear/current_ear
|
||||
///the next atom in atoms_that_need_ears an ear assigned to it
|
||||
var/atom/assigned_atom
|
||||
///the turf loc of the current assigned_atom. turfs are used to track oranges_ears already assigned to one location so we dont allocate more than one
|
||||
///because allocating more than one oranges_ear to a given loc wastes view iterations
|
||||
var/turf/turf_loc
|
||||
|
||||
for(var/current_ear_index in 1 to input_length)
|
||||
assigned_atom = atoms_that_need_ears[current_ear_index]
|
||||
|
||||
turf_loc = get_turf(assigned_atom)
|
||||
if(!turf_loc)
|
||||
continue
|
||||
|
||||
current_ear = pregenerated_oranges_ears[current_ear_index]
|
||||
|
||||
if(turf_loc.assigned_oranges_ear)
|
||||
turf_loc.assigned_oranges_ear.references += assigned_atom
|
||||
continue //if theres already an oranges_ear mob at assigned_movable's turf we give assigned_movable to it instead and dont allocate ourselves
|
||||
|
||||
current_ear.references += assigned_atom
|
||||
|
||||
current_ear.loc = turf_loc //normally this is bad, but since this is meant to be as fast as possible we literally just need to exist there for view() to see us
|
||||
turf_loc.assigned_oranges_ear = current_ear
|
||||
|
||||
. += current_ear
|
||||
|
||||
///debug proc for finding how full the cells of src's z level are
|
||||
/atom/proc/find_grid_statistics_for_z_level(insert_clients = 0)
|
||||
var/raw_clients = 0
|
||||
|
||||
@@ -53,19 +53,42 @@
|
||||
var/obj/item/parent_item = parent
|
||||
parent_item.update_slot_icon()
|
||||
|
||||
|
||||
/datum/component/bloodysoles/proc/reset_bloody_shoes()
|
||||
bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0)
|
||||
on_changed_bloody_shoes(BLOOD_STATE_NOT_BLOODY)
|
||||
|
||||
///lowers bloody_shoes[index] by adjust_by
|
||||
/datum/component/bloodysoles/proc/adjust_bloody_shoes(index, adjust_by)
|
||||
bloody_shoes[index] = max(bloody_shoes[index] - adjust_by, 0)
|
||||
on_changed_bloody_shoes()
|
||||
|
||||
/datum/component/bloodysoles/proc/set_bloody_shoes(index, new_value)
|
||||
bloody_shoes[index] = new_value
|
||||
on_changed_bloody_shoes(index)
|
||||
|
||||
///called whenever the value of bloody_soles changes
|
||||
/datum/component/bloodysoles/proc/on_changed_bloody_shoes(index)
|
||||
if(index && index != last_blood_state)
|
||||
last_blood_state = index
|
||||
if(!wielder)
|
||||
return
|
||||
if(bloody_shoes[last_blood_state] <= BLOOD_FOOTPRINTS_MIN * 2)//need twice that amount to make footprints
|
||||
UnregisterSignal(wielder, COMSIG_MOVABLE_MOVED)
|
||||
else
|
||||
RegisterSignal(wielder, COMSIG_MOVABLE_MOVED, .proc/on_moved, override = TRUE)
|
||||
|
||||
/**
|
||||
* Run to equally share the blood between us and a decal
|
||||
*/
|
||||
/datum/component/bloodysoles/proc/share_blood(obj/effect/decal/cleanable/pool)
|
||||
last_blood_state = pool.blood_state
|
||||
|
||||
// Share the blood between our boots and the blood pool
|
||||
var/total_bloodiness = pool.bloodiness + bloody_shoes[last_blood_state]
|
||||
var/total_bloodiness = pool.bloodiness + bloody_shoes[pool.blood_state]
|
||||
|
||||
// We can however be limited by how much blood we can hold
|
||||
var/new_our_bloodiness = min(BLOOD_ITEM_MAX, total_bloodiness / 2)
|
||||
|
||||
bloody_shoes[last_blood_state] = new_our_bloodiness
|
||||
set_bloody_shoes(pool.blood_state, new_our_bloodiness)
|
||||
pool.bloodiness = total_bloodiness - new_our_bloodiness // Give the pool the remaining blood incase we were limited
|
||||
|
||||
if(HAS_TRAIT(parent_atom, TRAIT_LIGHT_STEP)) //the character is agile enough to don't mess their clothing and hands just from one blood splatter at floor
|
||||
@@ -105,7 +128,8 @@
|
||||
|
||||
equipped_slot = slot
|
||||
wielder = equipper
|
||||
RegisterSignal(wielder, COMSIG_MOVABLE_MOVED, .proc/on_moved)
|
||||
if(bloody_shoes[last_blood_state] > BLOOD_FOOTPRINTS_MIN * 2)
|
||||
RegisterSignal(wielder, COMSIG_MOVABLE_MOVED, .proc/on_moved)
|
||||
RegisterSignal(wielder, COMSIG_STEP_ON_BLOOD, .proc/on_step_blood)
|
||||
|
||||
/**
|
||||
@@ -147,7 +171,7 @@
|
||||
oldLocFP.update_appearance()
|
||||
else if(find_pool_by_blood_state(oldLocTurf))
|
||||
// No footprints in the tile we left, but there was some other blood pool there. Add exit footprints on it
|
||||
bloody_shoes[last_blood_state] -= half_our_blood
|
||||
adjust_bloody_shoes(last_blood_state, half_our_blood)
|
||||
update_icon()
|
||||
|
||||
oldLocFP = new(oldLocTurf)
|
||||
@@ -167,7 +191,7 @@
|
||||
|
||||
// Create new footprints
|
||||
if(half_our_blood >= BLOOD_FOOTPRINTS_MIN)
|
||||
bloody_shoes[last_blood_state] -= half_our_blood
|
||||
adjust_bloody_shoes(last_blood_state, half_our_blood)
|
||||
update_icon()
|
||||
|
||||
var/obj/effect/decal/cleanable/blood/footprints/FP = new(get_turf(parent_atom))
|
||||
@@ -213,8 +237,7 @@
|
||||
if(!(clean_types & CLEAN_TYPE_BLOOD) || last_blood_state == BLOOD_STATE_NOT_BLOODY)
|
||||
return NONE
|
||||
|
||||
bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0)
|
||||
last_blood_state = BLOOD_STATE_NOT_BLOODY
|
||||
reset_bloody_shoes()
|
||||
update_icon()
|
||||
return COMPONENT_CLEANED
|
||||
|
||||
@@ -235,7 +258,6 @@
|
||||
bloody_feet = mutable_appearance('icons/effects/blood.dmi', "shoeblood", SHOES_LAYER)
|
||||
|
||||
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, .proc/on_clean)
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/on_moved)
|
||||
RegisterSignal(parent, COMSIG_STEP_ON_BLOOD, .proc/on_step_blood)
|
||||
RegisterSignal(parent, COMSIG_CARBON_UNEQUIP_SHOECOVER, .proc/unequip_shoecover)
|
||||
RegisterSignal(parent, COMSIG_CARBON_EQUIP_SHOECOVER, .proc/equip_shoecover)
|
||||
|
||||
@@ -114,7 +114,6 @@
|
||||
return
|
||||
|
||||
var/atom/movable/movable_parent = parent
|
||||
movable_parent.inertia_moving = FALSE
|
||||
movable_parent.setDir(old_dir)
|
||||
if(movable_parent.Process_Spacemove(drifting_loop.direction, continuous_move = TRUE))
|
||||
glide_to_halt(visual_delay)
|
||||
@@ -137,7 +136,7 @@
|
||||
if(!isturf(movable_parent.loc))
|
||||
qdel(src)
|
||||
return
|
||||
if(movable_parent.inertia_moving) //This'll be handled elsewhere
|
||||
if(movable_parent.inertia_moving)
|
||||
return
|
||||
if(!movable_parent.Process_Spacemove(drifting_loop.direction, continuous_move = TRUE))
|
||||
return
|
||||
|
||||
@@ -111,7 +111,6 @@
|
||||
. = ..()
|
||||
if(directional)
|
||||
RegisterSignal(parent, COMSIG_ATOM_DIR_CHANGE, .proc/on_parent_dir_change)
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/on_parent_moved)
|
||||
RegisterSignal(parent, COMSIG_ATOM_UPDATE_LIGHT_RANGE, .proc/set_range)
|
||||
RegisterSignal(parent, COMSIG_ATOM_UPDATE_LIGHT_POWER, .proc/set_power)
|
||||
RegisterSignal(parent, COMSIG_ATOM_UPDATE_LIGHT_COLOR, .proc/set_color)
|
||||
@@ -119,6 +118,7 @@
|
||||
RegisterSignal(parent, COMSIG_ATOM_UPDATE_LIGHT_FLAGS, .proc/on_light_flags_change)
|
||||
RegisterSignal(parent, COMSIG_ATOM_USED_IN_CRAFT, .proc/on_parent_crafted)
|
||||
RegisterSignal(parent, COMSIG_LIGHT_EATER_QUEUE, .proc/on_light_eater)
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/on_parent_moved)
|
||||
var/atom/movable/movable_parent = parent
|
||||
if(movable_parent.light_flags & LIGHT_ATTACHED)
|
||||
overlay_lighting_flags |= LIGHTING_ATTACHED
|
||||
@@ -245,8 +245,9 @@
|
||||
return
|
||||
if(new_holder != parent && new_holder != parent_attached_to)
|
||||
RegisterSignal(new_holder, COMSIG_PARENT_QDELETING, .proc/on_holder_qdel)
|
||||
RegisterSignal(new_holder, COMSIG_MOVABLE_MOVED, .proc/on_holder_moved)
|
||||
RegisterSignal(new_holder, COMSIG_LIGHT_EATER_QUEUE, .proc/on_light_eater)
|
||||
if(overlay_lighting_flags & LIGHTING_ON)
|
||||
RegisterSignal(new_holder, COMSIG_MOVABLE_MOVED, .proc/on_holder_moved)
|
||||
if(directional)
|
||||
RegisterSignal(new_holder, COMSIG_ATOM_DIR_CHANGE, .proc/on_holder_dir_change)
|
||||
set_direction(new_holder.dir)
|
||||
@@ -423,6 +424,8 @@
|
||||
cast_directional_light()
|
||||
add_dynamic_lumi()
|
||||
overlay_lighting_flags |= LIGHTING_ON
|
||||
if(current_holder && current_holder != parent && current_holder != parent_attached_to)
|
||||
RegisterSignal(current_holder, COMSIG_MOVABLE_MOVED, .proc/on_holder_moved)
|
||||
get_new_turfs()
|
||||
|
||||
|
||||
@@ -433,6 +436,8 @@
|
||||
if(current_holder)
|
||||
remove_dynamic_lumi()
|
||||
overlay_lighting_flags &= ~LIGHTING_ON
|
||||
if(current_holder)
|
||||
UnregisterSignal(current_holder, COMSIG_MOVABLE_MOVED)
|
||||
clean_old_turfs()
|
||||
|
||||
|
||||
|
||||
@@ -4,45 +4,66 @@
|
||||
|
||||
/datum/component/storage
|
||||
dupe_mode = COMPONENT_DUPE_UNIQUE
|
||||
var/datum/component/storage/concrete/master //If not null, all actions act on master and this is just an access point.
|
||||
///If not null, all actions act on master and this is just an access point.
|
||||
var/datum/component/storage/concrete/master
|
||||
|
||||
var/list/can_hold //if this is set, only items, and their children, will fit
|
||||
var/list/cant_hold //if this is set, items, and their children, won't fit
|
||||
var/list/exception_hold //if set, these items will be the exception to the max size of object that can fit.
|
||||
///if this is set, only items, and their children, will fit
|
||||
var/list/can_hold
|
||||
///if this is set, items, and their children, won't fit
|
||||
var/list/cant_hold
|
||||
///if set, these items will be the exception to the max size of object that can fit.
|
||||
var/list/exception_hold
|
||||
/// If set can only contain stuff with this single trait present.
|
||||
var/list/can_hold_trait
|
||||
|
||||
var/can_hold_description
|
||||
|
||||
var/list/mob/is_using //lazy list of mobs looking at the contents of this storage.
|
||||
///lazy list of mobs looking at the contents of this storage.
|
||||
var/list/mob/is_using
|
||||
|
||||
var/locked = FALSE //when locked nothing can see inside or use it.
|
||||
///when locked nothing can see inside or use it.
|
||||
var/locked = FALSE
|
||||
|
||||
var/max_w_class = WEIGHT_CLASS_SMALL //max size of objects that will fit.
|
||||
var/max_combined_w_class = 14 //max combined sizes of objects that will fit.
|
||||
var/max_items = 7 //max number of objects that will fit.
|
||||
///max size of objects that will fit.
|
||||
var/max_w_class = WEIGHT_CLASS_SMALL
|
||||
///max combined sizes of objects that will fit.
|
||||
var/max_combined_w_class = 14
|
||||
///max number of objects that will fit.
|
||||
var/max_items = 7
|
||||
|
||||
var/emp_shielded = FALSE
|
||||
|
||||
var/silent = FALSE //whether this makes a message when things are put in.
|
||||
var/click_gather = FALSE //whether this can be clicked on items to pick it up rather than the other way around.
|
||||
var/rustle_sound = TRUE //play rustle sound on interact.
|
||||
var/allow_quick_empty = FALSE //allow empty verb which allows dumping on the floor of everything inside quickly.
|
||||
var/allow_quick_gather = FALSE //allow toggle mob verb which toggles collecting all items from a tile.
|
||||
///whether this makes a message when things are put in.
|
||||
var/silent = FALSE
|
||||
///whether this can be clicked on items to pick it up rather than the other way around.
|
||||
var/click_gather = FALSE
|
||||
///play rustle sound on interact.
|
||||
var/rustle_sound = TRUE
|
||||
///allow empty verb which allows dumping on the floor of everything inside quickly.
|
||||
var/allow_quick_empty = FALSE
|
||||
///allow toggle mob verb which toggles collecting all items from a tile.
|
||||
var/allow_quick_gather = FALSE
|
||||
|
||||
var/collection_mode = COLLECT_EVERYTHING
|
||||
|
||||
var/insert_preposition = "in" //you put things "in" a bag, but "on" a tray.
|
||||
///you put things "in" a bag, but "on" a tray.
|
||||
var/insert_preposition = "in"
|
||||
|
||||
var/display_numerical_stacking = FALSE //stack things of the same type and show as a single object with a number.
|
||||
///stack things of the same type and show as a single object with a number.
|
||||
var/display_numerical_stacking = FALSE
|
||||
|
||||
var/atom/movable/screen/storage/boxes //storage display object
|
||||
var/atom/movable/screen/close/closer //close button object
|
||||
///storage display object
|
||||
var/atom/movable/screen/storage/boxes
|
||||
///close button object
|
||||
var/atom/movable/screen/close/closer
|
||||
|
||||
var/allow_big_nesting = FALSE //allow storage objects of the same or greater size.
|
||||
///allow storage objects of the same or greater size.
|
||||
var/allow_big_nesting = FALSE
|
||||
|
||||
var/attack_hand_interact = TRUE //interact on attack hand.
|
||||
var/quickdraw = FALSE //altclick interact
|
||||
///interact on attack hand.
|
||||
var/attack_hand_interact = TRUE
|
||||
///altclick interact
|
||||
var/quickdraw = FALSE
|
||||
|
||||
var/datum/action/item_action/storage_gather_mode/modeswitch_action
|
||||
|
||||
@@ -420,6 +441,9 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
|
||||
M.client.screen |= closer
|
||||
M.client.screen |= real_location.contents
|
||||
M.set_active_storage(src)
|
||||
if(ismovable(real_location))
|
||||
var/atom/movable/movable_loc = real_location
|
||||
movable_loc.become_active_storage(src)
|
||||
LAZYOR(is_using, M)
|
||||
RegisterSignal(M, COMSIG_PARENT_QDELETING, .proc/mob_deleted)
|
||||
return TRUE
|
||||
@@ -437,6 +461,10 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
|
||||
if(!M.client)
|
||||
return TRUE
|
||||
var/atom/real_location = real_location()
|
||||
if(!length(is_using) && ismovable(real_location))
|
||||
var/atom/movable/movable_loc = real_location
|
||||
movable_loc.lose_active_storage(src)
|
||||
|
||||
M.client.screen -= boxes
|
||||
M.client.screen -= closer
|
||||
M.client.screen -= real_location.contents
|
||||
|
||||
@@ -127,8 +127,7 @@
|
||||
decrease = max(0, decrease)
|
||||
if((is_wet() & TURF_WET_ICE) && t > T0C) //Ice melts into water!
|
||||
for(var/obj/O in T.contents)
|
||||
if(O.obj_flags & FROZEN)
|
||||
O.make_unfrozen()
|
||||
O.make_unfrozen()
|
||||
add_wet(TURF_WET_WATER, max_time_left())
|
||||
dry(null, TURF_WET_ICE)
|
||||
dry(null, ALL, FALSE, decrease)
|
||||
|
||||
@@ -32,9 +32,9 @@
|
||||
/// Deactivates the functionality defines by the element on the given datum
|
||||
/datum/element/proc/Detach(datum/source, ...)
|
||||
SIGNAL_HANDLER
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
|
||||
SEND_SIGNAL(source, COMSIG_ELEMENT_DETACH, src)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
UnregisterSignal(source, COMSIG_PARENT_QDELETING)
|
||||
|
||||
/datum/element/Destroy(force)
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
/datum/element/atmos_sensitive/Detach(datum/source)
|
||||
var/atom/us = source
|
||||
us.RemoveElement(/datum/element/connect_loc, pass_on)
|
||||
UnregisterSignal(source, COMSIG_MOVABLE_MOVED)
|
||||
if(us.flags_1 & ATMOS_IS_PROCESSING_1)
|
||||
us.atmos_end()
|
||||
SSair.atom_process -= us
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
RegisterSignal(target, COMSIG_ITEM_EQUIPPED, .proc/on_equipped)
|
||||
|
||||
/datum/element/chewable/Detach(datum/source, force)
|
||||
. = ..()
|
||||
processing -= source
|
||||
UnregisterSignal(source, list(COMSIG_ITEM_DROPPED, COMSIG_ITEM_EQUIPPED))
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
if(steps % 2)
|
||||
return
|
||||
|
||||
if(steps != 0 && !source.has_gravity(turf)) // don't need to step as often when you hop around
|
||||
if(steps != 0 && !source.has_gravity()) // don't need to step as often when you hop around
|
||||
return
|
||||
return turf
|
||||
|
||||
@@ -117,10 +117,10 @@
|
||||
return
|
||||
playsound(source_loc, pick(footstep_sounds[turf_footstep][1]), footstep_sounds[turf_footstep][2] * volume, TRUE, footstep_sounds[turf_footstep][3] + e_range, falloff_distance = 1, vary = sound_vary)
|
||||
|
||||
/datum/element/footstep/proc/play_humanstep(mob/living/carbon/human/source, atom/oldloc, direction)
|
||||
/datum/element/footstep/proc/play_humanstep(mob/living/carbon/human/source, atom/oldloc, direction, forced, list/old_locs, momentum_change)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if (SHOULD_DISABLE_FOOTSTEPS(source))
|
||||
if (SHOULD_DISABLE_FOOTSTEPS(source) || !momentum_change)
|
||||
return
|
||||
|
||||
var/volume_multiplier = 1
|
||||
@@ -134,21 +134,31 @@
|
||||
if(!source_loc)
|
||||
return
|
||||
|
||||
play_fov_effect(source, 5, "footstep", direction, ignore_self = TRUE)
|
||||
//cache for sanic speed (lists are references anyways)
|
||||
var/static/list/footstep_sounds = GLOB.footstep
|
||||
///list returned by playsound() filled by client mobs who heard the footstep. given to play_fov_effect()
|
||||
var/list/heard_clients
|
||||
|
||||
if ((source.wear_suit?.body_parts_covered | source.w_uniform?.body_parts_covered | source.shoes?.body_parts_covered) & FEET)
|
||||
// we are wearing shoes
|
||||
playsound(source_loc, pick(GLOB.footstep[source_loc.footstep][1]),
|
||||
GLOB.footstep[source_loc.footstep][2] * volume * volume_multiplier,
|
||||
|
||||
heard_clients = playsound(source_loc, pick(footstep_sounds[source_loc.footstep][1]),
|
||||
footstep_sounds[source_loc.footstep][2] * volume * volume_multiplier,
|
||||
TRUE,
|
||||
GLOB.footstep[source_loc.footstep][3] + e_range + range_adjustment, falloff_distance = 1, vary = sound_vary)
|
||||
footstep_sounds[source_loc.footstep][3] + e_range + range_adjustment, falloff_distance = 1, vary = sound_vary)
|
||||
else
|
||||
if(source.dna.species.special_step_sounds)
|
||||
playsound(source_loc, pick(source.dna.species.special_step_sounds), 50, TRUE, falloff_distance = 1, vary = sound_vary)
|
||||
heard_clients = playsound(source_loc, pick(source.dna.species.special_step_sounds), 50, TRUE, falloff_distance = 1, vary = sound_vary)
|
||||
else
|
||||
playsound(source_loc, pick(GLOB.barefootstep[source_loc.barefootstep][1]),
|
||||
GLOB.barefootstep[source_loc.barefootstep][2] * volume * volume_multiplier,
|
||||
var/static/list/bare_footstep_sounds = GLOB.barefootstep
|
||||
|
||||
heard_clients = playsound(source_loc, pick(bare_footstep_sounds[source_loc.barefootstep][1]),
|
||||
bare_footstep_sounds[source_loc.barefootstep][2] * volume * volume_multiplier,
|
||||
TRUE,
|
||||
GLOB.barefootstep[source_loc.barefootstep][3] + e_range + range_adjustment, falloff_distance = 1, vary = sound_vary)
|
||||
bare_footstep_sounds[source_loc.barefootstep][3] + e_range + range_adjustment, falloff_distance = 1, vary = sound_vary)
|
||||
|
||||
if(heard_clients)
|
||||
play_fov_effect(source, 5, "footstep", direction, ignore_self = TRUE, override_list = heard_clients)
|
||||
|
||||
|
||||
///Prepares a footstep for machine walking
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
/datum/element/forced_gravity
|
||||
element_flags = ELEMENT_BESPOKE
|
||||
id_arg_index = 2
|
||||
///the level of gravity we force unto our target
|
||||
var/gravity
|
||||
var/ignore_space
|
||||
///whether we will override the turf if it forces no gravity
|
||||
var/ignore_turf_gravity
|
||||
|
||||
/datum/element/forced_gravity/Attach(datum/target, gravity=1, ignore_space=FALSE)
|
||||
/datum/element/forced_gravity/Attach(datum/target, gravity=1, ignore_turf_gravity = FALSE)
|
||||
. = ..()
|
||||
if(!isatom(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
|
||||
src.gravity = gravity
|
||||
src.ignore_space = ignore_space
|
||||
src.ignore_turf_gravity = ignore_turf_gravity
|
||||
|
||||
RegisterSignal(target, COMSIG_ATOM_HAS_GRAVITY, .proc/gravity_check)
|
||||
if(isturf(target))
|
||||
@@ -24,10 +26,12 @@
|
||||
/datum/element/forced_gravity/proc/gravity_check(datum/source, turf/location, list/gravs)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!ignore_space && isspaceturf(location))
|
||||
return
|
||||
if(!ignore_turf_gravity && location.force_no_gravity)
|
||||
return FALSE
|
||||
gravs += gravity
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/element/forced_gravity/proc/turf_gravity_check(datum/source, atom/checker, list/gravs)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
|
||||
47
code/datums/elements/frozen.dm
Normal file
47
code/datums/elements/frozen.dm
Normal file
@@ -0,0 +1,47 @@
|
||||
///simple element to handle frozen obj's
|
||||
/datum/element/frozen
|
||||
element_flags = ELEMENT_DETACH
|
||||
|
||||
/datum/element/frozen/Attach(datum/target)
|
||||
. = ..()
|
||||
if(!isobj(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
|
||||
var/obj/target_obj = target
|
||||
if(target_obj.obj_flags & FREEZE_PROOF)
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
|
||||
target_obj.name = "frozen [target_obj.name]"
|
||||
target_obj.add_atom_colour(GLOB.freon_color_matrix, TEMPORARY_COLOUR_PRIORITY)
|
||||
target_obj.alpha -= 25
|
||||
|
||||
RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/on_moved)
|
||||
RegisterSignal(target, COMSIG_MOVABLE_POST_THROW, .proc/shatter_on_throw)
|
||||
RegisterSignal(target, COMSIG_OBJ_UNFREEZE, .proc/on_unfrozen)
|
||||
|
||||
/datum/element/frozen/Detach(datum/source, ...)
|
||||
var/obj/obj_source = source
|
||||
obj_source.name = replacetext(obj_source.name, "frozen ", "")
|
||||
obj_source.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, GLOB.freon_color_matrix)
|
||||
obj_source.alpha += 25
|
||||
. = ..()
|
||||
|
||||
/datum/element/frozen/proc/on_unfrozen(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
Detach(source)
|
||||
|
||||
/datum/element/frozen/proc/shatter_on_throw(datum/target)
|
||||
SIGNAL_HANDLER
|
||||
var/obj/obj_target = target
|
||||
obj_target.visible_message(span_danger("[obj_target] shatters into a million pieces!"))
|
||||
qdel(obj_target)
|
||||
|
||||
/datum/element/frozen/proc/on_moved(datum/target)
|
||||
SIGNAL_HANDLER
|
||||
var/obj/obj_target = target
|
||||
if(!isopenturf(obj_target.loc))
|
||||
return
|
||||
|
||||
var/turf/open/turf_loc = obj_target.loc
|
||||
if(turf_loc.air?.temperature >= T0C)//unfreezes target
|
||||
Detach(target)
|
||||
@@ -67,8 +67,7 @@
|
||||
/datum/element/light_eater/proc/table_buffet(atom/commisary, datum/devourer)
|
||||
. = list()
|
||||
SEND_SIGNAL(commisary, COMSIG_LIGHT_EATER_QUEUE, ., devourer)
|
||||
for(var/nom in commisary.light_sources)
|
||||
var/datum/light_source/morsel = nom
|
||||
for(var/datum/light_source/morsel as anything in commisary.light_sources)
|
||||
.[morsel.source_atom] = TRUE
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
processing |= target
|
||||
|
||||
/datum/element/obj_regen/Detach(obj/target)
|
||||
. = ..()
|
||||
UnregisterSignal(target, COMSIG_ATOM_TAKE_DAMAGE)
|
||||
processing -= target
|
||||
if(!length(processing))
|
||||
|
||||
@@ -403,7 +403,8 @@ GLOBAL_LIST_EMPTY(teleportlocs)
|
||||
/area/Entered(atom/movable/arrived, area/old_area)
|
||||
set waitfor = FALSE
|
||||
SEND_SIGNAL(src, COMSIG_AREA_ENTERED, arrived, old_area)
|
||||
if(!LAZYACCESS(arrived.important_recursive_contents, RECURSIVE_CONTENTS_AREA_SENSITIVE))
|
||||
|
||||
if(!arrived.important_recursive_contents?[RECURSIVE_CONTENTS_AREA_SENSITIVE])
|
||||
return
|
||||
for(var/atom/movable/recipient as anything in arrived.important_recursive_contents[RECURSIVE_CONTENTS_AREA_SENSITIVE])
|
||||
SEND_SIGNAL(recipient, COMSIG_ENTER_AREA, src)
|
||||
@@ -419,7 +420,18 @@ GLOBAL_LIST_EMPTY(teleportlocs)
|
||||
if(L.client?.prefs.toggles & SOUND_SHIP_AMBIENCE)
|
||||
SEND_SOUND(L, sound('sound/ambience/shipambience.ogg', repeat = 1, wait = 0, volume = 35, channel = CHANNEL_BUZZ))
|
||||
|
||||
/**
|
||||
* Called when an atom exits an area
|
||||
*
|
||||
* Sends signals COMSIG_AREA_EXITED and COMSIG_EXIT_AREA (to a list of atoms)
|
||||
*/
|
||||
/area/Exited(atom/movable/gone, direction)
|
||||
SEND_SIGNAL(src, COMSIG_AREA_EXITED, gone, direction)
|
||||
|
||||
if(!gone.important_recursive_contents?[RECURSIVE_CONTENTS_AREA_SENSITIVE])
|
||||
return
|
||||
for(var/atom/movable/recipient as anything in gone.important_recursive_contents[RECURSIVE_CONTENTS_AREA_SENSITIVE])
|
||||
SEND_SIGNAL(recipient, COMSIG_EXIT_AREA, src)
|
||||
|
||||
///Divides total beauty in the room by roomsize to allow us to get an average beauty per tile.
|
||||
/area/proc/update_beauty()
|
||||
@@ -431,20 +443,6 @@ GLOBAL_LIST_EMPTY(teleportlocs)
|
||||
return FALSE //Too big
|
||||
beauty = totalbeauty / areasize
|
||||
|
||||
|
||||
/**
|
||||
* Called when an atom exits an area
|
||||
*
|
||||
* Sends signals COMSIG_AREA_EXITED and COMSIG_EXIT_AREA (to a list of atoms)
|
||||
*/
|
||||
/area/Exited(atom/movable/gone, direction)
|
||||
SEND_SIGNAL(src, COMSIG_AREA_EXITED, gone, direction)
|
||||
if(!LAZYACCESS(gone.important_recursive_contents, RECURSIVE_CONTENTS_AREA_SENSITIVE))
|
||||
return
|
||||
for(var/atom/movable/recipient as anything in gone.important_recursive_contents[RECURSIVE_CONTENTS_AREA_SENSITIVE])
|
||||
SEND_SIGNAL(recipient, COMSIG_EXIT_AREA, src)
|
||||
|
||||
|
||||
/**
|
||||
* Setup an area (with the given name)
|
||||
*
|
||||
|
||||
@@ -1781,7 +1781,9 @@
|
||||
* Returns true if this atom has gravity for the passed in turf
|
||||
*
|
||||
* Sends signals [COMSIG_ATOM_HAS_GRAVITY] and [COMSIG_TURF_HAS_GRAVITY], both can force gravity with
|
||||
* the forced gravity var
|
||||
* the forced gravity var.
|
||||
*
|
||||
* micro-optimized to hell because this proc is very hot, being called several times per movement every movement.
|
||||
*
|
||||
* Gravity situations:
|
||||
* * No gravity if you're not in a turf
|
||||
@@ -1792,39 +1794,28 @@
|
||||
* * otherwise no gravity
|
||||
*/
|
||||
/atom/proc/has_gravity(turf/gravity_turf)
|
||||
if(!gravity_turf || !isturf(gravity_turf))
|
||||
if(!isturf(gravity_turf))
|
||||
gravity_turf = get_turf(src)
|
||||
|
||||
if(!gravity_turf)
|
||||
return 0
|
||||
|
||||
var/list/forced_gravity = list()
|
||||
SEND_SIGNAL(src, COMSIG_ATOM_HAS_GRAVITY, gravity_turf, forced_gravity)
|
||||
if(!forced_gravity.len)
|
||||
SEND_SIGNAL(gravity_turf, COMSIG_TURF_HAS_GRAVITY, src, forced_gravity)
|
||||
if(forced_gravity.len)
|
||||
var/max_grav = forced_gravity[1]
|
||||
for(var/i in forced_gravity)
|
||||
max_grav = max(max_grav, i)
|
||||
return max_grav
|
||||
|
||||
if(isspaceturf(gravity_turf)) // Turf never has gravity
|
||||
return 0
|
||||
if(istype(gravity_turf, /turf/open/openspace)) //openspace in a space area doesn't get gravity
|
||||
if(istype(get_area(gravity_turf), /area/space))
|
||||
if(!gravity_turf)//no gravity in nullspace
|
||||
return 0
|
||||
|
||||
var/area/turf_area = get_area(gravity_turf)
|
||||
if(turf_area.has_gravity) // Areas which always has gravity
|
||||
return turf_area.has_gravity
|
||||
else
|
||||
// There's a gravity generator on our z level
|
||||
if(GLOB.gravity_generators["[gravity_turf.z]"])
|
||||
var/max_grav = 0
|
||||
for(var/obj/machinery/gravity_generator/main/main_grav_gen as anything in GLOB.gravity_generators["[gravity_turf.z]"])
|
||||
max_grav = max(main_grav_gen.setting,max_grav)
|
||||
return max_grav
|
||||
return SSmapping.level_trait(gravity_turf.z, ZTRAIT_GRAVITY)
|
||||
//the list isnt created every time as this proc is very hot, its only accessed if anything is actually listening to the signal too
|
||||
var/static/list/forced_gravity = list()
|
||||
if(SEND_SIGNAL(src, COMSIG_ATOM_HAS_GRAVITY, gravity_turf, forced_gravity))
|
||||
if(!length(forced_gravity))
|
||||
SEND_SIGNAL(gravity_turf, COMSIG_TURF_HAS_GRAVITY, src, forced_gravity)
|
||||
|
||||
var/max_grav = 0
|
||||
for(var/i in forced_gravity)//our gravity is the strongest return forced gravity we get
|
||||
max_grav = max(max_grav, i)
|
||||
forced_gravity.Cut()
|
||||
//cut so we can reuse the list, this is ok since forced gravity movers are exceedingly rare compared to all other movement
|
||||
return max_grav
|
||||
|
||||
var/area/turf_area = gravity_turf.loc
|
||||
|
||||
return !gravity_turf.force_no_gravity && (SSmapping.gravity_by_z_level["[gravity_turf.z]"] || turf_area.has_gravity)
|
||||
|
||||
/**
|
||||
* Causes effects when the atom gets hit by a rust effect from heretics
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
glide_size = 8
|
||||
appearance_flags = TILE_BOUND|PIXEL_SCALE|LONG_GLIDE
|
||||
|
||||
///how many times a this movable had movement procs called on it since Moved() was last called
|
||||
var/move_stacks = 0
|
||||
var/last_move = null
|
||||
var/anchored = FALSE
|
||||
var/move_resist = MOVE_RESIST_DEFAULT
|
||||
@@ -36,8 +34,10 @@
|
||||
var/pass_flags = NONE
|
||||
/// If false makes [CanPass][/atom/proc/CanPass] call [CanPassThrough][/atom/movable/proc/CanPassThrough] on this type instead of using default behaviour
|
||||
var/generic_canpass = TRUE
|
||||
var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move
|
||||
var/atom/movable/moving_from_pull //attempt to resume grab after moving instead of before.
|
||||
///0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move
|
||||
var/moving_diagonally = 0
|
||||
///attempt to resume grab after moving instead of before.
|
||||
var/atom/movable/moving_from_pull
|
||||
///Holds information about any movement loops currently running/waiting to run on the movable. Lazy, will be null if nothing's going on
|
||||
var/datum/movement_packet/move_packet
|
||||
var/datum/forced_movement/force_moving = null //handled soley by forced_movement.dm
|
||||
@@ -441,8 +441,7 @@
|
||||
SEND_SIGNAL(src, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, target)
|
||||
glide_size = target
|
||||
|
||||
for(var/m in buckled_mobs)
|
||||
var/mob/buckled_mob = m
|
||||
for(var/mob/buckled_mob as anything in buckled_mobs)
|
||||
buckled_mob.set_glide_size(target)
|
||||
|
||||
/**
|
||||
@@ -452,9 +451,9 @@
|
||||
*/
|
||||
/atom/movable/proc/abstract_move(atom/new_loc)
|
||||
var/atom/old_loc = loc
|
||||
move_stacks++
|
||||
var/direction = get_dir(old_loc, new_loc)
|
||||
loc = new_loc
|
||||
Moved(old_loc)
|
||||
Moved(old_loc, direction)
|
||||
|
||||
////////////////////////////////////////
|
||||
// Here's where we rewrite how byond handles movement except slightly different
|
||||
@@ -468,7 +467,7 @@
|
||||
if(!direction)
|
||||
direction = get_dir(src, newloc)
|
||||
|
||||
if(set_dir_on_move)
|
||||
if(set_dir_on_move && dir != direction)
|
||||
setDir(direction)
|
||||
|
||||
var/is_multi_tile_object = bound_width > 32 || bound_height > 32
|
||||
@@ -508,7 +507,6 @@
|
||||
var/atom/oldloc = loc
|
||||
var/area/oldarea = get_area(oldloc)
|
||||
var/area/newarea = get_area(newloc)
|
||||
move_stacks++
|
||||
|
||||
loc = newloc
|
||||
|
||||
@@ -543,7 +541,7 @@
|
||||
return FALSE
|
||||
var/atom/oldloc = loc
|
||||
//Early override for some cases like diagonal movement
|
||||
if(glide_size_override)
|
||||
if(glide_size_override && glide_size != glide_size_override)
|
||||
set_glide_size(glide_size_override)
|
||||
|
||||
if(loc != newloc)
|
||||
@@ -597,10 +595,6 @@
|
||||
if(moving_diagonally == SECOND_DIAG_STEP)
|
||||
if(!. && set_dir_on_move)
|
||||
setDir(first_step_dir)
|
||||
else if (!inertia_moving)
|
||||
newtonian_move(direct)
|
||||
if(client_mobs_in_contents) // We're done moving, update our parallax now
|
||||
update_parallax_contents()
|
||||
moving_diagonally = 0
|
||||
return
|
||||
|
||||
@@ -631,12 +625,12 @@
|
||||
|
||||
//glide_size strangely enough can change mid movement animation and update correctly while the animation is playing
|
||||
//This means that if you don't override it late like this, it will just be set back by the movement update that's called when you move turfs.
|
||||
if(glide_size_override)
|
||||
if(glide_size_override && glide_size != glide_size_override)
|
||||
set_glide_size(glide_size_override)
|
||||
|
||||
last_move = direct
|
||||
|
||||
if(set_dir_on_move)
|
||||
if(set_dir_on_move && dir != direct)
|
||||
setDir(direct)
|
||||
if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc, direct, glide_size_override)) //movement failed due to buckled mob(s)
|
||||
. = FALSE
|
||||
@@ -661,11 +655,12 @@
|
||||
* * movement_dir is the direction the movement took place. Can be NONE if it was some sort of teleport.
|
||||
* * The forced flag indicates whether this was a forced move, which skips many checks of regular movement.
|
||||
* * The old_locs is an optional argument, in case the moved movable was present in multiple locations before the movement.
|
||||
* * momentum_change represents whether this movement is due to a "new" force if TRUE or an already "existing" force if FALSE
|
||||
**/
|
||||
/atom/movable/proc/Moved(atom/old_loc, movement_dir, forced = FALSE, list/old_locs)
|
||||
/atom/movable/proc/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
|
||||
if (!inertia_moving)
|
||||
if (!inertia_moving && momentum_change)
|
||||
newtonian_move(movement_dir)
|
||||
// If we ain't moving diagonally right now, update our parallax
|
||||
// We don't do this all the time because diag movements should trigger one call to this, not two
|
||||
@@ -673,14 +668,12 @@
|
||||
if (!moving_diagonally && client_mobs_in_contents)
|
||||
update_parallax_contents()
|
||||
|
||||
move_stacks--
|
||||
if(move_stacks > 0) //we want only the first Moved() call in the stack to send this signal, all the other ones have an incorrect old_loc
|
||||
return
|
||||
if(move_stacks < 0)
|
||||
stack_trace("move_stacks is negative in Moved()!")
|
||||
move_stacks = 0 //setting it to 0 so that we dont get every movable with negative move_stacks runtiming on every movement
|
||||
SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, old_loc, movement_dir, forced, old_locs, momentum_change)
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, old_loc, movement_dir, forced, old_locs)
|
||||
if(old_loc)
|
||||
SEND_SIGNAL(old_loc, COMSIG_ATOM_ABSTRACT_EXITED, src, movement_dir)
|
||||
if(loc)
|
||||
SEND_SIGNAL(loc, COMSIG_ATOM_ABSTRACT_ENTERED, src, old_loc, old_locs)
|
||||
|
||||
var/turf/old_turf = get_turf(old_loc)
|
||||
var/turf/new_turf = get_turf(src)
|
||||
@@ -843,7 +836,6 @@
|
||||
///allows this movable to know when it has "entered" another area no matter how many movable atoms its stuffed into, uses important_recursive_contents
|
||||
/atom/movable/proc/become_area_sensitive(trait_source = TRAIT_GENERIC)
|
||||
if(!HAS_TRAIT(src, TRAIT_AREA_SENSITIVE))
|
||||
//RegisterSignal(src, SIGNAL_REMOVETRAIT(TRAIT_AREA_SENSITIVE), .proc/on_area_sensitive_trait_loss)
|
||||
for(var/atom/movable/location as anything in get_nested_locs(src) + src)
|
||||
LAZYADDASSOCLIST(location.important_recursive_contents, RECURSIVE_CONTENTS_AREA_SENSITIVE, src)
|
||||
ADD_TRAIT(src, TRAIT_AREA_SENSITIVE, trait_source)
|
||||
@@ -887,6 +879,24 @@
|
||||
ASSOC_UNSETEMPTY(recursive_contents, RECURSIVE_CONTENTS_CLIENT_MOBS)
|
||||
UNSETEMPTY(movable_loc.important_recursive_contents)
|
||||
|
||||
///called when this movable becomes the parent of a storage component that is currently being viewed by a player. uses important_recursive_contents
|
||||
/atom/movable/proc/become_active_storage(datum/component/storage/component_source)
|
||||
if(!HAS_TRAIT(src, TRAIT_ACTIVE_STORAGE))
|
||||
for(var/atom/movable/location as anything in get_nested_locs(src) + src)
|
||||
LAZYADDASSOCLIST(location.important_recursive_contents, RECURSIVE_CONTENTS_ACTIVE_STORAGE, src)
|
||||
ADD_TRAIT(src, TRAIT_ACTIVE_STORAGE, component_source)
|
||||
|
||||
///called when this movable's storage component is no longer viewed by any players, unsets important_recursive_contents
|
||||
/atom/movable/proc/lose_active_storage(datum/component/storage/component_source)
|
||||
if(!HAS_TRAIT(src, TRAIT_ACTIVE_STORAGE))
|
||||
return
|
||||
REMOVE_TRAIT(src, TRAIT_ACTIVE_STORAGE, component_source)
|
||||
if(HAS_TRAIT(src, TRAIT_ACTIVE_STORAGE))
|
||||
return
|
||||
|
||||
for(var/atom/movable/location as anything in get_nested_locs(src) + src)
|
||||
LAZYREMOVEASSOC(location.important_recursive_contents, RECURSIVE_CONTENTS_ACTIVE_STORAGE, src)
|
||||
|
||||
///Sets the anchored var and returns if it was sucessfully changed or not.
|
||||
/atom/movable/proc/set_anchored(anchorvalue)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
@@ -917,12 +927,13 @@
|
||||
|
||||
/atom/movable/proc/doMove(atom/destination)
|
||||
. = FALSE
|
||||
move_stacks++
|
||||
var/atom/oldloc = loc
|
||||
var/is_multi_tile = bound_width > world.icon_size || bound_height > world.icon_size
|
||||
if(destination)
|
||||
///zMove already handles whether a pull from another movable should be broken.
|
||||
if(pulledby && !currently_z_moving)
|
||||
pulledby.stop_pulling()
|
||||
|
||||
var/same_loc = oldloc == destination
|
||||
var/area/old_area = get_area(oldloc)
|
||||
var/area/destarea = get_area(destination)
|
||||
@@ -933,23 +944,49 @@
|
||||
loc = destination
|
||||
|
||||
if(!same_loc)
|
||||
if(oldloc)
|
||||
oldloc.Exited(src, movement_dir)
|
||||
if(is_multi_tile && isturf(destination))
|
||||
var/list/new_locs = block(
|
||||
destination,
|
||||
locate(
|
||||
min(world.maxx, destination.x + ROUND_UP(bound_width / 32)),
|
||||
min(world.maxy, destination.y + ROUND_UP(bound_height / 32)),
|
||||
destination.z
|
||||
)
|
||||
)
|
||||
if(old_area && old_area != destarea)
|
||||
old_area.Exited(src, movement_dir)
|
||||
destination.Entered(src, oldloc)
|
||||
if(destarea && old_area != destarea)
|
||||
destarea.Entered(src, old_area)
|
||||
for(var/atom/left_loc as anything in locs - new_locs)
|
||||
left_loc.Exited(src, movement_dir)
|
||||
|
||||
for(var/atom/entering_loc as anything in new_locs - locs)
|
||||
entering_loc.Entered(src, movement_dir)
|
||||
|
||||
if(old_area && old_area != destarea)
|
||||
destarea.Entered(src, movement_dir)
|
||||
else
|
||||
if(oldloc)
|
||||
oldloc.Exited(src, movement_dir)
|
||||
if(old_area && old_area != destarea)
|
||||
old_area.Exited(src, movement_dir)
|
||||
destination.Entered(src, oldloc)
|
||||
if(destarea && old_area != destarea)
|
||||
destarea.Entered(src, old_area)
|
||||
|
||||
. = TRUE
|
||||
|
||||
//If no destination, move the atom into nullspace (don't do this unless you know what you're doing)
|
||||
else
|
||||
. = TRUE
|
||||
loc = null
|
||||
|
||||
if (oldloc)
|
||||
loc = null
|
||||
var/area/old_area = get_area(oldloc)
|
||||
oldloc.Exited(src, NONE)
|
||||
if(is_multi_tile && isturf(oldloc))
|
||||
for(var/atom/old_loc as anything in locs)
|
||||
old_loc.Exited(src, NONE)
|
||||
else
|
||||
oldloc.Exited(src, NONE)
|
||||
|
||||
if(old_area)
|
||||
old_area.Exited(src, NONE)
|
||||
|
||||
@@ -975,7 +1012,7 @@
|
||||
* Called whenever an object moves and by mobs when they attempt to move themselves through space
|
||||
* And when an object or action applies a force on src, see [newtonian_move][/atom/movable/proc/newtonian_move]
|
||||
*
|
||||
* Return 0 to have src start/keep drifting in a no-grav area and 1 to stop/not start drifting
|
||||
* Return FALSE to have src start/keep drifting in a no-grav area and TRUE to stop/not start drifting
|
||||
*
|
||||
* Mobs should return 1 if they should be able to move of their own volition, see [/client/proc/Move]
|
||||
*
|
||||
@@ -984,10 +1021,10 @@
|
||||
* * continuous_move - If this check is coming from something in the context of already drifting
|
||||
*/
|
||||
/atom/movable/proc/Process_Spacemove(movement_dir = 0, continuous_move = FALSE)
|
||||
if(SEND_SIGNAL(src, COMSIG_MOVABLE_SPACEMOVE, movement_dir, continuous_move) & COMSIG_MOVABLE_STOP_SPACEMOVE)
|
||||
if(has_gravity())
|
||||
return TRUE
|
||||
|
||||
if(has_gravity(src))
|
||||
if(SEND_SIGNAL(src, COMSIG_MOVABLE_SPACEMOVE, movement_dir, continuous_move) & COMSIG_MOVABLE_STOP_SPACEMOVE)
|
||||
return TRUE
|
||||
|
||||
if(pulledby && (pulledby.pulledby != src || moving_from_pull))
|
||||
@@ -1393,7 +1430,6 @@
|
||||
log_admin("[key_name(usr)] has added deadchat control to [src]")
|
||||
message_admins(span_notice("[key_name(usr)] has added deadchat control to [src]"))
|
||||
|
||||
|
||||
/**
|
||||
* A wrapper for setDir that should only be able to fail by living mobs.
|
||||
*
|
||||
|
||||
@@ -458,7 +458,7 @@
|
||||
continue
|
||||
if(!(M in rangers))
|
||||
rangers[M] = TRUE
|
||||
M.playsound_local(get_turf(M), null, volume, channel = CHANNEL_JUKEBOX, S = song_played, use_reverb = FALSE)
|
||||
M.playsound_local(get_turf(M), null, volume, channel = CHANNEL_JUKEBOX, sound_to_use = song_played, use_reverb = FALSE)
|
||||
for(var/mob/L in rangers)
|
||||
if(get_dist(src,L) > 10)
|
||||
rangers -= L
|
||||
|
||||
@@ -104,13 +104,6 @@
|
||||
span_danger("You [damage_verb] [src] with [attacking_item][damage ? "." : ", without leaving a mark!"]"), null, COMBAT_MESSAGE_RANGE)
|
||||
log_combat(user, src, "attacked", attacking_item)
|
||||
|
||||
/obj/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE)
|
||||
. = ..()
|
||||
if(obj_flags & FROZEN)
|
||||
visible_message(span_danger("[src] shatters into a million pieces!"))
|
||||
qdel(src)
|
||||
|
||||
|
||||
/obj/assume_air(datum/gas_mixture/giver)
|
||||
if(loc)
|
||||
return loc.assume_air(giver)
|
||||
|
||||
@@ -1,620 +0,0 @@
|
||||
|
||||
//Booleans in arguments are confusing, so I made them defines.
|
||||
#define LOCKED 1
|
||||
#define UNLOCKED 0
|
||||
|
||||
///Collect and command
|
||||
/datum/lift_master
|
||||
var/list/lift_platforms
|
||||
/// Typepath list of what to ignore smashing through, controls all lifts
|
||||
var/list/ignored_smashthroughs = list(
|
||||
/obj/machinery/power/supermatter_crystal,
|
||||
/obj/structure/holosign
|
||||
)
|
||||
|
||||
/datum/lift_master/New(obj/structure/industrial_lift/lift_platform)
|
||||
Rebuild_lift_plaform(lift_platform)
|
||||
ignored_smashthroughs = typecacheof(ignored_smashthroughs)
|
||||
|
||||
/datum/lift_master/Destroy()
|
||||
for(var/l in lift_platforms)
|
||||
var/obj/structure/industrial_lift/lift_platform = l
|
||||
lift_platform.lift_master_datum = null
|
||||
lift_platforms = null
|
||||
return ..()
|
||||
|
||||
|
||||
/datum/lift_master/proc/add_lift_platforms(obj/structure/industrial_lift/new_lift_platform)
|
||||
if(new_lift_platform in lift_platforms)
|
||||
return
|
||||
new_lift_platform.lift_master_datum = src
|
||||
LAZYADD(lift_platforms, new_lift_platform)
|
||||
RegisterSignal(new_lift_platform, COMSIG_PARENT_QDELETING, .proc/remove_lift_platforms)
|
||||
|
||||
/datum/lift_master/proc/remove_lift_platforms(obj/structure/industrial_lift/old_lift_platform)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!(old_lift_platform in lift_platforms))
|
||||
return
|
||||
old_lift_platform.lift_master_datum = null
|
||||
LAZYREMOVE(lift_platforms, old_lift_platform)
|
||||
UnregisterSignal(old_lift_platform, COMSIG_PARENT_QDELETING)
|
||||
|
||||
///Collect all bordered platforms
|
||||
/datum/lift_master/proc/Rebuild_lift_plaform(obj/structure/industrial_lift/base_lift_platform)
|
||||
add_lift_platforms(base_lift_platform)
|
||||
var/list/possible_expansions = list(base_lift_platform)
|
||||
while(possible_expansions.len)
|
||||
for(var/b in possible_expansions)
|
||||
var/obj/structure/industrial_lift/borderline = b
|
||||
var/list/result = borderline.lift_platform_expansion(src)
|
||||
if(length(result))
|
||||
for(var/p in result)
|
||||
if(lift_platforms.Find(p))
|
||||
continue
|
||||
var/obj/structure/industrial_lift/lift_platform = p
|
||||
add_lift_platforms(lift_platform)
|
||||
possible_expansions |= lift_platform
|
||||
possible_expansions -= borderline
|
||||
|
||||
/**
|
||||
* Moves the lift UP or DOWN, this is what users invoke with their hand.
|
||||
* This is a SAFE proc, ensuring every part of the lift moves SANELY.
|
||||
* It also locks controls for the (miniscule) duration of the movement, so the elevator cannot be broken by spamming.
|
||||
* Arguments:
|
||||
* going - UP or DOWN directions, where the lift should go. Keep in mind by this point checks of whether it should go up or down have already been done.
|
||||
* user - Whomever made the lift movement.
|
||||
*/
|
||||
/datum/lift_master/proc/MoveLift(going, mob/user)
|
||||
set_controls(LOCKED)
|
||||
for(var/p in lift_platforms)
|
||||
var/obj/structure/industrial_lift/lift_platform = p
|
||||
lift_platform.travel(going)
|
||||
set_controls(UNLOCKED)
|
||||
|
||||
/**
|
||||
* Moves the lift, this is what users invoke with their hand.
|
||||
* This is a SAFE proc, ensuring every part of the lift moves SANELY.
|
||||
* It also locks controls for the (miniscule) duration of the movement, so the elevator cannot be broken by spamming.
|
||||
*/
|
||||
/datum/lift_master/proc/MoveLiftHorizontal(going, z, gliding_amount = 8)
|
||||
var/max_x = 1
|
||||
var/max_y = 1
|
||||
var/min_x = world.maxx
|
||||
var/min_y = world.maxy
|
||||
|
||||
|
||||
set_controls(LOCKED)
|
||||
for(var/p in lift_platforms)
|
||||
var/obj/structure/industrial_lift/lift_platform = p
|
||||
max_x = max(max_x, lift_platform.x)
|
||||
max_y = max(max_y, lift_platform.y)
|
||||
min_x = min(min_x, lift_platform.x)
|
||||
min_y = min(min_y, lift_platform.y)
|
||||
|
||||
//This must be safe way to border tile to tile move of bordered platforms, that excludes platform overlapping.
|
||||
if( going & WEST )
|
||||
//Go along the X axis from min to max, from left to right
|
||||
for(var/x in min_x to max_x)
|
||||
if( going & NORTH )
|
||||
//Go along the Y axis from max to min, from up to down
|
||||
for(var/y in max_y to min_y step -1)
|
||||
var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z))
|
||||
lift_platform?.travel(going, gliding_amount)
|
||||
else
|
||||
//Go along the Y axis from min to max, from down to up
|
||||
for(var/y in min_y to max_y)
|
||||
var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z))
|
||||
lift_platform?.travel(going, gliding_amount)
|
||||
else
|
||||
//Go along the X axis from max to min, from right to left
|
||||
for(var/x in max_x to min_x step -1)
|
||||
if( going & NORTH )
|
||||
//Go along the Y axis from max to min, from up to down
|
||||
for(var/y in max_y to min_y step -1)
|
||||
var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z))
|
||||
lift_platform?.travel(going, gliding_amount)
|
||||
else
|
||||
//Go along the Y axis from min to max, from down to up
|
||||
for(var/y in min_y to max_y)
|
||||
var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z))
|
||||
lift_platform?.travel(going, gliding_amount)
|
||||
set_controls(UNLOCKED)
|
||||
|
||||
///Check destination turfs
|
||||
/datum/lift_master/proc/Check_lift_move(check_dir)
|
||||
for(var/l in lift_platforms)
|
||||
var/obj/structure/industrial_lift/lift_platform = l
|
||||
var/turf/T = get_step_multiz(lift_platform, check_dir)
|
||||
if(!T)//the edges of multi-z maps
|
||||
return FALSE
|
||||
if(check_dir == UP && !istype(T, /turf/open/openspace)) // We don't want to go through the ceiling!
|
||||
return FALSE
|
||||
if(check_dir == DOWN && !istype(get_turf(lift_platform), /turf/open/openspace)) // No going through the floor!
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Sets all lift parts's controls_locked variable. Used to prevent moving mid movement, or cooldowns.
|
||||
*/
|
||||
/datum/lift_master/proc/set_controls(state)
|
||||
for(var/l in lift_platforms)
|
||||
var/obj/structure/industrial_lift/lift_platform = l
|
||||
lift_platform.controls_locked = state
|
||||
|
||||
GLOBAL_LIST_EMPTY(lifts)
|
||||
/obj/structure/industrial_lift
|
||||
name = "lift platform"
|
||||
desc = "A lightweight lift platform. It moves up and down."
|
||||
icon = 'icons/obj/smooth_structures/catwalk.dmi'
|
||||
icon_state = "catwalk-0"
|
||||
base_icon_state = "catwalk"
|
||||
density = FALSE
|
||||
anchored = TRUE
|
||||
armor = list(MELEE = 50, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, FIRE = 80, ACID = 50)
|
||||
max_integrity = 50
|
||||
layer = LATTICE_LAYER //under pipes
|
||||
plane = FLOOR_PLANE
|
||||
smoothing_flags = SMOOTH_BITMASK
|
||||
smoothing_groups = list(SMOOTH_GROUP_INDUSTRIAL_LIFT)
|
||||
canSmoothWith = list(SMOOTH_GROUP_INDUSTRIAL_LIFT)
|
||||
obj_flags = CAN_BE_HIT | BLOCK_Z_OUT_DOWN
|
||||
|
||||
var/id = null //ONLY SET THIS TO ONE OF THE LIFT'S PARTS. THEY'RE CONNECTED! ONLY ONE NEEDS THE SIGNAL!
|
||||
var/pass_through_floors = FALSE //if true, the elevator works through floors
|
||||
var/controls_locked = FALSE //if true, the lift cannot be manually moved.
|
||||
var/list/atom/movable/lift_load //things to move
|
||||
var/datum/lift_master/lift_master_datum //control from
|
||||
|
||||
/obj/structure/industrial_lift/Initialize(mapload)
|
||||
. = ..()
|
||||
GLOB.lifts.Add(src)
|
||||
var/static/list/loc_connections = list(
|
||||
COMSIG_ATOM_EXITED =.proc/UncrossedRemoveItemFromLift,
|
||||
COMSIG_ATOM_ENTERED = .proc/AddItemOnLift,
|
||||
COMSIG_ATOM_INITIALIZED_ON = .proc/AddItemOnLift,
|
||||
)
|
||||
AddElement(/datum/element/connect_loc, loc_connections)
|
||||
RegisterSignal(src, COMSIG_MOVABLE_BUMP, .proc/GracefullyBreak)
|
||||
|
||||
if(!lift_master_datum)
|
||||
lift_master_datum = new(src)
|
||||
|
||||
|
||||
/obj/structure/industrial_lift/proc/UncrossedRemoveItemFromLift(datum/source, atom/movable/gone, direction)
|
||||
SIGNAL_HANDLER
|
||||
RemoveItemFromLift(gone)
|
||||
|
||||
/obj/structure/industrial_lift/proc/RemoveItemFromLift(atom/movable/potential_rider)
|
||||
SIGNAL_HANDLER
|
||||
if(!(potential_rider in lift_load))
|
||||
return
|
||||
if(isliving(potential_rider) && HAS_TRAIT(potential_rider, TRAIT_CANNOT_BE_UNBUCKLED))
|
||||
REMOVE_TRAIT(potential_rider, TRAIT_CANNOT_BE_UNBUCKLED, BUCKLED_TRAIT)
|
||||
LAZYREMOVE(lift_load, potential_rider)
|
||||
UnregisterSignal(potential_rider, COMSIG_PARENT_QDELETING)
|
||||
|
||||
/obj/structure/industrial_lift/proc/AddItemOnLift(datum/source, atom/movable/AM)
|
||||
SIGNAL_HANDLER
|
||||
if(istype(AM, /obj/structure/fluff/tram_rail) || AM.invisibility == INVISIBILITY_ABSTRACT) //prevents the tram from stealing things like landmarks
|
||||
return
|
||||
if(AM in lift_load)
|
||||
return
|
||||
if(isliving(AM) && !HAS_TRAIT(AM, TRAIT_CANNOT_BE_UNBUCKLED))
|
||||
ADD_TRAIT(AM, TRAIT_CANNOT_BE_UNBUCKLED, BUCKLED_TRAIT)
|
||||
LAZYADD(lift_load, AM)
|
||||
RegisterSignal(AM, COMSIG_PARENT_QDELETING, .proc/RemoveItemFromLift)
|
||||
|
||||
/**
|
||||
* Signal for when the tram runs into a field of which it cannot go through.
|
||||
* Stops the train's travel fully, sends a message, and destroys the train.
|
||||
* Arguments:
|
||||
* bumped_atom - The atom this tram bumped into
|
||||
*/
|
||||
/obj/structure/industrial_lift/proc/GracefullyBreak(atom/bumped_atom)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(istype(bumped_atom, /obj/machinery/field))
|
||||
return
|
||||
|
||||
bumped_atom.visible_message(span_userdanger("[src] crashes into the field violently!"))
|
||||
for(var/obj/structure/industrial_lift/tram/tram_part as anything in lift_master_datum.lift_platforms)
|
||||
tram_part.travel_distance = 0
|
||||
tram_part.set_travelling(FALSE)
|
||||
if(prob(15) || locate(/mob/living) in tram_part.lift_load) //always go boom on people on the track
|
||||
explosion(tram_part, devastation_range = rand(0, 1), heavy_impact_range = 2, light_impact_range = 3) //50% chance of gib
|
||||
qdel(tram_part)
|
||||
|
||||
/obj/structure/industrial_lift/proc/lift_platform_expansion(datum/lift_master/lift_master_datum)
|
||||
. = list()
|
||||
for(var/direction in GLOB.cardinals)
|
||||
var/obj/structure/industrial_lift/neighbor = locate() in get_step(src, direction)
|
||||
if(!neighbor)
|
||||
continue
|
||||
. += neighbor
|
||||
|
||||
/obj/structure/industrial_lift/proc/travel(going, gliding_amount = 8)
|
||||
var/list/things_to_move = LAZYCOPY(lift_load)
|
||||
var/turf/destination
|
||||
if(!isturf(going))
|
||||
destination = get_step_multiz(src, going)
|
||||
else
|
||||
destination = going
|
||||
///handles any special interactions objects could have with the lift/tram, handled on the item itself
|
||||
SEND_SIGNAL(destination, COMSIG_TURF_INDUSTRIAL_LIFT_ENTER, things_to_move)
|
||||
|
||||
if(istype(destination, /turf/closed/wall))
|
||||
var/turf/closed/wall/C = destination
|
||||
do_sparks(2, FALSE, C)
|
||||
C.dismantle_wall(devastated = TRUE)
|
||||
for(var/mob/M in urange(8, src))
|
||||
shake_camera(M, 2, 3)
|
||||
playsound(C, 'sound/effects/meteorimpact.ogg', 100, TRUE)
|
||||
|
||||
if(going == DOWN)
|
||||
for(var/mob/living/crushed in destination.contents)
|
||||
to_chat(crushed, span_userdanger("You are crushed by [src]!"))
|
||||
crushed.gib(FALSE,FALSE,FALSE)//the nicest kind of gibbing, keeping everything intact.
|
||||
|
||||
else if(going != UP) //can't really crush something upwards
|
||||
var/atom/throw_target = get_edge_target_turf(src, turn(going, pick(45, -45))) //finds a spot to throw the victim at for daring to be hit by a tram
|
||||
for(var/obj/structure/victim_structure in destination.contents)
|
||||
if(QDELETED(victim_structure))
|
||||
continue
|
||||
if(!is_type_in_typecache(victim_structure, lift_master_datum.ignored_smashthroughs) && victim_structure.layer >= LOW_OBJ_LAYER)
|
||||
if(victim_structure.anchored && initial(victim_structure.anchored) == TRUE)
|
||||
visible_message(span_danger("[src] smashes through [victim_structure]!"))
|
||||
victim_structure.deconstruct(FALSE)
|
||||
else
|
||||
visible_message(span_danger("[src] violently rams [victim_structure] out of the way!"))
|
||||
victim_structure.anchored = FALSE
|
||||
victim_structure.take_damage(rand(20, 25))
|
||||
victim_structure.throw_at(throw_target, 200, 4)
|
||||
for(var/obj/machinery/victim_machine in destination.contents)
|
||||
if(QDELETED(victim_machine))
|
||||
continue
|
||||
if(is_type_in_typecache(victim_machine, lift_master_datum.ignored_smashthroughs))
|
||||
continue
|
||||
if(istype(victim_machine, /obj/machinery/field)) //graceful break handles this scenario
|
||||
continue
|
||||
if(victim_machine.layer >= LOW_OBJ_LAYER) //avoids stuff that is probably flush with the ground
|
||||
playsound(src, 'sound/effects/bang.ogg', 50, TRUE)
|
||||
visible_message(span_danger("[src] smashes through [victim_machine]!"))
|
||||
qdel(victim_machine)
|
||||
|
||||
for(var/mob/living/collided in destination.contents)
|
||||
if(is_type_in_typecache(collided, lift_master_datum.ignored_smashthroughs))
|
||||
continue
|
||||
to_chat(collided, span_userdanger("[src] collides into you!"))
|
||||
playsound(src, 'sound/effects/splat.ogg', 50, TRUE)
|
||||
var/damage = rand(5, 10)
|
||||
collided.apply_damage(2 * damage, BRUTE, BODY_ZONE_HEAD)
|
||||
collided.apply_damage(2 * damage, BRUTE, BODY_ZONE_CHEST)
|
||||
collided.apply_damage(0.5 * damage, BRUTE, BODY_ZONE_L_LEG)
|
||||
collided.apply_damage(0.5 * damage, BRUTE, BODY_ZONE_R_LEG)
|
||||
collided.apply_damage(0.5 * damage, BRUTE, BODY_ZONE_L_ARM)
|
||||
collided.apply_damage(0.5 * damage, BRUTE, BODY_ZONE_R_ARM)
|
||||
|
||||
if(QDELETED(collided)) //in case it was a mob that dels on death
|
||||
continue
|
||||
var/turf/T = get_turf(src)
|
||||
T.add_mob_blood(collided)
|
||||
|
||||
collided.throw_at()
|
||||
//if going EAST, will turn to the NORTHEAST or SOUTHEAST and throw the ran over guy away
|
||||
var/datum/callback/land_slam = new(collided, /mob/living/.proc/tram_slam_land)
|
||||
collided.throw_at(throw_target, 200, 4, callback = land_slam)
|
||||
set_glide_size(gliding_amount)
|
||||
forceMove(destination)
|
||||
for(var/atom/movable/thing as anything in things_to_move)
|
||||
thing.set_glide_size(gliding_amount) //matches the glide size of the moving platform to stop them from jittering on it.
|
||||
thing.forceMove(destination)
|
||||
|
||||
/obj/structure/industrial_lift/proc/use(mob/living/user)
|
||||
if(!isliving(user) || !in_range(src, user) || user.combat_mode)
|
||||
return
|
||||
|
||||
var/list/tool_list = list()
|
||||
if(lift_master_datum.Check_lift_move(UP))
|
||||
tool_list["Up"] = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH)
|
||||
if(lift_master_datum.Check_lift_move(DOWN))
|
||||
tool_list["Down"] = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH)
|
||||
if(!length(tool_list))
|
||||
to_chat(user, span_warning("[src] doesn't seem to able to move anywhere!"))
|
||||
add_fingerprint(user)
|
||||
return
|
||||
if(controls_locked)
|
||||
to_chat(user, span_warning("[src] has its controls locked! It must already be trying to do something!"))
|
||||
add_fingerprint(user)
|
||||
return
|
||||
var/result = show_radial_menu(user, src, tool_list, custom_check = CALLBACK(src, .proc/check_menu, user, src.loc), require_near = TRUE, tooltips = TRUE)
|
||||
if(!isliving(user) || !in_range(src, user) || user.combat_mode)
|
||||
return //nice try
|
||||
switch(result)
|
||||
if("Up")
|
||||
// We have to make sure that they don't do illegal actions by not having their radial menu refresh from someone else moving the lift.
|
||||
if(!lift_master_datum.Check_lift_move(UP))
|
||||
to_chat(user, span_warning("[src] doesn't seem to able to move up!"))
|
||||
add_fingerprint(user)
|
||||
return
|
||||
lift_master_datum.MoveLift(UP, user)
|
||||
show_fluff_message(TRUE, user)
|
||||
use(user)
|
||||
if("Down")
|
||||
if(!lift_master_datum.Check_lift_move(DOWN))
|
||||
to_chat(user, span_warning("[src] doesn't seem to able to move down!"))
|
||||
add_fingerprint(user)
|
||||
return
|
||||
lift_master_datum.MoveLift(DOWN, user)
|
||||
show_fluff_message(FALSE, user)
|
||||
use(user)
|
||||
if("Cancel")
|
||||
return
|
||||
add_fingerprint(user)
|
||||
|
||||
/**
|
||||
* Proc to ensure that the radial menu closes when it should.
|
||||
* Arguments:
|
||||
* * user - The person that opened the menu.
|
||||
* * starting_loc - The location of the lift when the menu was opened, used to prevent the menu from being interacted with after the lift was moved by someone else.
|
||||
*
|
||||
* Returns:
|
||||
* * boolean, FALSE if the menu should be closed, TRUE if the menu is clear to stay opened.
|
||||
*/
|
||||
/obj/structure/industrial_lift/proc/check_menu(mob/user, starting_loc)
|
||||
if(user.incapacitated() || !user.Adjacent(src) || starting_loc != src.loc)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/structure/industrial_lift/attack_hand(mob/user, list/modifiers)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
use(user)
|
||||
|
||||
//ai probably shouldn't get to use lifts but they sure are great for admins to crush people with
|
||||
/obj/structure/industrial_lift/attack_ghost(mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if(isAdminGhostAI(user))
|
||||
use(user)
|
||||
|
||||
/obj/structure/industrial_lift/attack_paw(mob/user, list/modifiers)
|
||||
return use(user)
|
||||
|
||||
/obj/structure/industrial_lift/attackby(obj/item/W, mob/user, params)
|
||||
return use(user)
|
||||
|
||||
/obj/structure/industrial_lift/attack_robot(mob/living/silicon/robot/R)
|
||||
if(R.Adjacent(src))
|
||||
return use(R)
|
||||
|
||||
/**
|
||||
* Shows a message indicating that the lift has moved up or down.
|
||||
* Arguments:
|
||||
* * going_up - Boolean on whether or not we're going up, to adjust the message appropriately.
|
||||
* * user - The mob that caused the lift to move, for the visible message.
|
||||
*/
|
||||
/obj/structure/industrial_lift/proc/show_fluff_message(going_up, mob/user)
|
||||
if(going_up)
|
||||
user.visible_message(span_notice("[user] moves the lift upwards."), span_notice("You move the lift upwards."))
|
||||
else
|
||||
user.visible_message(span_notice("[user] moves the lift downwards."), span_notice("You move the lift downwards."))
|
||||
|
||||
/obj/structure/industrial_lift/Destroy()
|
||||
GLOB.lifts.Remove(src)
|
||||
QDEL_NULL(lift_master_datum)
|
||||
var/list/border_lift_platforms = lift_platform_expansion()
|
||||
moveToNullspace()
|
||||
for(var/border_lift in border_lift_platforms)
|
||||
lift_master_datum = new(border_lift)
|
||||
return ..()
|
||||
|
||||
/obj/structure/industrial_lift/debug
|
||||
name = "transport platform"
|
||||
desc = "A lightweight platform. It moves in any direction, except up and down."
|
||||
color = "#5286b9ff"
|
||||
|
||||
/obj/structure/industrial_lift/debug/use(mob/user)
|
||||
if (!in_range(src, user))
|
||||
return
|
||||
//NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST
|
||||
var/static/list/tool_list = list(
|
||||
"NORTH" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH),
|
||||
"NORTHEAST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH),
|
||||
"EAST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = EAST),
|
||||
"SOUTHEAST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = EAST),
|
||||
"SOUTH" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH),
|
||||
"SOUTHWEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH),
|
||||
"WEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = WEST),
|
||||
"NORTHWEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = WEST)
|
||||
)
|
||||
|
||||
var/result = show_radial_menu(user, src, tool_list, custom_check = CALLBACK(src, .proc/check_menu, user, loc), require_near = TRUE, tooltips = FALSE)
|
||||
if (!in_range(src, user))
|
||||
return // nice try
|
||||
|
||||
switch(result)
|
||||
if("NORTH")
|
||||
lift_master_datum.MoveLiftHorizontal(NORTH, z)
|
||||
use(user)
|
||||
if("NORTHEAST")
|
||||
lift_master_datum.MoveLiftHorizontal(NORTHEAST, z)
|
||||
use(user)
|
||||
if("EAST")
|
||||
lift_master_datum.MoveLiftHorizontal(EAST, z)
|
||||
use(user)
|
||||
if("SOUTHEAST")
|
||||
lift_master_datum.MoveLiftHorizontal(SOUTHEAST, z)
|
||||
use(user)
|
||||
if("SOUTH")
|
||||
lift_master_datum.MoveLiftHorizontal(SOUTH, z)
|
||||
use(user)
|
||||
if("SOUTHWEST")
|
||||
lift_master_datum.MoveLiftHorizontal(SOUTHWEST, z)
|
||||
use(user)
|
||||
if("WEST")
|
||||
lift_master_datum.MoveLiftHorizontal(WEST, z)
|
||||
use(user)
|
||||
if("NORTHWEST")
|
||||
lift_master_datum.MoveLiftHorizontal(NORTHWEST, z)
|
||||
use(user)
|
||||
if("Cancel")
|
||||
return
|
||||
|
||||
add_fingerprint(user)
|
||||
|
||||
/obj/structure/industrial_lift/tram
|
||||
name = "tram"
|
||||
desc = "A tram for traversing the station."
|
||||
icon = 'icons/turf/floors.dmi'
|
||||
icon_state = "titanium_yellow"
|
||||
base_icon_state = null
|
||||
smoothing_flags = NONE
|
||||
smoothing_groups = null
|
||||
canSmoothWith = null
|
||||
//kind of a centerpiece of the station, so pretty tough to destroy
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
/// Set by the tram control console in late initialize
|
||||
var/travelling = FALSE
|
||||
var/travel_distance = 0
|
||||
/// For finding the landmark initially - should be the exact same as the landmark's destination id.
|
||||
var/initial_id = "middle_part"
|
||||
var/obj/effect/landmark/tram/from_where
|
||||
var/travel_direction
|
||||
|
||||
GLOBAL_LIST_EMPTY_TYPED(central_trams, /obj/structure/industrial_lift/tram/central)
|
||||
|
||||
/obj/structure/industrial_lift/tram/Initialize(mapload)
|
||||
. = ..()
|
||||
return INITIALIZE_HINT_LATELOAD
|
||||
|
||||
/obj/structure/industrial_lift/tram/central
|
||||
var/tram_id = "tram_station"
|
||||
|
||||
/obj/structure/industrial_lift/tram/central/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!SStramprocess.can_fire)
|
||||
SStramprocess.can_fire = TRUE
|
||||
GLOB.central_trams += src
|
||||
|
||||
/obj/structure/industrial_lift/tram/central/Destroy()
|
||||
GLOB.central_trams -= src
|
||||
return ..()
|
||||
|
||||
/obj/structure/industrial_lift/tram/LateInitialize()
|
||||
. = ..()
|
||||
find_our_location()
|
||||
|
||||
|
||||
/**
|
||||
* Finds the location of the tram
|
||||
*
|
||||
* The initial_id is assumed to the be the landmark the tram is built on in the map
|
||||
* and where the tram will set itself to be on roundstart.
|
||||
* The central tram piece goes further into this by actually checking the contents of the turf its on
|
||||
* for a tram landmark when it docks anywhere. This assures the tram actually knows where it is after docking,
|
||||
* even in the worst cast scenario.
|
||||
*/
|
||||
/obj/structure/industrial_lift/tram/proc/find_our_location()
|
||||
for(var/obj/effect/landmark/tram/our_location in GLOB.landmarks_list)
|
||||
if(our_location.destination_id == initial_id)
|
||||
from_where = our_location
|
||||
break
|
||||
|
||||
/obj/structure/industrial_lift/tram/proc/set_travelling(travelling)
|
||||
if (src.travelling == travelling)
|
||||
return
|
||||
|
||||
src.travelling = travelling
|
||||
SEND_SIGNAL(src, COMSIG_TRAM_SET_TRAVELLING, travelling)
|
||||
|
||||
/obj/structure/industrial_lift/tram/use(mob/user) //dont click the floor dingus we use computers now
|
||||
return
|
||||
|
||||
/obj/structure/industrial_lift/tram/process(delta_time)
|
||||
if(!travel_distance)
|
||||
addtimer(CALLBACK(src, .proc/unlock_controls), 3 SECONDS)
|
||||
return PROCESS_KILL
|
||||
else
|
||||
travel_distance--
|
||||
lift_master_datum.MoveLiftHorizontal(travel_direction, z, DELAY_TO_GLIDE_SIZE(SStramprocess.wait))
|
||||
|
||||
/**
|
||||
* Handles moving the tram
|
||||
*
|
||||
* Tells the individual tram parts where to actually go and has an extra safety check
|
||||
* incase multiple inputs get through, preventing conflicting directions and the tram
|
||||
* literally ripping itself apart. The proc handles the first move before the subsystem
|
||||
* takes over to keep moving it in process()
|
||||
*/
|
||||
/obj/structure/industrial_lift/tram/proc/tram_travel(obj/effect/landmark/tram/to_where)
|
||||
if(to_where == from_where)
|
||||
return
|
||||
|
||||
visible_message(span_notice("[src] has been called to the [to_where]!"))
|
||||
|
||||
lift_master_datum.set_controls(LOCKED)
|
||||
travel_direction = get_dir(from_where, to_where)
|
||||
travel_distance = get_dist(from_where, to_where)
|
||||
//first movement is immediate
|
||||
for(var/obj/structure/industrial_lift/tram/other_tram_part as anything in lift_master_datum.lift_platforms) //only thing everyone needs to know is the new location.
|
||||
if(other_tram_part.travelling) //wee woo wee woo there was a double action queued. damn multi tile structs
|
||||
return //we don't care to undo locked controls, though, as that will resolve itself
|
||||
SEND_SIGNAL(src, COMSIG_TRAM_TRAVEL, from_where, to_where)
|
||||
other_tram_part.set_travelling(TRUE)
|
||||
other_tram_part.from_where = to_where
|
||||
lift_master_datum.MoveLiftHorizontal(travel_direction, z, DELAY_TO_GLIDE_SIZE(SStramprocess.wait))
|
||||
travel_distance--
|
||||
|
||||
START_PROCESSING(SStramprocess, src)
|
||||
|
||||
/**
|
||||
* Handles unlocking the tram controls for use after moving
|
||||
*
|
||||
* More safety checks to make sure the tram has actually docked properly
|
||||
* at a location before users are allowed to interact with the tram console again.
|
||||
* Tram finds its location at this point before fully unlocking controls to the user.
|
||||
*/
|
||||
/obj/structure/industrial_lift/tram/proc/unlock_controls()
|
||||
visible_message(span_notice("[src]'s controls are now unlocked."))
|
||||
for(var/obj/structure/industrial_lift/tram/tram_part as anything in lift_master_datum.lift_platforms) //only thing everyone needs to know is the new location.
|
||||
tram_part.set_travelling(FALSE)
|
||||
lift_master_datum.set_controls(UNLOCKED)
|
||||
|
||||
GLOBAL_LIST_EMPTY(tram_landmarks)
|
||||
|
||||
/obj/effect/landmark/tram
|
||||
name = "tram destination" //the tram buttons will mention this.
|
||||
icon_state = "tram"
|
||||
/// The ID of that particular destination.
|
||||
var/destination_id
|
||||
/// The ID of the tram that can travel to use
|
||||
var/tram_id = "tram_station"
|
||||
/// Icons for the tgui console to list out for what is at this location
|
||||
var/list/tgui_icons = list()
|
||||
|
||||
/obj/effect/landmark/tram/Initialize(mapload)
|
||||
. = ..()
|
||||
GLOB.tram_landmarks += src
|
||||
|
||||
/obj/effect/landmark/tram/Destroy()
|
||||
GLOB.tram_landmarks -= src
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/effect/landmark/tram/left_part
|
||||
name = "West Wing"
|
||||
destination_id = "left_part"
|
||||
tgui_icons = list("Arrivals" = "plane-arrival", "Command" = "bullhorn", "Security" = "gavel")
|
||||
|
||||
/obj/effect/landmark/tram/middle_part
|
||||
name = "Central Wing"
|
||||
destination_id = "middle_part"
|
||||
tgui_icons = list("Service" = "cocktail", "Medical" = "plus", "Engineering" = "wrench")
|
||||
|
||||
/obj/effect/landmark/tram/right_part
|
||||
name = "East Wing"
|
||||
destination_id = "right_part"
|
||||
tgui_icons = list("Departures" = "plane-departure", "Cargo" = "box", "Science" = "flask")
|
||||
@@ -23,120 +23,101 @@
|
||||
)
|
||||
environment = SOUND_ENVIRONMENT_NONE //Default to none so sounds without overrides dont get reverb
|
||||
|
||||
/*! playsound
|
||||
|
||||
playsound is a proc used to play a 3D sound in a specific range. This uses SOUND_RANGE + extra_range to determine that.
|
||||
|
||||
source - Origin of sound
|
||||
soundin - Either a file, or a string that can be used to get an SFX
|
||||
vol - The volume of the sound, excluding falloff and pressure affection.
|
||||
vary - bool that determines if the sound changes pitch every time it plays
|
||||
extrarange - modifier for sound range. This gets added on top of SOUND_RANGE
|
||||
falloff_exponent - Rate of falloff for the audio. Higher means quicker drop to low volume. Should generally be over 1 to indicate a quick dive to 0 rather than a slow dive.
|
||||
frequency - playback speed of audio
|
||||
channel - The channel the sound is played at
|
||||
pressure_affected - Whether or not difference in pressure affects the sound (E.g. if you can hear in space)
|
||||
ignore_walls - Whether or not the sound can pass through walls.
|
||||
falloff_distance - Distance at which falloff begins. Sound is at peak volume (in regards to falloff) aslong as it is in this range.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* playsound is a proc used to play a 3D sound in a specific range. This uses SOUND_RANGE + extra_range to determine that.
|
||||
*
|
||||
* source - Origin of sound.
|
||||
* soundin - Either a file, or a string that can be used to get an SFX.
|
||||
* vol - The volume of the sound, excluding falloff and pressure affection.
|
||||
* vary - bool that determines if the sound changes pitch every time it plays.
|
||||
* extrarange - modifier for sound range. This gets added on top of SOUND_RANGE.
|
||||
* falloff_exponent - Rate of falloff for the audio. Higher means quicker drop to low volume. Should generally be over 1 to indicate a quick dive to 0 rather than a slow dive.
|
||||
* frequency - playback speed of audio.
|
||||
* channel - The channel the sound is played at.
|
||||
* pressure_affected - Whether or not difference in pressure affects the sound (E.g. if you can hear in space).
|
||||
* ignore_walls - Whether or not the sound can pass through walls.
|
||||
* falloff_distance - Distance at which falloff begins. Sound is at peak volume (in regards to falloff) aslong as it is in this range.
|
||||
*/
|
||||
/proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff_exponent = SOUND_FALLOFF_EXPONENT, frequency = null, channel = 0, pressure_affected = TRUE, ignore_walls = TRUE, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, use_reverb = TRUE)
|
||||
if(isarea(source))
|
||||
CRASH("playsound(): source is an area")
|
||||
|
||||
var/turf/turf_source = get_turf(source)
|
||||
|
||||
if (!turf_source)
|
||||
if (!turf_source || !soundin || !vol)
|
||||
return
|
||||
|
||||
//allocate a channel if necessary now so its the same for everyone
|
||||
channel = channel || SSsounds.random_available_channel()
|
||||
|
||||
// Looping through the player list has the added bonus of working for mobs inside containers
|
||||
var/sound/S = sound(get_sfx(soundin))
|
||||
var/maxdistance = SOUND_RANGE + extrarange
|
||||
var/source_z = turf_source.z
|
||||
var/list/listeners = SSmobs.clients_by_zlevel[source_z].Copy()
|
||||
|
||||
. = list()//output everything that successfully heard the sound
|
||||
|
||||
var/turf/above_turf = SSmapping.get_turf_above(turf_source)
|
||||
var/turf/below_turf = SSmapping.get_turf_below(turf_source)
|
||||
|
||||
if(!ignore_walls) //these sounds don't carry through walls
|
||||
listeners = listeners & hearers(maxdistance,turf_source)
|
||||
if(ignore_walls)
|
||||
|
||||
if(above_turf && istransparentturf(above_turf))
|
||||
listeners += hearers(maxdistance,above_turf)
|
||||
|
||||
if(below_turf && istransparentturf(turf_source))
|
||||
listeners += hearers(maxdistance,below_turf)
|
||||
|
||||
else
|
||||
if(above_turf && istransparentturf(above_turf))
|
||||
listeners += SSmobs.clients_by_zlevel[above_turf.z]
|
||||
|
||||
if(below_turf && istransparentturf(turf_source))
|
||||
listeners += SSmobs.clients_by_zlevel[below_turf.z]
|
||||
|
||||
for(var/mob/listening_mob as anything in listeners)
|
||||
if(get_dist(listening_mob, turf_source) <= maxdistance)
|
||||
listening_mob.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, 1, use_reverb)
|
||||
for(var/mob/listening_mob as anything in SSmobs.dead_players_by_zlevel[source_z])
|
||||
if(get_dist(listening_mob, turf_source) <= maxdistance)
|
||||
listening_mob.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, 1, use_reverb)
|
||||
for(var/mob/listening_mob as anything in listeners)
|
||||
if(get_dist(listening_mob, turf_source) <= maxdistance)
|
||||
listening_mob.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, 1, use_reverb)
|
||||
. += listening_mob
|
||||
|
||||
/*! playsound
|
||||
else //these sounds don't carry through walls
|
||||
listeners = get_hearers_in_view(maxdistance, turf_source)
|
||||
|
||||
playsound_local is a proc used to play a sound directly on a mob from a specific turf.
|
||||
This is called by playsound to send sounds to players, in which case it also gets the max_distance of that sound.
|
||||
if(above_turf && istransparentturf(above_turf))
|
||||
listeners += get_hearers_in_view(maxdistance, above_turf)
|
||||
|
||||
turf_source - Origin of sound
|
||||
soundin - Either a file, or a string that can be used to get an SFX
|
||||
vol - The volume of the sound, excluding falloff
|
||||
vary - bool that determines if the sound changes pitch every time it plays
|
||||
frequency - playback speed of audio
|
||||
falloff_exponent - Rate of falloff for the audio. Higher means quicker drop to low volume. Should generally be over 1 to indicate a quick dive to 0 rather than a slow dive.
|
||||
channel - The channel the sound is played at
|
||||
pressure_affected - Whether or not difference in pressure affects the sound (E.g. if you can hear in space)
|
||||
max_distance - The peak distance of the sound, if this is a 3D sound
|
||||
falloff_distance - Distance at which falloff begins, if this is a 3D sound
|
||||
distance_multiplier - Can be used to multiply the distance at which the sound is heard
|
||||
if(below_turf && istransparentturf(turf_source))
|
||||
listeners += get_hearers_in_view(maxdistance, below_turf)
|
||||
|
||||
*/
|
||||
for(var/mob/listening_mob in listeners | SSmobs.dead_players_by_zlevel[source_z])//observers always hear through walls
|
||||
if(get_dist(listening_mob, turf_source) <= maxdistance)
|
||||
listening_mob.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, 1, use_reverb)
|
||||
. += listening_mob
|
||||
|
||||
/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff_exponent = SOUND_FALLOFF_EXPONENT, channel = 0, pressure_affected = TRUE, sound/S, max_distance, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, distance_multiplier = 1, use_reverb = TRUE)
|
||||
/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff_exponent = SOUND_FALLOFF_EXPONENT, channel = 0, pressure_affected = TRUE, sound/sound_to_use, max_distance, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, distance_multiplier = 1, use_reverb = TRUE)
|
||||
if(!client || !can_hear())
|
||||
return
|
||||
|
||||
if(!S)
|
||||
S = sound(get_sfx(soundin))
|
||||
if(!sound_to_use)
|
||||
sound_to_use = sound(get_sfx(soundin))
|
||||
|
||||
S.wait = 0 //No queue
|
||||
S.channel = channel || SSsounds.random_available_channel()
|
||||
S.volume = vol
|
||||
sound_to_use.wait = 0 //No queue
|
||||
sound_to_use.channel = channel || SSsounds.random_available_channel()
|
||||
sound_to_use.volume = vol
|
||||
|
||||
if(vary)
|
||||
if(frequency)
|
||||
S.frequency = frequency
|
||||
sound_to_use.frequency = frequency
|
||||
else
|
||||
S.frequency = get_rand_frequency()
|
||||
sound_to_use.frequency = get_rand_frequency()
|
||||
|
||||
if(isturf(turf_source))
|
||||
var/turf/T = get_turf(src)
|
||||
var/turf/turf_loc = get_turf(src)
|
||||
|
||||
//sound volume falloff with distance
|
||||
var/distance = get_dist(T, turf_source)
|
||||
|
||||
distance *= distance_multiplier
|
||||
var/distance = get_dist(turf_loc, turf_source) * distance_multiplier
|
||||
|
||||
if(max_distance) //If theres no max_distance we're not a 3D sound, so no falloff.
|
||||
S.volume -= (max(distance - falloff_distance, 0) ** (1 / falloff_exponent)) / ((max(max_distance, distance) - falloff_distance) ** (1 / falloff_exponent)) * S.volume
|
||||
sound_to_use.volume -= (max(distance - falloff_distance, 0) ** (1 / falloff_exponent)) / ((max(max_distance, distance) - falloff_distance) ** (1 / falloff_exponent)) * sound_to_use.volume
|
||||
//https://www.desmos.com/calculator/sqdfl8ipgf
|
||||
|
||||
if(pressure_affected)
|
||||
//Atmosphere affects sound
|
||||
var/pressure_factor = 1
|
||||
var/datum/gas_mixture/hearer_env = T.return_air()
|
||||
var/datum/gas_mixture/hearer_env = turf_loc.return_air()
|
||||
var/datum/gas_mixture/source_env = turf_source.return_air()
|
||||
|
||||
if(hearer_env && source_env)
|
||||
@@ -149,35 +130,35 @@ distance_multiplier - Can be used to multiply the distance at which the sound is
|
||||
if(distance <= 1)
|
||||
pressure_factor = max(pressure_factor, 0.15) //touching the source of the sound
|
||||
|
||||
S.volume *= pressure_factor
|
||||
sound_to_use.volume *= pressure_factor
|
||||
//End Atmosphere affecting sound
|
||||
|
||||
if(S.volume <= 0)
|
||||
if(sound_to_use.volume <= 0)
|
||||
return //No sound
|
||||
|
||||
var/dx = turf_source.x - T.x // Hearing from the right/left
|
||||
S.x = dx * distance_multiplier
|
||||
var/dz = turf_source.y - T.y // Hearing from infront/behind
|
||||
S.z = dz * distance_multiplier
|
||||
var/dy = (turf_source.z - T.z) * 5 * distance_multiplier // Hearing from above / below, multiplied by 5 because we assume height is further along coords.
|
||||
S.y = dy
|
||||
var/dx = turf_source.x - turf_loc.x // Hearing from the right/left
|
||||
sound_to_use.x = dx * distance_multiplier
|
||||
var/dz = turf_source.y - turf_loc.y // Hearing from infront/behind
|
||||
sound_to_use.z = dz * distance_multiplier
|
||||
var/dy = (turf_source.z - turf_loc.z) * 5 * distance_multiplier // Hearing from above / below, multiplied by 5 because we assume height is further along coords.
|
||||
sound_to_use.y = dy
|
||||
|
||||
S.falloff = max_distance || 1 //use max_distance, else just use 1 as we are a direct sound so falloff isnt relevant.
|
||||
sound_to_use.falloff = max_distance || 1 //use max_distance, else just use 1 as we are a direct sound so falloff isnt relevant.
|
||||
|
||||
// Sounds can't have their own environment. A sound's environment will be:
|
||||
// 1. the mob's
|
||||
// 2. the area's (defaults to SOUND_ENVRIONMENT_NONE)
|
||||
if(sound_environment_override != SOUND_ENVIRONMENT_NONE)
|
||||
S.environment = sound_environment_override
|
||||
sound_to_use.environment = sound_environment_override
|
||||
else
|
||||
var/area/A = get_area(src)
|
||||
S.environment = A.sound_environment
|
||||
sound_to_use.environment = A.sound_environment
|
||||
|
||||
if(use_reverb && S.environment != SOUND_ENVIRONMENT_NONE) //We have reverb, reset our echo setting
|
||||
S.echo[3] = 0 //Room setting, 0 means normal reverb
|
||||
S.echo[4] = 0 //RoomHF setting, 0 means normal reverb.
|
||||
if(use_reverb && sound_to_use.environment != SOUND_ENVIRONMENT_NONE) //We have reverb, reset our echo setting
|
||||
sound_to_use.echo[3] = 0 //Room setting, 0 means normal reverb
|
||||
sound_to_use.echo[4] = 0 //RoomHF setting, 0 means normal reverb.
|
||||
|
||||
SEND_SOUND(src, S)
|
||||
SEND_SOUND(src, sound_to_use)
|
||||
|
||||
/proc/sound_to_playing_players(soundin, volume = 100, vary = FALSE, frequency = 0, channel = 0, pressure_affected = FALSE, sound/S)
|
||||
if(!S)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/turf/open
|
||||
plane = FLOOR_PLANE
|
||||
var/slowdown = 0 //negative for faster, positive for slower
|
||||
///negative for faster, positive for slower
|
||||
var/slowdown = 0
|
||||
|
||||
var/footstep = null
|
||||
var/barefootstep = null
|
||||
@@ -187,10 +188,9 @@
|
||||
|
||||
/turf/open/proc/freeze_turf()
|
||||
for(var/obj/I in contents)
|
||||
if(I.resistance_flags & FREEZE_PROOF)
|
||||
continue
|
||||
if(!(I.obj_flags & FROZEN))
|
||||
I.make_frozen_visual()
|
||||
if(!HAS_TRAIT(I, TRAIT_FROZEN) && !(I.obj_flags & FREEZE_PROOF))
|
||||
I.AddElement(/datum/element/frozen)
|
||||
|
||||
for(var/mob/living/L in contents)
|
||||
if(L.bodytemperature <= 50)
|
||||
L.apply_status_effect(/datum/status_effect/freon)
|
||||
@@ -204,8 +204,7 @@
|
||||
M.apply_water()
|
||||
|
||||
wash(CLEAN_WASH)
|
||||
for(var/am in src)
|
||||
var/atom/movable/movable_content = am
|
||||
for(var/atom/movable/movable_content as anything in src)
|
||||
if(ismopable(movable_content)) // Will have already been washed by the wash call above at this point.
|
||||
continue
|
||||
movable_content.wash(CLEAN_WASH)
|
||||
|
||||
@@ -33,6 +33,9 @@ GLOBAL_DATUM_INIT(openspace_backdrop_one_for_all, /atom/movable/openspace_backdr
|
||||
. = ..()
|
||||
overlays += GLOB.openspace_backdrop_one_for_all //Special grey square for projecting backdrop darkness filter on it.
|
||||
RegisterSignal(src, COMSIG_ATOM_INITIALIZED_ON, .proc/on_atom_created)
|
||||
var/area/our_area = loc
|
||||
if(istype(our_area, /area/space))
|
||||
force_no_gravity = TRUE
|
||||
return INITIALIZE_HINT_LATELOAD
|
||||
|
||||
/turf/open/openspace/LateInitialize()
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
bullet_bounce_sound = null
|
||||
vis_flags = VIS_INHERIT_ID //when this be added to vis_contents of something it be associated with something on clicking, important for visualisation of turf in openspace and interraction with openspace that show you turf.
|
||||
|
||||
force_no_gravity = TRUE
|
||||
|
||||
/turf/open/space/basic/New() //Do not convert to Initialize
|
||||
//This is used to optimize the map loader
|
||||
return
|
||||
|
||||
@@ -74,6 +74,8 @@ GLOBAL_LIST_EMPTY(station_turfs)
|
||||
/// but is now destroyed, this will preserve the value.
|
||||
/// See __DEFINES/construction.dm for RCD_MEMORY_*.
|
||||
var/rcd_memory
|
||||
///whether or not this turf forces movables on it to have no gravity (unless they themselves have forced gravity)
|
||||
var/force_no_gravity = FALSE
|
||||
|
||||
/// How pathing algorithm will check if this turf is passable by itself (not including content checks). By default it's just density check.
|
||||
/// WARNING: Currently to use a density shortcircuiting this does not support dense turfs with special allow through function
|
||||
@@ -363,8 +365,6 @@ GLOBAL_LIST_EMPTY(station_turfs)
|
||||
var/canPassSelf = CanPass(mover, get_dir(src, mover))
|
||||
if(canPassSelf || (mover.movement_type & PHASING))
|
||||
for(var/atom/movable/thing as anything in contents)
|
||||
if(QDELETED(mover))
|
||||
return FALSE //We were deleted, do not attempt to proceed with movement.
|
||||
if(thing == mover || thing == mover.loc) // Multi tile objects and moving out of other objects
|
||||
continue
|
||||
if(!thing.Cross(mover))
|
||||
@@ -385,14 +385,6 @@ GLOBAL_LIST_EMPTY(station_turfs)
|
||||
return (mover.movement_type & PHASING)
|
||||
return TRUE
|
||||
|
||||
/turf/open/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
||||
. = ..()
|
||||
//melting
|
||||
if(isobj(arrived) && air && air.temperature > T0C)
|
||||
var/obj/O = arrived
|
||||
if(O.obj_flags & FROZEN)
|
||||
O.make_unfrozen()
|
||||
|
||||
// A proc in case it needs to be recreated or badmins want to change the baseturfs
|
||||
/turf/proc/assemble_baseturfs(turf/fake_baseturf_type)
|
||||
var/static/list/created_baseturf_lists = list()
|
||||
|
||||
@@ -185,34 +185,46 @@
|
||||
if(cooldown)
|
||||
return
|
||||
cooldown = TRUE
|
||||
var/obj/structure/industrial_lift/lift
|
||||
for(var/l in GLOB.lifts)
|
||||
var/obj/structure/industrial_lift/possible_lift = l
|
||||
if(possible_lift.id != id || possible_lift.z == z || possible_lift.controls_locked)
|
||||
var/datum/lift_master/lift
|
||||
for(var/datum/lift_master/possible_match as anything in GLOB.active_lifts_by_type[BASIC_LIFT_ID])
|
||||
if(possible_match.specific_lift_id != id || !check_z(possible_match) || possible_match.controls_locked)
|
||||
continue
|
||||
lift = possible_lift
|
||||
|
||||
lift = possible_match
|
||||
break
|
||||
|
||||
if(!lift)
|
||||
addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 2 SECONDS)
|
||||
return
|
||||
lift.visible_message(span_notice("[src] clinks and whirrs into automated motion, locking controls."))
|
||||
lift.lift_master_datum.set_controls(LOCKED)
|
||||
|
||||
var/obj/structure/industrial_lift/target = lift.lift_platforms[1]
|
||||
var/target_z = target.z
|
||||
|
||||
lift.set_controls(LIFT_PLATFORM_LOCKED)
|
||||
///The z level to which the elevator should travel
|
||||
var/targetZ = (abs(loc.z)) //The target Z (where the elevator should move to) is not our z level (we are just some assembly in nullspace) but actually the Z level of whatever we are contained in (e.g. elevator button)
|
||||
///The amount of z levels between the our and targetZ
|
||||
var/difference = abs(targetZ - lift.z)
|
||||
var/difference = abs(targetZ - target_z)
|
||||
///Direction (up/down) needed to go to reach targetZ
|
||||
var/direction = lift.z < targetZ ? UP : DOWN
|
||||
var/direction = target_z < targetZ ? UP : DOWN
|
||||
///How long it will/should take us to reach the target Z level
|
||||
var/travel_duration = FLOOR_TRAVEL_TIME * difference //100 / 2 floors up = 50 seconds on every floor, will always reach destination in the same time
|
||||
addtimer(VARSET_CALLBACK(src, cooldown, FALSE), travel_duration)
|
||||
|
||||
for(var/i in 1 to difference)
|
||||
sleep(FLOOR_TRAVEL_TIME)//hey this should be alright... right?
|
||||
if(QDELETED(lift) || QDELETED(src))//elevator control or button gone = don't go up anymore
|
||||
return
|
||||
lift.lift_master_datum.MoveLift(direction, null)
|
||||
lift.visible_message(span_notice("[src] clicks, ready to be manually operated again."))
|
||||
lift.lift_master_datum.set_controls(UNLOCKED)
|
||||
lift.MoveLift(direction, null)
|
||||
lift.set_controls(LIFT_PLATFORM_UNLOCKED)
|
||||
|
||||
///check if any of the lift platforms are already here
|
||||
/obj/item/assembly/control/elevator/proc/check_z(datum/lift_master/lift)
|
||||
for(var/obj/structure/industrial_lift/platform as anything in lift.lift_platforms)
|
||||
if(platform.z == loc.z)
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
#undef FLOOR_TRAVEL_TIME
|
||||
|
||||
@@ -221,17 +233,19 @@
|
||||
desc = "A small device used to bring trams to you."
|
||||
///for finding the landmark initially - should be the exact same as the landmark's destination id.
|
||||
var/initial_id
|
||||
///ID to link to allow us to link to one specific tram in the world
|
||||
var/specific_lift_id = MAIN_STATION_TRAM
|
||||
///this is our destination's landmark, so we only have to find it the first time.
|
||||
var/datum/weakref/to_where
|
||||
|
||||
/obj/item/assembly/control/tram/Initialize(mapload)
|
||||
. = ..()
|
||||
..()
|
||||
return INITIALIZE_HINT_LATELOAD
|
||||
|
||||
/obj/item/assembly/control/tram/LateInitialize()
|
||||
. = ..()
|
||||
//find where the tram needs to go to (our destination). only needs to happen the first time
|
||||
for(var/obj/effect/landmark/tram/our_destination as anything in GLOB.tram_landmarks)
|
||||
for(var/obj/effect/landmark/tram/our_destination as anything in GLOB.tram_landmarks[specific_lift_id])
|
||||
if(our_destination.destination_id == initial_id)
|
||||
to_where = WEAKREF(our_destination)
|
||||
break
|
||||
@@ -245,29 +259,27 @@
|
||||
return
|
||||
cooldown = TRUE
|
||||
addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 2 SECONDS)
|
||||
var/obj/structure/industrial_lift/tram/tram_part
|
||||
var/turf/current_turf = get_turf(src)
|
||||
if(!current_turf)
|
||||
return
|
||||
for(var/atom/tram as anything in GLOB.central_trams)
|
||||
if(tram.z != current_turf.z)
|
||||
continue
|
||||
tram_part = tram
|
||||
break
|
||||
if(!tram_part)
|
||||
|
||||
var/datum/lift_master/tram/tram
|
||||
for(var/datum/lift_master/tram/possible_match as anything in GLOB.active_lifts_by_type[TRAM_LIFT_ID])
|
||||
if(possible_match.specific_lift_id == specific_lift_id)
|
||||
tram = possible_match
|
||||
break
|
||||
|
||||
if(!tram)
|
||||
say("The tram is not responding to call signals. Please send a technician to repair the internals of the tram.")
|
||||
return
|
||||
if(tram_part.travelling) //in use
|
||||
say("The tram is already travelling to [tram_part.from_where].")
|
||||
if(tram.travelling) //in use
|
||||
say("The tram is already travelling to [tram.from_where].")
|
||||
return
|
||||
if(!to_where)
|
||||
return
|
||||
var/obj/effect/landmark/tram/current_location = to_where.resolve()
|
||||
if(!current_location)
|
||||
return
|
||||
if(tram_part.from_where == current_location) //already here
|
||||
if(tram.from_where == current_location) //already here
|
||||
say("The tram is already here. Please board the tram and select a destination.")
|
||||
return
|
||||
|
||||
say("The tram has been called to [current_location.name]. Please wait for its arrival.")
|
||||
tram_part.tram_travel(current_location)
|
||||
tram.tram_travel(current_location)
|
||||
|
||||
@@ -107,6 +107,7 @@
|
||||
air_update_turf(FALSE, FALSE)
|
||||
var/static/list/loc_connections = list(
|
||||
COMSIG_ATOM_ENTERED = .proc/on_entered,
|
||||
COMSIG_ATOM_ABSTRACT_ENTERED = .proc/on_entered,
|
||||
)
|
||||
AddElement(/datum/element/connect_loc, loc_connections)
|
||||
|
||||
|
||||
@@ -111,8 +111,7 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
|
||||
if(volume) // to prevent division by zero
|
||||
var/cached_gases = gases
|
||||
TOTAL_MOLES(cached_gases, .)
|
||||
. *= R_IDEAL_GAS_EQUATION * temperature / volume
|
||||
return
|
||||
return . * R_IDEAL_GAS_EQUATION * temperature / volume
|
||||
return 0
|
||||
|
||||
/// Calculate temperature in kelvins
|
||||
@@ -661,7 +660,7 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
|
||||
/datum/gas_mixture/proc/gas_pressure_quadratic(a, b, c, lower_limit, upper_limit)
|
||||
var/solution
|
||||
if(!IS_INF_OR_NAN(a) && !IS_INF_OR_NAN(b) && !IS_INF_OR_NAN(c))
|
||||
solution = max(SolveQuadratic(a, b, c))
|
||||
solution = max(SolveQuadratic(a, b, c))
|
||||
if(solution > lower_limit && solution < upper_limit) //SolveQuadratic can return empty lists so be careful here
|
||||
return solution
|
||||
stack_trace("Failed to solve pressure quadratic equation. A: [a]. B: [b]. C:[c]. Current value = [solution]. Expected lower limit: [lower_limit]. Expected upper limit: [upper_limit].")
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
/// green, amber, or red.
|
||||
var/signal_state = XING_STATE_GREEN
|
||||
/// The ID of the tram we control
|
||||
var/tram_id = "tram_station"
|
||||
var/tram_id = MAIN_STATION_TRAM
|
||||
/// Weakref to the tram piece we control
|
||||
var/datum/weakref/tram_ref
|
||||
/// Proximity threshold for amber warning (slow people may be in danger)
|
||||
@@ -42,14 +42,14 @@
|
||||
. = ..()
|
||||
find_tram()
|
||||
|
||||
var/obj/structure/industrial_lift/tram/central/tram_part = tram_ref?.resolve()
|
||||
var/datum/lift_master/tram/tram_part = tram_ref?.resolve()
|
||||
if(tram_part)
|
||||
RegisterSignal(tram_part, COMSIG_TRAM_SET_TRAVELLING, .proc/on_tram_travelling)
|
||||
|
||||
/obj/machinery/crossing_signal/Destroy()
|
||||
. = ..()
|
||||
|
||||
var/obj/structure/industrial_lift/tram/central/tram_part = tram_ref?.resolve()
|
||||
var/datum/lift_master/tram/tram_part = tram_ref?.resolve()
|
||||
if(tram_part)
|
||||
UnregisterSignal(tram_part, COMSIG_TRAM_SET_TRAVELLING)
|
||||
|
||||
@@ -67,8 +67,8 @@
|
||||
* Locates tram parts in the lift global list after everything is done.
|
||||
*/
|
||||
/obj/machinery/crossing_signal/proc/find_tram()
|
||||
for(var/obj/structure/industrial_lift/tram/central/tram as anything in GLOB.central_trams)
|
||||
if(tram.tram_id != tram_id)
|
||||
for(var/datum/lift_master/tram/tram as anything in GLOB.active_lifts_by_type[TRAM_LIFT_ID])
|
||||
if(tram.specific_lift_id != tram_id)
|
||||
continue
|
||||
tram_ref = WEAKREF(tram)
|
||||
break
|
||||
@@ -103,10 +103,10 @@
|
||||
end_processing()
|
||||
|
||||
/obj/machinery/crossing_signal/process()
|
||||
var/obj/structure/industrial_lift/tram/central/tram_part = tram_ref?.resolve()
|
||||
var/datum/lift_master/tram/tram = tram_ref?.resolve()
|
||||
|
||||
// Check for stopped states.
|
||||
if(!tram_part || !is_operational)
|
||||
if(!tram || !is_operational)
|
||||
// Tram missing, or we lost power.
|
||||
// Tram missing is always safe (green)
|
||||
set_signal_state(XING_STATE_GREEN, force = !is_operational)
|
||||
@@ -114,26 +114,32 @@
|
||||
|
||||
use_power(active_power_usage)
|
||||
|
||||
var/obj/structure/industrial_lift/tram/tram_part = tram.return_closest_platform_to(src)
|
||||
|
||||
if(QDELETED(tram_part))
|
||||
set_signal_state(XING_STATE_GREEN, force = !is_operational)
|
||||
return PROCESS_KILL
|
||||
|
||||
// Everything will be based on position and travel direction
|
||||
var/signal_pos
|
||||
var/tram_pos
|
||||
var/tram_velocity_sign // 1 for positive axis movement, -1 for negative
|
||||
// Try to be agnostic about N-S vs E-W movement
|
||||
if(tram_part.travel_direction & (NORTH|SOUTH))
|
||||
if(tram.travel_direction & (NORTH|SOUTH))
|
||||
signal_pos = y
|
||||
tram_pos = tram_part.y
|
||||
tram_velocity_sign = tram_part.travel_direction & NORTH ? 1 : -1
|
||||
tram_velocity_sign = tram.travel_direction & NORTH ? 1 : -1
|
||||
else
|
||||
signal_pos = x
|
||||
tram_pos = tram_part.x
|
||||
tram_velocity_sign = tram_part.travel_direction & EAST ? 1 : -1
|
||||
tram_velocity_sign = tram.travel_direction & EAST ? 1 : -1
|
||||
|
||||
// How far away are we? negative if already passed.
|
||||
var/approach_distance = tram_velocity_sign * (signal_pos - tram_pos)
|
||||
|
||||
// Check for stopped state.
|
||||
// Will kill the process since tram starting up will restart process.
|
||||
if(!tram_part.travelling)
|
||||
if(!tram.travelling)
|
||||
// if super close, show red anyway since tram could suddenly start moving
|
||||
if(abs(approach_distance) < red_distance_threshold)
|
||||
set_signal_state(XING_STATE_RED)
|
||||
752
code/modules/industrial_lift/industrial_lift.dm
Normal file
752
code/modules/industrial_lift/industrial_lift.dm
Normal file
@@ -0,0 +1,752 @@
|
||||
GLOBAL_LIST_EMPTY(lifts)
|
||||
|
||||
/obj/structure/industrial_lift
|
||||
name = "lift platform"
|
||||
desc = "A lightweight lift platform. It moves up and down."
|
||||
icon = 'icons/obj/smooth_structures/catwalk.dmi'
|
||||
icon_state = "catwalk-0"
|
||||
base_icon_state = "catwalk"
|
||||
density = FALSE
|
||||
anchored = TRUE
|
||||
armor = list(MELEE = 50, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, FIRE = 80, ACID = 50)
|
||||
max_integrity = 50
|
||||
layer = LATTICE_LAYER //under pipes
|
||||
plane = FLOOR_PLANE
|
||||
smoothing_flags = SMOOTH_BITMASK
|
||||
smoothing_groups = list(SMOOTH_GROUP_INDUSTRIAL_LIFT)
|
||||
canSmoothWith = list(SMOOTH_GROUP_INDUSTRIAL_LIFT)
|
||||
obj_flags = CAN_BE_HIT | BLOCK_Z_OUT_DOWN
|
||||
appearance_flags = PIXEL_SCALE|KEEP_TOGETHER //no TILE_BOUND since we're potentially multitile
|
||||
|
||||
///ID used to determine what lift types we can merge with
|
||||
var/lift_id = BASIC_LIFT_ID
|
||||
|
||||
///if true, the elevator works through floors
|
||||
var/pass_through_floors = FALSE
|
||||
|
||||
///what movables on our platform that we are moving
|
||||
var/list/atom/movable/lift_load
|
||||
///lazylist of weakrefs to the contents we have when we're first created. stored so that admins can clear the tram to its initial state
|
||||
///if someone put a bunch of stuff onto it.
|
||||
var/list/datum/weakref/initial_contents
|
||||
|
||||
///what glide_size we set our moving contents to.
|
||||
var/glide_size_override = 8
|
||||
///lazy list of movables inside lift_load who had their glide_size changed since our last movement.
|
||||
///used so that we dont have to change the glide_size of every object every movement, which scales to cost more than you'd think
|
||||
var/list/atom/movable/changed_gliders
|
||||
|
||||
///master datum that controls our movement. in general /industrial_lift subtypes control moving themselves, and
|
||||
/// /datum/lift_master instances control moving the entire tram and any behavior associated with that.
|
||||
var/datum/lift_master/lift_master_datum
|
||||
///what subtype of /datum/lift_master to create for itself if no other platform on this tram has created one yet.
|
||||
///very important for some behaviors since
|
||||
var/lift_master_type = /datum/lift_master
|
||||
|
||||
///how many tiles this platform extends on the x axis
|
||||
var/width = 1
|
||||
///how many tiles this platform extends on the y axis (north-south not up-down, that would be the z axis)
|
||||
var/height = 1
|
||||
|
||||
///if TRUE, this platform will late initialize and then expand to become a multitile object across all other linked platforms on this z level
|
||||
var/create_multitile_platform = FALSE
|
||||
|
||||
/obj/structure/industrial_lift/Initialize(mapload)
|
||||
. = ..()
|
||||
GLOB.lifts.Add(src)
|
||||
|
||||
set_movement_registrations()
|
||||
|
||||
//since lift_master datums find all connected platforms when an industrial lift first creates it and then
|
||||
//sets those platforms' lift_master_datum to itself, this check will only evaluate to true once per tram platform
|
||||
if(!lift_master_datum && lift_master_type)
|
||||
lift_master_datum = new lift_master_type(src)
|
||||
return INITIALIZE_HINT_LATELOAD
|
||||
|
||||
/obj/structure/industrial_lift/LateInitialize()
|
||||
//after everything is initialized the lift master can order everything
|
||||
lift_master_datum.order_platforms_by_z_level()
|
||||
|
||||
/obj/structure/industrial_lift/Destroy()
|
||||
GLOB.lifts.Remove(src)
|
||||
lift_master_datum = null
|
||||
return ..()
|
||||
|
||||
|
||||
///set the movement registrations to our current turf(s) so contents moving out of our tile(s) are removed from our movement lists
|
||||
/obj/structure/industrial_lift/proc/set_movement_registrations(list/turfs_to_set)
|
||||
for(var/turf/turf_loc as anything in turfs_to_set || locs)
|
||||
RegisterSignal(turf_loc, COMSIG_ATOM_EXITED, .proc/UncrossedRemoveItemFromLift)
|
||||
RegisterSignal(turf_loc, list(COMSIG_ATOM_ENTERED,COMSIG_ATOM_INITIALIZED_ON), .proc/AddItemOnLift)
|
||||
|
||||
///unset our movement registrations from turfs that no longer contain us (or every loc if turfs_to_unset is unspecified)
|
||||
/obj/structure/industrial_lift/proc/unset_movement_registrations(list/turfs_to_unset)
|
||||
var/static/list/registrations = list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_EXITED, COMSIG_ATOM_INITIALIZED_ON)
|
||||
for(var/turf/turf_loc as anything in turfs_to_unset || locs)
|
||||
UnregisterSignal(turf_loc, registrations)
|
||||
|
||||
|
||||
/obj/structure/industrial_lift/proc/UncrossedRemoveItemFromLift(datum/source, atom/movable/gone, direction)
|
||||
SIGNAL_HANDLER
|
||||
if(!(gone.loc in locs))
|
||||
RemoveItemFromLift(gone)
|
||||
|
||||
/obj/structure/industrial_lift/proc/RemoveItemFromLift(atom/movable/potential_rider)
|
||||
SIGNAL_HANDLER
|
||||
if(!(potential_rider in lift_load))
|
||||
return
|
||||
if(isliving(potential_rider) && HAS_TRAIT(potential_rider, TRAIT_CANNOT_BE_UNBUCKLED))
|
||||
REMOVE_TRAIT(potential_rider, TRAIT_CANNOT_BE_UNBUCKLED, BUCKLED_TRAIT)
|
||||
LAZYREMOVE(lift_load, potential_rider)
|
||||
LAZYREMOVE(changed_gliders, potential_rider)
|
||||
UnregisterSignal(potential_rider, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE))
|
||||
|
||||
/obj/structure/industrial_lift/proc/AddItemOnLift(datum/source, atom/movable/new_lift_contents)
|
||||
SIGNAL_HANDLER
|
||||
var/static/list/blacklisted_types = typecacheof(list(/obj/structure/fluff/tram_rail, /obj/effect/decal/cleanable, /obj/structure/industrial_lift, /mob/camera))
|
||||
if(is_type_in_typecache(new_lift_contents, blacklisted_types) || new_lift_contents.invisibility == INVISIBILITY_ABSTRACT) //prevents the tram from stealing things like landmarks
|
||||
return FALSE
|
||||
if(new_lift_contents in lift_load)
|
||||
return FALSE
|
||||
|
||||
if(isliving(new_lift_contents) && !HAS_TRAIT(new_lift_contents, TRAIT_CANNOT_BE_UNBUCKLED))
|
||||
ADD_TRAIT(new_lift_contents, TRAIT_CANNOT_BE_UNBUCKLED, BUCKLED_TRAIT)
|
||||
LAZYADD(lift_load, new_lift_contents)
|
||||
RegisterSignal(new_lift_contents, COMSIG_PARENT_QDELETING, .proc/RemoveItemFromLift)
|
||||
|
||||
return TRUE
|
||||
|
||||
///adds everything on our tile that can be added to our lift_load and initial_contents lists when we're created
|
||||
/obj/structure/industrial_lift/proc/add_initial_contents()
|
||||
for(var/turf/turf_loc in locs)
|
||||
for(var/atom/movable/movable_contents as anything in turf_loc)
|
||||
if(movable_contents == src)
|
||||
continue
|
||||
|
||||
if(AddItemOnLift(src, movable_contents))
|
||||
|
||||
var/datum/weakref/new_initial_contents = WEAKREF(movable_contents)
|
||||
if(!new_initial_contents)
|
||||
continue
|
||||
|
||||
LAZYADD(initial_contents, new_initial_contents)
|
||||
|
||||
///signal handler for COMSIG_MOVABLE_UPDATE_GLIDE_SIZE: when a movable in lift_load changes its glide_size independently.
|
||||
///adds that movable to a lazy list, movables in that list have their glide_size updated when the tram next moves
|
||||
/obj/structure/industrial_lift/proc/on_changed_glide_size(atom/movable/moving_contents, new_glide_size)
|
||||
SIGNAL_HANDLER
|
||||
if(new_glide_size != glide_size_override)
|
||||
LAZYADD(changed_gliders, moving_contents)
|
||||
|
||||
|
||||
///make this tram platform multitile, expanding to cover all the tram platforms adjacent to us and deleting them. makes movement more efficient.
|
||||
///the platform becoming multitile should be in the bottom left corner since thats assumed to be the loc of multitile objects
|
||||
/obj/structure/industrial_lift/proc/create_multitile_platform(min_x, min_y, max_x, max_y, z)
|
||||
|
||||
if(!(min_x && min_y && max_x && max_y && z))
|
||||
for(var/obj/structure/industrial_lift/other_lift as anything in lift_master_datum.lift_platforms)
|
||||
if(other_lift.z != z)
|
||||
continue
|
||||
|
||||
min_x = min(min_x, other_lift.x)
|
||||
max_x = max(max_x, other_lift.x)
|
||||
|
||||
min_y = min(min_y, other_lift.y)
|
||||
max_y = max(max_y, other_lift.y)
|
||||
|
||||
var/turf/bottom_left_loc = locate(min_x, min_y, z)
|
||||
var/obj/structure/industrial_lift/loc_corner_lift = locate() in bottom_left_loc
|
||||
|
||||
if(!loc_corner_lift)
|
||||
stack_trace("no lift in the bottom left corner of a lift level!")
|
||||
return FALSE
|
||||
|
||||
if(loc_corner_lift != src)
|
||||
//the loc of a multitile object must always be the lower left corner
|
||||
return loc_corner_lift.create_multitile_platform()
|
||||
|
||||
width = (max_x - min_x) + 1
|
||||
height = (max_y - min_y) + 1
|
||||
|
||||
///list of turfs we dont go over. if for whatever reason we encounter an already multitile lift platform
|
||||
///we add all of its locs to this list so we dont add that lift platform multiple times as we iterate through its locs
|
||||
var/list/locs_to_skip = locs.Copy()
|
||||
|
||||
bound_width = bound_width * width
|
||||
bound_height = bound_height * height
|
||||
|
||||
//multitile movement code assumes our loc is on the lower left corner of our bounding box
|
||||
|
||||
var/first_x = 0
|
||||
var/first_y = 0
|
||||
|
||||
var/last_x = max(max_x - min_x, 0)
|
||||
var/last_y = max(max_y - min_y, 0)
|
||||
|
||||
for(var/y in first_y to last_y)
|
||||
|
||||
var/y_pixel_offset = world.icon_size * y
|
||||
|
||||
for(var/x in first_x to last_x)
|
||||
|
||||
var/x_pixel_offset = world.icon_size * x
|
||||
|
||||
var/turf/lift_turf = locate(x + min_x, y + min_y, z)
|
||||
|
||||
if(!lift_turf)
|
||||
continue
|
||||
|
||||
if(lift_turf in locs_to_skip)
|
||||
continue
|
||||
|
||||
var/obj/structure/industrial_lift/other_lift = locate() in lift_turf
|
||||
|
||||
if(!other_lift)
|
||||
continue
|
||||
|
||||
locs_to_skip += other_lift.locs.Copy()//make sure we never go over multitile platforms multiple times
|
||||
|
||||
other_lift.pixel_x = x_pixel_offset
|
||||
other_lift.pixel_y = y_pixel_offset
|
||||
|
||||
overlays += other_lift
|
||||
|
||||
//now we vore all the other lifts connected to us on our z level
|
||||
for(var/obj/structure/industrial_lift/other_lift in lift_master_datum.lift_platforms)
|
||||
if(other_lift == src || other_lift.z != z)
|
||||
continue
|
||||
|
||||
lift_master_datum.lift_platforms -= other_lift
|
||||
if(other_lift.lift_load)
|
||||
LAZYOR(lift_load, other_lift.lift_load)
|
||||
if(other_lift.initial_contents)
|
||||
LAZYOR(initial_contents, other_lift.initial_contents)
|
||||
|
||||
qdel(other_lift)
|
||||
|
||||
lift_master_datum.multitile_platform = TRUE
|
||||
|
||||
var/turf/old_loc = loc
|
||||
|
||||
forceMove(locate(min_x, min_y, z))//move to the lower left corner
|
||||
set_movement_registrations(locs - old_loc)
|
||||
return TRUE
|
||||
|
||||
///returns an unordered list of all lift platforms adjacent to us. used so our lift_master_datum can control all connected platforms.
|
||||
///includes platforms directly above or below us as well. only includes platforms with an identical lift_id to our own.
|
||||
/obj/structure/industrial_lift/proc/lift_platform_expansion(datum/lift_master/lift_master_datum)
|
||||
. = list()
|
||||
for(var/direction in GLOB.cardinals_multiz)
|
||||
var/obj/structure/industrial_lift/neighbor = locate() in get_step_multiz(src, direction)
|
||||
if(!neighbor || neighbor.lift_id != lift_id)
|
||||
continue
|
||||
. += neighbor
|
||||
|
||||
///main proc for moving the lift in the direction [going]. handles horizontal and/or vertical movement for multi platformed lifts and multitile lifts.
|
||||
/obj/structure/industrial_lift/proc/travel(going)
|
||||
var/list/things_to_move = lift_load
|
||||
var/turf/destination
|
||||
if(!isturf(going))
|
||||
destination = get_step_multiz(src, going)
|
||||
else
|
||||
destination = going
|
||||
going = get_dir_multiz(loc, going)
|
||||
|
||||
var/x_offset = ROUND_UP(bound_width / 32) - 1 //how many tiles our horizontally farthest edge is from us
|
||||
var/y_offset = ROUND_UP(bound_height / 32) - 1 //how many tiles our vertically farthest edge is from us
|
||||
|
||||
//the x coordinate of the edge furthest from our future destination, which would be our right hand side
|
||||
var/back_edge_x = destination.x + x_offset//if we arent multitile this should just be destination.x
|
||||
var/top_edge_y = destination.y + y_offset
|
||||
|
||||
var/turf/top_right_corner = locate(min(world.maxx, back_edge_x), min(world.maxy, top_edge_y), destination.z)
|
||||
|
||||
var/list/dest_locs = block(
|
||||
destination,
|
||||
top_right_corner
|
||||
)
|
||||
|
||||
var/list/entering_locs = dest_locs - locs
|
||||
var/list/exited_locs = locs - dest_locs
|
||||
|
||||
if(going == DOWN)
|
||||
for(var/turf/dest_turf as anything in entering_locs)
|
||||
SEND_SIGNAL(dest_turf, COMSIG_TURF_INDUSTRIAL_LIFT_ENTER, things_to_move)
|
||||
|
||||
if(istype(dest_turf, /turf/closed/wall))
|
||||
var/turf/closed/wall/C = dest_turf
|
||||
do_sparks(2, FALSE, C)
|
||||
C.dismantle_wall(devastated = TRUE)
|
||||
for(var/mob/M in urange(8, src))
|
||||
shake_camera(M, 2, 3)
|
||||
playsound(C, 'sound/effects/meteorimpact.ogg', 100, TRUE)
|
||||
|
||||
for(var/mob/living/crushed in dest_turf.contents)
|
||||
to_chat(crushed, span_userdanger("You are crushed by [src]!"))
|
||||
crushed.gib(FALSE,FALSE,FALSE)//the nicest kind of gibbing, keeping everything intact.
|
||||
|
||||
else if(going == UP)
|
||||
for(var/turf/dest_turf as anything in entering_locs)
|
||||
///handles any special interactions objects could have with the lift/tram, handled on the item itself
|
||||
SEND_SIGNAL(dest_turf, COMSIG_TURF_INDUSTRIAL_LIFT_ENTER, things_to_move)
|
||||
|
||||
if(istype(dest_turf, /turf/closed/wall))
|
||||
var/turf/closed/wall/C = dest_turf
|
||||
do_sparks(2, FALSE, C)
|
||||
C.dismantle_wall(devastated = TRUE)
|
||||
for(var/mob/client_mob in SSspatial_grid.orthogonal_range_search(src, SPATIAL_GRID_CONTENTS_TYPE_CLIENTS, 8))
|
||||
shake_camera(client_mob, 2, 3)
|
||||
playsound(C, 'sound/effects/meteorimpact.ogg', 100, TRUE)
|
||||
|
||||
else
|
||||
///potentially finds a spot to throw the victim at for daring to be hit by a tram. is null if we havent found anything to throw
|
||||
var/atom/throw_target
|
||||
var/datum/lift_master/tram/our_lift = lift_master_datum
|
||||
var/collision_lethality = our_lift.collision_lethality
|
||||
|
||||
for(var/turf/dest_turf as anything in entering_locs)
|
||||
///handles any special interactions objects could have with the lift/tram, handled on the item itself
|
||||
SEND_SIGNAL(dest_turf, COMSIG_TURF_INDUSTRIAL_LIFT_ENTER, things_to_move)
|
||||
|
||||
if(istype(dest_turf, /turf/closed/wall))
|
||||
var/turf/closed/wall/collided_wall = dest_turf
|
||||
do_sparks(2, FALSE, collided_wall)
|
||||
collided_wall.dismantle_wall(devastated = TRUE)
|
||||
for(var/mob/client_mob in SSspatial_grid.orthogonal_range_search(collided_wall, SPATIAL_GRID_CONTENTS_TYPE_CLIENTS, 8))
|
||||
if(get_dist(dest_turf, client_mob) <= 8)
|
||||
shake_camera(client_mob, 2, 3)
|
||||
|
||||
playsound(collided_wall, 'sound/effects/meteorimpact.ogg', 100, TRUE)
|
||||
|
||||
for(var/obj/structure/victim_structure in dest_turf.contents)
|
||||
if(QDELING(victim_structure))
|
||||
continue
|
||||
if(!is_type_in_typecache(victim_structure, lift_master_datum.ignored_smashthroughs) && victim_structure.layer >= LOW_OBJ_LAYER)
|
||||
|
||||
if(victim_structure.anchored && initial(victim_structure.anchored) == TRUE)
|
||||
visible_message(span_danger("[src] smashes through [victim_structure]!"))
|
||||
victim_structure.deconstruct(FALSE)
|
||||
|
||||
else
|
||||
if(!throw_target)
|
||||
throw_target = get_edge_target_turf(src, turn(going, pick(45, -45)))
|
||||
visible_message(span_danger("[src] violently rams [victim_structure] out of the way!"))
|
||||
victim_structure.anchored = FALSE
|
||||
victim_structure.take_damage(rand(20, 25) * collision_lethality)
|
||||
victim_structure.throw_at(throw_target, 200 * collision_lethality, 4 * collision_lethality)
|
||||
|
||||
for(var/obj/machinery/victim_machine in dest_turf.contents)
|
||||
if(QDELING(victim_machine))
|
||||
continue
|
||||
if(is_type_in_typecache(victim_machine, lift_master_datum.ignored_smashthroughs))
|
||||
continue
|
||||
if(istype(victim_machine, /obj/machinery/field)) //graceful break handles this scenario
|
||||
continue
|
||||
if(victim_machine.layer >= LOW_OBJ_LAYER) //avoids stuff that is probably flush with the ground
|
||||
playsound(src, 'sound/effects/bang.ogg', 50, TRUE)
|
||||
visible_message(span_danger("[src] smashes through [victim_machine]!"))
|
||||
qdel(victim_machine)
|
||||
|
||||
for(var/mob/living/collided in dest_turf.contents)
|
||||
if(lift_master_datum.ignored_smashthroughs[collided.type])
|
||||
continue
|
||||
to_chat(collided, span_userdanger("[src] collides into you!"))
|
||||
playsound(src, 'sound/effects/splat.ogg', 50, TRUE)
|
||||
var/damage = rand(5, 10) * collision_lethality
|
||||
collided.apply_damage(2 * damage, BRUTE, BODY_ZONE_HEAD)
|
||||
collided.apply_damage(2 * damage, BRUTE, BODY_ZONE_CHEST)
|
||||
collided.apply_damage(0.5 * damage, BRUTE, BODY_ZONE_L_LEG)
|
||||
collided.apply_damage(0.5 * damage, BRUTE, BODY_ZONE_R_LEG)
|
||||
collided.apply_damage(0.5 * damage, BRUTE, BODY_ZONE_L_ARM)
|
||||
collided.apply_damage(0.5 * damage, BRUTE, BODY_ZONE_R_ARM)
|
||||
|
||||
if(QDELETED(collided)) //in case it was a mob that dels on death
|
||||
continue
|
||||
if(!throw_target)
|
||||
throw_target = get_edge_target_turf(src, turn(going, pick(45, -45)))
|
||||
|
||||
var/turf/T = get_turf(collided)
|
||||
T.add_mob_blood(collided)
|
||||
|
||||
collided.throw_at()
|
||||
//if going EAST, will turn to the NORTHEAST or SOUTHEAST and throw the ran over guy away
|
||||
var/datum/callback/land_slam = new(collided, /mob/living/.proc/tram_slam_land)
|
||||
collided.throw_at(throw_target, 200 * collision_lethality, 4 * collision_lethality, callback = land_slam)
|
||||
|
||||
unset_movement_registrations(exited_locs)
|
||||
group_move(things_to_move, going)
|
||||
set_movement_registrations(entering_locs)
|
||||
|
||||
///move the movers list of movables on our tile to destination if we successfully move there first.
|
||||
///this is like calling forceMove() on everything in movers and ourselves, except nothing in movers
|
||||
///has destination.Entered() and origin.Exited() called on them, as only our movement can be perceived.
|
||||
///none of the movers are able to react to the movement of any other mover, saving a lot of needless processing cost
|
||||
///and is more sensible. without this, if you and a banana are on the same platform, when that platform moves you will slip
|
||||
///on the banana even if youre not moving relative to it.
|
||||
/obj/structure/industrial_lift/proc/group_move(list/atom/movable/movers, movement_direction)
|
||||
if(movement_direction == NONE)
|
||||
stack_trace("an industrial lift was told to move to somewhere it already is!")
|
||||
return FALSE
|
||||
|
||||
var/turf/our_dest = get_step(src, movement_direction)
|
||||
|
||||
var/area/our_area = get_area(src)
|
||||
var/area/their_area = get_area(our_dest)
|
||||
var/different_areas = our_area != their_area
|
||||
var/turf/mover_old_loc
|
||||
|
||||
if(glide_size != glide_size_override)
|
||||
set_glide_size(glide_size_override)
|
||||
|
||||
forceMove(our_dest)
|
||||
if(loc != our_dest || QDELETED(src))//check if our movement succeeded, if it didnt then the movers cant be moved
|
||||
return FALSE
|
||||
|
||||
for(var/atom/movable/mover as anything in changed_gliders)
|
||||
if(mover.glide_size != glide_size_override)
|
||||
mover.set_glide_size(glide_size_override)
|
||||
|
||||
LAZYREMOVE(changed_gliders, mover)
|
||||
|
||||
if(different_areas)
|
||||
for(var/atom/movable/mover as anything in movers)
|
||||
if(QDELETED(mover))
|
||||
movers -= mover
|
||||
continue
|
||||
|
||||
//we dont need to call Entered() and Exited() for origin and destination here for each mover because
|
||||
//all of them are considered to be on top of us, so the turfs and anything on them can only perceive us,
|
||||
//which is why the platform itself uses forceMove()
|
||||
mover_old_loc = mover.loc
|
||||
|
||||
our_area.Exited(mover, movement_direction)
|
||||
mover.loc = get_step(mover, movement_direction)
|
||||
their_area.Entered(mover, movement_direction)
|
||||
|
||||
mover.Moved(mover_old_loc, movement_direction, TRUE, null, FALSE)
|
||||
|
||||
else
|
||||
for(var/atom/movable/mover as anything in movers)
|
||||
if(QDELETED(mover))
|
||||
movers -= mover
|
||||
continue
|
||||
|
||||
mover_old_loc = mover.loc
|
||||
mover.loc = get_step(mover, movement_direction)
|
||||
|
||||
mover.Moved(mover_old_loc, movement_direction, TRUE, null, FALSE)
|
||||
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* reset the contents of this lift platform to its original state in case someone put too much shit on it.
|
||||
* used by an admin via calling reset_lift_contents() on our lift_master_datum.
|
||||
*
|
||||
* Arguments:
|
||||
* * consider_anything_past - number. if > 0 this platform will only handle foreign contents that exceed this number on each of our locs
|
||||
* * foreign_objects - bool. if true this platform will consider /atom/movable's that arent mobs as part of foreign contents
|
||||
* * foreign_non_player_mobs - bool. if true we consider mobs that dont have a mind to be foreign
|
||||
* * consider_player_mobs - bool. if true we consider player mobs to be foreign. only works if foreign_non_player_mobs is true as well
|
||||
*/
|
||||
/obj/structure/industrial_lift/proc/reset_contents(consider_anything_past = 0, foreign_objects = TRUE, foreign_non_player_mobs = TRUE, consider_player_mobs = FALSE)
|
||||
if(!foreign_objects && !foreign_non_player_mobs && !consider_player_mobs)
|
||||
return FALSE
|
||||
|
||||
consider_anything_past = isnum(consider_anything_past) ? max(consider_anything_past, 0) : 0
|
||||
//just in case someone fucks up the arguments
|
||||
|
||||
if(consider_anything_past && length(lift_load) <= consider_anything_past)
|
||||
return FALSE
|
||||
|
||||
///list of resolve()'d initial_contents that are still in lift_load
|
||||
var/list/atom/movable/original_contents = list(src)
|
||||
|
||||
///list of objects we consider foreign according to the given arguments
|
||||
var/list/atom/movable/foreign_contents = list()
|
||||
|
||||
|
||||
for(var/datum/weakref/initial_contents_ref as anything in initial_contents)
|
||||
if(!initial_contents_ref)
|
||||
continue
|
||||
|
||||
var/atom/movable/resolved_contents = initial_contents_ref.resolve()
|
||||
|
||||
if(!resolved_contents)
|
||||
continue
|
||||
|
||||
if(!(resolved_contents in lift_load))
|
||||
continue
|
||||
|
||||
original_contents += resolved_contents
|
||||
|
||||
for(var/turf/turf_loc as anything in locs)
|
||||
var/list/atom/movable/foreign_contents_in_loc = list()
|
||||
|
||||
for(var/atom/movable/foreign_movable as anything in (turf_loc.contents - original_contents))
|
||||
if(foreign_objects && ismovable(foreign_movable) && !ismob(foreign_movable))
|
||||
foreign_contents_in_loc += foreign_movable
|
||||
continue
|
||||
|
||||
if(foreign_non_player_mobs && ismob(foreign_movable))
|
||||
var/mob/foreign_mob = foreign_movable
|
||||
if(consider_player_mobs || !foreign_mob.mind)
|
||||
foreign_contents_in_loc += foreign_mob
|
||||
continue
|
||||
|
||||
if(consider_anything_past)
|
||||
foreign_contents_in_loc.len -= consider_anything_past
|
||||
//hey cool this works, neat. this takes from the opposite side of the list that youd expect but its easy so idc
|
||||
//also this means that if you use consider_anything_past then foreign mobs are less likely to be deleted than foreign objects
|
||||
//because internally the contents list is 2 linked lists of obj contents - mob contents, thus mobs are always last in the order
|
||||
//when you iterate it.
|
||||
|
||||
foreign_contents += foreign_contents_in_loc
|
||||
|
||||
for(var/atom/movable/contents_to_delete as anything in foreign_contents)
|
||||
qdel(contents_to_delete)
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/structure/industrial_lift/proc/use(mob/living/user)
|
||||
if(!isliving(user) || !in_range(src, user) || user.combat_mode)
|
||||
return
|
||||
|
||||
var/list/tool_list = list()
|
||||
if(lift_master_datum.Check_lift_move(UP))
|
||||
tool_list["Up"] = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH)
|
||||
if(lift_master_datum.Check_lift_move(DOWN))
|
||||
tool_list["Down"] = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH)
|
||||
if(!length(tool_list))
|
||||
to_chat(user, span_warning("[src] doesn't seem to able to move anywhere!"))
|
||||
add_fingerprint(user)
|
||||
return
|
||||
if(lift_master_datum.controls_locked)
|
||||
to_chat(user, span_warning("[src] has its controls locked! It must already be trying to do something!"))
|
||||
add_fingerprint(user)
|
||||
return
|
||||
var/result = show_radial_menu(user, src, tool_list, custom_check = CALLBACK(src, .proc/check_menu, user, src.loc), require_near = TRUE, tooltips = TRUE)
|
||||
if(!isliving(user) || !in_range(src, user) || user.combat_mode)
|
||||
return //nice try
|
||||
switch(result)
|
||||
if("Up")
|
||||
// We have to make sure that they don't do illegal actions by not having their radial menu refresh from someone else moving the lift.
|
||||
if(!lift_master_datum.Check_lift_move(UP))
|
||||
to_chat(user, span_warning("[src] doesn't seem to able to move up!"))
|
||||
add_fingerprint(user)
|
||||
return
|
||||
lift_master_datum.MoveLift(UP, user)
|
||||
show_fluff_message(TRUE, user)
|
||||
use(user)
|
||||
if("Down")
|
||||
if(!lift_master_datum.Check_lift_move(DOWN))
|
||||
to_chat(user, span_warning("[src] doesn't seem to able to move down!"))
|
||||
add_fingerprint(user)
|
||||
return
|
||||
lift_master_datum.MoveLift(DOWN, user)
|
||||
show_fluff_message(FALSE, user)
|
||||
use(user)
|
||||
if("Cancel")
|
||||
return
|
||||
add_fingerprint(user)
|
||||
|
||||
/**
|
||||
* Proc to ensure that the radial menu closes when it should.
|
||||
* Arguments:
|
||||
* * user - The person that opened the menu.
|
||||
* * starting_loc - The location of the lift when the menu was opened, used to prevent the menu from being interacted with after the lift was moved by someone else.
|
||||
*
|
||||
* Returns:
|
||||
* * boolean, FALSE if the menu should be closed, TRUE if the menu is clear to stay opened.
|
||||
*/
|
||||
/obj/structure/industrial_lift/proc/check_menu(mob/user, starting_loc)
|
||||
if(user.incapacitated() || !user.Adjacent(src) || starting_loc != src.loc)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/structure/industrial_lift/attack_hand(mob/user, list/modifiers)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
use(user)
|
||||
|
||||
//ai probably shouldn't get to use lifts but they sure are great for admins to crush people with
|
||||
/obj/structure/industrial_lift/attack_ghost(mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if(isAdminGhostAI(user))
|
||||
use(user)
|
||||
|
||||
/obj/structure/industrial_lift/attack_paw(mob/user, list/modifiers)
|
||||
return use(user)
|
||||
|
||||
/obj/structure/industrial_lift/attackby(obj/item/W, mob/user, params)
|
||||
return use(user)
|
||||
|
||||
/obj/structure/industrial_lift/attack_robot(mob/living/silicon/robot/R)
|
||||
if(R.Adjacent(src))
|
||||
return use(R)
|
||||
|
||||
/**
|
||||
* Shows a message indicating that the lift has moved up or down.
|
||||
* Arguments:
|
||||
* * going_up - Boolean on whether or not we're going up, to adjust the message appropriately.
|
||||
* * user - The mob that caused the lift to move, for the visible message.
|
||||
*/
|
||||
/obj/structure/industrial_lift/proc/show_fluff_message(going_up, mob/user)
|
||||
if(going_up)
|
||||
user.visible_message(span_notice("[user] moves the lift upwards."), span_notice("You move the lift upwards."))
|
||||
else
|
||||
user.visible_message(span_notice("[user] moves the lift downwards."), span_notice("You move the lift downwards."))
|
||||
|
||||
/obj/structure/industrial_lift/debug
|
||||
name = "transport platform"
|
||||
desc = "A lightweight platform. It moves in any direction, except up and down."
|
||||
color = "#5286b9ff"
|
||||
lift_id = DEBUG_LIFT_ID
|
||||
|
||||
/obj/structure/industrial_lift/debug/use(mob/user)
|
||||
if (!in_range(src, user))
|
||||
return
|
||||
//NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST
|
||||
var/static/list/tool_list = list(
|
||||
"NORTH" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH),
|
||||
"NORTHEAST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH),
|
||||
"EAST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = EAST),
|
||||
"SOUTHEAST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = EAST),
|
||||
"SOUTH" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH),
|
||||
"SOUTHWEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH),
|
||||
"WEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = WEST),
|
||||
"NORTHWEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = WEST)
|
||||
)
|
||||
|
||||
var/result = show_radial_menu(user, src, tool_list, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE, tooltips = FALSE)
|
||||
if (!in_range(src, user))
|
||||
return // nice try
|
||||
|
||||
switch(result)
|
||||
if("NORTH")
|
||||
lift_master_datum.MoveLiftHorizontal(NORTH, z)
|
||||
use(user)
|
||||
if("NORTHEAST")
|
||||
lift_master_datum.MoveLiftHorizontal(NORTHEAST, z)
|
||||
use(user)
|
||||
if("EAST")
|
||||
lift_master_datum.MoveLiftHorizontal(EAST, z)
|
||||
use(user)
|
||||
if("SOUTHEAST")
|
||||
lift_master_datum.MoveLiftHorizontal(SOUTHEAST, z)
|
||||
use(user)
|
||||
if("SOUTH")
|
||||
lift_master_datum.MoveLiftHorizontal(SOUTH, z)
|
||||
use(user)
|
||||
if("SOUTHWEST")
|
||||
lift_master_datum.MoveLiftHorizontal(SOUTHWEST, z)
|
||||
use(user)
|
||||
if("WEST")
|
||||
lift_master_datum.MoveLiftHorizontal(WEST, z)
|
||||
use(user)
|
||||
if("NORTHWEST")
|
||||
lift_master_datum.MoveLiftHorizontal(NORTHWEST, z)
|
||||
use(user)
|
||||
if("Cancel")
|
||||
return
|
||||
|
||||
add_fingerprint(user)
|
||||
|
||||
/obj/structure/industrial_lift/tram
|
||||
name = "tram"
|
||||
desc = "A tram for tramversing the station."
|
||||
icon = 'icons/turf/floors.dmi'
|
||||
icon_state = "titanium_yellow"
|
||||
base_icon_state = null
|
||||
smoothing_flags = NONE
|
||||
smoothing_groups = null
|
||||
canSmoothWith = null
|
||||
//kind of a centerpiece of the station, so pretty tough to destroy
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
|
||||
lift_id = TRAM_LIFT_ID
|
||||
lift_master_type = /datum/lift_master/tram
|
||||
|
||||
/// Set by the tram control console in late initialize
|
||||
var/travelling = FALSE
|
||||
|
||||
//the following are only used to give to the lift_master datum when it's first created
|
||||
|
||||
///decisecond delay between horizontal movements. cannot make the tram move faster than 1 movement per world.tick_lag. only used to give to the lift_master
|
||||
var/horizontal_speed = 0.5
|
||||
|
||||
create_multitile_platform = TRUE
|
||||
|
||||
/obj/structure/industrial_lift/tram/AddItemOnLift(datum/source, atom/movable/AM)
|
||||
. = ..()
|
||||
if(travelling)
|
||||
on_changed_glide_size(AM, AM.glide_size)
|
||||
|
||||
/obj/structure/industrial_lift/tram/proc/set_travelling(travelling)
|
||||
if (src.travelling == travelling)
|
||||
return
|
||||
|
||||
for(var/atom/movable/glider as anything in lift_load)
|
||||
if(travelling)
|
||||
glider.set_glide_size(glide_size_override)
|
||||
RegisterSignal(glider, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, .proc/on_changed_glide_size)
|
||||
else
|
||||
LAZYREMOVE(changed_gliders, glider)
|
||||
UnregisterSignal(glider, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE)
|
||||
|
||||
src.travelling = travelling
|
||||
SEND_SIGNAL(src, COMSIG_TRAM_SET_TRAVELLING, travelling)
|
||||
|
||||
/obj/structure/industrial_lift/tram/use(mob/user) //dont click the floor dingus we use computers now
|
||||
return
|
||||
|
||||
/obj/structure/industrial_lift/tram/set_currently_z_moving()
|
||||
return FALSE //trams can never z fall and shouldnt waste any processing time trying to do so
|
||||
|
||||
/**
|
||||
* Handles unlocking the tram controls for use after moving
|
||||
*
|
||||
* More safety checks to make sure the tram has actually docked properly
|
||||
* at a location before users are allowed to interact with the tram console again.
|
||||
* Tram finds its location at this point before fully unlocking controls to the user.
|
||||
*/
|
||||
/obj/structure/industrial_lift/tram/proc/unlock_controls()
|
||||
visible_message(span_notice("[src]'s controls are now unlocked."))
|
||||
for(var/obj/structure/industrial_lift/tram/tram_part as anything in lift_master_datum.lift_platforms) //only thing everyone needs to know is the new location.
|
||||
tram_part.set_travelling(FALSE)
|
||||
lift_master_datum.set_controls(LIFT_PLATFORM_UNLOCKED)
|
||||
|
||||
///debug proc to highlight the locs of the tram platform
|
||||
/obj/structure/industrial_lift/tram/proc/find_dimensions(iterations = 100)
|
||||
message_admins("num turfs: [length(locs)]")
|
||||
|
||||
var/overlay = /obj/effect/overlay/ai_detect_hud
|
||||
var/list/turfs = list()
|
||||
|
||||
for(var/turf/our_turf as anything in locs)
|
||||
new overlay(our_turf)
|
||||
turfs += our_turf
|
||||
|
||||
addtimer(CALLBACK(src, .proc/clear_turfs, turfs, iterations), 1)
|
||||
|
||||
/obj/structure/industrial_lift/tram/proc/clear_turfs(list/turfs_to_clear, iterations)
|
||||
for(var/turf/our_old_turf as anything in turfs_to_clear)
|
||||
var/obj/effect/overlay/ai_detect_hud/hud = locate() in our_old_turf
|
||||
if(hud)
|
||||
qdel(hud)
|
||||
|
||||
var/overlay = /obj/effect/overlay/ai_detect_hud
|
||||
|
||||
for(var/turf/our_turf as anything in locs)
|
||||
new overlay(our_turf)
|
||||
|
||||
iterations--
|
||||
|
||||
var/list/turfs = list()
|
||||
for(var/turf/our_turf as anything in locs)
|
||||
turfs += our_turf
|
||||
|
||||
if(iterations)
|
||||
addtimer(CALLBACK(src, .proc/clear_turfs, turfs, iterations), 1)
|
||||
397
code/modules/industrial_lift/lift_master.dm
Normal file
397
code/modules/industrial_lift/lift_master.dm
Normal file
@@ -0,0 +1,397 @@
|
||||
///associative list of the form: list(lift_id = list(all lift_master datums attached to lifts of that type))
|
||||
GLOBAL_LIST_EMPTY(active_lifts_by_type)
|
||||
|
||||
///coordinate and control movement across linked industrial_lift's. allows moving large single multitile platforms and many 1 tile platforms.
|
||||
///also is capable of linking platforms across linked z levels
|
||||
/datum/lift_master
|
||||
///the lift platforms we consider as part of this lift. ordered in order of lowest z level to highest z level after init.
|
||||
///(the sorting algorithm sucks btw)
|
||||
var/list/obj/structure/industrial_lift/lift_platforms
|
||||
|
||||
/// Typepath list of what to ignore smashing through, controls all lifts
|
||||
var/static/list/ignored_smashthroughs = list(
|
||||
/obj/machinery/power/supermatter_crystal,
|
||||
/obj/structure/holosign,
|
||||
/obj/machinery/field,
|
||||
)
|
||||
|
||||
///whether the lift handled by this lift_master datum is multitile as opposed to nxm platforms per z level
|
||||
var/multitile_platform = FALSE
|
||||
|
||||
///taken from our lift platforms. if true we go through each z level of platforms and attempt to make the lowest left corner platform
|
||||
///into one giant multitile object the size of all other platforms on that z level.
|
||||
var/create_multitile_platform = FALSE
|
||||
|
||||
///lift platforms have already been sorted in order of z level.
|
||||
var/z_sorted = FALSE
|
||||
|
||||
///lift_id taken from our base lift platform, used to put us into GLOB.active_lifts_by_type
|
||||
var/lift_id = BASIC_LIFT_ID
|
||||
|
||||
///overridable ID string to link control units to this specific lift_master datum. created by placing a lift id landmark object
|
||||
///somewhere on the tram, if its anywhere on the tram we'll find it in init and set this to whatever it specifies
|
||||
var/specific_lift_id
|
||||
///what directions we're allowed to move
|
||||
var/allowed_travel_directions = ALL
|
||||
|
||||
///if true, the lift cannot be manually moved.
|
||||
var/controls_locked = FALSE
|
||||
|
||||
/datum/lift_master/New(obj/structure/industrial_lift/lift_platform)
|
||||
lift_id = lift_platform.lift_id
|
||||
create_multitile_platform = lift_platform.create_multitile_platform
|
||||
|
||||
Rebuild_lift_plaform(lift_platform)
|
||||
ignored_smashthroughs = typecacheof(ignored_smashthroughs)
|
||||
|
||||
LAZYADDASSOCLIST(GLOB.active_lifts_by_type, lift_id, src)
|
||||
|
||||
for(var/obj/structure/industrial_lift/lift as anything in lift_platforms)
|
||||
lift.add_initial_contents()
|
||||
|
||||
/datum/lift_master/Destroy()
|
||||
for(var/obj/structure/industrial_lift/lift_platform as anything in lift_platforms)
|
||||
lift_platform.lift_master_datum = null
|
||||
lift_platforms = null
|
||||
|
||||
LAZYREMOVEASSOC(GLOB.active_lifts_by_type, lift_id, src)
|
||||
if(isnull(GLOB.active_lifts_by_type))
|
||||
GLOB.active_lifts_by_type = list()//im lazy
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/lift_master/proc/add_lift_platforms(obj/structure/industrial_lift/new_lift_platform)
|
||||
if(new_lift_platform in lift_platforms)
|
||||
return
|
||||
for(var/obj/structure/industrial_lift/other_platform in new_lift_platform.loc)
|
||||
if(other_platform != new_lift_platform)
|
||||
stack_trace("there is more than one lift platform on a tile when a lift_master adds it. this causes problems")
|
||||
qdel(other_platform)
|
||||
|
||||
new_lift_platform.lift_master_datum = src
|
||||
LAZYADD(lift_platforms, new_lift_platform)
|
||||
RegisterSignal(new_lift_platform, COMSIG_PARENT_QDELETING, .proc/remove_lift_platforms)
|
||||
|
||||
check_for_landmarks(new_lift_platform)
|
||||
|
||||
if(z_sorted)//make sure we dont lose z ordering if we get additional platforms after init
|
||||
order_platforms_by_z_level()
|
||||
|
||||
/datum/lift_master/proc/remove_lift_platforms(obj/structure/industrial_lift/old_lift_platform)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!(old_lift_platform in lift_platforms))
|
||||
return
|
||||
|
||||
old_lift_platform.lift_master_datum = null
|
||||
LAZYREMOVE(lift_platforms, old_lift_platform)
|
||||
UnregisterSignal(old_lift_platform, COMSIG_PARENT_QDELETING)
|
||||
if(!length(lift_platforms))
|
||||
qdel(src)
|
||||
|
||||
///Collect all bordered platforms via a simple floodfill algorithm. allows multiz trams because its funny
|
||||
/datum/lift_master/proc/Rebuild_lift_plaform(obj/structure/industrial_lift/base_lift_platform)
|
||||
add_lift_platforms(base_lift_platform)
|
||||
var/list/possible_expansions = list(base_lift_platform)
|
||||
|
||||
while(possible_expansions.len)
|
||||
for(var/obj/structure/industrial_lift/borderline as anything in possible_expansions)
|
||||
var/list/result = borderline.lift_platform_expansion(src)
|
||||
if(length(result))
|
||||
for(var/obj/structure/industrial_lift/lift_platform as anything in result)
|
||||
if(lift_platforms.Find(lift_platform))
|
||||
continue
|
||||
|
||||
add_lift_platforms(lift_platform)
|
||||
possible_expansions |= lift_platform
|
||||
|
||||
possible_expansions -= borderline
|
||||
|
||||
///check for any landmarks placed inside the locs of the given lift_platform
|
||||
/datum/lift_master/proc/check_for_landmarks(obj/structure/industrial_lift/new_lift_platform)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
|
||||
for(var/turf/platform_loc as anything in new_lift_platform.locs)
|
||||
var/obj/effect/landmark/lift_id/id_giver = locate() in platform_loc
|
||||
|
||||
if(id_giver)
|
||||
set_info_from_id_landmark(id_giver)
|
||||
|
||||
///set vars and such given an overriding lift_id landmark
|
||||
/datum/lift_master/proc/set_info_from_id_landmark(obj/effect/landmark/lift_id/landmark)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
|
||||
if(!istype(landmark, /obj/effect/landmark/lift_id))//lift_master subtypes can want differnet id's than the base type wants
|
||||
return
|
||||
|
||||
if(landmark.specific_lift_id)
|
||||
specific_lift_id = landmark.specific_lift_id
|
||||
|
||||
qdel(landmark)
|
||||
|
||||
///orders the lift platforms in order of lowest z level to highest z level.
|
||||
/datum/lift_master/proc/order_platforms_by_z_level()
|
||||
//contains nested lists for every z level in the world. why? because its really easy to sort
|
||||
var/list/platforms_by_z = list()
|
||||
platforms_by_z.len = world.maxz
|
||||
|
||||
for(var/z in 1 to world.maxz)
|
||||
platforms_by_z[z] = list()
|
||||
|
||||
for(var/obj/structure/industrial_lift/lift_platform as anything in lift_platforms)
|
||||
if(QDELETED(lift_platform) || !lift_platform.z)
|
||||
lift_platforms -= lift_platform
|
||||
continue
|
||||
|
||||
platforms_by_z[lift_platform.z] += lift_platform
|
||||
|
||||
if(create_multitile_platform)
|
||||
for(var/list/z_list as anything in platforms_by_z)
|
||||
if(!length(z_list))
|
||||
continue
|
||||
|
||||
create_multitile_platform_for_z_level(z_list)//this will subtract all but one platform from the list
|
||||
|
||||
var/list/output = list()
|
||||
|
||||
for(var/list/z_list as anything in platforms_by_z)
|
||||
output += z_list
|
||||
|
||||
lift_platforms = output
|
||||
|
||||
z_sorted = TRUE
|
||||
|
||||
///goes through all platforms in the given list and finds the one in the lower left corner
|
||||
/datum/lift_master/proc/create_multitile_platform_for_z_level(list/obj/structure/industrial_lift/platforms_in_z)
|
||||
var/min_x = INFINITY
|
||||
var/max_x = 0
|
||||
|
||||
var/min_y = INFINITY
|
||||
var/max_y = 0
|
||||
|
||||
var/z = 0
|
||||
|
||||
for(var/obj/structure/industrial_lift/lift_to_sort as anything in platforms_in_z)
|
||||
if(!z)
|
||||
if(!lift_to_sort.z)
|
||||
stack_trace("create_multitile_platform_for_z_level() was given a platform in nullspace or not on a turf!")
|
||||
platforms_in_z -= lift_to_sort
|
||||
continue
|
||||
|
||||
z = lift_to_sort.z
|
||||
|
||||
if(z != lift_to_sort.z)
|
||||
stack_trace("create_multitile_platform_for_z_level() was given lifts on different z levels!")
|
||||
platforms_in_z -= lift_to_sort
|
||||
continue
|
||||
|
||||
min_x = min(min_x, lift_to_sort.x)
|
||||
max_x = max(max_x, lift_to_sort.x)
|
||||
|
||||
min_y = min(min_y, lift_to_sort.y)
|
||||
max_y = max(max_y, lift_to_sort.y)
|
||||
|
||||
var/turf/lower_left_corner_loc = locate(min_x, min_y, z)
|
||||
if(!lower_left_corner_loc)
|
||||
CRASH("was unable to find a turf at the lower left corner of this z")
|
||||
|
||||
var/obj/structure/industrial_lift/lower_left_corner_lift = locate() in lower_left_corner_loc
|
||||
|
||||
if(!lower_left_corner_lift)
|
||||
CRASH("there was no lift in the lower left corner of the given lifts")
|
||||
|
||||
platforms_in_z.Cut()
|
||||
platforms_in_z += lower_left_corner_lift//we want to change the list given to us not create a new one. so we do this
|
||||
|
||||
lower_left_corner_lift.create_multitile_platform(min_x, min_y, max_x, max_y, z)
|
||||
|
||||
///returns the closest lift to the specified atom, prioritizing lifts on the same z level. used for comparing distance
|
||||
/datum/lift_master/proc/return_closest_platform_to(atom/comparison, allow_multiple_answers = FALSE)
|
||||
if(!istype(comparison) || !comparison.z)
|
||||
return FALSE
|
||||
|
||||
var/list/obj/structure/industrial_lift/candidate_platforms = list()
|
||||
|
||||
for(var/obj/structure/industrial_lift/platform as anything in lift_platforms)
|
||||
if(platform.z == comparison.z)
|
||||
candidate_platforms += platform
|
||||
|
||||
var/obj/structure/industrial_lift/winner = candidate_platforms[1]
|
||||
var/winner_distance = get_dist(comparison, winner)
|
||||
|
||||
var/list/tied_winners = list(winner)
|
||||
|
||||
for(var/obj/structure/industrial_lift/platform_to_sort as anything in candidate_platforms)
|
||||
var/platform_distance = get_dist(comparison, platform_to_sort)
|
||||
|
||||
if(platform_distance < winner_distance)
|
||||
winner = platform_to_sort
|
||||
winner_distance = platform_distance
|
||||
|
||||
if(allow_multiple_answers)
|
||||
tied_winners = list(winner)
|
||||
|
||||
else if(platform_distance == winner_distance && allow_multiple_answers)
|
||||
tied_winners += platform_to_sort
|
||||
|
||||
if(allow_multiple_answers)
|
||||
return tied_winners
|
||||
|
||||
return winner
|
||||
|
||||
///returns all industrial_lifts associated with this tram on the given z level or given atoms z level
|
||||
/datum/lift_master/proc/get_platforms_on_level(atom/atom_reference_OR_z_level_number)
|
||||
var/z = atom_reference_OR_z_level_number
|
||||
if(isatom(atom_reference_OR_z_level_number))
|
||||
z = atom_reference_OR_z_level_number.z
|
||||
|
||||
if(!isnum(z) || z < 0 || z > world.maxz)
|
||||
return null
|
||||
|
||||
var/list/platforms_in_z = list()
|
||||
|
||||
for(var/obj/structure/industrial_lift/lift_to_check as anything in lift_platforms)
|
||||
if(lift_to_check.z)
|
||||
platforms_in_z += lift_to_check
|
||||
|
||||
return platforms_in_z
|
||||
|
||||
/**
|
||||
* Moves the lift UP or DOWN, this is what users invoke with their hand.
|
||||
* This is a SAFE proc, ensuring every part of the lift moves SANELY.
|
||||
* It also locks controls for the (miniscule) duration of the movement, so the elevator cannot be broken by spamming.
|
||||
* Arguments:
|
||||
* going - UP or DOWN directions, where the lift should go. Keep in mind by this point checks of whether it should go up or down have already been done.
|
||||
* user - Whomever made the lift movement.
|
||||
*/
|
||||
/datum/lift_master/proc/MoveLift(going, mob/user)
|
||||
set_controls(LIFT_PLATFORM_LOCKED)
|
||||
//lift_platforms are sorted in order of lowest z to highest z, so going upwards we need to move them in reverse order to not collide
|
||||
if(going == UP)
|
||||
var/obj/structure/industrial_lift/platform_to_move
|
||||
var/current_index = length(lift_platforms)
|
||||
|
||||
while(current_index > 0)
|
||||
platform_to_move = lift_platforms[current_index]
|
||||
current_index--
|
||||
|
||||
platform_to_move.travel(going)
|
||||
|
||||
else if(going == DOWN)
|
||||
for(var/obj/structure/industrial_lift/lift_platform as anything in lift_platforms)
|
||||
lift_platform.travel(going)
|
||||
set_controls(LIFT_PLATFORM_UNLOCKED)
|
||||
|
||||
/**
|
||||
* Moves the lift, this is what users invoke with their hand.
|
||||
* This is a SAFE proc, ensuring every part of the lift moves SANELY.
|
||||
* It also locks controls for the (miniscule) duration of the movement, so the elevator cannot be broken by spamming.
|
||||
*/
|
||||
/datum/lift_master/proc/MoveLiftHorizontal(going)
|
||||
set_controls(LIFT_PLATFORM_LOCKED)
|
||||
|
||||
if(multitile_platform)
|
||||
for(var/obj/structure/industrial_lift/platform_to_move as anything in lift_platforms)
|
||||
platform_to_move.travel(going)
|
||||
|
||||
set_controls(LIFT_PLATFORM_UNLOCKED)
|
||||
return
|
||||
|
||||
var/max_x = 0
|
||||
var/max_y = 0
|
||||
var/max_z = 0
|
||||
var/min_x = world.maxx
|
||||
var/min_y = world.maxy
|
||||
var/min_z = world.maxz
|
||||
|
||||
for(var/obj/structure/industrial_lift/lift_platform as anything in lift_platforms)
|
||||
max_z = max(max_z, lift_platform.z)
|
||||
min_z = min(min_z, lift_platform.z)
|
||||
|
||||
min_x = min(min_x, lift_platform.x)
|
||||
max_x = max(max_x, lift_platform.x)
|
||||
//this assumes that all z levels have identical horizontal bounding boxes
|
||||
//but if youre still using a non multitile tram platform at this point
|
||||
//then its your own problem. it wont runtime it will jsut be slower than it needs to be if this assumption isnt
|
||||
//the case
|
||||
|
||||
min_y = min(min_y, lift_platform.y)
|
||||
max_y = max(max_y, lift_platform.y)
|
||||
|
||||
for(var/z in min_z to max_z)
|
||||
//This must be safe way to border tile to tile move of bordered platforms, that excludes platform overlapping.
|
||||
if(going & WEST)
|
||||
//Go along the X axis from min to max, from left to right
|
||||
for(var/x in min_x to max_x)
|
||||
if(going & NORTH)
|
||||
//Go along the Y axis from max to min, from up to down
|
||||
for(var/y in max_y to min_y step -1)
|
||||
var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z))
|
||||
lift_platform?.travel(going)
|
||||
|
||||
else if(going & SOUTH)
|
||||
//Go along the Y axis from min to max, from down to up
|
||||
for(var/y in min_y to max_y)
|
||||
var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z))
|
||||
lift_platform?.travel(going)
|
||||
|
||||
else
|
||||
for(var/y in min_y to max_y)
|
||||
var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z))
|
||||
lift_platform?.travel(going)
|
||||
else
|
||||
//Go along the X axis from max to min, from right to left
|
||||
for(var/x in max_x to min_x step -1)
|
||||
if(going & NORTH)
|
||||
//Go along the Y axis from max to min, from up to down
|
||||
for(var/y in max_y to min_y step -1)
|
||||
var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z))
|
||||
lift_platform?.travel(going)
|
||||
|
||||
else if (going & SOUTH)
|
||||
for(var/y in min_y to max_y)
|
||||
var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z))
|
||||
lift_platform?.travel(going)
|
||||
|
||||
else
|
||||
//Go along the Y axis from min to max, from down to up
|
||||
for(var/y in min_y to max_y)
|
||||
var/obj/structure/industrial_lift/lift_platform = locate(/obj/structure/industrial_lift, locate(x, y, z))
|
||||
lift_platform?.travel(going)
|
||||
|
||||
set_controls(LIFT_PLATFORM_UNLOCKED)
|
||||
|
||||
///Check destination turfs
|
||||
/datum/lift_master/proc/Check_lift_move(check_dir)
|
||||
for(var/obj/structure/industrial_lift/lift_platform as anything in lift_platforms)
|
||||
for(var/turf/bound_turf in lift_platform.locs)
|
||||
var/turf/T = get_step_multiz(lift_platform, check_dir)
|
||||
if(!T)//the edges of multi-z maps
|
||||
return FALSE
|
||||
if(check_dir == UP && !istype(T, /turf/open/openspace)) // We don't want to go through the ceiling!
|
||||
return FALSE
|
||||
if(check_dir == DOWN && !istype(get_turf(lift_platform), /turf/open/openspace)) // No going through the floor!
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Sets all lift parts's controls_locked variable. Used to prevent moving mid movement, or cooldowns.
|
||||
*/
|
||||
/datum/lift_master/proc/set_controls(state)
|
||||
controls_locked = state
|
||||
|
||||
/**
|
||||
* resets the contents of all platforms to their original state in case someone put a bunch of shit onto the tram.
|
||||
* intended to be called by admins. passes all arguments to reset_contents() for each of our platforms.
|
||||
*
|
||||
* Arguments:
|
||||
* * consider_anything_past - number. if > 0 our platforms will only handle foreign contents that exceed this number in each of their locs
|
||||
* * foreign_objects - bool. if true our platforms will consider /atom/movable's that arent mobs as part of foreign contents
|
||||
* * foreign_non_player_mobs - bool. if true our platforms consider mobs that dont have a mind to be foreign
|
||||
* * consider_player_mobs - bool. if true our platforms consider player mobs to be foreign. only works if foreign_non_player_mobs is true as well
|
||||
*/
|
||||
/datum/lift_master/proc/reset_lift_contents(consider_anything_past = 0, foreign_objects = TRUE, foreign_non_player_mobs = TRUE, consider_player_mobs = FALSE)
|
||||
for(var/obj/structure/industrial_lift/lift_to_reset in lift_platforms)
|
||||
lift_to_reset.reset_contents(consider_anything_past, foreign_objects, foreign_non_player_mobs, consider_player_mobs)
|
||||
|
||||
return TRUE
|
||||
@@ -6,12 +6,14 @@
|
||||
circuit = /obj/item/circuitboard/computer/tram_controls
|
||||
flags_1 = NODECONSTRUCT_1 | SUPERMATTER_IGNORES_1
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
light_color = LIGHT_COLOR_GREEN
|
||||
///The ID of the tram we control
|
||||
var/tram_id = "tram_station"
|
||||
|
||||
light_range = 0 //we dont want to spam SSlighting with source updates every movement
|
||||
|
||||
///Weakref to the tram piece we control
|
||||
var/datum/weakref/tram_ref
|
||||
|
||||
var/specific_lift_id = MAIN_STATION_TRAM
|
||||
|
||||
/obj/machinery/computer/tram_controls/Initialize(mapload, obj/item/circuitboard/C)
|
||||
. = ..()
|
||||
AddComponent(/datum/component/usb_port, list(/obj/item/circuit_component/tram_controls))
|
||||
@@ -19,7 +21,6 @@
|
||||
|
||||
/obj/machinery/computer/tram_controls/LateInitialize()
|
||||
. = ..()
|
||||
//find the tram, late so the tram is all... set up so when this is called? i'm seriously stupid and 90% of what i do consists of barely educated guessing :)
|
||||
find_tram()
|
||||
|
||||
/**
|
||||
@@ -28,19 +29,17 @@
|
||||
* Locates tram parts in the lift global list after everything is done.
|
||||
*/
|
||||
/obj/machinery/computer/tram_controls/proc/find_tram()
|
||||
for(var/obj/structure/industrial_lift/tram/central/tram as anything in GLOB.central_trams)
|
||||
if(tram.tram_id != tram_id)
|
||||
continue
|
||||
tram_ref = WEAKREF(tram)
|
||||
break
|
||||
for(var/datum/lift_master/lift as anything in GLOB.active_lifts_by_type[TRAM_LIFT_ID])
|
||||
if(lift.specific_lift_id == specific_lift_id)
|
||||
tram_ref = WEAKREF(lift)
|
||||
|
||||
/obj/machinery/computer/tram_controls/ui_state(mob/user)
|
||||
return GLOB.not_incapacitated_state
|
||||
|
||||
/obj/machinery/computer/tram_controls/ui_status(mob/user,/datum/tgui/ui)
|
||||
var/obj/structure/industrial_lift/tram/central/tram_part = tram_ref?.resolve()
|
||||
var/datum/lift_master/tram/tram = tram_ref?.resolve()
|
||||
|
||||
if(tram_part?.travelling)
|
||||
if(tram?.travelling)
|
||||
return UI_CLOSE
|
||||
if(!in_range(user, src) && !isobserver(user))
|
||||
return UI_CLOSE
|
||||
@@ -54,11 +53,11 @@
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/computer/tram_controls/ui_data(mob/user)
|
||||
var/obj/structure/industrial_lift/tram/central/tram_part = tram_ref?.resolve()
|
||||
var/datum/lift_master/tram/tram_lift = tram_ref?.resolve()
|
||||
var/list/data = list()
|
||||
data["moving"] = tram_part?.travelling
|
||||
data["broken"] = tram_part ? FALSE : TRUE
|
||||
var/obj/effect/landmark/tram/current_loc = tram_part?.from_where
|
||||
data["moving"] = tram_lift?.travelling
|
||||
data["broken"] = tram_lift ? FALSE : TRUE
|
||||
var/obj/effect/landmark/tram/current_loc = tram_lift?.from_where
|
||||
if(current_loc)
|
||||
data["tram_location"] = current_loc.name
|
||||
return data
|
||||
@@ -77,9 +76,7 @@
|
||||
*/
|
||||
/obj/machinery/computer/tram_controls/proc/get_destinations()
|
||||
. = list()
|
||||
for(var/obj/effect/landmark/tram/destination as anything in GLOB.tram_landmarks)
|
||||
if(destination.tram_id != tram_id)
|
||||
continue
|
||||
for(var/obj/effect/landmark/tram/destination as anything in GLOB.tram_landmarks[specific_lift_id])
|
||||
var/list/this_destination = list()
|
||||
this_destination["name"] = destination.name
|
||||
this_destination["dest_icons"] = destination.tgui_icons
|
||||
@@ -94,9 +91,7 @@
|
||||
switch (action)
|
||||
if ("send")
|
||||
var/obj/effect/landmark/tram/to_where
|
||||
for (var/obj/effect/landmark/tram/destination as anything in GLOB.tram_landmarks)
|
||||
if(destination.tram_id != tram_id)
|
||||
continue
|
||||
for (var/obj/effect/landmark/tram/destination as anything in GLOB.tram_landmarks[specific_lift_id])
|
||||
if(destination.destination_id == params["destination"])
|
||||
to_where = destination
|
||||
break
|
||||
@@ -108,14 +103,13 @@
|
||||
|
||||
/// Attempts to sends the tram to the given destination
|
||||
/obj/machinery/computer/tram_controls/proc/try_send_tram(obj/effect/landmark/tram/to_where)
|
||||
var/obj/structure/industrial_lift/tram/central/tram_part = tram_ref?.resolve()
|
||||
var/datum/lift_master/tram/tram_part = tram_ref?.resolve()
|
||||
if(!tram_part)
|
||||
return FALSE
|
||||
if(tram_part.travelling)
|
||||
return FALSE
|
||||
if(tram_part.controls_locked) // someone else started
|
||||
if(tram_part.controls_locked) // someone else started already
|
||||
return FALSE
|
||||
tram_part.tram_travel(to_where)
|
||||
visible_message("The tram has been called to [to_where.name]")
|
||||
return TRUE
|
||||
|
||||
/obj/item/circuit_component/tram_controls
|
||||
@@ -147,12 +141,12 @@
|
||||
. = ..()
|
||||
if (istype(shell, /obj/machinery/computer/tram_controls))
|
||||
computer = shell
|
||||
var/obj/structure/industrial_lift/tram/central/tram_part = computer.tram_ref?.resolve()
|
||||
var/datum/lift_master/tram/tram_part = computer.tram_ref?.resolve()
|
||||
RegisterSignal(tram_part, COMSIG_TRAM_SET_TRAVELLING, .proc/on_tram_set_travelling)
|
||||
RegisterSignal(tram_part, COMSIG_TRAM_TRAVEL, .proc/on_tram_travel)
|
||||
|
||||
/obj/item/circuit_component/tram_controls/unregister_usb_parent(atom/movable/shell)
|
||||
var/obj/structure/industrial_lift/tram/central/tram_part = computer.tram_ref?.resolve()
|
||||
var/datum/lift_master/tram/tram_part = computer.tram_ref?.resolve()
|
||||
computer = null
|
||||
UnregisterSignal(tram_part, list(COMSIG_TRAM_SET_TRAVELLING, COMSIG_TRAM_TRAVEL))
|
||||
return ..()
|
||||
@@ -168,9 +162,7 @@
|
||||
return
|
||||
|
||||
var/destination
|
||||
for(var/obj/effect/landmark/tram/possible_destination as anything in GLOB.tram_landmarks)
|
||||
if(possible_destination.tram_id != computer.tram_id)
|
||||
continue
|
||||
for(var/obj/effect/landmark/tram/possible_destination as anything in GLOB.tram_landmarks[computer.specific_lift_id])
|
||||
if(possible_destination.name == new_destination.value)
|
||||
destination = possible_destination
|
||||
break
|
||||
47
code/modules/industrial_lift/tram_landmark.dm
Normal file
47
code/modules/industrial_lift/tram_landmark.dm
Normal file
@@ -0,0 +1,47 @@
|
||||
GLOBAL_LIST_EMPTY(tram_landmarks)
|
||||
|
||||
/obj/effect/landmark/tram
|
||||
name = "tram destination" //the tram buttons will mention this.
|
||||
icon_state = "tram"
|
||||
|
||||
///the id of the tram we're linked to.
|
||||
var/specific_lift_id = MAIN_STATION_TRAM
|
||||
/// The ID of that particular destination.
|
||||
var/destination_id
|
||||
/// Icons for the tgui console to list out for what is at this location
|
||||
var/list/tgui_icons = list()
|
||||
|
||||
/obj/effect/landmark/tram/Initialize(mapload)
|
||||
. = ..()
|
||||
LAZYADDASSOCLIST(GLOB.tram_landmarks, specific_lift_id, src)
|
||||
|
||||
/obj/effect/landmark/tram/Destroy()
|
||||
LAZYREMOVEASSOC(GLOB.tram_landmarks, specific_lift_id, src)
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/effect/landmark/tram/left_part
|
||||
name = "West Wing"
|
||||
destination_id = "left_part"
|
||||
tgui_icons = list("Arrivals" = "plane-arrival", "Command" = "bullhorn", "Security" = "gavel")
|
||||
|
||||
/obj/effect/landmark/tram/middle_part
|
||||
name = "Central Wing"
|
||||
destination_id = "middle_part"
|
||||
tgui_icons = list("Service" = "cocktail", "Medical" = "plus", "Engineering" = "wrench")
|
||||
|
||||
/obj/effect/landmark/tram/right_part
|
||||
name = "East Wing"
|
||||
destination_id = "right_part"
|
||||
tgui_icons = list("Departures" = "plane-departure", "Cargo" = "box", "Science" = "flask")
|
||||
|
||||
/**
|
||||
* lift_id landmarks. used to map in specific_lift_id to trams. when the trams lift_master encounters one on a trams tile
|
||||
* it sets its specific_lift_id to that landmark. allows you to have multiple trams and multiple controls linking to their specific tram
|
||||
*/
|
||||
/obj/effect/landmark/lift_id
|
||||
name = "lift id setter"
|
||||
icon_state = "lift_id"
|
||||
|
||||
///what specific id we give to the tram we're placed on, should explicitely set this if its a subtype, or weird things might happen
|
||||
var/specific_lift_id = MAIN_STATION_TRAM
|
||||
174
code/modules/industrial_lift/tram_lift_master.dm
Normal file
174
code/modules/industrial_lift/tram_lift_master.dm
Normal file
@@ -0,0 +1,174 @@
|
||||
/datum/lift_master/tram
|
||||
|
||||
///whether this tram is traveling across vertical and/or horizontal axis for some distance. not all lifts use this
|
||||
var/travelling = FALSE
|
||||
///if we're travelling, what direction are we going
|
||||
var/travel_direction = NONE
|
||||
///if we're travelling, how far do we have to go
|
||||
var/travel_distance = 0
|
||||
|
||||
///multiplier on how much damage/force the tram imparts on things it hits
|
||||
var/collision_lethality = 1
|
||||
|
||||
/// reference to the destination landmark we consider ourselves "at". since we potentially span multiple z levels we dont actually
|
||||
/// know where on us this platform is. as long as we know THAT its on us we can just move the distance and direction between this
|
||||
/// and the destination landmark.
|
||||
var/obj/effect/landmark/tram/from_where
|
||||
|
||||
///decisecond delay between horizontal movement. cannot make the tram move faster than 1 movement per world.tick_lag.
|
||||
///this var is poorly named its actually horizontal movement delay but whatever.
|
||||
var/horizontal_speed = 0.5
|
||||
|
||||
///version of horizontal_speed that gets set in init and is considered our base speed if our lift gets slowed down
|
||||
var/base_horizontal_speed = 0.5
|
||||
|
||||
///the world.time we should next move at. in case our speed is set to less than 1 movement per tick
|
||||
var/next_move = INFINITY
|
||||
|
||||
///whether we have been slowed down automatically
|
||||
var/slowed_down = FALSE
|
||||
|
||||
///how many times we moved while costing more than SStramprocess.max_time milliseconds per movement.
|
||||
///if this exceeds SStramprocess.max_exceeding_moves
|
||||
var/times_exceeded = 0
|
||||
|
||||
///how many times we moved while costing less than 0.5 * SStramprocess.max_time milliseconds per movement
|
||||
var/times_below = 0
|
||||
|
||||
/datum/lift_master/tram/New(obj/structure/industrial_lift/tram/lift_platform)
|
||||
. = ..()
|
||||
horizontal_speed = lift_platform.horizontal_speed
|
||||
base_horizontal_speed = lift_platform.horizontal_speed
|
||||
|
||||
check_starting_landmark()
|
||||
|
||||
/datum/lift_master/tram/vv_edit_var(var_name, var_value)
|
||||
. = ..()
|
||||
if(var_name == "base_horizontal_speed")
|
||||
horizontal_speed = max(horizontal_speed, base_horizontal_speed)
|
||||
|
||||
/datum/lift_master/tram/add_lift_platforms(obj/structure/industrial_lift/new_lift_platform)
|
||||
. = ..()
|
||||
RegisterSignal(new_lift_platform, COMSIG_MOVABLE_BUMP, .proc/gracefully_break)
|
||||
|
||||
/datum/lift_master/tram/check_for_landmarks(obj/structure/industrial_lift/tram/new_lift_platform)
|
||||
. = ..()
|
||||
for(var/turf/platform_loc as anything in new_lift_platform.locs)
|
||||
var/obj/effect/landmark/tram/initial_destination = locate() in platform_loc
|
||||
|
||||
if(initial_destination)
|
||||
from_where = initial_destination
|
||||
|
||||
/datum/lift_master/tram/proc/check_starting_landmark()
|
||||
if(!from_where)
|
||||
CRASH("a tram lift_master was initialized without any tram landmark to give it direction!")
|
||||
|
||||
SStramprocess.can_fire = TRUE
|
||||
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Signal for when the tram runs into a field of which it cannot go through.
|
||||
* Stops the train's travel fully, sends a message, and destroys the train.
|
||||
* Arguments:
|
||||
* bumped_atom - The atom this tram bumped into
|
||||
*/
|
||||
/datum/lift_master/tram/proc/gracefully_break(atom/bumped_atom)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(istype(bumped_atom, /obj/machinery/field))
|
||||
return
|
||||
|
||||
travel_distance = 0
|
||||
|
||||
bumped_atom.visible_message(span_userdanger("[src] crashes into the field violently!"))
|
||||
for(var/obj/structure/industrial_lift/tram/tram_part as anything in lift_platforms)
|
||||
tram_part.set_travelling(FALSE)
|
||||
if(prob(15) || locate(/mob/living) in tram_part.lift_load) //always go boom on people on the track
|
||||
explosion(tram_part, devastation_range = rand(0, 1), heavy_impact_range = 2, light_impact_range = 3) //50% chance of gib
|
||||
qdel(tram_part)
|
||||
|
||||
/**
|
||||
* Handles moving the tram
|
||||
*
|
||||
* Tells the individual tram parts where to actually go and has an extra safety check
|
||||
* incase multiple inputs get through, preventing conflicting directions and the tram
|
||||
* literally ripping itself apart. all of the actual movement is handled by SStramprocess
|
||||
*/
|
||||
/datum/lift_master/tram/proc/tram_travel(obj/effect/landmark/tram/to_where)
|
||||
if(to_where == from_where)
|
||||
return
|
||||
|
||||
travel_direction = get_dir(from_where, to_where)
|
||||
travel_distance = get_dist(from_where, to_where)
|
||||
from_where = to_where
|
||||
set_travelling(TRUE)
|
||||
set_controls(LIFT_PLATFORM_LOCKED)
|
||||
SEND_SIGNAL(src, COMSIG_TRAM_TRAVEL, from_where, to_where)
|
||||
|
||||
for(var/obj/structure/industrial_lift/tram/tram_part as anything in lift_platforms) //only thing everyone needs to know is the new location.
|
||||
if(tram_part.travelling) //wee woo wee woo there was a double action queued. damn multi tile structs
|
||||
return //we don't care to undo locked controls, though, as that will resolve itself
|
||||
|
||||
tram_part.glide_size_override = DELAY_TO_GLIDE_SIZE(horizontal_speed)
|
||||
tram_part.set_travelling(TRUE)
|
||||
|
||||
next_move = world.time + horizontal_speed
|
||||
|
||||
START_PROCESSING(SStramprocess, src)
|
||||
|
||||
/datum/lift_master/tram/process(delta_time)
|
||||
if(!travel_distance)
|
||||
addtimer(CALLBACK(src, .proc/unlock_controls), 3 SECONDS)
|
||||
return PROCESS_KILL
|
||||
else if(world.time >= next_move)
|
||||
var/start_time = TICK_USAGE
|
||||
travel_distance--
|
||||
|
||||
MoveLiftHorizontal(travel_direction)
|
||||
|
||||
var/duration = TICK_USAGE_TO_MS(start_time)
|
||||
if(slowed_down)
|
||||
if(duration <= (SStramprocess.max_time / 2))
|
||||
times_below++
|
||||
else
|
||||
times_below = 0
|
||||
|
||||
if(times_below >= SStramprocess.max_cheap_moves)
|
||||
horizontal_speed = base_horizontal_speed
|
||||
slowed_down = FALSE
|
||||
times_below = 0
|
||||
|
||||
else if(duration > SStramprocess.max_time)
|
||||
times_exceeded++
|
||||
|
||||
if(times_exceeded >= SStramprocess.max_exceeding_moves)
|
||||
message_admins("The tram at [ADMIN_JMP(lift_platforms[1])] is taking more than [SStramprocess.max_time] milliseconds per movement, halving its movement speed. if this continues to be a problem you can call reset_lift_contents() on the trams lift_master_datum to reset it to its original state and clear added objects")
|
||||
horizontal_speed = base_horizontal_speed * 2 //halves its speed
|
||||
slowed_down = TRUE
|
||||
times_exceeded = 0
|
||||
else
|
||||
times_exceeded = max(times_exceeded - 1, 0)
|
||||
|
||||
next_move = world.time + horizontal_speed
|
||||
|
||||
/**
|
||||
* Handles unlocking the tram controls for use after moving
|
||||
*
|
||||
* More safety checks to make sure the tram has actually docked properly
|
||||
* at a location before users are allowed to interact with the tram console again.
|
||||
* Tram finds its location at this point before fully unlocking controls to the user.
|
||||
*/
|
||||
/datum/lift_master/tram/proc/unlock_controls()
|
||||
set_travelling(FALSE)
|
||||
set_controls(LIFT_PLATFORM_UNLOCKED)
|
||||
for(var/obj/structure/industrial_lift/tram/tram_part as anything in lift_platforms) //only thing everyone needs to know is the new location.
|
||||
tram_part.set_travelling(FALSE)
|
||||
|
||||
|
||||
/datum/lift_master/tram/proc/set_travelling(new_travelling)
|
||||
if(travelling == new_travelling)
|
||||
return
|
||||
|
||||
travelling = new_travelling
|
||||
SEND_SIGNAL(src, COMSIG_TRAM_SET_TRAVELLING, travelling)
|
||||
39
code/modules/industrial_lift/tram_override_objects.dm
Normal file
39
code/modules/industrial_lift/tram_override_objects.dm
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* the tram has a few objects mapped onto it at roundstart, by default many of those objects have unwanted properties
|
||||
* for example grilles and windows have the atmos_sensitive element applied to them, which makes them register to
|
||||
* themselves moving to re register signals onto the turf via connect_loc. this is bad and dumb since it makes the tram
|
||||
* more expensive to move.
|
||||
*
|
||||
* if you map something on to the tram, make SURE if possible that it doesnt have anythign reacting to its own movement
|
||||
* it will make the tram more expensive to move and we dont want that because we dont want to return to the days where
|
||||
* the tram took a third of the tick per movement when its just carrying its default mapped in objects
|
||||
*/
|
||||
/obj/structure/grille/tram/Initialize(mapload)
|
||||
. = ..()
|
||||
RemoveElement(/datum/element/atmos_sensitive, mapload)
|
||||
//atmos_sensitive applies connect_loc which 1. reacts to movement in order to 2. unregister and register signals to
|
||||
//the old and new locs. we dont want that, pretend these grilles and windows are plastic or something idk
|
||||
|
||||
/obj/structure/window/reinforced/shuttle/tram/Initialize(mapload, direct)
|
||||
. = ..()
|
||||
RemoveElement(/datum/element/atmos_sensitive, mapload)
|
||||
|
||||
/obj/structure/shuttle/engine/propulsion/in_wall/tram
|
||||
//if this has opacity, then every movement of the tram causes lighting updates
|
||||
//DO NOT put something on the tram roundstart that has opacity, it WILL overload SSlighting
|
||||
opacity = FALSE
|
||||
|
||||
/obj/machinery/door/window/left/tram
|
||||
/obj/machinery/door/window/right/tram
|
||||
|
||||
/obj/machinery/door/window/left/tram/Initialize(mapload, set_dir, unres_sides)
|
||||
. = ..()
|
||||
RemoveElement(/datum/element/atmos_sensitive, mapload)
|
||||
|
||||
/obj/machinery/door/window/right/tram/Initialize(mapload, set_dir, unres_sides)
|
||||
. = ..()
|
||||
RemoveElement(/datum/element/atmos_sensitive, mapload)
|
||||
|
||||
MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/door/window/left/tram, 0)
|
||||
MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/door/window/right/tram, 0)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
base_icon_state = "wall"
|
||||
layer = LOW_OBJ_LAYER
|
||||
density = TRUE
|
||||
opacity = TRUE
|
||||
opacity = FALSE
|
||||
max_integrity = 100
|
||||
smoothing_flags = SMOOTH_BITMASK
|
||||
smoothing_groups = list(SMOOTH_GROUP_CLOSED_TURFS, SMOOTH_GROUP_WALLS)
|
||||
@@ -87,5 +87,5 @@
|
||||
L.apply_status_effect(/datum/status_effect/good_music)
|
||||
if(!(M?.client?.prefs?.toggles & SOUND_INSTRUMENTS))
|
||||
continue
|
||||
M.playsound_local(source, null, volume * using_instrument.volume_multiplier, S = music_played)
|
||||
M.playsound_local(source, null, volume * using_instrument.volume_multiplier, sound_to_use = music_played)
|
||||
// Could do environment and echo later but not for now
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
|
||||
#undef NONSENSICAL_VALUE
|
||||
|
||||
// Will update the light (duh).
|
||||
// Creates or destroys it if needed, makes it update values, makes sure it's got the correct source turf...
|
||||
/// Will update the light (duh).
|
||||
/// Creates or destroys it if needed, makes it update values, makes sure it's got the correct source turf...
|
||||
/atom/proc/update_light()
|
||||
set waitfor = FALSE
|
||||
if (QDELETED(src))
|
||||
@@ -80,13 +80,6 @@
|
||||
return
|
||||
recalculate_directional_opacity()
|
||||
|
||||
|
||||
/atom/movable/Moved(atom/OldLoc, Dir)
|
||||
. = ..()
|
||||
for (var/datum/light_source/light as anything in light_sources) // Cycle through the light sources on this atom and tell them to update.
|
||||
light.source_atom.update_light()
|
||||
|
||||
|
||||
/atom/proc/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = COLOR_WHITE, _duration = FLASH_LIGHT_DURATION)
|
||||
return
|
||||
|
||||
|
||||
@@ -40,10 +40,10 @@
|
||||
|
||||
/datum/light_source/New(atom/owner, atom/top)
|
||||
source_atom = owner // Set our new owner.
|
||||
LAZYADD(source_atom.light_sources, src)
|
||||
add_to_light_sources(source_atom.light_sources)
|
||||
top_atom = top
|
||||
if (top_atom != source_atom)
|
||||
LAZYADD(top_atom.light_sources, src)
|
||||
add_to_light_sources(top_atom.light_sources)
|
||||
|
||||
source_turf = top_atom
|
||||
pixel_turf = get_turf_pixel(top_atom) || source_turf
|
||||
@@ -59,10 +59,10 @@
|
||||
/datum/light_source/Destroy(force)
|
||||
remove_lum()
|
||||
if (source_atom)
|
||||
LAZYREMOVE(source_atom.light_sources, src)
|
||||
remove_from_light_sources(source_atom.light_sources)
|
||||
|
||||
if (top_atom)
|
||||
LAZYREMOVE(top_atom.light_sources, src)
|
||||
remove_from_light_sources(top_atom.light_sources)
|
||||
|
||||
if (needs_update)
|
||||
SSlighting.sources_queue -= src
|
||||
@@ -74,6 +74,35 @@
|
||||
|
||||
return ..()
|
||||
|
||||
///add this light source to new_atom_host's light_sources list. updating movement registrations as needed
|
||||
/datum/light_source/proc/add_to_light_sources(atom/new_atom_host)
|
||||
if(QDELETED(new_atom_host))
|
||||
return FALSE
|
||||
|
||||
LAZYADD(new_atom_host.light_sources, src)
|
||||
if(ismovable(new_atom_host) && new_atom_host == source_atom)
|
||||
RegisterSignal(new_atom_host, COMSIG_MOVABLE_MOVED, .proc/update_host_lights)
|
||||
return TRUE
|
||||
|
||||
///remove this light source from old_atom_host's light_sources list, unsetting movement registrations
|
||||
/datum/light_source/proc/remove_from_light_sources(atom/old_atom_host)
|
||||
if(QDELETED(old_atom_host))
|
||||
return FALSE
|
||||
|
||||
LAZYREMOVE(old_atom_host.light_sources, src)
|
||||
if(ismovable(old_atom_host) && old_atom_host == source_atom)
|
||||
UnregisterSignal(old_atom_host, COMSIG_MOVABLE_MOVED)
|
||||
return TRUE
|
||||
|
||||
///signal handler for when our host atom moves and we need to update our effects
|
||||
/datum/light_source/proc/update_host_lights(atom/movable/host)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(QDELETED(host))
|
||||
return
|
||||
|
||||
host.update_light()
|
||||
|
||||
// Yes this doesn't align correctly on anything other than 4 width tabs.
|
||||
// If you want it to go switch everybody to elastic tab stops.
|
||||
// Actually that'd be great if you could!
|
||||
@@ -84,17 +113,17 @@
|
||||
needs_update = level; \
|
||||
|
||||
|
||||
// This proc will cause the light source to update the top atom, and add itself to the update queue.
|
||||
/// This proc will cause the light source to update the top atom, and add itself to the update queue.
|
||||
/datum/light_source/proc/update(atom/new_top_atom)
|
||||
// This top atom is different.
|
||||
if (new_top_atom && new_top_atom != top_atom)
|
||||
if(top_atom != source_atom && top_atom.light_sources) // Remove ourselves from the light sources of that top atom.
|
||||
LAZYREMOVE(top_atom.light_sources, src)
|
||||
remove_from_light_sources(top_atom.light_sources)
|
||||
|
||||
top_atom = new_top_atom
|
||||
|
||||
if (top_atom != source_atom)
|
||||
LAZYADD(top_atom.light_sources, src) // Add ourselves to the light sources of our new top atom.
|
||||
add_to_light_sources(top_atom.light_sources)
|
||||
|
||||
EFFECT_UPDATE(LIGHTING_CHECK_UPDATE)
|
||||
|
||||
|
||||
@@ -41,8 +41,9 @@
|
||||
if(hit_object.resistance_flags & FREEZE_PROOF)
|
||||
hit_object.visible_message(span_warning("[hit_object] is freeze-proof!"))
|
||||
return
|
||||
if(!(hit_object.obj_flags & FROZEN))
|
||||
hit_object.make_frozen_visual()
|
||||
if(HAS_TRAIT(hit_object, TRAIT_FROZEN))
|
||||
return
|
||||
hit_object.AddElement(/datum/element/frozen)
|
||||
else if(isliving(hit_atom))
|
||||
var/mob/living/hit_mob = hit_atom
|
||||
SSmove_manager.stop_looping(hit_mob) //stops them mid pathing even if they're stunimmune
|
||||
|
||||
@@ -187,7 +187,7 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate)
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
if(M.z == z)
|
||||
to_chat(M, span_userdanger("Discordant whispers flood your mind in a thousand voices. Each one speaks your name, over and over. Something horrible has been released."))
|
||||
M.playsound_local(T, null, 100, FALSE, 0, FALSE, pressure_affected = FALSE, S = legion_sound)
|
||||
M.playsound_local(T, null, 100, FALSE, 0, FALSE, pressure_affected = FALSE, sound_to_use = legion_sound)
|
||||
flash_color(M, flash_color = "#FF0000", flash_time = 50)
|
||||
var/mutable_appearance/release_overlay = mutable_appearance('icons/effects/effects.dmi', "legiondoor")
|
||||
notify_ghosts("Legion has been released in the [get_area(src)]!", source = src, alert_overlay = release_overlay, action = NOTIFY_JUMP, flashwindow = FALSE)
|
||||
|
||||
@@ -494,19 +494,22 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999))
|
||||
else
|
||||
to_chat(user, "No vacated rooms.")
|
||||
|
||||
/obj/effect/landmark/lift_id/hilbert
|
||||
specific_lift_id = HILBERT_TRAM
|
||||
|
||||
/obj/effect/landmark/tram/left_part/hilbert
|
||||
specific_lift_id = HILBERT_TRAM
|
||||
destination_id = "left_part_hilbert"
|
||||
tram_id = "tram_hilbert"
|
||||
tgui_icons = list("Reception" = "briefcase", "Botany" = "leaf", "Chemistry" = "flask")
|
||||
|
||||
/obj/effect/landmark/tram/middle_part/hilbert
|
||||
specific_lift_id = HILBERT_TRAM
|
||||
destination_id = "middle_part_hilbert"
|
||||
tram_id = "tram_hilbert"
|
||||
tgui_icons = list("Processing" = "cogs", "Xenobiology" = "paw")
|
||||
|
||||
/obj/effect/landmark/tram/right_part/hilbert
|
||||
specific_lift_id = HILBERT_TRAM
|
||||
destination_id = "right_part_hilbert"
|
||||
tram_id = "tram_hilbert"
|
||||
tgui_icons = list("Ordnance" = "bullseye", "Office" = "user", "Dormitories" = "bed")
|
||||
|
||||
/obj/item/keycard/hilbert
|
||||
|
||||
@@ -57,18 +57,19 @@
|
||||
|
||||
/// Attempt to get the turf below the provided one according to Z traits
|
||||
/datum/controller/subsystem/mapping/proc/get_turf_below(turf/T)
|
||||
if (!T)
|
||||
if (!T || !initialized)
|
||||
return
|
||||
var/offset = level_trait(T.z, ZTRAIT_DOWN)
|
||||
var/offset = multiz_levels[T.z]["[DOWN]"]
|
||||
if (!offset)
|
||||
return
|
||||
return locate(T.x, T.y, T.z + offset)
|
||||
return locate(T.x, T.y, T.z - offset)
|
||||
|
||||
/// Attempt to get the turf above the provided one according to Z traits
|
||||
/datum/controller/subsystem/mapping/proc/get_turf_above(turf/T)
|
||||
if (!T)
|
||||
if (!T || !initialized)
|
||||
return
|
||||
var/offset = level_trait(T.z, ZTRAIT_UP)
|
||||
|
||||
var/offset = multiz_levels[T.z]["[UP]"]
|
||||
if (!offset)
|
||||
return
|
||||
return locate(T.x, T.y, T.z + offset)
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
// TODO: sleep here if the Z level needs to be cleared
|
||||
var/datum/space_level/S = new z_type(new_z, name, traits)
|
||||
z_list += S
|
||||
generate_linkages_for_z_level(new_z)
|
||||
calculate_z_level_gravity(new_z)
|
||||
adding_new_zlevel = FALSE
|
||||
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_NEW_Z, S)
|
||||
return S
|
||||
|
||||
@@ -221,7 +221,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust=1)) //for space dust eve
|
||||
continue
|
||||
var/dist = get_dist(M.loc, src.loc)
|
||||
shake_camera(M, dist > 20 ? 2 : 4, dist > 20 ? 1 : 3)
|
||||
M.playsound_local(src.loc, null, 50, 1, random_frequency, 10, S = meteor_sound)
|
||||
M.playsound_local(src.loc, null, 50, 1, random_frequency, 10, sound_to_use = meteor_sound)
|
||||
|
||||
///////////////////////
|
||||
//Meteor types
|
||||
|
||||
@@ -25,11 +25,11 @@
|
||||
AddComponent(/datum/component/bloodysoles/feet)
|
||||
AddElement(/datum/element/ridable, /datum/component/riding/creature/human)
|
||||
AddElement(/datum/element/strippable, GLOB.strippable_human_items, /mob/living/carbon/human/.proc/should_strip)
|
||||
GLOB.human_list += src
|
||||
var/static/list/loc_connections = list(
|
||||
COMSIG_ATOM_ENTERED = .proc/on_entered,
|
||||
)
|
||||
AddElement(/datum/element/connect_loc, loc_connections)
|
||||
GLOB.human_list += src
|
||||
|
||||
/mob/living/carbon/human/proc/setup_human_dna()
|
||||
//initialize dna. for spawned humans; overwritten by other code
|
||||
@@ -95,11 +95,6 @@
|
||||
. += "Chemical Storage: [changeling.chem_charges]/[changeling.total_chem_storage]"
|
||||
. += "Absorbed DNA: [changeling.absorbed_count]"
|
||||
|
||||
// called when something steps onto a human
|
||||
/mob/living/carbon/human/proc/on_entered(datum/source, atom/movable/AM)
|
||||
SIGNAL_HANDLER
|
||||
spreadFire(AM)
|
||||
|
||||
/mob/living/carbon/human/reset_perspective(atom/new_eye, force_reset = FALSE)
|
||||
if(dna?.species?.prevent_perspective_change && !force_reset) // This is in case a species needs to prevent perspective changes in certain cases, like Dullahans preventing perspective changes when they're looking through their head.
|
||||
update_fullscreen()
|
||||
@@ -371,6 +366,10 @@
|
||||
|
||||
..() //end of this massive fucking chain. TODO: make the hud chain not spooky. - Yeah, great job doing that.
|
||||
|
||||
//called when something steps onto a human
|
||||
/mob/living/carbon/human/proc/on_entered(datum/source, atom/movable/AM)
|
||||
SIGNAL_HANDLER
|
||||
spreadFire(AM)
|
||||
|
||||
/mob/living/carbon/human/proc/canUseHUD()
|
||||
return (mobility_flags & MOBILITY_USE)
|
||||
|
||||
@@ -879,7 +879,7 @@
|
||||
var/mob/living/L = pulledby
|
||||
L.set_pull_offsets(src, pulledby.grab_state)
|
||||
|
||||
if(active_storage && !(CanReach(active_storage.parent,view_only = TRUE)))
|
||||
if(active_storage && !((active_storage.parent in important_recursive_contents?[RECURSIVE_CONTENTS_ACTIVE_STORAGE]) || CanReach(active_storage.parent,view_only = TRUE)))
|
||||
active_storage.close(src)
|
||||
|
||||
if(body_position == LYING_DOWN && !buckled && prob(getBruteLoss()*200/maxHealth))
|
||||
@@ -1404,6 +1404,7 @@ GLOBAL_LIST_EMPTY(fire_appearances)
|
||||
var/datum/status_effect/fire_handler/fire_stacks/fire_status = has_status_effect(/datum/status_effect/fire_handler/fire_stacks)
|
||||
if(!fire_status || !fire_status.on_fire)
|
||||
return
|
||||
|
||||
remove_status_effect(/datum/status_effect/fire_handler/fire_stacks)
|
||||
|
||||
/**
|
||||
|
||||
@@ -209,3 +209,5 @@
|
||||
var/native_fov = FOV_90_DEGREES
|
||||
/// Lazy list of FOV traits that will apply a FOV view when handled.
|
||||
var/list/fov_traits
|
||||
///what multiplicative slowdown we get from turfs currently.
|
||||
var/current_turf_slowdown = 0
|
||||
|
||||
@@ -88,11 +88,20 @@
|
||||
UNSETEMPTY(fov_traits)
|
||||
update_fov()
|
||||
|
||||
//did you know you can subtype /image and /mutable_appearance?
|
||||
/image/fov_image
|
||||
icon = 'icons/effects/fov/fov_effects.dmi'
|
||||
layer = FOV_EFFECTS_LAYER
|
||||
appearance_flags = RESET_COLOR | RESET_TRANSFORM
|
||||
plane = FULLSCREEN_PLANE
|
||||
|
||||
/// Plays a visual effect representing a sound cue for people with vision obstructed by FOV or blindness
|
||||
/proc/play_fov_effect(atom/center, range, icon_state, dir = SOUTH, ignore_self = FALSE, angle = 0)
|
||||
/proc/play_fov_effect(atom/center, range, icon_state, dir = SOUTH, ignore_self = FALSE, angle = 0, list/override_list)
|
||||
var/turf/anchor_point = get_turf(center)
|
||||
var/image/fov_image
|
||||
for(var/mob/living/living_mob in get_hearers_in_view(range, center))
|
||||
var/image/fov_image/fov_image
|
||||
var/list/clients_shown
|
||||
|
||||
for(var/mob/living/living_mob in override_list || get_hearers_in_view(range, center))
|
||||
var/client/mob_client = living_mob.client
|
||||
if(!mob_client)
|
||||
continue
|
||||
@@ -101,18 +110,22 @@
|
||||
if(living_mob.in_fov(center, ignore_self))
|
||||
continue
|
||||
if(!fov_image) //Make the image once we found one recipient to receive it
|
||||
fov_image = image(icon = 'icons/effects/fov/fov_effects.dmi', icon_state = icon_state, loc = anchor_point)
|
||||
fov_image.plane = FULLSCREEN_PLANE
|
||||
fov_image.layer = FOV_EFFECTS_LAYER
|
||||
fov_image = new()
|
||||
fov_image.loc = anchor_point
|
||||
fov_image.icon_state = icon_state
|
||||
fov_image.dir = dir
|
||||
fov_image.appearance_flags = RESET_COLOR | RESET_TRANSFORM
|
||||
if(angle)
|
||||
var/matrix/matrix = new
|
||||
matrix.Turn(angle)
|
||||
fov_image.transform = matrix
|
||||
fov_image.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
LAZYADD(clients_shown, mob_client)
|
||||
|
||||
mob_client.images += fov_image
|
||||
addtimer(CALLBACK(GLOBAL_PROC, .proc/remove_image_from_client, fov_image, mob_client), 30)
|
||||
//when added as an image mutable_appearances act identically. we just make it an MA becuase theyre faster to change appearance
|
||||
|
||||
if(clients_shown)
|
||||
addtimer(CALLBACK(GLOBAL_PROC, .proc/remove_images_from_clients, fov_image, clients_shown), 30)
|
||||
|
||||
/atom/movable/screen/fov_blocker
|
||||
icon = 'icons/effects/fov/field_of_view.dmi'
|
||||
|
||||
@@ -28,9 +28,12 @@
|
||||
|
||||
/mob/living/proc/update_turf_movespeed(turf/open/T)
|
||||
if(isopenturf(T))
|
||||
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/turf_slowdown, multiplicative_slowdown = T.slowdown)
|
||||
else
|
||||
if(T.slowdown != current_turf_slowdown)
|
||||
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/turf_slowdown, multiplicative_slowdown = T.slowdown)
|
||||
current_turf_slowdown = T.slowdown
|
||||
else if(current_turf_slowdown)
|
||||
remove_movespeed_modifier(/datum/movespeed_modifier/turf_slowdown)
|
||||
current_turf_slowdown = 0
|
||||
|
||||
|
||||
/mob/living/proc/update_pull_movespeed()
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
if(observers?.len)
|
||||
for(var/mob/dead/observe as anything in observers)
|
||||
observe.reset_perspective(null)
|
||||
|
||||
qdel(hud_used)
|
||||
QDEL_LIST(client_colours)
|
||||
ghostize() //False, since we're deleting it currently
|
||||
|
||||
@@ -241,3 +241,6 @@
|
||||
var/thinking_indicator = FALSE
|
||||
/// User is thinking in character. Used to revert to thinking state after stop_typing
|
||||
var/thinking_IC = FALSE
|
||||
|
||||
///how much gravity is slowing us down
|
||||
var/gravity_slowdown = 0
|
||||
|
||||
@@ -70,9 +70,9 @@
|
||||
next_move_dir_sub = 0
|
||||
var/old_move_delay = move_delay
|
||||
move_delay = world.time + world.tick_lag //this is here because Move() can now be called mutiple times per tick
|
||||
if(!mob || !mob.loc)
|
||||
if(!direct || !new_loc)
|
||||
return FALSE
|
||||
if(!new_loc || !direct)
|
||||
if(!mob?.loc)
|
||||
return FALSE
|
||||
if(mob.notransform)
|
||||
return FALSE //This is sota the goto stop mobs from moving var
|
||||
@@ -118,7 +118,9 @@
|
||||
|
||||
//We are now going to move
|
||||
var/add_delay = mob.cached_multiplicative_slowdown
|
||||
mob.set_glide_size(DELAY_TO_GLIDE_SIZE(add_delay * ( (NSCOMPONENT(direct) && EWCOMPONENT(direct)) ? SQRT_2 : 1 ) )) // set it now in case of pulled objects
|
||||
var/new_glide_size = DELAY_TO_GLIDE_SIZE(add_delay * ( (NSCOMPONENT(direct) && EWCOMPONENT(direct)) ? SQRT_2 : 1 ) )
|
||||
if(mob.glide_size != new_glide_size)
|
||||
mob.set_glide_size(new_glide_size) // set it now in case of pulled objects
|
||||
//If the move was recent, count using old_move_delay
|
||||
//We want fractional behavior and all
|
||||
if(old_move_delay + world.tick_lag > world.time)
|
||||
@@ -137,10 +139,15 @@
|
||||
if((direct & (direct - 1)) && mob.loc == new_loc) //moved diagonally successfully
|
||||
add_delay *= SQRT_2
|
||||
|
||||
var/after_glide = 0
|
||||
if(visual_delay)
|
||||
mob.set_glide_size(visual_delay)
|
||||
after_glide = visual_delay
|
||||
else
|
||||
mob.set_glide_size(DELAY_TO_GLIDE_SIZE(add_delay))
|
||||
after_glide = DELAY_TO_GLIDE_SIZE(add_delay)
|
||||
|
||||
if(after_glide != mob.glide_size)
|
||||
mob.set_glide_size(after_glide)
|
||||
|
||||
move_delay += add_delay
|
||||
if(.) // If mob is null here, we deserve the runtime
|
||||
if(mob.throwing)
|
||||
@@ -358,10 +365,12 @@
|
||||
/// Update the gravity status of this mob
|
||||
/mob/proc/update_gravity(has_gravity, override=FALSE)
|
||||
var/speed_change = max(0, has_gravity - STANDARD_GRAVITY)
|
||||
if(!speed_change)
|
||||
if(!speed_change && gravity_slowdown)
|
||||
remove_movespeed_modifier(/datum/movespeed_modifier/gravity)
|
||||
else
|
||||
gravity_slowdown = 0
|
||||
else if(gravity_slowdown != speed_change)
|
||||
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/gravity, multiplicative_slowdown=speed_change)
|
||||
gravity_slowdown = speed_change
|
||||
|
||||
//bodypart selection verbs - Cyberboss
|
||||
//8: repeated presses toggles through head - eyes - mouth
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
rave_screen = mod.wearer.add_client_colour(/datum/client_colour/rave)
|
||||
rave_screen.update_colour(rainbow_order[rave_number])
|
||||
if(selection)
|
||||
SEND_SOUND(mod.wearer, sound(selection.song_path, volume = 50, channel = CHANNEL_JUKEBOX))
|
||||
mod.wearer.playsound_local(get_turf(src), null, 50, channel = CHANNEL_JUKEBOX, sound_to_use = sound(selection.song_path), use_reverb = FALSE)
|
||||
|
||||
/obj/item/mod/module/visor/rave/on_deactivation(display_message = TRUE, deleting = FALSE)
|
||||
. = ..()
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
// Gravity Generator
|
||||
//
|
||||
|
||||
GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding new gravity generators to the list, and keying it with the z level.
|
||||
/// We will keep track of this by adding new gravity generators to the list, and keying it with the z level.
|
||||
GLOBAL_LIST_EMPTY(gravity_generators)
|
||||
|
||||
#define POWER_IDLE 0
|
||||
#define POWER_UP 1
|
||||
@@ -146,7 +147,8 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
|
||||
var/charge_count = 100
|
||||
var/current_overlay = null
|
||||
var/broken_state = 0
|
||||
var/setting = 1 //Gravity value when on
|
||||
///Gravity value when on
|
||||
var/setting = 1
|
||||
|
||||
///Station generator that spawns with gravity turned off.
|
||||
/obj/machinery/gravity_generator/main/station/off
|
||||
@@ -407,7 +409,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
|
||||
M.update_gravity(M.has_gravity())
|
||||
if(M.client)
|
||||
shake_camera(M, 15, 1)
|
||||
M.playsound_local(T, null, 100, 1, 0.5, S = alert_sound)
|
||||
M.playsound_local(T, null, 100, 1, 0.5, sound_to_use = alert_sound)
|
||||
|
||||
/obj/machinery/gravity_generator/main/proc/gravity_in_level()
|
||||
var/turf/T = get_turf(src)
|
||||
@@ -434,6 +436,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
|
||||
GLOB.gravity_generators["[z]"] |= src
|
||||
else
|
||||
GLOB.gravity_generators["[z]"] -= src
|
||||
SSmapping.calculate_z_level_gravity(z)
|
||||
|
||||
/obj/machinery/gravity_generator/main/proc/change_setting(value)
|
||||
if(value != setting)
|
||||
|
||||
@@ -99,7 +99,7 @@ All ShuttleMove procs go here
|
||||
/atom/movable/proc/beforeShuttleMove(turf/newT, rotation, move_mode, obj/docking_port/mobile/moving_dock)
|
||||
return move_mode
|
||||
|
||||
// Called on atoms to move the atom to the new location
|
||||
/// Called on atoms to move the atom to the new location
|
||||
/atom/movable/proc/onShuttleMove(turf/newT, turf/oldT, list/movement_force, move_dir, obj/docking_port/stationary/old_dock, obj/docking_port/mobile/moving_dock)
|
||||
if(newT == oldT) // In case of in place shuttle rotation shenanigans.
|
||||
return
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
///are we registered in SSshuttles?
|
||||
var/registered = FALSE
|
||||
|
||||
///register to SSshuttles
|
||||
///register to SSshuttles
|
||||
/obj/docking_port/proc/register()
|
||||
if(registered)
|
||||
WARNING("docking_port registered multiple times")
|
||||
@@ -47,7 +47,7 @@
|
||||
registered = TRUE
|
||||
return
|
||||
|
||||
///unregister from SSshuttles
|
||||
///unregister from SSshuttles
|
||||
/obj/docking_port/proc/unregister()
|
||||
if(!registered)
|
||||
WARNING("docking_port unregistered multiple times")
|
||||
@@ -57,7 +57,7 @@
|
||||
/obj/docking_port/proc/Check_id()
|
||||
return
|
||||
|
||||
//these objects are indestructible
|
||||
//these objects are indestructible
|
||||
/obj/docking_port/Destroy(force)
|
||||
// unless you assert that you know what you're doing. Horrible things
|
||||
// may result.
|
||||
@@ -68,7 +68,7 @@
|
||||
return QDEL_HINT_LETMELIVE
|
||||
|
||||
/obj/docking_port/has_gravity(turf/T)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/docking_port/take_damage()
|
||||
return
|
||||
|
||||
@@ -90,6 +90,8 @@
|
||||
ignore += typesof(/obj/item/toy/cards/cardhand)
|
||||
//Needs a holodeck area linked to it which is not guarenteed to exist and technically is supposed to have a 1:1 relationship with computer anyway.
|
||||
ignore += typesof(/obj/machinery/computer/holodeck)
|
||||
//runtimes if not paired with a landmark
|
||||
ignore += typesof(/obj/structure/industrial_lift)
|
||||
|
||||
var/list/cached_contents = spawn_at.contents.Copy()
|
||||
var/baseturf_count = length(spawn_at.baseturfs)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
@@ -90,6 +90,7 @@
|
||||
#include "code\__DEFINES\icon_smoothing.dm"
|
||||
#include "code\__DEFINES\id_cards.dm"
|
||||
#include "code\__DEFINES\important_recursive_contents.dm"
|
||||
#include "code\__DEFINES\industrial_lift.dm"
|
||||
#include "code\__DEFINES\injection.dm"
|
||||
#include "code\__DEFINES\instruments.dm"
|
||||
#include "code\__DEFINES\interaction_flags.dm"
|
||||
@@ -984,6 +985,7 @@
|
||||
#include "code\datums\elements\firestacker.dm"
|
||||
#include "code\datums\elements\footstep.dm"
|
||||
#include "code\datums\elements\forced_gravity.dm"
|
||||
#include "code\datums\elements\frozen.dm"
|
||||
#include "code\datums\elements\haunted.dm"
|
||||
#include "code\datums\elements\honkspam.dm"
|
||||
#include "code\datums\elements\item_fov.dm"
|
||||
@@ -1263,7 +1265,6 @@
|
||||
#include "code\game\machinery\cell_charger.dm"
|
||||
#include "code\game\machinery\civilian_bounties.dm"
|
||||
#include "code\game\machinery\constructable_frame.dm"
|
||||
#include "code\game\machinery\crossing_signal.dm"
|
||||
#include "code\game\machinery\dance_machine.dm"
|
||||
#include "code\game\machinery\defibrillator_mount.dm"
|
||||
#include "code\game\machinery\deployable.dm"
|
||||
@@ -1343,7 +1344,6 @@
|
||||
#include "code\game\machinery\computer\station_alert.dm"
|
||||
#include "code\game\machinery\computer\teleporter.dm"
|
||||
#include "code\game\machinery\computer\terminal.dm"
|
||||
#include "code\game\machinery\computer\tram_controls.dm"
|
||||
#include "code\game\machinery\computer\warrant.dm"
|
||||
#include "code\game\machinery\computer\arcade\arcade.dm"
|
||||
#include "code\game\machinery\computer\arcade\orion.dm"
|
||||
@@ -1782,7 +1782,6 @@
|
||||
#include "code\game\objects\structures\headpike.dm"
|
||||
#include "code\game\objects\structures\hivebot.dm"
|
||||
#include "code\game\objects\structures\holosign.dm"
|
||||
#include "code\game\objects\structures\industrial_lift.dm"
|
||||
#include "code\game\objects\structures\janicart.dm"
|
||||
#include "code\game\objects\structures\kitchen_spike.dm"
|
||||
#include "code\game\objects\structures\ladders.dm"
|
||||
@@ -1812,7 +1811,6 @@
|
||||
#include "code\game\objects\structures\tank_dispenser.dm"
|
||||
#include "code\game\objects\structures\tank_holder.dm"
|
||||
#include "code\game\objects\structures\training_machine.dm"
|
||||
#include "code\game\objects\structures\tram_walls.dm"
|
||||
#include "code\game\objects\structures\traps.dm"
|
||||
#include "code\game\objects\structures\votingbox.dm"
|
||||
#include "code\game\objects\structures\watercloset.dm"
|
||||
@@ -3023,6 +3021,14 @@
|
||||
#include "code\modules\hydroponics\grown\weeds\kudzu.dm"
|
||||
#include "code\modules\hydroponics\grown\weeds\nettle.dm"
|
||||
#include "code\modules\hydroponics\grown\weeds\starthistle.dm"
|
||||
#include "code\modules\industrial_lift\crossing_signal.dm"
|
||||
#include "code\modules\industrial_lift\industrial_lift.dm"
|
||||
#include "code\modules\industrial_lift\lift_master.dm"
|
||||
#include "code\modules\industrial_lift\tram_controls.dm"
|
||||
#include "code\modules\industrial_lift\tram_landmark.dm"
|
||||
#include "code\modules\industrial_lift\tram_lift_master.dm"
|
||||
#include "code\modules\industrial_lift\tram_override_objects.dm"
|
||||
#include "code\modules\industrial_lift\tram_walls.dm"
|
||||
#include "code\modules\instruments\items.dm"
|
||||
#include "code\modules\instruments\piano_synth.dm"
|
||||
#include "code\modules\instruments\stationary.dm"
|
||||
|
||||
Reference in New Issue
Block a user