Citadel's folder's end (#5828)
* ERP, miscreants, clothing * github pls * guns, dogborg, areas, vendor * finishes moving around the last of the stuffs * cleaned up shit. italics on subtle messages vore code to modular_citadel too * updates codeowners and recompiles tgui because it's a healthy thing to do * reee, I had that spawner set byond * cleans up a bad pipe does the thing I've been meaning to do for a while now as well. * bumps up xenobio console requirements inb4 reee * snowflake commenting
@@ -1 +0,0 @@
|
||||
GLOBAL_VAR_INIT(enable_examine_tips, TRUE)
|
||||
@@ -1,8 +0,0 @@
|
||||
/mob/camera/god/UnarmedAttack(atom/A)
|
||||
A.attack_god(src)
|
||||
|
||||
/mob/camera/god/RangedAttack(atom/A)
|
||||
A.attack_god(src)
|
||||
|
||||
/atom/proc/attack_god(mob/user)
|
||||
return
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
/datum/hud/brain/show_hud(version = 0)
|
||||
if(!ismob(mymob))
|
||||
return 0
|
||||
if(!mymob.client)
|
||||
return 0
|
||||
mymob.client.screen = list()
|
||||
mymob.client.screen += mymob.client.void
|
||||
|
||||
/mob/living/brain/create_mob_hud()
|
||||
if(client && !hud_used)
|
||||
hud_used = new /datum/hud/brain(src)
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/area/maintenance/bar
|
||||
name = "Maintenance Bar"
|
||||
icon = 'code/citadel/icons/areas.dmi'
|
||||
icon_state = "maintbar"
|
||||
|
||||
/area/maintenance/bar/cafe
|
||||
name = "Abandoned Cafe"
|
||||
|
||||
/area/crew_quarters/theatre/clown
|
||||
name = "Clown's Office"
|
||||
|
||||
/area/crew_quarters/theatre/mime
|
||||
name = "Mime's Office"
|
||||
|
||||
/area/crew_quarters/cryopod
|
||||
name = "Cryogenics"
|
||||
icon = 'code/citadel/icons/areas.dmi'
|
||||
icon_state = "cryo"
|
||||
@@ -1,544 +0,0 @@
|
||||
//Mob vars
|
||||
/mob/living
|
||||
var/arousalloss = 0 //How aroused the mob is.
|
||||
var/min_arousal = 0 //The lowest this mobs arousal will get. default = 0
|
||||
var/max_arousal = 100 //The highest this mobs arousal will get. default = 100
|
||||
var/arousal_rate = 1 //The base rate that arousal will increase in this mob.
|
||||
var/arousal_loss_rate = 1 //How easily arousal can be relieved for this mob.
|
||||
var/canbearoused = FALSE //Mob-level disabler for arousal. Starts off and can be enabled as features are added for different mob types.
|
||||
var/mb_cd_length = 100 //5 second cooldown for masturbating because fuck spam.
|
||||
var/mb_cd_timer = 0 //The timer itself
|
||||
|
||||
/mob/living/carbon/human
|
||||
canbearoused = TRUE
|
||||
|
||||
var/saved_underwear = ""//saves their underwear so it can be toggled later
|
||||
var/saved_undershirt = ""
|
||||
|
||||
/mob/living/carbon/human/New()
|
||||
..()
|
||||
saved_underwear = underwear
|
||||
saved_undershirt = undershirt
|
||||
|
||||
//Species vars
|
||||
/datum/species
|
||||
var/arousal_gain_rate = 1 //Rate at which this species becomes aroused
|
||||
var/arousal_lose_rate = 1 //Multiplier for how easily arousal can be relieved
|
||||
var/list/cum_fluids = list("semen")
|
||||
var/list/milk_fluids = list("milk")
|
||||
var/list/femcum_fluids = list("femcum")
|
||||
|
||||
//Mob procs
|
||||
/mob/living/proc/handle_arousal()
|
||||
|
||||
|
||||
/mob/living/carbon/handle_arousal()
|
||||
if(canbearoused && dna)
|
||||
var/datum/species/S
|
||||
S = dna.species
|
||||
if(S && SSmobs.times_fired%36==2 && getArousalLoss() < max_arousal)//Totally stolen from breathing code. Do this every 36 ticks.
|
||||
adjustArousalLoss(arousal_rate * S.arousal_gain_rate)
|
||||
if(dna.features["exhibitionist"])
|
||||
var/amt_nude = 0
|
||||
if(is_chest_exposed() && (gender == FEMALE || getorganslot("breasts")))
|
||||
amt_nude++
|
||||
if(is_groin_exposed())
|
||||
if(getorganslot("penis"))
|
||||
amt_nude++
|
||||
if(getorganslot("vagina"))
|
||||
amt_nude++
|
||||
var/mob/living/M
|
||||
var/watchers = 0
|
||||
for(M in view(world.view, src))
|
||||
if(M.client && !M.stat && !M.eye_blind && (locate(src) in viewers(world.view,M)))
|
||||
watchers++
|
||||
if(watchers && amt_nude)
|
||||
adjustArousalLoss((amt_nude * watchers) + S.arousal_gain_rate)
|
||||
|
||||
|
||||
/mob/living/proc/getArousalLoss()
|
||||
return arousalloss
|
||||
|
||||
/mob/living/proc/adjustArousalLoss(amount, updating_arousal=1)
|
||||
if(status_flags & GODMODE || !canbearoused)
|
||||
return 0
|
||||
arousalloss = CLAMP(arousalloss + amount, min_arousal, max_arousal)
|
||||
if(updating_arousal)
|
||||
updatearousal()
|
||||
|
||||
/mob/living/proc/setArousalLoss(amount, updating_arousal=1)
|
||||
if(status_flags & GODMODE || !canbearoused)
|
||||
return 0
|
||||
arousalloss = CLAMP(amount, min_arousal, max_arousal)
|
||||
if(updating_arousal)
|
||||
updatearousal()
|
||||
|
||||
/mob/living/proc/getPercentAroused()
|
||||
return ((100 / max_arousal) * arousalloss)
|
||||
|
||||
/mob/living/proc/isPercentAroused(percentage)//returns true if the mob's arousal (measured in a percent of 100) is greater than the arg percentage.
|
||||
if(!isnum(percentage) || percentage > 100 || percentage < 0)
|
||||
CRASH("Provided percentage is invalid")
|
||||
if(getPercentAroused() >= percentage)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
//H U D//
|
||||
/mob/living/proc/updatearousal()
|
||||
update_arousal_hud()
|
||||
|
||||
/mob/living/proc/update_arousal_hud()
|
||||
return 0
|
||||
|
||||
/datum/species/proc/update_arousal_hud(mob/living/carbon/human/H)
|
||||
return 0
|
||||
|
||||
/mob/living/carbon/human/update_arousal_hud()
|
||||
if(!client || !hud_used)
|
||||
return 0
|
||||
if(dna.species.update_arousal_hud())
|
||||
return 0
|
||||
if(!canbearoused)
|
||||
hud_used.arousal.icon_state = ""
|
||||
return 0
|
||||
else
|
||||
if(hud_used.arousal)
|
||||
if(stat == DEAD)
|
||||
hud_used.arousal.icon_state = "arousal0"
|
||||
return 1
|
||||
if(getArousalLoss() == max_arousal)
|
||||
hud_used.arousal.icon_state = "arousal100"
|
||||
return 1
|
||||
if(getArousalLoss() >= (max_arousal / 100) * 90)//M O D U L A R , W O W
|
||||
hud_used.arousal.icon_state = "arousal90"
|
||||
return 1
|
||||
if(getArousalLoss() >= (max_arousal / 100) * 80)//M O D U L A R , W O W
|
||||
hud_used.arousal.icon_state = "arousal80"
|
||||
return 1
|
||||
if(getArousalLoss() >= (max_arousal / 100) * 70)//M O D U L A R , W O W
|
||||
hud_used.arousal.icon_state = "arousal70"
|
||||
return 1
|
||||
if(getArousalLoss() >= (max_arousal / 100) * 60)//M O D U L A R , W O W
|
||||
hud_used.arousal.icon_state = "arousal60"
|
||||
return 1
|
||||
if(getArousalLoss() >= (max_arousal / 100) * 50)//M O D U L A R , W O W
|
||||
hud_used.arousal.icon_state = "arousal50"
|
||||
return 1
|
||||
if(getArousalLoss() >= (max_arousal / 100) * 40)//M O D U L A R , W O W
|
||||
hud_used.arousal.icon_state = "arousal40"
|
||||
return 1
|
||||
if(getArousalLoss() >= (max_arousal / 100) * 30)//M O D U L A R , W O W
|
||||
hud_used.arousal.icon_state = "arousal30"
|
||||
return 1
|
||||
if(getArousalLoss() >= (max_arousal / 100) * 20)//M O D U L A R , W O W
|
||||
hud_used.arousal.icon_state = "arousal10"
|
||||
return 1
|
||||
if(getArousalLoss() >= (max_arousal / 100) * 10)//M O D U L A R , W O W
|
||||
hud_used.arousal.icon_state = "arousal10"
|
||||
return 1
|
||||
else
|
||||
hud_used.arousal.icon_state = "arousal0"
|
||||
|
||||
/obj/screen/arousal
|
||||
name = "arousal"
|
||||
icon_state = "arousal0"
|
||||
icon = 'code/citadel/icons/hud.dmi'
|
||||
screen_loc = ui_arousal
|
||||
|
||||
/obj/screen/arousal/Click()
|
||||
if(!isliving(usr))
|
||||
return 0
|
||||
var/mob/living/M = usr
|
||||
if(M.canbearoused)
|
||||
M.mob_climax()
|
||||
return 1
|
||||
else
|
||||
to_chat(M, "<span class='warning'>Arousal is disabled. Feature is unavailable.</span>")
|
||||
|
||||
|
||||
|
||||
/mob/living/proc/mob_climax()//This is just so I can test this shit without being forced to add actual content to get rid of arousal. Will be a very basic proc for a while.
|
||||
set name = "Masturbate"
|
||||
set category = "IC"
|
||||
if(canbearoused && !restrained() && !stat)
|
||||
if(mb_cd_timer <= world.time)
|
||||
//start the cooldown even if it fails
|
||||
mb_cd_timer = world.time + mb_cd_length
|
||||
if(getArousalLoss() >= ((max_arousal / 100) * 33))//33% arousal or greater required
|
||||
src.visible_message("<span class='danger'>[src] starts masturbating!</span>", \
|
||||
"<span class='userdanger'>You start masturbating.</span>")
|
||||
if(do_after(src, 30, target = src))
|
||||
src.visible_message("<span class='danger'>[src] relieves themself!</span>", \
|
||||
"<span class='userdanger'>You have relieved yourself.</span>")
|
||||
setArousalLoss(min_arousal)
|
||||
/*
|
||||
switch(gender)
|
||||
if(MALE)
|
||||
PoolOrNew(/obj/effect/decal/cleanable/semen, loc)
|
||||
if(FEMALE)
|
||||
PoolOrNew(/obj/effect/decal/cleanable/femcum, loc)
|
||||
*/
|
||||
else
|
||||
to_chat(src, "<span class='notice'>You aren't aroused enough for that.</span>")
|
||||
|
||||
|
||||
//These are various procs that we'll use later, split up for readability instead of having one, huge proc.
|
||||
//For all of these, we assume the arguments given are proper and have been checked beforehand.
|
||||
/mob/living/carbon/human/proc/mob_masturbate(obj/item/organ/genital/G, mb_time = 30) //Masturbation, keep it gender-neutral
|
||||
var/total_fluids = 0
|
||||
var/datum/reagents/fluid_source = null
|
||||
|
||||
if(G.producing) //Can it produce its own fluids, such as breasts?
|
||||
fluid_source = G.reagents
|
||||
else
|
||||
if(!G.linked_organ)
|
||||
to_chat(src, "<span class='warning'>Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.</span>")
|
||||
return
|
||||
fluid_source = G.linked_organ.reagents
|
||||
total_fluids = fluid_source.total_volume
|
||||
if(mb_time)
|
||||
src.visible_message("<span class='danger'>[src] starts to [G.masturbation_verb] [p_their()] [G.name].</span>", \
|
||||
"<span class='green'>You start to [G.masturbation_verb] your [G.name].</span>", \
|
||||
"<span class='green'>You start to [G.masturbation_verb] your [G.name].</span>")
|
||||
|
||||
if(do_after(src, mb_time, target = src))
|
||||
if(total_fluids > 5)
|
||||
fluid_source.reaction(src.loc, TOUCH, 1, 0)
|
||||
fluid_source.clear_reagents()
|
||||
src.visible_message("<span class='danger'>[src] orgasms, cumming[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""]!</span>", \
|
||||
"<span class='green'>You cum[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""].</span>", \
|
||||
"<span class='green'>You have relieved yourself.</span>")
|
||||
if(G.can_climax)
|
||||
setArousalLoss(min_arousal)
|
||||
|
||||
|
||||
/mob/living/carbon/human/proc/mob_climax_outside(obj/item/organ/genital/G, mb_time = 30) //This is used for forced orgasms and other hands-free climaxes
|
||||
var/total_fluids = 0
|
||||
var/datum/reagents/fluid_source = null
|
||||
var/unable_to_come = FALSE
|
||||
|
||||
if(G.producing) //Can it produce its own fluids, such as breasts?
|
||||
fluid_source = G.reagents
|
||||
total_fluids = fluid_source.total_volume
|
||||
else
|
||||
if(!G.linked_organ)
|
||||
unable_to_come = TRUE
|
||||
else
|
||||
fluid_source = G.linked_organ.reagents
|
||||
total_fluids = fluid_source.total_volume
|
||||
|
||||
if(unable_to_come)
|
||||
src.visible_message("<span class='danger'>[src] shudders, their [G.name] unable to cum.</span>", \
|
||||
"<span class='userdanger'>Your [G.name] cannot cum, giving no relief.</span>", \
|
||||
"<span class='userdanger'>Your [G.name] cannot cum, giving no relief.</span>")
|
||||
else
|
||||
total_fluids = fluid_source.total_volume
|
||||
if(mb_time) //as long as it's not instant, give a warning
|
||||
src.visible_message("<span class='danger'>[src] looks like they're about to cum.</span>", \
|
||||
"<span class='green'>You feel yourself about to orgasm.</span>", \
|
||||
"<span class='green'>You feel yourself about to orgasm.</span>")
|
||||
if(do_after(src, mb_time, target = src))
|
||||
if(total_fluids > 5)
|
||||
fluid_source.reaction(src.loc, TOUCH, 1, 0)
|
||||
fluid_source.clear_reagents()
|
||||
src.visible_message("<span class='danger'>[src] orgasms[istype(src.loc, /turf/open/floor) ? ", spilling onto [src.loc]" : ""], using [p_their()] [G.name]!</span>", \
|
||||
"<span class='green'>You climax[istype(src.loc, /turf/open/floor) ? ", spilling onto [src.loc]" : ""] with your [G.name].</span>", \
|
||||
"<span class='green'>You climax using your [G.name].</span>")
|
||||
if(G.can_climax)
|
||||
setArousalLoss(min_arousal)
|
||||
|
||||
|
||||
/mob/living/carbon/human/proc/mob_climax_partner(obj/item/organ/genital/G, mob/living/L, spillage = TRUE, mb_time = 30) //Used for climaxing with any living thing
|
||||
var/total_fluids = 0
|
||||
var/datum/reagents/fluid_source = null
|
||||
|
||||
if(G.producing) //Can it produce its own fluids, such as breasts?
|
||||
fluid_source = G.reagents
|
||||
else
|
||||
if(!G.linked_organ)
|
||||
to_chat(src, "<span class='warning'>Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.</span>")
|
||||
return
|
||||
fluid_source = G.linked_organ.reagents
|
||||
total_fluids = fluid_source.total_volume
|
||||
if(mb_time) //Skip warning if this is an instant climax.
|
||||
src.visible_message("[src] is about to climax with [L]!", \
|
||||
"You're about to climax with [L]!", \
|
||||
"<span class='danger'>You're preparing to climax with someone!</span>")
|
||||
if(spillage)
|
||||
if(do_after(src, mb_time, target = src) && in_range(src, L))
|
||||
fluid_source.trans_to(L, total_fluids*G.fluid_transfer_factor)
|
||||
total_fluids -= total_fluids*G.fluid_transfer_factor
|
||||
if(total_fluids > 5)
|
||||
fluid_source.reaction(L.loc, TOUCH, 1, 0)
|
||||
fluid_source.clear_reagents()
|
||||
src.visible_message("<span class='danger'>[src] climaxes with [L][spillage ? ", overflowing and spilling":""], using [p_their()] [G.name]!</span>", \
|
||||
"<span class='green'>You orgasm with [L][spillage ? ", spilling out of them":""], using your [G.name].</span>", \
|
||||
"<span class='green'>You have climaxed with someone[spillage ? ", spilling out of them":""], using your [G.name].</span>")
|
||||
if(G.can_climax)
|
||||
setArousalLoss(min_arousal)
|
||||
else //knots and other non-spilling orgasms
|
||||
if(do_after(src, mb_time, target = src) && in_range(src, L))
|
||||
fluid_source.trans_to(L, total_fluids)
|
||||
total_fluids = 0
|
||||
src.visible_message("<span class='danger'>[src] climaxes with [L], [p_their()] [G.name] spilling nothing!</span>", \
|
||||
"<span class='green'>You ejaculate with [L], your [G.name] spilling nothing.</span>", \
|
||||
"<span class='green'>You have climaxed inside someone, your [G.name] spilling nothing.</span>")
|
||||
if(G.can_climax)
|
||||
setArousalLoss(min_arousal)
|
||||
|
||||
|
||||
/mob/living/carbon/human/proc/mob_fill_container(obj/item/organ/genital/G, obj/item/reagent_containers/container, mb_time = 30) //For beaker-filling, beware the bartender
|
||||
var/total_fluids = 0
|
||||
var/datum/reagents/fluid_source = null
|
||||
|
||||
if(G.producing) //Can it produce its own fluids, such as breasts?
|
||||
fluid_source = G.reagents
|
||||
else
|
||||
if(!G.linked_organ)
|
||||
to_chat(src, "<span class='warning'>Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.</span>")
|
||||
return
|
||||
fluid_source = G.linked_organ.reagents
|
||||
total_fluids = fluid_source.total_volume
|
||||
|
||||
//if(!container) //Something weird happened
|
||||
// to_chat(src, "<span class='warning'>You need a container to do this!</span>")
|
||||
// return
|
||||
|
||||
src.visible_message("<span class='danger'>[src] starts to [G.masturbation_verb] their [G.name] over [container].</span>", \
|
||||
"<span class='userdanger'>You start to [G.masturbation_verb] your [G.name] over [container].</span>", \
|
||||
"<span class='userdanger'>You start to [G.masturbation_verb] your [G.name] over something.</span>")
|
||||
if(do_after(src, mb_time, target = src) && in_range(src, container))
|
||||
fluid_source.trans_to(container, total_fluids)
|
||||
src.visible_message("<span class='danger'>[src] uses [p_their()] [G.name] to fill [container]!</span>", \
|
||||
"<span class='green'>You used your [G.name] to fill [container].</span>", \
|
||||
"<span class='green'>You have relieved some pressure.</span>")
|
||||
if(G.can_climax)
|
||||
setArousalLoss(min_arousal)
|
||||
|
||||
/mob/living/carbon/human/proc/pick_masturbate_genitals()
|
||||
var/obj/item/organ/genital/ret_organ
|
||||
var/list/genitals_list = list()
|
||||
var/list/worn_stuff = get_equipped_items()
|
||||
|
||||
for(var/obj/item/organ/genital/G in internal_organs)
|
||||
if(G.can_masturbate_with) //filter out what you can't masturbate with
|
||||
if(G.is_exposed(worn_stuff)) //Nude or through_clothing
|
||||
genitals_list += G
|
||||
if(genitals_list.len)
|
||||
ret_organ = input(src, "with what?", "Masturbate", null) as null|obj in genitals_list
|
||||
return ret_organ
|
||||
return null //error stuff
|
||||
|
||||
|
||||
/mob/living/carbon/human/proc/pick_climax_genitals()
|
||||
var/obj/item/organ/genital/ret_organ
|
||||
var/list/genitals_list = list()
|
||||
var/list/worn_stuff = get_equipped_items()
|
||||
|
||||
for(var/obj/item/organ/genital/G in internal_organs)
|
||||
if(G.can_climax) //filter out what you can't masturbate with
|
||||
if(G.is_exposed(worn_stuff)) //Nude or through_clothing
|
||||
genitals_list += G
|
||||
if(genitals_list.len)
|
||||
ret_organ = input(src, "with what?", "Climax", null) as null|obj in genitals_list
|
||||
return ret_organ
|
||||
return null //error stuff
|
||||
|
||||
|
||||
/mob/living/carbon/human/proc/pick_partner()
|
||||
var/list/partners = list()
|
||||
if(src.pulling)
|
||||
partners += src.pulling //Yes, even objects for now
|
||||
if(src.pulledby)
|
||||
partners += src.pulledby
|
||||
//Now we got both of them, let's check if they're proper
|
||||
for(var/I in partners)
|
||||
if(isliving(I))
|
||||
if(iscarbon(I))
|
||||
var/mob/living/carbon/C = I
|
||||
if(!C.exposed_genitals.len) //Nothing through_clothing
|
||||
if(!C.is_groin_exposed()) //No pants undone
|
||||
if(!C.is_chest_exposed()) //No chest exposed
|
||||
partners -= I //Then not proper, remove them
|
||||
else
|
||||
partners -= I //No fucking objects
|
||||
//NOW the list should only contain correct partners
|
||||
if(!partners.len)
|
||||
return null //No one left.
|
||||
return input(src, "With whom?", "Sexual partner", null) in partners //pick one, default to null
|
||||
|
||||
/mob/living/carbon/human/proc/pick_climax_container()
|
||||
var/obj/item/reagent_containers/SC = null
|
||||
var/list/containers_list = list()
|
||||
|
||||
for(var/obj/item/reagent_containers/container in held_items)
|
||||
if(container.is_open_container() || istype(container, /obj/item/reagent_containers/food/snacks))
|
||||
containers_list += container
|
||||
|
||||
if(containers_list.len)
|
||||
SC = input(src, "Into or onto what?(Cancel for nowhere)", null) as null|obj in containers_list
|
||||
if(SC)
|
||||
if(in_range(src, SC))
|
||||
return SC
|
||||
return null //If nothing correct, give null.
|
||||
|
||||
|
||||
//Here's the main proc itself
|
||||
/mob/living/carbon/human/mob_climax(forced_climax=FALSE) //Forced is instead of the other proc, makes you cum if you have the tools for it, ignoring restraints
|
||||
if(mb_cd_timer > world.time)
|
||||
if(!forced_climax) //Don't spam the message to the victim if forced to come too fast
|
||||
to_chat(src, "<span class='warning'>You need to wait [round((mb_cd_timer - world.time)/(20))] seconds before you can do that again!</span>")
|
||||
return
|
||||
mb_cd_timer = (world.time + mb_cd_length)
|
||||
|
||||
|
||||
if(canbearoused && has_dna())
|
||||
if(stat==2)
|
||||
to_chat(src, "<span class='warning'>You can't do that while dead!</span>")
|
||||
return
|
||||
if(forced_climax) //Something forced us to cum, this is not a masturbation thing and does not progress to the other checks
|
||||
for(var/obj/item/organ/O in internal_organs)
|
||||
if(istype(O, /obj/item/organ/genital))
|
||||
var/obj/item/organ/genital/G = O
|
||||
if(!G.can_climax) //Skip things like wombs and testicles
|
||||
continue
|
||||
var/mob/living/partner
|
||||
var/check_target
|
||||
var/list/worn_stuff = get_equipped_items()
|
||||
|
||||
if(G.is_exposed(worn_stuff))
|
||||
if(src.pulling) //Are we pulling someone? Priority target, we can't be making option menus for this, has to be quick
|
||||
if(isliving(src.pulling)) //Don't fuck objects
|
||||
check_target = src.pulling
|
||||
if(src.pulledby && !check_target) //prioritise pulled over pulledby
|
||||
if(isliving(src.pulledby))
|
||||
check_target = src.pulledby
|
||||
//Now we should have a partner, or else we have to come alone
|
||||
if(check_target)
|
||||
if(iscarbon(check_target)) //carbons can have clothes
|
||||
var/mob/living/carbon/C = check_target
|
||||
if(C.exposed_genitals.len || C.is_groin_exposed() || C.is_chest_exposed()) //Are they naked enough?
|
||||
partner = C
|
||||
else //A cat is fine too
|
||||
partner = check_target
|
||||
if(partner) //Did they pass the clothing checks?
|
||||
mob_climax_partner(G, partner, mb_time = 0) //Instant climax due to forced
|
||||
continue //You've climaxed once with this organ, continue on
|
||||
//not exposed OR if no partner was found while exposed, climax alone
|
||||
mob_climax_outside(G, mb_time = 0) //removed climax timer for sudden, forced orgasms
|
||||
//Now all genitals that could climax, have.
|
||||
//Since this was a forced climax, we do not need to continue with the other stuff
|
||||
return
|
||||
//If we get here, then this is not a forced climax and we gotta check a few things.
|
||||
|
||||
if(stat==1) //No sleep-masturbation, you're unconscious.
|
||||
to_chat(src, "<span class='warning'>You must be conscious to do that!</span>")
|
||||
return
|
||||
if(getArousalLoss() < 33) //flat number instead of percentage
|
||||
to_chat(src, "<span class='warning'>You aren't aroused enough for that!</span>")
|
||||
return
|
||||
|
||||
//Ok, now we check what they want to do.
|
||||
var/choice = input(src, "Select sexual activity", "Sexual activity:") in list("Masturbate", "Climax alone", "Climax with partner", "Fill container")
|
||||
|
||||
switch(choice)
|
||||
if("Masturbate")
|
||||
if(restrained(TRUE)) //TRUE ignores grabs
|
||||
to_chat(src, "<span class='warning'>You can't do that while restrained!</span>")
|
||||
return
|
||||
var/free_hands = get_num_arms()
|
||||
if(!free_hands)
|
||||
to_chat(src, "<span class='warning'>You need at least one free arm.</span>")
|
||||
return
|
||||
for(var/helditem in held_items)//how many hands are free
|
||||
if(isobj(helditem))
|
||||
free_hands--
|
||||
if(free_hands <= 0)
|
||||
to_chat(src, "<span class='warning'>You're holding too many things.</span>")
|
||||
return
|
||||
//We got hands, let's pick an organ
|
||||
var/obj/item/organ/genital/picked_organ
|
||||
picked_organ = pick_masturbate_genitals()
|
||||
if(picked_organ)
|
||||
mob_masturbate(picked_organ)
|
||||
return
|
||||
else //They either lack organs that can masturbate, or they didn't pick one.
|
||||
to_chat(src, "<span class='warning'>You cannot masturbate without choosing genitals.</span>")
|
||||
return
|
||||
|
||||
if("Climax alone")
|
||||
if(restrained(TRUE)) //TRUE ignores grabs
|
||||
to_chat(src, "<span class='warning'>You can't do that while restrained!</span>")
|
||||
return
|
||||
var/free_hands = get_num_arms()
|
||||
if(!free_hands)
|
||||
to_chat(src, "<span class='warning'>You need at least one free arm.</span>")
|
||||
return
|
||||
for(var/helditem in held_items)//how many hands are free
|
||||
if(isobj(helditem))
|
||||
free_hands--
|
||||
if(free_hands <= 0)
|
||||
to_chat(src, "<span class='warning'>You're holding too many things.</span>")
|
||||
return
|
||||
//We got hands, let's pick an organ
|
||||
var/obj/item/organ/genital/picked_organ
|
||||
picked_organ = pick_climax_genitals()
|
||||
if(picked_organ)
|
||||
mob_climax_outside(picked_organ)
|
||||
return
|
||||
else //They either lack organs that can masturbate, or they didn't pick one.
|
||||
to_chat(src, "<span class='warning'>You cannot climax without choosing genitals.</span>")
|
||||
return
|
||||
|
||||
if("Climax with partner")
|
||||
//We need no hands, we can be restrained and so on, so let's pick an organ
|
||||
var/obj/item/organ/genital/picked_organ
|
||||
picked_organ = pick_climax_genitals()
|
||||
if(picked_organ)
|
||||
var/mob/living/partner = pick_partner() //Get someone
|
||||
if(partner)
|
||||
var/spillage = input(src, "Would your fluids spill outside?", "Choose overflowing option", "Yes") as anything in list("Yes", "No")
|
||||
if(spillage == "Yes")
|
||||
mob_climax_partner(picked_organ, partner, TRUE)
|
||||
else
|
||||
mob_climax_partner(picked_organ, partner, FALSE)
|
||||
return
|
||||
else
|
||||
to_chat(src, "<span class='warning'>You cannot do this alone.</span>")
|
||||
return
|
||||
else //They either lack organs that can masturbate, or they didn't pick one.
|
||||
to_chat(src, "<span class='warning'>You cannot climax without choosing genitals.</span>")
|
||||
return
|
||||
|
||||
if("Fill container")
|
||||
//We'll need hands and no restraints.
|
||||
if(restrained(TRUE)) //TRUE ignores grabs
|
||||
to_chat(src, "<span class='warning'>You can't do that while restrained!</span>")
|
||||
return
|
||||
var/free_hands = get_num_arms()
|
||||
if(!free_hands)
|
||||
to_chat(src, "<span class='warning'>You need at least one free arm.</span>")
|
||||
return
|
||||
for(var/helditem in held_items)//how many hands are free
|
||||
if(isobj(helditem))
|
||||
free_hands--
|
||||
if(free_hands <= 0)
|
||||
to_chat(src, "<span class='warning'>You're holding too many things.</span>")
|
||||
return
|
||||
//We got hands, let's pick an organ
|
||||
var/obj/item/organ/genital/picked_organ
|
||||
picked_organ = pick_climax_genitals() //Gotta be climaxable, not just masturbation, to fill with fluids.
|
||||
if(picked_organ)
|
||||
//Good, got an organ, time to pick a container
|
||||
var/obj/item/reagent_containers/fluid_container = pick_climax_container()
|
||||
if(fluid_container)
|
||||
mob_fill_container(picked_organ, fluid_container)
|
||||
return
|
||||
else
|
||||
to_chat(src, "<span class='warning'>You cannot do this without anything to fill.</span>")
|
||||
return
|
||||
else //They either lack organs that can climax, or they didn't pick one.
|
||||
to_chat(src, "<span class='warning'>You cannot fill anything without choosing genitals.</span>")
|
||||
return
|
||||
else //Somehow another option was taken, maybe something interrupted the selection or it was cancelled
|
||||
return //Just end it in that case.
|
||||
@@ -1,31 +0,0 @@
|
||||
/*/////////////////////////////////////////////////////////////////////////////////
|
||||
/////// ///////
|
||||
/////// Cit's exclusive jumpsuits, uniforms, etc. go here ///////
|
||||
/////// ///////
|
||||
*//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/obj/item/clothing/under/rank/security/skirt
|
||||
name = "security skirt"
|
||||
desc = "A tactical security skirt for officers complete with Nanotrasen belt buckle."
|
||||
icon = 'icons/obj/clothing/cit_clothes.dmi'
|
||||
icon_state = "secskirt"
|
||||
icon_override = 'icons/mob/citadel/uniforms.dmi'
|
||||
item_state = "r_suit"
|
||||
item_color = "secskirt"
|
||||
|
||||
/obj/item/clothing/under/rank/head_of_security/skirt
|
||||
name = "head of security's skirt"
|
||||
desc = "A security skirt decorated for those few with the dedication to achieve the position of Head of Security."
|
||||
icon = 'icons/obj/clothing/cit_clothes.dmi'
|
||||
icon_state = "hosskirt"
|
||||
icon_override = 'icons/mob/citadel/uniforms.dmi'
|
||||
item_state = "gy_suit"
|
||||
item_color = "hosskirt"
|
||||
|
||||
/obj/item/clothing/suit/armor/hos/trenchcoat/cloak
|
||||
name = "armored trenchcloak"
|
||||
desc = "A trenchcoat enchanced with a special lightweight kevlar. This one appears to be designed to be draped over one's shoulders rather than worn normally.."
|
||||
alternate_worn_icon = 'icons/mob/citadel/suit.dmi'
|
||||
icon_state = "hostrench"
|
||||
item_state = "hostrench"
|
||||
@@ -1,35 +0,0 @@
|
||||
/datum/controller/subsystem/ticker/proc/generate_crew_objectives()
|
||||
for(var/datum/mind/crewMind in SSticker.minds)
|
||||
if(prob(5) && !issilicon(crewMind.current) && !jobban_isbanned(crewMind, "Syndicate") && GLOB.miscreants_allowed && ROLE_MISCREANT in crewMind.current.client.prefs.be_special)
|
||||
generate_miscreant_objectives(crewMind)
|
||||
else
|
||||
if(CONFIG_GET(flag/allow_crew_objectives))
|
||||
generate_individual_objectives(crewMind)
|
||||
return
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/generate_individual_objectives(var/datum/mind/crewMind)
|
||||
if(!(CONFIG_GET(flag/allow_crew_objectives)))
|
||||
return
|
||||
if(!crewMind)
|
||||
return
|
||||
if(!crewMind.current || !crewMind.objectives || crewMind.special_role)
|
||||
return
|
||||
if(!crewMind.assigned_role)
|
||||
return
|
||||
var/list/validobjs = crewobjjobs["[ckey(crewMind.assigned_role)]"]
|
||||
if(!validobjs || !validobjs.len)
|
||||
return
|
||||
var/selectedObj = pick(validobjs)
|
||||
var/datum/objective/crew/newObjective = new selectedObj
|
||||
if(!newObjective)
|
||||
return
|
||||
newObjective.owner = crewMind
|
||||
crewMind.objectives += newObjective
|
||||
to_chat(crewMind, "<B>As a part of Nanotrasen's anti-tide efforts, you have been assigned an optional objective. It will be checked at the end of the shift. <font color=red>Performing traitorous acts in pursuit of your objective may result in termination of your employment.</font></B>")
|
||||
to_chat(crewMind, "<B>Your optional objective:</B> [newObjective.explanation_text]")
|
||||
|
||||
/datum/objective/crew/
|
||||
var/jobs = ""
|
||||
explanation_text = "Yell on the development discussion channel on Citadels discord if this ever shows up. Something just broke here, dude"
|
||||
|
||||
/datum/objective/crew/proc/setup()
|
||||
@@ -1,5 +0,0 @@
|
||||
/obj/structure/displaycase/clown
|
||||
desc = "In the event of clown, honk glass."
|
||||
alert = 1
|
||||
start_showpiece_type = /obj/item/bikehorn
|
||||
req_access = list(ACCESS_CENT_GENERAL)
|
||||
@@ -1,3 +0,0 @@
|
||||
//Disables the custom emote blacklist from TG that normally applies to slimes.
|
||||
/datum/emote/living/custom
|
||||
mob_type_blacklist_typecache = list(/mob/living/brain)
|
||||
@@ -1,21 +0,0 @@
|
||||
//Will include consumable gene mods in the future.
|
||||
|
||||
/obj/item/genemod
|
||||
name = "genetic modifier"
|
||||
desc = "Microbodies which can grow, morph, or otherwise change an organism into something else."
|
||||
icon = 'icons/obj/items_and_weapons.dmi'
|
||||
icon_state = "dnainjector"
|
||||
throw_speed = 3
|
||||
throw_range = 5
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
var/applied_region = "chest"
|
||||
var/list/add_mutations = list()
|
||||
var/list/remove_mutations = list()
|
||||
|
||||
var/list/add_mutations_static = list()
|
||||
var/list/remove_mutations_static = list()
|
||||
|
||||
var/used = 0
|
||||
|
||||
/obj/item/genemod/proc/use(mob/living/carbon/human/target)
|
||||
return
|
||||
@@ -1,41 +0,0 @@
|
||||
/obj/structure/reagent_dispensers/keg
|
||||
name = "keg"
|
||||
desc = "A keg."
|
||||
icon = 'code/citadel/icons/objects.dmi'
|
||||
icon_state = "keg"
|
||||
reagent_id = "water"
|
||||
|
||||
/obj/structure/reagent_dispensers/keg/mead
|
||||
name = "keg of mead"
|
||||
desc = "A keg of mead."
|
||||
icon_state = "orangekeg"
|
||||
reagent_id = "mead"
|
||||
|
||||
/obj/structure/reagent_dispensers/keg/aphro
|
||||
name = "keg of aphrodisiac"
|
||||
desc = "A keg of aphrodisiac."
|
||||
icon_state = "pinkkeg"
|
||||
reagent_id = "aphro"
|
||||
|
||||
/obj/structure/reagent_dispensers/keg/aphro/strong
|
||||
name = "keg of strong aphrodisiac"
|
||||
desc = "A keg of strong and addictive aphrodisiac."
|
||||
reagent_id = "aphro+"
|
||||
|
||||
/obj/structure/reagent_dispensers/keg/milk
|
||||
name = "keg of milk"
|
||||
desc = "It's not quite what you were hoping for."
|
||||
icon_state = "whitekeg"
|
||||
reagent_id = "milk"
|
||||
|
||||
/obj/structure/reagent_dispensers/keg/semen
|
||||
name = "keg of semen"
|
||||
desc = "Dear lord, where did this even come from?"
|
||||
icon_state = "whitekeg"
|
||||
reagent_id = "semen"
|
||||
|
||||
/obj/structure/reagent_dispensers/keg/gargle
|
||||
name = "keg of pan galactic gargleblaster"
|
||||
desc = "A keg of... wow that's a long name."
|
||||
icon_state = "bluekeg"
|
||||
reagent_id = "gargleblaster"
|
||||
@@ -1,52 +0,0 @@
|
||||
/datum/controller/subsystem/ticker/proc/generate_miscreant_objectives(var/datum/mind/crewMind)
|
||||
if(!GLOB.miscreants_allowed)
|
||||
return
|
||||
if(!crewMind)
|
||||
return
|
||||
if(!crewMind.current || !crewMind.objectives || crewMind.special_role)
|
||||
return
|
||||
if(!crewMind.assigned_role)
|
||||
return
|
||||
if(!(ROLE_MISCREANT in crewMind.current.client.prefs.be_special))
|
||||
return
|
||||
if(jobban_isbanned(crewMind, "Syndicate"))
|
||||
return
|
||||
var/list/objectiveTypes = miscreantobjlist
|
||||
if(!objectiveTypes.len)
|
||||
return
|
||||
var/selectedType = pick(objectiveTypes)
|
||||
var/datum/objective/miscreant/newObjective = new selectedType
|
||||
if(!newObjective)
|
||||
return
|
||||
newObjective.owner = crewMind
|
||||
crewMind.objectives += newObjective
|
||||
crewMind.special_role = "miscreant"
|
||||
to_chat(crewMind, "<B><font size=3 color=red>You are a Miscreant.</font></B>")
|
||||
to_chat(crewMind, "<B>Pursuing your objective is entirely optional, as the completion of your objective is unable to be tracked. Performing traitorous acts not directly related to your objective may result in permanent termination of your employment.</B>")
|
||||
to_chat(crewMind, "<B>Your objective:</B> [newObjective.explanation_text]")
|
||||
|
||||
/datum/objective/miscreant
|
||||
explanation_text = "Something broke. Horribly. Dear god, im so sorry. Yell about this in the development discussion channel of citadels discord."
|
||||
|
||||
/* Goon's Miscreant Objectives */
|
||||
|
||||
|
||||
/datum/objective/miscreant/incompetent
|
||||
explanation_text = "Be as useless and incompetent as possible without getting killed."
|
||||
|
||||
/datum/objective/miscreant/litterbug
|
||||
explanation_text = "Make a huge mess wherever you go."
|
||||
|
||||
/datum/objective/miscreant/creepy
|
||||
explanation_text = "Sneak around looking as suspicious as possible without actually doing anything illegal."
|
||||
|
||||
/datum/objective/miscreant/whiny
|
||||
explanation_text = "Complain incessantly about every minor issue you find."
|
||||
|
||||
/* Citadel's Miscreant Objectives */
|
||||
|
||||
/datum/objective/miscreant/immersions
|
||||
explanation_text = "Act as uncharacteristic as you possibly can." // corrected from "Act as out of character as you can" people thought it meant to just ooc in ic
|
||||
|
||||
/datum/objective/miscreant/cargonia
|
||||
explanation_text = "Attempt to establish independence of your department."
|
||||
@@ -1,274 +0,0 @@
|
||||
//body bluids
|
||||
/datum/reagent/consumable/semen
|
||||
name = "Semen"
|
||||
id = "semen"
|
||||
description = "Sperm from some animal. Useless for anything but insemination, really."
|
||||
taste_description = "something salty"
|
||||
taste_mult = 2 //Not very overpowering flavor
|
||||
data = list("donor"=null,"viruses"=null,"donor_DNA"=null,"blood_type"=null,"resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null)
|
||||
reagent_state = LIQUID
|
||||
color = "#FFFFFF" // rgb: 255, 255, 255
|
||||
nutriment_factor = 0.5 * REAGENTS_METABOLISM
|
||||
|
||||
/datum/reagent/consumable/semen/reaction_turf(turf/T, reac_volume)
|
||||
if(!istype(T))
|
||||
return
|
||||
if(reac_volume < 3)
|
||||
return
|
||||
|
||||
var/obj/effect/decal/cleanable/semen/S = locate() in T
|
||||
if(!S)
|
||||
S = new(T)
|
||||
S.reagents.add_reagent("semen", reac_volume)
|
||||
if(data["blood_DNA"])
|
||||
S.add_blood_DNA(list(data["blood_DNA"] = data["blood_type"]))
|
||||
|
||||
/obj/effect/decal/cleanable/semen
|
||||
name = "semen"
|
||||
desc = null
|
||||
gender = PLURAL
|
||||
density = 0
|
||||
layer = ABOVE_NORMAL_TURF_LAYER
|
||||
icon = 'code/citadel/icons/effects.dmi'
|
||||
icon_state = "semen1"
|
||||
random_icon_states = list("semen1", "semen2", "semen3", "semen4")
|
||||
|
||||
/obj/effect/decal/cleanable/semen/New()
|
||||
..()
|
||||
dir = pick(1,2,4,8)
|
||||
|
||||
/datum/reagent/consumable/semen/reaction_turf(turf/T, reac_volume)
|
||||
if(!isspaceturf(T))
|
||||
var/obj/effect/decal/cleanable/reagentdecal = new/obj/effect/decal/cleanable/semen(T)
|
||||
reagentdecal.reagents.add_reagent("semen", reac_volume)
|
||||
|
||||
/datum/reagent/consumable/femcum
|
||||
name = "Female Ejaculate"
|
||||
id = "femcum"
|
||||
description = "Vaginal lubricant found in most mammals and other animals of similar nature. Where you found this is your own business."
|
||||
taste_description = "something with a tang" // wew coders who haven't eaten out a girl.
|
||||
taste_mult = 2
|
||||
data = list("donor"=null,"viruses"=null,"donor_DNA"=null,"blood_type"=null,"resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null)
|
||||
reagent_state = LIQUID
|
||||
color = "#AAAAAA77"
|
||||
nutriment_factor = 0.5 * REAGENTS_METABOLISM
|
||||
|
||||
/obj/effect/decal/cleanable/femcum
|
||||
name = "female ejaculate"
|
||||
desc = null
|
||||
gender = PLURAL
|
||||
density = 0
|
||||
layer = ABOVE_NORMAL_TURF_LAYER
|
||||
icon = 'code/citadel/icons/effects.dmi'
|
||||
icon_state = "fem1"
|
||||
random_icon_states = list("fem1", "fem2", "fem3", "fem4")
|
||||
blood_state = null
|
||||
bloodiness = null
|
||||
|
||||
/obj/effect/decal/cleanable/femcum/New()
|
||||
..()
|
||||
dir = pick(1,2,4,8)
|
||||
add_blood_DNA(list("Non-human DNA" = "A+"))
|
||||
|
||||
/obj/effect/decal/cleanable/femcum/replace_decal(obj/effect/decal/cleanable/femcum/F)
|
||||
F.add_blood_DNA(return_blood_DNA())
|
||||
..()
|
||||
|
||||
/datum/reagent/consumable/femcum/reaction_turf(turf/T, reac_volume)
|
||||
if(!istype(T))
|
||||
return
|
||||
if(reac_volume < 3)
|
||||
return
|
||||
|
||||
var/obj/effect/decal/cleanable/femcum/S = locate() in T
|
||||
if(!S)
|
||||
S = new(T)
|
||||
S.reagents.add_reagent("femcum", reac_volume)
|
||||
if(data["blood_DNA"])
|
||||
S.add_blood_DNA(list(data["blood_DNA"] = data["blood_type"]))
|
||||
|
||||
//aphrodisiac & anaphrodisiac
|
||||
|
||||
/datum/reagent/drug/aphrodisiac
|
||||
name = "Crocin"
|
||||
id = "aphro"
|
||||
description = "Naturally found in the crocus and gardenia flowers, this drug acts as a natural and safe aphrodisiac."
|
||||
taste_description = "strawberry roofies"
|
||||
taste_mult = 2 //Hide the roofies in stronger flavors
|
||||
color = "#FFADFF"//PINK, rgb(255, 173, 255)
|
||||
|
||||
/datum/reagent/drug/aphrodisiac/on_mob_life(mob/living/M)
|
||||
if(M && M.canbearoused)
|
||||
if(prob(33))
|
||||
M.adjustArousalLoss(2)
|
||||
if(prob(5))
|
||||
M.emote(pick("moan","blush"))
|
||||
if(prob(5))
|
||||
var/aroused_message = pick("You feel frisky.", "You're having trouble suppressing your urges.", "You feel in the mood.")
|
||||
to_chat(M, "<span class='love'>[aroused_message]</span>")
|
||||
..()
|
||||
|
||||
/datum/reagent/drug/aphrodisiacplus
|
||||
name = "Hexacrocin"
|
||||
id = "aphro+"
|
||||
description = "Chemically condensed form of basic crocin. This aphrodisiac is extremely powerful and addictive in most animals.\
|
||||
Addiction withdrawals can cause brain damage and shortness of breath. Overdosage can lead to brain damage and a\
|
||||
permanent increase in libido (commonly referred to as 'bimbofication')."
|
||||
taste_description = "liquid desire"
|
||||
color = "#FF2BFF"//dark pink
|
||||
addiction_threshold = 20
|
||||
overdose_threshold = 20
|
||||
|
||||
/datum/reagent/drug/aphrodisiacplus/on_mob_life(mob/living/M)
|
||||
if(M && M.canbearoused)
|
||||
if(prob(33))
|
||||
M.adjustArousalLoss(6)//not quite six times as powerful, but still considerably more powerful.
|
||||
if(prob(5))
|
||||
if(M.getArousalLoss() > 75)
|
||||
M.say(pick("Hnnnnngghh...", "Ohh...", "Mmnnn..."))
|
||||
else
|
||||
M.emote(pick("moan","blush"))
|
||||
if(prob(5))
|
||||
var/aroused_message
|
||||
if(M.getArousalLoss() > 90)
|
||||
aroused_message = pick("You need to fuck someone!", "You're bursting with sexual tension!", "You can't get sex off your mind!")
|
||||
else
|
||||
aroused_message = pick("You feel a bit hot.", "You feel strong sexual urges.", "You feel in the mood.", "You're ready to go down on someone.")
|
||||
to_chat(M, "<span class='love'>[aroused_message]</span>")
|
||||
..()
|
||||
|
||||
/datum/reagent/drug/aphrodisiacplus/addiction_act_stage2(mob/living/M)
|
||||
if(prob(30))
|
||||
M.adjustBrainLoss(2)
|
||||
..()
|
||||
/datum/reagent/drug/aphrodisiacplus/addiction_act_stage3(mob/living/M)
|
||||
if(prob(30))
|
||||
M.adjustBrainLoss(3)
|
||||
|
||||
..()
|
||||
/datum/reagent/drug/aphrodisiacplus/addiction_act_stage4(mob/living/M)
|
||||
if(prob(30))
|
||||
M.adjustBrainLoss(4)
|
||||
..()
|
||||
|
||||
/datum/reagent/drug/aphrodisiacplus/overdose_process(mob/living/M)
|
||||
if(M && M.canbearoused && prob(33))
|
||||
if(M.getArousalLoss() >= 100 && ishuman(M) && M.has_dna())
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(prob(50)) //Less spam
|
||||
to_chat(H, "<span class='love'>Your libido is going haywire!</span>")
|
||||
H.mob_climax(forced_climax=TRUE)
|
||||
if(M.min_arousal < 50)
|
||||
M.min_arousal += 1
|
||||
if(M.min_arousal < M.max_arousal)
|
||||
M.min_arousal += 1
|
||||
M.adjustArousalLoss(2)
|
||||
..()
|
||||
|
||||
/datum/reagent/drug/anaphrodisiac
|
||||
name = "Camphor"
|
||||
id = "anaphro"
|
||||
description = "Naturally found in some species of evergreen trees, camphor is a waxy substance. When injested by most animals, it acts as an anaphrodisiac\
|
||||
, reducing libido and calming them. Non-habit forming and not addictive."
|
||||
taste_description = "dull bitterness"
|
||||
taste_mult = 2
|
||||
color = "#D9D9D9"//rgb(217, 217, 217)
|
||||
reagent_state = SOLID
|
||||
|
||||
/datum/reagent/drug/anaphrodisiac/on_mob_life(mob/living/M)
|
||||
if(M && M.canbearoused && prob(33))
|
||||
M.adjustArousalLoss(-2)
|
||||
..()
|
||||
|
||||
/datum/reagent/drug/anaphrodisiacplus
|
||||
name = "Hexacamphor"
|
||||
id = "anaphro+"
|
||||
description = "Chemically condensed camphor. Causes an extreme reduction in libido and a permanent one if overdosed. Non-addictive."
|
||||
taste_description = "tranquil celibacy"
|
||||
color = "#D9D9D9"//rgb(217, 217, 217)
|
||||
reagent_state = SOLID
|
||||
overdose_threshold = 20
|
||||
|
||||
/datum/reagent/drug/anaphrodisiacplus/on_mob_life(mob/living/M)
|
||||
if(M && M.canbearoused && prob(33))
|
||||
M.adjustArousalLoss(-4)
|
||||
..()
|
||||
|
||||
/datum/reagent/drug/anaphrodisiacplus/overdose_process(mob/living/M)
|
||||
if(M && M.canbearoused && prob(33))
|
||||
if(M.min_arousal > 0)
|
||||
M.min_arousal -= 1
|
||||
if(M.min_arousal > 50)
|
||||
M.min_arousal -= 1
|
||||
M.adjustArousalLoss(-2)
|
||||
..()
|
||||
|
||||
//recipes
|
||||
/datum/chemical_reaction/aphro
|
||||
name = "crocin"
|
||||
id = "aphro"
|
||||
results = list("aphro" = 6)
|
||||
required_reagents = list("carbon" = 2, "hydrogen" = 2, "oxygen" = 2, "water" = 1)
|
||||
required_temp = 400
|
||||
mix_message = "The mixture boils off a pink vapor..."//The water boils off, leaving the crocin
|
||||
|
||||
/datum/chemical_reaction/aphroplus
|
||||
name = "hexacrocin"
|
||||
id = "aphro+"
|
||||
results = list("aphro+" = 1)
|
||||
required_reagents = list("aphro" = 6, "phenol" = 1)
|
||||
required_temp = 400
|
||||
mix_message = "The mixture rapidly condenses and darkens in color..."
|
||||
|
||||
/datum/chemical_reaction/anaphro
|
||||
name = "camphor"
|
||||
id = "anaphro"
|
||||
results = list("anaphro" = 6)
|
||||
required_reagents = list("carbon" = 2, "hydrogen" = 2, "oxygen" = 2, "sulfur" = 1)
|
||||
required_temp = 400
|
||||
mix_message = "The mixture boils off a yellow, smelly vapor..."//Sulfur burns off, leaving the camphor
|
||||
|
||||
/datum/chemical_reaction/anaphroplus
|
||||
name = "pentacamphor"
|
||||
id = "anaphro+"
|
||||
results = list("anaphro+" = 1)
|
||||
required_reagents = list("anaphro" = 5, "acetone" = 1)
|
||||
required_temp = 300
|
||||
mix_message = "The mixture thickens and heats up slighty..."
|
||||
|
||||
//=========Drinks and Stuff!============//
|
||||
|
||||
/datum/reagent/consumable/ethanol/sake
|
||||
name = "Sake"
|
||||
id = "sake"
|
||||
description = "A sweet rice wine of questionable legality and extreme potency."
|
||||
color = "#DDDDDD"
|
||||
boozepwr = 70
|
||||
taste_description = "sweet rice wine"
|
||||
glass_icon_state = "sakecup"
|
||||
glass_name = "glass of sake"
|
||||
glass_desc = "A traditional cup of sake."
|
||||
|
||||
/datum/chemical_reaction/sake
|
||||
name = "sake"
|
||||
id = "sake"
|
||||
results = list("sake" = 20)
|
||||
required_reagents = list("rice" = 15, "sugar" = 5)
|
||||
required_temp = 400
|
||||
mix_message = "The rice grains ferment with the sugar into a clear, sweet-smelling liquid."
|
||||
|
||||
/obj/item/reagent_containers/food/drinks/bottle/sake
|
||||
name = "Traditional Sake"
|
||||
desc = "Sweet as can be, and burns like foxfire going down."
|
||||
icon = 'code/citadel/icons/drinks.dmi'
|
||||
icon_state = "sakebottle"
|
||||
list_reagents = list("sake" = 100)
|
||||
|
||||
/obj/item/reagent_containers/food/drinks/bottle/sake/Initialize()
|
||||
. = ..()
|
||||
if(prob(30))
|
||||
name = "Tetravulpine Sake"
|
||||
desc += " On the bottle is a picture of a kitsune with four tails."
|
||||
else if(prob(30))
|
||||
name = "Inubashiri's Home Brew"
|
||||
desc += " Awoo."
|
||||
@@ -1,7 +0,0 @@
|
||||
/obj/effect/spawner/lootdrop/keg
|
||||
name = "random keg spawner"
|
||||
lootcount = 1
|
||||
loot = list(/obj/structure/reagent_dispensers/keg/mead = 5,
|
||||
/obj/structure/reagent_dispensers/keg/aphro = 1,
|
||||
/obj/structure/reagent_dispensers/keg/aphro/strong = 1,
|
||||
/obj/structure/reagent_dispensers/keg/gargle = 1)
|
||||
@@ -1,40 +0,0 @@
|
||||
/obj/item/clothing/under/bb_sweater
|
||||
name = "cream sweater"
|
||||
desc = "Why trade style for comfort? Now you can go commando down south and still be cozy up north."
|
||||
icon_state = "bb_turtle"
|
||||
item_state = "w_suit"
|
||||
item_color = "bb_turtle"
|
||||
body_parts_covered = CHEST|ARMS
|
||||
can_adjust = 1
|
||||
icon = 'icons/obj/clothing/turtlenecks.dmi'
|
||||
icon_override = 'icons/mob/citadel/uniforms.dmi'
|
||||
|
||||
/obj/item/clothing/under/bb_sweater/black
|
||||
name = "black sweater"
|
||||
icon_state = "bb_turtleblk"
|
||||
item_state = "bl_suit"
|
||||
item_color = "bb_turtleblk"
|
||||
|
||||
/obj/item/clothing/under/bb_sweater/purple
|
||||
name = "purple sweater"
|
||||
icon_state = "bb_turtlepur"
|
||||
item_state = "p_suit"
|
||||
item_color = "bb_turtlepur"
|
||||
|
||||
/obj/item/clothing/under/bb_sweater/green
|
||||
name = "green sweater"
|
||||
icon_state = "bb_turtlegrn"
|
||||
item_state = "g_suit"
|
||||
item_color = "bb_turtlegrn"
|
||||
|
||||
/obj/item/clothing/under/bb_sweater/red
|
||||
name = "red sweater"
|
||||
icon_state = "bb_turtlered"
|
||||
item_state = "r_suit"
|
||||
item_color = "bb_turtlered"
|
||||
|
||||
/obj/item/clothing/under/bb_sweater/blue
|
||||
name = "blue sweater"
|
||||
icon_state = "bb_turtleblu"
|
||||
item_state = "b_suit"
|
||||
item_color = "bb_turtleblu"
|
||||
@@ -1,102 +0,0 @@
|
||||
#define STANDARD_CHARGE 1
|
||||
#define CONTRABAND_CHARGE 2
|
||||
#define COIN_CHARGE 3
|
||||
|
||||
/obj/machinery/vending/kink
|
||||
name = "KinkMate"
|
||||
desc = "A vending machine for all your unmentionable desires."
|
||||
icon = 'icons/obj/citvending.dmi'
|
||||
icon_state = "kink"
|
||||
product_slogans = "Kinky!;Sexy!;Check me out, big boy!"
|
||||
vend_reply = "Have fun, you shameless pervert!"
|
||||
products = list(
|
||||
/obj/item/clothing/under/maid = 5,
|
||||
/obj/item/clothing/under/stripper_pink = 5,
|
||||
/obj/item/clothing/under/stripper_green = 5,
|
||||
/obj/item/dildo/custom = 5
|
||||
)
|
||||
contraband = list(/obj/item/restraints/handcuffs/fake/kinky = 5,
|
||||
/obj/item/clothing/neck/petcollar = 5,
|
||||
/obj/item/clothing/under/mankini = 1,
|
||||
/obj/item/dildo/flared/huge = 1
|
||||
)
|
||||
premium = list(/obj/item/device/electropack/shockcollar = 1)
|
||||
refill_canister = /obj/item/vending_refill/kink
|
||||
/*
|
||||
/obj/machinery/vending/nazivend
|
||||
name = "Nazivend"
|
||||
desc = "A vending machine containing Nazi German supplies. A label reads: \"Remember the gorrilions lost.\""
|
||||
icon = 'icons/obj/citvending.dmi'
|
||||
icon_state = "nazi"
|
||||
vend_reply = "SIEG HEIL!"
|
||||
product_slogans = "Das Vierte Reich wird zuruckkehren!;ENTFERNEN JUDEN!;Billiger als die Juden jemals geben!;Rader auf dem adminbus geht rund und rund.;Warten Sie, warum wir wieder hassen Juden?- *BZZT*"
|
||||
products = list(
|
||||
/obj/item/clothing/head/stalhelm = 20,
|
||||
/obj/item/clothing/head/panzer = 20,
|
||||
/obj/item/clothing/suit/soldiercoat = 20,
|
||||
// /obj/item/clothing/under/soldieruniform = 20,
|
||||
/obj/item/clothing/shoes/jackboots = 20
|
||||
)
|
||||
contraband = list(
|
||||
/obj/item/clothing/head/naziofficer = 10,
|
||||
// /obj/item/clothing/suit/officercoat = 10,
|
||||
// /obj/item/clothing/under/officeruniform = 10,
|
||||
/obj/item/clothing/suit/space/hardsuit/nazi = 3,
|
||||
/obj/item/gun/energy/plasma/MP40k = 4
|
||||
)
|
||||
premium = list()
|
||||
|
||||
refill_canister = /obj/item/vending_refill/nazi
|
||||
*/
|
||||
/obj/machinery/vending/sovietvend
|
||||
name = "KomradeVendtink"
|
||||
desc = "Rodina-mat' zovyot!"
|
||||
icon = 'icons/obj/citvending.dmi'
|
||||
icon_state = "soviet"
|
||||
vend_reply = "The fascist and capitalist svin'ya shall fall, komrade!"
|
||||
product_slogans = "Quality worth waiting in line for!; Get Hammer and Sickled!; Sosvietsky soyuz above all!; With capitalist pigsky, you would have paid a fortunetink! ; Craftink in Motherland herself!"
|
||||
products = list(
|
||||
/obj/item/clothing/under/soviet = 20,
|
||||
/obj/item/clothing/head/ushanka = 20,
|
||||
/obj/item/clothing/shoes/jackboots = 20,
|
||||
/obj/item/clothing/head/squatter_hat = 20,
|
||||
/obj/item/clothing/under/squatter_outfit = 20,
|
||||
/obj/item/clothing/under/russobluecamooutfit = 20,
|
||||
/obj/item/clothing/head/russobluecamohat = 20
|
||||
)
|
||||
contraband = list(
|
||||
/obj/item/clothing/under/syndicate/tacticool = 4,
|
||||
/obj/item/clothing/mask/balaclava = 4,
|
||||
/obj/item/clothing/suit/russofurcoat = 4,
|
||||
/obj/item/clothing/head/russofurhat = 4,
|
||||
/obj/item/clothing/suit/space/hardsuit/soviet = 3,
|
||||
/obj/item/gun/energy/laser/LaserAK = 4
|
||||
)
|
||||
premium = list()
|
||||
|
||||
refill_canister = /obj/item/vending_refill/soviet
|
||||
|
||||
|
||||
#undef STANDARD_CHARGE
|
||||
#undef CONTRABAND_CHARGE
|
||||
#undef COIN_CHARGE
|
||||
|
||||
|
||||
/obj/item/vending_refill/kink
|
||||
machine_name = "KinkMate"
|
||||
icon = 'modular_citadel/icons/vending_restock.dmi'
|
||||
icon_state = "refill_kink"
|
||||
charges = list(8, 5, 0)// of 20 standard, 12 contraband, 0 premium
|
||||
init_charges = list(8, 5, 0)
|
||||
|
||||
/obj/item/vending_refill/nazi
|
||||
machine_name = "nazivend"
|
||||
icon_state = "refill_nazi"
|
||||
charges = list(33, 13, 0)
|
||||
init_charges = list(33, 13, 0)
|
||||
|
||||
/obj/item/vending_refill/soviet
|
||||
machine_name = "sovietvend"
|
||||
icon_state = "refill_soviet"
|
||||
charges = list(47, 7, 0)
|
||||
init_charges = list(47, 7, 0)
|
||||
@@ -1,81 +0,0 @@
|
||||
/* CARGO OBJECTIVES */
|
||||
|
||||
/datum/objective/crew/petsplosion
|
||||
explanation_text = "Ensure there are at least (If you see this, yell on citadels discord in the development discussion channel) pets on the station by the end of the shift. Interpret this as you wish."
|
||||
jobs = "quartermaster,cargotechnician"
|
||||
|
||||
/datum/objective/crew/petsplosion/New()
|
||||
. = ..()
|
||||
target_amount = rand(10,30)
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/crew/petsplosion/update_explanation_text()
|
||||
. = ..()
|
||||
explanation_text = "Ensure there are at least [target_amount] pets on the station by the end of the shift. Interpret this as you wish."
|
||||
|
||||
/datum/objective/crew/petsplosion/check_completion()
|
||||
var/petcount = target_amount
|
||||
for(var/mob/living/simple_animal/pet/P in GLOB.mob_list)
|
||||
if(!(P.stat == DEAD))
|
||||
if(P.z == SSmapping.station_start || SSshuttle.emergency.shuttle_areas[get_area(P)])
|
||||
petcount--
|
||||
for(var/mob/living/carbon/human/H in GLOB.mob_list)
|
||||
if(!(H.stat == DEAD))
|
||||
if(H.z == SSmapping.station_start || SSshuttle.emergency.shuttle_areas[get_area(H)])
|
||||
if(istype(H.wear_neck, /obj/item/clothing/neck/petcollar))
|
||||
petcount--
|
||||
if(petcount <= 0)
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/datum/objective/crew/points //ported from old hippie
|
||||
explanation_text = "Make sure the station has at least (Something broke, report this to the development discussion channel of citadels discord) supply points at the end of the shift."
|
||||
jobs = "quartermaster,cargotechnician"
|
||||
|
||||
/datum/objective/crew/points/New()
|
||||
. = ..()
|
||||
target_amount = rand(25000,100000)
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/crew/points/update_explanation_text()
|
||||
. = ..()
|
||||
explanation_text = "Make sure the station has at least [target_amount] supply points at the end of the shift."
|
||||
|
||||
/datum/objective/crew/points/check_completion()
|
||||
if(SSshuttle.points >= target_amount)
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/datum/objective/crew/bubblegum
|
||||
explanation_text = "Ensure Bubblegum is dead at the end of the shift."
|
||||
jobs = "shaftminer"
|
||||
|
||||
/datum/objective/crew/bubblegum/check_completion()
|
||||
for(var/mob/living/simple_animal/hostile/megafauna/bubblegum/B in GLOB.mob_list)
|
||||
if(!(B.stat == DEAD))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/objective/crew/fatstacks //ported from old hippie
|
||||
explanation_text = "Have at least (something broke, report this to the development discussion channel of citadels discord) mining points on your ID at the end of the shift."
|
||||
jobs = "shaftminer"
|
||||
|
||||
/datum/objective/crew/fatstacks/New()
|
||||
. = ..()
|
||||
target_amount = rand(15000,50000)
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/crew/fatstacks/update_explanation_text()
|
||||
. = ..()
|
||||
explanation_text = "Have at least [target_amount] mining points on your ID at the end of the shift."
|
||||
|
||||
/datum/objective/crew/fatstacks/check_completion()
|
||||
if(owner && owner.current)
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
var/obj/item/card/id/theID = H.get_idcard()
|
||||
if(istype(theID))
|
||||
if(theID.mining_points >= target_amount)
|
||||
return TRUE
|
||||
return FALSE
|
||||
@@ -1,249 +0,0 @@
|
||||
/* CIVILIAN OBJECTIVES */
|
||||
|
||||
/datum/objective/crew/druglordbot //ported from old Hippie with adjustments
|
||||
var/targetchem = "none"
|
||||
var/datum/reagent/chempath
|
||||
explanation_text = "Have at least (somethin broke here) harvested plants containing (report this on the development discussion channel of citadel's discord) when the shift ends."
|
||||
jobs = "botanist"
|
||||
|
||||
/datum/objective/crew/druglordbot/New()
|
||||
. = ..()
|
||||
target_amount = rand(3,20)
|
||||
var/blacklist = list(/datum/reagent/drug, /datum/reagent/drug/menthol, /datum/reagent/medicine, /datum/reagent/medicine/adminordrazine, /datum/reagent/medicine/adminordrazine/nanites, /datum/reagent/medicine/mine_salve, /datum/reagent/medicine/syndicate_nanites, /datum/reagent/medicine/strange_reagent, /datum/reagent/medicine/miningnanites, /datum/reagent/medicine/changelingadrenaline, /datum/reagent/medicine/changelinghaste)
|
||||
var/drugs = typesof(/datum/reagent/drug) - blacklist
|
||||
var/meds = typesof(/datum/reagent/medicine) - blacklist
|
||||
var/chemlist = drugs + meds
|
||||
chempath = pick(chemlist)
|
||||
targetchem = initial(chempath.id)
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/crew/druglordbot/update_explanation_text()
|
||||
. = ..()
|
||||
explanation_text = "Have at least [target_amount] harvested plants containing [initial(chempath.name)] when the shift ends."
|
||||
|
||||
/datum/objective/crew/druglordbot/check_completion()
|
||||
var/pillcount = target_amount
|
||||
if(owner && owner.current)
|
||||
if(owner.current.contents)
|
||||
for(var/obj/item/reagent_containers/food/snacks/grown/P in owner.current.get_contents())
|
||||
if(P.reagents.has_reagent(targetchem))
|
||||
pillcount--
|
||||
if(pillcount <= 0)
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/datum/objective/crew/foodhoard
|
||||
var/datum/crafting_recipe/food/targetfood
|
||||
var/obj/item/reagent_containers/food/foodpath
|
||||
explanation_text = "Personally deliver at least (yo something broke) (report this to the developer discussion channel in citadels discord)s to Centcom."
|
||||
jobs = "cook"
|
||||
|
||||
/datum/objective/crew/foodhoard/New()
|
||||
. = ..()
|
||||
target_amount = rand(2,10)
|
||||
var/blacklist = list(/datum/crafting_recipe/food, /datum/crafting_recipe/food/cak)
|
||||
var/possiblefoods = typesof(/datum/crafting_recipe/food) - blacklist
|
||||
targetfood = pick(possiblefoods)
|
||||
foodpath = initial(targetfood.result)
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/crew/foodhoard/update_explanation_text()
|
||||
. = ..()
|
||||
explanation_text = "Personally deliver at least [target_amount] [initial(foodpath.name)]s to Centcom."
|
||||
|
||||
/datum/objective/crew/foodhoard/check_completion()
|
||||
if(owner && owner.current && owner.current.check_contents_for(foodpath) && SSshuttle.emergency.shuttle_areas[get_area(owner.current)])
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/datum/objective/crew/responsibility
|
||||
explanation_text = "Make sure nobody dies with alcohol poisoning."
|
||||
jobs = "bartender"
|
||||
|
||||
/datum/objective/crew/responsibility/check_completion()
|
||||
for(var/mob/living/carbon/human/H in GLOB.mob_list)
|
||||
if(H.stat == DEAD && H.drunkenness >= 80)
|
||||
if(H.z == SSmapping.station_start || SSshuttle.emergency.shuttle_areas[get_area(H)])
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/objective/crew/clean //ported from old Hippie
|
||||
var/list/areas = list()
|
||||
var/hardmode = FALSE
|
||||
explanation_text = "Ensure sure that (Yo, something broke. Yell about this in citadels devlopmeent discussion channel.) remain spotless at the end of the shift."
|
||||
jobs = "janitor"
|
||||
|
||||
/datum/objective/crew/clean/New()
|
||||
. = ..()
|
||||
if(prob(1))
|
||||
hardmode = TRUE
|
||||
var/list/blacklistnormal = list(typesof(/area/space) - typesof(/area/lavaland) - typesof(/area/mine) - typesof(/area/ai_monitored/turret_protected) - typesof(/area/tcommsat))
|
||||
var/list/blacklisthard = list(typesof(/area/lavaland) - typesof(/area/mine))
|
||||
var/list/possibleareas = list()
|
||||
if(hardmode)
|
||||
possibleareas = GLOB.teleportlocs - /area - blacklisthard
|
||||
else
|
||||
possibleareas = GLOB.teleportlocs - /area - blacklistnormal
|
||||
for(var/i in 1 to rand(1,6))
|
||||
areas |= pick_n_take(possibleareas)
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/crew/clean/update_explanation_text()
|
||||
. = ..()
|
||||
explanation_text = "Ensure that the"
|
||||
for(var/i in 1 to areas.len)
|
||||
var/area/A = areas[i]
|
||||
explanation_text += " [A]"
|
||||
if(i != areas.len && areas.len >= 3)
|
||||
explanation_text += ","
|
||||
if(i == areas.len - 1)
|
||||
explanation_text += "and"
|
||||
explanation_text += " [(areas.len ==1) ? "is completely" : "are [(areas.len == 2) ? "completely" : "all"]"] clean at the end of the shift."
|
||||
if(hardmode)
|
||||
explanation_text += " Chop-chop."
|
||||
|
||||
/datum/objective/crew/clean/check_completion()
|
||||
for(var/area/A in areas)
|
||||
for(var/obj/effect/decal/cleanable/C in area_contents(A))
|
||||
if(C && C.alpha >= 150)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/objective/crew/slipster //ported from old Hippie with adjustments
|
||||
explanation_text = "Slip at least (Yell on citadel's development discussion channel if you see this) different people with your PDA, and have it on you at the end of the shift."
|
||||
jobs = "clown"
|
||||
|
||||
/datum/objective/crew/slipster/New()
|
||||
. = ..()
|
||||
target_amount = rand(5, 20)
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/crew/slipster/update_explanation_text()
|
||||
. = ..()
|
||||
explanation_text = "Slip at least [target_amount] different people with your PDA, and have it on you at the end of the shift."
|
||||
|
||||
/datum/objective/crew/slipster/check_completion()
|
||||
var/list/uniqueslips = list()
|
||||
if(owner && owner.current)
|
||||
for(var/obj/item/device/pda/clown/PDA in owner.current.get_contents())
|
||||
for(var/mob/living/carbon/human/H in PDA.slipvictims)
|
||||
uniqueslips |= H
|
||||
if(uniqueslips.len >= target_amount)
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/datum/objective/crew/vow //ported from old Hippie
|
||||
explanation_text = "Never break your vow of silence."
|
||||
jobs = "mime"
|
||||
|
||||
/datum/objective/crew/vow/check_completion()
|
||||
if(owner && owner.current)
|
||||
var/list/say_log = owner.current.logging[INDIVIDUAL_SAY_LOG]
|
||||
if(say_log.len > 0)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/objective/crew/nullrod
|
||||
explanation_text = "Don't lose your holy rod."
|
||||
jobs = "chaplain"
|
||||
|
||||
/datum/objective/crew/nullrod/check_completion()
|
||||
if(owner && owner.current)
|
||||
for(var/nullrodtypes in typesof(/obj/item/nullrod))
|
||||
if(owner.current.check_contents_for(nullrodtypes))
|
||||
return TRUE
|
||||
if(owner.current.getorgan(/obj/item/organ/genital/penis))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/objective/crew/reporter //ported from old hippie
|
||||
var/charcount = 100
|
||||
explanation_text = "Publish at least (Yo something broke) articles containing at least (Report this to Citadels development channel) characters."
|
||||
jobs = "curator"
|
||||
|
||||
/datum/objective/crew/reporter/New()
|
||||
. = ..()
|
||||
target_amount = rand(2,10)
|
||||
charcount = rand(20,250)
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/crew/reporter/update_explanation_text()
|
||||
. = ..()
|
||||
explanation_text = "Publish at least [target_amount] articles containing at least [charcount] characters."
|
||||
|
||||
/datum/objective/crew/reporter/check_completion()
|
||||
if(owner && owner.current)
|
||||
var/ownername = "[ckey(owner.current.real_name)][ckey(owner.assigned_role)]"
|
||||
for(var/datum/newscaster/feed_channel/chan in GLOB.news_network.network_channels)
|
||||
for(var/datum/newscaster/feed_message/msg in chan.messages)
|
||||
if(ckey(msg.returnAuthor()) == ckey(ownername))
|
||||
if(length(msg.returnBody()) >= charcount)
|
||||
target_amount--
|
||||
if(target_amount <= 0)
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/datum/objective/crew/pwrgame //ported from Goon with adjustments
|
||||
var/obj/item/clothing/targettidegarb
|
||||
explanation_text = "Get your grubby hands on a (Dear god something broke. Report this to Citadel's development dicussion channel)."
|
||||
jobs = "assistant"
|
||||
|
||||
/datum/objective/crew/pwrgame/New()
|
||||
. = ..()
|
||||
var/list/muhvalids = list(/obj/item/clothing/mask/gas, /obj/item/clothing/head/welding, /obj/item/clothing/head/ushanka, /obj/item/clothing/gloves/color/yellow, /obj/item/clothing/mask/gas/owl_mask)
|
||||
if(prob(10))
|
||||
muhvalids += list(/obj/item/clothing/suit/space)
|
||||
targettidegarb = pick(muhvalids)
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/crew/pwrgame/update_explanation_text()
|
||||
. = ..()
|
||||
explanation_text = "Get your grubby hands on a [initial(targettidegarb.name)]."
|
||||
/* DM is not a sane language in any way, shape, or form. If anyone wants to try to get this bit functioning proper, I hold no responsibility for broken keyboards.
|
||||
if(owner && owner.current)
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
if(H && H.dna && H.dna.species && H.dna.species.id)
|
||||
explanation_text = "Get your "
|
||||
if(H.dna.species.id == "avian")
|
||||
explanation_text += "scratchy claws "
|
||||
else if(H.dna.species.id == "mammal")
|
||||
explanation_text += "dirty paws "
|
||||
else if(H.dna.species.id == "aquatic")
|
||||
explanation_text += "fishy hands "
|
||||
else if(H.dna.species.id == "xeno")
|
||||
explanation_text += "weird claws "
|
||||
else if(H.dna.species.id == "guilmon")
|
||||
explanation_text += "digital claws "
|
||||
else if(H.dna.species.id == "lizard")
|
||||
explanation_text += "slimy claws "
|
||||
else if(H.dna.species.id == "datashark")
|
||||
explanation_text += "glitchy hands "
|
||||
else if(H.dna.species.id == "insect")
|
||||
explanation_text += "gross grabbers "
|
||||
else
|
||||
explanation_text += "grubby hands "
|
||||
explanation_text += "on a space suit." replace this if you're making this monstrosity work */
|
||||
|
||||
/datum/objective/crew/pwrgame/check_completion()
|
||||
if(owner && owner.current)
|
||||
for(var/tidegarbtypes in typesof(targettidegarb))
|
||||
if(owner.current.check_contents_for(tidegarbtypes))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/objective/crew/promotion //ported from Goon
|
||||
explanation_text = "Have a non-assistant ID registered to you at the end of the shift."
|
||||
jobs = "assistant"
|
||||
|
||||
/datum/objective/crew/promotion/check_completion()
|
||||
if(owner && owner.current)
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
var/obj/item/card/id/theID = H.get_idcard()
|
||||
if(istype(theID))
|
||||
if(!(H.get_assignment() == "Assistant") && !(H.get_assignment() == "No id") && !(H.get_assignment() == "No job"))
|
||||
return TRUE
|
||||
return FALSE
|
||||
@@ -1,33 +0,0 @@
|
||||
/* COMMAND OBJECTIVES */
|
||||
|
||||
/datum/objective/crew/caphat //Ported from Goon
|
||||
explanation_text = "Don't lose your hat."
|
||||
jobs = "captain"
|
||||
|
||||
/datum/objective/crew/caphat/check_completion()
|
||||
if(owner && owner.current && owner.current.check_contents_for(/obj/item/clothing/head/caphat))
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/datum/objective/crew/datfukkendisk //Ported from old Hippie
|
||||
explanation_text = "Defend the nuclear authentication disk at all costs, and be the one to personally deliver it to Centcom."
|
||||
jobs = "captain" //give this to other heads at your own risk.
|
||||
|
||||
/datum/objective/crew/datfukkendisk/check_completion()
|
||||
if(owner && owner.current && owner.current.check_contents_for(/obj/item/disk/nuclear) && SSshuttle.emergency.shuttle_areas[get_area(owner.current)])
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/datum/objective/crew/ian //Ported from old Hippie
|
||||
explanation_text = "Defend Ian at all costs, and ensure he gets delivered to Centcom at the end of the shift."
|
||||
jobs = "headofpersonnel"
|
||||
|
||||
/datum/objective/crew/ian/check_completion()
|
||||
if(owner && owner.current)
|
||||
for(var/mob/living/simple_animal/pet/dog/corgi/Ian/goodboy in GLOB.mob_list)
|
||||
if(goodboy.stat != DEAD && SSshuttle.emergency.shuttle_areas[get_area(goodboy)])
|
||||
return TRUE
|
||||
return FALSE
|
||||
return FALSE
|
||||
@@ -1,34 +0,0 @@
|
||||
/* ENGINEERING OBJECTIVES */
|
||||
|
||||
/datum/objective/crew/integrity //ported from old Hippie
|
||||
explanation_text = "Ensure the station's integrity rating is at least (Yo something broke, yell on the development discussion channel of citadels discord about this)% when the shift ends."
|
||||
jobs = "chiefengineer,stationengineer"
|
||||
|
||||
/datum/objective/crew/integrity/New()
|
||||
. = ..()
|
||||
target_amount = rand(60,95)
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/crew/integrity/update_explanation_text()
|
||||
. = ..()
|
||||
explanation_text = "Ensure the station's integrity rating is at least [target_amount]% when the shift ends."
|
||||
|
||||
/datum/objective/crew/integrity/check_completion()
|
||||
var/datum/station_state/end_state = new /datum/station_state()
|
||||
end_state.count()
|
||||
var/station_integrity = min(PERCENT(GLOB.start_state.score(end_state)), 100)
|
||||
if(!SSticker.mode.station_was_nuked && station_integrity >= target_amount)
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/datum/objective/crew/poly
|
||||
explanation_text = "Make sure Poly keeps his headset, and stays alive until the end of the shift."
|
||||
jobs = "chiefengineer"
|
||||
|
||||
/datum/objective/crew/poly/check_completion()
|
||||
for(var/mob/living/simple_animal/parrot/Poly/dumbbird in GLOB.mob_list)
|
||||
if(!(dumbbird.stat == DEAD) && dumbbird.ears)
|
||||
if(istype(dumbbird.ears, /obj/item/device/radio/headset))
|
||||
return TRUE
|
||||
return FALSE
|
||||
@@ -1,86 +0,0 @@
|
||||
/* MEDICAL OBJECTIVES */
|
||||
|
||||
/datum/objective/crew/morgue //Ported from old Hippie
|
||||
explanation_text = "Ensure there are no corpses on the station outside of the morgue when the shift ends."
|
||||
jobs = "chiefmedicalofficer,geneticist,medicaldoctor"
|
||||
|
||||
/datum/objective/crew/morgue/check_completion()
|
||||
for(var/mob/living/carbon/human/H in GLOB.mob_list)
|
||||
if(H.stat == DEAD && H.z == SSmapping.station_start)
|
||||
if(get_area(H) != /area/medical/morgue)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/objective/crew/chems //Ported from old Hippie
|
||||
var/targetchem = "none"
|
||||
var/datum/reagent/chempath
|
||||
explanation_text = "Have (yell about this in the development discussion channel of citadel's discord, something broke) in your bloodstream when the shift ends."
|
||||
jobs = "chiefmedicalofficer,chemist"
|
||||
|
||||
/datum/objective/crew/chems/New()
|
||||
. = ..()
|
||||
var/blacklist = list(/datum/reagent/drug, /datum/reagent/drug/nicotine, /datum/reagent/drug/menthol, /datum/reagent/medicine, /datum/reagent/medicine/adminordrazine, /datum/reagent/medicine/adminordrazine/nanites, /datum/reagent/medicine/mine_salve, /datum/reagent/medicine/omnizine, /datum/reagent/medicine/syndicate_nanites, /datum/reagent/medicine/earthsblood, /datum/reagent/medicine/strange_reagent, /datum/reagent/medicine/miningnanites, /datum/reagent/medicine/changelingadrenaline, /datum/reagent/medicine/changelinghaste)
|
||||
var/drugs = typesof(/datum/reagent/drug) - blacklist
|
||||
var/meds = typesof(/datum/reagent/medicine) - blacklist
|
||||
var/chemlist = drugs + meds
|
||||
chempath = pick(chemlist)
|
||||
targetchem = initial(chempath.id)
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/crew/chems/update_explanation_text()
|
||||
. = ..()
|
||||
explanation_text = "Have [initial(chempath.name)] in your bloodstream when the shift ends."
|
||||
|
||||
/datum/objective/crew/chems/check_completion()
|
||||
if(owner.current)
|
||||
if(!owner.current.stat == DEAD && owner.current.reagents)
|
||||
if(owner.current.reagents.has_reagent(targetchem))
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/datum/objective/crew/druglordchem //ported from old Hippie with adjustments
|
||||
var/targetchem = "none"
|
||||
var/datum/reagent/chempath
|
||||
var/chemamount = 0
|
||||
explanation_text = "Have at least (somethin broke here) pills containing at least (like really broke) units of(report this on the development discussion channel of citadel's discord) when the shift ends."
|
||||
jobs = "chemist"
|
||||
|
||||
/datum/objective/crew/druglordchem/New()
|
||||
. = ..()
|
||||
target_amount = rand(5,50)
|
||||
chemamount = rand(1,20)
|
||||
var/blacklist = list(/datum/reagent/drug, /datum/reagent/drug/nicotine, /datum/reagent/drug/menthol)
|
||||
var/drugs = typesof(/datum/reagent/drug) - blacklist
|
||||
var/chemlist = drugs
|
||||
chempath = pick(chemlist)
|
||||
targetchem = initial(chempath.id)
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/crew/druglordchem/update_explanation_text()
|
||||
. = ..()
|
||||
explanation_text = "Have at least [target_amount] pills containing at least [chemamount] units of [initial(chempath.name)] when the shift ends."
|
||||
|
||||
/datum/objective/crew/druglordchem/check_completion()
|
||||
var/pillcount = target_amount
|
||||
if(owner.current)
|
||||
if(owner.current.contents)
|
||||
for(var/obj/item/reagent_containers/pill/P in owner.current.get_contents())
|
||||
if(P.reagents.has_reagent(targetchem, chemamount))
|
||||
pillcount--
|
||||
if(pillcount <= 0)
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/datum/objective/crew/noinfections
|
||||
explanation_text = "Make sure there are no crew members with harmful diseases at the end of the shift."
|
||||
jobs = "virologist"
|
||||
|
||||
/datum/objective/crew/noinfections/check_completion()
|
||||
for(var/mob/living/carbon/human/H in GLOB.mob_list)
|
||||
if(!H.stat == DEAD)
|
||||
if(H.z == SSmapping.station_start || SSshuttle.emergency.shuttle_areas[get_area(H)])
|
||||
if(H.check_virus() == 2)
|
||||
return FALSE
|
||||
return TRUE
|
||||
@@ -1,45 +0,0 @@
|
||||
/* SCIENCE OBJECTIVES */
|
||||
|
||||
/datum/objective/crew/cyborgs //Ported from old Hippie
|
||||
explanation_text = "Ensure there are at least (Yo something broke here, yell on citadel's development discussion channel about this) functioning cyborgs when the shift ends."
|
||||
jobs = "researchdirector,roboticist"
|
||||
|
||||
/datum/objective/crew/cyborgs/New()
|
||||
. = ..()
|
||||
target_amount = rand(3,10)
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/crew/cyborgs/update_explanation_text()
|
||||
. = ..()
|
||||
explanation_text = "Ensure there are at least [target_amount] functioning cyborgs when the shift ends."
|
||||
|
||||
/datum/objective/crew/cyborgs/check_completion()
|
||||
var/borgcount = target_amount
|
||||
for(var/mob/living/silicon/robot/R in GLOB.alive_mob_list)
|
||||
if(!(R.stat == DEAD))
|
||||
borgcount--
|
||||
if(borgcount <= 0)
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/datum/objective/crew/research //inspired by old hippie's research level objective. should hopefully be compatible with techwebs when that gets finished. hopefully. should be easy to update in the event that it is incompatible with techwebs.
|
||||
var/datum/design/targetdesign
|
||||
explanation_text = "Make sure the research required to produce a (something broke, yell on citadel's development discussion channel about this) is available on the R&D server by the end of the shift."
|
||||
jobs = "researchdirector,scientist"
|
||||
|
||||
/datum/objective/crew/research/New()
|
||||
. = ..()
|
||||
targetdesign = pick(subtypesof(/datum/design))
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/crew/research/update_explanation_text()
|
||||
. = ..()
|
||||
explanation_text = "Make sure the research required to produce a [initial(targetdesign.name)] is available on the R&D server by the end of the shift."
|
||||
|
||||
/datum/objective/crew/research/check_completion()
|
||||
for(var/obj/machinery/rnd/server/S in GLOB.machines)
|
||||
if(S && S.stored_research)
|
||||
if(S.stored_research.researched_designs[initial(targetdesign.id)])
|
||||
return TRUE
|
||||
return FALSE
|
||||
@@ -1,23 +0,0 @@
|
||||
/* SECURITY OBJECTIVES */
|
||||
|
||||
/datum/objective/crew/enjoyyourstay
|
||||
explanation_text = "Enforce Space Law to the best of your ability."
|
||||
jobs = "headofsecurity,securityofficer,warden,detective"
|
||||
|
||||
/datum/objective/crew/enjoyyourstay/check_completion()
|
||||
if(owner && owner.current)
|
||||
if(owner.current.stat != DEAD)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/objective/crew/justicecrew
|
||||
explanation_text = "Ensure there are no innocent crew members in the brig when the shift ends."
|
||||
jobs = "lawyer"
|
||||
|
||||
/datum/objective/crew/justicecrew/check_completion()
|
||||
if(owner && owner.current)
|
||||
for(var/datum/mind/M in SSticker.minds)
|
||||
if(M.current && isliving(M.current))
|
||||
if(!M.special_role && !(M.assigned_role == "Security Officer") && !(M.assigned_role == "Detective") && !(M.assigned_role == "Head of Security") && !(M.assigned_role == "Lawyer") && !(M.assigned_role == "Warden") && get_area(M.current) != typesof(/area/security))
|
||||
return FALSE
|
||||
return TRUE
|
||||
@@ -1,306 +0,0 @@
|
||||
|
||||
//For custom items.
|
||||
|
||||
/obj/item/custom/ceb_soap
|
||||
name = "Cebutris' Soap"
|
||||
desc = "A generic bar of soap that doesn't really seem to work right."
|
||||
gender = PLURAL
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_state = "cebu"
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
flags_1 = NOBLUDGEON_1
|
||||
|
||||
/obj/item/soap/cebu //real versions, for admin shenanigans. Adminspawn only
|
||||
desc = "A bright blue bar of soap that smells of wolves"
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_state = "cebu"
|
||||
|
||||
/obj/item/soap/cebu/fast //speedyquick cleaning version. Still not as fast as Syndiesoap. Adminspawn only.
|
||||
cleanspeed = 15
|
||||
|
||||
|
||||
/*Inferno707*/
|
||||
|
||||
/obj/item/clothing/neck/cloak/inferno
|
||||
name = "Kiara's Cloak"
|
||||
desc = "The design on this seems a little too familiar."
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_state = "infcloak"
|
||||
icon_override = 'icons/mob/custom_w.dmi'
|
||||
item_state = "infcloak"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
body_parts_covered = CHEST|GROIN|LEGS|ARMS
|
||||
|
||||
/obj/item/clothing/neck/petcollar/inferno
|
||||
name = "Kiara's Collar"
|
||||
desc = "A soft black collar that seems to stretch to fit whoever wears it."
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_state = "infcollar"
|
||||
icon_override = 'icons/mob/custom_w.dmi'
|
||||
item_state = "infcollar"
|
||||
item_color = null
|
||||
tagname = null
|
||||
|
||||
/obj/item/clothing/accessory/medal/steele
|
||||
name = "Insignia Of Steele"
|
||||
desc = "An intricate pendant given to those who help a key member of the Steele Corporation."
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_state = "steele"
|
||||
item_color = "steele"
|
||||
medaltype = "medal-silver"
|
||||
|
||||
/*DirtyOldHarry*/
|
||||
|
||||
/obj/item/lighter/gold
|
||||
name = "\improper Engraved Zippo"
|
||||
desc = "A shiny and relatively expensive zippo lighter. There's a small etched in verse on the bottom that reads, 'No Gods, No Masters, Only Man.'"
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_state = "gold_zippo"
|
||||
item_state = "gold_zippo"
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
flags_1 = CONDUCT_1
|
||||
slot_flags = SLOT_BELT
|
||||
heat = 1500
|
||||
resistance_flags = FIRE_PROOF
|
||||
light_color = LIGHT_COLOR_FIRE
|
||||
|
||||
|
||||
/*Zombierobin*/
|
||||
|
||||
/obj/item/clothing/neck/scarf/zomb //Default white color, same functionality as beanies.
|
||||
name = "A special scarf"
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_state = "zombscarf"
|
||||
desc = "A fashionable collar"
|
||||
icon_override = 'icons/mob/custom_w.dmi'
|
||||
item_color = "zombscarf"
|
||||
dog_fashion = /datum/dog_fashion/head
|
||||
|
||||
/obj/item/clothing/suit/toggle/labcoat/mad/red
|
||||
name = "\improper The Mad's labcoat"
|
||||
desc = "An oddly special looking coat."
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_state = "labred"
|
||||
icon_override = 'icons/mob/custom_w.dmi'
|
||||
item_state = "labred"
|
||||
|
||||
|
||||
/*Improvedname*/
|
||||
|
||||
/obj/item/toy/plush/carrot
|
||||
name = "carrot plushie"
|
||||
desc = "While a normal carrot would be good for your eyes, this one seems a bit more for hugging then eating."
|
||||
icon = 'icons/obj/hydroponics/harvest.dmi'
|
||||
icon_state = "carrot"
|
||||
item_state = "carrot"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
attack_verb = list("slapped")
|
||||
resistance_flags = FLAMMABLE
|
||||
squeak_override = list('sound/items/bikehorn.ogg'= 1)
|
||||
|
||||
/obj/item/clothing/neck/cloak/carrot
|
||||
name = "carrot cloak"
|
||||
desc = "A cloak in the shape and color of a carrot!"
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_override = 'icons/mob/custom_w.dmi'
|
||||
icon_state = "carrotcloak"
|
||||
item_state = "carrotcloak"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
body_parts_covered = CHEST|GROIN|LEGS|ARMS
|
||||
|
||||
/obj/item/storage/backpack/satchel/carrot
|
||||
name = "carrot satchel"
|
||||
desc = "An satchel that is designed to look like an carrot"
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_state = "satchel_carrot"
|
||||
item_state = "satchel_carrot"
|
||||
icon_override = 'icons/mob/custom_w.dmi'
|
||||
|
||||
/obj/item/storage/backpack/satchel/carrot/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/squeak, list('sound/items/toysqueak1.ogg'=1), 50)
|
||||
|
||||
/*PLACEHOLDER*/
|
||||
|
||||
/obj/item/toy/plush/tree
|
||||
name = "christmass tree plushie"
|
||||
desc = "A festive plush that squeeks when you squeeze it!"
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_state = "pine_c"
|
||||
item_state = "pine_c"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
attack_verb = list("slapped")
|
||||
resistance_flags = FLAMMABLE
|
||||
squeak_override = list('sound/misc/server-ready.ogg'= 1)
|
||||
|
||||
/obj/item/clothing/neck/cloak/festive
|
||||
name = "Celebratory Cloak of Morozko"
|
||||
desc = " It probably will protect from snow, charcoal or elves."
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_state = "festive"
|
||||
item_state = "festive"
|
||||
icon_override = 'icons/mob/custom_w.dmi'
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
body_parts_covered = CHEST|GROIN|LEGS|ARMS
|
||||
|
||||
|
||||
/*Zigfie*/
|
||||
|
||||
/obj/item/clothing/mask/luchador/zigfie
|
||||
name = "Alboroto Rosa mask"
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_state = "lucharzigfie"
|
||||
icon_override = 'icons/mob/custom_w.dmi'
|
||||
item_state = "lucharzigfie"
|
||||
|
||||
|
||||
/*PLACEHOLDER*/
|
||||
|
||||
/obj/item/clothing/head/hardhat/reindeer/fluff
|
||||
name = "novelty reindeer hat"
|
||||
desc = "Some fake antlers and a very fake red nose - Sponsored by PWR Game(tm)"
|
||||
icon_state = "hardhat0_reindeer"
|
||||
item_state = "hardhat0_reindeer"
|
||||
item_color = "reindeer"
|
||||
flags_inv = 0
|
||||
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0)
|
||||
brightness_on = 0 //luminosity when on
|
||||
dynamic_hair_suffix = ""
|
||||
|
||||
/obj/item/clothing/head/santa/fluff
|
||||
name = "santa's hat"
|
||||
desc = "On the first day of christmas my employer gave to me! - From Vlad with Salad"
|
||||
icon_state = "santahatnorm"
|
||||
item_state = "that"
|
||||
dog_fashion = /datum/dog_fashion/head/santa
|
||||
|
||||
//Removed all of the space flags from this suit so it utilizes nothing special.
|
||||
/obj/item/clothing/suit/space/santa/fluff
|
||||
name = "Santa's suit"
|
||||
desc = "Festive!"
|
||||
icon_state = "santa"
|
||||
item_state = "santa"
|
||||
slowdown = 0
|
||||
|
||||
/obj/item/clothing/mask/sexymime
|
||||
name = "The Hollow heart"
|
||||
desc = "Sometimes things are too much to hide."
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_override = 'icons/mob/custom_w.dmi'
|
||||
icon_state = "sexymime"
|
||||
item_state = "sexymime"
|
||||
flags_inv = HIDEFACE|HIDEFACIALHAIR
|
||||
|
||||
|
||||
/*Brian*/
|
||||
|
||||
/obj/item/clothing/suit/trenchcoat/green
|
||||
name = "Reece's Great Coat"
|
||||
desc = "You would swear this was in your nightmares after eating too many veggies."
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_state = "hos-g"
|
||||
icon_override = 'icons/mob/custom_w.dmi'
|
||||
item_state = "hos-g"
|
||||
body_parts_covered = CHEST|GROIN|ARMS|LEGS
|
||||
|
||||
|
||||
/*Slomek*/
|
||||
/obj/item/reagent_containers/food/drinks/flask/russian
|
||||
name = "russian flask"
|
||||
desc = "Every good russian spaceman knows it's a good idea to bring along a couple of pints of whiskey wherever they go."
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_state = "russianflask"
|
||||
volume = 60
|
||||
|
||||
/obj/item/clothing/mask/gas/stalker
|
||||
name = "S.T.A.L.K.E.R. mask"
|
||||
desc = "Smells like reactor four."
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
item_state = "stalker"
|
||||
icon_override = 'icons/mob/custom_w.dmi'
|
||||
icon_state = "stalker"
|
||||
|
||||
|
||||
/*Sylas*/
|
||||
/obj/item/clothing/neck/petcollar/stripe //don't really wear this though please c'mon seriously guys
|
||||
name = "collar"
|
||||
desc = "It's a collar..."
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_state = "petcollar-stripe"
|
||||
icon_override = 'icons/mob/custom_w.dmi'
|
||||
item_state = "petcollar-stripe"
|
||||
tagname = null
|
||||
|
||||
|
||||
/*PLACEHOLDER*/
|
||||
/obj/item/clothing/under/singery/custom
|
||||
name = "bluish performer's outfit"
|
||||
desc = "Just looking at this makes you want to sing."
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_state = "singer"
|
||||
icon_override = 'icons/mob/custom_w.dmi'
|
||||
item_state = "singer"
|
||||
item_color = "singer"
|
||||
fitted = NO_FEMALE_UNIFORM
|
||||
alternate_worn_layer = ABOVE_SHOES_LAYER
|
||||
can_adjust = 0
|
||||
|
||||
|
||||
/obj/item/clothing/shoes/sneakers/pink
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_state = "pink"
|
||||
icon_override = 'icons/mob/custom_w.dmi'
|
||||
item_state = "pink"
|
||||
|
||||
/obj/item/clothing/neck/tie/bloodred
|
||||
name = "Blood Red Tie"
|
||||
desc = "A neosilk clip-on tie. This one has a black S on the tipping and looks rather unique."
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_state = "bloodredtie"
|
||||
icon_override = 'icons/mob/custom_w.dmi'
|
||||
|
||||
|
||||
/*Fractious*/
|
||||
|
||||
/obj/item/clothing/suit/vermillion
|
||||
name = "vermillion clothing"
|
||||
desc = "Some clothing."
|
||||
icon_state = "vermillion"
|
||||
item_state = "vermillion"
|
||||
body_parts_covered = CHEST|GROIN|LEGS|ARMS|HANDS
|
||||
icon = 'icons/obj/custom.dmi'
|
||||
icon_override = 'icons/mob/custom_w.dmi'
|
||||
|
||||
/*TechnicalMagi*/
|
||||
/obj/item/clothing/under/bb_sweater/black/naomi
|
||||
name = "worn black sweater"
|
||||
desc = "A well-loved sweater, showing signs of several cleanings and re-stitchings. And a few stains. Is that cat fur?"
|
||||
|
||||
/obj/item/clothing/neck/petcollar/naomi
|
||||
name = "worn pet collar"
|
||||
desc = "a pet collar that looks well used."
|
||||
|
||||
/obj/item/clothing/neck/petcollar/naomi/examine(mob/user)
|
||||
. = ..()
|
||||
if(usr.ckey != "technicalmagi")
|
||||
to_chat(user, "There's something odd about the it. You probably shouldn't wear it...")//warn people not to wear it if they're not Naomi, lest they become as crazy as she is
|
||||
|
||||
/obj/item/clothing/neck/petcollar/naomi/equipped()
|
||||
. = ..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/clothing/neck/petcollar/naomi/dropped()
|
||||
. = ..()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/clothing/neck/petcollar/naomi/process()
|
||||
var/mob/living/carbon/human/H
|
||||
if(ishuman(loc))
|
||||
H = loc
|
||||
if(!H)
|
||||
return
|
||||
else if(H.get_item_by_slot(slot_neck) == src)
|
||||
if(H.arousalloss < H.max_arousal / 3)
|
||||
H.arousalloss = H.max_arousal / 3
|
||||
if(prob(5) && H.hallucination < 15)
|
||||
H.hallucination += 10
|
||||
@@ -1,61 +0,0 @@
|
||||
|
||||
//Proc that does the actual loading of items to mob
|
||||
/*Itemlists are formatted as
|
||||
"[typepath]" = number_of_it_to_spawn
|
||||
*/
|
||||
|
||||
#define DROP_TO_FLOOR 0
|
||||
#define LOADING_TO_HUMAN 1
|
||||
|
||||
/proc/handle_roundstart_items(mob/living/M, ckey_override, job_override, special_override)
|
||||
if(!istype(M) || (!M.ckey && !ckey_override) || (!M.mind && (!job_override || !special_override)))
|
||||
return FALSE
|
||||
return load_itemlist_to_mob(M, parse_custom_roundstart_items(ckey_override? ckey_override : M.ckey, M.name, job_override? job_override : M.mind.assigned_role, special_override? special_override : M.mind.special_role), TRUE, TRUE, FALSE)
|
||||
|
||||
//Just incase there's extra mob selections in the future.....
|
||||
/proc/load_itemlist_to_mob(mob/living/L, list/itemlist, drop_on_floor_if_full = TRUE, load_to_all_slots = TRUE, replace_slots = FALSE)
|
||||
if(!istype(L) || !islist(itemlist))
|
||||
return FALSE
|
||||
var/loading_mode = DROP_TO_FLOOR
|
||||
var/turf/current_turf = get_turf(L)
|
||||
if(ishuman(L))
|
||||
loading_mode = LOADING_TO_HUMAN
|
||||
switch(loading_mode)
|
||||
if(DROP_TO_FLOOR)
|
||||
for(var/I in itemlist)
|
||||
var/typepath = text2path(I)
|
||||
if(!typepath)
|
||||
continue
|
||||
for(var/i = 0, i < itemlist[I], i++)
|
||||
new typepath(current_turf)
|
||||
return TRUE
|
||||
if(LOADING_TO_HUMAN)
|
||||
return load_itemlist_to_human(L, itemlist, drop_on_floor_if_full, load_to_all_slots, replace_slots)
|
||||
|
||||
/proc/load_itemlist_to_human(mob/living/carbon/human/H, list/itemlist, drop_on_floor_if_full = TRUE, load_to_all_slots = TRUE, replace_slots = FALSE)
|
||||
if(!istype(H) || !islist(itemlist))
|
||||
return FALSE
|
||||
var/turf/T = get_turf(H)
|
||||
for(var/item in itemlist)
|
||||
var/path = item
|
||||
if(!ispath(path))
|
||||
path = text2path(path)
|
||||
if(!path)
|
||||
continue
|
||||
var/amount = itemlist[item]
|
||||
for(var/i in 1 to amount)
|
||||
var/atom/movable/loaded_atom = new path
|
||||
if(!istype(loaded_atom))
|
||||
QDEL_NULL(loaded_atom)
|
||||
continue
|
||||
if(!istype(loaded_atom, /obj/item))
|
||||
loaded_atom.forceMove(T)
|
||||
continue
|
||||
var/obj/item/loaded = loaded_atom
|
||||
var/obj/item/storage/S = H.get_item_by_slot(slot_back)
|
||||
if(istype(S))
|
||||
S.handle_item_insertion(loaded, TRUE, H) //Force it into their backpack
|
||||
continue
|
||||
if(!H.put_in_hands(loaded)) //They don't have one/somehow that failed, put it in their hands
|
||||
loaded.forceMove(T) //Guess we're just dumping it on the floor!
|
||||
return TRUE
|
||||
@@ -1,71 +0,0 @@
|
||||
|
||||
GLOBAL_LIST(custom_item_list)
|
||||
//Layered list in form of custom_item_list[ckey][job][items][amounts]
|
||||
//ckey is key, job is specific jobs, or "ALL" for all jobs, items for items, amounts for amount of item.
|
||||
|
||||
//File should be in the format of ckey|exact job name/exact job name/or put ALL instead of any job names|/path/to/item=amount;/path/to/item=amount
|
||||
//Each ckey should be in a different line
|
||||
//if there's multiple entries of a single ckey the later ones will add to the earlier definitions.
|
||||
|
||||
/proc/reload_custom_roundstart_items_list(custom_filelist)
|
||||
if(!custom_filelist)
|
||||
custom_filelist = "config/custom_roundstart_items.txt"
|
||||
GLOB.custom_item_list = list()
|
||||
var/list/file_lines = world.file2list(custom_filelist)
|
||||
for(var/line in file_lines)
|
||||
if(length(line) == 0) //Emptyline, no one cares.
|
||||
continue
|
||||
if(copytext(line,1,3) == "//") //Commented line, ignore.
|
||||
continue
|
||||
var/ckey_str_sep = findtext(line, "|") //Process our stuff..
|
||||
var/char_str_sep = findtext(line, "|", ckey_str_sep+1)
|
||||
var/job_str_sep = findtext(line, "|", char_str_sep+1)
|
||||
var/item_str_sep = findtext(line, "|", job_str_sep+1)
|
||||
var/ckey_str = ckey(copytext(line, 1, ckey_str_sep))
|
||||
var/char_str = copytext(line, ckey_str_sep+1, char_str_sep)
|
||||
var/job_str = copytext(line, char_str_sep+1, job_str_sep)
|
||||
var/item_str = copytext(line, job_str_sep+1, item_str_sep)
|
||||
if(!ckey_str || !char_str || !job_str || !item_str || !length(ckey_str) || !length(char_str) || !length(job_str) || !length(item_str))
|
||||
log_admin("Errored custom_items_whitelist line: [line] - Component/separator missing!")
|
||||
if(!islist(GLOB.custom_item_list[ckey_str]))
|
||||
GLOB.custom_item_list[ckey_str] = list() //Initialize list for this ckey if it isn't initialized..
|
||||
var/list/characters = splittext(char_str, "/")
|
||||
for(var/character in characters)
|
||||
if(!islist(GLOB.custom_item_list[ckey_str][character]))
|
||||
GLOB.custom_item_list[ckey_str][character] = list()
|
||||
var/list/jobs = splittext(job_str, "/")
|
||||
for(var/job in jobs)
|
||||
for(var/character in characters)
|
||||
if(!islist(GLOB.custom_item_list[ckey_str][character][job]))
|
||||
GLOB.custom_item_list[ckey_str][character][job] = list() //Initialize item list for this job of this ckey if not already initialized.
|
||||
var/list/item_strings = splittext(item_str, ";") //Get item strings in format of /path/to/item=amount
|
||||
for(var/item_string in item_strings)
|
||||
var/path_str_sep = findtext(item_string, "=")
|
||||
var/path = copytext(item_string, 1, path_str_sep) //Path to spawn
|
||||
var/amount = copytext(item_string, path_str_sep+1) //Amount to spawn
|
||||
//world << "DEBUG: Item string [item_string] processed"
|
||||
amount = text2num(amount)
|
||||
path = text2path(path)
|
||||
if(!ispath(path) || !isnum(amount))
|
||||
log_admin("Errored custom_items_whitelist line: [line] - Path/number for item missing or invalid.")
|
||||
for(var/character in characters)
|
||||
for(var/job in jobs)
|
||||
if(!GLOB.custom_item_list[ckey_str][character][job][path]) //Doesn't exist, make it exist!
|
||||
GLOB.custom_item_list[ckey_str][character][job][path] = amount
|
||||
else
|
||||
GLOB.custom_item_list[ckey_str][character][job][path] += amount //Exists, we want more~
|
||||
return GLOB.custom_item_list
|
||||
|
||||
/proc/parse_custom_roundstart_items(ckey, char_name = "ALL", job_name = "ALL", special_role)
|
||||
var/list/ret = list()
|
||||
if(GLOB.custom_item_list[ckey])
|
||||
for(var/char in GLOB.custom_item_list[ckey])
|
||||
if((char_name == char) || (char_name == "ALL") || (char == "ALL"))
|
||||
for(var/job in GLOB.custom_item_list[ckey][char])
|
||||
if((job_name == job) || (job == "ALL") || (job_name == "ALL") || (special_role && (job == special_role)))
|
||||
for(var/item_path in GLOB.custom_item_list[ckey][char][job])
|
||||
if(ret[item_path])
|
||||
ret[item_path] += GLOB.custom_item_list[ckey][char][job][item_path]
|
||||
else
|
||||
ret[item_path] = GLOB.custom_item_list[ckey][char][job][item_path]
|
||||
return ret
|
||||
@@ -1,13 +0,0 @@
|
||||
/proc/send2maindiscord(var/msg)
|
||||
send2discord(msg, FALSE)
|
||||
|
||||
/proc/send2admindiscord(var/msg, var/ping = FALSE)
|
||||
send2discord(msg, TRUE, ping)
|
||||
|
||||
/proc/send2discord(var/msg, var/admin = FALSE, var/ping = FALSE)
|
||||
// if (!config.discord_url || !config.discord_password)
|
||||
// return
|
||||
|
||||
// var/url = "[config.discord_url]?pass=[url_encode(config.discord_password)]&admin=[admin ? "true" : "false"]&content=[url_encode(msg)]&ping=[ping ? "true" : "false"]"
|
||||
// world.Export(url)
|
||||
return
|
||||
@@ -1,76 +0,0 @@
|
||||
/obj/item/robot_module/loader
|
||||
name = "loader robot module"
|
||||
/obj/item/robot_module/loader/New()
|
||||
..()
|
||||
emag = new /obj/item/borg/stun(src)
|
||||
modules += new /obj/item/extinguisher(src)
|
||||
modules += new /obj/item/weldingtool/largetank/cyborg(src)
|
||||
modules += new /obj/item/screwdriver(src)
|
||||
modules += new /obj/item/wrench(src)
|
||||
modules += new /obj/item/crowbar(src)
|
||||
modules += new /obj/item/wirecutters(src)
|
||||
modules += new /obj/item/device/multitool(src)
|
||||
modules += new /obj/item/device/t_scanner(src)
|
||||
modules += new /obj/item/device/analyzer(src)
|
||||
modules += new /obj/item/device/assembly/signaler
|
||||
modules += new /obj/item/soap/nanotrasen(src)
|
||||
|
||||
fix_modules()
|
||||
|
||||
/obj/item/robot_module/k9
|
||||
name = "Security K-9 Unit module"
|
||||
/obj/item/robot_module/k9/New()
|
||||
..()
|
||||
modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg/dog(src)
|
||||
modules += new /obj/item/dogborg/jaws/big(src)
|
||||
modules += new /obj/item/dogborg/pounce(src)
|
||||
modules += new /obj/item/clothing/mask/gas/sechailer/cyborg(src)
|
||||
modules += new /obj/item/soap/tongue(src)
|
||||
modules += new /obj/item/device/analyzer/nose(src)
|
||||
modules += new /obj/item/storage/bag/borgdelivery(src)
|
||||
//modules += new /obj/item/device/assembly/signaler(src)
|
||||
//modules += new /obj/item/device/detective_scanner(src)
|
||||
modules += new /obj/item/gun/energy/disabler/cyborg(src)
|
||||
emag = new /obj/item/gun/energy/laser/cyborg(src)
|
||||
fix_modules()
|
||||
|
||||
/obj/item/robot_module/security/respawn_consumable(mob/living/silicon/robot/R, coeff = 1)
|
||||
..()
|
||||
var/obj/item/gun/energy/gun/advtaser/cyborg/T = locate(/obj/item/gun/energy/gun/advtaser/cyborg) in get_usable_modules()
|
||||
if(T)
|
||||
if(T.power_supply.charge < T.power_supply.maxcharge)
|
||||
var/obj/item/ammo_casing/energy/S = T.ammo_type[T.select]
|
||||
T.power_supply.give(S.e_cost * coeff)
|
||||
T.update_icon()
|
||||
else
|
||||
T.charge_tick = 0
|
||||
fix_modules()
|
||||
|
||||
/obj/item/robot_module/borgi
|
||||
name = "Borgi module"
|
||||
|
||||
/obj/item/robot_module/borgi/New()
|
||||
..()
|
||||
modules += new /obj/item/dogborg/jaws/small(src)
|
||||
modules += new /obj/item/storage/bag/borgdelivery(src)
|
||||
modules += new /obj/item/soap/tongue(src)
|
||||
modules += new /obj/item/device/healthanalyzer(src)
|
||||
modules += new /obj/item/device/analyzer/nose(src)
|
||||
emag = new /obj/item/dogborg/pounce(src)
|
||||
fix_modules()
|
||||
|
||||
/obj/item/robot_module/medihound
|
||||
name = "MediHound module"
|
||||
|
||||
/obj/item/robot_module/medihound/New()
|
||||
..()
|
||||
modules += new /obj/item/dogborg/jaws/small(src)
|
||||
modules += new /obj/item/storage/bag/borgdelivery(src)
|
||||
modules += new /obj/item/device/analyzer/nose(src)
|
||||
modules += new /obj/item/soap/tongue(src)
|
||||
modules += new /obj/item/device/healthanalyzer(src)
|
||||
modules += new /obj/item/dogborg/sleeper(src)
|
||||
modules += new /obj/item/twohanded/shockpaddles/hound(src)
|
||||
modules += new /obj/item/device/sensor_device(src)
|
||||
emag = new /obj/item/dogborg/pounce(src)
|
||||
fix_modules()
|
||||
@@ -1,214 +0,0 @@
|
||||
#define HYPO_SPRAY 0
|
||||
#define HYPO_INJECT 1
|
||||
|
||||
//A vial-loaded hypospray. Cartridge-based!
|
||||
/obj/item/reagent_containers/hypospray/mkii
|
||||
name = "hypospray mk.II"
|
||||
icon = 'icons/obj/citadel/hypospray.dmi'
|
||||
icon_state = "hypo2"
|
||||
var/list/allowed_containers = list(/obj/item/reagent_containers/glass/bottle/vial/small)
|
||||
desc = "A new development from DeForest Medical, this new hypospray takes 30-unit vials as the drug supply for easy swapping."
|
||||
volume = 0
|
||||
amount_per_transfer_from_this = 5
|
||||
possible_transfer_amounts = list(5,10,15)
|
||||
var/mode = HYPO_INJECT
|
||||
var/obj/item/reagent_containers/glass/bottle/vial/vial
|
||||
var/loaded_vial = /obj/item/reagent_containers/glass/bottle/vial/small
|
||||
var/spawnwithvial = TRUE
|
||||
var/start_vial = null
|
||||
|
||||
/obj/item/reagent_containers/hypospray/mkii/CMO
|
||||
name = "hypospray mk.II deluxe"
|
||||
allowed_containers = list(/obj/item/reagent_containers/glass/bottle/vial/small, /obj/item/reagent_containers/glass/bottle/vial/large)
|
||||
icon_state = "cmo2"
|
||||
ignore_flags = 1
|
||||
desc = "The Chief Medical Officer's hypospray is identically functional to the base model, excepting that it can take larger vials in addition to regular sized. It is also able to penetrate harder materials and deliver more reagents per spray."
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
loaded_vial = /obj/item/reagent_containers/glass/bottle/vial/large/preloaded/CMO
|
||||
possible_transfer_amounts = list(5,10,15,30,60) //cmo hypo should be able to dump lots into it
|
||||
|
||||
/obj/item/reagent_containers/hypospray/mkii/Initialize()
|
||||
. = ..()
|
||||
if(!spawnwithvial)
|
||||
update_icon()
|
||||
return
|
||||
if (!start_vial)
|
||||
start_vial = new loaded_vial(src)
|
||||
vial = start_vial
|
||||
update_icon()
|
||||
|
||||
/obj/item/reagent_containers/hypospray/mkii/update_icon()
|
||||
..()
|
||||
icon_state = "[initial(icon_state)][vial ? "" : "-e"]"
|
||||
if(ismob(loc))
|
||||
var/mob/M = loc
|
||||
M.update_inv_hands()
|
||||
return
|
||||
|
||||
/obj/item/reagent_containers/hypospray/mkii/examine(mob/user)
|
||||
. = ..()
|
||||
to_chat(user, "[src] is set to [mode ? "Inject" : "Spray"] contents on application.")
|
||||
|
||||
/obj/item/reagent_containers/hypospray/mkii/proc/unload_hypo(obj/item/I, mob/user)
|
||||
if((istype(I, /obj/item/reagent_containers/glass/bottle/vial)))
|
||||
var/obj/item/reagent_containers/glass/bottle/vial/V = I
|
||||
reagents.trans_to(V, reagents.total_volume)
|
||||
reagents.maximum_volume = 0
|
||||
V.forceMove(user.loc)
|
||||
user.put_in_hands(V)
|
||||
to_chat(user, "<span class='notice'>You remove the vial from the [src].</span>")
|
||||
vial = null
|
||||
update_icon()
|
||||
playsound(loc, 'sound/weapons/empty.ogg', 50, 1)
|
||||
else
|
||||
to_chat(user, "<span class='notice'>This hypo isn't loaded!</span>")
|
||||
return
|
||||
|
||||
/obj/item/reagent_containers/hypospray/mkii/attackby(obj/item/I, mob/living/user)
|
||||
if((istype(I, /obj/item/reagent_containers/glass/bottle/vial) && vial != null))
|
||||
to_chat(user, "<span class='warning'>[src] can not hold more than one vial!</span>")
|
||||
return FALSE
|
||||
if((istype(I, /obj/item/reagent_containers/glass/bottle/vial)))
|
||||
var/obj/item/reagent_containers/glass/bottle/vial/V = I
|
||||
if(!is_type_in_list(V, allowed_containers))
|
||||
to_chat(user, "<span class='notice'>\The [src] doesn't accept this vial.</span>")
|
||||
return
|
||||
vial = V
|
||||
reagents.maximum_volume = V.volume
|
||||
V.reagents.trans_to(src, V.reagents.total_volume)
|
||||
if(!user.transferItemToLoc(V,src))
|
||||
return
|
||||
user.visible_message("<span class='notice'>[user] has loads vial into \the [src].</span>","<span class='notice'>You have loaded [vial] into \the [src].</span>")
|
||||
update_icon()
|
||||
playsound(loc, 'sound/weapons/autoguninsert.ogg', 50, 1)
|
||||
return TRUE
|
||||
else
|
||||
to_chat(user, "<span class='notice'>This doesn't fit in \the [src].</span>")
|
||||
return FALSE
|
||||
return FALSE
|
||||
|
||||
/obj/item/reagent_containers/hypospray/mkii/attack(obj/item/I, mob/user, params)
|
||||
return
|
||||
|
||||
/obj/item/reagent_containers/hypospray/mkii/afterattack(atom/target, mob/user, proximity)
|
||||
if(!proximity)
|
||||
return
|
||||
|
||||
if(!ismob(target))
|
||||
return
|
||||
|
||||
var/mob/living/L
|
||||
if(isliving(target))
|
||||
L = target
|
||||
if(!L.can_inject(user, 1))
|
||||
return
|
||||
|
||||
if(!L && !target.is_injectable()) //only checks on non-living mobs, due to how can_inject() handles
|
||||
to_chat(user, "<span class='warning'>You cannot directly fill [target]!</span>")
|
||||
return
|
||||
|
||||
if(target.reagents.total_volume >= target.reagents.maximum_volume)
|
||||
to_chat(user, "<span class='notice'>[target] is full.</span>")
|
||||
return
|
||||
|
||||
var/contained = reagents.log_list()
|
||||
add_logs(user, L, "attemped to inject", src, addition="which had [contained]")
|
||||
//Always log attemped injections for admins
|
||||
if(vial != null)
|
||||
switch(mode)
|
||||
if(HYPO_INJECT)
|
||||
if(L) //living mob
|
||||
if(!L.can_inject(user, TRUE))
|
||||
return
|
||||
if(L != user)
|
||||
L.visible_message("<span class='danger'>[user] is trying to inject [L] with [src]!</span>", \
|
||||
"<span class='userdanger'>[user] is trying to inject [L] with the [src]!</span>")
|
||||
if(!do_mob(user, L, extra_checks=CALLBACK(L, /mob/living/proc/can_inject,user,1)))
|
||||
return
|
||||
if(!reagents.total_volume)
|
||||
return
|
||||
if(L.reagents.total_volume >= L.reagents.maximum_volume)
|
||||
return
|
||||
L.visible_message("<span class='danger'>[user] uses the [src] on [L]!</span>", \
|
||||
"<span class='userdanger'>[user] uses the [src] on [L]!</span>")
|
||||
else
|
||||
if(!do_mob(user, L, extra_checks=CALLBACK(L, /mob/living/proc/can_inject,user,1)))
|
||||
return
|
||||
if(!reagents.total_volume)
|
||||
return
|
||||
if(L.reagents.total_volume >= L.reagents.maximum_volume)
|
||||
return
|
||||
log_attack("<font color='red'>[user.name] ([user.ckey]) applied [src] to [L.name] ([L.ckey]), which had [contained] (INTENT: [uppertext(user.a_intent)]) (MODE: [src.mode])</font>")
|
||||
L.log_message("<font color='orange'>applied [src] to themselves ([contained]).</font>", INDIVIDUAL_ATTACK_LOG)
|
||||
|
||||
var/fraction = min(amount_per_transfer_from_this/reagents.total_volume, 1)
|
||||
reagents.reaction(L, INJECT, fraction)
|
||||
reagents.trans_to(target, amount_per_transfer_from_this)
|
||||
if(amount_per_transfer_from_this >= 15)
|
||||
playsound(loc,'sound/items/hypospray_long.ogg',50, 1, -1)
|
||||
if(amount_per_transfer_from_this < 15)
|
||||
playsound(loc, pick('sound/items/hypospray.ogg','sound/items/hypospray2.ogg'), 50, 1, -1)
|
||||
to_chat(user, "<span class='notice'>You inject [amount_per_transfer_from_this] units of the solution. The hypospray's cartridge now contains [reagents.total_volume] units.</span>")
|
||||
|
||||
if(HYPO_SPRAY)
|
||||
if(L) //living mob
|
||||
if(!L.can_inject(user, TRUE))
|
||||
return
|
||||
if(L != user)
|
||||
L.visible_message("<span class='danger'>[user] is trying to inject [L] with [src]!</span>", \
|
||||
"<span class='userdanger'>[user] is trying to inject [L] with the [src]!</span>")
|
||||
if(!do_mob(user, L, extra_checks=CALLBACK(L, /mob/living/proc/can_inject,user,1)))
|
||||
return
|
||||
if(!reagents.total_volume)
|
||||
return
|
||||
if(L.reagents.total_volume >= L.reagents.maximum_volume)
|
||||
return
|
||||
L.visible_message("<span class='danger'>[user] uses the [src] on [L]!</span>", \
|
||||
"<span class='userdanger'>[user] uses the [src] on [L]!</span>")
|
||||
else
|
||||
if(!do_mob(user, L, extra_checks=CALLBACK(L, /mob/living/proc/can_inject,user,1)))
|
||||
return
|
||||
if(!reagents.total_volume)
|
||||
return
|
||||
if(L.reagents.total_volume >= L.reagents.maximum_volume)
|
||||
return
|
||||
log_attack("<font color='red'>[user.name] ([user.ckey]) applied [src] to [L.name] ([L.ckey]), which had [contained] (INTENT: [uppertext(user.a_intent)]) (MODE: [src.mode])</font>")
|
||||
L.log_message("<font color='orange'>applied [src] to themselves ([contained]).</font>", INDIVIDUAL_ATTACK_LOG)
|
||||
var/fraction = min(amount_per_transfer_from_this/reagents.total_volume, 1)
|
||||
reagents.reaction(L, PATCH, fraction)
|
||||
reagents.trans_to(target, amount_per_transfer_from_this)
|
||||
if(amount_per_transfer_from_this >= 15)
|
||||
playsound(loc,'sound/items/hypospray_long.ogg',50, 1, -1)
|
||||
if(amount_per_transfer_from_this < 15)
|
||||
playsound(loc, pick('sound/items/hypospray.ogg','sound/items/hypospray2.ogg'), 50, 1, -1)
|
||||
to_chat(user, "<span class='notice'>You spray [amount_per_transfer_from_this] units of the solution. The hypospray's cartridge now contains [reagents.total_volume] units.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='notice'>[src] doesn't work here!</span>")
|
||||
return
|
||||
|
||||
/obj/item/reagent_containers/hypospray/mkii/AltClick(mob/living/user)
|
||||
if(user)
|
||||
if(user.incapacitated())
|
||||
return
|
||||
else if(!contents)
|
||||
to_chat(user, "This Hypo needs to be loaded first!")
|
||||
return
|
||||
else
|
||||
for(var/obj/item/I in contents)
|
||||
unload_hypo(I,user)
|
||||
|
||||
/obj/item/reagent_containers/hypospray/mkii/verb/modes()
|
||||
set name = "Change Application Method"
|
||||
set category = "Object"
|
||||
set src in usr
|
||||
var/mob/M = usr
|
||||
var/choice = alert(M, "Which application mode should this be? Current mode is: [mode ? "Spray" : "Inject"]", "", "Spray", "Cancel", "Inject")
|
||||
switch(choice)
|
||||
if("Cancel")
|
||||
return
|
||||
if("Inject")
|
||||
mode = HYPO_INJECT
|
||||
to_chat(M, "[src] is now set to inject contents on application.")
|
||||
if("Spray")
|
||||
mode = HYPO_SPRAY
|
||||
to_chat(M, "[src] is now set to spray contents on application.")
|
||||
@@ -1,105 +0,0 @@
|
||||
/obj/item/reagent_containers/glass/bottle/vial
|
||||
name = "hypospray vial"
|
||||
desc = "This is a vial suitable for loading into mk II hyposprays."
|
||||
icon = 'icons/obj/citadel/vial.dmi'
|
||||
icon_state = "hypovial"
|
||||
w_class = WEIGHT_CLASS_SMALL //Why would it be the same size as a beaker?
|
||||
container_type = OPENCONTAINER
|
||||
spillable = FALSE
|
||||
resistance_flags = ACID_PROOF
|
||||
var/comes_with = list() //Easy way of doing this.
|
||||
volume = 10
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/vial/Initialize()
|
||||
. = ..()
|
||||
if(!icon_state)
|
||||
icon_state = "hypovial"
|
||||
update_icon()
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/vial/on_reagent_change()
|
||||
update_icon()
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/vial/update_icon()
|
||||
cut_overlays()
|
||||
if(reagents.total_volume)
|
||||
var/mutable_appearance/filling = mutable_appearance('icons/obj/citadel/vial.dmi', "[icon_state]10")
|
||||
|
||||
var/percent = round((reagents.total_volume / volume) * 100)
|
||||
switch(percent)
|
||||
if(0 to 9)
|
||||
filling.icon_state = "[icon_state]10"
|
||||
if(10 to 29)
|
||||
filling.icon_state = "[icon_state]25"
|
||||
if(30 to 49)
|
||||
filling.icon_state = "[icon_state]50"
|
||||
if(50 to 69)
|
||||
filling.icon_state = "[icon_state]75"
|
||||
if(70 to INFINITY)
|
||||
filling.icon_state = "[icon_state]100"
|
||||
|
||||
filling.color = mix_color_from_reagents(reagents.reagent_list)
|
||||
add_overlay(filling)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/vial/small
|
||||
volume = 30
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/vial/large
|
||||
name = "large hypospray vial"
|
||||
desc = "This is a vial suitable for loading into the Chief Medical Officer's Hypospray mk II."
|
||||
icon_state = "hypoviallarge"
|
||||
volume = 60
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/vial/New()
|
||||
..()
|
||||
for(var/R in comes_with)
|
||||
reagents.add_reagent(R,comes_with[R])
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/vial/small/preloaded/bicaridine
|
||||
name = "vial (bicaridine)"
|
||||
icon_state = "hypovial-b"
|
||||
comes_with = list("bicaridine" = 30)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/vial/small/preloaded/antitoxin
|
||||
name = "vial (Anti-Tox)"
|
||||
icon_state = "hypovial-a"
|
||||
comes_with = list("antitoxin" = 30)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/vial/small/preloaded/kelotane
|
||||
name = "vial (kelotane)"
|
||||
icon_state = "hypovial-k"
|
||||
comes_with = list("kelotane" = 30)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/vial/small/preloaded/dexalin
|
||||
name = "vial (dexalin)"
|
||||
icon_state = "hypovial-d"
|
||||
comes_with = list("dexalin" = 30)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/vial/small/preloaded/tricordrazine
|
||||
name = "vial (tricordrazine)"
|
||||
icon_state = "hypovial"
|
||||
comes_with = list("tricordrazine" = 30)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/vial/large/preloaded/CMO
|
||||
name = "large vial (CMO Special)"
|
||||
icon_state = "hypoviallarge-cmos"
|
||||
comes_with = list("epinephrine" = 15, "kelotane" = 15, "charcoal" = 15, "bicaridine" = 15)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/vial/large/preloaded/bicaridine
|
||||
name = "large vial (bicaridine)"
|
||||
icon_state = "hypoviallarge-b"
|
||||
comes_with = list("bicaridine" = 60)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/vial/large/preloaded/antitoxin
|
||||
name = "large vial (Anti-Tox)"
|
||||
icon_state = "hypoviallarge-a"
|
||||
comes_with = list("antitoxin" = 60)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/vial/large/preloaded/kelotane
|
||||
name = "large vial (kelotane)"
|
||||
icon_state = "hypoviallarge-k"
|
||||
comes_with = list("kelotane" = 60)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/vial/large/preloaded/dexalin
|
||||
name = "large vial (dexalin)"
|
||||
icon_state = "hypoviallarge-d"
|
||||
comes_with = list("dexalin" = 60)
|
||||
|
Before Width: | Height: | Size: 559 B |
|
Before Width: | Height: | Size: 703 B |
|
Before Width: | Height: | Size: 668 B |
|
Before Width: | Height: | Size: 668 B |
|
Before Width: | Height: | Size: 347 B |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 182 B |
|
Before Width: | Height: | Size: 182 B |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 377 B |
|
Before Width: | Height: | Size: 775 B |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 182 B |
|
Before Width: | Height: | Size: 704 B |
|
Before Width: | Height: | Size: 405 B |
|
Before Width: | Height: | Size: 644 B |
@@ -1,65 +0,0 @@
|
||||
/obj/item/organ/genital/breasts
|
||||
name = "breasts"
|
||||
desc = "Female milk producing organs."
|
||||
icon_state = "breasts"
|
||||
icon = 'code/citadel/icons/breasts.dmi'
|
||||
zone = "chest"
|
||||
slot = "breasts"
|
||||
w_class = 3
|
||||
size = BREASTS_SIZE_DEF
|
||||
fluid_id = "milk"
|
||||
var/amount = 2
|
||||
producing = TRUE
|
||||
shape = "pair"
|
||||
can_masturbate_with = TRUE
|
||||
masturbation_verb = "massage"
|
||||
can_climax = TRUE
|
||||
fluid_transfer_factor =0.5
|
||||
|
||||
/obj/item/organ/genital/breasts/Initialize()
|
||||
. = ..()
|
||||
reagents.add_reagent(fluid_id, fluid_max_volume)
|
||||
|
||||
/obj/item/organ/genital/breasts/on_life()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
if(!reagents || !owner)
|
||||
return
|
||||
reagents.maximum_volume = fluid_max_volume
|
||||
if(fluid_id && producing)
|
||||
generate_milk()
|
||||
|
||||
/obj/item/organ/genital/breasts/proc/generate_milk()
|
||||
if(owner.stat == DEAD)
|
||||
return FALSE
|
||||
reagents.isolate_reagent(fluid_id)
|
||||
reagents.add_reagent(fluid_id, (fluid_mult * fluid_rate))
|
||||
|
||||
/obj/item/organ/genital/breasts/update_appearance()
|
||||
var/string = "breasts_[lowertext(shape)]_[size]"
|
||||
icon_state = sanitize_text(string)
|
||||
var/lowershape = lowertext(shape)
|
||||
switch(lowershape)
|
||||
if("pair")
|
||||
desc = "You see a pair of breasts."
|
||||
else
|
||||
desc = "You see some breasts, they seem to be quite exotic."
|
||||
if (size)
|
||||
desc += " You estimate that they're [uppertext(size)]-cups."
|
||||
else
|
||||
desc += " You wouldn't measure them in cup sizes."
|
||||
if(producing && aroused_state)
|
||||
desc += " They're leaking [fluid_id]."
|
||||
if(owner)
|
||||
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
|
||||
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
|
||||
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
|
||||
color = "#[skintone2hex(H.skin_tone)]"
|
||||
else
|
||||
color = "#[owner.dna.features["breasts_color"]]"
|
||||
|
||||
/obj/item/organ/genital/breasts/is_exposed()
|
||||
. = ..()
|
||||
if(.)
|
||||
return TRUE
|
||||
return owner.is_chest_exposed()
|
||||
@@ -1,15 +0,0 @@
|
||||
/obj/item/organ/genital/eggsack
|
||||
name = "Egg sack"
|
||||
desc = "An egg producing reproductive organ."
|
||||
icon_state = "egg_sack"
|
||||
icon = 'code/citadel/icons/ovipositor.dmi'
|
||||
zone = "groin"
|
||||
slot = "testicles"
|
||||
color = null //don't use the /genital color since it already is colored
|
||||
w_class = 3
|
||||
internal = TRUE
|
||||
var/egg_girth = EGG_GIRTH_DEF
|
||||
var/cum_mult = CUM_RATE_MULT
|
||||
var/cum_rate = CUM_RATE
|
||||
var/cum_efficiency = CUM_EFFICIENCY
|
||||
var/obj/item/organ/ovipositor/linked_ovi
|
||||
@@ -1,331 +0,0 @@
|
||||
/obj/item/organ/genital
|
||||
color = "#fcccb3"
|
||||
var/shape = "human"
|
||||
var/sensitivity = 1
|
||||
var/list/genital_flags = list()
|
||||
var/can_masturbate_with = FALSE
|
||||
var/masturbation_verb = "masturbate"
|
||||
var/can_climax = FALSE
|
||||
var/fluid_transfer_factor = 0.0 //How much would a partner get in them if they climax using this?
|
||||
var/size = 2 //can vary between num or text, just used in icon_state strings
|
||||
var/fluid_id = null
|
||||
var/fluid_max_volume = 50
|
||||
var/fluid_efficiency = 1
|
||||
var/fluid_rate = 1
|
||||
var/fluid_mult = 1
|
||||
var/producing = FALSE
|
||||
var/aroused_state = FALSE //Boolean used in icon_state strings
|
||||
var/aroused_amount = 50 //This is a num from 0 to 100 for arousal percentage for when to use arousal state icons.
|
||||
var/obj/item/organ/genital/linked_organ
|
||||
var/through_clothes = FALSE
|
||||
var/internal = FALSE
|
||||
|
||||
/obj/item/organ/genital/Initialize()
|
||||
. = ..()
|
||||
if(!reagents)
|
||||
create_reagents(fluid_max_volume)
|
||||
update()
|
||||
|
||||
/obj/item/organ/genital/Destroy()
|
||||
remove_ref()
|
||||
if(owner)
|
||||
Remove(owner, 1)//this should remove references to it, so it can be GCd correctly
|
||||
update_link()//this should remove any other links it has
|
||||
return ..()
|
||||
|
||||
/obj/item/organ/genital/proc/update()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
update_size()
|
||||
update_appearance()
|
||||
update_link()
|
||||
|
||||
//exposure and through-clothing code
|
||||
/mob/living/carbon
|
||||
var/list/exposed_genitals = list() //Keeping track of them so we don't have to iterate through every genitalia and see if exposed
|
||||
|
||||
/obj/item/organ/genital/proc/is_exposed()
|
||||
if(!owner)
|
||||
return FALSE
|
||||
if(internal)
|
||||
return FALSE
|
||||
if(through_clothes)
|
||||
return TRUE
|
||||
|
||||
/obj/item/organ/genital/proc/toggle_through_clothes()
|
||||
if(through_clothes)
|
||||
through_clothes = FALSE
|
||||
owner.exposed_genitals -= src
|
||||
else
|
||||
through_clothes = TRUE
|
||||
owner.exposed_genitals += src
|
||||
if(ishuman(owner)) //recast to use update genitals proc
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.update_genitals()
|
||||
|
||||
/mob/living/carbon/verb/toggle_genitals()
|
||||
set category = "IC"
|
||||
set name = "Expose/Hide genitals"
|
||||
set desc = "Allows you to toggle which genitals should show through clothes or not."
|
||||
|
||||
var/list/genital_list = list()
|
||||
for(var/obj/item/organ/O in internal_organs)
|
||||
if(istype(O, /obj/item/organ/genital))
|
||||
var/obj/item/organ/genital/G = O
|
||||
if(!G.internal)
|
||||
genital_list += G
|
||||
if(!genital_list.len) //There is nothing to expose
|
||||
return
|
||||
//Full list of exposable genitals created
|
||||
var/obj/item/organ/genital/picked_organ
|
||||
picked_organ = input(src, "Expose/Hide genitals", "Choose which genitalia to expose/hide", null) in genital_list
|
||||
if(picked_organ)
|
||||
picked_organ.toggle_through_clothes()
|
||||
return
|
||||
|
||||
|
||||
|
||||
|
||||
/obj/item/organ/genital/proc/update_size()
|
||||
|
||||
/obj/item/organ/genital/proc/update_appearance()
|
||||
|
||||
/obj/item/organ/genital/proc/update_link()
|
||||
|
||||
/obj/item/organ/genital/proc/remove_ref()
|
||||
if(linked_organ)
|
||||
linked_organ.linked_organ = null
|
||||
linked_organ = null
|
||||
|
||||
/obj/item/organ/genital/Insert(mob/living/carbon/M, special = 0)
|
||||
..()
|
||||
update()
|
||||
|
||||
/obj/item/organ/genital/Remove(mob/living/carbon/M, special = 0)
|
||||
..()
|
||||
update()
|
||||
|
||||
//proc to give a player their genitals and stuff when they log in
|
||||
/mob/living/carbon/human/proc/give_genitals(clean=0)//clean will remove all pre-existing genitals. proc will then give them any genitals that are enabled in their DNA
|
||||
if (NOGENITALS in dna.species.species_traits)
|
||||
return
|
||||
if(clean)
|
||||
var/obj/item/organ/genital/GtoClean
|
||||
for(GtoClean in internal_organs)
|
||||
qdel(GtoClean)
|
||||
//Order should be very important. FIRST vagina, THEN testicles, THEN penis, as this affects the order they are rendered in.
|
||||
if(dna.features["has_breasts"])
|
||||
give_breasts()
|
||||
if(dna.features["has_vag"])
|
||||
give_vagina()
|
||||
if(dna.features["has_womb"])
|
||||
give_womb()
|
||||
if(dna.features["has_balls"])
|
||||
give_balls()
|
||||
if(dna.features["has_cock"])
|
||||
give_penis()
|
||||
if(dna.features["has_ovi"])
|
||||
give_ovipositor()
|
||||
if(dna.features["has_eggsack"])
|
||||
give_eggsack()
|
||||
|
||||
/mob/living/carbon/human/proc/give_penis()
|
||||
if(!dna)
|
||||
return FALSE
|
||||
if(NOGENITALS in dna.species.species_traits)
|
||||
return FALSE
|
||||
if(!getorganslot("penis"))
|
||||
var/obj/item/organ/genital/penis/P = new
|
||||
P.Insert(src)
|
||||
if(P)
|
||||
if(dna.species.use_skintones && dna.features["genitals_use_skintone"])
|
||||
P.color = skintone2hex(skin_tone)
|
||||
else
|
||||
P.color = "#[dna.features["cock_color"]]"
|
||||
P.length = dna.features["cock_length"]
|
||||
P.girth_ratio = dna.features["cock_girth_ratio"]
|
||||
P.shape = dna.features["cock_shape"]
|
||||
P.update()
|
||||
|
||||
/mob/living/carbon/human/proc/give_balls()
|
||||
if(!dna)
|
||||
return FALSE
|
||||
if(NOGENITALS in dna.species.species_traits)
|
||||
return FALSE
|
||||
if(!getorganslot("testicles"))
|
||||
var/obj/item/organ/genital/testicles/T = new
|
||||
T.Insert(src)
|
||||
// if(dna.species.use_skintones && dna.features["genitals_use_skintone"])
|
||||
// T.color = skintone2hex(skin_tone)
|
||||
// else
|
||||
// T.color = "#[dna.features["balls_color"]]"
|
||||
if(T)
|
||||
T.size = dna.features["balls_size"]
|
||||
T.sack_size = dna.features["balls_sack_size"]
|
||||
T.fluid_id = dna.features["balls_fluid"]
|
||||
T.fluid_rate = dna.features["balls_cum_rate"]
|
||||
T.fluid_mult = dna.features["balls_cum_mult"]
|
||||
T.fluid_efficiency = dna.features["balls_efficiency"]
|
||||
T.update()
|
||||
|
||||
/mob/living/carbon/human/proc/give_breasts()
|
||||
if(!dna)
|
||||
return FALSE
|
||||
if(NOGENITALS in dna.species.species_traits)
|
||||
return FALSE
|
||||
if(!getorganslot("breasts"))
|
||||
var/obj/item/organ/genital/breasts/B = new
|
||||
B.Insert(src)
|
||||
if(B)
|
||||
if(dna.species.use_skintones && dna.features["genitals_use_skintone"])
|
||||
B.color = skintone2hex(skin_tone)
|
||||
else
|
||||
B.color = "#[dna.features["breasts_color"]]"
|
||||
B.size = dna.features["breasts_size"]
|
||||
B.shape = dna.features["breasts_shape"]
|
||||
B.fluid_id = dna.features["breasts_fluid"]
|
||||
B.update()
|
||||
|
||||
|
||||
/mob/living/carbon/human/proc/give_ovipositor()
|
||||
/mob/living/carbon/human/proc/give_eggsack()
|
||||
/mob/living/carbon/human/proc/give_vagina()
|
||||
if(!dna)
|
||||
return FALSE
|
||||
if(NOGENITALS in dna.species.species_traits)
|
||||
return FALSE
|
||||
if(!getorganslot("vagina"))
|
||||
var/obj/item/organ/genital/vagina/V = new
|
||||
V.Insert(src)
|
||||
if(V)
|
||||
if(dna.species.use_skintones && dna.features["genitals_use_skintone"])
|
||||
V.color = skintone2hex(skin_tone)
|
||||
else
|
||||
V.color = "[dna.features["vag_color"]]"
|
||||
V.shape = "[dna.features["vag_shape"]]"
|
||||
V.update()
|
||||
|
||||
/mob/living/carbon/human/proc/give_womb()
|
||||
if(!dna)
|
||||
return FALSE
|
||||
if(NOGENITALS in dna.species.species_traits)
|
||||
return FALSE
|
||||
if(!getorganslot("womb"))
|
||||
var/obj/item/organ/genital/womb/W = new
|
||||
W.Insert(src)
|
||||
if(W)
|
||||
W.update()
|
||||
|
||||
|
||||
/datum/species/proc/genitals_layertext(layer)
|
||||
switch(layer)
|
||||
if(GENITALS_BEHIND_LAYER)
|
||||
return "BEHIND"
|
||||
if(GENITALS_ADJ_LAYER)
|
||||
return "ADJ"
|
||||
if(GENITALS_FRONT_LAYER)
|
||||
return "FRONT"
|
||||
|
||||
//procs to handle sprite overlays being applied to humans
|
||||
|
||||
/obj/item/equipped(mob/user, slot)
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
H.update_genitals()
|
||||
..()
|
||||
|
||||
/mob/living/carbon/human/doUnEquip(obj/item/I, force)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
update_genitals()
|
||||
|
||||
/mob/living/carbon/human/proc/update_genitals()
|
||||
if(src && !QDELETED(src))
|
||||
dna.species.handle_genitals(src)
|
||||
|
||||
/datum/species/proc/handle_genitals(mob/living/carbon/human/H)
|
||||
if(!H)//no args
|
||||
CRASH("H = null")
|
||||
if(!LAZYLEN(H.internal_organs))//if they have no organs, we're done
|
||||
return
|
||||
if(NOGENITALS in species_traits)//golems and such
|
||||
return
|
||||
|
||||
var/list/genitals_to_add = list()
|
||||
var/list/relevant_layers = list(GENITALS_BEHIND_LAYER, GENITALS_ADJ_LAYER, GENITALS_FRONT_LAYER)
|
||||
var/list/standing = list()
|
||||
var/size = null
|
||||
|
||||
for(var/L in relevant_layers) //Less hardcode
|
||||
H.remove_overlay(L)
|
||||
|
||||
if(H.has_trait(TRAIT_HUSK))
|
||||
return
|
||||
//start scanning for genitals
|
||||
//var/list/worn_stuff = H.get_equipped_items()//cache this list so it's not built again
|
||||
for(var/obj/item/organ/O in H.internal_organs)
|
||||
if(istype(O, /obj/item/organ/genital))
|
||||
var/obj/item/organ/genital/G = O
|
||||
if(G.is_exposed()) //Checks appropriate clothing slot and if it's through_clothes
|
||||
genitals_to_add += H.getorganslot(G.slot)
|
||||
//Now we added all genitals that aren't internal and should be rendered
|
||||
|
||||
//start applying overlays
|
||||
for(var/layer in relevant_layers)
|
||||
var/layertext = genitals_layertext(layer)
|
||||
for(var/obj/item/organ/genital/G in genitals_to_add)
|
||||
var/datum/sprite_accessory/S
|
||||
size = G.size
|
||||
switch(G.type)
|
||||
if(/obj/item/organ/genital/penis)
|
||||
S = GLOB.cock_shapes_list[G.shape]
|
||||
if(/obj/item/organ/genital/vagina)
|
||||
S = GLOB.vagina_shapes_list[G.shape]
|
||||
if(/obj/item/organ/genital/breasts)
|
||||
S = GLOB.breasts_shapes_list[G.shape]
|
||||
|
||||
if(!S || S.icon_state == "none")
|
||||
continue
|
||||
var/mutable_appearance/genital_overlay = mutable_appearance(S.icon, layer = -layer)
|
||||
if(S.alt_aroused)
|
||||
G.aroused_state = H.isPercentAroused(G.aroused_amount)
|
||||
else
|
||||
G.aroused_state = FALSE
|
||||
genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size]_[G.aroused_state]_[layertext]"
|
||||
|
||||
if(S.center)
|
||||
genital_overlay = center_image(genital_overlay, S.dimension_x, S.dimension_y)
|
||||
|
||||
if(use_skintones && H.dna.features["genitals_use_skintone"])
|
||||
genital_overlay.color = "#[skintone2hex(H.skin_tone)]"
|
||||
else
|
||||
switch(S.color_src)
|
||||
if("cock_color")
|
||||
genital_overlay.color = "#[H.dna.features["cock_color"]]"
|
||||
if("breasts_color")
|
||||
genital_overlay.color = "#[H.dna.features["breasts_color"]]"
|
||||
if("vag_color")
|
||||
genital_overlay.color = "#[H.dna.features["vag_color"]]"
|
||||
if(MUTCOLORS)
|
||||
if(fixed_mut_color)
|
||||
genital_overlay.color = "#[fixed_mut_color]"
|
||||
else
|
||||
genital_overlay.color = "#[H.dna.features["mcolor"]]"
|
||||
if(MUTCOLORS2)
|
||||
if(fixed_mut_color2)
|
||||
genital_overlay.color = "#[fixed_mut_color2]"
|
||||
else
|
||||
genital_overlay.color = "#[H.dna.features["mcolor2"]]"
|
||||
if(MUTCOLORS3)
|
||||
if(fixed_mut_color3)
|
||||
genital_overlay.color = "#[fixed_mut_color3]"
|
||||
else
|
||||
genital_overlay.color = "#[H.dna.features["mcolor3"]]"
|
||||
standing += genital_overlay
|
||||
if(LAZYLEN(standing))
|
||||
H.overlays_standing[layer] = standing.Copy()
|
||||
standing = list()
|
||||
|
||||
for(var/L in relevant_layers)
|
||||
H.apply_overlay(L)
|
||||
@@ -1,116 +0,0 @@
|
||||
/datum/sprite_accessory
|
||||
var/alt_aroused = FALSE //CIT CODE if this is TRUE, then the genitals will use an alternate icon_state when aroused.
|
||||
|
||||
|
||||
//DICKS,COCKS,PENISES,WHATEVER YOU WANT TO CALL THEM
|
||||
/datum/sprite_accessory/penis
|
||||
icon = 'code/citadel/icons/penis_onmob.dmi'
|
||||
icon_state = null
|
||||
name = "penis" //the preview name of the accessory
|
||||
gender_specific = 0 //Might be needed somewhere down the list.
|
||||
color_src = "cock_color"
|
||||
locked = 0
|
||||
|
||||
/datum/sprite_accessory/penis/human
|
||||
icon_state = "human"
|
||||
name = "Human"
|
||||
|
||||
/datum/sprite_accessory/penis/knotted
|
||||
icon_state = "knotted"
|
||||
name = "Knotted"
|
||||
|
||||
/datum/sprite_accessory/penis/flared
|
||||
icon_state = "flared"
|
||||
name = "Flared"
|
||||
|
||||
/datum/sprite_accessory/penis/barbknot
|
||||
icon_state = "barbknot"
|
||||
name = "Barbed, Knotted"
|
||||
|
||||
/datum/sprite_accessory/penis/tapered
|
||||
icon_state = "tapered"
|
||||
name = "Tapered"
|
||||
|
||||
////////////////////////
|
||||
// Taur cocks go here //
|
||||
////////////////////////
|
||||
/datum/sprite_accessory/penis/taur_flared
|
||||
icon = 'code/citadel/icons/taur_penis_onmob.dmi' //Needed larger width
|
||||
icon_state = "flared"
|
||||
name = "Taur, Flared"
|
||||
center = TRUE //Center the image 'cause 2-tile wide.
|
||||
dimension_x = 64
|
||||
|
||||
/datum/sprite_accessory/penis/taur_knotted
|
||||
icon = 'code/citadel/icons/taur_penis_onmob.dmi' //Needed larger width
|
||||
icon_state = "knotted"
|
||||
name = "Taur, Knotted"
|
||||
center = TRUE //Center the image 'cause 2-tile wide.
|
||||
dimension_x = 64
|
||||
|
||||
/datum/sprite_accessory/penis/taur_tapered
|
||||
icon = 'code/citadel/icons/taur_penis_onmob.dmi' //Needed larger width
|
||||
icon_state = "tapered"
|
||||
name = "Taur, Tapered"
|
||||
center = TRUE //Center the image 'cause 2-tile wide.
|
||||
dimension_x = 64
|
||||
|
||||
|
||||
|
||||
|
||||
//Vaginas
|
||||
/datum/sprite_accessory/vagina
|
||||
icon = 'code/citadel/icons/vagina_onmob.dmi'
|
||||
icon_state = null
|
||||
name = "vagina"
|
||||
gender_specific = 0
|
||||
color_src = "vag_color"
|
||||
locked = 0
|
||||
alt_aroused = FALSE //if this is TRUE, then the genitals will use an alternate sprite for aroused states
|
||||
|
||||
/datum/sprite_accessory/vagina/human
|
||||
icon_state = "human"
|
||||
name = "Human"
|
||||
alt_aroused = TRUE
|
||||
|
||||
/datum/sprite_accessory/vagina/tentacles
|
||||
icon_state = "tentacle"
|
||||
name = "Tentacle"
|
||||
alt_aroused = TRUE
|
||||
|
||||
/datum/sprite_accessory/vagina/dentata
|
||||
icon_state = "dentata"
|
||||
name = "Dentata"
|
||||
alt_aroused = TRUE
|
||||
|
||||
/datum/sprite_accessory/vagina/hairy
|
||||
icon_state = "hairy"
|
||||
name = "Hairy"
|
||||
|
||||
|
||||
//BREASTS BE HERE
|
||||
/datum/sprite_accessory/breasts
|
||||
icon = 'code/citadel/icons/breasts_onmob.dmi'
|
||||
icon_state = null
|
||||
name = "breasts"
|
||||
gender_specific = 0
|
||||
color_src = "breasts_color"
|
||||
locked = 0
|
||||
|
||||
/datum/sprite_accessory/breasts/pair
|
||||
icon_state = "pair"
|
||||
name = "Pair"
|
||||
|
||||
|
||||
//OVIPOSITORS BE HERE
|
||||
/datum/sprite_accessory/ovipositor
|
||||
icon = 'code/citadel/icons/penis_onmob.dmi'
|
||||
icon_state = null
|
||||
name = "Ovipositor" //the preview name of the accessory
|
||||
gender_specific = 0 //Might be needed somewhere down the list.
|
||||
color_src = "cock_color"
|
||||
locked = 0
|
||||
|
||||
/datum/sprite_accessory/ovipositor/knotted
|
||||
icon_state = "knotted"
|
||||
name = "Knotted"
|
||||
@@ -1,16 +0,0 @@
|
||||
/obj/item/organ/genital/ovipositor
|
||||
name = "Ovipositor"
|
||||
desc = "An egg laying reproductive organ."
|
||||
icon_state = "ovi_knotted_2"
|
||||
icon = 'code/citadel/icons/ovipositor.dmi'
|
||||
zone = "groin"
|
||||
slot = "penis"
|
||||
w_class = 3
|
||||
shape = "knotted"
|
||||
size = 3
|
||||
var/length = 6 //inches
|
||||
var/girth = 0
|
||||
var/girth_ratio = COCK_GIRTH_RATIO_DEF //citadel_defines.dm for these defines
|
||||
var/knot_girth_ratio = KNOT_GIRTH_RATIO_DEF
|
||||
var/list/oviflags = list()
|
||||
var/obj/item/organ/eggsack/linked_eggsack
|
||||
@@ -1,66 +0,0 @@
|
||||
/obj/item/organ/genital/penis
|
||||
name = "penis"
|
||||
desc = "A male reproductive organ."
|
||||
icon_state = "penis"
|
||||
icon = 'code/citadel/icons/penis.dmi'
|
||||
zone = "groin"
|
||||
slot = "penis"
|
||||
w_class = 3
|
||||
can_masturbate_with = TRUE
|
||||
masturbation_verb = "stroke"
|
||||
can_climax = TRUE
|
||||
fluid_transfer_factor = 0.5
|
||||
size = 2 //arbitrary value derived from length and girth for sprites.
|
||||
var/length = 6 //inches
|
||||
var/cached_length //used to detect a change in length
|
||||
var/girth = 0
|
||||
var/girth_ratio = COCK_GIRTH_RATIO_DEF //0.73; check citadel_defines.dm
|
||||
var/knot_girth_ratio = KNOT_GIRTH_RATIO_DEF
|
||||
var/list/dickflags = list()
|
||||
var/list/knotted_types = list("knotted", "barbed, knotted")
|
||||
|
||||
/obj/item/organ/genital/penis/update_size()
|
||||
if(length == cached_length)
|
||||
return
|
||||
switch(length)
|
||||
if(-INFINITY to 5)
|
||||
size = 1
|
||||
if(5 to 9)
|
||||
size = 2
|
||||
if(9 to INFINITY)
|
||||
size = 3//no new sprites for anything larger yet
|
||||
/* if(9 to 15)
|
||||
size = 3
|
||||
if(15 to INFINITY)
|
||||
size = 3*/
|
||||
girth = (length * girth_ratio)
|
||||
cached_length = length
|
||||
|
||||
/obj/item/organ/genital/penis/update_appearance()
|
||||
var/string = "penis_[GLOB.cock_shapes_icons[shape]]_[size]"
|
||||
icon_state = sanitize_text(string)
|
||||
var/lowershape = lowertext(shape)
|
||||
desc = "You see a [lowershape] penis. You estimate it's about [round(length, 0.25)] inch[length > 1 ? "es" : ""] long."
|
||||
if(owner)
|
||||
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
|
||||
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
|
||||
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
|
||||
color = "#[skintone2hex(H.skin_tone)]"
|
||||
else
|
||||
color = "#[owner.dna.features["cock_color"]]"
|
||||
|
||||
/obj/item/organ/genital/penis/update_link()
|
||||
if(owner)
|
||||
linked_organ = (owner.getorganslot("testicles"))
|
||||
if(linked_organ)
|
||||
linked_organ.linked_organ = src
|
||||
else
|
||||
if(linked_organ)
|
||||
linked_organ.linked_organ = null
|
||||
linked_organ = null
|
||||
|
||||
/obj/item/organ/genital/penis/is_exposed()
|
||||
. = ..()
|
||||
if(.)
|
||||
return TRUE
|
||||
return owner.is_groin_exposed()
|
||||
@@ -1,54 +0,0 @@
|
||||
/obj/item/organ/genital/testicles
|
||||
name = "testicles"
|
||||
desc = "A male reproductive organ."
|
||||
icon_state = "testicles"
|
||||
icon = 'code/citadel/icons/penis.dmi'
|
||||
zone = "groin"
|
||||
slot = "testicles"
|
||||
w_class = 3
|
||||
internal = TRUE
|
||||
size = BALLS_SIZE_DEF
|
||||
var/sack_size = BALLS_SACK_SIZE_DEF
|
||||
fluid_id = "semen"
|
||||
producing = TRUE
|
||||
var/sent_full_message = 1 //defaults to 1 since they're full to start
|
||||
|
||||
/obj/item/organ/genital/testicles/Initialize()
|
||||
. = ..()
|
||||
reagents.add_reagent(fluid_id, fluid_max_volume)
|
||||
|
||||
/obj/item/organ/genital/testicles/on_life()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
if(reagents && producing)
|
||||
generate_cum()
|
||||
|
||||
/obj/item/organ/genital/testicles/proc/generate_cum()
|
||||
reagents.maximum_volume = fluid_max_volume
|
||||
if(reagents.total_volume >= reagents.maximum_volume)
|
||||
if(!sent_full_message)
|
||||
send_full_message()
|
||||
sent_full_message = 1
|
||||
return FALSE
|
||||
sent_full_message = 0
|
||||
update_link()
|
||||
if(!linked_organ)
|
||||
return FALSE
|
||||
reagents.isolate_reagent(fluid_id)//remove old reagents if it changed and just clean up generally
|
||||
reagents.add_reagent(fluid_id, (fluid_mult * fluid_rate))//generate the cum
|
||||
|
||||
/obj/item/organ/genital/testicles/update_link()
|
||||
if(owner && !QDELETED(src))
|
||||
linked_organ = (owner.getorganslot("penis"))
|
||||
if(linked_organ)
|
||||
linked_organ.linked_organ = src
|
||||
else
|
||||
if(linked_organ)
|
||||
linked_organ.linked_organ = null
|
||||
linked_organ = null
|
||||
|
||||
/obj/item/organ/genital/testicles/proc/send_full_message(msg = "Your balls finally feel full, again.")
|
||||
if(owner && istext(msg))
|
||||
owner << msg
|
||||
return TRUE
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/obj/item/organ/genital/vagina
|
||||
name = "vagina"
|
||||
desc = "A female reproductive organ."
|
||||
icon = 'code/citadel/icons/vagina.dmi'
|
||||
icon_state = "vagina"
|
||||
zone = "groin"
|
||||
slot = "vagina"
|
||||
size = 1 //There is only 1 size right now
|
||||
can_masturbate_with = TRUE
|
||||
masturbation_verb = "finger"
|
||||
can_climax = TRUE
|
||||
fluid_transfer_factor = 0.1 //Yes, some amount is exposed to you, go get your AIDS
|
||||
w_class = 3
|
||||
var/cap_length = 8//D E P T H (cap = capacity)
|
||||
var/cap_girth = 12
|
||||
var/cap_girth_ratio = 1.5
|
||||
var/clits = 1
|
||||
var/clit_diam = 0.25
|
||||
var/clit_len = 0.25
|
||||
var/list/vag_types = list("tentacle", "dentata", "hairy")
|
||||
|
||||
|
||||
/obj/item/organ/genital/vagina/update_appearance()
|
||||
var/string = "vagina" //Keeping this code here, so making multiple sprites for the different kinds is easier.
|
||||
icon_state = sanitize_text(string)
|
||||
var/lowershape = lowertext(shape)
|
||||
var/details
|
||||
|
||||
switch(lowershape)
|
||||
if("tentacle")
|
||||
details = "Its opening is lined with several tentacles and "
|
||||
if("dentata")
|
||||
details = "There's teeth inside it and it "
|
||||
if("hairy")
|
||||
details = "It has quite a bit of hair growing on it and "
|
||||
if("human")
|
||||
details = "It is taut with smooth skin, though without much hair and "
|
||||
if("gaping")
|
||||
details = "It is gaping slightly open, though without much hair and "
|
||||
else
|
||||
details = "It has an exotic shape and "
|
||||
if(aroused_state)
|
||||
details += "is slick with female arousal."
|
||||
else
|
||||
details += "seems to be dry."
|
||||
|
||||
desc = "You see a vagina. [details]"
|
||||
|
||||
if(owner)
|
||||
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
|
||||
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
|
||||
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
|
||||
color = "#[skintone2hex(H.skin_tone)]"
|
||||
else
|
||||
color = "#[owner.dna.features["vag_color"]]"
|
||||
|
||||
|
||||
/obj/item/organ/genital/vagina/update_link()
|
||||
if(owner)
|
||||
linked_organ = (owner.getorganslot("womb"))
|
||||
if(linked_organ)
|
||||
linked_organ.linked_organ = src
|
||||
else
|
||||
if(linked_organ)
|
||||
linked_organ.linked_organ = null
|
||||
linked_organ = null
|
||||
|
||||
/obj/item/organ/genital/vagina/is_exposed()
|
||||
. = ..()
|
||||
if(.)
|
||||
return TRUE
|
||||
return owner.is_groin_exposed()
|
||||
@@ -1,43 +0,0 @@
|
||||
/obj/item/organ/genital/womb
|
||||
name = "womb"
|
||||
desc = "A female reproductive organ."
|
||||
icon = 'code/citadel/icons/vagina.dmi'
|
||||
icon_state = "womb"
|
||||
zone = "groin"
|
||||
slot = "womb"
|
||||
w_class = 3
|
||||
internal = TRUE
|
||||
fluid_id = "femcum"
|
||||
producing = TRUE
|
||||
|
||||
|
||||
/obj/item/organ/genital/womb/Initialize()
|
||||
. = ..()
|
||||
reagents.add_reagent(fluid_id, fluid_max_volume)
|
||||
|
||||
/obj/item/organ/genital/womb/on_life()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
if(reagents && producing)
|
||||
generate_femcum()
|
||||
|
||||
/obj/item/organ/genital/womb/proc/generate_femcum()
|
||||
reagents.maximum_volume = fluid_max_volume
|
||||
update_link()
|
||||
if(!linked_organ)
|
||||
return FALSE
|
||||
reagents.isolate_reagent(fluid_id)//remove old reagents if it changed and just clean up generally
|
||||
reagents.add_reagent(fluid_id, (fluid_mult * fluid_rate))//generate the cum
|
||||
|
||||
/obj/item/organ/genital/womb/update_link()
|
||||
if(owner)
|
||||
linked_organ = (owner.getorganslot("vagina"))
|
||||
if(linked_organ)
|
||||
linked_organ.linked_organ = src
|
||||
else
|
||||
if(linked_organ)
|
||||
linked_organ.linked_organ = null
|
||||
linked_organ = null
|
||||
|
||||
/obj/item/organ/genital/womb/Destroy()
|
||||
return ..()
|
||||
@@ -1,23 +0,0 @@
|
||||
/obj/structure/guncase/plasma
|
||||
name = "plasma rifle locker"
|
||||
desc = "A locker that holds plasma rifles. Only opens in dire emergencies."
|
||||
icon_state = "ecase"
|
||||
case_type = "egun"
|
||||
gun_category = /obj/item/gun/energy/plasma
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF //because fuck you, powergaming nerds.
|
||||
|
||||
/obj/structure/guncase/plasma/attackby(obj/item/W, mob/user, params)
|
||||
return
|
||||
|
||||
/obj/structure/guncase/plasma/MouseDrop(over_object, src_location, over_location)
|
||||
if(GLOB.security_level == SEC_LEVEL_RED || GLOB.security_level == SEC_LEVEL_DELTA)
|
||||
. = ..()
|
||||
else
|
||||
to_chat(usr, "The storage unit will only unlock during a Red or Delta security alert.")
|
||||
|
||||
/obj/structure/guncase/plasma/attack_hand(mob/user)
|
||||
return MouseDrop(user)
|
||||
|
||||
/obj/structure/guncase/plasma/emag_act()
|
||||
to_chat(usr, "The locking mechanism is fitted with old style parts, The card has no effect.")
|
||||
return
|
||||
@@ -1,306 +0,0 @@
|
||||
//Sprites are trademarks of Gamefreak, Nintendo, The Pokemon Company, and Spike Chunsoft.
|
||||
#define ispokemon(A) (istype(A, /mob/living/simple_animal/pokemon))
|
||||
//POKEBALL
|
||||
/obj/item/pokeball
|
||||
name = "pokeball"
|
||||
icon = 'icons/obj/pokeball.dmi'
|
||||
icon_state = "pokeball"
|
||||
force = 0
|
||||
throwforce = 0
|
||||
var/success_chance = 25
|
||||
var/pokemon
|
||||
/obj/item/pokeball/great
|
||||
name = "great ball"
|
||||
icon_state = "pokeball_great"
|
||||
success_chance = 50
|
||||
/obj/item/pokeball/ultra
|
||||
icon_state = "pokeball_ultra"
|
||||
name = "ultra ball"
|
||||
success_chance = 75
|
||||
/obj/item/pokeball/master
|
||||
icon_state = "pokeball_master"
|
||||
name = "master ball"
|
||||
success_chance = 100
|
||||
/* //WIP
|
||||
/obj/item/pokeball/throw_impact(atom/hit_atom)
|
||||
if(ispokemon(hit_atom))
|
||||
var/mob/living/simple_animal/pokemon/pmon = hit_atom
|
||||
var/initial_success_chance = success_chance
|
||||
pmon.resize = 0.1
|
||||
pmon.color = "RED"
|
||||
pmon.canmove = 0
|
||||
sleep(15)
|
||||
if(pmon.pokeball == src)
|
||||
pmon.loc = src
|
||||
pokemon = pmon
|
||||
|
||||
return 1
|
||||
if(pmon.pokeball && pmon.pokeball !=src)
|
||||
return ..()
|
||||
var/bonus_chance = ((pmon.maxHealth - pmon.health) / 2)
|
||||
if(bonus_chance > 100)
|
||||
bonus_chance = 100
|
||||
success_chance = (success_chance + bonus_chance)
|
||||
if(success_chance > 100)
|
||||
success_chance = 100
|
||||
if(success_chance < 0)//just in case
|
||||
success_chance = 0
|
||||
sleep(15)
|
||||
if(prob(success_chance))
|
||||
visible_message("<span class='warning'>[src] shakes...</span>")
|
||||
else
|
||||
escape()
|
||||
sleep(15)
|
||||
if(prob(success_chance))
|
||||
visible_message("<span class='warning'>[src] shakes...</span>")
|
||||
else
|
||||
escape()
|
||||
sleep(15)
|
||||
if(prob(success_chance))
|
||||
visible_message("<span class='warning'>[src] shakes...</span>")
|
||||
else
|
||||
escape()
|
||||
else
|
||||
..()
|
||||
/obj/item/pokeball/proc/capture(mob/living/simple_animal/pokemon/pmon, mob/living/user)
|
||||
|
||||
/obj/item/pokeball/proc/escape(mob/living/simple_animal/pokemon/pmon, mob/living/user)
|
||||
if(!pokemon)
|
||||
return
|
||||
pmon.resize = 10
|
||||
pmon.color = null
|
||||
pmon.canmove = 1
|
||||
pmon.loc = src.loc
|
||||
if(pmon.pokeball != src)
|
||||
visible_message("<span class='warning'>[pmon] breaks free from [src]</span>")
|
||||
PoolOrNew(/obj/effect/particle_effect/sparks, loc)
|
||||
playsound(src.loc, "sparks", 50, 1)
|
||||
qdel(src)
|
||||
|
||||
else
|
||||
/obj/item/pokeball/proc/recall
|
||||
/obj/item/pokeball/proc/release
|
||||
*/
|
||||
/mob/living/simple_animal/pokemon
|
||||
name = "eevee"
|
||||
icon_state = "eevee"
|
||||
icon_living = "eevee"
|
||||
icon_dead = "eevee_d"
|
||||
desc = "Gotta catch 'em all!"
|
||||
icon = 'icons/mob/pokemon.dmi'
|
||||
var/pokeball
|
||||
pixel_x = -16
|
||||
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 5)
|
||||
ventcrawler = 2
|
||||
health = 100
|
||||
maxHealth = 100
|
||||
layer = 4
|
||||
response_help = "pets"
|
||||
wander = 1
|
||||
turns_per_move = 2
|
||||
pass_flags = PASSTABLE | PASSMOB
|
||||
|
||||
/mob/living/simple_animal/pokemon/proc/simple_lay_down()
|
||||
set name = "Rest"
|
||||
set category = "IC"
|
||||
|
||||
resting = !resting
|
||||
src << "<span class='notice'>You are now [resting ? "resting" : "getting up"].</span>"
|
||||
update_canmove()
|
||||
update_icon()
|
||||
|
||||
/mob/living/simple_animal/pokemon/proc/update_icon()
|
||||
if(lying || resting || sleeping)
|
||||
icon_state = "[icon_state]_rest"
|
||||
else
|
||||
icon_state = "[icon_living]"
|
||||
|
||||
/mob/living/simple_animal/pokemon/New()
|
||||
..()
|
||||
verbs += /mob/living/simple_animal/pokemon/proc/simple_lay_down
|
||||
|
||||
/*
|
||||
/////TEMPLATE/////
|
||||
|
||||
/mob/living/simple_animal/pokemon/
|
||||
name = ""
|
||||
icon_state = ""
|
||||
icon_living = ""
|
||||
icon_dead = ""
|
||||
*/
|
||||
|
||||
/mob/living/simple_animal/pokemon/leg
|
||||
icon = 'icons/mob/legendary.dmi'
|
||||
pixel_x = -32
|
||||
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 12)
|
||||
health = 200
|
||||
maxHealth = 200
|
||||
|
||||
|
||||
/mob/living/simple_animal/pokemon/leg/articuno
|
||||
name = "Articuno"
|
||||
icon_state = "articuno"
|
||||
icon_living = "articuno"
|
||||
icon_dead = "articuno_d"
|
||||
flying = 1
|
||||
|
||||
/mob/living/simple_animal/pokemon/leg/rayquaza
|
||||
name = "Rayquaza"
|
||||
icon_state = "rayquaza"
|
||||
icon_living = "rayquaza"
|
||||
icon_dead = "rayquaza_d"
|
||||
flying = 1
|
||||
|
||||
//ALPHABETICAL PLEASE
|
||||
|
||||
/mob/living/simple_animal/pokemon/absol
|
||||
name = "absol"
|
||||
icon_state = "absol"
|
||||
icon_living = "absol"
|
||||
icon_dead = "absol_d"
|
||||
speak = list("Absol!", "Ab-Absol!")
|
||||
|
||||
/mob/living/simple_animal/pokemon/aggron
|
||||
name = "aggron"
|
||||
icon_state = "aggron"
|
||||
icon_living = "aggron"
|
||||
icon_dead = "aggron_d"
|
||||
|
||||
/mob/living/simple_animal/pokemon/ampharos
|
||||
name = "ampharos"
|
||||
icon_state = "ampharos"
|
||||
icon_living = "ampharos"
|
||||
icon_dead = "ampharos_d"
|
||||
|
||||
/mob/living/simple_animal/pokemon/charmander
|
||||
name = "charmander"
|
||||
icon_state = "charmander"
|
||||
icon_living = "charmander"
|
||||
icon_dead = "charmander_d"
|
||||
|
||||
/mob/living/simple_animal/pokemon/ditto
|
||||
name = "ditto"
|
||||
icon_state = "ditto"
|
||||
icon_living = "ditto"
|
||||
icon_dead = "ditto_d"
|
||||
|
||||
/mob/living/simple_animal/pokemon/dratini/dragonair
|
||||
name = "dragonair"
|
||||
desc = "A Dragonair stores an enormous amount of energy inside its body. It is said to alter the weather around it by loosing energy from the crystals on its neck and tail."
|
||||
icon_state = "dragonair"
|
||||
icon_living = "dragonair"
|
||||
icon_dead = "dragonair_d"
|
||||
|
||||
/mob/living/simple_animal/pokemon/dratini/dragonair/dragonite
|
||||
name = "dragonite"
|
||||
desc = "It can circle the globe in just 16 hours. It is a kindhearted Pokémon that leads lost and foundering ships in a storm to the safety of land."
|
||||
icon_state = "dragonite"
|
||||
icon_living = "dragonite"
|
||||
icon_dead = "dragonite_d"
|
||||
|
||||
/mob/living/simple_animal/pokemon/dratini
|
||||
name = "dratini"
|
||||
desc = "A Dratini continually molts and sloughs off its old skin. It does so because the life energy within its body steadily builds to reach uncontrollable levels."
|
||||
icon_state = "dratini"
|
||||
icon_living = "dratini"
|
||||
icon_dead = "dratini_d"
|
||||
|
||||
/mob/living/simple_animal/pokemon/eevee
|
||||
name = "eevee"
|
||||
desc = "Eevee has an unstable genetic makeup that suddenly mutates due to its environment. Radiation from various stones causes this Pokémon to evolve."
|
||||
icon_state = "eevee"
|
||||
icon_living = "eevee"
|
||||
icon_dead = "eevee_d"
|
||||
speak = list("Eevee!", "Ee-Eevee!")
|
||||
response_help = "pets"
|
||||
response_harm = "hits"
|
||||
|
||||
/mob/living/simple_animal/pokemon/eevee/espeon
|
||||
name = "espeon"
|
||||
desc = "Espeon is extremely loyal to any trainer it considers to be worthy. It is said to have developed precognitive powers to protect its trainer from harm."
|
||||
icon_state = "espeon"
|
||||
icon_living = "espeon"
|
||||
icon_dead = "espeon_d"
|
||||
|
||||
/mob/living/simple_animal/pokemon/flaaffy
|
||||
name = "flaaffy"
|
||||
icon_state = "flaaffy"
|
||||
icon_living = "flaaffy"
|
||||
icon_dead = "flaaffy_d"
|
||||
|
||||
/mob/living/simple_animal/pokemon/eevee/flareon
|
||||
name = "flareon"
|
||||
desc = "Flareon's fluffy fur releases heat into the air so that its body does not get excessively hot. Its body temperature can rise to a maximum of 1,650 degrees F."
|
||||
icon_state = "flareon"
|
||||
icon_living = "flareon"
|
||||
icon_dead = "flareon_d"
|
||||
speak = list("Flare!", "Flareon!")
|
||||
|
||||
/mob/living/simple_animal/pokemon/eevee/glaceon
|
||||
name = "glaceon"
|
||||
desc = "By controlling its body heat, it can freeze the atmosphere around it to make a diamond-dust flurry."
|
||||
icon_state = "glaceon"
|
||||
icon_living = "glaceon"
|
||||
icon_dead = "glaceon_d"
|
||||
speak = list("Glace!", "Glaceon!")
|
||||
|
||||
/mob/living/simple_animal/pokemon/eevee/jolteon
|
||||
name = "jolteon"
|
||||
desc = "Its cells generate weak power that is amplified by its fur's static electricity to drop thunderbolts. The bristling fur is made of electrically charged needles."
|
||||
icon_state = "jolteon"
|
||||
icon_living = "jolteon"
|
||||
icon_dead = "jolteon_d"
|
||||
speak = list("Jolt!", "Jolteon!")
|
||||
|
||||
/mob/living/simple_animal/pokemon/larvitar
|
||||
name = "larvitar"
|
||||
desc = "It is born deep underground. It can't emerge until it has entirely consumed the soil around it."
|
||||
icon_state = "larvitar"
|
||||
icon_living = "larvitar"
|
||||
icon_dead = "larvitar_d"
|
||||
|
||||
/mob/living/simple_animal/pokemon/mareep
|
||||
name = "mareep"
|
||||
icon_state = "mareep"
|
||||
icon_living = "mareep"
|
||||
icon_dead = "mareep_d"
|
||||
|
||||
/mob/living/simple_animal/pokemon/poochyena/mightyena
|
||||
name = "mightyena"
|
||||
icon_state = "mightyena"
|
||||
icon_living = "mightyena"
|
||||
icon_dead = "mightyena"
|
||||
|
||||
/mob/living/simple_animal/pokemon/miltank
|
||||
name = "miltank"
|
||||
icon_state = "miltank"
|
||||
icon_living = "miltank"
|
||||
icon_dead = "miltank_d"
|
||||
|
||||
/mob/living/simple_animal/pokemon/poochyena
|
||||
name = "poochyena"
|
||||
icon_state = "poochyena"
|
||||
icon_living = "poochyena"
|
||||
icon_dead = "poochyena_d"
|
||||
|
||||
/mob/living/simple_animal/pokemon/eevee/sylveon
|
||||
name = "Sylveon"
|
||||
desc = "Sylveon, the Intertwining Pokémon. Sylveon affectionately wraps its ribbon-like feelers around its Trainer's arm as they walk together."
|
||||
icon_state = "sylveon"
|
||||
icon_living = "sylveon"
|
||||
icon_dead = "sylveon_d"
|
||||
speak = list("Sylveon!", "Syl!")
|
||||
response_help = "pets"
|
||||
response_harm = "hits"
|
||||
|
||||
/mob/living/simple_animal/pokemon/eevee/umbreon
|
||||
name = "umbreon"
|
||||
icon_state = "umbreon"
|
||||
icon_dead = "umbreon_d"
|
||||
icon_living = "umbreon"
|
||||
|
||||
/mob/living/simple_animal/pokemon/vulpix
|
||||
name = "vulpix"
|
||||
icon_state = "vulpix"
|
||||
icon_living = "vulpix"
|
||||
icon_dead = "vulpix_d"
|
||||
@@ -1,132 +0,0 @@
|
||||
//////////
|
||||
//DILDOS//
|
||||
//////////
|
||||
obj/item/dildo
|
||||
name = "dildo"
|
||||
desc = "Floppy!"
|
||||
icon = 'code/citadel/icons/dildo.dmi'
|
||||
damtype = BRUTE
|
||||
force = 0
|
||||
throwforce = 0
|
||||
icon_state = "dildo_knotted_2"
|
||||
alpha = 192//transparent
|
||||
var/can_customize = FALSE
|
||||
var/dildo_shape = "human"
|
||||
var/dildo_size = 2
|
||||
var/dildo_type = "dildo"//pretty much just used for the icon state
|
||||
var/random_color = TRUE
|
||||
var/random_size = FALSE
|
||||
var/random_shape = FALSE
|
||||
//Lists moved to _cit_helpers.dm as globals so they're not instanced individually
|
||||
|
||||
obj/item/dildo/proc/update_appearance()
|
||||
icon_state = "[dildo_type]_[dildo_shape]_[dildo_size]"
|
||||
var/sizeword = ""
|
||||
switch(dildo_size)
|
||||
if(1)
|
||||
sizeword = "small "
|
||||
if(3)
|
||||
sizeword = "big "
|
||||
if(4)
|
||||
sizeword = "huge "
|
||||
if(5)
|
||||
sizeword = "gigantic "
|
||||
|
||||
name = "[sizeword][dildo_shape] [can_customize ? "custom " : ""][dildo_type]"
|
||||
|
||||
obj/item/dildo/AltClick(mob/living/user)
|
||||
if(QDELETED(src))
|
||||
return
|
||||
if(!isliving(user))
|
||||
return
|
||||
if(isAI(user))
|
||||
return
|
||||
if(user.stat > 0)//unconscious or dead
|
||||
return
|
||||
customize(user)
|
||||
|
||||
obj/item/dildo/proc/customize(mob/living/user)
|
||||
if(!can_customize)
|
||||
return FALSE
|
||||
if(src && !user.incapacitated() && in_range(user,src))
|
||||
var/color_choice = input(user,"Choose a color for your dildo.","Dildo Color") as null|anything in GLOB.dildo_colors
|
||||
if(src && color_choice && !user.incapacitated() && in_range(user,src))
|
||||
sanitize_inlist(color_choice, GLOB.dildo_colors, "Red")
|
||||
color = GLOB.dildo_colors[color_choice]
|
||||
update_appearance()
|
||||
if(src && !user.incapacitated() && in_range(user,src))
|
||||
var/shape_choice = input(user,"Choose a shape for your dildo.","Dildo Shape") as null|anything in GLOB.dildo_shapes
|
||||
if(src && shape_choice && !user.incapacitated() && in_range(user,src))
|
||||
sanitize_inlist(shape_choice, GLOB.dildo_colors, "Knotted")
|
||||
dildo_shape = GLOB.dildo_shapes[shape_choice]
|
||||
update_appearance()
|
||||
if(src && !user.incapacitated() && in_range(user,src))
|
||||
var/size_choice = input(user,"Choose the size for your dildo.","Dildo Size") as null|anything in GLOB.dildo_sizes
|
||||
if(src && size_choice && !user.incapacitated() && in_range(user,src))
|
||||
sanitize_inlist(size_choice, GLOB.dildo_colors, "Medium")
|
||||
dildo_size = GLOB.dildo_sizes[size_choice]
|
||||
update_appearance()
|
||||
if(src && !user.incapacitated() && in_range(user,src))
|
||||
var/transparency_choice = input(user,"Choose the transparency of your dildo. Lower is more transparent!(192-255)","Dildo Transparency") as null|num
|
||||
if(src && transparency_choice && !user.incapacitated() && in_range(user,src))
|
||||
sanitize_integer(transparency_choice, 192, 255, 192)
|
||||
alpha = transparency_choice
|
||||
update_appearance()
|
||||
return TRUE
|
||||
|
||||
obj/item/dildo/Initialize()
|
||||
. = ..()
|
||||
if(random_color == TRUE)
|
||||
var/randcolor = pick(GLOB.dildo_colors)
|
||||
color = GLOB.dildo_colors[randcolor]
|
||||
if(random_shape == TRUE)
|
||||
var/randshape = pick(GLOB.dildo_shapes)
|
||||
dildo_shape = GLOB.dildo_shapes[randshape]
|
||||
if(random_size == TRUE)
|
||||
var/randsize = pick(GLOB.dildo_sizes)
|
||||
dildo_size = GLOB.dildo_sizes[randsize]
|
||||
update_appearance()
|
||||
alpha = rand(192, 255)
|
||||
pixel_y = rand(-7,7)
|
||||
pixel_x = rand(-7,7)
|
||||
|
||||
obj/item/dildo/examine(mob/user)
|
||||
..()
|
||||
if(can_customize)
|
||||
user << "<span class='notice'>Alt-Click \the [src.name] to customize it.</span>"
|
||||
|
||||
obj/item/dildo/random//totally random
|
||||
name = "random dildo"//this name will show up in vendors and shit so you know what you're vending(or don't, i guess :^))
|
||||
random_color = TRUE
|
||||
random_shape = TRUE
|
||||
random_size = TRUE
|
||||
|
||||
|
||||
obj/item/dildo/knotted
|
||||
dildo_shape = "knotted"
|
||||
name = "knotted dildo"
|
||||
|
||||
obj/item/dildo/human
|
||||
dildo_shape = "human"
|
||||
name = "human dildo"
|
||||
|
||||
obj/item/dildo/plain
|
||||
dildo_shape = "plain"
|
||||
name = "plain dildo"
|
||||
|
||||
obj/item/dildo/flared
|
||||
dildo_shape = "flared"
|
||||
name = "flared dildo"
|
||||
|
||||
obj/item/dildo/flared/huge
|
||||
name = "literal horse cock"
|
||||
desc = "THIS THING IS HUGE!"
|
||||
dildo_size = 4
|
||||
|
||||
obj/item/dildo/custom
|
||||
name = "customizable dildo"
|
||||
desc = "Thanks to significant advances in synthetic nanomaterials, this dildo is capable of taking on many different forms to fit the user's preferences! Pricy!"
|
||||
can_customize = TRUE
|
||||
random_color = TRUE
|
||||
random_shape = TRUE
|
||||
random_size = TRUE
|
||||
@@ -1,287 +0,0 @@
|
||||
GLOBAL_VAR_INIT(config_dir, "config/")
|
||||
GLOBAL_PROTECT(config_dir)
|
||||
|
||||
/datum/controller/configuration
|
||||
name = "Configuration"
|
||||
|
||||
var/hiding_entries_by_type = TRUE //Set for readability, admins can set this to FALSE if they want to debug it
|
||||
var/list/entries
|
||||
var/list/entries_by_type
|
||||
|
||||
var/list/maplist
|
||||
var/datum/map_config/defaultmap
|
||||
|
||||
var/list/modes // allowed modes
|
||||
var/list/gamemode_cache
|
||||
var/list/votable_modes // votable modes
|
||||
var/list/mode_names
|
||||
var/list/mode_reports
|
||||
var/list/mode_false_report_weight
|
||||
|
||||
/datum/controller/configuration/New()
|
||||
config = src
|
||||
var/list/config_files = InitEntries()
|
||||
LoadModes()
|
||||
for(var/I in config_files)
|
||||
LoadEntries(I)
|
||||
if(Get(/datum/config_entry/flag/maprotation))
|
||||
loadmaplist(CONFIG_MAPS_FILE)
|
||||
|
||||
/datum/controller/configuration/Destroy()
|
||||
entries_by_type.Cut()
|
||||
QDEL_LIST_ASSOC_VAL(entries)
|
||||
QDEL_LIST_ASSOC_VAL(maplist)
|
||||
QDEL_NULL(defaultmap)
|
||||
|
||||
config = null
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/controller/configuration/proc/InitEntries()
|
||||
var/list/_entries = list()
|
||||
entries = _entries
|
||||
var/list/_entries_by_type = list()
|
||||
entries_by_type = _entries_by_type
|
||||
|
||||
. = list()
|
||||
|
||||
for(var/I in typesof(/datum/config_entry)) //typesof is faster in this case
|
||||
var/datum/config_entry/E = I
|
||||
if(initial(E.abstract_type) == I)
|
||||
continue
|
||||
E = new I
|
||||
_entries_by_type[I] = E
|
||||
var/esname = E.name
|
||||
var/datum/config_entry/test = _entries[esname]
|
||||
if(test)
|
||||
log_config("Error: [test.type] has the same name as [E.type]: [esname]! Not initializing [E.type]!")
|
||||
qdel(E)
|
||||
continue
|
||||
_entries[esname] = E
|
||||
.[E.resident_file] = TRUE
|
||||
|
||||
/datum/controller/configuration/proc/RemoveEntry(datum/config_entry/CE)
|
||||
entries -= CE.name
|
||||
entries_by_type -= CE.type
|
||||
|
||||
/datum/controller/configuration/proc/LoadEntries(filename)
|
||||
log_config("Loading config file [filename]...")
|
||||
var/list/lines = world.file2list("[GLOB.config_dir][filename]")
|
||||
var/list/_entries = entries
|
||||
for(var/L in lines)
|
||||
if(!L)
|
||||
continue
|
||||
|
||||
if(copytext(L, 1, 2) == "#")
|
||||
continue
|
||||
|
||||
var/pos = findtext(L, " ")
|
||||
var/entry = null
|
||||
var/value = null
|
||||
|
||||
if(pos)
|
||||
entry = lowertext(copytext(L, 1, pos))
|
||||
value = copytext(L, pos + 1)
|
||||
else
|
||||
entry = lowertext(L)
|
||||
|
||||
if(!entry)
|
||||
continue
|
||||
|
||||
var/datum/config_entry/E = _entries[entry]
|
||||
if(!E)
|
||||
log_config("Unknown setting in configuration: '[entry]'")
|
||||
continue
|
||||
|
||||
if(filename != E.resident_file)
|
||||
log_config("Found [entry] in [filename] when it should have been in [E.resident_file]! Ignoring.")
|
||||
continue
|
||||
|
||||
var/validated = E.ValidateAndSet(value)
|
||||
if(!validated)
|
||||
log_config("Failed to validate setting \"[value]\" for [entry]")
|
||||
else if(E.modified && !E.dupes_allowed)
|
||||
log_config("Duplicate setting for [entry] ([value]) detected! Using latest.")
|
||||
|
||||
if(validated)
|
||||
E.modified = TRUE
|
||||
|
||||
/datum/controller/configuration/can_vv_get(var_name)
|
||||
return (var_name != "entries_by_type" || !hiding_entries_by_type) && ..()
|
||||
|
||||
/datum/controller/configuration/vv_edit_var(var_name, var_value)
|
||||
return !(var_name in list("entries_by_type", "entries")) && ..()
|
||||
|
||||
/datum/controller/configuration/stat_entry()
|
||||
if(!statclick)
|
||||
statclick = new/obj/effect/statclick/debug(null, "Edit", src)
|
||||
stat("[name]:", statclick)
|
||||
|
||||
/datum/controller/configuration/proc/Get(entry_type)
|
||||
if(IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Get" && GLOB.LastAdminCalledTargetRef == "\ref[src]")
|
||||
log_admin_private("Config access of [entry_type] attempted by [key_name(usr)]")
|
||||
return
|
||||
var/datum/config_entry/E = entry_type
|
||||
var/entry_is_abstract = initial(E.abstract_type) == entry_type
|
||||
if(entry_is_abstract)
|
||||
CRASH("Tried to retrieve an abstract config_entry: [entry_type]")
|
||||
E = entries_by_type[entry_type]
|
||||
if(!E)
|
||||
CRASH("Missing config entry for [entry_type]!")
|
||||
return E.value
|
||||
|
||||
/datum/controller/configuration/proc/Set(entry_type, new_val)
|
||||
if(IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Set" && GLOB.LastAdminCalledTargetRef == "\ref[src]")
|
||||
log_admin_private("Config rewrite of [entry_type] to [new_val] attempted by [key_name(usr)]")
|
||||
return
|
||||
var/datum/config_entry/E = entry_type
|
||||
var/entry_is_abstract = initial(E.abstract_type) == entry_type
|
||||
if(entry_is_abstract)
|
||||
CRASH("Tried to retrieve an abstract config_entry: [entry_type]")
|
||||
E = entries_by_type[entry_type]
|
||||
if(!E)
|
||||
CRASH("Missing config entry for [entry_type]!")
|
||||
return E.ValidateAndSet(new_val)
|
||||
|
||||
/datum/controller/configuration/proc/LoadModes()
|
||||
gamemode_cache = typecacheof(/datum/game_mode, TRUE)
|
||||
modes = list()
|
||||
mode_names = list()
|
||||
mode_reports = list()
|
||||
mode_false_report_weight = list()
|
||||
votable_modes = list()
|
||||
var/list/probabilities = Get(/datum/config_entry/keyed_number_list/probability)
|
||||
for(var/T in gamemode_cache)
|
||||
// I wish I didn't have to instance the game modes in order to look up
|
||||
// their information, but it is the only way (at least that I know of).
|
||||
var/datum/game_mode/M = new T()
|
||||
|
||||
if(M.config_tag)
|
||||
if(!(M.config_tag in modes)) // ensure each mode is added only once
|
||||
modes += M.config_tag
|
||||
mode_names[M.config_tag] = M.name
|
||||
probabilities[M.config_tag] = M.probability
|
||||
mode_reports[M.config_tag] = M.generate_report()
|
||||
mode_false_report_weight[M.config_tag] = M.false_report_weight
|
||||
if(M.votable)
|
||||
votable_modes += M.config_tag
|
||||
qdel(M)
|
||||
votable_modes += "secret"
|
||||
|
||||
/datum/controller/configuration/proc/loadmaplist(filename)
|
||||
filename = "[GLOB.config_dir][filename]"
|
||||
var/list/Lines = world.file2list(filename)
|
||||
|
||||
var/datum/map_config/currentmap = null
|
||||
for(var/t in Lines)
|
||||
if(!t)
|
||||
continue
|
||||
|
||||
t = trim(t)
|
||||
if(length(t) == 0)
|
||||
continue
|
||||
else if(copytext(t, 1, 2) == "#")
|
||||
continue
|
||||
|
||||
var/pos = findtext(t, " ")
|
||||
var/command = null
|
||||
var/data = null
|
||||
|
||||
if(pos)
|
||||
command = lowertext(copytext(t, 1, pos))
|
||||
data = copytext(t, pos + 1)
|
||||
else
|
||||
command = lowertext(t)
|
||||
|
||||
if(!command)
|
||||
continue
|
||||
|
||||
if (!currentmap && command != "map")
|
||||
continue
|
||||
|
||||
switch (command)
|
||||
if ("map")
|
||||
currentmap = new ("_maps/[data].json")
|
||||
if(currentmap.defaulted)
|
||||
log_config("Failed to load map config for [data]!")
|
||||
if ("minplayers","minplayer")
|
||||
currentmap.config_min_users = text2num(data)
|
||||
if ("maxplayers","maxplayer")
|
||||
currentmap.config_max_users = text2num(data)
|
||||
if ("weight","voteweight")
|
||||
currentmap.voteweight = text2num(data)
|
||||
if ("default","defaultmap")
|
||||
defaultmap = currentmap
|
||||
if ("endmap")
|
||||
LAZYINITLIST(maplist)
|
||||
maplist[currentmap.map_name] = currentmap
|
||||
currentmap = null
|
||||
if ("disabled")
|
||||
currentmap = null
|
||||
else
|
||||
WRITE_FILE(GLOB.config_error_log, "Unknown command in map vote config: '[command]'")
|
||||
|
||||
|
||||
/datum/controller/configuration/proc/pick_mode(mode_name)
|
||||
// I wish I didn't have to instance the game modes in order to look up
|
||||
// their information, but it is the only way (at least that I know of).
|
||||
// ^ This guy didn't try hard enough
|
||||
for(var/T in gamemode_cache)
|
||||
var/datum/game_mode/M = T
|
||||
var/ct = initial(M.config_tag)
|
||||
if(ct && ct == mode_name)
|
||||
return new T
|
||||
return new /datum/game_mode/extended()
|
||||
|
||||
/datum/controller/configuration/proc/get_runnable_modes()
|
||||
var/list/datum/game_mode/runnable_modes = new
|
||||
var/list/probabilities = Get(/datum/config_entry/keyed_number_list/probability)
|
||||
var/list/min_pop = Get(/datum/config_entry/keyed_number_list/min_pop)
|
||||
var/list/max_pop = Get(/datum/config_entry/keyed_number_list/max_pop)
|
||||
var/list/repeated_mode_adjust = Get(/datum/config_entry/number_list/repeated_mode_adjust)
|
||||
for(var/T in gamemode_cache)
|
||||
var/datum/game_mode/M = new T()
|
||||
if(!(M.config_tag in modes))
|
||||
qdel(M)
|
||||
continue
|
||||
if(probabilities[M.config_tag]<=0)
|
||||
qdel(M)
|
||||
continue
|
||||
if(min_pop[M.config_tag])
|
||||
M.required_players = min_pop[M.config_tag]
|
||||
if(max_pop[M.config_tag])
|
||||
M.maximum_players = max_pop[M.config_tag]
|
||||
if(M.can_start())
|
||||
var/final_weight = probabilities[M.config_tag]
|
||||
if(SSpersistence.saved_modes.len == 3 && repeated_mode_adjust.len == 3)
|
||||
var/recent_round = min(SSpersistence.saved_modes.Find(M.config_tag),3)
|
||||
var/adjustment = 0
|
||||
while(recent_round)
|
||||
adjustment += repeated_mode_adjust[recent_round]
|
||||
recent_round = SSpersistence.saved_modes.Find(M.config_tag,recent_round+1,0)
|
||||
final_weight *= ((100-adjustment)/100)
|
||||
runnable_modes[M] = final_weight
|
||||
return runnable_modes
|
||||
|
||||
/datum/controller/configuration/proc/get_runnable_midround_modes(crew)
|
||||
var/list/datum/game_mode/runnable_modes = new
|
||||
var/list/probabilities = Get(/datum/config_entry/keyed_number_list/probability)
|
||||
var/list/min_pop = Get(/datum/config_entry/keyed_number_list/min_pop)
|
||||
var/list/max_pop = Get(/datum/config_entry/keyed_number_list/max_pop)
|
||||
for(var/T in (gamemode_cache - SSticker.mode.type))
|
||||
var/datum/game_mode/M = new T()
|
||||
if(!(M.config_tag in modes))
|
||||
qdel(M)
|
||||
continue
|
||||
if(probabilities[M.config_tag]<=0)
|
||||
qdel(M)
|
||||
continue
|
||||
if(min_pop[M.config_tag])
|
||||
M.required_players = min_pop[M.config_tag]
|
||||
if(max_pop[M.config_tag])
|
||||
M.maximum_players = max_pop[M.config_tag]
|
||||
if(M.required_players <= crew)
|
||||
if(M.maximum_players >= 0 && M.maximum_players < crew)
|
||||
continue
|
||||
runnable_modes[M] = probabilities[M.config_tag]
|
||||
return runnable_modes
|
||||
@@ -1,87 +0,0 @@
|
||||
/**
|
||||
* Startup hook.
|
||||
* Called in world.dm when the server starts.
|
||||
*/
|
||||
/hook/startup
|
||||
|
||||
/**
|
||||
* Roundstart hook.
|
||||
* Called in gameticker.dm when a round starts.
|
||||
*/
|
||||
/hook/roundstart
|
||||
|
||||
/**
|
||||
* Roundend hook.
|
||||
* Called in gameticker.dm when a round ends.
|
||||
*/
|
||||
/hook/roundend
|
||||
|
||||
/**
|
||||
* Death hook.
|
||||
* Called in death.dm when someone dies.
|
||||
* Parameters: var/mob/living/carbon/human, var/gibbed
|
||||
*/
|
||||
/hook/death
|
||||
|
||||
/**
|
||||
* Cloning hook.
|
||||
* Called in cloning.dm when someone is brought back by the wonders of modern science.
|
||||
* Parameters: var/mob/living/carbon/human
|
||||
*/
|
||||
/hook/clone
|
||||
|
||||
/**
|
||||
* Debrained hook.
|
||||
* Called in brain_item.dm when someone gets debrained.
|
||||
* Parameters: var/obj/item/organ/brain
|
||||
*/
|
||||
/hook/debrain
|
||||
|
||||
/**
|
||||
* Borged hook.
|
||||
* Called in robot_parts.dm when someone gets turned into a cyborg.
|
||||
* Parameters: var/mob/living/silicon/robot
|
||||
*/
|
||||
/hook/borgify
|
||||
|
||||
/**
|
||||
* Podman hook.
|
||||
* Called in podmen.dm when someone is brought back as a Diona.
|
||||
* Parameters: var/mob/living/carbon/alien/diona
|
||||
*/
|
||||
/hook/harvest_podman
|
||||
|
||||
/**
|
||||
* Payroll revoked hook.
|
||||
* Called in Accounts_DB.dm when someone's payroll is stolen at the Accounts terminal.
|
||||
* Parameters: var/datum/money_account
|
||||
*/
|
||||
/hook/revoke_payroll
|
||||
|
||||
/**
|
||||
* Account suspension hook.
|
||||
* Called in Accounts_DB.dm when someone's account is suspended or unsuspended at the Accounts terminal.
|
||||
* Parameters: var/datum/money_account
|
||||
*/
|
||||
/hook/change_account_status
|
||||
|
||||
/**
|
||||
* Employee reassignment hook.
|
||||
* Called in card.dm when someone's card is reassigned at the HoP's desk.
|
||||
* Parameters: var/obj/item/card/id
|
||||
*/
|
||||
/hook/reassign_employee
|
||||
|
||||
/**
|
||||
* Employee terminated hook.
|
||||
* Called in card.dm when someone's card is terminated at the HoP's desk.
|
||||
* Parameters: var/obj/item/card/id
|
||||
*/
|
||||
/hook/terminate_employee
|
||||
|
||||
/**
|
||||
* Crate sold hook.
|
||||
* Called in supplyshuttle.dm when a crate is sold on the shuttle.
|
||||
* Parameters: var/obj/structure/closet/crate/sold, var/area/shuttle
|
||||
*/
|
||||
/hook/sell_crate
|
||||
@@ -1,510 +0,0 @@
|
||||
SUBSYSTEM_DEF(explosion)
|
||||
priority = 99
|
||||
wait = 1
|
||||
flags = SS_TICKER|SS_NO_INIT
|
||||
|
||||
var/list/explosions
|
||||
|
||||
var/rebuild_tick_split_count = FALSE
|
||||
var/tick_portions_required = 0
|
||||
|
||||
var/list/logs
|
||||
|
||||
var/list/zlevels_that_ignore_bombcap
|
||||
var/list/doppler_arrays
|
||||
|
||||
//legacy caps, set by config
|
||||
var/devastation_cap = 3
|
||||
var/heavy_cap = 7
|
||||
var/light_cap = 14
|
||||
var/flash_cap = 14
|
||||
var/flame_cap = 14
|
||||
var/dyn_ex_scale = 0.5
|
||||
|
||||
var/id_counter = 0
|
||||
|
||||
/datum/controller/subsystem/explosion/PreInit()
|
||||
doppler_arrays = list()
|
||||
logs = list()
|
||||
explosions = list()
|
||||
zlevels_that_ignore_bombcap = list("[ZLEVEL_MINING]")
|
||||
|
||||
/datum/controller/subsystem/explosion/Shutdown()
|
||||
QDEL_LIST(explosions)
|
||||
QDEL_LIST(logs)
|
||||
zlevels_that_ignore_bombcap.Cut()
|
||||
|
||||
/datum/controller/subsystem/explosion/Recover()
|
||||
explosions = SSexplosion.explosions
|
||||
logs = SSexplosion.logs
|
||||
id_counter = SSexplosion.id_counter
|
||||
rebuild_tick_split_count = TRUE
|
||||
zlevels_that_ignore_bombcap = SSexplosion.zlevels_that_ignore_bombcap
|
||||
doppler_arrays = SSexplosion.doppler_arrays
|
||||
|
||||
devastation_cap = SSexplosion.devastation_cap
|
||||
heavy_cap = SSexplosion.heavy_cap
|
||||
light_cap = SSexplosion.light_cap
|
||||
flash_cap = SSexplosion.flash_cap
|
||||
flame_cap = SSexplosion.flame_cap
|
||||
dyn_ex_scale = SSexplosion.dyn_ex_scale
|
||||
|
||||
/datum/controller/subsystem/explosion/fire()
|
||||
var/list/cached_explosions = explosions
|
||||
var/num_explosions = cached_explosions.len
|
||||
if(!num_explosions)
|
||||
return
|
||||
|
||||
//figure exactly how many tick splits are required
|
||||
var/num_splits
|
||||
if(rebuild_tick_split_count)
|
||||
var/reactionary = config.reactionary_explosions
|
||||
num_splits = num_explosions
|
||||
for(var/I in cached_explosions)
|
||||
var/datum/explosion/E = I
|
||||
if(!E.turfs_processed)
|
||||
++num_splits
|
||||
if(reactionary && !E.densities_processed)
|
||||
++num_splits
|
||||
tick_portions_required = num_splits
|
||||
else
|
||||
num_splits = tick_portions_required
|
||||
|
||||
MC_SPLIT_TICK_INIT(num_splits)
|
||||
|
||||
for(var/I in cached_explosions)
|
||||
var/datum/explosion/E = I
|
||||
|
||||
var/etp = E.turfs_processed
|
||||
if(!etp)
|
||||
if(GatherTurfs(E))
|
||||
--tick_portions_required
|
||||
etp = TRUE
|
||||
MC_SPLIT_TICK
|
||||
|
||||
var/edp = E.densities_processed
|
||||
if(!edp)
|
||||
if(DensityCalculate(E, etp))
|
||||
--tick_portions_required
|
||||
edp = TRUE
|
||||
MC_SPLIT_TICK
|
||||
|
||||
if(ProcessExplosion(E, edp)) //splits the tick
|
||||
--tick_portions_required
|
||||
explosions -= E
|
||||
logs += E
|
||||
NotifyDopplers(E)
|
||||
MC_SPLIT_TICK
|
||||
|
||||
/datum/controller/subsystem/explosion/proc/NotifyDopplers(datum/explosion/E)
|
||||
for(var/array in doppler_arrays)
|
||||
var/obj/machinery/doppler_array/A = array
|
||||
A.sense_explosion(E.epicenter, E.devastation, E.heavy, E.light, E.finished_at - E.started_at, E.orig_dev_range, E.orig_heavy_range, E.orig_light_range)
|
||||
|
||||
/datum/controller/subsystem/explosion/proc/Create(atom/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = TRUE, ignorecap = FALSE, flame_range = 0 , silent = FALSE, smoke = FALSE)
|
||||
epicenter = get_turf(epicenter)
|
||||
if(!epicenter)
|
||||
return
|
||||
|
||||
if(adminlog)
|
||||
message_admins("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area: [get_area(epicenter)] [ADMIN_COORDJMP(epicenter)]")
|
||||
log_game("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area [epicenter.loc.name] ([epicenter.x],[epicenter.y],[epicenter.z])")
|
||||
|
||||
var/datum/explosion/E = new(++id_counter, epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, flame_range, silent, smoke, ignorecap)
|
||||
|
||||
if(heavy_impact_range > 1)
|
||||
var/datum/effect_system/explosion/Eff
|
||||
if(smoke)
|
||||
Eff = new /datum/effect_system/explosion/smoke
|
||||
else
|
||||
Eff = new
|
||||
Eff.set_up(epicenter)
|
||||
Eff.start()
|
||||
|
||||
//flash mobs
|
||||
if(flash_range)
|
||||
for(var/mob/living/L in viewers(flash_range, epicenter))
|
||||
L.flash_act()
|
||||
|
||||
if(!silent)
|
||||
ExplosionSound(epicenter, devastation_range, heavy_impact_range, E.extent)
|
||||
|
||||
//add to SS
|
||||
if(E.extent)
|
||||
tick_portions_required += 2 + (config.reactionary_explosions ? 1 : 0)
|
||||
explosions += E
|
||||
else
|
||||
logs += E //Already done processing
|
||||
|
||||
/datum/controller/subsystem/explosion/proc/CreateDynamic(atom/epicenter, power, flash_range, adminlog = TRUE, ignorecap = TRUE, flame_range = 0 , silent = FALSE, smoke = TRUE)
|
||||
if(!power)
|
||||
return
|
||||
var/range = round((2 * power) ** dyn_ex_scale)
|
||||
Create(epicenter, round(range * 0.25), round(range * 0.5), round(range), flash_range*range, adminlog, ignorecap, flame_range*range, silent, smoke)
|
||||
|
||||
// Using default dyn_ex scale:
|
||||
// 100 explosion power is a (5, 10, 20) explosion.
|
||||
// 75 explosion power is a (4, 8, 17) explosion.
|
||||
// 50 explosion power is a (3, 7, 14) explosion.
|
||||
// 25 explosion power is a (2, 5, 10) explosion.
|
||||
// 10 explosion power is a (1, 3, 6) explosion.
|
||||
// 5 explosion power is a (0, 1, 3) explosion.
|
||||
// 1 explosion power is a (0, 0, 1) explosion.
|
||||
|
||||
/datum/explosion
|
||||
var/explosion_id
|
||||
var/turf/epicenter
|
||||
|
||||
var/started_at
|
||||
var/finished_at
|
||||
var/tick_started
|
||||
var/tick_finished
|
||||
|
||||
var/turfs_processed = FALSE
|
||||
var/densities_processed = FALSE
|
||||
|
||||
var/orig_dev_range
|
||||
var/orig_heavy_range
|
||||
var/orig_light_range
|
||||
var/orig_flash_range
|
||||
var/orig_flame_range
|
||||
|
||||
var/devastation
|
||||
var/heavy
|
||||
var/light
|
||||
var/extent
|
||||
|
||||
var/flash
|
||||
var/flame
|
||||
|
||||
var/gather_dist = 0
|
||||
|
||||
var/list/gathered_turfs
|
||||
var/list/calculated_turfs
|
||||
|
||||
var/list/unsafe_turfs
|
||||
|
||||
/datum/explosion/New(id, turf/epi, devastation_range, heavy_impact_range, light_impact_range, flash_range, flame_range, silent, smoke, ignorecap)
|
||||
explosion_id = id
|
||||
epicenter = epi
|
||||
|
||||
densities_processed = !config.reactionary_explosions
|
||||
|
||||
orig_dev_range = devastation_range
|
||||
orig_heavy_range = heavy_impact_range
|
||||
orig_light_range = light_impact_range
|
||||
orig_flash_range = flash_range
|
||||
orig_flame_range = flame_range
|
||||
|
||||
if(!ignorecap && !("[epicenter.z]" in SSexplosion.zlevels_that_ignore_bombcap))
|
||||
//Clamp all values
|
||||
devastation_range = min(SSexplosion.devastation_cap, devastation_range)
|
||||
heavy_impact_range = min(SSexplosion.heavy_cap, heavy_impact_range)
|
||||
light_impact_range = min(SSexplosion.light_cap, light_impact_range)
|
||||
flash_range = min(SSexplosion.flash_cap, flash_range)
|
||||
flame_range = min(SSexplosion.flame_cap, flame_range)
|
||||
|
||||
//store this
|
||||
devastation = devastation_range
|
||||
heavy = heavy_impact_range
|
||||
light = light_impact_range
|
||||
|
||||
extent = max(devastation_range, heavy_impact_range, light_impact_range, flame_range)
|
||||
|
||||
flash = flash_range
|
||||
flame = flame_range
|
||||
|
||||
started_at = REALTIMEOFDAY
|
||||
tick_started = world.time
|
||||
|
||||
gathered_turfs = list()
|
||||
calculated_turfs = list()
|
||||
unsafe_turfs = list()
|
||||
|
||||
// Play sounds; we want sounds to be different depending on distance so we will manually do it ourselves.
|
||||
// Stereo users will also hear the direction of the explosion!
|
||||
|
||||
// Calculate far explosion sound range. Only allow the sound effect for heavy/devastating explosions.
|
||||
// 3/7/14 will calculate to 80 + 35
|
||||
/proc/ExplosionSound(turf/epicenter, devastation_range, heavy_impact_range, extent)
|
||||
var/far_dist = 0
|
||||
far_dist += heavy_impact_range * 5
|
||||
far_dist += devastation_range * 20
|
||||
|
||||
var/z0 = epicenter.z
|
||||
|
||||
var/frequency = get_rand_frequency()
|
||||
var/ex_sound = get_sfx("explosion")
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
// Double check for client
|
||||
var/turf/M_turf = get_turf(M)
|
||||
if(M_turf && M_turf.z == z0)
|
||||
var/dist = get_dist(M_turf, epicenter)
|
||||
// If inside the blast radius + world.view - 2
|
||||
if(dist <= round(extent + world.view - 2, 1))
|
||||
M.playsound_local(epicenter, ex_sound, 100, 1, frequency, falloff = 5)
|
||||
// You hear a far explosion if you're outside the blast radius. Small bombs shouldn't be heard all over the station.
|
||||
else if(dist <= far_dist)
|
||||
var/far_volume = Clamp(far_dist, 30, 50) // Volume is based on explosion size and dist
|
||||
far_volume += (dist <= far_dist * 0.5 ? 50 : 0) // add 50 volume if the mob is pretty close to the explosion
|
||||
M.playsound_local(epicenter, 'sound/effects/explosionfar.ogg', far_volume, 1, frequency, falloff = 5)
|
||||
|
||||
/datum/explosion/Destroy()
|
||||
SSexplosion.explosions -= src
|
||||
SSexplosion.logs -= src
|
||||
LAZYCLEARLIST(gathered_turfs)
|
||||
LAZYCLEARLIST(calculated_turfs)
|
||||
LAZYCLEARLIST(unsafe_turfs)
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/explosion/proc/GatherTurfs(datum/explosion/E)
|
||||
var/turf/epicenter = E.epicenter
|
||||
|
||||
var/x0 = epicenter.x
|
||||
var/y0 = epicenter.y
|
||||
var/z0 = epicenter.z
|
||||
|
||||
var/c_dist = E.gather_dist
|
||||
var/dist = E.extent
|
||||
|
||||
var/list/L = E.gathered_turfs
|
||||
|
||||
if(!c_dist)
|
||||
L += epicenter
|
||||
++c_dist
|
||||
|
||||
while( c_dist <= dist )
|
||||
var/y = y0 + c_dist
|
||||
var/x = x0 - c_dist + 1
|
||||
for(x in x to x0 + c_dist)
|
||||
var/turf/T = locate(x, y, z0)
|
||||
if(T)
|
||||
L += T
|
||||
|
||||
y = y0 + c_dist - 1
|
||||
x = x0 + c_dist
|
||||
for(y in y0 - c_dist to y)
|
||||
var/turf/T = locate(x, y, z0)
|
||||
if(T)
|
||||
L += T
|
||||
|
||||
y = y0 - c_dist
|
||||
x = x0 + c_dist - 1
|
||||
for(x in x0 - c_dist to x)
|
||||
var/turf/T = locate(x, y, z0)
|
||||
if(T)
|
||||
L += T
|
||||
|
||||
y = y0 - c_dist + 1
|
||||
x = x0 - c_dist
|
||||
for(y in y to y0 + c_dist)
|
||||
var/turf/T = locate(x, y, z0)
|
||||
if(T)
|
||||
L += T
|
||||
++c_dist
|
||||
|
||||
if(MC_TICK_CHECK)
|
||||
break
|
||||
|
||||
if(c_dist > dist)
|
||||
E.turfs_processed = TRUE
|
||||
return TRUE
|
||||
else
|
||||
E.gather_dist = c_dist
|
||||
return FALSE
|
||||
|
||||
/datum/controller/subsystem/explosion/proc/DensityCalculate(datum/explosion/E, done_gathering_turfs)
|
||||
var/list/L = E.calculated_turfs
|
||||
var/cut_to = 1
|
||||
for(var/I in E.gathered_turfs) // we cache the explosion block rating of every turf in the explosion area
|
||||
var/turf/T = I
|
||||
++cut_to
|
||||
|
||||
var/current_exp_block = T.density ? T.explosion_block : 0
|
||||
|
||||
for(var/obj/machinery/door/D in T)
|
||||
if(D.density)
|
||||
current_exp_block += D.explosion_block
|
||||
|
||||
for(var/obj/structure/window/W in T)
|
||||
if(W.reinf && W.fulltile)
|
||||
current_exp_block += W.explosion_block
|
||||
|
||||
for(var/obj/structure/blob/B in T)
|
||||
current_exp_block += B.explosion_block
|
||||
|
||||
L[T] = current_exp_block
|
||||
|
||||
if(MC_TICK_CHECK)
|
||||
E.gathered_turfs.Cut(1, cut_to)
|
||||
return FALSE
|
||||
|
||||
E.gathered_turfs.Cut()
|
||||
return done_gathering_turfs
|
||||
|
||||
/datum/controller/subsystem/explosion/proc/ProcessExplosion(datum/explosion/E, done_calculating_turfs)
|
||||
//cache shit for speed
|
||||
var/id = E.explosion_id
|
||||
|
||||
var/list/cached_unsafe = E.unsafe_turfs
|
||||
var/list/cached_exp_block = E.calculated_turfs
|
||||
var/list/affected_turfs = cached_exp_block ? cached_exp_block : E.gathered_turfs
|
||||
|
||||
var/devastation_range = E.devastation
|
||||
var/heavy_impact_range = E.heavy
|
||||
var/light_impact_range = E.light
|
||||
|
||||
var/flame_range = E.flame
|
||||
var/throw_range_max = E.extent
|
||||
|
||||
var/turf/epi = E.epicenter
|
||||
|
||||
var/x0 = epi.x
|
||||
var/y0 = epi.y
|
||||
|
||||
var/cut_to = 1
|
||||
for(var/TI in affected_turfs)
|
||||
var/turf/T = TI
|
||||
++cut_to
|
||||
|
||||
var/init_dist = cheap_hypotenuse(T.x, T.y, x0, y0)
|
||||
var/dist = init_dist
|
||||
|
||||
if(cached_exp_block)
|
||||
var/turf/Trajectory = T
|
||||
while(Trajectory != epi)
|
||||
Trajectory = get_step_towards(Trajectory, epi)
|
||||
dist += cached_exp_block[Trajectory]
|
||||
|
||||
var/flame_dist = dist < flame_range
|
||||
var/throw_dist = dist
|
||||
|
||||
if(dist < devastation_range)
|
||||
dist = 1
|
||||
else if(dist < heavy_impact_range)
|
||||
dist = 2
|
||||
else if(dist < light_impact_range)
|
||||
dist = 3
|
||||
else
|
||||
dist = 0
|
||||
|
||||
//------- EX_ACT AND TURF FIRES -------
|
||||
|
||||
if(flame_dist && prob(40) && !isspaceturf(T) && !T.density)
|
||||
new /obj/effect/hotspot(T) //Mostly for ambience!
|
||||
|
||||
if(dist > 0)
|
||||
T.explosion_level = max(T.explosion_level, dist) //let the bigger one have it
|
||||
T.explosion_id = id
|
||||
T.ex_act(dist)
|
||||
cached_unsafe += T
|
||||
|
||||
//--- THROW ITEMS AROUND ---
|
||||
|
||||
var/throw_dir = get_dir(epi, T)
|
||||
for(var/obj/item/I in T)
|
||||
if(!I.anchored)
|
||||
var/throw_range = rand(throw_dist, throw_range_max)
|
||||
var/turf/throw_at = get_ranged_target_turf(I, throw_dir, throw_range)
|
||||
I.throw_speed = 4 //Temporarily change their throw_speed for embedding purposes (Resets when it finishes throwing, regardless of hitting anything)
|
||||
I.throw_at(throw_at, throw_range, 4)
|
||||
|
||||
if(MC_TICK_CHECK)
|
||||
var/circumference = (PI * (init_dist + 4) * 2) //+4 to radius to prevent shit gaps
|
||||
if(cached_unsafe.len > circumference) //only do this every revolution
|
||||
for(var/Unexplode in cached_unsafe)
|
||||
var/turf/UnexplodeT = Unexplode
|
||||
UnexplodeT.explosion_level = 0
|
||||
cached_unsafe.Cut()
|
||||
done_calculating_turfs = FALSE
|
||||
break
|
||||
|
||||
affected_turfs.Cut(1, cut_to)
|
||||
|
||||
if(!done_calculating_turfs)
|
||||
return FALSE
|
||||
|
||||
//unfuck the shit
|
||||
for(var/Unexplode in cached_unsafe)
|
||||
var/turf/UnexplodeT = Unexplode
|
||||
UnexplodeT.explosion_level = 0
|
||||
cached_unsafe.Cut()
|
||||
|
||||
E.finished_at = REALTIMEOFDAY
|
||||
E.tick_finished = world.time
|
||||
|
||||
return TRUE
|
||||
|
||||
/client/proc/check_bomb_impacts()
|
||||
set name = "Check Bomb Impact"
|
||||
set category = "Debug"
|
||||
|
||||
var/newmode = alert("Use reactionary explosions?","Check Bomb Impact", "Yes", "No")
|
||||
var/turf/epicenter = get_turf(mob)
|
||||
if(!epicenter)
|
||||
return
|
||||
|
||||
var/x0 = epicenter.x
|
||||
var/y0 = epicenter.y
|
||||
|
||||
var/dev = 0
|
||||
var/heavy = 0
|
||||
var/light = 0
|
||||
var/list/choices = list("Small Bomb","Medium Bomb","Big Bomb","Custom Bomb")
|
||||
var/choice = input("Bomb Size?") in choices
|
||||
switch(choice)
|
||||
if(null)
|
||||
return 0
|
||||
if("Small Bomb")
|
||||
dev = 1
|
||||
heavy = 2
|
||||
light = 3
|
||||
if("Medium Bomb")
|
||||
dev = 2
|
||||
heavy = 3
|
||||
light = 4
|
||||
if("Big Bomb")
|
||||
dev = 3
|
||||
heavy = 5
|
||||
light = 7
|
||||
if("Custom Bomb")
|
||||
dev = input("Devestation range (Tiles):") as num
|
||||
heavy = input("Heavy impact range (Tiles):") as num
|
||||
light = input("Light impact range (Tiles):") as num
|
||||
else
|
||||
return
|
||||
|
||||
var/datum/explosion/E = new(null, epicenter, dev, heavy, light, ignorecap = TRUE)
|
||||
|
||||
while(!SSexplosion.GatherTurfs(E))
|
||||
stoplag()
|
||||
var/list/turfs
|
||||
if(newmode)
|
||||
while(!SSexplosion.DensityCalculate(E, TRUE))
|
||||
stoplag()
|
||||
turfs = E.calculated_turfs.Copy()
|
||||
else
|
||||
turfs = E.gathered_turfs.Copy()
|
||||
|
||||
qdel(E)
|
||||
|
||||
for(var/I in turfs)
|
||||
var/turf/T = I
|
||||
var/dist = cheap_hypotenuse(T.x, T.y, x0, y0) + turfs[T]
|
||||
|
||||
if(dist < dev)
|
||||
T.color = "red"
|
||||
T.maptext = "Dev"
|
||||
else if (dist < heavy)
|
||||
T.color = "yellow"
|
||||
T.maptext = "Heavy"
|
||||
else if (dist < light)
|
||||
T.color = "blue"
|
||||
T.maptext = "Light"
|
||||
CHECK_TICK
|
||||
|
||||
sleep(100)
|
||||
for(var/I in turfs)
|
||||
var/turf/T = I
|
||||
T.color = null
|
||||
T.maptext = null
|
||||
@@ -1,42 +0,0 @@
|
||||
#define PING_BUFFER_TIME 25
|
||||
|
||||
SUBSYSTEM_DEF(ping)
|
||||
name = "Ping"
|
||||
wait = 6
|
||||
flags = SS_POST_FIRE_TIMING|SS_FIRE_IN_LOBBY
|
||||
priority = 10
|
||||
var/list/currentrun
|
||||
|
||||
/datum/controller/subsystem/ping/Initialize()
|
||||
if (config.hub)
|
||||
world.visibility = 1
|
||||
..()
|
||||
|
||||
/datum/controller/subsystem/ping/fire(resumed = FALSE)
|
||||
if (!resumed)
|
||||
src.currentrun = GLOB.clients.Copy()
|
||||
|
||||
var/round_started = Master.round_started
|
||||
var/list/currentrun = src.currentrun
|
||||
while (length(currentrun))
|
||||
var/client/C = currentrun[currentrun.len]
|
||||
currentrun.len--
|
||||
if (!C || world.time - C.connection_time < PING_BUFFER_TIME || C.inactivity >= (wait-1))
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
continue
|
||||
|
||||
if(round_started && C.is_afk(INACTIVITY_KICK))
|
||||
if(!istype(C.mob, /mob/dead))
|
||||
log_access("AFK: [key_name(C)]")
|
||||
to_chat(C, "<span class='danger'>You have been inactive for more than 10 minutes and have been disconnected.</span>")
|
||||
qdel(C)
|
||||
|
||||
winset(C, null, "command=.update_ping+[world.time+world.tick_lag*world.tick_usage/100]")
|
||||
|
||||
if (MC_TICK_CHECK) //one day, when ss13 has 1000 people per server, you guys are gonna be glad I added this tick check
|
||||
return
|
||||
|
||||
currentrun = null
|
||||
|
||||
#undef PING_BUFFER_TIME
|
||||
@@ -1,182 +0,0 @@
|
||||
#define ABDUCTOR_MAX_TEAMS 4
|
||||
|
||||
/datum/antagonist/abductor
|
||||
name = "Abductor"
|
||||
roundend_category = "abductors"
|
||||
antagpanel_category = "Abductor"
|
||||
job_rank = ROLE_ABDUCTOR
|
||||
show_in_antagpanel = FALSE //should only show subtypes
|
||||
var/datum/team/abductor_team/team
|
||||
var/sub_role
|
||||
var/outfit
|
||||
var/landmark_type
|
||||
var/greet_text
|
||||
|
||||
|
||||
/datum/antagonist/abductor/agent
|
||||
name = "Abductor Agent"
|
||||
sub_role = "Agent"
|
||||
outfit = /datum/outfit/abductor/agent
|
||||
landmark_type = /obj/effect/landmark/abductor/agent
|
||||
greet_text = "Use your stealth technology and equipment to incapacitate humans for your scientist to retrieve."
|
||||
show_in_antagpanel = TRUE
|
||||
|
||||
/datum/antagonist/abductor/scientist
|
||||
name = "Abductor Scientist"
|
||||
sub_role = "Scientist"
|
||||
outfit = /datum/outfit/abductor/scientist
|
||||
landmark_type = /obj/effect/landmark/abductor/scientist
|
||||
greet_text = "Use your stealth technology and equipment to incapacitate humans for your scientist to retrieve."
|
||||
show_in_antagpanel = TRUE
|
||||
|
||||
/datum/antagonist/abductor/create_team(datum/team/abductor_team/new_team)
|
||||
if(!new_team)
|
||||
return
|
||||
if(!istype(new_team))
|
||||
stack_trace("Wrong team type passed to [type] initialization.")
|
||||
team = new_team
|
||||
|
||||
/datum/antagonist/abductor/get_team()
|
||||
return team
|
||||
|
||||
/datum/antagonist/abductor/on_gain()
|
||||
SSticker.mode.abductors += owner
|
||||
owner.special_role = "[name] [sub_role]"
|
||||
owner.assigned_role = "[name] [sub_role]"
|
||||
owner.objectives += team.objectives
|
||||
finalize_abductor()
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/abductor/on_removal()
|
||||
SSticker.mode.abductors -= owner
|
||||
owner.objectives -= team.objectives
|
||||
if(owner.current)
|
||||
to_chat(owner.current,"<span class='userdanger'>You are no longer the [owner.special_role]!</span>")
|
||||
owner.special_role = null
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/abductor/greet()
|
||||
to_chat(owner.current, "<span class='notice'>You are the [owner.special_role]!</span>")
|
||||
to_chat(owner.current, "<span class='notice'>With the help of your teammate, kidnap and experiment on station crew members!</span>")
|
||||
to_chat(owner.current, "<span class='notice'>[greet_text]</span>")
|
||||
owner.announce_objectives()
|
||||
|
||||
/datum/antagonist/abductor/proc/finalize_abductor()
|
||||
//Equip
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
H.set_species(/datum/species/abductor)
|
||||
H.real_name = "[team.name] [sub_role]"
|
||||
H.equipOutfit(outfit)
|
||||
|
||||
//Teleport to ship
|
||||
for(var/obj/effect/landmark/abductor/LM in GLOB.landmarks_list)
|
||||
if(istype(LM, landmark_type) && LM.team_number == team.team_number)
|
||||
H.forceMove(LM.loc)
|
||||
break
|
||||
|
||||
SSticker.mode.update_abductor_icons_added(owner)
|
||||
|
||||
/datum/antagonist/abductor/scientist/finalize_abductor()
|
||||
..()
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
var/datum/species/abductor/A = H.dna.species
|
||||
A.scientist = TRUE
|
||||
|
||||
/datum/antagonist/abductor/admin_add(datum/mind/new_owner,mob/admin)
|
||||
var/list/current_teams = list()
|
||||
for(var/datum/team/abductor_team/T in get_all_teams(/datum/team/abductor_team))
|
||||
current_teams[T.name] = T
|
||||
var/choice = input(admin,"Add to which team ?") as null|anything in (current_teams + "new team")
|
||||
if (choice == "new team")
|
||||
team = new
|
||||
else if(choice in current_teams)
|
||||
team = current_teams[choice]
|
||||
else
|
||||
return
|
||||
new_owner.add_antag_datum(src)
|
||||
log_admin("[key_name(usr)] made [key_name(new_owner.current)] [name] on [choice]!")
|
||||
message_admins("[key_name_admin(usr)] made [key_name_admin(new_owner.current)] [name] on [choice] !")
|
||||
|
||||
/datum/antagonist/abductor/get_admin_commands()
|
||||
. = ..()
|
||||
.["Equip"] = CALLBACK(src,.proc/admin_equip)
|
||||
|
||||
/datum/antagonist/abductor/proc/admin_equip(mob/admin)
|
||||
if(!ishuman(owner.current))
|
||||
to_chat(admin, "<span class='warning'>This only works on humans!</span>")
|
||||
return
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
var/gear = alert(admin,"Agent or Scientist Gear","Gear","Agent","Scientist")
|
||||
if(gear)
|
||||
if(gear=="Agent")
|
||||
H.equipOutfit(/datum/outfit/abductor/agent)
|
||||
else
|
||||
H.equipOutfit(/datum/outfit/abductor/scientist)
|
||||
|
||||
/datum/team/abductor_team
|
||||
member_name = "abductor"
|
||||
var/team_number
|
||||
var/list/datum/mind/abductees = list()
|
||||
var/static/team_count = 1
|
||||
|
||||
/datum/team/abductor_team/New()
|
||||
..()
|
||||
team_number = team_count++
|
||||
name = "Mothership [pick(GLOB.possible_changeling_IDs)]" //TODO Ensure unique and actual alieny names
|
||||
add_objective(new/datum/objective/experiment)
|
||||
|
||||
/datum/team/abductor_team/is_solo()
|
||||
return FALSE
|
||||
|
||||
/datum/team/abductor_team/proc/add_objective(datum/objective/O)
|
||||
O.team = src
|
||||
O.update_explanation_text()
|
||||
objectives += O
|
||||
|
||||
/datum/team/abductor_team/roundend_report()
|
||||
var/list/result = list()
|
||||
|
||||
var/won = TRUE
|
||||
for(var/datum/objective/O in objectives)
|
||||
if(!O.check_completion())
|
||||
won = FALSE
|
||||
if(won)
|
||||
result += "<span class='greentext big'>[name] team fulfilled its mission!</span>"
|
||||
else
|
||||
result += "<span class='redtext big'>[name] team failed its mission.</span>"
|
||||
|
||||
result += "<span class='header'>The abductors of [name] were:</span>"
|
||||
for(var/datum/mind/abductor_mind in members)
|
||||
result += printplayer(abductor_mind)
|
||||
result += printobjectives(abductor_mind)
|
||||
|
||||
return result.Join("<br>")
|
||||
|
||||
/datum/antagonist/abductee
|
||||
name = "Abductee"
|
||||
roundend_category = "abductees"
|
||||
antagpanel_category = "Abductee"
|
||||
|
||||
/datum/antagonist/abductee/on_gain()
|
||||
give_objective()
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/abductee/greet()
|
||||
to_chat(owner, "<span class='warning'><b>Your mind snaps!</b></span>")
|
||||
to_chat(owner, "<big><span class='warning'><b>You can't remember how you got here...</b></span></big>")
|
||||
owner.announce_objectives()
|
||||
|
||||
/datum/antagonist/abductee/proc/give_objective()
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
if(istype(H))
|
||||
H.gain_trauma_type(BRAIN_TRAUMA_MILD)
|
||||
var/objtype = (prob(75) ? /datum/objective/abductee/random : pick(subtypesof(/datum/objective/abductee/) - /datum/objective/abductee/random))
|
||||
var/datum/objective/abductee/O = new objtype()
|
||||
objectives += O
|
||||
owner.objectives += objectives
|
||||
|
||||
/datum/antagonist/abductee/apply_innate_effects(mob/living/mob_override)
|
||||
SSticker.mode.update_abductor_icons_added(mob_override ? mob_override.mind : owner)
|
||||
|
||||
/datum/antagonist/abductee/remove_innate_effects(mob/living/mob_override)
|
||||
SSticker.mode.update_abductor_icons_removed(mob_override ? mob_override.mind : owner)
|
||||
@@ -1,246 +0,0 @@
|
||||
GLOBAL_LIST_EMPTY(antagonists)
|
||||
|
||||
/datum/antagonist
|
||||
var/name = "Antagonist"
|
||||
var/roundend_category = "other antagonists" //Section of roundend report, datums with same category will be displayed together, also default header for the section
|
||||
var/show_in_roundend = TRUE //Set to false to hide the antagonists from roundend report
|
||||
var/datum/mind/owner //Mind that owns this datum
|
||||
var/silent = FALSE //Silent will prevent the gain/lose texts to show
|
||||
var/can_coexist_with_others = TRUE //Whether or not the person will be able to have more than one datum
|
||||
var/list/typecache_datum_blacklist = list() //List of datums this type can't coexist with
|
||||
var/delete_on_mind_deletion = TRUE
|
||||
var/job_rank
|
||||
var/replace_banned = TRUE //Should replace jobbaned player with ghosts if granted.
|
||||
var/list/objectives = list()
|
||||
var/antag_memory = ""//These will be removed with antag datum
|
||||
|
||||
//Antag panel properties
|
||||
var/show_in_antagpanel = TRUE //This will hide adding this antag type in antag panel, use only for internal subtypes that shouldn't be added directly but still show if possessed by mind
|
||||
var/antagpanel_category = "Uncategorized" //Antagpanel will display these together, REQUIRED
|
||||
|
||||
/datum/antagonist/New()
|
||||
GLOB.antagonists += src
|
||||
typecache_datum_blacklist = typecacheof(typecache_datum_blacklist)
|
||||
|
||||
/datum/antagonist/Destroy()
|
||||
GLOB.antagonists -= src
|
||||
if(owner)
|
||||
LAZYREMOVE(owner.antag_datums, src)
|
||||
owner = null
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/proc/can_be_owned(datum/mind/new_owner)
|
||||
. = TRUE
|
||||
var/datum/mind/tested = new_owner || owner
|
||||
if(tested.has_antag_datum(type))
|
||||
return FALSE
|
||||
for(var/i in tested.antag_datums)
|
||||
var/datum/antagonist/A = i
|
||||
if(is_type_in_typecache(src, A.typecache_datum_blacklist))
|
||||
return FALSE
|
||||
|
||||
//This will be called in add_antag_datum before owner assignment.
|
||||
//Should return antag datum without owner.
|
||||
/datum/antagonist/proc/specialization(datum/mind/new_owner)
|
||||
return src
|
||||
|
||||
/datum/antagonist/proc/on_body_transfer(mob/living/old_body, mob/living/new_body)
|
||||
remove_innate_effects(old_body)
|
||||
apply_innate_effects(new_body)
|
||||
|
||||
//This handles the application of antag huds/special abilities
|
||||
/datum/antagonist/proc/apply_innate_effects(mob/living/mob_override)
|
||||
return
|
||||
|
||||
//This handles the removal of antag huds/special abilities
|
||||
/datum/antagonist/proc/remove_innate_effects(mob/living/mob_override)
|
||||
return
|
||||
|
||||
//Assign default team and creates one for one of a kind team antagonists
|
||||
/datum/antagonist/proc/create_team(datum/team/team)
|
||||
return
|
||||
|
||||
//Proc called when the datum is given to a mind.
|
||||
/datum/antagonist/proc/on_gain()
|
||||
if(owner && owner.current)
|
||||
if(!silent)
|
||||
greet()
|
||||
apply_innate_effects()
|
||||
if(is_banned(owner.current) && replace_banned)
|
||||
replace_banned_player()
|
||||
|
||||
/datum/antagonist/proc/is_banned(mob/M)
|
||||
if(!M)
|
||||
return FALSE
|
||||
. = (jobban_isbanned(M, ROLE_SYNDICATE) || (job_rank && jobban_isbanned(M,job_rank)))
|
||||
|
||||
/datum/antagonist/proc/replace_banned_player()
|
||||
set waitfor = FALSE
|
||||
|
||||
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [name]?", "[name]", null, job_rank, 50, owner.current)
|
||||
if(LAZYLEN(candidates))
|
||||
var/client/C = pick(candidates)
|
||||
to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
|
||||
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(owner.current)]) to replace a jobbaned player.")
|
||||
owner.current.ghostize(0)
|
||||
owner.current.key = C.key
|
||||
|
||||
/datum/antagonist/proc/on_removal()
|
||||
remove_innate_effects()
|
||||
if(owner)
|
||||
LAZYREMOVE(owner.antag_datums, src)
|
||||
if(!silent && owner.current)
|
||||
farewell()
|
||||
var/datum/team/team = get_team()
|
||||
if(team)
|
||||
team.remove_member(owner)
|
||||
qdel(src)
|
||||
|
||||
/datum/antagonist/proc/greet()
|
||||
return
|
||||
|
||||
/datum/antagonist/proc/farewell()
|
||||
return
|
||||
|
||||
//Returns the team antagonist belongs to if any.
|
||||
/datum/antagonist/proc/get_team()
|
||||
return
|
||||
|
||||
//Individual roundend report
|
||||
/datum/antagonist/proc/roundend_report()
|
||||
var/list/report = list()
|
||||
|
||||
if(!owner)
|
||||
CRASH("antagonist datum without owner")
|
||||
|
||||
report += printplayer(owner)
|
||||
|
||||
var/objectives_complete = TRUE
|
||||
if(owner.objectives.len)
|
||||
report += printobjectives(owner)
|
||||
for(var/datum/objective/objective in owner.objectives)
|
||||
if(!objective.check_completion())
|
||||
objectives_complete = FALSE
|
||||
break
|
||||
|
||||
if(owner.objectives.len == 0 || objectives_complete)
|
||||
report += "<span class='greentext big'>The [name] was successful!</span>"
|
||||
else
|
||||
report += "<span class='redtext big'>The [name] has failed!</span>"
|
||||
|
||||
return report.Join("<br>")
|
||||
|
||||
//Displayed at the start of roundend_category section, default to roundend_category header
|
||||
/datum/antagonist/proc/roundend_report_header()
|
||||
return "<span class='header'>The [roundend_category] were:</span><br>"
|
||||
|
||||
//Displayed at the end of roundend_category section
|
||||
/datum/antagonist/proc/roundend_report_footer()
|
||||
return
|
||||
|
||||
|
||||
//ADMIN TOOLS
|
||||
|
||||
//Called when using admin tools to give antag status
|
||||
/datum/antagonist/proc/admin_add(datum/mind/new_owner,mob/admin)
|
||||
message_admins("[key_name_admin(admin)] made [new_owner.current] into [name].")
|
||||
log_admin("[key_name(admin)] made [new_owner.current] into [name].")
|
||||
new_owner.add_antag_datum(src)
|
||||
|
||||
//Called when removing antagonist using admin tools
|
||||
/datum/antagonist/proc/admin_remove(mob/user)
|
||||
if(!user)
|
||||
return
|
||||
message_admins("[key_name_admin(user)] has removed [name] antagonist status from [owner.current].")
|
||||
log_admin("[key_name(user)] has removed [name] antagonist status from [owner.current].")
|
||||
on_removal()
|
||||
|
||||
//gamemode/proc/is_mode_antag(antagonist/A) => TRUE/FALSE
|
||||
|
||||
//Additional data to display in antagonist panel section
|
||||
//nuke disk code, genome count, etc
|
||||
/datum/antagonist/proc/antag_panel_data()
|
||||
return ""
|
||||
|
||||
/datum/antagonist/proc/enabled_in_preferences(datum/mind/M)
|
||||
if(job_rank)
|
||||
if(M.current && M.current.client && (job_rank in M.current.client.prefs.be_special))
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
// List if ["Command"] = CALLBACK(), user will be appeneded to callback arguments on execution
|
||||
/datum/antagonist/proc/get_admin_commands()
|
||||
. = list()
|
||||
|
||||
/datum/antagonist/Topic(href,href_list)
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
//Antag memory edit
|
||||
if (href_list["memory_edit"])
|
||||
edit_memory(usr)
|
||||
owner.traitor_panel()
|
||||
return
|
||||
|
||||
//Some commands might delete/modify this datum clearing or changing owner
|
||||
var/datum/mind/persistent_owner = owner
|
||||
|
||||
var/commands = get_admin_commands()
|
||||
for(var/admin_command in commands)
|
||||
if(href_list["command"] == admin_command)
|
||||
var/datum/callback/C = commands[admin_command]
|
||||
C.Invoke(usr)
|
||||
persistent_owner.traitor_panel()
|
||||
return
|
||||
|
||||
/datum/antagonist/proc/edit_memory(mob/user)
|
||||
var/new_memo = copytext(trim(input(user,"Write new memory", "Memory", antag_memory) as null|message),1,MAX_MESSAGE_LEN)
|
||||
if (isnull(new_memo))
|
||||
return
|
||||
antag_memory = new_memo
|
||||
|
||||
//Should probably be on ticker or job ss ?
|
||||
/proc/get_antagonists(antag_type,specific = FALSE)
|
||||
. = list()
|
||||
for(var/datum/antagonist/A in GLOB.antagonists)
|
||||
if(!A.owner)
|
||||
continue
|
||||
if(!antag_type || !specific && istype(A,antag_type) || specific && A.type == antag_type)
|
||||
. += A.owner
|
||||
|
||||
//This datum will autofill the name with special_role
|
||||
//Used as placeholder for minor antagonists, please create proper datums for these
|
||||
/datum/antagonist/auto_custom
|
||||
show_in_antagpanel = FALSE
|
||||
antagpanel_category = "Other"
|
||||
|
||||
/datum/antagonist/auto_custom/on_gain()
|
||||
..()
|
||||
name = owner.special_role
|
||||
//Add all objectives not already owned by other datums to this one.
|
||||
var/list/already_registered_objectives = list()
|
||||
for(var/datum/antagonist/A in owner.antag_datums)
|
||||
if(A == src)
|
||||
continue
|
||||
else
|
||||
already_registered_objectives |= A.objectives
|
||||
objectives = owner.objectives - already_registered_objectives
|
||||
|
||||
/datum/antagonist/auto_custom/antag_listing_name()
|
||||
return ..() + "([name])"
|
||||
|
||||
//This one is created by admin tools for custom objectives
|
||||
/datum/antagonist/custom
|
||||
antagpanel_category = "Custom"
|
||||
|
||||
/datum/antagonist/custom/admin_add(datum/mind/new_owner,mob/admin)
|
||||
var/custom_name = stripped_input(admin, "Custom antagonist name:", "Custom antag", "Antagonist")
|
||||
if(custom_name)
|
||||
name = custom_name
|
||||
else
|
||||
return
|
||||
..()
|
||||
|
||||
/datum/antagonist/custom/antag_listing_name()
|
||||
return ..() + "([name])"
|
||||
@@ -1,67 +0,0 @@
|
||||
/datum/antagonist/blob
|
||||
name = "Blob"
|
||||
roundend_category = "blobs"
|
||||
antagpanel_category = "Blob"
|
||||
job_rank = ROLE_BLOB
|
||||
|
||||
var/datum/action/innate/blobpop/pop_action
|
||||
var/starting_points_human_blob = 60
|
||||
var/point_rate_human_blob = 2
|
||||
|
||||
/datum/antagonist/blob/roundend_report()
|
||||
var/basic_report = ..()
|
||||
//Display max blobpoints for blebs that lost
|
||||
if(isovermind(owner.current)) //embarrasing if not
|
||||
var/mob/camera/blob/overmind = owner.current
|
||||
if(!overmind.victory_in_progress) //if it won this doesn't really matter
|
||||
var/point_report = "<br><b>[owner.name]</b> took over [overmind.max_count] tiles at the height of its growth."
|
||||
return basic_report+point_report
|
||||
return basic_report
|
||||
|
||||
/datum/antagonist/blob/greet()
|
||||
if(!isovermind(owner.current))
|
||||
to_chat(owner,"<span class='userdanger'>You feel bloated.</span>")
|
||||
|
||||
/datum/antagonist/blob/on_gain()
|
||||
create_objectives()
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/blob/proc/create_objectives()
|
||||
var/datum/objective/blob_takeover/main = new
|
||||
main.owner = owner
|
||||
objectives += main
|
||||
owner.objectives |= objectives
|
||||
|
||||
/datum/antagonist/blob/apply_innate_effects(mob/living/mob_override)
|
||||
if(!isovermind(owner.current))
|
||||
if(!pop_action)
|
||||
pop_action = new
|
||||
pop_action.Grant(owner.current)
|
||||
|
||||
/datum/objective/blob_takeover
|
||||
explanation_text = "Reach critical mass!"
|
||||
|
||||
//Non-overminds get this on blob antag assignment
|
||||
/datum/action/innate/blobpop
|
||||
name = "Pop"
|
||||
desc = "Unleash the blob"
|
||||
icon_icon = 'icons/mob/blob.dmi'
|
||||
button_icon_state = "blob"
|
||||
|
||||
/datum/action/innate/blobpop/Activate()
|
||||
var/mob/old_body = owner
|
||||
var/datum/antagonist/blob/blobtag = owner.mind.has_antag_datum(/datum/antagonist/blob)
|
||||
if(!blobtag)
|
||||
Remove()
|
||||
return
|
||||
var/mob/camera/blob/B = new /mob/camera/blob(get_turf(old_body), blobtag.starting_points_human_blob)
|
||||
owner.mind.transfer_to(B)
|
||||
old_body.gib()
|
||||
B.place_blob_core(blobtag.point_rate_human_blob, pop_override = TRUE)
|
||||
|
||||
/datum/antagonist/blob/antag_listing_status()
|
||||
. = ..()
|
||||
if(owner && owner.current)
|
||||
var/mob/camera/blob/B = owner.current
|
||||
if(istype(B))
|
||||
. += "(Progress: [B.blobs_legit.len]/[B.blobwincount])"
|
||||
@@ -1,154 +0,0 @@
|
||||
/datum/antagonist/brother
|
||||
name = "Brother"
|
||||
antagpanel_category = "Brother"
|
||||
job_rank = ROLE_BROTHER
|
||||
var/special_role = ROLE_BROTHER
|
||||
var/datum/team/brother_team/team
|
||||
|
||||
/datum/antagonist/brother/create_team(datum/team/brother_team/new_team)
|
||||
if(!new_team)
|
||||
return
|
||||
if(!istype(new_team))
|
||||
stack_trace("Wrong team type passed to [type] initialization.")
|
||||
team = new_team
|
||||
|
||||
/datum/antagonist/brother/get_team()
|
||||
return team
|
||||
|
||||
/datum/antagonist/brother/on_gain()
|
||||
SSticker.mode.brothers += owner
|
||||
objectives += team.objectives
|
||||
owner.objectives += objectives
|
||||
owner.special_role = special_role
|
||||
finalize_brother()
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/brother/on_removal()
|
||||
SSticker.mode.brothers -= owner
|
||||
owner.objectives -= objectives
|
||||
if(owner.current)
|
||||
to_chat(owner.current,"<span class='userdanger'>You are no longer the [special_role]!</span>")
|
||||
owner.special_role = null
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/brother/proc/give_meeting_area()
|
||||
if(!owner.current || !team || !team.meeting_area)
|
||||
return
|
||||
to_chat(owner.current, "<B>Your designated meeting area:</B> [team.meeting_area]")
|
||||
antag_memory += "<b>Meeting Area</b>: [team.meeting_area]<br>"
|
||||
|
||||
/datum/antagonist/brother/greet()
|
||||
var/brother_text = ""
|
||||
var/list/brothers = team.members - owner
|
||||
for(var/i = 1 to brothers.len)
|
||||
var/datum/mind/M = brothers[i]
|
||||
brother_text += M.name
|
||||
if(i == brothers.len - 1)
|
||||
brother_text += " and "
|
||||
else if(i != brothers.len)
|
||||
brother_text += ", "
|
||||
to_chat(owner.current, "<B><font size=3 color=red>You are the [owner.special_role] of [brother_text].</font></B>")
|
||||
to_chat(owner.current, "The Syndicate only accepts those that have proven themself. Prove yourself and prove your [team.member_name]s by completing your objectives together!")
|
||||
owner.announce_objectives()
|
||||
give_meeting_area()
|
||||
|
||||
/datum/antagonist/brother/proc/finalize_brother()
|
||||
SSticker.mode.update_brother_icons_added(owner)
|
||||
|
||||
/datum/antagonist/brother/admin_add(datum/mind/new_owner,mob/admin)
|
||||
//show list of possible brothers
|
||||
var/list/candidates = list()
|
||||
for(var/mob/living/L in GLOB.alive_mob_list)
|
||||
if(!L.mind || L.mind == new_owner || !can_be_owned(L.mind))
|
||||
continue
|
||||
candidates[L.mind.name] = L.mind
|
||||
|
||||
var/choice = input(admin,"Choose the blood brother.", "Brother") as null|anything in candidates
|
||||
if(!choice)
|
||||
return
|
||||
var/datum/mind/bro = candidates[choice]
|
||||
var/datum/team/brother_team/T = new
|
||||
T.add_member(new_owner)
|
||||
T.add_member(bro)
|
||||
T.pick_meeting_area()
|
||||
T.forge_brother_objectives()
|
||||
new_owner.add_antag_datum(/datum/antagonist/brother,T)
|
||||
bro.add_antag_datum(/datum/antagonist/brother, T)
|
||||
T.update_name()
|
||||
message_admins("[key_name_admin(admin)] made [new_owner.current] and [bro.current] into blood brothers.")
|
||||
log_admin("[key_name(admin)] made [new_owner.current] and [bro.current] into blood brothers.")
|
||||
|
||||
/datum/team/brother_team
|
||||
name = "brotherhood"
|
||||
member_name = "blood brother"
|
||||
var/meeting_area
|
||||
var/static/meeting_areas = list("The Bar", "Dorms", "Escape Dock", "Arrivals", "Holodeck", "Primary Tool Storage", "Recreation Area", "Chapel", "Library")
|
||||
|
||||
/datum/team/brother_team/is_solo()
|
||||
return FALSE
|
||||
|
||||
/datum/team/brother_team/proc/pick_meeting_area()
|
||||
meeting_area = pick(meeting_areas)
|
||||
meeting_areas -= meeting_area
|
||||
|
||||
/datum/team/brother_team/proc/update_name()
|
||||
var/list/last_names = list()
|
||||
for(var/datum/mind/M in members)
|
||||
var/list/split_name = splittext(M.name," ")
|
||||
last_names += split_name[split_name.len]
|
||||
|
||||
name = last_names.Join(" & ")
|
||||
|
||||
/datum/team/brother_team/roundend_report()
|
||||
var/list/parts = list()
|
||||
|
||||
parts += "<span class='header'>The blood brothers of [name] were:</span>"
|
||||
for(var/datum/mind/M in members)
|
||||
parts += printplayer(M)
|
||||
var/win = TRUE
|
||||
var/objective_count = 1
|
||||
for(var/datum/objective/objective in objectives)
|
||||
if(objective.check_completion())
|
||||
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
|
||||
else
|
||||
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
win = FALSE
|
||||
objective_count++
|
||||
if(win)
|
||||
parts += "<span class='greentext'>The blood brothers were successful!</span>"
|
||||
else
|
||||
parts += "<span class='redtext'>The blood brothers have failed!</span>"
|
||||
|
||||
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
|
||||
|
||||
/datum/team/brother_team/proc/add_objective(datum/objective/O, needs_target = FALSE)
|
||||
O.team = src
|
||||
if(needs_target)
|
||||
O.find_target()
|
||||
O.update_explanation_text()
|
||||
objectives += O
|
||||
|
||||
/datum/team/brother_team/proc/forge_brother_objectives()
|
||||
objectives = list()
|
||||
var/is_hijacker = prob(10)
|
||||
for(var/i = 1 to max(1, CONFIG_GET(number/brother_objectives_amount) + (members.len > 2) - is_hijacker))
|
||||
forge_single_objective()
|
||||
if(is_hijacker)
|
||||
if(!locate(/datum/objective/hijack) in objectives)
|
||||
add_objective(new/datum/objective/hijack)
|
||||
else if(!locate(/datum/objective/escape) in objectives)
|
||||
add_objective(new/datum/objective/escape)
|
||||
|
||||
/datum/team/brother_team/proc/forge_single_objective()
|
||||
if(prob(50))
|
||||
if(LAZYLEN(active_ais()) && prob(100/GLOB.joined_player_list.len))
|
||||
add_objective(new/datum/objective/destroy, TRUE)
|
||||
else if(prob(30))
|
||||
add_objective(new/datum/objective/maroon, TRUE)
|
||||
else
|
||||
add_objective(new/datum/objective/assassinate, TRUE)
|
||||
else
|
||||
add_objective(new/datum/objective/steal, TRUE)
|
||||
|
||||
/datum/team/brother_team/antag_listing_name()
|
||||
return "[name] blood brothers"
|
||||
@@ -1,545 +0,0 @@
|
||||
#define LING_FAKEDEATH_TIME 400 //40 seconds
|
||||
#define LING_DEAD_GENETICDAMAGE_HEAL_CAP 50 //The lowest value of geneticdamage handle_changeling() can take it to while dead.
|
||||
#define LING_ABSORB_RECENT_SPEECH 8 //The amount of recent spoken lines to gain on absorbing a mob
|
||||
|
||||
/datum/antagonist/changeling
|
||||
name = "Changeling"
|
||||
roundend_category = "changelings"
|
||||
antagpanel_category = "Changeling"
|
||||
job_rank = ROLE_CHANGELING
|
||||
|
||||
var/you_are_greet = TRUE
|
||||
var/give_objectives = TRUE
|
||||
var/team_mode = FALSE //Should assign team objectives ?
|
||||
|
||||
//Changeling Stuff
|
||||
|
||||
var/list/stored_profiles = list() //list of datum/changelingprofile
|
||||
var/datum/changelingprofile/first_prof = null
|
||||
var/dna_max = 6 //How many extra DNA strands the changeling can store for transformation.
|
||||
var/absorbedcount = 0
|
||||
var/chem_charges = 20
|
||||
var/chem_storage = 75
|
||||
var/chem_recharge_rate = 1
|
||||
var/chem_recharge_slowdown = 0
|
||||
var/sting_range = 2
|
||||
var/changelingID = "Changeling"
|
||||
var/geneticdamage = 0
|
||||
var/isabsorbing = 0
|
||||
var/islinking = 0
|
||||
var/geneticpoints = 10
|
||||
var/purchasedpowers = list()
|
||||
var/mimicing = ""
|
||||
var/canrespec = 0
|
||||
var/changeling_speak = 0
|
||||
var/datum/dna/chosen_dna
|
||||
var/obj/effect/proc_holder/changeling/sting/chosen_sting
|
||||
var/datum/cellular_emporium/cellular_emporium
|
||||
var/datum/action/innate/cellular_emporium/emporium_action
|
||||
|
||||
// wip stuff
|
||||
var/static/list/all_powers = typecacheof(/obj/effect/proc_holder/changeling,TRUE)
|
||||
|
||||
|
||||
/datum/antagonist/changeling/Destroy()
|
||||
QDEL_NULL(cellular_emporium)
|
||||
QDEL_NULL(emporium_action)
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/changeling/proc/generate_name()
|
||||
var/honorific
|
||||
if(owner.current.gender == FEMALE)
|
||||
honorific = "Ms."
|
||||
else
|
||||
honorific = "Mr."
|
||||
if(GLOB.possible_changeling_IDs.len)
|
||||
changelingID = pick(GLOB.possible_changeling_IDs)
|
||||
GLOB.possible_changeling_IDs -= changelingID
|
||||
changelingID = "[honorific] [changelingID]"
|
||||
else
|
||||
changelingID = "[honorific] [rand(1,999)]"
|
||||
|
||||
/datum/antagonist/changeling/proc/create_actions()
|
||||
cellular_emporium = new(src)
|
||||
emporium_action = new(cellular_emporium)
|
||||
|
||||
/datum/antagonist/changeling/on_gain()
|
||||
generate_name()
|
||||
create_actions()
|
||||
reset_powers()
|
||||
create_initial_profile()
|
||||
if(give_objectives)
|
||||
if(team_mode)
|
||||
forge_team_objectives()
|
||||
forge_objectives()
|
||||
remove_clownmut()
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/changeling/on_removal()
|
||||
//We'll be using this from now on
|
||||
var/mob/living/carbon/C = owner.current
|
||||
if(istype(C))
|
||||
var/obj/item/organ/brain/B = C.getorganslot(ORGAN_SLOT_BRAIN)
|
||||
if(B && (B.decoy_override != initial(B.decoy_override)))
|
||||
B.vital = TRUE
|
||||
B.decoy_override = FALSE
|
||||
remove_changeling_powers()
|
||||
owner.objectives -= objectives
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/changeling/proc/remove_clownmut()
|
||||
if (owner)
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
if(istype(H) && owner.assigned_role == "Clown")
|
||||
to_chat(H, "You have evolved beyond your clownish nature, allowing you to wield weapons without harming yourself.")
|
||||
H.dna.remove_mutation(CLOWNMUT)
|
||||
|
||||
/datum/antagonist/changeling/proc/reset_properties()
|
||||
changeling_speak = 0
|
||||
chosen_sting = null
|
||||
geneticpoints = initial(geneticpoints)
|
||||
sting_range = initial(sting_range)
|
||||
chem_storage = initial(chem_storage)
|
||||
chem_recharge_rate = initial(chem_recharge_rate)
|
||||
chem_charges = min(chem_charges, chem_storage)
|
||||
chem_recharge_slowdown = initial(chem_recharge_slowdown)
|
||||
mimicing = ""
|
||||
|
||||
/datum/antagonist/changeling/proc/remove_changeling_powers()
|
||||
if(ishuman(owner.current) || ismonkey(owner.current))
|
||||
reset_properties()
|
||||
for(var/obj/effect/proc_holder/changeling/p in purchasedpowers)
|
||||
if(p.always_keep)
|
||||
continue
|
||||
purchasedpowers -= p
|
||||
p.on_refund(owner.current)
|
||||
|
||||
//MOVE THIS
|
||||
if(owner.current.hud_used)
|
||||
owner.current.hud_used.lingstingdisplay.icon_state = null
|
||||
owner.current.hud_used.lingstingdisplay.invisibility = INVISIBILITY_ABSTRACT
|
||||
|
||||
/datum/antagonist/changeling/proc/reset_powers()
|
||||
if(purchasedpowers)
|
||||
remove_changeling_powers()
|
||||
//Repurchase free powers.
|
||||
for(var/path in all_powers)
|
||||
var/obj/effect/proc_holder/changeling/S = new path()
|
||||
if(!S.dna_cost)
|
||||
if(!has_sting(S))
|
||||
purchasedpowers += S
|
||||
S.on_purchase(owner.current,TRUE)
|
||||
|
||||
/datum/antagonist/changeling/proc/has_sting(obj/effect/proc_holder/changeling/power)
|
||||
for(var/obj/effect/proc_holder/changeling/P in purchasedpowers)
|
||||
if(initial(power.name) == P.name)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
/datum/antagonist/changeling/proc/purchase_power(sting_name)
|
||||
var/obj/effect/proc_holder/changeling/thepower = null
|
||||
|
||||
for(var/path in all_powers)
|
||||
var/obj/effect/proc_holder/changeling/S = path
|
||||
if(initial(S.name) == sting_name)
|
||||
thepower = new path()
|
||||
break
|
||||
|
||||
if(!thepower)
|
||||
to_chat(owner.current, "This is awkward. Changeling power purchase failed, please report this bug to a coder!")
|
||||
return
|
||||
|
||||
if(absorbedcount < thepower.req_dna)
|
||||
to_chat(owner.current, "We lack the energy to evolve this ability!")
|
||||
return
|
||||
|
||||
if(has_sting(thepower))
|
||||
to_chat(owner.current, "We have already evolved this ability!")
|
||||
return
|
||||
|
||||
if(thepower.dna_cost < 0)
|
||||
to_chat(owner.current, "We cannot evolve this ability.")
|
||||
return
|
||||
|
||||
if(geneticpoints < thepower.dna_cost)
|
||||
to_chat(owner.current, "We have reached our capacity for abilities.")
|
||||
return
|
||||
|
||||
if(owner.current.status_flags & FAKEDEATH)//To avoid potential exploits by buying new powers while in stasis, which clears your verblist.
|
||||
to_chat(owner.current, "We lack the energy to evolve new abilities right now.")
|
||||
return
|
||||
|
||||
geneticpoints -= thepower.dna_cost
|
||||
purchasedpowers += thepower
|
||||
thepower.on_purchase(owner.current)
|
||||
|
||||
/datum/antagonist/changeling/proc/readapt()
|
||||
if(!ishuman(owner.current))
|
||||
to_chat(owner.current, "<span class='danger'>We can't remove our evolutions in this form!</span>")
|
||||
return
|
||||
if(canrespec)
|
||||
to_chat(owner.current, "<span class='notice'>We have removed our evolutions from this form, and are now ready to readapt.</span>")
|
||||
reset_powers()
|
||||
canrespec = 0
|
||||
SSblackbox.record_feedback("tally", "changeling_power_purchase", 1, "Readapt")
|
||||
return 1
|
||||
else
|
||||
to_chat(owner.current, "<span class='danger'>You lack the power to readapt your evolutions!</span>")
|
||||
return 0
|
||||
|
||||
//Called in life()
|
||||
/datum/antagonist/changeling/proc/regenerate()
|
||||
var/mob/living/carbon/the_ling = owner.current
|
||||
if(istype(the_ling))
|
||||
emporium_action.Grant(the_ling)
|
||||
if(the_ling.stat == DEAD)
|
||||
chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), (chem_storage*0.5))
|
||||
geneticdamage = max(LING_DEAD_GENETICDAMAGE_HEAL_CAP,geneticdamage-1)
|
||||
else //not dead? no chem/geneticdamage caps.
|
||||
chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), chem_storage)
|
||||
geneticdamage = max(0, geneticdamage-1)
|
||||
|
||||
|
||||
/datum/antagonist/changeling/proc/get_dna(dna_owner)
|
||||
for(var/datum/changelingprofile/prof in stored_profiles)
|
||||
if(dna_owner == prof.name)
|
||||
return prof
|
||||
|
||||
/datum/antagonist/changeling/proc/has_dna(datum/dna/tDNA)
|
||||
for(var/datum/changelingprofile/prof in stored_profiles)
|
||||
if(tDNA.is_same_as(prof.dna))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/antagonist/changeling/proc/can_absorb_dna(mob/living/carbon/human/target, var/verbose=1)
|
||||
var/mob/living/carbon/user = owner.current
|
||||
if(!istype(user))
|
||||
return
|
||||
if(stored_profiles.len)
|
||||
var/datum/changelingprofile/prof = stored_profiles[1]
|
||||
if(prof.dna == user.dna && stored_profiles.len >= dna_max)//If our current DNA is the stalest, we gotta ditch it.
|
||||
if(verbose)
|
||||
to_chat(user, "<span class='warning'>We have reached our capacity to store genetic information! We must transform before absorbing more.</span>")
|
||||
return
|
||||
if(!target)
|
||||
return
|
||||
if(NO_DNA_COPY in target.dna.species.species_traits)
|
||||
if(verbose)
|
||||
to_chat(user, "<span class='warning'>[target] is not compatible with our biology.</span>")
|
||||
return
|
||||
if((target.has_disability(DISABILITY_NOCLONE)) || (target.has_disability(DISABILITY_NOCLONE)))
|
||||
if(verbose)
|
||||
to_chat(user, "<span class='warning'>DNA of [target] is ruined beyond usability!</span>")
|
||||
return
|
||||
if(!ishuman(target))//Absorbing monkeys is entirely possible, but it can cause issues with transforming. That's what lesser form is for anyway!
|
||||
if(verbose)
|
||||
to_chat(user, "<span class='warning'>We could gain no benefit from absorbing a lesser creature.</span>")
|
||||
return
|
||||
if(has_dna(target.dna))
|
||||
if(verbose)
|
||||
to_chat(user, "<span class='warning'>We already have this DNA in storage!</span>")
|
||||
return
|
||||
if(!target.has_dna())
|
||||
if(verbose)
|
||||
to_chat(user, "<span class='warning'>[target] is not compatible with our biology.</span>")
|
||||
return
|
||||
return 1
|
||||
|
||||
|
||||
/datum/antagonist/changeling/proc/create_profile(mob/living/carbon/human/H, protect = 0)
|
||||
var/datum/changelingprofile/prof = new
|
||||
|
||||
H.dna.real_name = H.real_name //Set this again, just to be sure that it's properly set.
|
||||
var/datum/dna/new_dna = new H.dna.type
|
||||
H.dna.copy_dna(new_dna)
|
||||
prof.dna = new_dna
|
||||
prof.name = H.real_name
|
||||
prof.protected = protect
|
||||
|
||||
prof.underwear = H.underwear
|
||||
prof.undershirt = H.undershirt
|
||||
prof.socks = H.socks
|
||||
|
||||
var/list/slots = list("head", "wear_mask", "back", "wear_suit", "w_uniform", "shoes", "belt", "gloves", "glasses", "ears", "wear_id", "s_store")
|
||||
for(var/slot in slots)
|
||||
if(slot in H.vars)
|
||||
var/obj/item/I = H.vars[slot]
|
||||
if(!I)
|
||||
continue
|
||||
prof.name_list[slot] = I.name
|
||||
prof.appearance_list[slot] = I.appearance
|
||||
prof.flags_cover_list[slot] = I.flags_cover
|
||||
prof.item_color_list[slot] = I.item_color
|
||||
prof.item_state_list[slot] = I.item_state
|
||||
prof.exists_list[slot] = 1
|
||||
else
|
||||
continue
|
||||
|
||||
return prof
|
||||
|
||||
/datum/antagonist/changeling/proc/add_profile(datum/changelingprofile/prof)
|
||||
if(stored_profiles.len > dna_max)
|
||||
if(!push_out_profile())
|
||||
return
|
||||
|
||||
if(!first_prof)
|
||||
first_prof = prof
|
||||
|
||||
stored_profiles += prof
|
||||
absorbedcount++
|
||||
|
||||
/datum/antagonist/changeling/proc/add_new_profile(mob/living/carbon/human/H, protect = 0)
|
||||
var/datum/changelingprofile/prof = create_profile(H, protect)
|
||||
add_profile(prof)
|
||||
return prof
|
||||
|
||||
/datum/antagonist/changeling/proc/remove_profile(mob/living/carbon/human/H, force = 0)
|
||||
for(var/datum/changelingprofile/prof in stored_profiles)
|
||||
if(H.real_name == prof.name)
|
||||
if(prof.protected && !force)
|
||||
continue
|
||||
stored_profiles -= prof
|
||||
qdel(prof)
|
||||
|
||||
/datum/antagonist/changeling/proc/get_profile_to_remove()
|
||||
for(var/datum/changelingprofile/prof in stored_profiles)
|
||||
if(!prof.protected)
|
||||
return prof
|
||||
|
||||
/datum/antagonist/changeling/proc/push_out_profile()
|
||||
var/datum/changelingprofile/removeprofile = get_profile_to_remove()
|
||||
if(removeprofile)
|
||||
stored_profiles -= removeprofile
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
/datum/antagonist/changeling/proc/create_initial_profile()
|
||||
var/mob/living/carbon/C = owner.current //only carbons have dna now, so we have to typecaste
|
||||
if(ishuman(C))
|
||||
add_new_profile(C)
|
||||
|
||||
/datum/antagonist/changeling/apply_innate_effects()
|
||||
//Brains optional.
|
||||
var/mob/living/carbon/C = owner.current
|
||||
if(istype(C))
|
||||
var/obj/item/organ/brain/B = C.getorganslot(ORGAN_SLOT_BRAIN)
|
||||
if(B)
|
||||
B.vital = FALSE
|
||||
B.decoy_override = TRUE
|
||||
update_changeling_icons_added()
|
||||
return
|
||||
|
||||
/datum/antagonist/changeling/remove_innate_effects()
|
||||
update_changeling_icons_removed()
|
||||
return
|
||||
|
||||
|
||||
/datum/antagonist/changeling/greet()
|
||||
if (you_are_greet)
|
||||
to_chat(owner.current, "<span class='boldannounce'>You are [changelingID], a changeling! You have absorbed and taken the form of a human.</span>")
|
||||
to_chat(owner.current, "<span class='boldannounce'>Use say \":g message\" to communicate with your fellow changelings.</span>")
|
||||
to_chat(owner.current, "<b>You must complete the following tasks:</b>")
|
||||
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ling_aler.ogg', 100, FALSE, pressure_affected = FALSE)
|
||||
|
||||
owner.announce_objectives()
|
||||
|
||||
/datum/antagonist/changeling/farewell()
|
||||
to_chat(owner.current, "<span class='userdanger'>You grow weak and lose your powers! You are no longer a changeling and are stuck in your current form!</span>")
|
||||
|
||||
/datum/antagonist/changeling/proc/forge_team_objectives()
|
||||
if(GLOB.changeling_team_objective_type)
|
||||
var/datum/objective/changeling_team_objective/team_objective = new GLOB.changeling_team_objective_type
|
||||
team_objective.owner = owner
|
||||
objectives += team_objective
|
||||
return
|
||||
|
||||
/datum/antagonist/changeling/proc/forge_objectives()
|
||||
//OBJECTIVES - random traitor objectives. Unique objectives "steal brain" and "identity theft".
|
||||
//No escape alone because changelings aren't suited for it and it'd probably just lead to rampant robusting
|
||||
//If it seems like they'd be able to do it in play, add a 10% chance to have to escape alone
|
||||
|
||||
var/escape_objective_possible = TRUE
|
||||
|
||||
//if there's a team objective, check if it's compatible with escape objectives
|
||||
for(var/datum/objective/changeling_team_objective/CTO in objectives)
|
||||
if(!CTO.escape_objective_compatible)
|
||||
escape_objective_possible = FALSE
|
||||
break
|
||||
|
||||
var/datum/objective/absorb/absorb_objective = new
|
||||
absorb_objective.owner = owner
|
||||
absorb_objective.gen_amount_goal(6, 8)
|
||||
objectives += absorb_objective
|
||||
|
||||
if(prob(60))
|
||||
if(prob(85))
|
||||
var/datum/objective/steal/steal_objective = new
|
||||
steal_objective.owner = owner
|
||||
steal_objective.find_target()
|
||||
objectives += steal_objective
|
||||
else
|
||||
var/datum/objective/download/download_objective = new
|
||||
download_objective.owner = owner
|
||||
download_objective.gen_amount_goal()
|
||||
objectives += download_objective
|
||||
|
||||
var/list/active_ais = active_ais()
|
||||
if(active_ais.len && prob(100/GLOB.joined_player_list.len))
|
||||
var/datum/objective/destroy/destroy_objective = new
|
||||
destroy_objective.owner = owner
|
||||
destroy_objective.find_target()
|
||||
objectives += destroy_objective
|
||||
else
|
||||
if(prob(70))
|
||||
var/datum/objective/assassinate/kill_objective = new
|
||||
kill_objective.owner = owner
|
||||
if(team_mode) //No backstabbing while in a team
|
||||
kill_objective.find_target_by_role(role = ROLE_CHANGELING, role_type = 1, invert = 1)
|
||||
else
|
||||
kill_objective.find_target()
|
||||
objectives += kill_objective
|
||||
else
|
||||
var/datum/objective/maroon/maroon_objective = new
|
||||
maroon_objective.owner = owner
|
||||
if(team_mode)
|
||||
maroon_objective.find_target_by_role(role = ROLE_CHANGELING, role_type = 1, invert = 1)
|
||||
else
|
||||
maroon_objective.find_target()
|
||||
objectives += maroon_objective
|
||||
|
||||
if (!(locate(/datum/objective/escape) in objectives) && escape_objective_possible)
|
||||
var/datum/objective/escape/escape_with_identity/identity_theft = new
|
||||
identity_theft.owner = owner
|
||||
identity_theft.target = maroon_objective.target
|
||||
identity_theft.update_explanation_text()
|
||||
objectives += identity_theft
|
||||
escape_objective_possible = FALSE
|
||||
|
||||
if (!(locate(/datum/objective/escape) in objectives) && escape_objective_possible)
|
||||
if(prob(50))
|
||||
var/datum/objective/escape/escape_objective = new
|
||||
escape_objective.owner = owner
|
||||
objectives += escape_objective
|
||||
else
|
||||
var/datum/objective/escape/escape_with_identity/identity_theft = new
|
||||
identity_theft.owner = owner
|
||||
if(team_mode)
|
||||
identity_theft.find_target_by_role(role = ROLE_CHANGELING, role_type = 1, invert = 1)
|
||||
else
|
||||
identity_theft.find_target()
|
||||
objectives += identity_theft
|
||||
escape_objective_possible = FALSE
|
||||
|
||||
owner.objectives |= objectives
|
||||
|
||||
/datum/antagonist/changeling/proc/update_changeling_icons_added()
|
||||
var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_CHANGELING]
|
||||
hud.join_hud(owner.current)
|
||||
set_antag_hud(owner.current, "changling")
|
||||
|
||||
/datum/antagonist/changeling/proc/update_changeling_icons_removed()
|
||||
var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_CHANGELING]
|
||||
hud.leave_hud(owner.current)
|
||||
set_antag_hud(owner.current, null)
|
||||
|
||||
/datum/antagonist/changeling/admin_add(datum/mind/new_owner,mob/admin)
|
||||
. = ..()
|
||||
to_chat(new_owner.current, "<span class='boldannounce'>Our powers have awoken. A flash of memory returns to us...we are [changelingID], a changeling!</span>")
|
||||
|
||||
/datum/antagonist/changeling/get_admin_commands()
|
||||
. = ..()
|
||||
if(stored_profiles.len && (owner.current.real_name != first_prof.name))
|
||||
.["Transform to initial appearance."] = CALLBACK(src,.proc/admin_restore_appearance)
|
||||
|
||||
/datum/antagonist/changeling/proc/admin_restore_appearance(mob/admin)
|
||||
if(!stored_profiles.len || !iscarbon(owner.current))
|
||||
to_chat(admin, "<span class='danger'>Resetting DNA failed!</span>")
|
||||
else
|
||||
var/mob/living/carbon/C = owner.current
|
||||
first_prof.dna.transfer_identity(C, transfer_SE=1)
|
||||
C.real_name = first_prof.name
|
||||
C.updateappearance(mutcolor_update=1)
|
||||
C.domutcheck()
|
||||
|
||||
// Profile
|
||||
|
||||
/datum/changelingprofile
|
||||
var/name = "a bug"
|
||||
|
||||
var/protected = 0
|
||||
|
||||
var/datum/dna/dna = null
|
||||
var/list/name_list = list() //associative list of slotname = itemname
|
||||
var/list/appearance_list = list()
|
||||
var/list/flags_cover_list = list()
|
||||
var/list/exists_list = list()
|
||||
var/list/item_color_list = list()
|
||||
var/list/item_state_list = list()
|
||||
|
||||
var/underwear
|
||||
var/undershirt
|
||||
var/socks
|
||||
|
||||
/datum/changelingprofile/Destroy()
|
||||
qdel(dna)
|
||||
. = ..()
|
||||
|
||||
/datum/changelingprofile/proc/copy_profile(datum/changelingprofile/newprofile)
|
||||
newprofile.name = name
|
||||
newprofile.protected = protected
|
||||
newprofile.dna = new dna.type
|
||||
dna.copy_dna(newprofile.dna)
|
||||
newprofile.name_list = name_list.Copy()
|
||||
newprofile.appearance_list = appearance_list.Copy()
|
||||
newprofile.flags_cover_list = flags_cover_list.Copy()
|
||||
newprofile.exists_list = exists_list.Copy()
|
||||
newprofile.item_color_list = item_color_list.Copy()
|
||||
newprofile.item_state_list = item_state_list.Copy()
|
||||
newprofile.underwear = underwear
|
||||
newprofile.undershirt = undershirt
|
||||
newprofile.socks = socks
|
||||
|
||||
|
||||
/datum/antagonist/changeling/xenobio
|
||||
name = "Xenobio Changeling"
|
||||
give_objectives = FALSE
|
||||
show_in_roundend = FALSE //These are here for admin tracking purposes only
|
||||
you_are_greet = FALSE
|
||||
|
||||
/datum/antagonist/changeling/roundend_report()
|
||||
var/list/parts = list()
|
||||
|
||||
var/changelingwin = 1
|
||||
if(!owner.current)
|
||||
changelingwin = 0
|
||||
|
||||
parts += printplayer(owner)
|
||||
|
||||
//Removed sanity if(changeling) because we -want- a runtime to inform us that the changelings list is incorrect and needs to be fixed.
|
||||
parts += "<b>Changeling ID:</b> [changelingID]."
|
||||
parts += "<b>Genomes Extracted:</b> [absorbedcount]"
|
||||
parts += " "
|
||||
if(objectives.len)
|
||||
var/count = 1
|
||||
for(var/datum/objective/objective in objectives)
|
||||
if(objective.check_completion())
|
||||
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='greentext'>Success!</b></span>"
|
||||
else
|
||||
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
changelingwin = 0
|
||||
count++
|
||||
|
||||
if(changelingwin)
|
||||
parts += "<span class='greentext'>The changeling was successful!</span>"
|
||||
else
|
||||
parts += "<span class='redtext'>The changeling has failed.</span>"
|
||||
|
||||
return parts.Join("<br>")
|
||||
|
||||
/datum/antagonist/changeling/antag_listing_name()
|
||||
return ..() + "([changelingID])"
|
||||
|
||||
/datum/antagonist/changeling/xenobio/antag_listing_name()
|
||||
return ..() + "(Xenobio)"
|
||||
@@ -1,219 +0,0 @@
|
||||
//CLOCKCULT PROOF OF CONCEPT
|
||||
/datum/antagonist/clockcult
|
||||
name = "Clock Cultist"
|
||||
roundend_category = "clock cultists"
|
||||
antagpanel_category = "Clockcult"
|
||||
job_rank = ROLE_SERVANT_OF_RATVAR
|
||||
var/datum/action/innate/hierophant/hierophant_network = new()
|
||||
var/datum/team/clockcult/clock_team
|
||||
var/make_team = TRUE //This should be only false for tutorial scarabs
|
||||
|
||||
/datum/antagonist/clockcult/silent
|
||||
silent = TRUE
|
||||
show_in_antagpanel = FALSE //internal
|
||||
|
||||
/datum/antagonist/clockcult/Destroy()
|
||||
qdel(hierophant_network)
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/clockcult/get_team()
|
||||
return clock_team
|
||||
|
||||
/datum/antagonist/clockcult/create_team(datum/team/clockcult/new_team)
|
||||
if(!new_team && make_team)
|
||||
//TODO blah blah same as the others, allow multiple
|
||||
for(var/datum/antagonist/clockcult/H in GLOB.antagonists)
|
||||
if(!H.owner)
|
||||
continue
|
||||
if(H.clock_team)
|
||||
clock_team = H.clock_team
|
||||
return
|
||||
clock_team = new /datum/team/clockcult
|
||||
return
|
||||
if(make_team && !istype(new_team))
|
||||
stack_trace("Wrong team type passed to [type] initialization.")
|
||||
clock_team = new_team
|
||||
|
||||
/datum/antagonist/clockcult/can_be_owned(datum/mind/new_owner)
|
||||
. = ..()
|
||||
if(.)
|
||||
. = is_eligible_servant(new_owner.current)
|
||||
|
||||
/datum/antagonist/clockcult/greet()
|
||||
if(!owner.current || silent)
|
||||
return
|
||||
owner.current.visible_message("<span class='heavy_brass'>[owner.current]'s eyes glow a blazing yellow!</span>", null, null, 7, owner.current) //don't show the owner this message
|
||||
to_chat(owner.current, "<span class='heavy_brass'>Assist your new companions in their righteous efforts. Your goal is theirs, and theirs yours. You serve the Clockwork \
|
||||
Justiciar above all else. Perform his every whim without hesitation.</span>")
|
||||
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/clockcultalr.ogg', 70, FALSE, pressure_affected = FALSE)
|
||||
|
||||
/datum/antagonist/clockcult/on_gain()
|
||||
var/mob/living/current = owner.current
|
||||
SSticker.mode.servants_of_ratvar += owner
|
||||
SSticker.mode.update_servant_icons_added(owner)
|
||||
owner.special_role = ROLE_SERVANT_OF_RATVAR
|
||||
owner.current.log_message("<font color=#BE8700>Has been converted to the cult of Ratvar!</font>", INDIVIDUAL_ATTACK_LOG)
|
||||
if(issilicon(current))
|
||||
if(iscyborg(current) && !silent)
|
||||
var/mob/living/silicon/robot/R = current
|
||||
if(R.connected_ai && !is_servant_of_ratvar(R.connected_ai))
|
||||
to_chat(R, "<span class='boldwarning'>You have been desynced from your master AI.<br>\
|
||||
In addition, your onboard camera is no longer active and you have gained additional equipment, including a limited clockwork slab.</span>")
|
||||
else
|
||||
to_chat(R, "<span class='boldwarning'>Your onboard camera is no longer active and you have gained additional equipment, including a limited clockwork slab.</span>")
|
||||
if(isAI(current))
|
||||
to_chat(current, "<span class='boldwarning'>You are now able to use your cameras to listen in on conversations, but can no longer speak in anything but Ratvarian.</span>")
|
||||
to_chat(current, "<span class='heavy_brass'>You can communicate with other servants by using the Hierophant Network action button in the upper left.</span>")
|
||||
else if(isbrain(current) || isclockmob(current))
|
||||
to_chat(current, "<span class='nezbere'>You can communicate with other servants by using the Hierophant Network action button in the upper left.</span>")
|
||||
..()
|
||||
to_chat(current, "<b>This is Ratvar's will:</b> [CLOCKCULT_OBJECTIVE]")
|
||||
antag_memory += "<b>Ratvar's will:</b> [CLOCKCULT_OBJECTIVE]<br>" //Memorize the objectives
|
||||
|
||||
/datum/antagonist/clockcult/apply_innate_effects(mob/living/mob_override)
|
||||
. = ..()
|
||||
var/mob/living/current = owner.current
|
||||
if(istype(mob_override))
|
||||
current = mob_override
|
||||
GLOB.all_clockwork_mobs += current
|
||||
current.faction |= "ratvar"
|
||||
current.grant_language(/datum/language/ratvar)
|
||||
current.update_action_buttons_icon() //because a few clockcult things are action buttons and we may be wearing/holding them for whatever reason, we need to update buttons
|
||||
if(issilicon(current))
|
||||
var/mob/living/silicon/S = current
|
||||
if(iscyborg(S))
|
||||
var/mob/living/silicon/robot/R = S
|
||||
if(!R.shell)
|
||||
R.UnlinkSelf()
|
||||
R.module.rebuild_modules()
|
||||
else if(isAI(S))
|
||||
var/mob/living/silicon/ai/A = S
|
||||
A.can_be_carded = FALSE
|
||||
A.requires_power = POWER_REQ_CLOCKCULT
|
||||
var/list/AI_frame = list(mutable_appearance('icons/mob/clockwork_mobs.dmi', "aiframe")) //make the AI's cool frame
|
||||
for(var/d in GLOB.cardinals)
|
||||
AI_frame += image('icons/mob/clockwork_mobs.dmi', A, "eye[rand(1, 10)]", dir = d) //the eyes are randomly fast or slow
|
||||
A.add_overlay(AI_frame)
|
||||
if(!A.lacks_power())
|
||||
A.ai_restore_power()
|
||||
if(A.eyeobj)
|
||||
A.eyeobj.relay_speech = TRUE
|
||||
for(var/mob/living/silicon/robot/R in A.connected_robots)
|
||||
if(R.connected_ai == A)
|
||||
add_servant_of_ratvar(R)
|
||||
S.laws = new/datum/ai_laws/ratvar
|
||||
S.laws.associate(S)
|
||||
S.update_icons()
|
||||
S.show_laws()
|
||||
hierophant_network.title = "Silicon"
|
||||
hierophant_network.span_for_name = "nezbere"
|
||||
hierophant_network.span_for_message = "brass"
|
||||
else if(isbrain(current))
|
||||
hierophant_network.title = "Vessel"
|
||||
hierophant_network.span_for_name = "nezbere"
|
||||
hierophant_network.span_for_message = "alloy"
|
||||
else if(isclockmob(current))
|
||||
hierophant_network.title = "Construct"
|
||||
hierophant_network.span_for_name = "nezbere"
|
||||
hierophant_network.span_for_message = "brass"
|
||||
hierophant_network.Grant(current)
|
||||
current.throw_alert("clockinfo", /obj/screen/alert/clockwork/infodump)
|
||||
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
|
||||
if(G.active && ishuman(current))
|
||||
current.add_overlay(mutable_appearance('icons/effects/genetics.dmi', "servitude", -MUTATIONS_LAYER))
|
||||
|
||||
/datum/antagonist/clockcult/remove_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/current = owner.current
|
||||
if(istype(mob_override))
|
||||
current = mob_override
|
||||
GLOB.all_clockwork_mobs -= current
|
||||
current.faction -= "ratvar"
|
||||
current.remove_language(/datum/language/ratvar)
|
||||
current.clear_alert("clockinfo")
|
||||
for(var/datum/action/innate/clockwork_armaments/C in owner.current.actions) //Removes any bound clockwork armor
|
||||
qdel(C)
|
||||
for(var/datum/action/innate/call_weapon/W in owner.current.actions) //and weapons too
|
||||
qdel(W)
|
||||
if(issilicon(current))
|
||||
var/mob/living/silicon/S = current
|
||||
if(isAI(S))
|
||||
var/mob/living/silicon/ai/A = S
|
||||
A.can_be_carded = initial(A.can_be_carded)
|
||||
A.requires_power = initial(A.requires_power)
|
||||
A.cut_overlays()
|
||||
S.make_laws()
|
||||
S.update_icons()
|
||||
S.show_laws()
|
||||
var/mob/living/temp_owner = current
|
||||
..()
|
||||
if(iscyborg(temp_owner))
|
||||
var/mob/living/silicon/robot/R = temp_owner
|
||||
R.module.rebuild_modules()
|
||||
if(temp_owner)
|
||||
temp_owner.update_action_buttons_icon() //because a few clockcult things are action buttons and we may be wearing/holding them, we need to update buttons
|
||||
temp_owner.cut_overlays()
|
||||
temp_owner.regenerate_icons()
|
||||
|
||||
/datum/antagonist/clockcult/on_removal()
|
||||
SSticker.mode.servants_of_ratvar -= owner
|
||||
SSticker.mode.update_servant_icons_removed(owner)
|
||||
if(!silent)
|
||||
owner.current.visible_message("<span class='deconversion_message'>[owner] seems to have remembered their true allegiance!</span>", null, null, null, owner.current)
|
||||
to_chat(owner, "<span class='userdanger'>A cold, cold darkness flows through your mind, extinguishing the Justiciar's light and all of your memories as his servant.</span>")
|
||||
owner.current.log_message("<font color=#BE8700>Has renounced the cult of Ratvar!</font>", INDIVIDUAL_ATTACK_LOG)
|
||||
owner.special_role = null
|
||||
if(iscyborg(owner.current))
|
||||
to_chat(owner.current, "<span class='warning'>Despite your freedom from Ratvar's influence, you are still irreparably damaged and no longer possess certain functions such as AI linking.</span>")
|
||||
. = ..()
|
||||
|
||||
|
||||
/datum/antagonist/clockcult/admin_add(datum/mind/new_owner,mob/admin)
|
||||
add_servant_of_ratvar(new_owner.current, TRUE)
|
||||
message_admins("[key_name_admin(admin)] has made [new_owner.current] into a servant of Ratvar.")
|
||||
log_admin("[key_name(admin)] has made [new_owner.current] into a servant of Ratvar.")
|
||||
|
||||
/datum/antagonist/clockcult/admin_remove(mob/user)
|
||||
remove_servant_of_ratvar(owner.current, TRUE)
|
||||
message_admins("[key_name_admin(user)] has removed clockwork servant status from [owner.current].")
|
||||
log_admin("[key_name(user)] has removed clockwork servant status from [owner.current].")
|
||||
|
||||
/datum/antagonist/clockcult/get_admin_commands()
|
||||
. = ..()
|
||||
.["Give slab"] = CALLBACK(src,.proc/admin_give_slab)
|
||||
|
||||
/datum/antagonist/clockcult/proc/admin_give_slab(mob/admin)
|
||||
if(!SSticker.mode.equip_servant(owner.current))
|
||||
to_chat(admin, "<span class='warning'>Failed to outfit [owner.current]!</span>")
|
||||
else
|
||||
to_chat(admin, "<span class='notice'>Successfully gave [owner.current] servant equipment!</span>")
|
||||
|
||||
/datum/team/clockcult
|
||||
name = "Clockcult"
|
||||
var/list/objective
|
||||
var/datum/mind/eminence
|
||||
|
||||
/datum/team/clockcult/proc/check_clockwork_victory()
|
||||
if(GLOB.clockwork_gateway_activated)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/team/clockcult/roundend_report()
|
||||
var/list/parts = list()
|
||||
|
||||
if(check_clockwork_victory())
|
||||
parts += "<span class='greentext big'>Ratvar's servants defended the Ark until its activation!</span>"
|
||||
else
|
||||
parts += "<span class='redtext big'>The Ark was destroyed! Ratvar will rust away for all eternity!</span>"
|
||||
parts += " "
|
||||
parts += "<b>The servants' objective was:</b> [CLOCKCULT_OBJECTIVE]."
|
||||
parts += "<b>Construction Value(CV)</b> was: <b>[GLOB.clockwork_construction_value]</b>"
|
||||
for(var/i in SSticker.scripture_states)
|
||||
if(i != SCRIPTURE_DRIVER)
|
||||
parts += "<b>[i] scripture</b> was: <b>[SSticker.scripture_states[i] ? "UN":""]LOCKED</b>"
|
||||
if(eminence)
|
||||
parts += "<span class='header'>The Eminence was:</span> [printplayer(eminence)]"
|
||||
if(members.len)
|
||||
parts += "<span class='header'>Ratvar's servants were:</span>"
|
||||
parts += printplayerlist(members - eminence)
|
||||
|
||||
return "<div class='panel clockborder'>[parts.Join("<br>")]</div>"
|
||||
@@ -1,329 +0,0 @@
|
||||
#define SUMMON_POSSIBILITIES 3
|
||||
|
||||
/datum/antagonist/cult
|
||||
name = "Cultist"
|
||||
roundend_category = "cultists"
|
||||
antagpanel_category = "Cult"
|
||||
var/datum/action/innate/cult/comm/communion = new
|
||||
var/datum/action/innate/cult/mastervote/vote = new
|
||||
job_rank = ROLE_CULTIST
|
||||
var/ignore_implant = FALSE
|
||||
var/give_equipment = FALSE
|
||||
|
||||
var/datum/team/cult/cult_team
|
||||
|
||||
/datum/antagonist/cult/get_team()
|
||||
return cult_team
|
||||
|
||||
/datum/antagonist/cult/create_team(datum/team/cult/new_team)
|
||||
if(!new_team)
|
||||
//todo remove this and allow admin buttons to create more than one cult
|
||||
for(var/datum/antagonist/cult/H in GLOB.antagonists)
|
||||
if(!H.owner)
|
||||
continue
|
||||
if(H.cult_team)
|
||||
cult_team = H.cult_team
|
||||
return
|
||||
cult_team = new /datum/team/cult
|
||||
cult_team.setup_objectives()
|
||||
return
|
||||
if(!istype(new_team))
|
||||
stack_trace("Wrong team type passed to [type] initialization.")
|
||||
cult_team = new_team
|
||||
|
||||
/datum/antagonist/cult/proc/add_objectives()
|
||||
objectives |= cult_team.objectives
|
||||
owner.objectives |= objectives
|
||||
|
||||
/datum/antagonist/cult/proc/remove_objectives()
|
||||
owner.objectives -= objectives
|
||||
|
||||
/datum/antagonist/cult/Destroy()
|
||||
QDEL_NULL(communion)
|
||||
QDEL_NULL(vote)
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/cult/can_be_owned(datum/mind/new_owner)
|
||||
. = ..()
|
||||
if(. && !ignore_implant)
|
||||
. = is_convertable_to_cult(new_owner.current,cult_team)
|
||||
|
||||
/datum/antagonist/cult/greet()
|
||||
to_chat(owner, "<span class='userdanger'>You are a member of the cult!</span>")
|
||||
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/bloodcult.ogg', 100, FALSE, pressure_affected = FALSE)//subject to change
|
||||
owner.announce_objectives()
|
||||
|
||||
/datum/antagonist/cult/on_gain()
|
||||
. = ..()
|
||||
var/mob/living/current = owner.current
|
||||
add_objectives()
|
||||
if(give_equipment)
|
||||
equip_cultist()
|
||||
SSticker.mode.cult += owner // Only add after they've been given objectives
|
||||
SSticker.mode.update_cult_icons_added(owner)
|
||||
current.log_message("<font color=#960000>Has been converted to the cult of Nar'Sie!</font>", INDIVIDUAL_ATTACK_LOG)
|
||||
|
||||
if(cult_team.blood_target && cult_team.blood_target_image && current.client)
|
||||
current.client.images += cult_team.blood_target_image
|
||||
|
||||
|
||||
/datum/antagonist/cult/proc/equip_cultist(tome=FALSE)
|
||||
var/mob/living/carbon/H = owner.current
|
||||
if(!istype(H))
|
||||
return
|
||||
if (owner.assigned_role == "Clown")
|
||||
to_chat(owner, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
|
||||
H.dna.remove_mutation(CLOWNMUT)
|
||||
|
||||
if(tome)
|
||||
. += cult_give_item(/obj/item/tome, H)
|
||||
else
|
||||
. += cult_give_item(/obj/item/paper/talisman/supply, H)
|
||||
to_chat(owner, "These will help you start the cult on this station. Use them well, and remember - you are not the only one.</span>")
|
||||
|
||||
|
||||
/datum/antagonist/cult/proc/cult_give_item(obj/item/item_path, mob/living/carbon/human/mob)
|
||||
var/list/slots = list(
|
||||
"backpack" = slot_in_backpack,
|
||||
"left pocket" = slot_l_store,
|
||||
"right pocket" = slot_r_store
|
||||
)
|
||||
|
||||
var/T = new item_path(mob)
|
||||
var/item_name = initial(item_path.name)
|
||||
var/where = mob.equip_in_one_of_slots(T, slots)
|
||||
if(!where)
|
||||
to_chat(mob, "<span class='userdanger'>Unfortunately, you weren't able to get a [item_name]. This is very bad and you should adminhelp immediately (press F1).</span>")
|
||||
return 0
|
||||
else
|
||||
to_chat(mob, "<span class='danger'>You have a [item_name] in your [where].</span>")
|
||||
if(where == "backpack")
|
||||
var/obj/item/storage/B = mob.back
|
||||
B.orient2hud(mob)
|
||||
B.show_to(mob)
|
||||
return 1
|
||||
|
||||
/datum/antagonist/cult/apply_innate_effects(mob/living/mob_override)
|
||||
. = ..()
|
||||
var/mob/living/current = owner.current
|
||||
if(mob_override)
|
||||
current = mob_override
|
||||
current.faction |= "cult"
|
||||
current.grant_language(/datum/language/narsie)
|
||||
current.verbs += /mob/living/proc/cult_help
|
||||
if(!cult_team.cult_mastered)
|
||||
vote.Grant(current)
|
||||
communion.Grant(current)
|
||||
current.throw_alert("bloodsense", /obj/screen/alert/bloodsense)
|
||||
|
||||
/datum/antagonist/cult/remove_innate_effects(mob/living/mob_override)
|
||||
. = ..()
|
||||
var/mob/living/current = owner.current
|
||||
if(mob_override)
|
||||
current = mob_override
|
||||
current.faction -= "cult"
|
||||
current.remove_language(/datum/language/narsie)
|
||||
current.verbs -= /mob/living/proc/cult_help
|
||||
vote.Remove(current)
|
||||
communion.Remove(current)
|
||||
current.clear_alert("bloodsense")
|
||||
|
||||
/datum/antagonist/cult/on_removal()
|
||||
remove_objectives()
|
||||
SSticker.mode.cult -= owner
|
||||
SSticker.mode.update_cult_icons_removed(owner)
|
||||
if(!silent)
|
||||
owner.current.visible_message("<span class='deconversion_message'>[owner.current] looks like [owner.current.p_they()] just reverted to their old faith!</span>", null, null, null, owner.current)
|
||||
to_chat(owner.current, "<span class='userdanger'>An unfamiliar white light flashes through your mind, cleansing the taint of the Geometer and all your memories as her servant.</span>")
|
||||
owner.current.log_message("<font color=#960000>Has renounced the cult of Nar'Sie!</font>", INDIVIDUAL_ATTACK_LOG)
|
||||
if(cult_team.blood_target && cult_team.blood_target_image && owner.current.client)
|
||||
owner.current.client.images -= cult_team.blood_target_image
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/cult/admin_add(datum/mind/new_owner,mob/admin)
|
||||
give_equipment = FALSE
|
||||
new_owner.add_antag_datum(src)
|
||||
message_admins("[key_name_admin(admin)] has cult'ed [new_owner.current].")
|
||||
log_admin("[key_name(admin)] has cult'ed [new_owner.current].")
|
||||
|
||||
/datum/antagonist/cult/admin_remove(mob/user)
|
||||
message_admins("[key_name_admin(user)] has decult'ed [owner.current].")
|
||||
log_admin("[key_name(user)] has decult'ed [owner.current].")
|
||||
SSticker.mode.remove_cultist(owner,silent=TRUE) //disgusting
|
||||
|
||||
/datum/antagonist/cult/get_admin_commands()
|
||||
. = ..()
|
||||
.["Tome"] = CALLBACK(src,.proc/admin_give_tome)
|
||||
.["Amulet"] = CALLBACK(src,.proc/admin_give_amulet)
|
||||
|
||||
/datum/antagonist/cult/proc/admin_give_tome(mob/admin)
|
||||
if(equip_cultist(owner.current,1))
|
||||
to_chat(admin, "<span class='danger'>Spawning tome failed!</span>")
|
||||
|
||||
/datum/antagonist/cult/proc/admin_give_amulet(mob/admin)
|
||||
if (equip_cultist(owner.current))
|
||||
to_chat(admin, "<span class='danger'>Spawning amulet failed!</span>")
|
||||
|
||||
/datum/antagonist/cult/master
|
||||
ignore_implant = TRUE
|
||||
show_in_antagpanel = FALSE //Feel free to add this later
|
||||
var/datum/action/innate/cult/master/finalreck/reckoning = new
|
||||
var/datum/action/innate/cult/master/cultmark/bloodmark = new
|
||||
var/datum/action/innate/cult/master/pulse/throwing = new
|
||||
|
||||
/datum/antagonist/cult/master/Destroy()
|
||||
QDEL_NULL(reckoning)
|
||||
QDEL_NULL(bloodmark)
|
||||
QDEL_NULL(throwing)
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/cult/master/on_gain()
|
||||
. = ..()
|
||||
var/mob/living/current = owner.current
|
||||
set_antag_hud(current, "cultmaster")
|
||||
|
||||
/datum/antagonist/cult/master/greet()
|
||||
to_chat(owner.current, "<span class='cultlarge'>You are the cult's Master</span>. As the cult's Master, you have a unique title and loud voice when communicating, are capable of marking \
|
||||
targets, such as a location or a noncultist, to direct the cult to them, and, finally, you are capable of summoning the entire living cult to your location <b><i>once</i></b>.")
|
||||
to_chat(owner.current, "Use these abilities to direct the cult to victory at any cost.")
|
||||
|
||||
/datum/antagonist/cult/master/apply_innate_effects(mob/living/mob_override)
|
||||
. = ..()
|
||||
var/mob/living/current = owner.current
|
||||
if(mob_override)
|
||||
current = mob_override
|
||||
if(!cult_team.reckoning_complete)
|
||||
reckoning.Grant(current)
|
||||
bloodmark.Grant(current)
|
||||
throwing.Grant(current)
|
||||
current.update_action_buttons_icon()
|
||||
current.apply_status_effect(/datum/status_effect/cult_master)
|
||||
|
||||
/datum/antagonist/cult/master/remove_innate_effects(mob/living/mob_override)
|
||||
. = ..()
|
||||
var/mob/living/current = owner.current
|
||||
if(mob_override)
|
||||
current = mob_override
|
||||
reckoning.Remove(current)
|
||||
bloodmark.Remove(current)
|
||||
throwing.Remove(current)
|
||||
current.update_action_buttons_icon()
|
||||
current.remove_status_effect(/datum/status_effect/cult_master)
|
||||
|
||||
/datum/team/cult
|
||||
name = "Cult"
|
||||
|
||||
var/blood_target
|
||||
var/image/blood_target_image
|
||||
var/blood_target_reset_timer
|
||||
|
||||
var/cult_vote_called = FALSE
|
||||
var/cult_mastered = FALSE
|
||||
var/reckoning_complete = FALSE
|
||||
|
||||
|
||||
/datum/team/cult/proc/setup_objectives()
|
||||
//SAC OBJECTIVE , todo: move this to objective internals
|
||||
var/list/target_candidates = list()
|
||||
var/datum/objective/sacrifice/sac_objective = new
|
||||
sac_objective.team = src
|
||||
|
||||
for(var/mob/living/carbon/human/player in GLOB.player_list)
|
||||
if(player.mind && !player.mind.has_antag_datum(/datum/antagonist/cult) && !is_convertable_to_cult(player) && player.stat != DEAD)
|
||||
target_candidates += player.mind
|
||||
|
||||
if(target_candidates.len == 0)
|
||||
message_admins("Cult Sacrifice: Could not find unconvertable target, checking for convertable target.")
|
||||
for(var/mob/living/carbon/human/player in GLOB.player_list)
|
||||
if(player.mind && !player.mind.has_antag_datum(/datum/antagonist/cult) && player.stat != DEAD)
|
||||
target_candidates += player.mind
|
||||
listclearnulls(target_candidates)
|
||||
if(LAZYLEN(target_candidates))
|
||||
sac_objective.target = pick(target_candidates)
|
||||
sac_objective.update_explanation_text()
|
||||
|
||||
var/datum/job/sacjob = SSjob.GetJob(sac_objective.target.assigned_role)
|
||||
var/datum/preferences/sacface = sac_objective.target.current.client.prefs
|
||||
var/icon/reshape = get_flat_human_icon(null, sacjob, sacface)
|
||||
reshape.Shift(SOUTH, 4)
|
||||
reshape.Shift(EAST, 1)
|
||||
reshape.Crop(7,4,26,31)
|
||||
reshape.Crop(-5,-3,26,30)
|
||||
sac_objective.sac_image = reshape
|
||||
|
||||
objectives += sac_objective
|
||||
else
|
||||
message_admins("Cult Sacrifice: Could not find unconvertable or convertable target. WELP!")
|
||||
|
||||
|
||||
//SUMMON OBJECTIVE
|
||||
|
||||
var/datum/objective/eldergod/summon_objective = new()
|
||||
summon_objective.team = src
|
||||
objectives += summon_objective
|
||||
|
||||
/datum/objective/sacrifice
|
||||
var/sacced = FALSE
|
||||
var/sac_image
|
||||
|
||||
/datum/objective/sacrifice/check_completion()
|
||||
return sacced || completed
|
||||
|
||||
/datum/objective/sacrifice/update_explanation_text()
|
||||
if(target)
|
||||
explanation_text = "Sacrifice [target], the [target.assigned_role] via invoking a Sacrifice rune with them on it and three acolytes around it."
|
||||
else
|
||||
explanation_text = "The veil has already been weakened here, proceed to the final objective."
|
||||
|
||||
/datum/objective/eldergod
|
||||
var/summoned = FALSE
|
||||
var/list/summon_spots = list()
|
||||
|
||||
/datum/objective/eldergod/New()
|
||||
..()
|
||||
var/sanity = 0
|
||||
while(summon_spots.len < SUMMON_POSSIBILITIES && sanity < 100)
|
||||
var/area/summon = pick(GLOB.sortedAreas - summon_spots)
|
||||
if(summon && is_station_level(summon.z) && summon.valid_territory)
|
||||
summon_spots += summon
|
||||
sanity++
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/eldergod/update_explanation_text()
|
||||
explanation_text = "Summon Nar-Sie by invoking the rune 'Summon Nar-Sie'. <b>The summoning can only be accomplished in [english_list(summon_spots)] - where the veil is weak enough for the ritual to begin.</b>"
|
||||
|
||||
/datum/objective/eldergod/check_completion()
|
||||
return summoned || completed
|
||||
|
||||
/datum/team/cult/proc/check_cult_victory()
|
||||
for(var/datum/objective/O in objectives)
|
||||
if(!O.check_completion())
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/team/cult/roundend_report()
|
||||
var/list/parts = list()
|
||||
|
||||
if(check_cult_victory())
|
||||
parts += "<span class='greentext big'>The cult has succeeded! Nar-sie has snuffed out another torch in the void!</span>"
|
||||
else
|
||||
parts += "<span class='redtext big'>The staff managed to stop the cult! Dark words and heresy are no match for Nanotrasen's finest!</span>"
|
||||
|
||||
if(objectives.len)
|
||||
parts += "<b>The cultists' objectives were:</b>"
|
||||
var/count = 1
|
||||
for(var/datum/objective/objective in objectives)
|
||||
if(objective.check_completion())
|
||||
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='greentext'>Success!</span>"
|
||||
else
|
||||
parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
count++
|
||||
|
||||
if(members.len)
|
||||
parts += "<span class='header'>The cultists were:</span>"
|
||||
parts += printplayerlist(members)
|
||||
|
||||
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
|
||||
|
||||
/datum/team/cult/is_gamemode_hero()
|
||||
return SSticker.mode.name == "cult"
|
||||
@@ -1,11 +0,0 @@
|
||||
/datum/antagonist/iaa
|
||||
|
||||
/datum/antagonist/iaa/apply_innate_effects()
|
||||
.=..() //in case the base is used in future
|
||||
if(owner&&owner.current)
|
||||
give_pinpointer(owner.current)
|
||||
|
||||
/datum/antagonist/iaa/remove_innate_effects()
|
||||
.=..()
|
||||
if(owner&&owner.current)
|
||||
owner.current.remove_status_effect(/datum/status_effect/agent_pinpointer)
|
||||
@@ -1,352 +0,0 @@
|
||||
/datum/antagonist/traitor
|
||||
name = "Traitor"
|
||||
roundend_category = "traitors"
|
||||
antagpanel_category = "Traitor"
|
||||
job_rank = ROLE_TRAITOR
|
||||
var/should_specialise = TRUE //do we split into AI and human, set to true on inital assignment only
|
||||
var/ai_datum = /datum/antagonist/traitor/AI
|
||||
var/human_datum = /datum/antagonist/traitor/human
|
||||
var/special_role = ROLE_TRAITOR
|
||||
var/employer = "The Syndicate"
|
||||
var/give_objectives = TRUE
|
||||
var/should_give_codewords = TRUE
|
||||
|
||||
|
||||
|
||||
/datum/antagonist/traitor/human
|
||||
show_in_antagpanel = FALSE
|
||||
should_specialise = FALSE
|
||||
var/should_equip = TRUE
|
||||
|
||||
|
||||
/datum/antagonist/traitor/AI
|
||||
show_in_antagpanel = FALSE
|
||||
should_specialise = FALSE
|
||||
|
||||
/datum/antagonist/traitor/specialization(datum/mind/new_owner)
|
||||
if(should_specialise)
|
||||
if(new_owner.current && isAI(new_owner.current))
|
||||
return new ai_datum()
|
||||
else
|
||||
return new human_datum()
|
||||
else
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/traitor/on_gain()
|
||||
SSticker.mode.traitors += owner
|
||||
owner.special_role = special_role
|
||||
if(give_objectives)
|
||||
forge_traitor_objectives()
|
||||
finalize_traitor()
|
||||
..()
|
||||
|
||||
/datum/antagonist/traitor/apply_innate_effects()
|
||||
if(owner.assigned_role == "Clown")
|
||||
var/mob/living/carbon/human/traitor_mob = owner.current
|
||||
if(traitor_mob && istype(traitor_mob))
|
||||
if(!silent)
|
||||
to_chat(traitor_mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
|
||||
traitor_mob.dna.remove_mutation(CLOWNMUT)
|
||||
|
||||
/datum/antagonist/traitor/remove_innate_effects()
|
||||
if(owner.assigned_role == "Clown")
|
||||
var/mob/living/carbon/human/traitor_mob = owner.current
|
||||
if(traitor_mob && istype(traitor_mob))
|
||||
traitor_mob.dna.add_mutation(CLOWNMUT)
|
||||
|
||||
/datum/antagonist/traitor/on_removal()
|
||||
SSticker.mode.traitors -= owner
|
||||
for(var/O in objectives)
|
||||
owner.objectives -= O
|
||||
objectives = list()
|
||||
if(!silent && owner.current)
|
||||
to_chat(owner.current,"<span class='userdanger'> You are no longer the [special_role]! </span>")
|
||||
owner.special_role = null
|
||||
..()
|
||||
|
||||
/datum/antagonist/traitor/AI/on_removal()
|
||||
if(owner.current && isAI(owner.current))
|
||||
var/mob/living/silicon/ai/A = owner.current
|
||||
A.set_zeroth_law("")
|
||||
A.verbs -= /mob/living/silicon/ai/proc/choose_modules
|
||||
A.malf_picker.remove_malf_verbs(A)
|
||||
qdel(A.malf_picker)
|
||||
..()
|
||||
|
||||
/datum/antagonist/traitor/proc/add_objective(var/datum/objective/O)
|
||||
owner.objectives += O
|
||||
objectives += O
|
||||
|
||||
/datum/antagonist/traitor/proc/remove_objective(var/datum/objective/O)
|
||||
owner.objectives -= O
|
||||
objectives -= O
|
||||
|
||||
/datum/antagonist/traitor/proc/forge_traitor_objectives()
|
||||
return
|
||||
|
||||
/datum/antagonist/traitor/human/forge_traitor_objectives()
|
||||
var/is_hijacker = FALSE
|
||||
if (GLOB.joined_player_list.len >= 30) // Less murderboning on lowpop thanks
|
||||
is_hijacker = prob(10)
|
||||
var/martyr_chance = prob(20)
|
||||
var/objective_count = is_hijacker //Hijacking counts towards number of objectives
|
||||
if(!SSticker.mode.exchange_blue && SSticker.mode.traitors.len >= 8) //Set up an exchange if there are enough traitors
|
||||
if(!SSticker.mode.exchange_red)
|
||||
SSticker.mode.exchange_red = owner
|
||||
else
|
||||
SSticker.mode.exchange_blue = owner
|
||||
assign_exchange_role(SSticker.mode.exchange_red)
|
||||
assign_exchange_role(SSticker.mode.exchange_blue)
|
||||
objective_count += 1 //Exchange counts towards number of objectives
|
||||
var/toa = CONFIG_GET(number/traitor_objectives_amount)
|
||||
for(var/i = objective_count, i < toa, i++)
|
||||
forge_single_objective()
|
||||
|
||||
if(is_hijacker && objective_count <= toa) //Don't assign hijack if it would exceed the number of objectives set in config.traitor_objectives_amount
|
||||
if (!(locate(/datum/objective/hijack) in owner.objectives))
|
||||
var/datum/objective/hijack/hijack_objective = new
|
||||
hijack_objective.owner = owner
|
||||
add_objective(hijack_objective)
|
||||
return
|
||||
|
||||
|
||||
var/martyr_compatibility = 1 //You can't succeed in stealing if you're dead.
|
||||
for(var/datum/objective/O in owner.objectives)
|
||||
if(!O.martyr_compatible)
|
||||
martyr_compatibility = 0
|
||||
break
|
||||
|
||||
if(martyr_compatibility && martyr_chance)
|
||||
var/datum/objective/martyr/martyr_objective = new
|
||||
martyr_objective.owner = owner
|
||||
add_objective(martyr_objective)
|
||||
return
|
||||
|
||||
else
|
||||
if(!(locate(/datum/objective/escape) in owner.objectives))
|
||||
var/datum/objective/escape/escape_objective = new
|
||||
escape_objective.owner = owner
|
||||
add_objective(escape_objective)
|
||||
return
|
||||
|
||||
/datum/antagonist/traitor/AI/forge_traitor_objectives()
|
||||
var/objective_count = 0
|
||||
|
||||
if(prob(30))
|
||||
objective_count += forge_single_objective()
|
||||
|
||||
for(var/i = objective_count, i < CONFIG_GET(number/traitor_objectives_amount), i++)
|
||||
var/datum/objective/assassinate/kill_objective = new
|
||||
kill_objective.owner = owner
|
||||
kill_objective.find_target()
|
||||
add_objective(kill_objective)
|
||||
|
||||
var/datum/objective/survive/exist/exist_objective = new
|
||||
exist_objective.owner = owner
|
||||
add_objective(exist_objective)
|
||||
/datum/antagonist/traitor/proc/forge_single_objective()
|
||||
return 0
|
||||
/datum/antagonist/traitor/human/forge_single_objective() //Returns how many objectives are added
|
||||
.=1
|
||||
if(prob(50))
|
||||
var/list/active_ais = active_ais()
|
||||
if(active_ais.len && prob(100/GLOB.joined_player_list.len))
|
||||
var/datum/objective/destroy/destroy_objective = new
|
||||
destroy_objective.owner = owner
|
||||
destroy_objective.find_target()
|
||||
add_objective(destroy_objective)
|
||||
else if(prob(30))
|
||||
var/datum/objective/maroon/maroon_objective = new
|
||||
maroon_objective.owner = owner
|
||||
maroon_objective.find_target()
|
||||
add_objective(maroon_objective)
|
||||
else
|
||||
var/datum/objective/assassinate/kill_objective = new
|
||||
kill_objective.owner = owner
|
||||
kill_objective.find_target()
|
||||
add_objective(kill_objective)
|
||||
else
|
||||
if(prob(15) && !(locate(/datum/objective/download in owner.objectives)))
|
||||
var/datum/objective/download/download_objective = new
|
||||
download_objective.owner = owner
|
||||
download_objective.gen_amount_goal()
|
||||
add_objective(download_objective)
|
||||
else
|
||||
var/datum/objective/steal/steal_objective = new
|
||||
steal_objective.owner = owner
|
||||
steal_objective.find_target()
|
||||
add_objective(steal_objective)
|
||||
|
||||
/datum/antagonist/traitor/AI/forge_single_objective()
|
||||
.=1
|
||||
var/special_pick = rand(1,4)
|
||||
switch(special_pick)
|
||||
if(1)
|
||||
var/datum/objective/block/block_objective = new
|
||||
block_objective.owner = owner
|
||||
add_objective(block_objective)
|
||||
if(2)
|
||||
var/datum/objective/purge/purge_objective = new
|
||||
purge_objective.owner = owner
|
||||
add_objective(purge_objective)
|
||||
if(3)
|
||||
var/datum/objective/robot_army/robot_objective = new
|
||||
robot_objective.owner = owner
|
||||
add_objective(robot_objective)
|
||||
if(4) //Protect and strand a target
|
||||
var/datum/objective/protect/yandere_one = new
|
||||
yandere_one.owner = owner
|
||||
add_objective(yandere_one)
|
||||
yandere_one.find_target()
|
||||
var/datum/objective/maroon/yandere_two = new
|
||||
yandere_two.owner = owner
|
||||
yandere_two.target = yandere_one.target
|
||||
yandere_two.update_explanation_text() // normally called in find_target()
|
||||
add_objective(yandere_two)
|
||||
.=2
|
||||
|
||||
/datum/antagonist/traitor/greet()
|
||||
to_chat(owner.current, "<B><font size=3 color=red>You are the [owner.special_role].</font></B>")
|
||||
owner.announce_objectives()
|
||||
if(should_give_codewords)
|
||||
give_codewords()
|
||||
|
||||
/datum/antagonist/traitor/proc/finalize_traitor()
|
||||
SSticker.mode.update_traitor_icons_added(owner)
|
||||
return
|
||||
|
||||
/datum/antagonist/traitor/AI/finalize_traitor()
|
||||
..()
|
||||
add_law_zero()
|
||||
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/malf.ogg', 100, FALSE, pressure_affected = FALSE)
|
||||
owner.current.grant_language(/datum/language/codespeak)
|
||||
|
||||
/datum/antagonist/traitor/human/finalize_traitor()
|
||||
..()
|
||||
if(should_equip)
|
||||
equip(silent)
|
||||
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/tatoralert.ogg', 100, FALSE, pressure_affected = FALSE)
|
||||
|
||||
/datum/antagonist/traitor/proc/give_codewords()
|
||||
if(!owner.current)
|
||||
return
|
||||
var/mob/traitor_mob=owner.current
|
||||
|
||||
to_chat(traitor_mob, "<U><B>The Syndicate provided you with the following information on how to identify their agents:</B></U>")
|
||||
to_chat(traitor_mob, "<B>Code Phrase</B>: <span class='danger'>[GLOB.syndicate_code_phrase]</span>")
|
||||
to_chat(traitor_mob, "<B>Code Response</B>: <span class='danger'>[GLOB.syndicate_code_response]</span>")
|
||||
|
||||
antag_memory += "<b>Code Phrase</b>: [GLOB.syndicate_code_phrase]<br>"
|
||||
antag_memory += "<b>Code Response</b>: [GLOB.syndicate_code_response]<br>"
|
||||
|
||||
to_chat(traitor_mob, "Use the code words in the order provided, during regular conversation, to identify other agents. Proceed with caution, however, as everyone is a potential foe.")
|
||||
|
||||
/datum/antagonist/traitor/AI/proc/add_law_zero()
|
||||
var/mob/living/silicon/ai/killer = owner.current
|
||||
if(!killer || !istype(killer))
|
||||
return
|
||||
var/law = "Accomplish your objectives at all costs."
|
||||
var/law_borg = "Accomplish your AI's objectives at all costs."
|
||||
killer.set_zeroth_law(law, law_borg)
|
||||
killer.set_syndie_radio()
|
||||
to_chat(killer, "Your radio has been upgraded! Use :t to speak on an encrypted channel with Syndicate Agents!")
|
||||
killer.add_malf_picker()
|
||||
|
||||
/datum/antagonist/traitor/proc/equip(var/silent = FALSE)
|
||||
return
|
||||
|
||||
/datum/antagonist/traitor/human/equip(var/silent = FALSE)
|
||||
owner.equip_traitor(employer, silent, src)
|
||||
|
||||
/datum/antagonist/traitor/human/proc/assign_exchange_role()
|
||||
//set faction
|
||||
var/faction = "red"
|
||||
if(owner == SSticker.mode.exchange_blue)
|
||||
faction = "blue"
|
||||
|
||||
//Assign objectives
|
||||
var/datum/objective/steal/exchange/exchange_objective = new
|
||||
exchange_objective.set_faction(faction,((faction == "red") ? SSticker.mode.exchange_blue : SSticker.mode.exchange_red))
|
||||
exchange_objective.owner = owner
|
||||
add_objective(exchange_objective)
|
||||
|
||||
if(prob(20))
|
||||
var/datum/objective/steal/exchange/backstab/backstab_objective = new
|
||||
backstab_objective.set_faction(faction)
|
||||
backstab_objective.owner = owner
|
||||
add_objective(backstab_objective)
|
||||
|
||||
//Spawn and equip documents
|
||||
var/mob/living/carbon/human/mob = owner.current
|
||||
|
||||
var/obj/item/folder/syndicate/folder
|
||||
if(owner == SSticker.mode.exchange_red)
|
||||
folder = new/obj/item/folder/syndicate/red(mob.loc)
|
||||
else
|
||||
folder = new/obj/item/folder/syndicate/blue(mob.loc)
|
||||
|
||||
var/list/slots = list (
|
||||
"backpack" = slot_in_backpack,
|
||||
"left pocket" = slot_l_store,
|
||||
"right pocket" = slot_r_store
|
||||
)
|
||||
|
||||
var/where = "At your feet"
|
||||
var/equipped_slot = mob.equip_in_one_of_slots(folder, slots)
|
||||
if (equipped_slot)
|
||||
where = "In your [equipped_slot]"
|
||||
to_chat(mob, "<BR><BR><span class='info'>[where] is a folder containing <b>secret documents</b> that another Syndicate group wants. We have set up a meeting with one of their agents on station to make an exchange. Exercise extreme caution as they cannot be trusted and may be hostile.</span><BR>")
|
||||
|
||||
//TODO Collate
|
||||
/datum/antagonist/traitor/roundend_report()
|
||||
var/list/result = list()
|
||||
|
||||
var/traitorwin = TRUE
|
||||
|
||||
result += printplayer(owner)
|
||||
|
||||
var/TC_uses = 0
|
||||
var/uplink_true = FALSE
|
||||
var/purchases = ""
|
||||
var/datum/uplink_purchase_log/H = GLOB.uplink_purchase_logs_by_key[owner.key]
|
||||
if(H)
|
||||
TC_uses = H.total_spent
|
||||
uplink_true = TRUE
|
||||
purchases += H.generate_render(FALSE)
|
||||
|
||||
var/objectives_text = ""
|
||||
if(objectives.len)//If the traitor had no objectives, don't need to process this.
|
||||
var/count = 1
|
||||
for(var/datum/objective/objective in objectives)
|
||||
if(objective.check_completion())
|
||||
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'>Success!</span>"
|
||||
else
|
||||
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
traitorwin = FALSE
|
||||
count++
|
||||
|
||||
if(uplink_true)
|
||||
var/uplink_text = "(used [TC_uses] TC) [purchases]"
|
||||
if(TC_uses==0 && traitorwin)
|
||||
var/static/icon/badass = icon('icons/badass.dmi', "badass")
|
||||
uplink_text += "<BIG>[icon2html(badass, world)]</BIG>"
|
||||
result += uplink_text
|
||||
|
||||
result += objectives_text
|
||||
|
||||
var/special_role_text = lowertext(name)
|
||||
|
||||
if(traitorwin)
|
||||
result += "<span class='greentext'>The [special_role_text] was successful!</span>"
|
||||
else
|
||||
result += "<span class='redtext'>The [special_role_text] has failed!</span>"
|
||||
SEND_SOUND(owner.current, 'sound/ambience/ambifailure.ogg')
|
||||
|
||||
return result.Join("<br>")
|
||||
|
||||
/datum/antagonist/traitor/roundend_report_footer()
|
||||
return "<br><b>The code phrases were:</b> <span class='codephrase'>[GLOB.syndicate_code_phrase]</span><br>\
|
||||
<b>The code responses were:</b> <span class='codephrase'>[GLOB.syndicate_code_response]</span><br>"
|
||||
|
||||
/datum/antagonist/traitor/is_gamemode_hero()
|
||||
return SSticker.mode.name == "traitor"
|
||||
@@ -1,582 +0,0 @@
|
||||
#define BLOOD_THRESHOLD 3 //How many souls are needed per stage.
|
||||
#define TRUE_THRESHOLD 7
|
||||
#define ARCH_THRESHOLD 12
|
||||
|
||||
#define BASIC_DEVIL 0
|
||||
#define BLOOD_LIZARD 1
|
||||
#define TRUE_DEVIL 2
|
||||
#define ARCH_DEVIL 3
|
||||
|
||||
#define LOSS_PER_DEATH 2
|
||||
|
||||
#define SOULVALUE soulsOwned.len-reviveNumber
|
||||
|
||||
#define DEVILRESURRECTTIME 600
|
||||
|
||||
GLOBAL_LIST_EMPTY(allDevils)
|
||||
GLOBAL_LIST_INIT(lawlorify, list (
|
||||
LORE = list(
|
||||
OBLIGATION_FOOD = "This devil seems to always offer its victims food before slaughtering them.",
|
||||
OBLIGATION_FIDDLE = "This devil will never turn down a musical challenge.",
|
||||
OBLIGATION_DANCEOFF = "This devil will never turn down a dance off.",
|
||||
OBLIGATION_GREET = "This devil seems to only be able to converse with people it knows the name of.",
|
||||
OBLIGATION_PRESENCEKNOWN = "This devil seems to be unable to attack from stealth.",
|
||||
OBLIGATION_SAYNAME = "He will always chant his name upon killing someone.",
|
||||
OBLIGATION_ANNOUNCEKILL = "This devil always loudly announces his kills for the world to hear.",
|
||||
OBLIGATION_ANSWERTONAME = "This devil always responds to his truename.",
|
||||
BANE_SILVER = "Silver seems to gravely injure this devil.",
|
||||
BANE_SALT = "Throwing salt at this devil will hinder his ability to use infernal powers temporarily.",
|
||||
BANE_LIGHT = "Bright flashes will disorient the devil, likely causing him to flee.",
|
||||
BANE_IRON = "Cold iron will slowly injure him, until he can purge it from his system.",
|
||||
BANE_WHITECLOTHES = "Wearing clean white clothing will help ward off this devil.",
|
||||
BANE_HARVEST = "Presenting the labors of a harvest will disrupt the devil.",
|
||||
BANE_TOOLBOX = "That which holds the means of creation also holds the means of the devil's undoing.",
|
||||
BAN_HURTWOMAN = "This devil seems to prefer hunting men.",
|
||||
BAN_CHAPEL = "This devil avoids holy ground.",
|
||||
BAN_HURTPRIEST = "The annointed clergy appear to be immune to his powers.",
|
||||
BAN_AVOIDWATER = "The devil seems to have some sort of aversion to water, though it does not appear to harm him.",
|
||||
BAN_STRIKEUNCONSCIOUS = "This devil only shows interest in those who are awake.",
|
||||
BAN_HURTLIZARD = "This devil will not strike a lizardman first.",
|
||||
BAN_HURTANIMAL = "This devil avoids hurting animals.",
|
||||
BANISH_WATER = "To banish the devil, you must infuse its body with holy water.",
|
||||
BANISH_COFFIN = "This devil will return to life if its remains are not placed within a coffin.",
|
||||
BANISH_FORMALDYHIDE = "To banish the devil, you must inject its lifeless body with embalming fluid.",
|
||||
BANISH_RUNES = "This devil will resurrect after death, unless its remains are within a rune.",
|
||||
BANISH_CANDLES = "A large number of nearby lit candles will prevent it from resurrecting.",
|
||||
BANISH_DESTRUCTION = "Its corpse must be utterly destroyed to prevent resurrection.",
|
||||
BANISH_FUNERAL_GARB = "If clad in funeral garments, this devil will be unable to resurrect. Should the clothes not fit, lay them gently on top of the devil's corpse."
|
||||
),
|
||||
LAW = list(
|
||||
OBLIGATION_FOOD = "When not acting in self defense, you must always offer your victim food before harming them.",
|
||||
OBLIGATION_FIDDLE = "When not in immediate danger, if you are challenged to a musical duel, you must accept it. You are not obligated to duel the same person twice.",
|
||||
OBLIGATION_DANCEOFF = "When not in immediate danger, if you are challenged to a dance off, you must accept it. You are not obligated to face off with the same person twice.",
|
||||
OBLIGATION_GREET = "You must always greet other people by their last name before talking with them.",
|
||||
OBLIGATION_PRESENCEKNOWN = "You must always make your presence known before attacking.",
|
||||
OBLIGATION_SAYNAME = "You must always say your true name after you kill someone.",
|
||||
OBLIGATION_ANNOUNCEKILL = "Upon killing someone, you must make your deed known to all within earshot, over comms if reasonably possible.",
|
||||
OBLIGATION_ANSWERTONAME = "If you are not under attack, you must always respond to your true name.",
|
||||
BAN_HURTWOMAN = "You must never harm a female outside of self defense.",
|
||||
BAN_CHAPEL = "You must never attempt to enter the chapel.",
|
||||
BAN_HURTPRIEST = "You must never attack a priest.",
|
||||
BAN_AVOIDWATER = "You must never willingly touch a wet surface.",
|
||||
BAN_STRIKEUNCONSCIOUS = "You must never strike an unconscious person.",
|
||||
BAN_HURTLIZARD = "You must never harm a lizardman outside of self defense.",
|
||||
BAN_HURTANIMAL = "You must never harm a non-sentient creature or robot outside of self defense.",
|
||||
BANE_SILVER = "Silver, in all of its forms shall be your downfall.",
|
||||
BANE_SALT = "Salt will disrupt your magical abilities.",
|
||||
BANE_LIGHT = "Blinding lights will prevent you from using offensive powers for a time.",
|
||||
BANE_IRON = "Cold wrought iron shall act as poison to you.",
|
||||
BANE_WHITECLOTHES = "Those clad in pristine white garments will strike you true.",
|
||||
BANE_HARVEST = "The fruits of the harvest shall be your downfall.",
|
||||
BANE_TOOLBOX = "Toolboxes are bad news for you, for some reason.",
|
||||
BANISH_WATER = "If your corpse is filled with holy water, you will be unable to resurrect.",
|
||||
BANISH_COFFIN = "If your corpse is in a coffin, you will be unable to resurrect.",
|
||||
BANISH_FORMALDYHIDE = "If your corpse is embalmed, you will be unable to resurrect.",
|
||||
BANISH_RUNES = "If your corpse is placed within a rune, you will be unable to resurrect.",
|
||||
BANISH_CANDLES = "If your corpse is near lit candles, you will be unable to resurrect.",
|
||||
BANISH_DESTRUCTION = "If your corpse is destroyed, you will be unable to resurrect.",
|
||||
BANISH_FUNERAL_GARB = "If your corpse is clad in funeral garments, you will be unable to resurrect."
|
||||
)
|
||||
))
|
||||
|
||||
//These are also used in the codex gigas, so let's declare them globally.
|
||||
GLOBAL_LIST_INIT(devil_pre_title, list("Dark ", "Hellish ", "Fallen ", "Fiery ", "Sinful ", "Blood ", "Fluffy "))
|
||||
GLOBAL_LIST_INIT(devil_title, list("Lord ", "Prelate ", "Count ", "Viscount ", "Vizier ", "Elder ", "Adept "))
|
||||
GLOBAL_LIST_INIT(devil_syllable, list("hal", "ve", "odr", "neit", "ci", "quon", "mya", "folth", "wren", "geyr", "hil", "niet", "twou", "phi", "coa"))
|
||||
GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master", ", the Lord of all things", ", Jr."))
|
||||
/datum/antagonist/devil
|
||||
name = "Devil"
|
||||
roundend_category = "devils"
|
||||
antagpanel_category = "Devil"
|
||||
job_rank = ROLE_DEVIL
|
||||
//Don't delete upon mind destruction, otherwise soul re-selling will break.
|
||||
delete_on_mind_deletion = FALSE
|
||||
var/obligation
|
||||
var/ban
|
||||
var/bane
|
||||
var/banish
|
||||
var/truename
|
||||
var/list/datum/mind/soulsOwned = new
|
||||
var/reviveNumber = 0
|
||||
var/form = BASIC_DEVIL
|
||||
var/static/list/devil_spells = typecacheof(list(
|
||||
/obj/effect/proc_holder/spell/aimed/fireball/hellish,
|
||||
/obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork,
|
||||
/obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork/greater,
|
||||
/obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork/ascended,
|
||||
/obj/effect/proc_holder/spell/targeted/infernal_jaunt,
|
||||
/obj/effect/proc_holder/spell/targeted/sintouch,
|
||||
/obj/effect/proc_holder/spell/targeted/sintouch/ascended,
|
||||
/obj/effect/proc_holder/spell/targeted/summon_contract,
|
||||
/obj/effect/proc_holder/spell/targeted/conjure_item/violin,
|
||||
/obj/effect/proc_holder/spell/targeted/summon_dancefloor))
|
||||
var/ascendable = FALSE
|
||||
|
||||
/datum/antagonist/devil/can_be_owned(datum/mind/new_owner)
|
||||
. = ..()
|
||||
return . && (ishuman(new_owner.current) || iscyborg(new_owner.current))
|
||||
|
||||
/datum/antagonist/devil/get_admin_commands()
|
||||
. = ..()
|
||||
.["Toggle ascendable"] = CALLBACK(src,.proc/admin_toggle_ascendable)
|
||||
|
||||
|
||||
/datum/antagonist/devil/proc/admin_toggle_ascendable(mob/admin)
|
||||
ascendable = !ascendable
|
||||
message_admins("[key_name_admin(admin)] set [owner.current] devil ascendable to [ascendable]")
|
||||
log_admin("[key_name_admin(admin)] set [owner.current] devil ascendable to [ascendable])")
|
||||
|
||||
/datum/antagonist/devil/admin_add(datum/mind/new_owner,mob/admin)
|
||||
switch(alert(admin,"Should the devil be able to ascend",,"Yes","No","Cancel"))
|
||||
if("Yes")
|
||||
ascendable = TRUE
|
||||
if("No")
|
||||
ascendable = FALSE
|
||||
else
|
||||
return
|
||||
new_owner.add_antag_datum(src)
|
||||
message_admins("[key_name_admin(admin)] has devil'ed [new_owner.current]. [ascendable ? "(Ascendable)":""]")
|
||||
log_admin("[key_name(admin)] has devil'ed [new_owner.current]. [ascendable ? "(Ascendable)":""]")
|
||||
|
||||
/datum/antagonist/devil/antag_listing_name()
|
||||
return ..() + "([truename])"
|
||||
|
||||
/proc/devilInfo(name)
|
||||
if(GLOB.allDevils[lowertext(name)])
|
||||
return GLOB.allDevils[lowertext(name)]
|
||||
else
|
||||
var/datum/fakeDevil/devil = new /datum/fakeDevil(name)
|
||||
GLOB.allDevils[lowertext(name)] = devil
|
||||
return devil
|
||||
|
||||
/proc/randomDevilName()
|
||||
var/name = ""
|
||||
if(prob(65))
|
||||
if(prob(35))
|
||||
name = pick(GLOB.devil_pre_title)
|
||||
name += pick(GLOB.devil_title)
|
||||
var/probability = 100
|
||||
name += pick(GLOB.devil_syllable)
|
||||
while(prob(probability))
|
||||
name += pick(GLOB.devil_syllable)
|
||||
probability -= 20
|
||||
if(prob(40))
|
||||
name += pick(GLOB.devil_suffix)
|
||||
return name
|
||||
|
||||
/proc/randomdevilobligation()
|
||||
return pick(OBLIGATION_FOOD, OBLIGATION_FIDDLE, OBLIGATION_DANCEOFF, OBLIGATION_GREET, OBLIGATION_PRESENCEKNOWN, OBLIGATION_SAYNAME, OBLIGATION_ANNOUNCEKILL, OBLIGATION_ANSWERTONAME)
|
||||
|
||||
/proc/randomdevilban()
|
||||
return pick(BAN_HURTWOMAN, BAN_CHAPEL, BAN_HURTPRIEST, BAN_AVOIDWATER, BAN_STRIKEUNCONSCIOUS, BAN_HURTLIZARD, BAN_HURTANIMAL)
|
||||
|
||||
/proc/randomdevilbane()
|
||||
return pick(BANE_SALT, BANE_LIGHT, BANE_IRON, BANE_WHITECLOTHES, BANE_SILVER, BANE_HARVEST, BANE_TOOLBOX)
|
||||
|
||||
/proc/randomdevilbanish()
|
||||
return pick(BANISH_WATER, BANISH_COFFIN, BANISH_FORMALDYHIDE, BANISH_RUNES, BANISH_CANDLES, BANISH_DESTRUCTION, BANISH_FUNERAL_GARB)
|
||||
|
||||
/datum/antagonist/devil/proc/add_soul(datum/mind/soul)
|
||||
if(soulsOwned.Find(soul))
|
||||
return
|
||||
soulsOwned += soul
|
||||
owner.current.nutrition = NUTRITION_LEVEL_FULL
|
||||
to_chat(owner.current, "<span class='warning'>You feel satiated as you received a new soul.</span>")
|
||||
update_hud()
|
||||
switch(SOULVALUE)
|
||||
if(0)
|
||||
to_chat(owner.current, "<span class='warning'>Your hellish powers have been restored.</span>")
|
||||
give_appropriate_spells()
|
||||
if(BLOOD_THRESHOLD)
|
||||
increase_blood_lizard()
|
||||
if(TRUE_THRESHOLD)
|
||||
increase_true_devil()
|
||||
if(ARCH_THRESHOLD)
|
||||
increase_arch_devil()
|
||||
|
||||
/datum/antagonist/devil/proc/remove_soul(datum/mind/soul)
|
||||
if(soulsOwned.Remove(soul))
|
||||
check_regression()
|
||||
to_chat(owner.current, "<span class='warning'>You feel as though a soul has slipped from your grasp.</span>")
|
||||
update_hud()
|
||||
|
||||
/datum/antagonist/devil/proc/check_regression()
|
||||
if(form == ARCH_DEVIL)
|
||||
return //arch devil can't regress
|
||||
//Yes, fallthrough behavior is intended, so I can't use a switch statement.
|
||||
if(form == TRUE_DEVIL && SOULVALUE < TRUE_THRESHOLD)
|
||||
regress_blood_lizard()
|
||||
if(form == BLOOD_LIZARD && SOULVALUE < BLOOD_THRESHOLD)
|
||||
regress_humanoid()
|
||||
if(SOULVALUE < 0)
|
||||
give_appropriate_spells()
|
||||
to_chat(owner.current, "<span class='warning'>As punishment for your failures, all of your powers except contract creation have been revoked.</span>")
|
||||
|
||||
/datum/antagonist/devil/proc/regress_humanoid()
|
||||
to_chat(owner.current, "<span class='warning'>Your powers weaken, have more contracts be signed to regain power.</span>")
|
||||
if(ishuman(owner.current))
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
H.set_species(/datum/species/human, 1)
|
||||
H.regenerate_icons()
|
||||
give_appropriate_spells()
|
||||
if(istype(owner.current.loc, /obj/effect/dummy/slaughter/))
|
||||
owner.current.forceMove(get_turf(owner.current))//Fixes dying while jaunted leaving you permajaunted.
|
||||
form = BASIC_DEVIL
|
||||
|
||||
/datum/antagonist/devil/proc/regress_blood_lizard()
|
||||
var/mob/living/carbon/true_devil/D = owner.current
|
||||
to_chat(D, "<span class='warning'>Your powers weaken, have more contracts be signed to regain power.</span>")
|
||||
D.oldform.forceMove(D.drop_location())
|
||||
owner.transfer_to(D.oldform)
|
||||
give_appropriate_spells()
|
||||
qdel(D)
|
||||
form = BLOOD_LIZARD
|
||||
update_hud()
|
||||
|
||||
|
||||
/datum/antagonist/devil/proc/increase_blood_lizard()
|
||||
to_chat(owner.current, "<span class='warning'>You feel as though your humanoid form is about to shed. You will soon turn into a blood lizard.</span>")
|
||||
sleep(50)
|
||||
if(ishuman(owner.current))
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
H.set_species(/datum/species/lizard, 1)
|
||||
H.underwear = "Nude"
|
||||
H.undershirt = "Nude"
|
||||
H.socks = "Nude"
|
||||
H.dna.features["mcolor"] = "511" //A deep red
|
||||
H.regenerate_icons()
|
||||
else //Did the devil get hit by a staff of transmutation?
|
||||
owner.current.color = "#501010"
|
||||
give_appropriate_spells()
|
||||
form = BLOOD_LIZARD
|
||||
|
||||
|
||||
|
||||
/datum/antagonist/devil/proc/increase_true_devil()
|
||||
to_chat(owner.current, "<span class='warning'>You feel as though your current form is about to shed. You will soon turn into a true devil.</span>")
|
||||
sleep(50)
|
||||
var/mob/living/carbon/true_devil/A = new /mob/living/carbon/true_devil(owner.current.loc)
|
||||
A.faction |= "hell"
|
||||
owner.current.forceMove(A)
|
||||
A.oldform = owner.current
|
||||
owner.transfer_to(A)
|
||||
A.set_name()
|
||||
give_appropriate_spells()
|
||||
form = TRUE_DEVIL
|
||||
update_hud()
|
||||
|
||||
/datum/antagonist/devil/proc/increase_arch_devil()
|
||||
if(!ascendable)
|
||||
return
|
||||
var/mob/living/carbon/true_devil/D = owner.current
|
||||
to_chat(D, "<span class='warning'>You feel as though your form is about to ascend.</span>")
|
||||
sleep(50)
|
||||
if(!D)
|
||||
return
|
||||
D.visible_message("<span class='warning'>[D]'s skin begins to erupt with spikes.</span>", \
|
||||
"<span class='warning'>Your flesh begins creating a shield around yourself.</span>")
|
||||
sleep(100)
|
||||
if(!D)
|
||||
return
|
||||
D.visible_message("<span class='warning'>The horns on [D]'s head slowly grow and elongate.</span>", \
|
||||
"<span class='warning'>Your body continues to mutate. Your telepathic abilities grow.</span>")
|
||||
sleep(90)
|
||||
if(!D)
|
||||
return
|
||||
D.visible_message("<span class='warning'>[D]'s body begins to violently stretch and contort.</span>", \
|
||||
"<span class='warning'>You begin to rend apart the final barriers to ultimate power.</span>")
|
||||
sleep(40)
|
||||
if(!D)
|
||||
return
|
||||
to_chat(D, "<i><b>Yes!</b></i>")
|
||||
sleep(10)
|
||||
if(!D)
|
||||
return
|
||||
to_chat(D, "<i><b><span class='big'>YES!!</span></b></i>")
|
||||
sleep(10)
|
||||
if(!D)
|
||||
return
|
||||
to_chat(D, "<i><b><span class='reallybig'>YE--</span></b></i>")
|
||||
sleep(1)
|
||||
if(!D)
|
||||
return
|
||||
to_chat(world, "<font size=5><span class='danger'><b>\"SLOTH, WRATH, GLUTTONY, ACEDIA, ENVY, GREED, PRIDE! FIRES OF HELL AWAKEN!!\"</font></span>")
|
||||
SEND_SOUND(world, sound('sound/hallucinations/veryfar_noise.ogg'))
|
||||
give_appropriate_spells()
|
||||
D.convert_to_archdevil()
|
||||
if(istype(D.loc, /obj/effect/dummy/slaughter/))
|
||||
D.forceMove(get_turf(D))//Fixes dying while jaunted leaving you permajaunted.
|
||||
var/area/A = get_area(owner.current)
|
||||
if(A)
|
||||
notify_ghosts("An arch devil has ascended in \the [A.name]. Reach out to the devil to be given a new shell for your soul.", source = owner.current, action=NOTIFY_ATTACK)
|
||||
sleep(50)
|
||||
if(!SSticker.mode.devil_ascended)
|
||||
SSshuttle.emergency.request(null, set_coefficient = 0.3)
|
||||
SSticker.mode.devil_ascended++
|
||||
form = ARCH_DEVIL
|
||||
|
||||
/datum/antagonist/devil/proc/remove_spells()
|
||||
for(var/X in owner.spell_list)
|
||||
var/obj/effect/proc_holder/spell/S = X
|
||||
if(is_type_in_typecache(S, devil_spells))
|
||||
owner.RemoveSpell(S)
|
||||
|
||||
/datum/antagonist/devil/proc/give_summon_contract()
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/summon_contract(null))
|
||||
if(obligation == OBLIGATION_FIDDLE)
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/violin(null))
|
||||
else if(obligation == OBLIGATION_DANCEOFF)
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/summon_dancefloor(null))
|
||||
|
||||
/datum/antagonist/devil/proc/give_appropriate_spells()
|
||||
remove_spells()
|
||||
give_summon_contract()
|
||||
if(SOULVALUE >= ARCH_THRESHOLD && ascendable)
|
||||
give_arch_spells()
|
||||
else if(SOULVALUE >= TRUE_THRESHOLD)
|
||||
give_true_spells()
|
||||
else if(SOULVALUE >= BLOOD_THRESHOLD)
|
||||
give_blood_spells()
|
||||
else if(SOULVALUE >= 0)
|
||||
give_base_spells()
|
||||
|
||||
/datum/antagonist/devil/proc/give_base_spells()
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball/hellish(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork(null))
|
||||
|
||||
/datum/antagonist/devil/proc/give_blood_spells()
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball/hellish(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/infernal_jaunt(null))
|
||||
|
||||
/datum/antagonist/devil/proc/give_true_spells()
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork/greater(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball/hellish(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/infernal_jaunt(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/sintouch(null))
|
||||
|
||||
/datum/antagonist/devil/proc/give_arch_spells()
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork/ascended(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/sintouch/ascended(null))
|
||||
|
||||
/datum/antagonist/devil/proc/beginResurrectionCheck(mob/living/body)
|
||||
if(SOULVALUE>0)
|
||||
to_chat(owner.current, "<span class='userdanger'>Your body has been damaged to the point that you may no longer use it. At the cost of some of your power, you will return to life soon. Remain in your body.</span>")
|
||||
sleep(DEVILRESURRECTTIME)
|
||||
if (!body || body.stat == DEAD)
|
||||
if(SOULVALUE>0)
|
||||
if(check_banishment(body))
|
||||
to_chat(owner.current, "<span class='userdanger'>Unfortunately, the mortals have finished a ritual that prevents your resurrection.</span>")
|
||||
return -1
|
||||
else
|
||||
to_chat(owner.current, "<span class='userdanger'>WE LIVE AGAIN!</span>")
|
||||
return hellish_resurrection(body)
|
||||
else
|
||||
to_chat(owner.current, "<span class='userdanger'>Unfortunately, the power that stemmed from your contracts has been extinguished. You no longer have enough power to resurrect.</span>")
|
||||
return -1
|
||||
else
|
||||
to_chat(owner.current, "<span class='danger'> You seem to have resurrected without your hellish powers.</span>")
|
||||
else
|
||||
to_chat(owner.current, "<span class='userdanger'>Your hellish powers are too weak to resurrect yourself.</span>")
|
||||
|
||||
/datum/antagonist/devil/proc/check_banishment(mob/living/body)
|
||||
switch(banish)
|
||||
if(BANISH_WATER)
|
||||
if(iscarbon(body))
|
||||
var/mob/living/carbon/H = body
|
||||
return H.reagents.has_reagent("holy water")
|
||||
return 0
|
||||
if(BANISH_COFFIN)
|
||||
return (body && istype(body.loc, /obj/structure/closet/coffin))
|
||||
if(BANISH_FORMALDYHIDE)
|
||||
if(iscarbon(body))
|
||||
var/mob/living/carbon/H = body
|
||||
return H.reagents.has_reagent("formaldehyde")
|
||||
return 0
|
||||
if(BANISH_RUNES)
|
||||
if(body)
|
||||
for(var/obj/effect/decal/cleanable/crayon/R in range(0,body))
|
||||
if (R.name == "rune")
|
||||
return 1
|
||||
return 0
|
||||
if(BANISH_CANDLES)
|
||||
if(body)
|
||||
var/count = 0
|
||||
for(var/obj/item/candle/C in range(1,body))
|
||||
count += C.lit
|
||||
if(count>=4)
|
||||
return 1
|
||||
return 0
|
||||
if(BANISH_DESTRUCTION)
|
||||
if(body)
|
||||
return 0
|
||||
return 1
|
||||
if(BANISH_FUNERAL_GARB)
|
||||
if(ishuman(body))
|
||||
var/mob/living/carbon/human/H = body
|
||||
if(H.w_uniform && istype(H.w_uniform, /obj/item/clothing/under/burial))
|
||||
return 1
|
||||
return 0
|
||||
else
|
||||
for(var/obj/item/clothing/under/burial/B in range(0,body))
|
||||
if(B.loc == get_turf(B)) //Make sure it's not in someone's inventory or something.
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/antagonist/devil/proc/hellish_resurrection(mob/living/body)
|
||||
message_admins("[owner.name] (true name is: [truename]) is resurrecting using hellish energy.</a>")
|
||||
if(SOULVALUE < ARCH_THRESHOLD || !ascendable) // once ascended, arch devils do not go down in power by any means.
|
||||
reviveNumber += LOSS_PER_DEATH
|
||||
update_hud()
|
||||
if(body)
|
||||
body.revive(TRUE, TRUE) //Adminrevive also recovers organs, preventing someone from resurrecting without a heart.
|
||||
if(istype(body.loc, /obj/effect/dummy/slaughter/))
|
||||
body.forceMove(get_turf(body))//Fixes dying while jaunted leaving you permajaunted.
|
||||
if(istype(body, /mob/living/carbon/true_devil))
|
||||
var/mob/living/carbon/true_devil/D = body
|
||||
if(D.oldform)
|
||||
D.oldform.revive(1,0) // Heal the old body too, so the devil doesn't resurrect, then immediately regress into a dead body.
|
||||
if(body.stat == DEAD)
|
||||
create_new_body()
|
||||
else
|
||||
create_new_body()
|
||||
check_regression()
|
||||
|
||||
/datum/antagonist/devil/proc/create_new_body()
|
||||
if(GLOB.blobstart.len > 0)
|
||||
var/turf/targetturf = get_turf(pick(GLOB.blobstart))
|
||||
var/mob/currentMob = owner.current
|
||||
if(!currentMob)
|
||||
currentMob = owner.get_ghost()
|
||||
if(!currentMob)
|
||||
message_admins("[owner.name]'s devil resurrection failed due to client logoff. Aborting.")
|
||||
return -1
|
||||
if(currentMob.mind != owner)
|
||||
message_admins("[owner.name]'s devil resurrection failed due to becoming a new mob. Aborting.")
|
||||
return -1
|
||||
currentMob.change_mob_type( /mob/living/carbon/human, targetturf, null, 1)
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
H.equip_to_slot_or_del(new /obj/item/clothing/under/lawyer/black(H), slot_w_uniform)
|
||||
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(H), slot_shoes)
|
||||
H.equip_to_slot_or_del(new /obj/item/storage/briefcase(H), slot_hands)
|
||||
H.equip_to_slot_or_del(new /obj/item/pen(H), slot_l_store)
|
||||
if(SOULVALUE >= BLOOD_THRESHOLD)
|
||||
H.set_species(/datum/species/lizard, 1)
|
||||
H.underwear = "Nude"
|
||||
H.undershirt = "Nude"
|
||||
H.socks = "Nude"
|
||||
H.dna.features["mcolor"] = "511"
|
||||
H.regenerate_icons()
|
||||
if(SOULVALUE >= TRUE_THRESHOLD) //Yes, BOTH this and the above if statement are to run if soulpower is high enough.
|
||||
var/mob/living/carbon/true_devil/A = new /mob/living/carbon/true_devil(targetturf)
|
||||
A.faction |= "hell"
|
||||
H.forceMove(A)
|
||||
A.oldform = H
|
||||
owner.transfer_to(A, TRUE)
|
||||
A.set_name()
|
||||
if(SOULVALUE >= ARCH_THRESHOLD && ascendable)
|
||||
A.convert_to_archdevil()
|
||||
else
|
||||
throw EXCEPTION("Unable to find a blobstart landmark for hellish resurrection")
|
||||
|
||||
|
||||
/datum/antagonist/devil/proc/update_hud()
|
||||
if(iscarbon(owner.current))
|
||||
var/mob/living/C = owner.current
|
||||
if(C.hud_used && C.hud_used.devilsouldisplay)
|
||||
C.hud_used.devilsouldisplay.update_counter(SOULVALUE)
|
||||
|
||||
/datum/antagonist/devil/greet()
|
||||
to_chat(owner.current, "<span class='warning'><b>You remember your link to the infernal. You are [truename], an agent of hell, a devil. And you were sent to the plane of creation for a reason. A greater purpose. Convince the crew to sin, and embroiden Hell's grasp.</b></span>")
|
||||
to_chat(owner.current, "<span class='warning'><b>However, your infernal form is not without weaknesses.</b></span>")
|
||||
to_chat(owner.current, "You may not use violence to coerce someone into selling their soul.")
|
||||
to_chat(owner.current, "You may not directly and knowingly physically harm a devil, other than yourself.")
|
||||
to_chat(owner.current, GLOB.lawlorify[LAW][bane])
|
||||
to_chat(owner.current, GLOB.lawlorify[LAW][ban])
|
||||
to_chat(owner.current, GLOB.lawlorify[LAW][obligation])
|
||||
to_chat(owner.current, GLOB.lawlorify[LAW][banish])
|
||||
to_chat(owner.current, "<span class='warning'>Remember, the crew can research your weaknesses if they find out your devil name.</span><br>")
|
||||
.=..()
|
||||
|
||||
/datum/antagonist/devil/on_gain()
|
||||
truename = randomDevilName()
|
||||
ban = randomdevilban()
|
||||
bane = randomdevilbane()
|
||||
obligation = randomdevilobligation()
|
||||
banish = randomdevilbanish()
|
||||
GLOB.allDevils[lowertext(truename)] = src
|
||||
|
||||
antag_memory += "Your devilic true name is [truename]<br>[GLOB.lawlorify[LAW][ban]]<br>You may not use violence to coerce someone into selling their soul.<br>You may not directly and knowingly physically harm a devil, other than yourself.<br>[GLOB.lawlorify[LAW][bane]]<br>[GLOB.lawlorify[LAW][obligation]]<br>[GLOB.lawlorify[LAW][banish]]<br>"
|
||||
if(issilicon(owner.current))
|
||||
var/mob/living/silicon/robot_devil = owner.current
|
||||
var/laws = list("You may not use violence to coerce someone into selling their soul.", "You may not directly and knowingly physically harm a devil, other than yourself.", GLOB.lawlorify[LAW][ban], GLOB.lawlorify[LAW][obligation], "Accomplish your objectives at all costs.")
|
||||
robot_devil.set_law_sixsixsix(laws)
|
||||
sleep(10)
|
||||
if(owner.assigned_role == "Clown" && ishuman(owner.current))
|
||||
var/mob/living/carbon/human/S = owner.current
|
||||
to_chat(S, "<span class='notice'>Your infernal nature has allowed you to overcome your clownishness.</span>")
|
||||
S.dna.remove_mutation(CLOWNMUT)
|
||||
.=..()
|
||||
|
||||
/datum/antagonist/devil/on_removal()
|
||||
to_chat(owner.current, "<span class='userdanger'>Your infernal link has been severed! You are no longer a devil!</span>")
|
||||
.=..()
|
||||
|
||||
/datum/antagonist/devil/apply_innate_effects(mob/living/mob_override)
|
||||
give_appropriate_spells()
|
||||
owner.current.grant_all_languages(TRUE)
|
||||
update_hud()
|
||||
.=..()
|
||||
|
||||
/datum/antagonist/devil/remove_innate_effects(mob/living/mob_override)
|
||||
for(var/X in owner.spell_list)
|
||||
var/obj/effect/proc_holder/spell/S = X
|
||||
if(is_type_in_typecache(S, devil_spells))
|
||||
owner.RemoveSpell(S)
|
||||
.=..()
|
||||
|
||||
/datum/antagonist/devil/proc/printdevilinfo()
|
||||
var/list/parts = list()
|
||||
parts += "The devil's true name is: [truename]"
|
||||
parts += "The devil's bans were:"
|
||||
parts += "[GLOB.TAB][GLOB.lawlorify[LORE][ban]]"
|
||||
parts += "[GLOB.TAB][GLOB.lawlorify[LORE][bane]]"
|
||||
parts += "[GLOB.TAB][GLOB.lawlorify[LORE][obligation]]"
|
||||
parts += "[GLOB.TAB][GLOB.lawlorify[LORE][banish]]"
|
||||
return parts.Join("<br>")
|
||||
|
||||
/datum/antagonist/devil/roundend_report()
|
||||
var/list/parts = list()
|
||||
parts += printplayer(owner)
|
||||
parts += printdevilinfo()
|
||||
parts += printobjectives(owner)
|
||||
return parts.Join("<br>")
|
||||
|
||||
/datum/antagonist/devil/roundend_report_footer()
|
||||
//sintouched go here for now as a hack , TODO proper antag datum for these
|
||||
var/list/parts = list()
|
||||
if(SSticker.mode.sintouched.len)
|
||||
parts += "<span class='header'>The sintouched were:</span>"
|
||||
var/list/sintouchedUnique = uniqueList(SSticker.mode.sintouched)
|
||||
for(var/S in sintouchedUnique)
|
||||
var/datum/mind/sintouched_mind = S
|
||||
parts += printplayer(sintouched_mind)
|
||||
parts += printobjectives(sintouched_mind)
|
||||
return parts.Join("<br>")
|
||||
|
||||
//A simple super light weight datum for the codex gigas.
|
||||
/datum/fakeDevil
|
||||
var/truename
|
||||
var/bane
|
||||
var/obligation
|
||||
var/ban
|
||||
var/banish
|
||||
var/ascendable
|
||||
|
||||
/datum/fakeDevil/New(name = randomDevilName())
|
||||
truename = name
|
||||
bane = randomdevilbane()
|
||||
obligation = randomdevilobligation()
|
||||
ban = randomdevilban()
|
||||
banish = randomdevilbanish()
|
||||
ascendable = prob(25)
|
||||
@@ -1,302 +0,0 @@
|
||||
#define PINPOINTER_MINIMUM_RANGE 15
|
||||
#define PINPOINTER_EXTRA_RANDOM_RANGE 10
|
||||
#define PINPOINTER_PING_TIME 40
|
||||
#define PROB_ACTUAL_TRAITOR 20
|
||||
#define TRAITOR_AGENT_ROLE "Syndicate External Affairs Agent"
|
||||
|
||||
/datum/antagonist/traitor/internal_affairs
|
||||
name = "Internal Affairs Agent"
|
||||
human_datum = /datum/antagonist/traitor/human/internal_affairs
|
||||
ai_datum = /datum/antagonist/traitor/AI/internal_affairs
|
||||
antagpanel_category = "IAA"
|
||||
|
||||
/datum/antagonist/traitor/AI/internal_affairs
|
||||
name = "Internal Affairs Agent"
|
||||
employer = "Nanotrasen"
|
||||
special_role = "internal affairs agent"
|
||||
antagpanel_category = "IAA"
|
||||
var/syndicate = FALSE
|
||||
var/last_man_standing = FALSE
|
||||
var/list/datum/mind/targets_stolen
|
||||
|
||||
|
||||
/datum/antagonist/traitor/human/internal_affairs
|
||||
name = "Internal Affairs Agent"
|
||||
employer = "Nanotrasen"
|
||||
special_role = "internal affairs agent"
|
||||
antagpanel_category = "IAA"
|
||||
var/syndicate = FALSE
|
||||
var/last_man_standing = FALSE
|
||||
var/list/datum/mind/targets_stolen
|
||||
|
||||
|
||||
/datum/antagonist/traitor/human/internal_affairs/proc/give_pinpointer()
|
||||
if(owner && owner.current)
|
||||
owner.current.apply_status_effect(/datum/status_effect/agent_pinpointer)
|
||||
|
||||
/datum/antagonist/traitor/human/internal_affairs/apply_innate_effects()
|
||||
.=..() //in case the base is used in future
|
||||
if(owner && owner.current)
|
||||
give_pinpointer(owner.current)
|
||||
|
||||
/datum/antagonist/traitor/human/internal_affairs/remove_innate_effects()
|
||||
.=..()
|
||||
if(owner && owner.current)
|
||||
owner.current.remove_status_effect(/datum/status_effect/agent_pinpointer)
|
||||
|
||||
/datum/antagonist/traitor/human/internal_affairs/on_gain()
|
||||
START_PROCESSING(SSprocessing, src)
|
||||
.=..()
|
||||
/datum/antagonist/traitor/human/internal_affairs/on_removal()
|
||||
STOP_PROCESSING(SSprocessing,src)
|
||||
.=..()
|
||||
/datum/antagonist/traitor/human/internal_affairs/process()
|
||||
iaa_process()
|
||||
|
||||
/datum/antagonist/traitor/AI/internal_affairs/on_gain()
|
||||
START_PROCESSING(SSprocessing, src)
|
||||
.=..()
|
||||
/datum/antagonist/traitor/AI/internal_affairs/on_removal()
|
||||
STOP_PROCESSING(SSprocessing,src)
|
||||
.=..()
|
||||
/datum/antagonist/traitor/AI/internal_affairs/process()
|
||||
iaa_process()
|
||||
|
||||
/datum/status_effect/agent_pinpointer
|
||||
id = "agent_pinpointer"
|
||||
duration = -1
|
||||
tick_interval = PINPOINTER_PING_TIME
|
||||
alert_type = /obj/screen/alert/status_effect/agent_pinpointer
|
||||
var/minimum_range = PINPOINTER_MINIMUM_RANGE
|
||||
var/mob/scan_target = null
|
||||
|
||||
/obj/screen/alert/status_effect/agent_pinpointer
|
||||
name = "Internal Affairs Integrated Pinpointer"
|
||||
desc = "Even stealthier than a normal implant."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "pinon"
|
||||
|
||||
/datum/status_effect/agent_pinpointer/proc/point_to_target() //If we found what we're looking for, show the distance and direction
|
||||
if(!scan_target)
|
||||
linked_alert.icon_state = "pinonnull"
|
||||
return
|
||||
var/turf/here = get_turf(owner)
|
||||
var/turf/there = get_turf(scan_target)
|
||||
if(here.z != there.z)
|
||||
linked_alert.icon_state = "pinonnull"
|
||||
return
|
||||
if(get_dist_euclidian(here,there)<=minimum_range + rand(0, PINPOINTER_EXTRA_RANDOM_RANGE))
|
||||
linked_alert.icon_state = "pinondirect"
|
||||
else
|
||||
linked_alert.setDir(get_dir(here, there))
|
||||
switch(get_dist(here, there))
|
||||
if(1 to 8)
|
||||
linked_alert.icon_state = "pinonclose"
|
||||
if(9 to 16)
|
||||
linked_alert.icon_state = "pinonmedium"
|
||||
if(16 to INFINITY)
|
||||
linked_alert.icon_state = "pinonfar"
|
||||
|
||||
/datum/status_effect/agent_pinpointer/proc/scan_for_target()
|
||||
scan_target = null
|
||||
if(owner)
|
||||
if(owner.mind)
|
||||
if(owner.mind.objectives)
|
||||
for(var/datum/objective/objective_ in owner.mind.objectives)
|
||||
if(!is_internal_objective(objective_))
|
||||
continue
|
||||
var/datum/objective/assassinate/internal/objective = objective_
|
||||
var/mob/current = objective.target.current
|
||||
if(current&¤t.stat!=DEAD)
|
||||
scan_target = current
|
||||
break
|
||||
|
||||
/datum/status_effect/agent_pinpointer/tick()
|
||||
if(!owner)
|
||||
qdel(src)
|
||||
return
|
||||
scan_for_target()
|
||||
point_to_target()
|
||||
|
||||
|
||||
/proc/is_internal_objective(datum/objective/O)
|
||||
return (istype(O, /datum/objective/assassinate/internal)||istype(O, /datum/objective/destroy/internal))
|
||||
|
||||
/datum/antagonist/traitor/proc/replace_escape_objective()
|
||||
if(!owner||!owner.objectives)
|
||||
return
|
||||
for (var/objective_ in owner.objectives)
|
||||
if(!(istype(objective_, /datum/objective/escape)||istype(objective_, /datum/objective/survive)))
|
||||
continue
|
||||
remove_objective(objective_)
|
||||
|
||||
var/datum/objective/martyr/martyr_objective = new
|
||||
martyr_objective.owner = owner
|
||||
add_objective(martyr_objective)
|
||||
|
||||
/datum/antagonist/traitor/proc/reinstate_escape_objective()
|
||||
if(!owner||!owner.objectives)
|
||||
return
|
||||
for (var/objective_ in owner.objectives)
|
||||
if(!istype(objective_, /datum/objective/martyr))
|
||||
continue
|
||||
remove_objective(objective_)
|
||||
|
||||
/datum/antagonist/traitor/human/internal_affairs/reinstate_escape_objective()
|
||||
..()
|
||||
var/datum/objective/escape/escape_objective = new
|
||||
escape_objective.owner = owner
|
||||
add_objective(escape_objective)
|
||||
|
||||
/datum/antagonist/traitor/AI/internal_affairs/reinstate_escape_objective()
|
||||
..()
|
||||
var/datum/objective/survive/survive_objective = new
|
||||
survive_objective.owner = owner
|
||||
add_objective(survive_objective)
|
||||
|
||||
/datum/antagonist/traitor/proc/steal_targets(datum/mind/victim)
|
||||
var/datum/antagonist/traitor/human/internal_affairs/this = src //Should only use this if IAA
|
||||
|
||||
if(!owner.current||owner.current.stat==DEAD)
|
||||
return
|
||||
to_chat(owner.current, "<span class='userdanger'> Target eliminated: [victim.name]</span>")
|
||||
for(var/objective_ in victim.objectives)
|
||||
if(istype(objective_, /datum/objective/assassinate/internal))
|
||||
var/datum/objective/assassinate/internal/objective = objective_
|
||||
if(objective.target==owner)
|
||||
continue
|
||||
else if(this.targets_stolen.Find(objective.target) == 0)
|
||||
var/datum/objective/assassinate/internal/new_objective = new
|
||||
new_objective.owner = owner
|
||||
new_objective.target = objective.target
|
||||
new_objective.update_explanation_text()
|
||||
add_objective(new_objective)
|
||||
this.targets_stolen += objective.target
|
||||
var/status_text = objective.check_completion() ? "neutralised" : "active"
|
||||
to_chat(owner.current, "<span class='userdanger'> New target added to database: [objective.target.name] ([status_text]) </span>")
|
||||
else if(istype(objective_, /datum/objective/destroy/internal))
|
||||
var/datum/objective/destroy/internal/objective = objective_
|
||||
var/datum/objective/destroy/internal/new_objective = new
|
||||
if(objective.target==owner)
|
||||
continue
|
||||
else if(this.targets_stolen.Find(objective.target) == 0)
|
||||
new_objective.owner = owner
|
||||
new_objective.target = objective.target
|
||||
new_objective.update_explanation_text()
|
||||
add_objective(new_objective)
|
||||
this.targets_stolen += objective.target
|
||||
var/status_text = objective.check_completion() ? "neutralised" : "active"
|
||||
to_chat(owner.current, "<span class='userdanger'> New target added to database: [objective.target.name] ([status_text]) </span>")
|
||||
this.last_man_standing = TRUE
|
||||
for(var/objective_ in owner.objectives)
|
||||
if(!is_internal_objective(objective_))
|
||||
continue
|
||||
var/datum/objective/assassinate/internal/objective = objective_
|
||||
if(!objective.check_completion())
|
||||
this.last_man_standing = FALSE
|
||||
return
|
||||
if(this.last_man_standing)
|
||||
if(this.syndicate)
|
||||
to_chat(owner.current,"<span class='userdanger'> All the loyalist agents are dead, and no more is required of you. Die a glorious death, agent. </span>")
|
||||
else
|
||||
to_chat(owner.current,"<span class='userdanger'> All the other agents are dead, and you're the last loose end. Stage a Syndicate terrorist attack to cover up for today's events. You no longer have any limits on collateral damage.</span>")
|
||||
replace_escape_objective(owner)
|
||||
|
||||
/datum/antagonist/traitor/proc/iaa_process()
|
||||
var/datum/antagonist/traitor/human/internal_affairs/this = src //Should only use this if IAA
|
||||
if(owner&&owner.current&&owner.current.stat!=DEAD)
|
||||
for(var/objective_ in owner.objectives)
|
||||
if(!is_internal_objective(objective_))
|
||||
continue
|
||||
var/datum/objective/assassinate/internal/objective = objective_
|
||||
if(!objective.target)
|
||||
continue
|
||||
if(objective.check_completion())
|
||||
if(objective.stolen)
|
||||
continue
|
||||
else
|
||||
steal_targets(objective.target)
|
||||
objective.stolen = TRUE
|
||||
else
|
||||
if(objective.stolen)
|
||||
var/fail_msg = "<span class='userdanger'>Your sensors tell you that [objective.target.current.real_name], one of the targets you were meant to have killed, pulled one over on you, and is still alive - do the job properly this time! </span>"
|
||||
if(this.last_man_standing)
|
||||
if(this.syndicate)
|
||||
fail_msg += "<span class='userdanger'> You no longer have permission to die. </span>"
|
||||
else
|
||||
fail_msg += "<span class='userdanger'> The truth could still slip out!</font><B><font size=5 color=red> Cease any terrorist actions as soon as possible, unneeded property damage or loss of employee life will lead to your contract being terminated.</span>"
|
||||
reinstate_escape_objective(owner)
|
||||
this.last_man_standing = FALSE
|
||||
to_chat(owner.current, fail_msg)
|
||||
objective.stolen = FALSE
|
||||
|
||||
/datum/antagonist/traitor/proc/forge_iaa_objectives()
|
||||
var/datum/antagonist/traitor/human/internal_affairs/this = src //Should only use this if IAA
|
||||
if(SSticker.mode.target_list.len && SSticker.mode.target_list[owner]) // Is a double agent
|
||||
|
||||
// Assassinate
|
||||
var/datum/mind/target_mind = SSticker.mode.target_list[owner]
|
||||
if(issilicon(target_mind.current))
|
||||
var/datum/objective/destroy/internal/destroy_objective = new
|
||||
destroy_objective.owner = owner
|
||||
destroy_objective.target = target_mind
|
||||
destroy_objective.update_explanation_text()
|
||||
else
|
||||
var/datum/objective/assassinate/internal/kill_objective = new
|
||||
kill_objective.owner = owner
|
||||
kill_objective.target = target_mind
|
||||
kill_objective.update_explanation_text()
|
||||
add_objective(kill_objective)
|
||||
|
||||
//Optional traitor objective
|
||||
if(prob(PROB_ACTUAL_TRAITOR))
|
||||
employer = "The Syndicate"
|
||||
owner.special_role = TRAITOR_AGENT_ROLE
|
||||
special_role = TRAITOR_AGENT_ROLE
|
||||
this.syndicate = TRUE
|
||||
forge_single_objective()
|
||||
|
||||
else
|
||||
..() // Give them standard objectives.
|
||||
return
|
||||
|
||||
/datum/antagonist/traitor/human/internal_affairs/forge_traitor_objectives()
|
||||
forge_iaa_objectives()
|
||||
var/datum/objective/escape/escape_objective = new
|
||||
escape_objective.owner = owner
|
||||
add_objective(escape_objective)
|
||||
|
||||
/datum/antagonist/traitor/AI/internal_affairs/forge_traitor_objectives()
|
||||
forge_iaa_objectives()
|
||||
var/datum/objective/survive/survive_objective = new
|
||||
survive_objective.owner = owner
|
||||
add_objective(survive_objective)
|
||||
|
||||
/datum/antagonist/traitor/proc/greet_iaa()
|
||||
var/datum/antagonist/traitor/human/internal_affairs/this = src //Should only use this if IAA
|
||||
var/crime = pick("distribution of contraband" , "unauthorized erotic action on duty", "embezzlement", "piloting under the influence", "dereliction of duty", "syndicate collaboration", "mutiny", "multiple homicides", "corporate espionage", "recieving bribes", "malpractice", "worship of prohbited life forms", "possession of profane texts", "murder", "arson", "insulting their manager", "grand theft", "conspiracy", "attempting to unionize", "vandalism", "gross incompetence")
|
||||
|
||||
to_chat(owner.current, "<span class='userdanger'>You are the [special_role].</span>")
|
||||
if(this.syndicate)
|
||||
to_chat(owner.current, "<span class='userdanger'>Your target has been framed for [crime], and you have been tasked with eliminating them to prevent them defending themselves in court.</span>")
|
||||
to_chat(owner.current, "<B><font size=5 color=red>Any damage you cause will be a further embarrassment to Nanotrasen, so you have no limits on collateral damage.</font></B>")
|
||||
to_chat(owner.current, "<span class='userdanger'> You have been provided with a standard uplink to accomplish your task. </span>")
|
||||
else
|
||||
to_chat(owner.current, "<span class='userdanger'>Your target is suspected of [crime], and you have been tasked with eliminating them by any means necessary to avoid a costly and embarrassing public trial.</span>")
|
||||
to_chat(owner.current, "<B><font size=5 color=red>While you have a license to kill, unneeded property damage or loss of employee life will lead to your contract being terminated.</font></B>")
|
||||
to_chat(owner.current, "<span class='userdanger'>For the sake of plausible deniability, you have been equipped with an array of captured Syndicate weaponry available via uplink.</span>")
|
||||
|
||||
to_chat(owner.current, "<span class='userdanger'>Finally, watch your back. Your target has friends in high places, and intel suggests someone may have taken out a contract of their own to protect them.</span>")
|
||||
owner.announce_objectives()
|
||||
|
||||
/datum/antagonist/traitor/AI/internal_affairs/greet()
|
||||
greet_iaa()
|
||||
|
||||
/datum/antagonist/traitor/human/internal_affairs/greet()
|
||||
greet_iaa()
|
||||
|
||||
|
||||
#undef PROB_ACTUAL_TRAITOR
|
||||
#undef PINPOINTER_EXTRA_RANDOM_RANGE
|
||||
#undef PINPOINTER_MINIMUM_RANGE
|
||||
#undef PINPOINTER_PING_TIME
|
||||
@@ -1,214 +0,0 @@
|
||||
#define MONKEYS_ESCAPED 1
|
||||
#define MONKEYS_LIVED 2
|
||||
#define MONKEYS_DIED 3
|
||||
#define DISEASE_LIVED 4
|
||||
|
||||
/datum/antagonist/monkey
|
||||
name = "Monkey"
|
||||
job_rank = ROLE_MONKEY
|
||||
roundend_category = "monkeys"
|
||||
antagpanel_category = "Monkey"
|
||||
var/datum/team/monkey/monkey_team
|
||||
var/monkey_only = TRUE
|
||||
|
||||
/datum/antagonist/monkey/can_be_owned(datum/mind/new_owner)
|
||||
return ..() && (!monkey_only || ismonkey(new_owner.current))
|
||||
|
||||
/datum/antagonist/monkey/get_team()
|
||||
return monkey_team
|
||||
|
||||
/datum/antagonist/monkey/on_gain()
|
||||
. = ..()
|
||||
SSticker.mode.ape_infectees += owner
|
||||
owner.special_role = "Infected Monkey"
|
||||
|
||||
var/datum/disease/D = new /datum/disease/transformation/jungle_fever/monkeymode
|
||||
if(!owner.current.HasDisease(D))
|
||||
owner.current.ForceContractDisease(D)
|
||||
else
|
||||
QDEL_NULL(D)
|
||||
|
||||
/datum/antagonist/monkey/greet()
|
||||
to_chat(owner, "<b>You are a monkey now!</b>")
|
||||
to_chat(owner, "<b>Bite humans to infect them, follow the orders of the monkey leaders, and help fellow monkeys!</b>")
|
||||
to_chat(owner, "<b>Ensure at least one infected monkey escapes on the Emergency Shuttle!</b>")
|
||||
to_chat(owner, "<b><i>As an intelligent monkey, you know how to use technology and how to ventcrawl while wearing things.</i></b>")
|
||||
to_chat(owner, "<b>You can use :k to talk to fellow monkeys!</b>")
|
||||
SEND_SOUND(owner.current, sound('sound/ambience/antag/monkey.ogg'))
|
||||
|
||||
/datum/antagonist/monkey/on_removal()
|
||||
owner.special_role = null
|
||||
SSticker.mode.ape_infectees -= owner
|
||||
|
||||
var/datum/disease/transformation/jungle_fever/D = locate() in owner.current.viruses
|
||||
if(D)
|
||||
D.remove_virus()
|
||||
qdel(D)
|
||||
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/monkey/create_team(datum/team/monkey/new_team)
|
||||
if(!new_team)
|
||||
for(var/datum/antagonist/monkey/H in GLOB.antagonists)
|
||||
if(!H.owner)
|
||||
continue
|
||||
if(H.monkey_team)
|
||||
monkey_team = H.monkey_team
|
||||
return
|
||||
monkey_team = new /datum/team/monkey
|
||||
monkey_team.update_objectives()
|
||||
return
|
||||
if(!istype(new_team))
|
||||
stack_trace("Wrong team type passed to [type] initialization.")
|
||||
monkey_team = new_team
|
||||
|
||||
/datum/antagonist/monkey/proc/forge_objectives()
|
||||
objectives |= monkey_team.objectives
|
||||
owner.objectives |= objectives
|
||||
|
||||
/datum/antagonist/monkey/admin_remove(mob/admin)
|
||||
var/mob/living/carbon/monkey/M = owner.current
|
||||
if(istype(M))
|
||||
switch(alert(admin, "Humanize?", "Humanize", "Yes", "No"))
|
||||
if("Yes")
|
||||
if(admin == M)
|
||||
admin = M.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_DEFAULTMSG)
|
||||
else
|
||||
M.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_DEFAULTMSG)
|
||||
if("No")
|
||||
//nothing
|
||||
else
|
||||
return
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/monkey/leader
|
||||
name = "Monkey Leader"
|
||||
monkey_only = FALSE
|
||||
|
||||
/datum/antagonist/monkey/leader/admin_add(datum/mind/new_owner,mob/admin)
|
||||
var/mob/living/carbon/human/H = new_owner.current
|
||||
if(istype(H))
|
||||
switch(alert(admin, "Monkeyize?", "Monkeyize", "Yes", "No"))
|
||||
if("Yes")
|
||||
if(admin == H)
|
||||
admin = H.monkeyize()
|
||||
else
|
||||
H.monkeyize()
|
||||
if("No")
|
||||
//nothing
|
||||
else
|
||||
return
|
||||
new_owner.add_antag_datum(src)
|
||||
log_admin("[key_name(admin)] made [key_name(new_owner.current)] a monkey leader!")
|
||||
message_admins("[key_name_admin(admin)] made [key_name_admin(new_owner.current)] a monkey leader!")
|
||||
|
||||
/datum/antagonist/monkey/leader/on_gain()
|
||||
. = ..()
|
||||
var/obj/item/organ/heart/freedom/F = new
|
||||
F.Insert(owner.current, drop_if_replaced = FALSE)
|
||||
SSticker.mode.ape_leaders += owner
|
||||
owner.special_role = "Monkey Leader"
|
||||
|
||||
/datum/antagonist/monkey/leader/on_removal()
|
||||
SSticker.mode.ape_leaders -= owner
|
||||
var/obj/item/organ/heart/H = new
|
||||
H.Insert(owner.current, drop_if_replaced = FALSE) //replace freedom heart with normal heart
|
||||
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/monkey/leader/greet()
|
||||
to_chat(owner, "<B><span class='notice'>You are the Jungle Fever patient zero!!</B></span>")
|
||||
to_chat(owner, "<b>You have been planted onto this station by the Animal Rights Consortium.</b>")
|
||||
to_chat(owner, "<b>Soon the disease will transform you into an ape. Afterwards, you will be able spread the infection to others with a bite.</b>")
|
||||
to_chat(owner, "<b>While your infection strain is undetectable by scanners, any other infectees will show up on medical equipment.</b>")
|
||||
to_chat(owner, "<b>Your mission will be deemed a success if any of the live infected monkeys reach CentCom.</b>")
|
||||
to_chat(owner, "<b>As an initial infectee, you will be considered a 'leader' by your fellow monkeys.</b>")
|
||||
to_chat(owner, "<b>You can use :k to talk to fellow monkeys!</b>")
|
||||
SEND_SOUND(owner.current, sound('sound/ambience/antag/monkey.ogg'))
|
||||
|
||||
/datum/objective/monkey
|
||||
explanation_text = "Ensure that infected monkeys escape on the emergency shuttle!"
|
||||
martyr_compatible = TRUE
|
||||
var/monkeys_to_win = 1
|
||||
var/escaped_monkeys = 0
|
||||
|
||||
/datum/objective/monkey/check_completion()
|
||||
var/datum/disease/D = new /datum/disease/transformation/jungle_fever()
|
||||
for(var/mob/living/carbon/monkey/M in GLOB.alive_mob_list)
|
||||
if (M.HasDisease(D) && (M.onCentCom() || M.onSyndieBase()))
|
||||
escaped_monkeys++
|
||||
if(escaped_monkeys >= monkeys_to_win)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/team/monkey
|
||||
name = "Monkeys"
|
||||
|
||||
/datum/team/monkey/proc/update_objectives()
|
||||
objectives = list()
|
||||
var/datum/objective/monkey/O = new()
|
||||
O.team = src
|
||||
objectives += O
|
||||
|
||||
/datum/team/monkey/proc/infected_monkeys_alive()
|
||||
var/datum/disease/D = new /datum/disease/transformation/jungle_fever()
|
||||
for(var/mob/living/carbon/monkey/M in GLOB.alive_mob_list)
|
||||
if(M.HasDisease(D))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/team/monkey/proc/infected_monkeys_escaped()
|
||||
var/datum/disease/D = new /datum/disease/transformation/jungle_fever()
|
||||
for(var/mob/living/carbon/monkey/M in GLOB.alive_mob_list)
|
||||
if(M.HasDisease(D) && (M.onCentCom() || M.onSyndieBase()))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/team/monkey/proc/infected_humans_escaped()
|
||||
var/datum/disease/D = new /datum/disease/transformation/jungle_fever()
|
||||
for(var/mob/living/carbon/human/M in GLOB.alive_mob_list)
|
||||
if(M.HasDisease(D) && (M.onCentCom() || M.onSyndieBase()))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/team/monkey/proc/infected_humans_alive()
|
||||
var/datum/disease/D = new /datum/disease/transformation/jungle_fever()
|
||||
for(var/mob/living/carbon/human/M in GLOB.alive_mob_list)
|
||||
if(M.HasDisease(D))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/team/monkey/proc/get_result()
|
||||
if(infected_monkeys_escaped())
|
||||
return MONKEYS_ESCAPED
|
||||
if(infected_monkeys_alive())
|
||||
return MONKEYS_LIVED
|
||||
if(infected_humans_alive() || infected_humans_escaped())
|
||||
return DISEASE_LIVED
|
||||
return MONKEYS_DIED
|
||||
|
||||
/datum/team/monkey/roundend_report()
|
||||
var/list/parts = list()
|
||||
switch(get_result())
|
||||
if(MONKEYS_ESCAPED)
|
||||
parts += "<span class='greentext big'><B>Monkey Major Victory!</B></span>"
|
||||
parts += "<span class='greentext'><B>Central Command and [station_name()] were taken over by the monkeys! Ook ook!</B></span>"
|
||||
if(MONKEYS_LIVED)
|
||||
parts += "<FONT size = 3><B>Monkey Minor Victory!</B></FONT>"
|
||||
parts += "<span class='greentext'><B>[station_name()] was taken over by the monkeys! Ook ook!</B></span>"
|
||||
if(DISEASE_LIVED)
|
||||
parts += "<span class='redtext big'><B>Monkey Minor Defeat!</B></span>"
|
||||
parts += "<span class='redtext'><B>All the monkeys died, but the disease lives on! The future is uncertain.</B></span>"
|
||||
if(MONKEYS_DIED)
|
||||
parts += "<span class='redtext big'><B>Monkey Major Defeat!</B></span>"
|
||||
parts += "<span class='redtext'><B>All the monkeys died, and Jungle Fever was wiped out!</B></span>"
|
||||
var/list/leaders = get_antagonists(/datum/antagonist/monkey/leader, TRUE)
|
||||
var/list/monkeys = get_antagonists(/datum/antagonist/monkey, TRUE)
|
||||
|
||||
if(LAZYLEN(leaders))
|
||||
parts += "<span class='header'>The monkey leaders were:</span>"
|
||||
parts += printplayerlist(SSticker.mode.ape_leaders)
|
||||
if(LAZYLEN(monkeys))
|
||||
parts += "<span class='header'>The monkeys were:</span>"
|
||||
parts += printplayerlist(SSticker.mode.ape_infectees)
|
||||
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
|
||||
@@ -1,155 +0,0 @@
|
||||
/datum/antagonist/ninja
|
||||
name = "Ninja"
|
||||
antagpanel_category = "Ninja"
|
||||
job_rank = ROLE_NINJA
|
||||
var/helping_station = FALSE
|
||||
var/give_objectives = TRUE
|
||||
var/give_equipment = TRUE
|
||||
|
||||
|
||||
/datum/antagonist/ninja/apply_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/M = mob_override || owner.current
|
||||
update_ninja_icons_added(M)
|
||||
|
||||
/datum/antagonist/ninja/remove_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/M = mob_override || owner.current
|
||||
update_ninja_icons_removed(M)
|
||||
|
||||
/datum/antagonist/ninja/proc/equip_space_ninja(mob/living/carbon/human/H = owner.current)
|
||||
return H.equipOutfit(/datum/outfit/ninja)
|
||||
|
||||
/datum/antagonist/ninja/proc/addMemories()
|
||||
antag_memory += "I am an elite mercenary assassin of the mighty Spider Clan. A <font color='red'><B>SPACE NINJA</B></font>!<br>"
|
||||
antag_memory += "Surprise is my weapon. Shadows are my armor. Without them, I am nothing. (//initialize your suit by right clicking on it, to use abilities like stealth)!<br>"
|
||||
antag_memory += "Officially, [helping_station?"Nanotrasen":"The Syndicate"] are my employer.<br>"
|
||||
|
||||
/datum/antagonist/ninja/proc/addObjectives(quantity = 6)
|
||||
var/list/possible_targets = list()
|
||||
for(var/datum/mind/M in SSticker.minds)
|
||||
if(M.current && M.current.stat != DEAD)
|
||||
if(ishuman(M.current))
|
||||
if(M.special_role)
|
||||
possible_targets[M] = 0 //bad-guy
|
||||
else if(M.assigned_role in GLOB.command_positions)
|
||||
possible_targets[M] = 1 //good-guy
|
||||
|
||||
var/list/possible_objectives = list(1,2,3,4)
|
||||
|
||||
while(objectives.len < quantity)
|
||||
switch(pick_n_take(possible_objectives))
|
||||
if(1) //research
|
||||
var/datum/objective/download/O = new /datum/objective/download()
|
||||
O.owner = owner
|
||||
O.gen_amount_goal()
|
||||
objectives += O
|
||||
|
||||
if(2) //steal
|
||||
var/datum/objective/steal/special/O = new /datum/objective/steal/special()
|
||||
O.owner = owner
|
||||
objectives += O
|
||||
|
||||
if(3) //protect/kill
|
||||
if(!possible_targets.len) continue
|
||||
var/index = rand(1,possible_targets.len)
|
||||
var/datum/mind/M = possible_targets[index]
|
||||
var/is_bad_guy = possible_targets[M]
|
||||
possible_targets.Cut(index,index+1)
|
||||
|
||||
if(is_bad_guy ^ helping_station) //kill (good-ninja + bad-guy or bad-ninja + good-guy)
|
||||
var/datum/objective/assassinate/O = new /datum/objective/assassinate()
|
||||
O.owner = owner
|
||||
O.target = M
|
||||
O.explanation_text = "Slay \the [M.current.real_name], the [M.assigned_role]."
|
||||
objectives += O
|
||||
else //protect
|
||||
var/datum/objective/protect/O = new /datum/objective/protect()
|
||||
O.owner = owner
|
||||
O.target = M
|
||||
O.explanation_text = "Protect \the [M.current.real_name], the [M.assigned_role], from harm."
|
||||
objectives += O
|
||||
if(4) //debrain/capture
|
||||
if(!possible_targets.len) continue
|
||||
var/selected = rand(1,possible_targets.len)
|
||||
var/datum/mind/M = possible_targets[selected]
|
||||
var/is_bad_guy = possible_targets[M]
|
||||
possible_targets.Cut(selected,selected+1)
|
||||
|
||||
if(is_bad_guy ^ helping_station) //debrain (good-ninja + bad-guy or bad-ninja + good-guy)
|
||||
var/datum/objective/debrain/O = new /datum/objective/debrain()
|
||||
O.owner = owner
|
||||
O.target = M
|
||||
O.explanation_text = "Steal the brain of [M.current.real_name]."
|
||||
objectives += O
|
||||
else //capture
|
||||
var/datum/objective/capture/O = new /datum/objective/capture()
|
||||
O.owner = owner
|
||||
O.gen_amount_goal()
|
||||
objectives += O
|
||||
else
|
||||
break
|
||||
var/datum/objective/O = new /datum/objective/survive()
|
||||
O.owner = owner
|
||||
owner.objectives |= objectives
|
||||
|
||||
|
||||
/proc/remove_ninja(mob/living/L)
|
||||
if(!L || !L.mind)
|
||||
return FALSE
|
||||
var/datum/antagonist/datum = L.mind.has_antag_datum(/datum/antagonist/ninja)
|
||||
datum.on_removal()
|
||||
return TRUE
|
||||
|
||||
/proc/is_ninja(mob/living/M)
|
||||
return M && M.mind && M.mind.has_antag_datum(/datum/antagonist/ninja)
|
||||
|
||||
|
||||
/datum/antagonist/ninja/greet()
|
||||
SEND_SOUND(owner.current, sound('sound/effects/ninja_greeting.ogg'))
|
||||
to_chat(owner.current, "I am an elite mercenary assassin of the mighty Spider Clan. A <font color='red'><B>SPACE NINJA</B></font>!")
|
||||
to_chat(owner.current, "Surprise is my weapon. Shadows are my armor. Without them, I am nothing. (//initialize your suit by right clicking on it, to use abilities like stealth)!")
|
||||
to_chat(owner.current, "Officially, [helping_station?"Nanotrasen":"The Syndicate"] are my employer.")
|
||||
return
|
||||
|
||||
/datum/antagonist/ninja/on_gain()
|
||||
if(give_objectives)
|
||||
addObjectives()
|
||||
addMemories()
|
||||
if(give_equipment)
|
||||
equip_space_ninja(owner.current)
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/ninja/admin_add(datum/mind/new_owner,mob/admin)
|
||||
var/adj
|
||||
switch(input("What kind of ninja?", "Ninja") as null|anything in list("Random","Syndicate","Nanotrasen","No objectives"))
|
||||
if("Random")
|
||||
helping_station = pick(TRUE,FALSE)
|
||||
adj = ""
|
||||
if("Syndicate")
|
||||
helping_station = FALSE
|
||||
adj = "syndie"
|
||||
if("Nanotrasen")
|
||||
helping_station = TRUE
|
||||
adj = "friendly"
|
||||
if("No objectives")
|
||||
give_objectives = FALSE
|
||||
adj = "objectiveless"
|
||||
else
|
||||
return
|
||||
new_owner.assigned_role = ROLE_NINJA
|
||||
new_owner.special_role = ROLE_NINJA
|
||||
new_owner.add_antag_datum(src)
|
||||
message_admins("[key_name_admin(admin)] has [adj] ninja'ed [new_owner.current].")
|
||||
log_admin("[key_name(admin)] has [adj] ninja'ed [new_owner.current].")
|
||||
|
||||
/datum/antagonist/ninja/antag_listing_name()
|
||||
return ..() + "(Ninja)"
|
||||
|
||||
/datum/antagonist/ninja/proc/update_ninja_icons_added(var/mob/living/carbon/human/ninja)
|
||||
var/datum/atom_hud/antag/ninjahud = GLOB.huds[ANTAG_HUD_NINJA]
|
||||
ninjahud.join_hud(ninja)
|
||||
set_antag_hud(ninja, "ninja")
|
||||
|
||||
/datum/antagonist/ninja/proc/update_ninja_icons_removed(var/mob/living/carbon/human/ninja)
|
||||
var/datum/atom_hud/antag/ninjahud = GLOB.huds[ANTAG_HUD_NINJA]
|
||||
ninjahud.leave_hud(ninja)
|
||||
set_antag_hud(ninja, null)
|
||||
@@ -1,379 +0,0 @@
|
||||
#define NUKE_RESULT_FLUKE 0
|
||||
#define NUKE_RESULT_NUKE_WIN 1
|
||||
#define NUKE_RESULT_CREW_WIN 2
|
||||
#define NUKE_RESULT_CREW_WIN_SYNDIES_DEAD 3
|
||||
#define NUKE_RESULT_DISK_LOST 4
|
||||
#define NUKE_RESULT_DISK_STOLEN 5
|
||||
#define NUKE_RESULT_NOSURVIVORS 6
|
||||
#define NUKE_RESULT_WRONG_STATION 7
|
||||
#define NUKE_RESULT_WRONG_STATION_DEAD 8
|
||||
|
||||
/datum/antagonist/nukeop
|
||||
name = "Nuclear Operative"
|
||||
roundend_category = "syndicate operatives" //just in case
|
||||
antagpanel_category = "NukeOp"
|
||||
job_rank = ROLE_OPERATIVE
|
||||
var/datum/team/nuclear/nuke_team
|
||||
var/always_new_team = FALSE //If not assigned a team by default ops will try to join existing ones, set this to TRUE to always create new team.
|
||||
var/send_to_spawnpoint = TRUE //Should the user be moved to default spawnpoint.
|
||||
var/nukeop_outfit = /datum/outfit/syndicate
|
||||
|
||||
/datum/antagonist/nukeop/proc/update_synd_icons_added(mob/living/M)
|
||||
var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS]
|
||||
opshud.join_hud(M)
|
||||
set_antag_hud(M, "synd")
|
||||
|
||||
/datum/antagonist/nukeop/proc/update_synd_icons_removed(mob/living/M)
|
||||
var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS]
|
||||
opshud.leave_hud(M)
|
||||
set_antag_hud(M, null)
|
||||
|
||||
/datum/antagonist/nukeop/apply_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/M = mob_override || owner.current
|
||||
update_synd_icons_added(M)
|
||||
|
||||
/datum/antagonist/nukeop/remove_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/M = mob_override || owner.current
|
||||
update_synd_icons_removed(M)
|
||||
|
||||
/datum/antagonist/nukeop/proc/equip_op()
|
||||
if(!ishuman(owner.current))
|
||||
return
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
|
||||
H.set_species(/datum/species/human) //Plasamen burn up otherwise, and lizards are vulnerable to asimov AIs
|
||||
|
||||
H.equipOutfit(nukeop_outfit)
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/nukeop/greet()
|
||||
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ops.ogg',100,0)
|
||||
to_chat(owner, "<span class='notice'>You are a [nuke_team ? nuke_team.syndicate_name : "syndicate"] agent!</span>")
|
||||
owner.announce_objectives()
|
||||
return
|
||||
|
||||
/datum/antagonist/nukeop/on_gain()
|
||||
give_alias()
|
||||
forge_objectives()
|
||||
. = ..()
|
||||
equip_op()
|
||||
memorize_code()
|
||||
if(send_to_spawnpoint)
|
||||
move_to_spawnpoint()
|
||||
|
||||
/datum/antagonist/nukeop/get_team()
|
||||
return nuke_team
|
||||
|
||||
/datum/antagonist/nukeop/proc/assign_nuke()
|
||||
if(nuke_team && !nuke_team.tracked_nuke)
|
||||
nuke_team.memorized_code = random_nukecode()
|
||||
var/obj/machinery/nuclearbomb/syndicate/nuke = locate() in GLOB.nuke_list
|
||||
if(nuke)
|
||||
nuke_team.tracked_nuke = nuke
|
||||
if(nuke.r_code == "ADMIN")
|
||||
nuke.r_code = nuke_team.memorized_code
|
||||
else //Already set by admins/something else?
|
||||
nuke_team.memorized_code = nuke.r_code
|
||||
else
|
||||
stack_trace("Syndicate nuke not found during nuke team creation.")
|
||||
nuke_team.memorized_code = null
|
||||
|
||||
/datum/antagonist/nukeop/proc/give_alias()
|
||||
if(nuke_team && nuke_team.syndicate_name)
|
||||
var/number = 1
|
||||
number = nuke_team.members.Find(owner)
|
||||
owner.current.real_name = "[nuke_team.syndicate_name] Operative #[number]"
|
||||
|
||||
/datum/antagonist/nukeop/proc/memorize_code()
|
||||
if(nuke_team && nuke_team.tracked_nuke && nuke_team.memorized_code)
|
||||
antag_memory += "<B>[nuke_team.tracked_nuke] Code</B>: [nuke_team.memorized_code]<br>"
|
||||
to_chat(owner, "The nuclear authorization code is: <B>[nuke_team.memorized_code]</B>")
|
||||
else
|
||||
to_chat(owner, "Unfortunately the syndicate was unable to provide you with nuclear authorization code.")
|
||||
|
||||
/datum/antagonist/nukeop/proc/forge_objectives()
|
||||
if(nuke_team)
|
||||
owner.objectives |= nuke_team.objectives
|
||||
|
||||
/datum/antagonist/nukeop/proc/move_to_spawnpoint()
|
||||
var/team_number = 1
|
||||
if(nuke_team)
|
||||
team_number = nuke_team.members.Find(owner)
|
||||
owner.current.forceMove(GLOB.nukeop_start[((team_number - 1) % GLOB.nukeop_start.len) + 1])
|
||||
|
||||
/datum/antagonist/nukeop/leader/move_to_spawnpoint()
|
||||
owner.current.forceMove(pick(GLOB.nukeop_leader_start))
|
||||
|
||||
/datum/antagonist/nukeop/create_team(datum/team/nuclear/new_team)
|
||||
if(!new_team)
|
||||
if(!always_new_team)
|
||||
for(var/datum/antagonist/nukeop/N in GLOB.antagonists)
|
||||
if(!N.owner)
|
||||
continue
|
||||
if(N.nuke_team)
|
||||
nuke_team = N.nuke_team
|
||||
return
|
||||
nuke_team = new /datum/team/nuclear
|
||||
nuke_team.update_objectives()
|
||||
assign_nuke() //This is bit ugly
|
||||
return
|
||||
if(!istype(new_team))
|
||||
stack_trace("Wrong team type passed to [type] initialization.")
|
||||
nuke_team = new_team
|
||||
|
||||
/datum/antagonist/nukeop/admin_add(datum/mind/new_owner,mob/admin)
|
||||
new_owner.assigned_role = ROLE_SYNDICATE
|
||||
new_owner.add_antag_datum(src)
|
||||
message_admins("[key_name_admin(admin)] has nuke op'ed [new_owner.current].")
|
||||
log_admin("[key_name(admin)] has nuke op'ed [new_owner.current].")
|
||||
|
||||
/datum/antagonist/nukeop/get_admin_commands()
|
||||
. = ..()
|
||||
.["Send to base"] = CALLBACK(src,.proc/admin_send_to_base)
|
||||
.["Tell code"] = CALLBACK(src,.proc/admin_tell_code)
|
||||
|
||||
/datum/antagonist/nukeop/proc/admin_send_to_base(mob/admin)
|
||||
owner.current.forceMove(pick(GLOB.nukeop_start))
|
||||
|
||||
/datum/antagonist/nukeop/proc/admin_tell_code(mob/admin)
|
||||
var/code
|
||||
for (var/obj/machinery/nuclearbomb/bombue in GLOB.machines)
|
||||
if (length(bombue.r_code) <= 5 && bombue.r_code != initial(bombue.r_code))
|
||||
code = bombue.r_code
|
||||
break
|
||||
if (code)
|
||||
antag_memory += "<B>Syndicate Nuclear Bomb Code</B>: [code]<br>"
|
||||
to_chat(owner.current, "The nuclear authorization code is: <B>[code]</B>")
|
||||
else
|
||||
to_chat(admin, "<span class='danger'>No valid nuke found!</span>")
|
||||
|
||||
/datum/antagonist/nukeop/leader
|
||||
name = "Nuclear Operative Leader"
|
||||
nukeop_outfit = /datum/outfit/syndicate/leader
|
||||
always_new_team = TRUE
|
||||
var/title
|
||||
|
||||
/datum/antagonist/nukeop/leader/memorize_code()
|
||||
..()
|
||||
if(nuke_team && nuke_team.memorized_code)
|
||||
var/obj/item/paper/P = new
|
||||
P.info = "The nuclear authorization code is: <b>[nuke_team.memorized_code]</b>"
|
||||
P.name = "nuclear bomb code"
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
if(!istype(H))
|
||||
P.forceMove(get_turf(H))
|
||||
else
|
||||
H.put_in_hands(P, TRUE)
|
||||
H.update_icons()
|
||||
|
||||
/datum/antagonist/nukeop/leader/give_alias()
|
||||
title = pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord")
|
||||
if(nuke_team && nuke_team.syndicate_name)
|
||||
owner.current.real_name = "[nuke_team.syndicate_name] [title]"
|
||||
else
|
||||
owner.current.real_name = "Syndicate [title]"
|
||||
|
||||
/datum/antagonist/nukeop/leader/greet()
|
||||
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ops.ogg',100,0)
|
||||
to_chat(owner, "<B>You are the Syndicate [title] for this mission. You are responsible for the distribution of telecrystals and your ID is the only one who can open the launch bay doors.</B>")
|
||||
to_chat(owner, "<B>If you feel you are not up to this task, give your ID to another operative.</B>")
|
||||
to_chat(owner, "<B>In your hand you will find a special item capable of triggering a greater challenge for your team. Examine it carefully and consult with your fellow operatives before activating it.</B>")
|
||||
owner.announce_objectives()
|
||||
addtimer(CALLBACK(src, .proc/nuketeam_name_assign), 1)
|
||||
|
||||
|
||||
/datum/antagonist/nukeop/leader/proc/nuketeam_name_assign()
|
||||
if(!nuke_team)
|
||||
return
|
||||
nuke_team.rename_team(ask_name())
|
||||
|
||||
/datum/team/nuclear/proc/rename_team(new_name)
|
||||
syndicate_name = new_name
|
||||
name = "[syndicate_name] Team"
|
||||
for(var/I in members)
|
||||
var/datum/mind/synd_mind = I
|
||||
var/mob/living/carbon/human/H = synd_mind.current
|
||||
if(!istype(H))
|
||||
continue
|
||||
var/chosen_name = H.dna.species.random_name(H.gender,0,syndicate_name)
|
||||
H.fully_replace_character_name(H.real_name,chosen_name)
|
||||
|
||||
/datum/antagonist/nukeop/leader/proc/ask_name()
|
||||
var/randomname = pick(GLOB.last_names)
|
||||
var/newname = stripped_input(owner.current,"You are the nuke operative [title]. Please choose a last name for your family.", "Name change",randomname)
|
||||
if (!newname)
|
||||
newname = randomname
|
||||
else
|
||||
newname = reject_bad_name(newname)
|
||||
if(!newname)
|
||||
newname = randomname
|
||||
|
||||
return capitalize(newname)
|
||||
|
||||
/datum/antagonist/nukeop/lone
|
||||
name = "Lone Operative"
|
||||
always_new_team = TRUE
|
||||
send_to_spawnpoint = FALSE //Handled by event
|
||||
nukeop_outfit = /datum/outfit/syndicate/full
|
||||
|
||||
/datum/antagonist/nukeop/lone/assign_nuke()
|
||||
if(nuke_team && !nuke_team.tracked_nuke)
|
||||
nuke_team.memorized_code = random_nukecode()
|
||||
var/obj/machinery/nuclearbomb/selfdestruct/nuke = locate() in GLOB.nuke_list
|
||||
if(nuke)
|
||||
nuke_team.tracked_nuke = nuke
|
||||
if(nuke.r_code == "ADMIN")
|
||||
nuke.r_code = nuke_team.memorized_code
|
||||
else //Already set by admins/something else?
|
||||
nuke_team.memorized_code = nuke.r_code
|
||||
else
|
||||
stack_trace("Station self destruct ot found during lone op team creation.")
|
||||
nuke_team.memorized_code = null
|
||||
|
||||
/datum/antagonist/nukeop/reinforcement
|
||||
send_to_spawnpoint = FALSE
|
||||
nukeop_outfit = /datum/outfit/syndicate/no_crystals
|
||||
|
||||
/datum/team/nuclear
|
||||
var/syndicate_name
|
||||
var/obj/machinery/nuclearbomb/tracked_nuke
|
||||
var/core_objective = /datum/objective/nuclear
|
||||
var/memorized_code
|
||||
|
||||
/datum/team/nuclear/New()
|
||||
..()
|
||||
syndicate_name = syndicate_name()
|
||||
|
||||
/datum/team/nuclear/proc/update_objectives()
|
||||
if(core_objective)
|
||||
var/datum/objective/O = new core_objective
|
||||
O.team = src
|
||||
objectives += O
|
||||
|
||||
/datum/team/nuclear/proc/disk_rescued()
|
||||
for(var/obj/item/disk/nuclear/D in GLOB.poi_list)
|
||||
if(!D.onCentCom())
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/team/nuclear/proc/operatives_dead()
|
||||
for(var/I in members)
|
||||
var/datum/mind/operative_mind = I
|
||||
if(ishuman(operative_mind.current) && (operative_mind.current.stat != DEAD))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/team/nuclear/proc/syndies_escaped()
|
||||
var/obj/docking_port/mobile/S = SSshuttle.getShuttle("syndicate")
|
||||
return S && (is_centcom_level(S.z) || is_transit_level(S.z))
|
||||
|
||||
/datum/team/nuclear/proc/get_result()
|
||||
var/evacuation = SSshuttle.emergency.mode == SHUTTLE_ENDGAME
|
||||
var/disk_rescued = disk_rescued()
|
||||
var/syndies_didnt_escape = !syndies_escaped()
|
||||
var/station_was_nuked = SSticker.mode.station_was_nuked
|
||||
var/nuke_off_station = SSticker.mode.nuke_off_station
|
||||
|
||||
if(nuke_off_station == NUKE_SYNDICATE_BASE)
|
||||
return NUKE_RESULT_FLUKE
|
||||
else if(!disk_rescued && station_was_nuked && !syndies_didnt_escape)
|
||||
return NUKE_RESULT_NUKE_WIN
|
||||
else if (!disk_rescued && station_was_nuked && syndies_didnt_escape)
|
||||
return NUKE_RESULT_NOSURVIVORS
|
||||
else if (!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape)
|
||||
return NUKE_RESULT_WRONG_STATION
|
||||
else if (!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape)
|
||||
return NUKE_RESULT_WRONG_STATION_DEAD
|
||||
else if ((disk_rescued || evacuation) && operatives_dead())
|
||||
return NUKE_RESULT_CREW_WIN_SYNDIES_DEAD
|
||||
else if (disk_rescued)
|
||||
return NUKE_RESULT_CREW_WIN
|
||||
else if (!disk_rescued && operatives_dead())
|
||||
return NUKE_RESULT_DISK_LOST
|
||||
else if (!disk_rescued && evacuation)
|
||||
return NUKE_RESULT_DISK_STOLEN
|
||||
else
|
||||
return //Undefined result
|
||||
|
||||
/datum/team/nuclear/roundend_report()
|
||||
var/list/parts = list()
|
||||
parts += "<span class='header'>[syndicate_name] Operatives:</span>"
|
||||
|
||||
switch(get_result())
|
||||
if(NUKE_RESULT_FLUKE)
|
||||
parts += "<span class='redtext big'>Humiliating Syndicate Defeat</span>"
|
||||
parts += "<B>The crew of [station_name()] gave [syndicate_name] operatives back their bomb! The syndicate base was destroyed!</B> Next time, don't lose the nuke!"
|
||||
if(NUKE_RESULT_NUKE_WIN)
|
||||
parts += "<span class='greentext big'>Syndicate Major Victory!</span>"
|
||||
parts += "<B>[syndicate_name] operatives have destroyed [station_name()]!</B>"
|
||||
if(NUKE_RESULT_NOSURVIVORS)
|
||||
parts += "<span class='neutraltext big'>Total Annihilation</span>"
|
||||
parts += "<B>[syndicate_name] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion.</B> Next time, don't lose the disk!"
|
||||
if(NUKE_RESULT_WRONG_STATION)
|
||||
parts += "<span class='redtext big'>Crew Minor Victory</span>"
|
||||
parts += "<B>[syndicate_name] operatives secured the authentication disk but blew up something that wasn't [station_name()].</B> Next time, don't do that!"
|
||||
if(NUKE_RESULT_WRONG_STATION_DEAD)
|
||||
parts += "<span class='redtext big'>[syndicate_name] operatives have earned Darwin Award!</span>"
|
||||
parts += "<B>[syndicate_name] operatives blew up something that wasn't [station_name()] and got caught in the explosion.</B> Next time, don't do that!"
|
||||
if(NUKE_RESULT_CREW_WIN_SYNDIES_DEAD)
|
||||
parts += "<span class='redtext big'>Crew Major Victory!</span>"
|
||||
parts += "<B>The Research Staff has saved the disk and killed the [syndicate_name] Operatives</B>"
|
||||
if(NUKE_RESULT_CREW_WIN)
|
||||
parts += "<span class='redtext big'>Crew Major Victory</span>"
|
||||
parts += "<B>The Research Staff has saved the disk and stopped the [syndicate_name] Operatives!</B>"
|
||||
if(NUKE_RESULT_DISK_LOST)
|
||||
parts += "<span class='neutraltext big'>Neutral Victory!</span>"
|
||||
parts += "<B>The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name] Operatives!</B>"
|
||||
if(NUKE_RESULT_DISK_STOLEN)
|
||||
parts += "<span class='greentext big'>Syndicate Minor Victory!</span>"
|
||||
parts += "<B>[syndicate_name] operatives survived the assault but did not achieve the destruction of [station_name()].</B> Next time, don't lose the disk!"
|
||||
else
|
||||
parts += "<span class='neutraltext big'>Neutral Victory</span>"
|
||||
parts += "<B>Mission aborted!</B>"
|
||||
|
||||
var/text = "<br><span class='header'>The syndicate operatives were:</span>"
|
||||
var/purchases = ""
|
||||
var/TC_uses = 0
|
||||
for(var/I in members)
|
||||
var/datum/mind/syndicate = I
|
||||
var/datum/uplink_purchase_log/H = GLOB.uplink_purchase_logs_by_key[syndicate.key]
|
||||
if(H)
|
||||
TC_uses += H.total_spent
|
||||
purchases += H.generate_render(show_key = FALSE)
|
||||
text += printplayerlist(members)
|
||||
text += "<br>"
|
||||
text += "(Syndicates used [TC_uses] TC) [purchases]"
|
||||
if(TC_uses == 0 && SSticker.mode.station_was_nuked && !operatives_dead())
|
||||
text += "<BIG>[icon2html('icons/badass.dmi', world, "badass")]</BIG>"
|
||||
|
||||
parts += text
|
||||
|
||||
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
|
||||
|
||||
/datum/team/nuclear/antag_listing_name()
|
||||
if(syndicate_name)
|
||||
return "[syndicate_name] Syndicates"
|
||||
else
|
||||
return "Syndicates"
|
||||
|
||||
/datum/team/nuclear/antag_listing_entry()
|
||||
var/disk_report = "<b>Nuclear Disk(s)</b><br>"
|
||||
disk_report += "<table cellspacing=5>"
|
||||
for(var/obj/item/disk/nuclear/N in GLOB.poi_list)
|
||||
disk_report += "<tr><td>[N.name], "
|
||||
var/atom/disk_loc = N.loc
|
||||
while(!isturf(disk_loc))
|
||||
if(ismob(disk_loc))
|
||||
var/mob/M = disk_loc
|
||||
disk_report += "carried by <a href='?_src_=holder;[HrefToken()];adminplayeropts=[REF(M)]'>[M.real_name]</a> "
|
||||
if(isobj(disk_loc))
|
||||
var/obj/O = disk_loc
|
||||
disk_report += "in \a [O.name] "
|
||||
disk_loc = disk_loc.loc
|
||||
disk_report += "in [disk_loc.loc] at ([disk_loc.x], [disk_loc.y], [disk_loc.z])</td><td><a href='?_src_=holder;[HrefToken()];adminplayerobservefollow=[REF(N)]'>FLW</a></td></tr>"
|
||||
disk_report += "</table>"
|
||||
var/common_part = ..()
|
||||
return common_part + disk_report
|
||||
|
||||
/datum/team/nuclear/is_gamemode_hero()
|
||||
return SSticker.mode.name == "nuclear emergency"
|
||||
@@ -1,132 +0,0 @@
|
||||
/datum/antagonist/pirate
|
||||
name = "Space Pirate"
|
||||
job_rank = ROLE_TRAITOR
|
||||
roundend_category = "space pirates"
|
||||
antagpanel_category = "Pirate"
|
||||
var/datum/team/pirate/crew
|
||||
|
||||
/datum/antagonist/pirate/greet()
|
||||
to_chat(owner, "<span class='boldannounce'>You are a Space Pirate!</span>")
|
||||
to_chat(owner, "<B>The station refused to pay for your protection, protect the ship, siphon the credits from the station and raid it for even more loot.</B>")
|
||||
owner.announce_objectives()
|
||||
|
||||
/datum/antagonist/pirate/get_team()
|
||||
return crew
|
||||
|
||||
/datum/antagonist/pirate/create_team(datum/team/pirate/new_team)
|
||||
if(!new_team)
|
||||
for(var/datum/antagonist/pirate/P in GLOB.antagonists)
|
||||
if(!P.owner)
|
||||
continue
|
||||
if(P.crew)
|
||||
crew = P.crew
|
||||
return
|
||||
if(!new_team)
|
||||
crew = new /datum/team/pirate
|
||||
crew.forge_objectives()
|
||||
return
|
||||
if(!istype(new_team))
|
||||
stack_trace("Wrong team type passed to [type] initialization.")
|
||||
crew = new_team
|
||||
|
||||
/datum/antagonist/pirate/on_gain()
|
||||
if(crew)
|
||||
owner.objectives |= crew.objectives
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/pirate/on_removal()
|
||||
if(crew)
|
||||
owner.objectives -= crew.objectives
|
||||
. = ..()
|
||||
|
||||
/datum/team/pirate
|
||||
name = "Pirate crew"
|
||||
|
||||
/datum/team/pirate/proc/forge_objectives()
|
||||
var/datum/objective/loot/getbooty = new()
|
||||
getbooty.team = src
|
||||
getbooty.storage_area = locate(/area/shuttle/pirate/vault) in GLOB.sortedAreas
|
||||
getbooty.update_initial_value()
|
||||
getbooty.update_explanation_text()
|
||||
objectives += getbooty
|
||||
for(var/datum/mind/M in members)
|
||||
M.objectives |= objectives
|
||||
|
||||
|
||||
GLOBAL_LIST_INIT(pirate_loot_cache, typecacheof(list(
|
||||
/obj/structure/reagent_dispensers/beerkeg,
|
||||
/mob/living/simple_animal/parrot,
|
||||
/obj/item/stack/sheet/mineral/gold,
|
||||
/obj/item/stack/sheet/mineral/diamond,
|
||||
/obj/item/stack/spacecash,
|
||||
/obj/item/melee/sabre,)))
|
||||
|
||||
/datum/objective/loot
|
||||
var/area/storage_area //Place where we we will look for the loot.
|
||||
explanation_text = "Acquire valuable loot and store it in designated area."
|
||||
var/target_value = 50000
|
||||
var/initial_value = 0 //Things in the vault at spawn time do not count
|
||||
|
||||
/datum/objective/loot/update_explanation_text()
|
||||
if(storage_area)
|
||||
explanation_text = "Acquire loot and store [target_value] of credits worth in [storage_area.name]."
|
||||
|
||||
/datum/objective/loot/proc/loot_listing()
|
||||
//Lists notable loot.
|
||||
if(!storage_area)
|
||||
return "Nothing"
|
||||
var/list/loot_table = list()
|
||||
for(var/atom/movable/AM in storage_area.GetAllContents())
|
||||
if(is_type_in_typecache(AM,GLOB.pirate_loot_cache))
|
||||
var/lootname = AM.name
|
||||
var/count = 1
|
||||
if(istype(AM,/obj/item/stack)) //Ugh.
|
||||
var/obj/item/stack/S = AM
|
||||
lootname = S.singular_name
|
||||
count = S.amount
|
||||
if(!loot_table[lootname])
|
||||
loot_table[lootname] = count
|
||||
else
|
||||
loot_table[lootname] += count
|
||||
var/list/loot_texts = list()
|
||||
for(var/key in loot_table)
|
||||
var/amount = loot_table[key]
|
||||
loot_texts += "[amount] [key][amount > 1 ? "s":""]"
|
||||
return loot_texts.Join(", ")
|
||||
|
||||
/datum/objective/loot/proc/get_loot_value()
|
||||
if(!storage_area)
|
||||
return 0
|
||||
var/value = 0
|
||||
for(var/turf/T in storage_area.contents)
|
||||
value += export_item_and_contents(T,TRUE, TRUE, dry_run = TRUE)
|
||||
return value - initial_value
|
||||
|
||||
/datum/objective/loot/proc/update_initial_value()
|
||||
initial_value = get_loot_value()
|
||||
|
||||
/datum/objective/loot/check_completion()
|
||||
return ..() || get_loot_value() >= target_value
|
||||
|
||||
/datum/team/pirate/roundend_report()
|
||||
var/list/parts = list()
|
||||
|
||||
parts += "<span class='header'>Space Pirates were:</span>"
|
||||
|
||||
var/all_dead = TRUE
|
||||
for(var/datum/mind/M in members)
|
||||
if(considered_alive(M))
|
||||
all_dead = FALSE
|
||||
parts += printplayerlist(members)
|
||||
|
||||
parts += "Loot stolen: "
|
||||
var/datum/objective/loot/L = locate() in objectives
|
||||
parts += L.loot_listing()
|
||||
parts += "Total loot value : [L.get_loot_value()]/[L.target_value] credits"
|
||||
|
||||
if(L.check_completion() && !all_dead)
|
||||
parts += "<span class='greentext big'>The pirate crew was successful!</span>"
|
||||
else
|
||||
parts += "<span class='redtext big'>The pirate crew has failed.</span>"
|
||||
|
||||
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
|
||||
@@ -1,368 +0,0 @@
|
||||
//How often to check for promotion possibility
|
||||
#define HEAD_UPDATE_PERIOD 300
|
||||
|
||||
/datum/antagonist/rev
|
||||
name = "Revolutionary"
|
||||
roundend_category = "revolutionaries" // if by some miracle revolutionaries without revolution happen
|
||||
antagpanel_category = "Revolution"
|
||||
job_rank = ROLE_REV
|
||||
var/hud_type = "rev"
|
||||
var/datum/team/revolution/rev_team
|
||||
|
||||
/datum/antagonist/rev/can_be_owned(datum/mind/new_owner)
|
||||
. = ..()
|
||||
if(.)
|
||||
if(new_owner.assigned_role in GLOB.command_positions)
|
||||
return FALSE
|
||||
if(new_owner.unconvertable)
|
||||
return FALSE
|
||||
if(new_owner.current && new_owner.current.isloyal())
|
||||
return FALSE
|
||||
|
||||
/datum/antagonist/rev/apply_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/M = mob_override || owner.current
|
||||
update_rev_icons_added(M)
|
||||
|
||||
/datum/antagonist/rev/remove_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/M = mob_override || owner.current
|
||||
update_rev_icons_removed(M)
|
||||
|
||||
/datum/antagonist/rev/proc/equip_rev()
|
||||
return
|
||||
|
||||
/datum/antagonist/rev/on_gain()
|
||||
. = ..()
|
||||
create_objectives()
|
||||
equip_rev()
|
||||
owner.current.log_message("<font color='red'>Has been converted to the revolution!</font>", INDIVIDUAL_ATTACK_LOG)
|
||||
|
||||
/datum/antagonist/rev/on_removal()
|
||||
remove_objectives()
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/rev/greet()
|
||||
to_chat(owner, "<span class='userdanger'>You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!</span>")
|
||||
owner.announce_objectives()
|
||||
|
||||
/datum/antagonist/rev/create_team(datum/team/revolution/new_team)
|
||||
if(!new_team)
|
||||
//For now only one revolution at a time
|
||||
for(var/datum/antagonist/rev/head/H in GLOB.antagonists)
|
||||
if(!H.owner)
|
||||
continue
|
||||
if(H.rev_team)
|
||||
rev_team = H.rev_team
|
||||
return
|
||||
rev_team = new /datum/team/revolution
|
||||
rev_team.update_objectives()
|
||||
rev_team.update_heads()
|
||||
return
|
||||
if(!istype(new_team))
|
||||
stack_trace("Wrong team type passed to [type] initialization.")
|
||||
rev_team = new_team
|
||||
|
||||
/datum/antagonist/rev/get_team()
|
||||
return rev_team
|
||||
|
||||
/datum/antagonist/rev/proc/create_objectives()
|
||||
owner.objectives |= rev_team.objectives
|
||||
|
||||
/datum/antagonist/rev/proc/remove_objectives()
|
||||
owner.objectives -= rev_team.objectives
|
||||
|
||||
//Bump up to head_rev
|
||||
/datum/antagonist/rev/proc/promote()
|
||||
var/old_team = rev_team
|
||||
var/datum/mind/old_owner = owner
|
||||
silent = TRUE
|
||||
owner.remove_antag_datum(/datum/antagonist/rev)
|
||||
var/datum/antagonist/rev/head/new_revhead = new()
|
||||
new_revhead.silent = TRUE
|
||||
old_owner.add_antag_datum(new_revhead,old_team)
|
||||
new_revhead.silent = FALSE
|
||||
to_chat(old_owner, "<span class='userdanger'>You have proved your devotion to revolution! You are a head revolutionary now!</span>")
|
||||
|
||||
/datum/antagonist/rev/get_admin_commands()
|
||||
. = ..()
|
||||
.["Promote"] = CALLBACK(src,.proc/admin_promote)
|
||||
|
||||
/datum/antagonist/rev/proc/admin_promote(mob/admin)
|
||||
var/datum/mind/O = owner
|
||||
promote()
|
||||
message_admins("[key_name_admin(admin)] has head-rev'ed [O].")
|
||||
log_admin("[key_name(admin)] has head-rev'ed [O].")
|
||||
|
||||
/datum/antagonist/rev/head/admin_add(datum/mind/new_owner,mob/admin)
|
||||
give_flash = TRUE
|
||||
give_hud = TRUE
|
||||
remove_clumsy = TRUE
|
||||
new_owner.add_antag_datum(src)
|
||||
message_admins("[key_name_admin(admin)] has head-rev'ed [new_owner.current].")
|
||||
log_admin("[key_name(admin)] has head-rev'ed [new_owner.current].")
|
||||
to_chat(new_owner.current, "<span class='userdanger'>You are a member of the revolutionaries' leadership now!</span>")
|
||||
|
||||
/datum/antagonist/rev/head/get_admin_commands()
|
||||
. = ..()
|
||||
. -= "Promote"
|
||||
.["Take flash"] = CALLBACK(src,.proc/admin_take_flash)
|
||||
.["Give flash"] = CALLBACK(src,.proc/admin_give_flash)
|
||||
.["Repair flash"] = CALLBACK(src,.proc/admin_repair_flash)
|
||||
.["Demote"] = CALLBACK(src,.proc/admin_demote)
|
||||
|
||||
/datum/antagonist/rev/head/proc/admin_take_flash(mob/admin)
|
||||
var/list/L = owner.current.get_contents()
|
||||
var/obj/item/device/assembly/flash/flash = locate() in L
|
||||
if (!flash)
|
||||
to_chat(admin, "<span class='danger'>Deleting flash failed!</span>")
|
||||
return
|
||||
qdel(flash)
|
||||
|
||||
/datum/antagonist/rev/head/proc/admin_give_flash(mob/admin)
|
||||
//This is probably overkill but making these impact state annoys me
|
||||
var/old_give_flash = give_flash
|
||||
var/old_give_hud = give_hud
|
||||
var/old_remove_clumsy = remove_clumsy
|
||||
give_flash = TRUE
|
||||
give_hud = FALSE
|
||||
remove_clumsy = FALSE
|
||||
equip_rev()
|
||||
give_flash = old_give_flash
|
||||
give_hud = old_give_hud
|
||||
remove_clumsy = old_remove_clumsy
|
||||
|
||||
/datum/antagonist/rev/head/proc/admin_repair_flash(mob/admin)
|
||||
var/list/L = owner.current.get_contents()
|
||||
var/obj/item/device/assembly/flash/flash = locate() in L
|
||||
if (!flash)
|
||||
to_chat(admin, "<span class='danger'>Repairing flash failed!</span>")
|
||||
else
|
||||
flash.crit_fail = 0
|
||||
flash.update_icon()
|
||||
|
||||
/datum/antagonist/rev/head/proc/admin_demote(datum/mind/target,mob/user)
|
||||
message_admins("[key_name_admin(user)] has demoted [owner.current] from head revolutionary.")
|
||||
log_admin("[key_name(user)] has demoted [owner.current] from head revolutionary.")
|
||||
demote()
|
||||
|
||||
/datum/antagonist/rev/head
|
||||
name = "Head Revolutionary"
|
||||
hud_type = "rev_head"
|
||||
var/remove_clumsy = FALSE
|
||||
var/give_flash = FALSE
|
||||
var/give_hud = TRUE
|
||||
|
||||
/datum/antagonist/rev/head/antag_listing_name()
|
||||
return ..() + "(Leader)"
|
||||
|
||||
/datum/antagonist/rev/proc/update_rev_icons_added(mob/living/M)
|
||||
var/datum/atom_hud/antag/revhud = GLOB.huds[ANTAG_HUD_REV]
|
||||
revhud.join_hud(M)
|
||||
set_antag_hud(M,hud_type)
|
||||
|
||||
/datum/antagonist/rev/proc/update_rev_icons_removed(mob/living/M)
|
||||
var/datum/atom_hud/antag/revhud = GLOB.huds[ANTAG_HUD_REV]
|
||||
revhud.leave_hud(M)
|
||||
set_antag_hud(M, null)
|
||||
|
||||
/datum/antagonist/rev/proc/can_be_converted(mob/living/candidate)
|
||||
if(!candidate.mind)
|
||||
return FALSE
|
||||
if(!can_be_owned(candidate.mind))
|
||||
return FALSE
|
||||
var/mob/living/carbon/C = candidate //Check to see if the potential rev is implanted
|
||||
if(!istype(C)) //Can't convert simple animals
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/rev/proc/add_revolutionary(datum/mind/rev_mind,stun = TRUE)
|
||||
if(!can_be_converted(rev_mind.current))
|
||||
return FALSE
|
||||
if(stun)
|
||||
if(iscarbon(rev_mind.current))
|
||||
var/mob/living/carbon/carbon_mob = rev_mind.current
|
||||
carbon_mob.silent = max(carbon_mob.silent, 5)
|
||||
carbon_mob.flash_act(1, 1)
|
||||
rev_mind.current.Stun(100)
|
||||
rev_mind.add_antag_datum(/datum/antagonist/rev,rev_team)
|
||||
rev_mind.special_role = ROLE_REV
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/rev/head/proc/demote()
|
||||
var/datum/mind/old_owner = owner
|
||||
var/old_team = rev_team
|
||||
silent = TRUE
|
||||
owner.remove_antag_datum(/datum/antagonist/rev/head)
|
||||
var/datum/antagonist/rev/new_rev = new /datum/antagonist/rev()
|
||||
new_rev.silent = TRUE
|
||||
old_owner.add_antag_datum(new_rev,old_team)
|
||||
new_rev.silent = FALSE
|
||||
to_chat(old_owner, "<span class='userdanger'>Revolution has been disappointed of your leader traits! You are a regular revolutionary now!</span>")
|
||||
|
||||
/datum/antagonist/rev/farewell()
|
||||
if(ishuman(owner.current))
|
||||
owner.current.visible_message("<span class='deconversion_message'>[owner.current] looks like they just remembered their real allegiance!</span>", null, null, null, owner.current)
|
||||
to_chat(owner, "<span class='userdanger'>You are no longer a brainwashed revolutionary! Your memory is hazy from the time you were a rebel...the only thing you remember is the name of the one who brainwashed you...</span>")
|
||||
else if(issilicon(owner.current))
|
||||
owner.current.visible_message("<span class='deconversion_message'>The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it.</span>", null, null, null, owner.current)
|
||||
to_chat(owner, "<span class='userdanger'>The frame's firmware detects and deletes your neural reprogramming! You remember nothing but the name of the one who flashed you.</span>")
|
||||
|
||||
/datum/antagonist/rev/proc/remove_revolutionary(borged, deconverter)
|
||||
log_attack("[owner.current] (Key: [key_name(owner.current)]) has been deconverted from the revolution by [deconverter] (Key: [key_name(deconverter)])!")
|
||||
if(borged)
|
||||
message_admins("[ADMIN_LOOKUPFLW(owner.current)] has been borged while being a [name]")
|
||||
owner.special_role = null
|
||||
if(iscarbon(owner.current))
|
||||
var/mob/living/carbon/C = owner.current
|
||||
C.Unconscious(100)
|
||||
owner.remove_antag_datum(type)
|
||||
|
||||
/datum/antagonist/rev/head/remove_revolutionary(borged,deconverter)
|
||||
if(!borged)
|
||||
return
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/rev/head/equip_rev()
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
if(!istype(H))
|
||||
return
|
||||
|
||||
if(remove_clumsy && owner.assigned_role == "Clown")
|
||||
to_chat(owner, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
|
||||
H.dna.remove_mutation(CLOWNMUT)
|
||||
|
||||
if(give_flash)
|
||||
var/obj/item/device/assembly/flash/T = new(H)
|
||||
var/list/slots = list (
|
||||
"backpack" = slot_in_backpack,
|
||||
"left pocket" = slot_l_store,
|
||||
"right pocket" = slot_r_store
|
||||
)
|
||||
var/where = H.equip_in_one_of_slots(T, slots)
|
||||
if (!where)
|
||||
to_chat(H, "The Syndicate were unfortunately unable to get you a flash.")
|
||||
else
|
||||
to_chat(H, "The flash in your [where] will help you to persuade the crew to join your cause.")
|
||||
|
||||
if(give_hud)
|
||||
var/obj/item/organ/cyberimp/eyes/hud/security/syndicate/S = new(H)
|
||||
S.Insert(H, special = FALSE, drop_if_replaced = FALSE)
|
||||
to_chat(H, "Your eyes have been implanted with a cybernetic security HUD which will help you keep track of who is mindshield-implanted, and therefore unable to be recruited.")
|
||||
|
||||
/datum/team/revolution
|
||||
name = "Revolution"
|
||||
var/max_headrevs = 3
|
||||
|
||||
/datum/team/revolution/proc/update_objectives(initial = FALSE)
|
||||
var/untracked_heads = SSjob.get_all_heads()
|
||||
for(var/datum/objective/mutiny/O in objectives)
|
||||
untracked_heads -= O.target
|
||||
for(var/datum/mind/M in untracked_heads)
|
||||
var/datum/objective/mutiny/new_target = new()
|
||||
new_target.team = src
|
||||
new_target.target = M
|
||||
new_target.update_explanation_text()
|
||||
objectives += new_target
|
||||
for(var/datum/mind/M in members)
|
||||
M.objectives |= objectives
|
||||
|
||||
addtimer(CALLBACK(src,.proc/update_objectives),HEAD_UPDATE_PERIOD,TIMER_UNIQUE)
|
||||
|
||||
/datum/team/revolution/proc/head_revolutionaries()
|
||||
. = list()
|
||||
for(var/datum/mind/M in members)
|
||||
if(M.has_antag_datum(/datum/antagonist/rev/head))
|
||||
. += M
|
||||
|
||||
/datum/team/revolution/proc/update_heads()
|
||||
if(SSticker.HasRoundStarted())
|
||||
var/list/datum/mind/head_revolutionaries = head_revolutionaries()
|
||||
var/list/datum/mind/heads = SSjob.get_all_heads()
|
||||
var/list/sec = SSjob.get_all_sec()
|
||||
|
||||
if(head_revolutionaries.len < max_headrevs && head_revolutionaries.len < round(heads.len - ((8 - sec.len) / 3)))
|
||||
var/list/datum/mind/non_heads = members - head_revolutionaries
|
||||
var/list/datum/mind/promotable = list()
|
||||
for(var/datum/mind/khrushchev in non_heads)
|
||||
if(khrushchev.current && !khrushchev.current.incapacitated() && !khrushchev.current.restrained() && khrushchev.current.client && khrushchev.current.stat != DEAD)
|
||||
if(ROLE_REV in khrushchev.current.client.prefs.be_special)
|
||||
promotable += khrushchev
|
||||
if(promotable.len)
|
||||
var/datum/mind/new_leader = pick(promotable)
|
||||
var/datum/antagonist/rev/rev = new_leader.has_antag_datum(/datum/antagonist/rev)
|
||||
rev.promote()
|
||||
|
||||
addtimer(CALLBACK(src,.proc/update_heads),HEAD_UPDATE_PERIOD,TIMER_UNIQUE)
|
||||
|
||||
|
||||
/datum/team/revolution/roundend_report()
|
||||
if(!members.len)
|
||||
return
|
||||
|
||||
var/list/result = list()
|
||||
|
||||
result += "<div class='panel redborder'>"
|
||||
|
||||
var/num_revs = 0
|
||||
var/num_survivors = 0
|
||||
for(var/mob/living/carbon/survivor in GLOB.alive_mob_list)
|
||||
if(survivor.ckey)
|
||||
num_survivors++
|
||||
if(survivor.mind)
|
||||
if(is_revolutionary(survivor))
|
||||
num_revs++
|
||||
if(num_survivors)
|
||||
result += "Command's Approval Rating: <B>[100 - round((num_revs/num_survivors)*100, 0.1)]%</B><br>"
|
||||
|
||||
|
||||
var/list/targets = list()
|
||||
var/list/datum/mind/headrevs = get_antagonists(/datum/antagonist/rev/head)
|
||||
var/list/datum/mind/revs = get_antagonists(/datum/antagonist/rev,TRUE)
|
||||
if(headrevs.len)
|
||||
var/list/headrev_part = list()
|
||||
headrev_part += "<span class='header'>The head revolutionaries were:</span>"
|
||||
headrev_part += printplayerlist(headrevs,TRUE)
|
||||
result += headrev_part.Join("<br>")
|
||||
|
||||
if(revs.len)
|
||||
var/list/rev_part = list()
|
||||
rev_part += "<span class='header'>The revolutionaries were:</span>"
|
||||
rev_part += printplayerlist(revs,TRUE)
|
||||
result += rev_part.Join("<br>")
|
||||
|
||||
var/list/heads = SSjob.get_all_heads()
|
||||
if(heads.len)
|
||||
var/head_text = "<span class='header'>The heads of staff were:</span>"
|
||||
head_text += "<ul class='playerlist'>"
|
||||
for(var/datum/mind/head in heads)
|
||||
var/target = (head in targets)
|
||||
head_text += "<li>"
|
||||
if(target)
|
||||
head_text += "<span class='redtext'>Target</span>"
|
||||
head_text += "[printplayer(head, 1)]</li>"
|
||||
head_text += "</ul><br>"
|
||||
result += head_text
|
||||
|
||||
result += "</div>"
|
||||
|
||||
return result.Join()
|
||||
|
||||
/datum/team/revolution/antag_listing_entry()
|
||||
var/common_part = ..()
|
||||
var/heads_report = "<b>Heads of Staff</b><br>"
|
||||
heads_report += "<table cellspacing=5>"
|
||||
for(var/datum/mind/N in SSjob.get_living_heads())
|
||||
var/mob/M = N.current
|
||||
if(M)
|
||||
heads_report += "<tr><td><a href='?_src_=holder;[HrefToken()];adminplayeropts=[REF(M)]'>[M.real_name]</a>[M.client ? "" : " <i>(No Client)</i>"][M.stat == DEAD ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
|
||||
heads_report += "<td><A href='?priv_msg=[M.ckey]'>PM</A></td>"
|
||||
heads_report += "<td><A href='?_src_=holder;[HrefToken()];adminplayerobservefollow=[REF(M)]'>FLW</a></td>"
|
||||
var/turf/mob_loc = get_turf(M)
|
||||
heads_report += "<td>[mob_loc.loc]</td></tr>"
|
||||
else
|
||||
heads_report += "<tr><td><a href='?_src_=vars;[HrefToken()];Vars=[REF(N)]'>[N.name]([N.key])</a><i>Head body destroyed!</i></td>"
|
||||
heads_report += "<td><A href='?priv_msg=[N.key]'>PM</A></td></tr>"
|
||||
heads_report += "</table>"
|
||||
return common_part + heads_report
|
||||
|
||||
/datum/team/revolution/is_gamemode_hero()
|
||||
return SSticker.mode.name == "revolution"
|
||||
@@ -1,339 +0,0 @@
|
||||
#define APPRENTICE_DESTRUCTION "destruction"
|
||||
#define APPRENTICE_BLUESPACE "bluespace"
|
||||
#define APPRENTICE_ROBELESS "robeless"
|
||||
#define APPRENTICE_HEALING "healing"
|
||||
|
||||
/datum/antagonist/wizard
|
||||
name = "Space Wizard"
|
||||
roundend_category = "wizards/witches"
|
||||
antagpanel_category = "Wizard"
|
||||
job_rank = ROLE_WIZARD
|
||||
var/give_objectives = TRUE
|
||||
var/strip = TRUE //strip before equipping
|
||||
var/allow_rename = TRUE
|
||||
var/hud_version = "wizard"
|
||||
var/datum/team/wizard/wiz_team //Only created if wizard summons apprentices
|
||||
var/move_to_lair = TRUE
|
||||
var/outfit_type = /datum/outfit/wizard
|
||||
var/wiz_age = WIZARD_AGE_MIN /* Wizards by nature cannot be too young. */
|
||||
|
||||
/datum/antagonist/wizard/on_gain()
|
||||
register()
|
||||
if(give_objectives)
|
||||
create_objectives()
|
||||
equip_wizard()
|
||||
if(move_to_lair)
|
||||
send_to_lair()
|
||||
. = ..()
|
||||
if(allow_rename)
|
||||
rename_wizard()
|
||||
|
||||
/datum/antagonist/wizard/proc/register()
|
||||
SSticker.mode.wizards |= owner
|
||||
|
||||
/datum/antagonist/wizard/proc/unregister()
|
||||
SSticker.mode.wizards -= src
|
||||
|
||||
/datum/antagonist/wizard/create_team(datum/team/wizard/new_team)
|
||||
if(!new_team)
|
||||
return
|
||||
if(!istype(new_team))
|
||||
stack_trace("Wrong team type passed to [type] initialization.")
|
||||
wiz_team = new_team
|
||||
|
||||
/datum/antagonist/wizard/get_team()
|
||||
return wiz_team
|
||||
|
||||
/datum/team/wizard
|
||||
name = "wizard team"
|
||||
var/datum/antagonist/wizard/master_wizard
|
||||
|
||||
/datum/antagonist/wizard/proc/create_wiz_team()
|
||||
wiz_team = new(owner)
|
||||
wiz_team.name = "[owner.current.real_name] team"
|
||||
wiz_team.master_wizard = src
|
||||
update_wiz_icons_added(owner.current)
|
||||
|
||||
/datum/antagonist/wizard/proc/send_to_lair()
|
||||
if(!owner || !owner.current)
|
||||
return
|
||||
if(!GLOB.wizardstart.len)
|
||||
SSjob.SendToLateJoin(owner.current)
|
||||
to_chat(owner, "HOT INSERTION, GO GO GO")
|
||||
owner.current.forceMove(pick(GLOB.wizardstart))
|
||||
|
||||
/datum/antagonist/wizard/proc/create_objectives()
|
||||
switch(rand(1,100))
|
||||
if(1 to 30)
|
||||
var/datum/objective/assassinate/kill_objective = new
|
||||
kill_objective.owner = owner
|
||||
kill_objective.find_target()
|
||||
objectives += kill_objective
|
||||
|
||||
if (!(locate(/datum/objective/escape) in owner.objectives))
|
||||
var/datum/objective/escape/escape_objective = new
|
||||
escape_objective.owner = owner
|
||||
objectives += escape_objective
|
||||
|
||||
if(31 to 60)
|
||||
var/datum/objective/steal/steal_objective = new
|
||||
steal_objective.owner = owner
|
||||
steal_objective.find_target()
|
||||
objectives += steal_objective
|
||||
|
||||
if (!(locate(/datum/objective/escape) in owner.objectives))
|
||||
var/datum/objective/escape/escape_objective = new
|
||||
escape_objective.owner = owner
|
||||
objectives += escape_objective
|
||||
|
||||
if(61 to 85)
|
||||
var/datum/objective/assassinate/kill_objective = new
|
||||
kill_objective.owner = owner
|
||||
kill_objective.find_target()
|
||||
objectives += kill_objective
|
||||
|
||||
var/datum/objective/steal/steal_objective = new
|
||||
steal_objective.owner = owner
|
||||
steal_objective.find_target()
|
||||
objectives += steal_objective
|
||||
|
||||
if (!(locate(/datum/objective/survive) in owner.objectives))
|
||||
var/datum/objective/survive/survive_objective = new
|
||||
survive_objective.owner = owner
|
||||
objectives += survive_objective
|
||||
|
||||
else
|
||||
if (!(locate(/datum/objective/hijack) in owner.objectives))
|
||||
var/datum/objective/hijack/hijack_objective = new
|
||||
hijack_objective.owner = owner
|
||||
objectives += hijack_objective
|
||||
|
||||
for(var/datum/objective/O in objectives)
|
||||
owner.objectives += O
|
||||
|
||||
/datum/antagonist/wizard/on_removal()
|
||||
unregister()
|
||||
for(var/objective in objectives)
|
||||
owner.objectives -= objective
|
||||
owner.RemoveAllSpells() // TODO keep track which spells are wizard spells which innate stuff
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/wizard/proc/equip_wizard()
|
||||
if(!owner)
|
||||
return
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
if(!istype(H))
|
||||
return
|
||||
if(strip)
|
||||
H.delete_equipment()
|
||||
//Wizards are human by default. Use the mirror if you want something else.
|
||||
H.set_species(/datum/species/human)
|
||||
if(H.age < wiz_age)
|
||||
H.age = wiz_age
|
||||
H.equipOutfit(outfit_type)
|
||||
|
||||
/datum/antagonist/wizard/greet()
|
||||
to_chat(owner, "<span class='boldannounce'>You are the Space Wizard!</span>")
|
||||
to_chat(owner, "<B>The Space Wizards Federation has given you the following tasks:</B>")
|
||||
owner.announce_objectives()
|
||||
to_chat(owner, "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.")
|
||||
to_chat(owner, "The spellbook is bound to you, and others cannot use it.")
|
||||
to_chat(owner, "In your pockets you will find a teleport scroll. Use it as needed.")
|
||||
to_chat(owner,"<B>Remember:</B> do not forget to prepare your spells.")
|
||||
|
||||
/datum/antagonist/wizard/farewell()
|
||||
to_chat(owner, "<span class='userdanger'>You have been brainwashed! You are no longer a wizard!</span>")
|
||||
|
||||
/datum/antagonist/wizard/proc/rename_wizard()
|
||||
set waitfor = FALSE
|
||||
|
||||
var/wizard_name_first = pick(GLOB.wizard_first)
|
||||
var/wizard_name_second = pick(GLOB.wizard_second)
|
||||
var/randomname = "[wizard_name_first] [wizard_name_second]"
|
||||
var/mob/living/wiz_mob = owner.current
|
||||
var/newname = copytext(sanitize(input(wiz_mob, "You are the [name]. Would you like to change your name to something else?", "Name change", randomname) as null|text),1,MAX_NAME_LEN)
|
||||
|
||||
if (!newname)
|
||||
newname = randomname
|
||||
|
||||
wiz_mob.fully_replace_character_name(wiz_mob.real_name, newname)
|
||||
|
||||
/datum/antagonist/wizard/apply_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/M = mob_override || owner.current
|
||||
update_wiz_icons_added(M, wiz_team ? TRUE : FALSE) //Don't bother showing the icon if you're solo wizard
|
||||
M.faction |= ROLE_WIZARD
|
||||
|
||||
/datum/antagonist/wizard/remove_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/M = mob_override || owner.current
|
||||
update_wiz_icons_removed(M)
|
||||
M.faction -= ROLE_WIZARD
|
||||
|
||||
|
||||
/datum/antagonist/wizard/get_admin_commands()
|
||||
. = ..()
|
||||
.["Send to Lair"] = CALLBACK(src,.proc/admin_send_to_lair)
|
||||
|
||||
/datum/antagonist/wizard/proc/admin_send_to_lair(mob/admin)
|
||||
owner.current.forceMove(pick(GLOB.wizardstart))
|
||||
|
||||
/datum/antagonist/wizard/apprentice
|
||||
name = "Wizard Apprentice"
|
||||
hud_version = "apprentice"
|
||||
var/datum/mind/master
|
||||
var/school = APPRENTICE_DESTRUCTION
|
||||
outfit_type = /datum/outfit/wizard/apprentice
|
||||
wiz_age = APPRENTICE_AGE_MIN
|
||||
|
||||
/datum/antagonist/wizard/apprentice/greet()
|
||||
to_chat(owner, "<B>You are [master.current.real_name]'s apprentice! You are bound by magic contract to follow their orders and help them in accomplishing their goals.")
|
||||
owner.announce_objectives()
|
||||
|
||||
/datum/antagonist/wizard/apprentice/register()
|
||||
SSticker.mode.apprentices |= owner
|
||||
|
||||
/datum/antagonist/wizard/apprentice/unregister()
|
||||
SSticker.mode.apprentices -= owner
|
||||
|
||||
/datum/antagonist/wizard/apprentice/equip_wizard()
|
||||
. = ..()
|
||||
if(!owner)
|
||||
return
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
if(!istype(H))
|
||||
return
|
||||
switch(school)
|
||||
if(APPRENTICE_DESTRUCTION)
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball(null))
|
||||
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned powerful, destructive spells. You are able to cast magic missile and fireball.")
|
||||
if(APPRENTICE_BLUESPACE)
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/area_teleport/teleport(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(null))
|
||||
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned reality bending mobility spells. You are able to cast teleport and ethereal jaunt.")
|
||||
if(APPRENTICE_HEALING)
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/charge(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/forcewall(null))
|
||||
H.put_in_hands(new /obj/item/gun/magic/staff/healing(H))
|
||||
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned livesaving survival spells. You are able to cast charge and forcewall.")
|
||||
if(APPRENTICE_ROBELESS)
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/mind_transfer(null))
|
||||
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned stealthy, robeless spells. You are able to cast knock and mindswap.")
|
||||
|
||||
/datum/antagonist/wizard/apprentice/create_objectives()
|
||||
var/datum/objective/protect/new_objective = new /datum/objective/protect
|
||||
new_objective.owner = owner
|
||||
new_objective.target = master
|
||||
new_objective.explanation_text = "Protect [master.current.real_name], the wizard."
|
||||
owner.objectives += new_objective
|
||||
objectives += new_objective
|
||||
|
||||
//Random event wizard
|
||||
/datum/antagonist/wizard/apprentice/imposter
|
||||
name = "Wizard Imposter"
|
||||
allow_rename = FALSE
|
||||
move_to_lair = FALSE
|
||||
|
||||
/datum/antagonist/wizard/apprentice/imposter/greet()
|
||||
to_chat(owner, "<B>You are an imposter! Trick and confuse the crew to misdirect malice from your handsome original!</B>")
|
||||
owner.announce_objectives()
|
||||
|
||||
/datum/antagonist/wizard/apprentice/imposter/equip_wizard()
|
||||
var/mob/living/carbon/human/master_mob = master.current
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
if(!istype(master_mob) || !istype(H))
|
||||
return
|
||||
if(master_mob.ears)
|
||||
H.equip_to_slot_or_del(new master_mob.ears.type, slot_ears)
|
||||
if(master_mob.w_uniform)
|
||||
H.equip_to_slot_or_del(new master_mob.w_uniform.type, slot_w_uniform)
|
||||
if(master_mob.shoes)
|
||||
H.equip_to_slot_or_del(new master_mob.shoes.type, slot_shoes)
|
||||
if(master_mob.wear_suit)
|
||||
H.equip_to_slot_or_del(new master_mob.wear_suit.type, slot_wear_suit)
|
||||
if(master_mob.head)
|
||||
H.equip_to_slot_or_del(new master_mob.head.type, slot_head)
|
||||
if(master_mob.back)
|
||||
H.equip_to_slot_or_del(new master_mob.back.type, slot_back)
|
||||
|
||||
//Operation: Fuck off and scare people
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/area_teleport/teleport(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/turf_teleport/blink(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(null))
|
||||
|
||||
/datum/antagonist/wizard/proc/update_wiz_icons_added(mob/living/wiz,join = TRUE)
|
||||
var/datum/atom_hud/antag/wizhud = GLOB.huds[ANTAG_HUD_WIZ]
|
||||
wizhud.join_hud(wiz)
|
||||
set_antag_hud(wiz, hud_version)
|
||||
|
||||
/datum/antagonist/wizard/proc/update_wiz_icons_removed(mob/living/wiz)
|
||||
var/datum/atom_hud/antag/wizhud = GLOB.huds[ANTAG_HUD_WIZ]
|
||||
wizhud.leave_hud(wiz)
|
||||
set_antag_hud(wiz, null)
|
||||
|
||||
|
||||
/datum/antagonist/wizard/academy
|
||||
name = "Academy Teacher"
|
||||
outfit_type = /datum/outfit/wizard/academy
|
||||
|
||||
/datum/antagonist/wizard/academy/equip_wizard()
|
||||
. = ..()
|
||||
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt)
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile)
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/aimed/fireball)
|
||||
|
||||
var/mob/living/M = owner.current
|
||||
if(!istype(M))
|
||||
return
|
||||
|
||||
var/obj/item/implant/exile/Implant = new/obj/item/implant/exile(M)
|
||||
Implant.implant(M)
|
||||
|
||||
/datum/antagonist/wizard/academy/create_objectives()
|
||||
var/datum/objective/new_objective = new("Protect Wizard Academy from the intruders")
|
||||
new_objective.owner = owner
|
||||
owner.objectives += new_objective
|
||||
objectives += new_objective
|
||||
|
||||
//Solo wizard report
|
||||
/datum/antagonist/wizard/roundend_report()
|
||||
var/list/parts = list()
|
||||
|
||||
parts += printplayer(owner)
|
||||
|
||||
var/count = 1
|
||||
var/wizardwin = 1
|
||||
for(var/datum/objective/objective in objectives)
|
||||
if(objective.check_completion())
|
||||
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'>Success!</span>"
|
||||
else
|
||||
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
|
||||
wizardwin = 0
|
||||
count++
|
||||
|
||||
if(wizardwin)
|
||||
parts += "<span class='greentext'>The wizard was successful!</span>"
|
||||
else
|
||||
parts += "<span class='redtext'>The wizard has failed!</span>"
|
||||
|
||||
if(owner.spell_list.len>0)
|
||||
parts += "<B>[owner.name] used the following spells: </B>"
|
||||
var/list/spell_names = list()
|
||||
for(var/obj/effect/proc_holder/spell/S in owner.spell_list)
|
||||
spell_names += S.name
|
||||
parts += spell_names.Join(", ")
|
||||
|
||||
return parts.Join("<br>")
|
||||
|
||||
//Wizard with apprentices report
|
||||
/datum/team/wizard/roundend_report()
|
||||
var/list/parts = list()
|
||||
|
||||
parts += "<span class='header'>Wizards/witches of [master_wizard.owner.name] team were:</span>"
|
||||
parts += master_wizard.roundend_report()
|
||||
parts += " "
|
||||
parts += "<span class='header'>[master_wizard.owner.name] apprentices were:</span>"
|
||||
parts += printplayerlist(members - master_wizard.owner)
|
||||
|
||||
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
|
||||
@@ -1,10 +0,0 @@
|
||||
/datum/votablemap
|
||||
var/name = ""
|
||||
var/friendlyname = ""
|
||||
var/minusers = 0
|
||||
var/maxusers = 0
|
||||
var/voteweight = 1
|
||||
|
||||
/datum/votablemap/New(name)
|
||||
src.name = name
|
||||
src.friendlyname = name
|
||||
@@ -1,57 +0,0 @@
|
||||
////////////Syndicate Cortical Borer
|
||||
obj/item/antag_spawner/syndi_borer
|
||||
name = "syndicate brain-slug container"
|
||||
desc = "Releases a modified cortical borer to assist the user."
|
||||
icon = 'icons/obj/device.dmi' //Temporary? Doesn't really look like a container for xenofauna... but IDK what else could work.
|
||||
icon_state = "locator"
|
||||
var/polling = FALSE
|
||||
|
||||
obj/item/antag_spawner/syndi_borer/spawn_antag(client/C, turf/T, mob/owner)
|
||||
var/mob/living/simple_animal/borer/syndi_borer/B = new /mob/living/simple_animal/borer/syndi_borer(T)
|
||||
|
||||
B.key = C.key
|
||||
if (owner)
|
||||
B.owner = owner
|
||||
B.faction = B.faction | owner.faction.Copy()
|
||||
|
||||
B.mind.assigned_role = B.name
|
||||
B.mind.special_role = B.name
|
||||
var/datum/objective/syndi_borer/new_objective
|
||||
new_objective = new /datum/objective/syndi_borer
|
||||
new_objective.owner = B.mind
|
||||
new_objective.target = owner.mind
|
||||
new_objective.explanation_text = "You are a modified cortical borer. You obey [owner.real_name] and must assist them in completing their objectives."
|
||||
B.mind.objectives += new_objective
|
||||
|
||||
to_chat(B, "<B>You are awake at last! Seek out whoever released you and aid them as best you can!</B>")
|
||||
if(new_objective)
|
||||
to_chat(B, "<B>Objective #[1]</B>: [new_objective.explanation_text]")
|
||||
|
||||
/obj/item/antag_spawner/syndi_borer/proc/check_usability(mob/user)
|
||||
if(used)
|
||||
to_chat(user, "<span class='warning'>[src] appears to be empty!</span>")
|
||||
return 0
|
||||
if(polling == TRUE)
|
||||
to_chat(user, "<span class='warning'>[src] is busy activating!</span>")
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/obj/item/antag_spawner/syndi_borer/attack_self(mob/user)
|
||||
if(!(check_usability(user)))
|
||||
return
|
||||
polling = TRUE
|
||||
var/list/borer_candidates = pollCandidatesForMob("Do you want to play as a syndicate cortical borer?", ROLE_BORER, null, ROLE_BORER, 150, src)
|
||||
if(borer_candidates.len)
|
||||
polling = FALSE
|
||||
if(!(check_usability(user)))
|
||||
return
|
||||
used = 1
|
||||
var/mob/dead/observer/theghost = pick(borer_candidates)
|
||||
spawn_antag(theghost.client, get_turf(src), user)
|
||||
var/datum/effect_system/spark_spread/S = new /datum/effect_system/spark_spread
|
||||
S.set_up(4, 1, src)
|
||||
S.start()
|
||||
qdel(src)
|
||||
else
|
||||
polling = FALSE
|
||||
to_chat(user, "<span class='warning'>Unable to connect to release specimen. Please wait and try again later or use the container on your uplink to get your points refunded.</span>")
|
||||
@@ -1,106 +0,0 @@
|
||||
#define MIN_LATE_TARGET_TIME 600 //lower bound of re-rolled timer, 1 min
|
||||
#define MAX_LATE_TARGET_TIME 6000 //upper bound of re-rolled timer, 10 min
|
||||
#define LATE_TARGET_HIT_CHANCE 70 //How often would the find_target succeed, otherwise it re-rolls later and tries again.
|
||||
//Hit chance is here to avoid people checking github and then hovering around new arrivals within the max minute range every round.
|
||||
|
||||
/datum/objective/assassinate/late
|
||||
martyr_compatible = FALSE
|
||||
|
||||
|
||||
/datum/objective/assassinate/late/find_target()
|
||||
var/list/possible_targets = list()
|
||||
for(var/mob/M in GLOB.latejoiners)
|
||||
var/datum/mind/possible_target = M.mind
|
||||
if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != 2) && is_unique_objective(possible_target))
|
||||
possible_targets += possible_target
|
||||
if(possible_targets.len > 0 && prob(LATE_TARGET_HIT_CHANCE))
|
||||
target = pick(possible_targets)
|
||||
martyr_compatible = TRUE //Might never matter, but I guess if an admin gives another random objective, this should now be compatible
|
||||
update_explanation_text()
|
||||
|
||||
message_admins("[target] has been selected as the assassination target of [owner].")
|
||||
log_game("[target] has been selected as the assassination target of [owner].")
|
||||
|
||||
to_chat(owner, "<span class='italics'>You hear a crackling noise in your ears, as a one-way syndicate message plays:</span>")
|
||||
to_chat(owner, "<span class='userdanger'><font size=5>You target has been located. To succeed, find and eliminate [target], the [!target_role_type ? target.assigned_role : target.special_role].</font></span>")
|
||||
return target
|
||||
else
|
||||
update_explanation_text()
|
||||
addtimer(CALLBACK(src, .proc/find_target),rand(MIN_LATE_TARGET_TIME, MAX_LATE_TARGET_TIME))
|
||||
return null
|
||||
|
||||
/datum/objective/assassinate/late/find_target_by_role(role, role_type=0, invert=0)
|
||||
var/list/possible_targets = list()
|
||||
for(var/mob/M in GLOB.latejoiners)
|
||||
var/datum/mind/possible_target = M.mind
|
||||
if((possible_target != owner) && ishuman(possible_target.current))
|
||||
var/is_role = 0
|
||||
if(role_type)
|
||||
if(possible_target.special_role == role)
|
||||
is_role++
|
||||
else
|
||||
if(possible_target.assigned_role == role)
|
||||
is_role++
|
||||
|
||||
if(invert)
|
||||
if(is_role)
|
||||
continue
|
||||
possible_targets += possible_target
|
||||
//break
|
||||
else if(is_role)
|
||||
possible_targets += possible_target
|
||||
//break
|
||||
if(possible_targets && prob(LATE_TARGET_HIT_CHANCE))
|
||||
target = pick(possible_targets)
|
||||
update_explanation_text()
|
||||
|
||||
message_admins("[target] has been selected as the assassination target of [owner].")
|
||||
log_game("[target] has been selected as the assassination target of [owner].")
|
||||
|
||||
to_chat(owner, "<span class='italics'>You hear a crackling noise in your ears, as a one-way syndicate message plays:</span>")
|
||||
to_chat(owner, "<span class='userdanger'><font size=5>You target has been located. To succeed, find and eliminate [target], the [!target_role_type ? target.assigned_role : target.special_role].</font></span>")
|
||||
else
|
||||
update_explanation_text()
|
||||
addtimer(CALLBACK(src, .proc/find_target_by_role, role, role_type, invert),rand(MIN_LATE_TARGET_TIME, MAX_LATE_TARGET_TIME))
|
||||
|
||||
|
||||
|
||||
/datum/objective/assassinate/late/check_completion()
|
||||
if(target && target.current) //If target WAS assigned
|
||||
if(target.current.stat == DEAD || issilicon(target.current) || isbrain(target.current) || target.current.z > 6 || !target.current.ckey) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite
|
||||
return TRUE
|
||||
return FALSE
|
||||
else //If no target was ever given
|
||||
if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current))
|
||||
return FALSE
|
||||
if(!is_special_character(owner.current))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/objective/assassinate/late/update_explanation_text()
|
||||
//..()
|
||||
if(target && target.current)
|
||||
explanation_text = "Assassinate [target.name], the [!target_role_type ? target.assigned_role : target.special_role]."
|
||||
else
|
||||
explanation_text = "Stay alive until your target arrives on the station, you will be notified when the target has been identified."
|
||||
|
||||
|
||||
|
||||
//BORER STUFF
|
||||
//Because borers didn't use to have objectives
|
||||
/datum/objective/normal_borer //Default objective, should technically never be used unmodified but CAN work unmodified.
|
||||
explanation_text = "You must escape with at least one borer with host on the shuttle."
|
||||
target_amount = 1
|
||||
martyr_compatible = 0
|
||||
|
||||
/datum/objective/normal_borer/check_completion()
|
||||
var/total_borer_hosts = 0
|
||||
for(var/mob/living/carbon/C in GLOB.mob_list)
|
||||
var/mob/living/simple_animal/borer/D = C.has_brain_worms()
|
||||
var/turf/location = get_turf(C)
|
||||
if(is_centcom_level(location.z) && D && D.stat != DEAD)
|
||||
total_borer_hosts++
|
||||
if(target_amount <= total_borer_hosts)
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
@@ -1,769 +0,0 @@
|
||||
/datum/action/innate/cult/blood_magic //Blood magic handles the creation of blood spells (formerly talismans)
|
||||
name = "Prepare Blood Magic"
|
||||
button_icon_state = "carve"
|
||||
desc = "Prepare blood magic by carving runes into your flesh. This rite is most effective with an <b>empowering rune</b>"
|
||||
var/list/spells = list()
|
||||
var/channeling = FALSE
|
||||
|
||||
/datum/action/innate/cult/blood_magic/Grant()
|
||||
..()
|
||||
button.screen_loc = "6:-29,4:-2"
|
||||
button.moved = "6:-29,4:-2"
|
||||
button.locked = TRUE
|
||||
|
||||
/datum/action/innate/cult/blood_magic/Remove()
|
||||
for(var/X in spells)
|
||||
qdel(X)
|
||||
..()
|
||||
|
||||
/datum/action/innate/cult/blood_magic/IsAvailable()
|
||||
if(!iscultist(owner))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/action/innate/cult/blood_magic/proc/Positioning()
|
||||
for(var/datum/action/innate/cult/blood_spell/B in spells)
|
||||
var/pos = -29+spells.Find(B)*31
|
||||
B.button.screen_loc = "6:[pos],4:-2"
|
||||
B.button.moved = B.button.screen_loc
|
||||
B.button.locked = TRUE
|
||||
|
||||
/datum/action/innate/cult/blood_magic/Activate()
|
||||
var/rune = FALSE
|
||||
var/limit = RUNELESS_MAX_BLOODCHARGE
|
||||
for(var/obj/effect/rune/empower/R in range(1, owner))
|
||||
rune = TRUE
|
||||
break
|
||||
if(rune)
|
||||
limit = MAX_BLOODCHARGE
|
||||
if(spells.len >= limit)
|
||||
if(rune)
|
||||
to_chat(owner, "<span class='cultitalic'>Your body has reached its limit, you cannot store more than [MAX_BLOODCHARGE] spells at once. <b>Pick a spell to nullify.</b></span>")
|
||||
else
|
||||
to_chat(owner, "<span class='cultitalic'>Your body has reached its limit, <b><u>you cannot have more than [RUNELESS_MAX_BLOODCHARGE] spells at once without an empowering rune! Pick a spell to nullify.</b></u></span>")
|
||||
var/nullify_spell = input(owner, "Choose a spell to remove.", "Current Spells") as null|anything in spells
|
||||
if(nullify_spell)
|
||||
qdel(nullify_spell)
|
||||
return
|
||||
var/entered_spell_name
|
||||
var/datum/action/innate/cult/blood_spell/BS
|
||||
var/list/possible_spells = list()
|
||||
for(var/I in subtypesof(/datum/action/innate/cult/blood_spell))
|
||||
var/datum/action/innate/cult/blood_spell/J = I
|
||||
var/cult_name = initial(J.name)
|
||||
possible_spells[cult_name] = J
|
||||
possible_spells += "(REMOVE SPELL)"
|
||||
entered_spell_name = input(owner, "Pick a blood spell to prepare...", "Spell Choices") as null|anything in possible_spells
|
||||
if(entered_spell_name == "(REMOVE SPELL)")
|
||||
var/nullify_spell = input(owner, "Choose a spell to remove.", "Current Spells") as null|anything in spells
|
||||
if(nullify_spell)
|
||||
qdel(nullify_spell)
|
||||
return
|
||||
BS = possible_spells[entered_spell_name]
|
||||
if(QDELETED(src) || owner.incapacitated() || !BS)
|
||||
return
|
||||
to_chat(owner,"<span class='warning'>You begin to carve unnatural symbols into your flesh!</span>")
|
||||
SEND_SOUND(owner, sound('sound/weapons/slice.ogg',0,1,10))
|
||||
if(!channeling)
|
||||
channeling = TRUE
|
||||
else
|
||||
to_chat(owner, "<span class='cultitalic'>You are already invoking blood magic!")
|
||||
return
|
||||
if(do_after(owner, 100 - rune*65, target = owner))
|
||||
if(ishuman(owner))
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.bleed(30 - rune*25)
|
||||
var/datum/action/innate/cult/blood_spell/new_spell = new BS(owner)
|
||||
new_spell.Grant(owner, src)
|
||||
spells += new_spell
|
||||
Positioning()
|
||||
to_chat(owner, "<span class='warning'>Your wounds glows with power, you have prepared a [new_spell.name] invocation!</span>")
|
||||
channeling = FALSE
|
||||
|
||||
/datum/action/innate/cult/blood_spell //The next generation of talismans
|
||||
name = "Blood Magic"
|
||||
button_icon_state = "telerune"
|
||||
desc = "Fear the Old Blood."
|
||||
var/charges = 1
|
||||
var/magic_path = null
|
||||
var/obj/item/melee/blood_magic/hand_magic
|
||||
var/datum/action/innate/cult/blood_magic/all_magic
|
||||
var/base_desc //To allow for updating tooltips
|
||||
var/invocation
|
||||
var/health_cost = 0
|
||||
|
||||
/datum/action/innate/cult/blood_spell/Grant(mob/living/owner, datum/action/innate/cult/blood_magic/BM)
|
||||
if(health_cost)
|
||||
desc += "<br>Deals <u>[health_cost] damage</u> to your arm per use."
|
||||
base_desc = desc
|
||||
desc += "<br><b><u>Has [charges] use\s remaining</u></b>."
|
||||
all_magic = BM
|
||||
..()
|
||||
|
||||
/datum/action/innate/cult/blood_spell/Remove()
|
||||
if(all_magic)
|
||||
all_magic.spells -= src
|
||||
if(hand_magic)
|
||||
qdel(hand_magic)
|
||||
hand_magic = null
|
||||
..()
|
||||
|
||||
/datum/action/innate/cult/blood_spell/IsAvailable()
|
||||
if(!iscultist(owner) || owner.incapacitated() || !charges)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/action/innate/cult/blood_spell/Activate()
|
||||
if(magic_path) //If this spell flows from the hand
|
||||
if(!hand_magic)
|
||||
hand_magic = new magic_path(owner, src)
|
||||
if(!owner.put_in_hands(hand_magic))
|
||||
qdel(hand_magic)
|
||||
hand_magic = null
|
||||
to_chat(owner, "<span class='warning'>You have no empty hand for invoking blood magic!</span>")
|
||||
return
|
||||
to_chat(owner, "<span class='notice'>Your old wounds glow again as you invoke the [name].</span>")
|
||||
return
|
||||
if(hand_magic)
|
||||
qdel(hand_magic)
|
||||
hand_magic = null
|
||||
to_chat(owner, "<span class='warning'>You snuff out the spell with your hand, saving its power for another time.</span>")
|
||||
|
||||
|
||||
//Cult Blood Spells
|
||||
/datum/action/innate/cult/blood_spell/stun
|
||||
name = "Stun"
|
||||
desc = "A potent spell that will stun and mute victims upon contact."
|
||||
button_icon_state = "hand"
|
||||
magic_path = "/obj/item/melee/blood_magic/stun"
|
||||
health_cost = 10
|
||||
|
||||
/datum/action/innate/cult/blood_spell/teleport
|
||||
name = "Teleport"
|
||||
desc = "A useful spell that teleport cultists to a chosen destination on contact."
|
||||
button_icon_state = "tele"
|
||||
magic_path = "/obj/item/melee/blood_magic/teleport"
|
||||
health_cost = 7
|
||||
|
||||
/datum/action/innate/cult/blood_spell/emp
|
||||
name = "Electromagnetic Pulse"
|
||||
desc = "A large spell that immediately disables all electronics in the area."
|
||||
button_icon_state = "emp"
|
||||
health_cost = 10
|
||||
invocation = "Ta'gh fara'qha fel d'amar det!"
|
||||
|
||||
/datum/action/innate/cult/blood_spell/emp/Activate()
|
||||
owner.visible_message("<span class='warning'>[owner]'s hand flashes a bright blue!</span>", \
|
||||
"<span class='cultitalic'>You speak the cursed words, emitting an EMP blast from your hand.</span>")
|
||||
empulse(owner, 3, 6)
|
||||
owner.whisper(invocation, language = /datum/language/common)
|
||||
charges--
|
||||
if(charges<=0)
|
||||
qdel(src)
|
||||
|
||||
/datum/action/innate/cult/blood_spell/shackles
|
||||
name = "Shadow Shackles"
|
||||
desc = "A stealthy spell that will handcuff and temporarily silence your victim."
|
||||
button_icon_state = "cuff"
|
||||
charges = 4
|
||||
magic_path = "/obj/item/melee/blood_magic/shackles"
|
||||
|
||||
/datum/action/innate/cult/blood_spell/construction
|
||||
name = "Twisted Construction"
|
||||
desc = "<u>A sinister spell used to convert:</u><br>Plasteel into runed metal<br>25 metal into a construct shell<br>Cyborgs directly into constructs<br>Cyborg shells into construct shells<br>Airlocks into runed airlocks (harm intent)"
|
||||
button_icon_state = "transmute"
|
||||
magic_path = "/obj/item/melee/blood_magic/construction"
|
||||
|
||||
/datum/action/innate/cult/blood_spell/equipment
|
||||
name = "Summon Equipment"
|
||||
desc = "A crucial spell that enables you to summon either a ritual dagger or combat gear including armored robes, the nar'sien bola, and an eldritch longsword."
|
||||
button_icon_state = "equip"
|
||||
magic_path = "/obj/item/melee/blood_magic/armor"
|
||||
|
||||
/datum/action/innate/cult/blood_spell/equipment/Activate()
|
||||
var/choice = alert(owner,"Choose your equipment type",,"Combat Equipment","Ritual Dagger","Cancel")
|
||||
if(choice == "Ritual Dagger")
|
||||
var/turf/T = get_turf(owner)
|
||||
owner.visible_message("<span class='warning'>[owner]'s hand glows red for a moment.</span>", \
|
||||
"<span class='cultitalic'>Red light begins to shimmer and take form within your hand!</span>")
|
||||
var/obj/O = new /obj/item/melee/cultblade/dagger(T)
|
||||
if(owner.put_in_hands(O))
|
||||
to_chat(owner, "<span class='warning'>A ritual dagger appears in your hand!</span>")
|
||||
else
|
||||
owner.visible_message("<span class='warning'>A ritual dagger appears at [owner]'s feet!</span>", \
|
||||
"<span class='cultitalic'>A ritual dagger materializes at your feet.</span>")
|
||||
SEND_SOUND(owner, sound('sound/effects/magic.ogg',0,1,25))
|
||||
charges--
|
||||
desc = base_desc
|
||||
desc += "<br><b><u>Has [charges] use\s remaining</u></b>."
|
||||
if(charges<=0)
|
||||
qdel(src)
|
||||
else if(choice == "Combat Equipment")
|
||||
..()
|
||||
|
||||
/datum/action/innate/cult/blood_spell/horror
|
||||
name = "Hallucinations"
|
||||
desc = "A <u>ranged yet stealthy</u> spell that will break the mind of the victim with nightmarish hallucinations."
|
||||
button_icon_state = "horror"
|
||||
var/obj/effect/proc_holder/horror/PH
|
||||
charges = 4
|
||||
|
||||
/datum/action/innate/cult/blood_spell/horror/New()
|
||||
PH = new()
|
||||
PH.attached_action = src
|
||||
..()
|
||||
|
||||
/datum/action/innate/cult/blood_spell/horror/Destroy()
|
||||
var/obj/effect/proc_holder/horror/destroy = PH
|
||||
. = ..()
|
||||
if(destroy && !QDELETED(destroy))
|
||||
QDEL_NULL(destroy)
|
||||
|
||||
/datum/action/innate/cult/blood_spell/horror/Activate()
|
||||
PH.toggle(owner) //the important bit
|
||||
return TRUE
|
||||
|
||||
/obj/effect/proc_holder/horror
|
||||
active = FALSE
|
||||
ranged_mousepointer = 'icons/effects/cult_target.dmi'
|
||||
var/datum/action/innate/cult/blood_spell/attached_action
|
||||
|
||||
/obj/effect/proc_holder/horror/Destroy()
|
||||
var/datum/action/innate/cult/blood_spell/AA = attached_action
|
||||
. = ..()
|
||||
if(AA && !QDELETED(AA))
|
||||
QDEL_NULL(AA)
|
||||
|
||||
/obj/effect/proc_holder/horror/proc/toggle(mob/user)
|
||||
if(active)
|
||||
remove_ranged_ability("<span class='cult'>You dispel the magic...</span>")
|
||||
else
|
||||
add_ranged_ability(user, "<span class='cult'>You prepare to horrify a target...</span>")
|
||||
|
||||
/obj/effect/proc_holder/horror/InterceptClickOn(mob/living/caller, params, atom/target)
|
||||
if(..())
|
||||
return
|
||||
if(ranged_ability_user.incapacitated() || !iscultist(caller))
|
||||
remove_ranged_ability()
|
||||
return
|
||||
var/turf/T = get_turf(ranged_ability_user)
|
||||
if(!isturf(T))
|
||||
return FALSE
|
||||
if(target in view(7, get_turf(ranged_ability_user)))
|
||||
if(!ishuman(target) || iscultist(target))
|
||||
return
|
||||
var/mob/living/carbon/human/H = target
|
||||
H.hallucination = max(H.hallucination, 240)
|
||||
SEND_SOUND(ranged_ability_user, sound('sound/effects/ghost.ogg',0,1,50))
|
||||
var/image/C = image('icons/effects/cult_effects.dmi',H,"bloodsparkles", ABOVE_MOB_LAYER)
|
||||
add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/cult, "cult_apoc", C, FALSE)
|
||||
addtimer(CALLBACK(H,/atom/.proc/remove_alt_appearance,"cult_apoc",TRUE), 2400, TIMER_OVERRIDE|TIMER_UNIQUE)
|
||||
to_chat(ranged_ability_user,"<span class='cult'><b>[H] has been cursed with living nightmares!</b></span>")
|
||||
attached_action.charges--
|
||||
attached_action.desc = attached_action.base_desc
|
||||
attached_action.desc += "<br><b><u>Has [attached_action.charges] use\s remaining</u></b>."
|
||||
attached_action.UpdateButtonIcon()
|
||||
if(attached_action.charges <= 0)
|
||||
remove_mousepointer(ranged_ability_user.client)
|
||||
remove_ranged_ability("<span class='cult'>You have exhausted the spell's power!</span>")
|
||||
qdel(src)
|
||||
|
||||
/datum/action/innate/cult/blood_spell/veiling
|
||||
name = "Conceal Presence"
|
||||
desc = "A multi-function spell that alternates between hiding and revealing nearby cult runes, structures, turf, and airlocks."
|
||||
invocation = "Kla'atu barada nikt'o!"
|
||||
button_icon_state = "gone"
|
||||
charges = 10
|
||||
var/revealing = FALSE //if it reveals or not
|
||||
|
||||
/datum/action/innate/cult/blood_spell/veiling/Activate()
|
||||
if(!revealing)
|
||||
owner.visible_message("<span class='warning'>Thin grey dust falls from [owner]'s hand!</span>", \
|
||||
"<span class='cultitalic'>You invoke the veiling spell, hiding nearby runes.</span>")
|
||||
charges--
|
||||
SEND_SOUND(owner, sound('sound/magic/smoke.ogg',0,1,25))
|
||||
owner.whisper(invocation, language = /datum/language/common)
|
||||
for(var/obj/effect/rune/R in range(5,owner))
|
||||
R.conceal()
|
||||
for(var/obj/structure/destructible/cult/S in range(5,owner))
|
||||
S.conceal()
|
||||
for(var/turf/open/floor/engine/cult/T in range(5,owner))
|
||||
T.realappearance.alpha = 0
|
||||
for(var/obj/machinery/door/airlock/cult/AL in range(5, owner))
|
||||
AL.conceal()
|
||||
revealing = TRUE
|
||||
name = "Reveal Runes"
|
||||
button_icon_state = "back"
|
||||
else
|
||||
owner.visible_message("<span class='warning'>A flash of light shines from [owner]'s hand!</span>", \
|
||||
"<span class='cultitalic'>You invoke the counterspell, revealing nearby runes.</span>")
|
||||
charges--
|
||||
owner.whisper(invocation, language = /datum/language/common)
|
||||
SEND_SOUND(owner, sound('sound/magic/enter_blood.ogg',0,1,25))
|
||||
for(var/obj/effect/rune/R in range(7,owner)) //More range in case you weren't standing in exactly the same spot
|
||||
R.reveal()
|
||||
for(var/obj/structure/destructible/cult/S in range(6,owner))
|
||||
S.reveal()
|
||||
for(var/turf/open/floor/engine/cult/T in range(6,owner))
|
||||
T.realappearance.alpha = initial(T.realappearance.alpha)
|
||||
for(var/obj/machinery/door/airlock/cult/AL in range(6, owner))
|
||||
AL.reveal()
|
||||
revealing = FALSE
|
||||
name = "Conceal Runes"
|
||||
button_icon_state = "gone"
|
||||
if(charges<= 0)
|
||||
qdel(src)
|
||||
desc = base_desc
|
||||
desc += "<br><b><u>Has [charges] use\s remaining</u></b>."
|
||||
UpdateButtonIcon()
|
||||
|
||||
/datum/action/innate/cult/blood_spell/manipulation
|
||||
name = "Blood Rites"
|
||||
desc = "A complex spell that allows you to gather blood and use it for healing or other powerful spells."
|
||||
invocation = "Fel'th Dol Ab'orod!"
|
||||
button_icon_state = "manip"
|
||||
charges = 5
|
||||
magic_path = "/obj/item/melee/blood_magic/manipulator"
|
||||
|
||||
|
||||
// The "magic hand" items
|
||||
/obj/item/melee/blood_magic
|
||||
name = "\improper magical aura"
|
||||
desc = "Sinister looking aura that distorts the flow of reality around it."
|
||||
icon = 'icons/obj/items_and_weapons.dmi'
|
||||
icon_state = "disintegrate"
|
||||
item_state = null
|
||||
flags_1 = ABSTRACT_1 | NODROP_1 | DROPDEL_1
|
||||
w_class = WEIGHT_CLASS_HUGE
|
||||
throwforce = 0
|
||||
throw_range = 0
|
||||
throw_speed = 0
|
||||
var/invocation
|
||||
var/uses = 1
|
||||
var/health_cost = 0 //The amount of health taken from the user when invoking the spell
|
||||
var/datum/action/innate/cult/blood_spell/source
|
||||
|
||||
/obj/item/melee/blood_magic/New(loc, spell)
|
||||
source = spell
|
||||
uses = source.charges
|
||||
health_cost = source.health_cost
|
||||
..()
|
||||
|
||||
/obj/item/melee/blood_magic/Destroy()
|
||||
if(!QDELETED(source))
|
||||
if(uses <= 0)
|
||||
source.hand_magic = null
|
||||
qdel(source)
|
||||
source = null
|
||||
else
|
||||
source.hand_magic = null
|
||||
source.charges = uses
|
||||
source.desc = source.base_desc
|
||||
source.desc += "<br><b><u>Has [uses] use\s remaining</u></b>."
|
||||
source.UpdateButtonIcon()
|
||||
..()
|
||||
|
||||
/obj/item/melee/blood_magic/attack_self(mob/living/user)
|
||||
afterattack(user, user, TRUE)
|
||||
|
||||
/obj/item/melee/blood_magic/attack(mob/living/M, mob/living/carbon/user)
|
||||
if(!iscarbon(user) || !iscultist(user))
|
||||
uses = 0
|
||||
qdel(src)
|
||||
return
|
||||
add_logs(user, M, "used a cult spell on", source.name, "")
|
||||
M.lastattacker = user.real_name
|
||||
M.lastattackerckey = user.ckey
|
||||
|
||||
/obj/item/melee/blood_magic/afterattack(atom/target, mob/living/carbon/user, proximity)
|
||||
if(invocation)
|
||||
user.whisper(invocation, language = /datum/language/common)
|
||||
if(health_cost)
|
||||
if(user.active_hand_index == 1)
|
||||
user.apply_damage(health_cost, BRUTE, "l_arm")
|
||||
else
|
||||
user.apply_damage(health_cost, BRUTE, "r_arm")
|
||||
if(uses <= 0)
|
||||
qdel(src)
|
||||
else if(source)
|
||||
source.desc = source.base_desc
|
||||
source.desc += "<br><b><u>Has [uses] use\s remaining</u></b>."
|
||||
source.UpdateButtonIcon()
|
||||
|
||||
//Stun
|
||||
/obj/item/melee/blood_magic/stun
|
||||
color = "#ff0000" // red
|
||||
invocation = "Fuu ma'jin!"
|
||||
|
||||
/obj/item/melee/blood_magic/stun/afterattack(atom/target, mob/living/carbon/user, proximity)
|
||||
if(!isliving(target) || !proximity)
|
||||
return
|
||||
var/mob/living/L = target
|
||||
if(iscultist(target))
|
||||
return
|
||||
if(iscultist(user))
|
||||
user.visible_message("<span class='warning'>[user] holds up their hand, which explodes in a flash of red light!</span>", \
|
||||
"<span class='cultitalic'>You stun [L] with the spell!</span>")
|
||||
var/obj/item/nullrod/N = locate() in L
|
||||
if(N)
|
||||
target.visible_message("<span class='warning'>[L]'s holy weapon absorbs the light!</span>", \
|
||||
"<span class='userdanger'>Your holy weapon absorbs the blinding light!</span>")
|
||||
else
|
||||
L.Knockdown(180)
|
||||
L.flash_act(1,1)
|
||||
if(issilicon(target))
|
||||
var/mob/living/silicon/S = L
|
||||
S.emp_act(EMP_HEAVY)
|
||||
else if(iscarbon(target))
|
||||
var/mob/living/carbon/C = L
|
||||
C.silent += 6
|
||||
C.stuttering += 15
|
||||
C.cultslurring += 15
|
||||
C.Jitter(15)
|
||||
if(is_servant_of_ratvar(L))
|
||||
L.adjustBruteLoss(15)
|
||||
uses--
|
||||
..()
|
||||
|
||||
//Teleportation
|
||||
/obj/item/melee/blood_magic/teleport
|
||||
color = RUNE_COLOR_TELEPORT
|
||||
desc = "A potent spell that teleport cultists on contact."
|
||||
invocation = "Sas'so c'arta forbici!"
|
||||
|
||||
/obj/item/melee/blood_magic/teleport/afterattack(atom/target, mob/living/carbon/user, proximity)
|
||||
if(!iscultist(target) || !proximity)
|
||||
to_chat(user, "<span class='warning'>You can only teleport adjacent cultists with this spell!</span>")
|
||||
return
|
||||
if(iscultist(user))
|
||||
var/list/potential_runes = list()
|
||||
var/list/teleportnames = list()
|
||||
for(var/R in GLOB.teleport_runes)
|
||||
var/obj/effect/rune/teleport/T = R
|
||||
potential_runes[avoid_assoc_duplicate_keys(T.listkey, teleportnames)] = T
|
||||
|
||||
if(!potential_runes.len)
|
||||
to_chat(user, "<span class='warning'>There are no valid runes to teleport to!</span>")
|
||||
log_game("Teleport talisman failed - no other teleport runes")
|
||||
return
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
if(is_away_level(T.z))
|
||||
to_chat(user, "<span class='cultitalic'>You are not in the right dimension!</span>")
|
||||
log_game("Teleport spell failed - user in away mission")
|
||||
return
|
||||
|
||||
var/input_rune_key = input(user, "Choose a rune to teleport to.", "Rune to Teleport to") as null|anything in potential_runes //we know what key they picked
|
||||
var/obj/effect/rune/teleport/actual_selected_rune = potential_runes[input_rune_key] //what rune does that key correspond to?
|
||||
if(QDELETED(src) || !user || !user.is_holding(src) || user.incapacitated() || !actual_selected_rune || !proximity)
|
||||
return
|
||||
var/turf/dest = get_turf(actual_selected_rune)
|
||||
if(is_blocked_turf(dest, TRUE))
|
||||
to_chat(user, "<span class='warning'>The target rune is blocked. Attempting to teleport to it would be massively unwise.</span>")
|
||||
return
|
||||
uses--
|
||||
user.visible_message("<span class='warning'>Dust flows from [user]'s hand, and [user.p_they()] disappear[user.p_s()] with a sharp crack!</span>", \
|
||||
"<span class='cultitalic'>You speak the words of the talisman and find yourself somewhere else!</span>", "<i>You hear a sharp crack.</i>")
|
||||
var/mob/living/L = target
|
||||
L.forceMove(dest)
|
||||
dest.visible_message("<span class='warning'>There is a boom of outrushing air as something appears above the rune!</span>", null, "<i>You hear a boom.</i>")
|
||||
..()
|
||||
|
||||
//Shackles
|
||||
/obj/item/melee/blood_magic/shackles
|
||||
name = "Shadow Shackles"
|
||||
desc = "Allows you to bind a victim and temporarily silence them."
|
||||
invocation = "In'totum Lig'abis!"
|
||||
color = "#000000" // black
|
||||
|
||||
/obj/item/melee/blood_magic/shackles/afterattack(atom/target, mob/living/carbon/user, proximity)
|
||||
if(iscultist(user) && iscarbon(target) && proximity)
|
||||
var/mob/living/carbon/C = target
|
||||
if(C.get_num_arms() >= 2 || C.get_arm_ignore())
|
||||
CuffAttack(C, user)
|
||||
else
|
||||
user.visible_message("<span class='cultitalic'>This victim doesn't have enough arms to complete the restraint!</span>")
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/item/melee/blood_magic/shackles/proc/CuffAttack(mob/living/carbon/C, mob/living/user)
|
||||
if(!C.handcuffed)
|
||||
playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2)
|
||||
C.visible_message("<span class='danger'>[user] begins restraining [C] with dark magic!</span>", \
|
||||
"<span class='userdanger'>[user] begins shaping a dark magic around your wrists!</span>")
|
||||
if(do_mob(user, C, 30))
|
||||
if(!C.handcuffed)
|
||||
C.handcuffed = new /obj/item/restraints/handcuffs/energy/cult/used(C)
|
||||
C.update_handcuffed()
|
||||
C.silent += 5
|
||||
to_chat(user, "<span class='notice'>You shackle [C].</span>")
|
||||
add_logs(user, C, "shackled")
|
||||
uses--
|
||||
else
|
||||
to_chat(user, "<span class='warning'>[C] is already bound.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You fail to shackle [C].</span>")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>[C] is already bound.</span>")
|
||||
|
||||
|
||||
/obj/item/restraints/handcuffs/energy/cult //For the shackling spell
|
||||
name = "shadow shackles"
|
||||
desc = "Shackles that bind the wrists with sinister magic."
|
||||
trashtype = /obj/item/restraints/handcuffs/energy/used
|
||||
flags_1 = DROPDEL_1
|
||||
|
||||
/obj/item/restraints/handcuffs/energy/cult/used/dropped(mob/user)
|
||||
user.visible_message("<span class='danger'>[user]'s shackles shatter in a discharge of dark magic!</span>", \
|
||||
"<span class='userdanger'>Your [src] shatters in a discharge of dark magic!</span>")
|
||||
. = ..()
|
||||
|
||||
|
||||
//Construction: Creates a construct shell out of 25 metal sheets, or converts plasteel into runed metal
|
||||
/obj/item/melee/blood_magic/construction
|
||||
name = "Twisted Construction"
|
||||
desc = "Corrupts metal and plasteel into more sinister forms."
|
||||
invocation = "Ethra p'ni dedol!"
|
||||
color = "#000000" // black
|
||||
|
||||
/obj/item/melee/blood_magic/construction/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
if(proximity_flag && iscultist(user))
|
||||
var/turf/T = get_turf(target)
|
||||
if(istype(target, /obj/item/stack/sheet/metal))
|
||||
var/obj/item/stack/sheet/candidate = target
|
||||
if(candidate.use(50))
|
||||
uses--
|
||||
to_chat(user, "<span class='warning'>A dark cloud eminates from your hand and swirls around the metal, twisting it into a construct shell!</span>")
|
||||
new /obj/structure/constructshell(T)
|
||||
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You need 50 metal to produce a construct shell!</span>")
|
||||
else if(istype(target, /obj/item/stack/sheet/plasteel))
|
||||
var/obj/item/stack/sheet/plasteel/candidate = target
|
||||
var/quantity = min(candidate.amount, uses)
|
||||
uses -= quantity
|
||||
new /obj/item/stack/sheet/runed_metal(T,quantity)
|
||||
candidate.use(quantity)
|
||||
to_chat(user, "<span class='warning'>A dark cloud eminates from you hand and swirls around the plasteel, transforming it into runed metal!</span>")
|
||||
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
|
||||
else if(istype(target,/mob/living/silicon/robot))
|
||||
var/mob/living/silicon/robot/candidate = target
|
||||
if(candidate.mmi)
|
||||
user.visible_message("<span class='danger'>A dark cloud eminates from [user]'s hand and swirls around [candidate]!</span>")
|
||||
playsound(T, 'sound/machines/airlock_alien_prying.ogg', 80, 1)
|
||||
var/prev_color = candidate.color
|
||||
candidate.color = "black"
|
||||
if(do_after(user, 90, target = candidate))
|
||||
candidate.emp_act(EMP_HEAVY)
|
||||
var/construct_class = alert(user, "Please choose which type of construct you wish to create.",,"Juggernaut","Wraith","Artificer")
|
||||
user.visible_message("<span class='danger'>The dark cloud receedes from what was formerly [candidate], revealing a\n [construct_class]!</span>")
|
||||
switch(construct_class)
|
||||
if("Juggernaut")
|
||||
makeNewConstruct(/mob/living/simple_animal/hostile/construct/armored, candidate, user, 0, T)
|
||||
if("Wraith")
|
||||
makeNewConstruct(/mob/living/simple_animal/hostile/construct/wraith, candidate, user, 0, T)
|
||||
if("Artificer")
|
||||
makeNewConstruct(/mob/living/simple_animal/hostile/construct/builder, candidate, user, 0, T)
|
||||
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
|
||||
uses--
|
||||
candidate.mmi = null
|
||||
qdel(candidate)
|
||||
else
|
||||
candidate.color = prev_color
|
||||
else
|
||||
uses--
|
||||
to_chat(user, "<span class='warning'>A dark cloud eminates from you hand and swirls around [candidate] - twisting it into a construct shell!</span>")
|
||||
new /obj/structure/constructshell(T)
|
||||
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
|
||||
else if(istype(target,/obj/machinery/door/airlock))
|
||||
target.narsie_act()
|
||||
uses--
|
||||
user.visible_message("<span class='warning'>Black ribbons suddenly eminate from [user]'s hand and cling to the airlock - twisting and corrupting it!</span>")
|
||||
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
|
||||
else
|
||||
to_chat(user, "<span class='warning'>The spell will not work on [target]!</span>")
|
||||
..()
|
||||
|
||||
//Armor: Gives the target a basic cultist combat loadout
|
||||
/obj/item/melee/blood_magic/armor
|
||||
name = "Sinister Armaments"
|
||||
desc = "A spell that will equip the target with cultist equipment if there is a slot to equip it to."
|
||||
color = "#33cc33" // green
|
||||
|
||||
/obj/item/melee/blood_magic/armor/afterattack(atom/target, mob/living/carbon/user, proximity)
|
||||
if(iscarbon(target) && proximity)
|
||||
uses--
|
||||
var/mob/living/carbon/C = target
|
||||
C.visible_message("<span class='warning'>Otherworldly armor suddenly appears on [C]!</span>")
|
||||
C.equip_to_slot_or_del(new /obj/item/clothing/under/color/black,slot_w_uniform)
|
||||
C.equip_to_slot_or_del(new /obj/item/clothing/head/culthood/alt(user), slot_head)
|
||||
C.equip_to_slot_or_del(new /obj/item/clothing/suit/cultrobes/alt(user), slot_wear_suit)
|
||||
C.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult/alt(user), slot_shoes)
|
||||
C.equip_to_slot_or_del(new /obj/item/storage/backpack/cultpack(user), slot_back)
|
||||
if(C == user)
|
||||
qdel(src) //Clears the hands
|
||||
C.put_in_hands(new /obj/item/melee/cultblade(user))
|
||||
C.put_in_hands(new /obj/item/restraints/legcuffs/bola/cult(user))
|
||||
..()
|
||||
|
||||
/obj/item/melee/blood_magic/manipulator
|
||||
name = "Blood Rite"
|
||||
desc = "A spell that will absorb blood from anything you touch.<br>Touching cultists and constructs can heal them.<br><b>Clicking the hand will potentially let you focus the spell into something stronger.</b>"
|
||||
color = "#7D1717"
|
||||
|
||||
/obj/item/melee/blood_magic/manipulator/afterattack(atom/target, mob/living/carbon/human/user, proximity)
|
||||
if(proximity)
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/H = target
|
||||
if(NOBLOOD in H.dna.species.species_traits)
|
||||
to_chat(user,"<span class='warning'>Blood rites do not work on species with no blood!</span>")
|
||||
return
|
||||
if(iscultist(H))
|
||||
if(H.stat == DEAD)
|
||||
to_chat(user,"<span class='warning'>Only a revive rune can bring back the dead!</span>")
|
||||
return
|
||||
if(H.blood_volume < BLOOD_VOLUME_SAFE)
|
||||
var/restore_blood = BLOOD_VOLUME_SAFE - H.blood_volume
|
||||
if(uses*2 < restore_blood)
|
||||
H.blood_volume += uses*2
|
||||
to_chat(user,"<span class='danger'>You use the last of your blood rites to restore what blood you could!</span>")
|
||||
uses = 0
|
||||
return ..()
|
||||
else
|
||||
H.blood_volume = BLOOD_VOLUME_SAFE
|
||||
uses -= round(restore_blood/2)
|
||||
to_chat(user,"<span class='warning'>Your blood rites have restored [H == user ? "your" : "their"] blood to safe levels!</span>")
|
||||
var/overall_damage = H.getBruteLoss() + H.getFireLoss() + H.getToxLoss() + H.getOxyLoss()
|
||||
if(overall_damage == 0)
|
||||
to_chat(user,"<span class='cult'>That cultist doesn't require healing!</span>")
|
||||
else
|
||||
var/ratio = uses/overall_damage
|
||||
if(H == user)
|
||||
to_chat(user,"<span class='cult'><b>Your blood healing is far less efficient when used on yourself!</b></span>")
|
||||
ratio *= 0.35 // Healing is half as effective if you can't perform a full heal
|
||||
uses -= round(overall_damage) // Healing is 65% more "expensive" even if you can still perform the full heal
|
||||
if(ratio>1)
|
||||
ratio = 1
|
||||
uses -= round(overall_damage)
|
||||
H.visible_message("<span class='warning'>[H] is fully healed by [H==user ? "their":"[H]'s"]'s blood magic!</span>")
|
||||
else
|
||||
H.visible_message("<span class='warning'>[H] is partially healed by [H==user ? "their":"[H]'s"] blood magic.</span>")
|
||||
uses = 0
|
||||
ratio *= -1
|
||||
H.adjustOxyLoss((overall_damage*ratio) * (H.getOxyLoss() / overall_damage), 0)
|
||||
H.adjustToxLoss((overall_damage*ratio) * (H.getToxLoss() / overall_damage), 0)
|
||||
H.adjustFireLoss((overall_damage*ratio) * (H.getFireLoss() / overall_damage), 0)
|
||||
H.adjustBruteLoss((overall_damage*ratio) * (H.getBruteLoss() / overall_damage), 0)
|
||||
H.updatehealth()
|
||||
playsound(get_turf(H), 'sound/magic/staff_healing.ogg', 25)
|
||||
new /obj/effect/temp_visual/cult/sparks(get_turf(H))
|
||||
user.Beam(H,icon_state="sendbeam",time=15)
|
||||
else
|
||||
if(H.stat == DEAD)
|
||||
to_chat(user,"<span class='warning'>Their blood has stopped flowing, you'll have to find another way to extract it.</span>")
|
||||
return
|
||||
if(H.cultslurring)
|
||||
to_chat(user,"<span class='danger'>Their blood has been tainted by an even stronger form of blood magic, it's no use to us like this!</span>")
|
||||
return
|
||||
if(H.blood_volume > BLOOD_VOLUME_SAFE)
|
||||
H.blood_volume -= 100
|
||||
uses += 50
|
||||
user.Beam(H,icon_state="drainbeam",time=10)
|
||||
playsound(get_turf(H), 'sound/magic/enter_blood.ogg', 50)
|
||||
H.visible_message("<span class='danger'>[user] has drained some of [H]'s blood!</span>")
|
||||
to_chat(user,"<span class='cultitalic'>Your blood rite gains 50 charges from draining [H]'s blood.</span>")
|
||||
new /obj/effect/temp_visual/cult/sparks(get_turf(H))
|
||||
else
|
||||
to_chat(user,"<span class='danger'>They're missing too much blood - you cannot drain them further!</span>")
|
||||
return
|
||||
if(isconstruct(target))
|
||||
var/mob/living/simple_animal/M = target
|
||||
var/missing = M.maxHealth - M.health
|
||||
if(missing)
|
||||
if(uses > missing)
|
||||
M.adjustHealth(-missing)
|
||||
M.visible_message("<span class='warning'>[M] is fully-healed by [user]'s blood magic!</span>")
|
||||
uses -= missing
|
||||
else
|
||||
M.adjustHealth(-uses)
|
||||
M.visible_message("<span class='warning'>[M] is healed by [user]'sblood magic!</span>")
|
||||
uses = 0
|
||||
playsound(get_turf(M), 'sound/magic/staff_healing.ogg', 25)
|
||||
user.Beam(M,icon_state="sendbeam",time=10)
|
||||
if(istype(target, /obj/effect/decal/cleanable/blood))
|
||||
blood_draw(target, user)
|
||||
..()
|
||||
|
||||
/obj/item/melee/blood_magic/manipulator/proc/blood_draw(atom/target, mob/living/carbon/human/user)
|
||||
var/temp = 0
|
||||
var/turf/T = get_turf(target)
|
||||
if(T)
|
||||
for(var/obj/effect/decal/cleanable/blood/B in view(T, 2))
|
||||
if(B.blood_state == "blood")
|
||||
if(B.bloodiness == 100) //Bonus for "pristine" bloodpools, also to prevent cheese with footprint spam
|
||||
temp += 30
|
||||
else
|
||||
temp += max((B.bloodiness**2)/800,1)
|
||||
new /obj/effect/temp_visual/cult/turf/floor(get_turf(B))
|
||||
qdel(B)
|
||||
for(var/obj/effect/decal/cleanable/trail_holder/TH in view(T, 2))
|
||||
qdel(TH)
|
||||
var/obj/item/clothing/shoes/shoecheck = user.shoes
|
||||
if(shoecheck && shoecheck.bloody_shoes["blood"])
|
||||
temp += shoecheck.bloody_shoes["blood"]/20
|
||||
shoecheck.bloody_shoes["blood"] = 0
|
||||
if(temp)
|
||||
user.Beam(T,icon_state="drainbeam",time=15)
|
||||
new /obj/effect/temp_visual/cult/sparks(get_turf(user))
|
||||
playsound(T, 'sound/magic/enter_blood.ogg', 50)
|
||||
to_chat(user, "<span class='cultitalic'>Your blood rite has gained [round(temp)] charge\s from blood sources around you!</span>")
|
||||
uses += round(temp)
|
||||
|
||||
/obj/item/melee/blood_magic/manipulator/attack_self(mob/living/user)
|
||||
if(iscultist(user))
|
||||
var/list/options = list("Blood Spear (200)", "Blood Bolt Barrage (400)", "Blood Beam (600)")
|
||||
var/choice = input(user, "Choose a greater blood rite...", "Greater Blood Rites") as null|anything in options
|
||||
if(!choice)
|
||||
to_chat(user, "<span class='cultitalic'>You decide against conducting a greater blood rite.</span>")
|
||||
return
|
||||
switch(choice)
|
||||
if("Blood Spear (200)")
|
||||
if(uses < 200)
|
||||
to_chat(user, "<span class='cultitalic'>You need 200 charges to perform this rite.</span>")
|
||||
else
|
||||
uses -= 200
|
||||
var/turf/T = get_turf(user)
|
||||
qdel(src)
|
||||
var/datum/action/innate/cult/spear/S = new(user)
|
||||
var/obj/item/twohanded/cult_spear/rite = new(T)
|
||||
S.Grant(user, rite)
|
||||
rite.spear_act = S
|
||||
if(user.put_in_hands(rite))
|
||||
to_chat(user, "<span class='cultitalic'>A [rite.name] appears in your hand!</span>")
|
||||
else
|
||||
user.visible_message("<span class='warning'>A [rite.name] appears at [user]'s feet!</span>", \
|
||||
"<span class='cultitalic'>A [rite.name] materializes at your feet.</span>")
|
||||
if("Blood Bolt Barrage (400)")
|
||||
if(uses < 400)
|
||||
to_chat(user, "<span class='cultitalic'>You need 400 charges to perform this rite.</span>")
|
||||
else
|
||||
var/obj/rite = new /obj/item/gun/ballistic/shotgun/boltaction/enchanted/arcane_barrage/blood()
|
||||
uses -= 400
|
||||
qdel(src)
|
||||
if(user.put_in_hands(rite))
|
||||
to_chat(user, "<span class='cult'><b>Your hands glow with power!</b></span>")
|
||||
else
|
||||
to_chat(user, "<span class='cultitalic'>You need a free hand for this rite!</span>")
|
||||
qdel(rite)
|
||||
if("Blood Beam (600)")
|
||||
if(uses < 600)
|
||||
to_chat(user, "<span class='cultitalic'>You need 600 charges to perform this rite.</span>")
|
||||
else
|
||||
var/obj/rite = new /obj/item/blood_beam()
|
||||
uses -= 600
|
||||
qdel(src)
|
||||
if(user.put_in_hands(rite))
|
||||
to_chat(user, "<span class='cultlarge'><b>Your hands glow with POWER OVERWHELMING!!!</b></span>")
|
||||
else
|
||||
to_chat(user, "<span class='cultitalic'>You need a free hand for this rite!</span>")
|
||||
qdel(rite)
|
||||
@@ -1,306 +1,4 @@
|
||||
/obj/item/dogborg/jaws/big
|
||||
name = "combat jaws"
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state = "jaws"
|
||||
desc = "The jaws of the law."
|
||||
flags_1 = CONDUCT_1
|
||||
force = 12
|
||||
throwforce = 0
|
||||
hitsound = 'sound/weapons/bite.ogg'
|
||||
attack_verb = list("chomped", "bit", "ripped", "mauled", "enforced")
|
||||
w_class = 3
|
||||
sharpness = IS_SHARP
|
||||
|
||||
/obj/item/dogborg/jaws/small
|
||||
name = "puppy jaws"
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state = "smalljaws"
|
||||
desc = "The jaws of a small dog."
|
||||
flags_1 = CONDUCT_1
|
||||
force = 6
|
||||
throwforce = 0
|
||||
hitsound = 'sound/weapons/bite.ogg'
|
||||
attack_verb = list("nibbled", "bit", "gnawed", "chomped", "nommed")
|
||||
w_class = 3
|
||||
sharpness = IS_SHARP
|
||||
|
||||
/obj/item/dogborg/jaws/attack(atom/A, mob/living/silicon/robot/user)
|
||||
..()
|
||||
user.do_attack_animation(A, ATTACK_EFFECT_BITE)
|
||||
|
||||
/obj/item/dogborg/jaws/small/attack_self(mob/user)
|
||||
var/mob/living/silicon/robot.R = user
|
||||
if(R.emagged)
|
||||
name = "combat jaws"
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state = "jaws"
|
||||
desc = "The jaws of the law."
|
||||
flags_1 = CONDUCT_1
|
||||
force = 12
|
||||
throwforce = 0
|
||||
hitsound = 'sound/weapons/bite.ogg'
|
||||
attack_verb = list("chomped", "bit", "ripped", "mauled", "enforced")
|
||||
w_class = 3
|
||||
sharpness = IS_SHARP
|
||||
else
|
||||
name = "puppy jaws"
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state = "smalljaws"
|
||||
desc = "The jaws of a small dog."
|
||||
flags_1 = CONDUCT_1
|
||||
force = 5
|
||||
throwforce = 0
|
||||
hitsound = 'sound/weapons/bite.ogg'
|
||||
attack_verb = list("nibbled", "bit", "gnawed", "chomped", "nommed")
|
||||
w_class = 3
|
||||
sharpness = IS_SHARP
|
||||
update_icon()
|
||||
|
||||
|
||||
//Cuffs
|
||||
|
||||
/obj/item/restraints/handcuffs/cable/zipties/cyborg/dog/attack(mob/living/carbon/C, mob/user)
|
||||
if(!C.handcuffed)
|
||||
playsound(loc, 'sound/weapons/cablecuff.ogg', 60, 1, -2)
|
||||
C.visible_message("<span class='danger'>[user] is trying to put zipties on [C]!</span>", \
|
||||
"<span class='userdanger'>[user] is trying to put zipties on [C]!</span>")
|
||||
if(do_mob(user, C, 60))
|
||||
if(!C.handcuffed)
|
||||
C.handcuffed = new /obj/item/restraints/handcuffs/cable/zipties/used(C)
|
||||
C.update_inv_handcuffed(0)
|
||||
to_chat(user,"<span class='notice'>You handcuff [C].</span>")
|
||||
playsound(loc, pick('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/binsult.ogg', 'sound/voice/bcreep.ogg'), 50, 0)
|
||||
add_logs(user, C, "handcuffed")
|
||||
else
|
||||
to_chat(user,"<span class='warning'>You fail to handcuff [C]!</span>")
|
||||
|
||||
|
||||
//Boop
|
||||
|
||||
/obj/item/device/analyzer/nose
|
||||
name = "boop module"
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state = "nose"
|
||||
desc = "The BOOP module"
|
||||
flags_1 = CONDUCT_1
|
||||
force = 0
|
||||
throwforce = 0
|
||||
attack_verb = list("nuzzled", "nosed", "booped")
|
||||
w_class = 1
|
||||
|
||||
/obj/item/device/analyzer/nose/attack_self(mob/user)
|
||||
user.visible_message("[user] sniffs around the air.", "<span class='warning'>You sniff the air for gas traces.</span>")
|
||||
|
||||
var/turf/location = user.loc
|
||||
if(!istype(location))
|
||||
return
|
||||
|
||||
var/datum/gas_mixture/environment = location.return_air()
|
||||
|
||||
var/pressure = environment.return_pressure()
|
||||
var/total_moles = environment.total_moles()
|
||||
|
||||
to_chat(user, "<span class='info'><B>Results:</B></span>")
|
||||
if(abs(pressure - ONE_ATMOSPHERE) < 10)
|
||||
to_chat(user, "<span class='info'>Pressure: [round(pressure,0.1)] kPa</span>")
|
||||
else
|
||||
to_chat(user, "<span class='alert'>Pressure: [round(pressure,0.1)] kPa</span>")
|
||||
if(total_moles)
|
||||
var/list/env_gases = environment.gases
|
||||
|
||||
environment.assert_gases(arglist(GLOB.hardcoded_gases))
|
||||
var/o2_concentration = env_gases[/datum/gas/oxygen][MOLES]/total_moles
|
||||
var/n2_concentration = env_gases[/datum/gas/nitrogen][MOLES]/total_moles
|
||||
var/co2_concentration = env_gases[/datum/gas/carbon_dioxide][MOLES]/total_moles
|
||||
var/plasma_concentration = env_gases[/datum/gas/plasma][MOLES]/total_moles
|
||||
environment.garbage_collect()
|
||||
|
||||
if(abs(n2_concentration - N2STANDARD) < 20)
|
||||
to_chat(user, "<span class='info'>Nitrogen: [round(n2_concentration*100, 0.01)] %</span>")
|
||||
else
|
||||
to_chat(user, "<span class='alert'>Nitrogen: [round(n2_concentration*100, 0.01)] %</span>")
|
||||
|
||||
if(abs(o2_concentration - O2STANDARD) < 2)
|
||||
to_chat(user, "<span class='info'>Oxygen: [round(o2_concentration*100, 0.01)] %</span>")
|
||||
else
|
||||
to_chat(user, "<span class='alert'>Oxygen: [round(o2_concentration*100, 0.01)] %</span>")
|
||||
|
||||
if(co2_concentration > 0.01)
|
||||
to_chat(user, "<span class='alert'>CO2: [round(co2_concentration*100, 0.01)] %</span>")
|
||||
else
|
||||
to_chat(user, "<span class='info'>CO2: [round(co2_concentration*100, 0.01)] %</span>")
|
||||
|
||||
if(plasma_concentration > 0.005)
|
||||
to_chat(user, "<span class='alert'>Plasma: [round(plasma_concentration*100, 0.01)] %</span>")
|
||||
else
|
||||
to_chat(user, "<span class='info'>Plasma: [round(plasma_concentration*100, 0.01)] %</span>")
|
||||
|
||||
|
||||
for(var/id in env_gases)
|
||||
if(id in GLOB.hardcoded_gases)
|
||||
continue
|
||||
var/gas_concentration = env_gases[id][MOLES]/total_moles
|
||||
to_chat(user, "<span class='alert'>[env_gases[id][GAS_META][META_GAS_NAME]]: [round(gas_concentration*100, 0.01)] %</span>")
|
||||
to_chat(user, "<span class='info'>Temperature: [round(environment.temperature-T0C)] °C</span>")
|
||||
|
||||
/obj/item/device/analyzer/nose/AltClick(mob/user) //Barometer output for measuring when the next storm happens
|
||||
. = ..()
|
||||
|
||||
//Delivery
|
||||
|
||||
/obj/item/storage/bag/borgdelivery
|
||||
name = "fetching storage"
|
||||
desc = "Fetch the thing!"
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state = "dbag"
|
||||
//Can hold one big item at a time. Drops contents on unequip.(see inventory.dm)
|
||||
w_class = 5
|
||||
max_w_class = 2
|
||||
max_combined_w_class = 2
|
||||
storage_slots = 1
|
||||
collection_mode = 0
|
||||
can_hold = list() // any
|
||||
cant_hold = list(/obj/item/disk/nuclear)
|
||||
|
||||
|
||||
//Tongue stuff
|
||||
|
||||
/obj/item/soap/tongue
|
||||
name = "synthetic tongue"
|
||||
desc = "Useful for slurping mess off the floor before affectionally licking the crew members in the face."
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state = "synthtongue"
|
||||
hitsound = 'sound/effects/attackblob.ogg'
|
||||
cleanspeed = 80
|
||||
|
||||
/obj/item/soap/tongue/scrubpup
|
||||
cleanspeed = 25 //slightly faster than a mop.
|
||||
|
||||
/obj/item/soap/tongue/New()
|
||||
..()
|
||||
flags_1 |= NOBLUDGEON_1 //No more attack messages
|
||||
|
||||
/obj/item/trash/rkibble
|
||||
name = "robo kibble"
|
||||
desc = "A novelty bowl of assorted mech fabricator byproducts. Mockingly feed this to the sec-dog to help it recharge."
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state= "kibble"
|
||||
|
||||
/obj/item/soap/tongue/attack_self(mob/user)
|
||||
var/mob/living/silicon/robot.R = user
|
||||
if(R.emagged)
|
||||
name = "hacked tongue of doom"
|
||||
desc = "Your tongue has been upgraded successfully. Congratulations."
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state = "syndietongue"
|
||||
cleanspeed = 10 //(nerf'd)tator soap stat
|
||||
else
|
||||
name = "synthetic tongue"
|
||||
desc = "Useful for slurping mess off the floor before affectionally licking the crew members in the face."
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state = "synthtongue"
|
||||
cleanspeed = initial(cleanspeed)
|
||||
update_icon()
|
||||
|
||||
/obj/item/soap/tongue/afterattack(atom/target, mob/user, proximity)
|
||||
var/mob/living/silicon/robot.R = user
|
||||
if(!proximity || !check_allowed_items(target))
|
||||
return
|
||||
if(R.client && (target in R.client.screen))
|
||||
to_chat(R, "<span class='warning'>You need to take that [target.name] off before cleaning it!</span>")
|
||||
else if(is_cleanable(target))
|
||||
R.visible_message("[R] begins to lick off \the [target.name].", "<span class='warning'>You begin to lick off \the [target.name]...</span>")
|
||||
if(do_after(R, src.cleanspeed, target = target))
|
||||
if(!in_range(src, target)) //Proximity is probably old news by now, do a new check.
|
||||
return //If they moved away, you can't eat them.
|
||||
to_chat(R, "<span class='notice'>You finish licking off \the [target.name].</span>")
|
||||
qdel(target)
|
||||
R.cell.give(50)
|
||||
else if(isobj(target)) //hoo boy. danger zone man
|
||||
if(istype(target,/obj/item/trash))
|
||||
R.visible_message("[R] nibbles away at \the [target.name].", "<span class='warning'>You begin to nibble away at \the [target.name]...</span>")
|
||||
if(do_after(R, src.cleanspeed, target = target))
|
||||
if(!in_range(src, target)) //Proximity is probably old news by now, do a new check.
|
||||
return //If they moved away, you can't eat them.
|
||||
to_chat(R, "<span class='notice'>You finish off \the [target.name].</span>")
|
||||
qdel(target)
|
||||
R.cell.give(250)
|
||||
return
|
||||
if(istype(target,/obj/item/stock_parts/cell))
|
||||
R.visible_message("[R] begins cramming \the [target.name] down its throat.", "<span class='warning'>You begin cramming \the [target.name] down your throat...</span>")
|
||||
if(do_after(R, 50, target = target))
|
||||
if(!in_range(src, target)) //Proximity is probably old news by now, do a new check.
|
||||
return //If they moved away, you can't eat them.
|
||||
to_chat(R, "<span class='notice'>You finish off \the [target.name].</span>")
|
||||
var/obj/item/stock_parts/cell.C = target
|
||||
R.cell.charge = R.cell.charge + (C.charge / 3) //Instant full cell upgrades op idgaf
|
||||
qdel(target)
|
||||
return
|
||||
var/obj/item/I = target //HAHA FUCK IT, NOT LIKE WE ALREADY HAVE A SHITTON OF WAYS TO REMOVE SHIT
|
||||
if(!I.anchored && R.emagged)
|
||||
R.visible_message("[R] begins chewing up \the [target.name]. Looks like it's trying to loophole around its diet restriction!", "<span class='warning'>You begin chewing up \the [target.name]...</span>")
|
||||
if(do_after(R, 100, target = I)) //Nerf dat time yo
|
||||
if(!in_range(src, target)) //Proximity is probably old news by now, do a new check. Even emags don't make you magically eat things at range.
|
||||
return //If they moved away, you can't eat them.
|
||||
visible_message("<span class='warning'>[R] chews up \the [target.name] and cleans off the debris!</span>")
|
||||
to_chat(R, "<span class='notice'>You finish off \the [target.name].</span>")
|
||||
qdel(I)
|
||||
R.cell.give(500)
|
||||
return
|
||||
R.visible_message("[R] begins to lick \the [target.name] clean...", "<span class='notice'>You begin to lick \the [target.name] clean...</span>")
|
||||
if(do_after(R, src.cleanspeed, target = target))
|
||||
if(!in_range(src, target)) //Proximity is probably old news by now, do a new check.
|
||||
return //If they moved away, you can't clean them.
|
||||
to_chat(R,"<span class='notice'>You clean \the [target.name].</span>")
|
||||
var/obj/effect/decal/cleanable/C = locate() in target
|
||||
qdel(C)
|
||||
SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
|
||||
else if(ishuman(target))
|
||||
if(R.emagged)
|
||||
var/mob/living/L = target
|
||||
if(R.cell.charge <= 666)
|
||||
return
|
||||
L.Stun(4) // normal stunbaton is force 7 gimme a break good sir!
|
||||
L.Knockdown(80)
|
||||
L.apply_effect(STUTTER, 4)
|
||||
L.visible_message("<span class='danger'>[R] has shocked [L] with its tongue!</span>", \
|
||||
"<span class='userdanger'>[R] has shocked you with its tongue! You can feel the betrayal.</span>")
|
||||
playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
|
||||
R.cell.use(666)
|
||||
else
|
||||
R.visible_message("<span class='warning'>\the [R] affectionally licks \the [target]'s face!</span>", "<span class='notice'>You affectionally lick \the [target]'s face!</span>")
|
||||
playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1)
|
||||
return
|
||||
else if(istype(target, /obj/structure/window))
|
||||
R.visible_message("[R] begins to lick \the [target.name] clean...", "<span class='notice'>You begin to lick \the [target.name] clean...</span>")
|
||||
if(do_after(R, src.cleanspeed, target = target))
|
||||
if(!in_range(src, target)) //Proximity is probably old news by now, do a new check.
|
||||
return //If they moved away, you can't clean them.
|
||||
to_chat(R, "<span class='notice'>You clean \the [target.name].</span>")
|
||||
target.color = initial(target.color)
|
||||
else
|
||||
R.visible_message("[R] begins to lick \the [target.name] clean...", "<span class='notice'>You begin to lick \the [target.name] clean...</span>")
|
||||
if(do_after(R, src.cleanspeed, target = target))
|
||||
if(!in_range(src, target)) //Proximity is probably old news by now, do a new check.
|
||||
return //If they moved away, you can't clean them.
|
||||
to_chat(R, "<span class='notice'>You clean \the [target.name].</span>")
|
||||
var/obj/effect/decal/cleanable/C = locate() in target
|
||||
qdel(C)
|
||||
SendSignal(COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
|
||||
return
|
||||
|
||||
|
||||
|
||||
//Defibs
|
||||
|
||||
/obj/item/twohanded/shockpaddles/cyborg/hound
|
||||
name = "defibrillator paws"
|
||||
desc = "MediHound specific shock paws."
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state = "defibpaddles0"
|
||||
item_state = "defibpaddles0"
|
||||
|
||||
//Sleeper
|
||||
// Dogborg Sleeper units
|
||||
|
||||
/obj/item/device/dogborg/sleeper
|
||||
name = "hound sleeper"
|
||||
@@ -363,7 +61,7 @@
|
||||
return
|
||||
if(!(target.client && target.client.prefs && target.client.prefs.toggles && (target.client.prefs.toggles & MEDIHOUND_SLEEPER)))
|
||||
to_chat(user, "<span class='warning'>This person is incompatible with our equipment.</span>")
|
||||
return
|
||||
return
|
||||
if(target.buckled)
|
||||
to_chat(user, "<span class='warning'>The user is buckled and can not be put into your [src.name].</span>")
|
||||
return
|
||||
@@ -799,101 +497,4 @@
|
||||
user.visible_message("<span class='warning'>[hound.name]'s garbage processor groans lightly as [trashman] slips inside.</span>", "<span class='notice'>Your garbage compactor groans lightly as [trashman] slips inside.</span>")
|
||||
playsound(hound, 'sound/effects/bin_close.ogg', 80, 1)
|
||||
return
|
||||
return
|
||||
|
||||
|
||||
// Pounce stuff for K-9
|
||||
|
||||
/obj/item/dogborg/pounce
|
||||
name = "pounce"
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state = "pounce"
|
||||
desc = "Leap at your target to momentarily stun them."
|
||||
force = 0
|
||||
throwforce = 0
|
||||
|
||||
/obj/item/dogborg/pounce/New()
|
||||
..()
|
||||
flags_1 |= NOBLUDGEON_1
|
||||
|
||||
/mob/living/silicon/robot
|
||||
var/leaping = 0
|
||||
var/pounce_cooldown = 0
|
||||
var/pounce_cooldown_time = 50 //Nearly doubled, u happy?
|
||||
var/pounce_spoolup = 3
|
||||
var/leap_at
|
||||
var/disabler
|
||||
var/laser
|
||||
var/sleeper_g
|
||||
var/sleeper_r
|
||||
|
||||
#define MAX_K9_LEAP_DIST 4 //because something's definitely borked the pounce functioning from a distance.
|
||||
|
||||
/obj/item/dogborg/pounce/afterattack(atom/A, mob/user)
|
||||
var/mob/living/silicon/robot/R = user
|
||||
if(R && !R.pounce_cooldown)
|
||||
R.pounce_cooldown = !R.pounce_cooldown
|
||||
to_chat(R, "<span class ='warning'>Your targeting systems lock on to [A]...</span>")
|
||||
addtimer(CALLBACK(R, /mob/living/silicon/robot.proc/leap_at, A), R.pounce_spoolup)
|
||||
spawn(R.pounce_cooldown_time)
|
||||
R.pounce_cooldown = !R.pounce_cooldown
|
||||
else if(R && R.pounce_cooldown)
|
||||
to_chat(R, "<span class='danger'>Your leg actuators are still recharging!</span>")
|
||||
|
||||
/mob/living/silicon/robot/proc/leap_at(atom/A)
|
||||
if(leaping || stat || buckled || lying)
|
||||
return
|
||||
|
||||
if(!has_gravity(src) || !has_gravity(A))
|
||||
to_chat(src,"<span class='danger'>It is unsafe to leap without gravity!</span>")
|
||||
//It's also extremely buggy visually, so it's balance+bugfix
|
||||
return
|
||||
|
||||
if(cell.charge <= 500)
|
||||
to_chat(src,"<span class='danger'>Insufficent reserves for jump actuators!</span>")
|
||||
return
|
||||
|
||||
else
|
||||
leaping = 1
|
||||
weather_immunities += "lava"
|
||||
pixel_y = 10
|
||||
update_icons()
|
||||
throw_at(A, MAX_K9_LEAP_DIST, 1, spin=0, diagonals_first = 1)
|
||||
cell.use(500) //Doubled the energy consumption
|
||||
weather_immunities -= "lava"
|
||||
|
||||
/mob/living/silicon/robot/throw_impact(atom/A)
|
||||
|
||||
if(!leaping)
|
||||
return ..()
|
||||
|
||||
if(A)
|
||||
if(isliving(A))
|
||||
var/mob/living/L = A
|
||||
var/blocked = 0
|
||||
if(ishuman(A))
|
||||
var/mob/living/carbon/human/H = A
|
||||
if(H.check_shields(0, "the [name]", src, attack_type = LEAP_ATTACK))
|
||||
blocked = 1
|
||||
if(!blocked)
|
||||
L.visible_message("<span class ='danger'>[src] pounces on [L]!</span>", "<span class ='userdanger'>[src] pounces on you!</span>")
|
||||
L.Knockdown(45)
|
||||
playsound(src, 'sound/weapons/Egloves.ogg', 50, 1)
|
||||
sleep(2)//Runtime prevention (infinite bump() calls on hulks)
|
||||
step_towards(src,L)
|
||||
else
|
||||
Knockdown(45, 1, 1)
|
||||
|
||||
pounce_cooldown = !pounce_cooldown
|
||||
spawn(pounce_cooldown_time) //3s by default
|
||||
pounce_cooldown = !pounce_cooldown
|
||||
else if(A.density && !A.CanPass(src))
|
||||
visible_message("<span class ='danger'>[src] smashes into [A]!</span>", "<span class ='userdanger'>You smash into [A]!</span>")
|
||||
playsound(src, 'sound/items/trayhit1.ogg', 50, 1)
|
||||
Knockdown(45, 1, 1)
|
||||
|
||||
if(leaping)
|
||||
leaping = 0
|
||||
pixel_y = initial(pixel_y)
|
||||
update_icons()
|
||||
update_canmove()
|
||||
return
|
||||
@@ -1,13 +0,0 @@
|
||||
/mob/var/skincmds = list()
|
||||
/obj/proc/SkinCmd(mob/user as mob, var/data as text)
|
||||
|
||||
/proc/SkinCmdRegister(mob/user, name as text, obj/O)
|
||||
user.skincmds[name] = O
|
||||
|
||||
/mob/verb/skincmd(data as text)
|
||||
set hidden = 1
|
||||
|
||||
var/ref = copytext(data, 1, findtext(data, ";"))
|
||||
if (src.skincmds[ref] != null)
|
||||
var/obj/a = src.skincmds[ref]
|
||||
a.SkinCmd(src, copytext(data, findtext(data, ";") + 1))
|
||||
@@ -1,36 +0,0 @@
|
||||
/obj/machinery/zvent
|
||||
name = "interfloor air transfer system"
|
||||
|
||||
icon = 'icons/obj/atmospherics/components/unary_devices.dmi'
|
||||
icon_state = "vent_map"
|
||||
density = FALSE
|
||||
anchored=1
|
||||
desc = "This may be needed some day."
|
||||
|
||||
var/on = FALSE
|
||||
var/volume_rate = 800
|
||||
|
||||
/obj/machinery/zvent/New()
|
||||
..()
|
||||
SSair.atmos_machinery += src
|
||||
|
||||
/obj/machinery/zvent/Destroy()
|
||||
SSair.atmos_machinery -= src
|
||||
return ..()
|
||||
|
||||
/obj/machinery/zvent/process_atmos()
|
||||
|
||||
//all this object does, is make its turf share air with the ones above and below it, if they have a vent too.
|
||||
if(isturf(loc)) //if we're not on a valid turf, forget it
|
||||
for (var/new_z in list(-1,1)) //change this list if a fancier system of z-levels gets implemented
|
||||
var/turf/open/zturf_conn = locate(x,y,z+new_z)
|
||||
if (istype(zturf_conn))
|
||||
var/obj/machinery/zvent/zvent_conn= locate(/obj/machinery/zvent) in zturf_conn
|
||||
if (istype(zvent_conn))
|
||||
//both floors have simulated turfs, share()
|
||||
var/turf/open/myturf = loc
|
||||
var/datum/gas_mixture/conn_air = zturf_conn.air //TODO: pop culture reference
|
||||
var/datum/gas_mixture/my_air = myturf.air
|
||||
if (istype(conn_air) && istype(my_air))
|
||||
my_air.share(conn_air)
|
||||
air_update_turf()
|
||||
@@ -1,25 +0,0 @@
|
||||
/client/verb/sethotkeys(from_pref = 0 as num)
|
||||
set name = "Set Hotkeys"
|
||||
set hidden = TRUE
|
||||
set waitfor = FALSE
|
||||
set desc = "Used to set mob-specific hotkeys or load hoykey mode from preferences"
|
||||
|
||||
var/hotkey_default = "default"
|
||||
var/hotkey_macro = "hotkeys"
|
||||
var/current_setting
|
||||
|
||||
var/list/default_macros = list("default", "robot-default")
|
||||
|
||||
if(from_pref)
|
||||
current_setting = (prefs.hotkeys ? hotkey_macro : hotkey_default)
|
||||
else
|
||||
current_setting = winget(src, "mainwindow", "macro")
|
||||
|
||||
if(mob)
|
||||
hotkey_macro = mob.macro_hotkeys
|
||||
hotkey_default = mob.macro_default
|
||||
|
||||
if(current_setting in default_macros)
|
||||
winset(src, null, "mainwindow.macro=[hotkey_default] input.focus=true input.background-color=#d3b5b5")
|
||||
else
|
||||
winset(src, null, "mainwindow.macro=[hotkey_macro] mapwindow.map.focus=true input.background-color=#e0e0e0")
|
||||
@@ -1,17 +0,0 @@
|
||||
/datum/round_event_control/solar_flare
|
||||
name = "Solar Flare"
|
||||
typepath = /datum/round_event/solar_flare
|
||||
max_occurrences = 1
|
||||
|
||||
/datum/round_event/solar_flare
|
||||
|
||||
/datum/round_event/solar_flare/setup()
|
||||
startWhen = 3
|
||||
endWhen = startWhen + 1
|
||||
announceWhen = 1
|
||||
|
||||
/datum/round_event/solar_flare/announce()
|
||||
priority_announce("Incoming solar flare detected near the station. Expect power outages in all exposed areas for a short duration.", "Anomaly Alert", 'sound/effects/alert.ogg')
|
||||
|
||||
/datum/round_event/solar_flare/start()
|
||||
SSweather.run_weather("solar flare",1)
|
||||
@@ -1,111 +0,0 @@
|
||||
/obj/machinery/computer/holodeck/attack_hand(var/mob/user as mob)
|
||||
user.set_machine(src)
|
||||
|
||||
var/dat = "<h3>Current Loaded Programs</h3>"
|
||||
dat += "<a href='?src=\ref[src];loadarea=[offline_program.type]'>Power Off</a><br>"
|
||||
for(var/area/A in program_cache)
|
||||
dat += "<a href='?src=\ref[src];loadarea=[A.type]'>[A.name]</a><br>"
|
||||
if(emagged && emag_programs.len)
|
||||
dat += "<span class='warning'>SUPERVISOR ACCESS - SAFETY PROTOCOLS DISABLED - CAUTION: EMITTER ANOMALY</span><br>"
|
||||
for(var/area/A in emag_programs)
|
||||
dat += "<a href='?src=\ref[src];loadarea=[A.type]'>[A.name]</a><br>"
|
||||
|
||||
var/datum/browser/popup = new(user, "computer", name, 400, 500)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
return
|
||||
|
||||
/obj/machinery/computer/holodeck/attack_ai(var/mob/user as mob)
|
||||
var/dat = "<h3>Current Loaded Programs</h3>"
|
||||
|
||||
dat += "<a href='?src=\ref[src];loadarea=[offline_program.type]'>Power Off</a><br>"
|
||||
for(var/area/A in program_cache)
|
||||
dat += "<a href='?src=\ref[src];loadarea=[A.type]'>[A.name]</a><br>"
|
||||
|
||||
if(emag_programs.len)
|
||||
dat += "<br>"
|
||||
if(emagged)
|
||||
dat += "Safety protocol: <span class='bad'>Offline</span> <a href='?\ref[src];safety=1'>Engage</a><br>"
|
||||
for(var/area/A in emag_programs)
|
||||
dat += "<a href='?src=\ref[src];loadarea=[A.type]'>[A.name]</a><br>"
|
||||
else
|
||||
dat += "Safety protocol: <span class='good'>Online</span> <a href='?\ref[src];safety=0'>Disengage</a><br>"
|
||||
|
||||
var/datum/browser/popup = new(user, "computer", name, 400, 500)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
|
||||
|
||||
/obj/machinery/computer/holodeck/proc/load_program(var/area/A, var/force = 0, var/delay = 0)
|
||||
if(stat)
|
||||
A = offline_program
|
||||
force = 1
|
||||
delay = 0
|
||||
if(program == A)
|
||||
return
|
||||
if(world.time < (last_change + 25 + (damaged?500:0)) && !force)
|
||||
if(delay)
|
||||
sleep(25)
|
||||
else
|
||||
if(world.time < (last_change + 15))//To prevent super-spam clicking, reduced process size and annoyance -Sieve
|
||||
return
|
||||
if(get_dist(usr,src) <= 3)
|
||||
to_chat(usr, "<span class='warning'>ERROR. Recalibrating projection apparatus.</span>")
|
||||
return
|
||||
|
||||
last_change = world.time
|
||||
active = (A != offline_program)
|
||||
use_power = active ? ACTIVE_POWER_USE : IDLE_POWER_USE
|
||||
|
||||
for(var/obj/effect/holodeck_effect/HE in effects)
|
||||
HE.deactivate(src)
|
||||
|
||||
for(var/item in spawned)
|
||||
derez(item, forced=force)
|
||||
|
||||
program = A
|
||||
// note nerfing does not yet work on guns, should
|
||||
// should also remove/limit/filter reagents?
|
||||
// this is an exercise left to others I'm afraid. -Sayu
|
||||
spawned = A.copy_contents_to(linked, 1, nerf_weapons = !emagged)
|
||||
for(var/obj/machinery/M in spawned)
|
||||
M.flags_1 |= NODECONSTRUCT_1
|
||||
for(var/obj/structure/S in spawned)
|
||||
S.flags_1 |= NODECONSTRUCT_1
|
||||
effects = list()
|
||||
|
||||
spawn(30)
|
||||
var/list/added = list()
|
||||
for(var/obj/effect/holodeck_effect/HE in spawned)
|
||||
effects += HE
|
||||
spawned -= HE
|
||||
var/atom/x = HE.activate(src)
|
||||
if(istype(x) || islist(x))
|
||||
spawned += x // holocarp are not forever
|
||||
added += x
|
||||
for(var/obj/machinery/M in added)
|
||||
M.flags_1 |= NODECONSTRUCT_1
|
||||
for(var/obj/structure/S in added)
|
||||
S.flags_1 |= NODECONSTRUCT_1
|
||||
|
||||
/obj/machinery/computer/holodeck/proc/derez(var/obj/obj, var/silent = 1, var/forced = 0)
|
||||
// Emagging a machine creates an anomaly in the derez systems.
|
||||
if(obj && src.emagged && !src.stat && !forced)
|
||||
if((ismob(obj) || istype(obj.loc,/mob)) && prob(50))
|
||||
spawn(50) .(obj,silent) // may last a disturbingly long time
|
||||
return
|
||||
spawned.Remove(obj)
|
||||
|
||||
if(!obj)
|
||||
return
|
||||
var/turf/T = get_turf(obj)
|
||||
for(var/atom/movable/AM in obj.contents) // these should be derezed if they were generated
|
||||
AM.loc = T
|
||||
if(ismob(AM))
|
||||
silent = FALSE // otherwise make sure they are dropped
|
||||
|
||||
if(!silent)
|
||||
visible_message("The [obj.name] fades away!")
|
||||
qdel(obj)
|
||||
@@ -98,7 +98,7 @@ proc/get_top_level_mob(var/mob/S)
|
||||
return FALSE
|
||||
|
||||
user.log_message(message, INDIVIDUAL_EMOTE_LOG)
|
||||
message = "<b>[user]</b> " + message
|
||||
message = "<b>[user]</b> " + "<i>[message]</i>"
|
||||
|
||||
for(var/mob/M in GLOB.dead_mob_list)
|
||||
if(!M.client || isnewplayer(M))
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
display_name = "Basic Bluespace Theory"
|
||||
description = "Basic studies into the mysterious alternate dimension known as bluespace."
|
||||
prereq_ids = list("base")
|
||||
design_ids = list("beacon", "xenobioconsole")
|
||||
design_ids = list("beacon") //CIT CHANGE removed xenobioconsole from here.
|
||||
research_cost = 2500
|
||||
export_price = 5000
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
display_name = "Applied Bluespace Research"
|
||||
description = "Using bluespace to make things faster and better."
|
||||
prereq_ids = list("bluespace_basic", "engineering")
|
||||
design_ids = list("bs_rped","minerbag_holding", "telesci_gps", "bluespacebeaker", "bluespacesyringe", "bluespacebodybag", "phasic_scanning", "roastingstick")
|
||||
design_ids = list("bs_rped","minerbag_holding", "telesci_gps", "bluespacebeaker", "bluespacesyringe", "bluespacebodybag", "phasic_scanning", "roastingstick", "xenobioconsole") //CIT CHANGE added xenobioconsole here
|
||||
research_cost = 5000
|
||||
export_price = 5000
|
||||
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
// THIS IS NOW MERELY LEGACY, because memes. hopefully it won't be dumb.
|
||||
|
||||
//
|
||||
// The belly object is what holds onto a mob while they're inside a predator.
|
||||
// It takes care of altering the pred's decription, digesting the prey, relaying struggles etc.
|
||||
//
|
||||
|
||||
// If you change what variables are on this, then you need to update the copy() proc.
|
||||
|
||||
//
|
||||
// Parent type of all the various "belly" varieties.
|
||||
//
|
||||
/datum/belly
|
||||
var/name // Name of this location
|
||||
var/inside_flavor // Flavor text description of inside sight/sound/smells/feels.
|
||||
var/vore_sound = 'sound/vore/pred/swallow_01.ogg' // Sound when ingesting someone
|
||||
var/vore_verb = "ingest" // Verb for eating with this in messages
|
||||
var/human_prey_swallow_time = 10 SECONDS // Time in deciseconds to swallow /mob/living/carbon/human
|
||||
var/nonhuman_prey_swallow_time = 5 SECONDS // Time in deciseconds to swallow anything else
|
||||
var/emoteTime = 30 SECONDS // How long between stomach emotes at prey
|
||||
var/digest_brute = 0 // Brute damage per tick in digestion mode
|
||||
var/digest_burn = 1 // Burn damage per tick in digestion mode
|
||||
var/digest_tickrate = 9 // Modulus this of air controller tick number to iterate gurgles on
|
||||
var/immutable = FALSE // Prevents this belly from being deleted
|
||||
var/escapable = FALSE // Belly can be resisted out of at any time
|
||||
var/escapetime = 60 SECONDS // Deciseconds, how long to escape this belly
|
||||
var/digestchance = 0 // % Chance of stomach beginning to digest if prey struggles
|
||||
// var/silenced = FALSE // Will the heartbeat/fleshy internal loop play?
|
||||
var/escapechance = 0 // % Chance of prey beginning to escape if prey struggles.
|
||||
|
||||
var/datum/belly/transferlocation = null // Location that the prey is released if they struggle and get dropped off.
|
||||
var/transferchance = 0 // % Chance of prey being transferred to transfer location when resisting
|
||||
var/autotransferchance = 0 // % Chance of prey being autotransferred to transfer location
|
||||
var/autotransferwait = 10 // Time between trying to transfer.
|
||||
var/can_taste = FALSE // If this belly prints the flavor of prey when it eats someone.
|
||||
|
||||
var/tmp/digest_mode = DM_HOLD // Whether or not to digest. Default to not digest.
|
||||
var/tmp/list/digest_modes = list(DM_HOLD,DM_DIGEST,DM_HEAL,DM_NOISY) // Possible digest modes
|
||||
var/tmp/mob/living/owner // The mob whose belly this is.
|
||||
var/tmp/list/internal_contents = list() // People/Things you've eaten into this belly!
|
||||
var/tmp/is_full // Flag for if digested remeans are present. (for disposal messages)
|
||||
var/tmp/emotePend = FALSE // If there's already a spawned thing counting for the next emote
|
||||
var/swallow_time = 10 SECONDS // for mob transfering automation
|
||||
var/vore_capacity = 1 // The capacity (in people) this person can hold
|
||||
|
||||
// Don't forget to watch your commas at the end of each line if you change these.
|
||||
var/list/struggle_messages_outside = list(
|
||||
"%pred's %belly wobbles with a squirming meal.",
|
||||
"%pred's %belly jostles with movement.",
|
||||
"%pred's %belly briefly swells outward as someone pushes from inside.",
|
||||
"%pred's %belly fidgets with a trapped victim.",
|
||||
"%pred's %belly jiggles with motion from inside.",
|
||||
"%pred's %belly sloshes around.",
|
||||
"%pred's %belly gushes softly.",
|
||||
"%pred's %belly lets out a wet squelch.")
|
||||
|
||||
var/list/struggle_messages_inside = list(
|
||||
"Your useless squirming only causes %pred's slimy %belly to squelch over your body.",
|
||||
"Your struggles only cause %pred's %belly to gush softly around you.",
|
||||
"Your movement only causes %pred's %belly to slosh around you.",
|
||||
"Your motion causes %pred's %belly to jiggle.",
|
||||
"You fidget around inside of %pred's %belly.",
|
||||
"You shove against the walls of %pred's %belly, making it briefly swell outward.",
|
||||
"You jostle %pred's %belly with movement.",
|
||||
"You squirm inside of %pred's %belly, making it wobble around.")
|
||||
|
||||
var/list/digest_messages_owner = list(
|
||||
"You feel %prey's body succumb to your digestive system, which breaks it apart into soft slurry.",
|
||||
"You hear a lewd glorp as your %belly muscles grind %prey into a warm pulp.",
|
||||
"Your %belly lets out a rumble as it melts %prey into sludge.",
|
||||
"You feel a soft gurgle as %prey's body loses form in your %belly. They're nothing but a soft mass of churning slop now.",
|
||||
"Your %belly begins gushing %prey's remains through your system, adding some extra weight to your thighs.",
|
||||
"Your %belly begins gushing %prey's remains through your system, adding some extra weight to your rump.",
|
||||
"Your %belly begins gushing %prey's remains through your system, adding some extra weight to your belly.",
|
||||
"Your %belly groans as %prey falls apart into a thick soup. You can feel their remains soon flowing deeper into your body to be absorbed.",
|
||||
"Your %belly kneads on every fiber of %prey, softening them down into mush to fuel your next hunt.",
|
||||
"Your %belly churns %prey down into a hot slush. You can feel the nutrients coursing through your digestive track with a series of long, wet glorps.")
|
||||
|
||||
var/list/digest_messages_prey = list(
|
||||
"Your body succumbs to %pred's digestive system, which breaks you apart into soft slurry.",
|
||||
"%pred's %belly lets out a lewd glorp as their muscles grind you into a warm pulp.",
|
||||
"%pred's %belly lets out a rumble as it melts you into sludge.",
|
||||
"%pred feels a soft gurgle as your body loses form in their %belly. You're nothing but a soft mass of churning slop now.",
|
||||
"%pred's %belly begins gushing your remains through their system, adding some extra weight to %pred's thighs.",
|
||||
"%pred's %belly begins gushing your remains through their system, adding some extra weight to %pred's rump.",
|
||||
"%pred's %belly begins gushing your remains through their system, adding some extra weight to %pred's belly.",
|
||||
"%pred's %belly groans as you fall apart into a thick soup. Your remains soon flow deeper into %pred's body to be absorbed.",
|
||||
"%pred's %belly kneads on every fiber of your body, softening you down into mush to fuel their next hunt.",
|
||||
"%pred's %belly churns you down into a hot slush. Your nutrient-rich remains course through their digestive track with a series of long, wet glorps.")
|
||||
|
||||
var/list/examine_messages = list(
|
||||
"They have something solid in their %belly!",
|
||||
"It looks like they have something in their %belly!")
|
||||
|
||||
//Mostly for being overridden on precreated bellies on mobs. Could be VV'd into
|
||||
//a carbon's belly if someone really wanted. No UI for carbons to adjust this.
|
||||
//List has indexes that are the digestion mode strings, and keys that are lists of strings.
|
||||
var/list/emote_lists = list()
|
||||
|
||||
//OLD: This only exists for legacy conversion purposes
|
||||
//It's called whenever an old datum-style belly is loaded
|
||||
/datum/belly/proc/copy(obj/belly/new_belly)
|
||||
|
||||
//// Non-object variables
|
||||
new_belly.name = name
|
||||
new_belly.desc = inside_flavor
|
||||
new_belly.vore_sound = vore_sound
|
||||
new_belly.vore_verb = vore_verb
|
||||
new_belly.human_prey_swallow_time = human_prey_swallow_time
|
||||
new_belly.nonhuman_prey_swallow_time = nonhuman_prey_swallow_time
|
||||
new_belly.emote_time = emoteTime
|
||||
new_belly.digest_brute = digest_brute
|
||||
new_belly.digest_burn = digest_burn
|
||||
new_belly.immutable = immutable
|
||||
new_belly.can_taste = can_taste
|
||||
new_belly.escapable = escapable
|
||||
new_belly.escapetime = escapetime
|
||||
new_belly.digestchance = digestchance
|
||||
new_belly.escapechance = escapechance
|
||||
new_belly.transferchance = transferchance
|
||||
new_belly.transferlocation = transferlocation
|
||||
|
||||
//// Object-holding variables
|
||||
//struggle_messages_outside - strings
|
||||
new_belly.struggle_messages_outside.Cut()
|
||||
for(var/I in struggle_messages_outside)
|
||||
new_belly.struggle_messages_outside += I
|
||||
|
||||
//struggle_messages_inside - strings
|
||||
new_belly.struggle_messages_inside.Cut()
|
||||
for(var/I in struggle_messages_inside)
|
||||
new_belly.struggle_messages_inside += I
|
||||
|
||||
//digest_messages_owner - strings
|
||||
new_belly.digest_messages_owner.Cut()
|
||||
for(var/I in digest_messages_owner)
|
||||
new_belly.digest_messages_owner += I
|
||||
|
||||
//digest_messages_prey - strings
|
||||
new_belly.digest_messages_prey.Cut()
|
||||
for(var/I in digest_messages_prey)
|
||||
new_belly.digest_messages_prey += I
|
||||
|
||||
//examine_messages - strings
|
||||
new_belly.examine_messages.Cut()
|
||||
for(var/I in examine_messages)
|
||||
new_belly.examine_messages += I
|
||||
|
||||
//emote_lists - index: digest mode, key: list of strings
|
||||
new_belly.emote_lists.Cut()
|
||||
for(var/K in emote_lists)
|
||||
new_belly.emote_lists[K] = list()
|
||||
for(var/I in emote_lists[K])
|
||||
new_belly.emote_lists[K] += I
|
||||
|
||||
return new_belly
|
||||
|
||||
// // // // // // // // // // // //
|
||||
// // // LEGACY USE ONLY!! // // //
|
||||
// // // // // // // // // // // //
|
||||
// See top of file! //
|
||||
// // // // // // // // // // // //
|
||||
@@ -1,655 +0,0 @@
|
||||
//#define VORE_SOUND_FALLOFF 0.05
|
||||
|
||||
//
|
||||
// Belly system 2.0, now using objects instead of datums because EH at datums.
|
||||
// How many times have I rewritten bellies and vore now? -Aro
|
||||
//
|
||||
|
||||
// If you change what variables are on this, then you need to update the copy() proc.
|
||||
|
||||
//
|
||||
// Parent type of all the various "belly" varieties.
|
||||
//
|
||||
/obj/belly
|
||||
name = "belly" // Name of this location
|
||||
desc = "It's a belly! You're in it!" // Flavor text description of inside sight/sound/smells/feels.
|
||||
var/vore_sound = 'sound/vore/pred/swallow_01.ogg' // Sound when ingesting someone
|
||||
var/vore_verb = "ingest" // Verb for eating with this in messages
|
||||
var/release_sound = 'sound/effects/splat.ogg'
|
||||
var/human_prey_swallow_time = 100 // Time in deciseconds to swallow /mob/living/carbon/human
|
||||
var/nonhuman_prey_swallow_time = 30 // Time in deciseconds to swallow anything else
|
||||
var/emote_time = 60 SECONDS // How long between stomach emotes at prey
|
||||
var/digest_brute = 2 // Brute damage per tick in digestion mode
|
||||
var/digest_burn = 2 // Burn damage per tick in digestion mode
|
||||
var/immutable = FALSE // Prevents this belly from being deleted
|
||||
var/escapable = TRUE // Belly can be resisted out of at any time
|
||||
var/escapetime = 20 SECONDS // Deciseconds, how long to escape this belly
|
||||
var/digestchance = 0 // % Chance of stomach beginning to digest if prey struggles
|
||||
var/absorbchance = 0 // % Chance of stomach beginning to absorb if prey struggles
|
||||
var/escapechance = 100 // % Chance of prey beginning to escape if prey struggles.
|
||||
var/can_taste = FALSE // If this belly prints the flavor of prey when it eats someone.
|
||||
var/bulge_size = 0.25 // The minimum size the prey has to be in order to show up on examine.
|
||||
// var/shrink_grow_size = 1 // This horribly named variable determines the minimum/maximum size it will shrink/grow prey to.
|
||||
var/silent = FALSE
|
||||
|
||||
var/transferlocation = null // Location that the prey is released if they struggle and get dropped off.
|
||||
var/transferchance = 0 // % Chance of prey being transferred to transfer location when resisting
|
||||
var/autotransferchance = 0 // % Chance of prey being autotransferred to transfer location
|
||||
var/autotransferwait = 10 // Time between trying to transfer.
|
||||
var/swallow_time = 10 SECONDS // for mob transfering automation
|
||||
var/vore_capacity = 1 // simple animal nom capacity
|
||||
|
||||
//I don't think we've ever altered these lists. making them static until someone actually overrides them somewhere.
|
||||
var/tmp/static/list/digest_modes = list(DM_HOLD,DM_DIGEST,DM_HEAL,DM_NOISY) // Possible digest modes
|
||||
|
||||
var/tmp/mob/living/owner // The mob whose belly this is.
|
||||
var/tmp/digest_mode = DM_HOLD // Current mode the belly is set to from digest_modes (+transform_modes if human)
|
||||
var/tmp/next_process = 0 // Waiting for this SSbellies times_fired to process again.
|
||||
var/tmp/list/items_preserved = list() // Stuff that wont digest so we shouldn't process it again.
|
||||
var/tmp/next_emote = 0 // When we're supposed to print our next emote, as a belly controller tick #
|
||||
var/tmp/recent_sound = FALSE // Prevent audio spam
|
||||
|
||||
// Don't forget to watch your commas at the end of each line if you change these.
|
||||
var/list/struggle_messages_outside = list(
|
||||
"%pred's %belly wobbles with a squirming meal.",
|
||||
"%pred's %belly jostles with movement.",
|
||||
"%pred's %belly briefly swells outward as someone pushes from inside.",
|
||||
"%pred's %belly fidgets with a trapped victim.",
|
||||
"%pred's %belly jiggles with motion from inside.",
|
||||
"%pred's %belly sloshes around.",
|
||||
"%pred's %belly gushes softly.",
|
||||
"%pred's %belly lets out a wet squelch.")
|
||||
|
||||
var/list/struggle_messages_inside = list(
|
||||
"Your useless squirming only causes %pred's slimy %belly to squelch over your body.",
|
||||
"Your struggles only cause %pred's %belly to gush softly around you.",
|
||||
"Your movement only causes %pred's %belly to slosh around you.",
|
||||
"Your motion causes %pred's %belly to jiggle.",
|
||||
"You fidget around inside of %pred's %belly.",
|
||||
"You shove against the walls of %pred's %belly, making it briefly swell outward.",
|
||||
"You jostle %pred's %belly with movement.",
|
||||
"You squirm inside of %pred's %belly, making it wobble around.")
|
||||
|
||||
var/list/digest_messages_owner = list(
|
||||
"You feel %prey's body succumb to your digestive system, which breaks it apart into soft slurry.",
|
||||
"You hear a lewd glorp as your %belly muscles grind %prey into a warm pulp.",
|
||||
"Your %belly lets out a rumble as it melts %prey into sludge.",
|
||||
"You feel a soft gurgle as %prey's body loses form in your %belly. They're nothing but a soft mass of churning slop now.",
|
||||
"Your %belly begins gushing %prey's remains through your system, adding some extra weight to your thighs.",
|
||||
"Your %belly begins gushing %prey's remains through your system, adding some extra weight to your rump.",
|
||||
"Your %belly begins gushing %prey's remains through your system, adding some extra weight to your belly.",
|
||||
"Your %belly groans as %prey falls apart into a thick soup. You can feel their remains soon flowing deeper into your body to be absorbed.",
|
||||
"Your %belly kneads on every fiber of %prey, softening them down into mush to fuel your next hunt.",
|
||||
"Your %belly churns %prey down into a hot slush. You can feel the nutrients coursing through your digestive track with a series of long, wet glorps.")
|
||||
|
||||
var/list/digest_messages_prey = list(
|
||||
"Your body succumbs to %pred's digestive system, which breaks you apart into soft slurry.",
|
||||
"%pred's %belly lets out a lewd glorp as their muscles grind you into a warm pulp.",
|
||||
"%pred's %belly lets out a rumble as it melts you into sludge.",
|
||||
"%pred feels a soft gurgle as your body loses form in their %belly. You're nothing but a soft mass of churning slop now.",
|
||||
"%pred's %belly begins gushing your remains through their system, adding some extra weight to %pred's thighs.",
|
||||
"%pred's %belly begins gushing your remains through their system, adding some extra weight to %pred's rump.",
|
||||
"%pred's %belly begins gushing your remains through their system, adding some extra weight to %pred's belly.",
|
||||
"%pred's %belly groans as you fall apart into a thick soup. Your remains soon flow deeper into %pred's body to be absorbed.",
|
||||
"%pred's %belly kneads on every fiber of your body, softening you down into mush to fuel their next hunt.",
|
||||
"%pred's %belly churns you down into a hot slush. Your nutrient-rich remains course through their digestive track with a series of long, wet glorps.")
|
||||
|
||||
var/list/examine_messages = list(
|
||||
"They have something solid in their %belly!",
|
||||
"It looks like they have something in their %belly!")
|
||||
|
||||
//Mostly for being overridden on precreated bellies on mobs. Could be VV'd into
|
||||
//a carbon's belly if someone really wanted. No UI for carbons to adjust this.
|
||||
//List has indexes that are the digestion mode strings, and keys that are lists of strings.
|
||||
var/tmp/list/emote_lists = list()
|
||||
|
||||
//For serialization, keep this updated, required for bellies to save correctly.
|
||||
/obj/belly/vars_to_save()
|
||||
return ..() + list(
|
||||
"name",
|
||||
"desc",
|
||||
"vore_sound",
|
||||
"vore_verb",
|
||||
"release_sound",
|
||||
"human_prey_swallow_time",
|
||||
"nonhuman_prey_swallow_time",
|
||||
"emote_time",
|
||||
"digest_brute",
|
||||
"digest_burn",
|
||||
"immutable",
|
||||
"can_taste",
|
||||
"escapable",
|
||||
"escapetime",
|
||||
"digestchance",
|
||||
"absorbchance",
|
||||
"escapechance",
|
||||
"transferchance",
|
||||
"transferlocation",
|
||||
"bulge_size",
|
||||
"struggle_messages_outside",
|
||||
"struggle_messages_inside",
|
||||
"digest_messages_owner",
|
||||
"digest_messages_prey",
|
||||
"examine_messages",
|
||||
"emote_lists",
|
||||
"silent"
|
||||
)
|
||||
|
||||
//ommitted list
|
||||
// "shrink_grow_size",
|
||||
/obj/belly/New(var/newloc)
|
||||
. = ..(newloc)
|
||||
//If not, we're probably just in a prefs list or something.
|
||||
if(isliving(newloc))
|
||||
owner = loc
|
||||
owner.vore_organs |= src
|
||||
SSbellies.belly_list += src
|
||||
|
||||
/obj/belly/Destroy()
|
||||
SSbellies.belly_list -= src
|
||||
if(owner)
|
||||
owner.vore_organs -= src
|
||||
owner = null
|
||||
. = ..()
|
||||
|
||||
// Called whenever an atom enters this belly
|
||||
/obj/belly/Entered(var/atom/movable/thing,var/atom/OldLoc)
|
||||
if(OldLoc in contents)
|
||||
return //Someone dropping something (or being stripdigested)
|
||||
|
||||
//Generic entered message
|
||||
to_chat(owner,"<span class='notice'>[thing] slides into your [lowertext(name)].</span>")
|
||||
|
||||
//Sound w/ antispam flag setting
|
||||
if(!silent && !recent_sound)
|
||||
for(var/mob/M in get_hearers_in_view(5, get_turf(owner)))
|
||||
if(M.client && M.client.prefs.toggles & EATING_NOISES)
|
||||
playsound(get_turf(owner),"[src.vore_sound]",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
|
||||
recent_sound = TRUE
|
||||
|
||||
//Messages if it's a mob
|
||||
if(isliving(thing))
|
||||
var/mob/living/M = thing
|
||||
if(desc)
|
||||
to_chat(M, "<span class='notice'><B>[desc]</B></span>")
|
||||
var/taste
|
||||
if(can_taste && (taste = M.get_taste_message(FALSE)))
|
||||
to_chat(owner, "<span class='notice'>[M] tastes of [taste].</span>")
|
||||
|
||||
// Release all contents of this belly into the owning mob's location.
|
||||
// If that location is another mob, contents are transferred into whichever of its bellies the owning mob is in.
|
||||
// Returns the number of mobs so released.
|
||||
/obj/belly/proc/release_all_contents(var/include_absorbed = FALSE)
|
||||
var/atom/destination = drop_location()
|
||||
var/count = 0
|
||||
for(var/thing in contents)
|
||||
var/atom/movable/AM = thing
|
||||
if(isliving(AM))
|
||||
var/mob/living/L = AM
|
||||
if(L.absorbed && !include_absorbed)
|
||||
continue
|
||||
L.absorbed = FALSE
|
||||
for(var/mob/living/W in AM)
|
||||
W.stop_sound_channel(CHANNEL_PREYLOOP)
|
||||
AM.forceMove(destination) // Move the belly contents into the same location as belly's owner.
|
||||
count++
|
||||
for(var/mob/M in get_hearers_in_view(5, get_turf(owner)))
|
||||
if(M.client && M.client.prefs.toggles & EATING_NOISES)
|
||||
playsound(get_turf(owner),"[src.release_sound]",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
|
||||
items_preserved.Cut()
|
||||
owner.visible_message("<font color='green'><b>[owner] expels everything from their [lowertext(name)]!</b></font>")
|
||||
owner.update_icons()
|
||||
|
||||
return count
|
||||
|
||||
// Release a specific atom from the contents of this belly into the owning mob's location.
|
||||
// If that location is another mob, the atom is transferred into whichever of its bellies the owning mob is in.
|
||||
// Returns the number of atoms so released.
|
||||
/obj/belly/proc/release_specific_contents(var/atom/movable/M)
|
||||
if (!(M in contents))
|
||||
return FALSE // They weren't in this belly anyway
|
||||
|
||||
M.forceMove(drop_location()) // Move the belly contents into the same location as belly's owner.
|
||||
items_preserved -= M
|
||||
for(var/mob/living/P in M)
|
||||
P.stop_sound_channel(CHANNEL_PREYLOOP)
|
||||
if(release_sound)
|
||||
for(var/mob/H in get_hearers_in_view(5, get_turf(owner)))
|
||||
if(H.client && H.client.prefs.toggles & EATING_NOISES)
|
||||
playsound(get_turf(owner),"[src.release_sound]",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
|
||||
|
||||
if(istype(M,/mob/living))
|
||||
var/mob/living/ML = M
|
||||
var/mob/living/OW = owner
|
||||
if(ML.absorbed)
|
||||
ML.absorbed = FALSE
|
||||
if(ishuman(M) && ishuman(OW))
|
||||
var/mob/living/carbon/human/Prey = M
|
||||
var/mob/living/carbon/human/Pred = OW
|
||||
var/absorbed_count = 2 //Prey that we were, plus the pred gets a portion
|
||||
for(var/mob/living/P in contents)
|
||||
if(P.absorbed)
|
||||
absorbed_count++
|
||||
Pred.reagents.trans_to(Prey, Pred.reagents.total_volume / absorbed_count)
|
||||
|
||||
owner.visible_message("<font color='green'><b>[owner] expels [M] from their [lowertext(name)]!</b></font>")
|
||||
owner.update_icons()
|
||||
return TRUE
|
||||
|
||||
// Actually perform the mechanics of devouring the tasty prey.
|
||||
// The purpose of this method is to avoid duplicate code, and ensure that all necessary
|
||||
// steps are taken.
|
||||
/obj/belly/proc/nom_mob(var/mob/prey, var/mob/user)
|
||||
var/sound/preyloop = sound('sound/vore/prey/loop.ogg', repeat = TRUE)
|
||||
if(owner.stat == DEAD)
|
||||
return
|
||||
if (prey.buckled)
|
||||
prey.buckled.unbuckle_mob(prey,TRUE)
|
||||
|
||||
prey.forceMove(src)
|
||||
prey.playsound_local(loc,preyloop,70,0, channel = CHANNEL_PREYLOOP)
|
||||
owner.updateVRPanel()
|
||||
|
||||
for(var/mob/living/M in contents)
|
||||
M.updateVRPanel()
|
||||
|
||||
// Setup the autotransfer checks if needed
|
||||
if(transferlocation && autotransferchance > 0)
|
||||
addtimer(CALLBACK(src, /obj/belly/.proc/check_autotransfer, prey), autotransferwait)
|
||||
|
||||
/obj/belly/proc/check_autotransfer(var/mob/prey)
|
||||
// Some sanity checks
|
||||
if(transferlocation && (autotransferchance > 0) && (prey in contents))
|
||||
if(prob(autotransferchance))
|
||||
// Double check transferlocation isn't insane
|
||||
if(verify_transferlocation())
|
||||
transfer_contents(prey, transferlocation)
|
||||
else
|
||||
// Didn't transfer, so wait before retrying
|
||||
addtimer(CALLBACK(src, /obj/belly/.proc/check_autotransfer, prey), autotransferwait)
|
||||
|
||||
/obj/belly/proc/verify_transferlocation()
|
||||
for(var/I in owner.vore_organs)
|
||||
var/obj/belly/B = owner.vore_organs[I]
|
||||
if(B == transferlocation)
|
||||
return TRUE
|
||||
|
||||
for(var/I in owner.vore_organs)
|
||||
var/obj/belly/B = owner.vore_organs[I]
|
||||
if(B == transferlocation)
|
||||
transferlocation = B
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
// Get the line that should show up in Examine message if the owner of this belly
|
||||
// is examined. By making this a proc, we not only take advantage of polymorphism,
|
||||
// but can easily make the message vary based on how many people are inside, etc.
|
||||
// Returns a string which shoul be appended to the Examine output.
|
||||
/obj/belly/proc/get_examine_msg()
|
||||
if(contents.len && examine_messages.len)
|
||||
var/formatted_message
|
||||
var/raw_message = pick(examine_messages)
|
||||
var/total_bulge = 0
|
||||
|
||||
formatted_message = replacetext(raw_message,"%belly",lowertext(name))
|
||||
formatted_message = replacetext(formatted_message,"%pred",owner)
|
||||
formatted_message = replacetext(formatted_message,"%prey",english_list(contents))
|
||||
for(var/mob/living/P in contents)
|
||||
if(!P.absorbed) //This is required first, in case there's a person absorbed and not absorbed in a stomach.
|
||||
total_bulge += P.mob_size
|
||||
if(total_bulge >= bulge_size && bulge_size != 0)
|
||||
return("<span class='warning'>[formatted_message]</span><BR>")
|
||||
else
|
||||
return ""
|
||||
|
||||
// The next function gets the messages set on the belly, in human-readable format.
|
||||
// This is useful in customization boxes and such. The delimiter right now is \n\n so
|
||||
// in message boxes, this looks nice and is easily delimited.
|
||||
/obj/belly/proc/get_messages(var/type, var/delim = "\n\n")
|
||||
ASSERT(type == "smo" || type == "smi" || type == "dmo" || type == "dmp" || type == "em")
|
||||
var/list/raw_messages
|
||||
|
||||
switch(type)
|
||||
if("smo")
|
||||
raw_messages = struggle_messages_outside
|
||||
if("smi")
|
||||
raw_messages = struggle_messages_inside
|
||||
if("dmo")
|
||||
raw_messages = digest_messages_owner
|
||||
if("dmp")
|
||||
raw_messages = digest_messages_prey
|
||||
if("em")
|
||||
raw_messages = examine_messages
|
||||
|
||||
var/messages = list2text(raw_messages,delim)
|
||||
return messages
|
||||
|
||||
// The next function sets the messages on the belly, from human-readable var
|
||||
// replacement strings and linebreaks as delimiters (two \n\n by default).
|
||||
// They also sanitize the messages.
|
||||
/obj/belly/proc/set_messages(var/raw_text, var/type, var/delim = "\n\n")
|
||||
ASSERT(type == "smo" || type == "smi" || type == "dmo" || type == "dmp" || type == "em")
|
||||
|
||||
var/list/raw_list = text2list(html_encode(raw_text),delim)
|
||||
if(raw_list.len > 10)
|
||||
raw_list.Cut(11)
|
||||
testing("[owner] tried to set [lowertext(name)] with 11+ messages")
|
||||
|
||||
for(var/i = 1, i <= raw_list.len, i++)
|
||||
if(length(raw_list[i]) > 160 || length(raw_list[i]) < 10) //160 is fudged value due to htmlencoding increasing the size
|
||||
raw_list.Cut(i,i)
|
||||
testing("[owner] tried to set [lowertext(name)] with >121 or <10 char message")
|
||||
else
|
||||
raw_list[i] = readd_quotes(raw_list[i])
|
||||
//Also fix % sign for var replacement
|
||||
raw_list[i] = replacetext(raw_list[i],"%","%")
|
||||
|
||||
ASSERT(raw_list.len <= 10) //Sanity
|
||||
|
||||
switch(type)
|
||||
if("smo")
|
||||
struggle_messages_outside = raw_list
|
||||
if("smi")
|
||||
struggle_messages_inside = raw_list
|
||||
if("dmo")
|
||||
digest_messages_owner = raw_list
|
||||
if("dmp")
|
||||
digest_messages_prey = raw_list
|
||||
if("em")
|
||||
examine_messages = raw_list
|
||||
|
||||
return
|
||||
|
||||
// Handle the death of a mob via digestion.
|
||||
// Called from the process_Life() methods of bellies that digest prey.
|
||||
// Default implementation calls M.death() and removes from internal contents.
|
||||
// Indigestable items are removed, and M is deleted.
|
||||
/obj/belly/proc/digestion_death(var/mob/living/M)
|
||||
//M.death(1) // "Stop it he's already dead..." Basically redundant and the reason behind screaming mouse carcasses.
|
||||
if(M.ckey)
|
||||
message_admins("[key_name(owner)] has digested [key_name(M)] in their [lowertext(name)] ([owner ? "<a href='?_src_=holder;adminplayerobservecoodjump=1;X=[owner.x];Y=[owner.y];Z=[owner.z]'>JMP</a>" : "null"])")
|
||||
log_attack("[key_name(owner)] digested [key_name(M)].")
|
||||
|
||||
// If digested prey is also a pred... anyone inside their bellies gets moved up.
|
||||
if(is_vore_predator(M))
|
||||
for(var/belly in M.vore_organs)
|
||||
var/obj/belly/B = belly
|
||||
for(var/thing in B)
|
||||
var/atom/movable/AM = thing
|
||||
AM.forceMove(owner.loc)
|
||||
if(isliving(AM))
|
||||
to_chat(AM,"As [M] melts away around you, you find yourself in [owner]'s [lowertext(name)]")
|
||||
|
||||
//Drop all items into the belly
|
||||
for(var/obj/item/W in M)
|
||||
if(!M.dropItemToGround(W))
|
||||
qdel(W)
|
||||
|
||||
/* //Reagent transfer //maybe someday
|
||||
if(ishuman(owner))
|
||||
var/mob/living/carbon/human/Pred = owner
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/Prey = M
|
||||
Prey.bloodstr.del_reagent("numbenzyme")
|
||||
Prey.bloodstr.trans_to_holder(Pred.bloodstr, Prey.bloodstr.total_volume, 0.5, TRUE) // Copy=TRUE because we're deleted anyway
|
||||
Prey.ingested.trans_to_holder(Pred.bloodstr, Prey.ingested.total_volume, 0.5, TRUE) // Therefore don't bother spending cpu
|
||||
Prey.touching.trans_to_holder(Pred.bloodstr, Prey.touching.total_volume, 0.5, TRUE) // On updating the prey's reagents
|
||||
else if(M.reagents)
|
||||
M.reagents.trans_to_holder(Pred.bloodstr, M.reagents.total_volume, 0.5, TRUE) */
|
||||
|
||||
// Delete the digested mob
|
||||
qdel(M)
|
||||
|
||||
// Handle a mob being absorbed
|
||||
/obj/belly/proc/absorb_living(var/mob/living/M)
|
||||
M.absorbed = TRUE
|
||||
to_chat(M,"<span class='notice'>[owner]'s [lowertext(name)] absorbs your body, making you part of them.</span>")
|
||||
to_chat(owner,"<span class='notice'>Your [lowertext(name)] absorbs [M]'s body, making them part of you.</span>")
|
||||
|
||||
// Reagent sharing is neat, but eh. I'll figure it out later
|
||||
/* if(ishuman(M) && ishuman(owner))
|
||||
var/mob/living/carbon/human/Prey = M
|
||||
var/mob/living/carbon/human/Pred = owner
|
||||
//Reagent sharing for absorbed with pred - Copy so both pred and prey have these reagents.
|
||||
Prey.bloodstr.trans_to_holder(Pred.bloodstr, Prey.bloodstr.total_volume, copy = TRUE)
|
||||
Prey.ingested.trans_to_holder(Pred.bloodstr, Prey.ingested.total_volume, copy = TRUE)
|
||||
Prey.touching.trans_to_holder(Pred.bloodstr, Prey.touching.total_volume, copy = TRUE)
|
||||
// TODO - Find a way to make the absorbed prey share the effects with the pred.
|
||||
// Currently this is infeasible because reagent containers are designed to have a single my_atom, and we get
|
||||
// problems when A absorbs B, and then C absorbs A, resulting in B holding onto an invalid reagent container.
|
||||
*/
|
||||
//This is probably already the case, but for sub-prey, it won't be.
|
||||
if(M.loc != src)
|
||||
M.forceMove(src)
|
||||
|
||||
//Seek out absorbed prey of the prey, absorb them too.
|
||||
//This in particular will recurse oddly because if there is absorbed prey of prey of prey...
|
||||
//it will just move them up one belly. This should never happen though since... when they were
|
||||
//absobred, they should have been absorbed as well!
|
||||
for(var/belly in M.vore_organs)
|
||||
var/obj/belly/B = belly
|
||||
for(var/mob/living/Mm in B)
|
||||
if(Mm.absorbed)
|
||||
absorb_living(Mm)
|
||||
|
||||
//Update owner
|
||||
owner.updateVRPanel()
|
||||
|
||||
//Digest a single item
|
||||
//Receives a return value from digest_act that's how much nutrition
|
||||
//the item should be worth
|
||||
/obj/belly/proc/digest_item(var/obj/item/item)
|
||||
var/digested = item.digest_act(src, owner)
|
||||
if(!digested)
|
||||
items_preserved |= item
|
||||
else
|
||||
// owner.nutrition += (5 * digested) // haha no.
|
||||
if(iscyborg(owner))
|
||||
var/mob/living/silicon/robot/R = owner
|
||||
R.cell.charge += (50 * digested)
|
||||
|
||||
//Determine where items should fall out of us into.
|
||||
//Typically just to the owner's location.
|
||||
/obj/belly/drop_location()
|
||||
//Should be the case 99.99% of the time
|
||||
if(owner)
|
||||
return owner.loc
|
||||
//Sketchy fallback for safety, put them somewhere safe.
|
||||
else if(ismob(src))
|
||||
testing("[src] (\ref[src]) doesn't have an owner, and dropped someone at a latespawn point!")
|
||||
SSjob.SendToLateJoin(src)
|
||||
// wew lad. let's see if this never gets used, hopefully
|
||||
else
|
||||
qdel(src) //final option, I guess.
|
||||
testing("[src] (\ref[src]) was QDEL'd for not having a drop_location!")
|
||||
|
||||
//Handle a mob struggling
|
||||
// Called from /mob/living/carbon/relaymove()
|
||||
/obj/belly/proc/relay_resist(var/mob/living/R)
|
||||
if (!(R in contents))
|
||||
return // User is not in this belly
|
||||
|
||||
R.setClickCooldown(50)
|
||||
|
||||
if(owner.stat || !owner.client && R.a_intent != INTENT_HELP) //If owner is stat (dead, KO) we can actually escape
|
||||
to_chat(R,"<span class='warning'>You attempt to climb out of \the [lowertext(name)]. (This will take around 5 seconds.)</span>")
|
||||
to_chat(owner,"<span class='warning'>Someone is attempting to climb out of your [lowertext(name)]!</span>")
|
||||
|
||||
if(do_after(R, 50, owner))
|
||||
if(owner.stat && (R in contents) && R.a_intent != INTENT_HELP) //Can still escape and want to?
|
||||
release_specific_contents(R)
|
||||
return
|
||||
else if(!(R in contents)) //Aren't even in the belly. Quietly fail.
|
||||
return
|
||||
else //Belly became inescapable or mob revived
|
||||
to_chat(R,"<span class='warning'>Your attempt to escape [lowertext(name)] has failed!</span>")
|
||||
to_chat(owner,"<span class='notice'>The attempt to escape from your [lowertext(name)] has failed!</span>")
|
||||
return
|
||||
return
|
||||
var/struggle_outer_message = pick(struggle_messages_outside)
|
||||
var/struggle_user_message = pick(struggle_messages_inside)
|
||||
|
||||
struggle_outer_message = replacetext(struggle_outer_message,"%pred",owner)
|
||||
struggle_outer_message = replacetext(struggle_outer_message,"%prey",R)
|
||||
struggle_outer_message = replacetext(struggle_outer_message,"%belly",lowertext(name))
|
||||
|
||||
struggle_user_message = replacetext(struggle_user_message,"%pred",owner)
|
||||
struggle_user_message = replacetext(struggle_user_message,"%prey",R)
|
||||
struggle_user_message = replacetext(struggle_user_message,"%belly",lowertext(name))
|
||||
|
||||
struggle_outer_message = "<span class='alert'>" + struggle_outer_message + "</span>"
|
||||
struggle_user_message = "<span class='alert'>" + struggle_user_message + "</span>"
|
||||
|
||||
for(var/mob/M in get_hearers_in_view(3, get_turf(owner)))
|
||||
M.show_message(struggle_outer_message, 2) // hearable
|
||||
to_chat(R,struggle_user_message)
|
||||
|
||||
if(!silent)
|
||||
for(var/mob/M in get_hearers_in_view(5, get_turf(owner)))
|
||||
if(M.client && M.client.prefs.toggles & EATING_NOISES)
|
||||
playsound(get_turf(owner),"struggle_sound",35,0,-5,1,ignore_walls = FALSE,channel=CHANNEL_PRED)
|
||||
R.stop_sound_channel(CHANNEL_PRED)
|
||||
var/sound/prey_struggle = sound(get_sfx("prey_struggle"))
|
||||
R.playsound_local(get_turf(R),prey_struggle,45,0)
|
||||
|
||||
if(R.a_intent != INTENT_HELP) //If on non help intent
|
||||
to_chat(R,"<span class='warning'>You start to climb out of \the [lowertext(name)].</span>")
|
||||
to_chat(owner,"<span class='warning'>Someone is attempting to climb out of your [lowertext(name)]!</span>")
|
||||
if(do_after(R, escapetime, owner))
|
||||
if((owner.stat || !owner.client || escapable) && (R in contents))
|
||||
release_specific_contents(R)
|
||||
to_chat(R,"<span class='warning'>You climb out of \the [lowertext(name)].</span>")
|
||||
to_chat(owner,"<span class='warning'>[R] climbs out of your [lowertext(name)]!</span>")
|
||||
for(var/mob/M in hearers(4, owner))
|
||||
M.show_message("<span class='warning'>[R] climbs out of [owner]'s [lowertext(name)]!</span>", 2)
|
||||
return
|
||||
else if(!istype(loc, /obj/belly)) //Aren't even in the belly. Quietly fail.
|
||||
return
|
||||
else //Belly became inescapable.
|
||||
to_chat(R,"<span class='warning'>Your attempt to escape [lowertext(name)] has failed!</span>")
|
||||
to_chat(owner,"<span class='notice'>The attempt to escape from your [lowertext(name)] has failed!</span>")
|
||||
return
|
||||
|
||||
else if(prob(transferchance) && transferlocation) //Next, let's have it see if they end up getting into an even bigger mess then when they started.
|
||||
var/obj/belly/dest_belly
|
||||
for(var/belly in owner.vore_organs)
|
||||
var/obj/belly/B = belly
|
||||
if(B.name == transferlocation)
|
||||
dest_belly = B
|
||||
break
|
||||
if(!dest_belly)
|
||||
to_chat(owner, "<span class='warning'>Something went wrong with your belly transfer settings. Your <b>[lowertext(name)]</b> has had it's transfer chance and transfer location cleared as a precaution.</span>")
|
||||
transferchance = 0
|
||||
transferlocation = null
|
||||
return
|
||||
|
||||
to_chat(R,"<span class='warning'>Your attempt to escape [lowertext(name)] has failed and your struggles only results in you sliding into [owner]'s [transferlocation]!</span>")
|
||||
to_chat(owner,"<span class='warning'>Someone slid into your [transferlocation] due to their struggling inside your [lowertext(name)]!</span>")
|
||||
transfer_contents(R, dest_belly)
|
||||
return
|
||||
/*
|
||||
else if(prob(absorbchance) && digest_mode != DM_ABSORB) //After that, let's have it run the absorb chance.
|
||||
to_chat(R,"<span class='warning'>In response to your struggling, \the [lowertext(name)] begins to cling more tightly...</span>")
|
||||
to_chat(owner,"<span class='warning'>You feel your [lowertext(name)] start to cling onto its contents...</span>")
|
||||
digest_mode = DM_ABSORB
|
||||
return
|
||||
|
||||
else if(prob(digestchance) && digest_mode != DM_ITEMWEAK && digest_mode != DM_DIGEST) //Finally, let's see if it should run the digest chance.
|
||||
to_chat(R,"<span class='warning'>In response to your struggling, \the [lowertext(name)] begins to get more active...</span>")
|
||||
to_chat(owner,"<span class='warning'>You feel your [lowertext(name)] beginning to become active!</span>")
|
||||
digest_mode = DM_ITEMWEAK
|
||||
return
|
||||
|
||||
else if(prob(digestchance) && digest_mode == DM_ITEMWEAK) //Oh god it gets even worse if you fail twice!
|
||||
to_chat(R,"<span class='warning'>In response to your struggling, \the [lowertext(name)] begins to get even more active!</span>")
|
||||
to_chat(owner,"<span class='warning'>You feel your [lowertext(name)] beginning to become even more active!</span>")
|
||||
digest_mode = DM_DIGEST
|
||||
return */
|
||||
else if(prob(digestchance)) //Finally, let's see if it should run the digest chance.)
|
||||
to_chat(R, "<span class='warning'>In response to your struggling, \the [name] begins to get more active...</span>")
|
||||
to_chat(owner, "<span class='warning'>You feel your [name] beginning to become active!</span>")
|
||||
digest_mode = DM_DIGEST
|
||||
return
|
||||
|
||||
else //Nothing interesting happened.
|
||||
to_chat(R,"<span class='warning'>You make no progress in escaping [owner]'s [lowertext(name)].</span>")
|
||||
to_chat(owner,"<span class='warning'>Your prey appears to be unable to make any progress in escaping your [lowertext(name)].</span>")
|
||||
return
|
||||
|
||||
//Transfers contents from one belly to another
|
||||
/obj/belly/proc/transfer_contents(var/atom/movable/content, var/obj/belly/target, silent = FALSE)
|
||||
if(!(content in src) || !istype(target))
|
||||
return
|
||||
target.nom_mob(content, target.owner)
|
||||
if(!silent)
|
||||
for(var/mob/M in get_hearers_in_view(5, get_turf(owner)))
|
||||
if(M.client && M.client.prefs.toggles & EATING_NOISES)
|
||||
playsound(get_turf(owner),"[src.vore_sound]",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
|
||||
owner.updateVRPanel()
|
||||
for(var/mob/living/M in contents)
|
||||
M.updateVRPanel()
|
||||
|
||||
// Belly copies and then returns the copy
|
||||
// Needs to be updated for any var changes
|
||||
/obj/belly/proc/copy(mob/new_owner)
|
||||
var/obj/belly/dupe = new /obj/belly(new_owner)
|
||||
|
||||
//// Non-object variables
|
||||
dupe.name = name
|
||||
dupe.desc = desc
|
||||
dupe.vore_sound = vore_sound
|
||||
dupe.vore_verb = vore_verb
|
||||
dupe.release_sound = release_sound
|
||||
dupe.human_prey_swallow_time = human_prey_swallow_time
|
||||
dupe.nonhuman_prey_swallow_time = nonhuman_prey_swallow_time
|
||||
dupe.emote_time = emote_time
|
||||
dupe.digest_brute = digest_brute
|
||||
dupe.digest_burn = digest_burn
|
||||
dupe.immutable = immutable
|
||||
dupe.can_taste = can_taste
|
||||
dupe.escapable = escapable
|
||||
dupe.escapetime = escapetime
|
||||
dupe.digestchance = digestchance
|
||||
dupe.absorbchance = absorbchance
|
||||
dupe.escapechance = escapechance
|
||||
dupe.transferchance = transferchance
|
||||
dupe.transferlocation = transferlocation
|
||||
dupe.bulge_size = bulge_size
|
||||
// dupe.shrink_grow_size = shrink_grow_size
|
||||
|
||||
//// Object-holding variables
|
||||
//struggle_messages_outside - strings
|
||||
dupe.struggle_messages_outside.Cut()
|
||||
for(var/I in struggle_messages_outside)
|
||||
dupe.struggle_messages_outside += I
|
||||
|
||||
//struggle_messages_inside - strings
|
||||
dupe.struggle_messages_inside.Cut()
|
||||
for(var/I in struggle_messages_inside)
|
||||
dupe.struggle_messages_inside += I
|
||||
|
||||
//digest_messages_owner - strings
|
||||
dupe.digest_messages_owner.Cut()
|
||||
for(var/I in digest_messages_owner)
|
||||
dupe.digest_messages_owner += I
|
||||
|
||||
//digest_messages_prey - strings
|
||||
dupe.digest_messages_prey.Cut()
|
||||
for(var/I in digest_messages_prey)
|
||||
dupe.digest_messages_prey += I
|
||||
|
||||
//examine_messages - strings
|
||||
dupe.examine_messages.Cut()
|
||||
for(var/I in examine_messages)
|
||||
dupe.examine_messages += I
|
||||
|
||||
//emote_lists - index: digest mode, key: list of strings
|
||||
dupe.emote_lists.Cut()
|
||||
for(var/K in emote_lists)
|
||||
dupe.emote_lists[K] = list()
|
||||
for(var/I in emote_lists[K])
|
||||
dupe.emote_lists[K] += I
|
||||
dupe.silent = silent
|
||||
|
||||
return dupe
|
||||
@@ -1,186 +0,0 @@
|
||||
// Process the predator's effects upon the contents of its belly (i.e digestion/transformation etc)
|
||||
/obj/belly/proc/process_belly(var/times_fired,var/wait) //Passed by controller
|
||||
if((times_fired < next_process) || !contents.len)
|
||||
recent_sound = FALSE
|
||||
return SSBELLIES_IGNORED
|
||||
|
||||
if(loc != owner)
|
||||
if(istype(owner))
|
||||
loc = owner
|
||||
else
|
||||
qdel(src)
|
||||
return SSBELLIES_PROCESSED
|
||||
|
||||
next_process = times_fired + (6 SECONDS/wait) //Set up our next process time.
|
||||
|
||||
/////////////////////////// Auto-Emotes ///////////////////////////
|
||||
if(contents.len && next_emote <= times_fired)
|
||||
next_emote = times_fired + round(emote_time/wait,1)
|
||||
var/list/EL = emote_lists[digest_mode]
|
||||
for(var/mob/living/M in contents)
|
||||
if(M.digestable || !(digest_mode == DM_DIGEST)) // don't give digesty messages to indigestible people
|
||||
to_chat(M,"<span class='notice'>[pick(EL)]</span>")
|
||||
|
||||
/////////////////////////// Exit Early ////////////////////////////
|
||||
var/list/touchable_items = contents - items_preserved
|
||||
if(!length(touchable_items))
|
||||
return SSBELLIES_PROCESSED
|
||||
|
||||
////////////////////////// Sound vars /////////////////////////////
|
||||
var/sound/prey_digest = sound(get_sfx("digest_prey"))
|
||||
var/sound/prey_death = sound(get_sfx("death_prey"))
|
||||
|
||||
|
||||
///////////////////////////// DM_HOLD /////////////////////////////
|
||||
if(digest_mode == DM_HOLD)
|
||||
return SSBELLIES_PROCESSED
|
||||
|
||||
//////////////////////////// DM_DIGEST ////////////////////////////
|
||||
else if(digest_mode == DM_DIGEST)
|
||||
for (var/mob/living/M in contents)
|
||||
if(prob(25))
|
||||
M.stop_sound_channel(CHANNEL_DIGEST)
|
||||
for(var/mob/H in get_hearers_in_view(5, get_turf(owner)))
|
||||
if(H.client && H.client.prefs.toggles & DIGESTION_NOISES)
|
||||
playsound(get_turf(owner),"digest_pred",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_DIGEST)
|
||||
M.stop_sound_channel(CHANNEL_DIGEST)
|
||||
M.playsound_local(get_turf(M), prey_digest, 45)
|
||||
|
||||
//Pref protection!
|
||||
if (!M.digestable || M.absorbed)
|
||||
continue
|
||||
|
||||
//Person just died in guts!
|
||||
if(M.stat == DEAD)
|
||||
var/digest_alert_owner = pick(digest_messages_owner)
|
||||
var/digest_alert_prey = pick(digest_messages_prey)
|
||||
|
||||
//Replace placeholder vars
|
||||
digest_alert_owner = replacetext(digest_alert_owner,"%pred",owner)
|
||||
digest_alert_owner = replacetext(digest_alert_owner,"%prey",M)
|
||||
digest_alert_owner = replacetext(digest_alert_owner,"%belly",lowertext(name))
|
||||
|
||||
digest_alert_prey = replacetext(digest_alert_prey,"%pred",owner)
|
||||
digest_alert_prey = replacetext(digest_alert_prey,"%prey",M)
|
||||
digest_alert_prey = replacetext(digest_alert_prey,"%belly",lowertext(name))
|
||||
|
||||
//Send messages
|
||||
to_chat(owner, "<span class='warning'>[digest_alert_owner]</span>")
|
||||
to_chat(M, "<span class='warning'>[digest_alert_prey]</span>")
|
||||
M.visible_message("<span class='notice'>You watch as [owner]'s form loses its additions.</span>")
|
||||
|
||||
owner.nutrition += 400 // so eating dead mobs gives you *something*.
|
||||
M.stop_sound_channel(DIGESTION_NOISES)
|
||||
for(var/mob/H in get_hearers_in_view(5, get_turf(owner)))
|
||||
if(H.client && H.client.prefs.toggles & DIGESTION_NOISES)
|
||||
playsound(get_turf(owner),"death_pred",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_DIGEST)
|
||||
M.stop_sound_channel(DIGESTION_NOISES)
|
||||
M.stop_sound_channel(CHANNEL_PREYLOOP)
|
||||
M.playsound_local(get_turf(M), prey_death, 65)
|
||||
digestion_death(M)
|
||||
owner.update_icons()
|
||||
continue
|
||||
|
||||
|
||||
// Deal digestion damage (and feed the pred)
|
||||
if(!(M.status_flags & GODMODE))
|
||||
M.adjustFireLoss(digest_burn)
|
||||
owner.nutrition += 1
|
||||
|
||||
//Contaminate or gurgle items
|
||||
var/obj/item/T = pick(touchable_items)
|
||||
if(istype(T))
|
||||
if(istype(T,/obj/item/reagent_containers/food) || istype(T,/obj/item/organ))
|
||||
digest_item(T)
|
||||
|
||||
owner.updateVRPanel()
|
||||
|
||||
///////////////////////////// DM_HEAL /////////////////////////////
|
||||
if(digest_mode == DM_HEAL)
|
||||
for (var/mob/living/M in contents)
|
||||
if(prob(25))
|
||||
M.stop_sound_channel(CHANNEL_DIGEST)
|
||||
for(var/mob/H in get_hearers_in_view(5, get_turf(owner)))
|
||||
if(H.client && H.client.prefs.toggles & DIGESTION_NOISES)
|
||||
playsound(get_turf(owner),"digest_pred",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_DIGEST)
|
||||
M.stop_sound_channel(CHANNEL_DIGEST)
|
||||
M.playsound_local(get_turf(M), prey_digest, 65)
|
||||
|
||||
if(M.stat != DEAD)
|
||||
if(owner.nutrition >= NUTRITION_LEVEL_STARVING && (M.health < M.maxHealth))
|
||||
M.adjustBruteLoss(-3)
|
||||
M.adjustFireLoss(-3)
|
||||
owner.nutrition -= 5
|
||||
return
|
||||
|
||||
////////////////////////// DM_NOISY /////////////////////////////////
|
||||
//for when you just want people to squelch around
|
||||
if(digest_mode == DM_NOISY)
|
||||
for (var/mob/living/M in contents)
|
||||
if(prob(35))
|
||||
M.stop_sound_channel(CHANNEL_DIGEST)
|
||||
for(var/mob/H in get_hearers_in_view(5, get_turf(owner)))
|
||||
if(H.client && H.client.prefs.toggles & DIGESTION_NOISES)
|
||||
playsound(get_turf(owner),"digest_pred",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_DIGEST)
|
||||
M.stop_sound_channel(CHANNEL_PRED)
|
||||
M.playsound_local(get_turf(M), prey_digest, 65)
|
||||
|
||||
|
||||
//////////////////////////DM_DRAGON /////////////////////////////////////
|
||||
//because dragons need snowflake guts
|
||||
if(digest_mode == DM_DRAGON)
|
||||
for (var/mob/living/M in contents)
|
||||
if(prob(25))
|
||||
M.stop_sound_channel(CHANNEL_DIGEST)
|
||||
for(var/mob/H in get_hearers_in_view(5, get_turf(owner)))
|
||||
if(H.client && H.client.prefs.toggles & DIGESTION_NOISES)
|
||||
playsound(get_turf(owner),"digest_pred",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_DIGEST)
|
||||
M.stop_sound_channel(CHANNEL_DIGEST)
|
||||
M.playsound_local(get_turf(M), prey_digest, 65)
|
||||
|
||||
//No digestion protection for megafauna.
|
||||
|
||||
//Person just died in guts!
|
||||
if(M.stat == DEAD)
|
||||
var/digest_alert_owner = pick(digest_messages_owner)
|
||||
var/digest_alert_prey = pick(digest_messages_prey)
|
||||
|
||||
//Replace placeholder vars
|
||||
digest_alert_owner = replacetext(digest_alert_owner,"%pred",owner)
|
||||
digest_alert_owner = replacetext(digest_alert_owner,"%prey",M)
|
||||
digest_alert_owner = replacetext(digest_alert_owner,"%belly",lowertext(name))
|
||||
|
||||
digest_alert_prey = replacetext(digest_alert_prey,"%pred",owner)
|
||||
digest_alert_prey = replacetext(digest_alert_prey,"%prey",M)
|
||||
digest_alert_prey = replacetext(digest_alert_prey,"%belly",lowertext(name))
|
||||
|
||||
//Send messages
|
||||
to_chat(owner, "<span class='warning'>[digest_alert_owner]</span>")
|
||||
to_chat(M, "<span class='warning'>[digest_alert_prey]</span>")
|
||||
M.visible_message("<span class='notice'>You watch as [owner]'s guts loudly rumble as it finishes off a meal.</span>")
|
||||
|
||||
M.stop_sound_channel(CHANNEL_DIGEST)
|
||||
for(var/mob/H in get_hearers_in_view(5, get_turf(owner)))
|
||||
if(H.client && H.client.prefs.toggles & DIGESTION_NOISES)
|
||||
playsound(get_turf(owner),"death_pred",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_DIGEST)
|
||||
M.stop_sound_channel(CHANNEL_DIGEST)
|
||||
M.playsound_local(get_turf(M), prey_death, 65)
|
||||
M.spill_organs(FALSE,TRUE,TRUE)
|
||||
M.stop_sound_channel(CHANNEL_PREYLOOP)
|
||||
digestion_death(M)
|
||||
owner.update_icons()
|
||||
continue
|
||||
|
||||
|
||||
// Deal digestion damage (and feed the pred)
|
||||
if(!(M.status_flags & GODMODE))
|
||||
M.adjustFireLoss(digest_burn)
|
||||
M.adjustToxLoss(2) // something something plasma based acids
|
||||
M.adjustCloneLoss(1) // eventually this'll kill you if you're healing everything else, you nerds.
|
||||
//Contaminate or gurgle items
|
||||
var/obj/item/T = pick(touchable_items)
|
||||
if(istype(T))
|
||||
if(istype(T,/obj/item/reagent_containers/food) || istype(T,/obj/item/organ))
|
||||
digest_item(T)
|
||||
|
||||
owner.updateVRPanel()
|
||||
@@ -1,119 +0,0 @@
|
||||
//Please make sure to:
|
||||
//return FALSE: You are not going away, stop asking me to digest.
|
||||
//return non-negative integer: Amount of nutrition/charge gained (scaled to nutrition, other end can multiply for charge scale).
|
||||
|
||||
// Ye default implementation.
|
||||
/obj/item/proc/digest_act(var/atom/movable/item_storage = null)
|
||||
for(var/obj/item/O in contents)
|
||||
if(istype(O,/obj/item/storage/internal)) //Dump contents from dummy pockets.
|
||||
for(var/obj/item/SO in O)
|
||||
if(item_storage)
|
||||
SO.forceMove(item_storage)
|
||||
qdel(O)
|
||||
else if(item_storage)
|
||||
O.forceMove(item_storage)
|
||||
|
||||
qdel(src)
|
||||
return w_class
|
||||
|
||||
/////////////
|
||||
// Some indigestible stuff
|
||||
/////////////
|
||||
/obj/item/hand_tele/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/card/id/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/aicard/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/paicard/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/pinpointer/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/disk/nuclear/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/device/perfect_tele_beacon/digest_act(...)
|
||||
return FALSE //Sorta important to not digest your own beacons.
|
||||
/obj/item/device/pda/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/gun/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/clothing/shoes/magboots/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/clothing/head/helmet/space/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/clothing/suit/space/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/reagent_containers/hypospray/CMO/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/tank/jetpack/oxygen/captain/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/clothing/accessory/medal/gold/captain/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/clothing/suit/armor/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/documents/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/nuke_core/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/nuke_core_container/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/areaeditor/blueprints/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/documents/syndicate/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/bombcore/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/grenade/digest_act(...)
|
||||
return FALSE
|
||||
/obj/item/storage/digest_act(...)
|
||||
return FALSE
|
||||
|
||||
/////////////
|
||||
// Some special treatment
|
||||
/////////////
|
||||
/*
|
||||
//PDAs need to lose their ID to not take it with them, so we can get a digested ID
|
||||
/obj/item/device/pda/digest_act(var/atom/movable/item_storage = null)
|
||||
if(id)
|
||||
id = null
|
||||
|
||||
. = ..()
|
||||
*/
|
||||
|
||||
/obj/item/reagent_containers/food/digest_act(var/atom/movable/item_storage = null)
|
||||
if(isbelly(item_storage))
|
||||
var/obj/belly/B = item_storage
|
||||
if(ishuman(B.owner))
|
||||
var/mob/living/carbon/human/H = B.owner
|
||||
reagents.trans_to(H, (reagents.total_volume * 0.3), 1, 0)
|
||||
else if(iscyborg(B.owner))
|
||||
var/mob/living/silicon/robot/R = B.owner
|
||||
R.cell.charge += 150
|
||||
|
||||
. = ..()
|
||||
|
||||
/*
|
||||
/obj/item/holder/digest_act(var/atom/movable/item_storage = null)
|
||||
for(var/mob/living/M in contents)
|
||||
if(item_storage)
|
||||
M.forceMove(item_storage)
|
||||
held_mob = null
|
||||
|
||||
. = ..() */
|
||||
|
||||
/obj/item/organ/digest_act(var/atom/movable/item_storage = null)
|
||||
if((. = ..()))
|
||||
. += 70 //Organs give a little more
|
||||
|
||||
/obj/item/storage/digest_act(var/atom/movable/item_storage = null)
|
||||
for(var/obj/item/I in contents)
|
||||
I.screen_loc = null
|
||||
|
||||
. = ..()
|
||||
|
||||
/////////////
|
||||
// Some more complicated stuff
|
||||
/////////////
|
||||
/obj/item/device/mmi/digital/posibrain/digest_act(var/atom/movable/item_storage = null)
|
||||
//Replace this with a VORE setting so all types of posibrains can/can't be digested on a whim
|
||||
return FALSE
|
||||