mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-07 15:32:47 +00:00
This hasn't been touched in a number of years, and has drifted over time out of sync with battery values, this aligns it to the stunbaton cost Engineering borgs would do well to avoid spamming this at risk of ending up out of power and dead in the water. Also incentivises malfunctioning AI's to stay under the radar until they have better batteries in their borgs
1042 lines
34 KiB
Plaintext
1042 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 = 1000
|
|
|
|
/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.head_color
|
|
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/peaceborg_dampener/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)
|
|
var/mob/living/silicon/robot/owner = get_host()
|
|
dampening_field = new(owner, field_radius, TRUE, src)
|
|
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)
|
|
|
|
/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."))
|