This commit is contained in:
Putnam3145
2022-03-30 12:35:12 -07:00
1281 changed files with 75215 additions and 68744 deletions
+28 -2
View File
@@ -14,6 +14,7 @@
var/response_timer_id = null
var/approval_time = 600
var/allow_unicode = FALSE
var/admin_approved = FALSE
var/static/regex/standard_station_regex
@@ -62,8 +63,32 @@
to_chat(user, "Your name has been sent to your employers for approval.")
// Autoapproves after a certain time
response_timer_id = addtimer(CALLBACK(src, .proc/rename_station, new_name, user.name, user.real_name, key_name(user)), approval_time, TIMER_STOPPABLE)
to_chat(GLOB.admins, "<span class='adminnotice'><b><font color=orange>CUSTOM STATION RENAME:</font></b>[ADMIN_LOOKUPFLW(user)] proposes to rename the [name_type] to [html_encode(new_name)] (will autoapprove in [DisplayTimeText(approval_time)]). [ADMIN_SMITE(user)] (<A HREF='?_src_=holder;[HrefToken(TRUE)];reject_custom_name=[REF(src)]'>REJECT</A>) [ADMIN_CENTCOM_REPLY(user)]</span>")
var/requires_approval = CONFIG_GET(flag/station_name_needs_approval)
response_timer_id = addtimer(CALLBACK(src, .proc/check_state, new_name, user.name, user.real_name, key_name(user)), approval_time, TIMER_STOPPABLE)
to_chat(GLOB.admins, "<span class='adminnotice'><b><font color=orange>CUSTOM STATION RENAME:</font></b>[ADMIN_LOOKUPFLW(user)] proposes to rename the [name_type] to [html_encode(new_name)] ([requires_approval ? "REQUIRES ADMIN APPROVAL and will autodeny" : "will autoapprove"] in [DisplayTimeText(approval_time)]). [ADMIN_SMITE(user)] (<A HREF='?_src_=holder;[HrefToken(TRUE)];reject_custom_name=[REF(src)]'>REJECT</A>)[requires_approval ? " (<A HREF='?_src_=holder;[HrefToken(TRUE)];approve_custom_name=[REF(src)]'>APPROVE</A>)" : ""] [ADMIN_CENTCOM_REPLY(user)]</span>")
/obj/item/station_charter/proc/check_state(designation, uname, ureal_name, ukey)
var/requires_approval = CONFIG_GET(flag/station_name_needs_approval)
if(requires_approval && !admin_approved)
var/turf/T = get_turf(src)
T.visible_message("<span class='warning'>A note appears on [src], stating this sector requires central command approval for its station names, which was not performed in time for this request. Looks like the change has been auto-rejected.</span>")
var/m = "Station rename has been autorejected due to config requiring admin approval."
message_admins(m)
log_admin(m)
else
rename_station(designation, uname, ureal_name, ukey)
response_timer_id = null
admin_approved = FALSE
/obj/item/station_charter/proc/allow_pass(user)
if(!user)
return
if(!response_timer_id)
return
admin_approved = TRUE
var/m = "[key_name(user)] has approved the proposed station name. It can still be denied prior to the timer expiring."
message_admins(m)
log_admin(m)
/obj/item/station_charter/proc/reject_proposed(user)
if(!user)
@@ -80,6 +105,7 @@
deltimer(response_timer_id)
response_timer_id = null
admin_approved = FALSE
/obj/item/station_charter/proc/rename_station(designation, uname, ureal_name, ukey)
set_station_name(designation)
+1 -1
View File
@@ -34,7 +34,7 @@
user.put_in_hands(PA)
/obj/item/chrono_eraser/item_action_slot_check(slot, mob/user, datum/action/A)
if(slot == SLOT_BACK)
if(slot == ITEM_SLOT_BACK)
return 1
/obj/item/gun/energy/chrono_gun
+4 -4
View File
@@ -89,7 +89,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
..()
/obj/item/proc/help_light_cig(mob/living/M)
var/mask_item = M.get_item_by_slot(SLOT_WEAR_MASK)
var/mask_item = M.get_item_by_slot(ITEM_SLOT_MASK)
if(istype(mask_item, /obj/item/clothing/mask/cigarette))
return mask_item
@@ -130,7 +130,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
reagents.add_reagent_list(list_reagents)
if(starts_lit)
light()
AddComponent(/datum/component/knockoff,90,list(BODY_ZONE_PRECISE_MOUTH),list(SLOT_WEAR_MASK))//90% to knock off when wearing a mask
AddComponent(/datum/component/knockoff,90,list(BODY_ZONE_PRECISE_MOUTH),list(ITEM_SLOT_MASK))//90% to knock off when wearing a mask
/obj/item/clothing/mask/cigarette/Destroy()
STOP_PROCESSING(SSobj, src)
@@ -819,7 +819,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
/obj/item/clothing/mask/vape/equipped(mob/user, slot)
. = ..()
if(slot == SLOT_WEAR_MASK)
if(slot == ITEM_SLOT_MASK)
if(!screw)
to_chat(user, "<span class='notice'>You start puffing on the vape.</span>")
reagents.reagents_holder_flags &= ~(NO_REACT)
@@ -830,7 +830,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
/obj/item/clothing/mask/vape/dropped(mob/user)
. = ..()
var/mob/living/carbon/C = user
if(C.get_item_by_slot(SLOT_WEAR_MASK) == src)
if(C.get_item_by_slot(ITEM_SLOT_MASK) == src)
reagents.reagents_holder_flags |= NO_REACT
STOP_PROCESSING(SSobj, src)
+60 -84
View File
@@ -298,7 +298,7 @@
/obj/item/toy/crayon/proc/draw_on(atom/target, mob/user, proximity, params)
var/static/list/punctuation = list("!","?",".",",","/","+","-","=","%","#","&")
var/istagger = HAS_TRAIT(user, TRAIT_TAGGER)
var/cost = 1
if(paint_mode == PAINT_LARGE_HORIZONTAL)
cost = 5
@@ -355,14 +355,14 @@
else if(drawing in graffiti|oriented)
temp = "graffiti"
// If a gang member is using a gang spraycan, it'll behave differently
var/gang_mode = FALSE
if(gang && user.mind && user.mind.has_antag_datum(/datum/antagonist/gang)) //Heres a check.
gang_mode = TRUE // No more runtimes if a non-gang member sprays a gang can, it just works like normal cans.
// discontinue if the area isn't valid for tagging because gang "honour"
var/gang_mode
if(user.mind)
gang_mode = user.mind.has_antag_datum(/datum/antagonist/gang)
if(gang_mode && (!can_claim_for_gang(user, target)))
return
var/graf_rot
if(drawing in oriented)
switch(user.dir)
@@ -390,16 +390,12 @@
audible_message("<span class='notice'>You hear spraying.</span>")
playsound(user.loc, 'sound/effects/spray.ogg', 5, 1, 5)
var/takes_time = !instant //For order purposes, since I'm maximum bad.
if(gang_mode)
takes_time = TRUE
var/wait_time = 50
if(paint_mode == PAINT_LARGE_HORIZONTAL)
wait_time *= 3
if(takes_time) //This is what deteremines the time it takes to spray a tag in gang mode. 50 is Default.
if(!do_after(user, gang_tag_delay, target = target)) //25 is a good number, but we have gang_tag_delay var now.
if(gang_mode || !instant)
if(!do_after(user, 50, target = target))
return
if(length(text_buffer))
@@ -410,16 +406,15 @@
if(actually_paints)
var/obj/effect/decal/cleanable/crayon/C = new(target, paint_color, drawing, temp, graf_rot)
if(gang_mode)
// Double check it wasn't tagged in the meanwhile.
if(!can_claim_for_gang(user, target))
return
tag_for_gang(user, target)
tag_for_gang(user, target, gang_mode)
affected_turfs += target
else
switch(paint_mode)
if(PAINT_NORMAL)
var/obj/effect/decal/cleanable/crayon/C = new(target, paint_color, drawing, temp, graf_rot)
C.add_hiddenprint(user)
if(precision_mode)
C.pixel_x = clamp(precision_x, -(world.icon_size/2), world.icon_size/2)
@@ -432,14 +427,18 @@
var/turf/left = locate(target.x-1,target.y,target.z)
var/turf/right = locate(target.x+1,target.y,target.z)
if(isValidSurface(left) && isValidSurface(right))
var/obj/effect/decal/cleanable/crayon/C = new(left, paint_color, drawing, temp, graf_rot, PAINT_LARGE_HORIZONTAL_ICON)
C.add_hiddenprint(user)
C = new(left, paint_color, drawing, temp, graf_rot, PAINT_LARGE_HORIZONTAL_ICON)
affected_turfs += left
affected_turfs += right
affected_turfs += target
else
to_chat(user, "<span class='warning'>There isn't enough space to paint!</span>")
return
C.add_hiddenprint(user)
if(istagger)
C.AddComponent(/datum/element/art, GOOD_ART)
else
C.AddComponent(/datum/element/art, BAD_ART)
if(!instant)
to_chat(user, "<span class='notice'>You finish drawing \the [temp].</span>")
@@ -462,52 +461,6 @@
reagents.trans_to(t, ., volume_multiplier)
check_empty(user)
//////////////Gang mode stuff/////////////////
/obj/item/toy/crayon/proc/can_claim_for_gang(mob/user, atom/target)
// Check area validity.
// Reject space, player-created areas, and non-station z-levels.
var/area/A = get_base_area(target)
if(!A || (!is_station_level(A.z)) || !(A.area_flags & VALID_TERRITORY))
to_chat(user, "<span class='warning'>[A] is unsuitable for tagging.</span>")
return FALSE
var/spraying_over = FALSE
for(var/G in target)
var/obj/effect/decal/cleanable/crayon/gang/gangtag = G
if(istype(gangtag))
var/datum/antagonist/gang/GA = user.mind.has_antag_datum(/datum/antagonist/gang)
if(gangtag.gang != GA.gang)
spraying_over = TRUE
break
var/occupying_gang = territory_claimed(A, user)
if(occupying_gang && !spraying_over)
to_chat(user, "<span class='danger'>[A] has already been tagged by the [occupying_gang] gang! You must get rid of or spray over the old tag first!</span>")
return FALSE
// If you pass the gauntlet of checks, you're good to proceed
return TRUE
/obj/item/toy/crayon/proc/territory_claimed(area/territory, mob/user)
for(var/datum/team/gang/G in GLOB.gangs)
if(territory.type in (G.territories|G.new_territories))
. = G.name
break
/obj/item/toy/crayon/proc/tag_for_gang(mob/user, atom/target)
//Delete any old markings on this tile, including other gang tags
for(var/obj/effect/decal/cleanable/crayon/old_marking in target)
qdel(old_marking)
var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang)
var/area/territory = get_base_area(target)
new /obj/effect/decal/cleanable/crayon/gang(target,G.gang,"graffiti",0,user) // Heres the gang tag.
to_chat(user, "<span class='notice'>You tagged [territory] for your gang!</span>")
/////////////////Gang end////////////////////
/obj/item/toy/crayon/attack(mob/M, mob/user)
if(edible && (M == user))
to_chat(user, "You take a bite of the [src.name]. Delicious!")
@@ -521,6 +474,49 @@
else
..()
//////////////Gang mode stuff/////////////////
/obj/item/toy/crayon/proc/can_claim_for_gang(mob/user, atom/target)
var/area/A = get_area(target)
if(!A || (!is_station_level(A.z)))
to_chat(user, "<span class='warning'>[A] is unsuitable for tagging.</span>")
return FALSE
var/spraying_over = FALSE
for(var/obj/effect/decal/cleanable/crayon/gang/G in target)
spraying_over = TRUE
for(var/obj/machinery/power/apc in target)
to_chat(user, "<span class='warning'>You can't tag an APC.</span>")
return FALSE
var/occupying_gang = territory_claimed(A, user)
if(occupying_gang && !spraying_over)
to_chat(user, "<span class='danger'>[A] has already been tagged by a gang! You must find and spray over the old tag first!</span>")
return FALSE
// stolen from oldgang lmao
return TRUE
/obj/item/toy/crayon/proc/tag_for_gang(mob/user, atom/target, datum/antagonist/gang/user_gang)
for(var/obj/effect/decal/cleanable/crayon/old_marking in target)
qdel(old_marking)
var/area/territory = get_area(target)
var/obj/effect/decal/cleanable/crayon/gang/tag = new /obj/effect/decal/cleanable/crayon/gang(target)
tag.my_gang = user_gang.my_gang
tag.icon_state = "[user_gang.gang_id]_tag"
tag.name = "[tag.my_gang.name] gang tag"
tag.desc = "Looks like someone's claimed this area for [tag.my_gang.name]."
to_chat(user, "<span class='notice'>You tagged [territory] for [tag.my_gang.name]!</span>")
/obj/item/toy/crayon/proc/territory_claimed(area/territory, mob/user)
for(var/obj/effect/decal/cleanable/crayon/gang/G in GLOB.gang_tags)
if(get_area(G) == territory)
return G
/////////////////Gang end////////////////////
/obj/item/toy/crayon/red
icon_state = "crayonred"
paint_color = "#DA0000"
@@ -761,7 +757,7 @@
return
if(isobj(target))
if(isobj(target) && !istype(target, /obj/effect/decal/cleanable/crayon/gang))
if(actually_paints)
if(istype(target, /obj/item/canvas)) //dont color our canvas neon green when im trying to paint please
return
@@ -866,26 +862,6 @@
post_noise = FALSE
reagent_contents = list(/datum/reagent/consumable/nothing = 1, /datum/reagent/toxin/mutetoxin = 1)
/obj/item/toy/crayon/spraycan/gang
charges = 20 // Charges back to 20, which is the default value for them.
gang = TRUE
gang_tag_delay = 15 //Its 50% faster than a regular spraycan, for tagging. After-all they did spend points/meet the boss.
pre_noise = FALSE
post_noise = TRUE // Its even more stealthy just a tad.
/obj/item/toy/crayon/spraycan/gang/Initialize(loc, datum/team/gang/G)
..()
if(G)
gang = G
paint_color = G.color
update_icon()
/obj/item/toy/crayon/spraycan/gang/examine(mob/user)
. = ..()
if(user.mind && user.mind.has_antag_datum(/datum/antagonist/gang) || isobserver(user))
. += "This spraycan has been specially modified with a stage 2 nozzle kit, making it faster."
/obj/item/toy/crayon/spraycan/infinite
name = "infinite spraycan"
charges = -1
+4 -4
View File
@@ -80,13 +80,13 @@
/obj/item/defibrillator/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user)
if(slot_flags == ITEM_SLOT_BACK)
if(user.get_item_by_slot(SLOT_BACK) == src)
if(user.get_item_by_slot(ITEM_SLOT_BACK) == src)
ui_action_click()
else
to_chat(user, "<span class='warning'>Put the defibrillator on your back first!</span>")
else if(slot_flags == ITEM_SLOT_BELT)
if(user.get_item_by_slot(SLOT_BELT) == src)
if(user.get_item_by_slot(ITEM_SLOT_BELT) == src)
ui_action_click()
else
to_chat(user, "<span class='warning'>Strap the defibrillator's belt on first!</span>")
@@ -182,7 +182,7 @@
/obj/item/defibrillator/equipped(mob/user, slot)
..()
if((slot_flags == ITEM_SLOT_BACK && slot != SLOT_BACK) || (slot_flags == ITEM_SLOT_BELT && slot != SLOT_BELT))
if((slot_flags == ITEM_SLOT_BACK && slot != ITEM_SLOT_BACK) || (slot_flags == ITEM_SLOT_BELT && slot != ITEM_SLOT_BELT))
remove_paddles(user)
update_power()
@@ -423,7 +423,7 @@
if((!req_defib && grab_ghost) || (req_defib && defib.grab_ghost))
H.notify_ghost_cloning("Your heart is being defibrillated!")
H.grab_ghost() // Shove them back in their body.
else if(H.can_defib())
else if(H.can_revive())
H.notify_ghost_cloning("Your heart is being defibrillated. Re-enter your corpse if you want to be revived!", source = src)
do_help(H, user)
@@ -369,6 +369,10 @@ GLOBAL_LIST_EMPTY(PDAs)
dat += "<li><a href='byond://?src=[REF(src)];choice=Toggle Door'>[PDAIMG(rdoor)]Toggle Remote Door</a></li>"
if (cartridge.access & CART_DRONEPHONE)
dat += "<li><a href='byond://?src=[REF(src)];choice=Drone Phone'>[PDAIMG(dronephone)]Drone Phone</a></li>"
if (cartridge.access & CART_BARTENDER)
dat += "<li><a href='byond://?src=[REF(src)];choice=Drink Recipe Browser'>[PDAIMG(bucket)]Drink Recipe Browser</a></li>"
if (cartridge.access & CART_CHEMISTRY)
dat += "<li><a href='byond://?src=[REF(src)];choice=Chemistry Recipe Browser'>[PDAIMG(bucket)]Chemistry Recipe Browser</a></li>"
dat += "<li><a href='byond://?src=[REF(src)];choice=3'>[PDAIMG(atmos)]Atmospheric Scan</a></li>"
dat += "<li><a href='byond://?src=[REF(src)];choice=Light'>[PDAIMG(flashlight)][fon ? "Disable" : "Enable"] Flashlight</a></li>"
if (pai)
@@ -705,6 +709,16 @@ GLOBAL_LIST_EMPTY(PDAs)
if(T)
pai.forceMove(T)
//DRINK RECIPE BROWSER=============================
if("Drink Recipe Browser")
if(cartridge && cartridge.access & CART_BARTENDER)
recipe_search(U, GLOB.drink_reactions_list)
//CHEMISTRY RECIPE BROWSER
if("Chemistry Recipe Browser")
if(cartridge && cartridge.access & CART_CHEMISTRY)
recipe_search(U, GLOB.normalized_chemical_reactions_list)
//LINK FUNCTIONS===================================
else//Cartridge menu linking
@@ -222,6 +222,7 @@
/obj/item/pda/bar
name = "bartender PDA"
icon_state = "pda-bartender"
default_cartridge = /obj/item/cartridge/bartender
inserted_item = /obj/item/pen/fountain
/obj/item/pda/atmos
+9 -1
View File
@@ -13,6 +13,8 @@
#define CART_QUARTERMASTER (1<<12)
#define CART_HYDROPONICS (1<<13)
#define CART_DRONEPHONE (1<<14)
#define CART_BARTENDER (1<<15)
#define CART_CHEMISTRY (1<<16)
/obj/item/cartridge
@@ -77,7 +79,7 @@
/obj/item/cartridge/chemistry
name = "\improper ChemWhiz cartridge"
icon_state = "cart-chem"
access = CART_REAGENT_SCANNER
access = CART_REAGENT_SCANNER | CART_CHEMISTRY
bot_access_flags = MED_BOT
/obj/item/cartridge/security
@@ -190,6 +192,12 @@
bot_access_flags = SEC_BOT | MULE_BOT | FLOOR_BOT | CLEAN_BOT | MED_BOT | FIRE_BOT
spam_enabled = 1
/obj/item/cartridge/bartender
name = "\improper B.O.O.Z.E cartridge"
desc = "Now with 12% alcohol!"
icon_state = "cart-bar"
access = CART_BARTENDER
/obj/item/cartridge/captain/New()
..()
radio = new(src)
@@ -19,6 +19,7 @@
w_class = WEIGHT_CLASS_SMALL
slot_flags = ITEM_SLOT_BELT
rad_flags = RAD_NO_CONTAMINATE
item_flags = NOBLUDGEON
custom_materials = list(/datum/material/iron = 150, /datum/material/glass = 150)
var/grace = RAD_GRACE_PERIOD
@@ -35,17 +36,15 @@
. = ..()
START_PROCESSING(SSobj, src)
soundloop = new(list(src), FALSE)
soundloop = new(src, FALSE)
/obj/item/geiger_counter/Destroy()
STOP_PROCESSING(SSobj, src)
QDEL_NULL(soundloop)
return ..()
/obj/item/geiger_counter/process()
update_icon()
update_sound()
/obj/item/geiger_counter/process(delta_time)
if(!scanning)
current_tick_amount = 0
return
@@ -64,49 +63,55 @@
current_tick_amount = 0
update_appearance()
update_sound()
/obj/item/geiger_counter/examine(mob/user)
. = ..()
if(!scanning)
return
. += "<span class='info'>Alt-click it to clear stored radiation levels.</span>"
. += span_info("Alt-click it to clear stored radiation levels.")
if(obj_flags & EMAGGED)
. += "<span class='warning'>The display seems to be incomprehensible.</span>"
. += span_warning("The display seems to be incomprehensible.")
return
switch(radiation_count)
if(-INFINITY to RAD_LEVEL_NORMAL)
. += "<span class='notice'>Ambient radiation level count reports that all is well.</span>"
. += span_notice("Ambient radiation level count reports that all is well.")
if(RAD_LEVEL_NORMAL + 1 to RAD_LEVEL_MODERATE)
. += "<span class='disarm'>Ambient radiation levels slightly above average.</span>"
. += span_alert("Ambient radiation levels slightly above average.")
if(RAD_LEVEL_MODERATE + 1 to RAD_LEVEL_HIGH)
. += "<span class='warning'>Ambient radiation levels above average.</span>"
. += span_warning("Ambient radiation levels above average.")
if(RAD_LEVEL_HIGH + 1 to RAD_LEVEL_VERY_HIGH)
. += "<span class='danger'>Ambient radiation levels highly above average.</span>"
. += span_danger("Ambient radiation levels highly above average.")
if(RAD_LEVEL_VERY_HIGH + 1 to RAD_LEVEL_CRITICAL)
. += "<span class='suicide'>Ambient radiation levels nearing critical level.</span>"
. += span_suicide("Ambient radiation levels nearing critical level.")
if(RAD_LEVEL_CRITICAL + 1 to INFINITY)
. += "<span class='boldannounce'>Ambient radiation levels above critical level!</span>"
. += span_boldannounce("Ambient radiation levels above critical level!")
. += "<span class='notice'>The last radiation amount detected was [last_tick_amount]</span>"
. += span_notice("The last radiation amount detected was [last_tick_amount]")
/obj/item/geiger_counter/update_icon_state()
if(!scanning)
icon_state = "geiger_off"
else if(obj_flags & EMAGGED)
return ..()
if(obj_flags & EMAGGED)
icon_state = "geiger_on_emag"
else
switch(radiation_count)
if(-INFINITY to RAD_LEVEL_NORMAL)
icon_state = "geiger_on_1"
if(RAD_LEVEL_NORMAL + 1 to RAD_LEVEL_MODERATE)
icon_state = "geiger_on_2"
if(RAD_LEVEL_MODERATE + 1 to RAD_LEVEL_HIGH)
icon_state = "geiger_on_3"
if(RAD_LEVEL_HIGH + 1 to RAD_LEVEL_VERY_HIGH)
icon_state = "geiger_on_4"
if(RAD_LEVEL_VERY_HIGH + 1 to RAD_LEVEL_CRITICAL)
icon_state = "geiger_on_4"
if(RAD_LEVEL_CRITICAL + 1 to INFINITY)
icon_state = "geiger_on_5"
return ..()
switch(radiation_count)
if(-INFINITY to RAD_LEVEL_NORMAL)
icon_state = "geiger_on_1"
if(RAD_LEVEL_NORMAL + 1 to RAD_LEVEL_MODERATE)
icon_state = "geiger_on_2"
if(RAD_LEVEL_MODERATE + 1 to RAD_LEVEL_HIGH)
icon_state = "geiger_on_3"
if(RAD_LEVEL_HIGH + 1 to RAD_LEVEL_VERY_HIGH)
icon_state = "geiger_on_4"
if(RAD_LEVEL_VERY_HIGH + 1 to RAD_LEVEL_CRITICAL)
icon_state = "geiger_on_4"
if(RAD_LEVEL_CRITICAL + 1 to INFINITY)
icon_state = "geiger_on_5"
return ..()
/obj/item/geiger_counter/proc/update_sound()
var/datum/looping_sound/geiger/loop = soundloop
@@ -124,21 +129,21 @@
if(amount <= RAD_BACKGROUND_RADIATION || !scanning)
return
current_tick_amount += amount
update_icon()
update_appearance()
/obj/item/geiger_counter/attack_self(mob/user)
scanning = !scanning
update_icon()
to_chat(user, "<span class='notice'>[icon2html(src, user)] You switch [scanning ? "on" : "off"] [src].</span>")
update_appearance()
to_chat(user, span_notice("[icon2html(src, user)] You switch [scanning ? "on" : "off"] [src]."))
/obj/item/geiger_counter/afterattack(atom/target, mob/user)
/obj/item/geiger_counter/afterattack(atom/target, mob/living/user)
. = ..()
if(user.a_intent == INTENT_HELP)
if(!(obj_flags & EMAGGED))
user.visible_message("<span class='notice'>[user] scans [target] with [src].</span>", "<span class='notice'>You scan [target]'s radiation levels with [src]...</span>")
user.visible_message(span_notice("[user] scans [target] with [src]."), span_notice("You scan [target]'s radiation levels with [src]..."))
addtimer(CALLBACK(src, .proc/scan, target, user), 20, TIMER_UNIQUE) // Let's not have spamming GetAllContents
else
user.visible_message("<span class='notice'>[user] scans [target] with [src].</span>", "<span class='danger'>You project [src]'s stored radiation into [target]!</span>")
user.visible_message(span_notice("[user] scans [target] with [src]."), span_danger("You project [src]'s stored radiation into [target]!"))
target.rad_act(radiation_count)
radiation_count = 0
return TRUE
@@ -156,57 +161,61 @@
if(isliving(A))
var/mob/living/M = A
if(!M.radiation)
to_chat(user, "<span class='notice'>[icon2html(src, user)] Radiation levels within normal boundaries.</span>")
to_chat(user, span_notice("[icon2html(src, user)] Radiation levels within normal boundaries."))
else
to_chat(user, "<span class='boldannounce'>[icon2html(src, user)] Subject is irradiated. Radiation levels: [M.radiation] rad.</span>")
to_chat(user, span_boldannounce("[icon2html(src, user)] Subject is irradiated. Radiation levels: [M.radiation]."))
if(rad_strength)
to_chat(user, "<span class='boldannounce'>[icon2html(src, user)] Target contains radioactive contamination. Radioactive strength: [rad_strength]</span>")
to_chat(user, span_boldannounce("[icon2html(src, user)] Target contains radioactive contamination. Radioactive strength: [rad_strength]"))
else
to_chat(user, "<span class='notice'>[icon2html(src, user)] Target is free of radioactive contamination.</span>")
to_chat(user, span_notice("[icon2html(src, user)] Target is free of radioactive contamination."))
/obj/item/geiger_counter/attackby(obj/item/I, mob/user, params)
if(I.tool_behaviour == TOOL_SCREWDRIVER && (obj_flags & EMAGGED))
if(scanning)
to_chat(user, "<span class='warning'>Turn off [src] before you perform this action!</span>")
return 0
user.visible_message("<span class='notice'>[user] unscrews [src]'s maintenance panel and begins fiddling with its innards...</span>", "<span class='notice'>You begin resetting [src]...</span>")
to_chat(user, span_warning("Turn off [src] before you perform this action!"))
return FALSE
user.visible_message(span_notice("[user] unscrews [src]'s maintenance panel and begins fiddling with its innards..."), span_notice("You begin resetting [src]..."))
if(!I.use_tool(src, user, 40, volume=50))
return 0
user.visible_message("<span class='notice'>[user] refastens [src]'s maintenance panel!</span>", "<span class='notice'>You reset [src] to its factory settings!</span>")
return FALSE
user.visible_message(span_notice("[user] refastens [src]'s maintenance panel!"), span_notice("You reset [src] to its factory settings!"))
obj_flags &= ~EMAGGED
radiation_count = 0
update_icon()
return 1
update_appearance()
return TRUE
else
return ..()
/obj/item/geiger_counter/AltClick(mob/living/user)
. = ..()
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE))
return
return ..()
if(!scanning)
to_chat(usr, "<span class='warning'>[src] must be on to reset its radiation level!</span>")
return TRUE
to_chat(usr, span_warning("[src] must be on to reset its radiation level!"))
return
radiation_count = 0
to_chat(usr, "<span class='notice'>You flush [src]'s radiation counts, resetting it to normal.</span>")
update_icon()
return TRUE
to_chat(usr, span_notice("You flush [src]'s radiation counts, resetting it to normal."))
update_appearance()
/obj/item/geiger_counter/emag_act(mob/user)
. = ..()
if(obj_flags & EMAGGED)
return
if(scanning)
to_chat(user, "<span class='warning'>Turn off [src] before you perform this action!</span>")
to_chat(user, span_warning("Turn off [src] before you perform this action!"))
return
to_chat(user, "<span class='warning'>You override [src]'s radiation storing protocols. It will now generate small doses of radiation, and stored rads are now projected into creatures you scan.</span>")
to_chat(user, span_warning("You override [src]'s radiation storing protocols. It will now generate small doses of radiation, and stored rads are now projected into creatures you scan."))
obj_flags |= EMAGGED
return TRUE
/obj/item/geiger_counter/cyborg
var/mob/listeningTo
/obj/item/geiger_counter/cyborg/cyborg_unequip(mob/user)
if(!scanning)
return
scanning = FALSE
update_appearance()
/obj/item/geiger_counter/cyborg/equipped(mob/user)
. = ..()
if(listeningTo == user)
@@ -217,6 +226,7 @@
listeningTo = user
/obj/item/geiger_counter/cyborg/proc/redirect_rad_act(datum/source, amount)
SIGNAL_HANDLER
rad_act(amount)
/obj/item/geiger_counter/cyborg/dropped(mob/user)
+1 -1
View File
@@ -19,7 +19,7 @@
/obj/item/megaphone/equipped(mob/M, slot)
. = ..()
if (slot == SLOT_HANDS)
if (slot == ITEM_SLOT_HANDS)
RegisterSignal(M, COMSIG_MOB_SAY, .proc/handle_speech)
else
UnregisterSignal(M, COMSIG_MOB_SAY)
@@ -156,7 +156,7 @@
category = list("hacked", "Misc")
/obj/item/electropack/shockcollar/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.get_item_by_slot(SLOT_NECK))
if(loc == user && user.get_item_by_slot(ITEM_SLOT_NECK))
to_chat(user, "<span class='warning'>The collar is fastened tight! You'll need help taking this off!</span>")
return
return ..()
@@ -167,7 +167,7 @@
if(isliving(loc) && on) //the "on" arg is currently useless
var/mob/living/L = loc
if(!L.get_item_by_slot(SLOT_NECK)) //**properly** stops pocket shockers
if(!L.get_item_by_slot(ITEM_SLOT_NECK)) //**properly** stops pocket shockers
return
if(shock_cooldown == TRUE)
return
@@ -56,7 +56,7 @@ GLOBAL_LIST_INIT(channel_tokens, list(
/obj/item/radio/headset/ComponentInitialize()
. = ..()
if (bowman)
AddComponent(/datum/component/wearertargeting/earprotection, list(SLOT_EARS))
AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_EARS))
/obj/item/radio/headset/Initialize()
. = ..()
@@ -373,4 +373,4 @@ GLOBAL_LIST_INIT(channel_tokens, list(
name = replacetext(name,"headset", "bowman headset")
desc = "[desc] Protects ears from flashbangs."
bowman = TRUE
AddComponent(/datum/component/wearertargeting/earprotection, list(SLOT_EARS))
AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_EARS))
@@ -24,8 +24,8 @@
/obj/item/reverse_bear_trap/Initialize()
. = ..()
soundloop = new(list(src))
soundloop2 = new(list(src))
soundloop = new(src)
soundloop2 = new(src)
/obj/item/reverse_bear_trap/Destroy()
QDEL_NULL(soundloop)
@@ -33,22 +33,22 @@
STOP_PROCESSING(SSprocessing, src)
return ..()
/obj/item/reverse_bear_trap/process()
/obj/item/reverse_bear_trap/process(delta_time)
if(!ticking)
return
time_left--
time_left -= delta_time
soundloop2.mid_length = max(0.5, time_left - 5) //beepbeepbeepbeepbeep
if(!time_left || !isliving(loc))
if(time_left <= 0 || !isliving(loc))
playsound(src, 'sound/machines/microwave/microwave-end.ogg', 100, FALSE)
soundloop.stop()
soundloop2.stop()
to_chat(loc, "<span class='userdanger'>*ding*</span>")
to_chat(loc, span_userdanger("*ding*"))
addtimer(CALLBACK(src, .proc/snap), 2)
/obj/item/reverse_bear_trap/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(iscarbon(user))
var/mob/living/carbon/C = user
if(C.get_item_by_slot(SLOT_HEAD) == src)
if(C.get_item_by_slot(ITEM_SLOT_HEAD) == src)
if(HAS_TRAIT_FROM(src, TRAIT_NODROP, REVERSE_BEAR_TRAP_TRAIT) && !struggling)
struggling = TRUE
var/fear_string
@@ -82,26 +82,26 @@
..()
/obj/item/reverse_bear_trap/attack(mob/living/target, mob/living/user)
if(target.get_item_by_slot(SLOT_HEAD))
if(target.get_item_by_slot(ITEM_SLOT_HEAD))
to_chat(user, "<span class='warning'>Remove [target.p_their()] headgear first!</span>")
return
target.visible_message("<span class='warning'>[user] starts forcing [src] onto [target]'s head!</span>", \
"<span class='userdanger'>[target] starts forcing [src] onto your head!</span>", "<i>You hear clanking.</i>")
to_chat(user, "<span class='danger'>You start forcing [src] onto [target]'s head...</span>")
if(!do_after(user, 30, target = target) || target.get_item_by_slot(SLOT_HEAD))
if(!do_after(user, 30, target = target) || target.get_item_by_slot(ITEM_SLOT_HEAD))
return
target.visible_message("<span class='warning'>[user] forces and locks [src] onto [target]'s head!</span>", \
"<span class='userdanger'>[target] locks [src] onto your head!</span>", "<i>You hear a click, and then a timer ticking down.</i>")
to_chat(user, "<span class='danger'>You force [src] onto [target]'s head and click the padlock shut.</span>")
user.dropItemToGround(src)
target.equip_to_slot_if_possible(src, SLOT_HEAD)
target.equip_to_slot_if_possible(src, ITEM_SLOT_HEAD)
arm()
notify_ghosts("[user] put a reverse bear trap on [target]!", source = src, action = NOTIFY_ORBIT, ghost_sound = 'sound/machines/beep.ogg')
/obj/item/reverse_bear_trap/proc/snap()
reset()
var/mob/living/carbon/human/H = loc
if(!istype(H) || H.get_item_by_slot(SLOT_HEAD) != src)
if(!istype(H) || H.get_item_by_slot(ITEM_SLOT_HEAD) != src)
visible_message("<span class='warning'>[src]'s jaws snap open with an ear-piercing crack!</span>")
playsound(src, 'sound/effects/snap.ogg', 75, TRUE)
else
@@ -116,13 +116,22 @@
/obj/item/reverse_bear_trap/proc/reset()
ticking = FALSE
update_appearance(UPDATE_OVERLAYS)
REMOVE_TRAIT(src, TRAIT_NODROP, REVERSE_BEAR_TRAP_TRAIT)
soundloop.stop()
soundloop2.stop()
STOP_PROCESSING(SSprocessing, src)
/obj/item/reverse_bear_trap/update_overlays()
. = ..()
if(ticking != TRUE)
return
/// note: this timer overlay increments one frame every second (to simulate a clock ticking). If you want to instead have it do a full cycle in a minute, set the 'delay' of each frame of the icon overlay to 75 rather than 10, and the worn overlay to twice that.
// . += "rbt_ticking"
/obj/item/reverse_bear_trap/proc/arm() //hulen
ticking = TRUE
update_appearance(UPDATE_OVERLAYS)
escape_chance = initial(escape_chance) //we keep these vars until re-arm, for tracking purposes
time_left = initial(time_left)
ADD_TRAIT(src, TRAIT_NODROP, REVERSE_BEAR_TRAP_TRAIT)
+7 -4
View File
@@ -325,7 +325,7 @@ GENETICS SCANNER
var/breathes = TRUE
var/blooded = TRUE
if(C.dna && C.dna.species)
if(HAS_TRAIT_FROM(C, TRAIT_NOBREATH, SPECIES_TRAIT))
if(!HAS_TRAIT_FROM(C, TRAIT_AUXILIARY_LUNGS, SPECIES_TRAIT) && HAS_TRAIT_FROM(C, TRAIT_NOBREATH, SPECIES_TRAIT))
breathes = FALSE
if(NOBLOOD in C.dna.species.species_traits)
blooded = FALSE
@@ -380,6 +380,8 @@ GENETICS SCANNER
mutant = TRUE
else if (S.mutantstomach != initial(S.mutantstomach))
mutant = TRUE
else if (S.flying_species != initial(S.flying_species))
mutant = TRUE
msg += "\t<span class='info'>Reported Species: [H.spec_trait_examine_font()][H.dna.custom_species ? H.dna.custom_species : S.name]</font></span>\n"
msg += "\t<span class='info'>Base Species: [H.spec_trait_examine_font()][S.name]</font></span>\n"
@@ -434,12 +436,13 @@ GENETICS SCANNER
if(R)
blood_type = R.name
if((C.scan_blood_volume() + C.integrating_blood) <= (BLOOD_VOLUME_SAFE * C.blood_ratio) && (C.scan_blood_volume() + C.integrating_blood) > (BLOOD_VOLUME_OKAY*C.blood_ratio))
msg += "<span class='danger'>LOW blood level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""].</span> <span class='info'>type: [blood_type]</span>\n"
msg += "<span class='danger'>LOW [HAS_TRAIT(C, TRAIT_ROBOTIC_ORGANISM) ? "coolant" : "blood"] level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""].</span> <span class='info'>type: [blood_type]</span>\n"
else if((C.scan_blood_volume() + C.integrating_blood) <= (BLOOD_VOLUME_OKAY * C.blood_ratio))
msg += "<span class='danger'>CRITICAL blood level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""].</span> <span class='info'>type: [blood_type]</span>\n"
msg += "<span class='danger'>CRITICAL [HAS_TRAIT(C, TRAIT_ROBOTIC_ORGANISM) ? "coolant" : "blood"] level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""].</span> <span class='info'>type: [blood_type]</span>\n"
else
msg += "<span class='info'>Blood level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""]. type: [blood_type]</span>\n"
msg += "<span class='info'>[HAS_TRAIT(C, TRAIT_ROBOTIC_ORGANISM) ? "Coolant" : "Blood"] level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""]. type: [blood_type]</span>\n"
var/cyberimp_detect
@@ -208,7 +208,7 @@ effective or pretty fucking useless.
actions_types = list(/datum/action/item_action/toggle)
/obj/item/shadowcloak/ui_action_click(mob/user)
if(user.get_item_by_slot(SLOT_BELT) == src)
if(user.get_item_by_slot(ITEM_SLOT_BELT) == src)
if(!on)
Activate(usr)
else
@@ -216,7 +216,7 @@ effective or pretty fucking useless.
return
/obj/item/shadowcloak/item_action_slot_check(slot, mob/user, datum/action/A)
if(slot == SLOT_BELT)
if(slot == ITEM_SLOT_BELT)
return 1
/obj/item/shadowcloak/proc/Activate(mob/living/carbon/human/user)
@@ -238,11 +238,11 @@ effective or pretty fucking useless.
/obj/item/shadowcloak/dropped(mob/user)
..()
if(user && user.get_item_by_slot(SLOT_BELT) != src)
if(user && user.get_item_by_slot(ITEM_SLOT_BELT) != src)
Deactivate()
/obj/item/shadowcloak/process()
if(user.get_item_by_slot(SLOT_BELT) != src)
if(user.get_item_by_slot(ITEM_SLOT_BELT) != src)
Deactivate()
return
var/turf/T = get_turf(src)
+1 -1
View File
@@ -6,7 +6,7 @@
// 1) Tennis balls, lines 39 - 99
// 2) Chew bones, lines 101 - 138
// 3) Frisbee, lines 140 - 166
*/
*/
/obj/item/toy/fluff
name = "Fluff Item"
+1 -1
View File
@@ -336,7 +336,7 @@
var/obj/item/clothing/magichead = new /obj/item/clothing/mask/horsehead/cursed(user.drop_location())
if(!user.dropItemToGround(user.wear_mask))
qdel(user.wear_mask)
user.equip_to_slot_if_possible(magichead, SLOT_WEAR_MASK, TRUE, TRUE)
user.equip_to_slot_if_possible(magichead, ITEM_SLOT_MASK, TRUE, TRUE)
qdel(src)
else
to_chat(user,"<span class='notice'>I say thee neigh</span>") //It still lives here
@@ -29,7 +29,7 @@
if(target.mind.has_antag_datum(ANTAG_DATUM_VASSAL))
SSticker.mode.remove_vassal(target.mind)
if(target.mind.has_antag_datum(/datum/antagonist/rev/head) || target.mind.unconvertable || target.mind.has_antag_datum(/datum/antagonist/gang/boss))
if(target.mind.has_antag_datum(/datum/antagonist/rev/head) || (target.mind.unconvertable))
if(!silent)
target.visible_message("<span class='warning'>[target] seems to resist the implant!</span>", "<span class='warning'>You feel something interfering with your mental conditioning, but you resist it!</span>")
var/obj/item/implanter/I = loc
@@ -20,4 +20,7 @@
imp_type = /obj/item/implant/uplink/precharged
/obj/item/implant/uplink/precharged
starting_tc = 10
starting_tc = TELECRYSTALS_PRELOADED_IMPLANT
/obj/item/implant/uplink/starting
starting_tc = TELECRYSTALS_DEFAULT - UPLINK_IMPLANT_TELECRYSTAL_COST
@@ -189,5 +189,7 @@
return FALSE
brainwash(C, objective)
message_admins("[ADMIN_LOOKUPFLW(user)] brainwashed [key_name_admin(C)] with objective '[objective]'.")
user.log_message("has brainwashed [key_name(C)] with the objective '[objective]' using \the [src]", LOG_ATTACK)
C.log_message("has been brainwashed with the objective '[objective]' by [key_name(user)] using \the [src]", LOG_VICTIM, log_globally = FALSE)
log_game("[key_name(user)] brainwashed [key_name(C)] with objective '[objective]'.")
return TRUE
+1 -1
View File
@@ -188,7 +188,7 @@
icon_state = "inducer-combat"
item_state = "inducer-combat"
w_class = WEIGHT_CLASS_BULKY
slot_flags = SLOT_BELT
slot_flags = ITEM_SLOT_BELT
desc = "A tool for inductively charging internal power cells. This one has been modified and upgraded to be able to charge into guns as well as normal electronics."
cell_type = /obj/item/stock_parts/cell/hyper
powertransfer = 1300
+1 -1
View File
@@ -193,7 +193,7 @@
/obj/item/melee/transforming/energy/sword/saber
possible_colors = list("red" = LIGHT_COLOR_RED, "blue" = LIGHT_COLOR_LIGHT_CYAN, "green" = LIGHT_COLOR_GREEN, "purple" = LIGHT_COLOR_LAVENDER)
unique_reskin = list("Sword" = "sword0", "saber" = "esaber0")
unique_reskin = list("Sword" = "sword0", "Saber" = "esaber0")
var/hacked = FALSE
var/saber = FALSE
@@ -34,6 +34,7 @@
/obj/item/target/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(pinnedLoc)
pinnedLoc.removeTarget(user)
. = ..()
/obj/item/target/syndicate
icon_state = "target_s"
@@ -488,6 +488,9 @@
icon_state = "nanogel"
var/being_applied = FALSE //No doafter stacking.
/obj/item/stack/medical/nanogel/one
amount = 1
/obj/item/stack/medical/nanogel/try_heal(mob/living/M, mob/user, silent = FALSE)
if(being_applied)
to_chat(user, "<span class='warning'>You are already applying [src]!</span>")
+6 -2
View File
@@ -53,7 +53,6 @@
desc = "A satchel that opens into a localized pocket of Blue Space."
icon_state = "holdingsat"
item_state = "holdingsat"
species_exception = list(/datum/species/angel)
/obj/item/storage/backpack/holding/duffel
name = "duffel bag of holding"
@@ -190,7 +189,6 @@
name = "satchel"
desc = "A trendy looking satchel."
icon_state = "satchel-norm"
species_exception = list(/datum/species/angel) //satchels can be equipped since they are on the side, not back
/obj/item/storage/backpack/satchel/leather
name = "leather satchel"
@@ -657,3 +655,9 @@
desc = "Worn by snails as armor and storage compartment."
icon_state = "snailshell"
item_state = "snailshell"
/obj/item/storage/backpack/henchmen
name = "wings"
desc = "Granted to the henchmen who deserve it. This probably doesn't include you."
icon_state = "henchmen"
item_state = "henchmen"
+5 -5
View File
@@ -108,7 +108,7 @@
desc = "This little bugger can be used to store and transport ores."
icon = 'icons/obj/mining.dmi'
icon_state = "satchel"
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_POCKET
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_POCKETS
w_class = WEIGHT_CLASS_NORMAL
component_type = /datum/component/storage/concrete/stack
var/spam_protection = FALSE //If this is TRUE, the holder won't receive any messages when they fail to pick up ore through crossing it
@@ -400,7 +400,7 @@
icon = 'icons/obj/chemical.dmi'
icon_state = "bag"
desc = "A bag for storing pills, patches, and bottles."
slot_flags = ITEM_SLOT_BELT|ITEM_SLOT_POCKET
slot_flags = ITEM_SLOT_BELT|ITEM_SLOT_POCKETS
resistance_flags = FLAMMABLE
/obj/item/storage/bag/chemistry/ComponentInitialize()
@@ -420,7 +420,7 @@
icon = 'icons/obj/chemical.dmi'
icon_state = "biobag"
desc = "A bag for the safe transportation and disposal of biowaste and other biological materials."
slot_flags = ITEM_SLOT_BELT|ITEM_SLOT_POCKET
slot_flags = ITEM_SLOT_BELT|ITEM_SLOT_POCKETS
resistance_flags = FLAMMABLE
/obj/item/storage/bag/bio/ComponentInitialize()
@@ -451,7 +451,7 @@
desc = "A pouch for your ammo that goes in your pocket."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "ammopouch"
slot_flags = ITEM_SLOT_POCKET
slot_flags = ITEM_SLOT_POCKETS
w_class = WEIGHT_CLASS_BULKY
resistance_flags = FLAMMABLE
@@ -472,7 +472,7 @@ Bag for holding materials
desc = "A pouch for sheets and RCD ammunition that manages to hang where you would normally put things in your pocket."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "materialpouch"
slot_flags = ITEM_SLOT_POCKET
slot_flags = ITEM_SLOT_POCKETS
w_class = WEIGHT_CLASS_BULKY
resistance_flags = FLAMMABLE
+1 -1
View File
@@ -73,7 +73,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list("bible", "koran", "scrapbook", "bible",
var/mob/living/carbon/human/H = usr
H.dna.add_mutation(CLOWNMUT)
H.dna.add_mutation(SMILE)
H.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/clown_hat(H), SLOT_WEAR_MASK)
H.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/clown_hat(H), ITEM_SLOT_MASK)
GLOB.bible_icon_state = icon_state
GLOB.bibleitemstates = item_state
+2 -2
View File
@@ -248,10 +248,10 @@
return ..()
var/obj/item/clothing/mask/cigarette/cig = locate(/obj/item/clothing/mask/cigarette) in contents
if(cig)
if(!user.wear_mask && !(SLOT_WEAR_MASK in M.check_obscured_slots()))
if(!user.wear_mask && !(ITEM_SLOT_MASK in M.check_obscured_slots()))
var/obj/item/clothing/mask/cigarette/W = cig
SEND_SIGNAL(src, COMSIG_TRY_STORAGE_TAKE, W, M)
M.equip_to_slot_if_possible(W, SLOT_WEAR_MASK)
M.equip_to_slot_if_possible(W, ITEM_SLOT_MASK)
contents -= W
to_chat(user, "<span class='notice'>You take \a [W] out of the pack.</span>")
else
+539
View File
@@ -0,0 +1,539 @@
/// doing nothing/orbiting idly
#define STATE_IDLE 0
/// performing reset animation
#define STATE_RESET 1
/// performing attack animation
#define STATE_ATTACK 2
/// performing animation between attacks
#define STATE_RECOVER 3
/**
* Simple summon weapon code in this file
*
* tl;dr latch onto target, repeatedly proc attacks, animate using transforms,
* no real hitboxes/collisions, think of /datum/component/orbit-adjacent
*/
/obj/item/summon
name = "a horrifying mistake"
desc = "Why does this exist?"
/// datum type
var/host_type
/// number of summons
var/summon_count = 6
/// how long it takes for a "stack" to fall off by itself
var/stack_duration = 5 SECONDS
/// our summon weapon host
var/datum/summon_weapon_host/host
/// range summons will chase to
var/range = 7
/// are we a ranged weapon?
var/melee_only = TRUE
/obj/item/summon/Initialize()
. = ..()
if(host_type)
host = new host_type(src, summon_count, range)
/obj/item/summon/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
. = ..()
if(!host)
return
if(!proximity_flag && melee_only)
return
Target(target)
/obj/item/summon/dropped(mob/user, silent)
. = ..()
addtimer(CALLBACK(src, .proc/check_activation), 0, TIMER_UNIQUE)
/obj/item/summon/equipped(mob/user, slot)
. = ..()
addtimer(CALLBACK(src, .proc/check_activation), 0, TIMER_UNIQUE)
/obj/item/summon/proc/check_activation()
if(!host)
return
if(!isliving(loc))
host.SetMaster(null)
var/mob/living/L = loc
if(!istype(L))
return
if(!L.is_holding(src))
host.SetMaster(src)
host.Suppress()
host.SetMaster(L)
host.Wake()
/obj/item/summon/proc/Target(atom/victim)
if(!host?.CheckTarget(victim))
return
host.AutoTarget(victim, stack_duration)
/obj/item/summon/sword
name = "spectral blade"
desc = "An eldritch blade that summons phantasms to attack one's enemies."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "spectral"
item_state = "spectral"
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
host_type = /datum/summon_weapon_host/sword
force = 15
sharpness = SHARP_EDGED
/**
* Serves as the master datum for summon weapons
*/
/datum/summon_weapon_host
/// master atom
var/atom/master
/// suppressed?
var/active = TRUE
/// actual projectiles
var/list/datum/summon_weapon/controlled
/// active projectiles - refreshing a projectile reorders the list, so if they all have the same stack durations, you can trust the list to have last-refreshed at [1]
var/list/datum/summon_weapon/attacking
/// idle projectiles
var/list/datum/summon_weapon/idle
/// projectile type
var/weapon_type
/// default stack time
var/stack_time = 5 SECONDS
/// range
var/range = 7
/datum/summon_weapon_host/New(atom/master, count, range)
src.master = master
src.range = range
controlled = list()
attacking = list()
idle = list()
Create(count)
/datum/summon_weapon_host/Destroy()
QDEL_LIST(controlled)
master = null
return ..()
/datum/summon_weapon_host/proc/SetMaster(atom/master, reset_on_failure = TRUE)
var/changed = src.master != master
src.master = master
if(changed)
for(var/datum/summon_weapon/weapon as anything in idle)
weapon.Reset()
if(!master && reset_on_failure)
for(var/datum/summon_weapon/weapon as anything in attacking)
weapon.Reset()
/datum/summon_weapon_host/proc/Create(count)
if(!weapon_type)
return
for(var/i in 1 to min(count, clamp(20 - controlled.len - count, 0, 20)))
var/datum/summon_weapon/weapon = new weapon_type
Associate(weapon)
/datum/summon_weapon_host/proc/Associate(datum/summon_weapon/linking)
if(linking.host && linking.host != src)
linking.host.Disassociate(linking)
linking.host = src
controlled |= linking
linking.Reset()
/datum/summon_weapon_host/proc/Disassociate(datum/summon_weapon/unlinking, reset = TRUE, autodel)
if(unlinking.host == src)
unlinking.host = null
controlled -= unlinking
if(reset)
unlinking.Reset(del_no_host = autodel)
idle -= unlinking
attacking -= unlinking
/datum/summon_weapon_host/proc/AutoTarget(atom/victim, duration = stack_time)
if(!active)
return
var/datum/summon_weapon/weapon = (idle.len && idle[1]) || (attacking.len && attacking[1])
if(!weapon)
return
if(!CheckTarget(victim))
return
weapon.Target(victim)
if(duration)
weapon.ResetIn(duration)
/datum/summon_weapon_host/proc/OnTarget(datum/summon_weapon/weapon, atom/victim)
attacking -= weapon
idle -= weapon
attacking |= weapon
/datum/summon_weapon_host/proc/OnReset(datum/summon_weapon/weapon, atom/victim)
attacking -= weapon
idle |= weapon
/datum/summon_weapon_host/proc/CheckTarget(atom/victim)
if(isitem(victim))
return FALSE
if(QDELETED(victim))
return FALSE
if(victim == master)
return FALSE
if(isliving(victim))
var/mob/living/L = victim
if(L.stat == DEAD)
return FALSE
return TRUE
if(isobj(victim))
var/obj/O = victim
return (O.obj_flags & CAN_BE_HIT)
return FALSE
/datum/summon_weapon_host/proc/Suppress()
active = FALSE
for(var/datum/summon_weapon/weapon as anything in controlled)
weapon.Reset()
/datum/summon_weapon_host/proc/Wake()
active = TRUE
for(var/datum/summon_weapon/weapon as anything in controlled)
weapon.Reset()
/datum/summon_weapon_host/sword
weapon_type = /datum/summon_weapon/sword
/**
* A singular summoned object
*
* How summon weapons work:
*
* Reset() - makes it go back to its master.
* Target() - locks onto a target for a duration
*
* The biggest challenge is synchronizing animations.
* Variables keep track of when things tick, but,
* animations are client-timed, and not server-timed
*
* Animations:
* The weapon can only track its "intended" angle and dist
* "Current" pixel x/y are always calculated relative to a target from the current orbiting atom the physical effect is on
* There's 3 animations,
* MoveTo(location, angle, dist, rotation)
* Orbit(location)
* Rotate(degrees)
*
* And an non-animation that just snaps it to a location,
* HardReset(location)
*/
/datum/summon_weapon
/// name
var/name = "summoned weapon"
/// host
var/datum/summon_weapon_host/host
/// icon file
var/icon = 'icons/effects/summon.dmi'
/// icon state
var/icon_state
/// mutable_appearance to use, will skip making from icon/icon state if so
var/mutable_appearance/appearance
/// the actual effect
var/atom/movable/summon_weapon_effect/atom
/// currently locked attack target
var/atom/victim
/// current angle from victim - clockwise from 0. null if not attacking.
var/angle
/// current distance from victim - pixels
var/dist
/// current rotation - angles clockwise from north
var/rotation
/// rand dist to rotate during reattack phase
var/angle_vary = 45
/// orbit distance from victim - pixels
var/orbit_dist = 72
/// orbit distance variation from victim
var/orbit_dist_vary = 24
/// attack delay in deciseconds - this is time spent between attacks
var/attack_speed = 1.5
/// attack length in deciseconds - this is the attack animation speed in total
var/attack_length = 1.5
/// attack damage
var/attack_damage = 5
/// reset animation duration
var/reset_speed = 2
/// attack damtype
var/attack_type = BRUTE
/// attack sound
var/attack_sound = list(
'sound/weapons/bladeslice.ogg',
'sound/weapons/bladesliceb.ogg'
)
/// attack verb
var/attack_verb = list(
"rended",
"pierced",
"penetrated",
"sliced"
)
/// current state
var/state = STATE_IDLE
/// animation locked until
var/animation_lock
/// animation lock timer
var/animation_timerid
/// reset timerid
var/reset_timerid
/datum/summon_weapon/New(mutable_appearance/appearance_override)
if(appearance_override)
appearance = appearance_override
Setup()
attack_verb = typelist(NAMEOF(src, attack_verb), attack_verb)
attack_sound = typelist(NAMEOF(src, attack_sound), attack_sound)
/datum/summon_weapon/Destroy()
host.Disassociate(src, autodel = FALSE)
QDEL_NULL(atom)
QDEL_NULL(appearance)
return ..()
/datum/summon_weapon/proc/Setup()
atom = new
if(!appearance)
GenerateAppearance()
atom.appearance = appearance
atom.moveToNullspace()
if(host)
Reset()
/datum/summon_weapon/proc/GenerateAppearance()
if(!appearance)
appearance = new
appearance.icon = icon
appearance.icon_state = icon_state
appearance.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
appearance.opacity = FALSE
appearance.plane = GAME_PLANE
appearance.layer = ABOVE_MOB_LAYER
appearance.appearance_flags = KEEP_TOGETHER
appearance.overlays = list(
emissive_appearance(icon, icon_state)
)
/datum/summon_weapon/proc/Reset(immediate = FALSE, del_no_host = TRUE)
angle = null
victim = null
if(reset_timerid)
deltimer(reset_timerid)
reset_timerid = null
host?.OnReset(src)
atom.Release()
state = STATE_RESET
if(!host)
if(del_no_host)
qdel(src)
return
HardReset(null)
atom.moveToNullspace()
return
if(immediate)
if(animation_timerid)
deltimer(animation_timerid)
Act()
else
Wake()
/datum/summon_weapon/proc/ResetIn(ds)
reset_timerid = addtimer(CALLBACK(src, .proc/Reset), ds, TIMER_STOPPABLE)
/datum/summon_weapon/proc/Target(atom/victim)
if(!istype(victim) || !isturf(victim.loc) || (host && !host.CheckTarget(victim)))
Reset()
return
src.victim = victim
host.OnTarget(src, victim)
state = STATE_ATTACK
Wake()
/datum/summon_weapon/proc/Wake()
if(!animation_timerid)
Act()
/datum/summon_weapon/proc/AnimationLock(duration)
if(animation_timerid)
deltimer(animation_timerid)
animation_timerid = addtimer(CALLBACK(src, .proc/Act), duration, TIMER_CLIENT_TIME | TIMER_STOPPABLE)
/datum/summon_weapon/proc/Act()
animation_timerid = null
switch(state)
if(STATE_IDLE)
return
if(STATE_ATTACK)
if(!isturf(victim.loc) || (host && !host.CheckTarget(victim)))
Reset(TRUE)
return
state = STATE_RECOVER
// register hit at the halfway mark
// we can do better math to approximate when the attack will hit but i'm too tired to bother
addtimer(CALLBACK(src, .proc/Hit, victim), attack_length / 2, TIMER_CLIENT_TIME)
// we need to approximate our incoming angle - again, better math exists but why bother
var/incoming_angle = angle
if(isturf(atom.loc) && (atom.loc != victim.loc))
incoming_angle = Get_Angle(atom.loc, victim.loc)
// pierce through target
// we do not want to turn while doing this so we pierce through them visually
incoming_angle += 180
var/outgoing_angle = SIMPLIFY_DEGREES(incoming_angle)
AnimationLock(MoveTo(victim, null, outgoing_angle, orbit_dist + rand(-orbit_dist_vary, orbit_dist_vary), outgoing_angle, attack_length))
if(STATE_RESET)
state = STATE_IDLE
if(!host || !host.active || !get_turf(host.master))
atom.moveToNullspace()
src.angle = null
src.dist = null
src.rotation = null
return
var/reset_angle = rand(0, 360)
AnimationLock(MoveTo(host.master, null, reset_angle, 30, 90, reset_speed))
addtimer(CALLBACK(src, .proc/Orbit, host.master, reset_angle, 30, 3 SECONDS), reset_speed, TIMER_CLIENT_TIME)
if(STATE_RECOVER)
state = STATE_ATTACK
AnimationLock(Rotate(rand(-angle_vary, angle_vary), attack_speed, null))
/datum/summon_weapon/proc/Hit(atom/victim)
if(!isobj(victim) && !isliving(victim))
return FALSE
if(isliving(victim))
var/mob/living/L = victim
L.apply_damage(attack_damage, attack_type)
playsound(victim, pick(attack_sound), 75)
else if(isobj(victim))
var/obj/O = victim
O.take_damage(attack_damage, attack_type)
return TRUE
/**
* relative to defaults to current location
*/
/datum/summon_weapon/proc/MoveTo(atom/destination, atom/relative_to, angle = 0, dist = 64, rotation = 180, time)
. = time
// construct final transform
var/matrix/dest = ConstructMatrix(angle, dist, rotation)
// move to
atom.Lock(destination)
// get relative first positions
relative_to = get_turf(relative_to || atom.locked)
destination = get_turf(destination)
// if none, move to immediately and end
if(!relative_to)
atom.transform = dest
src.angle = angle
src.dist = dist
src.rotation = rotation
// end animations
animate(atom, time = 0, flags = ANIMATION_END_NOW)
return 0
// grab source
var/rel_x = (destination.x - relative_to.x) * world.icon_size + src.dist * sin(src.angle)
var/rel_y = (destination.y - relative_to.y) * world.icon_size + src.dist * cos(src.angle)
// construct source matrix
var/matrix/source = new
source.Turn((relative_to == get_turf(atom.locked))? src.rotation : Get_Angle(relative_to, destination))
source.Translate(rel_x, rel_y)
// set vars
src.angle = angle
src.dist = dist
src.rotation = rotation
// animate
atom.transform = source
animate(atom, transform = dest, time, FALSE, LINEAR_EASING, ANIMATION_LINEAR_TRANSFORM | ANIMATION_END_NOW)
/**
* rotation defaults to facing towards locked atom
*/
/datum/summon_weapon/proc/Rotate(degrees, time, rotation)
. = time
if(!dist)
return 0
var/matrix/M = ConstructMatrix(angle + degrees, dist, rotation || src.rotation)
if(rotation)
src.rotation = rotation
angle += degrees
animate(atom, transform = M, time, FALSE, LINEAR_EASING, ANIMATION_END_NOW | ANIMATION_LINEAR_TRANSFORM)
/datum/summon_weapon/proc/Orbit(atom/destination, initial_degrees = rand(0, 360), dist, speed)
. = 0
atom.Lock(destination)
animate(atom, 0, FALSE, flags = ANIMATION_END_NOW)
atom.transform = ConstructMatrix(initial_degrees, dist, 90)
atom.SpinAnimation(speed, parallel = FALSE, segments = 10)
// we can't predict dist/angle anymre because clienttime vs servertime.
// well, we can, but, let's not be bothered with timeofday math eh.
dist = 0
angle = 0
/datum/summon_weapon/proc/ConstructMatrix(angle = 0, dist = 64, rotation = 0)
var/matrix/M = new
M.Turn(rotation)
M.Translate(0, dist)
M.Turn(angle)
return M
/datum/summon_weapon/proc/HardReset(atom/snap_to)
if(animation_timerid)
deltimer(animation_timerid)
atom.Release()
atom.forceMove(snap_to)
atom.transform = null
/datum/summon_weapon/sword
name = "spectral blade"
icon_state = "sword"
attack_damage = 5
attack_speed = 1.5
attack_length = 1.5
/atom/movable/summon_weapon_effect
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
plane = GAME_PLANE
layer = ABOVE_MOB_LAYER
opacity = FALSE
density = FALSE
/// locked atom
var/atom/locked
/atom/movable/summon_weapon_effect/Destroy()
Release()
return ..()
/atom/movable/summon_weapon_effect/proc/Lock(atom/target)
if(locked == target)
return
if(locked)
Release()
if(!target)
return
locked = target
forceMove(locked.loc)
if(ismovable(locked))
RegisterSignal(locked, COMSIG_MOVABLE_MOVED, .proc/Update)
/atom/movable/summon_weapon_effect/proc/Release()
if(ismovable(locked))
UnregisterSignal(locked, COMSIG_MOVABLE_MOVED)
locked = null
/atom/movable/summon_weapon_effect/proc/Update()
if(!locked)
return
if(loc != locked.loc)
forceMove(locked.loc)
#undef STATE_IDLE
#undef STATE_ATTACK
#undef STATE_RECOVER
#undef STATE_RESET
+2 -2
View File
@@ -291,8 +291,8 @@
message_admins("Explosive tank rupture! Last key to touch the tank was [src.fingerprintslast].")
log_game("Explosive tank rupture! Last key to touch the tank was [src.fingerprintslast].")
//Give the gas a chance to build up more pressure through reacting
air_contents.react(src)
air_contents.react(src)
for(var/i in 1 to TANK_POST_FRAGMENT_REACTIONS)
air_contents.react(src)
pressure = air_contents.return_pressure()
var/range = (pressure-TANK_FRAGMENT_PRESSURE)/TANK_FRAGMENT_SCALE
+19 -15
View File
@@ -58,7 +58,7 @@
/obj/item/watertank/equipped(mob/user, slot)
..()
if(slot != SLOT_BACK)
if(slot != ITEM_SLOT_BACK)
remove_noz()
/obj/item/watertank/proc/remove_noz()
@@ -271,18 +271,7 @@
if(resin_cooldown)
to_chat(user, "<span class='warning'>Resin launcher is still recharging...</span>")
return
resin_cooldown = TRUE
R.remove_any(100)
var/obj/effect/resin_container/A = new (get_turf(src))
log_game("[key_name(user)] used Resin Launcher at [AREACOORD(user)].")
playsound(src,'sound/items/syringeproj.ogg',40,1)
for(var/a=0, a<5, a++)
step_towards(A, target)
sleep(2)
A.Smoke()
spawn(100)
if(src)
resin_cooldown = FALSE
launch_resin(target, user)
return
if(nozzle_mode == RESIN_FOAM)
if(!Adj|| !isturf(target))
@@ -301,6 +290,21 @@
to_chat(user, "<span class='warning'>Resin foam mix is still being synthesized...</span>")
return
/obj/item/extinguisher/mini/nozzle/proc/launch_resin(atom/target, mob/user)
set waitfor = FALSE
resin_cooldown = TRUE
reagents.remove_any(100)
var/obj/effect/resin_container/A = new (get_turf(src))
log_game("[key_name(user)] used Resin Launcher at [AREACOORD(user)].")
playsound(src,'sound/items/syringeproj.ogg',40,1)
for(var/a=0, a<5, a++)
step_towards(A, target)
sleep(2)
A.Smoke()
spawn(100)
if(src)
resin_cooldown = FALSE
/obj/effect/resin_container
name = "resin container"
desc = "A compacted ball of expansive resin, used to repair the atmosphere in a room, or seal off breaches."
@@ -344,14 +348,14 @@
toggle_injection()
/obj/item/reagent_containers/chemtank/item_action_slot_check(slot, mob/user, datum/action/A)
if(slot == SLOT_BACK)
if(slot == ITEM_SLOT_BACK)
return 1
/obj/item/reagent_containers/chemtank/proc/toggle_injection()
var/mob/living/carbon/human/user = usr
if(!istype(user))
return
if (user.get_item_by_slot(SLOT_BACK) != src)
if (user.get_item_by_slot(ITEM_SLOT_BACK) != src)
to_chat(user, "<span class='warning'>The chemtank needs to be on your back before you can activate it!</span>")
return
if(on)
@@ -46,6 +46,8 @@
create_reagents(max_fuel)
reagents.add_reagent(/datum/reagent/fuel, max_fuel)
update_icon()
if(can_off_process)
START_PROCESSING(SSobj, src)
/obj/item/weldingtool/ComponentInitialize()
. = ..()
+117 -3
View File
@@ -1105,7 +1105,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon_state = "madeyoulook"
force = 0
throwforce = 0
item_flags = DROPDEL | ABSTRACT // | HAND_ITEM
item_flags = DROPDEL | ABSTRACT | HAND_ITEM
attack_verb = list("bopped")
/obj/item/circlegame/Initialize()
@@ -1207,7 +1207,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
item_state = "nothing"
force = 0
throwforce = 0
item_flags = DROPDEL | ABSTRACT // | HAND_ITEM
item_flags = DROPDEL | ABSTRACT | HAND_ITEM
attack_verb = list("slapped")
hitsound = 'sound/effects/snap.ogg'
@@ -1227,6 +1227,120 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
return FALSE
return TRUE
/obj/item/slapper/on_offered(mob/living/carbon/offerer)
. = TRUE
if(!(locate(/mob/living/carbon) in orange(1, offerer)))
visible_message(span_danger("[offerer] raises [offerer.p_their()] arm, looking around for a high-five, but there's no one around!"), \
span_warning("You post up, looking for a high-five, but finding no one within range!"), null, 2)
return
offerer.visible_message(span_notice("[offerer] raises [offerer.p_their()] arm, looking for a high-five!"), \
span_notice("You post up, looking for a high-five!"), null, 2)
offerer.apply_status_effect(STATUS_EFFECT_OFFERING, src, /atom/movable/screen/alert/give/highfive)
/// Yeah broh! This is where we do the high-fiving (or high-tenning :o)
/obj/item/slapper/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker)
. = TRUE
var/open_hands_taker
var/slappers_giver
for(var/i in taker.held_items) // see how many hands the taker has open for high'ing
if(isnull(i))
open_hands_taker++
if(!open_hands_taker)
to_chat(taker, span_warning("You can't high-five [offerer] with no open hands!"))
SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five_full_hand) // not so successful now!
return
for(var/i in offerer.held_items)
var/obj/item/slapper/slap_check = i
if(istype(slap_check))
slappers_giver++
if(slappers_giver >= 2) // we only check this if it's already established the taker has 2+ hands free
offerer.visible_message(span_notice("[taker] enthusiastically high-tens [offerer]!"), span_nicegreen("Wow! You're high-tenned [taker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), ignored_mobs=taker)
to_chat(taker, span_nicegreen("You give high-tenning [offerer] your all!"))
playsound(offerer, 'sound/weapons/slap.ogg', 100, TRUE, 1)
SEND_SIGNAL(offerer, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_ten)
SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_ten)
else
offerer.visible_message(span_notice("[taker] high-fives [offerer]!"), span_nicegreen("All right! You're high-fived by [taker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), ignored_mobs=taker)
to_chat(taker, span_nicegreen("You high-five [offerer]!"))
playsound(offerer, 'sound/weapons/slap.ogg', 50, TRUE, -1)
SEND_SIGNAL(offerer, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five)
SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five)
qdel(src)
/// Gangster secret handshakes.
/obj/item/slapper/secret_handshake
name = "Secret Handshake"
icon_state = "recruit"
icon = 'icons/obj/gang/actions.dmi'
/// References the active families gamemode handler (if one exists), for adding new family members to.
var/datum/gang_handler/handler
/// The typepath of the gang antagonist datum that the person who uses the package should have added to them -- remember that the distinction between e.g. Ballas and Grove Street is on the antag datum level, not the team datum level.
var/gang_to_use
/// The team datum that the person who uses this package should be added to.
var/datum/team/gang/team_to_use
/// Adds the user to the family that this package corresponds to, dispenses the free_clothes of that family, and adds them to the handler if it exists.
/obj/item/slapper/secret_handshake/proc/add_to_gang(mob/living/user, original_name)
var/datum/antagonist/gang/swappin_sides = new gang_to_use()
swappin_sides.original_name = original_name
swappin_sides.handler = handler
user.mind.add_antag_datum(swappin_sides, team_to_use)
var/policy = get_policy(ROLE_FAMILIES)
if(policy)
to_chat(user, policy)
team_to_use.add_member(user.mind)
swappin_sides.equip_gangster_in_inventory()
if (!isnull(handler) && !handler.gangbangers.Find(user.mind)) // if we have a handler and they're not tracked by it
handler.gangbangers += user.mind
/// Checks if the user is trying to use the package of the family they are in, and if not, adds them to the family, with some differing processing depending on whether the user is already a family member.
/obj/item/slapper/secret_handshake/proc/attempt_join_gang(mob/living/user)
if(!user?.mind)
return
var/datum/antagonist/gang/is_gangster = user.mind.has_antag_datum(/datum/antagonist/gang)
var/real_name_backup = user.real_name
if(is_gangster)
if(is_gangster.my_gang == team_to_use)
return
real_name_backup = is_gangster.original_name
is_gangster.my_gang.remove_member(user.mind)
user.mind.remove_antag_datum(/datum/antagonist/gang)
add_to_gang(user, real_name_backup)
/obj/item/slapper/secret_handshake/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker)
. = TRUE
if (!(null in taker.held_items))
to_chat(taker, span_warning("You can't get taught the secret handshake if [offerer] has no free hands!"))
return
if(HAS_TRAIT(taker, TRAIT_MINDSHIELD))
to_chat(taker, "You attended a seminar on not signing up for a gang and are not interested.")
return
var/datum/antagonist/gang/is_gangster = taker.mind.has_antag_datum(/datum/antagonist/gang)
if(is_gangster?.starter_gangster)
if(is_gangster.my_gang == team_to_use)
to_chat(taker, "You started your family. You don't need to join it.")
return
to_chat(taker, "You started your family. You can't turn your back on it now.")
return
offerer.visible_message(span_notice("[taker] is taught the secret handshake by [offerer]!"), span_nicegreen("All right! You've taught the secret handshake to [taker]!"), span_hear("You hear a bunch of weird shuffling and flesh slapping sounds!"), ignored_mobs=taker)
to_chat(taker, span_nicegreen("You get taught the secret handshake by [offerer]!"))
var/datum/antagonist/gang/owner_gang_datum = offerer.mind.has_antag_datum(/datum/antagonist/gang)
handler = owner_gang_datum.handler
gang_to_use = owner_gang_datum.type
team_to_use = owner_gang_datum.my_gang
attempt_join_gang(taker)
qdel(src)
/obj/item/extendohand
name = "extendo-hand"
desc = "Futuristic tech has allowed these classic spring-boxing toys to essentially act as a fully functional hand-operated hand prosthetic."
@@ -1261,7 +1375,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
// attack_verb_simple = list("whack", "thwack", "wallop", "sock")
// icon = 'icons/obj/items_and_weapons.dmi'
// icon_state = "gohei"
// inhand_icon_state = "gohei"
// item_state = "gohei"
// lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
// righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'