Merge branch 'master' of https://github.com/Citadel-Station-13/Citadel-Station-13 into station_traits
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>")
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
. = ..()
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user