-
-
Notifications
You must be signed in to change notification settings - Fork 444
/
Copy pathsupplypod.dm
646 lines (586 loc) · 32.4 KB
/
supplypod.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
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
//The "DPtarget" temp visual is created by anything that "launches" a supplypod. This is what animates the pod and makes the pod forcemove to the station.
//------------------------------------SUPPLY POD-------------------------------------//
/obj/structure/closet/supplypod
name = "supply pod" //Names and descriptions are normally created with the setStyle() proc during initialization, but we have these default values here as a failsafe
desc = "A Nanotrasen supply drop pod."
icon = 'icons/obj/supplypods.dmi'
icon_state = "pod" //This is a common base sprite shared by a number of pods
pixel_x = SUPPLYPOD_X_OFFSET //2x2 sprite
layer = TABLE_LAYER //So that the crate inside doesn't appear underneath
open_flags = ALLOW_OBJECTS | ALLOW_DENSE
delivery_icon = null
can_weld_shut = FALSE
armor = list(MELEE = 30, BULLET = 50, LASER = 50, ENERGY = 50, BOMB = 100, BIO = 0, RAD = 0, FIRE = 100, ACID = 80, ELECTRIC = 100)
anchored = TRUE //So it cant slide around after landing
anchorable = FALSE
flags_1 = PREVENT_CONTENTS_EXPLOSION_1
appearance_flags = KEEP_TOGETHER | PIXEL_SCALE
density = FALSE
///List of bitflags for supply pods, see: code\__DEFINES\obj_flags.dm
var/pod_flags = NONE
//*****NOTE*****: Many of these comments are similarly described in centcom_podlauncher.dm. If you change them here, please consider doing so in the centcom podlauncher code as well!
var/adminNamed = FALSE //Determines whether or not the pod has been named by an admin. If true, the pod's name will not get overridden when the style of the pod changes (changing the style of the pod normally also changes the name+desc)
var/bluespace = FALSE //If true, the pod deletes (in a shower of sparks) after landing
var/delays = list(POD_TRANSIT = 30, POD_FALLING = 4, POD_OPENING = 30, POD_LEAVING = 30)
var/reverse_delays = list(POD_TRANSIT = 30, POD_FALLING = 4, POD_OPENING = 30, POD_LEAVING = 30)
var/custom_rev_delay = FALSE
var/damage = 0 //Damage that occurs to any mob under the pod when it lands.
var/effectStun = FALSE //If true, stuns anyone under the pod when it launches until it lands, forcing them to get hit by the pod. Devilish!
var/effectLimb = FALSE //If true, pops off a limb (if applicable) from anyone caught under the pod when it lands
var/effectOrgans = FALSE //If true, yeets out every limb and organ from anyone caught under the pod when it lands
var/effectGib = FALSE //If true, anyone under the pod will be gibbed when it lands
var/effectStealth = FALSE //If true, a target icon isn't displayed on the turf where the pod will land
var/effectQuiet = FALSE //The female sniper. If true, the pod makes no noise (including related explosions, opening sounds, etc)
var/effectMissile = FALSE //If true, the pod deletes the second it lands. If you give it an explosion, it will act like a missile exploding as it hits the ground
var/effectCircle = FALSE //If true, allows the pod to come in at any angle. Bit of a weird feature but whatever its here
var/style = STYLE_STANDARD //Style is a variable that keeps track of what the pod is supposed to look like. It acts as an index to the GLOB.podstyles list in cargo.dm defines to get the proper icon/name/desc for the pod.
var/reversing = FALSE //If true, the pod will not send any items. Instead, after opening, it will close again (picking up items/mobs) and fly back to centcom
var/list/reverse_dropoff_coords //Turf that the reverse pod will drop off it's newly-acquired cargo to
var/fallingSoundLength = 11
var/fallingSound = 'sound/weapons/mortar_long_whistle.ogg'//Admin sound to play before the pod lands
var/landingSound //Admin sound to play when the pod lands
var/openingSound //Admin sound to play when the pod opens
var/leavingSound //Admin sound to play when the pod leaves
var/soundVolume = 80 //Volume to play sounds at. Ignores the cap
var/list/explosionSize = list(0,0,2,3)
var/stay_after_drop = FALSE
var/specialised = FALSE // It's not a general use pod for cargo/admin use
var/effectShrapnel = FALSE
//var/shrapnel_type = /obj/projectile/bullet/shrapnel
var/shrapnel_magnitude = 3
var/rubble_type //Rubble effect associated with this supplypod
var/decal = "default" //What kind of extra decals we add to the pod to make it look nice
var/door = "pod_door"
var/fin_mask = "topfin"
var/obj/effect/supplypod_rubble/rubble
var/obj/effect/engineglow/glow_effect
var/list/reverse_option_list = list("Mobs"=FALSE,"Objects"=FALSE,"Anchored"=FALSE,"Underfloor"=FALSE,"Wallmounted"=FALSE,"Floors"=FALSE,"Walls"=FALSE, "Mecha"=FALSE)
var/list/turfs_in_cargo = list()
/obj/structure/closet/supplypod/bluespacepod
style = STYLE_BLUESPACE
bluespace = TRUE
flags_1 = NODECONSTRUCT_1|PREVENT_CONTENTS_EXPLOSION_1
explosionSize = list(0,0,1,2)
delays = list(POD_TRANSIT = 15, POD_FALLING = 4, POD_OPENING = 30, POD_LEAVING = 30)
/obj/structure/closet/supplypod/extractionpod
name = "Syndicate Extraction Pod"
desc = "A specalised, blood-red styled pod for extracting high-value targets out of active mission areas. <b>Targets must be manually stuffed inside the pod for proper delivery.</b>"
specialised = TRUE
style = STYLE_SYNDICATE
bluespace = TRUE
flags_1 = NODECONSTRUCT_1|PREVENT_CONTENTS_EXPLOSION_1
explosionSize = list(0,0,1,2)
delays = list(POD_TRANSIT = 25, POD_FALLING = 4, POD_OPENING = 30, POD_LEAVING = 30)
/obj/structure/closet/supplypod/centcompod
style = STYLE_CENTCOM
bluespace = TRUE
flags_1 = NODECONSTRUCT_1|PREVENT_CONTENTS_EXPLOSION_1
explosionSize = list(0,0,0,0)
delays = list(POD_TRANSIT = 20, POD_FALLING = 4, POD_OPENING = 30, POD_LEAVING = 30)
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
/obj/structure/closet/supplypod/Initialize(mapload, customStyle = FALSE)
. = ..()
if (!loc)
var/shippingLane = GLOB.areas_by_type[/area/centcom/supplypod/podStorage] //temporary holder for supplypods mid-transit
forceMove(shippingLane)
if (customStyle)
style = customStyle
setStyle(style) //Upon initialization, give the supplypod an iconstate, name, and description based on the "style" variable. This system is important for the centcom_podlauncher to function correctly
/obj/structure/closet/supplypod/extractionpod/Initialize(mapload)
. = ..()
var/turf/picked_turf = pick(GLOB.holdingfacility)
reverse_dropoff_coords = list(picked_turf.x, picked_turf.y, picked_turf.z)
/obj/structure/closet/supplypod/proc/setStyle(chosenStyle) //Used to give the sprite an icon state, name, and description.
style = chosenStyle
var/base = GLOB.podstyles[chosenStyle][POD_BASE] //GLOB.podstyles is a 2D array we treat as a dictionary. The style represents the verticle index, with the icon state, name, and desc being stored in the horizontal indexes of the 2D array.
icon_state = base
decal = GLOB.podstyles[chosenStyle][POD_DECAL]
rubble_type = GLOB.podstyles[chosenStyle][POD_RUBBLE_TYPE]
if (!adminNamed && !specialised) //We dont want to name it ourselves if it has been specifically named by an admin using the centcom_podlauncher datum
name = GLOB.podstyles[chosenStyle][POD_NAME]
desc = GLOB.podstyles[chosenStyle][POD_DESC]
if (GLOB.podstyles[chosenStyle][POD_DOOR])
door = "[base]_door"
else
door = FALSE
update_appearance(UPDATE_ICON)
/obj/structure/closet/supplypod/proc/SetReverseIcon()
fin_mask = "bottomfin"
if (GLOB.podstyles[style][POD_SHAPE] == POD_SHAPE_NORML)
icon_state = GLOB.podstyles[style][POD_BASE] + "_reverse"
pixel_x = initial(pixel_x)
transform = matrix()
update_appearance(UPDATE_ICON)
/obj/structure/closet/supplypod/proc/backToNonReverseIcon()
fin_mask = initial(fin_mask)
if (GLOB.podstyles[style][POD_SHAPE] == POD_SHAPE_NORML)
icon_state = GLOB.podstyles[style][POD_BASE]
pixel_x = initial(pixel_x)
transform = matrix()
update_appearance(UPDATE_ICON)
/obj/structure/closet/supplypod/update_overlays()
. = ..()
if(style == STYLE_INVISIBLE)
return
if(rubble)
. += rubble.getForeground(src)
if(style == STYLE_SEETHROUGH)
for(var/atom/A in contents)
var/mutable_appearance/itemIcon = new(A)
itemIcon.transform = matrix().Translate(-1 * SUPPLYPOD_X_OFFSET, 0)
. += itemIcon
for(var/t in turfs_in_cargo)//T is just a turf's type
var/turf/turf_type = t
var/mutable_appearance/itemIcon = mutable_appearance(initial(turf_type.icon), initial(turf_type.icon_state))
itemIcon.transform = matrix().Translate(-1 * SUPPLYPOD_X_OFFSET, 0)
. += itemIcon
return
if(opened) //We're opened means all we have to worry about is masking a decal if we have one
if(!decal) //We don't have a decal to mask
return
if(!door) //We have a decal but no door, so let's just add the decal
. += decal
return
var/icon/masked_decal = new(icon, decal) //The decal we want to apply
var/icon/door_masker = new(icon, door) //The door shape we want to 'cut out' of the decal
door_masker.MapColors(0,0,0,1, 0,0,0,1, 0,0,0,1, 1,1,1,0, 0,0,0,1)
door_masker.SwapColor("#ffffffff", null)
door_masker.Blend("#000000", ICON_SUBTRACT)
masked_decal.Blend(door_masker, ICON_ADD)
. += masked_decal
return
//If we're closed
if(!door) //We have no door, lets see if we have a decal. If not, theres nothing we need to do
if(decal)
. += decal
return
else if (GLOB.podstyles[style][POD_SHAPE] != POD_SHAPE_NORML) //If we're not a normal pod shape (aka, if we don't have fins), just add the door without masking
. += door
else
var/icon/masked_door = new(icon, door) //The door we want to apply
var/icon/fin_masker = new(icon, "mask_[fin_mask]") //The fin shape we want to 'cut out' of the door
fin_masker.MapColors(0,0,0,1, 0,0,0,1, 0,0,0,1, 1,1,1,0, 0,0,0,1)
fin_masker.SwapColor("#ffffffff", null)
fin_masker.Blend("#000000", ICON_SUBTRACT)
masked_door.Blend(fin_masker, ICON_ADD)
. += masked_door
if(decal)
. += decal
/obj/structure/closet/supplypod/ex_act() //Explosions dont do SHIT TO US! This is because supplypods create explosions when they land.
return
/obj/structure/closet/supplypod/contents_explosion() //Supplypods also protect their contents from the harmful effects of fucking exploding.
return
/obj/structure/closet/supplypod/toggle(mob/living/user)
return
/obj/structure/closet/supplypod/open(mob/living/user)
return
/obj/structure/closet/supplypod/proc/handleReturnAfterDeparting(atom/movable/holder = src)
reversing = FALSE //Now that we're done reversing, we set this to false (otherwise we would get stuck in an infinite loop of calling the close proc at the bottom of open_pod() )
bluespace = TRUE //Make it so that the pod doesn't stay in centcom forever
flags_1 = NODECONSTRUCT_1|PREVENT_CONTENTS_EXPLOSION_1
pod_flags &= ~FIRST_SOUNDS //Make it so we play sounds now
if (!effectQuiet && style != STYLE_SEETHROUGH)
audible_message(span_notice("The pod hisses, closing and launching itself away from the station."), span_notice("The ground vibrates, and you hear the sound of engines firing."))
stay_after_drop = FALSE
holder.pixel_z = initial(holder.pixel_z)
holder.alpha = initial(holder.alpha)
var/shippingLane = GLOB.areas_by_type[/area/centcom/supplypod/podStorage]
forceMove(shippingLane) //Move to the centcom-z-level until the DPtarget says we can drop back down again
if (!reverse_dropoff_coords) //If we're centcom-launched, the reverse dropoff turf will be a centcom loading bay. If we're an extraction pod, it should be the ninja jail. Thus, this shouldn't ever really happen.
var/obj/error_landmark = locate(/obj/effect/landmark/error) in GLOB.landmarks_list
var/turf/error_landmark_turf = get_turf(error_landmark)
reverse_dropoff_coords = list(error_landmark_turf.x, error_landmark_turf.y, error_landmark_turf.z)
if (custom_rev_delay)
delays = reverse_delays
backToNonReverseIcon()
var/turf/return_turf = locate(reverse_dropoff_coords[1], reverse_dropoff_coords[2], reverse_dropoff_coords[3])
new /obj/effect/DPtarget(return_turf, src)
/obj/structure/closet/supplypod/proc/preOpen() //Called before the open_pod() proc. Handles anything that occurs right as the pod lands.
var/turf/turf_underneath = get_turf(src)
var/list/B = explosionSize //Mostly because B is more readable than explosionSize :p
density = TRUE //Density is originally false so the pod doesn't block anything while it's still falling through the air
for (var/mob/living/target_living in turf_underneath)
if (iscarbon(target_living)) //If effectLimb is true (which means we pop limbs off when we hit people):
if (effectLimb)
var/mob/living/carbon/carbon_target_mob = target_living
for (var/bp in carbon_target_mob.bodyparts) //Look at the bodyparts in our poor mob beneath our pod as it lands
var/obj/item/bodypart/bodypart = bp
if(!(bodypart.body_part & (HEAD|CHEST)))//we dont want to kill him, just teach em a lesson!
if (bodypart.dismemberable)
bodypart.dismember() //Using the power of flextape i've sawed this man's limb in half!
break
if (effectOrgans) //effectOrgans means remove every organ in our mob
var/mob/living/carbon/carbon_target_mob = target_living
for(var/organ in carbon_target_mob.internal_organs)
var/destination = get_edge_target_turf(turf_underneath, pick(GLOB.alldirs)) //Pick a random direction to toss them in
var/obj/item/organ/organ_to_yeet = organ
organ_to_yeet.Remove(carbon_target_mob) //Note that this isn't the same proc as for lists
organ_to_yeet.forceMove(turf_underneath) //Move the organ outta the body
organ_to_yeet.throw_at(destination, 2, 3) //Thow the organ at a random tile 3 spots away
sleep(0.1 SECONDS)
for (var/bp in carbon_target_mob.bodyparts) //Look at the bodyparts in our poor mob beneath our pod as it lands
var/obj/item/bodypart/bodypart = bp
var/destination = get_edge_target_turf(turf_underneath, pick(GLOB.alldirs))
if (bodypart.dismemberable)
bodypart.dismember() //Using the power of flextape i've sawed this man's bodypart in half!
bodypart.throw_at(destination, 2, 3)
sleep(0.1 SECONDS)
if (effectGib) //effectGib is on, that means whatever's underneath us better be fucking oof'd on
target_living.adjustBruteLoss(5000) //THATS A LOT OF DAMAGE (called just in case gib() doesnt work on em)
if (!QDELETED(target_living))
target_living.gib() //After adjusting the fuck outta that brute loss we finish the job with some satisfying gibs
else
target_living.adjustBruteLoss(damage)
var/explosion_sum = B[1] + B[2] + B[3] + B[4]
if (explosion_sum != 0) //If the explosion list isn't all zeroes, call an explosion
explosion(turf_underneath, B[1], B[2], B[3], flame_range = B[4], silent = effectQuiet, ignorecap = istype(src, /obj/structure/closet/supplypod/centcompod)) //less advanced equipment than bluespace pod, so larger explosion when landing
else if (!effectQuiet && !(pod_flags & FIRST_SOUNDS)) //If our explosion list IS all zeroes, we still make a nice explosion sound (unless the effectQuiet var is true)
playsound(src, "explosion", landingSound ? soundVolume * 0.25 : soundVolume, TRUE)
if (landingSound)
playsound(turf_underneath, landingSound, soundVolume, FALSE, FALSE)
if (effectMissile) //If we are acting like a missile, then right after we land and finish fucking shit up w explosions, we should delete
opened = TRUE //We set opened to TRUE to avoid spending time trying to open (due to being deleted) during the Destroy() proc
qdel(src)
return
if (style == STYLE_GONDOLA) //Checks if we are supposed to be a gondola pod. If so, create a gondolapod mob, and move this pod to nullspace. I'd like to give a shout out, to my man oranges
var/mob/living/simple_animal/pet/gondola/gondolapod/benis = new(turf_underneath, src)
benis.contents |= contents //Move the contents of this supplypod into the gondolapod mob.
moveToNullspace()
addtimer(CALLBACK(src, PROC_REF(open_pod), benis), delays[POD_OPENING]) //After the opening delay passes, we use the open proc from this supplyprod while referencing the contents of the "holder", in this case the gondolapod mob
else if (style == STYLE_SEETHROUGH)
open_pod(src)
else
addtimer(CALLBACK(src, PROC_REF(open_pod), src), delays[POD_OPENING]) //After the opening delay passes, we use the open proc from this supplypod, while referencing this supplypod's contents
/obj/structure/closet/supplypod/proc/open_pod(atom/movable/holder, broken = FALSE, forced = FALSE) //The holder var represents an atom whose contents we will be working with
if (!holder)
return
if (opened) //This is to ensure we don't open something that has already been opened
return
holder.setOpened()
var/turf/turf_underneath = get_turf(holder) //Get the turf of whoever's contents we're talking about
if (istype(holder, /mob)) //Allows mobs to assume the role of the holder, meaning we look at the mob's contents rather than the supplypod's contents. Typically by this point the supplypod's contents have already been moved over to the mob's contents
var/mob/holder_as_mob = holder
if (holder_as_mob.key && !forced && !broken) //If we are player controlled, then we shouldn't open unless the opening is manual, or if it is due to being destroyed (represented by the "broken" parameter)
return
if (openingSound)
playsound(get_turf(holder), openingSound, soundVolume, FALSE, FALSE) //Special admin sound to play
for (var/turf_type in turfs_in_cargo)
turf_underneath.place_on_top(turf_type)
for (var/cargo in contents)
var/atom/movable/movable_cargo = cargo
movable_cargo.forceMove(turf_underneath)
if (!effectQuiet && !openingSound && style != STYLE_SEETHROUGH && !(pod_flags & FIRST_SOUNDS)) //If we aren't being quiet, play the default pod open sound
playsound(get_turf(holder), open_sound, 15, TRUE, -3)
if (broken) //If the pod is opening because it's been destroyed, we end here
return
if (style == STYLE_SEETHROUGH)
startExitSequence(src)
else
if (reversing)
addtimer(CALLBACK(src, PROC_REF(SetReverseIcon)), delays[POD_LEAVING]/2) //Finish up the pod's duties after a certain amount of time
if(!stay_after_drop) // Departing should be handled manually
addtimer(CALLBACK(src, PROC_REF(startExitSequence), holder), delays[POD_LEAVING]*(4/5)) //Finish up the pod's duties after a certain amount of time
/obj/structure/closet/supplypod/proc/startExitSequence(atom/movable/holder)
if (leavingSound)
playsound(get_turf(holder), leavingSound, soundVolume, FALSE, FALSE)
if (reversing) //If we're reversing, we call the close proc. This sends the pod back up to centcom
close(holder)
else if (bluespace) //If we're a bluespace pod, then delete ourselves (along with our holder, if a seperate holder exists)
deleteRubble()
if (!effectQuiet && style != STYLE_INVISIBLE && style != STYLE_SEETHROUGH)
do_sparks(5, TRUE, holder) //Create some sparks right before closing
qdel(src) //Delete ourselves and the holder
if (holder != src)
qdel(holder)
/obj/structure/closet/supplypod/close(atom/movable/holder) //Closes the supplypod and sends it back to centcom. Should only ever be called if the "reversing" variable is true
if (!holder)
return
take_contents(holder)
playsound(holder, close_sound, soundVolume*0.75, TRUE, -3)
holder.setClosed()
addtimer(CALLBACK(src, PROC_REF(preReturn), holder), delays[POD_LEAVING] * 0.2) //Start to leave a bit after closing for cinematic effect
/obj/structure/closet/supplypod/take_contents(atom/movable/holder)
if(!holder)
return
var/turf/turf_underneath = holder.drop_location()
for(var/atom_to_check in turf_underneath)
if(atom_to_check != src && !insert(atom_to_check, holder)) // Can't insert that
continue
insert(turf_underneath, holder)
/obj/structure/closet/supplypod/insert(atom/to_insert, atom/movable/holder)
if(insertion_allowed(to_insert))
if(isturf(to_insert))
var/turf/turf_to_insert = to_insert
turfs_in_cargo += turf_to_insert.type
turf_to_insert.ScrapeAway()
else
var/atom/movable/movable_to_insert = to_insert
movable_to_insert.forceMove(holder)
return TRUE
else
return FALSE
/obj/structure/closet/supplypod/insertion_allowed(atom/to_insert)
if(to_insert.invisibility == INVISIBILITY_ABSTRACT)
return FALSE
if(ismob(to_insert))
if(!reverse_option_list["Mobs"])
return FALSE
if(!isliving(to_insert)) //let's not put ghosts or camera mobs inside
return FALSE
var/mob/living/mob_to_insert = to_insert
if(mob_to_insert.anchored || mob_to_insert.incorporeal_move)
return FALSE
mob_to_insert.stop_pulling()
else if(isobj(to_insert))
var/obj/obj_to_insert = to_insert
if(istype(obj_to_insert, /obj/structure/closet/supplypod))
return FALSE
if(istype(obj_to_insert, /obj/effect/supplypod_smoke))
return FALSE
if(istype(obj_to_insert, /obj/effect/DPtarget))
return FALSE
if(istype(obj_to_insert, /obj/effect/supplypod_rubble))
return FALSE
if(HAS_TRAIT(obj_to_insert, TRAIT_UNDERFLOOR))
return !!reverse_option_list["Underfloor"]
if(isProbablyWallMounted(obj_to_insert))
return !!reverse_option_list["Wallmounted"]
if(!obj_to_insert.anchored && reverse_option_list["Unanchored"])
return TRUE
if(obj_to_insert.anchored && !ismecha(obj_to_insert) && reverse_option_list["Anchored"]) //Mecha are anchored but there is a separate option for them
return TRUE
if(ismecha(obj_to_insert) && reverse_option_list["Mecha"])
return TRUE
return FALSE
else if (isturf(to_insert))
if(isfloorturf(to_insert) && reverse_option_list["Floors"])
return TRUE
if(isfloorturf(to_insert) && !reverse_option_list["Floors"])
return FALSE
if(isclosedturf(to_insert) && reverse_option_list["Walls"])
return TRUE
if(isclosedturf(to_insert) && !reverse_option_list["Walls"])
return FALSE
return FALSE
return TRUE
/obj/structure/closet/supplypod/proc/preReturn(atom/movable/holder)
deleteRubble()
animate(holder, alpha = 0, time = 0.8 SECONDS, easing = QUAD_EASING|EASE_IN, flags = ANIMATION_PARALLEL)
animate(holder, pixel_z = 400, time = 1 SECONDS, easing = QUAD_EASING|EASE_IN, flags = ANIMATION_PARALLEL) //Animate our rising pod
addtimer(CALLBACK(src, PROC_REF(handleReturnAfterDeparting), holder), 15) //Finish up the pod's duties after a certain amount of time
/obj/structure/closet/supplypod/setOpened() //Proc exists here, as well as in any atom that can assume the role of a "holder" of a supplypod. Check the open_pod() proc for more details
opened = TRUE
density = FALSE
update_appearance(UPDATE_ICON)
/obj/structure/closet/supplypod/extractionpod/setOpened()
opened = TRUE
density = TRUE
update_appearance(UPDATE_ICON)
/obj/structure/closet/supplypod/setClosed() //Ditto
opened = FALSE
density = TRUE
update_appearance(UPDATE_ICON)
/obj/structure/closet/supplypod/proc/tryMakeRubble(turf/T) //Ditto
if (rubble_type == RUBBLE_NONE)
return
if (rubble)
return
if (effectMissile)
return
if (isspaceturf(T) || isclosedturf(T))
return
rubble = new /obj/effect/supplypod_rubble(T)
rubble.setStyle(rubble_type, src)
update_appearance(UPDATE_ICON)
/obj/structure/closet/supplypod/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
deleteRubble()
return ..()
/obj/structure/closet/supplypod/proc/deleteRubble()
rubble?.fadeAway()
rubble = null
update_appearance(UPDATE_ICON)
/obj/structure/closet/supplypod/proc/addGlow()
if (GLOB.podstyles[style][POD_SHAPE] != POD_SHAPE_NORML)
return
glow_effect = new(src)
glow_effect.icon_state = "pod_glow_" + GLOB.podstyles[style][POD_GLOW]
vis_contents += glow_effect
glow_effect.layer = GASFIRE_LAYER
/obj/structure/closet/supplypod/proc/endGlow()
if(!glow_effect)
return
glow_effect.layer = LOW_ITEM_LAYER
glow_effect.fadeAway(delays[POD_OPENING])
/obj/structure/closet/supplypod/Destroy()
deleteRubble()
open_pod(src, broken = TRUE) //Lets dump our contents by opening up
return ..()
//------------------------------------TEMPORARY_VISUAL-------------------------------------//
/obj/effect/supplypod_smoke //Falling pod smoke
name = ""
icon = 'icons/obj/supplypods_32x32.dmi'
icon_state = "smoke"
desc = ""
layer = PROJECTILE_HIT_THRESHHOLD_LAYER
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
alpha = 0
/obj/effect/engineglow //Falling pod smoke
name = ""
icon = 'icons/obj/supplypods.dmi'
icon_state = "pod_engineglow"
desc = ""
layer = GASFIRE_LAYER
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
alpha = 255
/obj/effect/engineglow/proc/fadeAway(leaveTime)
var/duration = min(leaveTime, 2.5 SECONDS)
animate(src, alpha=0, time = duration)
QDEL_IN(src, duration + 0.5 SECONDS)
/obj/effect/supplypod_smoke/proc/drawSelf(amount)
alpha = max(0, 255-(amount*20))
/obj/effect/supplypod_rubble //This is the object that forceMoves the supplypod to it's location
name = "Debris"
desc = "A small crater of rubble. Closer inspection reveals the debris to be made primarily of space-grade metal fragments. You're pretty sure that this will disperse before too long."
icon = 'icons/obj/supplypods.dmi'
layer = PROJECTILE_HIT_THRESHHOLD_LAYER // We want this to go right below the layer of supplypods and supplypod_rubble's forground.
icon_state = "rubble_bg"
anchored = TRUE
pixel_x = SUPPLYPOD_X_OFFSET
var/foreground = "rubble_fg"
var/verticle_offset = 0
/obj/effect/supplypod_rubble/proc/getForeground(obj/structure/closet/supplypod/pod)
var/mutable_appearance/rubble_overlay = mutable_appearance('icons/obj/supplypods.dmi', foreground)
rubble_overlay.appearance_flags = KEEP_APART|RESET_TRANSFORM
rubble_overlay.transform = matrix().Translate(SUPPLYPOD_X_OFFSET - pod.pixel_x, verticle_offset)
return rubble_overlay
/obj/effect/supplypod_rubble/proc/fadeAway()
animate(src, alpha=0, time = 3 SECONDS)
QDEL_IN(src, 35)
/obj/effect/supplypod_rubble/proc/setStyle(type, obj/structure/closet/supplypod/pod)
if (type == RUBBLE_WIDE)
icon_state += "_wide"
foreground += "_wide"
if (type == RUBBLE_THIN)
icon_state += "_thin"
foreground += "_thin"
if (pod.style == STYLE_BOX)
verticle_offset = -2
else
verticle_offset = initial(verticle_offset)
pixel_y = verticle_offset
/obj/effect/DPtarget_effect
name = ""
desc = ""
icon = 'icons/obj/supplypods_32x32.dmi'
icon_state = "LZ_Slider"
layer = PROJECTILE_HIT_THRESHHOLD_LAYER
/obj/effect/DPtarget_effect/Initialize(mapload, obj/structure/closet/supplypod/pod)
transform = matrix() * 1.5
animate(src, transform = matrix()*0.01, time = pod.delays[POD_TRANSIT]+pod.delays[POD_FALLING])
..()
/obj/effect/DPtarget //This is the object that forceMoves the supplypod to it's location
name = "Landing Zone Indicator"
desc = "A holographic projection designating the landing zone of something. It's probably best to stand back."
icon = 'icons/obj/supplypods_32x32.dmi'
icon_state = "LZ"
layer = PROJECTILE_HIT_THRESHHOLD_LAYER
light_range = 2
anchored = TRUE
alpha = 0
var/obj/structure/closet/supplypod/pod //The supplyPod that will be landing ontop of this DPtarget
var/obj/effect/DPtarget_effect/helper
var/list/smoke_effects = new /list(13)
/obj/effect/ex_act()
return
/obj/effect/DPtarget/Initialize(mapload, podParam, single_order = null, clientman)
. = ..()
if (ispath(podParam)) //We can pass either a path for a pod (as expressconsoles do), or a reference to an instantiated pod (as the centcom_podlauncher does)
podParam = new podParam() //If its just a path, instantiate it
pod = podParam
if (!pod.effectStealth)
helper = new (drop_location(), pod)
alpha = 255
animate(src, transform = matrix().Turn(90), time = pod.delays[POD_TRANSIT]+pod.delays[POD_FALLING])
if (single_order)
if (istype(single_order, /datum/supply_order))
var/datum/supply_order/SO = single_order
SO.generate(pod)
else if (istype(single_order, /atom/movable))
var/atom/movable/O = single_order
O.forceMove(pod)
for (var/mob/living/mob_in_pod in pod) //If there are any mobs in the supplypod, we want to set their view to the DPtarget. This is so that they can see where they are about to land
mob_in_pod.reset_perspective(src)
if(pod.effectStun) //If effectStun is true, stun any mobs caught on this DPtarget until the pod gets a chance to hit them
for (var/mob/living/target_living in get_turf(src))
target_living.Stun(pod.delays[POD_TRANSIT]+1 SECONDS, ignore_canstun = TRUE)//you ain't goin nowhere, kid.
if (pod.delays[POD_FALLING] == initial(pod.delays[POD_FALLING]) && pod.delays[POD_TRANSIT] + pod.delays[POD_FALLING] < pod.fallingSoundLength)
pod.fallingSoundLength = 0.3 SECONDS //The default falling sound is a little long, so if the landing time is shorter than the default falling sound, use a special, shorter default falling sound
pod.fallingSound = 'sound/weapons/mortar_whistle.ogg'
var/soundStartTime = pod.delays[POD_TRANSIT] - pod.fallingSoundLength + pod.delays[POD_FALLING]
if (soundStartTime < 0)
soundStartTime = 1
if (!pod.effectQuiet && !(pod.pod_flags & FIRST_SOUNDS))
addtimer(CALLBACK(src, PROC_REF(playFallingSound)), soundStartTime)
addtimer(CALLBACK(src, PROC_REF(beginLaunch), pod.effectCircle), pod.delays[POD_TRANSIT])
/obj/effect/DPtarget/proc/playFallingSound()
playsound(src, pod.fallingSound, pod.soundVolume, TRUE, 6)
/obj/effect/DPtarget/proc/beginLaunch(effectCircle) //Begin the animation for the pod falling. The effectCircle param determines whether the pod gets to come in from any descent angle
pod.addGlow()
pod.update_appearance(UPDATE_ICON)
if (pod.style != STYLE_INVISIBLE)
pod.add_filter("motionblur",1,list("type"="motion_blur", "x"=0, "y"=3))
pod.forceMove(drop_location())
for (var/mob/living/M in pod) //Remember earlier (initialization) when we moved mobs into the DPtarget so they wouldnt get lost in nullspace? Time to get them out
M.reset_perspective(null)
var/angle = effectCircle ? rand(0,360) : rand(70,110) //The angle that we can come in from
pod.pixel_x = cos(angle)*32*length(smoke_effects) //Use some ADVANCED MATHEMATICS to set the animated pod's position to somewhere on the edge of a circle with the center being the DPtarget
pod.pixel_z = sin(angle)*32*length(smoke_effects)
var/rotation = Get_Pixel_Angle(pod.pixel_z, pod.pixel_x) //CUSTOM HOMEBREWED proc that is just arctan with extra steps
setupSmoke(rotation)
pod.transform = matrix().Turn(rotation)
pod.layer = FLY_LAYER
if (pod.style != STYLE_INVISIBLE)
animate(pod.get_filter("motionblur"), y = 0, time = pod.delays[POD_FALLING], flags = ANIMATION_PARALLEL)
animate(pod, pixel_z = -1 * abs(sin(rotation))*4, pixel_x = SUPPLYPOD_X_OFFSET + (sin(rotation) * 20), time = pod.delays[POD_FALLING], easing = LINEAR_EASING, flags = ANIMATION_PARALLEL) //Make the pod fall! At an angle!
addtimer(CALLBACK(src, PROC_REF(endLaunch)), pod.delays[POD_FALLING], TIMER_CLIENT_TIME) //Go onto the last step after a very short falling animation
/obj/effect/DPtarget/proc/setupSmoke(rotation)
if (pod.style == STYLE_INVISIBLE || pod.style == STYLE_SEETHROUGH)
return
for ( var/i in 1 to length(smoke_effects))
var/obj/effect/supplypod_smoke/smoke_part = new (drop_location())
if (i == 1)
smoke_part.layer = FLY_LAYER
smoke_part.icon_state = "smoke_start"
smoke_part.transform = matrix().Turn(rotation)
smoke_effects[i] = smoke_part
smoke_part.pixel_x = sin(rotation)*32 * i
smoke_part.pixel_y = abs(cos(rotation))*32 * i
smoke_part.filters += filter(type = "blur", size = 4)
var/time = (pod.delays[POD_FALLING] / length(smoke_effects))*(length(smoke_effects)-i)
addtimer(CALLBACK(smoke_part, TYPE_PROC_REF(/obj/effect/supplypod_smoke, drawSelf), i), time, TIMER_CLIENT_TIME) //Go onto the last step after a very short falling animation
QDEL_IN(smoke_part, pod.delays[POD_FALLING] + 3.5 SECONDS)
/obj/effect/DPtarget/proc/drawSmoke()
if (pod.style == STYLE_INVISIBLE || pod.style == STYLE_SEETHROUGH)
return
for (var/obj/effect/supplypod_smoke/smoke_part in smoke_effects)
animate(smoke_part, alpha = 0, time = 2 SECONDS, flags = ANIMATION_PARALLEL)
animate(smoke_part.filters[1], size = 6, time = 1.5 SECONDS, easing = CUBIC_EASING|EASE_OUT, flags = ANIMATION_PARALLEL)
/obj/effect/DPtarget/proc/endLaunch()
pod.tryMakeRubble(drop_location())
pod.layer = initial(pod.layer)
pod.endGlow()
QDEL_NULL(helper)
pod.preOpen() //Begin supplypod open procedures. Here effects like explosions, damage, and other dangerous (and potentially admin-caused, if the centcom_podlauncher datum was used) memes will take place
drawSmoke()
//pod.AddComponent(/datum/component/pellet_cloud, projectile_type=pod.shrapnel_type, magnitude=pod.shrapnel_magnitude)
///if(pod.effectShrapnel)
// SEND_SIGNAL(pod, COMSIG_SUPPLYPOD_LANDED)
qdel(src) //The DPtarget's purpose is complete. It can rest easy now
//------------------------------------UPGRADES-------------------------------------//
/obj/item/disk/cargo/bluespace_pod //Disk that can be inserted into the Express Console to allow for Advanced Bluespace Pods
name = "Bluespace Drop Pod Upgrade"
desc = "This disk provides a firmware update to the Express Supply Console, granting the use of Nanotrasen's Bluespace Drop Pods to the supply department."
icon = 'icons/obj/module.dmi'
icon_state = "cargodisk"
item_state = "card-id"
w_class = WEIGHT_CLASS_SMALL