-
-
Notifications
You must be signed in to change notification settings - Fork 444
/
Copy pathchasm.dm
304 lines (262 loc) · 11.5 KB
/
chasm.dm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
// Used by /turf/open/chasm and subtypes to implement the "dropping" mechanic
/datum/component/chasm
var/turf/target_turf
var/obj/effect/abstract/chasm_storage/storage
var/fall_message = "GAH! Ah... where are you?"
var/oblivion_message = "You stumble and stare into the abyss before you. It stares back, and you fall into the enveloping dark."
var/static/list/falling_atoms = list() // Atoms currently falling into chasms
var/static/list/forbidden_types = typecacheof(list(
/obj/singularity,
/obj/docking_port,
/obj/structure/lattice,
/obj/structure/stone_tile,
/obj/projectile,
/obj/effect/projectile,
/obj/effect/portal,
/obj/effect/abstract,
/obj/effect/hotspot,
/obj/effect/landmark,
/obj/effect/temp_visual,
/obj/effect/light_emitter/tendril,
/obj/effect/collapse,
/obj/effect/particle_effect/ion_trails,
/obj/effect/dummy/phased_mob,
/obj/effect/dummy/crawling //yogs
))
/datum/component/chasm/Initialize(turf/target, mapload)
if(!isturf(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, SIGNAL_ADDTRAIT(TRAIT_CHASM_STOPPED), PROC_REF(on_chasm_stopped))
RegisterSignal(parent, SIGNAL_REMOVETRAIT(TRAIT_CHASM_STOPPED), PROC_REF(on_chasm_no_longer_stopped))
target_turf = target
RegisterSignal(parent, COMSIG_ATOM_ABSTRACT_ENTERED, PROC_REF(entered))
RegisterSignal(parent, COMSIG_ATOM_ABSTRACT_EXITED, PROC_REF(exited))
RegisterSignal(parent, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, PROC_REF(initialized_on))
RegisterSignal(parent, COMSIG_ATOM_INTERCEPT_TELEPORTING, PROC_REF(block_teleport))
RegisterSignal(parent, COMSIG_ATOM_ATTACKBY, PROC_REF(fish))
//allow catwalks to give the turf the CHASM_STOPPED trait before dropping stuff when the turf is changed.
//otherwise don't do anything because turfs and areas are initialized before movables.
if(!mapload)
addtimer(CALLBACK(src, PROC_REF(drop_stuff)), 0)
START_PROCESSING(SSobj, src) // process on create, in case stuff is still there
/datum/component/chasm/proc/Entered(datum/source, atom/movable/AM)
START_PROCESSING(SSobj, src)
drop_stuff(AM)
/datum/component/chasm/process()
if (!drop_stuff())
STOP_PROCESSING(SSobj, src)
/datum/component/chasm/UnregisterFromParent()
storage = null
/datum/component/chasm/proc/entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
SIGNAL_HANDLER
drop_stuff()
/datum/component/chasm/proc/exited(datum/source, atom/movable/exited)
SIGNAL_HANDLER
UnregisterSignal(exited, list(COMSIG_MOVETYPE_FLAG_DISABLED, COMSIG_LIVING_SET_BUCKLED, COMSIG_MOVABLE_THROW_LANDED))
/datum/component/chasm/proc/initialized_on(datum/source, atom/movable/movable, mapload)
SIGNAL_HANDLER
drop_stuff(movable)
/datum/component/chasm/proc/block_teleport()
return COMPONENT_BLOCK_TELEPORT
/datum/component/chasm/proc/on_chasm_stopped(datum/source)
SIGNAL_HANDLER
UnregisterSignal(source, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_EXITED, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON))
for(var/atom/movable/movable as anything in source)
UnregisterSignal(movable, list(COMSIG_MOVETYPE_FLAG_DISABLED, COMSIG_LIVING_SET_BUCKLED, COMSIG_MOVABLE_THROW_LANDED))
/datum/component/chasm/proc/on_chasm_no_longer_stopped(datum/source)
SIGNAL_HANDLER
RegisterSignal(parent, COMSIG_ATOM_ENTERED, PROC_REF(entered))
RegisterSignal(parent, COMSIG_ATOM_EXITED, PROC_REF(exited))
RegisterSignal(parent, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, PROC_REF(initialized_on))
drop_stuff()
#define CHASM_NOT_DROPPING 0
#define CHASM_DROPPING 1
///Doesn't drop the movable, but registers a few signals to try again if the conditions change.
#define CHASM_REGISTER_SIGNALS 2
/datum/component/chasm/proc/drop_stuff(atom/movable/dropped_thing)
if(HAS_TRAIT(parent, TRAIT_CHASM_STOPPED))
return
var/atom/atom_parent = parent
var/to_check = dropped_thing ? list(dropped_thing) : atom_parent.contents
for (var/atom/movable/thing as anything in to_check)
var/dropping = droppable(thing)
switch(dropping)
if(CHASM_DROPPING)
INVOKE_ASYNC(src, PROC_REF(drop), thing)
if(CHASM_REGISTER_SIGNALS)
RegisterSignals(thing, list(COMSIG_MOVETYPE_FLAG_DISABLED, COMSIG_LIVING_SET_BUCKLED, COMSIG_MOVABLE_THROW_LANDED), PROC_REF(drop_stuff), TRUE)
/datum/component/chasm/proc/droppable(atom/movable/dropped_thing)
var/datum/weakref/falling_ref = WEAKREF(dropped_thing)
// avoid an infinite loop, but allow falling a large distance
if(falling_atoms[falling_ref] && falling_atoms[falling_ref] > 30)
return CHASM_NOT_DROPPING
if(is_type_in_typecache(dropped_thing, forbidden_types) || (!isliving(dropped_thing) && !isobj(dropped_thing)))
return CHASM_NOT_DROPPING
if(dropped_thing.throwing || (dropped_thing.movement_type & MOVETYPES_NOT_TOUCHING_GROUND))
return CHASM_REGISTER_SIGNALS
//Flies right over the chasm
if(ismob(dropped_thing))
var/mob/M = dropped_thing
if(M.buckled) //middle statement to prevent infinite loops just in case!
var/mob/buckled_to = M.buckled
if((!ismob(M.buckled) || (buckled_to.buckled != M)) && !droppable(M.buckled))
return CHASM_REGISTER_SIGNALS
if(ishuman(dropped_thing))
var/mob/living/carbon/human/victim = dropped_thing
for(var/obj/item/wormhole_jaunter/jaunter in victim.get_all_contents())
var/turf/chasm = get_turf(victim)
var/fall_into_chasm = jaunter.chasm_react(victim)
if(!fall_into_chasm)
chasm.visible_message(span_boldwarning("[victim] falls into the [chasm]!")) //To freak out any bystanders
return fall_into_chasm ? CHASM_DROPPING : CHASM_NOT_DROPPING
return CHASM_DROPPING
#undef CHASM_NOT_DROPPING
#undef CHASM_DROPPING
#undef CHASM_REGISTER_SIGNALS
/datum/component/chasm/proc/drop(atom/movable/dropped_thing)
var/datum/weakref/falling_ref = WEAKREF(dropped_thing)
//Make sure the item is still there after our sleep
if(!dropped_thing || !falling_ref?.resolve())
falling_atoms -= falling_ref
return
falling_atoms[falling_ref] = (falling_atoms[falling_ref] || 0) + 1
var/turf/below_turf = target_turf
var/atom/parent = src.parent
if(falling_atoms[falling_ref] > 1)
return // We're already handling this
if(below_turf)
if(HAS_TRAIT(dropped_thing, TRAIT_CHASM_DESTROYED))
qdel(dropped_thing)
return
// send to the turf below
var/turf/above_turf = get_turf(dropped_thing)
dropped_thing.visible_message(span_boldwarning("[dropped_thing] falls into [parent]!"), span_userdanger("[fall_message]"))
below_turf.visible_message(span_boldwarning("[dropped_thing] falls from above!"))
dropped_thing.forceMove(below_turf)
below_turf.zImpact(dropped_thing, abs(above_turf.z - below_turf.z), get_turf(dropped_thing), FALL_NO_MESSAGE)
falling_atoms -= falling_ref
return
// send to oblivion
dropped_thing.visible_message(span_boldwarning("[dropped_thing] falls into [parent]!"), span_userdanger("[oblivion_message]"))
if (isliving(dropped_thing))
var/mob/living/falling_mob = dropped_thing
falling_mob.Paralyze(20 SECONDS)
var/oldtransform = dropped_thing.transform
var/oldcolor = dropped_thing.color
var/oldalpha = dropped_thing.alpha
var/oldoffset = dropped_thing.pixel_y
animate(dropped_thing, transform = matrix() - matrix(), alpha = 0, color = rgb(0, 0, 0), time = 10)
for(var/i in 1 to 5)
//Make sure the item is still there after our sleep
if(!dropped_thing || QDELETED(dropped_thing))
return
dropped_thing.pixel_y--
sleep(0.2 SECONDS)
//Make sure the item is still there after our sleep
if(!dropped_thing || QDELETED(dropped_thing))
return
if(HAS_TRAIT(dropped_thing, TRAIT_CHASM_DESTROYED))
qdel(dropped_thing)
return
if(!storage)
storage = (locate() in parent) || new(parent)
if(storage.contains(dropped_thing))
return
dropped_thing.alpha = oldalpha
dropped_thing.color = oldcolor
dropped_thing.transform = oldtransform
dropped_thing.pixel_y = oldoffset
if(!dropped_thing.forceMove(storage))
parent.visible_message(span_boldwarning("[parent] spits out [dropped_thing]!"))
dropped_thing.throw_at(get_edge_target_turf(parent, pick(GLOB.alldirs)), rand(1, 10), rand(1, 10))
else if(isliving(dropped_thing))
var/mob/living/fallen_mob = dropped_thing
if (fallen_mob.stat != DEAD)
fallen_mob.investigate_log("has died from falling into a chasm.", INVESTIGATE_DEATHS)
fallen_mob.death(TRUE)
fallen_mob.adjustBruteLoss(300)
falling_atoms -= falling_ref
/**
* Called when something has left the chasm depths storage.
* Arguments
*
* * source - Chasm object holder.
* * gone - Item which has just left the chasm contents.
*/
/datum/component/chasm/proc/left_chasm(atom/source, atom/movable/gone)
SIGNAL_HANDLER
UnregisterSignal(gone, COMSIG_LIVING_REVIVE)
///Global list needed to let fishermen with a rescue hook fish fallen mobs from any place
GLOBAL_LIST_EMPTY(chasm_fallen_mobs)
/**
* An abstract object which is basically just a bag that the chasm puts people inside
*/
/obj/effect/abstract/chasm_storage
name = "chasm depths"
desc = "The bottom of a hole. You shouldn't be able to interact with this."
anchored = TRUE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/obj/effect/abstract/chasm_storage/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_SECLUDED_LOCATION, INNATE_TRAIT)
/obj/effect/abstract/chasm_storage/Entered(atom/movable/arrived)
. = ..()
if(isliving(arrived))
RegisterSignal(arrived, COMSIG_LIVING_REVIVE, PROC_REF(on_revive))
GLOB.chasm_fallen_mobs += arrived
/obj/effect/abstract/chasm_storage/Exited(atom/movable/gone)
. = ..()
if(isliving(gone))
UnregisterSignal(gone, COMSIG_LIVING_REVIVE)
GLOB.chasm_fallen_mobs -= gone
#define CHASM_TRAIT "chasm trait"
/**
* Called if something comes back to life inside the pit. Expected sources are badmins and changelings.
* Ethereals should take enough damage to be smashed and not revive.
* Arguments
* escapee - Lucky guy who just came back to life at the bottom of a hole.
*/
/obj/effect/abstract/chasm_storage/proc/on_revive(mob/living/escapee)
SIGNAL_HANDLER
var/turf/turf = get_turf(src)
if(turf.GetComponent(/datum/component/chasm))
turf.visible_message(span_boldwarning("After a long climb, [escapee] leaps out of [turf]!"))
else
playsound(turf, 'sound/effects/bang.ogg', 50, TRUE)
turf.visible_message(span_boldwarning("[escapee] busts through [turf], leaping out of the chasm below"))
turf.ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR)
ADD_TRAIT(escapee, TRAIT_MOVE_FLYING, CHASM_TRAIT) //Otherwise they instantly fall back in
escapee.forceMove(turf)
escapee.throw_at(get_edge_target_turf(turf, pick(GLOB.alldirs)), rand(1, 10), rand(1, 10))
REMOVE_TRAIT(escapee, TRAIT_MOVE_FLYING, CHASM_TRAIT)
escapee.Paralyze(20 SECONDS, TRUE)
UnregisterSignal(escapee, COMSIG_LIVING_REVIVE)
#undef CHASM_TRAIT
/datum/component/chasm/proc/fish(datum/source, obj/item/I, mob/user, params)
if(!istype(I,/obj/item/fishingrod))
return
var/obj/item/fishingrod/rod = I
if(!HAS_TRAIT(rod, TRAIT_WIELDED))
to_chat(user, span_warning("You need to wield the rod in both hands before you can fish in the chasm!"))
return
if(do_after(user, 3 SECONDS, parent))
if(!HAS_TRAIT(rod, TRAIT_WIELDED))
return
var/list/fishing_contents = list()
for(var/turf/T in range(3, src.parent))
if(ischasm(T))
fishing_contents += T.get_all_contents()
if(!length(fishing_contents))
to_chat(user, span_warning("There's nothing here!"))
return
var/found = FALSE
for(var/mob/M in fishing_contents) //since only mobs can fall in here this really isnt needed but on the off chance something naughty happens..
M.forceMove(get_turf(user))
UnregisterSignal(M, COMSIG_LIVING_REVIVE)
found = TRUE
break //only one mob rather than all
if(found)
to_chat(user, span_warning("You reel in something!"))
else
to_chat(user, span_warning("There's nothing here!"))
return