mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-31 03:52:31 +00:00
* Cyborgs can now shake up compromised carbons (#62468) The Hug Module from Medical and Peacekeeper Cyborgs now removes negative stats (the same as a human) from carbons when used on Mode 1. Put in a simple way, this allows the three referred Cyborg types to wake up and remove stuns from carbons with the use of their Hug Module. * Cyborgs can now shake up compromised carbons Co-authored-by: Justice <42555530+Justice12354@users.noreply.github.com>
1048 lines
34 KiB
Plaintext
1048 lines
34 KiB
Plaintext
/**********************************************************************
|
|
Cyborg Spec Items
|
|
***********************************************************************/
|
|
/obj/item/borg
|
|
icon = 'icons/mob/robot_items.dmi'
|
|
|
|
|
|
/obj/item/borg/stun
|
|
name = "electrically-charged arm"
|
|
icon_state = "elecarm"
|
|
var/charge_cost = 30
|
|
|
|
/obj/item/borg/stun/attack(mob/living/M, mob/living/user)
|
|
if(ishuman(M))
|
|
var/mob/living/carbon/human/H = M
|
|
if(H.check_shields(src, 0, "[M]'s [name]", MELEE_ATTACK))
|
|
playsound(M, 'sound/weapons/genhit.ogg', 50, TRUE)
|
|
return FALSE
|
|
if(iscyborg(user))
|
|
var/mob/living/silicon/robot/R = user
|
|
if(!R.cell.use(charge_cost))
|
|
return
|
|
|
|
user.do_attack_animation(M)
|
|
M.Paralyze(100)
|
|
M.apply_effect(EFFECT_STUTTER, 5)
|
|
|
|
M.visible_message(span_danger("[user] prods [M] with [src]!"), \
|
|
span_userdanger("[user] prods you with [src]!"))
|
|
|
|
playsound(loc, 'sound/weapons/egloves.ogg', 50, TRUE, -1)
|
|
|
|
log_combat(user, M, "stunned", src, "(Combat mode: [user.combat_mode ? "On" : "Off"])")
|
|
|
|
/obj/item/borg/cyborghug
|
|
name = "hugging module"
|
|
icon_state = "hugmodule"
|
|
desc = "For when a someone really needs a hug."
|
|
var/mode = 0 //0 = Hugs 1 = "Hug" 2 = Shock 3 = CRUSH
|
|
var/ccooldown = 0
|
|
var/scooldown = 0
|
|
var/shockallowed = FALSE//Can it be a stunarm when emagged. Only PK borgs get this by default.
|
|
var/boop = FALSE
|
|
|
|
/obj/item/borg/cyborghug/attack_self(mob/living/user)
|
|
if(iscyborg(user))
|
|
var/mob/living/silicon/robot/P = user
|
|
if(P.emagged&&shockallowed == 1)
|
|
if(mode < 3)
|
|
mode++
|
|
else
|
|
mode = 0
|
|
else if(mode < 1)
|
|
mode++
|
|
else
|
|
mode = 0
|
|
switch(mode)
|
|
if(0)
|
|
to_chat(user, "<span class='infoplain'>Power reset. Hugs!</span>")
|
|
if(1)
|
|
to_chat(user, "<span class='infoplain'>Power increased!</span>")
|
|
if(2)
|
|
to_chat(user, "<span class='warningplain'>BZZT. Electrifying arms...</span>")
|
|
if(3)
|
|
to_chat(user, "<span class='warningplain'>ERROR: ARM ACTUATORS OVERLOADED.</span>")
|
|
|
|
/obj/item/borg/cyborghug/attack(mob/living/M, mob/living/silicon/robot/user, params)
|
|
if(M == user)
|
|
return
|
|
switch(mode)
|
|
if(0)
|
|
if(M.health >= 0)
|
|
if(isanimal(M))
|
|
var/list/modifiers = params2list(params)
|
|
if (!user.combat_mode && !LAZYACCESS(modifiers, RIGHT_CLICK))
|
|
M.attack_hand(user, modifiers) //This enables borgs to get the floating heart icon and mob emote from simple_animal's that have petbonus == true.
|
|
return
|
|
if(user.zone_selected == BODY_ZONE_HEAD)
|
|
user.visible_message(span_notice("[user] playfully boops [M] on the head!"), \
|
|
span_notice("You playfully boop [M] on the head!"))
|
|
user.do_attack_animation(M, ATTACK_EFFECT_BOOP)
|
|
playsound(loc, 'sound/weapons/tap.ogg', 50, TRUE, -1)
|
|
else if(ishuman(M))
|
|
if(user.body_position == LYING_DOWN)
|
|
user.visible_message(span_notice("[user] shakes [M] trying to get [M.p_them()] up!"), \
|
|
span_notice("You shake [M] trying to get [M.p_them()] up!"))
|
|
else
|
|
user.visible_message(span_notice("[user] hugs [M] to make [M.p_them()] feel better!"), \
|
|
span_notice("You hug [M] to make [M.p_them()] feel better!"))
|
|
if(M.resting)
|
|
M.set_resting(FALSE, TRUE)
|
|
else
|
|
user.visible_message(span_notice("[user] pets [M]!"), \
|
|
span_notice("You pet [M]!"))
|
|
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
|
|
if(1)
|
|
if(M.health >= 0)
|
|
if(ishuman(M))
|
|
M.adjust_status_effects_on_shake_up()
|
|
if(M.body_position == LYING_DOWN)
|
|
user.visible_message(span_notice("[user] shakes [M] trying to get [M.p_them()] up!"), \
|
|
span_notice("You shake [M] trying to get [M.p_them()] up!"))
|
|
else if(user.zone_selected == BODY_ZONE_HEAD)
|
|
user.visible_message(span_warning("[user] bops [M] on the head!"), \
|
|
span_warning("You bop [M] on the head!"))
|
|
user.do_attack_animation(M, ATTACK_EFFECT_PUNCH)
|
|
else
|
|
user.visible_message(span_warning("[user] hugs [M] in a firm bear-hug! [M] looks uncomfortable..."), \
|
|
span_warning("You hug [M] firmly to make [M.p_them()] feel better! [M] looks uncomfortable..."))
|
|
if(M.resting)
|
|
M.set_resting(FALSE, TRUE)
|
|
else
|
|
user.visible_message(span_warning("[user] bops [M] on the head!"), \
|
|
span_warning("You bop [M] on the head!"))
|
|
playsound(loc, 'sound/weapons/tap.ogg', 50, TRUE, -1)
|
|
if(2)
|
|
if(scooldown < world.time)
|
|
if(M.health >= 0)
|
|
if(ishuman(M))
|
|
M.electrocute_act(5, "[user]", flags = SHOCK_NOGLOVES)
|
|
user.visible_message(span_userdanger("[user] electrocutes [M] with [user.p_their()] touch!"), \
|
|
span_danger("You electrocute [M] with your touch!"))
|
|
else
|
|
if(!iscyborg(M))
|
|
M.adjustFireLoss(10)
|
|
user.visible_message(span_userdanger("[user] shocks [M]!"), \
|
|
span_danger("You shock [M]!"))
|
|
else
|
|
user.visible_message(span_userdanger("[user] shocks [M]. It does not seem to have an effect"), \
|
|
span_danger("You shock [M] to no effect."))
|
|
playsound(loc, 'sound/effects/sparks2.ogg', 50, TRUE, -1)
|
|
user.cell.charge -= 500
|
|
scooldown = world.time + 20
|
|
if(3)
|
|
if(ccooldown < world.time)
|
|
if(M.health >= 0)
|
|
if(ishuman(M))
|
|
user.visible_message(span_userdanger("[user] crushes [M] in [user.p_their()] grip!"), \
|
|
span_danger("You crush [M] in your grip!"))
|
|
else
|
|
user.visible_message(span_userdanger("[user] crushes [M]!"), \
|
|
span_danger("You crush [M]!"))
|
|
playsound(loc, 'sound/weapons/smash.ogg', 50, TRUE, -1)
|
|
M.adjustBruteLoss(15)
|
|
user.cell.charge -= 300
|
|
ccooldown = world.time + 10
|
|
|
|
/obj/item/borg/cyborghug/peacekeeper
|
|
shockallowed = TRUE
|
|
|
|
/obj/item/borg/cyborghug/medical
|
|
boop = TRUE
|
|
|
|
/obj/item/borg/charger
|
|
name = "power connector"
|
|
icon_state = "charger_draw"
|
|
item_flags = NOBLUDGEON
|
|
var/mode = "draw"
|
|
var/static/list/charge_machines = typecacheof(list(/obj/machinery/cell_charger, /obj/machinery/recharger, /obj/machinery/recharge_station, /obj/machinery/mech_bay_recharge_port))
|
|
var/static/list/charge_items = typecacheof(list(/obj/item/stock_parts/cell, /obj/item/gun/energy))
|
|
|
|
/obj/item/borg/charger/update_icon_state()
|
|
icon_state = "charger_[mode]"
|
|
return ..()
|
|
|
|
/obj/item/borg/charger/attack_self(mob/user)
|
|
if(mode == "draw")
|
|
mode = "charge"
|
|
else
|
|
mode = "draw"
|
|
to_chat(user, span_notice("You toggle [src] to \"[mode]\" mode."))
|
|
update_appearance()
|
|
|
|
/obj/item/borg/charger/afterattack(obj/item/target, mob/living/silicon/robot/user, proximity_flag)
|
|
. = ..()
|
|
if(!proximity_flag || !iscyborg(user))
|
|
return
|
|
if(mode == "draw")
|
|
if(is_type_in_list(target, charge_machines))
|
|
var/obj/machinery/M = target
|
|
if((M.machine_stat & (NOPOWER|BROKEN)) || !M.anchored)
|
|
to_chat(user, span_warning("[M] is unpowered!"))
|
|
return
|
|
|
|
to_chat(user, span_notice("You connect to [M]'s power line..."))
|
|
while(do_after(user, 15, target = M, progress = 0))
|
|
if(!user || !user.cell || mode != "draw")
|
|
return
|
|
|
|
if((M.machine_stat & (NOPOWER|BROKEN)) || !M.anchored)
|
|
break
|
|
|
|
if(!user.cell.give(150))
|
|
break
|
|
|
|
M.use_power(200)
|
|
|
|
to_chat(user, span_notice("You stop charging yourself."))
|
|
|
|
else if(is_type_in_list(target, charge_items))
|
|
var/obj/item/stock_parts/cell/cell = target
|
|
if(!istype(cell))
|
|
cell = locate(/obj/item/stock_parts/cell) in target
|
|
if(!cell)
|
|
to_chat(user, span_warning("[target] has no power cell!"))
|
|
return
|
|
|
|
if(istype(target, /obj/item/gun/energy))
|
|
var/obj/item/gun/energy/E = target
|
|
if(!E.can_charge)
|
|
to_chat(user, span_warning("[target] has no power port!"))
|
|
return
|
|
|
|
if(!cell.charge)
|
|
to_chat(user, span_warning("[target] has no power!"))
|
|
|
|
|
|
to_chat(user, span_notice("You connect to [target]'s power port..."))
|
|
|
|
while(do_after(user, 15, target = target, progress = 0))
|
|
if(!user || !user.cell || mode != "draw")
|
|
return
|
|
|
|
if(!cell || !target)
|
|
return
|
|
|
|
if(cell != target && cell.loc != target)
|
|
return
|
|
|
|
var/draw = min(cell.charge, cell.chargerate*0.5, user.cell.maxcharge-user.cell.charge)
|
|
if(!cell.use(draw))
|
|
break
|
|
if(!user.cell.give(draw))
|
|
break
|
|
target.update_appearance()
|
|
|
|
to_chat(user, span_notice("You stop charging yourself."))
|
|
|
|
else if(is_type_in_list(target, charge_items))
|
|
var/obj/item/stock_parts/cell/cell = target
|
|
if(!istype(cell))
|
|
cell = locate(/obj/item/stock_parts/cell) in target
|
|
if(!cell)
|
|
to_chat(user, span_warning("[target] has no power cell!"))
|
|
return
|
|
|
|
if(istype(target, /obj/item/gun/energy))
|
|
var/obj/item/gun/energy/E = target
|
|
if(!E.can_charge)
|
|
to_chat(user, span_warning("[target] has no power port!"))
|
|
return
|
|
|
|
if(cell.charge >= cell.maxcharge)
|
|
to_chat(user, span_warning("[target] is already charged!"))
|
|
|
|
to_chat(user, span_notice("You connect to [target]'s power port..."))
|
|
|
|
while(do_after(user, 15, target = target, progress = 0))
|
|
if(!user || !user.cell || mode != "charge")
|
|
return
|
|
|
|
if(!cell || !target)
|
|
return
|
|
|
|
if(cell != target && cell.loc != target)
|
|
return
|
|
|
|
var/draw = min(user.cell.charge, cell.chargerate*0.5, cell.maxcharge-cell.charge)
|
|
if(!user.cell.use(draw))
|
|
break
|
|
if(!cell.give(draw))
|
|
break
|
|
target.update_appearance()
|
|
|
|
to_chat(user, span_notice("You stop charging [target]."))
|
|
|
|
/obj/item/harmalarm
|
|
name = "\improper Sonic Harm Prevention Tool"
|
|
desc = "Releases a harmless blast that confuses most organics. For when the harm is JUST TOO MUCH."
|
|
icon = 'icons/obj/device.dmi'
|
|
icon_state = "megaphone"
|
|
var/cooldown = 0
|
|
|
|
/obj/item/harmalarm/emag_act(mob/user)
|
|
obj_flags ^= EMAGGED
|
|
if(obj_flags & EMAGGED)
|
|
to_chat(user, "<font color='red'>You short out the safeties on [src]!</font>")
|
|
else
|
|
to_chat(user, "<font color='red'>You reset the safeties on [src]!</font>")
|
|
|
|
/obj/item/harmalarm/attack_self(mob/user)
|
|
var/safety = !(obj_flags & EMAGGED)
|
|
if(cooldown > world.time)
|
|
to_chat(user, "<font color='red'>The device is still recharging!</font>")
|
|
return
|
|
|
|
if(iscyborg(user))
|
|
var/mob/living/silicon/robot/R = user
|
|
if(!R.cell || R.cell.charge < 1200)
|
|
to_chat(user, span_warning("You don't have enough charge to do this!"))
|
|
return
|
|
R.cell.charge -= 1000
|
|
if(R.emagged)
|
|
safety = FALSE
|
|
|
|
if(safety == TRUE)
|
|
user.visible_message("<font color='red' size='2'>[user] blares out a near-deafening siren from its speakers!</font>", \
|
|
span_userdanger("The siren pierces your hearing and confuses you!"), \
|
|
span_danger("The siren pierces your hearing!"))
|
|
for(var/mob/living/carbon/M in get_hearers_in_view(9, user))
|
|
if(M.get_ear_protection() == FALSE)
|
|
M.add_confusion(6)
|
|
audible_message("<font color='red' size='7'>HUMAN HARM</font>")
|
|
playsound(get_turf(src), 'sound/ai/harmalarm.ogg', 70, 3)
|
|
cooldown = world.time + 200
|
|
user.log_message("used a Cyborg Harm Alarm in [AREACOORD(user)]", LOG_ATTACK)
|
|
if(iscyborg(user))
|
|
var/mob/living/silicon/robot/R = user
|
|
to_chat(R.connected_ai, "<br>[span_notice("NOTICE - Peacekeeping 'HARM ALARM' used by: [user]")]<br>")
|
|
|
|
return
|
|
|
|
if(safety == FALSE)
|
|
user.audible_message("<font color='red' size='7'>BZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZT</font>")
|
|
for(var/mob/living/carbon/C in get_hearers_in_view(9, user))
|
|
var/bang_effect = C.soundbang_act(2, 0, 0, 5)
|
|
switch(bang_effect)
|
|
if(1)
|
|
C.add_confusion(5)
|
|
C.stuttering += 10
|
|
C.Jitter(10)
|
|
if(2)
|
|
C.Paralyze(40)
|
|
C.add_confusion(10)
|
|
C.stuttering += 15
|
|
C.Jitter(25)
|
|
playsound(get_turf(src), 'sound/machines/warning-buzzer.ogg', 130, 3)
|
|
cooldown = world.time + 600
|
|
user.log_message("used an emagged Cyborg Harm Alarm in [AREACOORD(user)]", LOG_ATTACK)
|
|
|
|
#define DISPENSE_LOLLIPOP_MODE 1
|
|
#define THROW_LOLLIPOP_MODE 2
|
|
#define THROW_GUMBALL_MODE 3
|
|
#define DISPENSE_ICECREAM_MODE 4
|
|
|
|
/obj/item/borg/lollipop
|
|
name = "treat fabricator"
|
|
desc = "Reward humans with various treats. Toggle in-module to switch between dispensing and high velocity ejection modes."
|
|
icon_state = "lollipop"
|
|
var/candy = 5
|
|
var/candymax = 5
|
|
var/charge_delay = 10 SECONDS
|
|
var/charging = FALSE
|
|
var/mode = DISPENSE_LOLLIPOP_MODE
|
|
|
|
var/firedelay = 0
|
|
var/hitspeed = 2
|
|
|
|
/obj/item/borg/lollipop/clown
|
|
|
|
/obj/item/borg/lollipop/equipped()
|
|
. = ..()
|
|
check_amount()
|
|
|
|
/obj/item/borg/lollipop/dropped()
|
|
. = ..()
|
|
check_amount()
|
|
|
|
/obj/item/borg/lollipop/proc/check_amount() //Doesn't even use processing ticks.
|
|
if(!charging && candy < candymax)
|
|
addtimer(CALLBACK(src, .proc/charge_lollipops), charge_delay)
|
|
charging = TRUE
|
|
|
|
/obj/item/borg/lollipop/proc/charge_lollipops()
|
|
candy++
|
|
charging = FALSE
|
|
check_amount()
|
|
|
|
/obj/item/borg/lollipop/proc/dispense(atom/A, mob/user)
|
|
if(candy <= 0)
|
|
to_chat(user, span_warning("No treats left in storage!"))
|
|
return FALSE
|
|
var/turf/T = get_turf(A)
|
|
if(!T || !istype(T) || !isopenturf(T))
|
|
return FALSE
|
|
if(isobj(A))
|
|
var/obj/O = A
|
|
if(O.density)
|
|
return FALSE
|
|
|
|
var/obj/item/food_item
|
|
switch(mode)
|
|
if(DISPENSE_LOLLIPOP_MODE)
|
|
food_item = new /obj/item/food/lollipop(T)
|
|
if(DISPENSE_ICECREAM_MODE)
|
|
food_item = new /obj/item/food/icecream(T, list(ICE_CREAM_VANILLA))
|
|
food_item.desc = "Eat the ice cream."
|
|
|
|
var/into_hands = FALSE
|
|
if(ismob(A))
|
|
var/mob/M = A
|
|
into_hands = M.put_in_hands(food_item)
|
|
|
|
candy--
|
|
check_amount()
|
|
|
|
if(into_hands)
|
|
user.visible_message(span_notice("[user] dispenses a treat into the hands of [A]."), span_notice("You dispense a treat into the hands of [A]."), span_hear("You hear a click."))
|
|
else
|
|
user.visible_message(span_notice("[user] dispenses a treat."), span_notice("You dispense a treat."), span_hear("You hear a click."))
|
|
|
|
playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE)
|
|
return TRUE
|
|
|
|
/obj/item/borg/lollipop/proc/shootL(atom/target, mob/living/user, params)
|
|
if(candy <= 0)
|
|
to_chat(user, span_warning("Not enough lollipops left!"))
|
|
return FALSE
|
|
candy--
|
|
|
|
var/obj/item/ammo_casing/caseless/lollipop/A
|
|
var/mob/living/silicon/robot/R = user
|
|
if(istype(R) && R.emagged)
|
|
A = new /obj/item/ammo_casing/caseless/lollipop/harmful(src)
|
|
else
|
|
A = new /obj/item/ammo_casing/caseless/lollipop(src)
|
|
|
|
playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE)
|
|
A.fire_casing(target, user, params, 0, 0, null, 0, src)
|
|
user.visible_message(span_warning("[user] blasts a flying lollipop at [target]!"))
|
|
check_amount()
|
|
|
|
/obj/item/borg/lollipop/proc/shootG(atom/target, mob/living/user, params) //Most certainly a good idea.
|
|
if(candy <= 0)
|
|
to_chat(user, span_warning("Not enough gumballs left!"))
|
|
return FALSE
|
|
candy--
|
|
var/obj/item/ammo_casing/caseless/gumball/A
|
|
var/mob/living/silicon/robot/R = user
|
|
if(istype(R) && R.emagged)
|
|
A = new /obj/item/ammo_casing/caseless/gumball/harmful(src)
|
|
else
|
|
A = new /obj/item/ammo_casing/caseless/gumball(src)
|
|
|
|
A.loaded_projectile.color = rgb(rand(0, 255), rand(0, 255), rand(0, 255))
|
|
playsound(src.loc, 'sound/weapons/bulletflyby3.ogg', 50, TRUE)
|
|
A.fire_casing(target, user, params, 0, 0, null, 0, src)
|
|
user.visible_message(span_warning("[user] shoots a high-velocity gumball at [target]!"))
|
|
check_amount()
|
|
|
|
/obj/item/borg/lollipop/afterattack(atom/target, mob/living/user, proximity, click_params)
|
|
. = ..()
|
|
check_amount()
|
|
if(iscyborg(user))
|
|
var/mob/living/silicon/robot/R = user
|
|
if(!R.cell.use(12))
|
|
to_chat(user, span_warning("Not enough power."))
|
|
return FALSE
|
|
switch(mode)
|
|
if(DISPENSE_LOLLIPOP_MODE, DISPENSE_ICECREAM_MODE)
|
|
if(!proximity)
|
|
return FALSE
|
|
dispense(target, user)
|
|
if(THROW_LOLLIPOP_MODE)
|
|
shootL(target, user, click_params)
|
|
if(THROW_GUMBALL_MODE)
|
|
shootG(target, user, click_params)
|
|
|
|
/obj/item/borg/lollipop/attack_self(mob/living/user)
|
|
switch(mode)
|
|
if(DISPENSE_LOLLIPOP_MODE)
|
|
mode = THROW_LOLLIPOP_MODE
|
|
to_chat(user, span_notice("Module is now throwing lollipops."))
|
|
if(THROW_LOLLIPOP_MODE)
|
|
mode = THROW_GUMBALL_MODE
|
|
to_chat(user, span_notice("Module is now blasting gumballs."))
|
|
if(THROW_GUMBALL_MODE)
|
|
mode = DISPENSE_ICECREAM_MODE
|
|
to_chat(user, span_notice("Module is now dispensing ice cream."))
|
|
if(DISPENSE_ICECREAM_MODE)
|
|
mode = DISPENSE_LOLLIPOP_MODE
|
|
to_chat(user, span_notice("Module is now dispensing lollipops."))
|
|
..()
|
|
|
|
#undef DISPENSE_LOLLIPOP_MODE
|
|
#undef THROW_LOLLIPOP_MODE
|
|
#undef THROW_GUMBALL_MODE
|
|
#undef DISPENSE_ICECREAM_MODE
|
|
|
|
/obj/item/ammo_casing/caseless/gumball
|
|
name = "Gumball"
|
|
desc = "Why are you seeing this?!"
|
|
projectile_type = /obj/projectile/bullet/reusable/gumball
|
|
click_cooldown_override = 2
|
|
|
|
/obj/item/ammo_casing/caseless/gumball/harmful
|
|
projectile_type = /obj/projectile/bullet/reusable/gumball/harmful
|
|
|
|
/obj/projectile/bullet/reusable/gumball
|
|
name = "gumball"
|
|
desc = "Oh noes! A fast-moving gumball!"
|
|
icon_state = "gumball"
|
|
ammo_type = /obj/item/food/gumball
|
|
nodamage = TRUE
|
|
damage = 0
|
|
speed = 0.5
|
|
|
|
/obj/projectile/bullet/reusable/gumball/harmful
|
|
nodamage = FALSE
|
|
damage = 10 //mediborgs get 5 shots before needing to reload at a rate of 1 shot/10 seconds, so they can do 50 damage from range max before needing to close the distance or retreat
|
|
|
|
/obj/projectile/bullet/reusable/gumball/handle_drop()
|
|
if(!dropped)
|
|
var/turf/T = get_turf(src)
|
|
var/obj/item/food/gumball/S = new ammo_type(T)
|
|
S.color = color
|
|
dropped = TRUE
|
|
|
|
/obj/item/ammo_casing/caseless/lollipop //NEEDS RANDOMIZED COLOR LOGIC.
|
|
name = "Lollipop"
|
|
desc = "Why are you seeing this?!"
|
|
projectile_type = /obj/projectile/bullet/reusable/lollipop
|
|
click_cooldown_override = 2
|
|
|
|
// rejected name: DumDum lollipop (get it, cause it embeds?)
|
|
/obj/item/ammo_casing/caseless/lollipop/harmful
|
|
projectile_type = /obj/projectile/bullet/reusable/lollipop/harmful
|
|
|
|
/obj/projectile/bullet/reusable/lollipop
|
|
name = "lollipop"
|
|
desc = "Oh noes! A fast-moving lollipop!"
|
|
icon_state = "lollipop_1"
|
|
ammo_type = /obj/item/food/lollipop/cyborg
|
|
nodamage = TRUE
|
|
damage = 0
|
|
speed = 0.5
|
|
var/color2 = rgb(0, 0, 0)
|
|
|
|
/obj/projectile/bullet/reusable/lollipop/harmful
|
|
embedding = list(embed_chance=35, fall_chance=2, jostle_chance=0, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.5, pain_mult=3, rip_time=10)
|
|
damage = 10
|
|
nodamage = FALSE
|
|
embed_falloff_tile = 0
|
|
|
|
/obj/projectile/bullet/reusable/lollipop/Initialize(mapload)
|
|
. = ..()
|
|
var/obj/item/food/lollipop/S = new ammo_type(src)
|
|
color2 = S.headcolor
|
|
var/mutable_appearance/head = mutable_appearance('icons/obj/guns/projectiles.dmi', "lollipop_2")
|
|
head.color = color2
|
|
add_overlay(head)
|
|
|
|
/obj/projectile/bullet/reusable/lollipop/handle_drop()
|
|
if(!dropped)
|
|
var/turf/T = get_turf(src)
|
|
var/obj/item/food/lollipop/S = new ammo_type(T)
|
|
S.change_head_color(color2)
|
|
dropped = TRUE
|
|
|
|
/obj/item/cautery/prt //it's a subtype of cauteries so that it inherits the cautery sprites and behavior and stuff, because I'm too lazy to make sprites for this thing
|
|
name = "plating repair tool"
|
|
desc = "A tiny heating device that's powered by a cyborg's excess heat. Its intended purpose is to repair burnt or damaged hull platings, but it can also be used as a crude lighter or cautery."
|
|
toolspeed = 1.5 //it's not designed to be used as a cautery (although it's close enough to one to be considered to be a proper cautery instead of just a hot object for the purposes of surgery)
|
|
heat = 3800 //this thing is intended for metal-shaping, so it's the same temperature as a lit welder
|
|
resistance_flags = FIRE_PROOF //if it's channeling a cyborg's excess heat, it's probably fireproof
|
|
force = 5
|
|
damtype = BURN
|
|
usesound = list('sound/items/welder.ogg', 'sound/items/welder2.ogg') //the usesounds of a lit welder
|
|
hitsound = 'sound/items/welder.ogg' //the hitsound of a lit welder
|
|
|
|
|
|
#define PKBORG_DAMPEN_CYCLE_DELAY 20
|
|
|
|
//Peacekeeper Cyborg Projectile Dampenening Field
|
|
/obj/item/borg/projectile_dampen
|
|
name = "\improper Hyperkinetic Dampening projector"
|
|
desc = "A device that projects a dampening field that weakens kinetic energy above a certain threshold. <span class='boldnotice'>Projects a field that drains power per second while active, that will weaken and slow damaging projectiles inside its field.</span> Still being a prototype, it tends to induce a charge on ungrounded metallic surfaces."
|
|
icon = 'icons/obj/device.dmi'
|
|
icon_state = "shield"
|
|
var/maxenergy = 1500
|
|
var/energy = 1500
|
|
/// Recharging rate in energy per second
|
|
var/energy_recharge = 37.5
|
|
var/energy_recharge_cyborg_drain_coefficient = 0.4
|
|
var/cyborg_cell_critical_percentage = 0.05
|
|
var/mob/living/silicon/robot/host = null
|
|
var/datum/proximity_monitor/advanced/dampening_field
|
|
var/projectile_damage_coefficient = 0.5
|
|
/// Energy cost per tracked projectile damage amount per second
|
|
var/projectile_damage_tick_ecost_coefficient = 10
|
|
var/projectile_speed_coefficient = 1.5 //Higher the coefficient slower the projectile.
|
|
/// Energy cost per tracked projectile per second
|
|
var/projectile_tick_speed_ecost = 75
|
|
var/list/obj/projectile/tracked
|
|
var/image/projectile_effect
|
|
var/field_radius = 3
|
|
var/active = FALSE
|
|
var/cycle_delay = 0
|
|
|
|
/obj/item/borg/projectile_dampen/debug
|
|
maxenergy = 50000
|
|
energy = 50000
|
|
energy_recharge = 5000
|
|
|
|
/obj/item/borg/projectile_dampen/Initialize(mapload)
|
|
. = ..()
|
|
projectile_effect = image('icons/effects/fields.dmi', "projectile_dampen_effect")
|
|
tracked = list()
|
|
icon_state = "shield0"
|
|
START_PROCESSING(SSfastprocess, src)
|
|
host = loc
|
|
RegisterSignal(host, COMSIG_LIVING_DEATH, .proc/on_death)
|
|
|
|
/obj/item/borg/projectile_dampen/proc/on_death(datum/source, gibbed)
|
|
SIGNAL_HANDLER
|
|
|
|
deactivate_field()
|
|
|
|
/obj/item/borg/projectile_dampen/Destroy()
|
|
STOP_PROCESSING(SSfastprocess, src)
|
|
return ..()
|
|
|
|
/obj/item/borg/projectile_dampen/attack_self(mob/user)
|
|
if(cycle_delay > world.time)
|
|
to_chat(user, span_boldwarning("[src] is still recycling its projectors!"))
|
|
return
|
|
cycle_delay = world.time + PKBORG_DAMPEN_CYCLE_DELAY
|
|
if(!active)
|
|
if(!user.has_buckled_mobs())
|
|
activate_field()
|
|
else
|
|
to_chat(user, span_warning("[src]'s safety cutoff prevents you from activating it due to living beings being ontop of you!"))
|
|
else
|
|
deactivate_field()
|
|
update_appearance()
|
|
to_chat(user, span_boldnotice("You [active? "activate":"deactivate"] [src]."))
|
|
|
|
/obj/item/borg/projectile_dampen/update_icon_state()
|
|
icon_state = "[initial(icon_state)][active]"
|
|
return ..()
|
|
|
|
/obj/item/borg/projectile_dampen/proc/activate_field()
|
|
if(istype(dampening_field))
|
|
QDEL_NULL(dampening_field)
|
|
dampening_field = make_field(/datum/proximity_monitor/advanced/peaceborg_dampener, list("current_range" = field_radius, "host" = src, "projector" = src))
|
|
var/mob/living/silicon/robot/owner = get_host()
|
|
if(owner)
|
|
owner.model.allow_riding = FALSE
|
|
active = TRUE
|
|
|
|
/obj/item/borg/projectile_dampen/proc/deactivate_field()
|
|
QDEL_NULL(dampening_field)
|
|
visible_message(span_warning("\The [src] shuts off!"))
|
|
for(var/P in tracked)
|
|
restore_projectile(P)
|
|
active = FALSE
|
|
|
|
var/mob/living/silicon/robot/owner = get_host()
|
|
if(owner)
|
|
owner.model.allow_riding = TRUE
|
|
|
|
/obj/item/borg/projectile_dampen/proc/get_host()
|
|
if(istype(host))
|
|
return host
|
|
else
|
|
if(iscyborg(host.loc))
|
|
return host.loc
|
|
return null
|
|
|
|
/obj/item/borg/projectile_dampen/dropped()
|
|
. = ..()
|
|
host = loc
|
|
|
|
/obj/item/borg/projectile_dampen/equipped()
|
|
. = ..()
|
|
host = loc
|
|
|
|
/obj/item/borg/projectile_dampen/cyborg_unequip(mob/user)
|
|
deactivate_field()
|
|
. = ..()
|
|
|
|
/obj/item/borg/projectile_dampen/process(delta_time)
|
|
process_recharge(delta_time)
|
|
process_usage(delta_time)
|
|
update_location()
|
|
|
|
/obj/item/borg/projectile_dampen/proc/update_location()
|
|
if(dampening_field)
|
|
dampening_field.HandleMove()
|
|
|
|
/obj/item/borg/projectile_dampen/proc/process_usage(delta_time)
|
|
var/usage = 0
|
|
for(var/I in tracked)
|
|
var/obj/projectile/P = I
|
|
if(!P.stun && P.nodamage) //No damage
|
|
continue
|
|
usage += projectile_tick_speed_ecost * delta_time
|
|
usage += tracked[I] * projectile_damage_tick_ecost_coefficient * delta_time
|
|
energy = clamp(energy - usage, 0, maxenergy)
|
|
if(energy <= 0)
|
|
deactivate_field()
|
|
visible_message(span_warning("[src] blinks \"ENERGY DEPLETED\"."))
|
|
|
|
/obj/item/borg/projectile_dampen/proc/process_recharge(delta_time)
|
|
if(!istype(host))
|
|
if(iscyborg(host.loc))
|
|
host = host.loc
|
|
else
|
|
energy = clamp(energy + energy_recharge * delta_time, 0, maxenergy)
|
|
return
|
|
if(host.cell && (host.cell.charge >= (host.cell.maxcharge * cyborg_cell_critical_percentage)) && (energy < maxenergy))
|
|
host.cell.use(energy_recharge * delta_time * energy_recharge_cyborg_drain_coefficient)
|
|
energy += energy_recharge * delta_time
|
|
|
|
/obj/item/borg/projectile_dampen/proc/dampen_projectile(obj/projectile/P, track_projectile = TRUE)
|
|
if(tracked[P])
|
|
return
|
|
if(track_projectile)
|
|
tracked[P] = P.damage
|
|
P.damage *= projectile_damage_coefficient
|
|
P.speed *= projectile_speed_coefficient
|
|
P.add_overlay(projectile_effect)
|
|
|
|
/obj/item/borg/projectile_dampen/proc/restore_projectile(obj/projectile/P)
|
|
tracked -= P
|
|
P.damage *= (1/projectile_damage_coefficient)
|
|
P.speed *= (1/projectile_speed_coefficient)
|
|
P.cut_overlay(projectile_effect)
|
|
|
|
/**********************************************************************
|
|
HUD/SIGHT things
|
|
***********************************************************************/
|
|
/obj/item/borg/sight
|
|
var/sight_mode = null
|
|
|
|
|
|
/obj/item/borg/sight/xray
|
|
name = "\proper X-ray vision"
|
|
icon = 'icons/obj/decals.dmi'
|
|
icon_state = "securearea"
|
|
sight_mode = BORGXRAY
|
|
|
|
/obj/item/borg/sight/thermal
|
|
name = "\proper thermal vision"
|
|
sight_mode = BORGTHERM
|
|
icon_state = "thermal"
|
|
|
|
|
|
/obj/item/borg/sight/meson
|
|
name = "\proper meson vision"
|
|
sight_mode = BORGMESON
|
|
icon_state = "meson"
|
|
|
|
/obj/item/borg/sight/material
|
|
name = "\proper material vision"
|
|
sight_mode = BORGMATERIAL
|
|
icon_state = "material"
|
|
|
|
/obj/item/borg/sight/hud
|
|
name = "hud"
|
|
var/obj/item/clothing/glasses/hud/hud = null
|
|
|
|
|
|
/obj/item/borg/sight/hud/med
|
|
name = "medical hud"
|
|
icon_state = "healthhud"
|
|
|
|
/obj/item/borg/sight/hud/med/Initialize(mapload)
|
|
. = ..()
|
|
hud = new /obj/item/clothing/glasses/hud/health(src)
|
|
|
|
|
|
/obj/item/borg/sight/hud/sec
|
|
name = "security hud"
|
|
icon_state = "securityhud"
|
|
|
|
/obj/item/borg/sight/hud/sec/Initialize(mapload)
|
|
. = ..()
|
|
hud = new /obj/item/clothing/glasses/hud/security(src)
|
|
|
|
|
|
/**********************************************************************
|
|
Borg apparatus
|
|
***********************************************************************/
|
|
//These are tools that can hold only specific items. For example, the mediborg gets one that can only hold beakers and bottles.
|
|
|
|
/obj/item/borg/apparatus/
|
|
name = "unknown storage apparatus"
|
|
desc = "This device seems nonfunctional."
|
|
icon = 'icons/mob/robot_items.dmi'
|
|
icon_state = "hugmodule"
|
|
var/obj/item/stored
|
|
var/list/storable = list()
|
|
|
|
/obj/item/borg/apparatus/Initialize(mapload)
|
|
. = ..()
|
|
RegisterSignal(loc.loc, COMSIG_BORG_SAFE_DECONSTRUCT, .proc/safedecon)
|
|
|
|
/obj/item/borg/apparatus/Destroy()
|
|
QDEL_NULL(stored)
|
|
. = ..()
|
|
|
|
///If we're safely deconstructed, we put the item neatly onto the ground, rather than deleting it.
|
|
/obj/item/borg/apparatus/proc/safedecon()
|
|
SIGNAL_HANDLER
|
|
|
|
if(stored)
|
|
stored.forceMove(get_turf(src))
|
|
stored = null
|
|
|
|
/obj/item/borg/apparatus/Exited(atom/movable/gone, direction)
|
|
if(gone == stored) //sanity check
|
|
UnregisterSignal(stored, COMSIG_ATOM_UPDATED_ICON)
|
|
stored = null
|
|
update_appearance()
|
|
return ..()
|
|
|
|
///A right-click verb, for those not using hotkey mode.
|
|
/obj/item/borg/apparatus/verb/verb_dropHeld()
|
|
set category = "Object"
|
|
set name = "Drop"
|
|
|
|
if(usr != loc || !stored)
|
|
return
|
|
stored.forceMove(get_turf(usr))
|
|
return
|
|
|
|
/**
|
|
* Attack_self will pass for the stored item.
|
|
*/
|
|
/obj/item/borg/apparatus/attack_self(mob/living/silicon/robot/user)
|
|
if(!stored || !issilicon(user))
|
|
return ..()
|
|
stored.attack_self(user)
|
|
|
|
//Alt click drops the stored item.
|
|
/obj/item/borg/apparatus/AltClick(mob/living/silicon/robot/user)
|
|
if(!stored || !issilicon(user))
|
|
return ..()
|
|
stored.forceMove(user.drop_location())
|
|
|
|
/obj/item/borg/apparatus/pre_attack(atom/A, mob/living/user, params)
|
|
if(!stored)
|
|
var/itemcheck = FALSE
|
|
for(var/i in storable)
|
|
if(istype(A, i))
|
|
itemcheck = TRUE
|
|
break
|
|
if(itemcheck)
|
|
var/obj/item/O = A
|
|
O.forceMove(src)
|
|
stored = O
|
|
RegisterSignal(stored, COMSIG_ATOM_UPDATED_ICON, .proc/on_stored_updated_icon)
|
|
update_appearance()
|
|
return
|
|
else
|
|
stored.melee_attack_chain(user, A, params)
|
|
return
|
|
. = ..()
|
|
|
|
/**
|
|
* Updates the appearance of the apparatus when the stored object's icon gets updated.
|
|
*
|
|
* Returns NONE as we have not done anything to the stored object itself,
|
|
* which is where this signal that this handler intercepts is sent from.
|
|
*/
|
|
/obj/item/borg/apparatus/proc/on_stored_updated_icon(datum/source, updates)
|
|
SIGNAL_HANDLER
|
|
update_appearance()
|
|
return NONE
|
|
|
|
/obj/item/borg/apparatus/attackby(obj/item/W, mob/user, params)
|
|
if(stored)
|
|
W.melee_attack_chain(user, stored, params)
|
|
return
|
|
. = ..()
|
|
|
|
/////////////////
|
|
//beaker holder//
|
|
/////////////////
|
|
|
|
/obj/item/borg/apparatus/beaker
|
|
name = "beaker storage apparatus"
|
|
desc = "A special apparatus for carrying beakers without spilling the contents."
|
|
icon_state = "borg_beaker_apparatus"
|
|
storable = list(/obj/item/reagent_containers/glass/beaker,
|
|
/obj/item/reagent_containers/glass/bottle)
|
|
|
|
/obj/item/borg/apparatus/beaker/Initialize(mapload)
|
|
. = ..()
|
|
stored = new /obj/item/reagent_containers/glass/beaker/large(src)
|
|
RegisterSignal(stored, COMSIG_ATOM_UPDATED_ICON, .proc/on_stored_updated_icon)
|
|
update_appearance()
|
|
|
|
/obj/item/borg/apparatus/beaker/Destroy()
|
|
if(stored)
|
|
var/obj/item/reagent_containers/C = stored
|
|
C.SplashReagents(get_turf(src))
|
|
QDEL_NULL(stored)
|
|
. = ..()
|
|
|
|
/obj/item/borg/apparatus/beaker/examine()
|
|
. = ..()
|
|
if(stored)
|
|
var/obj/item/reagent_containers/C = stored
|
|
. += "The apparatus currently has [C] secured, which contains:"
|
|
if(length(C.reagents.reagent_list))
|
|
for(var/datum/reagent/R in C.reagents.reagent_list)
|
|
. += "[R.volume] units of [R.name]"
|
|
else
|
|
. += "Nothing."
|
|
|
|
. += span_notice(" <i>Right-clicking</i> will splash the beaker on the ground.")
|
|
. += span_notice(" <i>Alt-click</i> will drop the currently stored beaker. ")
|
|
|
|
/obj/item/borg/apparatus/beaker/update_overlays()
|
|
. = ..()
|
|
var/mutable_appearance/arm = mutable_appearance(icon = icon, icon_state = "borg_beaker_apparatus_arm")
|
|
if(stored)
|
|
COMPILE_OVERLAYS(stored)
|
|
stored.pixel_x = 0
|
|
stored.pixel_y = 0
|
|
var/mutable_appearance/stored_copy = new /mutable_appearance(stored)
|
|
if(istype(stored, /obj/item/reagent_containers/glass/beaker))
|
|
arm.pixel_y = arm.pixel_y - 3
|
|
stored_copy.layer = FLOAT_LAYER
|
|
stored_copy.plane = FLOAT_PLANE
|
|
. += stored_copy
|
|
else
|
|
arm.pixel_y = arm.pixel_y - 5
|
|
. += arm
|
|
|
|
/// Secondary attack spills the content of the beaker.
|
|
/obj/item/borg/apparatus/beaker/pre_attack_secondary(atom/target, mob/living/silicon/robot/user)
|
|
var/obj/item/reagent_containers/stored_beaker = stored
|
|
stored_beaker.SplashReagents(drop_location(user))
|
|
loc.visible_message(span_notice("[user] spills the contents of [stored_beaker] all over the ground."))
|
|
. = ..()
|
|
|
|
/obj/item/borg/apparatus/beaker/extra
|
|
name = "secondary beaker storage apparatus"
|
|
desc = "A supplementary beaker storage apparatus."
|
|
|
|
/obj/item/borg/apparatus/beaker/service
|
|
name = "beverage storage apparatus"
|
|
desc = "A special apparatus for carrying drinks without spilling the contents. Will resynthesize any drinks you pour out!"
|
|
icon_state = "borg_beaker_apparatus"
|
|
storable = list(/obj/item/reagent_containers/food/drinks,
|
|
/obj/item/reagent_containers/food/condiment)
|
|
|
|
/obj/item/borg/apparatus/beaker/service/Initialize(mapload)
|
|
. = ..()
|
|
stored = new /obj/item/reagent_containers/food/drinks/drinkingglass(src)
|
|
RegisterSignal(stored, COMSIG_ATOM_UPDATED_ICON, .proc/on_stored_updated_icon)
|
|
update_appearance()
|
|
|
|
/////////////////////
|
|
//organ storage bag//
|
|
/////////////////////
|
|
|
|
/obj/item/borg/apparatus/organ_storage //allows medical cyborgs to manipulate organs without hands
|
|
name = "organ storage bag"
|
|
desc = "A container for holding body parts."
|
|
icon = 'icons/obj/storage.dmi'
|
|
icon_state = "evidenceobj"
|
|
item_flags = SURGICAL_TOOL
|
|
storable = list(/obj/item/organ,
|
|
/obj/item/bodypart)
|
|
|
|
/obj/item/borg/apparatus/organ_storage/examine()
|
|
. = ..()
|
|
. += "The organ bag currently contains:"
|
|
if(stored)
|
|
var/obj/item/organ = stored
|
|
. += organ.name
|
|
else
|
|
. += "Nothing."
|
|
. += span_notice(" <i>Alt-click</i> will drop the currently stored organ. ")
|
|
|
|
/obj/item/borg/apparatus/organ_storage/update_overlays()
|
|
. = ..()
|
|
icon_state = null // hides the original icon (otherwise it's drawn underneath)
|
|
var/mutable_appearance/bag
|
|
if(stored)
|
|
COMPILE_OVERLAYS(stored)
|
|
var/mutable_appearance/stored_organ = new /mutable_appearance(stored)
|
|
stored_organ.layer = FLOAT_LAYER
|
|
stored_organ.plane = FLOAT_PLANE
|
|
stored_organ.pixel_x = 0
|
|
stored_organ.pixel_y = 0
|
|
. += stored_organ
|
|
bag = mutable_appearance(icon, icon_state = "evidence") // full bag
|
|
else
|
|
bag = mutable_appearance(icon, icon_state = "evidenceobj") // empty bag
|
|
. += bag
|
|
|
|
/obj/item/borg/apparatus/organ_storage/AltClick(mob/living/silicon/robot/user)
|
|
. = ..()
|
|
if(stored)
|
|
var/obj/item/organ = stored
|
|
user.visible_message(span_notice("[user] dumps [organ] from [src]."), span_notice("You dump [organ] from [src]."))
|
|
cut_overlays()
|
|
organ.forceMove(get_turf(src))
|
|
else
|
|
to_chat(user, span_notice("[src] is empty."))
|
|
return
|
|
|
|
////////////////////////////
|
|
//engi circuitboard holder//
|
|
////////////////////////////
|
|
|
|
/obj/item/borg/apparatus/circuit
|
|
name = "circuit manipulation apparatus"
|
|
desc = "A special apparatus for carrying and manipulating circuit boards."
|
|
icon_state = "borg_hardware_apparatus"
|
|
storable = list(/obj/item/circuitboard,
|
|
/obj/item/electronics)
|
|
|
|
/obj/item/borg/apparatus/circuit/Initialize(mapload)
|
|
. = ..()
|
|
update_appearance()
|
|
|
|
/obj/item/borg/apparatus/circuit/update_overlays()
|
|
. = ..()
|
|
var/mutable_appearance/arm = mutable_appearance(icon, "borg_hardware_apparatus_arm1")
|
|
if(stored)
|
|
COMPILE_OVERLAYS(stored)
|
|
stored.pixel_x = -3
|
|
stored.pixel_y = 0
|
|
if(!istype(stored, /obj/item/circuitboard))
|
|
arm.icon_state = "borg_hardware_apparatus_arm2"
|
|
var/mutable_appearance/stored_copy = new /mutable_appearance(stored)
|
|
stored_copy.layer = FLOAT_LAYER
|
|
stored_copy.plane = FLOAT_PLANE
|
|
. += stored_copy
|
|
. += arm
|
|
|
|
/obj/item/borg/apparatus/circuit/examine()
|
|
. = ..()
|
|
if(stored)
|
|
. += "The apparatus currently has [stored] secured."
|
|
. += span_notice(" <i>Alt-click</i> will drop the currently stored circuit. ")
|
|
|
|
/obj/item/borg/apparatus/circuit/pre_attack(atom/A, mob/living/user, params)
|
|
. = ..()
|
|
if(istype(A, /obj/item/ai_module) && !stored) //If an admin wants a borg to upload laws, who am I to stop them? Otherwise, we can hint that it fails
|
|
to_chat(user, span_warning("This circuit board doesn't seem to have standard robot apparatus pin holes. You're unable to pick it up."))
|