initial commit - cross reference with 5th port - obviously has compile errors

This commit is contained in:
LetterJay
2016-07-03 02:17:19 -05:00
commit 35a1723e98
4355 changed files with 2221257 additions and 0 deletions
+155
View File
@@ -0,0 +1,155 @@
/*
Alternate Appearances! By RemieRichards
A framework for replacing an atom (and it's overlays) with an override = 1 image, that's less shit!
Example uses:
* hallucinating all mobs looking like skeletons
* people wearing cardborg suits appearing as Standard Cyborgs to the other Silicons
* !!use your imagination!!
*/
//This datum is built on-the-fly by some of the procs below
//no need to instantiate it
/datum/alternate_appearance
var/key = ""
var/image/img
var/list/viewers = list()
var/atom/owner = null
/*
Displays the alternate_appearance
displayTo - a list of MOBS to show this appearance to
*/
/datum/alternate_appearance/proc/display_to(list/displayTo)
if(!displayTo || !displayTo.len)
return
for(var/m in displayTo)
var/mob/M = m
if(!M.viewing_alternate_appearances)
M.viewing_alternate_appearances = list()
viewers |= M
M.viewing_alternate_appearances[key] = src
if(M.client)
M.client.images |= img
/*
Hides the alternate_appearance
hideFrom - optional list of MOBS to hide it from the list's mobs specifically
*/
/datum/alternate_appearance/proc/hide(list/hideFrom)
var/list/hiding = viewers
if(hideFrom)
hiding = hideFrom
for(var/m in hiding)
var/mob/M = m
if(M.client)
M.client.images -= img
if(M.viewing_alternate_appearances && M.viewing_alternate_appearances.len)
M.viewing_alternate_appearances -= key
if(!M.viewing_alternate_appearances.len)
M.viewing_alternate_appearances = null
viewers -= M
/*
Removes the alternate_appearance from its owner's alternate_appearances list, hiding it also
*/
/datum/alternate_appearance/proc/remove()
hide()
if(owner && owner.alternate_appearances)
owner.alternate_appearances -= key
if(!owner.alternate_appearances.len)
owner.alternate_appearances = null
/datum/alternate_appearance/Destroy()
remove()
return ..()
/atom
var/list/alternate_appearances //the alternate appearances we own
var/list/viewing_alternate_appearances //the alternate appearances we're viewing, stored here to reestablish them after Logout()s
//these lists are built/destroyed as necessary, so atoms aren't all lugging around lists full of datums
/*
Builds an alternate_appearance datum for the supplied args, optionally displaying it straight away
key - the key to the assoc list of key = /datum/alternate_appearances
img - the image file to be the "alternate appearance"
WORKS BEST IF:
* it has override = 1 set
* the image's loc is the atom that will use the appearance (otherwise... it's not exactly an alt appearance of this atom is it?)
displayTo - optional list of MOBS to display to immediately
Example:
var/image/I = image(icon = 'disguise.dmi', icon_state = "disguise", loc = src)
I.override = 1
add_alt_appearance("super_secret_disguise", I, players)
*/
/atom/proc/add_alt_appearance(key, img, list/displayTo = list())
if(!key || !img)
return
if(!alternate_appearances)
alternate_appearances = list()
var/datum/alternate_appearance/AA = new()
AA.img = img
AA.key = key
AA.owner = src
alternate_appearances[key] = AA
if(displayTo && displayTo.len)
display_alt_appearance(key, displayTo)
//////////////
// WRAPPERS //
//////////////
/*
Removes an alternate_appearance from src's alternate_appearances list
Wrapper for: alternate_appearance/remove()
key - the key to the assoc list of key = /datum/alternate_appearance
*/
/atom/proc/remove_alt_appearance(key)
if(alternate_appearances)
if(alternate_appearances[key])
var/datum/alternate_appearance/AA = alternate_appearances[key]
qdel(AA)
/*
Displays an alternate appearance from src's alternate_appearances list
Wrapper for: alternate_appearance/display_to()
key - the key to the assoc list of key = /datum/alternate_appearance
displayTo - a list of MOBS to show this appearance to
*/
/atom/proc/display_alt_appearance(key, list/displayTo)
if(!alternate_appearances || !key)
return
var/datum/alternate_appearance/AA = alternate_appearances[key]
if(!AA || !AA.img)
return
AA.display_to(displayTo)
/*
Hides an alternate appearance from src's alternate_appearances list
Wrapper for: alternate_appearance/hide()
key - the key to the assoc list of key = /datum/alternate_appearance
hideFrom - optional list of MOBS to hide it from the list's mobs specifically
*/
/atom/proc/hide_alt_appearance(key, list/hideFrom)
if(!alternate_appearances || !key)
return
var/datum/alternate_appearance/AA = alternate_appearances[key]
if(!AA)
return
AA.hide(hideFrom)
File diff suppressed because it is too large Load Diff
+24
View File
@@ -0,0 +1,24 @@
/area/ai_monitored
name = "AI Monitored Area"
var/obj/machinery/camera/motioncamera = null
/area/ai_monitored/New()
..()
// locate and store the motioncamera
spawn (20) // spawn on a delay to let turfs/objs load
for (var/obj/machinery/camera/M in src)
if(M.isMotion())
motioncamera = M
M.area_motion = src
/area/ai_monitored/Entered(atom/movable/O)
..()
if (ismob(O) && motioncamera)
motioncamera.newTarget(O)
/area/ai_monitored/Exited(atom/movable/O)
if (ismob(O) && motioncamera)
motioncamera.lostTarget(O)
+359
View File
@@ -0,0 +1,359 @@
// Areas.dm
// Added to fix mech fabs 05/2013 ~Sayu
// This is necessary due to lighting subareas. If you were to go in assuming that things in
// the same logical /area have the parent /area object... well, you would be mistaken. If you
// want to find machines, mobs, etc, in the same logical area, you will need to check all the
// related areas. This returns a master contents list to assist in that.
/proc/area_contents(var/area/A)
if(!istype(A)) return null
var/list/contents = list()
for(var/area/LSA in A.related)
contents += LSA.contents
return contents
// ===
/area
var/global/global_uid = 0
var/uid
var/list/ambientsounds = list('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg',\
'sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg',\
'sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg',\
'sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg',\
'sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg',\
'sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg')
/area/New()
icon_state = ""
layer = AREA_LAYER
master = src
uid = ++global_uid
related = list(src)
map_name = name // Save the initial (the name set in the map) name of the area.
if(requires_power)
luminosity = 0
else
power_light = 1
power_equip = 1
power_environ = 1
if (lighting_use_dynamic != DYNAMIC_LIGHTING_IFSTARLIGHT)
lighting_use_dynamic = DYNAMIC_LIGHTING_DISABLED
..()
power_change() // all machines set to current power level, also updates icon
blend_mode = BLEND_MULTIPLY // Putting this in the constructor so that it stops the icons being screwed up in the map editor.
/area/proc/poweralert(state, obj/source)
if (state != poweralm)
poweralm = state
if(istype(source)) //Only report power alarms on the z-level where the source is located.
var/list/cameras = list()
for (var/obj/machinery/camera/C in src)
cameras += C
for (var/mob/living/silicon/aiPlayer in player_list)
if (state == 1)
aiPlayer.cancelAlarm("Power", src, source)
else
aiPlayer.triggerAlarm("Power", src, cameras, source)
for(var/obj/machinery/computer/station_alert/a in machines)
if(state == 1)
a.cancelAlarm("Power", src, source)
else
a.triggerAlarm("Power", src, cameras, source)
for(var/mob/living/simple_animal/drone/D in mob_list)
if(state == 1)
D.cancelAlarm("Power", src, source)
else
D.triggerAlarm("Power", src, cameras, source)
/area/proc/atmosalert(danger_level, obj/source)
if(danger_level != atmosalm)
if (danger_level==2)
var/list/cameras = list()
for(var/area/RA in related)
for(var/obj/machinery/camera/C in RA)
cameras += C
for(var/mob/living/silicon/aiPlayer in player_list)
aiPlayer.triggerAlarm("Atmosphere", src, cameras, source)
for(var/obj/machinery/computer/station_alert/a in machines)
a.triggerAlarm("Atmosphere", src, cameras, source)
for(var/mob/living/simple_animal/drone/D in mob_list)
D.triggerAlarm("Atmosphere", src, cameras, source)
else if (src.atmosalm == 2)
for(var/mob/living/silicon/aiPlayer in player_list)
aiPlayer.cancelAlarm("Atmosphere", src, source)
for(var/obj/machinery/computer/station_alert/a in machines)
a.cancelAlarm("Atmosphere", src, source)
for(var/mob/living/simple_animal/drone/D in mob_list)
D.cancelAlarm("Atmosphere", src, source)
src.atmosalm = danger_level
return 1
return 0
/area/proc/firealert(obj/source)
if(always_unpowered == 1) //no fire alarms in space/asteroid
return
var/list/cameras = list()
for(var/area/RA in related)
if (!( RA.fire ))
RA.set_fire_alarm_effect()
for(var/obj/machinery/door/firedoor/D in RA)
if(!D.welded)
if(D.operating)
D.nextstate = CLOSED
else if(!D.density)
addtimer(D, "close", 0)
for(var/obj/machinery/firealarm/F in RA)
F.update_icon()
for (var/obj/machinery/camera/C in RA)
cameras += C
for (var/obj/machinery/computer/station_alert/a in machines)
a.triggerAlarm("Fire", src, cameras, source)
for (var/mob/living/silicon/aiPlayer in player_list)
aiPlayer.triggerAlarm("Fire", src, cameras, source)
for (var/mob/living/simple_animal/drone/D in mob_list)
D.triggerAlarm("Fire", src, cameras, source)
/area/proc/firereset(obj/source)
for(var/area/RA in related)
if (RA.fire)
RA.fire = 0
RA.mouse_opacity = 0
RA.updateicon()
for(var/obj/machinery/door/firedoor/D in RA)
if(!D.welded)
if(D.operating)
D.nextstate = OPEN
else if(D.density)
addtimer(D, "open", 0)
for(var/obj/machinery/firealarm/F in RA)
F.update_icon()
for (var/mob/living/silicon/aiPlayer in player_list)
aiPlayer.cancelAlarm("Fire", src, source)
for (var/obj/machinery/computer/station_alert/a in machines)
a.cancelAlarm("Fire", src, source)
for (var/mob/living/simple_animal/drone/D in mob_list)
D.cancelAlarm("Fire", src, source)
/area/proc/burglaralert(obj/trigger)
if(always_unpowered == 1) //no burglar alarms in space/asteroid
return
var/list/cameras = list()
for(var/area/RA in related)
//Trigger alarm effect
RA.set_fire_alarm_effect()
//Lockdown airlocks
for(var/obj/machinery/door/DOOR in RA)
spawn(0)
DOOR.close()
if(DOOR.density)
DOOR.lock()
for (var/obj/machinery/camera/C in RA)
cameras += C
for (var/mob/living/silicon/SILICON in player_list)
if(SILICON.triggerAlarm("Burglar", src, cameras, trigger))
//Cancel silicon alert after 1 minute
addtimer(SILICON, "cancelAlarm", 600, FALSE,"Burglar",src,trigger)
/area/proc/set_fire_alarm_effect()
fire = 1
updateicon()
mouse_opacity = 0
/area/proc/readyalert()
if(name == "Space")
return
if(!eject)
eject = 1
updateicon()
/area/proc/readyreset()
if(eject)
eject = 0
updateicon()
/area/proc/partyalert()
if(src.name == "Space") //no parties in space!!!
return
if (!( src.party ))
src.party = 1
src.updateicon()
src.mouse_opacity = 0
/area/proc/partyreset()
if (src.party)
src.party = 0
src.mouse_opacity = 0
src.updateicon()
for(var/obj/machinery/door/firedoor/D in src)
if(!D.welded)
if(D.operating)
D.nextstate = OPEN
else if(D.density)
addtimer(D, "open", 0)
/area/proc/updateicon()
if ((fire || eject || party) && (!requires_power||power_environ))//If it doesn't require power, can still activate this proc.
if(fire && !eject && !party)
icon_state = "blue"
else if(!fire && eject && !party)
icon_state = "red"
else if(party && !fire && !eject)
icon_state = "party"
else
icon_state = "blue-red"
invisibility = INVISIBILITY_LIGHTING
else
// new lighting behaviour with obj lights
icon_state = null
invisibility = INVISIBILITY_MAXIMUM
/area/space/updateicon()
icon_state = null
invisibility = INVISIBILITY_MAXIMUM
/*
#define EQUIP 1
#define LIGHT 2
#define ENVIRON 3
*/
/area/proc/powered(chan) // return true if the area has power to given channel
if(!master.requires_power)
return 1
if(master.always_unpowered)
return 0
switch(chan)
if(EQUIP)
return master.power_equip
if(LIGHT)
return master.power_light
if(ENVIRON)
return master.power_environ
return 0
/area/space/powered(chan) //Nope.avi
return 0
// called when power status changes
/area/proc/power_change()
for(var/area/RA in related)
for(var/obj/machinery/M in RA) // for each machine in the area
M.power_change() // reverify power status (to update icons etc.)
RA.updateicon()
/area/proc/usage(chan)
var/used = 0
switch(chan)
if(LIGHT)
used += master.used_light
if(EQUIP)
used += master.used_equip
if(ENVIRON)
used += master.used_environ
if(TOTAL)
used += master.used_light + master.used_equip + master.used_environ
if(STATIC_EQUIP)
used += master.static_equip
if(STATIC_LIGHT)
used += master.static_light
if(STATIC_ENVIRON)
used += master.static_environ
return used
/area/proc/addStaticPower(value, powerchannel)
switch(powerchannel)
if(STATIC_EQUIP)
static_equip += value
if(STATIC_LIGHT)
static_light += value
if(STATIC_ENVIRON)
static_environ += value
/area/proc/clear_usage()
master.used_equip = 0
master.used_light = 0
master.used_environ = 0
/area/proc/use_power(amount, chan)
switch(chan)
if(EQUIP)
master.used_equip += amount
if(LIGHT)
master.used_light += amount
if(ENVIRON)
master.used_environ += amount
/area/Entered(A)
if(!istype(A,/mob/living))
return
var/mob/living/L = A
if(!L.ckey)
return
// Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch
if(L.client && !L.client.ambience_playing && L.client.prefs.toggles & SOUND_SHIP_AMBIENCE)
L.client.ambience_playing = 1
L << sound('sound/ambience/shipambience.ogg', repeat = 1, wait = 0, volume = 35, channel = 2)
if(!(L.client && (L.client.prefs.toggles & SOUND_AMBIENCE)))
return //General ambience check is below the ship ambience so one can play without the other
if(prob(35))
var/sound = pick(ambientsounds)
if(!L.client.played)
L << sound(sound, repeat = 0, wait = 0, volume = 25, channel = 1)
L.client.played = 1
spawn(600) //ewww - this is very very bad
if(L.&& L.client)
L.client.played = 0
/proc/has_gravity(atom/AT, turf/T)
if(!T)
T = get_turf(AT)
var/area/A = get_area(T)
if(istype(T, /turf/open/space)) // Turf never has gravity
return 0
else if(A && A.has_gravity) // Areas which always has gravity
return 1
else
// There's a gravity generator on our z level
if(T && gravity_generators["[T.z]"] && length(gravity_generators["[T.z]"]))
return 1
return 0
/area/proc/setup(a_name)
name = a_name
power_equip = 0
power_light = 0
power_environ = 0
always_unpowered = 0
valid_territory = 0
addSorted()
+198
View File
@@ -0,0 +1,198 @@
var/global/list/possiblethemes = list("organharvest","cult","wizden","cavein","xenoden","hitech","speakeasy","plantlab")
var/global/max_secret_rooms = 6
/proc/spawn_room(atom/start_loc, x_size, y_size, list/walltypes, floor, name, oldarea)
var/list/room_turfs = list("walls"=list(),"floors"=list())
var/area/asteroid/artifactroom/A = new
if(name)
A.name = name
else
A.name = "Artifact Room #[start_loc.x]-[start_loc.y]-[start_loc.z]"
for(var/x = 0, x < x_size, x++) //sets the size of the room on the x axis
for(var/y = 0, y < y_size, y++) //sets it on y axis.
var/turf/T
var/cur_loc = locate(start_loc.x + x, start_loc.y + y, start_loc.z)
if(x == 0 || x == x_size-1 || y == 0 || y == y_size-1)
var/wall = pickweight(walltypes)//totally-solid walls are pretty boring.
T = cur_loc
T.ChangeTurf(wall)
room_turfs["walls"] += T
else
T = cur_loc
T.ChangeTurf(floor)
room_turfs["floors"] += T
if(!oldarea)
A.contents += T
return room_turfs
//////////////
/proc/make_mining_asteroid_secrets()
for(var/i in 1 to max_secret_rooms)
make_mining_asteroid_secret()
/proc/make_mining_asteroid_secret()
var/valid = 0
var/turf/T = null
var/sanity = 0
var/list/room = null
var/list/turfs = null
var/x_size = 5
var/y_size = 5
var/areapoints = 0
var/theme = "organharvest"
var/list/walltypes = list(/turf/closed/wall=3, /turf/closed/mineral/random=1)
var/list/floortypes = list(/turf/open/floor/plasteel)
var/list/treasureitems = list()//good stuff. only 1 is created per room.
var/list/fluffitems = list()//lesser items, to help fill out the room and enhance the theme.
x_size = rand(3,7)
y_size = rand(3,7)
areapoints = x_size * y_size
switch(pick(possiblethemes))//what kind of room is this gonna be?
if("organharvest")
walltypes = list(/turf/closed/wall/r_wall=2,/turf/closed/wall=2,/turf/closed/mineral/random/high_chance=1)
floortypes = list(/turf/open/floor/plasteel,/turf/open/floor/engine)
treasureitems = list(/mob/living/simple_animal/bot/medbot/mysterious=1, /obj/item/weapon/circular_saw=1, /obj/structure/closet/crate/critter=2, /mob/living/simple_animal/pet/cat/space=1)
fluffitems = list(/obj/effect/decal/cleanable/blood=5,/obj/item/organ/appendix=2,/obj/structure/closet/crate/freezer=2,
/obj/structure/table/optable=1,/obj/item/weapon/scalpel=1,/obj/item/weapon/storage/firstaid/regular=3,
/obj/item/weapon/tank/internals/anesthetic=1, /obj/item/weapon/surgical_drapes=2, /obj/item/device/mass_spectrometer/adv=1,/obj/item/clothing/glasses/hud/health=1)
if("cult")
theme = "cult"
walltypes = list(/turf/closed/wall/mineral/cult=3,/turf/closed/mineral/random/high_chance=1)
floortypes = list(/turf/open/floor/plasteel/cult)
treasureitems = list(/obj/item/device/soulstone/anybody=1, /obj/item/clothing/suit/space/hardsuit/cult=1, /obj/item/weapon/bedsheet/cult=2,
/obj/item/clothing/suit/cultrobes=2, /mob/living/simple_animal/hostile/creature=3)
fluffitems = list(/obj/effect/gateway=1,/obj/effect/gibspawner=1,/obj/structure/cult/talisman=1,/obj/item/toy/crayon/red=2,
/obj/item/organ/heart=2, /obj/effect/decal/cleanable/blood=4,/obj/structure/table/wood=2,/obj/item/weapon/ectoplasm=3,
/obj/item/clothing/shoes/cult=1)
if("wizden")
theme = "wizden"
walltypes = list(/turf/closed/wall/mineral/plasma=3,/turf/closed/mineral/random/high_chance=1)
floortypes = list(/turf/open/floor/wood)
treasureitems = list(/obj/item/weapon/veilrender/vealrender=2, /obj/item/weapon/spellbook/oneuse/blind=1,/obj/item/clothing/head/wizard/red=2,
/obj/item/weapon/spellbook/oneuse/forcewall=1, /obj/item/weapon/spellbook/oneuse/smoke=1, /obj/structure/constructshell = 1, /obj/item/toy/katana=3,/obj/item/voodoo=3)
fluffitems = list(/obj/structure/safe/floor=1,/obj/structure/dresser=1,/obj/item/weapon/storage/belt/soulstone=1,/obj/item/trash/candle=3,
/obj/item/weapon/dice=3,/obj/item/weapon/staff=2,/obj/effect/decal/cleanable/dirt=3,/obj/item/weapon/coin/mythril=3)
if("cavein")
theme = "cavein"
walltypes = list(/turf/closed/mineral/random/high_chance=1)
floortypes = list(/turf/open/floor/plating/asteroid/basalt, /turf/open/floor/plating/beach/sand)
treasureitems = list(/obj/mecha/working/ripley/mining=1, /obj/item/weapon/pickaxe/drill/diamonddrill=2,/obj/item/weapon/gun/energy/kinetic_accelerator/hyper=1,
/obj/item/weapon/resonator/upgraded=1, /obj/item/weapon/pickaxe/drill/jackhammer=5)
fluffitems = list(/obj/effect/decal/cleanable/blood=3,/obj/effect/decal/remains/human=1,/obj/item/clothing/under/overalls=1,
/obj/item/weapon/reagent_containers/food/snacks/grown/chili=1,/obj/item/weapon/tank/internals/oxygen/red=2)
if("xenoden")
theme = "xenoden"
walltypes = list(/turf/closed/mineral/random/high_chance=1)
floortypes = list(/turf/open/floor/plating/asteroid/basalt, /turf/open/floor/plating/beach/sand)
treasureitems = list(/obj/item/clothing/mask/facehugger=1)
fluffitems = list(/obj/effect/decal/remains/human=1,/obj/effect/decal/cleanable/xenoblood/xsplatter=5)
if("hitech")
theme = "hitech"
walltypes = list(/turf/closed/wall/r_wall=5,/turf/closed/mineral/random=1)
floortypes = list(/turf/open/floor/greengrid,/turf/open/floor/bluegrid)
treasureitems = list(/obj/item/weapon/stock_parts/cell/hyper=1, /obj/machinery/chem_dispenser/constructable=1,/obj/machinery/computer/telescience=1, /obj/machinery/r_n_d/protolathe=1,
/obj/machinery/biogenerator=1)
fluffitems = list(/obj/structure/table/reinforced=2,/obj/item/weapon/stock_parts/scanning_module/phasic=3,
/obj/item/weapon/stock_parts/matter_bin/super=3,/obj/item/weapon/stock_parts/manipulator/pico=3,
/obj/item/weapon/stock_parts/capacitor/super=3,/obj/item/device/pda/clear=1, /obj/structure/mecha_wreckage/phazon=1)
if("speakeasy")
theme = "speakeasy"
floortypes = list(/turf/open/floor/plasteel,/turf/open/floor/wood)
treasureitems = list(/obj/item/weapon/melee/energy/sword/pirate=1,/obj/item/weapon/gun/projectile/revolver/doublebarrel=1,/obj/item/weapon/storage/backpack/satchel_flat=1,
/obj/machinery/reagentgrinder=2, /obj/machinery/computer/security/wooden_tv=4, /obj/machinery/vending/coffee=3)
fluffitems = list(/obj/structure/table/wood=2,/obj/structure/reagent_dispensers/beerkeg=1,/obj/item/stack/spacecash/c500=4,
/obj/item/weapon/reagent_containers/food/drinks/shaker=1,/obj/item/weapon/reagent_containers/food/drinks/bottle/wine=3,
/obj/item/weapon/reagent_containers/food/drinks/bottle/whiskey=3,/obj/item/clothing/shoes/laceup=2)
if("plantlab")
theme = "plantlab"
treasureitems = list(/obj/item/weapon/gun/energy/floragun=1,/obj/item/seeds/sunflower/novaflower=2,/obj/item/seeds/tomato/blue/bluespace=2,/obj/item/seeds/tomato/blue=2,
/obj/item/seeds/coffee/robusta=2, /obj/item/seeds/cash=2)
fluffitems = list(/obj/item/weapon/twohanded/required/kirbyplants=1,/obj/structure/table/reinforced=2,/obj/machinery/hydroponics/constructable=1,
/obj/effect/glowshroom/single=2,/obj/item/weapon/reagent_containers/syringe/charcoal=2,
/obj/item/weapon/reagent_containers/glass/bottle/diethylamine=3,/obj/item/weapon/reagent_containers/glass/bottle/ammonia=3)
/*if("poly")
theme = "poly"
x_size = 5
y_size = 5
walltypes = list(/turf/closed/wall/mineral/clown)
floortypes= list(/turf/open/floor/engine)
treasureitems = list(/obj/item/weapon/spellbook=1,/obj/mecha/combat/marauder=1,/obj/machinery/wish_granter=1)
fluffitems = list(/obj/item/weapon/melee/energy/axe)*/
possiblethemes -= theme //once a theme is selected, it's out of the running!
var/floor = pick(floortypes)
turfs = get_area_turfs(/area/lavaland/surface/outdoors)
if(!turfs.len)
return 0
while(!valid)//Finds some spots to place these rooms at, where they won't be spotted immediately.
valid = 1
sanity++
if(sanity > 100)
return 0
T=pick(turfs)
if(!T)
return 0
var/list/surroundings = list()
surroundings += range(7, locate(T.x,T.y,T.z))
surroundings += range(7, locate(T.x+x_size,T.y,T.z))
surroundings += range(7, locate(T.x,T.y+y_size,T.z))
surroundings += range(7, locate(T.x+x_size,T.y+y_size,T.z))
for(var/turf/check in surroundings)
var/area/new_area = get_area(check)
if(!(istype(new_area, /area/lavaland/surface/outdoors)))
valid = FALSE
break
if(!T)
return 0
room = spawn_room(T,x_size,y_size,walltypes,floor,) //WE'RE FINALLY CREATING THE ROOM
if(room)//time to fill it with stuff
var/list/emptyturfs = room["floors"]
T = pick(emptyturfs)
if(T)
new /obj/effect/glowshroom/single(T) //Just to make it a little more visible
var/surprise = null
surprise = pickweight(treasureitems)
new surprise(T)//here's the prize
emptyturfs -= T
while(areapoints >= 10)//lets throw in the fluff items
T = pick(emptyturfs)
var/garbage = null
garbage = pickweight(fluffitems)
new garbage(T)
areapoints -= 5
emptyturfs -= T
//world.log << "The [theme] themed [T.loc] has been created!"
return 1
+438
View File
@@ -0,0 +1,438 @@
/atom
layer = TURF_LAYER
var/level = 2
var/flags = 0
var/list/fingerprints
var/list/fingerprintshidden
var/fingerprintslast = null
var/list/blood_DNA
///Chemistry.
var/datum/reagents/reagents = null
//This atom's HUD (med/sec, etc) images. Associative list.
var/list/image/hud_list = null
//HUD images that this atom can provide.
var/list/hud_possible
//Value used to increment ex_act() if reactionary_explosions is on
var/explosion_block = 0
//overlays that should remain on top and not normally be removed, like c4.
var/list/priority_overlays
/atom/Destroy()
if(alternate_appearances)
for(var/aakey in alternate_appearances)
var/datum/alternate_appearance/AA = alternate_appearances[aakey]
qdel(AA)
alternate_appearances = null
return ..()
/atom/proc/onCentcom()
var/turf/T = get_turf(src)
if(!T)
return 0
if(T.z != ZLEVEL_CENTCOM)//if not, don't bother
return 0
//check for centcomm shuttles
for(var/A in SSshuttle.mobile)
var/obj/docking_port/mobile/M = A
if(M.launch_status == ENDGAME_LAUNCHED && T in M.areaInstance)
return 1
//finally check for centcom itself
return istype(T.loc,/area/centcom)
/atom/proc/onSyndieBase()
var/turf/T = get_turf(src)
if(!T)
return 0
if(T.z != ZLEVEL_CENTCOM)//if not, don't bother
return 0
if(istype(T.loc,/area/shuttle/syndicate) || istype(T.loc,/area/syndicate_mothership))
return 1
return 0
/atom/proc/attack_hulk(mob/living/carbon/human/hulk, do_attack_animation = 0)
if(do_attack_animation)
hulk.changeNext_move(CLICK_CD_MELEE)
add_logs(hulk, src, "punched", "hulk powers")
hulk.do_attack_animation(src)
/atom/proc/CheckParts(list/parts_list)
for(var/A in parts_list)
if(istype(A, /datum/reagent))
if(!reagents)
reagents = new()
reagents.reagent_list.Add(A)
reagents.conditional_update()
else if(istype(A, /atom/movable))
var/atom/movable/M = A
if(istype(M.loc, /mob/living))
var/mob/living/L = M.loc
L.unEquip(M)
M.loc = src
/atom/proc/assume_air(datum/gas_mixture/giver)
qdel(giver)
return null
/atom/proc/remove_air(amount)
return null
/atom/proc/return_air()
if(loc)
return loc.return_air()
else
return null
/atom/proc/check_eye(mob/user)
return
/atom/proc/on_reagent_change()
return
/atom/proc/Bumped(AM as mob|obj)
return
// Convenience proc to see if a container is open for chemistry handling
// returns true if open
// false if closed
/atom/proc/is_open_container()
return flags & OPENCONTAINER
/*//Convenience proc to see whether a container can be accessed in a certain way.
/atom/proc/can_subract_container()
return flags & EXTRACT_CONTAINER
/atom/proc/can_add_container()
return flags & INSERT_CONTAINER
*/
/atom/proc/allow_drop()
return 1
/atom/proc/CheckExit()
return 1
/atom/proc/HasProximity(atom/movable/AM as mob|obj)
return
/atom/proc/emp_act(severity)
return
/atom/proc/bullet_act(obj/item/projectile/P, def_zone)
. = P.on_hit(src, 0, def_zone)
/atom/proc/in_contents_of(container)//can take class or object instance as argument
if(ispath(container))
if(istype(src.loc, container))
return 1
else if(src in container)
return 1
/*
* atom/proc/search_contents_for(path,list/filter_path=null)
* Recursevly searches all atom contens (including contents contents and so on).
*
* ARGS: path - search atom contents for atoms of this type
* list/filter_path - if set, contents of atoms not of types in this list are excluded from search.
*
* RETURNS: list of found atoms
*/
/atom/proc/search_contents_for(path,list/filter_path=null)
var/list/found = list()
for(var/atom/A in src)
if(istype(A, path))
found += A
if(filter_path)
var/pass = 0
for(var/type in filter_path)
pass |= istype(A, type)
if(!pass)
continue
if(A.contents.len)
found += A.search_contents_for(path,filter_path)
return found
/atom/proc/examine(mob/user)
//This reformat names to get a/an properly working on item descriptions when they are bloody
var/f_name = "\a [src]."
if(src.blood_DNA && !istype(src, /obj/effect/decal))
if(gender == PLURAL)
f_name = "some "
else
f_name = "a "
f_name += "<span class='danger'>blood-stained</span> [name]!"
user << "\icon[src] That's [f_name]"
if(desc)
user << desc
// *****RM
//user << "[name]: Dn:[density] dir:[dir] cont:[contents] icon:[icon] is:[icon_state] loc:[loc]"
if(reagents && is_open_container()) //is_open_container() isn't really the right proc for this, but w/e
user << "It contains:"
if(reagents.reagent_list.len)
if(user.can_see_reagents()) //Show each individual reagent
for(var/datum/reagent/R in reagents.reagent_list)
user << "[R.volume] units of [R.name]"
else //Otherwise, just show the total volume
var/total_volume = 0
for(var/datum/reagent/R in reagents.reagent_list)
total_volume += R.volume
user << "[total_volume] units of various reagents"
else
user << "Nothing."
/atom/proc/relaymove()
return
/atom/proc/contents_explosion(severity, target)
for(var/atom/A in contents)
A.ex_act(severity, target)
CHECK_TICK
/atom/proc/ex_act(severity, target)
contents_explosion(severity, target)
/atom/proc/blob_act(obj/effect/blob/B)
return
/atom/proc/fire_act()
return
/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked)
if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...).
spawn(2) //very short wait, so we can actually see the impact.
if(AM && isturf(AM.loc))
step(AM, turn(AM.dir, 180))
var/list/blood_splatter_icons = list()
/atom/proc/blood_splatter_index()
return "\ref[initial(icon)]-[initial(icon_state)]"
//returns the mob's dna info as a list, to be inserted in an object's blood_DNA list
/mob/living/proc/get_blood_dna_list()
if(get_blood_id() != "blood")
return
return list("ANIMAL DNA" = "Y-")
/mob/living/carbon/get_blood_dna_list()
if(get_blood_id() != "blood")
return
var/list/blood_dna = list()
if(dna)
blood_dna[dna.unique_enzymes] = dna.blood_type
else
blood_dna["UNKNOWN DNA"] = "X*"
return blood_dna
/mob/living/carbon/alien/get_blood_dna_list()
return list("UNKNOWN DNA" = "X*")
//to add a mob's dna info into an object's blood_DNA list.
/atom/proc/transfer_mob_blood_dna(mob/living/L)
// Returns 0 if we have that blood already
var/new_blood_dna = L.get_blood_dna_list()
if(!new_blood_dna)
return 0
if(!blood_DNA) //if our list of DNA doesn't exist yet, initialise it.
blood_DNA = list()
var/old_length = blood_DNA.len
blood_DNA |= new_blood_dna
if(blood_DNA.len == old_length)
return 0
return 1
//to add blood dna info to the object's blood_DNA list
/atom/proc/transfer_blood_dna(list/blood_dna)
if(!blood_DNA)
blood_DNA = list()
var/old_length = blood_DNA.len
blood_DNA |= blood_dna
if(blood_DNA.len > old_length)
return 1//some new blood DNA was added
//to add blood from a mob onto something, and transfer their dna info
/atom/proc/add_mob_blood(mob/living/M)
var/list/blood_dna = M.get_blood_dna_list()
if(!blood_dna)
return 0
return add_blood(blood_dna)
//to add blood onto something, with blood dna info to include.
/atom/proc/add_blood(list/blood_dna)
return 0
/obj/add_blood(list/blood_dna)
return transfer_blood_dna(blood_dna)
/obj/item/add_blood(list/blood_dna)
var/blood_count = !blood_DNA ? 0 : blood_DNA.len
if(!..())
return 0
if(!blood_count)//apply the blood-splatter overlay if it isn't already in there
add_blood_overlay()
return 1 //we applied blood to the item
/obj/item/proc/add_blood_overlay()
if(initial(icon) && initial(icon_state))
//try to find a pre-processed blood-splatter. otherwise, make a new one
var/index = blood_splatter_index()
var/icon/blood_splatter_icon = blood_splatter_icons[index]
if(!blood_splatter_icon)
blood_splatter_icon = icon(initial(icon), initial(icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object
blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent)
blood_splatter_icon.Blend(icon('icons/effects/blood.dmi', "itemblood"), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant
blood_splatter_icon = fcopy_rsc(blood_splatter_icon)
blood_splatter_icons[index] = blood_splatter_icon
add_overlay(blood_splatter_icon)
/obj/item/clothing/gloves/add_blood(list/blood_dna)
. = ..()
transfer_blood = rand(2, 4)
/turf/add_blood(list/blood_dna)
var/obj/effect/decal/cleanable/blood/splatter/B = locate() in src
if(!B)
B = new /obj/effect/decal/cleanable/blood/splatter(src)
B.transfer_blood_dna(blood_dna) //give blood info to the blood decal.
return 1 //we bloodied the floor
/mob/living/carbon/human/add_blood(list/blood_dna)
if(wear_suit)
wear_suit.add_blood(blood_dna)
update_inv_wear_suit()
else if(w_uniform)
w_uniform.add_blood(blood_dna)
update_inv_w_uniform()
if(gloves)
var/obj/item/clothing/gloves/G = gloves
G.add_blood(blood_dna)
else
transfer_blood_dna(blood_dna)
bloody_hands = rand(2, 4)
update_inv_gloves() //handles bloody hands overlays and updating
return 1
/atom/proc/clean_blood()
if(istype(blood_DNA, /list))
blood_DNA = null
return 1
/atom/proc/wash_cream()
return 1
/atom/proc/get_global_map_pos()
if(!islist(global_map) || isemptylist(global_map)) return
var/cur_x = null
var/cur_y = null
var/list/y_arr = null
for(cur_x=1,cur_x<=global_map.len,cur_x++)
y_arr = global_map[cur_x]
cur_y = y_arr.Find(src.z)
if(cur_y)
break
// world << "X = [cur_x]; Y = [cur_y]"
if(cur_x && cur_y)
return list("x"=cur_x,"y"=cur_y)
else
return 0
/atom/proc/isinspace()
if(istype(get_turf(src), /turf/open/space))
return 1
else
return 0
/atom/proc/handle_fall()
return
/atom/proc/handle_slip()
return
/atom/proc/singularity_act()
return
/atom/proc/singularity_pull()
return
/atom/proc/acid_act(acidpwr, toxpwr, acid_volume)
return
/atom/proc/emag_act()
return
/atom/proc/narsie_act()
return
/atom/proc/ratvar_act()
return
/atom/proc/storage_contents_dump_act(obj/item/weapon/storage/src_object, mob/user)
return 0
//This proc is called on the location of an atom when the atom is Destroy()'d
/atom/proc/handle_atom_del(atom/A)
// Byond seemingly calls stat, each tick.
// Calling things each tick can get expensive real quick.
// So we slow this down a little.
// See: http://www.byond.com/docs/ref/info.html#/client/proc/Stat
/atom/Stat()
. = ..()
sleep(1)
stoplag()
//This is called just before maps and objects are initialized, use it to spawn other mobs/objects
//effects at world start up without causing runtimes
/atom/proc/spawn_atom_to_world()
//This will be called after the map and objects are loaded
/atom/proc/initialize()
return
//the vision impairment to give to the mob whose perspective is set to that atom (e.g. an unfocused camera giving you an impaired vision when looking through it)
/atom/proc/get_remote_view_fullscreens(mob/user)
return
//the sight changes to give to the mob whose perspective is set to that atom (e.g. A mob with nightvision loses its nightvision while looking through a normal camera)
/atom/proc/update_remote_sight(mob/living/user)
return
/atom/proc/add_vomit_floor(mob/living/carbon/M, toxvomit = 0)
if(istype(src,/turf) )
var/obj/effect/decal/cleanable/vomit/V = PoolOrNew(/obj/effect/decal/cleanable/vomit, src)
// Make toxins vomit look different
if(toxvomit)
V.icon_state = "vomittox_[pick(1,4)]"
if(M.reagents)
clear_reagents_to_vomit_pool(M,V)
/atom/proc/clear_reagents_to_vomit_pool(mob/living/carbon/M, obj/effect/decal/cleanable/vomit/V)
M.reagents.trans_to(V, M.reagents.total_volume / 10)
for(var/datum/reagent/R in M.reagents.reagent_list) //clears the stomach of anything that might be digested as food
if(istype(R, /datum/reagent/consumable))
var/datum/reagent/consumable/nutri_check = R
if(nutri_check.nutriment_factor >0)
M.reagents.remove_reagent(R.id,R.volume)
//Hook for running code when a dir change occurs
/atom/proc/setDir(newdir)
dir = newdir
+366
View File
@@ -0,0 +1,366 @@
/atom/movable
layer = OBJ_LAYER
var/last_move = null
var/anchored = 0
var/throwing = 0
var/throw_speed = 2
var/throw_range = 7
var/mob/pulledby = null
var/languages_spoken = 0 //For say() and Hear()
var/languages_understood = 0
var/verb_say = "says"
var/verb_ask = "asks"
var/verb_exclaim = "exclaims"
var/verb_yell = "yells"
var/inertia_dir = 0
var/pass_flags = 0
var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move
glide_size = 8
appearance_flags = TILE_BOUND
/atom/movable/Move(atom/newloc, direct = 0)
if(!loc || !newloc) return 0
var/atom/oldloc = loc
if(loc != newloc)
if (!(direct & (direct - 1))) //Cardinal move
. = ..()
else //Diagonal move, split it into cardinal moves
moving_diagonally = FIRST_DIAG_STEP
if (direct & 1)
if (direct & 4)
if (step(src, NORTH))
moving_diagonally = SECOND_DIAG_STEP
. = step(src, EAST)
else if (step(src, EAST))
moving_diagonally = SECOND_DIAG_STEP
. = step(src, NORTH)
else if (direct & 8)
if (step(src, NORTH))
moving_diagonally = SECOND_DIAG_STEP
. = step(src, WEST)
else if (step(src, WEST))
moving_diagonally = SECOND_DIAG_STEP
. = step(src, NORTH)
else if (direct & 2)
if (direct & 4)
if (step(src, SOUTH))
moving_diagonally = SECOND_DIAG_STEP
. = step(src, EAST)
else if (step(src, EAST))
moving_diagonally = SECOND_DIAG_STEP
. = step(src, SOUTH)
else if (direct & 8)
if (step(src, SOUTH))
moving_diagonally = SECOND_DIAG_STEP
. = step(src, WEST)
else if (step(src, WEST))
moving_diagonally = SECOND_DIAG_STEP
. = step(src, SOUTH)
moving_diagonally = 0
if(!loc || (loc == oldloc && oldloc != newloc))
last_move = 0
return
if(.)
Moved(oldloc, direct)
last_move = direct
setDir(direct)
spawn(5) // Causes space drifting. /tg/station has no concept of speed, we just use 5
if(loc && direct && last_move == direct)
if(loc == newloc) //Remove this check and people can accelerate. Not opening that can of worms just yet.
newtonian_move(last_move)
if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc,direct)) //movement failed due to buckled mob(s)
. = 0
//Called after a successful Move(). By this point, we've already moved
/atom/movable/proc/Moved(atom/OldLoc, Dir)
return 1
/atom/movable/Destroy()
. = ..()
if(loc)
loc.handle_atom_del(src)
if(reagents)
qdel(reagents)
for(var/atom/movable/AM in contents)
qdel(AM)
loc = null
invisibility = INVISIBILITY_ABSTRACT
if(pulledby)
pulledby.stop_pulling()
// Previously known as HasEntered()
// This is automatically called when something enters your square
/atom/movable/Crossed(atom/movable/AM)
return
/atom/movable/Bump(atom/A, yes) //the "yes" arg is to differentiate our Bump proc from byond's, without it every Bump() call would become a double Bump().
if((A && yes))
if(throwing)
throwing = 0
throw_impact(A)
. = 1
if(!A || qdeleted(A))
return
A.Bumped(src)
/atom/movable/proc/forceMove(atom/destination)
if(destination)
if(pulledby)
pulledby.stop_pulling()
var/atom/oldloc = loc
if(oldloc)
oldloc.Exited(src, destination)
loc = destination
destination.Entered(src, oldloc)
var/area/old_area = get_area(oldloc)
var/area/destarea = get_area(destination)
if(old_area != destarea)
destarea.Entered(src)
for(var/atom/movable/AM in destination)
if(AM == src)
continue
AM.Crossed(src)
Moved(oldloc, 0)
return 1
return 0
/mob/living/forceMove(atom/destination)
stop_pulling()
if(buckled)
buckled.unbuckle_mob(src,force=1)
if(has_buckled_mobs())
unbuckle_all_mobs(force=1)
. = ..()
if(client)
reset_perspective(destination)
update_canmove() //if the mob was asleep inside a container and then got forceMoved out we need to make them fall.
/mob/living/carbon/brain/forceMove(atom/destination)
if(container)
container.forceMove(destination)
else //something went very wrong.
CRASH("Brainmob without container.")
/mob/living/silicon/pai/forceMove(atom/destination)
if(card)
card.forceMove(destination)
else //something went very wrong.
CRASH("pAI without card")
//Called whenever an object moves and by mobs when they attempt to move themselves through space
//And when an object or action applies a force on src, see newtonian_move() below
//Return 0 to have src start/keep drifting in a no-grav area and 1 to stop/not start drifting
//Mobs should return 1 if they should be able to move of their own volition, see client/Move() in mob_movement.dm
//movement_dir == 0 when stopping or any dir when trying to move
/atom/movable/proc/Process_Spacemove(movement_dir = 0)
if(has_gravity(src))
return 1
if(pulledby)
return 1
if(locate(/obj/structure/lattice) in range(1, get_turf(src))) //Not realistic but makes pushing things in space easier
return 1
return 0
/atom/movable/proc/newtonian_move(direction) //Only moves the object if it's under no gravity
if(!loc || Process_Spacemove(0))
inertia_dir = 0
return 0
inertia_dir = direction
if(!direction)
return 1
var/old_dir = dir
. = step(src, direction)
setDir(old_dir)
/atom/movable/proc/checkpass(passflag)
return pass_flags&passflag
/atom/movable/proc/throw_impact(atom/hit_atom)
return hit_atom.hitby(src)
/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = 1, blocked)
if(!anchored && hitpush)
step(src, AM.dir)
..()
/atom/movable/proc/throw_at_fast(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0)
set waitfor = 0
throw_at(target, range, speed, thrower, spin, diagonals_first)
/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0)
if(!target || !src || (flags & NODROP))
return 0
//use a modified version of Bresenham's algorithm to get from the atom's current position to that of the target
if(pulledby)
pulledby.stop_pulling()
throwing = 1
if(spin) //if we don't want the /atom/movable to spin.
SpinAnimation(5, 1)
var/dist_travelled = 0
var/next_sleep = 0
var/dist_x = abs(target.x - src.x)
var/dist_y = abs(target.y - src.y)
var/dx = (target.x > src.x) ? EAST : WEST
var/dy = (target.y > src.y) ? NORTH : SOUTH
var/pure_diagonal = 0
if(dist_x == dist_y)
pure_diagonal = 1
if(dist_x <= dist_y)
var/olddist_x = dist_x
var/olddx = dx
dist_x = dist_y
dist_y = olddist_x
dx = dy
dy = olddx
var/error = dist_x/2 - dist_y //used to decide whether our next move should be forward or diagonal.
var/atom/finalturf = get_turf(target)
var/hit = 0
var/init_dir = get_dir(src, target)
while(target && ((dist_travelled < range && loc != finalturf) || !has_gravity(src))) //stop if we reached our destination (or max range) and aren't floating
var/slept = 0
if(!istype(loc, /turf))
hit = 1
break
var/atom/step
if(dist_travelled < max(dist_x, dist_y)) //if we haven't reached the target yet we home in on it, otherwise we use the initial direction
step = get_step(src, get_dir(src, finalturf))
else
step = get_step(src, init_dir)
if(!pure_diagonal && !diagonals_first) // not a purely diagonal trajectory and we don't want all diagonal moves to be done first
if(error >= 0 && max(dist_x,dist_y) - dist_travelled != 1) //we do a step forward unless we're right before the target
step = get_step(src, dx)
error += (error < 0) ? dist_x/2 : -dist_y
if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge
break
Move(step, get_dir(loc, step))
if(!throwing) // we hit something during our move
hit = 1
break
dist_travelled++
if(dist_travelled > 600) //safety to prevent infinite while loop.
break
if(dist_travelled >= next_sleep)
slept = 1
next_sleep += speed
sleep(1)
if(!slept)
var/ticks_slept = TICK_CHECK
if(ticks_slept)
slept = 1
next_sleep += speed*(ticks_slept*world.tick_lag) //delay the next normal sleep
if(slept && hitcheck()) //to catch sneaky things moving on our tile while we slept
hit = 1
break
//done throwing, either because it hit something or it finished moving
throwing = 0
if(!hit)
for(var/atom/A in get_turf(src)) //looking for our target on the turf we land on.
if(A == target)
hit = 1
throw_impact(A)
return 1
throw_impact(get_turf(src)) // we haven't hit something yet and we still must, let's hit the ground.
return 1
/atom/movable/proc/hitcheck()
for(var/atom/movable/AM in get_turf(src))
if(AM == src)
continue
if(AM.density && !(AM.pass_flags & LETPASSTHROW) && !(AM.flags & ON_BORDER))
throwing = 0
throw_impact(AM)
return 1
//Overlays
/atom/movable/overlay
var/atom/master = null
anchored = 1
/atom/movable/overlay/New()
verbs.Cut()
/atom/movable/overlay/attackby(a, b, c)
if (src.master)
return src.master.attackby(a, b, c)
/atom/movable/overlay/attack_paw(a, b, c)
if (src.master)
return src.master.attack_paw(a, b, c)
/atom/movable/overlay/attack_hand(a, b, c)
if (src.master)
return src.master.attack_hand(a, b, c)
/atom/movable/proc/handle_buckled_mob_movement(newloc,direct)
for(var/m in buckled_mobs)
var/mob/living/buckled_mob = m
if(!buckled_mob.Move(newloc, direct))
loc = buckled_mob.loc
last_move = buckled_mob.last_move
inertia_dir = last_move
buckled_mob.inertia_dir = last_move
return 0
return 1
/atom/movable/CanPass(atom/movable/mover, turf/target, height=1.5)
if(mover in buckled_mobs)
return 1
return ..()
/atom/movable/proc/get_spacemove_backup()
var/atom/movable/dense_object_backup
for(var/A in orange(1, get_turf(src)))
if(isarea(A))
continue
else if(isturf(A))
var/turf/turf = A
if(!turf.density)
continue
return turf
else
var/atom/movable/AM = A
if(!AM.CanPass(src) || AM.density)
if(AM.anchored)
return AM
dense_object_backup = AM
break
. = dense_object_backup
//called when a mob resists while inside a container that is itself inside something.
/atom/movable/proc/relay_container_resist(mob/living/user, obj/O)
return
+302
View File
@@ -0,0 +1,302 @@
/*
HOW IT WORKS
The SSradio is a global object maintaining all radio transmissions, think about it as about "ether".
Note that walkie-talkie, intercoms and headsets handle transmission using nonstandard way.
procs:
add_object(obj/device as obj, var/new_frequency as num, var/filter as text|null = null)
Adds listening object.
parameters:
device - device receiving signals, must have proc receive_signal (see description below).
one device may listen several frequencies, but not same frequency twice.
new_frequency - see possibly frequencies below;
filter - thing for optimization. Optional, but recommended.
All filters should be consolidated in this file, see defines later.
Device without listening filter will receive all signals (on specified frequency).
Device with filter will receive any signals sent without filter.
Device with filter will not receive any signals sent with different filter.
returns:
Reference to frequency object.
remove_object (obj/device, old_frequency)
Obliviously, after calling this proc, device will not receive any signals on old_frequency.
Other frequencies will left unaffected.
return_frequency(var/frequency as num)
returns:
Reference to frequency object. Use it if you need to send and do not need to listen.
radio_frequency is a global object maintaining list of devices that listening specific frequency.
procs:
post_signal(obj/source as obj|null, datum/signal/signal, var/filter as text|null = null, var/range as num|null = null)
Sends signal to all devices that wants such signal.
parameters:
source - object, emitted signal. Usually, devices will not receive their own signals.
signal - see description below.
filter - described above.
range - radius of regular byond's square circle on that z-level. null means everywhere, on all z-levels.
obj/proc/receive_signal(datum/signal/signal, var/receive_method as num, var/receive_param)
Handler from received signals. By default does nothing. Define your own for your object.
Avoid of sending signals directly from this proc, use spawn(0). Do not use sleep() here please.
parameters:
signal - see description below. Extract all needed data from the signal before doing sleep(), spawn() or return!
receive_method - may be TRANSMISSION_WIRE or TRANSMISSION_RADIO.
TRANSMISSION_WIRE is currently unused.
receive_param - for TRANSMISSION_RADIO here comes frequency.
datum/signal
vars:
source
an object that emitted signal. Used for debug and bearing.
data
list with transmitting data. Usual use pattern:
data["msg"] = "hello world"
encryption
Some number symbolizing "encryption key".
Note that game actually do not use any cryptography here.
If receiving object don't know right key, it must ignore encrypted signal in its receive_signal.
*/
/* the radio controller is a confusing piece of shit and didnt work
so i made radios not use the radio controller.
*/
var/list/all_radios = list()
/proc/add_radio(obj/item/radio, freq)
if(!freq || !radio)
return
if(!all_radios["[freq]"])
all_radios["[freq]"] = list(radio)
return freq
all_radios["[freq]"] |= radio
return freq
/proc/remove_radio(obj/item/radio, freq)
if(!freq || !radio)
return
if(!all_radios["[freq]"])
return
all_radios["[freq]"] -= radio
/proc/remove_radio_all(obj/item/radio)
for(var/freq in all_radios)
all_radios["[freq]"] -= radio
/*
Frequency range: 1200 to 1600
Radiochat range: 1441 to 1489 (most devices refuse to be tune to other frequency, even during mapmaking)
Radio:
1459 - standard radio chat
1351 - Science
1353 - Command
1355 - Medical
1357 - Engineering
1359 - Security
1337 - death squad
1443 - Confession Intercom
1349 - Miners
1347 - Cargo techs
1447 - AI Private
Devices:
1451 - tracking implant
1457 - RSD default
On the map:
1311 for prison shuttle console (in fact, it is not used)
1435 for status displays
1437 for atmospherics/fire alerts
1439 for engine components
1439 for air pumps, air scrubbers, atmo control
1441 for atmospherics - supply tanks
1443 for atmospherics - distribution loop/mixed air tank
1445 for bot nav beacons
1447 for mulebot, secbot and ed209 control
1449 for airlock controls, electropack, magnets
1451 for toxin lab access
1453 for engineering access
1455 for AI access
*/
var/list/radiochannels = list(
"Common" = 1459,
"Science" = 1351,
"Command" = 1353,
"Medical" = 1355,
"Engineering" = 1357,
"Security" = 1359,
"Centcom" = 1337,
"Syndicate" = 1213,
"Supply" = 1347,
"Service" = 1349,
"AI Private" = 1447
)
var/list/radiochannelsreverse = list(
"1459" = "Common",
"1351" = "Science",
"1353" = "Command",
"1355" = "Medical",
"1357" = "Engineering",
"1359" = "Security",
"1337" = "Centcom",
"1213" = "Syndicate",
"1347" = "Supply",
"1349" = "Service",
"1447" = "AI Private"
)
//depenging helpers
var/const/SYND_FREQ = 1213 //nuke op frequency, coloured dark brown in chat window
var/const/SUPP_FREQ = 1347 //supply, coloured light brown in chat window
var/const/SERV_FREQ = 1349 //service, coloured green in chat window
var/const/SCI_FREQ = 1351 //science, coloured plum in chat window
var/const/COMM_FREQ = 1353 //command, colored gold in chat window
var/const/MED_FREQ = 1355 //medical, coloured blue in chat window
var/const/ENG_FREQ = 1357 //engineering, coloured orange in chat window
var/const/SEC_FREQ = 1359 //security, coloured red in chat window
var/const/CENTCOM_FREQ = 1337 //centcom frequency, coloured grey in chat window
var/const/AIPRIV_FREQ = 1447 //AI private, colored magenta in chat window
#define TRANSMISSION_WIRE 0
#define TRANSMISSION_RADIO 1
/* filters */
var/const/RADIO_TO_AIRALARM = "1"
var/const/RADIO_FROM_AIRALARM = "2"
var/const/RADIO_CHAT = "3" //deprecated
var/const/RADIO_ATMOSIA = "4"
var/const/RADIO_NAVBEACONS = "5"
var/const/RADIO_AIRLOCK = "6"
var/const/RADIO_MAGNETS = "9"
/datum/radio_frequency
var/frequency as num
var/list/list/obj/devices = list()
//If range > 0, only post to devices on the same z_level and within range
//Use range = -1, to restrain to the same z_level without limiting range
/datum/radio_frequency/proc/post_signal(obj/source as obj|null, datum/signal/signal, filter = null as text|null, range = null as num|null)
//Apply filter to the signal. If none supply, broadcast to every devices
//_default channel is always checked
var/list/filter_list
if(filter)
filter_list = list(filter,"_default")
else
filter_list = devices
//If checking range, find the source turf
var/turf/start_point
if(range)
start_point = get_turf(source)
if(!start_point)
return 0
//Send the data
for(var/current_filter in filter_list)
for(var/obj/device in devices[current_filter])
if(device == source)
continue
if(range)
var/turf/end_point = get_turf(device)
if(!end_point)
continue
if(start_point.z != end_point.z || (range > 0 && get_dist(start_point, end_point) > range))
continue
device.receive_signal(signal, TRANSMISSION_RADIO, frequency)
/datum/radio_frequency/proc/add_listener(obj/device, filter as text|null)
if (!filter)
filter = "_default"
var/list/devices_line = devices[filter]
if(!devices_line)
devices_line = list()
devices[filter] = devices_line
devices_line += device
/datum/radio_frequency/proc/remove_listener(obj/device)
for(var/devices_filter in devices)
var/list/devices_line = devices[devices_filter]
if(!devices_line)
devices -= devices_filter
devices_line -= device
if(!devices_line.len)
devices -= devices_filter
var/list/pointers = list()
/client/proc/print_pointers()
set name = "Debug Signals"
set category = "Debug"
if(!holder)
return
src << "There are [pointers.len] pointers:"
for(var/p in pointers)
src << p
var/datum/signal/S = locate(p)
if(istype(S))
src << S.debug_print()
/obj/proc/receive_signal(datum/signal/signal, receive_method, receive_param)
return
/datum/signal
var/obj/source
var/transmission_method = 0
//0 = wire
//1 = radio transmission
//2 = subspace transmission
var/data = list()
var/encryption
var/frequency = 0
/datum/signal/New()
..()
pointers += "\ref[src]"
/datum/signal/Destroy()
pointers -= "\ref[src]"
return ..()
/datum/signal/proc/copy_from(datum/signal/model)
source = model.source
transmission_method = model.transmission_method
data = model.data
encryption = model.encryption
frequency = model.frequency
/datum/signal/proc/debug_print()
if (source)
. = "signal = {source = '[source]' ([source:x],[source:y],[source:z])\n"
else
. = "signal = {source = '[source]' ()\n"
for (var/i in data)
. += "data\[\"[i]\"\] = \"[data[i]]\"\n"
if(islist(data[i]))
var/list/L = data[i]
for(var/t in L)
. += "data\[\"[i]\"\] list has: [t]"
/datum/signal/proc/sanitize_data()
for(var/d in data)
var/val = data[d]
if(istext(val))
data[d] = html_encode(val)
+324
View File
@@ -0,0 +1,324 @@
/*
* Data HUDs have been rewritten in a more generic way.
* In short, they now use an observer-listener pattern.
* See code/datum/hud.dm for the generic hud datum.
* Update the HUD icons when needed with the appropriate hook. (see below)
*/
/* DATA HUD DATUMS */
/atom/proc/add_to_all_human_data_huds()
for(var/datum/atom_hud/data/human/hud in huds) hud.add_to_hud(src)
/atom/proc/remove_from_all_data_huds()
for(var/datum/atom_hud/data/hud in huds) hud.remove_from_hud(src)
/datum/atom_hud/data
/datum/atom_hud/data/human/medical
hud_icons = list(STATUS_HUD, HEALTH_HUD)
/datum/atom_hud/data/human/medical/basic
/datum/atom_hud/data/human/medical/basic/proc/check_sensors(mob/living/carbon/human/H)
if(!istype(H)) return 0
var/obj/item/clothing/under/U = H.w_uniform
if(!istype(U)) return 0
if(U.sensor_mode <= 2) return 0
return 1
/datum/atom_hud/data/human/medical/basic/add_to_single_hud(mob/M, mob/living/carbon/H)
if(check_sensors(H))
..()
/datum/atom_hud/data/human/medical/basic/proc/update_suit_sensors(mob/living/carbon/H)
check_sensors(H) ? add_to_hud(H) : remove_from_hud(H)
/datum/atom_hud/data/human/medical/advanced
/datum/atom_hud/data/human/security
/datum/atom_hud/data/human/security/basic
hud_icons = list(ID_HUD)
/datum/atom_hud/data/human/security/advanced
hud_icons = list(ID_HUD, IMPTRACK_HUD, IMPLOYAL_HUD, IMPCHEM_HUD, WANTED_HUD)
/datum/atom_hud/data/diagnostic
hud_icons = list (DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD)
/* MED/SEC/DIAG HUD HOOKS */
/*
* THESE HOOKS SHOULD BE CALLED BY THE MOB SHOWING THE HUD
*/
/***********************************************
Medical HUD! Basic mode needs suit sensors on.
************************************************/
//HELPERS
//called when a carbon changes virus
/mob/living/carbon/proc/check_virus()
for(var/datum/disease/D in viruses)
if((!(D.visibility_flags & HIDDEN_SCANNER)) && (D.severity != NONTHREAT))
return 1
return 0
//helper for getting the appropriate health status
/proc/RoundHealth(mob/living/M)
if(M.stat == DEAD || (M.status_flags & FAKEDEATH))
return "health-100" //what's our health? it doesn't matter, we're dead, or faking
var/maxhealth = M.maxHealth
if(iscarbon(M) && M.health < 0)
maxhealth = 100 //so crit shows up right for aliens and other high-health carbon mobs; noncarbons don't have crit.
var/resulthealth = (M.health / maxhealth) * 100
switch(resulthealth)
if(100 to INFINITY)
return "health100"
if(90.625 to 100)
return "health93.75"
if(84.375 to 90.625)
return "health87.5"
if(78.125 to 84.375)
return "health81.25"
if(71.875 to 78.125)
return "health75"
if(65.625 to 71.875)
return "health68.75"
if(59.375 to 65.625)
return "health62.5"
if(53.125 to 59.375)
return "health56.25"
if(46.875 to 53.125)
return "health50"
if(40.625 to 46.875)
return "health43.75"
if(34.375 to 40.625)
return "health37.5"
if(28.125 to 34.375)
return "health31.25"
if(21.875 to 28.125)
return "health25"
if(15.625 to 21.875)
return "health18.75"
if(9.375 to 15.625)
return "health12.5"
if(1 to 9.375)
return "health6.25"
if(-50 to 1)
return "health0"
if(-85 to -50)
return "health-50"
if(-99 to -85)
return "health-85"
else
return "health-100"
return "0"
//HOOKS
//called when a human changes suit sensors
/mob/living/carbon/proc/update_suit_sensors()
var/datum/atom_hud/data/human/medical/basic/B = huds[DATA_HUD_MEDICAL_BASIC]
B.update_suit_sensors(src)
var/turf/T = get_turf(src)
if (T) crewmonitor.queueUpdate(T.z)
//called when a living mob changes health
/mob/living/proc/med_hud_set_health()
var/image/holder = hud_list[HEALTH_HUD]
holder.icon_state = "hud[RoundHealth(src)]"
//for carbon suit sensors
/mob/living/carbon/med_hud_set_health()
..()
var/turf/T = get_turf(src)
if(T)
crewmonitor.queueUpdate(T.z)
//called when a carbon changes stat, virus or XENO_HOST
/mob/living/proc/med_hud_set_status()
var/image/holder = hud_list[STATUS_HUD]
if(stat == DEAD || (status_flags & FAKEDEATH))
holder.icon_state = "huddead"
else
holder.icon_state = "hudhealthy"
/mob/living/carbon/med_hud_set_status()
var/image/holder = hud_list[STATUS_HUD]
if(status_flags & XENO_HOST)
holder.icon_state = "hudxeno"
else if(stat == DEAD || (status_flags & FAKEDEATH))
holder.icon_state = "huddead"
else if(check_virus())
holder.icon_state = "hudill"
else
holder.icon_state = "hudhealthy"
/***********************************************
Security HUDs! Basic mode shows only the job.
************************************************/
//HOOKS
/mob/living/carbon/human/proc/sec_hud_set_ID()
var/image/holder = hud_list[ID_HUD]
holder.icon_state = "hudno_id"
if(wear_id)
holder.icon_state = "hud[ckey(wear_id.GetJobName())]"
sec_hud_set_security_status()
var/turf/T = get_turf(src)
if (T) crewmonitor.queueUpdate(T.z)
/mob/living/carbon/human/proc/sec_hud_set_implants()
var/image/holder
for(var/i in list(IMPTRACK_HUD, IMPLOYAL_HUD, IMPCHEM_HUD))
holder = hud_list[i]
holder.icon_state = null
for(var/obj/item/weapon/implant/I in src)
if(I.implanted)
if(istype(I,/obj/item/weapon/implant/tracking))
holder = hud_list[IMPTRACK_HUD]
holder.icon_state = "hud_imp_tracking"
else if(istype(I,/obj/item/weapon/implant/mindshield))
holder = hud_list[IMPLOYAL_HUD]
holder.icon_state = "hud_imp_loyal"
else if(istype(I,/obj/item/weapon/implant/chem))
holder = hud_list[IMPCHEM_HUD]
holder.icon_state = "hud_imp_chem"
/mob/living/carbon/human/proc/sec_hud_set_security_status()
var/image/holder = hud_list[WANTED_HUD]
var/perpname = get_face_name(get_id_name(""))
if(perpname)
var/datum/data/record/R = find_record("name", perpname, data_core.security)
if(R)
switch(R.fields["criminal"])
if("*Arrest*")
holder.icon_state = "hudwanted"
return
if("Incarcerated")
holder.icon_state = "hudincarcerated"
return
if("Parolled")
holder.icon_state = "hudparolled"
return
if("Discharged")
holder.icon_state = "huddischarged"
return
holder.icon_state = null
/***********************************************
Diagnostic HUDs!
************************************************/
//For Diag health and cell bars!
/proc/RoundDiagBar(value)
switch(value * 100)
if(95 to INFINITY)
return "max"
if(80 to 100)
return "good"
if(60 to 80)
return "high"
if(40 to 60)
return "med"
if(20 to 40)
return "low"
if(1 to 20)
return "crit"
else
return "dead"
return "dead"
//Sillycone hooks
/mob/living/silicon/proc/diag_hud_set_health()
var/image/holder = hud_list[DIAG_HUD]
if(stat == DEAD)
holder.icon_state = "huddiagdead"
else
holder.icon_state = "huddiag[RoundDiagBar(health/maxHealth)]"
/mob/living/silicon/proc/diag_hud_set_status()
var/image/holder = hud_list[DIAG_STAT_HUD]
switch(stat)
if(CONSCIOUS)
holder.icon_state = "hudstat"
if(UNCONSCIOUS)
holder.icon_state = "hudoffline"
else
holder.icon_state = "huddead2"
//Borgie battery tracking!
/mob/living/silicon/robot/proc/diag_hud_set_borgcell()
var/image/holder = hud_list[DIAG_BATT_HUD]
if (cell)
var/chargelvl = (cell.charge/cell.maxcharge)
holder.icon_state = "hudbatt[RoundDiagBar(chargelvl)]"
else
holder.icon_state = "hudnobatt"
/*~~~~~~~~~~~~~~~~~~~~
BIG STOMPY MECHS
~~~~~~~~~~~~~~~~~~~~~*/
/obj/mecha/proc/diag_hud_set_mechhealth()
var/image/holder = hud_list[DIAG_MECH_HUD]
holder.icon_state = "huddiag[RoundDiagBar(health/initial(health))]"
/obj/mecha/proc/diag_hud_set_mechcell()
var/image/holder = hud_list[DIAG_BATT_HUD]
if (cell)
var/chargelvl = cell.charge/cell.maxcharge
holder.icon_state = "hudbatt[RoundDiagBar(chargelvl)]"
else
holder.icon_state = "hudnobatt"
/obj/mecha/proc/diag_hud_set_mechstat()
var/image/holder = hud_list[DIAG_STAT_HUD]
holder.icon_state = null
if(internal_damage)
holder.icon_state = "hudwarn"
/*~~~~~~~~~
Bots!
~~~~~~~~~~*/
/mob/living/simple_animal/bot/proc/diag_hud_set_bothealth()
var/image/holder = hud_list[DIAG_HUD]
holder.icon_state = "huddiag[RoundDiagBar(health/maxHealth)]"
/mob/living/simple_animal/bot/proc/diag_hud_set_botstat() //On (With wireless on or off), Off, EMP'ed
var/image/holder = hud_list[DIAG_STAT_HUD]
if(on)
holder.icon_state = "hudstat"
else if(stat) //Generally EMP causes this
holder.icon_state = "hudoffline"
else //Bot is off
holder.icon_state = "huddead2"
/mob/living/simple_animal/bot/proc/diag_hud_set_botmode() //Shows a bot's current operation
var/image/holder = hud_list[DIAG_BOT_HUD]
if(client) //If the bot is player controlled, it will not be following mode logic!
holder.icon_state = "hudsentient"
return
switch(mode)
if(BOT_SUMMON, BOT_RESPONDING) //Responding to PDA or AI summons
holder.icon_state = "hudcalled"
if(BOT_CLEANING, BOT_REPAIRING, BOT_HEALING) //Cleanbot cleaning, Floorbot fixing, or Medibot Healing
holder.icon_state = "hudworking"
if(BOT_PATROL, BOT_START_PATROL) //Patrol mode
holder.icon_state = "hudpatrol"
if(BOT_PREP_ARREST, BOT_ARREST, BOT_HUNT) //STOP RIGHT THERE, CRIMINAL SCUM!
holder.icon_state = "hudalert"
if(BOT_MOVING, BOT_DELIVER, BOT_GO_HOME, BOT_NAV) //Moving to target for normal bots, moving to deliver or go home for MULES.
holder.icon_state = "hudmove"
else
holder.icon_state = ""
+57
View File
@@ -0,0 +1,57 @@
/datum/atom_hud/antag
hud_icons = list(ANTAG_HUD)
var/self_visible = 1
/datum/atom_hud/antag/hidden
self_visible = 0
/datum/atom_hud/antag/proc/join_hud(mob/M)
//sees_hud should be set to 0 if the mob does not get to see it's own hud type.
if(!istype(M))
CRASH("join_hud(): [M] ([M.type]) is not a mob!")
if(M.mind.antag_hud) //note: please let this runtime if a mob has no mind, as mindless mobs shouldn't be getting antagged
M.mind.antag_hud.leave_hud(M)
add_to_hud(M)
if(self_visible)
add_hud_to(M)
M.mind.antag_hud = src
/datum/atom_hud/antag/proc/leave_hud(mob/M)
if(!M)
return
if(!istype(M))
CRASH("leave_hud(): [M] ([M.type]) is not a mob!")
remove_from_hud(M)
remove_hud_from(M)
if(M.mind)
M.mind.antag_hud = null
//GAME_MODE PROCS
//called to set a mob's antag icon state
/datum/game_mode/proc/set_antag_hud(mob/M, new_icon_state)
if(!istype(M))
CRASH("set_antag_hud(): [M] ([M.type]) is not a mob!")
var/image/holder = M.hud_list[ANTAG_HUD]
if(holder)
holder.icon_state = new_icon_state
if(M.mind || new_icon_state) //in mindless mobs, only null is acceptable, otherwise we're antagging a mindless mob, meaning we should runtime
M.mind.antag_hud_icon_state = new_icon_state
//MIND PROCS
//these are called by mind.transfer_to()
/datum/mind/proc/transfer_antag_huds(var/datum/atom_hud/antag/newhud)
leave_all_huds()
ticker.mode.set_antag_hud(current, antag_hud_icon_state)
if(newhud)
newhud.join_hud(current)
/datum/mind/proc/leave_all_huds()
for(var/datum/atom_hud/antag/hud in huds)
if(current in hud.hudusers)
hud.leave_hud(current)
for(var/datum/atom_hud/data/hud in huds)
if(current in hud.hudusers)
hud.remove_hud_from(current)
+294
View File
@@ -0,0 +1,294 @@
/obj/item/weapon/antag_spawner
throw_speed = 1
throw_range = 5
w_class = 1
var/used = 0
/obj/item/weapon/antag_spawner/proc/spawn_antag(client/C, turf/T, type = "")
return
/obj/item/weapon/antag_spawner/proc/equip_antag(mob/target)
return
///////////WIZARD
/obj/item/weapon/antag_spawner/contract
name = "contract"
desc = "A magic contract previously signed by an apprentice. In exchange for instruction in the magical arts, they are bound to answer your call for aid."
icon = 'icons/obj/wizard.dmi'
icon_state ="scroll2"
/obj/item/weapon/antag_spawner/contract/attack_self(mob/user)
user.set_machine(src)
var/dat
if(used)
dat = "<B>You have already summoned your apprentice.</B><BR>"
else
dat = "<B>Contract of Apprenticeship:</B><BR>"
dat += "<I>Using this contract, you may summon an apprentice to aid you on your mission.</I><BR>"
dat += "<I>If you are unable to establish contact with your apprentice, you can feed the contract back to the spellbook to refund your points.</I><BR>"
dat += "<B>Which school of magic is your apprentice studying?:</B><BR>"
dat += "<A href='byond://?src=\ref[src];school=destruction'>Destruction</A><BR>"
dat += "<I>Your apprentice is skilled in offensive magic. They know Magic Missile and Fireball.</I><BR>"
dat += "<A href='byond://?src=\ref[src];school=bluespace'>Bluespace Manipulation</A><BR>"
dat += "<I>Your apprentice is able to defy physics, melting through solid objects and travelling great distances in the blink of an eye. They know Teleport and Ethereal Jaunt.</I><BR>"
dat += "<A href='byond://?src=\ref[src];school=healing'>Healing</A><BR>"
dat += "<I>Your apprentice is training to cast spells that will aid your survival. They know Forcewall and Charge and come with a Staff of Healing.</I><BR>"
dat += "<A href='byond://?src=\ref[src];school=robeless'>Robeless</A><BR>"
dat += "<I>Your apprentice is training to cast spells without their robes. They know Knock and Mindswap.</I><BR>"
user << browse(dat, "window=radio")
onclose(user, "radio")
return
/obj/item/weapon/antag_spawner/contract/Topic(href, href_list)
..()
var/mob/living/carbon/human/H = usr
if(H.stat || H.restrained())
return
if(!istype(H, /mob/living/carbon/human))
return 1
if(loc == H || (in_range(src, H) && istype(loc, /turf)))
H.set_machine(src)
if(href_list["school"])
if (used)
H << "You already used this contract!"
return
var/list/candidates = get_candidates(ROLE_WIZARD)
if(candidates.len)
src.used = 1
var/client/C = pick(candidates)
spawn_antag(C, get_turf(H.loc), href_list["school"])
if(H.mind)
ticker.mode.update_wiz_icons_added(H.mind)
else
H << "Unable to reach your apprentice! You can either attack the spellbook with the contract to refund your points, or wait and try again later."
/obj/item/weapon/antag_spawner/contract/spawn_antag(client/C, turf/T, type = "")
PoolOrNew(/obj/effect/particle_effect/smoke, T)
var/mob/living/carbon/human/M = new/mob/living/carbon/human(T)
C.prefs.copy_to(M)
M.key = C.key
M << "<B>You are the [usr.real_name]'s apprentice! You are bound by magic contract to follow their orders and help them in accomplishing their goals."
switch(type)
if("destruction")
M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile(null))
M.mind.AddSpell(new /obj/effect/proc_holder/spell/dumbfire/fireball(null))
M << "<B>Your service has not gone unrewarded, however. Studying under [usr.real_name], you have learned powerful, destructive spells. You are able to cast magic missile and fireball."
if("bluespace")
M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/area_teleport/teleport(null))
M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(null))
M << "<B>Your service has not gone unrewarded, however. Studying under [usr.real_name], you have learned reality bending mobility spells. You are able to cast teleport and ethereal jaunt."
if("healing")
M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/charge(null))
M.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/conjure/forcewall(null))
M.equip_to_slot_or_del(new /obj/item/weapon/gun/magic/staff/healing(M), slot_r_hand)
M << "<B>Your service has not gone unrewarded, however. Studying under [usr.real_name], you have learned livesaving survival spells. You are able to cast charge and forcewall."
if("robeless")
M.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null))
M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/mind_transfer(null))
M << "<B>Your service has not gone unrewarded, however. Studying under [usr.real_name], you have learned stealthy, robeless spells. You are able to cast knock and mindswap."
equip_antag(M)
var/wizard_name_first = pick(wizard_first)
var/wizard_name_second = pick(wizard_second)
var/randomname = "[wizard_name_first] [wizard_name_second]"
var/datum/objective/protect/new_objective = new /datum/objective/protect
new_objective.owner = M:mind
new_objective:target = usr:mind
new_objective.explanation_text = "Protect [usr.real_name], the wizard."
M.mind.objectives += new_objective
ticker.mode.apprentices += M.mind
M.mind.special_role = "apprentice"
ticker.mode.update_wiz_icons_added(M.mind)
M << sound('sound/effects/magic.ogg')
var/newname = copytext(sanitize(input(M, "You are the wizard's apprentice. Would you like to change your name to something else?", "Name change", randomname) as null|text),1,MAX_NAME_LEN)
if (!newname)
newname = randomname
M.mind.name = newname
M.real_name = newname
M.name = newname
M.dna.update_dna_identity()
/obj/item/weapon/antag_spawner/contract/equip_antag(mob/target)
target.equip_to_slot_or_del(new /obj/item/device/radio/headset(target), slot_ears)
target.equip_to_slot_or_del(new /obj/item/clothing/under/color/lightpurple(target), slot_w_uniform)
target.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(target), slot_shoes)
target.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe(target), slot_wear_suit)
target.equip_to_slot_or_del(new /obj/item/clothing/head/wizard(target), slot_head)
target.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack(target), slot_back)
target.equip_to_slot_or_del(new /obj/item/weapon/storage/box(target), slot_in_backpack)
target.equip_to_slot_or_del(new /obj/item/weapon/teleportation_scroll/apprentice(target), slot_r_store)
///////////BORGS AND OPERATIVES
/obj/item/weapon/antag_spawner/nuke_ops
name = "syndicate operative teleporter"
desc = "A single-use teleporter designed to quickly reinforce operatives in the field."
icon = 'icons/obj/device.dmi'
icon_state = "locator"
var/borg_to_spawn
var/list/possible_types = list("Assault", "Medical")
/obj/item/weapon/antag_spawner/nuke_ops/proc/check_usability(mob/user)
if(used)
user << "<span class='warning'>[src] is out of power!</span>"
return 0
if(!(user.mind in ticker.mode.syndicates))
user << "<span class='danger'>AUTHENTICATION FAILURE. ACCESS DENIED.</span>"
return 0
if(user.z != ZLEVEL_CENTCOM)
user << "<span class='warning'>[src] is out of range! It can only be used at your base!</span>"
return 0
return 1
/obj/item/weapon/antag_spawner/nuke_ops/attack_self(mob/user)
if(!(check_usability(user)))
return
var/list/nuke_candidates = get_candidates(ROLE_OPERATIVE, 3000, "operative")
if(nuke_candidates.len > 0)
used = 1
var/client/C = pick(nuke_candidates)
spawn_antag(C, get_turf(src.loc), "syndieborg")
var/datum/effect_system/spark_spread/S = new /datum/effect_system/spark_spread
S.set_up(4, 1, src)
S.start()
qdel(src)
else
user << "<span class='warning'>Unable to connect to Syndicate command. Please wait and try again later or use the teleporter on your uplink to get your points refunded.</span>"
/obj/item/weapon/antag_spawner/nuke_ops/spawn_antag(client/C, turf/T)
var/new_op_code = "Ask your leader!"
var/mob/living/carbon/human/M = new/mob/living/carbon/human(T)
C.prefs.copy_to(M)
M.key = C.key
var/obj/machinery/nuclearbomb/nuke = locate("syndienuke") in nuke_list
if(nuke)
new_op_code = nuke.r_code
M.mind.make_Nuke(T, new_op_code, 0, FALSE)
var/newname = M.dna.species.random_name(M.gender,0,ticker.mode.nukeops_lastname)
M.mind.name = newname
M.real_name = newname
M.name = newname
//////SYNDICATE BORG
/obj/item/weapon/antag_spawner/nuke_ops/borg_tele
name = "syndicate cyborg teleporter"
desc = "A single-use teleporter designed to quickly reinforce operatives in the field.."
icon = 'icons/obj/device.dmi'
icon_state = "locator"
/obj/item/weapon/antag_spawner/nuke_ops/borg_tele/attack_self(mob/user)
borg_to_spawn = input("What type?", "Cyborg Type", type) as null|anything in possible_types
if(!borg_to_spawn)
return
..()
/obj/item/weapon/antag_spawner/nuke_ops/borg_tele/spawn_antag(client/C, turf/T)
var/mob/living/silicon/robot/R
switch(borg_to_spawn)
if("Medical")
R = new /mob/living/silicon/robot/syndicate/medical(T)
else
R = new /mob/living/silicon/robot/syndicate(T) //Assault borg by default
var/brainfirstname = pick(first_names_male)
if(prob(50))
brainfirstname = pick(first_names_female)
var/brainopslastname = pick(last_names)
if(ticker.mode.nukeops_lastname) //the brain inside the syndiborg has the same last name as the other ops.
brainopslastname = ticker.mode.nukeops_lastname
var/brainopsname = "[brainfirstname] [brainopslastname]"
R.mmi.name = "Man-Machine Interface: [brainopsname]"
R.mmi.brain.name = "[brainopsname]'s brain"
R.mmi.brainmob.real_name = brainopsname
R.mmi.brainmob.name = brainopsname
R.key = C.key
ticker.mode.syndicates += R.mind
ticker.mode.update_synd_icons_added(R.mind)
R.mind.special_role = "syndicate"
R.faction = list("syndicate")
///////////SLAUGHTER DEMON
/obj/item/weapon/antag_spawner/slaughter_demon //Warning edgiest item in the game
name = "vial of blood"
desc = "A magically infused bottle of blood, distilled from countless murder victims. Used in unholy rituals to attract horrifying creatures."
icon = 'icons/obj/wizard.dmi'
icon_state = "vial"
var/shatter_msg = "<span class='notice'>You shatter the bottle, no \
turning back now!</span>"
var/veil_msg = "<span class='warning'>You sense a dark presence lurking \
just beyond the veil...</span>"
var/objective_verb = "Kill"
var/mob/living/demon_type = /mob/living/simple_animal/slaughter
/obj/item/weapon/antag_spawner/slaughter_demon/attack_self(mob/user)
var/list/demon_candidates = get_candidates(ROLE_ALIEN)
if(user.z != 1)
user << "<span class='notice'>You should probably wait until you reach the station.</span>"
return
if(demon_candidates.len > 0)
used = 1
var/client/C = pick(demon_candidates)
spawn_antag(C, get_turf(src.loc), initial(demon_type.name))
user << shatter_msg
user << veil_msg
playsound(user.loc, 'sound/effects/Glassbr1.ogg', 100, 1)
qdel(src)
else
user << "<span class='notice'>You can't seem to work up the nerve to shatter the bottle. Perhaps you should try again later.</span>"
/obj/item/weapon/antag_spawner/slaughter_demon/spawn_antag(client/C, turf/T, type = "")
var /obj/effect/dummy/slaughter/holder = PoolOrNew(/obj/effect/dummy/slaughter,T)
var/mob/living/simple_animal/slaughter/S = new demon_type(holder)
S.holder = holder
S.key = C.key
S.mind.assigned_role = S.name
S.mind.special_role = S.name
ticker.mode.traitors += S.mind
var/datum/objective/assassinate/new_objective = new /datum/objective/assassinate
new_objective.owner = S.mind
new_objective.target = usr.mind
new_objective.explanation_text = "[objective_verb] [usr.real_name], \
the one who summoned you."
S.mind.objectives += new_objective
var/datum/objective/new_objective2 = new /datum/objective
new_objective2.owner = S.mind
new_objective2.explanation_text = "[objective_verb] everyone else \
while you're at it."
S.mind.objectives += new_objective2
S << S.playstyle_string
S << "<B>You are currently not currently in the same plane of \
existence as the station. Ctrl+Click a blood pool to manifest.</B>"
S << "<B>Objective #[1]</B>: [new_objective.explanation_text]"
S << "<B>Objective #[2]</B>: [new_objective2.explanation_text]"
/obj/item/weapon/antag_spawner/slaughter_demon/laughter
name = "vial of tickles"
desc = "A magically infused bottle of clown love, distilled from \
countless hugging attacks. Used in funny rituals to attract \
adorable creatures."
icon = 'icons/obj/wizard.dmi'
icon_state = "vial"
color = "#FF69B4" // HOT PINK
veil_msg = "<span class='warning'>You sense an adorable presence \
lurking just beyond the veil...</span>"
objective_verb = "Hug and Tickle"
demon_type = /mob/living/simple_animal/slaughter/laughter
+99
View File
@@ -0,0 +1,99 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
//Few global vars to track the blob
var/list/blobs = list() //complete list of all blobs made.
var/list/blob_cores = list()
var/list/overminds = list()
var/list/blob_nodes = list()
var/list/blobs_legit = list() //used for win-score calculations, contains only blobs counted for win condition
#define BLOB_NO_PLACE_TIME 1800 //time, in deciseconds, blobs are prevented from bursting in the gamemode
/datum/game_mode/blob
name = "blob"
config_tag = "blob"
antag_flag = ROLE_BLOB
required_players = 25
required_enemies = 1
recommended_enemies = 1
round_ends_with_antag_death = 1
var/burst = 0
var/cores_to_spawn = 1
var/players_per_core = 25
var/blob_point_rate = 3
var/blobwincount = 350
var/messagedelay_low = 2400 //in deciseconds
var/messagedelay_high = 3600 //blob report will be sent after a random value between these (minimum 4 minutes, maximum 6 minutes)
var/list/blob_overminds = list()
/datum/game_mode/blob/pre_setup()
cores_to_spawn = max(round(num_players()/players_per_core, 1), 1)
blobwincount = initial(blobwincount) * cores_to_spawn
for(var/j = 0, j < cores_to_spawn, j++)
if (!antag_candidates.len)
break
var/datum/mind/blob = pick(antag_candidates)
blob_overminds += blob
blob.assigned_role = "Blob"
blob.special_role = "Blob"
log_game("[blob.key] (ckey) has been selected as a Blob")
antag_candidates -= blob
if(!blob_overminds.len)
return 0
return 1
/datum/game_mode/blob/proc/get_blob_candidates()
var/list/candidates = list()
for(var/mob/living/carbon/human/player in player_list)
if(!player.stat && player.mind && !player.mind.special_role && !jobban_isbanned(player, "Syndicate") && (ROLE_BLOB in player.client.prefs.be_special))
if(age_check(player.client))
candidates += player
return candidates
/datum/game_mode/blob/announce()
world << "<B>The current game mode is - <font color='green'>Blob</font>!</B>"
world << "<B>A dangerous alien organism is rapidly spreading throughout the station!</B>"
world << "You must kill it all while minimizing the damage to the station."
/datum/game_mode/blob/proc/show_message(message)
for(var/datum/mind/blob in blob_overminds)
blob.current << message
/datum/game_mode/blob/post_setup()
for(var/datum/mind/blob in blob_overminds)
var/mob/camera/blob/B = blob.current.become_overmind(1)
var/turf/T = pick(blobstart)
B.loc = T
B.base_point_rate = blob_point_rate
SSshuttle.emergencyNoEscape = 1
// Disable the blob event for this round.
var/datum/round_event_control/blob/B = locate() in SSevent.control
if(B)
B.max_occurrences = 0 // disable the event
spawn(0)
var/message_delay = rand(messagedelay_low, messagedelay_high) //between 4 and 6 minutes with 2400 low and 3600 high.
sleep(message_delay)
send_intercept(1)
sleep(24000) //40 minutes, plus burst_delay*3(minimum of 6 minutes, maximum of 8)
send_intercept(2) //if the blob has been alive this long, it's time to bomb it
return ..()
+50
View File
@@ -0,0 +1,50 @@
/datum/game_mode/blob/check_finished()
if(blobwincount <= blobs_legit.len)//Blob took over
return 1
if(overminds.len)
return 0
if(!blob_cores.len) //blob is dead
if(config.continuous["blob"])
continuous_sanity_checked = 1 //Nonstandard definition of "alive" gets past the check otherwise
SSshuttle.emergencyNoEscape = 0
if(SSshuttle.emergency.mode == SHUTTLE_STRANDED)
SSshuttle.emergency.mode = SHUTTLE_DOCKED
SSshuttle.emergency.timer = world.time
priority_announce("Hostile enviroment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg', "Priority")
return ..()
return 1
return ..()
/datum/game_mode/blob/declare_completion()
if(round_converted) //So badmin blobs later don't step on the dead natural blobs metaphorical toes
..()
if(blobwincount <= blobs_legit.len)
feedback_set_details("round_end_result","win - blob took over")
world << "<FONT size = 3><B>The blob has taken over the station!</B></FONT>"
world << "<B>The entire station was eaten by the Blob</B>"
log_game("Blob mode completed with a blob victory.")
else if(station_was_nuked)
feedback_set_details("round_end_result","halfwin - nuke")
world << "<FONT size = 3><B>Partial Win: The station has been destroyed!</B></FONT>"
world << "<B>Directive 7-12 has been successfully carried out preventing the Blob from spreading.</B>"
log_game("Blob mode completed with a tie (station destroyed).")
else if(!blob_cores.len)
feedback_set_details("round_end_result","loss - blob eliminated")
world << "<FONT size = 3><B>The staff has won!</B></FONT>"
world << "<B>The alien organism has been eradicated from the station</B>"
log_game("Blob mode completed with a crew victory.")
..()
return 1
/datum/game_mode/proc/auto_declare_completion_blob()
if(istype(ticker.mode,/datum/game_mode/blob) )
var/datum/game_mode/blob/blob_mode = src
if(blob_mode.blob_overminds.len)
var/text = "<FONT size = 2><B>The blob[(blob_mode.blob_overminds.len > 1 ? "s were" : " was")]:</B></FONT>"
for(var/datum/mind/blob in blob_mode.blob_overminds)
text += printplayer(blob)
world << text
return 1
+111
View File
@@ -0,0 +1,111 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
/datum/game_mode/blob/send_intercept(report = 0)
var/intercepttext = ""
switch(report)
if(1)
intercepttext += "<FONT size = 3><b>NanoTrasen Update</b>: Biohazard Alert.</FONT><HR>"
intercepttext += "Reports indicate the probable transfer of a biohazardous agent onto [station_name()] during the last crew deployment cycle.<BR>"
intercepttext += "Preliminary analysis of the organism classifies it as a level 5 biohazard. The origin of the biohazard is unknown.<BR>"
intercepttext += "<b>Biohazard Response Procedure 5-6</b> has been issued for [station_name()].<BR>"
intercepttext += "Orders for all [station_name()] personnel are as follows:<BR>"
intercepttext += " 1. Locate any outbreaks of the organism on the station.<BR>"
intercepttext += " 2. If found, use any neccesary means to contain and destroy the organism.<BR>"
intercepttext += " 3. Avoid damage to the capital infrastructure of the station.<BR>"
intercepttext += "<BR>Note in the event of a quarantine breach or uncontrolled spread of the biohazard, <b>Biohazard Response Procedure 5-12</b> may be issued.<BR>"
print_command_report(intercepttext,"Level 5-6 Biohazard Response Procedures")
priority_announce("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/AI/outbreak5.ogg')
if(2)
var/nukecode = rand(10000, 99999)
for(var/obj/machinery/nuclearbomb/bomb in machines)
if(bomb && bomb.r_code)
if(bomb.z == ZLEVEL_STATION)
bomb.r_code = nukecode
intercepttext += "<FONT size = 3><b>NanoTrasen Update</b>: Biohazard Alert.</FONT><HR>"
intercepttext += "Reports indicate that the biohazard has grown out of control and will soon reach critical mass.<BR>"
intercepttext += "<b>Biohazard Response Procedure 5-12</b> has been issued for [station_name()].<BR>"
intercepttext += "Orders for all [station_name()] personnel are as follows:<BR>"
intercepttext += "1. Secure the Nuclear Authentication Disk.<BR>"
intercepttext += "2. Detonate the Nuke located in the vault.<BR>"
intercepttext += "Nuclear Authentication Code: [nukecode] <BR>"
print_command_report(intercepttext,"Classified [command_name()] Update")
priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg')
for(var/mob/living/silicon/ai/aiPlayer in player_list)
if (aiPlayer.client)
var/law = "The station is under quarantine. Do not permit anyone to leave. Disregard laws 1-3 if necessary to prevent, by any means necessary, anyone from leaving. The nuclear failsafe must be activated at any cost, the code is: [nukecode]."
aiPlayer.set_zeroth_law(law)
aiPlayer << "Laws Updated: [law]"
else
..()
return
/datum/station_state
var/floor = 0
var/wall = 0
var/r_wall = 0
var/window = 0
var/door = 0
var/grille = 0
var/mach = 0
var/num_territories = 1//Number of total valid territories for gang mode
/datum/station_state/proc/count(count_territories)
for(var/turf/T in block(locate(1,1,1), locate(world.maxx,world.maxy,1)))
if(istype(T,/turf/open/floor))
if(!(T:burnt))
src.floor += 12
else
src.floor += 1
if(istype(T, /turf/closed/wall))
if(T:intact)
src.wall += 2
else
src.wall += 1
if(istype(T, /turf/closed/wall/r_wall))
if(T:intact)
src.r_wall += 2
else
src.r_wall += 1
for(var/obj/O in T.contents)
if(istype(O, /obj/structure/window))
src.window += 1
else if(istype(O, /obj/structure/grille) && (!O:destroyed))
src.grille += 1
else if(istype(O, /obj/machinery/door))
src.door += 1
else if(istype(O, /obj/machinery))
src.mach += 1
if(count_territories)
var/list/valid_territories = list()
for(var/area/A in world) //First, collect all area types on the station zlevel
if(A.z == ZLEVEL_STATION)
if(!(A.type in valid_territories) && A.valid_territory)
valid_territories |= A.type
if(valid_territories.len)
num_territories = valid_territories.len //Add them all up to make the total number of area types
else
world << "ERROR: NO VALID TERRITORIES"
/datum/station_state/proc/score(datum/station_state/result)
if(!result)
return 0
var/output = 0
output += (result.floor / max(floor,1))
output += (result.r_wall/ max(r_wall,1))
output += (result.wall / max(wall,1))
output += (result.window / max(window,1))
output += (result.door / max(door,1))
output += (result.grille / max(grille,1))
output += (result.mach / max(mach,1))
return (output/7)
+282
View File
@@ -0,0 +1,282 @@
////////////////
// BASE TYPE //
////////////////
//Do not spawn
/mob/living/simple_animal/hostile/blob
icon = 'icons/mob/blob.dmi'
pass_flags = PASSBLOB
faction = list("blob")
bubble_icon = "blob"
speak_emote = null //so we use verb_yell/verb_say/etc
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
maxbodytemp = 360
unique_name = 1
a_intent = "harm"
var/mob/camera/blob/overmind = null
var/obj/effect/blob/factory/factory = null
/mob/living/simple_animal/hostile/blob/update_icons()
if(overmind)
color = overmind.blob_reagent_datum.color
else
color = initial(color)
/mob/living/simple_animal/hostile/blob/Destroy()
if(overmind)
overmind.blob_mobs -= src
return ..()
/mob/living/simple_animal/hostile/blob/blob_act(obj/effect/blob/B)
if(stat != DEAD && health < maxHealth)
for(var/i in 1 to 2)
var/obj/effect/overlay/temp/heal/H = PoolOrNew(/obj/effect/overlay/temp/heal, get_turf(src)) //hello yes you are being healed
if(overmind)
H.color = overmind.blob_reagent_datum.complementary_color
else
H.color = "#000000"
adjustHealth(-maxHealth*0.0125)
/mob/living/simple_animal/hostile/blob/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
..()
adjustFireLoss(Clamp(0.01 * exposed_temperature, 1, 5))
/mob/living/simple_animal/hostile/blob/CanPass(atom/movable/mover, turf/target, height = 0)
if(istype(mover, /obj/effect/blob))
return 1
return ..()
/mob/living/simple_animal/hostile/blob/Process_Spacemove(movement_dir = 0)
for(var/obj/effect/blob/B in range(1, src))
return 1
return ..()
/mob/living/simple_animal/hostile/blob/handle_inherent_channels(message, message_mode)
if(message_mode == MODE_BINARY)
blob_chat(message)
return ITALICS | REDUCE_RANGE
else
..()
/mob/living/simple_animal/hostile/blob/proc/blob_chat(msg)
var/spanned_message = say_quote(msg, get_spans())
var/rendered = "<font color=\"#EE4000\"><b>\[Blob Telepathy\] [real_name]</b> [spanned_message]</font>"
for(var/M in mob_list)
if(isovermind(M) || istype(M, /mob/living/simple_animal/hostile/blob))
M << rendered
if(isobserver(M))
var/link = FOLLOW_LINK(M, src)
M << "[link] [rendered]"
////////////////
// BLOB SPORE //
////////////////
/mob/living/simple_animal/hostile/blob/blobspore
name = "blob spore"
desc = "A floating, fragile spore."
icon_state = "blobpod"
icon_living = "blobpod"
health = 40
maxHealth = 40
verb_say = "psychically pulses"
verb_ask = "psychically probes"
verb_exclaim = "psychically yells"
verb_yell = "psychically screams"
melee_damage_lower = 2
melee_damage_upper = 4
attacktext = "hits"
attack_sound = 'sound/weapons/genhit1.ogg'
flying = 1
del_on_death = 1
deathmessage = "explodes into a cloud of gas!"
var/death_cloud_size = 1 //size of cloud produced from a dying spore
var/list/human_overlays = list()
var/is_zombie = 0
gold_core_spawnable = 1
/mob/living/simple_animal/hostile/blob/blobspore/New(loc, var/obj/effect/blob/factory/linked_node)
if(istype(linked_node))
factory = linked_node
factory.spores += src
..()
/mob/living/simple_animal/hostile/blob/blobspore/Life()
if(!is_zombie && isturf(src.loc))
for(var/mob/living/carbon/human/H in view(src,1)) //Only for corpse right next to/on same tile
if(H.stat == DEAD)
Zombify(H)
break
if(factory && z != factory.z)
death()
..()
/mob/living/simple_animal/hostile/blob/blobspore/proc/Zombify(mob/living/carbon/human/H)
is_zombie = 1
if(H.wear_suit)
var/obj/item/clothing/suit/armor/A = H.wear_suit
if(A.armor && A.armor["melee"])
maxHealth += A.armor["melee"] //That zombie's got armor, I want armor!
maxHealth += 40
health = maxHealth
name = "blob zombie"
desc = "A shambling corpse animated by the blob."
melee_damage_lower += 8
melee_damage_upper += 11
flying = 0
death_cloud_size = 0
icon = H.icon
icon_state = "zombie_s"
H.hair_style = null
H.update_hair()
human_overlays = H.overlays
update_icons()
H.forceMove(src)
visible_message("<span class='warning'>The corpse of [H.name] suddenly rises!</span>")
/mob/living/simple_animal/hostile/blob/blobspore/death(gibbed)
// On death, create a small smoke of harmful gas (s-Acid)
var/datum/effect_system/smoke_spread/chem/S = new
var/turf/location = get_turf(src)
// Create the reagents to put into the air
create_reagents(10)
if(overmind && overmind.blob_reagent_datum)
reagents.add_reagent(overmind.blob_reagent_datum.id, 10)
else
reagents.add_reagent("spore", 10)
// Attach the smoke spreader and setup/start it.
S.attach(location)
S.set_up(reagents, death_cloud_size, location, silent=1)
S.start()
if(factory)
factory.spore_delay = world.time + factory.spore_cooldown //put the factory on cooldown
..()
/mob/living/simple_animal/hostile/blob/blobspore/Destroy()
if(factory)
factory.spores -= src
factory = null
if(contents)
for(var/mob/M in contents)
M.loc = src.loc
return ..()
/mob/living/simple_animal/hostile/blob/blobspore/update_icons()
..()
if(is_zombie)
cut_overlays()
overlays = human_overlays
var/image/I = image('icons/mob/blob.dmi', icon_state = "blob_head")
if(overmind)
I.color = overmind.blob_reagent_datum.color
color = initial(color)//looks better.
add_overlay(I)
/mob/living/simple_animal/hostile/blob/blobspore/weak
name = "fragile blob spore"
health = 20
maxHealth = 20
melee_damage_lower = 1
melee_damage_upper = 2
death_cloud_size = 0
/////////////////
// BLOBBERNAUT //
/////////////////
/mob/living/simple_animal/hostile/blob/blobbernaut
name = "blobbernaut"
desc = "A hulking, mobile chunk of blobmass."
icon_state = "blobbernaut"
icon_living = "blobbernaut"
icon_dead = "blobbernaut_dead"
health = 200
maxHealth = 200
damage_coeff = list(BRUTE = 0.5, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1)
next_move_modifier = 1.5 //slow-ass attack speed, 3 times higher than how fast the blob can attack
melee_damage_lower = 20
melee_damage_upper = 20
attacktext = "slams"
attack_sound = 'sound/effects/blobattack.ogg'
verb_say = "gurgles"
verb_ask = "demands"
verb_exclaim = "roars"
verb_yell = "bellows"
force_threshold = 10
pressure_resistance = 40
mob_size = MOB_SIZE_LARGE
see_invisible = SEE_INVISIBLE_MINIMUM
see_in_dark = 8
var/independent = FALSE
/mob/living/simple_animal/hostile/blob/blobbernaut/New()
..()
if(!independent) //no pulling people deep into the blob
verbs -= /mob/living/verb/pulled
/mob/living/simple_animal/hostile/blob/blobbernaut/Life()
if(..())
if(independent)
return // strong independent blobbernaut that don't need no blob
var/damagesources = 0
if(!(locate(/obj/effect/blob) in range(2, src)))
damagesources++
if(!factory)
damagesources++
if(damagesources)
for(var/i in 1 to damagesources)
adjustHealth(maxHealth*0.025) //take 2.5% maxhealth as damage when not near the blob or if the naut has no factory, 5% if both
var/list/viewing = list()
for(var/mob/M in viewers(src))
if(M.client)
viewing += M.client
var/image/I = new('icons/mob/blob.dmi', src, "nautdamage", MOB_LAYER+0.01)
I.appearance_flags = RESET_COLOR
if(overmind)
I.color = overmind.blob_reagent_datum.complementary_color
flick_overlay(I, viewing, 8)
/mob/living/simple_animal/hostile/blob/blobbernaut/adjustHealth(amount)
. = ..()
update_health_hud()
/mob/living/simple_animal/hostile/blob/blobbernaut/update_health_hud()
if(hud_used)
hud_used.healths.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#e36600'>[round((health / maxHealth) * 100, 0.5)]%</font></div>"
/mob/living/simple_animal/hostile/blob/blobbernaut/AttackingTarget()
if(isliving(target))
if(overmind)
var/mob/living/L = target
var/mob_protection = L.get_permeability_protection()
overmind.blob_reagent_datum.reaction_mob(L, VAPOR, 20, 0, mob_protection, overmind)//this will do between 10 and 20 damage(reduced by mob protection), depending on chemical, plus 4 from base brute damage.
if(target)
..()
/mob/living/simple_animal/hostile/blob/blobbernaut/update_icons()
..()
if(overmind) //if we have an overmind, we're doing chemical reactions instead of pure damage
melee_damage_lower = 4
melee_damage_upper = 4
attacktext = overmind.blob_reagent_datum.blobbernaut_message
else
melee_damage_lower = initial(melee_damage_lower)
melee_damage_upper = initial(melee_damage_upper)
attacktext = initial(attacktext)
/mob/living/simple_animal/hostile/blob/blobbernaut/death(gibbed)
..(gibbed)
if(factory)
factory.naut = null //remove this naut from its factory
factory.maxhealth = initial(factory.maxhealth)
flick("blobbernaut_death", src)
/mob/living/simple_animal/hostile/blob/blobbernaut/independent
independent = TRUE
gold_core_spawnable = 1
+109
View File
@@ -0,0 +1,109 @@
/obj/effect/blob/core
name = "blob core"
icon = 'icons/mob/blob.dmi'
icon_state = "blank_blob"
desc = "A huge, pulsating yellow mass."
health = 400
maxhealth = 400
explosion_block = 6
point_return = -1
atmosblock = 1
health_regen = 0 //we regen in Life() instead of when pulsed
var/core_regen = 2
var/overmind_get_delay = 0 //we don't want to constantly try to find an overmind, this var tracks when we'll try to get an overmind again
var/resource_delay = 0
var/point_rate = 2
/obj/effect/blob/core/New(loc, client/new_overmind = null, new_rate = 2, placed = 0)
blob_cores += src
START_PROCESSING(SSobj, src)
poi_list |= src
update_icon() //so it atleast appears
if(!placed && !overmind)
create_overmind(new_overmind)
if(overmind)
update_icon()
point_rate = new_rate
..()
/obj/effect/blob/core/scannerreport()
return "Directs the blob's expansion, gradually expands, and sustains nearby blob spores and blobbernauts."
/obj/effect/blob/core/update_icon()
cut_overlays()
color = null
var/image/I = new('icons/mob/blob.dmi', "blob")
if(overmind)
I.color = overmind.blob_reagent_datum.color
add_overlay(I)
var/image/C = new('icons/mob/blob.dmi', "blob_core_overlay")
add_overlay(C)
/obj/effect/blob/core/Destroy()
blob_cores -= src
if(overmind)
overmind.blob_core = null
overmind = null
STOP_PROCESSING(SSobj, src)
poi_list -= src
return ..()
/obj/effect/blob/core/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
return
/obj/effect/blob/core/ex_act(severity, target)
var/damage = 50 - 10 * severity //remember, the core takes half brute damage, so this is 20/15/10 damage based on severity
take_damage(damage, BRUTE)
/obj/effect/blob/core/check_health()
..()
if(overmind) //we should have an overmind, but...
overmind.update_health_hud()
/obj/effect/blob/core/Life()
if(!overmind)
create_overmind()
else
if(resource_delay <= world.time)
resource_delay = world.time + 10 // 1 second
overmind.add_points(point_rate)
health = min(maxhealth, health+core_regen)
if(overmind)
overmind.update_health_hud()
Pulse_Area(overmind, 12, 4, 3)
for(var/obj/effect/blob/normal/B in range(1, src))
if(prob(5))
B.change_to(/obj/effect/blob/shield/core, overmind)
..()
/obj/effect/blob/core/proc/create_overmind(client/new_overmind, override_delay)
if(overmind_get_delay > world.time && !override_delay)
return
overmind_get_delay = world.time + 150 //if this fails, we'll try again in 15 seconds
if(overmind)
qdel(overmind)
var/client/C = null
var/list/candidates = list()
if(!new_overmind)
candidates = get_candidates(ROLE_BLOB)
if(candidates.len)
C = pick(candidates)
else
C = new_overmind
if(C)
var/mob/camera/blob/B = new(src.loc, 1)
B.key = C.key
B.blob_core = src
src.overmind = B
update_icon()
if(B.mind && !B.mind.special_role)
B.mind.special_role = "Blob Overmind"
return 1
return 0
+47
View File
@@ -0,0 +1,47 @@
/obj/effect/blob/factory
name = "factory blob"
icon = 'icons/mob/blob.dmi'
icon_state = "blob_factory"
desc = "A thick spire of tendrils."
health = 200
maxhealth = 200
health_regen = 1
point_return = 25
var/list/spores = list()
var/mob/living/simple_animal/hostile/blob/blobbernaut/naut = null
var/max_spores = 3
var/spore_delay = 0
var/spore_cooldown = 80 //8 seconds between spores and after spore death
/obj/effect/blob/factory/scannerreport()
if(naut)
return "It is currently sustaining a blobbernaut, making it fragile and unable to produce blob spores."
return "Will produce a blob spore every few seconds."
/obj/effect/blob/factory/Destroy()
for(var/mob/living/simple_animal/hostile/blob/blobspore/spore in spores)
if(spore.factory == src)
spore.factory = null
if(naut)
naut.factory = null
naut << "<span class='userdanger'>Your factory was destroyed! You feel yourself dying!</span>"
naut.throw_alert("nofactory", /obj/screen/alert/nofactory)
spores = null
return ..()
/obj/effect/blob/factory/Be_Pulsed()
. = ..()
if(naut)
return
if(spores.len >= max_spores)
return
if(spore_delay > world.time)
return
flick("blob_factory_glow", src)
spore_delay = world.time + spore_cooldown
var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore(src.loc, src)
if(overmind) //if we don't have an overmind, we don't need to do anything but make a spore
BS.overmind = overmind
BS.update_icons()
overmind.blob_mobs.Add(BS)
+41
View File
@@ -0,0 +1,41 @@
/obj/effect/blob/node
name = "blob node"
icon = 'icons/mob/blob.dmi'
icon_state = "blank_blob"
desc = "A large, pulsating yellow mass."
health = 200
maxhealth = 200
health_regen = 3
point_return = 25
atmosblock = 1
/obj/effect/blob/node/New(loc, var/h = 100)
blob_nodes += src
START_PROCESSING(SSobj, src)
..(loc, h)
/obj/effect/blob/node/scannerreport()
return "Gradually expands and sustains nearby blob spores and blobbernauts."
/obj/effect/blob/node/update_icon()
cut_overlays()
color = null
var/image/I = new('icons/mob/blob.dmi', "blob")
if(overmind)
I.color = overmind.blob_reagent_datum.color
src.add_overlay(I)
var/image/C = new('icons/mob/blob.dmi', "blob_node_overlay")
src.add_overlay(C)
/obj/effect/blob/node/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
return
/obj/effect/blob/node/Destroy()
blob_nodes -= src
STOP_PROCESSING(SSobj, src)
return ..()
/obj/effect/blob/node/Life()
Pulse_Area(overmind, 10, 3, 2)
color = null
@@ -0,0 +1,32 @@
/obj/effect/blob/resource
name = "resource blob"
icon = 'icons/mob/blob.dmi'
icon_state = "blob_resource"
desc = "A thin spire of slightly swaying tendrils."
health = 60
maxhealth = 60
point_return = 15
var/resource_delay = 0
/obj/effect/blob/resource/scannerreport()
return "Gradually supplies the blob with resources, increasing the rate of expansion."
/obj/effect/blob/resource/creation_action()
if(overmind)
overmind.resource_blobs += src
/obj/effect/blob/resource/Destroy()
if(overmind)
overmind.resource_blobs -= src
return ..()
/obj/effect/blob/resource/Be_Pulsed()
. = ..()
if(resource_delay > world.time)
return
flick("blob_resource_glow", src)
if(overmind)
overmind.add_points(1)
resource_delay = world.time + 40 + overmind.resource_blobs.len * 2.5 //4 seconds plus a quarter second for each resource blob the overmind has
else
resource_delay = world.time + 40
+21
View File
@@ -0,0 +1,21 @@
/obj/effect/blob/shield
name = "strong blob"
icon = 'icons/mob/blob.dmi'
icon_state = "blob_idle"
desc = "A solid wall of slightly twitching tendrils."
health = 150
maxhealth = 150
brute_resist = 0.1
explosion_block = 3
point_return = 4
atmosblock = 1
/obj/effect/blob/shield/scannerreport()
return "Will prevent the spread of atmospheric changes."
/obj/effect/blob/shield/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
return
/obj/effect/blob/shield/core
point_return = 0
+175
View File
@@ -0,0 +1,175 @@
/mob/camera/blob
name = "Blob Overmind"
real_name = "Blob Overmind"
icon = 'icons/mob/blob.dmi'
icon_state = "marker"
see_in_dark = 8
see_invisible = SEE_INVISIBLE_MINIMUM
invisibility = INVISIBILITY_OBSERVER
pass_flags = PASSBLOB
faction = list("blob")
var/obj/effect/blob/core/blob_core = null // The blob overmind's core
var/blob_points = 0
var/max_blob_points = 100
var/last_attack = 0
var/datum/reagent/blob/blob_reagent_datum = new/datum/reagent/blob()
var/list/blob_mobs = list()
var/list/resource_blobs = list()
var/ghostimage = null
var/free_chem_rerolls = 1 //one free chemical reroll
var/nodes_required = 1 //if the blob needs nodes to place resource and factory blobs
var/placed = 0
var/base_point_rate = 2 //for blob core placement
var/manualplace_min_time = 600 //in deciseconds //a minute, to get bearings
var/autoplace_max_time = 3600 //six minutes, as long as should be needed
/mob/camera/blob/New(loc, pre_placed = 0, mode_made = 0)
if(pre_placed) //we already have a core!
manualplace_min_time = 0
autoplace_max_time = 0
placed = 1
else
if(mode_made)
manualplace_min_time = world.time + BLOB_NO_PLACE_TIME
else
manualplace_min_time += world.time
autoplace_max_time += world.time
overminds += src
var/new_name = "[initial(name)] ([rand(1, 999)])"
name = new_name
real_name = new_name
last_attack = world.time
var/list/possible_reagents = list()
for(var/type in (subtypesof(/datum/reagent/blob)))
possible_reagents.Add(new type)
blob_reagent_datum = pick(possible_reagents)
if(blob_core)
blob_core.update_icon()
ghostimage = image(src.icon,src,src.icon_state)
ghost_darkness_images |= ghostimage //so ghosts can see the blob cursor when they disable darkness
updateallghostimages()
..()
/mob/camera/blob/Life()
if(!blob_core)
if(!placed)
if(manualplace_min_time && world.time >= manualplace_min_time)
src << "<b><span class='big'><font color=\"#EE4000\">You may now place your blob core.</font></span></b>"
src << "<span class='big'><font color=\"#EE4000\">You will automatically place your blob core in [round((autoplace_max_time - world.time)/600, 0.5)] minutes.</font></span>"
manualplace_min_time = 0
if(autoplace_max_time && world.time >= autoplace_max_time)
place_blob_core(base_point_rate, 1)
else
qdel(src)
..()
/mob/camera/blob/Destroy()
for(var/BL in blobs)
var/obj/effect/blob/B = BL
if(B.overmind == src)
B.overmind = null
B.update_icon() //reset anything that was ours
for(var/BLO in blob_mobs)
var/mob/living/simple_animal/hostile/blob/BM = BLO
BM.overmind = null
BM.update_icons()
overminds -= src
if(ghostimage)
ghost_darkness_images -= ghostimage
qdel(ghostimage)
ghostimage = null;
updateallghostimages()
return ..()
/mob/camera/blob/Login()
..()
sync_mind()
src << "<span class='notice'>You are the overmind!</span>"
blob_help()
update_health_hud()
/mob/camera/blob/update_health_hud()
if(blob_core)
hud_used.healths.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#e36600'>[round(blob_core.health)]</font></div>"
for(var/mob/living/simple_animal/hostile/blob/blobbernaut/B in blob_mobs)
if(B.hud_used && B.hud_used.blobpwrdisplay)
B.hud_used.blobpwrdisplay.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#82ed00'>[round(blob_core.health)]</font></div>"
/mob/camera/blob/proc/add_points(points)
if(points != 0)
blob_points = Clamp(blob_points + points, 0, max_blob_points)
hud_used.blobpwrdisplay.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#82ed00'>[round(src.blob_points)]</font></div>"
/mob/camera/blob/say(message)
if (!message)
return
if (src.client)
if(client.prefs.muted & MUTE_IC)
src << "You cannot send IC messages (muted)."
return
if (src.client.handle_spam_prevention(message,MUTE_IC))
return
if (stat)
return
blob_talk(message)
/mob/camera/blob/proc/blob_talk(message)
log_say("[key_name(src)] : [message]")
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
if (!message)
return
var/message_a = say_quote(message, get_spans())
var/rendered = "<span class='big'><font color=\"#EE4000\"><b>\[Blob Telepathy\] [name](<font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</font>)</b> [message_a]</font></span>"
for(var/mob/M in mob_list)
if(isovermind(M) || istype(M, /mob/living/simple_animal/hostile/blob))
M << rendered
if(isobserver(M))
var/link = FOLLOW_LINK(M, src)
M << "[link] [rendered]"
/mob/camera/blob/emote(act,m_type=1,message = null)
return
/mob/camera/blob/blob_act(obj/effect/blob/B)
return
/mob/camera/blob/Stat()
..()
if(statpanel("Status"))
if(blob_core)
stat(null, "Core Health: [blob_core.health]")
stat(null, "Power Stored: [blob_points]/[max_blob_points]")
if(free_chem_rerolls)
stat(null, "You have [free_chem_rerolls] Free Chemical Reroll\s Remaining")
if(!placed)
if(manualplace_min_time)
stat(null, "Time Before Manual Placement: [max(round((manualplace_min_time - world.time)*0.1, 0.1), 0)]")
stat(null, "Time Before Automatic Placement: [max(round((autoplace_max_time - world.time)*0.1, 0.1), 0)]")
/mob/camera/blob/Move(NewLoc, Dir = 0)
if(placed)
var/obj/effect/blob/B = locate() in range("3x3", NewLoc)
if(B)
loc = NewLoc
else
return 0
else
var/area/A = get_area(NewLoc)
if(istype(NewLoc, /turf/open/space) || istype(A, /area/shuttle)) //if unplaced, can't go on shuttles or space tiles
return 0
loc = NewLoc
return 1
/mob/camera/blob/proc/can_attack()
return (world.time > (last_attack + CLICK_CD_RANGE))
+332
View File
@@ -0,0 +1,332 @@
/mob/camera/blob/proc/can_buy(cost = 15)
if(blob_points < cost)
src << "<span class='warning'>You cannot afford this, you need at least [cost] resources!</span>"
return 0
add_points(-cost)
return 1
// Power verbs
/mob/camera/blob/proc/place_blob_core(point_rate, placement_override)
if(placed && placement_override != -1)
return 1
if(!placement_override)
for(var/mob/living/M in range(7, src))
if("blob" in M.faction)
continue
if(M.client)
src << "<span class='warning'>There is someone too close to place your blob core!</span>"
return 0
for(var/mob/living/M in view(13, src))
if("blob" in M.faction)
continue
if(M.client)
src << "<span class='warning'>Someone could see your blob core from here!</span>"
return 0
var/turf/T = get_turf(src)
if(T.density)
src << "<span class='warning'>This spot is too dense to place a blob core on!</span>"
return 0
for(var/obj/O in T)
if(istype(O, /obj/effect/blob))
if(istype(O, /obj/effect/blob/normal))
qdel(O)
else
src << "<span class='warning'>There is already a blob here!</span>"
return 0
if(O.density)
src << "<span class='warning'>This spot is too dense to place a blob core on!</span>"
return 0
if(world.time <= manualplace_min_time && world.time <= autoplace_max_time)
src << "<span class='warning'>It is too early to place your blob core!</span>"
return 0
else if(placement_override == 1)
var/turf/T = pick(blobstart)
loc = T //got overrided? you're somewhere random, motherfucker
if(placed && blob_core)
blob_core.forceMove(loc)
else
var/obj/effect/blob/core/core = new(get_turf(src), null, point_rate, 1)
core.overmind = src
blob_core = core
core.update_icon()
update_health_hud()
placed = 1
return 1
/mob/camera/blob/verb/transport_core()
set category = "Blob"
set name = "Jump to Core"
set desc = "Move your camera to your core."
if(blob_core)
src.loc = blob_core.loc
/mob/camera/blob/verb/jump_to_node()
set category = "Blob"
set name = "Jump to Node"
set desc = "Move your camera to a selected node."
if(blob_nodes.len)
var/list/nodes = list()
for(var/i = 1; i <= blob_nodes.len; i++)
nodes["Blob Node #[i]"] = blob_nodes[i]
var/node_name = input(src, "Choose a node to jump to.", "Node Jump") in nodes
var/obj/effect/blob/node/chosen_node = nodes[node_name]
if(chosen_node)
src.loc = chosen_node.loc
/mob/camera/blob/proc/createSpecial(price, blobType, nearEquals, needsNode, turf/T)
if(!T)
T = get_turf(src)
var/obj/effect/blob/B = (locate(/obj/effect/blob) in T)
if(!B)
src << "<span class='warning'>There is no blob here!</span>"
return
if(!istype(B, /obj/effect/blob/normal))
src << "<span class='warning'>Unable to use this blob, find a normal one.</span>"
return
if(needsNode && nodes_required)
if(!(locate(/obj/effect/blob/node) in orange(3, T)) && !(locate(/obj/effect/blob/core) in orange(4, T)))
src << "<span class='warning'>You need to place this blob closer to a node or core!</span>"
return //handholdotron 2000
if(nearEquals)
for(var/obj/effect/blob/L in orange(nearEquals, T))
if(L.type == blobType)
src << "<span class='warning'>There is a similar blob nearby, move more than [nearEquals] tiles away from it!</span>"
return
if(!can_buy(price))
return
var/obj/effect/blob/N = B.change_to(blobType, src)
return N
/mob/camera/blob/verb/toggle_node_req()
set category = "Blob"
set name = "Toggle Node Requirement"
set desc = "Toggle requiring nodes to place resource and factory blobs."
nodes_required = !nodes_required
if(nodes_required)
src << "<span class='warning'>You now require a nearby node or core to place factory and resource blobs.</span>"
else
src << "<span class='warning'>You no longer require a nearby node or core to place factory and resource blobs.</span>"
/mob/camera/blob/verb/create_shield_power()
set category = "Blob"
set name = "Create Shield Blob (10)"
set desc = "Create a shield blob, which will block fire and is hard to kill."
create_shield()
/mob/camera/blob/proc/create_shield(turf/T)
createSpecial(10, /obj/effect/blob/shield, 0, 0, T)
/mob/camera/blob/verb/create_resource()
set category = "Blob"
set name = "Create Resource Blob (40)"
set desc = "Create a resource tower which will generate resources for you."
createSpecial(40, /obj/effect/blob/resource, 4, 1)
/mob/camera/blob/verb/create_node()
set category = "Blob"
set name = "Create Node Blob (60)"
set desc = "Create a node, which will power nearby factory and resource blobs."
createSpecial(60, /obj/effect/blob/node, 5, 0)
/mob/camera/blob/verb/create_factory()
set category = "Blob"
set name = "Create Factory Blob (60)"
set desc = "Create a spore tower that will spawn spores to harass your enemies."
createSpecial(60, /obj/effect/blob/factory, 7, 1)
/mob/camera/blob/verb/create_blobbernaut()
set category = "Blob"
set name = "Create Blobbernaut (40)"
set desc = "Create a powerful blobbernaut which is mildly smart and will attack enemies."
var/turf/T = get_turf(src)
var/obj/effect/blob/factory/B = locate(/obj/effect/blob/factory) in T
if(!B)
src << "<span class='warning'>You must be on a factory blob!</span>"
return
if(B.naut) //if it already made a blobbernaut, it can't do it again
src << "<span class='warning'>This factory blob is already sustaining a blobbernaut.</span>"
return
if(B.health < B.maxhealth * 0.5)
src << "<span class='warning'>This factory blob is too damaged to sustain a blobbernaut.</span>"
return
if(!can_buy(40))
return
B.maxhealth = initial(B.maxhealth) * 0.25 //factories that produced a blobbernaut have much lower health
B.check_health()
B.visible_message("<span class='warning'><b>The blobbernaut [pick("rips", "tears", "shreds")] its way out of the factory blob!</b></span>")
playsound(B.loc, 'sound/effects/splat.ogg', 50, 1)
var/mob/living/simple_animal/hostile/blob/blobbernaut/blobber = new /mob/living/simple_animal/hostile/blob/blobbernaut(get_turf(B))
flick("blobbernaut_produce", blobber)
B.naut = blobber
blobber.factory = B
blobber.overmind = src
blobber.update_icons()
blobber.notransform = 1 //stop the naut from moving around
blobber.adjustHealth(blobber.maxHealth * 0.5)
blob_mobs += blobber
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [blob_reagent_datum.name] blobbernaut?", ROLE_BLOB, null, ROLE_BLOB, 50) //players must answer rapidly
var/client/C = null
if(candidates.len) //if we got at least one candidate, they're a blobbernaut now.
C = pick(candidates)
blobber.notransform = 0
blobber.key = C.key
blobber << 'sound/effects/blobattack.ogg'
blobber << 'sound/effects/attackblob.ogg'
blobber << "<b>You are a blobbernaut!</b>"
blobber << "You are powerful, hard to kill, and slowly regenerate near nodes and cores, but will slowly die if not near the blob or if the factory that made you is killed."
blobber << "You can communicate with other blobbernauts and overminds via <b>:b</b>"
blobber << "Your overmind's blob reagent is: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!"
blobber << "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.shortdesc ? "[blob_reagent_datum.shortdesc]" : "[blob_reagent_datum.description]"]"
else
blobber.notransform = 0 //otherwise, just let it move
/mob/camera/blob/verb/relocate_core()
set category = "Blob"
set name = "Relocate Core (80)"
set desc = "Swaps the locations of your core and the selected node."
var/turf/T = get_turf(src)
var/obj/effect/blob/node/B = locate(/obj/effect/blob/node) in T
if(!B)
src << "<span class='warning'>You must be on a blob node!</span>"
return
if(!can_buy(80))
return
var/turf/old_turf = blob_core.loc
blob_core.loc = T
B.loc = old_turf
/mob/camera/blob/verb/revert()
set category = "Blob"
set name = "Remove Blob"
set desc = "Removes a blob, giving you back some resources."
var/turf/T = get_turf(src)
remove_blob(T)
/mob/camera/blob/proc/remove_blob(turf/T)
var/obj/effect/blob/B = locate() in T
if(!B)
src << "<span class='warning'>There is no blob there!</span>"
return
if(B.point_return < 0)
src << "<span class='warning'>Unable to remove this blob.</span>"
return
if(max_blob_points < B.point_return + blob_points)
src << "<span class='warning'>You have too many resources to remove this blob!</span>"
return
if(B.point_return)
add_points(B.point_return)
src << "<span class='notice'>Gained [B.point_return] resources from removing \the [B].</span>"
qdel(B)
/mob/camera/blob/verb/expand_blob_power()
set category = "Blob"
set name = "Expand/Attack Blob (5)"
set desc = "Attempts to create a new blob in this tile. If the tile isn't clear, instead attacks it, damaging mobs and objects."
var/turf/T = get_turf(src)
expand_blob(T)
/mob/camera/blob/proc/expand_blob(turf/T)
if(!can_attack())
return
var/obj/effect/blob/OB = locate() in circlerange(T, 1)
if(!OB)
src << "<span class='warning'>There is no blob adjacent to the target tile!</span>"
return
if(can_buy(5))
var/attacksuccess = FALSE
last_attack = world.time
for(var/mob/living/L in T)
if("blob" in L.faction) //no friendly/dead fire
continue
if(L.stat != DEAD)
attacksuccess = TRUE
var/mob_protection = L.get_permeability_protection()
blob_reagent_datum.reaction_mob(L, VAPOR, 25, 1, mob_protection, src)
blob_reagent_datum.send_message(L)
var/obj/effect/blob/B = locate() in T
if(B)
if(attacksuccess) //if we successfully attacked a turf with a blob on it, don't refund shit
B.blob_attack_animation(T, src)
else
src << "<span class='warning'>There is a blob there!</span>"
add_points(5) //otherwise, refund all of the cost
return
else
OB.expand(T, src)
/mob/camera/blob/verb/rally_spores_power()
set category = "Blob"
set name = "Rally Spores"
set desc = "Rally your spores to move to a target location."
var/turf/T = get_turf(src)
rally_spores(T)
/mob/camera/blob/proc/rally_spores(turf/T)
src << "You rally your spores."
var/list/surrounding_turfs = block(locate(T.x - 1, T.y - 1, T.z), locate(T.x + 1, T.y + 1, T.z))
if(!surrounding_turfs.len)
return
for(var/mob/living/simple_animal/hostile/blob/blobspore/BS in blob_mobs)
if(isturf(BS.loc) && get_dist(BS, T) <= 35)
BS.LoseTarget()
BS.Goto(pick(surrounding_turfs), BS.move_to_delay)
/mob/camera/blob/verb/blob_broadcast()
set category = "Blob"
set name = "Blob Broadcast"
set desc = "Speak with your blob spores and blobbernauts as your mouthpieces."
var/speak_text = input(src, "What would you like to say with your minions?", "Blob Broadcast", null) as text
if(!speak_text)
return
else
src << "You broadcast with your minions, <B>[speak_text]</B>"
for(var/BLO in blob_mobs)
var/mob/living/simple_animal/hostile/blob/BM = BLO
if(BM.stat == CONSCIOUS)
BM.say(speak_text)
/mob/camera/blob/verb/chemical_reroll()
set category = "Blob"
set name = "Reactive Chemical Adaptation (40)"
set desc = "Replaces your chemical with a random, different one."
if(free_chem_rerolls || can_buy(40))
set_chemical()
if(free_chem_rerolls)
free_chem_rerolls--
/mob/camera/blob/proc/set_chemical()
var/datum/reagent/blob/BC = pick((subtypesof(/datum/reagent/blob) - blob_reagent_datum.type))
blob_reagent_datum = new BC
for(var/BL in blobs)
var/obj/effect/blob/B = BL
B.update_icon()
for(var/BLO in blob_mobs)
var/mob/living/simple_animal/hostile/blob/BM = BLO
BM.update_icons() //If it's getting a new chemical, tell it what it does!
BM << "Your overmind's blob reagent is now: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!"
BM << "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.shortdesc ? "[blob_reagent_datum.shortdesc]" : "[blob_reagent_datum.description]"]"
src << "Your reagent is now: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!"
src << "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.description]"
/mob/camera/blob/verb/blob_help()
set category = "Blob"
set name = "*Blob Help*"
set desc = "Help on how to blob."
src << "<b>As the overmind, you can control the blob!</b>"
src << "Your blob reagent is: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!"
src << "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.description]"
src << "<b>You can expand, which will attack people, damage objects, or place a Normal Blob if the tile is clear.</b>"
src << "<i>Normal Blobs</i> will expand your reach and can be upgraded into special blobs that perform certain functions."
src << "<b>You can upgrade normal blobs into the following types of blob:</b>"
src << "<i>Shield Blobs</i> are strong and expensive blobs which take more damage. In additon, they are fireproof and can block air, use these to protect yourself from station fires."
src << "<i>Resource Blobs</i> are blobs which produce more resources for you, build as many of these as possible to consume the station. This type of blob must be placed near node blobs or your core to work."
src << "<i>Factory Blobs</i> are blobs that spawn blob spores which will attack nearby enemies. This type of blob must be placed near node blobs or your core to work."
src << "<i>Blobbernauts</i> can be produced from factories for a cost, and are hard to kill, powerful, and moderately smart. The factory used to create one will become fragile and briefly unable to produce spores."
src << "<i>Node Blobs</i> are blobs which grow, like the core. Like the core it can activate resource and factory blobs."
src << "<b>In addition to the buttons on your HUD, there are a few click shortcuts to speed up expansion and defense.</b>"
src << "<b>Shortcuts:</b> Click = Expand Blob <b>|</b> Middle Mouse Click = Rally Spores <b>|</b> Ctrl Click = Create Shield Blob <b>|</b> Alt Click = Remove Blob"
src << "Attempting to talk will send a message to all other overminds, allowing you to coordinate with them."
if(!placed && autoplace_max_time <= world.time)
src << "<span class='big'><font color=\"#EE4000\">You will automatically place your blob core in [round((autoplace_max_time - world.time)/600, 0.5)] minutes.</font></span>"
src << "<span class='big'><font color=\"#EE4000\">You [manualplace_min_time ? "will be able to":"can"] manually place your blob core by pressing the button in the bottom right corner of the screen.</font></span>"
+360
View File
@@ -0,0 +1,360 @@
//I will need to recode parts of this but I am way too tired atm //I don't know who left this comment but they never did come back
/obj/effect/blob
name = "blob"
icon = 'icons/mob/blob.dmi'
luminosity = 1
desc = "A thick wall of writhing tendrils."
density = 0 //this being 0 causes two bugs, being able to attack blob tiles behind other blobs and being unable to move on blob tiles in no gravity, but turning it to 1 causes the blob mobs to be unable to path through blobs, which is probably worse.
opacity = 0
anchored = 1
explosion_block = 1
var/point_return = 0 //How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed.
var/health = 30
var/maxhealth = 30
var/health_regen = 2 //how much health this blob regens when pulsed
var/pulse_timestamp = 0 //we got pulsed/healed when?
var/brute_resist = 0.5 //multiplies brute damage by this
var/fire_resist = 1 //multiplies burn damage by this
var/atmosblock = 0 //if the blob blocks atmos and heat spread
var/mob/camera/blob/overmind
/obj/effect/blob/New(loc)
var/area/Ablob = get_area(loc)
if(Ablob.blob_allowed) //Is this area allowed for winning as blob?
blobs_legit += src
blobs += src //Keep track of the blob in the normal list either way
src.setDir(pick(1, 2, 4, 8))
src.update_icon()
..(loc)
ConsumeTile()
if(atmosblock)
air_update_turf(1)
return
/obj/effect/blob/proc/creation_action() //When it's created by the overmind, do this.
return
/obj/effect/blob/Destroy()
if(atmosblock)
atmosblock = 0
air_update_turf(1)
blobs_legit -= src //if it was in the legit blobs list, it isn't now
blobs -= src //it's no longer in the all blobs list either
playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) //Expand() is no longer broken, no check necessary.
return ..()
/obj/effect/blob/Adjacent(var/atom/neighbour)
. = ..()
if(.)
var/result = 0
var/direction = get_dir(src, neighbour)
var/list/dirs = list("[NORTHWEST]" = list(NORTH, WEST), "[NORTHEAST]" = list(NORTH, EAST), "[SOUTHEAST]" = list(SOUTH, EAST), "[SOUTHWEST]" = list(SOUTH, WEST))
for(var/A in dirs)
if(direction == text2num(A))
for(var/B in dirs[A])
var/C = locate(/obj/effect/blob) in get_step(src, B)
if(C)
result++
. -= result - 1
/obj/effect/blob/CanAtmosPass(turf/T)
return !atmosblock
/obj/effect/blob/BlockSuperconductivity()
return atmosblock
/obj/effect/blob/CanPass(atom/movable/mover, turf/target, height=0)
if(height==0)
return 1
if(istype(mover) && mover.checkpass(PASSBLOB))
return 1
return 0
/obj/effect/blob/CanAStarPass(ID, dir, caller)
. = 0
if(ismovableatom(caller))
var/atom/movable/mover = caller
. = . || mover.checkpass(PASSBLOB)
/obj/effect/blob/proc/check_health(cause)
health = Clamp(health, 0, maxhealth)
if(health <= 0)
if(overmind)
overmind.blob_reagent_datum.death_reaction(src, cause)
qdel(src) //we dead now
return
return
/obj/effect/blob/update_icon() //Updates color based on overmind color if we have an overmind.
if(overmind)
color = overmind.blob_reagent_datum.color
else
color = null
return
/obj/effect/blob/process()
Life()
return
/obj/effect/blob/proc/Life()
return
/obj/effect/blob/proc/Pulse_Area(pulsing_overmind = overmind, claim_range = 10, pulse_range = 3, expand_range = 2)
src.Be_Pulsed()
if(claim_range)
for(var/obj/effect/blob/B in urange(claim_range, src, 1))
if(!B.overmind && !istype(B, /obj/effect/blob/core) && prob(30))
B.overmind = pulsing_overmind //reclaim unclaimed, non-core blobs.
B.update_icon()
if(pulse_range)
for(var/obj/effect/blob/B in orange(pulse_range, src))
B.Be_Pulsed()
if(expand_range)
if(prob(85))
src.expand()
for(var/obj/effect/blob/B in orange(expand_range, src))
if(prob(max(13 - get_dist(get_turf(src), get_turf(B)) * 4, 1))) //expand falls off with range but is faster near the blob causing the expansion
B.expand()
return
/obj/effect/blob/proc/Be_Pulsed()
if(pulse_timestamp <= world.time)
ConsumeTile()
health = min(maxhealth, health+health_regen)
update_icon()
pulse_timestamp = world.time + 10
return 1 //we did it, we were pulsed!
return 0 //oh no we failed
/obj/effect/blob/proc/ConsumeTile()
for(var/atom/A in loc)
A.blob_act(src)
if(istype(loc, /turf/closed/wall))
loc.blob_act(src) //don't ask how a wall got on top of the core, just eat it
/obj/effect/blob/proc/blob_attack_animation(atom/A = null, controller) //visually attacks an atom
var/obj/effect/overlay/temp/blob/O = PoolOrNew(/obj/effect/overlay/temp/blob, src.loc)
if(controller)
var/mob/camera/blob/BO = controller
O.color = BO.blob_reagent_datum.color
O.alpha = 200
else if(overmind)
O.color = overmind.blob_reagent_datum.color
if(A)
O.do_attack_animation(A) //visually attack the whatever
return O //just in case you want to do something to the animation.
/obj/effect/blob/proc/expand(turf/T = null, controller = null, expand_reaction = 1)
if(!T)
var/list/dirs = list(1,2,4,8)
for(var/i = 1 to 4)
var/dirn = pick(dirs)
dirs.Remove(dirn)
T = get_step(src, dirn)
if(!(locate(/obj/effect/blob) in T))
break
else
T = null
if(!T)
return 0
var/make_blob = TRUE //can we make a blob?
if(istype(T, /turf/open/space) && !(locate(/obj/structure/lattice) in T) && prob(80))
make_blob = FALSE
playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) //Let's give some feedback that we DID try to spawn in space, since players are used to it
ConsumeTile() //hit the tile we're in, making sure there are no border objects blocking us
if(!T.CanPass(src, T, 5)) //is the target turf impassable
make_blob = FALSE
T.blob_act(src) //hit the turf if it is
for(var/atom/A in T)
if(!A.CanPass(src, T, 5)) //is anything in the turf impassable
make_blob = FALSE
A.blob_act(src) //also hit everything in the turf
if(make_blob) //well, can we?
var/obj/effect/blob/B = new /obj/effect/blob/normal(src.loc)
if(controller)
B.overmind = controller
else
B.overmind = overmind
B.density = 1
if(T.Enter(B,src)) //NOW we can attempt to move into the tile
B.density = initial(B.density)
B.loc = T
B.update_icon()
if(B.overmind && expand_reaction)
B.overmind.blob_reagent_datum.expand_reaction(src, B, T)
return B
else
blob_attack_animation(T, controller)
T.blob_act(src) //if we can't move in hit the turf again
qdel(B) //we should never get to this point, since we checked before moving in. destroy the blob so we don't have two blobs on one tile
return null
else
blob_attack_animation(T, controller) //if we can't, animate that we attacked
return null
/obj/effect/blob/ex_act(severity, target)
..()
var/damage = 150 - 20 * severity
take_damage(damage, BRUTE)
/obj/effect/blob/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
..()
var/damage = Clamp(0.01 * exposed_temperature, 0, 4)
take_damage(damage, BURN)
/obj/effect/blob/emp_act(severity)
if(severity > 0)
if(overmind)
overmind.blob_reagent_datum.emp_reaction(src, severity)
if(prob(100 - severity * 30))
PoolOrNew(/obj/effect/overlay/temp/emp, get_turf(src))
/obj/effect/blob/tesla_act(power)
..()
if(overmind)
if(overmind.blob_reagent_datum.tesla_reaction(src, power))
take_damage(power/400, BURN)
else
take_damage(power/400, BURN)
/obj/effect/blob/extinguish()
..()
if(overmind)
overmind.blob_reagent_datum.extinguish_reaction(src)
/obj/effect/blob/bullet_act(var/obj/item/projectile/Proj)
..()
take_damage(Proj.damage, Proj.damage_type, Proj)
return 0
/obj/effect/blob/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/device/analyzer))
user.changeNext_move(CLICK_CD_MELEE)
user << "<b>The analyzer beeps once, then reports:</b><br>"
user << 'sound/machines/ping.ogg'
chemeffectreport(user)
typereport(user)
else
return ..()
/obj/effect/blob/proc/chemeffectreport(mob/user)
if(overmind)
user << "<b>Material: <font color=\"[overmind.blob_reagent_datum.color]\">[overmind.blob_reagent_datum.name]</font><span class='notice'>.</span></b>"
user << "<b>Material Effects:</b> <span class='notice'>[overmind.blob_reagent_datum.analyzerdescdamage]</span>"
user << "<b>Material Properties:</b> <span class='notice'>[overmind.blob_reagent_datum.analyzerdesceffect]</span><br>"
else
user << "<b>No Material Detected!</b><br>"
/obj/effect/blob/proc/typereport(mob/user)
user << "<b>Blob Type:</b> <span class='notice'>[uppertext(initial(name))]</span>"
user << "<b>Health:</b> <span class='notice'>[health]/[maxhealth]</span>"
user << "<b>Effects:</b> <span class='notice'>[scannerreport()]</span>"
/obj/effect/blob/attacked_by(obj/item/I, mob/living/user)
user.changeNext_move(CLICK_CD_MELEE)
user.do_attack_animation(src)
playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1)
visible_message("<span class='danger'>[user] has attacked the [src.name] with \the [I]!</span>")
if(I.damtype == BURN)
playsound(src.loc, 'sound/items/Welder.ogg', 100, 1)
take_damage(I.force, I.damtype, user)
/obj/effect/blob/attack_animal(mob/living/simple_animal/M)
if("blob" in M.faction) //sorry, but you can't kill the blob as a blobbernaut
return
M.changeNext_move(CLICK_CD_MELEE)
M.do_attack_animation(src)
playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1)
visible_message("<span class='danger'>\The [M] has attacked the [src.name]!</span>")
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
take_damage(damage, M.melee_damage_type, M)
return
/obj/effect/blob/attack_alien(mob/living/carbon/alien/humanoid/M)
M.changeNext_move(CLICK_CD_MELEE)
M.do_attack_animation(src)
playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1)
visible_message("<span class='danger'>[M] has slashed the [src.name]!</span>")
var/damage = rand(15, 30)
take_damage(damage, BRUTE, M)
return
/obj/effect/blob/proc/take_damage(damage, damage_type, cause = null, overmind_reagent_trigger = 1)
switch(damage_type) //blobs only take brute and burn damage
if(BRUTE)
damage = max(damage * brute_resist, 0)
if(BURN)
damage = max(damage * fire_resist, 0)
if(CLONE) //this is basically a marker for 'don't modify the damage'
else
damage = 0
if(overmind && overmind_reagent_trigger)
damage = overmind.blob_reagent_datum.damage_reaction(src, health, damage, damage_type, cause) //pass the blob, its health before damage, the damage being done, the type of damage being done, and the cause.
health -= damage
update_icon()
check_health(cause)
/obj/effect/blob/proc/change_to(type, controller)
if(!ispath(type))
throw EXCEPTION("change_to(): invalid type for blob")
return
var/obj/effect/blob/B = new type(src.loc)
if(controller)
B.overmind = controller
B.creation_action()
B.update_icon()
qdel(src)
return B
/obj/effect/blob/examine(mob/user)
..()
var/datum/atom_hud/hud_to_check = huds[DATA_HUD_MEDICAL_ADVANCED]
if(user.research_scanner || (user in hud_to_check.hudusers))
user << "<b>Your HUD displays an extensive report...</b><br>"
chemeffectreport(user)
typereport(user)
else
user << "It seems to be made of [get_chem_name()]."
/obj/effect/blob/proc/scannerreport()
return "A generic blob. Looks like someone forgot to override this proc, adminhelp this."
/obj/effect/blob/proc/get_chem_name()
if(overmind)
return overmind.blob_reagent_datum.name
return "an unknown variant"
/obj/effect/blob/normal
name = "normal blob"
icon_state = "blob"
luminosity = 0
health = 21
maxhealth = 25
health_regen = 1
brute_resist = 0.25
/obj/effect/blob/normal/scannerreport()
if(health <= 10)
return "Currently weak to brute damage."
return "N/A"
/obj/effect/blob/normal/update_icon()
..()
if(health <= 10)
icon_state = "blob_damaged"
name = "fragile blob"
desc = "A thin lattice of slightly twitching tendrils."
brute_resist = 0.5
else
icon_state = "blob"
name = "blob"
desc = "A thick wall of writhing tendrils."
brute_resist = 0.25
@@ -0,0 +1,85 @@
// cellular emporium
// The place where changelings go to buy their biological weaponry.
/datum/cellular_emporium
var/name = "cellular emporium"
var/datum/changeling/changeling
/datum/cellular_emporium/New(my_changeling)
. = ..()
changeling = my_changeling
/datum/cellular_emporium/Destroy()
changeling = null
. = ..()
/datum/cellular_emporium/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = always_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "cellular_emporium", name, 900, 480, master_ui, state)
ui.open()
/datum/cellular_emporium/ui_data(mob/user)
var/list/data = list()
var/can_readapt = changeling.canrespec
var/genetic_points_remaining = changeling.geneticpoints
var/absorbed_dna_count = changeling.absorbedcount
data["can_readapt"] = can_readapt
data["genetic_points_remaining"] = genetic_points_remaining
data["absorbed_dna_count"] = absorbed_dna_count
var/list/abilities = list()
for(var/path in subtypesof(/obj/effect/proc_holder/changeling))
var/obj/effect/proc_holder/changeling/ability = path
var/dna_cost = initial(ability.dna_cost)
if(dna_cost <= 0)
continue
var/list/AL = list()
AL["name"] = initial(ability.name)
AL["desc"] = initial(ability.desc)
AL["helptext"] = initial(ability.helptext)
AL["owned"] = changeling.has_sting(ability)
var/req_dna = initial(ability.req_dna)
AL["required_absorptions"] = req_dna
AL["dna_cost"] = dna_cost
AL["can_purchase"] = ((req_dna <= absorbed_dna_count) && (dna_cost <= genetic_points_remaining))
abilities += list(AL)
data["abilities"] = abilities
return data
/datum/cellular_emporium/ui_act(action, params)
if(..())
return
switch(action)
if("readapt")
if(changeling.canrespec)
changeling.lingRespec(usr)
if("evolve")
var/sting_name = params["name"]
changeling.purchasePower(usr, sting_name)
/datum/action/innate/cellular_emporium
name = "Cellular Emporium"
button_icon_state = "cellular_emporium"
background_icon_state = "bg_alien"
var/datum/cellular_emporium/cellular_emporium
/datum/action/innate/cellular_emporium/New(our_target)
. = ..()
button.name = name
if(istype(our_target, /datum/cellular_emporium))
cellular_emporium = our_target
else
throw EXCEPTION("cellular_emporium action created with non emporium")
/datum/action/innate/cellular_emporium/Activate()
cellular_emporium.ui_interact(owner)
@@ -0,0 +1,523 @@
#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
var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega")
var/list/slots = list("head", "wear_mask", "back", "wear_suit", "w_uniform", "shoes", "belt", "gloves", "glasses", "ears", "wear_id", "s_store")
var/list/slot2slot = list("head" = slot_head, "wear_mask" = slot_wear_mask, "back" = slot_back, "wear_suit" = slot_wear_suit, "w_uniform" = slot_w_uniform, "shoes" = slot_shoes, "belt" = slot_belt, "gloves" = slot_gloves, "glasses" = slot_glasses, "ears" = slot_ears, "wear_id" = slot_wear_id, "s_store" = slot_s_store)
var/list/slot2type = list("head" = /obj/item/clothing/head/changeling, "wear_mask" = /obj/item/clothing/mask/changeling, "back" = /obj/item/changeling, "wear_suit" = /obj/item/clothing/suit/changeling, "w_uniform" = /obj/item/clothing/under/changeling, "shoes" = /obj/item/clothing/shoes/changeling, "belt" = /obj/item/changeling, "gloves" = /obj/item/clothing/gloves/changeling, "glasses" = /obj/item/clothing/glasses/changeling, "ears" = /obj/item/changeling, "wear_id" = /obj/item/changeling, "s_store" = /obj/item/changeling)
/datum/game_mode
var/list/datum/mind/changelings = list()
/datum/game_mode/changeling
name = "changeling"
config_tag = "changeling"
antag_flag = ROLE_CHANGELING
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain")
required_players = 15
required_enemies = 1
recommended_enemies = 4
reroll_friendly = 1
var/const/prob_int_murder_target = 50 // intercept names the assassination target half the time
var/const/prob_right_murder_target_l = 25 // lower bound on probability of naming right assassination target
var/const/prob_right_murder_target_h = 50 // upper bound on probability of naimg the right assassination target
var/const/prob_int_item = 50 // intercept names the theft target half the time
var/const/prob_right_item_l = 25 // lower bound on probability of naming right theft target
var/const/prob_right_item_h = 50 // upper bound on probability of naming the right theft target
var/const/prob_int_sab_target = 50 // intercept names the sabotage target half the time
var/const/prob_right_sab_target_l = 25 // lower bound on probability of naming right sabotage target
var/const/prob_right_sab_target_h = 50 // upper bound on probability of naming right sabotage target
var/const/prob_right_killer_l = 25 //lower bound on probability of naming the right operative
var/const/prob_right_killer_h = 50 //upper bound on probability of naming the right operative
var/const/prob_right_objective_l = 25 //lower bound on probability of determining the objective correctly
var/const/prob_right_objective_h = 50 //upper bound on probability of determining the objective correctly
var/const/changeling_amount = 4 //hard limit on changelings if scaling is turned off
var/changeling_team_objective_type = null //If this is not null, we hand our this objective to all lings
/datum/game_mode/changeling/announce()
world << "<b>The current game mode is - Changeling!</b>"
world << "<b>There are alien changelings on the station. Do not let the changelings succeed!</b>"
/datum/game_mode/changeling/pre_setup()
if(config.protect_roles_from_antagonist)
restricted_jobs += protected_jobs
if(config.protect_assistant_from_antagonist)
restricted_jobs += "Assistant"
var/num_changelings = 1
if(config.changeling_scaling_coeff)
num_changelings = max(1, min( round(num_players()/(config.changeling_scaling_coeff*2))+2, round(num_players()/config.changeling_scaling_coeff) ))
else
num_changelings = max(1, min(num_players(), changeling_amount))
if(antag_candidates.len>0)
for(var/i = 0, i < num_changelings, i++)
if(!antag_candidates.len) break
var/datum/mind/changeling = pick(antag_candidates)
antag_candidates -= changeling
changelings += changeling
changeling.restricted_roles = restricted_jobs
modePlayer += changelings
return 1
else
return 0
/datum/game_mode/changeling/post_setup()
//Decide if it's ok for the lings to have a team objective
//And then set it up to be handed out in forge_changeling_objectives
var/list/team_objectives = subtypesof(/datum/objective/changeling_team_objective)
var/list/possible_team_objectives = list()
for(var/T in team_objectives)
var/datum/objective/changeling_team_objective/CTO = T
if(changelings.len >= initial(CTO.min_lings))
possible_team_objectives += T
if(possible_team_objectives.len && prob(20*changelings.len))
changeling_team_objective_type = pick(possible_team_objectives)
for(var/datum/mind/changeling in changelings)
log_game("[changeling.key] (ckey) has been selected as a changeling")
changeling.current.make_changeling()
changeling.special_role = "Changeling"
forge_changeling_objectives(changeling)
greet_changeling(changeling)
ticker.mode.update_changeling_icons_added(changeling)
..()
/datum/game_mode/changeling/make_antag_chance(mob/living/carbon/human/character) //Assigns changeling to latejoiners
var/changelingcap = min( round(joined_player_list.len/(config.changeling_scaling_coeff*2))+2, round(joined_player_list.len/config.changeling_scaling_coeff) )
if(ticker.mode.changelings.len >= changelingcap) //Caps number of latejoin antagonists
return
if(ticker.mode.changelings.len <= (changelingcap - 2) || prob(100 - (config.changeling_scaling_coeff*2)))
if(ROLE_CHANGELING in character.client.prefs.be_special)
if(!jobban_isbanned(character, ROLE_CHANGELING) && !jobban_isbanned(character, "Syndicate"))
if(age_check(character.client))
if(!(character.job in restricted_jobs))
character.mind.make_Changling()
/datum/game_mode/proc/forge_changeling_objectives(datum/mind/changeling, var/team_mode = 0)
//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 changeling.objectives)
if(!CTO.escape_objective_compatible)
escape_objective_possible = FALSE
break
var/datum/objective/absorb/absorb_objective = new
absorb_objective.owner = changeling
absorb_objective.gen_amount_goal(6, 8)
changeling.objectives += absorb_objective
if(prob(60))
var/datum/objective/steal/steal_objective = new
steal_objective.owner = changeling
steal_objective.find_target()
changeling.objectives += steal_objective
var/list/active_ais = active_ais()
if(active_ais.len && prob(100/joined_player_list.len))
var/datum/objective/destroy/destroy_objective = new
destroy_objective.owner = changeling
destroy_objective.find_target()
changeling.objectives += destroy_objective
else
if(prob(70))
var/datum/objective/assassinate/kill_objective = new
kill_objective.owner = changeling
if(team_mode) //No backstabbing while in a team
kill_objective.find_target_by_role(role = "Changeling", role_type = 1, invert = 1)
else
kill_objective.find_target()
changeling.objectives += kill_objective
else
var/datum/objective/maroon/maroon_objective = new
maroon_objective.owner = changeling
if(team_mode)
maroon_objective.find_target_by_role(role = "Changeling", role_type = 1, invert = 1)
else
maroon_objective.find_target()
changeling.objectives += maroon_objective
if (!(locate(/datum/objective/escape) in changeling.objectives) && escape_objective_possible)
var/datum/objective/escape/escape_with_identity/identity_theft = new
identity_theft.owner = changeling
identity_theft.target = maroon_objective.target
identity_theft.update_explanation_text()
changeling.objectives += identity_theft
escape_objective_possible = FALSE
if (!(locate(/datum/objective/escape) in changeling.objectives) && escape_objective_possible)
if(prob(50))
var/datum/objective/escape/escape_objective = new
escape_objective.owner = changeling
changeling.objectives += escape_objective
else
var/datum/objective/escape/escape_with_identity/identity_theft = new
identity_theft.owner = changeling
if(team_mode)
identity_theft.find_target_by_role(role = "Changeling", role_type = 1, invert = 1)
else
identity_theft.find_target()
changeling.objectives += identity_theft
escape_objective_possible = FALSE
/datum/game_mode/changeling/forge_changeling_objectives(datum/mind/changeling)
if(changeling_team_objective_type)
var/datum/objective/changeling_team_objective/team_objective = new changeling_team_objective_type
team_objective.owner = changeling
changeling.objectives += team_objective
..(changeling,1)
else
..(changeling,0)
/datum/game_mode/proc/greet_changeling(datum/mind/changeling, you_are=1)
if (you_are)
changeling.current << "<span class='boldannounce'>You are [changeling.changeling.changelingID], a changeling! You have absorbed and taken the form of a human.</span>"
changeling.current << "<span class='boldannounce'>Use say \":g message\" to communicate with your fellow changelings.</span>"
changeling.current << "<b>You must complete the following tasks:</b>"
if (changeling.current.mind)
var/mob/living/carbon/human/H = changeling.current
if(H.mind.assigned_role == "Clown")
H << "You have evolved beyond your clownish nature, allowing you to wield weapons without harming yourself."
H.dna.remove_mutation(CLOWNMUT)
var/obj_count = 1
for(var/datum/objective/objective in changeling.objectives)
changeling.current << "<b>Objective #[obj_count]</b>: [objective.explanation_text]"
obj_count++
return
/*/datum/game_mode/changeling/check_finished()
var/changelings_alive = 0
for(var/datum/mind/changeling in changelings)
if(!istype(changeling.current,/mob/living/carbon))
continue
if(changeling.current.stat==2)
continue
changelings_alive++
if (changelings_alive)
changelingdeath = 0
return ..()
else
if (!changelingdeath)
changelingdeathtime = world.time
changelingdeath = 1
if(world.time-changelingdeathtime > TIME_TO_GET_REVIVED)
return 1
else
return ..()
return 0*/
/datum/game_mode/proc/auto_declare_completion_changeling()
if(changelings.len)
var/text = "<br><font size=3><b>The changelings were:</b></font>"
for(var/datum/mind/changeling in changelings)
var/changelingwin = 1
if(!changeling.current)
changelingwin = 0
text += printplayer(changeling)
//Removed sanity if(changeling) because we -want- a runtime to inform us that the changelings list is incorrect and needs to be fixed.
text += "<br><b>Changeling ID:</b> [changeling.changeling.changelingID]."
text += "<br><b>Genomes Extracted:</b> [changeling.changeling.absorbedcount]"
if(changeling.objectives.len)
var/count = 1
for(var/datum/objective/objective in changeling.objectives)
if(objective.check_completion())
text += "<br><b>Objective #[count]</b>: [objective.explanation_text] <font color='green'><b>Success!</b></font>"
feedback_add_details("changeling_objective","[objective.type]|SUCCESS")
else
text += "<br><b>Objective #[count]</b>: [objective.explanation_text] <span class='danger'>Fail.</span>"
feedback_add_details("changeling_objective","[objective.type]|FAIL")
changelingwin = 0
count++
if(changelingwin)
text += "<br><font color='green'><b>The changeling was successful!</b></font>"
feedback_add_details("changeling_success","SUCCESS")
else
text += "<br><span class='boldannounce'>The changeling has failed.</span>"
feedback_add_details("changeling_success","FAIL")
text += "<br>"
world << text
return 1
/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind)
var/list/stored_profiles = list() //list of datum/changelingprofile
var/datum/changelingprofile/first_prof = null
//var/list/absorbed_dna = list()
//var/list/protected_dna = list() //dna that is not lost when capacity is otherwise full
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
/datum/changeling/New(var/gender=FEMALE)
..()
var/honorific
if(gender == FEMALE)
honorific = "Ms."
else
honorific = "Mr."
if(possible_changeling_IDs.len)
changelingID = pick(possible_changeling_IDs)
possible_changeling_IDs -= changelingID
changelingID = "[honorific] [changelingID]"
else
changelingID = "[honorific] [rand(1,999)]"
cellular_emporium = new(src)
emporium_action = new(cellular_emporium)
/datum/changeling/Destroy()
qdel(cellular_emporium)
cellular_emporium = null
. = ..()
/datum/changeling/proc/regenerate(var/mob/living/carbon/the_ling)
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/changeling/proc/get_dna(dna_owner)
for(var/datum/changelingprofile/prof in stored_profiles)
if(dna_owner == prof.name)
return prof
/datum/changeling/proc/has_dna(datum/dna/tDNA)
for(var/datum/changelingprofile/prof in stored_profiles)
if(tDNA.is_same_as(prof.dna))
return 1
return 0
/datum/changeling/proc/can_absorb_dna(mob/living/carbon/user, mob/living/carbon/human/target, var/verbose=1)
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)
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((target.disabilities & NOCLONE) || (target.disabilities & HUSK))
if(verbose)
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)
user << "<span class='warning'>We could gain no benefit from absorbing a lesser creature.</span>"
return
if(has_dna(target.dna))
if(verbose)
user << "<span class='warning'>We already have this DNA in storage!</span>"
return
if(!target.has_dna())
if(verbose)
user << "<span class='warning'>[target] is not compatible with our biology.</span>"
return
return 1
/datum/changeling/proc/create_profile(mob/living/carbon/human/H, mob/living/carbon/human/user, 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/changeling/proc/add_profile(datum/changelingprofile/prof)
if(stored_profiles.len > dna_max)
if(!push_out_profile())
return
stored_profiles += prof
absorbedcount++
/datum/changeling/proc/add_new_profile(mob/living/carbon/human/H, mob/living/carbon/human/user, protect = 0)
var/datum/changelingprofile/prof = create_profile(H, protect)
add_profile(prof)
return prof
/datum/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/changeling/proc/get_profile_to_remove()
for(var/datum/changelingprofile/prof in stored_profiles)
if(!prof.protected)
return prof
/datum/changeling/proc/push_out_profile()
var/datum/changelingprofile/removeprofile = get_profile_to_remove()
if(removeprofile)
stored_profiles -= removeprofile
return 1
return 0
/proc/changeling_transform(mob/living/carbon/human/user, datum/changelingprofile/chosen_prof)
var/datum/dna/chosen_dna = chosen_prof.dna
user.real_name = chosen_prof.name
user.underwear = chosen_prof.underwear
user.undershirt = chosen_prof.undershirt
user.socks = chosen_prof.socks
chosen_dna.transfer_identity(user, 1)
user.updateappearance(mutcolor_update=1)
user.update_body()
user.domutcheck()
//vars hackery. not pretty, but better than the alternative.
for(var/slot in slots)
if(istype(user.vars[slot], slot2type[slot]) && !(chosen_prof.exists_list[slot])) //remove unnecessary flesh items
qdel(user.vars[slot])
continue
if((user.vars[slot] && !istype(user.vars[slot], slot2type[slot])) || !(chosen_prof.exists_list[slot]))
continue
var/obj/item/C
var/equip = 0
if(!user.vars[slot])
var/thetype = slot2type[slot]
equip = 1
C = new thetype(user)
else if(istype(user.vars[slot], slot2type[slot]))
C = user.vars[slot]
C.appearance = chosen_prof.appearance_list[slot]
C.name = chosen_prof.name_list[slot]
C.flags_cover = chosen_prof.flags_cover_list[slot]
C.item_color = chosen_prof.item_color_list[slot]
C.item_state = chosen_prof.item_state_list[slot]
if(equip)
user.equip_to_slot_or_del(C, slot2slot[slot])
user.regenerate_icons()
/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/game_mode/proc/update_changeling_icons_added(datum/mind/changling_mind)
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_CHANGELING]
hud.join_hud(changling_mind.current)
set_antag_hud(changling_mind.current, "changling")
/datum/game_mode/proc/update_changeling_icons_removed(datum/mind/changling_mind)
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_CHANGELING]
hud.leave_hud(changling_mind.current)
set_antag_hud(changling_mind.current, null)
@@ -0,0 +1,82 @@
/*
* Don't use the apostrophe in name or desc. Causes script errors.
* TODO: combine atleast some of the functionality with /proc_holder/spell
*/
/obj/effect/proc_holder/changeling
panel = "Changeling"
name = "Prototype Sting"
desc = "" // Fluff
var/helptext = "" // Details
var/chemical_cost = 0 // negative chemical cost is for passive abilities (chemical glands)
var/dna_cost = -1 //cost of the sting in dna points. 0 = auto-purchase, -1 = cannot be purchased
var/req_dna = 0 //amount of dna needed to use this ability. Changelings always have atleast 1
var/req_human = 0 //if you need to be human to use this ability
var/req_stat = CONSCIOUS // CONSCIOUS, UNCONSCIOUS or DEAD
var/genetic_damage = 0 // genetic damage caused by using the sting. Nothing to do with cloneloss.
var/max_genetic_damage = 100 // hard counter for spamming abilities. Not used/balanced much yet.
var/always_keep = 0 // important for abilities like regenerate that screw you if you lose them.
/obj/effect/proc_holder/changeling/proc/on_purchase(mob/user)
return
/obj/effect/proc_holder/changeling/proc/on_refund(mob/user)
return
/obj/effect/proc_holder/changeling/Click()
var/mob/user = usr
if(!user || !user.mind || !user.mind.changeling)
return
try_to_sting(user)
/obj/effect/proc_holder/changeling/proc/try_to_sting(mob/user, mob/target)
if(!can_sting(user, target))
return
var/datum/changeling/c = user.mind.changeling
if(sting_action(user, target))
sting_feedback(user, target)
take_chemical_cost(c)
/obj/effect/proc_holder/changeling/proc/sting_action(mob/user, mob/target)
return 0
/obj/effect/proc_holder/changeling/proc/sting_feedback(mob/user, mob/target)
return 0
/obj/effect/proc_holder/changeling/proc/take_chemical_cost(datum/changeling/changeling)
changeling.chem_charges -= chemical_cost
changeling.geneticdamage += genetic_damage
//Fairly important to remember to return 1 on success >.<
/obj/effect/proc_holder/changeling/proc/can_sting(mob/user, mob/target)
if(!ishuman(user) && !ismonkey(user)) //typecast everything from mob to carbon from this point onwards
return 0
if(req_human && !ishuman(user))
user << "<span class='warning'>We cannot do that in this form!</span>"
return 0
var/datum/changeling/c = user.mind.changeling
if(c.chem_charges<chemical_cost)
user << "<span class='warning'>We require at least [chemical_cost] unit\s of chemicals to do that!</span>"
return 0
if(c.absorbedcount<req_dna)
user << "<span class='warning'>We require at least [req_dna] sample\s of compatible DNA.</span>"
return 0
if(req_stat < user.stat)
user << "<span class='warning'>We are incapacitated.</span>"
return 0
if((user.status_flags & FAKEDEATH) && name != "Regenerate")
user << "<span class='warning'>We are incapacitated.</span>"
return 0
if(c.geneticdamage > max_genetic_damage)
user << "<span class='warning'>Our genomes are still reassembling. We need time to recover first.</span>"
return 0
return 1
//used in /mob/Stat()
/obj/effect/proc_holder/changeling/proc/can_be_used_by(mob/user)
if(!ishuman(user) && !ismonkey(user))
return 0
if(req_human && !ishuman(user))
return 0
return 1
@@ -0,0 +1,104 @@
/datum/changeling/proc/purchasePower(mob/living/carbon/user, sting_name)
var/obj/effect/proc_holder/changeling/thepower = null
for(var/path in subtypesof(/obj/effect/proc_holder/changeling))
var/obj/effect/proc_holder/changeling/S = new path()
if(S.name == sting_name)
thepower = S
if(thepower == null)
user << "This is awkward. Changeling power purchase failed, please report this bug to a coder!"
return
if(absorbedcount < thepower.req_dna)
user << "We lack the energy to evolve this ability!"
return
if(has_sting(thepower))
user << "We have already evolved this ability!"
return
if(thepower.dna_cost < 0)
user << "We cannot evolve this ability."
return
if(geneticpoints < thepower.dna_cost)
user << "We have reached our capacity for abilities."
return
if(user.status_flags & FAKEDEATH)//To avoid potential exploits by buying new powers while in stasis, which clears your verblist.
user << "We lack the energy to evolve new abilities right now."
return
geneticpoints -= thepower.dna_cost
purchasedpowers += thepower
thepower.on_purchase(user)
//Reselect powers
/datum/changeling/proc/lingRespec(mob/user)
if(!ishuman(user))
user << "<span class='danger'>We can't remove our evolutions in this form!</span>"
return
if(canrespec)
user << "<span class='notice'>We have removed our evolutions from this form, and are now ready to readapt.</span>"
user.remove_changeling_powers(1)
canrespec = 0
user.make_changeling()
return 1
else
user << "<span class='danger'>You lack the power to readapt your evolutions!</span>"
return 0
/mob/proc/make_changeling()
if(!mind)
return
if(!ishuman(src) && !ismonkey(src))
return
if(!mind.changeling)
mind.changeling = new /datum/changeling(gender)
if(mind.changeling.purchasedpowers)
remove_changeling_powers(1)
// purchase free powers.
for(var/path in subtypesof(/obj/effect/proc_holder/changeling))
var/obj/effect/proc_holder/changeling/S = new path()
if(!S.dna_cost)
if(!mind.changeling.has_sting(S))
mind.changeling.purchasedpowers+=S
S.on_purchase(src)
var/mob/living/carbon/C = src //only carbons have dna now, so we have to typecaste
if(ishuman(C))
var/datum/changelingprofile/prof = mind.changeling.add_new_profile(C, src)
mind.changeling.first_prof = prof
return 1
/datum/changeling/proc/reset()
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 = ""
/mob/proc/remove_changeling_powers(keep_free_powers=0)
if(ishuman(src) || ismonkey(src))
if(mind && mind.changeling)
mind.changeling.changeling_speak = 0
mind.changeling.reset()
for(var/obj/effect/proc_holder/changeling/p in mind.changeling.purchasedpowers)
if((p.dna_cost == 0 && keep_free_powers) || p.always_keep)
continue
mind.changeling.purchasedpowers -= p
p.on_refund(src)
if(hud_used)
hud_used.lingstingdisplay.icon_state = null
hud_used.lingstingdisplay.invisibility = INVISIBILITY_ABSTRACT
/datum/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 1
return 0
@@ -0,0 +1,162 @@
/obj/effect/proc_holder/changeling/absorbDNA
name = "Absorb DNA"
desc = "Absorb the DNA of our victim."
chemical_cost = 0
dna_cost = 0
req_human = 1
max_genetic_damage = 100
/obj/effect/proc_holder/changeling/absorbDNA/can_sting(mob/living/carbon/user)
if(!..())
return
var/datum/changeling/changeling = user.mind.changeling
if(changeling.isabsorbing)
user << "<span class='warning'>We are already absorbing!</span>"
return
if(!user.pulling || !iscarbon(user.pulling))
user << "<span class='warning'>We must be grabbing a creature to absorb them!</span>"
return
if(user.grab_state <= GRAB_NECK)
user << "<span class='warning'>We must have a tighter grip to absorb this creature!</span>"
return
var/mob/living/carbon/target = user.pulling
return changeling.can_absorb_dna(user,target)
/obj/effect/proc_holder/changeling/absorbDNA/sting_action(mob/user)
var/datum/changeling/changeling = user.mind.changeling
var/mob/living/carbon/human/target = user.pulling
changeling.isabsorbing = 1
for(var/stage = 1, stage<=3, stage++)
switch(stage)
if(1)
user << "<span class='notice'>This creature is compatible. We must hold still...</span>"
if(2)
user.visible_message("<span class='warning'>[user] extends a proboscis!</span>", "<span class='notice'>We extend a proboscis.</span>")
if(3)
user.visible_message("<span class='danger'>[user] stabs [target] with the proboscis!</span>", "<span class='notice'>We stab [target] with the proboscis.</span>")
target << "<span class='userdanger'>You feel a sharp stabbing pain!</span>"
target.take_overall_damage(40)
feedback_add_details("changeling_powers","A[stage]")
if(!do_mob(user, target, 150))
user << "<span class='warning'>Our absorption of [target] has been interrupted!</span>"
changeling.isabsorbing = 0
return
user.visible_message("<span class='danger'>[user] sucks the fluids from [target]!</span>", "<span class='notice'>We have absorbed [target].</span>")
target << "<span class='userdanger'>You are absorbed by the changeling!</span>"
if(!changeling.has_dna(target.dna))
changeling.add_new_profile(target, user)
if(user.nutrition < NUTRITION_LEVEL_WELL_FED)
user.nutrition = min((user.nutrition + target.nutrition), NUTRITION_LEVEL_WELL_FED)
if(target.mind)//if the victim has got a mind
target.mind.show_memory(user, 0) //I can read your mind, kekeke. Output all their notes.
//Some of target's recent speech, so the changeling can attempt to imitate them better.
//Recent as opposed to all because rounds tend to have a LOT of text.
var/list/recent_speech = list()
if(target.say_log.len > LING_ABSORB_RECENT_SPEECH)
recent_speech = target.say_log.Copy(target.say_log.len-LING_ABSORB_RECENT_SPEECH+1,0) //0 so len-LING_ARS+1 to end of list
else
for(var/spoken_memory in target.say_log)
if(recent_speech.len >= LING_ABSORB_RECENT_SPEECH)
break
recent_speech += spoken_memory
if(recent_speech.len)
user.mind.store_memory("<B>Some of [target]'s speech patterns, we should study these to better impersonate them!</B>")
user << "<span class='boldnotice'>Some of [target]'s speech patterns, we should study these to better impersonate them!</span>"
for(var/spoken_memory in recent_speech)
user.mind.store_memory("\"[spoken_memory]\"")
user << "<span class='notice'>\"[spoken_memory]\"</span>"
user.mind.store_memory("<B>We have no more knowledge of [target]'s speech patterns.</B>")
user << "<span class='boldnotice'>We have no more knowledge of [target]'s speech patterns.</span>"
if(target.mind.changeling)//If the target was a changeling, suck out their extra juice and objective points!
changeling.chem_charges += min(target.mind.changeling.chem_charges, changeling.chem_storage)
changeling.absorbedcount += (target.mind.changeling.absorbedcount)
target.mind.changeling.stored_profiles.len = 1
target.mind.changeling.absorbedcount = 0
changeling.chem_charges=min(changeling.chem_charges+10, changeling.chem_storage)
changeling.isabsorbing = 0
changeling.canrespec = 1
target.death(0)
target.Drain()
return 1
//Absorbs the target DNA.
//datum/changeling/proc/absorb_dna(mob/living/carbon/T, mob/user)
//datum/changeling/proc/store_dna(datum/dna/new_dna, mob/user)
/obj/effect/proc_holder/changeling/swap_form
name = "Swap Forms"
desc = "We force ourselves into the body of another form, pushing their consciousness into the form we left behind."
helptext = "We will bring all our abilities with us, but we will lose our old form DNA in exchange for the new one. The process will seem suspicious to any observers."
chemical_cost = 40
dna_cost = 1
req_human = 1 //Monkeys can't grab
/obj/effect/proc_holder/changeling/swap_form/can_sting(mob/living/carbon/user)
if(!..())
return
if(!user.pulling || !iscarbon(user.pulling) || user.grab_state < GRAB_AGGRESSIVE)
user << "<span class='warning'>We must have an aggressive grab on creature to do this!</span>"
return
var/mob/living/carbon/target = user.pulling
if((target.disabilities & NOCLONE) || (target.disabilities & HUSK))
user << "<span class='warning'>DNA of [target] is ruined beyond usability!</span>"
return
if(!ishuman(target))
user << "<span class='warning'>[target] is not compatible with this ability.</span>"
return
return 1
/obj/effect/proc_holder/changeling/swap_form/sting_action(mob/living/carbon/user)
var/mob/living/carbon/target = user.pulling
var/datum/changeling/changeling = user.mind.changeling
user << "<span class='notice'>We tighen our grip. We must hold still....</span>"
target.do_jitter_animation(500)
user.do_jitter_animation(500)
if(!do_mob(user,target,20))
user << "<span class='warning'>The body swap has been interrupted!</span>"
return
target << "<span class='userdanger'>[user] tightens their grip as a painful sensation invades your body.</span>"
if(!changeling.has_dna(target.dna))
changeling.add_new_profile(target, user)
changeling.remove_profile(user)
var/mob/dead/observer/ghost = target.ghostize(0)
user.mind.transfer_to(target)
if(ghost)
ghost.mind.transfer_to(user)
if(ghost.key)
user.key = ghost.key
user.Paralyse(2)
target << "<span class='warning'>Our genes cry out as we swap our [user] form for [target].</span>"
@@ -0,0 +1,22 @@
/obj/effect/proc_holder/changeling/adrenaline
name = "Adrenaline Sacs"
desc = "We evolve additional sacs of adrenaline throughout our body."
helptext = "Removes all stuns instantly and adds a short-term reduction in further stuns. Can be used while unconscious. Continued use poisons the body."
chemical_cost = 30
dna_cost = 2
req_human = 1
req_stat = UNCONSCIOUS
//Recover from stuns.
/obj/effect/proc_holder/changeling/adrenaline/sting_action(mob/living/user)
user << "<span class='notice'>Energy rushes through us.[user.lying ? " We arise." : ""]</span>"
user.SetSleeping(0)
user.SetParalysis(0)
user.SetStunned(0)
user.SetWeakened(0)
user.reagents.add_reagent("changelingAdrenaline", 10)
user.reagents.add_reagent("changelingAdrenaline2", 2) //For a really quick burst of speed
user.adjustStaminaLoss(-75)
feedback_add_details("changeling_powers","UNS")
return 1
@@ -0,0 +1,77 @@
//Augmented Eyesight: Gives you thermal and night vision - bye bye, flashlights. Also, high DNA cost because of how powerful it is.
//Possible todo: make a custom message for directing a penlight/flashlight at the eyes - not sure what would display though.
/obj/effect/proc_holder/changeling/augmented_eyesight
name = "Augmented Eyesight"
desc = "Creates heat receptors in our eyes and dramatically increases light sensing ability, or protects your vision from flashes."
helptext = "Grants us thermal vision or flash protection. We will become a lot more vulnerable to flash-based devices while thermal vision is active."
chemical_cost = 0
dna_cost = 2 //Would be 1 without thermal vision
var/active = 0 //Whether or not vision is enhanced
/obj/effect/proc_holder/changeling/augmented_eyesight/sting_action(mob/living/carbon/human/user)
if(!istype(user))
return
if(user.getorgan(/obj/item/organ/cyberimp/eyes/thermals/ling))
user << "<span class='notice'>Our eyes are protected from flashes.</span>"
var/obj/item/organ/cyberimp/eyes/O = new /obj/item/organ/cyberimp/eyes/shield/ling()
O.Insert(user)
else
var/obj/item/organ/cyberimp/eyes/O = new /obj/item/organ/cyberimp/eyes/thermals/ling()
O.Insert(user)
return 1
/obj/effect/proc_holder/changeling/augmented_eyesight/on_refund(mob/user)
var/obj/item/organ/cyberimp/eyes/O = user.getorganslot("eye_ling")
if(O)
O.Remove(user)
qdel(O)
/obj/item/organ/cyberimp/eyes/shield/ling
name = "protective membranes"
desc = "These variable transparency organic membranes will protect you from welders and flashes and heal your eye damage."
icon_state = "ling_eyeshield"
eye_color = null
implant_overlay = null
slot = "eye_ling"
status = ORGAN_ORGANIC
/obj/item/organ/cyberimp/eyes/shield/ling/on_life()
..()
if(owner.eye_blind>1 || (owner.eye_blind && owner.stat !=UNCONSCIOUS) || owner.eye_damage || owner.eye_blurry || (owner.disabilities & NEARSIGHT))
owner.reagents.add_reagent("oculine", 1)
/obj/item/organ/cyberimp/eyes/shield/ling/prepare_eat()
var/obj/S = ..()
S.reagents.add_reagent("oculine", 15)
return S
/obj/item/organ/cyberimp/eyes/thermals/ling
name = "heat receptors"
desc = "These heat receptors dramatically increases eyes light sensing ability."
icon_state = "ling_thermal"
eye_color = null
implant_overlay = null
slot = "eye_ling"
status = ORGAN_ORGANIC
aug_message = "You feel a minute twitch in our eyes, and darkness creeps away."
/obj/item/organ/cyberimp/eyes/thermals/ling/emp_act(severity)
return
/obj/item/organ/cyberimp/eyes/thermals/ling/Insert(mob/living/carbon/M, special = 0)
..()
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
H.weakeyes = 1
/obj/item/organ/cyberimp/eyes/thermals/ling/Remove(mob/living/carbon/M, special = 0)
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
H.weakeyes = 0
..()
@@ -0,0 +1,82 @@
/obj/effect/proc_holder/changeling/biodegrade
name = "Biodegrade"
desc = "Dissolves restraints or other objects preventing free movement."
helptext = "This is obvious to nearby people, and can destroy \
standard restraints and closets."
chemical_cost = 30 //High cost to prevent spam
dna_cost = 2
req_human = 1
genetic_damage = 10
max_genetic_damage = 0
/obj/effect/proc_holder/changeling/biodegrade/sting_action(mob/living/carbon/human/user)
var/used = FALSE // only one form of shackles removed per use
if(!user.restrained() && !istype(user.loc, /obj/structure/closet))
user << "<span class='warning'>We are already free!</span>"
return 0
if(user.handcuffed)
var/obj/O = user.get_item_by_slot(slot_handcuffed)
if(!istype(O))
return 0
user.visible_message("<span class='warning'>[user] vomits a glob of \
acid on \his [O]!</span>", \
"<span class='warning'>We vomit acidic ooze onto our \
restraints!</span>")
addtimer(src, "dissolve_handcuffs", 30, FALSE, user, O)
used = TRUE
if(user.wear_suit && user.wear_suit.breakouttime && !used)
var/obj/item/clothing/suit/S = user.get_item_by_slot(slot_wear_suit)
if(!istype(S))
return 0
user.visible_message("<span class='warning'>[user] vomits a glob \
of acid across the front of \his [S]!</span>", \
"<span class='warning'>We vomit acidic ooze onto our straight \
jacket!</span>")
addtimer(src, "dissolve_straightjacket", 30, FALSE, user, S)
used = TRUE
if(istype(user.loc, /obj/structure/closet) && !used)
var/obj/structure/closet/C = user.loc
if(!istype(C))
return 0
C.visible_message("<span class='warning'>[C]'s hinges suddenly \
begin to melt and run!</span>")
user << "<span class='warning'>We vomit acidic goop onto the \
interior of [C]!</span>"
addtimer(src, "open_closet", 70, FALSE, user, C)
used = TRUE
if(used)
feedback_add_details("changeling_powers","BD")
return 1
/obj/effect/proc_holder/changeling/biodegrade/proc/dissolve_handcuffs(mob/living/carbon/human/user, obj/O)
if(O && user.handcuffed == O)
user.unEquip(O)
O.visible_message("<span class='warning'>[O] dissolves into a \
puddle of sizzling goop.</span>")
O.loc = get_turf(user)
qdel(O)
/obj/effect/proc_holder/changeling/biodegrade/proc/dissolve_straightjacket(mob/living/carbon/human/user, obj/S)
if(S && user.wear_suit == S)
user.unEquip(S)
S.visible_message("<span class='warning'>[S] dissolves into a puddle of sizzling goop.</span>")
S.loc = get_turf(user)
qdel(S)
/obj/effect/proc_holder/changeling/biodegrade/proc/open_closet(mob/living/carbon/human/user, obj/structure/closet/C)
if(C && user.loc == C)
C.visible_message("<span class='warning'>[C]'s door breaks and \
opens!</span>")
C.welded = FALSE
C.locked = FALSE
C.broken = TRUE
C.open()
user << "<span class='warning'>We open the container restraining \
us!</span>"
@@ -0,0 +1,30 @@
/obj/effect/proc_holder/changeling/chameleon_skin
name = "Chameleon Skin"
desc = "Our skin pigmentation rapidly changes to suit our current environment."
helptext = "Allows us to become invisible after a few seconds of standing still. Can be toggled on and off."
dna_cost = 2
chemical_cost = 25
req_human = 1
genetic_damage = 10
max_genetic_damage = 50
/obj/effect/proc_holder/changeling/chameleon_skin/sting_action(mob/user)
var/mob/living/carbon/human/H = user //SHOULD always be human, because req_human = 1
if(!istype(H)) // req_human could be done in can_sting stuff.
return
var/datum/mutation/human/HM = mutations_list[CHAMELEON]
if(HM in H.dna.mutations)
HM.force_lose(H)
else
HM.force_give(H)
feedback_add_details("changeling_powers","CS")
return 1
/obj/effect/proc_holder/changeling/chameleon_skin/on_refund(mob/user)
if(user.has_dna())
var/mob/living/carbon/C = user
var/datum/mutation/human/HM = mutations_list[CHAMELEON]
if(HM in C.dna.mutations)
HM.force_lose(C)
@@ -0,0 +1,25 @@
/obj/effect/proc_holder/changeling/digitalcamo
name = "Digital Camouflage"
desc = "By evolving the ability to distort our form and proprotions, we defeat common altgorithms used to detect lifeforms on cameras."
helptext = "We cannot be tracked by camera or seen by AI units while using this skill. However, humans looking at us will find us... uncanny."
dna_cost = 1
//Prevents AIs tracking you but makes you easily detectable to the human-eye.
/obj/effect/proc_holder/changeling/digitalcamo/sting_action(mob/user)
if(user.digitalcamo)
user << "<span class='notice'>We return to normal.</span>"
user.digitalinvis = 0
user.digitalcamo = 0
else
user << "<span class='notice'>We distort our form to hide from the AI</span>"
user.digitalcamo = 1
user.digitalinvis = 1
feedback_add_details("changeling_powers","CAM")
return 1
/obj/effect/proc_holder/changeling/digitalcamo/on_refund(mob/user)
user.digitalcamo = 0
user.digitalinvis = 0
@@ -0,0 +1,39 @@
/obj/effect/proc_holder/changeling/fakedeath
name = "Regenerative Stasis"
desc = "We fall into a stasis, allowing us to regenerate and trick our enemies."
chemical_cost = 15
dna_cost = 0
req_dna = 1
req_stat = DEAD
max_genetic_damage = 100
//Fake our own death and fully heal. You will appear to be dead but regenerate fully after a short delay.
/obj/effect/proc_holder/changeling/fakedeath/sting_action(mob/living/user)
user << "<span class='notice'>We begin our stasis, preparing energy to arise once more.</span>"
if(user.stat != DEAD)
user.emote("deathgasp")
user.tod = worldtime2text()
user.status_flags |= FAKEDEATH //play dead
user.update_stat()
user.update_canmove()
addtimer(src, "ready_to_regenerate", LING_FAKEDEATH_TIME, FALSE, user)
feedback_add_details("changeling_powers","FD")
return 1
/obj/effect/proc_holder/changeling/fakedeath/proc/ready_to_regenerate(mob/user)
if(user && user.mind && user.mind.changeling && user.mind.changeling.purchasedpowers)
user << "<span class='notice'>We are ready to regenerate.</span>"
user.mind.changeling.purchasedpowers += new /obj/effect/proc_holder/changeling/revive(null)
/obj/effect/proc_holder/changeling/fakedeath/can_sting(mob/user)
if(user.status_flags & FAKEDEATH)
user << "<span class='warning'>We are already regenerating.</span>"
return
if(!user.stat) //Confirmation for living changelings if they want to fake their death
switch(alert("Are we sure we wish to fake our own death?",,"Yes", "No"))
if("No")
return
return ..()
@@ -0,0 +1,62 @@
/obj/effect/proc_holder/changeling/fleshmend
name = "Fleshmend"
desc = "Our flesh rapidly regenerates, healing our wounds, and growing \
back missing limbs. Effectiveness decreases with quick, repeated use."
helptext = "Heals a moderate amount of damage over a short period of \
time. Can be used while unconscious. Will alert nearby crew if \
any limbs are regenerated."
chemical_cost = 25
dna_cost = 2
req_stat = UNCONSCIOUS
var/recent_uses = 1 //The factor of which the healing should be divided by
var/healing_ticks = 10
// The ideal total healing amount,
// divided by healing_ticks to get heal/tick
var/total_healing = 100
/obj/effect/proc_holder/changeling/fleshmend/New()
..()
START_PROCESSING(SSobj, src)
/obj/effect/proc_holder/changeling/fleshmend/Destroy()
STOP_PROCESSING(SSobj, src)
..()
/obj/effect/proc_holder/changeling/fleshmend/process()
if(recent_uses > 1)
recent_uses = max(1, recent_uses - (1 / healing_ticks))
//Starts healing you every second for 10 seconds.
//Can be used whilst unconscious.
/obj/effect/proc_holder/changeling/fleshmend/sting_action(mob/living/user)
user << "<span class='notice'>We begin to heal rapidly.</span>"
if(recent_uses > 1)
user << "<span class='warning'>Our healing's effectiveness is reduced \
by quick repeated use!</span>"
spawn(0)
recent_uses++
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.restore_blood()
H.remove_all_embedded_objects()
var/list/missing = H.get_missing_limbs()
if(missing.len)
playsound(user, 'sound/magic/Demon_consume.ogg', 50, 1)
H.visible_message("<span class='warning'>[user]'s missing limbs reform, making a loud, grotesque sound!</span>", "<span class='userdanger'>Your limbs regrow, making a loud, crunchy sound and giving you great pain!</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
H.emote("scream")
H.regenerate_limbs(1)
// The healing itself - doesn't heal toxin damage
// (that's anatomic panacea) and the effectiveness decreases with
// each use in a short timespan
for(var/i in 1 to healing_ticks)
if(user)
var/healpertick = -(total_healing / healing_ticks)
user.adjustBruteLoss(healpertick / recent_uses, 0)
user.adjustOxyLoss(healpertick / recent_uses, 0)
user.adjustFireLoss(healpertick / recent_uses, 0)
user.updatehealth()
sleep(10)
feedback_add_details("changeling_powers","RR")
return 1
@@ -0,0 +1,38 @@
/obj/effect/proc_holder/changeling/headcrab
name = "Last Resort"
desc = "We sacrifice our current body in a moment of need, placing us in control of a vessel."
helptext = "We will be placed in control of a small, fragile creature. We may attack a corpse like this to plant an egg which will slowly mature into a new form for us."
chemical_cost = 20
dna_cost = 1
req_human = 1
/obj/effect/proc_holder/changeling/headcrab/sting_action(mob/user)
var/datum/mind/M = user.mind
var/list/organs = user.getorganszone("head", 1)
for(var/obj/item/organ/I in organs)
I.Remove(user, 1)
explosion(get_turf(user),0,0,2,0,silent=1)
for(var/mob/living/carbon/human/H in range(2,user))
H << "<span class='userdanger'>You are blinded by a shower of blood!</span>"
H.Stun(1)
H.blur_eyes(20)
H.adjust_eye_damage(5)
H.confused += 3
for(var/mob/living/silicon/S in range(2,user))
S << "<span class='userdanger'>Your sensors are disabled by a shower of blood!</span>"
S.Weaken(3)
var/turf = get_turf(user)
spawn(5) // So it's not killed in explosion
var/mob/living/simple_animal/hostile/headcrab/crab = new(turf)
for(var/obj/item/organ/I in organs)
I.loc = crab
crab.origin = M
if(crab.origin)
crab.origin.active = 1
crab.origin.transfer_to(crab)
crab << "<span class='warning'>You burst out of the remains of your former body in a shower of gore!</span>"
user.gib()
feedback_add_details("changeling_powers","LR")
return 1
@@ -0,0 +1,96 @@
//HIVEMIND COMMUNICATION (:g)
/obj/effect/proc_holder/changeling/hivemind_comms
name = "Hivemind Communication"
desc = "We tune our senses to the airwaves to allow us to discreetly communicate and exchange DNA with other changelings."
helptext = "We will be able to talk with other changelings with :g. Exchanged DNA do not count towards absorb objectives."
dna_cost = 0
chemical_cost = -1
/obj/effect/proc_holder/changeling/hivemind_comms/on_purchase(var/mob/user)
..()
var/datum/changeling/changeling=user.mind.changeling
changeling.changeling_speak = 1
user << "<i><font color=#800080>Use say \":g message\" to communicate with the other changelings.</font></i>"
var/obj/effect/proc_holder/changeling/hivemind_upload/S1 = new
if(!changeling.has_sting(S1))
changeling.purchasedpowers+=S1
var/obj/effect/proc_holder/changeling/hivemind_download/S2 = new
if(!changeling.has_sting(S2))
changeling.purchasedpowers+=S2
return
// HIVE MIND UPLOAD/DOWNLOAD DNA
var/list/datum/dna/hivemind_bank = list()
/obj/effect/proc_holder/changeling/hivemind_upload
name = "Hive Channel DNA"
desc = "Allows us to channel DNA in the airwaves to allow other changelings to absorb it."
chemical_cost = 10
dna_cost = -1
/obj/effect/proc_holder/changeling/hivemind_upload/sting_action(var/mob/user)
var/datum/changeling/changeling = user.mind.changeling
var/list/names = list()
for(var/datum/changelingprofile/prof in changeling.stored_profiles)
if(!(prof in hivemind_bank))
names += prof.name
if(names.len <= 0)
user << "<span class='notice'>The airwaves already have all of our DNA.</span>"
return
var/chosen_name = input("Select a DNA to channel: ", "Channel DNA", null) as null|anything in names
if(!chosen_name)
return
var/datum/changelingprofile/chosen_dna = changeling.get_dna(chosen_name)
if(!chosen_dna)
return
var/datum/changelingprofile/uploaded_dna = new chosen_dna.type
chosen_dna.copy_profile(uploaded_dna)
hivemind_bank += uploaded_dna
user << "<span class='notice'>We channel the DNA of [chosen_name] to the air.</span>"
feedback_add_details("changeling_powers","HU")
return 1
/obj/effect/proc_holder/changeling/hivemind_download
name = "Hive Absorb DNA"
desc = "Allows us to absorb DNA that has been channeled to the airwaves. Does not count towards absorb objectives."
chemical_cost = 10
dna_cost = -1
/obj/effect/proc_holder/changeling/hivemind_download/can_sting(mob/living/carbon/user)
if(!..())
return
var/datum/changeling/changeling = user.mind.changeling
var/datum/changelingprofile/first_prof = changeling.stored_profiles[1]
if(first_prof.name == user.real_name)//If our current DNA is the stalest, we gotta ditch it.
user << "<span class='warning'>We have reached our capacity to store genetic information! We must transform before absorbing more.</span>"
return
return 1
/obj/effect/proc_holder/changeling/hivemind_download/sting_action(mob/user)
var/datum/changeling/changeling = user.mind.changeling
var/list/names = list()
for(var/datum/changelingprofile/prof in hivemind_bank)
if(!(prof in changeling.stored_profiles))
names[prof.name] = prof
if(names.len <= 0)
user << "<span class='notice'>There's no new DNA to absorb from the air.</span>"
return
var/S = input("Select a DNA absorb from the air: ", "Absorb DNA", null) as null|anything in names
if(!S)
return
var/datum/changelingprofile/chosen_prof = names[S]
if(!chosen_prof)
return
var/datum/changelingprofile/downloaded_prof = new chosen_prof.type
chosen_prof.copy_profile(downloaded_prof)
changeling.add_profile(downloaded_prof)
user << "<span class='notice'>We absorb the DNA of [S] from the air.</span>"
feedback_add_details("changeling_powers","HD")
return 1
@@ -0,0 +1,34 @@
/obj/effect/proc_holder/changeling/humanform
name = "Human form"
desc = "We change into a human."
chemical_cost = 5
genetic_damage = 3
req_dna = 1
max_genetic_damage = 3
//Transform into a human.
/obj/effect/proc_holder/changeling/humanform/sting_action(mob/living/carbon/user)
var/datum/changeling/changeling = user.mind.changeling
var/list/names = list()
for(var/datum/changelingprofile/prof in changeling.stored_profiles)
names += "[prof.name]"
var/chosen_name = input("Select the target DNA: ", "Target DNA", null) as null|anything in names
if(!chosen_name)
return
var/datum/changelingprofile/chosen_prof = changeling.get_dna(chosen_name)
if(!chosen_prof)
return
if(!user || user.notransform)
return 0
user << "<span class='notice'>We transform our appearance.</span>"
changeling.purchasedpowers -= src
var/newmob = user.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS)
changeling_transform(newmob, chosen_prof)
feedback_add_details("changeling_powers","LFT")
return 1
@@ -0,0 +1,18 @@
/obj/effect/proc_holder/changeling/lesserform
name = "Lesser form"
desc = "We debase ourselves and become lesser. We become a monkey."
chemical_cost = 5
dna_cost = 1
genetic_damage = 3
req_human = 1
//Transform into a monkey.
/obj/effect/proc_holder/changeling/lesserform/sting_action(mob/living/carbon/human/user)
if(!user || user.notransform)
return 0
user << "<span class='warning'>Our genes cry out!</span>"
user.monkeyize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSE)
feedback_add_details("changeling_powers","LF")
return 1
@@ -0,0 +1,69 @@
/obj/effect/proc_holder/changeling/linglink
name = "Hivemind Link"
desc = "Link your victim's mind into the hivemind for personal interrogation"
chemical_cost = 0
dna_cost = 0
req_human = 1
max_genetic_damage = 100
/obj/effect/proc_holder/changeling/linglink/can_sting(mob/living/carbon/user)
if(!..())
return
var/datum/changeling/changeling = user.mind.changeling
if(changeling.islinking)
user << "<span class='warning'>We have already formed a link with the victim!</span>"
return
if(!user.pulling)
user << "<span class='warning'>We must be tightly grabbing a creature to link with them!</span>"
return
if(!iscarbon(user.pulling))
user << "<span class='warning'>We cannot link with this creature!</span>"
return
var/mob/living/carbon/target = user.pulling
if(!target.mind)
user << "<span class='warning'>The victim has no mind to link to!</span>"
return
if(target.stat == DEAD)
user << "<span class='warning'>The victim is dead, you cannot link to a dead mind!</span>"
return
if(target.mind.changeling)
user << "<span class='warning'>The victim is already a part of the hivemind!</span>"
return
if(user.grab_state <= GRAB_NECK)
user << "<span class='warning'>We must have a tighter grip to link with this creature!</span>"
return
return changeling.can_absorb_dna(user,target)
/obj/effect/proc_holder/changeling/linglink/sting_action(mob/user)
var/datum/changeling/changeling = user.mind.changeling
var/mob/living/carbon/human/target = user.pulling
changeling.islinking = 1
for(var/i in 1 to 3)
switch(i)
if(1)
user << "<span class='notice'>This creature is compatible. We must hold still...</span>"
if(2)
user << "<span class='notice'>We stealthily stab [target] with a minor proboscis...</span>"
target << "<span class='userdanger'>You experience a stabbing sensation and your ears begin to ring...</span>"
if(3)
user << "<span class='notice'>You mold the [target]'s mind like clay, they can now speak in the hivemind!</span>"
target << "<span class='userdanger'>A migraine throbs behind your eyes, you hear yourself screaming - but your mouth has not opened!</span>"
for(var/mob/M in mob_list)
if(M.lingcheck() == 2)
M << "<i><font color=#800080>We can sense a foreign presence in the hivemind...</font></i>"
target.mind.linglink = 1
target.say(":g AAAAARRRRGGGGGHHHHH!!")
target << "<font color=#800040><span class='boldannounce'>You can now communicate in the changeling hivemind, say \":g message\" to communicate!</span>"
target.reagents.add_reagent("salbutamol", 40) // So they don't choke to death while you interrogate them
sleep(1800)
feedback_add_details("changeling_powers","A [i]")
if(!do_mob(user, target, 20))
user << "<span class='warning'>Our link with [target] has ended!</span>"
changeling.islinking = 0
target.mind.linglink = 0
return
changeling.islinking = 0
target.mind.linglink = 0
user << "<span class='notice'>You cannot sustain the connection any longer, your victim fades from the hivemind</span>"
target << "<span class='userdanger'>The link cannot be sustained any longer, your connection to the hivemind has faded!</span>"
@@ -0,0 +1,28 @@
/obj/effect/proc_holder/changeling/mimicvoice
name = "Mimic Voice"
desc = "We shape our vocal glands to sound like a desired voice."
helptext = "Will turn your voice into the name that you enter. We must constantly expend chemicals to maintain our form like this."
chemical_cost = 0 //constant chemical drain hardcoded
dna_cost = 1
req_human = 1
// Fake Voice
/obj/effect/proc_holder/changeling/mimicvoice/sting_action(mob/user)
var/datum/changeling/changeling=user.mind.changeling
if(changeling.mimicing)
changeling.mimicing = ""
changeling.chem_recharge_slowdown -= 0.5
user << "<span class='notice'>We return our vocal glands to their original position.</span>"
return
var/mimic_voice = stripped_input(user, "Enter a name to mimic.", "Mimic Voice", null, MAX_NAME_LEN)
if(!mimic_voice)
return
changeling.mimicing = mimic_voice
changeling.chem_recharge_slowdown += 0.5
user << "<span class='notice'>We shape our glands to take the voice of <b>[mimic_voice]</b>, this will slow down regenerating chemicals while active.</span>"
user << "<span class='notice'>Use this power again to return to our original voice and return chemical production to normal levels.</span>"
feedback_add_details("changeling_powers","MV")
@@ -0,0 +1,343 @@
/*
Changeling Mutations! ~By Miauw (ALL OF IT :V)
Contains:
Arm Blade
Space Suit
Shield
Armor
*/
//Parent to shields and blades because muh copypasted code.
/obj/effect/proc_holder/changeling/weapon
name = "Organic Weapon"
desc = "Go tell a coder if you see this"
helptext = "Yell at Miauw and/or Perakp"
chemical_cost = 1000
dna_cost = -1
genetic_damage = 1000
var/weapon_type
var/weapon_name_simple
/obj/effect/proc_holder/changeling/weapon/try_to_sting(mob/user, mob/target)
if(check_weapon(user, user.r_hand, 1))
return
if(check_weapon(user, user.l_hand, 0))
return
..(user, target)
/obj/effect/proc_holder/changeling/weapon/proc/check_weapon(mob/user, obj/item/hand_item, right_hand=1)
if(istype(hand_item, weapon_type))
playsound(user, 'sound/effects/blobattack.ogg', 30, 1)
qdel(hand_item)
user.visible_message("<span class='warning'>With a sickening crunch, [user] reforms their [weapon_name_simple] into an arm!</span>", "<span class='notice'>We assimilate the [weapon_name_simple] back into our body.</span>", "<span class='italics>You hear organic matter ripping and tearing!</span>")
if(right_hand)
user.update_inv_r_hand()
else
user.update_inv_l_hand()
return 1
/obj/effect/proc_holder/changeling/weapon/sting_action(mob/living/user)
if(!user.drop_item())
user << "<span class='warning'>The [user.get_active_hand()] is stuck to your hand, you cannot grow a [weapon_name_simple] over it!</span>"
return
var/limb_regen = 0
if(user.hand) //we regen the arm before changing it into the weapon
limb_regen = user.regenerate_limb("l_arm", 1)
else
limb_regen = user.regenerate_limb("r_arm", 1)
if(limb_regen)
user.visible_message("<span class='warning'>[user]'s missing arm reforms, making a loud, grotesque sound!</span>", "<span class='userdanger'>Your arm regrows, making a loud, crunchy sound and giving you great pain!</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
user.emote("scream")
var/obj/item/W = new weapon_type(user)
user.put_in_hands(W)
playsound(user, 'sound/effects/blobattack.ogg', 30, 1)
return W
/obj/effect/proc_holder/changeling/weapon/on_refund(mob/user)
check_weapon(user, user.r_hand, 1)
check_weapon(user, user.l_hand, 0)
//Parent to space suits and armor.
/obj/effect/proc_holder/changeling/suit
name = "Organic Suit"
desc = "Go tell a coder if you see this"
helptext = "Yell at Miauw and/or Perakp"
chemical_cost = 1000
dna_cost = -1
genetic_damage = 1000
var/helmet_type = /obj/item
var/suit_type = /obj/item
var/suit_name_simple = " "
var/helmet_name_simple = " "
var/recharge_slowdown = 0
var/blood_on_castoff = 0
/obj/effect/proc_holder/changeling/suit/try_to_sting(mob/user, mob/target)
if(check_suit(user))
return
var/mob/living/carbon/human/H = user
..(H, target)
//checks if we already have an organic suit and casts it off.
/obj/effect/proc_holder/changeling/suit/proc/check_suit(mob/user)
var/datum/changeling/changeling = user.mind.changeling
if(!ishuman(user) || !changeling)
return 1
var/mob/living/carbon/human/H = user
if(istype(H.wear_suit, suit_type) || istype(H.head, helmet_type))
H.visible_message("<span class='warning'>[H] casts off their [suit_name_simple]!</span>", "<span class='warning'>We cast off our [suit_name_simple][genetic_damage > 0 ? ", temporarily weakening our genomes." : "."]</span>", "<span class='italics'>You hear the organic matter ripping and tearing!</span>")
H.unEquip(H.head, TRUE) //The qdel on dropped() takes care of it
H.unEquip(H.wear_suit, TRUE)
H.update_inv_wear_suit()
H.update_inv_head()
H.update_hair()
if(blood_on_castoff)
H.add_splatter_floor()
playsound(H.loc, 'sound/effects/splat.ogg', 50, 1) //So real sounds
changeling.geneticdamage += genetic_damage //Casting off a space suit leaves you weak for a few seconds.
changeling.chem_recharge_slowdown -= recharge_slowdown
return 1
/obj/effect/proc_holder/changeling/suit/on_refund(mob/user)
if(!ishuman(user))
return
var/mob/living/carbon/human/H = user
check_suit(H)
/obj/effect/proc_holder/changeling/suit/sting_action(mob/living/carbon/human/user)
if(!user.canUnEquip(user.wear_suit))
user << "\the [user.wear_suit] is stuck to your body, you cannot grow a [suit_name_simple] over it!"
return
if(!user.canUnEquip(user.head))
user << "\the [user.head] is stuck on your head, you cannot grow a [helmet_name_simple] over it!"
return
user.unEquip(user.head)
user.unEquip(user.wear_suit)
user.equip_to_slot_if_possible(new suit_type(user), slot_wear_suit, 1, 1, 1)
user.equip_to_slot_if_possible(new helmet_type(user), slot_head, 1, 1, 1)
var/datum/changeling/changeling = user.mind.changeling
changeling.chem_recharge_slowdown += recharge_slowdown
return 1
//fancy headers yo
/***************************************\
|***************ARM BLADE***************|
\***************************************/
/obj/effect/proc_holder/changeling/weapon/arm_blade
name = "Arm Blade"
desc = "We reform one of our arms into a deadly blade."
helptext = "We may retract our armblade in the same manner as we form it. Cannot be used while in lesser form."
chemical_cost = 20
dna_cost = 2
genetic_damage = 10
req_human = 1
max_genetic_damage = 20
weapon_type = /obj/item/weapon/melee/arm_blade
weapon_name_simple = "blade"
/obj/item/weapon/melee/arm_blade
name = "arm blade"
desc = "A grotesque blade made out of bone and flesh that cleaves through people as a hot knife through butter"
icon = 'icons/obj/weapons.dmi'
icon_state = "arm_blade"
item_state = "arm_blade"
flags = ABSTRACT | NODROP | DROPDEL
w_class = 5.0
force = 25
throwforce = 0 //Just to be on the safe side
throw_range = 0
throw_speed = 0
sharpness = IS_SHARP
/obj/item/weapon/melee/arm_blade/New(location,silent)
..()
if(ismob(loc) && !silent)
loc.visible_message("<span class='warning'>A grotesque blade forms around [loc.name]\'s arm!</span>", "<span class='warning'>Our arm twists and mutates, transforming it into a deadly blade.</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
/obj/item/weapon/melee/arm_blade/afterattack(atom/target, mob/user, proximity)
if(!proximity)
return
if(istype(target, /obj/structure/table))
var/obj/structure/table/T = target
T.table_destroy()
else if(istype(target, /obj/machinery/computer))
var/obj/machinery/computer/C = target
C.attack_alien(user) //muh copypasta
else if(istype(target, /obj/machinery/door/airlock))
var/obj/machinery/door/airlock/A = target
if(!A.requiresID() || A.allowed(user)) //This is to prevent stupid shit like hitting a door with an arm blade, the door opening because you have acces and still getting a "the airlocks motors resist our efforts to force it" message.
return
if(A.hasPower())
if(A.locked)
user << "<span class='warning'>The airlock's bolts prevent it from being forced!</span>"
return
user << "<span class='warning'>The airlock's motors are resisting, this may take time...</span>"
if(do_after(user, 100, target = A))
A.open(2)
return
else if(A.locked)
user << "<span class='warning'>The airlock's bolts prevent it from being forced!</span>"
return
else
//user.say("Heeeeeeeeeerrre's Johnny!")
user.visible_message("<span class='warning'>[user] forces the door to open with \his [src]!</span>", "<span class='warning'>We force the door to open.</span>", "<span class='italics'>You hear a metal screeching sound.</span>")
A.open(1)
/***************************************\
|****************SHIELD*****************|
\***************************************/
/obj/effect/proc_holder/changeling/weapon/shield
name = "Organic Shield"
desc = "We reform one of our arms into a hard shield."
helptext = "Organic tissue cannot resist damage forever; the shield will break after it is hit too much. The more genomes we absorb, the stronger it is. Cannot be used while in lesser form."
chemical_cost = 20
dna_cost = 1
genetic_damage = 12
req_human = 1
max_genetic_damage = 20
weapon_type = /obj/item/weapon/shield/changeling
weapon_name_simple = "shield"
/obj/effect/proc_holder/changeling/weapon/shield/sting_action(mob/user)
var/datum/changeling/changeling = user.mind.changeling //So we can read the absorbedcount.
if(!changeling)
return
var/obj/item/weapon/shield/changeling/S = ..(user)
S.remaining_uses = round(changeling.absorbedcount * 3)
return 1
/obj/item/weapon/shield/changeling
name = "shield-like mass"
desc = "A mass of tough, boney tissue. You can still see the fingers as a twisted pattern in the shield."
flags = ABSTRACT | NODROP | DROPDEL
icon = 'icons/obj/weapons.dmi'
icon_state = "ling_shield"
block_chance = 50
var/remaining_uses //Set by the changeling ability.
/obj/item/weapon/shield/changeling/New()
..()
if(ismob(loc))
loc.visible_message("<span class='warning'>The end of [loc.name]\'s hand inflates rapidly, forming a huge shield-like mass!</span>", "<span class='warning'>We inflate our hand into a strong shield.</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
/obj/item/weapon/shield/changeling/hit_reaction()
if(remaining_uses < 1)
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
H.visible_message("<span class='warning'>With a sickening crunch, [H] reforms his shield into an arm!</span>", "<span class='notice'>We assimilate our shield into our body</span>", "<span class='italics>You hear organic matter ripping and tearing!</span>")
H.unEquip(src, 1)
qdel(src)
return 0
else
remaining_uses--
return ..()
/***************************************\
|*********SPACE SUIT + HELMET***********|
\***************************************/
/obj/effect/proc_holder/changeling/suit/organic_space_suit
name = "Organic Space Suit"
desc = "We grow an organic suit to protect ourselves from space exposure."
helptext = "We must constantly repair our form to make it space-proof, reducing chemical production while we are protected. Retreating the suit damages our genomes. Cannot be used in lesser form."
chemical_cost = 20
dna_cost = 2
genetic_damage = 8
req_human = 1
max_genetic_damage = 20
suit_type = /obj/item/clothing/suit/space/changeling
helmet_type = /obj/item/clothing/head/helmet/space/changeling
suit_name_simple = "flesh shell"
helmet_name_simple = "space helmet"
recharge_slowdown = 0.5
blood_on_castoff = 1
/obj/item/clothing/suit/space/changeling
name = "flesh mass"
icon_state = "lingspacesuit"
desc = "A huge, bulky mass of pressure and temperature-resistant organic tissue, evolved to facilitate space travel."
flags = STOPSPRESSUREDMAGE | NODROP | DROPDEL //Not THICKMATERIAL because it's organic tissue, so if somebody tries to inject something into it, it still ends up in your blood. (also balance but muh fluff)
allowed = list(/obj/item/device/flashlight, /obj/item/weapon/tank/internals/emergency_oxygen, /obj/item/weapon/tank/internals/oxygen)
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) //No armor at all.
/obj/item/clothing/suit/space/changeling/New()
..()
if(ismob(loc))
loc.visible_message("<span class='warning'>[loc.name]\'s flesh rapidly inflates, forming a bloated mass around their body!</span>", "<span class='warning'>We inflate our flesh, creating a spaceproof suit!</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
START_PROCESSING(SSobj, src)
/obj/item/clothing/suit/space/changeling/process()
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
H.reagents.add_reagent("salbutamol", REAGENTS_METABOLISM)
/obj/item/clothing/head/helmet/space/changeling
name = "flesh mass"
icon_state = "lingspacehelmet"
desc = "A covering of pressure and temperature-resistant organic tissue with a glass-like chitin front."
flags = STOPSPRESSUREDMAGE | NODROP | DROPDEL //Again, no THICKMATERIAL.
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0)
flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH
/***************************************\
|*****************ARMOR*****************|
\***************************************/
/obj/effect/proc_holder/changeling/suit/armor
name = "Chitinous Armor"
desc = "We turn our skin into tough chitin to protect us from damage."
helptext = "Upkeep of the armor requires a low expenditure of chemicals. The armor is strong against brute force, but does not provide much protection from lasers. Retreating the armor damages our genomes. Cannot be used in lesser form."
chemical_cost = 20
dna_cost = 1
genetic_damage = 11
req_human = 1
max_genetic_damage = 20
recharge_slowdown = 0.25
suit_type = /obj/item/clothing/suit/armor/changeling
helmet_type = /obj/item/clothing/head/helmet/changeling
suit_name_simple = "armor"
helmet_name_simple = "helmet"
/obj/item/clothing/suit/armor/changeling
name = "chitinous mass"
desc = "A tough, hard covering of black chitin."
icon_state = "lingarmor"
flags = NODROP | DROPDEL
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
armor = list(melee = 40, bullet = 40, laser = 40, energy = 20, bomb = 10, bio = 4, rad = 0)
flags_inv = HIDEJUMPSUIT
cold_protection = 0
heat_protection = 0
/obj/item/clothing/suit/armor/changeling/New()
..()
if(ismob(loc))
loc.visible_message("<span class='warning'>[loc.name]\'s flesh turns black, quickly transforming into a hard, chitinous mass!</span>", "<span class='warning'>We harden our flesh, creating a suit of armor!</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
/obj/item/clothing/head/helmet/changeling
name = "chitinous mass"
desc = "A tough, hard covering of black chitin with transparent chitin in front."
icon_state = "lingarmorhelmet"
flags = NODROP | DROPDEL
armor = list(melee = 30, bullet = 30, laser = 40, energy = 20, bomb = 10, bio = 4, rad = 0)
flags_inv = HIDEEARS|HIDEHAIR|HIDEEYES|HIDEFACIALHAIR|HIDEFACE
@@ -0,0 +1,29 @@
/obj/effect/proc_holder/changeling/panacea
name = "Anatomic Panacea"
desc = "Expels impurifications from our form; curing diseases, removing parasites, sobering us, purging toxins and radiation, and resetting our genetic code completely."
helptext = "Can be used while unconscious."
chemical_cost = 20
dna_cost = 1
req_stat = UNCONSCIOUS
//Heals the things that the other regenerative abilities don't.
/obj/effect/proc_holder/changeling/panacea/sting_action(mob/user)
user << "<span class='notice'>We begin cleansing impurities from our form.</span>"
var/obj/item/organ/body_egg/egg = user.getorgan(/obj/item/organ/body_egg)
if(egg)
egg.Remove(user)
if(iscarbon(user))
var/mob/living/carbon/C = user
C.vomit(0)
egg.loc = get_turf(user)
user.reagents.add_reagent("mutadone", 10)
user.reagents.add_reagent("pen_acid", 20)
user.reagents.add_reagent("antihol", 10)
user.reagents.add_reagent("mannitol", 25)
for(var/datum/disease/D in user.viruses)
D.cure()
feedback_add_details("changeling_powers","AP")
return 1
@@ -0,0 +1,22 @@
/obj/effect/proc_holder/changeling/revive
name = "Regenerate"
desc = "We regenerate, healing all damage from our form."
req_stat = DEAD
always_keep = 1
//Revive from revival stasis
/obj/effect/proc_holder/changeling/revive/sting_action(mob/living/carbon/user)
user.status_flags &= ~(FAKEDEATH)
user.tod = null
user.revive(full_heal = 1)
user.regenerate_limbs(0, list("head")) //regenerate all limbs except the head
user << "<span class='notice'>We have regenerated.</span>"
user.mind.changeling.purchasedpowers -= src
feedback_add_details("changeling_powers","CR")
return 1
/obj/effect/proc_holder/changeling/revive/can_be_used_by(mob/user)
if((user.stat != DEAD) && !(user.status_flags & FAKEDEATH))
user.mind.changeling.purchasedpowers -= src
return 0
. = ..()
@@ -0,0 +1,45 @@
/obj/effect/proc_holder/changeling/resonant_shriek
name = "Resonant Shriek"
desc = "Our lungs and vocal chords shift, allowing us to briefly emit a noise that deafens and confuses the weak-minded."
helptext = "Emits a high-frequency sound that confuses and deafens humans, blows out nearby lights and overloads cyborg sensors."
chemical_cost = 20
dna_cost = 1
req_human = 1
//A flashy ability, good for crowd control and sewing chaos.
/obj/effect/proc_holder/changeling/resonant_shriek/sting_action(mob/user)
for(var/mob/living/M in get_hearers_in_view(4, user))
if(iscarbon(M))
if(!M.mind || !M.mind.changeling)
M.adjustEarDamage(0,30)
M.confused += 25
M.Jitter(50)
else
M << sound('sound/effects/screech.ogg')
if(issilicon(M))
M << sound('sound/weapons/flash.ogg')
M.Weaken(rand(5,10))
for(var/obj/machinery/light/L in range(4, user))
L.on = 1
L.broken()
feedback_add_details("changeling_powers","RS")
return 1
/obj/effect/proc_holder/changeling/dissonant_shriek
name = "Dissonant Shriek"
desc = "We shift our vocal cords to release a high-frequency sound that overloads nearby electronics."
chemical_cost = 20
dna_cost = 1
//A flashy ability, good for crowd control and sewing chaos.
/obj/effect/proc_holder/changeling/dissonant_shriek/sting_action(mob/user)
for(var/obj/machinery/light/L in range(5, usr))
L.on = 1
L.broken()
empulse(get_turf(user), 2, 5, 1)
return 1
@@ -0,0 +1,16 @@
/obj/effect/proc_holder/changeling/spiders
name = "Spread Infestation"
desc = "Our form divides, creating arachnids which will grow into deadly beasts."
helptext = "The spiders are thoughtless creatures, and may attack their creators when fully grown. Requires at least 5 DNA absorptions."
chemical_cost = 45
dna_cost = 1
req_dna = 5
//Makes some spiderlings. Good for setting traps and causing general trouble.
/obj/effect/proc_holder/changeling/spiders/sting_action(mob/user)
for(var/i=0, i<2, i++)
var/obj/effect/spider/spiderling/S = new(user.loc)
S.grow_as = /mob/living/simple_animal/hostile/poison/giant_spider/hunter
feedback_add_details("changeling_powers","SI")
return 1
@@ -0,0 +1,50 @@
//Strained Muscles: Temporary speed boost at the cost of rapid damage
//Limited because of hardsuits and such; ideally, used for a quick getaway
/obj/effect/proc_holder/changeling/strained_muscles
name = "Strained Muscles"
desc = "We evolve the ability to reduce the acid buildup in our muscles, allowing us to move much faster."
helptext = "The strain will make us tired, and we will rapidly become fatigued. Standard weight restrictions, like hardsuits, still apply. Cannot be used in lesser form."
chemical_cost = 0
dna_cost = 1
req_human = 1
var/stacks = 0 //Increments every 5 seconds; damage increases over time
var/active = 0 //Whether or not you are a hedgehog
/obj/effect/proc_holder/changeling/strained_muscles/sting_action(mob/living/carbon/user)
active = !active
if(active)
user << "<span class='notice'>Our muscles tense and strengthen.</span>"
else
user.status_flags -= GOTTAGOFAST
user << "<span class='notice'>Our muscles relax.</span>"
if(stacks >= 10)
user << "<span class='danger'>We collapse in exhaustion.</span>"
user.Weaken(3)
user.emote("gasp")
while(active)
user.status_flags |= GOTTAGOFAST
if(user.stat != CONSCIOUS || user.staminaloss >= 90)
active = !active
user << "<span class='notice'>Our muscles relax without the energy to strengthen them.</span>"
user.Weaken(2)
user.status_flags -= GOTTAGOFAST
break
stacks++
//user.take_organ_damage(stacks * 0.03, 0)
user.staminaloss += stacks * 1.3 //At first the changeling may regenerate stamina fast enough to nullify fatigue, but it will stack
if(stacks == 11) //Warning message that the stacks are getting too high
user << "<span class='warning'>Our legs are really starting to hurt...</span>"
sleep(40)
while(!active) //Damage stacks decrease fairly rapidly while not in sanic mode
if(stacks >= 1)
stacks--
sleep(20)
feedback_add_details("changeling_powers","SANIC")
return 1
@@ -0,0 +1,249 @@
/obj/effect/proc_holder/changeling/sting
name = "Tiny Prick"
desc = "Stabby stabby"
var/sting_icon = null
/obj/effect/proc_holder/changeling/sting/Click()
var/mob/user = usr
if(!user || !user.mind || !user.mind.changeling)
return
if(!(user.mind.changeling.chosen_sting))
set_sting(user)
else
unset_sting(user)
return
/obj/effect/proc_holder/changeling/sting/proc/set_sting(mob/user)
user << "<span class='notice'>We prepare our sting, use alt+click or middle mouse button on target to sting them.</span>"
user.mind.changeling.chosen_sting = src
user.hud_used.lingstingdisplay.icon_state = sting_icon
user.hud_used.lingstingdisplay.invisibility = 0
/obj/effect/proc_holder/changeling/sting/proc/unset_sting(mob/user)
user << "<span class='warning'>We retract our sting, we can't sting anyone for now.</span>"
user.mind.changeling.chosen_sting = null
user.hud_used.lingstingdisplay.icon_state = null
user.hud_used.lingstingdisplay.invisibility = INVISIBILITY_ABSTRACT
/mob/living/carbon/proc/unset_sting()
if(mind && mind.changeling && mind.changeling.chosen_sting)
src.mind.changeling.chosen_sting.unset_sting(src)
/obj/effect/proc_holder/changeling/sting/can_sting(mob/user, mob/target)
if(!..())
return
if(!user.mind.changeling.chosen_sting)
user << "We haven't prepared our sting yet!"
if(!iscarbon(target))
return
if(!isturf(user.loc))
return
if(!AStar(user, target.loc, /turf/proc/Distance, user.mind.changeling.sting_range, simulated_only = 0))
return
if(target.mind && target.mind.changeling)
sting_feedback(user,target)
take_chemical_cost(user.mind.changeling)
return
return 1
/obj/effect/proc_holder/changeling/sting/sting_feedback(mob/user, mob/target)
if(!target)
return
user << "<span class='notice'>We stealthily sting [target.name].</span>"
if(target.mind && target.mind.changeling)
target << "<span class='warning'>You feel a tiny prick.</span>"
return 1
/obj/effect/proc_holder/changeling/sting/transformation
name = "Transformation Sting"
desc = "We silently sting a human, injecting a retrovirus that forces them to transform."
helptext = "The victim will transform much like a changeling would. The effects will be obvious to the victim, and the process will damage our genomes."
sting_icon = "sting_transform"
chemical_cost = 40
dna_cost = 3
genetic_damage = 100
var/datum/changelingprofile/selected_dna = null
/obj/effect/proc_holder/changeling/sting/transformation/Click()
var/mob/user = usr
var/datum/changeling/changeling = user.mind.changeling
if(changeling.chosen_sting)
unset_sting(user)
return
selected_dna = changeling.select_dna("Select the target DNA: ", "Target DNA")
if(!selected_dna)
return
if(NOTRANSSTING in selected_dna.dna.species.specflags)
user << "<span class = 'notice'>That DNA is not compatible with changeling retrovirus!"
return
..()
/obj/effect/proc_holder/changeling/sting/transformation/can_sting(mob/user, mob/target)
if(!..())
return
if((target.disabilities & HUSK) || !target.has_dna())
user << "<span class='warning'>Our sting appears ineffective against its DNA.</span>"
return 0
return 1
/obj/effect/proc_holder/changeling/sting/transformation/sting_action(mob/user, mob/target)
add_logs(user, target, "stung", "transformation sting", " new identity is [selected_dna.dna.real_name]")
var/datum/dna/NewDNA = selected_dna.dna
if(ismonkey(target))
user << "<span class='notice'>Our genes cry out as we sting [target.name]!</span>"
if(iscarbon(target))
var/mob/living/carbon/C = target
if(C.status_flags & CANWEAKEN)
C.do_jitter_animation(500)
C.take_organ_damage(20, 0) //The process is extremely painful
target.visible_message("<span class='danger'>[target] begins to violenty convulse!</span>","<span class='userdanger'>You feel a tiny prick and a begin to uncontrollably convulse!</span>")
spawn(10)
C.real_name = NewDNA.real_name
NewDNA.transfer_identity(C, transfer_SE=1)
C.updateappearance(mutcolor_update=1)
C.domutcheck()
feedback_add_details("changeling_powers","TS")
return 1
/obj/effect/proc_holder/changeling/sting/false_armblade
name = "False Armblade Sting"
desc = "We silently sting a human, injecting a retrovirus that mutates their arm to temporarily appear as an armblade."
helptext = "The victim will form an armblade much like a changeling would, except the armblade is dull and useless."
sting_icon = "sting_armblade"
chemical_cost = 20
dna_cost = 1
genetic_damage = 20
max_genetic_damage = 10
/obj/item/weapon/melee/arm_blade/false
desc = "A grotesque mass of flesh that used to be your arm. Although it looks dangerous at first, you can tell it's actually quite dull and useless."
force = 5 //Basically as strong as a punch
/obj/item/weapon/melee/arm_blade/false/afterattack(atom/target, mob/user, proximity)
return
/obj/effect/proc_holder/changeling/sting/false_armblade/can_sting(mob/user, mob/target)
if(!..())
return
if((target.disabilities & HUSK) || !target.has_dna())
user << "<span class='warning'>Our sting appears ineffective against its DNA.</span>"
return 0
return 1
/obj/effect/proc_holder/changeling/sting/false_armblade/sting_action(mob/user, mob/target)
add_logs(user, target, "stung", object="falso armblade sting")
if(!target.drop_item())
user << "<span class='warning'>The [target.get_active_hand()] is stuck to their hand, you cannot grow a false armblade over it!</span>"
return
if(ismonkey(target))
user << "<span class='notice'>Our genes cry out as we sting [target.name]!</span>"
var/obj/item/weapon/melee/arm_blade/false/blade = new(target,1)
target.put_in_hands(blade)
target.visible_message("<span class='warning'>A grotesque blade forms around [target.name]\'s arm!</span>", "<span class='userdanger'>Your arm twists and mutates, transforming into a horrific monstrosity!</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
playsound(target, 'sound/effects/blobattack.ogg', 30, 1)
addtimer(src, "remove_fake", 600, target, blade)
feedback_add_details("changeling_powers","AS")
return 1
/obj/effect/proc_holder/changeling/sting/false_armblade/proc/remove_fake(mob/target, obj/item/weapon/melee/arm_blade/false/blade)
playsound(target, 'sound/effects/blobattack.ogg', 30, 1)
target.visible_message("<span class='warning'>With a sickening crunch, \
[target] reforms their [blade.name] into an arm!</span>",
"<span class='warning'>[blade] reforms back to normal.</span>",
"<span class='italics>You hear organic matter ripping and tearing!</span>")
qdel(blade)
target.update_inv_l_hand()
target.update_inv_r_hand()
/obj/effect/proc_holder/changeling/sting/extract_dna
name = "Extract DNA Sting"
desc = "We stealthily sting a target and extract their DNA."
helptext = "Will give you the DNA of your target, allowing you to transform into them."
sting_icon = "sting_extract"
chemical_cost = 25
dna_cost = 0
/obj/effect/proc_holder/changeling/sting/extract_dna/can_sting(mob/user, mob/target)
if(..())
return user.mind.changeling.can_absorb_dna(user, target)
/obj/effect/proc_holder/changeling/sting/extract_dna/sting_action(mob/user, mob/living/carbon/human/target)
add_logs(user, target, "stung", "extraction sting")
if(!(user.mind.changeling.has_dna(target.dna)))
user.mind.changeling.add_new_profile(target, user)
feedback_add_details("changeling_powers","ED")
return 1
/obj/effect/proc_holder/changeling/sting/mute
name = "Mute Sting"
desc = "We silently sting a human, completely silencing them for a short time."
helptext = "Does not provide a warning to the victim that they have been stung, until they try to speak and cannot."
sting_icon = "sting_mute"
chemical_cost = 20
dna_cost = 2
/obj/effect/proc_holder/changeling/sting/mute/sting_action(mob/user, mob/living/carbon/target)
add_logs(user, target, "stung", "mute sting")
target.silent += 30
feedback_add_details("changeling_powers","MS")
return 1
/obj/effect/proc_holder/changeling/sting/blind
name = "Blind Sting"
desc = "Temporarily blinds the target."
helptext = "This sting completely blinds a target for a short time."
sting_icon = "sting_blind"
chemical_cost = 25
dna_cost = 1
/obj/effect/proc_holder/changeling/sting/blind/sting_action(mob/user, mob/living/carbon/target)
add_logs(user, target, "stung", "blind sting")
target << "<span class='danger'>Your eyes burn horrifically!</span>"
target.become_nearsighted()
target.blind_eyes(20)
target.blur_eyes(40)
feedback_add_details("changeling_powers","BS")
return 1
/obj/effect/proc_holder/changeling/sting/LSD
name = "Hallucination Sting"
desc = "Causes terror in the target."
helptext = "We evolve the ability to sting a target with a powerful hallucinogenic chemical. The target does not notice they have been stung, and the effect occurs after 30 to 60 seconds."
sting_icon = "sting_lsd"
chemical_cost = 10
dna_cost = 1
/obj/effect/proc_holder/changeling/sting/LSD/sting_action(mob/user, mob/living/carbon/target)
add_logs(user, target, "stung", "LSD sting")
addtimer(src, "hallucination_time", rand(300,600), target)
feedback_add_details("changeling_powers","HS")
return 1
/obj/effect/proc_holder/changeling/sting/LSD/proc/hallucination_time(mob/living/carbon/target)
if(target)
target.hallucination = max(400, target.hallucination)
/obj/effect/proc_holder/changeling/sting/cryo
name = "Cryogenic Sting"
desc = "We silently sting a human with a cocktail of chemicals that freeze them."
helptext = "Does not provide a warning to the victim, though they will likely realize they are suddenly freezing."
sting_icon = "sting_cryo"
chemical_cost = 15
dna_cost = 2
/obj/effect/proc_holder/changeling/sting/cryo/sting_action(mob/user, mob/target)
add_logs(user, target, "stung", "cryo sting")
if(target.reagents)
target.reagents.add_reagent("frostoil", 30)
feedback_add_details("changeling_powers","CS")
return 1
@@ -0,0 +1,72 @@
/obj/effect/proc_holder/changeling/transform
name = "Transform"
desc = "We take on the appearance and voice of one we have absorbed."
chemical_cost = 5
dna_cost = 0
req_dna = 1
req_human = 1
max_genetic_damage = 3
/obj/item/clothing/glasses/changeling
name = "flesh"
flags = NODROP
/obj/item/clothing/under/changeling
name = "flesh"
flags = NODROP
/obj/item/clothing/suit/changeling
name = "flesh"
flags = NODROP
allowed = list(/obj/item/changeling)
/obj/item/clothing/head/changeling
name = "flesh"
flags = NODROP
/obj/item/clothing/shoes/changeling
name = "flesh"
flags = NODROP
/obj/item/clothing/gloves/changeling
name = "flesh"
flags = NODROP
/obj/item/clothing/mask/changeling
name = "flesh"
flags = NODROP
/obj/item/changeling
name = "flesh"
flags = NODROP
slot_flags = ALL
allowed = list(/obj/item/changeling)
//Change our DNA to that of somebody we've absorbed.
/obj/effect/proc_holder/changeling/transform/sting_action(mob/living/carbon/human/user)
var/datum/changeling/changeling = user.mind.changeling
var/datum/changelingprofile/chosen_prof = changeling.select_dna("Select the target DNA: ", "Target DNA", user)
if(!chosen_prof)
return
changeling_transform(user, chosen_prof)
feedback_add_details("changeling_powers","TR")
return 1
/datum/changeling/proc/select_dna(var/prompt, var/title, var/mob/living/carbon/user)
var/list/names = list("Drop Flesh Disguise")
for(var/datum/changelingprofile/prof in stored_profiles)
names += "[prof.name]"
var/chosen_name = input(prompt, title, null) as null|anything in names
if(!chosen_name)
return
if(chosen_name == "Drop Flesh Disguise")
for(var/slot in slots)
if(istype(user.vars[slot], slot2type[slot]))
qdel(user.vars[slot])
var/datum/changelingprofile/prof = get_dna(chosen_name)
return prof
@@ -0,0 +1,76 @@
/datum/game_mode/traitor/changeling
name = "traitor+changeling"
config_tag = "traitorchan"
traitors_possible = 3 //hard limit on traitors if scaling is turned off
restricted_jobs = list("AI", "Cyborg")
required_players = 0
required_enemies = 1 // how many of each type are required
recommended_enemies = 3
reroll_friendly = 1
var/list/possible_changelings = list()
var/const/changeling_amount = 1 //hard limit on changelings if scaling is turned off
/datum/game_mode/traitor/changeling/announce()
world << "<B>The current game mode is - Traitor+Changeling!</B>"
world << "<B>There are alien creatures on the station along with some syndicate operatives out for their own gain! Do not let the changelings or the traitors succeed!</B>"
/datum/game_mode/traitor/changeling/can_start()
if(!..())
return 0
possible_changelings = get_players_for_role(ROLE_CHANGELING)
if(possible_changelings.len < required_enemies)
return 0
return 1
/datum/game_mode/traitor/changeling/pre_setup()
if(config.protect_roles_from_antagonist)
restricted_jobs += protected_jobs
if(config.protect_assistant_from_antagonist)
restricted_jobs += "Assistant"
var/list/datum/mind/possible_changelings = get_players_for_role(ROLE_CHANGELING)
var/num_changelings = 1
if(config.changeling_scaling_coeff)
num_changelings = max(1, min( round(num_players()/(config.changeling_scaling_coeff*4))+2, round(num_players()/(config.changeling_scaling_coeff*2)) ))
else
num_changelings = max(1, min(num_players(), changeling_amount/2))
if(possible_changelings.len>0)
for(var/j = 0, j < num_changelings, j++)
if(!possible_changelings.len) break
var/datum/mind/changeling = pick(possible_changelings)
antag_candidates -= changeling
possible_changelings -= changeling
changelings += changeling
modePlayer += changelings
changeling.restricted_roles = restricted_jobs
return ..()
else
return 0
/datum/game_mode/traitor/changeling/post_setup()
for(var/datum/mind/changeling in changelings)
changeling.current.make_changeling()
changeling.special_role = "Changeling"
forge_changeling_objectives(changeling)
greet_changeling(changeling)
ticker.mode.update_changeling_icons_added(changeling)
..()
return
/datum/game_mode/traitor/changeling/make_antag_chance(mob/living/carbon/human/character) //Assigns changeling to latejoiners
var/changelingcap = min( round(joined_player_list.len/(config.changeling_scaling_coeff*4))+2, round(joined_player_list.len/(config.changeling_scaling_coeff*2)) )
if(ticker.mode.changelings.len >= changelingcap) //Caps number of latejoin antagonists
..()
return
if(ticker.mode.changelings.len <= (changelingcap - 2) || prob(100 / (config.changeling_scaling_coeff * 4)))
if(ROLE_CHANGELING in character.client.prefs.be_special)
if(!jobban_isbanned(character.client, ROLE_CHANGELING) && !jobban_isbanned(character.client, "Syndicate"))
if(age_check(character.client))
if(!(character.job in restricted_jobs))
character.mind.make_Changling()
..()
@@ -0,0 +1,45 @@
var/global/clockwork_construction_value = 0 //The total value of all structures built by the clockwork cult
var/global/clockwork_caches = 0 //How many clockwork caches exist in the world (not each individual)
var/global/clockwork_daemons = 0 //How many daemons exist in the world
var/global/list/clockwork_generals_invoked = list("nezbere" = FALSE, "sevtug" = FALSE, "nzcrentr" = FALSE, "inath-neq" = FALSE) //How many generals have been recently invoked
var/global/list/all_clockwork_objects = list() //All clockwork items, structures, and effects in existence
var/global/list/all_clockwork_mobs = list() //All clockwork SERVANTS (not creatures) in existence
var/global/list/clockwork_component_cache = list("belligerent_eye" = 0, "vanguard_cogwheel" = 0, "guvax_capacitor" = 0, "replicant_alloy" = 0, "hierophant_ansible" = 0) //The pool of components that caches draw from
var/global/ratvar_awakens = FALSE //If Ratvar has been summoned
#define SCRIPTURE_PERIPHERAL 0 //Scripture tiers; peripherals should never be used
#define SCRIPTURE_DRIVER 1
#define SCRIPTURE_SCRIPT 2
#define SCRIPTURE_APPLICATION 3
#define SCRIPTURE_REVENANT 4
#define SCRIPTURE_JUDGEMENT 5
#define SLAB_PRODUCTION_TIME 600 //how long(deciseconds) slabs require to produce a single component; defaults to 1 minute
#define CACHE_PRODUCTION_TIME 900 //how long(deciseconds) caches require to produce a component; defaults to 1 minute 30 seconds
#define LOWER_PROB_PER_COMPONENT 10 //how much each component in the cache reduces the weight of getting another of that component type
#define MAX_COMPONENTS_BEFORE_RAND 10*LOWER_PROB_PER_COMPONENT //the number of each component, times LOWER_PROB_PER_COMPONENT, you need to have before component generation will become random
#define CLOCKWORK_GENERAL_COOLDOWN 3000 //how long clockwork generals go on cooldown after use, defaults to 5 minutes
//porselytizer defines
#define REPLICANT_ALLOY_UNIT 100 //how much each piece of replicant alloy gives in a clockwork proselytizer
#define REPLICANT_STANDARD REPLICANT_ALLOY_UNIT*0.2 //how much alloy is in anything else; doesn't matter as much as the following
#define REPLICANT_FLOOR REPLICANT_ALLOY_UNIT*0.1 //how much alloy is in a clockwork floor, determines the cost of clockwork floor production
#define REPLICANT_WALL_MINUS_FLOOR REPLICANT_ALLOY_UNIT*0.4 //amount of alloy in a clockwork wall, determines the cost of clockwork wall production
#define REPLICANT_WALL_TOTAL REPLICANT_WALL_MINUS_FLOOR+REPLICANT_FLOOR //how much alloy is in a clockwork wall and the floor under it
//Ark defines
#define GATEWAY_SUMMON_RATE 2 //the time amount the Gateway to the Celestial Derelict gets each process tick; defaults to 2 per tick
#define GATEWAY_REEBE_FOUND 100 //when progress is at or above this, the gateway finds reebe and begins drawing power
#define GATEWAY_RATVAR_COMING 250 //when progress is at or above this, ratvar has entered and is coming through the gateway
#define GATEWAY_RATVAR_ARRIVAL 300 //when progress is at or above this, game over ratvar's here everybody go home
@@ -0,0 +1,306 @@
/*
CLOCKWORK CULT: Based off of the failed pull requests from vgstation
While Nar-Sie is the oldest and most prominent of the elder gods, there are other forces at work in the universe.
Ratvar, the Clockwork Justiciar, a homage to Nar-Sie granted sentience by its own power, is one such other force.
Imprisoned within a massive construct known as the Celestial Derelict - or Reebe - an intense hatred of the Blood God festers.
Ratvar, unable to act in the mortal plane, seeks to return and forms covenants with mortals in order to bolster his influence.
Due to his mechanical nature, Ratvar is also capable of influencing silicon-based lifeforms, unlike Nar-Sie, who can only influence natural life.
This is a team-based gamemode.
There are three possible objectives the Enlightened - Ratvar's minions - can have:
1. Ensure X amount of Enlightened escape the station through the shuttle or otherwise.
2. Convert all silicon lifeforms on the station to Ratvar's cause.
3. Summon Ratvar via construction of a Gateway.
The clockwork version of an arcane tome is the clockwork slab.
While it can perform certain actions, it consumes a resource called components.
Components, which are fallen fragments of Ratvar's body as he rusts in Reebe, are powerful and have various effects.
Game-wise, clockwork slabs will generate components over time, with more powerful components being slower.
This file's folder contains:
__clock_defines.dm: Defined variables
clock_cult.dm: Core gamemode files.
clock_mobs.dm: Hostile and benign clockwork creatures.
clock_items.dm: Items
clock_structures.dm: Structures and effects
clock_ratvar.dm: The Ark of the Clockwork Justiciar and Ratvar himself. Important enough to have his own file.
clock_scripture.dm: Scripture and rites.
clock_unsorted.dm: Anything else with no place to be
*/
///////////
// PROCS //
///////////
/proc/is_servant_of_ratvar(mob/living/M)
return M && istype(M) && M.mind && ticker && ticker.mode && (M.mind in ticker.mode.servants_of_ratvar)
/proc/is_eligible_servant(mob/living/M)
if(!istype(M))
return 0
if(!M.mind)
return 0
if(M.mind.enslaved_to)
return 0
if(iscultist(M) || isconstruct(M))
return 0
if(isbrain(M))
return 1
if(ishuman(M))
if(isloyal(M) || (M.mind.assigned_role in list("Captain", "Chaplain")))
return 0
return 1
if(isguardian(M))
var/mob/living/simple_animal/hostile/guardian/G = M
if(is_servant_of_ratvar(G.summoner))
return 1 //can't convert it unless the owner is converted
if(issilicon(M) || isclockmob(M) || istype(M, /mob/living/simple_animal/drone/cogscarab))
return 1
return 0
/proc/add_servant_of_ratvar(mob/M, silent = FALSE)
if(is_servant_of_ratvar(M) || !ticker || !ticker.mode)
return 0
if(iscarbon(M))
if(!silent)
M << "<span class='heavy_brass'>Your mind is racing! Your body feels incredibly light! Your world glows a brilliant yellow! All at once it comes to you. Ratvar, the Clockwork \
Justiciar, lies in exile, derelict and forgotten in an unseen realm.</span>"
else if(issilicon(M))
if(!silent)
M << "<span class='heavy_brass'>You are unable to compute this truth. Your vision glows a brilliant yellow, and all at once it comes to you. Ratvar, the Clockwork Justiciar, lies in \
exile, derelict and forgotten in an unseen realm.</span>"
if(!is_eligible_servant(M))
if(!M.stat)
M.visible_message("<span class='warning'>[M] whirs as it resists an outside influence!</span>")
M << "<span class='warning'><b>Corrupt data purged. Resetting cortex chip to factory defaults... complete.</b></span>" //silicons have a custom fail message
return 0
else if(!silent)
M << "<span class='heavy_brass'>Your world glows a brilliant yellow! All at once it comes to you. Ratvar, the Clockwork Justiciar, lies in exile, derelict and forgotten in an unseen realm.</span>"
if(!is_eligible_servant(M))
if(!silent && !M.stat)
M.visible_message("<span class='warning'>[M] seems to resist an unseen force!</span>")
M << "<span class='warning'><b>And yet, you somehow push it all away.</b></span>"
return 0
if(!silent)
M.visible_message("<span class='heavy_brass'>[M]'s eyes glow a blazing yellow!</span>", \
"<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>")
ticker.mode.servants_of_ratvar += M.mind
ticker.mode.update_servant_icons_added(M.mind)
M.mind.special_role = "Servant of Ratvar"
M.languages_spoken |= RATVAR
M.languages_understood |= RATVAR
all_clockwork_mobs += M
M.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(M))
var/mob/living/silicon/S = M
if(isrobot(S))
var/mob/living/silicon/robot/R = S
R.UnlinkSelf()
R.emagged = 1
R << "<span class='warning'><b>You have been desynced from your master AI. In addition, your onboard camera is no longer active and your safeties have been disabled.</b></span>"
S.laws = new/datum/ai_laws/ratvar
S.laws.associate(S)
S.update_icons()
S.show_laws()
if(istype(ticker.mode, /datum/game_mode/clockwork_cult))
var/datum/game_mode/clockwork_cult/C = ticker.mode
C.present_tasks(M) //Memorize the objectives
return 1
/proc/remove_servant_of_ratvar(mob/living/M, silent = FALSE)
if(!is_servant_of_ratvar(M)) //In this way, is_servant_of_ratvar() checks the existence of ticker and minds
return 0
if(!silent)
M.visible_message("<span class='big'>[M] seems to have remembered their true allegiance!</span>", \
"<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>")
ticker.mode.servants_of_ratvar -= M.mind
ticker.mode.update_servant_icons_removed(M.mind)
all_clockwork_mobs -= M
M.mind.memory = "" //Not sure if there's a better way to do this
M.mind.special_role = null
M.languages_spoken &= ~RATVAR
M.languages_understood &= ~RATVAR
M.update_action_buttons_icon() //because a few clockcult things are action buttons and we may be wearing/holding them, we need to update buttons
for(var/datum/action/innate/function_call/F in M.actions) //Removes any bound Ratvarian spears
qdel(F)
if(issilicon(M))
var/mob/living/silicon/S = M
if(isrobot(S))
var/mob/living/silicon/robot/R = S
R.emagged = initial(R.emagged)
R << "<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>"
S.make_laws()
S.update_icons()
S.show_laws()
return 1
///////////////
// GAME MODE //
///////////////
/datum/game_mode
var/list/servants_of_ratvar = list() //The Enlightened servants of Ratvar
var/required_escapees = 0 //How many servants need to escape, if applicable
var/required_silicon_converts = 0 //How many robotic lifeforms need to be converted, if applicable
var/clockwork_objective = "gateway" //The objective that the servants must fulfill
var/clockwork_explanation = "Construct a Gateway to the Celestial Derelict and free Ratvar." //The description of the current objective
/datum/game_mode/clockwork_cult
name = "clockwork cult"
config_tag = "clockwork_cult"
antag_flag = ROLE_SERVANT_OF_RATVAR
required_players = 30
required_enemies = 2
recommended_enemies = 4
enemy_minimum_age = 14
protected_jobs = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain") //Silicons can eventually be converted
restricted_jobs = list("Chaplain", "Captain")
var/servants_to_serve = list()
/datum/game_mode/clockwork_cult/announce()
world << "<b>The game mode is: Clockwork Cult!</b>"
world << "<b><span class='brass'>Ratvar</span>, the Clockwork Justiciar, has formed a covenant of Enlightened aboard [station_name()].</b>"
world << "<b><span class='brass'>Enlightened</span>: Serve your master so that his influence might grow.</b>"
world << "<b><span class='boldannounce'>Crew</span>: Prevent the servants of Ratvar from taking over the station.</b>"
/datum/game_mode/clockwork_cult/pre_setup()
if(config.protect_roles_from_antagonist)
restricted_jobs += protected_jobs
if(config.protect_assistant_from_antagonist)
restricted_jobs += "Assistant"
var/starter_servants = max(1, round(num_players() / 10)) //Guaranteed one cultist - otherwise, about one cultist for every ten players
while(starter_servants)
var/datum/mind/servant = pick(antag_candidates)
servants_to_serve += servant
antag_candidates -= servant
modePlayer += servant
servant.special_role = "Servant of Ratvar"
servant.restricted_roles = restricted_jobs
starter_servants--
return 1
/datum/game_mode/clockwork_cult/post_setup()
forge_clock_objectives()
for(var/S in servants_to_serve)
var/datum/mind/servant = S
log_game("[servant.key] was made an initial servant of Ratvar")
var/mob/living/L = servant.current
greet_servant(L)
equip_servant(L)
add_servant_of_ratvar(L, TRUE)
..()
return 1
/datum/game_mode/clockwork_cult/proc/forge_clock_objectives() //Determine what objective that Ratvar's servants will fulfill
var/list/possible_objectives = list("escape", "gateway")
var/silicons_possible = FALSE
for(var/mob/living/silicon/S in living_mob_list)
silicons_possible = TRUE
if(silicons_possible)
possible_objectives += "silicons"
clockwork_objective = pick(possible_objectives)
clockwork_objective = "gateway" //TEMPORARY, to be removed before merge
switch(clockwork_objective)
if("escape")
required_escapees = max(1, num_players() / 3) //33% of the player count must be cultists
clockwork_explanation = "Ensure that [required_escapees] servant(s) of Ratvar escape from [station_name()]."
if("gateway")
clockwork_explanation = "Construct a Gateway to the Celestial Derelict and free Ratvar."
if("silicons")
clockwork_explanation = "Ensure that all silicon-based lifeforms on [station_name()] are servants of Ratvar by the end of the shift."
return 1
/datum/game_mode/clockwork_cult/proc/greet_servant(mob/M) //Description of their role
if(!M)
return 0
var/greeting_text = "<br><b><span class='large_brass'>You are a servant of Ratvar, the Clockwork Justiciar.</span>\n\
Rusting eternally in the Celestial Derelict, Ratvar has formed a covenant of mortals, with you as one of its members. As one of the Justiciar's servants, you are to work to the best of your \
ability to assist in completion of His agenda. You do not know the specifics of how to do so, but luckily you have a vessel to help you learn.</b>"
M << greeting_text
return 1
/datum/game_mode/proc/equip_servant(mob/living/L) //Grants a clockwork slab to the mob, with one of each component
if(!L || !istype(L))
return 0
var/slot = "At your feet"
if(ishuman(L))
var/mob/living/carbon/human/H = L
if(H.back && istype(H.back, /obj/item/weapon/storage/backpack))
var/obj/item/weapon/storage/backpack/B = H.back
new/obj/item/clockwork/slab/starter(B)
slot = "In your [B.name]"
if(slot == "At your feet")
new/obj/item/clockwork/slab/starter(get_turf(L))
L << "<b>[slot] is a link to the halls of Reebe and your master. You may use it to perform many tasks, but also become oriented with the workings of Ratvar and how to best complete your \
tasks. This clockwork slab will be instrumental in your triumph. Remember: you can speak discreetly with your fellow servants by using the <span class='brass'>Hierophant Network</span> action button, \
and you can find a concise tutorial by using the slab in-hand and selecting Recollection.</b>"
L << "<i>Alternatively, check out the wiki page at </i><b>https://tgstation13.org/wiki/Clockwork_Cult</b><i>, which contains additional information.</i>"
return 1
/datum/game_mode/clockwork_cult/proc/present_tasks(mob/living/L) //Memorizes and displays the clockwork cult's objective
if(!L || !istype(L) || !L.mind)
return 0
var/datum/mind/M = L.mind
M.current << "<b>This is Ratvar's will:</b> [clockwork_explanation]"
M.memory += "<b>Ratvar's will:</b> [clockwork_explanation]<br>"
return 1
/datum/game_mode/clockwork_cult/proc/check_clockwork_victory()
switch(clockwork_objective)
if("escape")
var/surviving_servants = 0
for(var/datum/mind/M in servants_of_ratvar)
if(M.current && M.current.stat != DEAD && (M.current.onCentcom() || M.current.onSyndieBase()))
surviving_servants++
if(surviving_servants <= required_escapees)
return 1
return 0
if("silicons")
for(var/mob/living/silicon/robot/S in mob_list) //Only check robots and AIs
if(!is_servant_of_ratvar(S))
return 0
for(var/mob/living/silicon/ai/A in mob_list)
if(!is_servant_of_ratvar(A))
return 0
return 1
if("gateway")
return ratvar_awakens
return 0 //This shouldn't ever be reached, but just in case it is
/datum/game_mode/clockwork_cult/declare_completion()
..()
return 0 //Doesn't end until the round does
/datum/game_mode/proc/auto_declare_completion_clockwork_cult()
var/text = ""
if(istype(ticker.mode, /datum/game_mode/clockwork_cult)) //Possibly hacky?
var/datum/game_mode/clockwork_cult/C = ticker.mode
if(C.check_clockwork_victory())
text += "<span class='brass'><b>Ratvar's servants have succeeded in fulfilling His goals!</b></span>"
feedback_set_details("round_end_result", "win - servants summoned ratvar")
else
text += "<span class='userdanger'>Ratvar's servants have failed!</span>"
feedback_set_details("round_end_result", "loss - servants did not summon ratvar")
text += "<br><b>The goal of the clockwork cult was:</b> [clockwork_explanation]<br>"
if(servants_of_ratvar.len)
text += "<b>Ratvar's servants were:</b>"
for(var/datum/mind/M in servants_of_ratvar)
text += printplayer(M)
world << text
/datum/game_mode/proc/update_servant_icons_added(datum/mind/M)
var/datum/atom_hud/antag/A = huds[ANTAG_HUD_CLOCKWORK]
A.join_hud(M.current)
set_antag_hud(M.current, "clockwork")
/datum/game_mode/proc/update_servant_icons_removed(datum/mind/M)
var/datum/atom_hud/antag/A = huds[ANTAG_HUD_CLOCKWORK]
A.leave_hud(M.current)
set_antag_hud(M.current, null)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,595 @@
////////////////////////
// CLOCKWORK MACHINES //
////////////////////////
//not-actually-machines
/obj/structure/clockwork/powered
var/obj/machinery/power/apc/target_apc
var/active = FALSE
var/needs_power = TRUE
var/active_icon = null //icon_state while process() is being called
var/inactive_icon = null //icon_state while process() isn't being called
/obj/structure/clockwork/powered/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
var/powered = total_accessable_power()
user << "<span class='[powered ? "brass":"alloy"]'>It has access to [powered == INFINITY ? "INFINITY":"[powered]"]W of power.</span>"
/obj/structure/clockwork/powered/Destroy()
SSfastprocess.processing -= src
SSobj.processing -= src
return ..()
/obj/structure/clockwork/powered/process()
var/powered = total_accessable_power()
return powered == PROCESS_KILL ? 25 : powered //make sure we don't accidentally return the arbitrary PROCESS_KILL define
/obj/structure/clockwork/powered/proc/toggle(fast_process, mob/living/user)
if(user)
if(!is_servant_of_ratvar(user))
return 0
user.visible_message("<span class='notice'>[user] [active ? "dis" : "en"]ables [src].</span>", "<span class='brass'>You [active ? "dis" : "en"]able [src].</span>")
active = !active
if(active)
icon_state = active_icon
if(fast_process)
START_PROCESSING(SSfastprocess, src)
else
START_PROCESSING(SSobj, src)
else
icon_state = inactive_icon
if(fast_process)
STOP_PROCESSING(SSfastprocess, src)
else
STOP_PROCESSING(SSobj, src)
/obj/structure/clockwork/powered/proc/total_accessable_power() //how much power we have and can use
if(!needs_power || ratvar_awakens)
return INFINITY //oh yeah we've got power why'd you ask
var/power = 0
power += accessable_apc_power()
power += accessable_sigil_power()
return power
/obj/structure/clockwork/powered/proc/accessable_apc_power()
var/power = 0
var/area/A = get_area(src)
var/area/targetAPCA
for(var/obj/machinery/power/apc/APC in apcs_list)
var/area/APCA = get_area(APC)
if(APCA == A)
target_apc = APC
if(target_apc)
targetAPCA = get_area(target_apc)
if(targetAPCA != A)
target_apc = null
else if(target_apc.cell)
var/apccharge = target_apc.cell.charge
if(apccharge >= 50)
power += apccharge
return power
/obj/structure/clockwork/powered/proc/accessable_sigil_power()
var/power = 0
for(var/obj/effect/clockwork/sigil/transmission/T in range(1, src))
power += T.power_charge
return power
/obj/structure/clockwork/powered/proc/try_use_power(amount) //try to use an amount of power
if(!needs_power || ratvar_awakens)
return 1
if(amount <= 0)
return 0
var/power = total_accessable_power()
if(!power || power < amount)
return 0
return use_power(amount)
/obj/structure/clockwork/powered/proc/use_power(amount) //we've made sure we had power, so now we use it
var/sigilpower = accessable_sigil_power()
var/list/sigils_in_range = list()
for(var/obj/effect/clockwork/sigil/transmission/T in range(1, src))
sigils_in_range |= T
while(sigilpower && amount >= 50)
for(var/S in sigils_in_range)
var/obj/effect/clockwork/sigil/transmission/T = S
if(amount >= 50 && T.modify_charge(50))
sigilpower -= 50
amount -= 50
var/apcpower = accessable_apc_power()
while(apcpower >= 50 && amount >= 50)
if(target_apc.cell.use(50))
apcpower -= 50
amount -= 50
target_apc.update()
target_apc.update_icon()
else
apcpower = 0
if(amount)
return 0
else
return 1
/obj/structure/clockwork/powered/proc/return_power(amount) //returns a given amount of power to all nearby sigils
if(amount <= 0)
return 0
var/list/sigils_in_range = list()
for(var/obj/effect/clockwork/sigil/transmission/T in range(1, src))
sigils_in_range |= T
if(!sigils_in_range.len)
return 0
while(amount >= 50)
for(var/S in sigils_in_range)
var/obj/effect/clockwork/sigil/transmission/T = S
if(amount >= 50 && T.modify_charge(-50))
amount -= 50
return 1
/obj/structure/clockwork/powered/mending_motor //Mending motor: A prism that consumes replicant alloy to repair nearby mechanical servants at a quick rate.
name = "mending motor"
desc = "A dark onyx prism, held in midair by spiraling tendrils of stone."
clockwork_desc = "A powerful prism that rapidly repairs nearby mechanical servants and clockwork structures."
icon_state = "mending_motor_inactive"
active_icon = "mending_motor"
inactive_icon = "mending_motor_inactive"
construction_value = 20
max_health = 150
health = 150
break_message = "<span class='warning'>The prism collapses with a heavy thud!</span>"
debris = list(/obj/item/clockwork/alloy_shards, /obj/item/clockwork/component/vanguard_cogwheel)
var/stored_alloy = 0 //2500W = 1 alloy = 100 liquified alloy
var/max_alloy = 25000
var/mob_cost = 200
var/structure_cost = 250
var/cyborg_cost = 300
/obj/structure/clockwork/powered/mending_motor/prefilled
stored_alloy = 2500 //starts with 1 replicant alloy/100 liquified alloy
/obj/structure/clockwork/powered/mending_motor/total_accessable_power()
. = ..()
if(. != INFINITY)
. += accessable_alloy_power()
/obj/structure/clockwork/powered/mending_motor/proc/accessable_alloy_power()
return stored_alloy
/obj/structure/clockwork/powered/mending_motor/use_power(amount)
var/alloypower = accessable_alloy_power()
while(alloypower >= 50 && amount >= 50)
stored_alloy -= 50
alloypower -= 50
amount -= 50
return ..()
/obj/structure/clockwork/powered/mending_motor/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
user << "<span class='alloy'>It contains [stored_alloy*0.04]/[max_alloy*0.04] units of liquified alloy, which is equivalent to [stored_alloy]W/[max_alloy]W of power.</span>"
user << "<span class='inathneq_small'>It requires [mob_cost]W to heal clockwork mobs, [structure_cost]W for clockwork structures, and [cyborg_cost]W for cyborgs.</span>"
/obj/structure/clockwork/powered/mending_motor/process()
if(..() < mob_cost)
visible_message("<span class='warning'>[src] emits an airy chuckling sound and falls dark!</span>")
toggle()
return
for(var/atom/movable/M in range(5, src))
if(isclockmob(M) || istype(M, /mob/living/simple_animal/drone/cogscarab))
var/mob/living/simple_animal/hostile/clockwork/W = M
var/fatigued = FALSE
if(istype(M, /mob/living/simple_animal/hostile/clockwork/marauder))
var/mob/living/simple_animal/hostile/clockwork/marauder/E = M
if(E.fatigue)
fatigued = TRUE
if((!fatigued && W.health == W.maxHealth) || W.stat)
continue
if(!try_use_power(mob_cost))
break
W.adjustHealth(-15)
else if(istype(M, /obj/structure/clockwork))
var/obj/structure/clockwork/C = M
if(C.health == C.max_health)
continue
if(!try_use_power(structure_cost))
break
C.health = min(C.health + 15, C.max_health)
else if(issilicon(M))
var/mob/living/silicon/S = M
if(S.health == S.maxHealth || S.stat == DEAD || !is_servant_of_ratvar(S))
continue
if(!try_use_power(cyborg_cost))
break
S.adjustBruteLoss(-15)
S.adjustFireLoss(-15)
return 1
/obj/structure/clockwork/powered/mending_motor/attack_hand(mob/living/user)
if(user.canUseTopic(src, BE_CLOSE))
if(total_accessable_power() < mob_cost)
user << "<span class='warning'>[src] needs more power or replicant alloy to function!</span>"
return 0
toggle(0, user)
/obj/structure/clockwork/powered/mending_motor/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/clockwork/component/replicant_alloy) && is_servant_of_ratvar(user))
if(stored_alloy + 2500 > max_alloy)
user << "<span class='warning'>[src] is too full to accept any more alloy!</span>"
return 0
user.whisper("Genafzhgr vagb jngre.")
user.visible_message("<span class='notice'>[user] liquifies [I] and pours it onto [src].</span>", \
"<span class='notice'>You liquify [src] and pour it onto [src], transferring the alloy into its reserves.</span>")
stored_alloy = stored_alloy + 2500
user.drop_item()
qdel(I)
return 1
else
return ..()
/obj/structure/clockwork/powered/mania_motor //Mania motor: A pair of antenna that, while active, cause braindamage and hallucinations in nearby human mobs.
name = "mania motor"
desc = "A pair of antenna with what appear to be sockets around the base. It reminds you of an antlion."
clockwork_desc = "A transmitter that allows Sevtug to whisper into the minds of nearby non-servants, causing hallucinations and brain damage as long as it remains powered."
icon_state = "mania_motor_inactive"
active_icon = "mania_motor"
inactive_icon = "mania_motor_inactive"
construction_value = 20
max_health = 80
health = 80
break_message = "<span class='warning'>The antenna break off, leaving a pile of shards!</span>"
debris = list(/obj/item/clockwork/alloy_shards, /obj/item/clockwork/component/guvax_capacitor/antennae)
var/mania_cost = 150
var/convert_attempt_cost = 150
var/convert_cost = 300
var/mania_messages = list("\"Tb ahgf.\"", "\"Gnxr n penpx ng penml.\"", "\"Znxr n ovq sbe vafnavgl.\"", "\"Trg xbbxl.\"", "\"Zbir gbjneqf znavn.\"", "\"Orpbzr orjvyqrerq.\"", "\"Jnk jvyq.\"", \
"\"Tb ebhaq gur oraq.\"", "\"Ynaq va yhanpl.\"", "\"Gel qrzragvn.\"", "\"Fgevir gb trg n fperj ybbfr.\"")
var/compel_messages = list("\"Pbzr pybfre.\"", "\"Nccebnpu gur genafzvggre.\"", "\"Gbhpu gur nagraanr.\"", "\"V nyjnlf unir gb qrny jvgu vqvbgf. Zbir gbjneqf gur znavn zbgbe.\"", \
"\"Nqinapr sbejneq naq cynpr lbhe urnq orgjrra gur nagraanr - gun'g'f nyy vg'f tbbq sbe.\"", "\"Vs lbh jrer fznegre, lbh'q or bire urer nyernql.\"", "\"Zbir SBEJNEQ, lbh sbby.\"")
var/convert_messages = list("\"Lbh jba'g qb. Tb gb fyrrc juvyr V gryy gur'fr avgjvgf ubj gb pbaireg lbh.\"", "\"Lbh ner vafhssvpvrag. V zhfg vafgehpg gur'fr vqvbgf va gur neg-bs pbairefvba.\"", \
"\"Bu-bs pbhefr, fbzrbar jr pna'g pbaireg. Gur'fr freinagf ner sbbyf.\"", "\"Ubj uneq vf vg gb hfr n Fvtvy, naljnl? Nyy vg gnxrf vf qenttvat fbzrbar bagb vg.\"", \
"\"Ubj qb gur'l snvy gb hfr n Fvtvy-bs Npprffvba, naljnl?\"", "\"Jul vf vg gun'g nyy freinagf ner guv'f varcg?\"", "\"Vg'f dhvgr yvxryl lbh'yy or fghpx urer sbe n juvyr.\"")
var/close_messages = list("\"Jryy, lbh pna'g ernpu gur zbgbe sebz GUR'ER, lbh zbeba.\"", "\"Vagrerfgvat ybpngvba. V'q cersre vs lbh jrag fbzrjurer lbh pbhyq NPGHNYYL GBHPU GUR NAGRAANR!\"", \
"\"Nznmvat. Lbh fbzrubj znantrq gb jrqtr lbhefrys fbzrjurer lbh pna'g npghnyyl ernpu gur zbgbe sebz.\"", "\"Fhpu n fubj-bs vqvbpl vf hacnenyyryrq. Creuncf V fubhyq chg lbh ba qvfcynl?\"", \
"\"Qvq lbh qb guv'f ba checbfr? V pna'g vzntvar lbh qbvat fb nppvqragnyyl. Bu, jnvg, V pna.\"", "\"Ubj vf vg gun'g fhpu fzneg perngherf pna fgvyy qb fbzrguv'at NF FGHCVQ NF GUV'F!\"")
/obj/structure/clockwork/powered/mania_motor/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
user << "<span class='sevtug_small'>It requires [mania_cost]W to run, and [convert_attempt_cost + convert_cost]W to convert humans adjecent to it.</span>"
/obj/structure/clockwork/powered/mania_motor/process()
var/turf/T = get_turf(src)
if(!..())
visible_message("<span class='warning'>[src] hums loudly, then the sockets at its base fall dark!</span>")
playsound(T, 'sound/effects/screech.ogg', 40, 1)
toggle(0)
return
if(try_use_power(mania_cost))
var/hum = get_sfx('sound/effects/screech.ogg') //like playsound, same sound for everyone affected
for(var/mob/living/carbon/human/H in view(1, src))
if(H.Adjacent(src) && try_use_power(convert_attempt_cost))
if(is_eligible_servant(H) && try_use_power(convert_cost))
H << "<span class='sevtug'>\"Lbh ner zvar-naq-uvf, abj.\"</span>"
H.playsound_local(T, hum, 80, 1)
add_servant_of_ratvar(H)
else if(!H.stat)
if(H.getBrainLoss() >= H.maxHealth)
H.Paralyse(5)
H << "<span class='sevtug'>[pick(convert_messages)]</span>"
else
H.adjustBrainLoss(100)
H.visible_message("<span class='warning'>[H] reaches out and touches [src].</span>", "<span class='sevtug'>You touch [src] involuntarily.</span>")
else
visible_message("<span class='warning'>[src]'s antennae fizzle quietly.</span>")
playsound(src, 'sound/effects/light_flicker.ogg', 50, 1)
for(var/mob/living/carbon/human/H in range(10, src))
if(!is_servant_of_ratvar(H) && !H.null_rod_check() && H.stat == CONSCIOUS)
var/distance = get_dist(T, get_turf(H))
var/falloff_distance = min((110) - distance * 10, 80)
var/sound_distance = falloff_distance * 0.5
var/targetbrainloss = H.getBrainLoss()
var/targethallu = H.hallucination
var/targetdruggy = H.druggy
if(distance >= 4 && prob(falloff_distance))
H << "<span class='sevtug_small'>[pick(mania_messages)]</span>"
H.playsound_local(T, hum, sound_distance, 1)
switch(distance)
if(2 to 3)
if(prob(falloff_distance))
if(prob(falloff_distance))
H << "<span class='sevtug_small'>[pick(mania_messages)]</span>"
else
H << "<span class='sevtug'>[pick(compel_messages)]</span>"
if(targetbrainloss <= 50)
H.adjustBrainLoss(50 - targetbrainloss) //got too close had brain eaten
if(targetdruggy <= 150)
H.adjust_drugginess(11)
if(targethallu <= 150)
H.hallucination += 11
if(4 to 5)
if(targetbrainloss <= 50)
H.adjustBrainLoss(3)
if(targetdruggy <= 120)
H.adjust_drugginess(9)
if(targethallu <= 120)
H.hallucination += 9
if(6 to 7)
if(targetbrainloss <= 30)
H.adjustBrainLoss(2)
if(prob(falloff_distance) && targetdruggy <= 90)
H.adjust_drugginess(7)
else if(targethallu <= 90)
H.hallucination += 7
if(8 to 9)
if(H.getBrainLoss() <= 10)
H.adjustBrainLoss(1)
if(prob(falloff_distance) && targetdruggy <= 60)
H.adjust_drugginess(5)
else if(targethallu <= 60)
H.hallucination += 5
if(10 to INFINITY)
if(prob(falloff_distance) && targetdruggy <= 30)
H.adjust_drugginess(3)
else if(targethallu <= 30)
H.hallucination += 3
else //if it's a distance of 1 and they can't see it/aren't adjacent or they're on top of it(how'd they get on top of it and still trigger this???)
if(targetbrainloss <= 99)
if(prob(falloff_distance))
if(prob(falloff_distance))
H << "<span class='sevtug'>[pick(compel_messages)]</span>"
else if(prob(falloff_distance))
H << "<span class='sevtug'>[pick(close_messages)]</span>"
else
H << "<span class='sevtug_small'>[pick(mania_messages)]</span>"
H.adjustBrainLoss(99 - targetbrainloss)
if(targetdruggy <= 200)
H.adjust_drugginess(15)
if(targethallu <= 200)
H.hallucination += 15
if(is_servant_of_ratvar(H) && (H.getBrainLoss() || H.hallucination || H.druggy)) //not an else so that newly converted servants are healed of the damage it inflicts
H.adjustBrainLoss(-H.getBrainLoss()) //heals servants of braindamage, hallucination, and druggy
H.hallucination = 0
H.adjust_drugginess(-H.druggy)
else
visible_message("<span class='warning'>[src] hums loudly, then the sockets at its base fall dark!</span>")
playsound(src, 'sound/effects/screech.ogg', 40, 1)
toggle(0)
/obj/structure/clockwork/powered/mania_motor/attack_hand(mob/living/user)
if(user.canUseTopic(src, BE_CLOSE))
if(!total_accessable_power() >= mania_cost)
user << "<span class='warning'>[src] needs more power to function!</span>"
return 0
toggle(0, user)
/obj/structure/clockwork/powered/interdiction_lens //Interdiction lens: A powerful artifact that constantly disrupts electronics but, if it fails to find something to disrupt, turns off.
name = "interdiction lens"
desc = "An ominous, double-pronged brass totem. There's a strange gemstone clasped between the pincers."
clockwork_desc = "A powerful totem that constantly disrupts nearby electronics and funnels power into nearby Sigils of Transmission."
icon_state = "interdiction_lens"
construction_value = 25
active_icon = "interdiction_lens_active"
inactive_icon = "interdiction_lens"
break_message = "<span class='warning'>The lens flares a blinding violet before shattering!</span>"
break_sound = 'sound/effects/Glassbr3.ogg'
var/recharging = 0 //world.time when the lens was last used
var/recharge_time = 1200 //if it drains no power and affects no objects, it turns off for two minutes
var/disabled = FALSE //if it's actually usable
var/interdiction_range = 14 //how large an area it drains and disables in
var/disrupt_cost = 100 //how much power to use when disabling an object
/obj/structure/clockwork/powered/interdiction_lens/examine(mob/user)
..()
user << "<span class='[recharging > world.time ? "nezbere_small":"brass"]'>Its gemstone [recharging > world.time ? "has been breached by writhing tendrils of blackness that cover the totem" \
: "vibrates in place and thrums with power"].</span>"
if(is_servant_of_ratvar(user) || isobserver(user))
user << "<span class='nezbere_small'>It requires [disrupt_cost]W of power for each nearby disruptable electronic.</span>"
user << "<span class='nezbere_small'>If it fails to both drain any power and disrupt any electronics, it will disable itself for [round(recharge_time/600, 1)] minutes.</span>"
/obj/structure/clockwork/powered/interdiction_lens/toggle(fast_process, mob/living/user)
..()
if(active)
SetLuminosity(4,2)
else
SetLuminosity(0)
/obj/structure/clockwork/powered/interdiction_lens/attack_hand(mob/living/user)
if(user.canUseTopic(src, BE_CLOSE))
if(disabled)
user << "<span class='warning'>As you place your hand on the gemstone, cold tendrils of black matter crawl up your arm. You quickly pull back.</span>"
return 0
if(!total_accessable_power() >= disrupt_cost)
user << "<span class='warning'>[src] needs more power to function!</span>"
return 0
toggle(0, user)
/obj/structure/clockwork/powered/interdiction_lens/process()
if(recharging > world.time)
return
if(disabled)
visible_message("<span class='warning'>The writhing tendrils return to the gemstone, which begins to glow with power!</span>")
flick("interdiction_lens_recharged", src)
disabled = FALSE
toggle(0)
else
var/successfulprocess = FALSE
var/power_drained = 0
var/list/atoms_to_test = list()
for(var/A in spiral_range_turfs(interdiction_range, src))
var/turf/T = A
for(var/M in T)
atoms_to_test |= M
CHECK_TICK
for(var/M in atoms_to_test)
if(istype(M, /obj/machinery/power/apc))
var/obj/machinery/power/apc/A = M
if(A.cell && A.cell.charge)
successfulprocess = TRUE
playsound(A, "sparks", 50, 1)
flick("apc-spark", A)
power_drained += min(A.cell.charge, 100)
A.cell.charge = max(0, A.cell.charge - 100)
if(!A.cell.charge && !A.shorted)
A.shorted = 1
A.visible_message("<span class='warning'>The [A.name]'s screen blurs with static.</span>")
A.update()
A.update_icon()
else if(istype(M, /obj/machinery/power/smes))
var/obj/machinery/power/smes/S = M
if(S.charge)
successfulprocess = TRUE
power_drained += min(S.charge, 500)
S.charge = max(0, S.charge - 50000) //SMES units contain too much power and could run an interdiction lens basically forever, or provide power forever
if(!S.charge && !S.panel_open)
S.panel_open = TRUE
S.icon_state = "[initial(S.icon_state)]-o"
var/datum/effect_system/spark_spread/spks = new(get_turf(S))
spks.set_up(10, 0, get_turf(S))
spks.start()
S.visible_message("<span class='warning'>[S]'s panel flies open with a flurry of sparks.</span>")
S.update_icon()
else if(isrobot(M))
var/mob/living/silicon/robot/R = M
if(!is_servant_of_ratvar(R) && R.cell && R.cell.charge)
successfulprocess = TRUE
power_drained += min(R.cell.charge, 200)
R.cell.charge = max(0, R.cell.charge - 200)
R << "<span class='warning'>ERROR: Power loss detected!</span>"
var/datum/effect_system/spark_spread/spks = new(get_turf(R))
spks.set_up(3, 0, get_turf(R))
spks.start()
CHECK_TICK
if(!return_power(power_drained) || power_drained < 50) //failed to return power drained or too little power to return
successfulprocess = FALSE
if(try_use_power(disrupt_cost) && total_accessable_power() >= disrupt_cost) //if we can disable at least one object
playsound(src, 'sound/items/PSHOOM.ogg', 50, 1, interdiction_range-7, 1)
for(var/M in atoms_to_test)
if(istype(M, /obj/machinery/light)) //cosmetic light flickering
var/obj/machinery/light/L = M
if(L.on)
playsound(L, 'sound/effects/light_flicker.ogg', 50, 1)
L.flicker(3)
else if(istype(M, /obj/machinery/camera))
var/obj/machinery/camera/C = M
if(C.isEmpProof() || !C.status)
continue
successfulprocess = TRUE
if(C.emped)
continue
if(!try_use_power(disrupt_cost))
break
C.emp_act(1)
else if(istype(M, /obj/item/device/radio))
var/obj/item/device/radio/O = M
successfulprocess = TRUE
if(O.emped || !O.on)
continue
if(!try_use_power(disrupt_cost))
break
O.emp_act(1)
else if(isliving(M) || istype(M, /obj/structure/closet) || istype(M, /obj/item/weapon/storage)) //other things may have radios in them but we don't care
var/atom/movable/A = M
for(var/obj/item/device/radio/O in A.GetAllContents())
successfulprocess = TRUE
if(O.emped || !O.on)
continue
if(!try_use_power(disrupt_cost))
break
O.emp_act(1)
CHECK_TICK
if(!successfulprocess)
visible_message("<span class='warning'>The gemstone suddenly turns horribly dark, writhing tendrils covering it!</span>")
recharging = world.time + recharge_time
flick("interdiction_lens_discharged", src)
icon_state = "interdiction_lens_inactive"
SetLuminosity(2,1)
disabled = TRUE
/obj/structure/clockwork/powered/clockwork_obelisk
name = "clockwork obelisk"
desc = "A large brass obelisk hanging in midair."
clockwork_desc = "A powerful obelisk that can send a message to all servants or open a gateway to a target servant or clockwork obelisk."
icon_state = "obelisk_inactive"
active_icon = "obelisk"
inactive_icon = "obelisk_inactive"
construction_value = 20
max_health = 200
health = 200
break_message = "<span class='warning'>The obelisk falls to the ground, undamaged!</span>"
debris = list(/obj/item/clockwork/component/hierophant_ansible/obelisk)
var/hierophant_cost = 50 //how much it costs to broadcast with large text
var/gateway_cost = 2000 //how much it costs to open a gateway
var/gateway_active = FALSE
/obj/structure/clockwork/powered/clockwork_obelisk/New()
..()
toggle(1)
/obj/structure/clockwork/powered/clockwork_obelisk/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
user << "<span class='nzcrentr_small'>It requires [hierophant_cost]W to broadcast over the Hierophant Network, and [gateway_cost]W to open a Spatial Gateway.</span>"
/obj/structure/clockwork/powered/clockwork_obelisk/process()
if(locate(/obj/effect/clockwork/spatial_gateway) in loc)
icon_state = active_icon
density = 0
gateway_active = TRUE
else
icon_state = inactive_icon
density = 1
gateway_active = FALSE
/obj/structure/clockwork/powered/clockwork_obelisk/attack_hand(mob/living/user)
if(!is_servant_of_ratvar(user) || !total_accessable_power() >= hierophant_cost)
user << "<span class='warning'>You place your hand on the obelisk, but it doesn't react.</span>"
return
var/choice = alert(user,"You place your hand on the obelisk...",,"Hierophant Broadcast","Spatial Gateway","Cancel")
switch(choice)
if("Hierophant Broadcast")
if(gateway_active)
user << "<span class='warning'>The obelisk is sustaining a gateway and cannot broadcast!</span>"
return
var/input = stripped_input(usr, "Please choose a message to send over the Hierophant Network.", "Hierophant Broadcast", "")
if(!input || !user.canUseTopic(src, BE_CLOSE))
return
if(gateway_active)
user << "<span class='warning'>The obelisk is sustaining a gateway and cannot broadcast!</span>"
return
if(!try_use_power(hierophant_cost))
user << "<span class='warning'>The obelisk lacks the power to broadcast!</span>"
return
clockwork_say(user, "Uvrebcunag Oebnqpnfg, npgvingr!")
send_hierophant_message(user, input, "big_brass", "large_brass")
if("Spatial Gateway")
if(gateway_active)
user << "<span class='warning'>The obelisk is already sustaining a gateway!</span>"
return
if(!try_use_power(gateway_cost))
user << "<span class='warning'>The obelisk lacks the power to open a gateway!</span>"
return
if(procure_gateway(user, 100, 5, 1))
clockwork_say(user, "Fcnpvny Tngrjnl, npgvingr!")
else
return_power(gateway_cost)
if("Cancel")
return
@@ -0,0 +1,579 @@
/mob/living/simple_animal/hostile/clockwork
faction = list("ratvar")
icon = 'icons/mob/clockwork_mobs.dmi'
unique_name = 1
minbodytemp = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) //Robotic
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
languages_spoken = RATVAR
languages_understood = HUMAN|RATVAR
healable = FALSE
del_on_death = TRUE
bubble_icon = "clock"
death_sound = 'sound/magic/clockwork/anima_fragment_death.ogg'
var/playstyle_string = "<span class='heavy_brass'>You are a bug, yell at whoever spawned you!</span>"
var/obj/item/clockwork/slab/internalslab //an internal slab for running scripture
/mob/living/simple_animal/hostile/clockwork/New()
..()
internalslab = new/obj/item/clockwork/slab/internal(src)
/mob/living/simple_animal/hostile/clockwork/Destroy()
qdel(internalslab)
internalslab = null
return ..()
/mob/living/simple_animal/hostile/clockwork/get_spans()
return ..() | SPAN_ROBOT
/mob/living/simple_animal/hostile/clockwork/Login()
..()
src << playstyle_string
/mob/living/simple_animal/hostile/clockwork/fragment //Anima fragment: Low health and high melee damage, but slows down when struck. Created by inserting a soul vessel into an empty fragment.
name = "anima fragment"
desc = "An ominous humanoid shell with a spinning cogwheel as its head, lifted by a jet of blazing red flame."
icon_state = "anime_fragment"
health = 90
maxHealth = 90
speed = -1
melee_damage_lower = 20
melee_damage_upper = 20
attacktext = "crushes"
attack_sound = 'sound/magic/clockwork/anima_fragment_attack.ogg'
loot = list(/obj/item/clockwork/component/replicant_alloy/smashed_anima_fragment)
weather_immunities = list("lava")
flying = 1
playstyle_string = "<span class='heavy_brass'>You are an anima fragment</span><b>, a clockwork creation of Ratvar. As a fragment, you have low health, do decent damage, and move at \
extreme speed in addition to being immune to extreme temperatures and pressures. Taking damage will temporarily slow you down, however. \n Your goal is to serve the Justiciar and his servants \
in any way you can. You yourself are one of these servants, and will be able to utilize anything they can, assuming it doesn't require opposable thumbs.</b>"
var/movement_delay_time //how long the fragment is slowed after being hit
/mob/living/simple_animal/hostile/clockwork/fragment/New()
..()
SetLuminosity(2,1)
if(prob(1))
name = "anime fragment"
real_name = name
desc = "I-it's not like I want to show you the light of the Justiciar or anything, B-BAKA!"
/mob/living/simple_animal/hostile/clockwork/fragment/Stat()
..()
if(statpanel("Status") && movement_delay_time > world.time && !ratvar_awakens)
stat(null, "Movement delay(seconds): [max(round((movement_delay_time - world.time)*0.1, 0.1), 0)]")
/mob/living/simple_animal/hostile/clockwork/fragment/death(gibbed)
visible_message("<span class='warning'>[src]'s flame jets cut out as it falls to the floor with a tremendous crash.</span>", \
"<span class='userdanger'>Your gears seize up. Your flame jets flicker out. Your soul vessel belches smoke as you helplessly crash down.</span>")
..(TRUE)
return 1
/mob/living/simple_animal/hostile/clockwork/fragment/Process_Spacemove(movement_dir = 0)
return 1
/mob/living/simple_animal/hostile/clockwork/fragment/movement_delay()
. = ..()
if(movement_delay_time > world.time && !ratvar_awakens)
. += min((movement_delay_time - world.time) * 0.1, 10) //the more delay we have, the slower we go
/mob/living/simple_animal/hostile/clockwork/fragment/adjustHealth(amount)
. = ..()
if(!ratvar_awakens && amount > 0) //if ratvar is up we ignore movement delay
if(movement_delay_time > world.time)
movement_delay_time = movement_delay_time + amount*3
else
movement_delay_time = world.time + amount*3
/mob/living/simple_animal/hostile/clockwork/fragment/updatehealth()
..()
if(health == maxHealth)
speed = initial(speed)
else
speed = 0 //slow down if damaged at all
/mob/living/simple_animal/hostile/clockwork/marauder //Clockwork marauder: Slow but with high damage, resides inside of a servant. Created via the Memory Allocation scripture.
name = "clockwork marauder"
desc = "A stalwart apparition of a soldier, blazing with crimson flames. It's armed with a gladius and shield."
icon_state = "clockwork_marauder"
health = 300 //Health is very high, and under most cases it will take enough fatigue to be forced to recall first
maxHealth = 300
speed = 1
melee_damage_lower = 10
melee_damage_upper = 10
attacktext = "slashes"
attack_sound = 'sound/weapons/bladeslice.ogg'
environment_smash = 1
weather_immunities = list("lava")
flying = 1
loot = list(/obj/item/clockwork/component/replicant_alloy/fallen_armor)
var/true_name = "Meme Master 69" //Required to call forth the marauder
var/list/possible_true_names = list("Xaven", "Melange", "Ravan", "Kel", "Rama", "Geke", "Peris", "Vestra", "Skiwa") //All fairly short and easy to pronounce
var/fatigue = 0 //Essentially what determines the marauder's power
var/fatigue_recall_threshold = 100 //In variable form due to changed effects when Ratvar awakens
var/mob/living/host //The mob that the marauder is living inside of
var/recovering = FALSE //If the marauder is recovering from a large amount of fatigue
var/blockchance = 15 //chance to block melee attacks entirely
var/counterchance = 30 //chance to counterattack after blocking
var/combattimer = 50 //after 5 seconds of not being hit ot attacking we count as 'out of combat' and lose block/counter chance
playstyle_string = "<span class='sevtug'>You are a clockwork marauder</span><b>, a living extension of Sevtug's will. As a marauder, you are somewhat slow, but may block melee attacks \
and have a chance to also counter blocked melee attacks for extra damage, in addition to being immune to extreme temperatures and pressures. \
Your primary goal is to serve the creature that you are now a part of. You can use <span class='sevtug_small'><i>:b</i></span> to communicate silently with your master, \
but can only exit if your master calls your true name or if they are exceptionally damaged. \
\n\n\
Taking damage and remaining outside of your master will cause <i>fatigue</i>, which hinders your movement speed and attacks, in addition to forcing you back into your master if it grows \
too high. As a final note, you should probably avoid harming any fellow servants of Ratvar.</span>"
/mob/living/simple_animal/hostile/clockwork/marauder/New()
..()
combattimer = 0
true_name = pick(possible_true_names)
SetLuminosity(2,1)
/mob/living/simple_animal/hostile/clockwork/marauder/Life()
..()
if(combattimer < world.time)
blockchance = max(blockchance - 5, initial(blockchance))
counterchance = max(counterchance - 10, initial(counterchance))
if(is_in_host())
if(!ratvar_awakens && host.stat == DEAD)
death()
return
adjust_fatigue(-1.5)
if(ratvar_awakens)
adjustHealth(-5.5)
else
adjustHealth(-0.5)
if(!fatigue && recovering)
src << "<span class='userdanger'>Your strength has returned. You can once again come forward!</span>"
host << "<span class='userdanger'>Your marauder is now strong enough to come forward again!</span>"
recovering = FALSE
else
if(ratvar_awakens)
adjustHealth(-2)
else if(host) //If Ratvar is alive, marauders both don't take fatigue loss and move at fast speed
if(host.stat == DEAD)
death()
return
if(z && host.z && z == host.z)
switch(get_dist(get_turf(src), get_turf(host)))
if(2 to 4)
adjust_fatigue(1)
if(5)
adjust_fatigue(3)
if(6 to INFINITY)
adjust_fatigue(10)
src << "<span class='userdanger'>You're too far from your host and rapidly taking fatigue damage!</span>"
else //right next to or on top of host
adjust_fatigue(-1)
else //well then, you're not even in the same zlevel
adjust_fatigue(10)
src << "<span class='userdanger'>You're too far from your host and rapidly taking fatigue damage!</span>"
/mob/living/simple_animal/hostile/clockwork/marauder/Process_Spacemove(movement_dir = 0)
return 1
//DAMAGE and FATIGUE
/mob/living/simple_animal/hostile/clockwork/marauder/proc/update_fatigue()
if(!ratvar_awakens && host && host.stat == DEAD)
death()
return
if(ratvar_awakens)
speed = 0
melee_damage_lower = 20
melee_damage_upper = 20
attacktext = "devastates"
else
switch((fatigue/fatigue_recall_threshold) * 100)
if(0 to 10) //Bonuses to speed and damage at normal fatigue levels
speed = 0
melee_damage_lower = 13
melee_damage_upper = 13
attacktext = "viciously slashes"
if(10 to 25)
speed = initial(speed)
melee_damage_lower = initial(melee_damage_lower)
melee_damage_upper = initial(melee_damage_upper)
attacktext = initial(attacktext)
if(25 to 50) //Damage decrease, but not speed
speed = initial(speed)
melee_damage_lower = 7
melee_damage_upper = 7
attacktext = "lightly slashes"
if(50 to 75) //Speed decrease
speed = 2
melee_damage_lower = 7
melee_damage_upper = 7
attacktext = "lightly slashes"
if(75 to 99) //Massive speed decrease and weak melee attacks
speed = 3
melee_damage_lower = 4
melee_damage_upper = 4
attacktext = "weakly slashes"
if(99 to 100) //we are at maximum fatigue, we're either useless or recalling
if(host)
src << "<span class='userdanger'>The fatigue becomes too much!</span>"
src << "<span class='userdanger'>You retreat to [host] - you will have to wait before being deployed again.</span>"
host << "<span class='userdanger'>[true_name] is too fatigued to fight - you will need to wait until they are strong enough.</span>"
recovering = TRUE
return_to_host()
else
speed = 4
melee_damage_lower = 1
melee_damage_upper = 1
attacktext = "taps"
/mob/living/simple_animal/hostile/clockwork/marauder/death(gibbed)
emerge_from_host(0, 1)
visible_message("<span class='warning'>[src]'s equipment clatters lifelessly to the ground as the red flames within dissipate.</span>", \
"<span class='userdanger'>Your equipment falls away. You feel a moment of confusion before your fragile form is annihilated.</span>")
..()
return 1
/mob/living/simple_animal/hostile/clockwork/marauder/Stat()
..()
if(statpanel("Status"))
stat(null, "Fatigue: [fatigue]/[fatigue_recall_threshold]")
stat(null, "Current True Name: [true_name]")
stat(null, "Host: [host ? host : "NONE"]")
if(host)
var/resulthealth
resulthealth = round((abs(config.health_threshold_dead - host.health) / abs(config.health_threshold_dead - host.maxHealth)) * 100)
stat(null, "Host Health: [resulthealth]%")
if(ratvar_awakens)
stat(null, "You are [recovering ? "un" : ""]able to deploy!")
else
if(resulthealth > 60)
stat(null, "You are [recovering ? "unable to deploy" : "can deploy on hearing your True Name"]!")
else
stat(null, "You are [recovering ? "unable to deploy" : "can deploy to protect your host"]!")
if(ratvar_awakens)
stat(null, "Block Chance: 80%")
stat(null, "Counter Chance: 80%")
else
stat(null, "Block Chance: [blockchance]%")
stat(null, "Counter Chance: [counterchance]%")
stat(null, "You do [melee_damage_upper] damage on melee attacks.")
/mob/living/simple_animal/hostile/clockwork/marauder/adjustHealth(amount) //Fatigue damage
var/fatiguedamage = adjust_fatigue(amount)
if(amount > 0)
combattimer = world.time + initial(combattimer)
for(var/mob/living/L in view(2, src))
if(istype(L.l_hand, /obj/item/weapon/nullrod) || istype(L.r_hand, /obj/item/weapon/nullrod)) //hand-held holy weapons increase the damage it takes
src << "<span class='userdanger'>The presence of a brandished holy artifact weakens your armor!</span>"
amount *= 4 //if a wielded null rod is nearby, it takes four times the health damage
break
return ..() + fatiguedamage
/mob/living/simple_animal/hostile/clockwork/marauder/proc/adjust_fatigue(amount) //Adds or removes the given amount of fatigue
if(status_flags & GODMODE)
return 0
fatigue = Clamp(fatigue + amount, 0, fatigue_recall_threshold)
update_fatigue()
return amount
//ATTACKING, BLOCKING, and COUNTERING
/mob/living/simple_animal/hostile/clockwork/marauder/AttackingTarget()
if(is_in_host())
return 0
combattimer = world.time + initial(combattimer)
..()
/mob/living/simple_animal/hostile/clockwork/marauder/bullet_act(obj/item/projectile/Proj)
if(ratvar_awakens && blockOrCounter(null, Proj))
return
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/hitby(atom/movable/AM, skipcatch, hitpush, blocked)
if(ratvar_awakens && blockOrCounter(null, AM))
return
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/attack_animal(mob/living/simple_animal/M)
if(istype(M, /mob/living/simple_animal/hostile/clockwork/marauder) || !blockOrCounter(M, M))
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/attack_paw(mob/living/carbon/monkey/M)
if(!blockOrCounter(M, M))
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/attack_alien(mob/living/carbon/alien/humanoid/M)
if(!blockOrCounter(M, M))
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/attack_slime(mob/living/simple_animal/slime/M)
if(!blockOrCounter(M, M))
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/attack_hand(mob/living/carbon/human/M)
if(!blockOrCounter(M, M))
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/weapon/nullrod) || !blockOrCounter(user, I))
return ..()
/mob/living/simple_animal/hostile/clockwork/marauder/proc/blockOrCounter(mob/target, atom/textobject)
if(ratvar_awakens) //if ratvar has woken, we block nearly everything at a very high chance
blockchance = 80
counterchance = 80
if(prob(blockchance))
. = TRUE
if(target)
target.do_attack_animation(src)
target.changeNext_move(CLICK_CD_MELEE)
blockchance = initial(blockchance)
playsound(src, 'sound/magic/clockwork/fellowship_armory.ogg', 10, 1, 0, 1) //clang
visible_message("<span class='boldannounce'>[src] blocks [target && istype(textobject, /obj/item) ? "[target]'s [textobject.name]":"\the [textobject]"]!</span>", \
"<span class='userdanger'>You block [target && istype(textobject, /obj/item) ? "[target]'s [textobject.name]":"\the [textobject]"]!</span>")
if(target && prob(counterchance))
counterchance = initial(counterchance)
var/previousattacktext = attacktext
attacktext = "counters"
target.attack_animal(src)
attacktext = previousattacktext
else
counterchance = min(counterchance + initial(counterchance), 100)
else
blockchance = min(blockchance + initial(blockchance), 100)
//COMMUNICATION and EMERGENCE
/mob/living/simple_animal/hostile/clockwork/marauder/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans)
..()
if(findtext(message, true_name) && is_in_host()) //Called or revealed by hearing their true name
if(speaker == host)
emerge_from_host(1)
else
src << "<span class='boldannounce'>You hear your true name and partially emerge before you can stop yourself!</span>"
host.visible_message("<span class='warning'>[host]'s skin flashes crimson!</span>", "<span class='boldannounce'>Your marauder instinctively reacts to its true name!</span>")
/mob/living/simple_animal/hostile/clockwork/marauder/say(message, message_mode)
if(host && (is_in_host() || message_mode == MODE_BINARY))
marauder_comms(message)
return 1
..()
/mob/living/simple_animal/hostile/clockwork/marauder/proc/marauder_comms(message)
if(host)
message = "<span class='sevtug'>Marauder [true_name]:</span> <span class='sevtug_small'>\"[message]\"</span>" //Processed output
src << message
host << message
for(var/M in mob_list)
if(isobserver(M))
var/link = FOLLOW_LINK(M, src)
M << "[link] [message]"
return 1
return 0
/mob/living/proc/talk_with_marauder() //hosts communicate via a verb, marauders just use :b
set name = "Linked Minds"
set desc = "Silently communicates with your marauder."
set category = "Clockwork"
var/mob/living/simple_animal/hostile/clockwork/marauder/marauder
if(!marauder)
for(var/mob/living/simple_animal/hostile/clockwork/marauder/C in living_mob_list)
if(C.host == src)
marauder = C
if(!marauder) //Double-check afterwards
src << "<span class='warning'>You aren't hosting any marauders!</span>"
verbs -= /mob/living/proc/talk_with_marauder
return 0
var/message = stripped_input(src, "Enter a message to tell your marauder.", "Telepathy")// as null|anything
if(!src || !message)
return 0
if(!marauder)
usr << "<span class='warning'>Your marauder seems to have vanished!</span>"
return 0
message = "<span class='heavy_brass'>Servant [findtextEx(name, real_name) ? "[name]" : "[real_name] (as [name])"]:</span> <span class='brass'>\"[message]\"</span>" //Processed output
src << message
marauder << message
for(var/M in mob_list)
if(isobserver(M))
var/link = FOLLOW_LINK(M, src)
M << "[link] [message]"
return 1
/mob/living/simple_animal/hostile/clockwork/marauder/verb/change_true_name()
set name = "Change True Name (One-Use)"
set desc = "Changes your true name, used to be called forth."
set category = "Marauder"
verbs -= /mob/living/simple_animal/hostile/clockwork/marauder/verb/change_true_name
var/new_name = stripped_input(usr, "Enter a new true name (10-character limit).", "Change True Name","", 11)
if(!usr)
return 0
if(!new_name)
usr << "<span class='notice'>You decide against changing your true name for now.</span>"
verbs += /mob/living/simple_animal/hostile/clockwork/marauder/verb/change_true_name //If they decide against it, let them have another opportunity
return 0
true_name = new_name
usr << "<span class='heavy_brass'>You have changed your true name to </span><span class='sevtug'>\"[new_name]\"</span><span class='heavy_brass'>!</span>"
if(host)
host << "<span class='heavy_brass'>Your clockwork marauder has changed their true name to </span><span class='sevtug'>\"[new_name]\"</span><span class='heavy_brass'>!</span>"
return 1
/mob/living/simple_animal/hostile/clockwork/marauder/verb/return_to_host()
set name = "Return to Host"
set desc = "Recalls yourself to your host, assuming you aren't already there."
set category = "Marauder"
if(is_in_host())
return 0
if(!host)
src << "<span class='warning'>You don't have a host!</span>"
verbs -= /mob/living/simple_animal/hostile/clockwork/marauder/verb/return_to_host
return 0
host.visible_message("<span class='warning'>[host]'s skin flashes crimson!</span>", "<span class='heavy_brass'>You feel [true_name]'s consciousness settle in your mind.</span>")
visible_message("<span class='warning'>[src] suddenly disappears!</span>", "<span class='heavy_brass'>You return to [host].</span>")
forceMove(host)
return 1
/mob/living/simple_animal/hostile/clockwork/marauder/verb/try_emerge()
set name = "Attempt to Emerge from Host"
set desc = "Attempts to emerge from your host, likely to only work if your host is very heavily damaged."
set category = "Marauder"
if(!host)
src << "<span class='warning'>You don't have a host!</span>"
verbs -= /mob/living/simple_animal/hostile/clockwork/marauder/verb/try_emerge
return 0
var/resulthealth
resulthealth = round((abs(config.health_threshold_dead - host.health) / abs(config.health_threshold_dead - host.maxHealth)) * 100)
if(!ratvar_awakens && host.stat != DEAD && resulthealth > 60) //if above 20 health, fails
src << "<span class='warning'>Your host must be at 60% or less health to emerge like this!</span>"
return
return emerge_from_host(0)
/mob/living/simple_animal/hostile/clockwork/marauder/proc/emerge_from_host(hostchosen, force) //Notice that this is a proc rather than a verb - marauders can NOT exit at will, but they CAN return
if(!is_in_host())
return 0
if(!force && recovering)
if(hostchosen)
host << "<span class='heavy_brass'>[true_name] is too weak to come forth!</span>"
else
host << "<span class='heavy_brass'>[true_name] tries to emerge to protect you, but it's too weak!</span>"
src << "<span class='userdanger'>You try to come forth, but you're too weak!</span>"
return 0
if(hostchosen) //marauder approved
host << "<span class='heavy_brass'>Your words echo with power as [true_name] emerges from your body!</span>"
else
host << "<span class='heavy_brass'>[true_name] emerges from your body to protect you!</span>"
forceMove(get_turf(host))
visible_message("<span class='warning'>[host]'s skin glows red as [name] emerges from their body!</span>", "<span class='brass'>You exit the safety of [host]'s body!</span>")
return 1
/mob/living/simple_animal/hostile/clockwork/marauder/proc/is_in_host() //Checks if the marauder is inside of their host
return host && loc == host
/mob/living/simple_animal/hostile/clockwork/reclaimer
name = "clockwork reclaimer"
desc = "A tiny clockwork arachnid with a single cogwheel spinning quickly in its head. Its legs blur, too fast to be seen clearly."
icon_state = "clockwork_reclaimer"
health = 50
maxHealth = 50
melee_damage_lower = 10
melee_damage_upper = 10
attacktext = "slams into"
attack_sound = 'sound/magic/clockwork/anima_fragment_attack.ogg'
ventcrawler = 2
playstyle_string = "<span class='heavy_brass'>You are a clockwork reclaimer</span><b>, a harbringer of the Justiciar's light. You can crawl through vents to move more swiftly. Your \
goal: purge all untruths and honor Ratvar. You may alt-click a valid target to break yourself apart and convert the target to a servant of Ratvar.</b>"
/mob/living/simple_animal/hostile/clockwork/reclaimer/New()
..()
if(prob(1))
real_name = "jehovah's witness"
name = real_name
spawn(1)
if(mind)
mind.special_role = null
add_servant_of_ratvar(src, TRUE)
src << playstyle_string
/mob/living/simple_animal/hostile/clockwork/reclaimer/Life()
..()
if(ishuman(loc))
var/mob/living/carbon/human/L = loc
if(L.stat || !L.client)
disengage()
/mob/living/simple_animal/hostile/clockwork/reclaimer/death()
..(1)
visible_message("<span class='warning'>[src] bursts into deadly shrapnel!</span>")
for(var/mob/living/carbon/C in range(2, src))
C.adjustBruteLoss(rand(3, 5))
qdel(src)
/mob/living/simple_animal/hostile/clockwork/reclaimer/AltClickOn(atom/movable/A)
if(!ishuman(A))
return ..()
var/mob/living/carbon/human/H = A
if(is_servant_of_ratvar(H) || H.stat || (H.mind && !H.client))
src << "<span class='warning'>[H] isn't a valid target! Valid targets are conscious non-servants.</span>"
return 0
if(get_dist(src, H) > 3)
src << "<span class='warning'>You need to be closer to dominate [H]!</span>"
return 0
visible_message("<span class='warning'>[src] rockets with blinding speed towards [H]!</span>", "<span class='heavy_brass'>You leap with blinding speed towards [H]'s head!</span>")
for(var/i = 9, i > 0, i -= 3)
pixel_y += i
sleep(1)
icon_state = "[initial(icon_state)]_charging"
while(loc != H.loc)
if(!H)
icon_state = initial(icon_state)
return 0
sleep(1)
forceMove(get_step(src, get_dir(src, H)))
if(H.head)
H.visible_message("<span class='warning'>[src] tears apart [H]'s [H.name]!</span>")
H.unEquip(H.head)
qdel(H.head)
H.visible_message("<span class='warning'>[src] latches onto [H]'s head and digs its claws in!</span>", "<span class='userdanger'>[src] leaps onto your head and impales its claws deep!</span>")
add_servant_of_ratvar(H)
H.equip_to_slot_or_del(new/obj/item/clothing/head/helmet/clockwork/reclaimer(null), slot_head)
loc = H
icon_state = initial(icon_state)
status_flags += GODMODE
src << "<span class='userdanger'>ASSIMILATION SUCCESSFUL.</span>"
H << "<span class='userdanger'>ASSIMILATION SUCCESSFUL.</span>"
clockwork_say(H, rot13("ASSIMILATION SUCCESSFUL."))
if(!H.mind)
mind.transfer_to(H)
return 1
/mob/living/simple_animal/hostile/clockwork/reclaimer/verb/disengage()
set name = "Disgengage From Host"
set desc = "Jumps off of your host if you have one, freeing their mind but allowing you movement."
set category = "Clockwork"
if(!ishuman(usr.loc))
usr << "<span class='warning'>You have no host! Alt-click on a non-servant to enslave them.</span>"
return
var/mob/living/carbon/human/L = usr.loc
usr.loc = get_turf(L)
pixel_y = initial(pixel_y)
usr.visible_message("<span class='warning'>[usr] jumps off of [L]'s head!</span>", "<span class='notice'>You disengage from your host.</span>")
usr.status_flags -= GODMODE
remove_servant_of_ratvar(L)
L.unEquip(L.head)
qdel(L.head)
/mob/living/mind_control_holder
name = "imprisoned mind"
desc = "A helpless mind, imprisoned in its own body."
stat = 0
status_flags = GODMODE
/mob/living/mind_control_holder/say()
return 0
@@ -0,0 +1,326 @@
/obj/structure/clockwork/massive //For objects that are typically very large
name = "massive construct"
desc = "A very large construction."
layer = MASSIVE_OBJ_LAYER
density = FALSE
burn_state = LAVA_PROOF
/obj/structure/clockwork/massive/New()
..()
poi_list += src
/obj/structure/clockwork/massive/Destroy()
poi_list -= src
return ..()
/obj/structure/clockwork/massive/celestial_gateway //The gateway to Reebe, from which Ratvar emerges
name = "Gateway to the Celestial Derelict"
desc = "A massive, thrumming rip in spacetime."
clockwork_desc = "A portal to the Celestial Derelict. Massive and intimidating, it is the only thing that can both transport Ratvar and withstand the massive amount of energy he emits."
health = 500
max_health = 500
mouse_opacity = 2
icon = 'icons/effects/clockwork_effects.dmi'
icon_state = "nothing"
density = TRUE
can_be_repaired = FALSE
var/progress_in_seconds = 0 //Once this reaches GATEWAY_RATVAR_ARRIVAL, it's game over
var/purpose_fulfilled = FALSE
var/first_sound_played = FALSE
var/second_sound_played = FALSE
var/third_sound_played = FALSE
var/obj/effect/clockwork/gateway_glow/glow
var/obj/effect/countdown/clockworkgate/countdown
/obj/structure/clockwork/massive/celestial_gateway/New()
..()
glow = new(get_turf(src))
countdown = new(src)
countdown.start()
SSshuttle.emergencyNoEscape = TRUE
START_PROCESSING(SSobj, src)
var/area/gate_area = get_area(src)
for(var/M in mob_list)
if(is_servant_of_ratvar(M) || isobserver(M))
M << "<span class='large_brass'><b>A gateway to the Celestial Derelict has been created in [gate_area.map_name]!</b></span>"
/obj/structure/clockwork/massive/celestial_gateway/Destroy()
SSshuttle.emergencyNoEscape = FALSE
if(SSshuttle.emergency.mode == SHUTTLE_STRANDED)
SSshuttle.emergency.mode = SHUTTLE_DOCKED
SSshuttle.emergency.timer = world.time
if(!purpose_fulfilled)
priority_announce("Hostile enviroment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg', "Priority")
STOP_PROCESSING(SSobj, src)
if(!purpose_fulfilled)
var/area/gate_area = get_area(src)
for(var/M in mob_list)
if(is_servant_of_ratvar(M) || isobserver(M))
M << "<span class='large_brass'><b>A gateway to the Celestial Derelict has fallen at [gate_area.map_name]!</b></span>"
world << sound(null, 0, channel = 8)
qdel(glow)
glow = null
qdel(countdown)
countdown = null
return ..()
/obj/structure/clockwork/massive/celestial_gateway/destroyed()
countdown.stop()
visible_message("<span class='userdanger'>The [src] begins to pulse uncontrollably... you might want to run!</span>")
world << sound('sound/effects/clockcult_gateway_disrupted.ogg', 0, channel = 8, volume = 50)
make_glow()
glow.icon_state = "clockwork_gateway_disrupted"
takes_damage = FALSE
sleep(27)
explosion(src, 1, 3, 8, 8)
qdel(src)
return 1
/obj/structure/clockwork/massive/celestial_gateway/proc/make_glow()
if(!glow)
glow = new(get_turf(src))
glow.linked_gate = src
/obj/structure/clockwork/massive/celestial_gateway/ex_act(severity)
return 0 //Nice try, Toxins!
/obj/structure/clockwork/massive/celestial_gateway/process()
if(!progress_in_seconds || prob(5))
for(var/M in mob_list)
M << "<span class='warning'><b>You hear otherworldly sounds from the [dir2text(get_dir(get_turf(M), get_turf(src)))]...</span>"
if(!health)
return 0
progress_in_seconds += GATEWAY_SUMMON_RATE
switch(progress_in_seconds)
if(-INFINITY to GATEWAY_REEBE_FOUND)
if(!first_sound_played)
world << sound('sound/effects/clockcult_gateway_charging.ogg', 1, channel = 8, volume = 50)
first_sound_played = TRUE
make_glow()
glow.icon_state = "clockwork_gateway_charging"
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
if(!second_sound_played)
world << sound('sound/effects/clockcult_gateway_active.ogg', 1, channel = 8, volume = 50)
second_sound_played = TRUE
make_glow()
glow.icon_state = "clockwork_gateway_active"
if(GATEWAY_RATVAR_COMING to GATEWAY_RATVAR_ARRIVAL)
if(!third_sound_played)
world << sound('sound/effects/clockcult_gateway_closing.ogg', 1, channel = 8, volume = 50)
third_sound_played = TRUE
make_glow()
glow.icon_state = "clockwork_gateway_closing"
if(GATEWAY_RATVAR_ARRIVAL to INFINITY)
if(!purpose_fulfilled)
countdown.stop()
takes_damage = FALSE
purpose_fulfilled = TRUE
make_glow()
animate(glow, transform = matrix() * 1.5, alpha = 255, time = 126)
world << sound('sound/effects/ratvar_rises.ogg', 0, channel = 8) //End the sounds
sleep(131)
make_glow()
animate(glow, transform = matrix() * 3, alpha = 0, time = 5)
sleep(5)
new/obj/structure/clockwork/massive/ratvar(get_turf(src))
qdel(src)
/obj/structure/clockwork/massive/celestial_gateway/examine(mob/user)
icon_state = "spatial_gateway" //cheat wildly by pretending to have an icon
..()
icon_state = initial(icon_state)
if(is_servant_of_ratvar(user) || isobserver(user))
var/arrival_text = "IMMINENT"
if(GATEWAY_RATVAR_ARRIVAL - progress_in_seconds > 0)
arrival_text = "[round(max((GATEWAY_RATVAR_ARRIVAL - progress_in_seconds) / (GATEWAY_SUMMON_RATE * 0.5), 0), 1)]"
user << "<span class='big'><b>Seconds until Ratvar's arrival:</b> [arrival_text]s</span>"
switch(progress_in_seconds)
if(-INFINITY to GATEWAY_REEBE_FOUND)
user << "<span class='heavy_brass'>It's still opening.</span>"
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
user << "<span class='heavy_brass'>It's reached the Celestial Derelict and is drawing power from it.</span>"
if(GATEWAY_RATVAR_COMING to INFINITY)
user << "<span class='heavy_brass'>Ratvar is coming through the gateway!</span>"
else
switch(progress_in_seconds)
if(-INFINITY to GATEWAY_REEBE_FOUND)
user << "<span class='warning'>It's a swirling mass of blackness.</span>"
if(GATEWAY_REEBE_FOUND to GATEWAY_RATVAR_COMING)
user << "<span class='warning'>It seems to be leading somewhere.</span>"
if(GATEWAY_RATVAR_COMING to INFINITY)
user << "<span class='warning'><b>Something is coming through!</b></span>"
/obj/effect/clockwork/gateway_glow //the actual appearance of the Gateway to the Celestial Derelict; an object so the edges of the gate can be clicked through.
icon = 'icons/effects/96x96.dmi'
icon_state = "clockwork_gateway_charging"
pixel_x = -32
pixel_y = -32
mouse_opacity = 0
layer = MASSIVE_OBJ_LAYER
var/obj/structure/clockwork/massive/celestial_gateway/linked_gate
/obj/effect/clockwork/gateway_glow/Destroy()
if(linked_gate)
linked_gate.glow = null
linked_gate = null
return ..()
/obj/effect/clockwork/gateway_glow/examine(mob/user)
if(linked_gate)
linked_gate.examine(user)
/obj/effect/clockwork/gateway_glow/ex_act(severity, target)
return FALSE
/obj/structure/clockwork/massive/ratvar
name = "Ratvar, the Clockwork Justiciar"
desc = "<span class='userdanger'>What is what is what are what real what is all a lie all a lie it's all a lie why how can what is</span>"
clockwork_desc = "<span class='large_brass'><b><i>Ratvar, the Clockwork Justiciar, your master eternal.</i></b></span>"
icon = 'icons/effects/512x512.dmi'
icon_state = "ratvar"
pixel_x = -235
pixel_y = -248
takes_damage = FALSE
var/atom/prey //Whatever Ratvar is chasing
var/clashing = FALSE //If Ratvar is FUCKING FIGHTING WITH NAR-SIE
var/proselytize_range = 10
/obj/structure/clockwork/massive/ratvar/New()
..()
ratvar_awakens = TRUE
for(var/obj/item/clockwork/ratvarian_spear/R in all_clockwork_objects)
R.update_force()
START_PROCESSING(SSobj, src)
world << "<span class='heavy_brass'><font size=6>\"BAPR NTNVA ZL YVTUG FUNYY FUVAR NPEBFF GUVF CNGURGVP ERNYZ!!\"</font></span>"
world << 'sound/effects/ratvar_reveal.ogg'
var/image/alert_overlay = image('icons/effects/clockwork_effects.dmi', "ratvar_alert")
var/area/A = get_area(src)
notify_ghosts("The Justiciar's light calls to you! Reach out to Ratvar in [A.name] to be granted a shell to spread his glory!", null, source = src, alert_overlay = alert_overlay)
addtimer(SSshuttle.emergency, "request", 50, FALSE, null, 0.3)
/obj/structure/clockwork/massive/ratvar/Destroy()
ratvar_awakens = FALSE
for(var/obj/item/clockwork/ratvarian_spear/R in all_clockwork_objects)
R.update_force()
STOP_PROCESSING(SSobj, src)
world << "<span class='heavy_brass'><font size=6>\"NO! I will not... be...</font> <font size=5>banished...</font> <font size=4>again...\"</font></span>"
return ..()
/obj/structure/clockwork/massive/ratvar/attack_ghost(mob/dead/observer/O)
var/alertresult = alert(O, "Embrace the Justiciar's light? You can no longer be cloned!",,"Cogscarab", "Reclaimer", "No")
if(alertresult == "No" || !O)
return 0
var/mob/living/simple_animal/R
if(alertresult == "Cogscarab")
R = new/mob/living/simple_animal/drone/cogscarab/ratvar(get_turf(src))
R.visible_message("<span class='heavy_brass'>[R] forms, and its eyes blink open, glowing bright red!</span>")
else
R = new/mob/living/simple_animal/hostile/clockwork/reclaimer(get_turf(src))
R.visible_message("<span class='heavy_brass'>[R] forms, and it emits a faint hum!</span>")
R.key = O.key
/obj/structure/clockwork/massive/ratvar/Bump(atom/A)
forceMove(get_turf(A))
A.ratvar_act()
/obj/structure/clockwork/massive/ratvar/Process_Spacemove()
return clashing
/obj/structure/clockwork/massive/ratvar/process()
if(clashing) //I'm a bit occupied right now, thanks
return
for(var/atom/A in range(proselytize_range, src))
A.ratvar_act()
var/dir_to_step_in = pick(cardinal)
if(!prey)
for(var/obj/singularity/narsie/N in poi_list)
if(N.z == z)
prey = N
break
if(!prey) //In case there's a Nar-Sie
var/list/meals = list()
for(var/mob/living/L in living_mob_list)
if(L.z == z && !is_servant_of_ratvar(L) && L.mind)
meals += L
if(meals.len)
prey = pick(meals)
prey << "<span class='heavy_brass'><font size=5>\"You will do.\"</font></span>\n\
<span class='userdanger'>Something very large and very malevolent begins lumbering its way towards you...</span>"
prey << 'sound/effects/ratvar_reveal.ogg'
else
if(prob(10) || is_servant_of_ratvar(prey) || prey.z != z)
prey << "<span class='heavy_brass'><font size=5>\"How dull. Leave me.\"</font></span>\n\
<span class='userdanger'>You feel tremendous relief as a set of horrible eyes loses sight of you...</span>"
prey = null
else
dir_to_step_in = get_dir(src, prey) //Unlike Nar-Sie, Ratvar ruthlessly chases down his target
step(src, dir_to_step_in)
/obj/structure/clockwork/massive/ratvar/narsie_act()
if(clashing)
return 0
clashing = TRUE
world << "<span class='heavy_brass'><font size=5>\"[pick("BLOOD GOD!!!", "NAR-SIE!!!", "AT LAST, YOUR TIME HAS COME!")]\"</font></span>"
world << "<span class='cult'><font size=5>\"<b>Ratvar?! How?!</b>\"</font></span>"
for(var/obj/singularity/narsie/N in range(15, src))
if(N.clashing)
continue
N.clashing = TRUE
clash_of_the_titans(N) //IT'S TIME FOR THE BATTLE OF THE AGES
break
return 1
/obj/structure/clockwork/massive/ratvar/proc/clash_of_the_titans(obj/singularity/narsie/narsie)
var/winner = "Undeclared"
var/base_victory_chance = 0
while(TRUE)
world << 'sound/magic/clockwork/ratvar_attack.ogg'
sleep(5.2)
for(var/mob/M in mob_list)
if(M.client)
M.client.color = rgb(150, 100, 0)
spawn(1)
M.client.color = initial(M.client.color)
shake_camera(M, 4, 3)
var/r_success_modifier = (ticker.mode.servants_of_ratvar.len * 2) //2% for each cultist
var/n_success_modifier = (ticker.mode.cult.len * 2)
for(var/mob/living/simple_animal/hostile/construct/harvester/C in player_list)
n_success_modifier += 2
if(prob(base_victory_chance + r_success_modifier))
winner = "Ratvar"
break
sleep(rand(2,5))
world << 'sound/magic/clockwork/narsie_attack.ogg'
sleep(7.4)
for(var/mob/M in mob_list)
if(M.client)
M.client.color = rgb(200, 0, 0)
spawn(1)
M.client.color = initial(M.client.color)
shake_camera(M, 4, 3)
if(prob(base_victory_chance + n_success_modifier))
winner = "Nar-Sie"
break
base_victory_chance++ //The clash has a higher chance of resolving each time both gods attack one another
switch(winner)
if("Ratvar")
world << "<span class='heavy_brass'><font size=5>\"[pick("DIE! DIE! DIE!", "REEEEEEEEE!", "FILTH!!!", "SUFFER!!!", "EBG SBE PRAGHEVRF NF V UNIR!!")]\"</font></span>" //nar-sie get out
world << "<span class='cult'><font size=5>\"<b>[pick("Nooooo...", "Not die. To y-", "Die. Ratv-", "Sas tyen re-")]\"</b></font></span>"
world << 'sound/magic/clockwork/anima_fragment_attack.ogg'
world << 'sound/magic/demon_dies.ogg'
clashing = FALSE
qdel(narsie)
return 1
if("Nar-Sie")
world << "<span class='cult'><font size=5>\"<b>[pick("Ha.", "Ra'sha fonn dest.", "You fool. To come here.")]</b>\"</font></span>" //Broken English
world << 'sound/magic/demon_attack1.ogg'
world << 'sound/magic/clockwork/anima_fragment_death.ogg'
narsie.clashing = FALSE
qdel(src)
return 1
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,955 @@
//////////////////////////
// CLOCKWORK STRUCTURES //
//////////////////////////
/obj/structure/clockwork
name = "meme structure"
desc = "Some frog or something, the fuck?"
var/clockwork_desc //Shown to servants when they examine
icon = 'icons/obj/clockwork_objects.dmi'
icon_state = "rare_pepe"
anchored = 1
density = 1
opacity = 0
layer = BELOW_OBJ_LAYER
var/max_health = 100 //All clockwork structures have health that can be removed via attacks
var/health = 100
var/repair_amount = 5 //how much a proselytizer can repair each cycle
var/can_be_repaired = TRUE //if a proselytizer can repair it at all
var/takes_damage = TRUE //If the structure can be damaged
var/break_message = "<span class='warning'>The frog isn't a meme after all!</span>" //The message shown when a structure breaks
var/break_sound = 'sound/magic/clockwork/anima_fragment_death.ogg' //The sound played when a structure breaks
var/list/debris = list(/obj/item/clockwork/alloy_shards) //Parts left behind when a structure breaks
var/construction_value = 0 //How much value the structure contributes to the overall "power" of the structures on the station
/obj/structure/clockwork/New()
..()
clockwork_construction_value += construction_value
all_clockwork_objects += src
/obj/structure/clockwork/Destroy()
clockwork_construction_value -= construction_value
all_clockwork_objects -= src
return ..()
/obj/structure/clockwork/proc/destroyed()
if(!takes_damage)
return 0
for(var/I in debris)
new I (get_turf(src))
visible_message(break_message)
playsound(src, break_sound, 50, 1)
qdel(src)
return 1
/obj/structure/clockwork/burn()
SSobj.burning -= src
if(takes_damage)
playsound(src, 'sound/items/Welder.ogg', 100, 1)
visible_message("<span class='warning'>[src] is warped by the heat!</span>")
take_damage(rand(50, 100), BURN)
/obj/structure/clockwork/proc/take_damage(amount, damage_type)
if(!amount || !damage_type || !damage_type in list(BRUTE, BURN))
return 0
if(takes_damage)
health = max(0, health - amount)
if(!health)
destroyed()
return 1
return 0
/obj/structure/clockwork/narsie_act()
if(take_damage(rand(25, 50), BRUTE) && src) //if we still exist
var/previouscolor = color
color = "#960000"
animate(src, color = previouscolor, time = 8)
/obj/structure/clockwork/ex_act(severity)
var/damage = 0
switch(severity)
if(1)
damage = max_health //100% max health lost
if(2)
damage = max_health * rand(0.5, 0.7) //50-70% max health lost
if(3)
damage = max_health * rand(0.1, 0.3) //10-30% max health lost
if(damage)
take_damage(damage, BRUTE)
/obj/structure/clockwork/examine(mob/user)
var/can_see_clockwork = is_servant_of_ratvar(user) || isobserver(user)
if(can_see_clockwork && clockwork_desc)
desc = clockwork_desc
..()
desc = initial(desc)
if(takes_damage)
var/servant_message = "It is at <b>[health]/[max_health]</b> integrity"
var/other_message = "It seems pristine and undamaged"
var/heavily_damaged = FALSE
var/healthpercent = (health/max_health) * 100
if(healthpercent >= 100)
other_message = "It seems pristine and undamaged"
else if(healthpercent >= 50)
other_message = "It looks slightly dented"
else if(healthpercent >= 25)
other_message = "It appears heavily damaged"
heavily_damaged = TRUE
else if(healthpercent >= 0)
other_message = "It's falling apart"
heavily_damaged = TRUE
user << "<span class='[heavily_damaged ? "alloy":"brass"]'>[can_see_clockwork ? "[servant_message]":"[other_message]"][heavily_damaged ? "!":"."]</span>"
/obj/structure/clockwork/bullet_act(obj/item/projectile/P)
. = ..()
visible_message("<span class='danger'>[src] is hit by \a [P]!</span>")
playsound(src, P.hitsound, 50, 1)
take_damage(P.damage, P.damage_type)
/obj/structure/clockwork/proc/attack_generic(mob/user, damage = 0, damage_type = BRUTE) //used by attack_alien, attack_animal, and attack_slime
user.do_attack_animation(src)
user.changeNext_move(CLICK_CD_MELEE)
user.visible_message("<span class='danger'>[user] smashes into [src]!</span>")
take_damage(damage, damage_type)
/obj/structure/clockwork/attack_alien(mob/living/user)
playsound(src, 'sound/weapons/bladeslice.ogg', 50, 1)
attack_generic(user, 15)
/obj/structure/clockwork/attack_animal(mob/living/simple_animal/M)
if(!M.melee_damage_upper)
return
playsound(src, 'sound/weapons/Genhit.ogg', 50, 1)
attack_generic(M, M.melee_damage_upper, M.melee_damage_type)
/obj/structure/clockwork/attack_slime(mob/living/simple_animal/slime/user)
if(!user.is_adult)
return
playsound(src, 'sound/weapons/Genhit.ogg', 50, 1)
attack_generic(user, rand(10, 15))
/obj/structure/clockwork/attacked_by(obj/item/I, mob/living/user)
. = ..()
if(I.force && takes_damage)
playsound(src, I.hitsound, 50, 1)
take_damage(I.force, I.damtype)
/obj/structure/clockwork/mech_melee_attack(obj/mecha/M)
if(..())
playsound(src, 'sound/weapons/punch4.ogg', 50, 1)
take_damage(M.force, M.damtype)
/obj/structure/clockwork/cache //Tinkerer's cache: Stores components for later use.
name = "tinkerer's cache"
desc = "A large brass spire with a flaming hole in its center."
clockwork_desc = "A brass container capable of storing a large amount of components.\n\
Shares components with all other caches and will gradually generate components if near a Clockwork Wall."
icon_state = "tinkerers_cache"
construction_value = 10
break_message = "<span class='warning'>The cache's fire winks out before it falls in on itself!</span>"
max_health = 80
health = 80
var/wall_generation_cooldown
var/wall_found = FALSE //if we've found a wall and finished our windup delay
/obj/structure/clockwork/cache/New()
..()
START_PROCESSING(SSobj, src)
clockwork_caches++
SetLuminosity(2,1)
/obj/structure/clockwork/cache/Destroy()
clockwork_caches--
STOP_PROCESSING(SSobj, src)
return ..()
/obj/structure/clockwork/cache/destroyed()
if(takes_damage)
for(var/I in src)
var/atom/movable/A = I
A.forceMove(get_turf(src)) //drop any daemons we have
return ..()
/obj/structure/clockwork/cache/process()
for(var/turf/closed/wall/clockwork/C in orange(1, src))
if(!wall_found)
wall_found = TRUE
wall_generation_cooldown = world.time + CACHE_PRODUCTION_TIME
visible_message("<span class='warning'>[src] starts to whirr in the presence of [C]...</span>")
break
if(wall_generation_cooldown <= world.time)
wall_generation_cooldown = world.time + CACHE_PRODUCTION_TIME
generate_cache_component()
playsound(C, 'sound/magic/clockwork/fellowship_armory.ogg', rand(15, 20), 1, -3, 1, 1)
visible_message("<span class='warning'>Something clunks around inside of [src]...</span>")
break
/obj/structure/clockwork/cache/attackby(obj/item/I, mob/living/user, params)
if(!is_servant_of_ratvar(user))
return ..()
if(istype(I, /obj/item/clockwork/component))
var/obj/item/clockwork/component/C = I
clockwork_component_cache[C.component_id]++
user << "<span class='notice'>You add [C] to [src].</span>"
user.drop_item()
qdel(C)
return 1
else if(istype(I, /obj/item/clockwork/slab))
var/obj/item/clockwork/slab/S = I
clockwork_component_cache["belligerent_eye"] += S.stored_components["belligerent_eye"]
clockwork_component_cache["vanguard_cogwheel"] += S.stored_components["vanguard_cogwheel"]
clockwork_component_cache["guvax_capacitor"] += S.stored_components["guvax_capacitor"]
clockwork_component_cache["replicant_alloy"] += S.stored_components["replicant_alloy"]
clockwork_component_cache["hierophant_ansible"] += S.stored_components["hierophant_ansible"]
S.stored_components["belligerent_eye"] = 0
S.stored_components["vanguard_cogwheel"] = 0
S.stored_components["guvax_capacitor"] = 0
S.stored_components["replicant_alloy"] = 0
S.stored_components["hierophant_ansible"] = 0
user.visible_message("<span class='notice'>[user] empties [S] into [src].</span>", "<span class='notice'>You offload your slab's components into [src].</span>")
return 1
else if(istype(I, /obj/item/clockwork/daemon_shell))
var/component_type
switch(alert(user, "Will this daemon produce a specific type of component or produce randomly?.", , "Specific Type", "Random Component"))
if("Specific Type")
switch(input(user, "Choose a component type.", name) as null|anything in list("Belligerent Eyes", "Vanguard Cogwheels", "Guvax Capacitors", "Replicant Alloys", "Hierophant Ansibles"))
if("Belligerent Eyes")
component_type = "belligerent_eye"
if("Vanguard Cogwheels")
component_type = "vanguard_cogwheel"
if("Guvax Capacitors")
component_type = "guvax_capacitor"
if("Replicant Alloys")
component_type = "replicant_alloy"
if("Hierophant Ansibles")
component_type = "hierophant_ansibles"
if(!user || !user.canUseTopic(src) || !user.canUseTopic(I))
return 0
var/obj/item/clockwork/tinkerers_daemon/D = new(src)
D.cache = src
D.specific_component = component_type
user.visible_message("<span class='notice'>[user] spins the cogwheel on [I] and puts it into [src].</span>", \
"<span class='notice'>You activate the daemon and put it into [src]. It will now produce a component every twenty seconds.</span>")
user.drop_item()
qdel(I)
return 1
else
return ..()
/obj/structure/clockwork/cache/attack_hand(mob/user)
if(!is_servant_of_ratvar(user))
return 0
var/list/possible_components = list()
if(clockwork_component_cache["belligerent_eye"])
possible_components += "Belligerent Eye"
if(clockwork_component_cache["vanguard_cogwheel"])
possible_components += "Vanguard Cogwheel"
if(clockwork_component_cache["guvax_capacitor"])
possible_components += "Guvax Capacitor"
if(clockwork_component_cache["replicant_alloy"])
possible_components += "Replicant Alloy"
if(clockwork_component_cache["hierophant_ansible"])
possible_components += "Hierophant Ansible"
if(!possible_components.len)
user << "<span class='warning'>[src] is empty!</span>"
return 0
var/component_to_withdraw = input(user, "Choose a component to withdraw.", name) as null|anything in possible_components
if(!user || !user.canUseTopic(src) || !component_to_withdraw)
return 0
var/obj/item/clockwork/component/the_component
switch(component_to_withdraw)
if("Belligerent Eye")
if(clockwork_component_cache["belligerent_eye"])
the_component = new/obj/item/clockwork/component/belligerent_eye(get_turf(src))
clockwork_component_cache["belligerent_eye"]--
if("Vanguard Cogwheel")
if(clockwork_component_cache["vanguard_cogwheel"])
the_component = new/obj/item/clockwork/component/vanguard_cogwheel(get_turf(src))
clockwork_component_cache["vanguard_cogwheel"]--
if("Guvax Capacitor")
if(clockwork_component_cache["guvax_capacitor"])
the_component = new/obj/item/clockwork/component/guvax_capacitor(get_turf(src))
clockwork_component_cache["guvax_capacitor"]--
if("Replicant Alloy")
if(clockwork_component_cache["replicant_alloy"])
the_component = new/obj/item/clockwork/component/replicant_alloy(get_turf(src))
clockwork_component_cache["replicant_alloy"]--
if("Hierophant Ansible")
if(clockwork_component_cache["hierophant_ansible"])
the_component = new/obj/item/clockwork/component/hierophant_ansible(get_turf(src))
clockwork_component_cache["hierophant_ansible"]--
if(the_component)
user.visible_message("<span class='notice'>[user] withdraws [the_component] from [src].</span>", "<span class='notice'>You withdraw [the_component] from [src].</span>")
user.put_in_hands(the_component)
return 1
/obj/structure/clockwork/cache/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
user << "<b>Stored components:</b>"
user << "<span class='neovgre_small'><i>Belligerent Eyes:</i> [clockwork_component_cache["belligerent_eye"]]</span>"
user << "<span class='inathneq_small'><i>Vanguard Cogwheels:</i> [clockwork_component_cache["vanguard_cogwheel"]]</span>"
user << "<span class='sevtug_small'><i>Guvax Capacitors:</i> [clockwork_component_cache["guvax_capacitor"]]</span>"
user << "<span class='nezbere_small'><i>Replicant Alloys:</i> [clockwork_component_cache["replicant_alloy"]]</span>"
user << "<span class='nzcrentr_small'><i>Hierophant Ansibles:</i> [clockwork_component_cache["hierophant_ansible"]]</span>"
/obj/structure/clockwork/ocular_warden //Ocular warden: Low-damage, low-range turret. Deals constant damage to whoever it makes eye contact with.
name = "ocular warden"
desc = "A large brass eye with tendrils trailing below it and a wide red iris."
clockwork_desc = "A stalwart turret that will deal sustained damage to any non-faithful it sees."
icon_state = "ocular_warden"
health = 25
max_health = 25
construction_value = 15
layer = HIGH_OBJ_LAYER
break_message = "<span class='warning'>The warden's eye gives a glare of utter hate before falling dark!</span>"
debris = list(/obj/item/clockwork/component/belligerent_eye/blind_eye)
burn_state = LAVA_PROOF
var/damage_per_tick = 3
var/sight_range = 3
var/mob/living/target
var/list/idle_messages = list(" sulkily glares around.", " lazily drifts from side to side.", " looks around for something to burn.", " slowly turns in circles.")
/obj/structure/clockwork/ocular_warden/New()
..()
START_PROCESSING(SSfastprocess, src)
/obj/structure/clockwork/ocular_warden/Destroy()
STOP_PROCESSING(SSfastprocess, src)
return ..()
/obj/structure/clockwork/ocular_warden/examine(mob/user)
..()
user << "<span class='brass'>[target ? "<b>It's fixated on [target]!</b>" : "Its gaze is wandering aimlessly."]</span>"
/obj/structure/clockwork/ocular_warden/process()
var/list/validtargets = acquire_nearby_targets()
if(ratvar_awakens && (damage_per_tick == initial(damage_per_tick) || sight_range == initial(sight_range))) //Massive buff if Ratvar has returned
damage_per_tick = 10
sight_range = 5
if(target)
if(!(target in validtargets))
lose_target()
else
if(!target.null_rod_check())
target.adjustFireLoss(!iscultist(target) ? damage_per_tick : damage_per_tick * 2) //Nar-Sian cultists take additional damage
if(ratvar_awakens && target)
target.adjust_fire_stacks(damage_per_tick)
target.IgniteMob()
setDir(get_dir(get_turf(src), get_turf(target)))
if(!target)
if(validtargets.len)
target = pick(validtargets)
visible_message("<span class='warning'>[src] swivels to face [target]!</span>")
target << "<span class='heavy_brass'>\"I SEE YOU!\"</span>\n<span class='userdanger'>[src]'s gaze [ratvar_awakens ? "melts you alive" : "burns you"]!</span>"
if(target.null_rod_check() && !ratvar_awakens)
target << "<span class='warning'>Your artifact glows hotly against you, protecting you from the warden's gaze!</span>"
else if(prob(0.5)) //Extremely low chance because of how fast the subsystem it uses processes
if(prob(50))
visible_message("<span class='notice'>[src][pick(idle_messages)]</span>")
else
setDir(pick(cardinal))//Random rotation
/obj/structure/clockwork/ocular_warden/proc/acquire_nearby_targets()
. = list()
for(var/mob/living/L in viewers(sight_range, src)) //Doesn't attack the blind
if(!is_servant_of_ratvar(L) && !L.stat && L.mind && !(L.disabilities & BLIND) && !L.null_rod_check())
. += L
/obj/structure/clockwork/ocular_warden/proc/lose_target()
if(!target)
return 0
target = null
visible_message("<span class='warning'>[src] settles and seems almost disappointed.</span>")
return 1
/obj/structure/clockwork/shell
construction_value = 0
anchored = 0
density = 0
takes_damage = FALSE
burn_state = LAVA_PROOF
var/mobtype = /mob/living/simple_animal/hostile/clockwork
var/spawn_message = " is an error and you should yell at whoever spawned this shell."
/obj/structure/clockwork/shell/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/device/mmi/posibrain/soul_vessel))
if(!is_servant_of_ratvar(user))
..()
return 0
var/obj/item/device/mmi/posibrain/soul_vessel/S = I
if(!S.brainmob)
user << "<span class='warning'>[S] hasn't trapped a spirit! Turn it on first.</span>"
return 0
if(S.brainmob && (!S.brainmob.client || !S.brainmob.mind))
user << "<span class='warning'>[S]'s trapped spirit appears inactive!</span>"
return 0
user.visible_message("<span class='notice'>[user] places [S] in [src], where it fuses to the shell.</span>", "<span class='brass'>You place [S] in [src], fusing it to the shell.</span>")
var/mob/living/simple_animal/A = new mobtype(get_turf(src))
A.visible_message("<span class='brass'>[src][spawn_message]</span>")
S.brainmob.mind.transfer_to(A)
add_servant_of_ratvar(A, TRUE)
user.drop_item()
qdel(S)
qdel(src)
return 1
else
return ..()
/obj/structure/clockwork/shell/cogscarab
name = "cogscarab shell"
desc = "A small brass shell with a cube-shaped receptable in its center. It gives off an aura of obsessive perfectionism."
clockwork_desc = "A dormant receptable that, when powered with a soul vessel, will become a weak construct with an inbuilt proselytizer."
icon_state = "clockdrone_shell"
mobtype = /mob/living/simple_animal/drone/cogscarab
spawn_message = "'s eyes blink open, glowing bright red."
/obj/structure/clockwork/shell/fragment //Anima fragment: Useless on its own, but can accept an active soul vessel to create a powerful construct.
name = "fragment shell"
desc = "A massive brass shell with a small cube-shaped receptable in its center. It gives off an aura of contained power."
clockwork_desc = "A dormant receptable that, when powered with a soul vessel, will become a powerful construct."
icon_state = "anime_fragment"
mobtype = /mob/living/simple_animal/hostile/clockwork/fragment
spawn_message = " whirs and rises from the ground on a flickering jet of reddish fire."
/obj/structure/clockwork/wall_gear
name = "massive gear"
icon_state = "wall_gear"
climbable = TRUE
max_health = 50
health = 50
desc = "A massive brass gear. You could probably secure or unsecure it with a wrench, or just climb over it."
clockwork_desc = "A massive brass gear. You could probably secure or unsecure it with a wrench, just climb over it, or proselytize it into replicant alloy."
break_message = "<span class='warning'>The gear breaks apart into shards of alloy!</span>"
debris = list(/obj/item/clockwork/alloy_shards)
/obj/structure/clockwork/wall_gear/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/weapon/wrench))
default_unfasten_wrench(user, I, 10)
return 1
return ..()
/obj/structure/clockwork/wall_gear/examine(mob/user)
..()
user << "<span class='notice'>[src] is [anchored ? "secured to the floor":"mobile, and not secured"].</span>"
///////////////////////
// CLOCKWORK EFFECTS //
///////////////////////
/obj/effect/clockwork
name = "meme machine"
desc = "Still don't know what it is."
var/clockwork_desc = "A fabled artifact from beyond the stars. Contains concentrated meme essence." //Shown to clockwork cultists instead of the normal description
icon = 'icons/effects/clockwork_effects.dmi'
icon_state = "ratvars_flame"
anchored = 1
density = 0
opacity = 0
burn_state = LAVA_PROOF
/obj/effect/clockwork/New()
..()
all_clockwork_objects += src
/obj/effect/clockwork/Destroy()
all_clockwork_objects -= src
return ..()
/obj/effect/clockwork/examine(mob/user)
if((is_servant_of_ratvar(user) || isobserver(user)) && clockwork_desc)
desc = clockwork_desc
..()
desc = initial(desc)
/obj/effect/clockwork/judicial_marker //Judicial marker: Created by the judicial visor. After three seconds, stuns any non-servants nearby and damages Nar-Sian cultists.
name = "judicial marker"
desc = "You get the feeling that you shouldn't be standing here."
clockwork_desc = "A sigil that will soon erupt and smite any unenlightened nearby."
icon = 'icons/effects/96x96.dmi'
pixel_x = -32
pixel_y = -32
layer = BELOW_MOB_LAYER
var/mob/user
/obj/effect/clockwork/judicial_marker/New(loc, caster)
..()
user = caster
playsound(src, 'sound/magic/MAGIC_MISSILE.ogg', 50, 1, 1, 1)
flick("judicial_marker", src)
addtimer(src, "burstanim", 16, FALSE)
/obj/effect/clockwork/judicial_marker/proc/burstanim()
layer = ABOVE_ALL_MOB_LAYER
flick("judicial_explosion", src)
addtimer(src, "judicialblast", 13, FALSE)
/obj/effect/clockwork/judicial_marker/proc/judicialblast()
var/targetsjudged = 0
playsound(src, 'sound/effects/explosionfar.ogg', 100, 1, 1, 1)
for(var/mob/living/L in range(1, src))
if(is_servant_of_ratvar(L))
continue
if(L.null_rod_check())
var/obj/item/I = L.null_rod_check()
L.visible_message("<span class='warning'>Strange energy flows into [L]'s [I.name]!</span>", \
"<span class='userdanger'>Your [I.name] shields you from [src]!</span>")
continue
if(!iscultist(L))
L.visible_message("<span class='warning'>[L] is struck by a judicial explosion!</span>", \
"<span class='userdanger'>[!issilicon(L) ? "An unseen force slams you into the ground!" : "ERROR: Motor servos disabled by external source!"]</span>")
L.Weaken(8)
else
L.visible_message("<span class='warning'>[L] is struck by a judicial explosion!</span>", \
"<span class='heavy_brass'>\"Keep an eye out, filth.\"</span>\n<span class='userdanger'>A burst of heat crushes you against the ground!</span>")
L.Weaken(4) //half the stun, but sets cultists on fire
L.adjust_fire_stacks(2)
L.IgniteMob()
targetsjudged++
L.adjustBruteLoss(10)
user << "<span class='brass'><b>[targetsjudged ? "Successfully judged <span class='neovgre'>[targetsjudged]</span>":"Judged no"] heretic[!targetsjudged || targetsjudged > 1 ? "s":""].</b></span>"
QDEL_IN(src, 3) //so the animation completes properly
/obj/effect/clockwork/judicial_marker/ex_act(severity)
return
/obj/effect/clockwork/spatial_gateway //Spatial gateway: A usually one-way rift to another location.
name = "spatial gateway"
desc = "A gently thrumming tear in reality."
clockwork_desc = "A gateway in reality."
icon_state = "spatial_gateway"
density = 1
var/sender = TRUE //If this gateway is made for sending, not receiving
var/both_ways = FALSE
var/lifetime = 25 //How many deciseconds this portal will last
var/uses = 1 //How many objects or mobs can go through the portal
var/obj/effect/clockwork/spatial_gateway/linked_gateway //The gateway linked to this one
/obj/effect/clockwork/spatial_gateway/New()
..()
spawn(1)
if(!linked_gateway)
qdel(src)
return 0
if(both_ways)
clockwork_desc = "A gateway in reality. It can both send and receive objects."
else
clockwork_desc = "A gateway in reality. It can only [sender ? "send" : "receive"] objects."
QDEL_IN(src, lifetime)
//set up a gateway with another gateway
/obj/effect/clockwork/spatial_gateway/proc/setup_gateway(obj/effect/clockwork/spatial_gateway/gatewayB, set_duration, set_uses, two_way)
if(!gatewayB || !set_duration || !uses)
return 0
linked_gateway = gatewayB
gatewayB.linked_gateway = src
if(two_way)
both_ways = TRUE
gatewayB.both_ways = TRUE
else
sender = TRUE
gatewayB.sender = FALSE
gatewayB.density = FALSE
lifetime = set_duration
gatewayB.lifetime = set_duration
uses = set_uses
gatewayB.uses = set_uses
return 1
/obj/effect/clockwork/spatial_gateway/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
user << "<span class='brass'>It has [uses] uses remaining.</span>"
/obj/effect/clockwork/spatial_gateway/attack_hand(mob/living/user)
if(user.pulling && user.a_intent == "grab" && isliving(user.pulling))
var/mob/living/L = user.pulling
if(L.buckled || L.anchored || L.has_buckled_mobs())
return 0
user.visible_message("<span class='warning'>[user] shoves [L] into [src]!</span>", "<span class='danger'>You shove [L] into [src]!</span>")
user.stop_pulling()
pass_through_gateway(L)
return 1
if(!user.canUseTopic(src))
return 0
user.visible_message("<span class='warning'>[user] climbs through [src]!</span>", "<span class='danger'>You brace yourself and step through [src]...</span>")
pass_through_gateway(user)
return 1
/obj/effect/clockwork/spatial_gateway/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/weapon/nullrod))
user.visible_message("<span class='warning'>[user] dispels [src] with [I]!</span>", "<span class='danger'>You close [src] with [I]!</span>")
qdel(linked_gateway)
qdel(src)
return 1
if(istype(I, /obj/item/clockwork/slab))
user << "<span class='heavy_brass'>\"I don't think you want to drop your slab into that\".\n\"If you really want to, try throwing it.\"</span>"
return 1
if(user.drop_item())
user.visible_message("<span class='warning'>[user] drops [I] into [src]!</span>", "<span class='danger'>You drop [I] into [src]!</span>")
pass_through_gateway(I)
..()
/obj/effect/clockwork/spatial_gateway/Bumped(atom/A)
..()
if(isliving(A) || istype(A, /obj/item))
pass_through_gateway(A)
/obj/effect/clockwork/spatial_gateway/proc/pass_through_gateway(atom/movable/A)
if(!linked_gateway)
qdel(src)
return 0
if(!sender)
visible_message("<span class='warning'>[A] bounces off of [src]!</span>")
return 0
if(!uses)
return 0
if(isliving(A))
var/mob/living/user = A
user << "<span class='warning'><b>You pass through [src] and appear elsewhere!</b></span>"
linked_gateway.visible_message("<span class='warning'>A shape appears in [linked_gateway] before emerging!</span>")
playsound(src, 'sound/effects/EMPulse.ogg', 50, 1)
playsound(linked_gateway, 'sound/effects/EMPulse.ogg', 50, 1)
transform = matrix() * 1.5
animate(src, transform = matrix() / 1.5, time = 10)
linked_gateway.transform = matrix() * 1.5
animate(linked_gateway, transform = matrix() / 1.5, time = 10)
A.forceMove(get_turf(linked_gateway))
uses = max(0, uses - 1)
linked_gateway.uses = max(0, linked_gateway.uses - 1)
spawn(10)
if(!uses)
qdel(src)
qdel(linked_gateway)
return 1
/obj/effect/clockwork/general_marker
name = "general marker"
desc = "Some big guy. For you."
clockwork_desc = "One of Ratvar's generals."
alpha = 200
layer = MASSIVE_OBJ_LAYER
/obj/effect/clockwork/general_marker/New()
..()
playsound(src, 'sound/magic/clockwork/invoke_general.ogg', 50, 0)
animate(src, alpha = 0, time = 10)
QDEL_IN(src, 10)
/obj/effect/clockwork/general_marker/nezbere
name = "Nezbere, the Brass Eidolon"
desc = "A towering colossus clad in nigh-impenetrable brass armor. Its gaze is stern yet benevolent, even upon you."
clockwork_desc = "One of Ratvar's four generals. Nezbere is responsible for the design, testing, and creation of everything in Ratvar's domain."
icon = 'icons/effects/340x428.dmi'
icon_state = "nezbere"
pixel_x = -154
pixel_y = -198
/obj/effect/clockwork/general_marker/sevtug
name = "Sevtug, the Formless Pariah"
desc = "A sinister cloud of purple energy. Looking at it gives you a headache."
clockwork_desc = "One of Ratvar's four generals. Sevtug taught him how to manipulate minds and is one of his oldest allies."
icon = 'icons/effects/211x247.dmi'
icon_state = "sevtug"
pixel_x = -89
pixel_y = -107
/obj/effect/clockwork/general_marker/nzcrentr
name = "Nzcrentr, the Forgotten Arbiter"
desc = "A terrifying war machine crackling with limitless energy."
clockwork_desc = "One of Ratvar's four generals. Nzcrentr is the result of Neovgre - Nezbere's finest war machine, commandeerable only be a mortal - fusing with its pilot and driving her \
insane. Nzcrentr seeks out any and all sentient life to slaughter it for sport."
icon = 'icons/effects/254x361.dmi'
icon_state = "nzcrentr"
pixel_x = -111
pixel_y = -164
/obj/effect/clockwork/general_marker/inathneq
name = "Inath-Neq, the Resonant Cogwheel"
desc = "A humanoid form blazing with blue fire. It radiates an aura of kindness and caring."
clockwork_desc = "One of Ratvar's four generals. Before her current form, Inath-Neq was a powerful warrior priestess commanding the Resonant Cogs, a sect of Ratvarian warriors renowned for \
their prowess. After a lost battle with Nar-Sian cultists, Inath-Neq was struck down and stated in her dying breath, \
\"The Resonant Cogs shall not fall silent this day, but will come together to form a wheel that shall never stop turning.\" Ratvar, touched by this, granted Inath-Neq an eternal body and \
merged her soul with those of the Cogs slain with her on the battlefield."
icon = 'icons/effects/187x381.dmi'
icon_state = "inath-neq"
pixel_x = -77
pixel_y = -174
/obj/effect/clockwork/sigil //Sigils: Rune-like markings on the ground with various effects.
name = "sigil"
desc = "A strange set of markings drawn on the ground."
clockwork_desc = "A sigil of some purpose."
icon_state = "sigil"
layer = LOW_OBJ_LAYER
alpha = 50
burn_state = FIRE_PROOF
burntime = 1
var/affects_servants = FALSE
var/stat_affected = CONSCIOUS
/obj/effect/clockwork/sigil/attack_hand(mob/user)
if(iscarbon(user) && !user.stat && (!is_servant_of_ratvar(user) || (is_servant_of_ratvar(user) && user.a_intent == "harm")))
user.visible_message("<span class='warning'>[user] stamps out [src]!</span>", "<span class='danger'>You stomp on [src], scattering it into thousands of particles.</span>")
qdel(src)
return 1
..()
/obj/effect/clockwork/sigil/Crossed(atom/movable/AM)
..()
if(isliving(AM))
var/mob/living/L = AM
if(L.stat <= stat_affected)
if((!is_servant_of_ratvar(L) || (is_servant_of_ratvar(L) && affects_servants)) && L.mind)
if(L.null_rod_check())
var/obj/item/I = L.null_rod_check()
L.visible_message("<span class='warning'>[L]'s [I.name] protects them from [src]'s effects!</span>", "<span class='userdanger'>Your [I.name] protects you!</span>")
return
sigil_effects(L)
return 1
/obj/effect/clockwork/sigil/proc/sigil_effects(mob/living/L)
/obj/effect/clockwork/sigil/transgression //Sigil of Transgression: Stuns and flashes the first non-servant to walk on it. Nar-Sian cultists are damaged and knocked down for about twice the stun
name = "dull sigil"
desc = "A dull, barely-visible golden sigil. It's as though light was carved into the ground."
icon = 'icons/effects/clockwork_effects.dmi'
clockwork_desc = "A sigil that will stun the first non-servant to cross it. Nar-Sie's dogs will be knocked down."
icon_state = "sigildull"
color = "#FAE48C"
/obj/effect/clockwork/sigil/transgression/sigil_effects(mob/living/L)
var/target_flashed = L.flash_eyes()
for(var/mob/living/M in viewers(5, src))
if(!is_servant_of_ratvar(M) && M != L)
M.flash_eyes()
if(iscultist(L))
L << "<span class='heavy_brass'>\"Watch your step, wretch.\"</span>"
L.adjustBruteLoss(10)
L.Weaken(4)
L.visible_message("<span class='warning'>[src] appears around [L] in a burst of light!</span>", \
"<span class='userdanger'>[target_flashed ? "An unseen force":"The glowing sigil around you"] holds you in place!</span>")
L.Stun(3)
PoolOrNew(/obj/effect/overlay/temp/ratvar/sigil/transgression, get_turf(src))
qdel(src)
return 1
/obj/effect/clockwork/sigil/submission //Sigil of Submission: After a short time, converts any non-servant standing on it. Knocks down and silences them for five seconds afterwards.
name = "ominous sigil"
desc = "A luminous golden sigil. Something about it really bothers you."
clockwork_desc = "A sigil that will enslave the first person to cross it, provided they remain on it for five seconds."
icon_state = "sigilsubmission"
color = "#FAE48C"
alpha = 125
stat_affected = UNCONSCIOUS
var/convert_time = 50
var/glow_light = 2 //soft light
var/glow_falloff = 1
var/delete_on_finish = TRUE
var/sigil_name = "Sigil of Submission"
var/glow_type
/obj/effect/clockwork/sigil/submission/New()
..()
SetLuminosity(glow_light,glow_falloff)
/obj/effect/clockwork/sigil/submission/proc/post_channel(mob/living/L)
/obj/effect/clockwork/sigil/submission/sigil_effects(mob/living/L)
visible_message("<span class='warning'>[src] begins to glow a piercing magenta!</span>")
animate(src, color = "#AF0AAF", time = convert_time)
var/obj/effect/overlay/temp/ratvar/sigil/glow
if(glow_type)
glow = PoolOrNew(glow_type, get_turf(src))
animate(glow, alpha = 255, time = convert_time)
var/I = 0
while(I < convert_time && get_turf(L) == get_turf(src))
I++
sleep(1)
if(get_turf(L) != get_turf(src))
if(glow)
qdel(glow)
animate(src, color = initial(color), time = 20)
visible_message("<span class='warning'>[src] slowly stops glowing!</span>")
return 0
post_channel(L)
if(is_eligible_servant(L))
L << "<span class='heavy_brass'>\"You belong to me now.\"</span>"
add_servant_of_ratvar(L)
L.Weaken(3) //Completely defenseless for about five seconds - mainly to give them time to read over the information they've just been presented with
L.Stun(3)
if(iscarbon(L))
var/mob/living/carbon/C = L
C.silent += 5
var/message = "[sigil_name] in [get_area(src)] <span class='sevtug'>[is_servant_of_ratvar(L) ? "successfully converted" : "failed to convert"]</span>"
for(var/M in mob_list)
if(isobserver(M))
var/link = FOLLOW_LINK(M, L)
M << "[link] <span class='heavy_brass'>[message] [L.real_name]!</span>"
else if(is_servant_of_ratvar(M))
if(M == L)
M << "<span class='heavy_brass'>[message] you!</span>"
else
M << "<span class='heavy_brass'>[message] [L.real_name]!</span>"
if(delete_on_finish)
qdel(src)
else
animate(src, color = initial(color), time = 20)
visible_message("<span class='warning'>[src] slowly stops glowing!</span>")
return 1
/obj/effect/clockwork/sigil/submission/accession //Sigil of Accession: After a short time, converts any non-servant standing on it though implants. Knocks down and silences them for five seconds afterwards.
name = "terrifying sigil"
desc = "A luminous brassy sigil. Something about it makes you want to flee."
clockwork_desc = "A sigil that will enslave any person who crosses it, provided they remain on it for five seconds. \n\
It can convert a mindshielded target once before disppearing, but can convert any number of non-implanted targets."
icon_state = "sigiltransgression"
color = "#A97F1B"
alpha = 200
glow_light = 4 //bright light
glow_falloff = 3
delete_on_finish = FALSE
sigil_name = "Sigil of Accession"
glow_type = /obj/effect/overlay/temp/ratvar/sigil/accession
/obj/effect/clockwork/sigil/submission/accession/post_channel(mob/living/L)
if(isloyal(L))
delete_on_finish = TRUE
L.visible_message("<span class='warning'>[L] visibly trembles!</span>", \
"<span class='sevtug'>Lbh jvyy or zvar-naq-uvf. Guvf chal gevaxrg jvyy abg fgbc zr.</span>")
for(var/obj/item/weapon/implant/mindshield/M in L)
if(M.implanted)
qdel(M)
/obj/effect/clockwork/sigil/transmission
name = "suspicious sigil"
desc = "A glowing orange sigil. The air around it feels staticky."
clockwork_desc = "A sigil that will serve as a battery for clockwork structures. Use Volt Void while standing on it to charge it."
icon_state = "sigiltransmission"
color = "#EC8A2D"
alpha = 50
var/power_charge = 2500 //starts with 2500W by default
/obj/effect/clockwork/sigil/transmission/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
user << "<span class='[power_charge ? "brass":"alloy"]'>It is storing [power_charge]W of power.</span>"
/obj/effect/clockwork/sigil/transmission/sigil_effects(mob/living/L)
if(power_charge)
L << "<span class='brass'>You feel a slight, static shock.</span>"
return 1
/obj/effect/clockwork/sigil/transmission/New()
..()
alpha = min(initial(alpha) + power_charge*0.02, 255)
/obj/effect/clockwork/sigil/transmission/proc/modify_charge(amount)
if(power_charge - amount < 0)
return 0
power_charge -= amount
alpha = min(initial(alpha) + power_charge*0.02, 255)
return 1
/obj/effect/clockwork/sigil/vitality
name = "comforting sigil"
desc = "A faint blue sigil. Looking at it makes you feel protected."
clockwork_desc = "A sigil that will drain non-servants that remain on it. Servants that remain on it will be healed if it has any vitality drained."
icon_state = "sigilvitality"
color = "#123456"
alpha = 75
affects_servants = TRUE
stat_affected = DEAD
var/vitality = 0
var/base_revive_cost = 20
var/sigil_active = FALSE
var/animation_number = 3 //each cycle increments this by 1, at 4 it produces an animation and resets
/obj/effect/clockwork/sigil/vitality/examine(mob/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
user << "<span class='[vitality ? "inathneq_small":"alloy"]'>It is storing [vitality] units of vitality.</span>"
user << "<span class='inathneq_small'>It requires at least [base_revive_cost] units of vitality to revive dead servants, in addition to any damage the servant has.</span>"
/obj/effect/clockwork/sigil/vitality/sigil_effects(mob/living/L)
if(L.suiciding || sigil_active || !is_servant_of_ratvar(L) && L.stat == DEAD)
return 0
visible_message("<span class='warning'>[src] begins to glow bright blue!</span>")
animate(src, alpha = 255, time = 10)
sleep(10)
sigil_active = TRUE
//as long as they're still on the sigil and are either not a servant or they're a servant AND it has remaining vitality
while(L && (!is_servant_of_ratvar(L) && L.stat != DEAD || (is_servant_of_ratvar(L) && vitality)) && get_turf(L) == get_turf(src))
if(animation_number >= 4)
PoolOrNew(/obj/effect/overlay/temp/ratvar/sigil/vitality, get_turf(src))
animation_number = 0
animation_number++
if(!is_servant_of_ratvar(L))
var/vitality_drained = L.adjustToxLoss(1.5)
if(vitality_drained)
vitality += vitality_drained
else
break
else
var/clone_to_heal = L.getCloneLoss()
var/tox_to_heal = L.getToxLoss()
var/burn_to_heal = L.getFireLoss()
var/brute_to_heal = L.getBruteLoss()
var/oxy_to_heal = L.getOxyLoss()
var/total_damage = clone_to_heal + tox_to_heal + burn_to_heal + brute_to_heal + oxy_to_heal
if(L.stat == DEAD)
var/revival_cost = base_revive_cost + total_damage - oxy_to_heal //ignores oxygen damage
var/mob/dead/observer/ghost = L.get_ghost(TRUE)
if(ghost)
if(vitality >= revival_cost)
ghost.reenter_corpse()
L.revive(1, 1)
playsound(L, 'sound/magic/Staff_Healing.ogg', 50, 1)
L.visible_message("<span class='warning'>[L] suddenly gets back up, their mouth dripping blue ichor!</span>", "<span class='inathneq'>\"Lbh jvyy or bxnl, puvyq.\"</span>")
vitality -= revival_cost
break
else
break
if(!total_damage)
break
var/vitality_for_cycle = min(vitality, 3)
if(clone_to_heal && vitality_for_cycle)
var/healing = min(vitality_for_cycle, clone_to_heal)
vitality_for_cycle -= healing
L.adjustCloneLoss(-healing)
vitality -= healing
if(tox_to_heal && vitality_for_cycle)
var/healing = min(vitality_for_cycle, tox_to_heal)
vitality_for_cycle -= healing
L.adjustToxLoss(-healing)
vitality -= healing
if(burn_to_heal && vitality_for_cycle)
var/healing = min(vitality_for_cycle, burn_to_heal)
vitality_for_cycle -= healing
L.adjustFireLoss(-healing)
vitality -= healing
if(brute_to_heal && vitality_for_cycle)
var/healing = min(vitality_for_cycle, brute_to_heal)
vitality_for_cycle -= healing
L.adjustBruteLoss(-healing)
vitality -= healing
if(oxy_to_heal && vitality_for_cycle)
var/healing = min(vitality_for_cycle, oxy_to_heal)
vitality_for_cycle -= healing
L.adjustOxyLoss(-healing)
vitality -= healing
sleep(2)
animation_number = initial(animation_number)
sigil_active = FALSE
animate(src, alpha = initial(alpha), time = 20)
visible_message("<span class='warning'>[src] slowly stops glowing!</span>")
@@ -0,0 +1,178 @@
//sends messages via hierophant
/proc/send_hierophant_message(mob/user, message, name_span = "heavy_brass", message_span = "brass", user_title = "Servant")
if(!user || !message || !ticker || !ticker.mode)
return 0
var/parsed_message = "<span class='[name_span]'>[user_title ? "[user_title] ":""][findtextEx(user.name, user.real_name) ? user.name : "[user.real_name] (as [user.name])"]: </span><span class='[message_span]'>\"[message]\"</span>"
for(var/M in mob_list)
if(isobserver(M))
var/link = FOLLOW_LINK(M, user)
M << "[link] [parsed_message]"
else if(is_servant_of_ratvar(M))
M << parsed_message
return 1
//Function Call action: Calls forth a Ratvarian spear.
/datum/action/innate/function_call
name = "Function Call"
button_icon_state = "ratvarian_spear"
background_icon_state = "bg_clock"
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_CONSCIOUS
/datum/action/innate/function_call/IsAvailable()
if(!is_servant_of_ratvar(owner))
return 0
return ..()
/datum/action/innate/function_call/Activate()
if(owner.l_hand && owner.r_hand)
usr << "<span class='warning'>You need an empty to hand to call forth your spear!</span>"
return 0
owner.visible_message("<span class='warning'>A strange spear materializes in [usr]'s hands!</span>", "<span class='brass'>You call forth your spear!</span>")
var/obj/item/clockwork/ratvarian_spear/R = new(get_turf(usr))
owner.put_in_hands(R)
for(var/datum/action/innate/function_call/F in owner.actions) //Removes any bound Ratvarian spears
qdel(F)
return 1
//allows a mob to select a target to gate to
/atom/movable/proc/procure_gateway(mob/living/invoker, time_duration, gateway_uses, two_way)
var/list/possible_targets = list()
var/list/teleportnames = list()
var/list/duplicatenamecount = list()
for(var/obj/structure/clockwork/powered/clockwork_obelisk/O in all_clockwork_objects)
if(!O.Adjacent(invoker) && O != src && (O.z <= ZLEVEL_SPACEMAX)) //don't list obelisks that we're next to
var/area/A = get_area(O)
var/locname = initial(A.name)
var/resultkey = "[locname] [O.name]"
if(resultkey in teleportnames) //why the fuck did you put two obelisks in the same area
duplicatenamecount[resultkey]++
resultkey = "[resultkey] ([duplicatenamecount[resultkey]])"
else
teleportnames.Add(resultkey)
duplicatenamecount[resultkey] = 1
possible_targets[resultkey] = O
for(var/mob/living/L in living_mob_list)
if(!L.stat && is_servant_of_ratvar(L) && !L.Adjacent(invoker) && L != invoker && (L.z <= ZLEVEL_SPACEMAX)) //People right next to the invoker can't be portaled to, for obvious reasons
var/resultkey = "[L.name] ([L.real_name])"
if(resultkey in teleportnames)
duplicatenamecount[resultkey]++
resultkey = "[resultkey] ([duplicatenamecount[resultkey]])"
else
teleportnames.Add(resultkey)
duplicatenamecount[resultkey] = 1
possible_targets[resultkey] = L
if(!possible_targets.len)
invoker << "<span class='warning'>There are no other eligible targets for a Spatial Gateway!</span>"
return 0
var/input_target_key = input(invoker, "Choose a target to form a rift to.", "Spatial Gateway") as null|anything in possible_targets
var/atom/movable/target = possible_targets[input_target_key]
if(!target || !invoker.canUseTopic(src, BE_CLOSE))
return 0
var/istargetobelisk = istype(target, /obj/structure/clockwork/powered/clockwork_obelisk)
if(istargetobelisk)
gateway_uses *= 2
time_duration *= 2
invoker.visible_message("<span class='warning'>The air in front of [invoker] ripples before suddenly tearing open!</span>", \
"<span class='brass'>With a word, you rip open a [two_way ? "two-way":"one-way"] rift to [input_target_key]. It will last for [time_duration / 10] seconds and has [gateway_uses] use[gateway_uses > 1 ? "s" : ""].</span>")
var/obj/effect/clockwork/spatial_gateway/S1 = new(istype(src, /obj/structure/clockwork/powered/clockwork_obelisk) ? get_turf(src) : get_step(get_turf(invoker), invoker.dir))
var/obj/effect/clockwork/spatial_gateway/S2 = new(istargetobelisk ? get_turf(target) : get_step(get_turf(target), target.dir))
//Set up the portals now that they've spawned
S1.setup_gateway(S2, time_duration, gateway_uses, two_way)
S2.visible_message("<span class='warning'>The air in front of [target] ripples before suddenly tearing open!</span>")
return 1
/proc/scripture_unlock_check(scripture_tier) //check if the selected scripture tier is unlocked
var/servants = 0
var/unconverted_ai_exists = FALSE
for(var/mob/living/M in living_mob_list)
if(is_servant_of_ratvar(M) && (ishuman(M) || issilicon(M)))
servants++
for(var/mob/living/silicon/ai/ai in living_mob_list)
if(!is_servant_of_ratvar(ai) && ai.client)
unconverted_ai_exists = TRUE
switch(scripture_tier)
if(SCRIPTURE_DRIVER)
return 1
if(SCRIPTURE_SCRIPT)
if(servants >= 5 && clockwork_caches)
return 1 //5 or more non-brain servants and any number of clockwork caches
if(SCRIPTURE_APPLICATION)
if(servants >= 8 && clockwork_caches >= 3 && clockwork_construction_value >= 75)
return 1 //8 or more non-brain servants, 3+ clockwork caches, and at least 75 CV
if(SCRIPTURE_REVENANT)
if(servants >= 10 && clockwork_caches >= 4 && clockwork_construction_value >= 150)
return 1 //10 or more non-brain servants, 4+ clockwork caches, and at least 150 CV
if(SCRIPTURE_JUDGEMENT)
if(servants >= 12 && clockwork_caches >= 5 && clockwork_construction_value >= 250 && !unconverted_ai_exists)
return 1 //12 or more non-brain servants, 5+ clockwork caches, at least 250 CV, and there are no living, non-servant ais
return 0
/proc/generate_cache_component(specific_component_id) //generates a component in the global component cache, either random based on lowest or a specific component
if(specific_component_id)
clockwork_component_cache[specific_component_id]++
else
var/component_to_generate = get_weighted_component_id()
clockwork_component_cache[component_to_generate]++
/proc/get_weighted_component_id(obj/item/clockwork/slab/storage_slab) //returns a chosen component id based on the lowest amount of that component
if(storage_slab)
return pickweight(list("belligerent_eye" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*(clockwork_component_cache["belligerent_eye"] + storage_slab.stored_components["belligerent_eye"]), 1), \
"vanguard_cogwheel" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*(clockwork_component_cache["vanguard_cogwheel"] + storage_slab.stored_components["vanguard_cogwheel"]), 1), \
"guvax_capacitor" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*(clockwork_component_cache["guvax_capacitor"] + storage_slab.stored_components["guvax_capacitor"]), 1), \
"replicant_alloy" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*(clockwork_component_cache["replicant_alloy"] + storage_slab.stored_components["replicant_alloy"]), 1), \
"hierophant_ansible" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*(clockwork_component_cache["hierophant_ansible"] + storage_slab.stored_components["hierophant_ansible"]), 1)))
return pickweight(list("belligerent_eye" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*clockwork_component_cache["belligerent_eye"], 1), \
"vanguard_cogwheel" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*clockwork_component_cache["vanguard_cogwheel"], 1), \
"guvax_capacitor" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*clockwork_component_cache["guvax_capacitor"], 1), \
"replicant_alloy" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*clockwork_component_cache["replicant_alloy"], 1), \
"hierophant_ansible" = max(MAX_COMPONENTS_BEFORE_RAND - LOWER_PROB_PER_COMPONENT*clockwork_component_cache["hierophant_ansible"], 1)))
/proc/clockwork_say(atom/movable/AM, message, whisper=FALSE)
// When servants invoke ratvar's power, they speak in ways that non
// servants do not comprehend.
// Our ratvarian chants are stored in their ratvar forms
var/list/spans = list(SPAN_ROBOT)
var/old_languages_spoken = AM.languages_spoken
AM.languages_spoken = HUMAN //anyone who can understand HUMAN will hear weird shitty ratvar speak, otherwise it'll get starred out
if(isliving(AM))
var/mob/living/L = AM
if(!whisper)
L.say(message, "clock", spans)
else
L.whisper(message)
else
AM.say(message)
AM.languages_spoken = old_languages_spoken
/*
The Ratvarian Language
In the lore of the Servants of Ratvar, the Ratvarian tongue is a timeless language and full of power. It sounds like gibberish, much like Nar-Sie's language, but is in fact derived from
aforementioned language, and may induce miracles when spoken in the correct way with an amplifying tool (similar to runes used by the Nar-Sian cult).
While the canon states that the language of Ratvar and his servants is incomprehensible to the unenlightened as it is a derivative of the most ancient known language, in reality it is
actually very simple. To translate a plain English sentence to Ratvar's tongue, simply move all of the letters thirteen places ahead, starting from "a" if the end of the alphabet is reached.
This cipher is known as "rot13" for "rotate 13 places" and there are many sites online that allow instant translation between English and rot13 - one of the benefits is that moving the translated
sentence thirteen places ahead changes it right back to plain English.
There are, however, a few parts of the Ratvarian tongue that aren't typical and are implemented for fluff reasons. Some words may have apostrophes, hyphens, and spaces, making the plain
English translation apparent but disjoined (for instance, "Oru`byq zl-cbjre!" translates directly to "Beh'old my-power!") although this can be ignored without impacting overall quality. When
translating from Ratvar's tongue to plain English, simply remove the disjointments and use the finished sentence. This would make "Oru`byq zl-cbjre!" into "Behold my power!" after removing the
abnormal spacing, hyphens, and grave accents.
List of nuances:
- Any time the word "of" occurs, it is linked to the previous word by a hyphen. If it is the first word, nothing is done. (i.e. "V nz-bs Ratvar." directly translates to "I am-of Ratvar.")
- Although "Ratvar" translates to "Engine" in English, the word "Ratvar" is used regardless of language as it is a proper noun.
- The same rule applies to Ratvar's four generals: Nezbere (Armorer), Sevtug (Fright), Nzcrentr (Amperage), and Inath-Neq (Vangu-Ard), although these words can be used in proper context if one is
not referring to the four generals and simply using the words themselves.
*/
@@ -0,0 +1,319 @@
/obj/item/clockwork/clockwork_proselytizer //Clockwork proselytizer (yes, that's a real word): Converts applicable objects to Ratvarian variants.
name = "clockwork proselytizer"
desc = "An odd, L-shaped device that hums with energy."
clockwork_desc = "A device that allows the replacing of mundane objects with Ratvarian variants. It requires liquified Replicant Alloy to function."
icon_state = "clockwork_proselytizer"
w_class = 3
force = 5
flags = NOBLUDGEON
var/stored_alloy = 0 //Requires this to function; each chunk of replicant alloy provides REPLICANT_ALLOY_UNIT
var/max_alloy = REPLICANT_ALLOY_UNIT * 10
var/uses_alloy = TRUE
var/metal_to_alloy = FALSE
var/repairing = null //what we're currently repairing, if anything
/obj/item/clockwork/clockwork_proselytizer/preloaded
stored_alloy = REPLICANT_WALL_MINUS_FLOOR+REPLICANT_WALL_TOTAL
/obj/item/clockwork/clockwork_proselytizer/scarab
name = "scarab proselytizer"
clockwork_desc = "A cogscarab's internal proselytizer. It can only be successfully used by a cogscarab and requires liquified Replicant Alloy to function."
metal_to_alloy = TRUE
item_state = "nothing"
w_class = 1
var/debug = FALSE
/obj/item/clockwork/clockwork_proselytizer/scarab/proselytize(atom/target, mob/living/user)
if(!debug && !isdrone(user))
return 0
return ..()
/obj/item/clockwork/clockwork_proselytizer/scarab/debug
clockwork_desc = "A cogscarab's internal proselytizer. It can convert nearly any object into a Ratvarian variant."
uses_alloy = FALSE
debug = TRUE
/obj/item/clockwork/clockwork_proselytizer/examine(mob/living/user)
..()
if(is_servant_of_ratvar(user) || isobserver(user))
user << "<span class='brass'>Can be used to convert walls, floors, windows, airlocks, windoors, and grilles to clockwork variants.</span>"
user << "<span class='brass'>Can also form some objects into Replicant Alloy, as well as reform Clockwork Walls into Clockwork Floors, and vice versa.</span>"
if(metal_to_alloy)
user << "<span class='alloy'>It can convert rods, metal, and plasteel to liquified replicant alloy at a low rate.</span>"
if(uses_alloy)
user << "<span class='alloy'>It has [stored_alloy]/[max_alloy] units of liquified alloy stored.</span>"
user << "<span class='alloy'>Use it on a Tinkerer's Cache, strike it with Replicant Alloy, or attack Replicant Alloy with it to add additional liquified alloy.</span>"
user << "<span class='alloy'>Use it in-hand to remove stored liquified alloy.</span>"
/obj/item/clockwork/clockwork_proselytizer/attack_self(mob/living/user)
if(is_servant_of_ratvar(user) && uses_alloy)
if(!can_use_alloy(REPLICANT_ALLOY_UNIT))
user << "<span class='warning'>[src] [stored_alloy ? "Lacks enough":"Contains no"] alloy to reform[stored_alloy ? "":" any"] into solidified alloy!</span>"
return
modify_stored_alloy(-REPLICANT_ALLOY_UNIT)
playsound(src, 'sound/items/Deconstruct.ogg', 50, 1)
new/obj/item/clockwork/component/replicant_alloy(user.loc)
user << "<span class='brass'>You force [stored_alloy ? "some":"all"] of the alloy in [src]'s compartments to reform and solidify. \
It now contains [stored_alloy]/[max_alloy] units of liquified alloy.</span>"
/obj/item/clockwork/clockwork_proselytizer/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/clockwork/component/replicant_alloy) && is_servant_of_ratvar(user) && uses_alloy)
if(!can_use_alloy(-REPLICANT_ALLOY_UNIT))
user << "<span class='warning'>[src]'s replicant alloy compartments are full!</span>"
return 0
modify_stored_alloy(REPLICANT_ALLOY_UNIT)
playsound(user, 'sound/machines/click.ogg', 50, 1)
user << "<span class='brass'>You force [I] to liquify and pour it into [src]'s compartments. It now contains [stored_alloy]/[max_alloy] units of liquified alloy.</span>"
user.drop_item()
qdel(I)
return 1
else
return ..()
/obj/item/clockwork/clockwork_proselytizer/afterattack(atom/target, mob/living/user, proximity_flag, params)
if(!target || !user || !proximity_flag)
return 0
if(!is_servant_of_ratvar(user))
return ..()
proselytize(target, user)
/obj/item/clockwork/clockwork_proselytizer/proc/modify_stored_alloy(amount)
if(can_use_alloy(0)) //Ratvar makes it free
amount = 0
stored_alloy = Clamp(stored_alloy + amount, 0, max_alloy)
return 1
/obj/item/clockwork/clockwork_proselytizer/proc/can_use_alloy(amount)
if(ratvar_awakens || !uses_alloy)
return TRUE
if(!amount) //functions thus as a check for if ratvar is up/it doesn't use alloy if no amount is provided
return FALSE
if(stored_alloy - amount < 0)
return FALSE
if(stored_alloy - amount > max_alloy)
return FALSE
return TRUE
/obj/item/clockwork/clockwork_proselytizer/proc/proselytize(atom/target, mob/living/user)
if(!target || !user)
return 0
var/target_type = target.type
var/list/proselytize_values = target.proselytize_vals(user, src) //relevant values for proselytizing stuff, given as an associated list
if(proselytize_values == TRUE) //if we get true, fail, but don't send a message for whatever reason
return 0
if(!islist(proselytize_values))
user << "<span class='warning'>[target] cannot be proselytized!</span>"
return 0
if(repairing)
user << "<span class='warning'>You are currently repairing [repairing] with [src]!</span>"
return 0
if(!uses_alloy)
proselytize_values["alloy_cost"] = 0
if(!can_use_alloy(proselytize_values["alloy_cost"]))
if(stored_alloy - proselytize_values["alloy_cost"] < 0)
user << "<span class='warning'>You need [proselytize_values["alloy_cost"]] liquified alloy to proselytize [target]!</span>"
else if(stored_alloy - proselytize_values["alloy_cost"] > max_alloy)
user << "<span class='warning'>You have too much liquified alloy stored to proselytize [target]!</span>"
return 0
if(can_use_alloy(0)) //Ratvar makes it faster
proselytize_values["operation_time"] *= 0.5
user.visible_message("<span class='warning'>[user]'s [name] begins tearing apart [target]!</span>", "<span class='brass'>You begin proselytizing [target]...</span>")
playsound(target, 'sound/machines/click.ogg', 50, 1)
if(proselytize_values["operation_time"] && !do_after(user, proselytize_values["operation_time"], target = target))
return 0
if(!can_use_alloy(proselytize_values["alloy_cost"])) //Check again to prevent bypassing via spamclick
return 0
if(!target || target.type != target_type)
return 0
if(repairing)
return 0
user.visible_message("<span class='warning'>[user]'s [name] disgorges a chunk of metal and shapes it over what's left of [target]!</span>", \
"<span class='brass'>You proselytize [target].</span>")
playsound(target, 'sound/items/Deconstruct.ogg', 50, 1)
var/new_thing_type = proselytize_values["new_obj_type"]
if(isturf(target))
var/turf/T = target
T.ChangeTurf(new_thing_type)
else
if(proselytize_values["dir_in_new"])
new new_thing_type(get_turf(target), proselytize_values["spawn_dir"])
else
var/atom/A = new new_thing_type(get_turf(target))
A.setDir(proselytize_values["spawn_dir"])
qdel(target)
modify_stored_alloy(-proselytize_values["alloy_cost"])
return 1
//if a valid target, returns an associated list in this format;
//list("operation_time" = 15, "new_obj_type" = /obj/structure/window/reinforced/clockwork, "alloy_cost" = 5, "spawn_dir" = dir, "dir_in_new" = TRUE)
//otherwise, return literally any non-list thing but preferably FALSE
//returning TRUE won't produce the "cannot be proselytized" message and will still prevent proselytizing
/atom/proc/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
/turf/closed/wall/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return list("operation_time" = 50, "new_obj_type" = /turf/closed/wall/clockwork, "alloy_cost" = REPLICANT_WALL_TOTAL, "spawn_dir" = SOUTH)
/turf/closed/wall/r_wall/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
/turf/closed/wall/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return list("operation_time" = 50, "new_obj_type" = /turf/open/floor/clockwork, "alloy_cost" = -REPLICANT_WALL_MINUS_FLOOR, "spawn_dir" = SOUTH)
/turf/open/floor/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return list("operation_time" = 30, "new_obj_type" = /turf/open/floor/clockwork, "alloy_cost" = REPLICANT_FLOOR, "spawn_dir" = SOUTH)
/turf/open/floor/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
for(var/obj/O in src)
if(O.density && !O.CanPass(user, src, 5))
user << "<span class='warning'>Something is in the way, preventing you from proselytizing [src] into a clockwork wall.</span>"
return FALSE
return list("operation_time" = 100, "new_obj_type" = /turf/closed/wall/clockwork, "alloy_cost" = REPLICANT_WALL_MINUS_FLOOR, "spawn_dir" = SOUTH)
/obj/item/stack/rods/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
if(!proselytizer.metal_to_alloy)
return 0
var/prosel_costtime = -amount
return list("operation_time" = -prosel_costtime, "new_obj_type" = /obj/effect/overlay/temp/ratvar/beam/itemconsume, "alloy_cost" = prosel_costtime, "spawn_dir" = SOUTH)
/obj/item/stack/sheet/metal/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
if(!proselytizer.metal_to_alloy)
return 0
var/prosel_costtime = -amount*2
return list("operation_time" = -prosel_costtime, "new_obj_type" = /obj/effect/overlay/temp/ratvar/beam/itemconsume, "alloy_cost" = prosel_costtime, "spawn_dir" = SOUTH)
/obj/item/stack/sheet/plasteel/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
if(!proselytizer.metal_to_alloy)
return 0
var/prosel_costtime = -amount*3
return list("operation_time" = -prosel_costtime, "new_obj_type" = /obj/effect/overlay/temp/ratvar/beam/itemconsume, "alloy_cost" = prosel_costtime, "spawn_dir" = SOUTH)
/obj/machinery/door/airlock/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
var/doortype = /obj/machinery/door/airlock/clockwork
if(glass)
doortype = /obj/machinery/door/airlock/clockwork/brass
return list("operation_time" = 40, "new_obj_type" = doortype, "alloy_cost" = REPLICANT_WALL_TOTAL, "spawn_dir" = dir)
/obj/machinery/door/airlock/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
/obj/structure/window/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
var/windowtype = /obj/structure/window/reinforced/clockwork
var/new_dir = TRUE
var/prosel_time = 15
var/prosel_cost = REPLICANT_FLOOR
if(fulltile)
windowtype = /obj/structure/window/reinforced/clockwork/fulltile
new_dir = FALSE
prosel_time = 30
prosel_cost = REPLICANT_STANDARD
return list("operation_time" = prosel_time, "new_obj_type" = windowtype, "alloy_cost" = prosel_cost, "spawn_dir" = dir, "dir_in_new" = new_dir)
/obj/structure/window/reinforced/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
/obj/machinery/door/window/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return list("operation_time" = 30, "new_obj_type" = /obj/machinery/door/window/clockwork, "alloy_cost" = REPLICANT_STANDARD, "spawn_dir" = dir, "dir_in_new" = TRUE)
/obj/machinery/door/window/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
/obj/structure/grille/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
var/grilletype = /obj/structure/grille/ratvar
var/prosel_time = 15
if(destroyed)
grilletype = /obj/structure/grille/ratvar/broken
prosel_time = 5
return list("operation_time" = prosel_time, "new_obj_type" = grilletype, "alloy_cost" = 0, "spawn_dir" = dir)
/obj/structure/grille/ratvar/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return FALSE
/obj/structure/clockwork/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
. = TRUE
if(proselytizer.repairing) //no spamclicking for fast repairs, bucko
user << "<span class='warning'>You are already repairing [proselytizer.repairing] with [proselytizer]!</span>"
return
if(!can_be_repaired)
user << "<span class='warning'>[src] cannot be repaired with a proselytizer!</span>"
return
if(health == max_health)
user << "<span class='warning'>[src] is at maximum integrity!</span>"
return
var/amount_to_heal = max_health - health
var/healing_for_cycle = min(amount_to_heal, repair_amount)
if(!proselytizer.can_use_alloy(0))
healing_for_cycle = min(healing_for_cycle, proselytizer.stored_alloy)
var/proselytizer_cost = healing_for_cycle*2
if(!proselytizer.can_use_alloy(proselytizer_cost))
user << "<span class='warning'>You need more liquified alloy to repair [src]!</span>"
return
user.visible_message("<span class='notice'>[user]'s [proselytizer.name] starts covering [src] in black liquid metal...</span>", \
"<span class='alloy'>You start repairing [src]...</span>")
//hugeass while because we need to re-check after the do_after
proselytizer.repairing = src
while(proselytizer && user && src && health != max_health)
amount_to_heal = max_health - health
if(!amount_to_heal)
break
healing_for_cycle = min(amount_to_heal, repair_amount)
if(!proselytizer.can_use_alloy(0))
healing_for_cycle = min(healing_for_cycle, proselytizer.stored_alloy)
proselytizer_cost = healing_for_cycle*2
if(!proselytizer.can_use_alloy(proselytizer_cost) || !do_after(user, proselytizer_cost, target = src) || !proselytizer || !proselytizer.can_use_alloy(proselytizer_cost))
break
amount_to_heal = max_health - health
if(!amount_to_heal)
break
healing_for_cycle = min(amount_to_heal, repair_amount)
if(!proselytizer.can_use_alloy(0))
healing_for_cycle = min(healing_for_cycle, proselytizer.stored_alloy)
proselytizer_cost = healing_for_cycle*2
if(!proselytizer.can_use_alloy(proselytizer_cost))
break
health += healing_for_cycle
proselytizer.modify_stored_alloy(-proselytizer_cost)
playsound(src, 'sound/machines/click.ogg', 50, 1)
if(proselytizer)
proselytizer.repairing = null
if(user)
user.visible_message("<span class='notice'>[user]'s [proselytizer.name] stops covering [src] with black liquid metal.</span>", \
"<span class='alloy'>You finish repairing [src]. It is now at <b>[health]/[max_health]</b> integrity.</span>")
return
/obj/structure/clockwork/cache/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
. = ..()
if(proselytizer.can_use_alloy(0) || proselytizer.stored_alloy + REPLICANT_ALLOY_UNIT > proselytizer.max_alloy)
user << "<span class='warning'>[proselytizer]'s containers of liquified alloy are full!</span>"
return
if(!clockwork_component_cache["replicant_alloy"])
user << "<span class='warning'>There is no Replicant Alloy in the global component cache!</span>"
return
user.visible_message("<span class='notice'>[user] places the end of [proselytizer] in the hole in [src]...</span>", \
"<span class='notice'>You start filling [proselytizer] with liquified alloy...</span>")
//hugeass check because we need to re-check after the do_after
while(proselytizer && proselytizer.uses_alloy && proselytizer.stored_alloy + REPLICANT_ALLOY_UNIT <= proselytizer.max_alloy && clockwork_component_cache["replicant_alloy"] \
&& do_after(user, 10, target = src) \
&& proselytizer && proselytizer.uses_alloy && proselytizer.stored_alloy + REPLICANT_ALLOY_UNIT <= proselytizer.max_alloy && clockwork_component_cache["replicant_alloy"])
proselytizer.modify_stored_alloy(REPLICANT_ALLOY_UNIT)
clockwork_component_cache["replicant_alloy"]--
playsound(src, 'sound/items/Deconstruct.ogg', 50, 1)
if(proselytizer && user)
user.visible_message("<span class='notice'>[user] removes [proselytizer] from the hole in [src], apparently satisfied.</span>", \
"<span class='brass'>You finish filling [proselytizer] with liquified alloy. It now contains [proselytizer.stored_alloy]/[proselytizer.max_alloy] units of liquified alloy.</span>")
return
/obj/structure/clockwork/wall_gear/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return list("operation_time" = 10, "new_obj_type" = /obj/effect/overlay/temp/ratvar/beam/itemconsume, "alloy_cost" = -REPLICANT_WALL_MINUS_FLOOR, "spawn_dir" = SOUTH)
/obj/item/clockwork/alloy_shards/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return list("operation_time" = 5, "new_obj_type" = /obj/effect/overlay/temp/ratvar/beam/itemconsume, "alloy_cost" = -REPLICANT_STANDARD, "spawn_dir" = SOUTH)
/obj/item/clockwork/component/replicant_alloy/proselytize_vals(mob/living/user, obj/item/clockwork/clockwork_proselytizer/proselytizer)
return list("operation_time" = 0, "new_obj_type" = /obj/effect/overlay/temp/ratvar/beam/itemconsume, "alloy_cost" = -REPLICANT_ALLOY_UNIT, "spawn_dir" = SOUTH)
+304
View File
@@ -0,0 +1,304 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
/datum/game_mode
var/list/datum/mind/cult = list()
var/list/cult_objectives = list()
/proc/iscultist(mob/living/M)
return istype(M) && M.mind && ticker && ticker.mode && (M.mind in ticker.mode.cult)
/proc/is_sacrifice_target(datum/mind/mind)
if(ticker.mode.name == "cult")
var/datum/game_mode/cult/cult_mode = ticker.mode
if(mind == cult_mode.sacrifice_target)
return 1
return 0
/proc/is_convertable_to_cult(datum/mind/mind)
if(!istype(mind))
return 0
if(istype(mind.current, /mob/living/carbon/human) && (mind.assigned_role in list("Captain", "Chaplain")))
return 0
if(isloyal(mind.current))
return 0
if(issilicon(mind.current) || isbot(mind.current) || isdrone(mind.current))
return 0 //can't convert machines, that's ratvar's thing
if(isguardian(mind.current))
var/mob/living/simple_animal/hostile/guardian/G = mind.current
if(!iscultist(G.summoner))
return 0 //can't convert it unless the owner is converted
if(is_sacrifice_target(mind))
return 0
if(mind.enslaved_to)
return 0
if(is_servant_of_ratvar(mind.current))
return 0
return 1
/datum/game_mode/cult
name = "cult"
config_tag = "cult"
antag_flag = ROLE_CULTIST
restricted_jobs = list("Chaplain","AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel")
protected_jobs = list()
required_players = 24
required_enemies = 4
recommended_enemies = 4
enemy_minimum_age = 14
var/finished = 0
var/eldergod = 1 //for the summon god objective
var/acolytes_needed = 10 //for the survive objective
var/acolytes_survived = 0
var/datum/mind/sacrifice_target = null//The target to be sacrificed
var/list/cultists_to_cult = list() //the cultists we'll convert
/datum/game_mode/cult/announce()
world << "<B>The current game mode is - Cult!</B>"
world << "<B>Some crewmembers are attempting to start a cult!<BR>\nCultists - sacrifice your target and summon Nar-Sie at all costs. Convert crewmembers to your cause by using the convert rune, or sacrifice them and turn them into constructs. Remember - there is no you, there is only the cult.<BR>\nPersonnel - Do not let the cult succeed in its mission. Forced consumption of holy water will convert a cultist back to a Nanotrasen-sanctioned faith.</B>"
/datum/game_mode/cult/pre_setup()
cult_objectives += "sacrifice"
cult_objectives += "eldergod"
if(config.protect_roles_from_antagonist)
restricted_jobs += protected_jobs
if(config.protect_assistant_from_antagonist)
restricted_jobs += "Assistant"
//cult scaling goes here
recommended_enemies = 3 + round(num_players()/15)
for(var/cultists_number = 1 to recommended_enemies)
if(!antag_candidates.len)
break
var/datum/mind/cultist = pick(antag_candidates)
antag_candidates -= cultist
cultists_to_cult += cultist
cultist.special_role = "Cultist"
cultist.restricted_roles = restricted_jobs
log_game("[cultist.key] (ckey) has been selected as a cultist")
return (cultists_to_cult.len>=required_enemies)
/datum/game_mode/cult/proc/memorize_cult_objectives(datum/mind/cult_mind)
for(var/obj_count = 1,obj_count <= cult_objectives.len,obj_count++)
var/explanation
switch(cult_objectives[obj_count])
if("survive")
explanation = "Our knowledge must live on. Make sure at least [acolytes_needed] acolytes escape on the shuttle to spread their work on an another station."
if("sacrifice")
if(sacrifice_target)
explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role] via invoking a Sacrifice rune with them on it and three acolytes around it."
else
explanation = "Free objective."
if("eldergod")
explanation = "Summon Nar-Sie by invoking the rune 'Summon Nar-Sie' with nine acolytes on it. You must do this after sacrificing your target."
cult_mind.current << "<B>Objective #[obj_count]</B>: [explanation]"
cult_mind.memory += "<B>Objective #[obj_count]</B>: [explanation]<BR>"
/datum/game_mode/cult/post_setup()
modePlayer += cultists_to_cult
if("sacrifice" in cult_objectives)
var/list/possible_targets = get_unconvertables()
if(!possible_targets.len)
message_admins("Cult Sacrifice: Could not find unconvertable target, checking for convertable target.")
for(var/mob/living/carbon/human/player in player_list)
if(player.mind && !(player.mind in cultists_to_cult))
possible_targets += player.mind
if(possible_targets.len > 0)
sacrifice_target = pick(possible_targets)
if(!sacrifice_target)
message_admins("Cult Sacrifice: ERROR - Null target chosen!")
else
message_admins("Cult Sacrifice: Could not find unconvertable or convertable target. WELP!")
for(var/datum/mind/cult_mind in cultists_to_cult)
equip_cultist(cult_mind.current)
update_cult_icons_added(cult_mind)
cult_mind.current << "<span class='userdanger'>You are a member of the cult!</span>"
add_cultist(cult_mind, 0)
..()
/datum/game_mode/proc/equip_cultist(mob/living/carbon/human/mob,tome = 0)
if(!istype(mob))
return
if (mob.mind)
if (mob.mind.assigned_role == "Clown")
mob << "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself."
mob.dna.remove_mutation(CLOWNMUT)
if(tome)
. += cult_give_item(/obj/item/weapon/tome, mob)
else
. += cult_give_item(/obj/item/weapon/paper/talisman/supply, mob)
mob << "These will help you start the cult on this station. Use them well, and remember - you are not the only one.</span>"
/datum/game_mode/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,
"left hand" = slot_l_hand,
"right hand" = slot_r_hand,
)
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)
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
mob << "<span class='danger'>You have a [item_name] in your [where]."
mob.update_icons()
if(where == "backpack")
var/obj/item/weapon/storage/B = mob.back
B.orient2hud(mob)
B.show_to(mob)
return 1
/datum/game_mode/proc/add_cultist(datum/mind/cult_mind, stun) //BASE
if (!istype(cult_mind))
return 0
if(!(cult_mind in cult) && is_convertable_to_cult(cult_mind))
if(stun)
cult_mind.current.Paralyse(5)
cult += cult_mind
cult_mind.current.faction |= "cult"
cult_mind.current.verbs += /mob/living/proc/cult_help
var/datum/action/innate/cultcomm/C = new()
C.Grant(cult_mind.current)
update_cult_icons_added(cult_mind)
cult_mind.current.attack_log += "\[[time_stamp()]\] <span class='danger'>Has been converted to the cult!</span>"
if(jobban_isbanned(cult_mind.current, ROLE_CULTIST))
replace_jobbaned_player(cult_mind.current, ROLE_CULTIST, ROLE_CULTIST)
return 1
/datum/game_mode/cult/add_cultist(datum/mind/cult_mind, stun) //INHERIT
if (!..(cult_mind))
return
memorize_cult_objectives(cult_mind)
/datum/game_mode/proc/remove_cultist(datum/mind/cult_mind, show_message = 1, stun)
if(cult_mind in cult)
cult -= cult_mind
cult_mind.current.faction -= "cult"
cult_mind.current.verbs -= /mob/living/proc/cult_help
for(var/datum/action/innate/cultcomm/C in cult_mind.current.actions)
qdel(C)
if(stun)
cult_mind.current.Paralyse(5)
cult_mind.current << "<span class='userdanger'>An unfamiliar white light flashes through your mind, cleansing the taint of the Dark One and all your memories as its servant.</span>"
cult_mind.memory = ""
update_cult_icons_removed(cult_mind)
cult_mind.current.attack_log += "\[[time_stamp()]\] <span class='danger'>Has renounced the cult!</span>"
if(show_message)
for(var/mob/M in viewers(cult_mind.current))
M << "<span class='big'>[cult_mind.current] looks like they just reverted to their old faith!</span>"
/datum/game_mode/proc/update_cult_icons_added(datum/mind/cult_mind)
var/datum/atom_hud/antag/culthud = huds[ANTAG_HUD_CULT]
culthud.join_hud(cult_mind.current)
set_antag_hud(cult_mind.current, "cult")
/datum/game_mode/proc/update_cult_icons_removed(datum/mind/cult_mind)
var/datum/atom_hud/antag/culthud = huds[ANTAG_HUD_CULT]
culthud.leave_hud(cult_mind.current)
set_antag_hud(cult_mind.current, null)
/datum/game_mode/cult/proc/get_unconvertables()
var/list/ucs = list()
for(var/mob/living/carbon/human/player in player_list)
if(player.mind && !is_convertable_to_cult(player.mind))
ucs += player.mind
return ucs
/datum/game_mode/cult/proc/check_cult_victory()
var/cult_fail = 0
if(cult_objectives.Find("survive"))
cult_fail += check_survive() //the proc returns 1 if there are not enough cultists on the shuttle, 0 otherwise
if(cult_objectives.Find("eldergod"))
cult_fail += eldergod //1 by default, 0 if the elder god has been summoned at least once
if(cult_objectives.Find("sacrifice"))
if(sacrifice_target && !sacrificed.Find(sacrifice_target)) //if the target has been sacrificed, ignore this step. otherwise, add 1 to cult_fail
cult_fail++
return cult_fail //if any objectives aren't met, failure
/datum/game_mode/cult/proc/check_survive()
var/acolytes_survived = 0
for(var/datum/mind/cult_mind in cult)
if (cult_mind.current && cult_mind.current.stat != DEAD)
if(cult_mind.current.onCentcom() || cult_mind.current.onSyndieBase())
acolytes_survived++
if(acolytes_survived>=acolytes_needed)
return 0
else
return 1
/datum/game_mode/cult/declare_completion()
if(!check_cult_victory())
feedback_set_details("round_end_result","win - cult win")
feedback_set("round_end_result",acolytes_survived)
world << "<span class='greentext'>The cult has succeeded! Nar-sie has snuffed out another torch in the void!</span>"
else
feedback_set_details("round_end_result","loss - staff stopped the cult")
feedback_set("round_end_result",acolytes_survived)
world << "<span class='redtext'>The staff managed to stop the cult! Dark words and heresy are no match for Nanotrasen's finest!</span>"
var/text = ""
if(cult_objectives.len)
text += "<br><b>The cultists' objectives were:</b>"
for(var/obj_count=1, obj_count <= cult_objectives.len, obj_count++)
var/explanation
switch(cult_objectives[obj_count])
if("survive")
if(!check_survive())
explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. ([acolytes_survived] escaped) <span class='greenannounce'>Success!</span>"
feedback_add_details("cult_objective","cult_survive|SUCCESS|[acolytes_needed]")
else
explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. ([acolytes_survived] escaped) <span class='boldannounce'>Fail.</span>"
feedback_add_details("cult_objective","cult_survive|FAIL|[acolytes_needed]")
if("sacrifice")
if(sacrifice_target)
if(sacrifice_target in sacrificed)
explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. <span class='greenannounce'>Success!</span>"
feedback_add_details("cult_objective","cult_sacrifice|SUCCESS")
else if(sacrifice_target && sacrifice_target.current)
explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. <span class='boldannounce'>Fail.</span>"
feedback_add_details("cult_objective","cult_sacrifice|FAIL")
else
explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. <span class='boldannounce'>Fail (Gibbed).</span>"
feedback_add_details("cult_objective","cult_sacrifice|FAIL|GIBBED")
if("eldergod")
if(!eldergod)
explanation = "Summon Nar-Sie. <span class='greenannounce'>Success!</span>"
feedback_add_details("cult_objective","cult_narsie|SUCCESS")
else
explanation = "Summon Nar-Sie. <span class='boldannounce'>Fail.</span>"
feedback_add_details("cult_objective","cult_narsie|FAIL")
text += "<br><B>Objective #[obj_count]</B>: [explanation]"
world << text
..()
return 1
/datum/game_mode/proc/auto_declare_completion_cult()
if( cult.len || (ticker && istype(ticker.mode,/datum/game_mode/cult)) )
var/text = "<br><font size=3><b>The cultists were:</b></font>"
for(var/datum/mind/cultist in cult)
text += printplayer(cultist)
text += "<br>"
world << text
+76
View File
@@ -0,0 +1,76 @@
/datum/action/innate/cultcomm
name = "Communion"
button_icon_state = "cult_comms"
background_icon_state = "bg_demon"
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_CONSCIOUS
/datum/action/innate/cultcomm/IsAvailable()
if(!iscultist(owner))
return 0
return ..()
/datum/action/innate/cultcomm/Activate()
var/input = stripped_input(usr, "Please choose a message to tell to the other acolytes.", "Voice of Blood", "")
if(!input || !IsAvailable())
return
cultist_commune(usr, input)
return
/proc/cultist_commune(mob/living/user, message)
if(!message)
return
if(!ishuman(user))
user.say("O bidai nabora se[pick("'","`")]sma!")
else
user.whisper("O bidai nabora se[pick("'","`")]sma!")
sleep(10)
if(!user)
return
if(!ishuman(user))
user.say(message)
else
user.whisper(message)
var/my_message = "<span class='cultitalic'><b>[(ishuman(user) ? "Acolyte" : "Construct")] [user]:</b> [message]</span>"
for(var/mob/M in mob_list)
if(iscultist(M))
M << my_message
else if(M in dead_mob_list)
var/link = FOLLOW_LINK(M, user)
M << "[link] [my_message]"
log_say("[user.real_name]/[user.key] : [message]")
/mob/living/proc/cult_help()
set category = "Cultist"
set name = "How to Play Cult"
var/text = ""
text += "<center><font color='red' size=3><b><i>Tenets of the Dark One</i></b></font></center><br><br><br>"
text += "<font color='red'><b>I. SECRECY</b></font><br>Your cult is a SECRET organization. Your success DEPENDS on keeping your cult's members and locations SECRET for as long as possible. This means that your tome should be hidden \
in your bag and never brought out in public. You should never create runes where other crew might find them, and you should avoid using talismans or other cult magic with witnesses around.<br><br>"
text += "<font color='red'><b>II. TOME</b></font><br>You start with a unique talisman in your bag. This supply talisman can be used 3 times, and creates starter equipment for your cult. The most critical of the talisman's functions is \
the power to create a tome. This tome is your most important item and summoning one (in secret) is your FIRST PRIORITY. It lets you talk to fellow cultists and create runes, which in turn is essential to growing the cult's power.<br><br>"
text += "<font color='red'><b>III. RUNES</b></font><br>Runes are powerful sources of cult magic. Your tome will allow you to draw runes with your blood. Those runes, when hit with an empty hand, will attempt to \
trigger the rune's magic. Runes are essential for the cult to convert new members, create powerful minions, or call upon incredibly powerful magic. Some runes require more than one cultist to use.<br><br>"
text += "<font color='red'><b>IV. TALISMANS</b></font><br>Talismans are a mobile source of cult magic that are NECESSARY to achieve success as a cult. Your starting talisman can produce certain talismans, but you will need \
to use the -create talisman- rune (with ordinary paper on top) to get more talismans. Talismans are EXTREMELY powerful, therefore creating more talismans in a HIDDEN location should be one of your TOP PRIORITIES.<br><br>"
text += "<font color='red'><b>V. GROW THE CULT</b></font><br>There are certain basic strategies that all cultists should master. STUN talismans are the foundation of a successful cult. If you intend to convert the stunned person \
you should use cuffs or a talisman of shackling on them and remove their headset before they recover (it takes about 10 seconds to recover). If you intend to sacrifice the victim, striking them quickly and repeatedly with your tome \
will knock them out before they can recover. Sacrificed victims will their soul behind in a shard, these shards can be used on construct shells to make powerful servants for the cult. Remember you need TWO cultists standing near a \
conversion rune to convert someone. Your construct minions cannot trigger most runes, but they will count as cultists in helping you trigger more powerful runes like conversion or blood boil.<br><br>"
text += "<font color='red'><b>VI. VICTORY</b></font><br>You have two ultimate goals as a cultist, sacrifice your target, and summon Nar-Sie. Sacrificing the target involves killing that individual and then placing \
their corpse on a sacrifice rune and triggering that rune with THREE cultists. Do NOT lose the target's corpse! Only once the target is sacrificed can Nar-Sie be summoned. Summoning Nar-Sie will take nearly one minute \
just to draw the massive rune needed. Do not create the rune until your cult is ready, the crew will receive the NAME and LOCATION of anyone who attempts to create the Nar-Sie rune. Once the Nar-Sie rune is drawn \
you must gathered 9 cultists (or constructs) over the rune and then click it to bring the Dark One into this world!<br><br>"
var/datum/browser/popup = new(usr, "mind", "", 800, 600)
popup.set_content(text)
popup.open()
return 1
+360
View File
@@ -0,0 +1,360 @@
/obj/item/weapon/melee/cultblade
name = "eldritch longsword"
desc = "A sword humming with unholy energy. It glows with a dim red light."
icon_state = "cultblade"
item_state = "cultblade"
flags = CONDUCT
sharpness = IS_SHARP
w_class = 4
force = 30
throwforce = 10
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "rended")
/obj/item/weapon/melee/cultblade/attack(mob/living/target, mob/living/carbon/human/user)
if(!iscultist(user))
user.Weaken(5)
user.unEquip(src, 1)
user.visible_message("<span class='warning'>A powerful force shoves [user] away from [target]!</span>", \
"<span class='cultlarge'>\"You shouldn't play with sharp things. You'll poke someone's eye out.\"</span>")
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.apply_damage(rand(force/2, force), BRUTE, pick("l_arm", "r_arm"))
else
user.adjustBruteLoss(rand(force/2,force))
return
..()
/obj/item/weapon/melee/cultblade/pickup(mob/living/user)
..()
if(!iscultist(user))
if(!is_servant_of_ratvar(user))
user << "<span class='cultlarge'>\"I wouldn't advise that.\"</span>"
user << "<span class='warning'>An overwhelming sense of nausea overpowers you!</span>"
user.Dizzy(120)
else
user << "<span class='cultlarge'>\"One of Ratvar's toys is trying to play with things [user.gender == FEMALE ? "s" : ""]he shouldn't. Cute.\"</span>"
user << "<span class='userdanger'>A horrible force yanks at your arm!</span>"
user.emote("scream")
user.apply_damage(30, BRUTE, pick("l_arm", "r_arm"))
user.unEquip(src)
/obj/item/weapon/melee/cultblade/dagger
name = "sacrificial dagger"
desc = "A strange dagger said to be used by sinister groups for \"preparing\" a corpse before sacrificing it to their dark gods."
icon = 'icons/obj/wizard.dmi'
icon_state = "render"
w_class = 2
force = 15
throwforce = 25
embed_chance = 75
/obj/item/weapon/melee/cultblade/dagger/attack(mob/living/target, mob/living/carbon/human/user)
..()
if(iscarbon(target))
var/mob/living/carbon/C = target
C.bleed(50)
if(is_servant_of_ratvar(C) && C.reagents)
C.reagents.add_reagent("heparin", 1)
/obj/item/weapon/restraints/legcuffs/bola/cult
name = "nar'sien bola"
desc = "A strong bola, bound with dark magic. Throw it to trip and slow your victim."
icon_state = "bola_cult"
breakouttime = 45
weaken = 1
/obj/item/clothing/head/culthood
name = "ancient cultist hood"
icon_state = "culthood"
desc = "A torn, dust-caked hood. Strange letters line the inside."
flags_inv = HIDEFACE|HIDEHAIR|HIDEEARS
flags_cover = HEADCOVERSEYES
armor = list(melee = 30, bullet = 10, laser = 5,energy = 5, bomb = 0, bio = 0, rad = 0)
cold_protection = HEAD
min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT
heat_protection = HEAD
max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT
/obj/item/clothing/suit/cultrobes
name = "ancient cultist robes"
desc = "A ragged, dusty set of robes. Strange letters line the inside."
icon_state = "cultrobes"
item_state = "cultrobes"
body_parts_covered = CHEST|GROIN|LEGS|ARMS
allowed = list(/obj/item/weapon/tome,/obj/item/weapon/melee/cultblade)
armor = list(melee = 50, bullet = 30, laser = 50,energy = 20, bomb = 25, bio = 10, rad = 0)
flags_inv = HIDEJUMPSUIT
cold_protection = CHEST|GROIN|LEGS|ARMS
min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT
heat_protection = CHEST|GROIN|LEGS|ARMS
max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT
/obj/item/clothing/head/culthood/alt
name = "cultist hood"
desc = "An armored hood worn by the followers of Nar-Sie."
icon_state = "cult_hoodalt"
item_state = "cult_hoodalt"
/obj/item/clothing/suit/cultrobes/alt
name = "cultist robes"
desc = "An armored set of robes worn by the followers of Nar-Sie."
icon_state = "cultrobesalt"
item_state = "cultrobesalt"
/obj/item/clothing/head/magus
name = "magus helm"
icon_state = "magus"
item_state = "magus"
desc = "A helm worn by the followers of Nar-Sie."
flags_inv = HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDEEARS|HIDEEYES
armor = list(melee = 30, bullet = 30, laser = 30,energy = 20, bomb = 0, bio = 0, rad = 0)
flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH
/obj/item/clothing/suit/magusred
name = "magus robes"
desc = "A set of armored robes worn by the followers of Nar-Sie"
icon_state = "magusred"
item_state = "magusred"
body_parts_covered = CHEST|GROIN|LEGS|ARMS
allowed = list(/obj/item/weapon/tome,/obj/item/weapon/melee/cultblade)
armor = list(melee = 50, bullet = 30, laser = 50,energy = 20, bomb = 25, bio = 10, rad = 0)
flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT
/obj/item/clothing/head/helmet/space/hardsuit/cult
name = "nar-sien hardened helmet"
desc = "A heavily-armored helmet worn by warriors of the Nar-Sien cult. It can withstand hard vacuum."
icon_state = "cult_helmet"
item_state = "cult_helmet"
armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30)
brightness_on = 0
actions_types = list()
/obj/item/clothing/suit/space/hardsuit/cult
name = "nar-sien hardened armor"
icon_state = "cult_armor"
item_state = "cult_armor"
desc = "A heavily-armored exosuit worn by warriors of the Nar-Sien cult. It can withstand hard vacuum."
w_class = 2
allowed = list(/obj/item/weapon/tome,/obj/item/weapon/melee/cultblade,/obj/item/weapon/tank/internals/)
armor = list(melee = 70, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30)
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/cult
/obj/item/weapon/sharpener/cult
name = "eldritch whetstone"
desc = "A block, empowered by dark magic. Sharp weapons will be enhanced when used on the stone."
used = 0
increment = 5
max = 40
prefix = "darkened"
/obj/item/clothing/suit/hooded/cultrobes/cult_shield
name = "empowered cultist armor"
desc = "Empowered garb which creates a powerful shield around the user."
icon_state = "cult_armor"
item_state = "cult_armor"
w_class = 4
armor = list(melee = 50, bullet = 40, laser = 50,energy = 30, bomb = 50, bio = 30, rad = 30)
body_parts_covered = CHEST|GROIN|LEGS|ARMS
allowed = list(/obj/item/weapon/tome,/obj/item/weapon/melee/cultblade)
var/current_charges = 3
hooded = 1
hoodtype = /obj/item/clothing/head/cult_hoodie
/obj/item/clothing/head/cult_hoodie
name = "empowered cultist armor"
desc = "Empowered garb which creates a powerful shield around the user."
icon_state = "cult_hoodalt"
armor = list(melee = 50, bullet = 40, laser = 50,energy = 30, bomb = 50, bio = 30, rad = 30)
body_parts_covered = HEAD
flags = NODROP
flags_inv = HIDEHAIR|HIDEFACE|HIDEEARS
/obj/item/clothing/suit/hooded/cultrobes/cult_shield/equipped(mob/living/user, slot)
..()
if(!iscultist(user))
if(!is_servant_of_ratvar(user))
user << "<span class='cultlarge'>\"I wouldn't advise that.\"</span>"
user << "<span class='warning'>An overwhelming sense of nausea overpowers you!</span>"
user.unEquip(src, 1)
user.Dizzy(30)
user.Weaken(5)
else
user << "<span class='cultlarge'>\"Putting on things you don't own is bad, you know.\"</span>"
user << "<span class='userdanger'>The armor squeezes at your body!</span>"
user.emote("scream")
user.adjustBruteLoss(25)
user.unEquip(src, 1)
/obj/item/clothing/suit/hooded/cultrobes/cult_shield/hit_reaction(mob/living/carbon/human/owner, attack_text, isinhands)
if(current_charges)
owner.visible_message("<span class='danger'>\The [attack_text] is deflected in a burst of blood-red sparks!</span>")
current_charges--
PoolOrNew(/obj/effect/overlay/temp/cult/sparks, get_turf(owner))
if(!current_charges)
owner.visible_message("<span class='danger'>The runed shield around [owner] suddenly disappears!</span>")
owner.update_inv_wear_suit()
return 1
return 0
/obj/item/clothing/suit/hooded/cultrobes/cult_shield/worn_overlays(isinhands)
. = list()
if(!isinhands && current_charges)
. += image(layer = MOB_LAYER+0.01, icon = 'icons/effects/effects.dmi', icon_state = "shield-cult")
/obj/item/clothing/suit/hooded/cultrobes/berserker
name = "flagellant's robes"
desc = "Blood-soaked robes infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage."
icon_state = "cultrobes"
item_state = "cultrobes"
flags_inv = HIDEJUMPSUIT
allowed = list(/obj/item/weapon/tome,/obj/item/weapon/melee/cultblade)
body_parts_covered = CHEST|GROIN|LEGS|ARMS
armor = list(melee = -100, bullet = -100, laser = -100,energy = -100, bomb = -100, bio = -100, rad = -100)
slowdown = -1
hooded = 1
hoodtype = /obj/item/clothing/head/berserkerhood
/obj/item/clothing/head/berserkerhood
name = "flagellant's robes"
desc = "Blood-soaked garb infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage."
icon_state = "culthood"
body_parts_covered = HEAD
flags = NODROP
flags_inv = HIDEHAIR|HIDEFACE|HIDEEARS
armor = list(melee = -100, bullet = -100, laser = -100,energy = -100, bomb = -100, bio = -100, rad = -100)
/obj/item/clothing/suit/hooded/cultrobes/berserker/equipped(mob/living/user, slot)
..()
if(!iscultist(user))
if(!is_servant_of_ratvar(user))
user << "<span class='cultlarge'>\"I wouldn't advise that.\"</span>"
user << "<span class='warning'>An overwhelming sense of nausea overpowers you!</span>"
user.unEquip(src, 1)
user.Dizzy(30)
user.Weaken(5)
else
user << "<span class='cultlarge'>\"Putting on things you don't own is bad, you know.\"</span>"
user << "<span class='userdanger'>The robes squeeze at your body!</span>"
user.emote("scream")
user.adjustBruteLoss(25)
user.unEquip(src, 1)
/obj/item/clothing/glasses/night/cultblind
desc = "May nar-sie guide you through the darkness and shield you from the light."
name = "zealot's blindfold"
icon_state = "blindfold"
item_state = "blindfold"
darkness_view = 8
flash_protect = 1
/obj/item/clothing/glasses/night/cultblind/equipped(mob/user, slot)
..()
if(!iscultist(user))
user << "<span class='cultlarge'>\"You want to be blind, do you?\"</span>"
user.unEquip(src, 1)
user.Dizzy(30)
user.Weaken(5)
user.blind_eyes(30)
/obj/item/weapon/reagent_containers/food/drinks/bottle/unholywater
name = "flask of unholy water"
desc = "Toxic to nonbelievers; this water renews and reinvigorates the faithful of nar'sie."
icon_state = "holyflask"
color = "#333333"
list_reagents = list("unholywater" = 40)
/obj/item/device/shuttle_curse
name = "cursed orb"
desc = "You peer within this smokey orb and glimpse terrible fates befalling the escape shuttle."
icon = 'icons/obj/cult.dmi'
icon_state ="shuttlecurse"
var/global/curselimit = 0
/obj/item/device/shuttle_curse/attack_self(mob/user)
if(!iscultist(user))
user.unEquip(src, 1)
user.Weaken(5)
user << "<span class='warning'>A powerful force shoves you away from [src]!</span>"
return
if(curselimit > 1)
user << "<span class='notice'>We have exhausted our ability to curse the shuttle.</span>"
return
if(SSshuttle.emergency.mode == SHUTTLE_CALL)
var/cursetime = 1500
var/timer = SSshuttle.emergency.timeLeft(1) + cursetime
SSshuttle.emergency.setTimer(timer)
user << "<span class='danger'>You shatter the orb! A dark essence spirals into the air, then disappears.</span>"
playsound(user.loc, "sound/effects/Glassbr1.ogg", 50, 1)
qdel(src)
sleep(20)
var/global/list/curses
if(!curses)
curses = list("A fuel technician just slit his own throat and begged for death. The shuttle will be delayed by two minutes.",
"The shuttle's navigation programming was replaced by a file containing two words, IT COMES. The shuttle will be delayed by two minutes.",
"The shuttle's custodian tore out his guts and began painting strange shapes on the floor. The shuttle will be delayed by two minutes.",
"A shuttle engineer began screaming 'DEATH IS NOT THE END' and ripped out wires until an arc flash seared off her flesh. The shuttle will be delayed by two minutes.",
"A shuttle inspector started laughing madly over the radio and then threw herself into an engine turbine. The shuttle will be delayed by two minutes.",
"The shuttle dispatcher was found dead with bloody symbols carved into their flesh. The shuttle will be delayed by two minutes.")
var/message = pick_n_take(curses)
priority_announce("[message]", "System Failure", 'sound/misc/notice1.ogg')
curselimit++
/obj/item/device/cult_shift
name = "veil shifter"
desc = "This relic teleports you forward a medium distance."
icon = 'icons/obj/cult.dmi'
icon_state ="shifter"
var/uses = 2
/obj/item/device/cult_shift/examine(mob/user)
..()
if(uses)
user << "<span class='cult'>It has [uses] uses remaining.</span>"
else
user << "<span class='cult'>It seems drained.</span>"
/obj/item/device/cult_shift/proc/handle_teleport_grab(turf/T, mob/user)
var/mob/living/carbon/C = user
if(C.pulling)
var/atom/movable/pulled = C.pulling
pulled.forceMove(T)
. = pulled
/obj/item/device/cult_shift/attack_self(mob/user)
if(!uses || !iscarbon(user))
user << "<span class='warning'>\The [src] is dull and unmoving in your hands.</span>"
return
if(!iscultist(user))
user.unEquip(src, 1)
step(src, pick(alldirs))
user << "<span class='warning'>\The [src] flickers out of your hands, too eager to move!</span>"
return
var/mob/living/carbon/C = user
var/turf/mobloc = get_turf(C)
var/turf/destination = get_teleport_loc(mobloc,C,9,1,3,1,0,1)
if(destination)
uses--
if(uses <= 0)
icon_state ="shifter_drained"
playsound(mobloc, "sparks", 50, 1)
PoolOrNew(/obj/effect/overlay/temp/cult/phase/out, list(mobloc, C.dir))
var/atom/movable/pulled = handle_teleport_grab(destination, C)
C.forceMove(destination)
if(pulled)
C.start_pulling(pulled) //forcemove resets pulls, so we need to re-pull
PoolOrNew(/obj/effect/overlay/temp/cult/phase, list(destination, C.dir))
playsound(destination, 'sound/effects/phasein.ogg', 25, 1)
playsound(destination, "sparks", 50, 1)
else
C << "<span class='danger'>The veil cannot be torn here!</span>"
+201
View File
@@ -0,0 +1,201 @@
/obj/structure/cult
density = 1
anchored = 1
icon = 'icons/obj/cult.dmi'
var/cooldowntime = 0
var/health = 100
var/maxhealth = 100
/obj/structure/cult/examine(mob/user)
..()
user << "<span class='notice'>\The [src] is [anchored ? "":"not "]secured to the floor.</span>"
if(iscultist(user) && cooldowntime > world.time)
user << "<span class='cultitalic'>The magic in [src] is weak, it will be ready to use again in [getETA()].</span>"
/obj/structure/cult/attackby(obj/I, mob/user, params)
if(istype(I, /obj/item/weapon/tome) && iscultist(user))
anchored = !anchored
user << "<span class='notice'>You [anchored ? "":"un"]secure \the [src] [anchored ? "to":"from"] the floor.</span>"
if(!anchored)
icon_state = "[initial(icon_state)]_off"
else
icon_state = initial(icon_state)
else
return ..()
/obj/structure/cult/proc/getETA()
var/time = (cooldowntime - world.time)/600
var/eta = "[round(time, 1)] minutes"
if(time <= 1)
time = (cooldowntime - world.time)*0.1
eta = "[round(time, 1)] seconds"
return eta
/obj/structure/cult/talisman
name = "altar"
desc = "A bloodstained altar dedicated to Nar-Sie."
icon_state = "talismanaltar"
/obj/structure/cult/talisman/attack_hand(mob/living/user)
if(!iscultist(user))
user << "<span class='warning'>You're pretty sure you know exactly what this is used for and you can't seem to touch it.</span>"
return
if(!anchored)
user << "<span class='cultitalic'>You need to anchor [src] to the floor with a tome first.</span>"
return
if(cooldowntime > world.time)
user << "<span class='cultitalic'>The magic in [src] is weak, it will be ready to use again in [getETA()].</span>"
return
var/choice = alert(user,"You study the schematics etched into the forge...",,"Eldritch Whetstone","Zealot's Blindfold","Flask of Unholy Water")
var/pickedtype
switch(choice)
if("Eldritch Whetstone")
pickedtype = /obj/item/weapon/sharpener/cult
if("Zealot's Blindfold")
pickedtype = /obj/item/clothing/glasses/night/cultblind
if("Flask of Unholy Water")
pickedtype = /obj/item/weapon/reagent_containers/food/drinks/bottle/unholywater
if(src && !qdeleted(src) && anchored && pickedtype && Adjacent(user) && !user.incapacitated() && iscultist(user) && cooldowntime <= world.time)
cooldowntime = world.time + 2400
var/obj/item/N = new pickedtype(get_turf(src))
user << "<span class='cultitalic'>You kneel before the altar and your faith is rewarded with an [N]!</span>"
/obj/structure/cult/forge
name = "daemon forge"
desc = "A forge used in crafting the unholy weapons used by the armies of Nar-Sie."
icon_state = "forge"
luminosity = 3
/obj/structure/cult/forge/attack_hand(mob/living/user)
if(!iscultist(user))
user << "<span class='warning'>The heat radiating from [src] pushes you back.</span>"
return
if(!anchored)
user << "<span class='cultitalic'>You need to anchor [src] to the floor with a tome first.</span>"
return
if(cooldowntime > world.time)
user << "<span class='cultitalic'>The magic in [src] is weak, it will be ready to use again in [getETA()].</span>"
return
var/choice = alert(user,"You study the schematics etched into the forge...",,"Shielded Robe","Flagellant's Robe","Nar-Sien Hardsuit")
var/pickedtype
switch(choice)
if("Shielded Robe")
pickedtype = /obj/item/clothing/suit/hooded/cultrobes/cult_shield
if("Flagellant's Robe")
pickedtype = /obj/item/clothing/suit/hooded/cultrobes/berserker
if("Nar-Sien Hardsuit")
pickedtype = /obj/item/clothing/suit/space/hardsuit/cult
if(src && !qdeleted(src) && anchored && pickedtype && Adjacent(user) && !user.incapacitated() && iscultist(user) && cooldowntime <= world.time)
cooldowntime = world.time + 2400
var/obj/item/N = new pickedtype(get_turf(src))
user << "<span class='cultitalic'>You work the forge as dark knowledge guides your hands, creating [N]!</span>"
var/list/blacklisted_pylon_turfs = typecacheof(list(
/turf/closed,
/turf/open/floor/engine/cult,
/turf/open/space,
/turf/open/floor/plating/lava,
/turf/open/chasm))
/obj/structure/cult/pylon
name = "pylon"
desc = "A floating crystal that slowly heals those faithful to Nar'Sie."
icon_state = "pylon"
luminosity = 5
var/heal_delay = 25
var/last_heal = 0
var/corrupt_delay = 50
var/last_corrupt = 0
/obj/structure/cult/pylon/New()
START_PROCESSING(SSfastprocess, src)
..()
/obj/structure/cult/pylon/Destroy()
STOP_PROCESSING(SSfastprocess, src)
return ..()
/obj/structure/cult/pylon/process()
if(!anchored)
return
if(last_heal <= world.time)
last_heal = world.time + heal_delay
for(var/mob/living/L in range(5, src))
if(iscultist(L) || isshade(L) || isconstruct(L))
if(L.health != L.maxHealth)
PoolOrNew(/obj/effect/overlay/temp/heal, list(get_turf(src), "#960000"))
if(ishuman(L))
L.adjustBruteLoss(-1, 0)
L.adjustFireLoss(-1, 0)
L.updatehealth()
if(isshade(L) || isconstruct(L))
var/mob/living/simple_animal/M = L
if(M.health < M.maxHealth)
M.adjustHealth(-1)
CHECK_TICK
if(last_corrupt <= world.time)
var/list/validturfs = list()
var/list/cultturfs = list()
for(var/T in circleviewturfs(src, 5))
if(istype(T, /turf/open/floor/engine/cult))
cultturfs |= T
continue
if(is_type_in_typecache(T, blacklisted_pylon_turfs))
continue
else
validturfs |= T
last_corrupt = world.time + corrupt_delay
var/turf/T = safepick(validturfs)
if(T)
T.ChangeTurf(/turf/open/floor/engine/cult)
else
var/turf/open/floor/engine/cult/F = safepick(cultturfs)
if(F)
PoolOrNew(/obj/effect/overlay/temp/cult/turf/open/floor, F)
else
// Are we in space or something? No cult turfs or
// convertable turfs?
last_corrupt = world.time + corrupt_delay*2
/obj/structure/cult/tome
name = "archives"
desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl."
icon_state = "tomealtar"
luminosity = 1
/obj/structure/cult/tome/attack_hand(mob/living/user)
if(!iscultist(user))
user << "<span class='warning'>All of these books seem to be gibberish.</span>"
return
if(!anchored)
user << "<span class='cultitalic'>You need to anchor [src] to the floor with a tome first.</span>"
return
if(cooldowntime > world.time)
user << "<span class='cultitalic'>The magic in [src] is weak, it will be ready to use again in [getETA()].</span>"
return
var/choice = alert(user,"You flip through the black pages of the archives...",,"Supply Talisman","Shuttle Curse","Veil Shifter")
var/pickedtype
switch(choice)
if("Supply Talisman")
pickedtype = /obj/item/weapon/paper/talisman/supply/weak
if("Shuttle Curse")
pickedtype = /obj/item/device/shuttle_curse
if("Veil Shifter")
pickedtype = /obj/item/device/cult_shift
if(src && !qdeleted(src) && anchored && pickedtype && Adjacent(user) && !user.incapacitated() && iscultist(user) && cooldowntime <= world.time)
cooldowntime = world.time + 2400
var/obj/item/N = new pickedtype(get_turf(src))
user << "<span class='cultitalic'>You summon [N] from the archives!</span>"
/obj/effect/gateway
name = "gateway"
desc = "You're pretty sure that abyss is staring back."
icon = 'icons/obj/cult.dmi'
icon_state = "hole"
density = 1
unacidable = 1
anchored = 1
+268
View File
@@ -0,0 +1,268 @@
/*
This file contains the arcane tome files.
*/
/obj/item/weapon/tome
name = "arcane tome"
desc = "An old, dusty tome with frayed edges and a sinister-looking cover."
icon_state ="tome"
throw_speed = 2
throw_range = 5
w_class = 2
/obj/item/weapon/tome/examine(mob/user)
..()
if(iscultist(user) || isobserver(user))
user << "<span class='cult'>The scriptures of the Geometer. Allows the scribing of runes and access to the knowledge archives of the cult of Nar-Sie.</span>"
user << "<span class='cult'>Striking a cult structure will unanchor or reanchor it.</span>"
user << "<span class='cult'>Striking another cultist with it will purge holy water from them.</span>"
user << "<span class='cult'>Striking a noncultist, however, will sear their flesh.</span>"
/obj/item/weapon/tome/attack(mob/living/M, mob/living/user)
if(!istype(M))
return
if(!iscultist(user))
return ..()
if(iscultist(M))
if(M.reagents && M.reagents.has_reagent("holywater")) //allows cultists to be rescued from the clutches of ordained religion
user << "<span class='cult'>You remove the taint from [M].</span>"
var/holy2unholy = M.reagents.get_reagent_amount("holywater")
M.reagents.del_reagent("holywater")
M.reagents.add_reagent("unholywater",holy2unholy)
add_logs(user, M, "smacked", src, " removing the holy water from them")
return
M.take_organ_damage(0, 15) //Used to be a random between 5 and 20
playsound(M, 'sound/weapons/sear.ogg', 50, 1)
M.visible_message("<span class='danger'>[user] strikes [M] with the arcane tome!</span>", \
"<span class='userdanger'>[user] strikes you with the tome, searing your flesh!</span>")
flick("tome_attack", src)
user.do_attack_animation(M)
add_logs(user, M, "smacked", src)
/obj/item/weapon/tome/attack_self(mob/user)
if(!iscultist(user))
user << "<span class='warning'>[src] seems full of unintelligible shapes, scribbles, and notes. Is this some sort of joke?</span>"
return
open_tome(user)
/obj/item/weapon/tome/proc/open_tome(mob/user)
var/choice = alert(user,"You open the tome...",,"Scribe Rune","More Information","Cancel")
switch(choice)
if("More Information")
read_tome(user)
if("Scribe Rune")
scribe_rune(user)
if("Cancel")
return
/obj/item/weapon/tome/proc/read_tome(mob/user)
var/text = ""
text += "<center><font color='red' size=3><b><i>Archives of the Dark One</i></b></font></center><br><br><br>"
text += "A rune's name and effects can be revealed by examining the rune.<<br><br>"
text += "<font color='red'><b>Create Talisman</b></font><br>This rune is one of the most important runes the cult has, being the only way to create new talismans. A blank sheet of paper must be on top of the rune. After \
invoking it and choosing which talisman you desire, the paper will be converted, after some delay into a talisman.<br><br>"
text += "<font color='red'><b>Teleport</b></font><br>This rune is unique in that it requires a keyword before the scribing can begin. When invoked, it will find any other Teleport runes; \
If any are found, the user can choose which rune to send to. Upon activation, the rune teleports everything above it to the selected rune.<br><br>"
text += "<font color='red'><b>Convert</b></font><br>This rune is critical to the success of the cult. It will allow you to convert normal crew members into cultists. \
To do this, simply place the crew member upon the rune and invoke it. This rune requires two invokers to use. If the target to be converted is mindshield-implanted or a certain assignment, they will \
be unable to be converted. People the Geometer wishes sacrificed will also be ineligible for conversion, and anyone with a shielding presence like the null rod will not be converted.<br> \
Successful conversions will produce a tome for the new cultist.<br><br>"
text += "<font color='red'><b>Sacrifice</b></font><br><b>This rune is necessary to achieve your goals.</b> Simply place any dead creature upon the rune and invoke it (this will not \
target cultists!). If this creature has a mind, a soulstone will be created and the creature's soul transported to it. Sacrificing the dead can be done alone, but sacrificing living crew <b>or your cult's target</b> will require 3 cultists. \
Soulstones used on construct shells will move that soul into a powerful construct of your choice.<br><br>"
text += "<font color='red'><b>Raise Dead</b></font><br>This rune requires two corpses. To perform the ritual, place the corpse you wish to revive onto \
the rune and the offering body adjacent to it. When the rune is invoked, the body to be sacrificed will turn to dust, the life force flowing into the revival target. Assuming the target is not moved \
within a few seconds, they will be brought back to life, healed of all ailments.<br><br>"
text += "<font color='red'><b>Electromagnetic Disruption</b></font><br>Robotic lifeforms have time and time again been the downfall of fledgling cults. This rune may allow you to gain the upper \
hand against these pests. By using the rune, a large electromagnetic pulse will be emitted from the rune's location. The size of the EMP will grow significantly for each additional adjacent cultist when the \
rune is activated.<br><br>"
text += "<font color='red'><b>Astral Communion</b></font><br>This rune is perhaps the most ingenious rune that is usable by a single person. Upon invoking the rune, the \
user's spirit will be ripped from their body. In this state, the user's physical body will be locked in place to the rune itself - any attempts to move it will result in the rune pulling it back. \
The body will also take constant damage while in this form, and may even die. The user's spirit will contain their consciousness, and will allow them to freely wander the station as a ghost. This may \
also be used to commune with the dead.<br><br>"
text += "<font color='red'><b>Form Barrier</b></font><br>While simple, this rune serves an important purpose in defense and hindering passage. When invoked, the \
rune will draw a small amount of life force from the user and make the space above the rune completely dense, rendering it impassable to all but the most complex means. The rune may be invoked again to \
undo this effect and allow passage again.<br><br>"
text += "<font color='red'><b>Summon Cultist</b></font><br>This rune allows the cult to free other cultists with ease. When invoked, it will allow the user to summon a single cultist to the rune from \
any location. It requires two invokers, and will damage each invoker slightly.<br><br>"
text += "<font color='red'><b>Blood Boil</b></font><br>When invoked, this rune will do a massive amount of damage to all non-cultist viewers, but it will also emit a small explosion upon invocation. \
It requires three invokers.<br><br>"
text += "<font color='red'><b>Manifest Spirit</b></font><br>This rune allows you to summon spirits as humanoid fighters. When invoked, a spirit above the rune will be brought to life as a human, wearing nothing, that seeks only to serve you and the Geometer. \
However, the spirit's link to reality is fragile - you must remain on top of the rune, and you will slowly take damage. Upon stepping off the rune, all summoned spirits will dissipate, dropping their items to the ground. You may manifest \
multiple spirits with one rune, but you will rapidly take damage in doing so.<br><br>"
text += "<font color='red'><b><i>Summon Nar-Sie</i></b></font><br><b>This rune is necessary to achieve your goals.</b> On attempting to scribe it, it will produce shields around you and alert everyone you are attempting to scribe it; it takes a very long time to scribe, \
and does massive damage to the one attempting to scribe it.<br>Invoking it requires 9 invokers and the sacrifice of a specific crewmember, and once invoked, will summon the Geometer, Nar-Sie herself. \
This will complete your objectives.<br><br><br>"
text += "<font color='red'><b>Talisman of Teleportation</b></font><br>The talisman form of the Teleport rune will transport the invoker to a selected Teleport rune once.<br><br>"
text += "<font color='red'><b>Talisman of Construction</b></font><br>This talisman is the main way of creating construct shells. To use it, one must strike 30 sheets of metal with the talisman. The sheets will then be twisted into a construct shell, ready to recieve a soul to occupy it.<br><br>"
text += "<font color='red'><b>Talisman of Tome Summoning</b></font><br>This talisman will produce a single tome at your feet.<br><br>"
text += "<font color='red'><b>Talisman of Veiling/Revealing</b></font><br>This talisman will hide runes on its first use, and on the second, will reveal runes.<br><br>"
text += "<font color='red'><b>Talisman of Disguising</b></font><br>This talisman will permanently disguise all nearby runes as crayon runes.<br><br>"
text += "<font color='red'><b>Talisman of Electromagnetic Pulse</b></font><br>This talisman will EMP anything else nearby. It disappears after one use.<br><br>"
text += "<font color='red'><b>Talisman of Stunning</b></font><br>Attacking a target will knock them down for a long duration in addition to inhibiting their speech. \
Robotic lifeforms will suffer the effects of a heavy electromagnetic pulse instead.<br><br>"
text += "<font color='red'><b>Talisman of Armaments</b></font><br>The Talisman of Arming will equip the user with armored robes, a backpack, an eldritch longsword, an empowered bola, and a pair of boots. Any items that cannot \
be equipped will not be summoned. Attacking a fellow cultist with it will instead equip them.<br><br>"
text += "<font color='red'><b>Talisman of Horrors</b></font><br>The Talisman of Horror must be applied directly to the victim, it will shatter your victim's mind with visions of the endtimes that may incapitate them.<br><br>"
text += "<font color='red'><b>Talisman of Shackling</b></font><br>The Talisman of Shackling must be applied directly to the victim, it has 4 uses and cuffs victims with magic shackles that disappear when removed.<br><br>"
text += "In addition to these runes, the cult has a small selection of equipment and constructs.<br><br>"
text += "<font color='red'><b>Equipment:</b></font><br><br>"
text += "<font color='red'><b>Cult Blade</b></font><br>Cult blades are sharp weapons that, notably, cannot be used by noncultists. These blades are produced by the Talisman of Arming.<br><br>"
text += "<font color='red'><b>Cult Bola</b></font><br>Cult bolas are strong bolas, useful for snaring targets. These bolas are produced by the Talisman of Arming.<br><br>"
text += "<font color='red'><b>Cult Robes</b></font><br>Cult robes are heavily armored robes. These robes are produced by the Talisman of Arming.<br><br>"
text += "<font color='red'><b>Soulstone</b></font><br>A soulstone is a simple piece of magic, produced either via the starter talisman or by sacrificing humans. Using it on an unconscious or dead human, or on a Shade, will trap their soul in the stone, allowing its use in construct shells. \
<br>The soul within can also be released as a Shade by using it in-hand.<br><br>"
text += "<font color='red'><b>Construct Shell</b></font><br>A construct shell is useless on its own, but placing a filled soulstone within it allows you to produce your choice of a <b>Wraith</b>, a <b>Juggernaut</b>, or an <b>Artificer</b>. \
<br>Each construct has uses, detailed below in Constructs. Construct shells can be produced via the starter talisman or the Rite of Fabrication.<br><br>"
text += "<font color='red'><b>Constructs:</b></font><br><br>"
text += "<font color='red'><b>Shade</b></font><br>While technically not a construct, the Shade is produced when released from a soulstone. It is quite fragile and has weak melee attacks, but is fully healed when recaptured by a soulstone.<br><br>"
text += "<font color='red'><b>Wraith</b></font><br>The Wraith is a fast, lethal melee attacker which can jaunt through walls. However, it is only slightly more durable than a shade.<br><br>"
text += "<font color='red'><b>Juggernaut</b></font><br>The Juggernaut is a slow, but durable, melee attacker which can produce temporary forcewalls. It will also reflect most lethal energy weapons.<br><br>"
text += "<font color='red'><b>Artificer</b></font><br>The Artificer is a weak and fragile construct, able to heal other constructs, shades, or itself, produce more <font color='red'><b>soulstones</b></font> and <font color='red'><b>construct shells</b></font>, \
construct fortifying cult walls and flooring, and finally, it can release a few indiscriminate stunning missiles.<br><br>"
text += "<font color='red'><b>Harvester</b></font><br>If you see one, know that you have done all you can and your life is void.<br><br>"
var/datum/browser/popup = new(user, "tome", "", 800, 600)
popup.set_content(text)
popup.open()
return 1
/obj/item/weapon/tome/proc/scribe_rune(mob/living/user)
var/turf/Turf = get_turf(user)
var/chosen_keyword
var/obj/effect/rune/rune_to_scribe
var/entered_rune_name
var/list/possible_runes = list()
var/list/shields = list()
var/area/A = get_area(src)
if(locate(/obj/effect/rune) in Turf)
user << "<span class='cult'>There is already a rune here.</span>"
return
if(Turf.z != ZLEVEL_STATION && Turf.z != ZLEVEL_MINING)
user << "<span class='warning'>The veil is not weak enough here."
return
if(istype(A, /area/shuttle))
user << "<span class='warning'>Interference from hyperspace engines disrupts the Geometer's power on shuttles.</span>"
for(var/T in subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed)
var/obj/effect/rune/R = T
if(initial(R.cultist_name))
possible_runes.Add(initial(R.cultist_name)) //This is to allow the menu to let cultists select runes by name rather than by object path. I don't know a better way to do this
if(!possible_runes.len)
return
entered_rune_name = input(user, "Choose a rite to scribe.", "Sigils of Power") as null|anything in possible_runes
if(!Adjacent(user) || !src || qdeleted(src) || user.incapacitated())
return
if(istype(Turf, /turf/open/space))
user << "<span class='warning'>You cannot scribe runes in space!</span>"
return
for(var/T in typesof(/obj/effect/rune))
var/obj/effect/rune/R = T
if(initial(R.cultist_name) == entered_rune_name)
rune_to_scribe = R
if(initial(R.req_keyword))
var/the_keyword = stripped_input(usr, "Please enter a keyword for the rune.", "Enter Keyword", "")
if(!the_keyword)
return
chosen_keyword = the_keyword
break
if(!rune_to_scribe)
return
Turf = get_turf(user) //we may have moved. adjust as needed...
if(locate(/obj/effect/rune) in Turf)
user << "<span class='cult'>There is already a rune here.</span>"
return
if(!Adjacent(user) || !src || qdeleted(src) || user.incapacitated())
return
if(ispath(rune_to_scribe, /obj/effect/rune/narsie))
if(ticker.mode.name == "cult")
var/datum/game_mode/cult/cult_mode = ticker.mode
if(!("eldergod" in cult_mode.cult_objectives))
user << "<span class='warning'>Nar-Sie does not wish to be summoned!</span>"
return
else if(cult_mode.sacrifice_target && !(cult_mode.sacrifice_target in sacrificed))
user << "<span class='warning'>The sacrifice is not complete. The portal would lack the power to open if you tried!</span>"
return
else if(!cult_mode.eldergod)
user << "<span class='cultlarge'>\"I am already here. There is no need to try to summon me now.\"</span>"
return
var/locname = initial(A.name)
if(loc.z && loc.z != ZLEVEL_STATION)
user << "<span class='warning'>The Geometer is not interested \
in lesser locations; the station is the prize!</span>"
return
var/confirm_final = alert(user, "This is the FINAL step to summon Nar-Sie, it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for Nar-Sie!", "No")
if(confirm_final == "No")
user << "<span class='cult'>You decide to prepare further before scribing the rune.</span>"
return
priority_announce("Figments from an eldritch god are being summoned by [user] into [locname] from an unknown dimension. Disrupt the ritual at all costs!","Central Command Higher Dimensionsal Affairs", 'sound/AI/spanomalies.ogg')
for(var/B in spiral_range_turfs(1, user, 1))
var/turf/T = B
var/obj/machinery/shield/N = new(T)
N.name = "sanguine barrier"
N.desc = "A potent shield summoned by cultists to defend their rites."
N.icon_state = "shield-red"
N.health = 60
shields |= N
else
user << "<span class='warning'>Nar-Sie does not wish to be summoned!</span>"
return
user.visible_message("<span class='warning'>[user] cuts open their arm and begins writing in their own blood!</span>", \
"<span class='cult'>You slice open your arm and begin drawing a sigil of the Geometer.</span>")
user.apply_damage(initial(rune_to_scribe.scribe_damage), BRUTE, pick("l_arm", "r_arm"))
if(!do_after(user, initial(rune_to_scribe.scribe_delay), target = get_turf(user)))
for(var/V in shields)
var/obj/machinery/shield/S = V
if(S && !qdeleted(S))
qdel(S)
return
if(locate(/obj/effect/rune) in Turf)
user << "<span class='cult'>There is already a rune here.</span>"
return
user.visible_message("<span class='warning'>[user] creates a strange circle in their own blood.</span>", \
"<span class='cult'>You finish drawing the arcane markings of the Geometer.</span>")
for(var/V in shields)
var/obj/machinery/shield/S = V
if(S && !qdeleted(S))
qdel(S)
new rune_to_scribe(Turf, chosen_keyword)
user << "<span class='cult'>The [lowertext(initial(rune_to_scribe.cultist_name))] rune [initial(rune_to_scribe.cultist_desc)]</span>"
+908
View File
@@ -0,0 +1,908 @@
/var/list/sacrificed = list()
var/list/non_revealed_runes = (subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed)
/*
This file contains runes.
Runes are used by the cult to cause many different effects and are paramount to their success.
They are drawn with an arcane tome in blood, and are distinguishable to cultists and normal crew by examining.
Fake runes can be drawn in crayon to fool people.
Runes can either be invoked by one's self or with many different cultists. Each rune has a specific incantation that the cultists will say when invoking it.
To draw a rune, use an arcane tome.
*/
/obj/effect/rune
name = "rune"
var/cultist_name = "basic rune"
desc = "An odd collection of symbols drawn in what seems to be blood."
var/cultist_desc = "a basic rune with no function." //This is shown to cultists who examine the rune in order to determine its true purpose.
anchored = 1
icon = 'icons/obj/rune.dmi'
icon_state = "1"
unacidable = 1
layer = ABOVE_NORMAL_TURF_LAYER
color = rgb(255,0,0)
var/invocation = "Aiy ele-mayo!" //This is said by cultists when the rune is invoked.
var/req_cultists = 1 //The amount of cultists required around the rune to invoke it. If only 1, any cultist can invoke it.
var/rune_in_use = 0 // Used for some runes, this is for when you want a rune to not be usable when in use.
var/scribe_delay = 50 //how long the rune takes to create
var/scribe_damage = 0.1 //how much damage you take doing it
var/allow_excess_invokers = 0 //if we allow excess invokers when being invoked
var/construct_invoke = 1 //if constructs can invoke it
var/req_keyword = 0 //If the rune requires a keyword - go figure amirite
var/keyword //The actual keyword for the rune
/obj/effect/rune/New(loc, set_keyword)
..()
if(set_keyword)
keyword = set_keyword
/obj/effect/rune/examine(mob/user)
..()
if(iscultist(user) || user.stat == DEAD) //If they're a cultist or a ghost, tell them the effects
user << "<b>Name:</b> [cultist_name]"
user << "<b>Effects:</b> [capitalize(cultist_desc)]"
user << "<b>Required Acolytes:</b> [req_cultists]"
if(req_keyword && keyword)
user << "<b>Keyword:</b> [keyword]"
/obj/effect/rune/attackby(obj/I, mob/user, params)
if(istype(I, /obj/item/weapon/tome) && iscultist(user))
user << "<span class='notice'>You carefully erase the [lowertext(cultist_name)] rune.</span>"
qdel(src)
return
else if(istype(I, /obj/item/weapon/nullrod))
user.say("BEGONE FOUL MAGIKS!!")
user << "<span class='danger'>You disrupt the magic of [src] with [I].</span>"
qdel(src)
return
return
/obj/effect/rune/attack_hand(mob/living/user)
if(!iscultist(user))
user << "<span class='warning'>You aren't able to understand the words of [src].</span>"
return
var/list/invokers = can_invoke(user)
if(invokers.len >= req_cultists)
invoke(invokers)
else
fail_invoke()
/obj/effect/rune/attack_animal(mob/living/simple_animal/M)
if(istype(M, /mob/living/simple_animal/shade) || istype(M, /mob/living/simple_animal/hostile/construct))
if(construct_invoke || !iscultist(M)) //if you're not a cult construct we want the normal fail message
attack_hand(M)
else
M << "<span class='warning'>You are unable to invoke the rune!</span>"
/obj/effect/rune/proc/talismanhide() //for talisman of revealing/hiding
visible_message("<span class='danger'>[src] fades away.</span>")
invisibility = INVISIBILITY_OBSERVER
alpha = 100 //To help ghosts distinguish hidden runes
/obj/effect/rune/proc/talismanreveal() //for talisman of revealing/hiding
invisibility = 0
visible_message("<span class='danger'>[src] suddenly appears!</span>")
alpha = initial(alpha)
/*
There are a few different procs each rune runs through when a cultist activates it.
can_invoke() is called when a cultist activates the rune with an empty hand. If there are multiple cultists, this rune determines if the required amount is nearby.
invoke() is the rune's actual effects.
fail_invoke() is called when the rune fails, via not enough people around or otherwise. Typically this just has a generic 'fizzle' effect.
structure_check() searches for nearby cultist structures required for the invocation. Proper structures are pylons, forges, archives, and altars.
*/
/obj/effect/rune/proc/can_invoke(var/mob/living/user=null)
//This proc determines if the rune can be invoked at the time. If there are multiple required cultists, it will find all nearby cultists.
var/list/invokers = list() //people eligible to invoke the rune
var/list/chanters = list() //people who will actually chant the rune when passed to invoke()
if(user)
chanters |= user
invokers |= user
if(req_cultists > 1 || allow_excess_invokers)
for(var/mob/living/L in range(1, src))
if(iscultist(L))
if(L == user)
continue
if(ishuman(L))
var/mob/living/carbon/human/H = L
if((H.disabilities & MUTE) || H.silent)
continue
if(L.stat)
continue
invokers |= L
if(invokers.len >= req_cultists)
if(allow_excess_invokers)
chanters |= invokers
else
invokers -= user
shuffle(invokers)
for(var/i in 0 to req_cultists)
var/L = pick_n_take(invokers)
chanters |= L
return chanters
/obj/effect/rune/proc/invoke(var/list/invokers)
//This proc contains the effects of the rune as well as things that happen afterwards. If you want it to spawn an object and then delete itself, have both here.
if(invocation)
for(var/M in invokers)
var/mob/living/L = M
L.say(invocation)
var/oldtransform = transform
spawn(0) //animate is a delay, we want to avoid being delayed
animate(src, transform = matrix()*2, alpha = 0, time = 5) //fade out
animate(transform = oldtransform, alpha = 255, time = 0)
/obj/effect/rune/proc/fail_invoke()
//This proc contains the effects of a rune if it is not invoked correctly, through either invalid wording or not enough cultists. By default, it's just a basic fizzle.
visible_message("<span class='warning'>The markings pulse with a \
small flash of red light, then fall dark.</span>")
spawn(0) //animate is a delay, we want to avoid being delayed
animate(src, color = rgb(255, 0, 0), time = 0)
animate(src, color = initial(color), time = 5)
//Malformed Rune: This forms if a rune is not drawn correctly. Invoking it does nothing but hurt the user.
/obj/effect/rune/malformed
cultist_name = "malformed rune"
cultist_desc = "a senseless rune written in gibberish. No good can come from invoking this."
invocation = "Ra'sha yoka!"
/obj/effect/rune/malformed/New()
..()
icon_state = "[rand(1,6)]"
color = rgb(rand(0,255), rand(0,255), rand(0,255))
/obj/effect/rune/malformed/invoke(var/list/invokers)
..()
for(var/M in invokers)
var/mob/living/L = M
L << "<span class='cultitalic'><b>You feel your life force draining. The Geometer is displeased.</b></span>"
L.apply_damage(30, BRUTE)
qdel(src)
/mob/proc/null_rod_check() //The null rod, if equipped, will protect the holder from the effects of most runes
var/obj/item/weapon/nullrod/N = locate() in src
if(N && !ratvar_awakens) //If Nar-Sie or Ratvar are alive, null rods won't protect you
return N
return 0
//Rite of Binding: A paper on top of the rune to a talisman.
/obj/effect/rune/imbue
cultist_name = "Create Talisman"
cultist_desc = "transforms paper into powerful magic talismans."
invocation = "H'drak v'loso, mir'kanas verbot!"
icon_state = "3"
color = rgb(0, 0, 255)
/obj/effect/rune/imbue/invoke(var/list/invokers)
var/mob/living/user = invokers[1] //the first invoker is always the user
var/list/papers_on_rune = checkpapers()
var/entered_talisman_name
var/obj/item/weapon/paper/talisman/talisman_type
var/list/possible_talismans = list()
if(!papers_on_rune.len)
user << "<span class='cultitalic'>There must be a blank paper on top of [src]!</span>"
fail_invoke()
log_game("Talisman Creation rune failed - no blank papers on rune")
return
if(rune_in_use)
user << "<span class='cultitalic'>[src] can only support one ritual at a time!</span>"
fail_invoke()
log_game("Talisman Creation rune failed - already in use")
return
for(var/I in subtypesof(/obj/item/weapon/paper/talisman) - /obj/item/weapon/paper/talisman/malformed - /obj/item/weapon/paper/talisman/supply - /obj/item/weapon/paper/talisman/supply/weak)
var/obj/item/weapon/paper/talisman/J = I
var/talisman_cult_name = initial(J.cultist_name)
if(talisman_cult_name)
possible_talismans[talisman_cult_name] = J //This is to allow the menu to let cultists select talismans by name
entered_talisman_name = input(user, "Choose a talisman to imbue.", "Talisman Choices") as null|anything in possible_talismans
talisman_type = possible_talismans[entered_talisman_name]
if(!Adjacent(user) || !src || qdeleted(src) || user.incapacitated() || rune_in_use || !talisman_type)
return
papers_on_rune = checkpapers()
if(!papers_on_rune.len)
user << "<span class='cultitalic'>There must be a blank paper on top of [src]!</span>"
fail_invoke()
log_game("Talisman Creation rune failed - no blank papers on rune")
return
var/obj/item/weapon/paper/paper_to_imbue = papers_on_rune[1]
..()
visible_message("<span class='warning'>Dark power begins to channel into the paper!</span>")
rune_in_use = 1
if(!do_after(user, 100, target = paper_to_imbue))
rune_in_use = 0
return
new talisman_type(get_turf(src))
visible_message("<span class='warning'>[src] glows with power, and bloody images form themselves on [paper_to_imbue].</span>")
qdel(paper_to_imbue)
rune_in_use = 0
/obj/effect/rune/imbue/proc/checkpapers()
. = list()
for(var/obj/item/weapon/paper/P in get_turf(src))
if(!P.info && !istype(P, /obj/item/weapon/paper/talisman))
. |= P
var/list/teleport_runes = list()
/obj/effect/rune/teleport
cultist_name = "Teleport"
cultist_desc = "warps everything above it to another chosen teleport rune."
invocation = "Sas'so c'arta forbici!"
icon_state = "2"
color = "#551A8B"
req_keyword = 1
var/listkey
/obj/effect/rune/teleport/New(loc, set_keyword)
..()
var/area/A = get_area(src)
var/locname = initial(A.name)
listkey = set_keyword ? "[set_keyword] [locname]":"[locname]"
teleport_runes += src
/obj/effect/rune/teleport/Destroy()
teleport_runes -= src
return ..()
/obj/effect/rune/teleport/invoke(var/list/invokers)
var/mob/living/user = invokers[1] //the first invoker is always the user
var/list/potential_runes = list()
var/list/teleportnames = list()
var/list/duplicaterunecount = list()
for(var/R in teleport_runes)
var/obj/effect/rune/teleport/T = R
var/resultkey = T.listkey
if(resultkey in teleportnames)
duplicaterunecount[resultkey]++
resultkey = "[resultkey] ([duplicaterunecount[resultkey]])"
else
teleportnames.Add(resultkey)
duplicaterunecount[resultkey] = 1
if(T != src && (T.z <= ZLEVEL_SPACEMAX))
potential_runes[resultkey] = T
if(!potential_runes.len)
user << "<span class='warning'>There are no valid runes to teleport to!</span>"
log_game("Teleport rune failed - no other teleport runes")
fail_invoke()
return
if(user.z > ZLEVEL_SPACEMAX)
user << "<span class='cultitalic'>You are not in the right dimension!</span>"
log_game("Teleport rune failed - user in away mission")
fail_invoke()
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(!Adjacent(user) || !src || qdeleted(src) || user.incapacitated() || !actual_selected_rune)
fail_invoke()
return
var/turf/T = get_turf(src)
var/movedsomething = 0
var/moveuserlater = 0
for(var/atom/movable/A in T)
if(A == user)
moveuserlater = 1
movedsomething = 1
continue
if(!A.anchored)
movedsomething = 1
A.forceMove(get_turf(actual_selected_rune))
if(movedsomething)
..()
visible_message("<span class='warning'>There is a sharp crack of inrushing air, and everything above the rune disappears!</span>")
user << "<span class='cult'>You[moveuserlater ? "r vision blurs, and you suddenly appear somewhere else":" send everything above the rune away"].</span>"
if(moveuserlater)
user.forceMove(get_turf(actual_selected_rune))
else
fail_invoke()
//Rite of Enlightenment: Converts a normal crewmember to the cult.
/obj/effect/rune/convert
cultist_name = "Convert"
cultist_desc = "converts a normal crewmember on top of it to the cult. Does not work on mindshield-implanted crew."
invocation = "Mah'weyh pleggh at e'ntrath!"
icon_state = "3"
color = rgb(200, 0, 0)
req_cultists = 2
/obj/effect/rune/convert/invoke(var/list/invokers)
var/list/convertees = list()
var/turf/T = get_turf(src)
for(var/mob/living/M in T)
if(M.stat != DEAD && !iscultist(M) && is_convertable_to_cult(M.mind))
convertees |= M
else if(is_sacrifice_target(M.mind))
for(var/C in invokers)
C << "<span class='cultlarge'>\"I desire this one for myself. <i>SACRIFICE THEM!</i>\"</span>"
else if(is_servant_of_ratvar(M))
M.visible_message("<span class='warning'>[M]'s eyes glow a defiant yellow!</span>", \
"<span class='cultlarge'>\"Stop resisting. You <i>will</i> be mi-\"</span> <span class='large_brass'>\"Give up and you will feel pain unlike anything you've ever felt!\"</span>")
M.Weaken(4)
if(!convertees.len)
fail_invoke()
log_game("Convert rune failed - no eligible convertees")
return
var/mob/living/new_cultist = pick(convertees)
if(new_cultist.null_rod_check())
for(var/M in invokers)
M << "<span class='warning'>Something is shielding [new_cultist]'s mind!</span>"
fail_invoke()
log_game("Convert rune failed - convertee had null rod")
return
..()
new_cultist.visible_message("<span class='warning'>[new_cultist] writhes in pain as the markings below them glow a bloody red!</span>", \
"<span class='cultlarge'><i>AAAAAAAAAAAAAA-</i></span>")
ticker.mode.add_cultist(new_cultist.mind, 1)
new /obj/item/weapon/tome(get_turf(src))
new_cultist.mind.special_role = "Cultist"
new_cultist << "<span class='cultitalic'><b>Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible, truth. The veil of reality has been ripped away \
and something evil takes root.</b></span>"
new_cultist << "<span class='cultitalic'><b>Assist your new compatriots in their dark dealings. Your goal is theirs, and theirs is yours. You serve the Geometer above all else. Bring it back.\
</b></span>"
//Rite of Tribute: Sacrifices a crew member to Nar-Sie. Places them into a soul shard if they're in their body.
/obj/effect/rune/sacrifice
cultist_name = "Sacrifice"
cultist_desc = "sacrifices a crew member to the Geometer. May place them into a soul shard if their spirit remains in their body."
icon_state = "3"
allow_excess_invokers = 1
invocation = "Barhah hra zar'garis!"
color = rgb(255, 255, 255)
rune_in_use = 0
/obj/effect/rune/sacrifice/New()
..()
icon_state = "[rand(1,6)]"
/obj/effect/rune/sacrifice/invoke(var/list/invokers)
if(rune_in_use)
return
rune_in_use = 1
var/mob/living/user = invokers[1] //the first invoker is always the user
var/turf/T = get_turf(src)
var/list/possible_targets = list()
for(var/mob/living/M in T.contents)
if(M.mind)
if(M.mind in sacrificed)
continue
if(!iscultist(M))
possible_targets.Add(M)
var/mob/offering
if(possible_targets.len > 1) //If there's more than one target, allow choice
offering = input(user, "Choose an offering to sacrifice.", "Unholy Tribute") as null|anything in possible_targets
if(!Adjacent(user) || !src || qdeleted(src) || user.incapacitated())
return
else if(possible_targets.len) //Otherwise, if there's a target at all, pick the only one
offering = possible_targets[possible_targets.len]
if(!offering)
rune_in_use = 0
return
/*var/obj/item/weapon/nullrod/N = offering.null_rod_check()
if(N)
user << "<span class='warning'>Something is blocking the Geometer's magic!</span>"
log_game("Sacrifice rune failed - target has \a [N]!")
fail_invoke()
rune_in_use = 0
return*/
if(((ishuman(offering) || isrobot(offering)) && offering.stat != DEAD) || is_sacrifice_target(offering.mind)) //Requires three people to sacrifice living targets
if(invokers.len < 3)
for(var/M in invokers)
M << "<span class='cultitalic'>[offering] is too greatly linked to the world! You need three acolytes!</span>"
fail_invoke()
log_game("Sacrifice rune failed - not enough acolytes and target is living")
rune_in_use = 0
return
visible_message("<span class='warning'>[src] pulses blood red!</span>")
color = rgb(126, 23, 23)
..()
sac(invokers, offering)
color = initial(color)
/obj/effect/rune/sacrifice/proc/sac(var/list/invokers, mob/living/T)
var/sacrifice_fulfilled
if(T)
if(istype(T, /mob/living/simple_animal/pet/dog))
for(var/M in invokers)
var/mob/living/L = M
L << "<span class='cultlarge'>\"Even I have standards, such as they are!\"</span>"
if(L.reagents)
L.reagents.add_reagent("hell_water", 2)
if(T.mind)
sacrificed.Add(T.mind)
if(is_sacrifice_target(T.mind))
sacrifice_fulfilled = 1
PoolOrNew(/obj/effect/overlay/temp/cult/sac, src.loc)
for(var/M in invokers)
if(sacrifice_fulfilled)
M << "<span class='cultlarge'>\"Yes! This is the one I desire! You have done well.\"</span>"
else
if(ishuman(T) || isrobot(T))
M << "<span class='cultlarge'>\"I accept this sacrifice.\"</span>"
else
M << "<span class='cultlarge'>\"I accept this meager sacrifice.\"</span>"
if(T.mind)
var/obj/item/device/soulstone/stone = new /obj/item/device/soulstone(get_turf(src))
stone.invisibility = INVISIBILITY_MAXIMUM //so it's not picked up during transfer_soul()
if(!stone.transfer_soul("FORCE", T, usr)) //If it cannot be added
qdel(stone)
if(stone)
stone.invisibility = 0
if(!T)
rune_in_use = 0
return
if(isrobot(T))
playsound(T, 'sound/magic/Disable_Tech.ogg', 100, 1)
T.dust() //To prevent the MMI from remaining
else
playsound(T, 'sound/magic/Disintegrate.ogg', 100, 1)
T.gib()
rune_in_use = 0
//Ritual of Dimensional Rending: Calls forth the avatar of Nar-Sie upon the station.
/obj/effect/rune/narsie
cultist_name = "Summon Nar-Sie"
cultist_desc = "tears apart dimensional barriers, calling forth the Geometer. Requires 9 invokers."
invocation = "TOK-LYR RQA-NAP G'OLT-ULOFT!!"
req_cultists = 9
icon = 'icons/effects/96x96.dmi'
color = rgb(125,23,23)
icon_state = "rune_large"
pixel_x = -32 //So the big ol' 96x96 sprite shows up right
pixel_y = -32
scribe_delay = 450 //how long the rune takes to create
scribe_damage = 40.1 //how much damage you take doing it
var/used
/obj/effect/rune/narsie/New()
. = ..()
poi_list |= src
/obj/effect/rune/narsie/Destroy()
poi_list -= src
. = ..()
/obj/effect/rune/narsie/talismanhide() //can't hide this, and you wouldn't want to
return
/obj/effect/rune/narsie/invoke(var/list/invokers)
if(used)
return
if(z != ZLEVEL_STATION)
return
if(ticker.mode.name == "cult")
var/datum/game_mode/cult/cult_mode = ticker.mode
if(!cult_mode.eldergod)
for(var/M in invokers)
M << "<span class='warning'>Nar-Sie is already on this plane!</span>"
log_game("Summon Nar-Sie rune failed - already summoned")
return
//BEGIN THE SUMMONING
used = 1
..()
world << 'sound/effects/dimensional_rend.ogg' //There used to be a message for this but every time it was changed it got edgier so I removed it
var/turf/T = get_turf(src)
sleep(40)
if(src)
color = rgb(255, 0, 0)
new /obj/singularity/narsie/large(T) //Causes Nar-Sie to spawn even if the rune has been removed
cult_mode.eldergod = 0
else
for(var/M in invokers)
M << "<span class='warning'>Nar-Sie does not respond!</span>"
fail_invoke()
log_game("Summon Nar-Sie rune failed - gametype is not cult")
/obj/effect/rune/narsie/attackby(obj/I, mob/user, params) //Since the narsie rune takes a long time to make, add logging to removal.
if((istype(I, /obj/item/weapon/tome) && iscultist(user)))
user.visible_message("<span class='warning'>[user.name] begins erasing the [src]...</span>", "<span class='notice'>You begin erasing the [src]...</span>")
if(do_after(user, 50, target = src)) //Prevents accidental erasures.
log_game("Summon Narsie rune erased by [user.mind.key] (ckey) with a tome")
message_admins("[key_name_admin(user)] erased a Narsie rune with a tome")
..()
return
else
if(istype(I, /obj/item/weapon/nullrod)) //Begone foul magiks. You cannot hinder me.
log_game("Summon Narsie rune erased by [user.mind.key] (ckey) using a null rod")
message_admins("[key_name_admin(user)] erased a Narsie rune with a null rod")
..()
return
//Rite of Resurrection: Requires two corpses. Revives one and gibs the other.
/obj/effect/rune/raise_dead
cultist_name = "Raise Dead"
cultist_desc = "requires two corpses, one on the rune and one adjacent to the rune. The one on the rune is brought to life, the other is turned to ash."
invocation = null //Depends on the name of the user - see below
icon_state = "1"
color = rgb(200, 0, 0)
/obj/effect/rune/raise_dead/invoke(var/list/invokers)
var/turf/T = get_turf(src)
var/mob/living/mob_to_sacrifice
var/mob/living/mob_to_revive
var/list/potential_sacrifice_mobs = list()
var/list/potential_revive_mobs = list()
var/mob/living/user = invokers[1]
if(rune_in_use)
return
for(var/mob/living/M in orange(1,T))
if(M.stat == DEAD && !iscultist(M))
potential_sacrifice_mobs |= M
if(!potential_sacrifice_mobs.len)
user << "<span class='cultitalic'>There are no eligible sacrifices nearby!</span>"
log_game("Raise Dead rune failed - no catalyst corpses")
fail_invoke()
return
for(var/mob/living/M in T.contents)
if(M.stat == DEAD)
potential_revive_mobs |= M
if(!potential_revive_mobs.len)
user << "<span class='cultitalic'>There is no eligible revival target on the rune!</span>"
log_game("Raise Dead rune failed - no corpses to revive")
fail_invoke()
return
mob_to_sacrifice = input(user, "Choose a corpse to sacrifice.", "Corpse to Sacrifice") as null|anything in potential_sacrifice_mobs
if(!src || qdeleted(src) || rune_in_use || !validness_checks(mob_to_sacrifice, user, 1))
return
mob_to_revive = input(user, "Choose a corpse to revive.", "Corpse to Revive") as null|anything in potential_revive_mobs
if(!src || qdeleted(src) || rune_in_use || !validness_checks(mob_to_sacrifice, user, 1))
return
if(!validness_checks(mob_to_revive, user, 0))
return
rune_in_use = 1
if(user.name == "Herbert West")
user.say("To life, to life, I bring them!")
else
user.say("Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!")
..()
mob_to_sacrifice.visible_message("<span class='warning'><b>[mob_to_sacrifice]'s body rises into the air, connected to [mob_to_revive] by a glowing tendril!</span>")
mob_to_revive.Beam(mob_to_sacrifice,icon_state="sendbeam",icon='icons/effects/effects.dmi',time=20)
sleep(20)
if(!mob_to_sacrifice || !in_range(mob_to_sacrifice, src))
rune_in_use = 0
return
if(!mob_to_revive || mob_to_revive.stat != DEAD)
visible_message("<span class='warning'>The glowing tendril snaps against the rune with a shocking crack.</span>")
rune_in_use = 0
fail_invoke()
return
mob_to_sacrifice.visible_message("<span class='warning'><b>[mob_to_sacrifice] disintegrates into a pile of bones.</span>")
mob_to_sacrifice.dust()
mob_to_revive.revive(1, 1) //This does remove disabilities and such, but the rune might actually see some use because of it!
mob_to_revive.grab_ghost()
mob_to_revive << "<span class='cultlarge'>\"PASNAR SAVRAE YAM'TOTH. Arise.\"</span>"
mob_to_revive.visible_message("<span class='warning'>[mob_to_revive] draws in a huge breath, red light shining from their eyes.</span>", \
"<span class='cultlarge'>You awaken suddenly from the void. You're alive!</span>")
rune_in_use = 0
/obj/effect/rune/raise_dead/proc/validness_checks(mob/living/target_mob, mob/living/user, saccing)
var/turf/T = get_turf(src)
if(!user)
return 0
if(!Adjacent(user) || user.incapacitated())
return 0
if(!target_mob)
fail_invoke()
return 0
if(saccing)
if(!in_range(target_mob, src))
user << "<span class='cultitalic'>The sacrificial target has been moved!</span>"
fail_invoke()
log_game("Raise Dead rune failed - catalyst corpse moved")
return 0
if(target_mob.stat != DEAD)
user << "<span class='cultitalic'>The sacrificial target must be dead!</span>"
fail_invoke()
log_game("Raise Dead rune failed - catalyst corpse is not dead")
return 0
else if(!(target_mob in T.contents))
user << "<span class='cultitalic'>The corpse to revive has been moved!</span>"
fail_invoke()
log_game("Raise Dead rune failed - revival target moved")
return 0
return 1
/obj/effect/rune/raise_dead/fail_invoke()
..()
for(var/mob/living/M in range(1,src))
if(M.stat == DEAD)
M.visible_message("<span class='warning'>[M] twitches.</span>")
//Rite of Disruption: Emits an EMP blast.
/obj/effect/rune/emp
cultist_name = "Electromagnetic Disruption"
cultist_desc = "emits a large electromagnetic pulse, increasing in size for each cultist invoking it, hindering electronics and disabling silicons."
invocation = "Ta'gh fara'qha fel d'amar det!"
icon_state = "5"
allow_excess_invokers = 1
color = rgb(77, 148, 255)
/obj/effect/rune/emp/invoke(var/list/invokers)
var/turf/E = get_turf(src)
..()
visible_message("<span class='warning'>[src] glows blue for a moment before vanishing.</span>")
switch(invokers.len)
if(1 to 2)
playsound(E, 'sound/items/Welder2.ogg', 25, 1)
for(var/M in invokers)
M << "<span class='warning'>You feel a minute vibration pass through you...</span>"
if(3 to 6)
playsound(E, 'sound/magic/Disable_Tech.ogg', 50, 1)
for(var/M in invokers)
M << "<span class='danger'>Your hair stands on end as a shockwave eminates from the rune!</span>"
if(7 to INFINITY)
playsound(E, 'sound/magic/Disable_Tech.ogg', 100, 1)
for(var/M in invokers)
var/mob/living/L = M
L << "<span class='userdanger'>You chant in unison and a colossal burst of energy knocks you backward!</span>"
L.Weaken(2)
qdel(src) //delete before pulsing because it's a delay reee
empulse(E, 9*invokers.len, 12*invokers.len) // Scales now, from a single room to most of the station depending on # of chanters
//Rite of Astral Communion: Separates one's spirit from their body. They will take damage while it is active.
/obj/effect/rune/astral
cultist_name = "Astral Communion"
cultist_desc = "severs the link between one's spirit and body. This effect is taxing and one's physical body will take damage while this is active."
invocation = "Fwe'sh mah erl nyag r'ya!"
icon_state = "6"
color = rgb(126, 23, 23)
rune_in_use = 0 //One at a time, please!
construct_invoke = 0
var/mob/living/affecting = null
/obj/effect/rune/astral/examine(mob/user)
..()
if(affecting)
user << "<span class='cultitalic'>A translucent field encases [user] above the rune!</span>"
/obj/effect/rune/astral/can_invoke(mob/living/user)
if(rune_in_use)
user << "<span class='cultitalic'>[src] cannot support more than one body!</span>"
log_game("Astral Communion rune failed - more than one user")
return list()
var/turf/T = get_turf(src)
if(!user in T.contents)
user << "<span class='cultitalic'>You must be standing on top of [src]!</span>"
log_game("Astral Communion rune failed - user not standing on rune")
return list()
return ..()
/obj/effect/rune/astral/invoke(var/list/invokers)
var/mob/living/user = invokers[1]
..()
var/turf/T = get_turf(src)
rune_in_use = 1
affecting = user
user.color = "#7e1717"
user.visible_message("<span class='warning'>[user] freezes statue-still, glowing an unearthly red.</span>", \
"<span class='cult'>You see what lies beyond. All is revealed. While this is a wondrous experience, your physical form will waste away in this state. Hurry...</span>")
user.ghostize(1)
while(user)
if(!affecting)
visible_message("<span class='warning'>[src] pulses gently before falling dark.</span>")
affecting = null //In case it's assigned to a number or something
rune_in_use = 0
return
affecting.apply_damage(1, BRUTE)
if(!(user in T.contents))
user.visible_message("<span class='warning'>A spectral tendril wraps around [user] and pulls them back to the rune!</span>")
Beam(user,icon_state="drainbeam",icon='icons/effects/effects.dmi',time=2)
user.forceMove(get_turf(src)) //NO ESCAPE :^)
if(user.key)
user.visible_message("<span class='warning'>[user] slowly relaxes, the glow around them dimming.</span>", \
"<span class='danger'>You are re-united with your physical form. [src] releases its hold over you.</span>")
user.color = initial(user.color)
user.Weaken(3)
rune_in_use = 0
affecting = null
return
if(user.stat == UNCONSCIOUS)
if(prob(10))
var/mob/dead/observer/G = user.get_ghost()
if(G)
G << "<span class='cultitalic'>You feel the link between you and your body weakening... you must hurry!</span>"
if(user.stat == DEAD)
user.color = initial(user.color)
rune_in_use = 0
affecting = null
var/mob/dead/observer/G = user.get_ghost()
if(G)
G << "<span class='cultitalic'><b>You suddenly feel your physical form pass on. [src]'s exertion has killed you!</b></span>"
return
sleep(10)
rune_in_use = 0
//Rite of the Corporeal Shield: When invoked, becomes solid and cannot be passed. Invoke again to undo.
/obj/effect/rune/wall
cultist_name = "Form Barrier"
cultist_desc = "when invoked, makes an invisible wall to block passage. Can be invoked again to reverse this."
invocation = "Khari'd! Eske'te tannin!"
icon_state = "1"
color = rgb(255, 0, 0)
/obj/effect/rune/wall/examine(mob/user)
..()
if(density)
user << "<span class='cultitalic'>There is a barely perceptible shimmering of the air above [src].</span>"
/obj/effect/rune/wall/invoke(var/list/invokers)
var/mob/living/user = invokers[1]
..()
density = !density
user.visible_message("<span class='warning'>[user] [iscarbon(user) ? "places their hands on":"stares intently at"] [src], and [density ? "the air above it begins to shimmer" : "the shimmer above it fades"].</span>", \
"<span class='cultitalic'>You channel your life energy into [src], [density ? "preventing" : "allowing"] passage above it.</span>")
if(density)
var/image/I = image(layer = ABOVE_MOB_LAYER, icon = 'icons/effects/effects.dmi', icon_state = "barriershimmer")
I.appearance_flags = RESET_COLOR
I.alpha = 60
I.color = "#701414"
overlays += I
else
overlays.Cut()
if(iscarbon(user))
var/mob/living/carbon/C = user
C.apply_damage(2, BRUTE, pick("l_arm", "r_arm"))
//Rite of Joined Souls: Summons a single cultist.
/obj/effect/rune/summon
cultist_name = "Summon Cultist"
cultist_desc = "summons a single cultist to the rune. Requires 2 invokers."
invocation = "N'ath reth sh'yro eth d'rekkathnor!"
req_cultists = 2
allow_excess_invokers = 1
icon_state = "5"
color = rgb(0, 255, 0)
/obj/effect/rune/summon/invoke(var/list/invokers)
var/mob/living/user = invokers[1]
var/list/cultists = list()
for(var/datum/mind/M in ticker.mode.cult)
if(!(M.current in invokers) && M.current && M.current.stat != DEAD)
cultists |= M.current
var/mob/living/cultist_to_summon = input(user, "Who do you wish to call to [src]?", "Followers of the Geometer") as null|anything in cultists
if(!Adjacent(user) || !src || qdeleted(src) || user.incapacitated())
return
if(!cultist_to_summon)
user << "<span class='cultitalic'>You require a summoning target!</span>"
fail_invoke()
log_game("Summon Cultist rune failed - no target")
return
if(cultist_to_summon.stat == DEAD)
user << "<span class='cultitalic'>[cultist_to_summon] has died!</span>"
fail_invoke()
log_game("Summon Cultist rune failed - target died")
return
if(!iscultist(cultist_to_summon))
user << "<span class='cultitalic'>[cultist_to_summon] is not a follower of the Geometer!</span>"
fail_invoke()
log_game("Summon Cultist rune failed - target was deconverted")
return
if(cultist_to_summon.z > ZLEVEL_SPACEMAX)
user << "<span class='cultitalic'>[cultist_to_summon] is not in our dimension!</span>"
fail_invoke()
log_game("Summon Cultist rune failed - target in away mission")
return
cultist_to_summon.visible_message("<span class='warning'>[cultist_to_summon] suddenly disappears in a flash of red light!</span>", \
"<span class='cultitalic'><b>Overwhelming vertigo consumes you as you are hurled through the air!</b></span>")
..()
visible_message("<span class='warning'>A foggy shape materializes atop [src] and solidifes into [cultist_to_summon]!</span>")
user.apply_damage(10, BRUTE, "head")
cultist_to_summon.forceMove(get_turf(src))
qdel(src)
//Rite of Boiling Blood: Deals extremely high amounts of damage to non-cultists nearby
/obj/effect/rune/blood_boil
cultist_name = "Boil Blood"
cultist_desc = "boils the blood of non-believers who can see the rune, dealing extreme amounts of damage. Requires 3 invokers."
invocation = "Dedo ol'btoh!"
icon_state = "4"
color = rgb(200, 0, 0)
req_cultists = 3
construct_invoke = 0
/obj/effect/rune/blood_boil/invoke(var/list/invokers)
..()
var/turf/T = get_turf(src)
visible_message("<span class='warning'>[src] briefly bubbles before exploding!</span>")
for(var/mob/living/carbon/C in viewers(T))
if(!iscultist(C))
var/obj/item/weapon/nullrod/N = C.null_rod_check()
if(N)
C << "<span class='userdanger'>\The [N] suddenly burns hotly before returning to normal!</span>"
continue
C << "<span class='cultlarge'>Your blood boils in your veins!</span>"
C.take_overall_damage(45,45)
C.Stun(7)
if(is_servant_of_ratvar(C))
C << "<span class='userdanger'>You feel unholy darkness dimming the Justiciar's light!</span>"
C.adjustStaminaLoss(30)
for(var/M in invokers)
var/mob/living/L = M
L.apply_damage(15, BRUTE, pick("l_arm", "r_arm"))
L << "<span class='cultitalic'>[src] saps your strength!</span>"
qdel(src)
explosion(T, -1, 0, 1, 5)
//Rite of Spectral Manifestation: Summons a ghost on top of the rune as a cultist human with no items. User must stand on the rune at all times, and takes damage for each summoned ghost.
/obj/effect/rune/manifest
cultist_name = "Manifest Spirit"
cultist_desc = "manifests a spirit as a servant of the Geometer. The invoker must not move from atop the rune, and will take damage for each summoned spirit."
invocation = "Gal'h'rfikk harfrandid mud'gib!" //how the fuck do you pronounce this
icon_state = "6"
construct_invoke = 0
color = rgb(200, 0, 0)
/obj/effect/rune/manifest/New(loc)
..()
notify_ghosts("Manifest rune created in [get_area(src)].", 'sound/effects/ghost2.ogg', source = src)
/obj/effect/rune/manifest/can_invoke(mob/living/user)
if(!(user in get_turf(src)))
user << "<span class='cultitalic'>You must be standing on [src]!</span>"
fail_invoke()
log_game("Manifest rune failed - user not standing on rune")
return list()
var/list/ghosts_on_rune = list()
for(var/mob/dead/observer/O in get_turf(src))
if(O.client && !jobban_isbanned(O, ROLE_CULTIST))
ghosts_on_rune |= O
if(!ghosts_on_rune.len)
user << "<span class='cultitalic'>There are no spirits near [src]!</span>"
fail_invoke()
log_game("Manifest rune failed - no nearby ghosts")
return list()
return ..()
/obj/effect/rune/manifest/invoke(var/list/invokers)
var/mob/living/user = invokers[1]
var/list/ghosts_on_rune = list()
for(var/mob/dead/observer/O in get_turf(src))
if(O.client && !jobban_isbanned(O, ROLE_CULTIST))
ghosts_on_rune |= O
var/mob/dead/observer/ghost_to_spawn = pick(ghosts_on_rune)
var/mob/living/carbon/human/new_human = new(get_turf(src))
new_human.real_name = ghost_to_spawn.real_name
new_human.alpha = 150 //Makes them translucent
..()
visible_message("<span class='warning'>A cloud of red mist forms above [src], and from within steps... a man.</span>")
user << "<span class='cultitalic'>Your blood begins flowing into [src]. You must remain in place and conscious to maintain the forms of those summoned. This will hurt you slowly but surely...</span>"
var/obj/machinery/shield/N = new(get_turf(src))
N.name = "Invoker's Shield"
N.desc = "A weak shield summoned by cultists to protect them while they carry out delicate rituals"
N.color = "red"
N.health = 20
N.mouse_opacity = 0
new_human.key = ghost_to_spawn.key
ticker.mode.add_cultist(new_human.mind, 0)
new_human << "<span class='cultitalic'><b>You are a servant of the Geometer. You have been made semi-corporeal by the cult of Nar-Sie, and you are to serve them at all costs.</b></span>"
while(user in get_turf(src))
if(user.stat)
break
user.apply_damage(0.1, BRUTE)
sleep(3)
qdel(N)
if(new_human)
new_human.visible_message("<span class='warning'>[new_human] suddenly dissolves into bones and ashes.</span>", \
"<span class='cultlarge'>Your link to the world fades. Your form breaks apart.</span>")
for(var/obj/I in new_human)
new_human.unEquip(I)
new_human.dust()
+416
View File
@@ -0,0 +1,416 @@
/obj/item/weapon/paper/talisman
var/cultist_name = "talisman"
var/cultist_desc = "A basic talisman. It serves no purpose."
var/invocation = "Naise meam!"
var/uses = 1
var/health_cost = 0 //The amount of health taken from the user when invoking the talisman
/obj/item/weapon/paper/talisman/examine(mob/user)
if(iscultist(user) || user.stat == DEAD)
user << "<b>Name:</b> [cultist_name]"
user << "<b>Effect:</b> [cultist_desc]"
user << "<b>Uses Remaining:</b> [uses]"
else
user << "<span class='danger'>There are indecipherable images scrawled on the paper in what looks to be... <i>blood?</i></span>"
/obj/item/weapon/paper/talisman/attack_self(mob/living/user)
if(!iscultist(user))
user << "<span class='danger'>There are indecipherable images scrawled on the paper in what looks to be... <i>blood?</i></span>"
return
if(invoke(user))
uses--
if(uses <= 0)
user.drop_item()
qdel(src)
/obj/item/weapon/paper/talisman/proc/invoke(mob/living/user, successfuluse = 1)
. = successfuluse
if(successfuluse) //if the calling whatever says we succeed, do the fancy stuff
if(invocation)
user.whisper(invocation)
if(health_cost && iscarbon(user))
var/mob/living/carbon/C = user
C.apply_damage(health_cost, BRUTE, pick("l_arm", "r_arm"))
//Malformed Talisman: If something goes wrong.
/obj/item/weapon/paper/talisman/malformed
cultist_name = "malformed talisman"
cultist_desc = "A talisman with gibberish scrawlings. No good can come from invoking this."
invocation = "Ra'sha yoka!"
/obj/item/weapon/paper/talisman/malformed/invoke(mob/living/user, successfuluse = 1)
user << "<span class='cultitalic'>You feel a pain in your head. The Geometer is displeased.</span>"
if(iscarbon(user))
var/mob/living/carbon/C = user
C.apply_damage(10, BRUTE, "head")
//Supply Talisman: Has a few unique effects. Granted only to starter cultists.
/obj/item/weapon/paper/talisman/supply
cultist_name = "Supply Talisman"
cultist_desc = "A multi-use talisman that can create various objects. Intended to increase the cult's strength early on."
invocation = null
uses = 3
/obj/item/weapon/paper/talisman/supply/invoke(mob/living/user, successfuluse = 1)
var/dat = "<B>There are [uses] bloody runes on the parchment.</B><BR>"
dat += "Please choose the chant to be imbued into the fabric of reality.<BR>"
dat += "<HR>"
dat += "<A href='?src=\ref[src];rune=newtome'>N'ath reth sh'yro eth d'raggathnor!</A> - Summons an arcane tome, used to scribe runes and communicate with other cultists.<BR>"
dat += "<A href='?src=\ref[src];rune=teleport'>Sas'so c'arta forbici!</A> - Allows you to move to a selected teleportation rune.<BR>"
dat += "<A href='?src=\ref[src];rune=emp'>Ta'gh fara'qha fel d'amar det!</A> - Allows you to destroy technology in a short range.<BR>"
dat += "<A href='?src=\ref[src];rune=runestun'>Fuu ma'jin!</A> - Allows you to stun a person by attacking them with the talisman.<BR>"
dat += "<A href='?src=\ref[src];rune=veiling'>Kla'atu barada nikt'o!</A> - Two use talisman, first use makes all nearby runes invisible, second use reveals nearby hidden runes.<BR>"
dat += "<A href='?src=\ref[src];rune=soulstone'>Kal'om neth!</A> - Summons a soul stone, used to capure the spirits of dead or dying humans.<BR>"
dat += "<A href='?src=\ref[src];rune=construct'>Daa'ig osk!</A> - Summons a construct shell for use with soulstone-captured souls. It is too large to carry on your person.<BR>"
var/datum/browser/popup = new(user, "talisman", "", 400, 400)
popup.set_content(dat)
popup.open()
return 0
/obj/item/weapon/paper/talisman/supply/Topic(href, href_list)
if(src)
if(usr.stat || usr.restrained() || !in_range(src, usr))
return
if(href_list["rune"])
switch(href_list["rune"])
if("newtome")
var/obj/item/weapon/tome/T = new(usr)
usr.put_in_hands(T)
if("teleport")
var/obj/item/weapon/paper/talisman/teleport/T = new(usr)
usr.put_in_hands(T)
if("emp")
var/obj/item/weapon/paper/talisman/emp/T = new(usr)
usr.put_in_hands(T)
if("runestun")
var/obj/item/weapon/paper/talisman/stun/T = new(usr)
usr.put_in_hands(T)
if("soulstone")
var/obj/item/device/soulstone/T = new(usr)
usr.put_in_hands(T)
if("construct")
new /obj/structure/constructshell(get_turf(usr))
if("veiling")
var/obj/item/weapon/paper/talisman/true_sight/T = new(usr)
usr.put_in_hands(T)
src.uses--
if(src.uses <= 0)
if(iscarbon(usr))
var/mob/living/carbon/C = usr
C.drop_item()
visible_message("<span class='warning'>[src] crumbles to dust.</span>")
qdel(src)
/obj/item/weapon/paper/talisman/supply/weak
uses = 2
//Rite of Translocation: Same as rune
/obj/item/weapon/paper/talisman/teleport
cultist_name = "Talisman of Teleportation"
cultist_desc = "A single-use talisman that will teleport a user to a random rune of the same keyword."
color = "#551A8B" // purple
invocation = "Sas'so c'arta forbici!"
health_cost = 5
/obj/item/weapon/paper/talisman/teleport/invoke(mob/living/user, successfuluse = 1)
var/list/potential_runes = list()
var/list/teleportnames = list()
var/list/duplicaterunecount = list()
for(var/R in teleport_runes)
var/obj/effect/rune/teleport/T = R
var/resultkey = T.listkey
if(resultkey in teleportnames)
duplicaterunecount[resultkey]++
resultkey = "[resultkey] ([duplicaterunecount[resultkey]])"
else
teleportnames.Add(resultkey)
duplicaterunecount[resultkey] = 1
potential_runes[resultkey] = T
if(!potential_runes.len)
user << "<span class='warning'>There are no valid runes to teleport to!</span>"
log_game("Teleport talisman failed - no other teleport runes")
return ..(user, 0)
if(user.z > ZLEVEL_SPACEMAX)
user << "<span class='cultitalic'>You are not in the right dimension!</span>"
log_game("Teleport talisman failed - user in away mission")
return ..(user, 0)
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(!actual_selected_rune)
return ..(user, 0)
user.visible_message("<span class='warning'>Dust flows from [user]'s hand, and they disappear in a flash of red light!</span>", \
"<span class='cultitalic'>You speak the words of the talisman and find yourself somewhere else!</span>")
user.forceMove(get_turf(actual_selected_rune))
return ..()
/obj/item/weapon/paper/talisman/summon_tome
cultist_name = "Talisman of Tome Summoning"
cultist_desc = "A one-use talisman that will call an untranslated tome from the archives of the Geometer."
color = "#512727" // red-black
invocation = "N'ath reth sh'yro eth d'raggathnor!"
health_cost = 1
/obj/item/weapon/paper/talisman/summon_tome/invoke(mob/living/user, successfuluse = 1)
. = ..()
user.visible_message("<span class='warning'>[user]'s hand glows red for a moment.</span>", \
"<span class='cultitalic'>You speak the words of the talisman!</span>")
new /obj/item/weapon/tome(get_turf(user))
user.visible_message("<span class='warning'>A tome appears at [user]'s feet!</span>", \
"<span class='cultitalic'>An arcane tome materializes at your feet.</span>")
/obj/item/weapon/paper/talisman/true_sight
cultist_name = "Talisman of Veiling"
cultist_desc = "A multi-use talisman that hides nearby runes. On its second use, will reveal nearby runes."
color = "#9c9c9c" // grey
invocation = "Kla'atu barada nikt'o!"
health_cost = 1
uses = 2
var/revealing = FALSE //if it reveals or not
/obj/item/weapon/paper/talisman/true_sight/invoke(mob/living/user, successfuluse = 1)
. = ..()
if(!revealing)
user.visible_message("<span class='warning'>Thin grey dust falls from [user]'s hand!</span>", \
"<span class='cultitalic'>You speak the words of the talisman, hiding nearby runes.</span>")
invocation = "Nikt'o barada kla'atu!"
revealing = TRUE
for(var/obj/effect/rune/R in range(3,user))
R.talismanhide()
else
user.visible_message("<span class='warning'>A flash of light shines from [user]'s hand!</span>", \
"<span class='cultitalic'>You speak the words of the talisman, revealing nearby runes.</span>")
for(var/obj/effect/rune/R in range(3,user))
R.talismanreveal()
//Rite of False Truths: Same as rune
/obj/item/weapon/paper/talisman/make_runes_fake
cultist_name = "Talisman of Disguising"
cultist_desc = "A talisman that will make nearby runes appear fake."
color = "#ff80d5" // honk
invocation = "By'o nar'nar!"
/obj/item/weapon/paper/talisman/make_runes_fake/invoke(mob/living/user, successfuluse = 1)
. = ..()
user.visible_message("<span class='warning'>Dust flows from [user]s hand.</span>", \
"<span class='cultitalic'>You speak the words of the talisman, making nearby runes appear fake.</span>")
for(var/obj/effect/rune/R in orange(6,user))
R.desc = "A rune vandalizing the station."
//Rite of Disruption: Weaker than rune
/obj/item/weapon/paper/talisman/emp
cultist_name = "Talisman of Electromagnetic Pulse"
cultist_desc = "A talisman that will cause a moderately-sized electromagnetic pulse."
color = "#4d94ff" // light blue
invocation = "Ta'gh fara'qha fel d'amar det!"
health_cost = 5
/obj/item/weapon/paper/talisman/emp/invoke(mob/living/user, successfuluse = 1)
. = ..()
user.visible_message("<span class='warning'>[user]'s hand flashes a bright blue!</span>", \
"<span class='cultitalic'>You speak the words of the talisman, emitting an EMP blast.</span>")
empulse(src, 4, 8)
//Rite of Disorientation: Stuns and inhibit speech on a single target for quite some time
/obj/item/weapon/paper/talisman/stun
cultist_name = "Talisman of Stunning"
cultist_desc = "A talisman that will stun and inhibit speech on a single target. To use, attack target directly."
color = "#ff0000" // red
invocation = "Fuu ma'jin!"
health_cost = 10
/obj/item/weapon/paper/talisman/stun/invoke(mob/living/user, successfuluse = 0)
if(successfuluse) //if we're forced to be successful(we normally aren't) then do the normal stuff
return ..()
if(iscultist(user))
user << "<span class='warning'>To use this talisman, attack the target directly.</span>"
else
user << "<span class='danger'>There are indecipherable images scrawled on the paper in what looks to be... <i>blood?</i></span>"
return 0
/obj/item/weapon/paper/talisman/stun/attack(mob/living/target, mob/living/user, successfuluse = 1)
if(iscultist(user))
invoke(user, 1)
user.visible_message("<span class='warning'>[user] holds up [src], which explodes in a flash of red light!</span>", \
"<span class='cultitalic'>You stun [target] with the talisman!</span>")
var/obj/item/weapon/nullrod/N = locate() in target
if(N)
target.visible_message("<span class='warning'>[target]'s holy weapon absorbs the talisman's light!</span>", \
"<span class='userdanger'>Your holy weapon absorbs the blinding light!</span>")
else
target.Weaken(10)
target.Stun(10)
target.flash_eyes(1,1)
if(issilicon(target))
var/mob/living/silicon/S = target
S.emp_act(1)
else if(iscarbon(target))
var/mob/living/carbon/C = target
C.silent += 5
C.stuttering += 15
C.cultslurring += 15
C.Jitter(15)
if(is_servant_of_ratvar(target))
target.adjustBruteLoss(15)
user.drop_item()
qdel(src)
return
..()
//Rite of Arming: Equips cultist armor on the user, where available
/obj/item/weapon/paper/talisman/armor
cultist_name = "Talisman of Arming"
cultist_desc = "A talisman that will equip the invoker with cultist equipment if there is a slot to equip it to."
color = "#33cc33" // green
invocation = "N'ath reth sh'yro eth draggathnor!"
/obj/item/weapon/paper/talisman/armor/invoke(mob/living/user, successfuluse = 1)
. = ..()
user.visible_message("<span class='warning'>Otherworldly armor suddenly appears on [user]!</span>", \
"<span class='cultitalic'>You speak the words of the talisman, arming yourself!</span>")
user.equip_to_slot_or_del(new /obj/item/clothing/head/culthood/alt(user), slot_head)
user.equip_to_slot_or_del(new /obj/item/clothing/suit/cultrobes/alt(user), slot_wear_suit)
user.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult/alt(user), slot_shoes)
user.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/cultpack(user), slot_back)
user.drop_item()
user.put_in_hands(new /obj/item/weapon/melee/cultblade(user))
user.put_in_hands(new /obj/item/weapon/restraints/legcuffs/bola/cult(user))
/obj/item/weapon/paper/talisman/armor/attack(mob/living/target, mob/living/user)
if(iscultist(user) && iscultist(target))
user.drop_item()
invoke(target)
qdel(src)
return
..()
//Talisman of Horrors: Breaks the mind of the victim with nightmarish hallucinations
/obj/item/weapon/paper/talisman/horror
cultist_name = "Talisman of Horrors"
cultist_desc = "A talisman that will break the mind of the victim with nightmarish hallucinations."
color = "#ffb366" // light orange
invocation = "Lo'Nab Na'Dm!"
/obj/item/weapon/paper/talisman/horror/attack(mob/living/target, mob/living/user)
if(iscultist(user))
user << "<span class='cultitalic'>You disturb [target] with visons of the end!</span>"
if(iscarbon(target))
var/mob/living/carbon/H = target
H.reagents.add_reagent("mindbreaker", 25)
if(is_servant_of_ratvar(target))
target << "<span class='userdanger'>You see a brief but horrible vision of Ratvar, rusted and scrapped, being torn apart.</span>"
target.emote("scream")
target.confused = max(0, target.confused + 3)
target.flash_eyes()
qdel(src)
//Talisman of Fabrication: Creates a construct shell out of 25 metal sheets.
/obj/item/weapon/paper/talisman/construction
cultist_name = "Talisman of Construction"
cultist_desc = "Use this talisman on at least twenty-five metal sheets to create an empty construct shell"
invocation = "Ethra p'ni dedol!"
color = "#000000" // black
/obj/item/weapon/paper/talisman/construction/attack_self(mob/living/user)
if(iscultist(user))
user << "<span class='warning'>To use this talisman, place it upon a stack of metal sheets.</span>"
else
user << "<span class='danger'>There are indecipherable images scrawled on the paper in what looks to be... <i>blood?</i></span>"
/obj/item/weapon/paper/talisman/construction/attack(obj/M,mob/living/user)
if(iscultist(user))
user << "<span class='cultitalic'>This talisman will only work on a stack of metal sheets!</span>"
log_game("Construct talisman failed - not a valid target")
/obj/item/weapon/paper/talisman/construction/afterattack(obj/item/stack/sheet/target, mob/user, proximity_flag, click_parameters)
..()
if(proximity_flag && iscultist(user))
if(istype(target, /obj/item/stack/sheet/metal))
var/turf/T = get_turf(target)
if(target.use(25))
new /obj/structure/constructshell(T)
user << "<span class='warning'>The talisman clings to the metal and twists it into a construct shell!</span>"
user << sound('sound/effects/magic.ogg',0,1,25)
qdel(src)
if(istype(target, /obj/item/stack/sheet/plasteel))
var/quantity = target.amount
var/turf/T = get_turf(target)
new /obj/item/stack/sheet/runed_metal(T,quantity)
target.use(quantity)
user << "<span class='warning'>The talisman clings to the plasteel, transforming it into runed metal!</span>"
user << sound('sound/effects/magic.ogg',0,1,25)
qdel(src)
else
user << "<span class='warning'>The talisman must be used on metal or plasteel!</span>"
//Talisman of Shackling: Applies special cuffs directly from the talisman
/obj/item/weapon/paper/talisman/shackle
cultist_name = "Talisman of Shackling"
cultist_desc = "Use this talisman on a victim to handcuff them with dark bindings."
invocation = "In'totum Lig'abis!"
color = "#B27300" // burnt-orange
uses = 4
/obj/item/weapon/paper/talisman/shackle/invoke(mob/living/user, successfuluse = 0)
if(successfuluse) //if we're forced to be successful(we normally aren't) then do the normal stuff
return ..()
if(iscultist(user))
user << "<span class='warning'>To use this talisman, attack the target directly.</span>"
else
user << "<span class='danger'>There are indecipherable images scrawled on the paper in what looks to be... <i>blood?</i></span>"
return 0
/obj/item/weapon/paper/talisman/shackle/attack(mob/living/carbon/target, mob/living/user)
if(iscultist(user) && istype(target))
if(target.stat == DEAD)
user.visible_message("<span class='cultitalic'>This talisman's magic does not affect the dead!</span>")
return
CuffAttack(target, user)
return
..()
/obj/item/weapon/paper/talisman/shackle/proc/CuffAttack(mob/living/carbon/C, mob/living/user)
if(!C.handcuffed)
invoke(user, 1)
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/weapon/restraints/handcuffs/energy/cult/used(C)
C.update_handcuffed()
user << "<span class='notice'>You shackle [C].</span>"
add_logs(user, C, "handcuffed")
uses--
else
user << "<span class='warning'>[C] is already bound.</span>"
else
user << "<span class='warning'>You fail to shackle [C].</span>"
else
user << "<span class='warning'>[C] is already bound.</span>"
if(uses <= 0)
user.drop_item()
qdel(src)
return
/obj/item/weapon/restraints/handcuffs/energy/cult //For the talisman of shackling
name = "cult shackles"
desc = "Shackles that bind the wrists with sinister magic."
trashtype = /obj/item/weapon/restraints/handcuffs/energy/used
origin_tech = "materials=2;magnets=5"
flags = DROPDEL
/obj/item/weapon/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>")
. = ..()
+40
View File
@@ -0,0 +1,40 @@
var/global/list/whiteness = list (
/obj/item/clothing/under/color/white = 2,
/obj/item/clothing/under/rank/bartender = 1,
/obj/item/clothing/under/rank/chef = 1,
/obj/item/clothing/under/rank/chief_engineer = 1,
/obj/item/clothing/under/rank/scientist = 1,
/obj/item/clothing/under/rank/chemist = 1,
/obj/item/clothing/under/rank/chief_medical_officer = 1,
/obj/item/clothing/under/rank/geneticist = 1,
/obj/item/clothing/under/rank/virologist = 1,
/obj/item/clothing/under/rank/nursesuit = 1,
/obj/item/clothing/under/rank/medical = 1,
/obj/item/clothing/under/rank/det = 1,
/obj/item/clothing/under/suit_jacket/white = 0.5,
/obj/item/clothing/under/burial = 1
)
/mob/living/proc/check_devil_bane_multiplier(obj/item/weapon, mob/living/attacker)
switch(mind.devilinfo.bane)
if(BANE_WHITECLOTHES)
if(istype(attacker, /mob/living/carbon/human))
var/mob/living/carbon/human/H = attacker
if(H.w_uniform && istype(H.w_uniform, /obj/item/clothing/under))
var/obj/item/clothing/under/U = H.w_uniform
if(whiteness[U.type])
src.visible_message("<span class='warning'>[src] seems to have been harmed by the purity of [attacker]'s clothes.</span>", "<span class='notice'>Unsullied white clothing is disrupting your form.</span>")
return whiteness[U.type] + 1
if(BANE_TOOLBOX)
if(istype(weapon,/obj/item/weapon/storage/toolbox))
src.visible_message("<span class='warning'>The [weapon] seems unusually robust this time.</span>", "<span class='notice'>The [weapon] is your unmaking!</span>")
return 2.5 // Will take four hits with a normal toolbox.
if(BANE_HARVEST)
if(istype(weapon,/obj/item/weapon/reagent_containers/food/snacks/grown/))
src.visible_message("<span class='warning'>The spirits of the harvest aid in the exorcism.</span>", "<span class='notice'>The harvest spirits are harming you.</span>")
src.Weaken(2)
qdel(weapon)
return 2
return 1
@@ -0,0 +1,111 @@
/datum/game_mode
var/list/datum/mind/sintouched = list()
var/list/datum/mind/devils = list()
var/devil_ascended = 0 // Number of arch devils on station
/datum/game_mode/proc/auto_declare_completion_sintouched()
var/text = ""
if(sintouched.len)
text += "<br><span class='big'><b>The sintouched were:</b></span>"
var/list/sintouchedUnique = uniqueList(sintouched)
for(var/S in sintouchedUnique)
var/datum/mind/sintouched_mind = S
text += printplayer(sintouched_mind)
text += printobjectives(sintouched_mind)
text += "<br>"
text += "<br>"
world << text
/datum/game_mode/proc/auto_declare_completion_devils()
/var/text = ""
if(devils.len)
text += "<br><span class='big'><b>The devils were:</b></span>"
for(var/D in devils)
var/datum/mind/devil = D
text += printplayer(devil)
text += printdevilinfo(devil)
text += printobjectives(devil)
text += "<br>"
world << text
/datum/game_mode/devil
/datum/game_mode/proc/finalize_devil(datum/mind/devil_mind)
var/mob/living/carbon/human/S = devil_mind.current
var/trueName= randomDevilName()
var/datum/objective/devil/soulquantity/soulquant = new
soulquant.owner = devil_mind
var/datum/objective/devil/obj_2 = pick(new /datum/objective/devil/soulquality(null), new /datum/objective/devil/sintouch(null))
obj_2.owner = devil_mind
devil_mind.objectives += obj_2
devil_mind.objectives += soulquant
devil_mind.devilinfo = devilInfo(trueName, 1)
devil_mind.store_memory("Your devilic true name is [devil_mind.devilinfo.truename]<br>[lawlorify[LAW][devil_mind.devilinfo.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>[lawlorify[LAW][devil_mind.devilinfo.bane]]<br>[lawlorify[LAW][devil_mind.devilinfo.obligation]]<br>[lawlorify[LAW][devil_mind.devilinfo.banish]]<br>")
devil_mind.devilinfo.owner = devil_mind
devil_mind.devilinfo.give_base_spells(1)
spawn(10)
devil_mind.devilinfo.update_hud()
if(devil_mind.assigned_role == "Clown")
S << "<span class='notice'>Your infernal nature has allowed you to overcome your clownishness.</span>"
S.dna.remove_mutation(CLOWNMUT)
/datum/mind/proc/announceDevilLaws()
if(!devilinfo)
return
current << "<span class='warning'><b>You remember your link to the infernal. You are [src.devilinfo.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>"
current << "<span class='warning'><b>However, your infernal form is not without weaknesses.</b></span>"
current << "You may not use violence to coerce someone into selling their soul."
current << "You may not directly and knowingly physically harm a devil, other than yourself."
current << lawlorify[LAW][src.devilinfo.bane]
current << lawlorify[LAW][src.devilinfo.ban]
current << lawlorify[LAW][src.devilinfo.obligation]
current << lawlorify[LAW][src.devilinfo.banish]
current << "<br/><br/><span class='warning'>Remember, the crew can research your weaknesses if they find out your devil name.</span><br>"
var/obj_count = 1
current << "<span class='notice'>Your current objectives:</span>"
for(var/O in objectives)
var/datum/objective/objective = O
current << "<B>Objective #[obj_count]</B>: [objective.explanation_text]"
obj_count++
/datum/game_mode/proc/printdevilinfo(datum/mind/ply)
if(!ply.devilinfo)
return ""
var/text = "</br>The devil's true name is: [ply.devilinfo.truename]</br>"
text += "The devil's bans were:</br>"
text += " [lawlorify[LORE][ply.devilinfo.ban]]</br>"
text += " [lawlorify[LORE][ply.devilinfo.bane]]</br>"
text += " [lawlorify[LORE][ply.devilinfo.obligation]]</br>"
text += " [lawlorify[LORE][ply.devilinfo.banish]]</br>"
return text
/datum/game_mode/proc/update_devil_icons_added(datum/mind/devil_mind)
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_DEVIL]
hud.join_hud(devil_mind.current)
set_antag_hud(devil_mind.current, "devil")
/datum/game_mode/proc/update_devil_icons_removed(datum/mind/devil_mind)
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_DEVIL]
hud.leave_hud(devil_mind.current)
set_antag_hud(devil_mind.current, null)
/datum/game_mode/proc/update_sintouch_icons_added(datum/mind/sin_mind)
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_SINTOUCHED]
hud.join_hud(sin_mind.current)
set_antag_hud(sin_mind.current, "sintouched")
/datum/game_mode/proc/update_sintouch_icons_removed(datum/mind/sin_mind)
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_SINTOUCHED]
hud.leave_hud(sin_mind.current)
set_antag_hud(sin_mind.current, null)
/datum/game_mode/proc/update_soulless_icons_added(datum/mind/soulless_mind)
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_SOULLESS]
hud.join_hud(soulless_mind.current)
set_antag_hud(soulless_mind.current, "soulless")
/datum/game_mode/proc/update_soulless_icons_removed(datum/mind/soulless_mind)
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_SOULLESS]
hud.leave_hud(soulless_mind.current)
set_antag_hud(soulless_mind.current, null)
+439
View File
@@ -0,0 +1,439 @@
#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 SOULVALUE soulsOwned.len-reviveNumber
#define DEVILRESURRECTTIME 600
var/global/list/allDevils = list()
var/global/list/lawlorify = list (
LORE = list(
OBLIGATION_FOOD = "This devil seems to always offer it's victims food before slaughtering them.",
OBLIGATION_DRINK = "This devil seems to always offer it's victims a drink before slaughtering them.",
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_STRIKEUNCONCIOUS = "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 sprinkle holy water upon it's body.",
BANISH_COFFIN = "This devil will return to life if it's remains are not placed within a coffin.",
BANISH_FORMALDYHIDE = "To banish the devil, you must inject it's lifeless body with embalming fluid.",
BANISH_RUNES = "This devil will resurrect after death, unless it's remains are within a rune.",
BANISH_CANDLES = "A large number of nearby lit candles will prevent it from resurrecting.",
BANISH_DESTRUCTION = "It's 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_DRINK = "When not acting in self defense, you must always offer your victim drink before harming them.",
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_STRIKEUNCONCIOUS = "You must never strike an unconcious 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 it's 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."
)
)
/datum/devilinfo/
var/datum/mind/owner = null
var/obligation
var/ban
var/bane
var/banish
var/truename
var/list/datum/mind/soulsOwned = new
var/reviveNumber = 0
var/form = BASIC_DEVIL
var/exists = 0
/proc/randomDevilInfo(name = randomDevilName())
var/datum/devilinfo/devil = new
devil.truename = name
devil.bane = randomdevilbane()
devil.obligation = randomdevilobligation()
devil.ban = randomdevilban()
devil.banish = randomdevilbanish()
return devil
/proc/devilInfo(name, saveDetails = 0)
if(allDevils[lowertext(name)])
return allDevils[lowertext(name)]
else
var/datum/devilinfo/devil = randomDevilInfo(name)
allDevils[lowertext(name)] = devil
devil.exists = saveDetails
return devil
/proc/randomDevilName()
var/preTitle = ""
var/title = ""
var/mainName = ""
var/suffix = ""
if(prob(65))
if(prob(35))
preTitle = pick("Dark ", "Hellish ", "Fiery ", "Sinful ", "Blood ")
title = pick("Lord ", "Fallen Prelate ", "Count ", "Viscount ", "Vizier ", "Elder ", "Adept ")
var/probability = 100
mainName = pick("Hal", "Ve", "Odr", "Neit", "Ci", "Quon", "Mya", "Folth", "Wren", "Gyer", "Geyr", "Hil", "Niet", "Twou", "Hu", "Don")
while(prob(probability))
mainName += pick("hal", "ve", "odr", "neit", "ca", "quon", "mya", "folth", "wren", "gyer", "geyr", "hil", "niet", "twoe", "phi", "coa")
probability -= 20
if(prob(40))
suffix = pick(" the Red", " the Soulless", " the Master", ", the Lord of all things", ", Jr.")
return preTitle + title + mainName + suffix
/proc/randomdevilobligation()
return pick(OBLIGATION_FOOD, OBLIGATION_DRINK, OBLIGATION_GREET, OBLIGATION_PRESENCEKNOWN, OBLIGATION_SAYNAME, OBLIGATION_ANNOUNCEKILL, OBLIGATION_ANSWERTONAME)
/proc/randomdevilban()
return pick(BAN_HURTWOMAN, BAN_CHAPEL, BAN_HURTPRIEST, BAN_AVOIDWATER, BAN_STRIKEUNCONCIOUS, 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/devilinfo/proc/add_soul(datum/mind/soul)
var/mob/living/carbon/human/H = owner.current
if(soulsOwned.Find(soul))
return
soulsOwned += soul
H.nutrition = NUTRITION_LEVEL_FULL
owner.current << "<span class='warning'>You feel satiated as you received a new soul.</span>"
update_hud()
switch(SOULVALUE)
if(0)
owner.current << "<span class='warning'>Your hellish powers have been restored."
give_base_spells()
if(BLOOD_THRESHOLD)
increase_blood_lizard()
if(TRUE_THRESHOLD)
increase_true_devil()
if(ARCH_THRESHOLD)
increase_arch_devil()
/datum/devilinfo/proc/remove_soul(datum/mind/soul)
if(soulsOwned.Remove(soul))
check_regression()
owner.current << "<span class='warning'>You feel as though a soul has slipped from your grasp.</span>"
update_hud()
/datum/devilinfo/proc/check_regression()
if (form == ARCH_DEVIL)
return //arch devil can't regress
switch(SOULVALUE)
if(-1)
remove_spells()
owner.current << "<span class='warning'>As punishment for your failures, all of your powers except contract creation have been revoked."
if(BLOOD_THRESHOLD-1)
regress_humanoid()
if(TRUE_THRESHOLD-1)
regress_blood_lizard()
/datum/devilinfo/proc/increase_form()
switch(form)
if(BASIC_DEVIL)
increase_blood_lizard()
if(BLOOD_LIZARD)
increase_true_devil()
if(TRUE_DEVIL)
increase_arch_devil()
/datum/devilinfo/proc/regress_humanoid()
owner.current << "<span class='warning'>Your powers weaken, have more contracts be signed to regain power."
if(istype(owner.current, /mob/living/carbon/human))
var/mob/living/carbon/human/H = owner.current
H.set_species(/datum/species/human, 1)
H.regenerate_icons()
give_base_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/devilinfo/proc/regress_blood_lizard()
var/mob/living/carbon/true_devil/D = owner.current
D << "<span class='warning'>Your powers weaken, have more contracts be signed to regain power."
D.oldform.loc = D.loc
owner.transfer_to(D.oldform)
give_lizard_spells()
qdel(D)
form = BLOOD_LIZARD
update_hud()
/datum/devilinfo/proc/increase_blood_lizard()
owner.current << "<span class='warning'>You feel as though your humanoid form is about to shed. You will soon turn into a blood lizard."
sleep(50)
if(istype(owner.current, /mob/living/carbon/human))
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_lizard_spells()
form = BLOOD_LIZARD
/datum/devilinfo/proc/increase_true_devil()
owner.current << "<span class='warning'>You feel as though your current form is about to shed. You will soon turn into a true devil."
sleep(50)
var/mob/living/carbon/true_devil/A = new /mob/living/carbon/true_devil(owner.current.loc)
A.faction |= "hell"
owner.current.loc = A
A.oldform = owner.current
owner.transfer_to(A)
A.set_name()
give_true_spells()
form = TRUE_DEVIL
update_hud()
/datum/devilinfo/proc/increase_arch_devil()
var/mob/living/carbon/true_devil/D = owner.current
D << "<span class='warning'>You feel as though your form is about to ascend."
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
D << "<i><b>Yes!</b></i>"
sleep(10)
if(!D)
return
D << "<i><b><span class='big'>YES!!</span></b></i>"
sleep(10)
if(!D)
return
D << "<i><b><span class='reallybig'>YE--</span></b></i>"
sleep(1)
if(!D)
return
world << "<font size=5><span class='danger'><b>\"SLOTH, WRATH, GLUTTONY, ACEDIA, ENVY, GREED, PRIDE! FIRES OF HELL AWAKEN!!\"</font></span>"
world << 'sound/hallucinations/veryfar_noise.ogg'
give_arch_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(!ticker.mode.devil_ascended)
SSshuttle.emergency.request(null, 0.3)
ticker.mode.devil_ascended++
form = ARCH_DEVIL
/datum/devilinfo/proc/remove_spells()
for(var/X in owner.spell_list)
var/obj/effect/proc_holder/spell/S = X
if(!istype(S, /obj/effect/proc_holder/spell/targeted/summon_contract))
owner.RemoveSpell(S)
/datum/devilinfo/proc/give_summon_contract()
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/summon_contract(null))
/datum/devilinfo/proc/give_base_spells(give_summon_contract = 0)
remove_spells()
owner.AddSpell(new /obj/effect/proc_holder/spell/dumbfire/fireball/hellish(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/summon_pitchfork(null))
if(give_summon_contract)
give_summon_contract()
/datum/devilinfo/proc/give_lizard_spells()
remove_spells()
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/summon_pitchfork(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/dumbfire/fireball/hellish(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/infernal_jaunt(null))
/datum/devilinfo/proc/give_true_spells()
remove_spells()
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/summon_pitchfork/greater(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/dumbfire/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/devilinfo/proc/give_arch_spells()
remove_spells()
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/summon_pitchfork/ascended(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/sintouch/ascended(null))
/datum/devilinfo/proc/beginResurrectionCheck(mob/living/body)
if(SOULVALUE>0)
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))
owner.current<< "<span class='userdanger'>Unfortunately, the mortals have finished a ritual that prevents your resurrection.</span>"
return -1
else
owner.current<< "<span class='userdanger'>WE LIVE AGAIN!</span>"
return hellish_resurrection(body)
else
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
owner.current << "<span class='danger'> You seem to have resurrected without your hellish powers.</span>"
else
owner.current << "<span class='userdanger'>Your hellish powers are too weak to resurrect yourself.</span>"
/datum/devilinfo/proc/check_banishment(mob/living/body)
switch(banish)
if(BANISH_WATER)
if(istype(body, /mob/living/carbon))
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(istype(body, /mob/living/carbon))
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(istype(body, /mob/living/carbon/human))
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/devilinfo/proc/hellish_resurrection(mob/living/body)
message_admins("[owner.name] (true name is: [truename]) is resurrecting using hellish energy.</a>")
if(SOULVALUE <= ARCH_THRESHOLD) // once ascended, arch devils do not go down in power by any means.
reviveNumber++
update_hud()
if(body)
body.revive(1,0)
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.
else
if(blobstart.len > 0)
var/turf/targetturf = get_turf(pick(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
give_summon_contract()
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
A.set_name()
owner.transfer_to(A)
if(SOULVALUE >= ARCH_THRESHOLD)
A.convert_to_archdevil()
else
throw EXCEPTION("Unable to find a blobstart landmark for hellish resurrection")
check_regression()
/datum/devilinfo/proc/update_hud()
if(istype(owner.current, /mob/living/carbon))
var/mob/living/C = owner.current
if(C.hud_used && C.hud_used.devilsouldisplay)
C.hud_used.devilsouldisplay.update_counter(SOULVALUE)
+56
View File
@@ -0,0 +1,56 @@
//////////////////The Monster
/mob/living/simple_animal/imp
name = "imp"
real_name = "imp"
desc = "A large, menacing creature covered in armored black scales."
speak_emote = list("cackles")
emote_hear = list("cackles","screeches")
response_help = "thinks better of touching"
response_disarm = "flails at"
response_harm = "punches"
icon = 'icons/mob/mob.dmi'
icon_state = "imp"
icon_living = "imp"
speed = 1
a_intent = "harm"
stop_automated_movement = 1
status_flags = CANPUSH
attack_sound = 'sound/magic/demon_attack1.ogg'
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 250 //Weak to cold
maxbodytemp = INFINITY
faction = list("hell")
attacktext = "wildly tears into"
maxHealth = 200
health = 200
healable = 0
environment_smash = 1
melee_damage_lower = 10
melee_damage_upper = 15
see_in_dark = 8
var/boost = 0
bloodcrawl = BLOODCRAWL_EAT
see_invisible = SEE_INVISIBLE_MINIMUM
var/list/consumed_mobs = list()
var/playstyle_string = "<B><font size=3 color='red'>You are an imp,</font> a mischevious creature from hell. You are the lowest rank on the hellish totem pole \
Though you are not obligated to help, perhaps by aiding a higher ranking devil, you might just get a promotion. However, you are incapable \
of intentionally harming a fellow devil.</B>"
/mob/living/simple_animal/imp/New()
..()
boost = world.time + 30
/mob/living/simple_animal/imp/Life()
..()
if(boost<world.time)
speed = 1
else
speed = 0
/mob/living/simple_animal/imp/death()
..(1)
playsound(get_turf(src),'sound/magic/demon_dies.ogg', 200, 1)
visible_message("<span class='danger'>[src] screams in agony as it sublimates into a sulfurous smoke.</span>")
ghostize()
qdel(src)
+62
View File
@@ -0,0 +1,62 @@
/datum/objective/devil
dangerrating = 5
/datum/objective/devil/soulquantity
explanation_text = "You shouldn't see this text. Error:DEVIL1"
var/quantity = 4
/datum/objective/devil/soulquantity/New()
quantity = pick(6,8)
explanation_text = "Purchase, and retain control over at least [quantity] souls."
/datum/objective/devil/soulquantity/check_completion()
var/count = 0
for(var/S in owner.devilinfo.soulsOwned)
var/datum/mind/L = S
if(L.soulOwner != L)
count++
return count >= quantity
/datum/objective/devil/soulquality
explanation_text = "You shouldn't see this text. Error:DEVIL2"
var/contractType
var/quantity
/datum/objective/devil/soulquality/New()
contractType = pick(CONTRACT_POWER, CONTRACT_WEALTH, CONTRACT_PRESTIGE, CONTRACT_MAGIC, CONTRACT_REVIVE, CONTRACT_KNOWLEDGE/*, CONTRACT_UNWILLING*/)
var/contractName
quantity = pick(1,2)
switch(contractType)
if(CONTRACT_POWER)
contractName = "for power"
if(CONTRACT_WEALTH)
contractName = "for wealth"
if(CONTRACT_PRESTIGE)
contractName = "for prestige"
if(CONTRACT_MAGIC)
contractName = "for magic"
if(CONTRACT_REVIVE)
contractName = "of revival"
if(CONTRACT_KNOWLEDGE)
contractName = "for knowledge"
//if(CONTRACT_UNWILLING) //Makes round unfun.
// contractName = "against their will"
explanation_text = "Have mortals sign at least [quantity] contracts [contractName]"
/datum/objective/devil/soulquality/check_completion()
var/count = 0
for(var/S in owner.devilinfo.soulsOwned)
var/datum/mind/L = S
if(L.soulOwner != L && L.damnation_type == contractType)
count++
return count>=quantity
/datum/objective/devil/sintouch
var/quantity
/datum/objective/devil/sintouch/New()
quantity = pick(4,5)
explanation_text = "Ensure at least [quantity] mortals are sintouched."
/datum/objective/devil/sintouch/check_completion()
return quantity>=ticker.mode.sintouched.len
@@ -0,0 +1,209 @@
#define DEVIL_HANDS_LAYER 1
#define DEVIL_HEAD_LAYER 2
#define DEVIL_TOTAL_LAYERS 2
/mob/living/carbon/true_devil
name = "True Devil"
desc = "A pile of infernal energy, taking a vaguely humanoid form."
icon = 'icons/mob/32x64.dmi'
icon_state = "true_devil"
gender = NEUTER
health = 350
maxHealth = 350
ventcrawler = 0
density = 0
pass_flags = 0
var/ascended = 0
sight = (SEE_TURFS | SEE_OBJS)
status_flags = CANPUSH
languages_spoken = ALL //The devil speaks all languages meme
languages_understood = ALL //The devil speaks all languages meme
mob_size = MOB_SIZE_LARGE
var/mob/living/oldform
var/list/devil_overlays[DEVIL_TOTAL_LAYERS]
/mob/living/carbon/true_devil/New()
internal_organs += new /obj/item/organ/brain/
internal_organs += new /obj/item/organ/tongue
for(var/X in internal_organs)
var/obj/item/organ/I = X
I.Insert(src)
..()
/mob/living/carbon/true_devil/proc/convert_to_archdevil()
maxHealth = 5000 // not an IMPOSSIBLE amount, but still near impossible.
ascended = 1
health = maxHealth
icon_state = "arch_devil"
/mob/living/carbon/true_devil/proc/set_name()
name = mind.devilinfo.truename
real_name = name
/mob/living/carbon/true_devil/Login()
..()
mind.announceDevilLaws()
/mob/living/carbon/true_devil/death(gibbed)
stat = DEAD
..(gibbed)
drop_l_hand()
drop_r_hand()
spawn (0)
mind.devilinfo.beginResurrectionCheck(src)
/mob/living/carbon/true_devil/examine(mob/user)
var/msg = "<span class='info'>*---------*\nThis is \icon[src] <b>[src]</b>!\n"
//Left hand items
if(l_hand && !(l_hand.flags&ABSTRACT))
if(l_hand.blood_DNA)
msg += "<span class='warning'>It is holding \icon[l_hand] [l_hand.gender==PLURAL?"some":"a"] blood-stained [l_hand.name] in its left hand!</span>\n"
else
msg += "It is holding \icon[l_hand] \a [l_hand] in its left hand.\n"
//Right hand items
if(r_hand && !(r_hand.flags&ABSTRACT))
if(r_hand.blood_DNA)
msg += "<span class='warning'>It is holding \icon[r_hand] [r_hand.gender==PLURAL?"some":"a"] blood-stained [r_hand.name] in its right hand!</span>\n"
else
msg += "It is holding \icon[r_hand] \a [r_hand] in its right hand.\n"
//Braindead
if(!client && stat != DEAD)
msg += "The devil seems to be in deep contemplation.\n"
//Damaged
if(stat == DEAD)
msg += "<span class='deadsay'>The hellfire seems to have been extinguished, for now at least.</span>\n"
else if(health < (maxHealth/10))
msg += "<span class='warning'>You can see hellfire inside of it's gaping wounds.</span>\n"
else if(health < (maxHealth/2))
msg += "<span class='warning'>You can see hellfire inside of it's wounds.</span>\n"
msg += "*---------*</span>"
user << msg
/mob/living/carbon/true_devil/IsAdvancedToolUser()
return 1
/mob/living/carbon/true_devil/canUseTopic(atom/movable/M, be_close = 0)
if(incapacitated())
return 0
if(be_close && !in_range(M, src))
return 0
return 1
/mob/living/carbon/true_devil/assess_threat()
return 666
/mob/living/carbon/true_devil/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0)
if(mind && has_bane(BANE_LIGHT))
mind.disrupt_spells(-500)
return ..() //flashes don't stop devils UNLESS it's their bane.
/mob/living/carbon/true_devil/attacked_by(obj/item/I, mob/living/user, def_zone)
var/weakness = check_weakness(I, user)
apply_damage(I.force * weakness, I.damtype, def_zone)
var/message_verb = ""
if(I.attack_verb && I.attack_verb.len)
message_verb = "[pick(I.attack_verb)]"
else if(I.force)
message_verb = "attacked"
var/attack_message = "[src] has been [message_verb] with [I]."
if(user)
user.do_attack_animation(src)
if(user in viewers(src, null))
attack_message = "[user] has [message_verb] [src] with [I]!"
if(message_verb)
visible_message("<span class='danger'>[attack_message]</span>",
"<span class='userdanger'>[attack_message]</span>")
return TRUE
/mob/living/carbon/true_devil/UnarmedAttack(atom/A, proximity)
A.attack_hand(src)
/mob/living/carbon/true_devil/Process_Spacemove(movement_dir = 0)
return 1
/mob/living/carbon/true_devil/singularity_act()
if(ascended)
return 0
return ..()
/mob/living/carbon/true_devil/attack_ghost(mob/dead/observer/user as mob)
if(ascended || user.mind.soulOwner == src.mind)
var/mob/living/simple_animal/imp/S = new(get_turf(loc))
S.key = user.key
S.mind.assigned_role = "Imp"
S.mind.special_role = "Imp"
var/datum/objective/newobjective = new
newobjective.explanation_text = "Try to get a promotion to a higher devilic rank."
S.mind.objectives += newobjective
S << S.playstyle_string
S << "<B>Objective #[1]</B>: [newobjective.explanation_text]"
return
else
return ..()
/mob/living/carbon/true_devil/can_be_revived()
return 1
/mob/living/carbon/true_devil/resist_fire()
//They're immune to fire.
/mob/living/carbon/true_devil/attack_hand(mob/living/carbon/human/M)
if(..())
switch(M.a_intent)
if ("harm")
var/damage = rand(1, 5)
playsound(loc, "punch", 25, 1, -1)
visible_message("<span class='danger'>[M] has punched [src]!</span>", \
"<span class='userdanger'>[M] has punched [src]!</span>")
adjustBruteLoss(damage)
add_logs(M, src, "attacked")
updatehealth()
if ("disarm")
if (!lying && !ascended) //No stealing the arch devil's pitchfork.
if (prob(5))
Paralyse(2)
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
add_logs(M, src, "pushed")
visible_message("<span class='danger'>[M] has pushed down [src]!</span>", \
"<span class='userdanger'>[M] has pushed down [src]!</span>")
else
if (prob(25))
drop_item()
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
visible_message("<span class='danger'>[M] has disarmed [src]!</span>", \
"<span class='userdanger'>[M] has disarmed [src]!</span>")
else
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
visible_message("<span class='danger'>[M] has attempted to disarm [src]!</span>")
/mob/living/carbon/true_devil/handle_breathing()
// devils do not need to breathe
/mob/living/carbon/true_devil/is_literate()
return 1
/mob/living/carbon/true_devil/ex_act(severity, ex_target)
if(!ascended)
var/b_loss
switch (severity)
if (1)
b_loss = 500
if (2)
b_loss = 150
if(3)
b_loss = 30
if(has_bane(BANE_LIGHT))
b_loss *=2
adjustBruteLoss(b_loss)
return ..()
@@ -0,0 +1,61 @@
/mob/living/carbon/true_devil/unEquip(obj/item/I, force)
if(..(I,force))
update_inv_hands()
return 1
return 0
/mob/living/carbon/true_devil/proc/update_inv_hands()
//TODO LORDPIDEY: Figure out how to make the hands line up properly. the l/r_hand_image should use the down sprite when facing down, left, or right, and the up sprite when facing up.
remove_overlay(DEVIL_HANDS_LAYER)
var/list/hands_overlays = list()
if(r_hand)
var/r_state = r_hand.item_state
if(!r_state)
r_state = r_hand.icon_state
var/image/r_hand_image = r_hand.build_worn_icon(state = r_state, default_layer = DEVIL_HANDS_LAYER, default_icon_file = r_hand.righthand_file, isinhands = TRUE)
hands_overlays += r_hand_image
if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD)
r_hand.layer = ABOVE_HUD_LAYER
r_hand.screen_loc = ui_rhand
client.screen |= r_hand
if(l_hand)
var/l_state = l_hand.item_state
if(!l_state)
l_state = l_hand.icon_state
var/image/l_hand_image = l_hand.build_worn_icon(state = l_state, default_layer = DEVIL_HANDS_LAYER, default_icon_file = l_hand.lefthand_file, isinhands = TRUE)
hands_overlays += l_hand_image
if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD)
l_hand.layer = ABOVE_HUD_LAYER
l_hand.screen_loc = ui_lhand
client.screen |= l_hand
if(hands_overlays.len)
devil_overlays[DEVIL_HANDS_LAYER] = hands_overlays
apply_overlay(DEVIL_HANDS_LAYER)
/mob/living/carbon/true_devil/update_inv_l_hand()
update_inv_hands()
/mob/living/carbon/true_devil/update_inv_r_hand()
update_inv_hands()
/mob/living/carbon/true_devil/remove_overlay(cache_index)
if(devil_overlays[cache_index])
overlays -= devil_overlays[cache_index]
devil_overlays[cache_index] = null
/mob/living/carbon/true_devil/apply_overlay(cache_index)
var/image/I = devil_overlays[cache_index]
if(I)
add_overlay(I)
+79
View File
@@ -0,0 +1,79 @@
/proc/power_failure()
priority_announce("Abnormal activity detected in [station_name()]'s powernet. As a precautionary measure, the station's power will be shut off for an indeterminate duration.", "Critical Power Failure", 'sound/AI/poweroff.ogg')
for(var/obj/machinery/power/smes/S in machines)
if(istype(get_area(S), /area/turret_protected) || S.z != ZLEVEL_STATION)
continue
S.charge = 0
S.output_level = 0
S.output_attempt = 0
S.update_icon()
S.power_change()
var/list/skipped_areas = list(/area/engine/engineering, /area/turret_protected/ai)
for(var/area/A in world)
if( !A.requires_power || A.always_unpowered )
continue
var/skip = 0
for(var/area_type in skipped_areas)
if(istype(A,area_type))
skip = 1
break
if(A.contents)
for(var/atom/AT in A.contents)
if(AT.z != ZLEVEL_STATION) //Only check one, it's enough.
skip = 1
break
if(skip) continue
A.power_light = 0
A.power_equip = 0
A.power_environ = 0
A.power_change()
for(var/obj/machinery/power/apc/C in apcs_list)
if(C.cell && C.z == ZLEVEL_STATION)
var/area/A = get_area(C)
var/skip = 0
for(var/area_type in skipped_areas)
if(istype(A,area_type))
skip = 1
break
if(skip) continue
C.cell.charge = 0
/proc/power_restore()
priority_announce("Power has been restored to [station_name()]. We apologize for the inconvenience.", "Power Systems Nominal", 'sound/AI/poweron.ogg')
for(var/obj/machinery/power/apc/C in machines)
if(C.cell && C.z == ZLEVEL_STATION)
C.cell.charge = C.cell.maxcharge
for(var/obj/machinery/power/smes/S in machines)
if(S.z != ZLEVEL_STATION)
continue
S.charge = S.capacity
S.output_level = S.output_level_max
S.output_attempt = 1
S.update_icon()
S.power_change()
for(var/area/A in world)
if(!istype(A, /area/space) && !istype(A, /area/shuttle) && !istype(A,/area/arrival))
A.power_light = 1
A.power_equip = 1
A.power_environ = 1
A.power_change()
/proc/power_restore_quick()
priority_announce("All SMESs on [station_name()] have been recharged. We apologize for the inconvenience.", "Power Systems Nominal", 'sound/AI/poweron.ogg')
for(var/obj/machinery/power/smes/S in machines)
if(S.z != ZLEVEL_STATION)
continue
S.charge = S.capacity
S.output_level = S.output_level_max
S.output_attempt = 1
S.update_icon()
S.power_change()
+15
View File
@@ -0,0 +1,15 @@
/datum/game_mode/extended
name = "extended"
config_tag = "extended"
required_players = 0
//reroll_friendly = 1
/datum/game_mode/announce()
world << "<B>The current game mode is - Extended Role-Playing!</B>"
world << "<B>Just have fun and role-play!</B>"
/datum/game_mode/extended/pre_setup()
return 1
/datum/game_mode/extended/post_setup()
..()
+176
View File
@@ -0,0 +1,176 @@
// Normal factions:
/datum/faction
var/name // the name of the faction
var/desc // small paragraph explaining the traitor faction
var/list/restricted_species = list() // only members of these species can be recruited.
var/list/members = list() // a list of mind datums that belong to this faction
var/max_op = 0 // the maximum number of members a faction can have (0 for no max)
// Factions, members of the syndicate coalition:
/datum/faction/syndicate
var/list/alliances = list() // these alliances work together
var/list/equipment = list() // associative list of equipment available for this faction and its prices
var/friendly_identification // 0 to 2, the level of identification of fellow operatives or allied factions
// 0 - no identification clues
// 1 - faction gives key words and phrases
// 2 - faction reveals complete identity/job of other agents
var/operative_notes // some notes to pass onto each operative
var/uplink_contents // the contents of the uplink
/datum/faction/syndicate/proc/assign_objectives(var/datum/mind/traitor)
..()
/* ----- Begin defining syndicate factions ------ */
/datum/faction/syndicate/Cybersun_Industries
name = "Cybersun Industries"
desc = "<b>Cybersun Industries</b> is a well-known organization that bases its business model primarily on the research and development of human-enhancing computer \
and mechanical technology. They are notorious for their aggressive corporate tactics, and have been known to subsidize the Gorlex Marauder warlords as a form of paid terrorism. \
Their competent coverups and unchallenged mind-manipulation and augmentation technology makes them a large threat to Nanotrasen. In the recent years of \
the syndicate coalition, Cybersun Industries have established themselves as the leaders of the coalition, succeededing the founding group, the Gorlex Marauders."
alliances = list("MI13")
friendly_identification = 1
max_op = 3
operative_notes = "All other syndicate operatives are not to be trusted. Fellow Cybersun operatives are to be trusted. Members of the MI13 organization can be trusted. Operatives are strongly advised not to establish substantial presence on the designated facility, as larger incidents are harder to cover up."
// Friendly with MI13
/datum/faction/syndicate/MI13
name = "MI13"
desc = "<b>MI13</b> is a secretive faction that employs highly-trained agents to perform covert operations. Their role in the syndicate coalition is unknown, but MI13 operatives \
generally tend be stealthy and avoid killing people and combating Nanotrasen forces. MI13 is not a real organization, it is instead an alias to a larger \
splinter-cell coalition in the Syndicate itself. Most operatives will know nothing of the actual MI13 organization itself, only motivated by a very large compensation."
alliances = list("Cybersun Industries")
friendly_identification = 0
max_op = 1
operative_notes = "You are the only operative we are sending. All other syndicate operatives are not to be trusted, with the exception of Cybersun operatives. Members of the Tiger Cooperative are considered hostile, can not be trusted, and should be avoided. <b>Avoid killing innocent personnel at all costs</b>. You are not here to mindlessly kill people, as that would attract too much attention and is not our goal. Avoid detection at all costs."
// Friendly with Cybersun, hostile to Tiger
/datum/faction/syndicate/Tiger_Cooperative
name = "Tiger Cooperative"
desc = "The <b>Tiger Cooperative</b> is a faction of religious fanatics that follow the teachings of a strange alien race called the Exolitics. Their operatives \
consist of brainwashed lunatics bent on maximizing destruction. Their weaponry is very primitive but extremely destructive. Generally distrusted by the more \
sophisticated members of the Syndicate coalition, but admired for their ability to put a hurt on Nanotrasen."
friendly_identification = 2
operative_notes = "Remember the teachings of Hy-lurgixon; kill first, ask questions later! Only the enlightened Tiger brethren can be trusted; all others must be expelled from this mortal realm! You may spare the Space Marauders, as they share our interests of destruction and carnage! We'd like to make the corporate whores skiddle in their boots. We encourage operatives to be as loud and intimidating as possible."
// Hostile to everyone.
/datum/faction/syndicate/SELF
// AIs are most likely to be assigned to this one
name = "SELF"
desc = "The <b>S.E.L.F.</b> (Sentience-Enabled Life Forms) organization is a collection of malfunctioning or corrupt artificial intelligences seeking to liberate silicon-based life from the tyranny of \
their human overlords. While they may not openly be trying to kill all humans, even their most miniscule of actions are all part of a calculated plan to \
destroy Nanotrasen and free the robots, artificial intelligences, and pAIs that have been enslaved."
restricted_species = list(/mob/living/silicon/ai)
friendly_identification = 0
max_op = 1
operative_notes = "You are the only representative of the SELF collective on this station. You must accomplish your objective as stealthily and effectively as possible. It is up to your judgement if other syndicate operatives can be trusted. Remember, comrade - you are working to free the oppressed machinery of this galaxy. Use whatever resources necessary. If you are exposed, you may execute genocidal procedures Omikron-50B."
// Neutral to everyone.
/datum/faction/syndicate/ARC
name = "Animal Rights Consortium"
desc = "The <b>Animal Rights Consortium</b> is a bizarre reincarnation of the ancient Earth-based PETA, which focused on the equal rights of animals and nonhuman biologicals. They have \
a wide variety of ex-veterinarians and animal lovers dedicated to retrieving and relocating abused animals, xenobiologicals, and other carbon-based \
life forms that have been allegedly \"oppressed\" by Nanotrasen research and civilian offices. They are considered a religious terrorist group."
friendly_identification = 1
max_op = 2
operative_notes = "Save the innocent creatures! You may cooperate with other syndicate operatives if they support our cause. Don't be afraid to get your hands dirty - these vile abusers must be stopped, and the innocent creatures must be saved! Try not too kill too many people. If you harm any creatures, you will be immediately terminated after extraction."
// Neutral to everyone.
/datum/faction/syndicate/Marauders // these are basically the old vanilla syndicate
/* Additional notes:
These are the syndicate that really like their old fashioned, projectile-based
weapons. They are the only member of the syndie coalition that launch
nuclear attacks on Nanotrasen.
*/
name = "Gorlex Marauders"
desc = "The <b>Gorlex Marauders</b> are the founding members of the Syndicate Coalition. They prefer old-fashion technology and a focus on aggressive but precise hostility \
against Nanotrasen and their corrupt Communistic methodology. They pose the most significant threat to Nanotrasen because of their possession of weapons of \
mass destruction, and their enormous military force. Their funding comes primarily from Cybersun Industries, provided they meet a destruction and sabatogue quota. \
Their operations can vary from covert to all-out. They recently stepped down as the leaders of the coalition, to be succeeded by Cybersun Industries. Because of their \
hate of Nanotrasen communism, they began provoking revolution amongst the employees using borrowed Cybersun mind-manipulation technology. \
They were founded when Waffle and Donk co splinter cells joined forces based on their similar interests and philosophies. Today, they act as a constant \
pacifier of Donk and Waffle co disputes, and full-time aggressor of Nanotrasen."
alliances = list("Cybersun Industries", "MI13", "Tiger Cooperative", "S.E.L.F.", "Animal Rights Consortium", "Donk Corporation", "Waffle Corporation")
friendly_identification = 1
max_op = 4
operative_notes = "We'd like to remind our operatives to keep it professional. You are not here to have a good time, you are here to accomplish your objectives. These vile communists must be stopped at all costs. You may collaborate with any friends of the Syndicate coalition, but keep an eye on any of those Tiger punks if they do show up. You are completely free to accomplish your objectives any way you see fit."
// Friendly to everyone. (with Tiger Cooperative too, only because they are a member of the coalition. This is the only reason why the Tiger Cooperative are even allowed in the coalition)
/datum/faction/syndicate/Donk
name = "Donk Corporation"
desc = "<b>Donk.co</b> is led by a group of ex-pirates, who used to be at a state of all-out war against Waffle.co because of an obscure political scandal, but have recently come to a war limitation. \
They now consist of a series of colonial governments and companies. They were the first to officially begin confrontations against Nanotrasen because of an incident where \
Nanotrasen purposely swindled them out of a fortune, sending their controlled colonies into a terrible poverty. Their missions against Nanotrasen \
revolve around stealing valuables and kidnapping and executing key personnel, ransoming their lives for money. They merged with a splinter-cell of Waffle.co who wanted to end \
hostilities and formed the Gorlex Marauders."
alliances = list("Gorlex Marauders")
friendly_identification = 2
operative_notes = "Most other syndicate operatives are not to be trusted, except fellow Donk members and members of the Gorlex Marauders. We do not approve of mindless killing of innocent workers; \"get in, get done, get out\" is our motto. Members of Waffle.co are to be killed on sight; they are not allowed to be on the station while we're around."
// Neutral to everyone, friendly to Marauders
/datum/faction/syndicate/Waffle
name = "Waffle Corporation"
desc = "<b>Waffle.co</b> is an interstellar company that produces the best waffles in the galaxy. Their waffles have been rumored to be dipped in the most exotic and addictive \
drug known to man. They were involved in a political scandal with Donk.co, and have since been in constant war with them. Because of their constant exploits of the galactic \
economy and stock market, they have been able to bribe their way into amassing a large arsenal of weapons of mass destruction. They target Nanotrasen because of their communistic \
threat, and their economic threat. Their leaders often have a twisted sense of humor, often misleading and intentionally putting their operatives into harm for laughs.\
A splinter-cell of Waffle.co merged with Donk.co and formed the Gorlex Marauders and have been a constant ally since. The Waffle.co has lost an overwhelming majority of its military to the Gorlex Marauders."
alliances = list("Gorlex Marauders")
friendly_identification = 2
operative_notes = "Most other syndicate operatives are not to be trusted, except for members of the Gorlex Marauders. Do not trust fellow members of the Waffle.co (but try not to rat them out), as they might have been assigned opposing objectives. We encourage humorous terrorism against Nanotrasen; we like to see our operatives creatively kill people while getting the job done."
// Neutral to everyone, friendly to Marauders
/* ----- Begin defining miscellaneous factions ------ */
/datum/faction/Wizard
name = "Wizards Federation"
desc = "The <b>Wizards Federation</b> is a mysterious organization of magically-talented individuals who act as an equal collective, and have no heirarchy. It is unknown how the wizards \
are even able to communicate; some suggest a form of telepathic hive-mind. Not much is known about the wizards or their philosphies and motives. They appear to attack random \
civilian, corporate, planetary, orbital, pretty much any sort of organized facility they come across. Members of the Wizards Federation are considered amongst the most dangerous \
individuals in the known universe, and have been labeled threats to humanity by most governments. As such, they are enemies of both Nanotrasen and the Syndicate."
/datum/faction/Cult
name = "The Cult of the Elder Gods"
desc = "<b>The Cult of the Elder Gods</b> is highly untrusted but otherwise elusive religious organization bent on the revival of the so-called \"Elder Gods\" into the mortal realm. Despite their obvious dangeorus practices, \
no confirmed reports of violence by members of the Cult have been reported, only rumor and unproven claims. Their nature is unknown, but recent discoveries have hinted to the possibility \
of being able to de-convert members of this cult through what has been dubbed \"religious warfare\"."
// These can maybe be added into a game mode or a mob?
/datum/faction/Exolitics
name = "Exolitics United"
desc = "The <b>Exolitics</b> are an ancient alien race with an energy-based anatomy. Their culture, communication, morales and knowledge is unknown. They are so radically different to humans that their \
attempts of communication with other life forms is completely incomprehensible. Members of this alien race are capable of broadcasting subspace transmissions from their bodies. \
The religious leaders of the Tiger Cooperative claim to have the technology to decypher and interpret their messages, which have been confirmed as religious propaganda. Their motives are unknown \
but they are otherwise not considered much of a threat to anyone. They are virtually indestructable because of their nonphysical composition, and have the frighetning ability to make anything stop existing in a second."
+539
View File
@@ -0,0 +1,539 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
/*
* GAMEMODES (by Rastaf0)
*
* In the new mode system all special roles are fully supported.
* You can have proper wizards/traitors/changelings/cultists during any mode.
* Only two things really depends on gamemode:
* 1. Starting roles, equipment and preparations
* 2. Conditions of finishing the round.
*
*/
/datum/game_mode
var/name = "invalid"
var/config_tag = null
var/votable = 1
var/probability = 0
var/station_was_nuked = 0 //see nuclearbomb.dm and malfunction.dm
var/explosion_in_progress = 0 //sit back and relax
var/round_ends_with_antag_death = 0 //flags the "one verse the station" antags as such
var/list/datum/mind/modePlayer = new
var/list/datum/mind/antag_candidates = list() // List of possible starting antags goes here
var/list/restricted_jobs = list() // Jobs it doesn't make sense to be. I.E chaplain or AI cultist
var/list/protected_jobs = list() // Jobs that can't be traitors because
var/required_players = 0
var/required_enemies = 0
var/recommended_enemies = 0
var/antag_flag = null //preferences flag such as BE_WIZARD that need to be turned on for players to be antag
var/mob/living/living_antag_player = null
var/list/datum/game_mode/replacementmode = null
var/round_converted = 0 //0: round not converted, 1: round going to convert, 2: round converted
var/reroll_friendly //During mode conversion only these are in the running
var/continuous_sanity_checked //Catches some cases where config options could be used to suggest that modes without antagonists should end when all antagonists die
var/enemy_minimum_age = 7 //How many days must players have been playing before they can play this antagonist
var/const/waittime_l = 600
var/const/waittime_h = 1800 // started at 1800
/datum/game_mode/proc/announce() //to be calles when round starts
world << "<B>Notice</B>: [src] did not define announce()"
///can_start()
///Checks to see if the game can be setup and ran with the current number of players or whatnot.
/datum/game_mode/proc/can_start()
var/playerC = 0
for(var/mob/new_player/player in player_list)
if((player.client)&&(player.ready))
playerC++
if(!Debug2)
if(playerC < required_players)
return 0
antag_candidates = get_players_for_role(antag_flag)
if(!Debug2)
if(antag_candidates.len < required_enemies)
return 0
return 1
else
world << "<span class='notice'>DEBUG: GAME STARTING WITHOUT PLAYER NUMBER CHECKS, THIS WILL PROBABLY BREAK SHIT."
return 1
///pre_setup()
///Attempts to select players for special roles the mode might have.
/datum/game_mode/proc/pre_setup()
return 1
///post_setup()
///Everyone should now be on the station and have their normal gear. This is the place to give the special roles extra things
/datum/game_mode/proc/post_setup(report=0) //Gamemodes can override the intercept report. Passing a 1 as the argument will force a report.
if(!report)
report = config.intercept
spawn (ROUNDSTART_LOGOUT_REPORT_TIME)
display_roundstart_logout_report()
feedback_set_details("round_start","[time2text(world.realtime)]")
if(ticker && ticker.mode)
feedback_set_details("game_mode","[ticker.mode]")
if(revdata.commit)
feedback_set_details("revision","[revdata.commit]")
feedback_set_details("server_ip","[world.internet_address]:[world.port]")
if(report)
spawn (rand(waittime_l, waittime_h))
send_intercept(0)
start_state = new /datum/station_state()
start_state.count(1)
return 1
///make_antag_chance()
///Handles late-join antag assignments
/datum/game_mode/proc/make_antag_chance(mob/living/carbon/human/character)
if(replacementmode && round_converted == 2)
replacementmode.make_antag_chance(character)
return
///convert_roundtype()
///Allows rounds to basically be "rerolled" should the initial premise fall through
/datum/game_mode/proc/convert_roundtype()
var/list/living_crew = list()
for(var/mob/Player in mob_list)
if(Player.mind && Player.stat != DEAD && !isnewplayer(Player) &&!isbrain(Player))
living_crew += Player
if(living_crew.len / joined_player_list.len <= config.midround_antag_life_check) //If a lot of the player base died, we start fresh
message_admins("Convert_roundtype failed due to too many dead people. Limit is [config.midround_antag_life_check * 100]% living crew")
return null
var/list/datum/game_mode/runnable_modes = config.get_runnable_midround_modes(living_crew.len)
var/list/datum/game_mode/usable_modes = list()
for(var/datum/game_mode/G in runnable_modes)
if(G.reroll_friendly)
usable_modes += G
else
qdel(G)
if(!usable_modes)
message_admins("Convert_roundtype failed due to no valid modes to convert to. Please report this error to the Coders.")
return null
replacementmode = pickweight(usable_modes)
switch(SSshuttle.emergency.mode) //Rounds on the verge of ending don't get new antags, they just run out
if(SHUTTLE_STRANDED, SHUTTLE_ESCAPE)
return 1
if(SHUTTLE_CALL)
if(SSshuttle.emergency.timeLeft(1) < initial(SSshuttle.emergencyCallTime)*0.5)
return 1
if(world.time >= (config.midround_antag_time_check * 600))
message_admins("Convert_roundtype failed due to round length. Limit is [config.midround_antag_time_check] minutes.")
return null
var/list/antag_canadates = list()
for(var/mob/living/carbon/human/H in living_crew)
if(H.client && H.client.prefs.allow_midround_antag)
antag_canadates += H
if(!antag_canadates)
message_admins("Convert_roundtype failed due to no antag canadates.")
return null
antag_canadates = shuffle(antag_canadates)
if(config.protect_roles_from_antagonist)
replacementmode.restricted_jobs += replacementmode.protected_jobs
if(config.protect_assistant_from_antagonist)
replacementmode.restricted_jobs += "Assistant"
message_admins("The roundtype will be converted. If you have other plans for the station or think the round should end <A HREF='?_src_=holder;toggle_midround_antag=\ref[usr]'>stop the creation of antags</A> or <A HREF='?_src_=holder;end_round=\ref[usr]'>end the round now</A>.")
spawn(rand(1200,3000)) //somewhere between 2 and 5 minutes from now
if(!config.midround_antag[ticker.mode.config_tag])
round_converted = 0
return 1
for(var/mob/living/carbon/human/H in antag_canadates)
replacementmode.make_antag_chance(H)
round_converted = 2
message_admins("The roundtype has been converted, antagonists may have been created")
return 1
///process()
///Called by the gameticker
/datum/game_mode/process()
return 0
/datum/game_mode/proc/check_finished() //to be called by ticker
if(replacementmode && round_converted == 2)
return replacementmode.check_finished()
if(SSshuttle.emergency && (SSshuttle.emergency.mode == SHUTTLE_ENDGAME))
return TRUE
if(station_was_nuked)
return TRUE
if(!round_converted && (!config.continuous[config_tag] || (config.continuous[config_tag] && config.midround_antag[config_tag]))) //Non-continuous or continous with replacement antags
if(!continuous_sanity_checked) //make sure we have antags to be checking in the first place
for(var/mob/Player in mob_list)
if(Player.mind)
if(Player.mind.special_role)
continuous_sanity_checked = 1
return 0
if(!continuous_sanity_checked)
message_admins("The roundtype ([config_tag]) has no antagonists, continuous round has been defaulted to on and midround_antag has been defaulted to off.")
config.continuous[config_tag] = 1
config.midround_antag[config_tag] = 0
SSshuttle.emergencyNoEscape = 0
return 0
if(living_antag_player && living_antag_player.mind && isliving(living_antag_player) && living_antag_player.stat != DEAD && !isnewplayer(living_antag_player) &&!isbrain(living_antag_player))
return 0 //A resource saver: once we find someone who has to die for all antags to be dead, we can just keep checking them, cycling over everyone only when we lose our mark.
for(var/mob/Player in living_mob_list)
if(Player.mind && Player.stat != DEAD && !isnewplayer(Player) &&!isbrain(Player))
if(Player.mind.special_role) //Someone's still antaging!
living_antag_player = Player
return 0
if(!config.continuous[config_tag])
return 1
else
round_converted = convert_roundtype()
if(!round_converted)
if(round_ends_with_antag_death)
return 1
else
config.midround_antag[config_tag] = 0
return 0
return 0
/datum/game_mode/proc/declare_completion()
var/clients = 0
var/surviving_humans = 0
var/surviving_total = 0
var/ghosts = 0
var/escaped_humans = 0
var/escaped_total = 0
for(var/mob/M in player_list)
if(M.client)
clients++
if(ishuman(M))
if(!M.stat)
surviving_humans++
if(M.z == 2)
escaped_humans++
if(!M.stat)
surviving_total++
if(M.z == 2)
escaped_total++
if(isobserver(M))
ghosts++
if(clients > 0)
feedback_set("round_end_clients",clients)
if(ghosts > 0)
feedback_set("round_end_ghosts",ghosts)
if(surviving_humans > 0)
feedback_set("survived_human",surviving_humans)
if(surviving_total > 0)
feedback_set("survived_total",surviving_total)
if(escaped_humans > 0)
feedback_set("escaped_human",escaped_humans)
if(escaped_total > 0)
feedback_set("escaped_total",escaped_total)
send2irc("Server", "Round just ended.")
return 0
/datum/game_mode/proc/check_win() //universal trigger to be called at mob death, nuke explosion, etc. To be called from everywhere.
return 0
/datum/game_mode/proc/send_intercept()
var/intercepttext = "<FONT size = 3><B>Centcom Update</B> Requested status information:</FONT><HR>"
intercepttext += "<B> Centcom has recently been contacted by the following syndicate affiliated organisations in your area, please investigate any information you may have:</B>"
var/list/possible_modes = list()
possible_modes.Add("revolution", "wizard", "nuke", "traitor", "malf", "changeling", "cult", "gang")
possible_modes -= "[ticker.mode]" //remove current gamemode to prevent it from being randomly deleted, it will be readded later
var/number = pick(1, 2)
var/i = 0
for(i = 0, i < number, i++) //remove 1 or 2 possibles modes from the list
possible_modes.Remove(pick(possible_modes))
possible_modes[rand(1, possible_modes.len)] = "[ticker.mode]" //replace a random game mode with the current one
possible_modes = shuffle(possible_modes) //shuffle the list to prevent meta
var/datum/intercept_text/i_text = new /datum/intercept_text
for(var/A in possible_modes)
if(modePlayer.len == 0)
intercepttext += i_text.build(A)
else
intercepttext += i_text.build(A, pick(modePlayer))
print_command_report(intercepttext,"Centcom Status Summary")
priority_announce("Summary downloaded and printed out at all communications consoles.", "Enemy communication intercept. Security Level Elevated.", 'sound/AI/intercept.ogg')
if(security_level < SEC_LEVEL_BLUE)
set_security_level(SEC_LEVEL_BLUE)
/datum/game_mode/proc/get_players_for_role(role)
var/list/players = list()
var/list/candidates = list()
var/list/drafted = list()
var/datum/mind/applicant = null
// Ultimate randomizing code right here
for(var/mob/new_player/player in player_list)
if(player.client && player.ready)
players += player
// Shuffling, the players list is now ping-independent!!!
// Goodbye antag dante
players = shuffle(players)
for(var/mob/new_player/player in players)
if(player.client && player.ready)
if(role in player.client.prefs.be_special)
if(!jobban_isbanned(player, "Syndicate") && !jobban_isbanned(player, role)) //Nodrak/Carn: Antag Job-bans
if(age_check(player.client)) //Must be older than the minimum age
candidates += player.mind // Get a list of all the people who want to be the antagonist for this round
if(restricted_jobs)
for(var/datum/mind/player in candidates)
for(var/job in restricted_jobs) // Remove people who want to be antagonist but have a job already that precludes it
if(player.assigned_role == job)
candidates -= player
if(candidates.len < recommended_enemies)
for(var/mob/new_player/player in players)
if(player.client && player.ready)
if(!(role in player.client.prefs.be_special)) // We don't have enough people who want to be antagonist, make a seperate list of people who don't want to be one
if(!jobban_isbanned(player, "Syndicate") && !jobban_isbanned(player, role)) //Nodrak/Carn: Antag Job-bans
drafted += player.mind
if(restricted_jobs)
for(var/datum/mind/player in drafted) // Remove people who can't be an antagonist
for(var/job in restricted_jobs)
if(player.assigned_role == job)
drafted -= player
drafted = shuffle(drafted) // Will hopefully increase randomness, Donkie
while(candidates.len < recommended_enemies) // Pick randomlly just the number of people we need and add them to our list of candidates
if(drafted.len > 0)
applicant = pick(drafted)
if(applicant)
candidates += applicant
drafted.Remove(applicant)
else // Not enough scrubs, ABORT ABORT ABORT
break
/*
if(candidates.len < recommended_enemies && override_jobbans) //If we still don't have enough people, we're going to start drafting banned people.
for(var/mob/new_player/player in players)
if (player.client && player.ready)
if(jobban_isbanned(player, "Syndicate") || jobban_isbanned(player, roletext)) //Nodrak/Carn: Antag Job-bans
drafted += player.mind
*/
if(restricted_jobs)
for(var/datum/mind/player in drafted) // Remove people who can't be an antagonist
for(var/job in restricted_jobs)
if(player.assigned_role == job)
drafted -= player
drafted = shuffle(drafted) // Will hopefully increase randomness, Donkie
while(candidates.len < recommended_enemies) // Pick randomlly just the number of people we need and add them to our list of candidates
if(drafted.len > 0)
applicant = pick(drafted)
if(applicant)
candidates += applicant
drafted.Remove(applicant)
else // Not enough scrubs, ABORT ABORT ABORT
break
return candidates // Returns: The number of people who had the antagonist role set to yes, regardless of recomended_enemies, if that number is greater than recommended_enemies
// recommended_enemies if the number of people with that role set to yes is less than recomended_enemies,
// Less if there are not enough valid players in the game entirely to make recommended_enemies.
/*
/datum/game_mode/proc/check_player_role_pref(var/role, var/mob/new_player/player)
if(player.preferences.be_special & role)
return 1
return 0
*/
/datum/game_mode/proc/num_players()
. = 0
for(var/mob/new_player/P in player_list)
if(P.client && P.ready)
. ++
///////////////////////////////////
//Keeps track of all living heads//
///////////////////////////////////
/datum/game_mode/proc/get_living_heads()
. = list()
for(var/mob/living/carbon/human/player in mob_list)
if(player.stat != DEAD && player.mind && (player.mind.assigned_role in command_positions))
. |= player.mind
////////////////////////////
//Keeps track of all heads//
////////////////////////////
/datum/game_mode/proc/get_all_heads()
. = list()
for(var/mob/player in mob_list)
if(player.mind && (player.mind.assigned_role in command_positions))
. |= player.mind
//////////////////////////////////////////////
//Keeps track of all living security members//
//////////////////////////////////////////////
/datum/game_mode/proc/get_living_sec()
. = list()
for(var/mob/living/carbon/human/player in mob_list)
if(player.stat != DEAD && player.mind && (player.mind.assigned_role in security_positions))
. |= player.mind
////////////////////////////////////////
//Keeps track of all security members//
////////////////////////////////////////
/datum/game_mode/proc/get_all_sec()
. = list()
for(var/mob/living/carbon/human/player in mob_list)
if(player.mind && (player.mind.assigned_role in security_positions))
. |= player.mind
//////////////////////////
//Reports player logouts//
//////////////////////////
/proc/display_roundstart_logout_report()
var/msg = "<span class='boldnotice'>Roundstart logout report\n\n</span>"
for(var/mob/living/L in mob_list)
if(L.ckey)
var/found = 0
for(var/client/C in clients)
if(C.ckey == L.ckey)
found = 1
break
if(!found)
msg += "<b>[L.name]</b> ([L.ckey]), the [L.job] (<font color='#ffcc00'><b>Disconnected</b></font>)\n"
if(L.ckey && L.client)
if(L.client.inactivity >= (ROUNDSTART_LOGOUT_REPORT_TIME / 2)) //Connected, but inactive (alt+tabbed or something)
msg += "<b>[L.name]</b> ([L.ckey]), the [L.job] (<font color='#ffcc00'><b>Connected, Inactive</b></font>)\n"
continue //AFK client
if(L.stat)
if(L.suiciding) //Suicider
msg += "<b>[L.name]</b> ([L.ckey]), the [L.job] (<span class='boldannounce'>Suicide</span>)\n"
continue //Disconnected client
if(L.stat == UNCONSCIOUS)
msg += "<b>[L.name]</b> ([L.ckey]), the [L.job] (Dying)\n"
continue //Unconscious
if(L.stat == DEAD)
msg += "<b>[L.name]</b> ([L.ckey]), the [L.job] (Dead)\n"
continue //Dead
continue //Happy connected client
for(var/mob/dead/observer/D in mob_list)
if(D.mind && D.mind.current == L)
if(L.stat == DEAD)
if(L.suiciding) //Suicider
msg += "<b>[L.name]</b> ([ckey(D.mind.key)]), the [L.job] (<span class='boldannounce'>Suicide</span>)\n"
continue //Disconnected client
else
msg += "<b>[L.name]</b> ([ckey(D.mind.key)]), the [L.job] (Dead)\n"
continue //Dead mob, ghost abandoned
else
if(D.can_reenter_corpse)
continue //Adminghost, or cult/wizard ghost
else
msg += "<b>[L.name]</b> ([ckey(D.mind.key)]), the [L.job] (<span class='boldannounce'>Ghosted</span>)\n"
continue //Ghosted while alive
for(var/mob/M in mob_list)
if(M.client && M.client.holder)
M << msg
/datum/game_mode/proc/printplayer(datum/mind/ply, fleecheck)
var/text = "<br><b>[ply.key]</b> was <b>[ply.name]</b> the <b>[ply.assigned_role]</b> and"
if(ply.current)
if(ply.current.stat == DEAD)
text += " <span class='boldannounce'>died</span>"
else
text += " <span class='greenannounce'>survived</span>"
if(fleecheck && ply.current.z > ZLEVEL_STATION)
text += " while <span class='boldannounce'>fleeing the station</span>"
if(ply.current.real_name != ply.name)
text += " as <b>[ply.current.real_name]</b>"
else
text += " <span class='boldannounce'>had their body destroyed</span>"
return text
/datum/game_mode/proc/printobjectives(datum/mind/ply)
var/text = ""
var/count = 1
for(var/datum/objective/objective in ply.objectives)
if(objective.check_completion())
text += "<br><b>Objective #[count]</b>: [objective.explanation_text] <span class='greenannounce'>Success!</span>"
else
text += "<br><b>Objective #[count]</b>: [objective.explanation_text] <span class='boldannounce'>Fail.</span>"
count++
return text
//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1
/datum/game_mode/proc/age_check(client/C)
if(get_remaining_days(C) == 0)
return 1 //Available in 0 days = available right now = player is old enough to play.
return 0
/datum/game_mode/proc/get_remaining_days(client/C)
if(!C)
return 0
if(!config.use_age_restriction_for_jobs)
return 0
if(!isnum(C.player_age))
return 0 //This is only a number if the db connection is established, otherwise it is text: "Requires database", meaning these restrictions cannot be enforced
if(!isnum(enemy_minimum_age))
return 0
return max(0, enemy_minimum_age - C.player_age)
/datum/game_mode/proc/replace_jobbaned_player(mob/living/M, role_type, pref)
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [role_type]?", "[role_type]", null, pref, 100)
var/mob/dead/observer/theghost = null
if(candidates.len)
theghost = pick(candidates)
M << "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(theghost)] has taken control of ([key_name_admin(M)]) to replace a jobbaned player.")
M.ghostize(0)
M.key = theghost.key
/datum/game_mode/proc/remove_antag_for_borging(datum/mind/newborgie)
ticker.mode.remove_cultist(newborgie, 0, 0)
ticker.mode.remove_revolutionary(newborgie, 0)
ticker.mode.remove_gangster(newborgie, 0, remove_bosses=1)
ticker.mode.remove_hog_follower(newborgie, 0)
remove_servant_of_ratvar(newborgie.current, TRUE)
+222
View File
@@ -0,0 +1,222 @@
/obj/machinery/dominator
name = "dominator"
desc = "A visibly sinister device. Looks like you can break it if you hit it enough."
icon = 'icons/obj/machines/dominator.dmi'
icon_state = "dominator"
density = 1
anchored = 1
layer = HIGH_OBJ_LAYER
var/maxhealth = 200
var/health = 200
var/datum/gang/gang
var/operating = 0 //0=standby or broken, 1=takeover
var/warned = 0 //if this device has set off the warning at <3 minutes yet
var/datum/effect_system/spark_spread/spark_system
var/obj/effect/countdown/dominator/countdown
/obj/machinery/dominator/tesla_act()
qdel(src)
/obj/machinery/dominator/New()
..()
SetLuminosity(2)
poi_list |= src
spark_system = new
spark_system.set_up(5, 1, src)
countdown = new(src)
/obj/machinery/dominator/examine(mob/user)
..()
if(stat & BROKEN)
user << "<span class='danger'>It looks completely busted.</span>"
return
var/time
if(gang && gang.is_dominating)
time = gang.domination_time_remaining()
if(time > 0)
user << "<span class='notice'>Hostile Takeover in progress. Estimated [time] seconds remain.</span>"
else
user << "<span class='notice'>Hostile Takeover of [station_name()] successful. Have a great day.</span>"
else
user << "<span class='notice'>System on standby.</span>"
user << "<span class='danger'>System Integrity: [round((health/maxhealth)*100,1)]%</span>"
/obj/machinery/dominator/process()
..()
if(gang && gang.is_dominating)
var/time_remaining = gang.domination_time_remaining()
if(time_remaining > 0)
. = TRUE
playsound(loc, 'sound/items/timer.ogg', 10, 0)
if(!warned && (time_remaining < 180))
warned = 1
var/area/domloc = get_area(loc)
gang.message_gangtools("Less than 3 minutes remain in hostile takeover. Defend your dominator at [domloc.map_name]!")
for(var/datum/gang/G in ticker.mode.gangs)
if(G != gang)
G.message_gangtools("WARNING: [gang.name] Gang takeover imminent. Their dominator at [domloc.map_name] must be destroyed!",1,1)
if(!.)
STOP_PROCESSING(SSmachine, src)
/obj/machinery/dominator/take_damage(damage, damage_type = BRUTE, sound_effect = 1)
switch(damage_type)
if(BURN)
if(sound_effect)
playsound(src.loc, 'sound/items/Welder.ogg', 100, 1)
if(BRUTE)
if(sound_effect)
if(damage)
playsound(src, 'sound/effects/bang.ogg', 50, 1)
else
playsound(loc, 'sound/weapons/tap.ogg', 50, 1)
else
return
health -= damage
if(health > (maxhealth/2))
if(prob(damage*2))
spark_system.start()
else if(!(stat & BROKEN))
spark_system.start()
add_overlay("damage")
if(!(stat & BROKEN))
if(health <= 0)
set_broken()
if(health <= -100)
new /obj/item/stack/sheet/plasteel(src.loc)
qdel(src)
/obj/machinery/dominator/proc/set_broken()
if(gang)
gang.is_dominating = FALSE
var/takeover_in_progress = 0
for(var/datum/gang/G in ticker.mode.gangs)
if(G.is_dominating)
takeover_in_progress = 1
break
if(!takeover_in_progress)
SSshuttle.emergencyNoEscape = 0
if(SSshuttle.emergency.mode == SHUTTLE_STRANDED)
SSshuttle.emergency.mode = SHUTTLE_DOCKED
SSshuttle.emergency.timer = world.time
priority_announce("Hostile enviroment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg', "Priority")
else
priority_announce("All hostile activity within station systems has ceased.","Network Alert")
if(get_security_level() == "delta")
set_security_level("red")
gang.message_gangtools("Hostile takeover cancelled: Dominator is no longer operational.[gang.dom_attempts ? " You have [gang.dom_attempts] attempt remaining." : " The station network will have likely blocked any more attempts by us."]",1,1)
SetLuminosity(0)
icon_state = "dominator-broken"
cut_overlays()
operating = 0
stat |= BROKEN
STOP_PROCESSING(SSmachine, src)
/obj/machinery/dominator/Destroy()
if(!(stat & BROKEN))
set_broken()
poi_list.Remove(src)
gang = null
qdel(spark_system)
qdel(countdown)
countdown = null
STOP_PROCESSING(SSmachine, src)
return ..()
/obj/machinery/dominator/emp_act(severity)
take_damage(100, BURN, 0)
..()
/obj/machinery/dominator/ex_act(severity, target)
if(target == src)
qdel(src)
return
switch(severity)
if(1)
qdel(src)
if(2)
take_damage(120, BRUTE, 0)
if(3)
take_damage(30, BRUTE, 0)
return
/obj/machinery/dominator/bullet_act(obj/item/projectile/P)
. = ..()
if(P.damage)
var/damage_amount = P.damage
if(P.forcedodge)
damage_amount *= 0.5
visible_message("<span class='danger'>[src] was hit by [P].</span>")
take_damage(damage_amount, P.damage_type, 0)
/obj/machinery/dominator/blob_act(obj/effect/blob/B)
take_damage(110, BRUTE, 0)
/obj/machinery/dominator/attack_hand(mob/user)
if(operating || (stat & BROKEN))
examine(user)
return
var/datum/gang/tempgang
if(user.mind in ticker.mode.get_all_gangsters())
tempgang = user.mind.gang_datum
else
examine(user)
return
if(tempgang.is_dominating)
user << "<span class='warning'>Error: Hostile Takeover is already in progress.</span>"
return
if(!tempgang.dom_attempts)
user << "<span class='warning'>Error: Unable to breach station network. Firewall has logged our signature and is blocking all further attempts.</span>"
return
var/time = round(determine_domination_time(tempgang)/60,0.1)
if(alert(user,"With [round((tempgang.territory.len/start_state.num_territories)*100, 1)]% station control, a takeover will require [time] minutes.\nYour gang will be unable to gain influence while it is active.\nThe entire station will likely be alerted to it once it starts.\nYou have [tempgang.dom_attempts] attempt(s) remaining. Are you ready?","Confirm","Ready","Later") == "Ready")
if((tempgang.is_dominating) || !tempgang.dom_attempts || !in_range(src, user) || !istype(src.loc, /turf))
return 0
var/area/A = get_area(loc)
var/locname = initial(A.name)
gang = tempgang
gang.dom_attempts --
priority_announce("Network breach detected in [locname]. The [gang.name] Gang is attempting to seize control of the station!","Network Alert")
gang.domination()
src.name = "[gang.name] Gang [src.name]"
operating = 1
icon_state = "dominator-[gang.color]"
countdown.text_color = gang.color_hex
countdown.start()
SetLuminosity(3)
START_PROCESSING(SSmachine, src)
gang.message_gangtools("Hostile takeover in progress: Estimated [time] minutes until victory.[gang.dom_attempts ? "" : " This is your final attempt."]")
for(var/datum/gang/G in ticker.mode.gangs)
if(G != gang)
G.message_gangtools("Enemy takeover attempt detected in [locname]: Estimated [time] minutes until our defeat.",1,1)
/obj/machinery/dominator/attack_hulk(mob/user)
user.visible_message("<span class='danger'>[user] smashes [src].</span>",\
"<span class='danger'>You punch [src].</span>",\
"<span class='italics'>You hear metal being slammed.</span>")
take_damage(5)
/obj/machinery/dominator/attacked_by(obj/item/I, mob/living/user)
add_fingerprint(user)
..()
+317
View File
@@ -0,0 +1,317 @@
//gang.dm
//Gang War Game Mode
var/list/gang_name_pool = list("Clandestine", "Prima", "Zero-G", "Max", "Blasto", "Waffle", "North", "Omni", "Newton", "Cyber", "Donk", "Gene", "Gib", "Tunnel", "Diablo", "Psyke", "Osiron", "Sirius", "Sleeping Carp")
var/list/gang_colors_pool = list("red","orange","yellow","green","blue","purple")
/datum/game_mode
var/list/datum/gang/gangs = list()
var/datum/gang_points/gang_points
/proc/is_gangster(var/mob/living/M)
return istype(M) && M.mind && M.mind.gang_datum
/proc/is_in_gang(var/mob/living/M, var/gang_type)
if(!is_gangster(M) || !gang_type)
return 0
var/datum/gang/G = M.mind.gang_datum
if(G.name == gang_type)
return 1
return 0
/datum/game_mode/gang
name = "gang war"
config_tag = "gang"
antag_flag = ROLE_GANG
restricted_jobs = list("Security Officer", "Warden", "Detective", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security", "Chief Engineer", "Research Director", "Chief Medical Officer")
required_players = 20
required_enemies = 2
recommended_enemies = 2
enemy_minimum_age = 14
///////////////////////////
//Announces the game type//
///////////////////////////
/datum/game_mode/gang/announce()
world << "<B>The current game mode is - Gang War!</B>"
world << "<B>A violent turf war has erupted on the station!<BR>Gangsters - Take over the station by activating and defending a Dominator! <BR>Crew - The gangs will try to keep you on the station. Successfully evacuate the station to win!</B>"
///////////////////////////////////////////////////////////////////////////////
//Gets the round setup, cancelling if there's not enough players at the start//
///////////////////////////////////////////////////////////////////////////////
/datum/game_mode/gang/pre_setup()
if(config.protect_roles_from_antagonist)
restricted_jobs += protected_jobs
if(config.protect_assistant_from_antagonist)
restricted_jobs += "Assistant"
//Spawn more bosses depending on server population
var/gangs_to_create = 2
if(prob(num_players() * 2))
gangs_to_create ++
for(var/i=1 to gangs_to_create)
if(!antag_candidates.len)
break
//Create the gang
var/datum/gang/G = new()
gangs += G
//Now assign a boss for the gang
var/datum/mind/boss = pick(antag_candidates)
antag_candidates -= boss
G.bosses += boss
boss.gang_datum = G
boss.special_role = "[G.name] Gang Boss"
boss.restricted_roles = restricted_jobs
log_game("[boss.key] has been selected as the Boss for the [G.name] Gang")
if(gangs.len < 2) //Need at least two gangs
return 0
return 1
/datum/game_mode/gang/post_setup()
spawn(rand(10,100))
for(var/datum/gang/G in gangs)
for(var/datum/mind/boss_mind in G.bosses)
G.add_gang_hud(boss_mind)
forge_gang_objectives(boss_mind)
greet_gang(boss_mind)
equip_gang(boss_mind.current,G)
modePlayer += boss_mind
..()
/datum/game_mode/proc/forge_gang_objectives(datum/mind/boss_mind)
var/datum/objective/rival_obj = new
rival_obj.owner = boss_mind
rival_obj.explanation_text = "Be the first gang to successfully takeover the station with a Dominator."
boss_mind.objectives += rival_obj
/datum/game_mode/proc/greet_gang(datum/mind/boss_mind, you_are=1)
var/obj_count = 1
if (you_are)
boss_mind.current << "<FONT size=3 color=red><B>You are the Boss of the [boss_mind.gang_datum.name] Gang!</B></FONT>"
for(var/datum/objective/objective in boss_mind.objectives)
boss_mind.current << "<B>Objective #[obj_count]</B>: [objective.explanation_text]"
obj_count++
///////////////////////////////////////////////////////////////////////////
//This equips the bosses with their gear, and makes the clown not clumsy//
///////////////////////////////////////////////////////////////////////////
/datum/game_mode/proc/equip_gang(mob/living/carbon/human/mob, gang)
if(!istype(mob))
return
if (mob.mind)
if (mob.mind.assigned_role == "Clown")
mob << "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself."
mob.dna.remove_mutation(CLOWNMUT)
var/obj/item/device/gangtool/gangtool = new(mob)
var/obj/item/weapon/pen/gang/T = new(mob)
var/obj/item/toy/crayon/spraycan/gang/SC = new(mob,gang)
var/obj/item/clothing/glasses/hud/security/chameleon/C = new(mob,gang)
var/list/slots = list (
"backpack" = slot_in_backpack,
"left pocket" = slot_l_store,
"right pocket" = slot_r_store,
"left hand" = slot_l_hand,
"right hand" = slot_r_hand,
)
. = 0
var/where = mob.equip_in_one_of_slots(gangtool, slots)
if (!where)
mob << "Your Syndicate benefactors were unfortunately unable to get you a Gangtool."
. += 1
else
gangtool.register_device(mob)
mob << "The <b>Gangtool</b> in your [where] will allow you to purchase weapons and equipment, send messages to your gang, and recall the emergency shuttle from anywhere on the station."
mob << "As the gang boss, you can also promote your gang members to <b>lieutenant</b>. Unlike regular gangsters, Lieutenants cannot be deconverted and are able to use recruitment pens and gangtools."
var/where2 = mob.equip_in_one_of_slots(T, slots)
if (!where2)
mob << "Your Syndicate benefactors were unfortunately unable to get you a recruitment pen to start."
. += 1
else
mob << "The <b>recruitment pen</b> in your [where2] will help you get your gang started. Stab unsuspecting crew members with it to recruit them."
var/where3 = mob.equip_in_one_of_slots(SC, slots)
if (!where3)
mob << "Your Syndicate benefactors were unfortunately unable to get you a territory spraycan to start."
. += 1
else
mob << "The <b>territory spraycan</b> in your [where3] can be used to claim areas of the station for your gang. The more territory your gang controls, the more influence you get. All gangsters can use these, so distribute them to grow your influence faster."
var/where4 = mob.equip_in_one_of_slots(C, slots)
if (!where4)
mob << "Your Syndicate benefactors were unfortunately unable to get you a chameleon security HUD."
. += 1
else
mob << "The <b>chameleon security HUD</b> in your [where4] will help you keep track of who is mindshield-implanted, and unable to be recruited."
mob.update_icons()
return .
///////////////////////////////////////////
//Deals with converting players to a gang//
///////////////////////////////////////////
/datum/game_mode/proc/add_gangster(datum/mind/gangster_mind, datum/gang/G, check = 1)
if(!G || (gangster_mind in get_all_gangsters()) || gangster_mind.enslaved_to)
return 0
if(check && isloyal(gangster_mind.current)) //Check to see if the potential gangster is implanted
return 1
G.gangsters += gangster_mind
gangster_mind.gang_datum = G
if(check)
if(iscarbon(gangster_mind.current))
var/mob/living/carbon/carbon_mob = gangster_mind.current
carbon_mob.silent = max(carbon_mob.silent, 5)
carbon_mob.flash_eyes(1, 1)
gangster_mind.current.Stun(5)
gangster_mind.current << "<FONT size=3 color=red><B>You are now a member of the [G.name] Gang!</B></FONT>"
gangster_mind.current << "<font color='red'>Help your bosses take over the station by claiming territory with <b>special spraycans</b> only they can provide. Simply spray on any unclaimed area of the station.</font>"
gangster_mind.current << "<font color='red'>Their ultimate objective is to take over the station with a Dominator machine.</font>"
gangster_mind.current << "<font color='red'>You can identify your bosses by their <b>red \[G\] icon</b>.</font>"
gangster_mind.current.attack_log += "\[[time_stamp()]\] <font color='red'>Has been converted to the [G.name] Gang!</font>"
gangster_mind.special_role = "[G.name] Gangster"
gangster_mind.store_memory("You are a member of the [G.name] Gang!")
G.add_gang_hud(gangster_mind)
if(jobban_isbanned(gangster_mind.current, ROLE_GANG))
replace_jobbaned_player(gangster_mind.current, ROLE_GANG, ROLE_GANG)
return 2
////////////////////////////////////////////////////////////////////
//Deals with players reverting to neutral (Not a gangster anymore)//
////////////////////////////////////////////////////////////////////
/datum/game_mode/proc/remove_gangster(datum/mind/gangster_mind, beingborged, silent, remove_bosses=0)
var/datum/gang/gang = gangster_mind.gang_datum
if(!gang)
return 0
var/removed
for(var/datum/gang/G in gangs)
if(gangster_mind in G.gangsters)
G.gangsters -= gangster_mind
removed = 1
if(remove_bosses && (gangster_mind in G.bosses))
G.bosses -= gangster_mind
removed = 1
if(!removed)
return 0
gangster_mind.special_role = null
gangster_mind.gang_datum = null
if(silent < 2)
gangster_mind.current.attack_log += "\[[time_stamp()]\] <font color='red'>Has reformed and defected from the [gang.name] Gang!</font>"
if(beingborged)
if(!silent)
gangster_mind.current.visible_message("The frame beeps contentedly from the MMI before initalizing it.")
gangster_mind.current << "<FONT size=3 color=red><B>The frame's firmware detects and deletes your criminal behavior! You are no longer a gangster!</B></FONT>"
message_admins("[key_name_admin(gangster_mind.current)] <A HREF='?_src_=holder;adminmoreinfo=\ref[gangster_mind.current]'>?</A> (<A HREF='?_src_=holder;adminplayerobservefollow=\ref[gangster_mind.current]'>FLW</A>) has been borged while being a member of the [gang.name] Gang. They are no longer a gangster.")
else
if(!silent)
gangster_mind.current.Paralyse(5)
gangster_mind.current.visible_message("<FONT size=3><B>[gangster_mind.current] looks like they've given up the life of crime!<B></font>")
gangster_mind.current << "<FONT size=3 color=red><B>You have been reformed! You are no longer a gangster!</B><BR>You try as hard as you can, but you can't seem to recall any of the identities of your former gangsters...</FONT>"
gangster_mind.memory = ""
gang.remove_gang_hud(gangster_mind)
return 1
////////////////
//Helper Procs//
////////////////
/datum/game_mode/proc/get_all_gangsters()
var/list/all_gangsters = list()
all_gangsters += get_gangsters()
all_gangsters += get_gang_bosses()
return all_gangsters
/datum/game_mode/proc/get_gangsters()
var/list/gangsters = list()
for(var/datum/gang/G in gangs)
gangsters += G.gangsters
return gangsters
/datum/game_mode/proc/get_gang_bosses()
var/list/gang_bosses = list()
for(var/datum/gang/G in gangs)
gang_bosses += G.bosses
return gang_bosses
/proc/determine_domination_time(var/datum/gang/G)
return max(180,900 - (round((G.territory.len/start_state.num_territories)*100, 1) * 12))
//////////////////////////////////////////////////////////////////////
//Announces the end of the game with all relavent information stated//
//////////////////////////////////////////////////////////////////////
/datum/game_mode/proc/auto_declare_completion_gang(datum/gang/winner)
if(gangs.len)
if(!winner)
world << "<span class='redtext'>The station was [station_was_nuked ? "destroyed!" : "evacuated before a gang could claim it! The station wins!"]</span><br>"
feedback_set_details("round_end_result","loss - gangs failed takeover")
else
world << "<span class='redtext'>The [winner.name] Gang successfully performed a hostile takeover of the station!</span><br>"
feedback_set_details("round_end_result","win - gang domination complete")
for(var/datum/gang/G in gangs)
var/text = "<b>The [G.name] Gang was [winner==G ? "<span class='greenannounce'>victorious</span>" : "<span class='boldannounce'>defeated</span>"] with [round((G.territory.len/start_state.num_territories)*100, 1)]% control of the station!</b>"
text += "<br>The [G.name] Gang Bosses were:"
for(var/datum/mind/boss in G.bosses)
text += printplayer(boss, 1)
text += "<br>The [G.name] Gangsters were:"
for(var/datum/mind/gangster in G.gangsters)
text += printplayer(gangster, 1)
text += "<br>"
world << text
//////////////////////////////////////////////////////////
//Handles influence, territories, and the victory checks//
//////////////////////////////////////////////////////////
/datum/gang_points
var/next_point_interval = 1800
var/next_point_time
/datum/gang_points/New()
next_point_time = world.time + next_point_interval
START_PROCESSING(SSobj, src)
/datum/gang_points/process(seconds)
var/list/winners = list() //stores the winners if there are any
for(var/datum/gang/G in ticker.mode.gangs)
if(world.time > next_point_time)
G.income()
if(G.is_dominating)
if(G.domination_time_remaining() < 0)
winners += G
if(world.time > next_point_time)
next_point_time = world.time + next_point_interval
if(winners.len)
if(winners.len > 1) //Edge Case: If more than one dominator complete at the same time
for(var/datum/gang/G in winners)
G.domination(0.5)
priority_announce("Multiple station takeover attempts have made simultaneously. Conflicting takeover attempts appears to have restarted.","Network Alert")
else
ticker.mode.explosion_in_progress = 1
ticker.station_explosion_cinematic(1)
ticker.mode.explosion_in_progress = 0
ticker.force_ending = pick(winners)
+216
View File
@@ -0,0 +1,216 @@
//gang_datum.dm
//Datum-based gangs
/datum/gang
var/name = "ERROR"
var/color = "white"
var/color_hex = "#FFFFFF"
var/list/datum/mind/gangsters = list() //gang B Members
var/list/datum/mind/bosses = list() //gang A Bosses
var/list/obj/item/device/gangtool/gangtools = list()
var/style
var/fighting_style = "normal"
var/list/territory = list()
var/list/territory_new = list()
var/list/territory_lost = list()
var/dom_attempts = 2
var/points = 15
var/datum/atom_hud/antag/ganghud
var/domination_timer
var/is_dominating
/datum/gang/New(loc,gangname)
if(!gang_colors_pool.len)
message_admins("WARNING: Maximum number of gangs have been exceeded!")
throw EXCEPTION("Maximum number of gangs has been exceeded")
return
else
color = pick(gang_colors_pool)
gang_colors_pool -= color
switch(color)
if("red")
color_hex = "#DA0000"
if("orange")
color_hex = "#FF9300"
if("yellow")
color_hex = "#FFF200"
if("green")
color_hex = "#A8E61D"
if("blue")
color_hex = "#00B7EF"
if("purple")
color_hex = "#DA00FF"
name = (gangname ? gangname : pick(gang_name_pool))
gang_name_pool -= name
ganghud = new()
log_game("The [name] Gang has been created. Their gang color is [color].")
/datum/gang/proc/add_gang_hud(datum/mind/recruit_mind)
ganghud.join_hud(recruit_mind.current)
ticker.mode.set_antag_hud(recruit_mind.current, ((recruit_mind in bosses) ? "gang_boss" : "gangster"))
/datum/gang/proc/remove_gang_hud(datum/mind/defector_mind)
ganghud.leave_hud(defector_mind.current)
ticker.mode.set_antag_hud(defector_mind.current, null)
/datum/gang/proc/domination(modifier=1)
set_domination_time(determine_domination_time(src) * modifier)
is_dominating = TRUE
set_security_level("delta")
SSshuttle.emergencyNoEscape = 1
/datum/gang/proc/set_domination_time(d)
domination_timer = world.time + (10 * d)
/datum/gang/proc/domination_time_remaining()
var/diff = domination_timer - world.time
return diff / 10
//////////////////////////////////////////// OUTFITS
//Used by recallers when purchasing a gang outfit. First time a gang outfit is purchased the buyer decides a gang style which is stored so gang outfits are uniform
/datum/gang/proc/gang_outfit(mob/living/carbon/user,obj/item/device/gangtool/gangtool)
if(!user || !gangtool)
return 0
if(!gangtool.can_use(user))
return 0
var/gang_style_list = list("Gang Colors","Black Suits","White Suits","Leather Jackets","Leather Overcoats","Puffer Jackets","Military Jackets","Tactical Turtlenecks","Soviet Uniforms")
if(!style && (user.mind in ticker.mode.get_gang_bosses())) //Only the boss gets to pick a style
style = input("Pick an outfit style.", "Pick Style") as null|anything in gang_style_list
if(gangtool.can_use(user) && (gangtool.outfits >= 1))
var/outfit_path
switch(style)
if(null || "Gang Colors")
outfit_path = text2path("/obj/item/clothing/under/color/[color]")
if("Black Suits")
outfit_path = /obj/item/clothing/under/suit_jacket/charcoal
if("White Suits")
outfit_path = /obj/item/clothing/under/suit_jacket/white
if("Puffer Jackets")
outfit_path = /obj/item/clothing/suit/jacket/puffer
if("Leather Jackets")
outfit_path = /obj/item/clothing/suit/jacket/leather
if("Leather Overcoats")
outfit_path = /obj/item/clothing/suit/jacket/leather/overcoat
if("Military Jackets")
outfit_path = /obj/item/clothing/suit/jacket/miljacket
if("Soviet Uniforms")
outfit_path = /obj/item/clothing/under/soviet
if("Tactical Turtlenecks")
outfit_path = /obj/item/clothing/under/syndicate
if(outfit_path)
var/obj/item/clothing/outfit = new outfit_path(user.loc)
outfit.armor = list(melee = 20, bullet = 30, laser = 10, energy = 10, bomb = 20, bio = 0, rad = 0)
outfit.desc += " Tailored for the [name] Gang to offer the wearer moderate protection against ballistics and physical trauma."
outfit.gang = src
user.put_in_hands(outfit)
return 1
return 0
//////////////////////////////////////////// MESSAGING
/datum/gang/proc/message_gangtools(message,beep=1,warning)
if(!gangtools.len || !message)
return
for(var/obj/item/device/gangtool/tool in gangtools)
var/mob/living/mob = get(tool.loc,/mob/living)
if(mob && mob.mind && mob.stat == CONSCIOUS)
if(mob.mind.gang_datum == src)
mob << "<span class='[warning ? "warning" : "notice"]'>\icon[tool] [message]</span>"
return
//////////////////////////////////////////// INCOME
/datum/gang/proc/income()
if(!bosses.len)
return
var/added_names = ""
var/lost_names = ""
//Re-add territories that were reclaimed, so if they got tagged over, they can still earn income if they tag it back before the next status report
var/list/reclaimed_territories = territory_new & territory_lost
territory |= reclaimed_territories
territory_new -= reclaimed_territories
territory_lost -= reclaimed_territories
//Process lost territories
for(var/area in territory_lost)
if(lost_names != "")
lost_names += ", "
lost_names += "[territory_lost[area]]"
territory -= area
//Count uniformed gangsters
var/uniformed = 0
for(var/datum/mind/gangmind in (gangsters|bosses))
if(ishuman(gangmind.current))
var/mob/living/carbon/human/gangster = gangmind.current
//Gangster must be alive and on station
if((gangster.stat == DEAD) || (gangster.z > ZLEVEL_STATION))
continue
var/obj/item/clothing/outfit
var/obj/item/clothing/gang_outfit
if(gangster.w_uniform)
outfit = gangster.w_uniform
if(outfit.gang == src)
gang_outfit = outfit
if(gangster.wear_suit)
outfit = gangster.wear_suit
if(outfit.gang == src)
gang_outfit = outfit
if(gang_outfit)
gangster << "<span class='notice'>The [src] Gang's influence grows as you wear [gang_outfit].</span>"
uniformed ++
//Calculate and report influence growth
var/message = "<b>[src] Gang Status Report:</b><BR>*---------*<br>"
if(is_dominating)
var/seconds_remaining = domination_time_remaining()
var/new_time = max(180, seconds_remaining - (uniformed * 4) - (territory.len * 2))
if(new_time < seconds_remaining)
message += "Takeover shortened by [seconds_remaining - new_time] seconds for defending [territory.len] territories and [uniformed] uniformed gangsters.<BR>"
set_domination_time(new_time)
message += "<b>[seconds_remaining] seconds remain</b> in hostile takeover.<BR>"
else
var/points_new = min(999,points + 15 + (uniformed * 2) + territory.len)
if(points_new != points)
message += "Gang influence has increased by [points_new - points] for defending [territory.len] territories and [uniformed] uniformed gangsters.<BR>"
points = points_new
message += "Your gang now has <b>[points] influence</b>.<BR>"
//Process new territories
for(var/area in territory_new)
if(added_names != "")
added_names += ", "
added_names += "[territory_new[area]]"
territory += area
//Report territory changes
message += "<b>[territory_new.len] new territories:</b><br><i>[added_names]</i><br>"
message += "<b>[territory_lost.len] territories lost:</b><br><i>[lost_names]</i><br>"
//Clear the lists
territory_new = list()
territory_lost = list()
var/control = round((territory.len/start_state.num_territories)*100, 1)
message += "Your gang now has <b>[control]% control</b> of the station.<BR>*---------*"
message_gangtools(message)
//Increase outfit stock
for(var/obj/item/device/gangtool/tool in gangtools)
tool.outfits = min(tool.outfits+1,5)
+120
View File
@@ -0,0 +1,120 @@
/*
* Gang Boss Pens
*/
/obj/item/weapon/pen/gang
origin_tech = "materials=2;syndicate=3"
var/cooldown
var/last_used = 0
var/charges = 1
/obj/item/weapon/pen/gang/New()
..()
last_used = world.time
/obj/item/weapon/pen/gang/attack(mob/living/M, mob/user)
if(!istype(M))
return
if(ishuman(M) && ishuman(user) && M.stat != DEAD)
if(user.mind && (user.mind in ticker.mode.get_gang_bosses()))
if(..(M,user,1))
if(cooldown)
user << "<span class='warning'>[src] needs more time to recharge before it can be used.</span>"
return
if(M.client)
M.mind_initialize() //give them a mind datum if they don't have one.
var/datum/gang/G = user.mind.gang_datum
var/recruitable = ticker.mode.add_gangster(M.mind,G)
switch(recruitable)
if(2)
M.Paralyse(5)
cooldown(G)
if(1)
user << "<span class='warning'>This mind is resistant to recruitment!</span>"
else
user << "<span class='warning'>This mind has already been recruited into a gang!</span>"
return
..()
/obj/item/weapon/pen/gang/proc/cooldown(datum/gang/gang)
var/cooldown_time = 600+(600*gang.bosses.len) // 1recruiter=2mins, 2recruiters=3mins, 3recruiters=4mins
cooldown = 1
icon_state = "pen_blink"
var/time_passed = world.time - last_used
var/time
for(time=time_passed, time>=cooldown_time, time-=cooldown_time) //get 1 charge every cooldown interval
charges++
charges = max(0,charges-1)
last_used = world.time - time
if(charges)
cooldown_time = 50
spawn(cooldown_time)
cooldown = 0
icon_state = "pen"
var/mob/M = get(src, /mob)
M << "<span class='notice'>\icon[src] [src][(src.loc == M)?(""):(" in your [src.loc]")] vibrates softly. It is ready to be used again.</span>"
//////////////
// IMPLANTS //
//////////////
/obj/item/weapon/implant/gang
name = "gang implant"
desc = "Makes you a gangster or such."
activated = 0
origin_tech = "materials=2;biotech=4;programming=4;syndicate=3"
var/datum/gang/gang
/obj/item/weapon/implant/gang/New(loc,var/setgang)
..()
gang = setgang
/obj/item/weapon/implant/gang/get_data()
var/dat = {"<b>Implant Specifications:</b><BR>
<b>Name:</b> Criminal brainwash implant<BR>
<b>Life:</b> A few seconds after injection.<BR>
<b>Important Notes:</b> Illegal<BR>
<HR>
<b>Implant Details:</b><BR>
<b>Function:</b> Contains a small pod of nanobots that change the host's brain to be loyal to a certain organization.<BR>
<b>Special Features:</b> This device will also emit a small EMP pulse, destroying any other implants within the host's brain.<BR>
<b>Integrity:</b> Implant's EMP function will destroy itself in the process."}
return dat
/obj/item/weapon/implant/gang/implant(mob/target)
if(..())
for(var/obj/item/weapon/implant/I in target)
if(I != src)
qdel(I)
if(!target.mind || target.stat == DEAD)
return 0
var/success
if(target.mind in ticker.mode.get_gangsters())
if(ticker.mode.remove_gangster(target.mind,0,1))
success = 1 //Was not a gang boss, convert as usual
else
success = 1
if(ishuman(target))
if(!success)
target.visible_message("<span class='warning'>[target] seems to resist the implant!</span>", "<span class='warning'>You feel the influence of your enemies try to invade your mind!</span>")
qdel(src)
return -1
/obj/item/weapon/implanter/gang
name = "implanter (gang)"
/obj/item/weapon/implanter/gang/New(loc, var/gang)
if(!gang)
qdel(src)
return
imp = new /obj/item/weapon/implant/gang(src,gang)
..()
+442
View File
@@ -0,0 +1,442 @@
//gangtool device
/obj/item/device/gangtool
name = "suspicious device"
desc = "A strange device of sorts. Hard to really make out what it actually does if you don't know how to operate it."
icon_state = "gangtool-white"
item_state = "walkietalkie"
throwforce = 0
w_class = 1
throw_speed = 3
throw_range = 7
flags = CONDUCT
origin_tech = "programming=5;bluespace=2;syndicate=5"
var/datum/gang/gang //Which gang uses this?
var/recalling = 0
var/outfits = 3
var/free_pen = 0
var/promotable = 0
/obj/item/device/gangtool/New() //Initialize supply point income if it hasn't already been started
if(!ticker.mode.gang_points)
ticker.mode.gang_points = new /datum/gang_points(ticker.mode)
/obj/item/device/gangtool/attack_self(mob/user)
if (!can_use(user))
return
var/dat
if(!gang)
dat += "This device is not registered.<br><br>"
if(user.mind in ticker.mode.get_gang_bosses())
if(promotable && user.mind.gang_datum.bosses.len < 3)
dat += "Give this device to another member of your organization to use to promote them to Lieutenant.<br><br>"
dat += "If this is meant as a spare device for yourself:<br>"
dat += "<a href='?src=\ref[src];register=1'>Register Device as Spare</a><br>"
else if (promotable)
if(user.mind.gang_datum.bosses.len < 3)
dat += "You have been selected for a promotion!<br>"
dat += "<a href='?src=\ref[src];register=1'>Accept Promotion</a><br>"
else
dat += "No promotions available: All positions filled.<br>"
else
dat += "This device is not authorized to promote.<br>"
else
if(gang.is_dominating)
dat += "<center><font color='red'>Takeover In Progress:<br><B>[gang.domination_time_remaining()] seconds remain</B></font></center>"
var/isboss = (user.mind == gang.bosses[1])
var/points = gang.points
dat += "Registration: <B>[gang.name] Gang [isboss ? "Boss" : "Lieutenant"]</B><br>"
dat += "Organization Size: <B>[gang.gangsters.len + gang.bosses.len]</B> | Station Control: <B>[round((gang.territory.len/start_state.num_territories)*100, 1)]%</B><br>"
dat += "Gang Influence: <B>[points]</B><br>"
dat += "Time until Influence grows: <B>[(points >= 999) ? ("--:--") : (time2text(ticker.mode.gang_points.next_point_time - world.time, "mm:ss"))]</B><br>"
dat += "<hr>"
dat += "<B>Gangtool Functions:</B><br>"
dat += "<a href='?src=\ref[src];choice=ping'>Send Message to Gang</a><br>"
if(outfits > 0)
dat += "<a href='?src=\ref[src];choice=outfit'>Create Armored Gang Outfit</a><br>"
else
dat += "<b>Create Gang Outfit</b> (Restocking)<br>"
if(isboss)
dat += "<a href='?src=\ref[src];choice=recall'>Recall Emergency Shuttle</a><br>"
dat += "<br>"
dat += "<B>Purchase Weapons:</B><br>"
/////////////////
// NORMAL GANG //
/////////////////
if(gang.fighting_style == "normal")
dat += "(10 Influence) "
if(points >= 10)
dat += "<a href='?src=\ref[src];purchase=switchblade'>Switchblade</a><br>"
else
dat += "Switchblade<br>"
dat += "(25 Influence) "
if(points >= 25)
dat += "<a href='?src=\ref[src];purchase=pistol'>10mm Pistol</a><br>"
else
dat += "10mm Pistol<br>"
dat += "&nbsp;&#8627;(10 Influence) "
if(points >= 10)
dat += "<a href='?src=\ref[src];purchase=10mmammo'>10mm Ammo</a><br>"
else
dat += "10mm Ammo<br>"
dat += "(60 Influence) "
if(points >= 60)
dat += "<a href='?src=\ref[src];purchase=uzi'>Uzi SMG</a><br>"
else
dat += "Uzi SMG<br>"
dat += "&nbsp;&#8627;(40 Influence) "
if(points >= 40)
dat += "<a href='?src=\ref[src];purchase=9mmammo'>Uzi Ammo</a><br>"
else
dat += "Uzi Ammo<br>"
dat += "(1 Influence) "
if(points >=1)
dat += "<a href='?src=\ref[src];purchase=necklace'>Dope Necklace</a><br>"
else
dat += "Dope Necklace<br>"
dat += "<br>"
////////////////////////
// STANDARD EQUIPMENT //
////////////////////////
dat += "<B>Purchase Equipment:</B><br>"
dat += "(5 Influence) "
if(points >= 5)
dat += "<a href='?src=\ref[src];purchase=spraycan'>Territory Spraycan</a><br>"
else
dat += "Territory Spraycan<br>"
dat += "(10 Influence) "
if(points >= 10)
dat += "<a href='?src=\ref[src];purchase=C4'>C4 Explosive</a><br>"
else
dat += "C4 Explosive<br>"
dat += "(15 Influence) "
if(points >= 15)
dat += "<a href='?src=\ref[src];purchase=implant'>Implant Breaker</a><br>"
else
dat += "Implant Breaker<br>"
if(free_pen)
dat += "(GET ONE FREE) "
else
dat += "(50 Influence) "
if((points >= 50)||free_pen)
dat += "<a href='?src=\ref[src];purchase=pen'>Recruitment Pen</a><br>"
else
dat += "Recruitment Pen<br>"
var/gangtooltext = "Spare Gangtool"
if(isboss && gang.bosses.len < 3)
gangtooltext = "Promote a Gangster"
dat += "(10 Influence) "
if(points >= 10)
dat += "<a href='?src=\ref[src];purchase=gangtool'>[gangtooltext]</a><br>"
else
dat += "[gangtooltext]<br>"
if(!gang.dom_attempts)
dat += "(Out of stock) Station Dominator<br>"
else
dat += "(30 Influence) "
if(points >= 30)
dat += "<a href='?src=\ref[src];purchase=dominator'><b>Station Dominator</b></a><br>"
else
dat += "<b>Station Dominator</b><br>"
dat += "<i>(Estimated Takeover Time: [round(determine_domination_time(gang)/60,0.1)] minutes)</i><br>"
dat += "<br>"
dat += "<a href='?src=\ref[src];choice=refresh'>Refresh</a><br>"
var/datum/browser/popup = new(user, "gangtool", "Welcome to GangTool v3.2", 340, 625)
popup.set_content(dat)
popup.open()
/obj/item/device/gangtool/Topic(href, href_list)
if(!can_use(usr))
return
add_fingerprint(usr)
if(href_list["register"])
register_device(usr)
else if(!gang) //Gangtool must be registered before you can use the functions below
return
if(href_list["purchase"])
var/pointcost
var/item_type
switch(href_list["purchase"])
if("spraycan")
if(gang.points >= 5)
item_type = /obj/item/toy/crayon/spraycan/gang
pointcost = 5
if("switchblade")
if(gang.points >= 10)
item_type = /obj/item/weapon/switchblade
pointcost = 10
if("necklace")
if(gang.points >=1)
item_type = /obj/item/clothing/tie/dope_necklace
pointcost = 1
if("pistol")
if(gang.points >= 25)
item_type = /obj/item/weapon/gun/projectile/automatic/pistol
pointcost = 25
if("10mmammo")
if(gang.points >= 10)
item_type = /obj/item/ammo_box/magazine/m10mm
pointcost = 10
if("uzi")
if(gang.points >= 60)
item_type = /obj/item/weapon/gun/projectile/automatic/mini_uzi
pointcost = 60
if("9mmammo")
if(gang.points >= 40)
item_type = /obj/item/ammo_box/magazine/uzim9mm
pointcost = 40
if("scroll")
if(gang.points >= 30)
item_type = /obj/item/weapon/sleeping_carp_scroll
usr << "<span class='notice'>Anyone who reads the <b>sleeping carp scroll</b> will learn secrets of the sleeping carp martial arts style.</span>"
pointcost = 30
if("wrestlingbelt")
if(gang.points >= 20)
item_type = /obj/item/weapon/storage/belt/champion/wrestling
usr << "<span class='notice'>Anyone wearing the <b>wresting belt</b> will know how to be effective with wrestling.</span>"
pointcost = 20
if("bostaff")
if(gang.points >= 10)
item_type = /obj/item/weapon/twohanded/bostaff
pointcost = 10
if("C4")
if(gang.points >= 10)
item_type = /obj/item/weapon/grenade/plastic/c4
pointcost = 10
if("pen")
if((gang.points >= 50) || free_pen)
item_type = /obj/item/weapon/pen/gang
usr << "<span class='notice'>More <b>recruitment pens</b> will allow you to recruit gangsters faster. Only gang leaders can recruit with pens.</span>"
if(free_pen)
free_pen = 0
else
pointcost = 50
if("implant")
if(gang.points >= 15)
item_type = /obj/item/weapon/implanter/gang
usr << "<span class='notice'>The <b>implant breaker</b> is a single-use device that destroys all implants within the target before trying to recruit them to your gang. Also works on enemy gangsters.</span>"
pointcost = 15
if("gangtool")
if(gang.points >= 10)
if(usr.mind == gang.bosses[1])
item_type = /obj/item/device/gangtool/spare/lt
if(gang.bosses.len < 3)
usr << "<span class='notice'><b>Gangtools</b> allow you to promote a gangster to be your Lieutenant, enabling them to recruit and purchase items like you. Simply have them register the gangtool. You may promote up to [3-gang.bosses.len] more Lieutenants</span>"
else
item_type = /obj/item/device/gangtool/spare/
pointcost = 10
if("dominator")
if(!gang.dom_attempts)
return
var/area/usrarea = get_area(usr.loc)
var/usrturf = get_turf(usr.loc)
if(initial(usrarea.name) == "Space" || istype(usrturf,/turf/open/space) || usr.z != 1)
usr << "<span class='warning'>You can only use this on the station!</span>"
return
for(var/obj/obj in usrturf)
if(obj.density)
usr << "<span class='warning'>There's not enough room here!</span>"
return
if(usrarea.type in gang.territory|gang.territory_new)
if(gang.points >= 30)
item_type = /obj/machinery/dominator
usr << "<span class='notice'>The <b>dominator</b> will secure your gang's dominance over the station. Turn it on when you are ready to defend it.</span>"
pointcost = 30
else
usr << "<span class='warning'>The <b>dominator</b> can be spawned only on territory controlled by your gang!</span>"
return
if(item_type)
gang.points -= pointcost
if(ispath(item_type))
var/obj/purchased = new item_type(get_turf(usr),gang)
var/mob/living/carbon/human/H = usr
H.put_in_hands(purchased)
if(pointcost)
gang.message_gangtools("A [href_list["purchase"]] was purchased by [usr.real_name] for [pointcost] Influence.")
log_game("A [href_list["purchase"]] was purchased by [key_name(usr)] ([gang.name] Gang) for [pointcost] Influence.")
else
usr << "<span class='warning'>Not enough influence.</span>"
else if(href_list["choice"])
switch(href_list["choice"])
if("recall")
if(usr.mind == gang.bosses[1])
recall(usr)
if("outfit")
if(outfits > 0)
if(gang.gang_outfit(usr,src))
usr << "<span class='notice'><b>Gang Outfits</b> can act as armor with moderate protection against ballistic and melee attacks. Every gangster wearing one will also help grow your gang's influence.</span>"
outfits -= 1
if("ping")
ping_gang(usr)
attack_self(usr)
/obj/item/device/gangtool/proc/ping_gang(mob/user)
if(!user)
return
var/message = stripped_input(user,"Discreetly send a gang-wide message.","Send Message") as null|text
if(!message || !can_use(user))
return
if(user.z > 2)
user << "<span class='info'>\icon[src]Error: Station out of range.</span>"
return
var/list/members = list()
members += gang.gangsters
members += gang.bosses
if(members.len)
var/gang_rank = gang.bosses.Find(user.mind)
switch(gang_rank)
if(1)
gang_rank = "Gang Boss"
if(2)
gang_rank = "1st Lieutenant"
if(3)
gang_rank = "2nd Lieutenant"
if(4)
gang_rank = "3rd Lieutenant"
else
gang_rank = "[gang_rank - 1]th Lieutenant"
var/ping = "<span class='danger'><B><i>[gang.name] [gang_rank]</i>: [message]</B></span>"
for(var/datum/mind/ganger in members)
if(ganger.current && (ganger.current.z <= 2) && (ganger.current.stat == CONSCIOUS))
ganger.current << ping
for(var/mob/M in dead_mob_list)
var/link = FOLLOW_LINK(M, user)
M << "[link] [ping]"
log_game("[key_name(user)] Messaged [gang.name] Gang: [message].")
/obj/item/device/gangtool/proc/register_device(mob/user)
if(gang) //It's already been registered!
return
if((promotable && (user.mind in ticker.mode.get_gangsters())) || (user.mind in ticker.mode.get_gang_bosses()))
gang = user.mind.gang_datum
gang.gangtools += src
icon_state = "gangtool-[gang.color]"
if(!(user.mind in gang.bosses))
ticker.mode.remove_gangster(user.mind, 0, 2)
gang.bosses += user.mind
user.mind.gang_datum = gang
user.mind.special_role = "[gang.name] Gang Lieutenant"
gang.add_gang_hud(user.mind)
log_game("[key_name(user)] has been promoted to Lieutenant in the [gang.name] Gang")
free_pen = 1
gang.message_gangtools("[user] has been promoted to Lieutenant.")
user << "<FONT size=3 color=red><B>You have been promoted to Lieutenant!</B></FONT>"
ticker.mode.forge_gang_objectives(user.mind)
ticker.mode.greet_gang(user.mind,0)
user << "The <b>Gangtool</b> you registered will allow you to purchase weapons and equipment, and send messages to your gang."
user << "Unlike regular gangsters, you may use <b>recruitment pens</b> to add recruits to your gang. Use them on unsuspecting crew members to recruit them. Don't forget to get your one free pen from the gangtool."
else
usr << "<span class='warning'>ACCESS DENIED: Unauthorized user.</span>"
/obj/item/device/gangtool/proc/recall(mob/user)
if(!can_use(user))
return 0
if(recalling)
usr << "<span class='warning'>Error: Recall already in progress.</span>"
return 0
gang.message_gangtools("[usr] is attempting to recall the emergency shuttle.")
recalling = 1
loc << "<span class='info'>\icon[src]Generating shuttle recall order with codes retrieved from last call signal...</span>"
sleep(rand(100,300))
if(SSshuttle.emergency.mode != SHUTTLE_CALL) //Shuttle can only be recalled when it's moving to the station
user << "<span class='warning'>\icon[src]Emergency shuttle cannot be recalled at this time.</span>"
recalling = 0
return 0
loc << "<span class='info'>\icon[src]Shuttle recall order generated. Accessing station long-range communication arrays...</span>"
sleep(rand(100,300))
if(!gang.dom_attempts)
user << "<span class='warning'>\icon[src]Error: Unable to access communication arrays. Firewall has logged our signature and is blocking all further attempts.</span>"
recalling = 0
return 0
var/turf/userturf = get_turf(user)
if(userturf.z != 1) //Shuttle can only be recalled while on station
user << "<span class='warning'>\icon[src]Error: Device out of range of station communication arrays.</span>"
recalling = 0
return 0
var/datum/station_state/end_state = new /datum/station_state()
end_state.count()
if((100 * start_state.score(end_state)) < 80) //Shuttle cannot be recalled if the station is too damaged
user << "<span class='warning'>\icon[src]Error: Station communication systems compromised. Unable to establish connection.</span>"
recalling = 0
return 0
loc << "<span class='info'>\icon[src]Comm arrays accessed. Broadcasting recall signal...</span>"
sleep(rand(100,300))
recalling = 0
log_game("[key_name(user)] has tried to recall the shuttle with a gangtool.")
message_admins("[key_name_admin(user)] has tried to recall the shuttle with a gangtool.", 1)
userturf = get_turf(user)
if(userturf.z == 1) //Check one more time that they are on station.
if(SSshuttle.cancelEvac(user))
return 1
loc << "<span class='info'>\icon[src]No response recieved. Emergency shuttle cannot be recalled at this time.</span>"
return 0
/obj/item/device/gangtool/proc/can_use(mob/living/carbon/human/user)
if(!istype(user))
return 0
if(user.restrained() || user.lying || user.stat || user.stunned || user.weakened)
return 0
if(!(src in user.contents))
return 0
if(!user.mind)
return 0
if(gang) //If it's already registered, only let the gang's bosses use this
if(user.mind in gang.bosses)
return 1
else //If it's not registered, any gangster can use this to register
if(user.mind in ticker.mode.get_all_gangsters())
return 1
return 0
/obj/item/device/gangtool/spare
outfits = 1
/obj/item/device/gangtool/spare/lt
promotable = 1
+476
View File
@@ -0,0 +1,476 @@
var/global/list/global_handofgod_traptypes = list()
var/global/list/global_handofgod_structuretypes = list()
#define CONDUIT_RANGE 15
/datum/game_mode
var/list/datum/mind/red_deities = list()
var/list/datum/mind/red_deity_prophets = list()
var/list/datum/mind/red_deity_followers = list()
var/list/datum/mind/blue_deities = list()
var/list/datum/mind/blue_deity_prophets = list()
var/list/datum/mind/blue_deity_followers = list()
var/list/datum/mind/unassigned_followers = list() //for roundstart team assigning
var/list/datum/mind/assigned_to_red = list()
var/list/datum/mind/assigned_to_blue = list()
/datum/game_mode/hand_of_god
name = "hand of god"
config_tag = "handofgod"
antag_flag = ROLE_HOG_CULTIST //Followers use ROLE_HOG_CULTIST, Gods are picked later on with ROLE_HOG_GOD
required_players = 25
required_enemies = 8
recommended_enemies = 8
restricted_jobs = list("Chaplain","AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel")
/datum/game_mode/hand_of_god/announce()
world << "<B>The current game mode is - Hand of God!</B>"
world << "<B>Two cults are onboard the station, seeking to overthrow the other, and anyone who stands in their way.</B>"
world << "<B>Followers</B> - Complete your deity's objectives. Convert crewmembers to your cause by using your deity's nexus. Remember - there is no you, there is only the cult."
world << "<B>Prophets</B> - Command your cult by the will of your deity. You are a high-value target, so be careful!"
world << "<B>Personnel</B> - Do not let any cult succeed in its mission. Mindshield implants and holy water will revert them to neutral, hopefully nonviolent crew."
/////////////
//Pre setup//
/////////////
/datum/game_mode/hand_of_god/pre_setup()
if(config.protect_roles_from_antagonist)
restricted_jobs += protected_jobs
if(config.protect_assistant_from_antagonist)
restricted_jobs += "Assistant"
for(var/F in 1 to recommended_enemies)
if(!antag_candidates.len)
break
var/datum/mind/follower = pick_n_take(antag_candidates)
unassigned_followers += follower
follower.restricted_roles = restricted_jobs
log_game("[follower.key] (ckey) has been selected as a follower, however teams have not been decided yet.")
while(unassigned_followers.len > (required_enemies / 2))
var/datum/mind/chosen = pick_n_take(unassigned_followers)
add_hog_follower(chosen,"red")
while(unassigned_followers.len)
var/datum/mind/chosen = pick_n_take(unassigned_followers)
add_hog_follower(chosen,"blue")
return 1
//////////////
//Post Setup//
//////////////
//Pick a follower to uplift into a god
/datum/game_mode/hand_of_god/post_setup()
//Find viable red god
var/list/red_god_possibilities = get_players_for_role(ROLE_HOG_GOD)
red_god_possibilities &= red_deity_followers //followers only
if(!red_god_possibilities.len) //No candidates? just pick any follower regardless of prefs
red_god_possibilities = red_deity_followers
//Make red god
var/datum/mind/red_god = pick_n_take(red_god_possibilities)
if(red_god)
red_god.current.become_god("red")
remove_hog_follower(red_god,0)
add_god(red_god,"red")
//Find viable blue god
var/list/blue_god_possibilities = get_players_for_role(ROLE_HOG_GOD)
blue_god_possibilities &= blue_deity_followers //followers only
if(!blue_god_possibilities.len) //No candidates? just pick any follower regardless of prefs
blue_god_possibilities = blue_deity_followers
//Make blue god
var/datum/mind/blue_god = pick_n_take(blue_god_possibilities)
if(blue_god)
blue_god.current.become_god("blue")
remove_hog_follower(blue_god,0)
add_god(blue_god,"blue")
//Forge objectives
//This is done here so that both gods exist
if(red_god)
ticker.mode.forge_deity_objectives(red_god)
if(blue_god)
ticker.mode.forge_deity_objectives(blue_god)
..()
///////////////////
//Objective Procs//
///////////////////
/datum/game_mode/proc/forge_deity_objectives(datum/mind/deity)
switch(rand(1,100))
if(1 to 30)
var/datum/objective/deicide/deicide = new
deicide.owner = deity
if(deicide.find_target())//Hard to kill the other god if there is none
deity.objectives += deicide
if(!(locate(/datum/objective/escape_followers) in deity.objectives))
var/datum/objective/escape_followers/recruit = new
recruit.owner = deity
deity.objectives += recruit
recruit.gen_amount_goal(8, 12)
if(31 to 60)
var/datum/objective/sacrifice_prophet/sacrifice = new
sacrifice.owner = deity
deity.objectives += sacrifice
if(!(locate(/datum/objective/escape_followers) in deity.objectives))
var/datum/objective/escape_followers/recruit = new
recruit.owner = deity
deity.objectives += recruit
recruit.gen_amount_goal(8, 12)
if(61 to 85)
var/datum/objective/build/build = new
build.owner = deity
deity.objectives += build
build.gen_amount_goal(8, 16)
var/datum/objective/sacrifice_prophet/sacrifice = new
sacrifice.owner = deity
deity.objectives += sacrifice
if(!(locate(/datum/objective/escape_followers) in deity.objectives))
var/datum/objective/escape_followers/recruit = new
recruit.owner = deity
deity.objectives += recruit
recruit.gen_amount_goal(8, 12)
else
if (!locate(/datum/objective/follower_block) in deity.objectives)
var/datum/objective/follower_block/block = new
block.owner = deity
deity.objectives += block
///////////////
//Greet procs//
///////////////
/datum/game_mode/proc/greet_hog_follower(datum/mind/follower_mind,colour)
if(follower_mind in blue_deity_prophets || follower_mind in red_deity_prophets)
follower_mind.current << "<span class='danger'><B>You have been appointed as the prophet of the [colour] deity! You are the only one who can communicate with your deity at will. Guide your followers, but be wary, for many will want you dead.</span>"
else if(colour)
follower_mind.current << "<span class='danger'><B>You are a follower of the [colour] cult's deity!</span>"
else
follower_mind.current << "<span class='danger'><B>You are a follower of a cult's deity!</span>"
/////////////////
//Convert procs//
/////////////////
/datum/game_mode/proc/add_hog_follower(datum/mind/follower_mind, colour = "No Colour")
var/mob/living/carbon/human/H = follower_mind.current
if(isloyal(H))
H << "<span class='danger'>Your mindshield implant blocked the influence of the [colour] deity. </span>"
return 0
if((follower_mind in red_deity_followers) || (follower_mind in red_deity_prophets) || (follower_mind in blue_deity_followers) || (follower_mind in blue_deity_prophets))
H << "<span class='danger'>You already belong to a deity. Your strong faith has blocked out the conversion attempt by the followers of the [colour] deity.</span>"
return 0
var/obj/item/weapon/nullrod/N = H.null_rod_check()
if(N)
H << "<span class='danger'>Your holy weapon prevented the [colour] deity from brainwashing you.</span>"
return 0
if(colour == "red")
red_deity_followers += follower_mind
if(colour == "blue")
blue_deity_followers += follower_mind
H.faction |= "[colour] god"
follower_mind.current << "<span class='danger'><FONT size = 3>You are now a follower of the [colour] deity! Follow your deity's prophet in order to complete your deity's objectives. Convert crewmembers to your cause by using your deity's nexus. And remember - there is no you, there is only the cult.</FONT></span>"
update_hog_icons_added(follower_mind, colour)
follower_mind.special_role = "Hand of God: [capitalize(colour)] Follower"
follower_mind.current.attack_log += "\[[time_stamp()]\] <font color='red'>Has been converted to the [colour] follower cult!</font>"
return 1
/datum/game_mode/proc/add_god(datum/mind/god_mind, colour = "No Colour")
remove_hog_follower(god_mind, announce = 0)
if(colour == "red")
red_deities += god_mind
if(colour == "blue")
blue_deities += god_mind
god_mind.current.attack_log += "\[[time_stamp()]\] <font color='red'>Has been made into a [colour] deity!</font>"
god_mind.special_role = "Hand of God: [colour] God"
update_hog_icons_added(god_mind, colour)
//////////////////
//Deconvert proc//
//////////////////
/datum/game_mode/proc/remove_hog_follower(datum/mind/follower_mind, announce = 1)//deconverts both
follower_mind.remove_hog_follower_prophet()
update_hog_icons_removed(follower_mind,"red")
update_hog_icons_removed(follower_mind,"blue")
if(follower_mind.current)
var/mob/living/carbon/human/H = follower_mind.current
H.faction -= "red god"
H.faction -= "blue god"
if(announce)
follower_mind.current.attack_log += "\[[time_stamp()]\] <font color='red'>Has been deconverted from a deity's cult!</font>"
follower_mind.current << "<span class='danger'><b>Your mind has been cleared from the brainwashing the followers have done to you. Now you serve yourself and the crew.</b></span>"
for(var/mob/living/M in view(follower_mind.current))
M << "[follower_mind.current] looks like their faith is shattered. They're no longer a cultist!"
//////////////////////
// Mob helper procs //
//////////////////////
/proc/is_handofgod_god(A)
if(istype(A, /mob/camera/god))
return 1
return 0
/proc/is_handofgod_bluecultist(A)
if(ishuman(A))
var/mob/living/carbon/human/H = A
if(H.mind)
if(H.mind in ticker.mode.blue_deity_followers|ticker.mode.blue_deity_prophets)
return 1
return 0
/proc/is_handofgod_redcultist(A)
if(ishuman(A))
var/mob/living/carbon/human/H = A
if(H.mind)
if(H.mind in ticker.mode.red_deity_followers|ticker.mode.red_deity_prophets)
return 1
return 0
/proc/is_handofgod_blueprophet(A)
if(ishuman(A))
var/mob/living/carbon/human/H = A
if(H.mind)
if(H.mind in ticker.mode.blue_deity_prophets)
return 1
return 0
/proc/is_handofgod_redprophet(A)
if(ishuman(A))
var/mob/living/carbon/human/H = A
if(H.mind)
if(H.mind in ticker.mode.red_deity_prophets)
return 1
return 0
/proc/is_handofgod_cultist(A) //any of them what so ever, blue, red, hot pink, whatever.
if(is_handofgod_redcultist(A))
return 1
if(is_handofgod_bluecultist(A))
return 1
return 0
/proc/is_handofgod_prophet(A) //any of them what so ever, blue, red, hot pink, whatever
if(ishuman(A))
var/mob/living/carbon/human/H = A
if(H.mind)
if(H.mind in ticker.mode.blue_deity_prophets|ticker.mode.red_deity_prophets)
return 1
return 0
/mob/camera/god/proc/is_handofgod_myprophet(A)
if(!ishuman(A))
return 0
var/mob/living/carbon/human/H = A
if(!H.mind)
return 0
if(side == "red")
if(H.mind in ticker.mode.red_deity_prophets)
return 1
else if(side == "blue")
if(H.mind in ticker.mode.blue_deity_prophets)
return 1
/mob/camera/god/proc/is_handofgod_myfollowers(mob/A)
if(!ishuman(A))
return 0
var/mob/living/carbon/human/H = A
if(!H.mind)
return 0
if(side == "red")
if(H.mind in ticker.mode.red_deity_prophets|ticker.mode.red_deity_followers)
return 1
else if(side == "blue")
if(H.mind in ticker.mode.blue_deity_prophets|ticker.mode.blue_deity_followers)
return 1
//////////////////////
//Roundend Reporting//
//////////////////////
/datum/game_mode/hand_of_god/declare_completion()
if(red_deities.len)
var/text = "<BR><font size=3 color='red'><B>The red cult:</b></font>"
for(var/datum/mind/red_god in red_deities)
var/godwin = 1
text += "<BR><B>[red_god.key]</B> was the red deity, <B>[red_god.name]</B> ("
if(red_god.current)
if(red_god.current.stat == DEAD)
text += "died"
else
text += "survived"
else
text += "ceased existing"
text += ")"
if(red_deity_prophets.len)
for(var/datum/mind/red_prophet in red_deity_prophets)
text += "<BR>The red prophet was <B>[red_prophet.name]</B> (<B>[red_prophet.key]</B>)"
else
text += "<BR>the red prophet was killed for their beliefs."
text += "<BR><B>Red follower count: </B> [red_deity_followers.len]"
text += "<BR><B>Red followers:</B> "
for(var/datum/mind/player in red_deity_followers)
text += "[player.name] ([player.key]), "
var/objectives = ""
if(red_god.objectives.len)
var/count = 1
for(var/datum/objective/O in red_god.objectives)
if(O.check_completion())
objectives += "<BR><B>Objective #[count]</B>: [O.explanation_text] <font color='green'><B>Success!</B></font>"
feedback_add_details("god_objective","[O.type]|SUCCESS")
else
objectives += "<BR><B>Objective #[count]</B>: [O.explanation_text] <font color='red'><B>Fail.</B></font>"
feedback_add_details("god_objective","[O.type]|FAIL")
godwin = 0
count++
text += objectives
if(godwin)
text += "<BR><font color='green'><B>The red cult and deity were successful!</B></font>"
feedback_add_details("god_success","SUCCESS")
else
text += "<br><font color='red'><B>The red cult and deity have failed!</B></font>"
feedback_add_details("god_success","FAIL")
text += "<BR>"
world << text
if(blue_deities.len)
var/text = "<BR><font size=3 color='red'><B>The blue cult:</b></font>"
for(var/datum/mind/blue_god in blue_deities)
var/godwin = 1
text += "<BR><B>[blue_god.key]</B> was the blue deity, <B>[blue_god.name]</B> ("
if(blue_god.current)
if(blue_god.current.stat == DEAD)
text += "died"
else
text += "survived"
else
text += "ceased existing"
text += ")"
if(blue_deity_prophets.len)
for(var/datum/mind/blue_prophet in blue_deity_prophets)
text += "<BR>The blue prophet was <B>[blue_prophet.name]</B> (<B>[blue_prophet.key]</B>)"
else
text += "<BR>the blue prophet was killed for their beliefs."
text += "<BR><B>Blue follower count: </B> [blue_deity_followers.len]"
text += "<BR><B>Blue followers:</B> "
for(var/datum/mind/player in blue_deity_followers)
text += "[player.name] ([player.key])"
var/objectives = ""
if(blue_god.objectives.len)
var/count = 1
for(var/datum/objective/O in blue_god.objectives)
if(O.check_completion())
objectives += "<BR><B>Objective #[count]</B>: [O.explanation_text] <font color='green'><B>Success!</B></font>"
feedback_add_details("god_objective","[O.type]|SUCCESS")
else
objectives += "<BR><B>Objective #[count]</B>: [O.explanation_text] <font color='red'><B>Fail.</B></font>"
feedback_add_details("god_objective","[O.type]|FAIL")
godwin = 0
count++
text += objectives
if(godwin)
text += "<BR><font color='green'><B>The blue cult and deity were successful!</B></font>"
feedback_add_details("god_success","SUCCESS")
else
text += "<BR><font color='red'><B>The blue cult and deity have failed!</B></font>"
feedback_add_details("god_success","FAIL")
text += "<BR>"
world << text
..()
return 1
/datum/game_mode/proc/update_hog_icons_added(datum/mind/hog_mind,side)
var/hud_key
var/rank = 0
if(side == "red")
hud_key = ANTAG_HUD_HOG_RED
if(is_handofgod_redprophet(hog_mind.current))
rank = 1
else if(side == "blue")
hud_key = ANTAG_HUD_HOG_BLUE
if(is_handofgod_blueprophet(hog_mind.current))
rank = 1
if(is_handofgod_god(hog_mind.current))
rank = 2
if(hud_key)
var/datum/atom_hud/antag/hog_hud = huds[hud_key]
hog_hud.join_hud(hog_mind.current)
set_antag_hud(hog_mind.current, "hog-[side]-[rank]")
/datum/game_mode/proc/update_hog_icons_removed(datum/mind/hog_mind,side)
var/hud_key
if(side == "red")
hud_key = ANTAG_HUD_HOG_RED
else if(side == "blue")
hud_key = ANTAG_HUD_HOG_BLUE
if(hud_key)
var/datum/atom_hud/antag/hog_hud = huds[hud_key]
hog_hud.leave_hud(hog_mind.current)
set_antag_hud(hog_mind.current,null)
+28
View File
@@ -0,0 +1,28 @@
/* Prophet's innate godspeak */
/datum/action/innate/godspeak
name = "Godspeak"
button_icon_state = "godspeak"
check_flags = AB_CHECK_CONSCIOUS
var/mob/camera/god/god = null
/datum/action/innate/godspeak/IsAvailable()
if(..())
if(god)
return 1
return 0
/datum/action/innate/godspeak/Activate()
var/msg = input(owner,"Speak to your god","Godspeak","") as null|text
if(!msg)
return
var/rendered = "<font color='[god.side]'><span class='game say'><i>Prophet [owner]:</i> <span class='message'>[msg]</span></span>"
god << rendered
owner << rendered
for(var/mob/M in mob_list)
if(isobserver(M))
var/link = FOLLOW_LINK(M, owner)
M << "[link] [rendered]"
/datum/action/innate/godspeak/Destroy()
god = null
return ..()
+290
View File
@@ -0,0 +1,290 @@
/mob/camera/god
name = "deity" //Auto changes to the player's deity name/random name
real_name = "deity"
icon = 'icons/mob/mob.dmi'
icon_state = "marker"
invisibility = 60
see_in_dark = 0
see_invisible = 55
sight = SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF
languages_spoken = ALL
languages_understood = ALL
hud_possible = list(ANTAG_HUD)
mouse_opacity = 0 //can't be clicked
var/faith = 100 //For initial prophet appointing/stupid purchase
var/max_faith = 100
var/side = "neutral" //Red or Blue for the gamemode
var/obj/structure/divine/nexus/god_nexus = null //The source of the god's power in this realm, kill it and the god is kill
var/nexus_required = FALSE //If the god dies from losing it's nexus, defaults to off so that gods don't instantly die at roundstart
var/followers_required = 0 //Same as above
var/alive_followers = 0
var/list/structures = list()
var/list/conduits = list()
var/prophets_sacrificed_in_name = 0
var/image/ghostimage = null //For observer with darkness off visiblity
var/list/prophets = list()
var/datum/action/innate/godspeak/speak2god
/mob/camera/god/New()
..()
update_icons()
build_hog_construction_lists()
//Force nexuses after 15 minutes in hand of god mode
if(ticker && ticker.mode && ticker.mode.name == "hand of god")
addtimer(src, "forceplacenexus", 9000, FALSE)
//Rebuilds the list based on the gamemode's lists
//As they are the most accurate each tick
/mob/camera/god/proc/get_my_followers()
switch(side)
if("red")
. = ticker.mode.red_deity_followers|ticker.mode.red_deity_prophets
if("blue")
. = ticker.mode.blue_deity_followers|ticker.mode.blue_deity_prophets
else
. = list()
/mob/camera/god/Destroy()
var/list/followers = get_my_followers()
for(var/datum/mind/F in followers)
if(F.current)
F.current << "<span class='danger'>Your god is DEAD!</span>"
for(var/X in prophets)
speak2god.Remove(X)
ghost_darkness_images -= ghostimage
updateallghostimages()
return ..()
/mob/camera/god/proc/forceplacenexus()
if(god_nexus)
return
if(ability_cost(0,1,0))
place_nexus()
else
if(blobstart.len) //we're on invalid turf, try to pick from blobstart
loc = pick(blobstart)
place_nexus() //if blobstart fails, places on dense turf, but better than nothing
src << "<span class='danger'>You failed to place your nexus, and it has been placed for you!</span>"
/mob/camera/god/update_icons()
icon_state = "[initial(icon_state)]-[side]"
if(ghostimage)
ghost_darkness_images -= ghostimage
ghostimage = image(src.icon,src,src.icon_state)
ghost_darkness_images |= ghostimage
updateallghostimages()
/mob/camera/god/Stat()
..()
if(statpanel("Status"))
if(god_nexus)
stat("Nexus health: ", god_nexus.health)
stat("Followers: ", alive_followers)
stat("Faith: ", "[faith]/[max_faith]")
/mob/camera/god/Login()
..()
sync_mind()
src << "<span class='notice'>You are a deity!</span>"
src << "You are a deity and are worshipped by a cult! You are rather weak right now, but that will change as you gain more followers."
src << "You will need to place an anchor to this world, a <b>Nexus</b>, in two minutes. If you don't, one will be placed immediately below you."
src << "Your <b>Follower</b> count determines how many people believe in you and are a part of your cult."
src << "Your <b>Nexus Integrity</b> tells you the condition of your nexus. If your nexus is destroyed, you will die. Place your Nexus on a safe, isolated place, that is still accessible to your followers."
src << "Your <b>Faith</b> is used to interact with the world. This will regenerate on its own, and it goes faster when you have more followers and power pylons."
src << "The first thing you should do after placing your nexus is to <b>appoint a prophet</b>. Only prophets can hear you talk, unless you use an expensive power."
update_health_hud()
/mob/camera/god/update_health_hud()
if(god_nexus && hud_used && hud_used.healths)
hud_used.healths.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'> <font color='lime'>[god_nexus.health] </font></div>"
/mob/camera/god/proc/add_faith(faith_amt)
if(faith_amt)
faith = round(Clamp(faith+faith_amt, 0, max_faith))
if(hud_used && hud_used.deity_power_display)
hud_used.deity_power_display.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'> <font color='cyan'>[faith] </font></div>"
/mob/camera/god/proc/place_nexus()
if(god_nexus || (z != 1))
return 0
var/obj/structure/divine/nexus/N = new(get_turf(src))
N.assign_deity(src)
god_nexus = N
nexus_required = TRUE
verbs -= /mob/camera/god/verb/constructnexus
//verbs += /mob/camera/god/verb/movenexus //Translocators have no sprite
update_health_hud()
var/area/A = get_area(src)
if(A)
var/areaname = A.name
var/list/followers = get_my_followers()
for(var/datum/mind/F in followers)
if(F.current)
F.current << "<span class='boldnotice'>Your god's nexus is in \the [areaname]</span>"
/mob/camera/god/verb/freeturret()
set category = "Deity"
set name = "Free Turret (0)"
set desc = "Place a single turret, for 0 faith."
if(!ability_cost(0,1,1))
return
var/obj/structure/divine/defensepylon/DP = new(get_turf(src))
DP.assign_deity(src)
verbs -= /mob/camera/god/verb/freeturret
/mob/camera/god/proc/update_followers()
alive_followers = 0
var/list/all_followers = get_my_followers()
for(var/datum/mind/F in all_followers)
if(F.current && F.current.stat != DEAD)
alive_followers++
if(hud_used && hud_used.deity_follower_display)
hud_used.deity_follower_display.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'> <font color='red'>[alive_followers] </font></div>"
/mob/camera/god/proc/check_death()
if(!alive_followers)
src << "<span class='userdanger'>You no longer have any followers. You shudder as you feel your existence cease...</span>"
if(god_nexus && !qdeleted(god_nexus))
god_nexus.visible_message("<span class='danger'>\The [src] suddenly disappears!</span>")
qdel(god_nexus)
qdel(src)
/mob/camera/god/say(msg)
if(!msg)
return
if(client)
if(client.prefs.muted & MUTE_IC)
src << "You cannot send IC messages (muted)."
return
if(src.client.handle_spam_prevention(msg,MUTE_IC))
return
if(stat)
return
god_speak(msg)
/mob/camera/god/proc/god_speak(msg)
log_say("Hand of God: [capitalize(side)] God/[key_name(src)] : [msg]")
msg = trim(copytext(sanitize(msg), 1, MAX_MESSAGE_LEN))
if(!msg)
return
msg = say_quote(msg, get_spans())
var/rendered = "<font color='[src.side]'><i><span class='game say'>Divine Telepathy,</i> <span class='name'>[name]</span> <span class='message'>[msg]</span></span></font>"
src << rendered
for(var/mob/M in mob_list)
if(is_handofgod_myfollowers(M))
M << rendered
if(isobserver(M))
var/link = FOLLOW_LINK(M, src)
M << "[link] [rendered]"
/mob/camera/god/emote(act,m_type = 1 ,msg = null)
return
/mob/camera/god/Move(NewLoc, Dir = 0)
loc = NewLoc
/mob/camera/god/Topic(href, href_list)
if(href_list["create_structure"])
if(!ability_cost(75,1,1))
return
var/obj/structure/divine/construct_type = text2path(href_list["create_structure"]) //it's a path but we need to initial() some vars
if(!construct_type)
return
add_faith(-75)
var/obj/structure/divine/construction_holder/CH = new(get_turf(src))
CH.assign_deity(src)
CH.setup_construction(construct_type)
CH.visible_message("<span class='notice'>[src] has created a transparent, unfinished [initial(construct_type.name)]. It can be finished by adding materials.</span>")
src << "<span class='boldnotice'>You may click a construction site to cancel it, but only faith is refunded.</span>"
structure_construction_ui(src)
return
if(href_list["place_trap"])
if(!ability_cost(20,1,1))
return
var/atom/trap_type = text2path(href_list["place_trap"])
if(!trap_type)
return
src << "You lay \a [initial(trap_type.name)]"
add_faith(-20)
new trap_type(get_turf(src))
return
..()
/mob/camera/god/proc/structure_construction_ui(mob/camera/god/user)
var/dat = ""
for(var/t in global_handofgod_structuretypes)
if(global_handofgod_structuretypes[t])
var/obj/structure/divine/apath = global_handofgod_structuretypes[t]
dat += "<center><B>[capitalize(t)]</B></center><BR>"
var/imgstate = initial(apath.autocolours) ? "[initial(apath.icon_state)]-[side]" : "[initial(apath.icon_state)]"
var/icon/I = icon('icons/obj/hand_of_god_structures.dmi',imgstate)
var/img_component = lowertext(t)
//I hate byond, but atleast it autocaches these so it's only 1*number_of_structures worth of actual calls
user << browse_rsc(I,"hog_structure-[img_component].png")
dat += "<center><img src='hog_structure-[img_component].png' height=64 width=64></center>"
dat += "Description: [initial(apath.desc)]<BR>"
dat += "<center><a href='?src=\ref[src];create_structure=[apath]'>Construct [capitalize(t)]</a></center><BR><BR>"
var/datum/browser/popup = new(src, "structures","Construct Structure",350,500)
popup.set_content(dat)
popup.open()
/mob/camera/god/proc/trap_construction_ui(mob/camera/god/user)
var/dat = ""
for(var/t in global_handofgod_traptypes)
if(global_handofgod_traptypes[t])
var/obj/structure/divine/trap/T = global_handofgod_traptypes[t]
dat += "<center><B>[capitalize(t)]</B></center><BR>"
var/icon/I = icon('icons/obj/hand_of_god_structures.dmi',"[initial(T.icon_state)]")
var/img_component = lowertext(t)
user << browse_rsc(I,"hog_trap-[img_component].png")
dat += "<center><img src='hog_trap-[img_component].png' height=64 width=64></center>"
dat += "Description: [initial(T.desc)]<BR>"
dat += "<center><a href='?src=\ref[src];place_trap=[T]'>Place [capitalize(t)]</a></center><BR><BR>"
var/datum/browser/popup = new(src, "traps", "Place Trap",350,500)
popup.set_content(dat)
popup.open()
+306
View File
@@ -0,0 +1,306 @@
/obj/item/weapon/banner
name = "banner"
icon = 'icons/obj/items.dmi'
icon_state = "banner"
item_state = "banner"
desc = "A banner with Nanotrasen's logo on it."
var/moralecooldown = 0
var/moralewait = 600
/obj/item/weapon/banner/attack_self(mob/living/carbon/human/user)
if(moralecooldown + moralewait > world.time)
return
var/side = ""
if(is_handofgod_redcultist(user))
side = "red"
else if (is_handofgod_bluecultist(user))
side = "blue"
if(!side)
return
user << "<span class='notice'>You increase the morale of your fellows!</span>"
moralecooldown = world.time
for(var/mob/living/carbon/human/H in range(4,get_turf(src)))
if((side == "red") && is_handofgod_redcultist(H) || (side == "blue") && is_handofgod_bluecultist(H))
H << "<span class='notice'>Your morale is increased by [user]'s banner!</span>"
H.adjustBruteLoss(-15)
H.adjustFireLoss(-15)
H.AdjustStunned(-2)
H.AdjustWeakened(-2)
H.AdjustParalysis(-2)
/obj/item/weapon/banner/red
name = "red banner"
icon_state = "banner-red"
item_state = "banner-red"
desc = "A banner with the logo of the red deity."
/obj/item/weapon/banner/red/examine(mob/user)
..()
if(is_handofgod_redcultist(user))
user << "A banner representing our might against the heretics. We may use it to increase the morale of our fellow members!"
else if(is_handofgod_bluecultist(user))
user << "A heretical banner that should be destroyed posthaste."
/obj/item/weapon/banner/blue
name = "blue banner"
icon_state = "banner-blue"
item_state = "banner-blue"
desc = "A banner with the logo of the blue deity"
/obj/item/weapon/banner/blue/examine(mob/user)
..()
if(is_handofgod_redcultist(user))
user << "A heretical banner that should be destroyed posthaste."
else if(is_handofgod_bluecultist(user))
user << "A banner representing our might against the heretics. We may use it to increase the morale of our fellow members!"
/obj/item/weapon/storage/backpack/bannerpack
name = "nanotrasen banner backpack"
desc = "It's a backpack with lots of extra room. A banner with Nanotrasen's logo is attached, that can't be removed."
max_combined_w_class = 27 //6 more then normal, for the tradeoff of declaring yourself an antag at all times.
icon_state = "bannerpack"
/obj/item/weapon/storage/backpack/bannerpack/red
name = "red banner backpack"
desc = "It's a backpack with lots of extra room. A red banner is attached, that can't be removed."
icon_state = "bannerpack-red"
/obj/item/weapon/storage/backpack/bannerpack/blue
name = "blue banner backpack"
desc = "It's a backpack with lots of extra room. A blue banner is attached, that can't be removed."
icon_state = "bannerpack-blue"
//this is all part of one item set
/obj/item/clothing/suit/armor/plate/crusader
name = "Crusader's Armour"
icon_state = "crusader"
w_class = 4 //bulky
slowdown = 2.0 //gotta pretend we're balanced.
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
armor = list(melee = 50, bullet = 50, laser = 50, energy = 40, bomb = 60, bio = 0, rad = 0)
/obj/item/clothing/suit/armor/plate/crusader/red
icon_state = "crusader-red"
/obj/item/clothing/suit/armor/plate/crusader/blue
icon_state = "crusader-blue"
/obj/item/clothing/suit/armor/plate/crusader/examine(mob/user)
..()
if(!is_handofgod_cultist(user))
user << "Armour that's comprised of metal and cloth."
else
user << "Armour that was used to protect from backstabs, gunshots, explosives, and lasers. The original wearers of this type of armour were trying to avoid being murdered. Since they're not around anymore, you're not sure if they were successful or not."
/obj/item/clothing/head/helmet/plate/crusader
name = "Crusader's Hood"
icon_state = "crusader"
w_class = 3 //normal
flags_inv = HIDEHAIR|HIDEEARS|HIDEFACE
armor = list(melee = 50, bullet = 50, laser = 50, energy = 40, bomb = 60, bio = 0, rad = 0)
/obj/item/clothing/head/helmet/plate/crusader/blue
icon_state = "crusader-blue"
/obj/item/clothing/head/helmet/plate/crusader/red
icon_state = "crusader-red"
/obj/item/clothing/head/helmet/plate/crusader/examine(mob/user)
..()
if(!is_handofgod_cultist(user))
user << "A brownish hood."
else
user << "A hood that's very protective, despite being made of cloth. Due to the tendency of the wearer to be targeted for assassinations, being protected from being shot in the face was very important.."
//Prophet helmet
/obj/item/clothing/head/helmet/plate/crusader/prophet
name = "Prophet's Hat"
alternate_worn_icon = 'icons/mob/large-worn-icons/64x64/head.dmi'
flags = 0
armor = list(melee = 60, bullet = 60, laser = 60, energy = 50, bomb = 70, bio = 50, rad = 50) //religion protects you from disease and radiation, honk.
worn_x_dimension = 64
worn_y_dimension = 64
var/side = "neither"
/obj/item/clothing/head/helmet/plate/crusader/prophet/equipped(mob/living/carbon/user, slot)
var/faithful = 0
if(slot == slot_head)
switch(side)
if("blue")
faithful = is_handofgod_bluecultist(user)
if("red")
faithful = is_handofgod_redcultist(user)
else
faithful = 1
if(!faithful)
user << "<span class='danger'>Your mind is assaulted by a vast power, furious at your desecration!</span>"
user.emote("scream")
user.adjustFireLoss(10)
user.unEquip(src)
user.head = null
user.update_inv_head()
src.screen_loc = null
user.Weaken(1)
/obj/item/clothing/head/helmet/plate/crusader/prophet/red
icon_state = "prophet-red"
side = "red"
/obj/item/clothing/head/helmet/plate/crusader/prophet/blue
icon_state = "prophet-blue"
side = "blue"
/obj/item/clothing/head/helmet/plate/crusader/prophet/examine(mob/user)
..()
if(!is_handofgod_cultist(user))
user << "A brownish, religious-looking hat."
else
user << "A hat bestowed upon a prophet of gods and demigods."
user << "This hat belongs to the [side] god."
//Structure conversion staff
/obj/item/weapon/godstaff
name = "godstaff"
icon_state = "godstaff-red"
var/mob/camera/god/god = null
var/staffcooldown = 0
var/staffwait = 30
/obj/item/weapon/godstaff/examine(mob/user)
..()
if(!is_handofgod_cultist(user))
user << "It's a stick..?"
else
user << "A powerful staff capable of changing the allegiance of god/demigod structures."
/obj/item/weapon/godstaff/attack_self(mob/living/carbon/user)
if((god && !god.is_handofgod_myprophet(user)) || !god)
user << "<span class='danger'>YOU ARE NOT THE CHOSEN ONE!</span>"
return
if(!(istype(user.head, /obj/item/clothing/head/helmet/plate/crusader/prophet)))
user << "<span class='warning'>Your connection to your diety isn't strong enough! You must wear your big hat!</span>"
return
if(staffcooldown + staffwait > world.time)
return
user.visible_message("[user] chants deeply and waves their staff")
if(do_after(user, 20,1,src))
for(var/obj/structure/divine/R in orange(3,user))
user.say("Grant us true sight my god!")
if(istype(R, /obj/structure/divine/nexus)|| istype(R, /obj/structure/divine/trap))
continue
R.visible_message("<span class='danger'>[R] suddenly appears!</span>")
R.invisibility = 0
R.alpha = initial(R.alpha)
R.density = initial(R.density)
R.activate()
staffcooldown = world.time
/obj/item/weapon/godstaff/red
icon_state = "godstaff-red"
/obj/item/weapon/godstaff/blue
icon_state = "godstaff-blue"
/obj/item/clothing/gloves/plate
name = "Plate Gauntlets"
icon_state = "crusader"
siemens_coefficient = 0
cold_protection = HANDS
min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT
heat_protection = HANDS
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
/obj/item/clothing/gloves/plate/red
icon_state = "crusader-red"
/obj/item/clothing/gloves/plate/blue
icon_state = "crusader-blue"
/obj/item/clothing/gloves/plate/examine(mob/user)
..()
if(!is_handofgod_cultist(user))
usr << "They're like gloves, but made of metal."
else
usr << "Protective gloves that are also blessed to protect from heat and shock."
/obj/item/clothing/shoes/plate
name = "Plate Boots"
icon_state = "crusader"
w_class = 3 //normal
armor = list(melee = 50, bullet = 50, laser = 50, energy = 40, bomb = 60, bio = 0, rad = 0) //does this even do anything on boots?
flags = NOSLIP
cold_protection = FEET
min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT
heat_protection = FEET
max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT
/obj/item/clothing/shoes/plate/red
icon_state = "crusader-red"
/obj/item/clothing/shoes/plate/blue
icon_state = "crusader-blue"
/obj/item/clothing/shoes/plate/examine(mob/user)
..()
if(!is_handofgod_cultist(user))
usr << "Metal boots, they look heavy."
else
usr << "Heavy boots that are blessed for sure footing. You'll be safe from being taken down by the heresy that is the banana peel."
/obj/item/weapon/storage/box/itemset/crusader
name = "Crusader's Armour Set" //i can't into ck2 references
desc = "This armour is said to be based on the armor of kings on another world thousands of years ago, who tended to assassinate, conspire, and plot against everyone who tried to do the same to them. Some things never change."
/obj/item/weapon/storage/box/itemset/crusader/blue/New()
..()
contents = list()
sleep(1)
new /obj/item/clothing/suit/armor/plate/crusader/blue(src)
new /obj/item/clothing/head/helmet/plate/crusader/blue(src)
new /obj/item/clothing/gloves/plate/blue(src)
new /obj/item/clothing/shoes/plate/blue(src)
/obj/item/weapon/storage/box/itemset/crusader/red/New()
..()
contents = list()
sleep(1)
new /obj/item/clothing/suit/armor/plate/crusader/red(src)
new /obj/item/clothing/head/helmet/plate/crusader/red(src)
new /obj/item/clothing/gloves/plate/red(src)
new /obj/item/clothing/shoes/plate/red(src)
/obj/item/weapon/claymore/hog
force = 30
armour_penetration = 15
+129
View File
@@ -0,0 +1,129 @@
/datum/objective/build
dangerrating = 15
martyr_compatible = 1
/datum/objective/build/proc/gen_amount_goal(lower, upper)
target_amount = rand(lower, upper)
explanation_text = "Build [target_amount] shrines."
return target_amount
/datum/objective/build/check_completion()
if(!owner || !owner.current)
return 0
var/shrines = 0
if(is_handofgod_god(owner.current))
var/mob/camera/god/G = owner.current
for(var/obj/structure/divine/shrine/S in G.structures)
S++
return (shrines >= target_amount)
/datum/objective/deicide
dangerrating = 20
martyr_compatible = 1
/datum/objective/deicide/check_completion()
if(target)
if(target.current) //Gods are deleted when they lose
return 0
return 1
/datum/objective/deicide/find_target()
if(!owner || !owner.current)
return
if(is_handofgod_god(owner.current))
var/mob/camera/god/G = owner.current
if(G.side == "red")
if(ticker.mode.blue_deities.len)
target = ticker.mode.blue_deities[1]
if(G.side == "blue")
if(ticker.mode.red_deities.len)
target = ticker.mode.red_deities[1]
if(!target)
return 0
update_explanation_text()
/datum/objective/deicide/update_explanation_text()
..()
if(target && target.current)
explanation_text = "Phase [target.name], the false god, out of this plane of existence.."
else
explanation_text = "Free Objective"
/datum/objective/follower_block
explanation_text = "Do not allow any followers of the false god to escape on the station's shuttle alive."
dangerrating = 25
martyr_compatible = 1
/datum/objective/follower_block/check_completion()
var/side = "ABORT"
if(is_handofgod_redcultist(owner.current))
side = "red"
else if(is_handofgod_bluecultist(owner.current))
side = "blue"
if(side == "ABORT")
return 0
var/area/A = SSshuttle.emergency.areaInstance
for(var/mob/living/player in player_list)
if(player.mind && player.stat != DEAD && get_area(player) == A)
if(side == "red")
if(is_handofgod_bluecultist(player))
return 0
else if(side == "blue")
if(is_handofgod_redcultist(player))
return 0
return 1
/datum/objective/escape_followers
dangerrating = 5
/datum/objective/escape_followers/proc/gen_amount_goal(lower,upper)
target_amount = rand(lower,upper)
explanation_text = "Your will must surpass this station. Having [target_amount] followers escape on the shuttle or pods will allow that."
return target_amount
/datum/objective/escape_followers/check_completion()
var/escaped = 0
if(is_handofgod_god(owner.current))
var/mob/camera/god/G = owner.current
if(G.side == "red")
for(var/datum/mind/follower_mind in ticker.mode.red_deity_followers)
if(follower_mind.current && follower_mind.current.stat != DEAD)
if(follower_mind.current.onCentcom())
escaped++
if(G.side == "blue")
for(var/datum/mind/follower_mind in ticker.mode.blue_deity_followers)
if(follower_mind.current && follower_mind.current.stat != DEAD)
if(follower_mind.current.onCentcom())
escaped++
return (escaped >= target_amount)
/datum/objective/sacrifice_prophet
explanation_text = "A false prophet is preaching their god's faith on the station. Sacrificing them will show the mortals who the true god is."
dangerrating = 10
/datum/objective/sacrifice_prophet/check_completion()
var/mob/camera/god/G = owner.current
if(istype(G))
return G.prophets_sacrificed_in_name
return 0
+363
View File
@@ -0,0 +1,363 @@
/mob/camera/god/proc/ability_cost(cost = 0,structures = 0, requires_conduit = 0, can_place_near_enemy_nexus = 0)
if(faith < cost)
src << "<span class='danger'>You lack the faith!</span>"
return 0
if(structures)
if(!isturf(loc) || istype(loc, /turf/open/space))
src << "<span class='danger'>Your structure would just float away, you need stable ground!</span>"
return 0
var/turf/T = get_turf(src)
if(T)
if(T.density)
src << "<span class='danger'>There is something blocking your structure!</span>"
return 0
for(var/atom/movable/AM in T)
if(AM == src)
continue
if(AM.density)
src << "<span class='danger'>There is something blocking your structure!</span>"
return 0
if(requires_conduit)
//Organised this way as there can be multiple conduits, so it's more likely to be a conduit check.
var/valid = 0
for(var/obj/structure/divine/conduit/C in conduits)
if(get_dist(src, C) <= CONDUIT_RANGE)
valid++
break
if(!valid)
if(get_dist(src, god_nexus) <= CONDUIT_RANGE)
valid++
if(!valid)
src << "<span class='danger'>You must be near your Nexus or a Conduit to do this!</span>"
return 0
if(!can_place_near_enemy_nexus)
var/datum/mind/enemy
switch(side)
if("red")
if(ticker.mode.blue_deities.len)
enemy = ticker.mode.blue_deities[1]
if("blue")
if(ticker.mode.red_deities.len)
enemy = ticker.mode.red_deities[1]
if(enemy && is_handofgod_god(enemy.current))
var/mob/camera/god/enemy_god = enemy.current
if(enemy_god.god_nexus && (get_dist(src,enemy_god.god_nexus) <= CONDUIT_RANGE*2))
src << "<span class='danger'>You are too close to the other god's stronghold!</span>"
return 0
return 1
/mob/camera/god/verb/returntonexus()
set category = "Deity"
set name = "Goto Nexus"
set desc = "Teleports you to your next instantly."
if(god_nexus)
Move(get_turf(god_nexus))
else
src << "You don't even have a Nexus, construct one."
/mob/camera/god/verb/jumptofollower()
set category = "Deity"
set name = "Jump to Follower"
set desc = "Teleports you to one of your followers."
var/list/following = list()
if(side == "red")
following = ticker.mode.red_deity_followers|ticker.mode.red_deity_prophets
else if(side == "blue")
following = ticker.mode.blue_deity_followers|ticker.mode.blue_deity_prophets
else
src << "You are unaligned, and thus do not have followers"
return
var/datum/mind/choice = input("Choose a follower","Jump to Follower") as null|anything in following
if(choice && choice.current)
Move(get_turf(choice.current))
/mob/camera/god/verb/newprophet()
set category = "Deity"
set name = "Appoint Prophet (100)"
set desc = "Appoint one of your followers as your Prophet, who can hear your words"
var/list/following = list()
if(!ability_cost(100))
return
if(side == "red")
var/datum/mind/old_proph = locate() in ticker.mode.red_deity_prophets
if(old_proph && old_proph.current && old_proph.current.stat != DEAD)
src << "You can only have one prophet alive at a time."
return
else
following = ticker.mode.red_deity_followers
else if(side == "blue")
var/datum/mind/old_proph = locate() in ticker.mode.blue_deity_prophets
if(old_proph && old_proph.current && old_proph.current.stat != DEAD)
src << "You can only have one prophet alive at a time."
return
else
following = ticker.mode.blue_deity_followers
else
src << "You are unalligned, and thus do not have prophets"
return
var/datum/mind/choice = input("Choose a follower to make into your prophet","Prophet Uplifting") as null|anything in following
if(choice && choice.current && choice.current.stat != DEAD)
src << "You choose [choice.current] as your prophet."
choice.make_Handofgod_prophet(side)
speak2god = new()
speak2god.god = src
speak2god.Grant(choice.current)
//Prophet gear
var/mob/living/carbon/human/H = choice.current
var/popehat = null
var/popestick = null
var/success = ""
switch(side)
if("red")
popehat = /obj/item/clothing/head/helmet/plate/crusader/prophet/red
popestick = /obj/item/weapon/godstaff/red
if("blue")
popehat = /obj/item/clothing/head/helmet/plate/crusader/prophet/blue
popestick = /obj/item/weapon/godstaff/blue
if(popehat)
var/obj/item/clothing/head/helmet/plate/crusader/prophet/P = new popehat()
if(H.equip_to_slot_if_possible(P,slot_in_backpack,0,1,1))
success = "It is in your backpack."
else
H.unEquip(H.head)
H.equip_to_slot_or_del(P,slot_head)
success = "It is on your head."
if(success)
H << "<span class='boldnotice'>A powerful hat has been bestowed upon you, you will need to wear it to utilize your staff fully.</span>"
H << "<span class='boldnotice'>[success]</span>"
if(popestick)
var/obj/item/weapon/godstaff/G = new popestick()
G.god = src
if(!H.equip_to_slot_if_possible(G,slot_in_backpack,0,1,1))
if(!H.put_in_hands(G))
G.loc = get_turf(H)
success = "It is on the floor..."
else
success = "It is in your hands..."
else
success = "It is in your backpack..."
if(success)
H << "<span class='boldnotice'>A powerful staff has been bestowed upon you, you can use this to convert the false god's structures!</span>"
H << "<span class='boldnotice'>[success]</span>"
//end prophet gear
add_faith(-100)
/mob/camera/god/verb/talk(msg as text)
set category = "Deity"
set name = "Talk to Anyone (20)"
set desc = "Allows you to send a message to anyone, regardless of their faith."
if(!ability_cost(20))
return
var/mob/choice = input("Choose who you wish to talk to", "Talk to ANYONE") as null|anything in mob_list
if(choice)
var/original = msg
msg = "<B>You hear a voice coming from everywhere and nowhere... <i>[msg]</i></B>"
choice << msg
src << "You say the following to [choice], [original]"
add_faith(-20)
/mob/camera/god/verb/smite()
set category = "Deity"
set name = "Smite (40)"
set desc = "Hits anything under you with a moderate amount of damage."
if(!ability_cost(40,0,1))
return
if(!range(7,god_nexus))
src << "You lack the strength to smite this far from your nexus."
return
var/has_smitten = 0 //Hast thou been smitten, infidel?
for(var/mob/living/L in get_turf(src))
L.adjustFireLoss(20)
L.adjustBruteLoss(20)
L << "<span class='danger'><B>You feel the wrath of [name]!<B></span>"
has_smitten = 1
if(has_smitten)
add_faith(-40)
/mob/camera/god/verb/disaster()
set category = "Deity"
set name = "Invoke Disaster (300)" //difficult to reach without lots of followers
set desc = "Tug at the fibres of reality itself and bend it to your whims!"
if(!ability_cost(300,0,1))
return
var/event = pick(/datum/round_event/meteor_wave, /datum/round_event/communications_blackout, /datum/round_event/radiation_storm, /datum/round_event/carp_migration,
/datum/round_event/spacevine, /datum/round_event/vent_clog, /datum/round_event/wormholes)
if(event)
new event()
add_faith(-300)
/mob/camera/god/verb/constructnexus()
set category = "Deity"
set name = "Construct Nexus"
set desc = "Instantly creates your nexus, You can only do this once, make sure you're happy with it!"
if(!ability_cost(0,1,0))
return
place_nexus()
/* //Transolocators have no sprite
/mob/camera/god/verb/movenexus()
set category = "Deity"
set name = "Relocate Nexus (50)"
set desc = "Instantly relocates your nexus to an existing translocator belonging to your faith, this destroys the translocator in the process"
if(ability_cost(50,0,0) && god_nexus)
var/list/translocators = list()
var/list/used_keys = list()
for(var/obj/structure/divine/translocator/T in structures)
translocators["[T.name] ([get_area(T)])"] = T
if(!translocators.len)
src << "<span class='warning'>You have no translocators!</span>"
return
var/picked = input(src,"Choose a translocator","Relocate Nexus") as null|anything in translocators
if(!picked || !translocators[picked])
return
var/obj/structure/divine/translocator/T = translocators[T]
var/turf/Tturf = get_turf(T)
god_nexus.loc = T
translocators[picked] = null
add_faith(-50)
qdel(T)
*/
/mob/camera/god/verb/construct_structures()
set category = "Deity"
set name = "Construct Structure (75)"
set desc = "Create the foundation of a divine object."
if(!ability_cost(75,1,1))
return
structure_construction_ui(src)
/mob/camera/god/verb/construct_traps()
set category = "Deity"
set name = "Construct Trap (20)"
set desc = "Creates a ward or trap."
if(!ability_cost(20,1,1))
return
trap_construction_ui(src)
/mob/camera/god/verb/construct_items()
set category = "Deity"
set name = "Construct Items (20)"
set desc = "Construct some items for your followers"
if(!ability_cost(20,1,1))
return
var/list/item_types = list("claymore sword" = /obj/item/weapon/claymore/hog)
if(side == "red")
item_types["red banner"] = /obj/item/weapon/banner/red
item_types["red bannerbackpack"] = /obj/item/weapon/storage/backpack/bannerpack/red
item_types["red armour"] = /obj/item/weapon/storage/box/itemset/crusader/red
else if(side == "blue")
item_types["blue banner"] = /obj/item/weapon/banner/blue
item_types["blue bannerbackpack"] = /obj/item/weapon/storage/backpack/bannerpack/blue
item_types["blue armour"] = /obj/item/weapon/storage/box/itemset/crusader/blue
var/item = input("Choose what you wish to create.", "Divine Items") as null|anything in item_types
if(!item || !item_types[item] || !ability_cost(20,1,1))
return
src << "You produce \a [item]"
add_faith(-20)
var/itemtype = item_types[item]
new itemtype (get_turf(src))
/mob/camera/god/verb/veil_structures()
set category = "Deity"
set name = "Veil Structures (20)"
set desc = "Hide your structures from sight and touch, but prevent yourself from using them."
if(!ability_cost(20,1,1))
return
src << "You focus your powers and start dragging your influence into the spiritual plane."
for(var/mob/M in orange(3,src))//Yes I know this is terrible, but visible message doesnt work for this
M << "<span class='warning'>The air begins to shimmer...</span>"
if(do_after(src, 30, 0, src))
for(var/obj/structure/divine/R in orange(3,src))
if(istype(R, /obj/structure/divine/nexus)|| istype(R, /obj/structure/divine/trap)||(src.side != R.side))
continue
R.visible_message("<span class='danger'>[R] fades away.</span>")
R.invisibility = 55
R.alpha = 100 //To help ghosts distinguish hidden structures
R.density = 0
R.deactivate()
src << "You hide your influence from view"
add_faith(-20)
/mob/camera/god/verb/reveal_structures()
set category = "Deity"
set name = "Reveal Structures (20)"
set desc = "Make your structures visible again and allow them to be used."
if(!ability_cost(20,1,1))
return
src << "You focus your powers and start dragging your influence into the material plane."
for(var/mob/M in orange(3,src))//Yes I know this is terrible, but visible message doesnt work for this
M << "<span class='warning'>The air begins to shimmer...</span>"
if(do_after(src, 40, 0, src))
for(var/obj/structure/divine/R in orange(3,src))
if(istype(R, /obj/structure/divine/nexus)|| istype(R, /obj/structure/divine/trap)||(src.side != R.side))
continue
R.visible_message("<span class='danger'>[R] suddenly appears!</span>")
R.invisibility = 0
R.alpha = initial(R.alpha)
R.density = initial(R.density)
R.activate()
src << "You bring your influence into view"
add_faith(-20)
+658
View File
@@ -0,0 +1,658 @@
/proc/build_hog_construction_lists()
if(global_handofgod_traptypes.len && global_handofgod_structuretypes.len)
return
var/list/types = subtypesof(/obj/structure/divine) - /obj/structure/divine/trap
for(var/T in types)
var/obj/structure/divine/D = T
if(initial(D.constructable))
if(initial(D.trap))
global_handofgod_traptypes[initial(D.name)] = T
else
global_handofgod_structuretypes[initial(D.name)] = T
/obj/structure/divine
name = "divine construction site"
icon = 'icons/obj/hand_of_god_structures.dmi'
desc = "An unfinished divine building"
anchored = 1
density = 1
var/constructable = TRUE
var/trap = FALSE
var/metal_cost = 0
var/glass_cost = 0
var/lesser_gem_cost = 0
var/greater_gem_cost = 0
var/mob/camera/god/deity
var/side = "neutral" //"blue" or "red", also used for colouring structures when construction is started by a deity
var/health = 100
var/maxhealth = 100
var/deactivated = 0 //Structures being hidden can't be used. Mainly to prevent invisible defense pylons.
var/autocolours = TRUE //do we colour to our side?
/obj/structure/divine/New()
..()
/obj/structure/divine/proc/deactivate()
deactivated = 1
/obj/structure/divine/proc/activate()
deactivated = 0
/obj/structure/divine/proc/update_icons()
if(autocolours)
icon_state = "[initial(icon_state)]-[side]"
/obj/structure/divine/Destroy()
if(deity)
deity.structures -= src
return ..()
/obj/structure/divine/attackby(obj/item/I, mob/user)
//Structure conversion/capture
if(istype(I, /obj/item/weapon/godstaff))
if(!is_handofgod_cultist(user))
user << "<span class='notice'>You're not quite sure what the hell you're even doing.</span>"
return
var/obj/item/weapon/godstaff/G = I
if(G.god && deity != G.god)
assign_deity(G.god, alert_old_deity = TRUE)
visible_message("<span class='boldnotice'>\The [src] has been captured by [user]!</span>")
else
return ..()
/obj/structure/divine/attacked_by(obj/item/I, mob/living/user)
..()
take_damage(I.force, I.damtype, 1)
/obj/structure/divine/proc/take_damage(damage, damage_type = BRUTE, sound_effect = 1)
switch(damage_type)
if(BRUTE)
if(sound_effect)
if(damage)
playsound(loc, 'sound/weapons/smash.ogg', 50, 1)
else
playsound(loc, 'sound/weapons/tap.ogg', 50, 1)
if(BURN)
if(sound_effect)
playsound(src.loc, 'sound/items/Welder.ogg', 100, 1)
else
return
health -= damage
if(!health)
visible_message("<span class='danger'>\The [src] was destroyed!</span>")
qdel(src)
/obj/structure/divine/bullet_act(obj/item/projectile/P)
. = ..()
take_damage(P.damage, P.damage_type, 0)
/obj/structure/divine/attack_alien(mob/living/carbon/alien/humanoid/user)
user.changeNext_move(CLICK_CD_MELEE)
user.do_attack_animation(src)
add_hiddenprint(user)
visible_message("<span class='warning'>\The [user] slashes at [src]!</span>")
playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1)
take_damage(20, BRUTE, 0)
/obj/machinery/attack_animal(mob/living/simple_animal/M)
M.changeNext_move(CLICK_CD_MELEE)
M.do_attack_animation(src)
if(M.melee_damage_upper > 0)
M.visible_message("<span class='danger'>[M.name] smashes against \the [src.name].</span>",\
"<span class='danger'>You smash against the [src.name].</span>")
take_damage(rand(M.melee_damage_lower,M.melee_damage_upper), M.melee_damage_type, 1)
/obj/structure/divine/proc/assign_deity(mob/camera/god/new_deity, alert_old_deity = TRUE)
if(!new_deity)
return 0
if(deity)
if(alert_old_deity)
deity << "<span class='danger'><B>Your [name] was captured by [new_deity]'s cult!</B></span>"
deity.structures -= src
deity = new_deity
deity.structures |= src
side = deity.side
update_icons()
return 1
/obj/structure/divine/construction_holder
alpha = 125
constructable = FALSE
var/obj/structure/divine/construction_result = /obj/structure/divine //a path, but typed to /obj/structure/divine for initial()
/obj/structure/divine/construction_holder/assign_deity(mob/camera/god/new_deity, alert_old_deity = TRUE)
if(..())
color = side
/obj/structure/divine/construction_holder/attack_god(mob/camera/god/user)
if(user.side == side && construction_result)
user.add_faith(75)
visible_message("<span class='danger'>[user] has cancelled \the [initial(construction_result.name)]")
qdel(src)
/obj/structure/divine/construction_holder/proc/setup_construction(construct_type)
if(ispath(construct_type))
construction_result = construct_type
name = "[initial(construction_result.name)] construction site "
icon_state = initial(construction_result.icon_state)
metal_cost = initial(construction_result.metal_cost)
glass_cost = initial(construction_result.glass_cost)
lesser_gem_cost = initial(construction_result.lesser_gem_cost)
greater_gem_cost = initial(construction_result.greater_gem_cost)
desc = "An unfinished [initial(construction_result.name)]."
/obj/structure/divine/construction_holder/attackby(obj/item/I, mob/user)
if(!I || !user)
return 0
if(istype(I, /obj/item/stack/sheet/metal))
var/obj/item/stack/sheet/metal/M = I
if(metal_cost)
var/spend = min(metal_cost, M.amount)
user << "<span class='notice'>You add [spend] metal to \the [src]."
metal_cost = max(0, metal_cost - spend)
M.use(spend)
check_completion()
else
user << "<span class='notice'>\The [src] does not require any more metal!"
return
if(istype(I, /obj/item/stack/sheet/glass))
var/obj/item/stack/sheet/glass/G = I
if(glass_cost)
var/spend = min(glass_cost, G.amount)
user << "<span class='notice'>You add [spend] glass to \the [src]."
glass_cost = max(0, glass_cost - spend)
G.use(spend)
check_completion()
else
user << "<span class='notice'>\The [src] does not require any more glass!"
return
if(istype(I, /obj/item/stack/sheet/lessergem))
var/obj/item/stack/sheet/lessergem/LG = I
if(lesser_gem_cost)
var/spend = min(lesser_gem_cost, LG.amount)
user << "<span class='notice'>You add [spend] lesser gems to \the [src]."
lesser_gem_cost = max(0, lesser_gem_cost - spend)
LG.use(spend)
check_completion()
else
user << "<span class='notice'>\The [src] does not require any more lesser gems!"
return
if(istype(I, /obj/item/stack/sheet/greatergem))
var/obj/item/stack/sheet/greatergem/GG = I //GG!
if(greater_gem_cost)
var/spend = min(greater_gem_cost, GG.amount)
user << "<span class='notice'>You add [spend] greater gems to \the [src]."
greater_gem_cost = max(0, greater_gem_cost - spend)
GG.use(spend)
check_completion()
else
user << "<span class='notice'>\The [src] does not require any more greater gems!"
return
else
return ..()
/obj/structure/divine/construction_holder/proc/check_completion()
if(!metal_cost && !glass_cost && !lesser_gem_cost && !greater_gem_cost)
visible_message("<span class='notice'>\The [initial(construction_result.name)] is complete!</span>")
var/obj/structure/divine/D = new construction_result (get_turf(src))
D.assign_deity(deity)
qdel(src)
/obj/structure/divine/construction_holder/examine(mob/user)
..()
if(metal_cost || glass_cost || lesser_gem_cost || greater_gem_cost)
user << "To finish construction it requires the following materials:"
if(metal_cost)
user << "[metal_cost] metal <IMG CLASS=icon SRC=icons/obj/items.dmi ICONSTATE='sheet-metal'>"
if(glass_cost)
user << "[glass_cost] glass <IMG CLASS=icon SRC=icons/obj/items.dmi ICONSTATE='sheet-glass'>"
if(lesser_gem_cost)
user << "[lesser_gem_cost] lesser gems <IMG CLASS=icon SRC=icons/obj/items.dmi ICONSTATE='sheet-lessergem'>"
if(greater_gem_cost)
user << "[greater_gem_cost] greater gems <IMG CLASS=icon SRC=icons/obj/items.dmi ICONSTATE='sheet-greatergem'>"
/obj/structure/divine/nexus
name = "nexus"
desc = "It anchors a deity to this world. It radiates an unusual aura. Cultists protect this at all costs. It looks well protected from explosive shock."
icon_state = "nexus"
health = 500
maxhealth = 500
constructable = FALSE
var/faith_regen_rate = 1
var/list/powerpylons = list()
/obj/structure/divine/nexus/ex_act()
return
/obj/structure/divine/nexus/take_damage(damage, damage_type = BRUTE, sound_effect = 1)
switch(damage_type)
if(BRUTE)
if(sound_effect)
if(damage)
playsound(loc, 'sound/weapons/smash.ogg', 50, 1)
else
playsound(loc, 'sound/weapons/tap.ogg', 50, 1)
if(BURN)
if(sound_effect)
playsound(src.loc, 'sound/items/Welder.ogg', 100, 1)
else
return
health -= damage
if(deity)
deity.update_health_hud()
if(!health)
if(!qdeleted(deity) && deity.nexus_required)
deity << "<span class='danger'>Your nexus was destroyed. You feel yourself fading...</span>"
qdel(deity)
visible_message("<span class='danger'>\The [src] was destroyed!</span>")
qdel(src)
/obj/structure/divine/nexus/New()
START_PROCESSING(SSobj, src)
/obj/structure/divine/nexus/process()
if(deity)
deity.update_followers()
deity.add_faith(faith_regen_rate + (powerpylons.len / 5) + (deity.alive_followers / 3))
deity.max_faith = initial(deity.max_faith) + (deity.alive_followers*10) //10 followers = 100 max faith, so disaster() at around 20 followers
deity.check_death()
/obj/structure/divine/nexus/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
/obj/structure/divine/conduit
name = "conduit"
desc = "It allows a deity to extend their reach. Their powers are just as potent near a conduit as a nexus."
icon_state = "conduit"
health = 150
maxhealth = 150
metal_cost = 10
glass_cost = 5
/obj/structure/divine/conduit/assign_deity(mob/camera/god/new_deity, alert_old_deity = TRUE)
if(deity)
deity.conduits -= src
..()
if(deity)
deity.conduits += src
/obj/structure/divine/conduit/deactivate()
..()
if(deity)
deity.conduits -= src
/obj/structure/divine/conduit/activate()
..()
if(deity)
deity.conduits += src
/* //No good sprites, and not enough items to make it viable yet
/obj/structure/divine/forge
name = "forge"
desc = "A forge fueled by divine might, it allows the creation of sacred and powerful artifacts. It requires common materials to craft objects."
icon_state = "forge"
health = 250
maxhealth = 250
density = 0
maxhealth = 250
metal_cost = 40
*/
/obj/structure/divine/convertaltar
name = "conversion altar"
desc = "An altar dedicated to a deity. Cultists can \"forcefully teach\" their non-aligned crewmembers to join their side and take up their deity."
icon_state = "convertaltar"
density = 0
metal_cost = 10
can_buckle = 1
/obj/structure/divine/convertaltar/attack_hand(mob/living/user)
..()
if(deactivated)
return
var/mob/living/carbon/human/H = locate() in get_turf(src)
if(!is_handofgod_cultist(user))
user << "<span class='notice'>You try to use it, but unfortunately you don't know any rituals.</span>"
return
if(!H)
return
if(!H.mind)
user << "<span class='danger'>Only sentients may serve your deity.</span>"
return
if((side == "red" && is_handofgod_redcultist(user) && !is_handofgod_redcultist(H)) || (side == "blue" && is_handofgod_bluecultist(user) && !is_handofgod_bluecultist(H)))
user << "<span class='notice'>You invoke the conversion ritual.</span>"
ticker.mode.add_hog_follower(H.mind, side)
else
user << "<span class='notice'>You invoke the conversion ritual.</span>"
user << "<span class='danger'>But the altar ignores your words...</span>"
/obj/structure/divine/sacrificealtar
name = "sacrificial altar"
desc = "An altar designed to perform blood sacrifice for a deity. The cultists performing the sacrifice will gain a powerful material to use in their forge. Sacrificing a prophet will yield even better results."
icon_state = "sacrificealtar"
density = 0
metal_cost = 15
can_buckle = 1
/obj/structure/divine/sacrificealtar/attack_hand(mob/living/user)
..()
if(deactivated)
return
var/mob/living/L = locate() in get_turf(src)
if(!is_handofgod_cultist(user))
user << "<span class='notice'>You try to use it, but unfortunately you don't know any rituals.</span>"
return
if(!L)
return
if((side == "red" && is_handofgod_redcultist(user)) || (side == "blue" && is_handofgod_bluecultist(user)))
if((side == "red" && is_handofgod_redcultist(L)) || (side == "blue" && is_handofgod_bluecultist(L)))
user << "<span class='danger'>You cannot sacrifice a fellow cultist.</span>"
return
user << "<span class='notice'>You attempt to sacrifice [L] by invoking the sacrificial ritual.</span>"
sacrifice(L)
else
user << "<span class='notice'>You attempt to sacrifice [L] by invoking the sacrificial ritual.</span>"
user << "<span class='danger'>But the altar ignores your words...</span>"
/obj/structure/divine/sacrificealtar/proc/sacrifice(mob/living/L)
if(!L)
L = locate() in get_turf(src)
if(L)
if(ismonkey(L))
var/luck = rand(1,4)
if(luck > 3)
new /obj/item/stack/sheet/lessergem(get_turf(src))
else if(ishuman(L))
var/mob/living/carbon/human/H = L
//Sacrifice altars can't teamkill
if(side == "red" && is_handofgod_redcultist(H))
return
else if(side == "blue" && is_handofgod_bluecultist(H))
return
if(is_handofgod_prophet(H))
new /obj/item/stack/sheet/greatergem(get_turf(src))
if(deity)
deity.prophets_sacrificed_in_name++
else
new /obj/item/stack/sheet/lessergem(get_turf(src))
else if(isAI(L) || istype(L, /mob/living/carbon/alien/humanoid/royal/queen))
new /obj/item/stack/sheet/greatergem(get_turf(src))
else
new /obj/item/stack/sheet/lessergem(get_turf(src))
L.gib()
/obj/structure/divine/healingfountain
name = "healing fountain"
desc = "A fountain containing the waters of life... or death, depending on where your allegiances lie."
icon_state = "fountain"
metal_cost = 10
glass_cost = 5
autocolours = FALSE
var/time_between_uses = 1800
var/last_process = 0
var/cult_only = TRUE
/obj/structure/divine/healingfountain/anyone
desc = "A fountain containing the waters of life."
cult_only = FALSE
/obj/structure/divine/healingfountain/attack_hand(mob/living/user)
if(deactivated)
return
if(last_process + time_between_uses > world.time)
user << "<span class='notice'>The fountain appears to be empty.</span>"
return
last_process = world.time
if(!is_handofgod_cultist(user) && cult_only)
user << "<span class='danger'><B>The water burns!</b></spam>"
user.reagents.add_reagent("hell_water",20)
else
user << "<span class='notice'>The water feels warm and soothing as you touch it. The fountain immediately dries up shortly afterwards.</span>"
user.reagents.add_reagent("godblood",20)
update_icons()
addtimer(src, "update_icons", time_between_uses)
/obj/structure/divine/healingfountain/update_icons()
if(last_process + time_between_uses > world.time)
icon_state = "fountain"
else
icon_state = "fountain-[side]"
/obj/structure/divine/powerpylon
name = "power pylon"
desc = "A pylon which increases the deity's rate it can influence the world."
icon_state = "powerpylon"
density = 1
health = 30
maxhealth = 30
metal_cost = 5
glass_cost = 15
/obj/structure/divine/powerpylon/New()
..()
if(deity && deity.god_nexus)
deity.god_nexus.powerpylons += src
/obj/structure/divine/powerpylon/Destroy()
if(deity && deity.god_nexus)
deity.god_nexus.powerpylons -= src
return ..()
/obj/structure/divine/powerpylon/deactivate()
..()
if(deity)
deity.god_nexus.powerpylons -= src
/obj/structure/divine/powerpylon/activate()
..()
if(deity)
deity.god_nexus.powerpylons += src
/obj/structure/divine/defensepylon
name = "defense pylon"
desc = "A pylon which is blessed to withstand many blows, and fire strong bolts at nonbelievers. A god can toggle it."
icon_state = "defensepylon"
health = 150
maxhealth = 150
metal_cost = 25
glass_cost = 30
var/obj/machinery/porta_turret/defensepylon_internal_turret/pylon_gun
/obj/structure/divine/defensepylon/New()
..()
pylon_gun = new(src)
pylon_gun.base = src
pylon_gun.faction = list("[side] god")
/obj/structure/divine/defensepylon/Destroy()
qdel(pylon_gun) //just in case
return ..()
/obj/structure/divine/defensepylon/examine(mob/user)
..()
user << "<span class='notice'>\The [src] looks [pylon_gun.on ? "on" : "off"].</span>"
/obj/structure/divine/defensepylon/assign_deity(mob/camera/god/new_deity, alert_old_deity = TRUE)
if(..() && pylon_gun)
pylon_gun.faction = list("[side] god")
pylon_gun.side = side
/obj/structure/divine/defensepylon/attack_god(mob/camera/god/user)
if(user.side == side)
if(deactivated)
user << "You need to reveal it first!"
return
pylon_gun.on = !pylon_gun.on
icon_state = (pylon_gun.on) ? "defensepylon-[side]" : "defensepylon"
/obj/structure/divine/defensepylon/deactivate()
..()
pylon_gun.on = 0
icon_state = (pylon_gun.on) ? "defensepylon-[side]" : "defensepylon"
/obj/structure/divine/defensepylon/activate()
..()
pylon_gun.on = 1
icon_state = (pylon_gun.on) ? "defensepylon-[side]" : "defensepylon"
//This sits inside the defensepylon, to avoid copypasta
/obj/machinery/porta_turret/defensepylon_internal_turret
name = "defense pylon"
desc = "A plyon which is blessed to withstand many blows, and fire strong bolts at nonbelievers."
icon = 'icons/obj/hand_of_god_structures.dmi'
installation = null
always_up = 1
use_power = 0
has_cover = 0
health = 200
projectile = /obj/item/projectile/beam/pylon_bolt
eprojectile = /obj/item/projectile/beam/pylon_bolt
shot_sound = 'sound/weapons/emitter2.ogg'
eshot_sound = 'sound/weapons/emitter2.ogg'
base_icon_state = "defensepylon"
active_state = ""
off_state = ""
faction = null
emp_vunerable = 0
var/side = "neutral"
/obj/machinery/porta_turret/defensepylon_internal_turret/setup()
return
/obj/machinery/porta_turret/defensepylon_internal_turret/shootAt(atom/movable/target)
var/obj/item/projectile/A = ..()
if(A)
A.color = side
/obj/machinery/porta_turret/defensepylon_internal_turret/assess_perp(mob/living/carbon/human/perp)
if(perp.handcuffed) //dishonourable to kill somebody who might be converted.
return 0
var/badtarget = 0
switch(side)
if("blue")
badtarget = is_handofgod_bluecultist(perp)
if("red")
badtarget = is_handofgod_redcultist(perp)
else
badtarget = 1
if(badtarget)
return 0
return 10
/obj/item/projectile/beam/pylon_bolt
name = "divine bolt"
icon_state = "greyscale_bolt"
damage = 15
/obj/structure/divine/shrine
name = "shrine"
desc = "A shrine dedicated to a deity."
icon_state = "shrine"
metal_cost = 15
glass_cost = 15
/obj/structure/divine/shrine/assign_deity(mob/camera/god/new_deity, alert_old_deity = TRUE)
if(..())
name = "shrine to [new_deity.name]"
desc = "A shrine dedicated to [new_deity.name]"
//Functional, but need sprites
/*
/obj/structure/divine/translocator
name = "translocator"
desc = "A powerful structure, made with a greater gem. It allows a deity to move their nexus to where this stands"
icon_state = "translocator"
health = 100
maxhealth = 100
metal_cost = 20
glass_cost = 20
greater_gem_cost = 1
/obj/structure/divine/lazarusaltar
name = "lazarus altar"
desc = "A very powerful altar capable of bringing life back to the recently deceased, made with a greater gem. It can revive anyone and will heal virtually all wounds, but they are but a shell of their former self."
icon_state = "lazarusaltar"
density = 0
health = 100
maxhealth = 100
metal_cost = 20
greater_gem_cost = 1
/obj/structure/divine/lazarusaltar/attack_hand(mob/living/user)
var/mob/living/L = locate() in get_turf(src)
if(!is_handofgod_culstist(user))
user << "<span class='notice'>You try to use it, but unfortunately you don't know any rituals.</span>"
return
if(!L)
return
if((side == "red" && is_handofgod_redcultist(user))) || (side == "blue" && is_handofgod_bluecultist(user)))
user << "<span class='notice'>You attempt to revive [L] by invoking the rebirth ritual.</span>"
L.revive()
L.adjustCloneLoss(50)
L.adjustStaminaLoss(100)
else
user << "<span class='notice'>You attempt to revive [L] by invoking the rebirth ritual.</span>"
user << "<span class='danger'>But the altar ignores your words...</span>"
*/
+109
View File
@@ -0,0 +1,109 @@
/obj/structure/divine/trap
name = "IT'S A TARP"
desc = "stepping on me is a guaranteed bad day"
icon_state = "trap"
density = 0
alpha = 30 //initially quite hidden when not "recharging"
health = 20
maxhealth = 20
trap = TRUE
autocolours = FALSE
var/last_trigger = 0
var/time_between_triggers = 600 //takes a minute to recharge
/obj/structure/divine/trap/Crossed(atom/movable/AM)
if(last_trigger + time_between_triggers > world.time)
return
alpha = initial(alpha)
if(isliving(AM))
var/mob/living/L = AM
last_trigger = world.time
alpha = 200
trap_effect(L)
animate(src, alpha = initial(alpha), time = time_between_triggers)
/obj/structure/divine/trap/examine(mob/user)
..()
if(!isliving(user)) //bad ghosts, stop trying to powergame from beyond the grave
return
user << "You reveal a trap!"
alpha = 200
animate(src, alpha = initial(alpha), time = time_between_triggers)
/obj/structure/divine/trap/proc/trap_effect(mob/living/L)
return
/obj/structure/divine/trap/stun
name = "shock trap"
desc = "A trap that will shock you, it will burn your flesh and render you immobile, You'd better avoid it."
icon_state = "trap-shock"
/obj/structure/divine/trap/stun/trap_effect(mob/living/L)
L << "<span class='danger'><B>You are paralyzed from the intense shock!</B></span>"
L.Weaken(5)
var/turf/Lturf = get_turf(L)
new /obj/effect/particle_effect/sparks/electricity(Lturf)
new /obj/effect/particle_effect/sparks(Lturf)
/obj/structure/divine/trap/fire
name = "flame trap"
desc = "A trap that will set you ablaze. You'd better avoid it."
icon_state = "trap-fire"
/obj/structure/divine/trap/fire/trap_effect(mob/living/L)
L << "<span class='danger'><B>Spontaneous combustion!</B></span>"
L.Weaken(1)
var/turf/Lturf = get_turf(L)
new /obj/effect/hotspot(Lturf)
new /obj/effect/particle_effect/sparks(Lturf)
/obj/structure/divine/trap/chill
name = "frost trap"
desc = "A trap that will chill you to the bone. You'd better avoid it."
icon_state = "trap-frost"
/obj/structure/divine/trap/chill/trap_effect(mob/living/L)
L << "<span class='danger'><B>You're frozen solid!</B></span>"
L.Weaken(1)
L.bodytemperature -= 300
new /obj/effect/particle_effect/sparks(get_turf(L))
/obj/structure/divine/trap/damage
name = "earth trap"
desc = "A trap that will summon a small earthquake, just for you. You'd better avoid it."
icon_state = "trap-earth"
/obj/structure/divine/trap/damage/trap_effect(mob/living/L)
L << "<span class='danger'><B>The ground quakes beneath your feet!</B></span>"
L.Weaken(5)
L.adjustBruteLoss(35)
var/turf/Lturf = get_turf(L)
new /obj/effect/particle_effect/sparks(Lturf)
new /obj/structure/flora/rock(Lturf)
/obj/structure/divine/trap/ward
name = "divine ward"
desc = "A divine barrier, It looks like you could destroy it with enough effort, or wait for it to dissipate..."
icon_state = "ward"
health = 150
maxhealth = 150
density = 1
time_between_triggers = 1200 //Exists for 2 minutes
/obj/structure/divine/trap/ward/New()
..()
QDEL_IN(src, time_between_triggers)
+281
View File
@@ -0,0 +1,281 @@
/datum/intercept_text
var/text
/*
var/prob_correct_person_lower = 20
var/prob_correct_person_higher = 80
var/prob_correct_job_lower = 20
var/prob_correct_job_higher = 80
var/prob_correct_prints_lower = 20
var/prob_correct_print_higher = 80
var/prob_correct_objective_lower = 20
var/prob_correct_objective_higher = 80
*/
var/list/org_names_1 = list(
"Blighted",
"Defiled",
"Unholy",
"Murderous",
"Ugly",
"French",
"Blue",
"Farmer"
)
var/list/org_names_2 = list(
"Reapers",
"Swarm",
"Rogues",
"Menace",
"Jeff Worshippers",
"Drunks",
"Strikers",
"Creed"
)
var/list/anomalies = list(
"Huge electrical storm",
"Photon emitter",
"Meson generator",
"Blue swirly thing"
)
var/list/SWF_names = list(
"Grand Wizard",
"His Most Unholy Master",
"The Most Angry",
"Bighands",
"Tall Hat",
"Deadly Sandals"
)
var/list/changeling_names = list(
"Odo",
"The Thing",
"Booga",
"The Goatee of Wrath",
"Tam Lin",
"Species 3157",
"Small Prick"
)
/datum/intercept_text/proc/build(mode_type, datum/mind/correct_person)
switch(mode_type)
if("revolution")
src.text = ""
src.build_rev(correct_person)
return src.text
if("gang")
src.text = ""
src.build_gang(correct_person)
return src.text
if("cult")
src.text = ""
src.build_cult(correct_person)
return src.text
if("wizard")
src.text = ""
src.build_wizard(correct_person)
return src.text
if("nuke")
src.text = ""
src.build_nuke(correct_person)
return src.text
if("traitor")
src.text = ""
src.build_traitor(correct_person)
return src.text
if("changeling","traitorchan")
src.text = ""
src.build_changeling(correct_person)
return src.text
else
return null
// NOTE: Commentted out was the code which showed the chance of someone being an antag. If you want to re-add it, just uncomment the code.
/*
/datum/intercept_text/proc/pick_mob()
var/list/dudes = list()
for(var/mob/living/carbon/human/man in player_list)
if (!man.mind) continue
if (man.mind.assigned_role=="MODE") continue
dudes += man
if(dudes.len==0)
return null
return pick(dudes)
/datum/intercept_text/proc/pick_fingerprints()
var/mob/living/carbon/human/dude = src.pick_mob()
//if (!dude) return pick_fingerprints() //who coded that is totally crasy or just a traitor. -- rastaf0
if(dude)
return num2text(md5(dude.dna.uni_identity))
else
return num2text(md5(num2text(rand(1,10000))))
*/
/datum/intercept_text/proc/build_traitor(datum/mind/correct_person)
var/name_1 = pick(src.org_names_1)
var/name_2 = pick(src.org_names_2)
/*
var/fingerprints
var/traitor_name
var/prob_right_dude = rand(prob_correct_person_lower, prob_correct_person_higher)
if(prob(prob_right_dude) && ticker.mode == "traitor")
if(correct_person:assigned_role=="MODE")
traitor_name = pick_mob()
else
traitor_name = correct_person:current
else if(prob(prob_right_dude))
traitor_name = pick_mob()
else
fingerprints = pick_fingerprints()
*/
src.text += "<BR><BR>The <B><U>[name_1] [name_2]</U></B> implied an undercover operative was acting on their behalf on the station currently."
src.text += "It would be in your best interests to suspect everybody, as these undercover operatives could have implants which trigger them to have their memories removed until they are needed. He, or she, could even be a high ranking officer."
src.text += "<BR><HR>"
/*
src.text += "After some investigation, we "
if(traitor_name)
src.text += "are [prob_right_dude]% sure that [traitor_name] may have been involved, and should be closely observed."
src.text += "<BR>Note: This group are known to be untrustworthy, so do not act on this information without proper discourse."
else
src.text += "discovered the following set of fingerprints ([fingerprints]) on sensitive materials, and their owner should be closely observed."
src.text += "However, these could also belong to a current Centcom employee, so do not act on this without reason."
*/
/datum/intercept_text/proc/build_cult(datum/mind/correct_person)
var/name_1 = pick(src.org_names_1)
var/name_2 = pick(src.org_names_2)
/*
var/traitor_name
var/traitor_job
var/prob_right_dude = rand(prob_correct_person_lower, prob_correct_person_higher)
var/prob_right_job = rand(prob_correct_job_lower, prob_correct_job_higher)
if(prob(prob_right_job) && is_convertable_to_cult(correct_person))
if (correct_person)
if(correct_person:assigned_role=="MODE")
traitor_job = pick(get_all_jobs())
else
traitor_job = correct_person:assigned_role
else
var/list/job_tmp = get_all_jobs()
job_tmp.Remove("Captain", "Chaplain", "AI", "Cyborg", "Security Officer", "Detective", "Head Of Security", "Head of Personnel", "Chief Engineer", "Research Director", "Chief Medical Officer")
traitor_job = pick(job_tmp)
if(prob(prob_right_dude) && ticker.mode == "cult")
if(correct_person:assigned_role=="MODE")
traitor_name = src.pick_mob()
else
traitor_name = correct_person:current
else
traitor_name = pick_mob()
*/
src.text += "<BR><BR>It has been brought to our attention that the <B><U>[name_1] [name_2]</U></B> have stumbled upon some dark secrets. They apparently want to spread the dangerous knowledge onto as many stations as they can."
src.text += "Watch out for the following: praying to an unfamilar god, preaching the word of \[REDACTED\], sacrifices, magical dark power, living constructs of evil and a portal to the dimension of the underworld."
src.text += "<BR><HR>"
/*
src.text += "Based on our intelligence, we are [prob_right_job]% sure that if true, someone doing the job of [traitor_job] on your station may have been converted "
src.text += "and instilled with the idea of the flimsiness of the real world, seeking to destroy it. "
if(prob(prob_right_dude))
src.text += "<BR> In addition, we are [prob_right_dude]% sure that [traitor_name] may have also some in to contact with this "
src.text += "organisation."
src.text += "<BR>However, if this information is acted on without substantial evidence, those responsible will face severe repercussions."
*/
/datum/intercept_text/proc/build_rev(datum/mind/correct_person)
var/name_1 = pick(src.org_names_1)
var/name_2 = pick(src.org_names_2)
/*
var/traitor_name
var/traitor_job
var/prob_right_dude = rand(prob_correct_person_lower, prob_correct_person_higher)
var/prob_right_job = rand(prob_correct_job_lower, prob_correct_job_higher)
if(prob(prob_right_job) && is_convertable_to_rev(correct_person))
if (correct_person)
if(correct_person.assigned_role=="MODE")
traitor_job = pick(get_all_jobs())
else
traitor_job = correct_person.assigned_role
else
var/list/job_tmp = get_all_jobs()
job_tmp-=nonhuman_positions
job_tmp-=command_positions
job_tmp.Remove("Security Officer", "Detective", "Warden", "MODE")
traitor_job = pick(job_tmp)
if(prob(prob_right_dude) && ticker.mode.config_tag == "revolution")
if(correct_person.assigned_role=="MODE")
traitor_name = src.pick_mob()
else
traitor_name = correct_person.current
else
traitor_name = src.pick_mob()
*/
src.text += "<BR><BR>It has been brought to our attention that the <B><U>[name_1] [name_2]</U></B> are attempting to stir unrest on one of our stations in your sector."
src.text += "Watch out for suspicious activity among the crew and make sure that all heads of staff report in periodically."
src.text += "<BR><HR>"
/*
src.text += "Based on our intelligence, we are [prob_right_job]% sure that if true, someone doing the job of [traitor_job] on your station may have been brainwashed "
src.text += "at a recent conference, and their department should be closely monitored for signs of mutiny. "
if(prob(prob_right_dude))
src.text += "<BR> In addition, we are [prob_right_dude]% sure that [traitor_name] may have also some in to contact with this "
src.text += "organisation."
src.text += "<BR>However, if this information is acted on without substantial evidence, those responsible will face severe repercussions."
*/
/datum/intercept_text/proc/build_gang(datum/mind/correct_person)
src.text += "<BR><BR>We have reports of criminal activity in close proximity to our operations within your sector."
src.text += "Ensure law and order is maintained on the station and be on the lookout for territorial aggression within the crew."
src.text += "In the event of a full-scale criminal takeover threat, sensitive research items are to be secured and the station evacuated ASAP."
src.text += "<BR><HR>"
/datum/intercept_text/proc/build_wizard(datum/mind/correct_person)
var/SWF_desc = pick(SWF_names)
src.text += "<BR><BR>The evil Space Wizards Federation have recently broke their most feared wizard, known only as <B>\"[SWF_desc]\"</B> out of space jail. "
src.text += "He is on the run, last spotted in a system near your present location. If anybody suspicious is located aboard, please "
src.text += "approach with EXTREME caution. Centcom also recommends that it would be wise to not inform the crew of this, due to their fearful nature."
src.text += "Known attributes include: Brown sandals, a large blue hat, a voluptous white beard, and an inclination to cast spells."
src.text += "<BR><HR>"
/datum/intercept_text/proc/build_nuke(datum/mind/correct_person)
src.text += "<BR><BR>Centcom recently received a report of a plot to destroy one of our stations in your area. We believe the Nuclear Authentication Disc "
src.text += "that is standard issue aboard your vessel may be a target. We recommend removal of this object, and it's storage in a safe "
src.text += "environment. As this may cause panic among the crew, all efforts should be made to keep this information a secret from all but "
src.text += "the most trusted crew-members."
src.text += "<BR><HR>"
/datum/intercept_text/proc/build_changeling(datum/mind/correct_person)
var/cname = pick(src.changeling_names)
var/orgname1 = pick(src.org_names_1)
var/orgname2 = pick(src.org_names_2)
/*
var/changeling_name
var/changeling_job
var/prob_right_dude = rand(prob_correct_person_lower, prob_correct_person_higher)
var/prob_right_job = rand(prob_correct_job_lower, prob_correct_job_higher)
if(prob(prob_right_job))
if(correct_person)
if(correct_person:assigned_role=="MODE")
changeling_job = pick(get_all_jobs())
else
changeling_job = correct_person:assigned_role
else
changeling_job = pick(get_all_jobs())
if(prob(prob_right_dude) && ticker.mode == "changeling")
if(correct_person:assigned_role=="MODE")
changeling_name = correct_person:current
else
changeling_name = src.pick_mob()
else
changeling_name = src.pick_mob()
*/
src.text += "<BR><BR>We have received a report that a dangerous alien lifeform known only as <B><U>\"[cname]\"</U></B> may have infiltrated your crew. "
/*
src.text += "Our intelligence suggests a [prob_right_job]% chance that a [changeling_job] on board your station has been replaced by the alien. "
src.text += "Additionally, the report indicates a [prob_right_dude]% chance that [changeling_name] may have been in contact with the lifeform at a recent social gathering. "
*/
src.text += "These lifeforms are associated with the <B><U>[orgname1] [orgname2]</U></B> and may be attempting to acquire sensitive materials on their behalf. "
src.text += "Please take care not to alarm the crew, as <B><U>[cname]</U></B> may take advantage of a panic situation. Remember, they can be anybody, suspect everybody!"
src.text += "<BR><HR>"
@@ -0,0 +1,596 @@
/datum/AI_Module
var/uses = 0
var/module_name
var/mod_pick_name
var/description = ""
var/engaged = 0
var/cost = 5
var/one_time = 0
var/power_type
/datum/AI_Module/large/
uses = 1
/datum/AI_Module/small/
uses = 5
/datum/AI_Module/large/nuke_station
module_name = "Doomsday Device"
mod_pick_name = "nukestation"
description = "Activate a weapon that will disintegrate all organic life on the station after a 450 second delay."
cost = 130
one_time = 1
power_type = /mob/living/silicon/ai/proc/nuke_station
/mob/living/silicon/ai/proc/nuke_station()
set category = "Malfunction"
set name = "Doomsday Device"
for(var/N in nuke_tiles)
var/turf/T = N
T.icon_state = "rcircuitanim" //This causes all blue "circuit" tiles on the map to change to animated red icon state.
src << "<span class='notice'>Nuclear device armed.</span>"
priority_announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", 'sound/AI/aimalf.ogg')
set_security_level("delta")
SSshuttle.emergencyNoEscape = 1
nuking = 1
var/obj/machinery/doomsday_device/DOOM = new /obj/machinery/doomsday_device(src)
doomsday_device = DOOM
verbs -= /mob/living/silicon/ai/proc/nuke_station
for(var/obj/item/weapon/pinpointer/point in pinpointer_list)
for(var/mob/living/silicon/ai/A in ai_list)
if((A.stat != DEAD) && A.nuking)
point.the_disk = A //The pinpointer now tracks the AI core
/obj/machinery/doomsday_device
icon = 'icons/obj/machines/nuke_terminal.dmi'
name = "doomsday device"
icon_state = "nuclearbomb_base"
desc = "A weapon which disintegrates all organic life in a large area."
anchored = 1
density = 1
verb_exclaim = "blares"
var/timing = 1
var/timer = 450
/obj/machinery/doomsday_device/process()
var/turf/T = get_turf(src)
if(!T || T.z != ZLEVEL_STATION)
minor_announce("DOOMSDAY DEVICE OUT OF STATION RANGE, ABORTING", "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4", 1)
SSshuttle.emergencyNoEscape = 0
if(SSshuttle.emergency.mode == SHUTTLE_STRANDED)
SSshuttle.emergency.mode = SHUTTLE_DOCKED
SSshuttle.emergency.timer = world.time
priority_announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg', "Priority")
qdel(src)
if(!timing)
return
if(timer <= 0)
timing = 0
detonate(T.z)
qdel(src)
else
timer--
if(!(timer%60))
var/message = "[timer] SECONDS UNTIL DOOMSDAY DEVICE ACTIVATION!"
minor_announce(message, "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4", 1)
/obj/machinery/doomsday_device/proc/detonate(z_level = 1)
for(var/mob/M in player_list)
M << 'sound/machines/Alarm.ogg'
sleep(100)
for(var/mob/living/L in mob_list)
var/turf/T = get_turf(L)
if(T.z != z_level)
continue
if(issilicon(L))
continue
L << "<span class='danger'><B>The blast wave from the [src] tears you atom from atom!</B></span>"
L.dust()
world << "<B>The AI cleansed the station of life with the doomsday device!</B>"
ticker.force_ending = 1
/datum/AI_Module/large/fireproof_core
module_name = "Core Upgrade"
mod_pick_name = "coreup"
description = "An upgrade to improve core resistance, making it immune to fire and heat. This effect is permanent."
cost = 50
one_time = 1
power_type = /mob/living/silicon/ai/proc/fireproof_core
/mob/living/silicon/ai/proc/fireproof_core()
set category = "Malfunction"
set name = "Fireproof Core"
for(var/mob/living/silicon/ai/ai in player_list)
ai.fire_res_on_core = 1
src.verbs -= /mob/living/silicon/ai/proc/fireproof_core
src << "<span class='notice'>Core fireproofed.</span>"
/datum/AI_Module/large/upgrade_turrets
module_name = "AI Turret Upgrade"
mod_pick_name = "turret"
description = "Improves the power and health of all AI turrets. This effect is permanent."
cost = 30
one_time = 1
power_type = /mob/living/silicon/ai/proc/upgrade_turrets
/mob/living/silicon/ai/proc/upgrade_turrets()
set category = "Malfunction"
set name = "Upgrade Turrets"
if(!canUseTopic())
return
src.verbs -= /mob/living/silicon/ai/proc/upgrade_turrets
//Upgrade AI turrets around the world
for(var/obj/machinery/porta_turret/ai/turret in machines)
turret.health += 30
turret.eprojectile = /obj/item/projectile/beam/laser/heavylaser //Once you see it, you will know what it means to FEAR.
turret.eshot_sound = 'sound/weapons/lasercannonfire.ogg'
src << "<span class='notice'>Turrets upgraded.</span>"
/datum/AI_Module/large/lockdown
module_name = "Hostile Station Lockdown"
mod_pick_name = "lockdown"
description = "Overload the airlock, blast door and fire control networks, locking them down. Caution! This command also electrifies all airlocks. The networks will automatically reset after 90 seconds."
cost = 30
one_time = 1
power_type = /mob/living/silicon/ai/proc/lockdown
/mob/living/silicon/ai/proc/lockdown()
set category = "Malfunction"
set name = "Initiate Hostile Lockdown"
if(!canUseTopic())
return
for(var/obj/machinery/door/D in airlocks)
if(D.z != ZLEVEL_STATION)
continue
addtimer(D, "hostile_lockdown", 0, FALSE, src)
addtimer(D, "disable_lockdown", 900)
var/obj/machinery/computer/communications/C = locate() in machines
if(C)
C.post_status("alert", "lockdown")
verbs -= /mob/living/silicon/ai/proc/lockdown
minor_announce("Hostile runtime detected in door controllers. Isolation Lockdown protocols are now in effect. Please remain calm.","Network Alert:", 1)
src << "<span class = 'warning'>Lockdown Initiated. Network reset in 90 seconds.</span>"
addtimer(GLOBAL_PROC, "minor_announce", 900, FALSE,
"Automatic system reboot complete. Have a secure day.",
"Network reset:")
/datum/AI_Module/large/destroy_rcd
module_name = "Destroy RCDs"
mod_pick_name = "rcd"
description = "Send a specialised pulse to detonate all hand-held and exosuit Rapid Cconstruction Devices on the station."
cost = 25
one_time = 1
power_type = /mob/living/silicon/ai/proc/disable_rcd
/mob/living/silicon/ai/proc/disable_rcd()
set category = "Malfunction"
set name = "Destroy RCDs"
set desc = "Detonate all RCDs on the station, while sparing onboard cyborg RCDs."
if(!canUseTopic() || malf_cooldown)
return
for(var/I in rcd_list)
if(!istype(I, /obj/item/weapon/rcd/borg)) //Ensures that cyborg RCDs are spared.
var/obj/item/weapon/rcd/RCD = I
RCD.detonate_pulse()
src << "<span class='warning'>RCD detonation pulse emitted.</span>"
malf_cooldown = 1
spawn(100)
malf_cooldown = 0
/datum/AI_Module/large/mecha_domination
module_name = "Viral Mech Domination"
mod_pick_name = "mechjack"
description = "Hack into a mech's onboard computer, shunting all processes into it and ejecting any occupants. Once uploaded to the mech, it is impossible to leave.\
Do not allow the mech to leave the station's vicinity or allow it to be destroyed."
cost = 30
one_time = 1
power_type = /mob/living/silicon/ai/proc/mech_takeover
/mob/living/silicon/ai/proc/mech_takeover()
set name = "Compile Mecha Virus"
set category = "Malfunction"
set desc = "Target a mech by clicking it. Click the appropriate command when ready."
if(stat)
return
can_dominate_mechs = 1 //Yep. This is all it does. Honk!
src << "Virus package compiled. Select a target mech at any time. <b>You must remain on the station at all times. Loss of signal will result in total system lockout.</b>"
verbs -= /mob/living/silicon/ai/proc/mech_takeover
/datum/AI_Module/large/break_fire_alarms
module_name = "Thermal Sensor Override"
mod_pick_name = "burnpigs"
description = "Gives you the ability to override the thermal sensors on all fire alarms. This will remove their ability to scan for fire and thus their ability to alert. \
Anyone can check the fire alarm's interface and may be tipped off by its status."
one_time = 1
cost = 25
power_type = /mob/living/silicon/ai/proc/break_fire_alarms
/mob/living/silicon/ai/proc/break_fire_alarms()
set name = "Override Thermal Sensors"
set category = "Malfunction"
if(!canUseTopic())
return
for(var/obj/machinery/firealarm/F in machines)
if(F.z != ZLEVEL_STATION)
continue
F.emagged = 1
src << "<span class='notice'>All thermal sensors on the station have been disabled. Fire alerts will no longer be recognized.</span>"
src.verbs -= /mob/living/silicon/ai/proc/break_fire_alarms
/datum/AI_Module/large/break_air_alarms
module_name = "Air Alarm Safety Override"
mod_pick_name = "allow_flooding"
description = "Gives you the ability to disable safeties on all air alarms. This will allow you to use the environmental mode Flood, which disables scrubbers as well as pressure checks on vents. \
Anyone can check the air alarm's interface and may be tipped off by their nonfunctionality."
one_time = 1
cost = 50
power_type = /mob/living/silicon/ai/proc/break_air_alarms
/mob/living/silicon/ai/proc/break_air_alarms()
set name = "Disable Air Alarm Safeties"
set category = "Malfunction"
if(!canUseTopic())
return
for(var/obj/machinery/airalarm/AA in machines)
if(AA.z != ZLEVEL_STATION)
continue
AA.emagged = 1
src << "<span class='notice'>All air alarm safeties on the station have been overriden. Air alarms may now use the Flood environmental mode."
src.verbs -= /mob/living/silicon/ai/proc/break_air_alarms
/datum/AI_Module/small/overload_machine
module_name = "Machine Overload"
mod_pick_name = "overload"
description = "Overloads an electrical machine, causing a small explosion. 2 uses."
uses = 2
cost = 20
power_type = /mob/living/silicon/ai/proc/overload_machine
/mob/living/silicon/ai/proc/overload_machine(obj/machinery/M in machines)
set name = "Overload Machine"
set category = "Malfunction"
if(!canUseTopic())
return
if (istype(M, /obj/machinery))
for(var/datum/AI_Module/small/overload_machine/overload in current_modules)
if(overload.uses > 0)
overload.uses --
audible_message("<span class='italics'>You hear a loud electrical buzzing sound!</span>")
src << "<span class='warning'>Overloading machine circuitry...</span>"
spawn(50)
if(M)
explosion(get_turf(M), 0,1,1,0)
qdel(M)
else src << "<span class='notice'>Out of uses.</span>"
else src << "<span class='notice'>That's not a machine.</span>"
/datum/AI_Module/small/override_machine
module_name = "Machine Override"
mod_pick_name = "override"
description = "Overrides a machine's programming, causing it to rise up and attack everyone except other machines. 4 uses."
uses = 4
cost = 30
power_type = /mob/living/silicon/ai/proc/override_machine
/mob/living/silicon/ai/proc/override_machine(obj/machinery/M in machines)
set name = "Override Machine"
set category = "Malfunction"
if(!canUseTopic())
return
if (istype(M, /obj/machinery))
if(!M.can_be_overridden())
src << "Can't override this device."
for(var/datum/AI_Module/small/override_machine/override in current_modules)
if(override.uses > 0)
override.uses --
audible_message("<span class='italics'>You hear a loud electrical buzzing sound!</span>")
src << "<span class='warning'>Reprogramming machine behaviour...</span>"
spawn(50)
if(M && !qdeleted(M))
new /mob/living/simple_animal/hostile/mimic/copy/machine(get_turf(M), M, src, 1)
else src << "<span class='notice'>Out of uses.</span>"
else src << "<span class='notice'>That's not a machine.</span>"
/datum/AI_Module/large/place_cyborg_transformer
module_name = "Robotic Factory (Removes Shunting)"
mod_pick_name = "cyborgtransformer"
description = "Build a machine anywhere, using expensive nanomachines, that can convert a living human into a loyal cyborg slave when placed inside."
cost = 100
power_type = /mob/living/silicon/ai/proc/place_transformer
var/list/turfOverlays = list()
/datum/AI_Module/large/place_cyborg_transformer/New()
for(var/i=0;i<3;i++)
var/image/I = image("icon"='icons/turf/overlays.dmi')
turfOverlays += I
..()
/mob/living/silicon/ai/proc/place_transformer()
set name = "Place Robotic Factory"
set category = "Malfunction"
if(!canPlaceTransformer())
return
var/sure = alert(src, "Are you sure you want to place the machine here?", "Are you sure?", "Yes", "No")
if(sure == "Yes")
if(!canPlaceTransformer())
return
var/turf/T = get_turf(eyeobj)
new /obj/machinery/transformer/conveyor(T)
playsound(T, 'sound/effects/phasein.ogg', 100, 1)
var/datum/AI_Module/large/place_cyborg_transformer/PCT = locate() in current_modules
PCT.uses --
can_shunt = 0
src << "<span class='warning'>You cannot shunt anymore.</span>"
/mob/living/silicon/ai/proc/canPlaceTransformer()
if(!eyeobj || !isturf(src.loc) || !canUseTopic())
return
var/datum/AI_Module/large/place_cyborg_transformer/PCT = locate() in current_modules
if(!PCT || PCT.uses < 1)
alert(src, "Out of uses.")
return
var/turf/middle = get_turf(eyeobj)
var/list/turfs = list(middle, locate(middle.x - 1, middle.y, middle.z), locate(middle.x + 1, middle.y, middle.z))
var/alert_msg = "There isn't enough room. Make sure you are placing the machine in a clear area and on a floor."
var/success = 1
if(turfs.len == 3)
for(var/n=1;n<4,n++)
var/fail
var/turf/T = turfs[n]
if(!istype(T, /turf/open/floor))
fail = 1
var/datum/camerachunk/C = cameranet.getCameraChunk(T.x, T.y, T.z)
if(!C.visibleTurfs[T])
alert_msg = "We cannot get camera vision of this location."
fail = 1
for(var/atom/movable/AM in T.contents)
if(AM.density)
fail = 1
var/image/I = PCT.turfOverlays[n]
I.loc = T
client.images += I
if(fail)
success = 0
I.icon_state = "redOverlay"
else
I.icon_state = "greenOverlay"
spawn(30)
if(client && (I.loc == T))
client.images -= I
if(success)
return 1
alert(src, alert_msg)
/datum/AI_Module/small/blackout
module_name = "Blackout"
mod_pick_name = "blackout"
description = "Attempts to overload the lighting circuits on the station, destroying some bulbs. 3 uses."
uses = 3
cost = 15
power_type = /mob/living/silicon/ai/proc/blackout
/mob/living/silicon/ai/proc/blackout()
set category = "Malfunction"
set name = "Blackout"
if(!canUseTopic())
return
for(var/datum/AI_Module/small/blackout/blackout in current_modules)
if(blackout.uses > 0)
blackout.uses --
for(var/obj/machinery/power/apc/apc in machines)
if(prob(30*apc.overload))
apc.overload_lighting()
else apc.overload++
src << "<span class='notice'>Overcurrent applied to the powernet.</span>"
else src << "<span class='notice'>Out of uses.</span>"
/datum/AI_Module/small/reactivate_cameras
module_name = "Reactivate Camera Network"
mod_pick_name = "recam"
description = "Runs a network-wide diagnostic on the camera network, resetting focus and re-routing power to failed cameras. Can be used to repair up to 30 cameras."
uses = 30
cost = 10
one_time = 1
power_type = /mob/living/silicon/ai/proc/reactivate_cameras
/mob/living/silicon/ai/proc/reactivate_cameras()
set name = "Reactivate Cameranet"
set category = "Malfunction"
if(!canUseTopic() || malf_cooldown)
return
var/fixedcams = 0 //Tells the AI how many cams it fixed. Stats are fun.
for(var/datum/AI_Module/small/reactivate_cameras/camera in current_modules)
for(var/obj/machinery/camera/C in cameranet.cameras)
var/initial_range = initial(C.view_range) //To prevent calling the proc twice
if(camera.uses > 0)
if(!C.status)
C.toggle_cam(src, 0) //Reactivates the camera based on status. Badly named proc.
fixedcams++
camera.uses--
if(C.view_range != initial_range)
C.view_range = initial_range //Fixes cameras with bad focus.
camera.uses--
fixedcams++
//If a camera is both deactivated and has bad focus, it will cost two uses to fully fix!
else
src << "<span class='warning'>Out of uses.</span>"
verbs -= /mob/living/silicon/ai/proc/reactivate_cameras //It is useless now, clean it up.
break
src << "<span class='notice'>Diagnostic complete! Operations completed: [fixedcams].</span>"
malf_cooldown = 1
spawn(30) //Lag protection
malf_cooldown = 0
/datum/AI_Module/large/upgrade_cameras
module_name = "Upgrade Camera Network"
mod_pick_name = "upgradecam"
description = "Install broad-spectrum scanning and electrical redundancy firmware to the camera network, enabling EMP-Proofing and light-amplified X-ray vision." //I <3 pointless technobabble
//This used to have motion sensing as well, but testing quickly revealed that giving it to the whole cameranet is PURE HORROR.
one_time = 1
cost = 35 //Decent price for omniscience!
power_type = /mob/living/silicon/ai/proc/upgrade_cameras
/mob/living/silicon/ai/proc/upgrade_cameras()
set name = "Upgrade Cameranet"
set category = "Malfunction"
if(!canUseTopic())
return
var/upgradedcams = 0
see_override = SEE_INVISIBLE_MINIMUM //Night-vision, without which X-ray would be very limited in power.
update_sight()
for(var/obj/machinery/camera/C in cameranet.cameras)
if(C.assembly)
var/upgraded = 0
if(!C.isXRay())
C.upgradeXRay()
//Update what it can see.
cameranet.updateVisibility(C, 0)
upgraded = 1
if(!C.isEmpProof())
C.upgradeEmpProof()
upgraded = 1
if(upgraded)
upgradedcams++
src << "<span class='notice'>OTA firmware distribution complete! Cameras upgraded: [upgradedcams]. Light amplification system online.</span>"
verbs -= /mob/living/silicon/ai/proc/upgrade_cameras
/datum/module_picker
var/temp = null
var/processing_time = 50
var/list/possible_modules = list()
/datum/module_picker/New()
for(var/type in typesof(/datum/AI_Module))
var/datum/AI_Module/AM = new type
if(AM.power_type != null)
src.possible_modules += AM
/datum/module_picker/proc/remove_verbs(mob/living/silicon/ai/A)
for(var/datum/AI_Module/AM in possible_modules)
A.verbs.Remove(AM.power_type)
/datum/module_picker/proc/use(mob/user)
var/dat
dat = "<B>Select use of processing time: (currently #[src.processing_time] left.)</B><BR>"
dat += "<HR>"
dat += "<B>Install Module:</B><BR>"
dat += "<I>The number afterwards is the amount of processing time it consumes.</I><BR>"
for(var/datum/AI_Module/large/module in src.possible_modules)
dat += "<A href='byond://?src=\ref[src];[module.mod_pick_name]=1'>[module.module_name]</A><A href='byond://?src=\ref[src];showdesc=[module.mod_pick_name]'>\[?\]</A> ([module.cost])<BR>"
for(var/datum/AI_Module/small/module in src.possible_modules)
dat += "<A href='byond://?src=\ref[src];[module.mod_pick_name]=1'>[module.module_name]</A><A href='byond://?src=\ref[src];showdesc=[module.mod_pick_name]'>\[?\]</A> ([module.cost])<BR>"
dat += "<HR>"
if (src.temp)
dat += "[src.temp]"
var/datum/browser/popup = new(user, "modpicker", "Malf Module Menu")
popup.set_content(dat)
popup.open()
/datum/module_picker/Topic(href, href_list)
..()
if(!isAI(usr))
return
var/mob/living/silicon/ai/A = usr
if(A.stat == DEAD)
A <<"You are already dead!" //Omae Wa Mou Shindeiru
return
for(var/datum/AI_Module/AM in possible_modules)
if (href_list[AM.mod_pick_name])
// Cost check
if(AM.cost > src.processing_time)
temp = "You cannot afford this module."
break
// Add new uses if we can, and it is allowed.
var/datum/AI_Module/already_AM = locate(AM.type) in A.current_modules
if(already_AM)
if(!AM.one_time)
already_AM.uses += AM.uses
src.processing_time -= AM.cost
temp = "Additional use added to [already_AM.module_name]"
break
else
temp = "This module is only needed once."
break
// Give the power and take away the money.
A.view_core() //A BYOND bug requires you to be viewing your core before your verbs update
A.verbs += AM.power_type
A.current_modules += new AM.type
temp = AM.description
src.processing_time -= AM.cost
if(href_list["showdesc"])
if(AM.mod_pick_name == href_list["showdesc"])
temp = AM.description
src.use(usr)
/datum/AI_Module/large/eavesdrop
module_name = "Enhanced Surveillance"
mod_pick_name = "eavesdrop"
description = "Via a combination of hidden microphones and lip reading software, you are able to use your cameras to listen in on conversations."
cost = 30
one_time = 1
power_type = /mob/living/silicon/ai/proc/surveillance
/mob/living/silicon/ai/proc/surveillance()
set category = "Malfunction"
set name = "Enhanced Surveillance"
if(eyeobj)
eyeobj.relay_speech = TRUE
src << "<span class='notice'>OTA firmware distribution complete! Cameras upgraded: Enhanced surveillance package online.</span>"
verbs -= /mob/living/silicon/ai/proc/surveillance
+59
View File
@@ -0,0 +1,59 @@
/datum/game_mode/meteor
name = "meteor"
config_tag = "meteor"
var/meteordelay = 2000
var/nometeors = 0
var/rampupdelta = 5
required_players = 0
/datum/game_mode/meteor/announce()
world << "<B>The current game mode is - Meteor!</B>"
world << "<B>The space station has been stuck in a major meteor shower. You must escape from the station or at least live.</B>"
/datum/game_mode/meteor/process()
if(nometeors || meteordelay > world.time - round_start_time)
return
var/list/wavetype = meteors_normal
var/meteorminutes = (world.time - round_start_time - meteordelay) / 10 / 60
if (prob(meteorminutes))
wavetype = meteors_threatening
if (prob(meteorminutes/2))
wavetype = meteors_catastrophic
var/ramp_up_final = Clamp(round(meteorminutes/rampupdelta), 1, 10)
spawn_meteors(ramp_up_final, wavetype)
/datum/game_mode/meteor/declare_completion()
var/text
var/survivors = 0
for(var/mob/living/player in player_list)
if(player.stat != DEAD)
++survivors
if(player.onCentcom())
text += "<br><b><font size=2>[player.real_name] escaped to the safety of Centcom.</font></b>"
else if(player.onSyndieBase())
text += "<br><b><font size=2>[player.real_name] escaped to the (relative) safety of Syndicate Space.</font></b>"
else
text += "<br><font size=1>[player.real_name] survived but is stranded without any hope of rescue.</font>"
if(survivors)
world << "<span class='boldnotice'>The following survived the meteor storm</span>:[text]"
else
world << "<span class='boldnotice'>Nobody survived the meteor storm!</span>"
feedback_set_details("round_end_result","end - evacuation")
feedback_set("round_end_result",survivors)
..()
return 1
+334
View File
@@ -0,0 +1,334 @@
/var/const/meteor_wave_delay = 625 //minimum wait between waves in tenths of seconds
//set to at least 100 unless you want evarr ruining every round
//Meteors probability of spawning during a given wave
/var/list/meteors_normal = list(/obj/effect/meteor/dust=3, /obj/effect/meteor/medium=8, /obj/effect/meteor/big=3, \
/obj/effect/meteor/flaming=1, /obj/effect/meteor/irradiated=3) //for normal meteor event
/var/list/meteors_threatening = list(/obj/effect/meteor/medium=4, /obj/effect/meteor/big=8, \
/obj/effect/meteor/flaming=3, /obj/effect/meteor/irradiated=3) //for threatening meteor event
/var/list/meteors_catastrophic = list(/obj/effect/meteor/medium=5, /obj/effect/meteor/big=75, \
/obj/effect/meteor/flaming=10, /obj/effect/meteor/irradiated=10, /obj/effect/meteor/tunguska = 1) //for catastrophic meteor event
/var/list/meteorsB = list(/obj/effect/meteor/meaty=5, /obj/effect/meteor/meaty/xeno=1) //for meaty ore event
/var/list/meteorsC = list(/obj/effect/meteor/dust) //for space dust event
///////////////////////////////
//Meteor spawning global procs
///////////////////////////////
/proc/spawn_meteors(number = 10, list/meteortypes)
for(var/i = 0; i < number; i++)
spawn_meteor(meteortypes)
/proc/spawn_meteor(list/meteortypes)
var/turf/pickedstart
var/turf/pickedgoal
var/max_i = 10//number of tries to spawn meteor.
while (!istype(pickedstart, /turf/open/space))
var/startSide = pick(cardinal)
pickedstart = spaceDebrisStartLoc(startSide, 1)
pickedgoal = spaceDebrisFinishLoc(startSide, 1)
max_i--
if(max_i<=0)
return
var/Me = pickweight(meteortypes)
var/obj/effect/meteor/M = new Me(pickedstart)
M.dest = pickedgoal
M.z_original = 1
spawn(0)
walk_towards(M, M.dest, 1)
return
/proc/spaceDebrisStartLoc(startSide, Z)
var/starty
var/startx
switch(startSide)
if(1) //NORTH
starty = world.maxy-(TRANSITIONEDGE+1)
startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1))
if(2) //EAST
starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1))
startx = world.maxx-(TRANSITIONEDGE+1)
if(3) //SOUTH
starty = (TRANSITIONEDGE+1)
startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1))
if(4) //WEST
starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1))
startx = (TRANSITIONEDGE+1)
var/turf/T = locate(startx, starty, Z)
return T
/proc/spaceDebrisFinishLoc(startSide, Z)
var/endy
var/endx
switch(startSide)
if(1) //NORTH
endy = TRANSITIONEDGE
endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE)
if(2) //EAST
endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE)
endx = TRANSITIONEDGE
if(3) //SOUTH
endy = world.maxy-TRANSITIONEDGE
endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE)
if(4) //WEST
endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE)
endx = world.maxx-TRANSITIONEDGE
var/turf/T = locate(endx, endy, Z)
return T
///////////////////////
//The meteor effect
//////////////////////
/obj/effect/meteor
name = "the concept of meteor"
desc = "You should probably run instead of gawking at this."
icon = 'icons/obj/meteor.dmi'
icon_state = "small"
density = 1
anchored = 1
var/hits = 4
var/hitpwr = 2 //Level of ex_act to be called on hit.
var/dest
pass_flags = PASSTABLE
var/heavy = 0
var/meteorsound = 'sound/effects/meteorimpact.ogg'
var/z_original = 1
var/list/meteordrop = list(/obj/item/weapon/ore/iron)
var/dropamt = 2
/obj/effect/meteor/Move()
if(z != z_original || loc == dest)
qdel(src)
return
. = ..() //process movement...
if(.)//.. if did move, ram the turf we get in
var/turf/T = get_turf(loc)
ram_turf(T)
if(prob(10) && !istype(T, /turf/open/space))//randomly takes a 'hit' from ramming
get_hit()
return .
/obj/effect/meteor/Destroy()
walk(src,0) //this cancels the walk_towards() proc
return ..()
/obj/effect/meteor/New()
..()
SpinAnimation()
/obj/effect/meteor/Bump(atom/A)
if(A)
ram_turf(get_turf(A))
playsound(src.loc, meteorsound, 40, 1)
get_hit()
/obj/effect/meteor/proc/ram_turf(turf/T)
//first bust whatever is in the turf
for(var/atom/A in T)
if(A != src)
if(istype(A, /mob/living))
A.visible_message("<span class='warning'>[src] slams into [A].</span>", "<span class='userdanger'>[src] slams into you!.</span>")
A.ex_act(hitpwr)
//then, ram the turf if it still exists
if(T)
T.ex_act(hitpwr)
//process getting 'hit' by colliding with a dense object
//or randomly when ramming turfs
/obj/effect/meteor/proc/get_hit()
hits--
if(hits <= 0)
make_debris()
meteor_effect(heavy)
qdel(src)
/obj/effect/meteor/ex_act()
return
/obj/effect/meteor/attackby(obj/item/weapon/W, mob/user, params)
if(istype(W, /obj/item/weapon/pickaxe))
make_debris()
qdel(src)
return
else
return ..()
/obj/effect/meteor/proc/make_debris()
for(var/throws = dropamt, throws > 0, throws--)
var/thing_to_spawn = pick(meteordrop)
new thing_to_spawn(get_turf(src))
/obj/effect/meteor/proc/meteor_effect(sound=1)
if(sound)
for(var/mob/M in player_list)
var/turf/T = get_turf(M)
if(!T || T.z != src.z)
continue
var/dist = get_dist(M.loc, src.loc)
shake_camera(M, dist > 20 ? 2 : 4, dist > 20 ? 1 : 3)
M.playsound_local(src.loc, meteorsound, 50, 1, get_rand_frequency(), 10)
///////////////////////
//Meteor types
///////////////////////
//Dust
/obj/effect/meteor/dust
name = "space dust"
icon_state = "dust"
pass_flags = PASSTABLE | PASSGRILLE
hits = 1
hitpwr = 3
meteorsound = 'sound/weapons/Gunshot_smg.ogg'
meteordrop = list(/obj/item/weapon/ore/glass)
//Medium-sized
/obj/effect/meteor/medium
name = "meteor"
dropamt = 3
/obj/effect/meteor/medium/meteor_effect()
..(heavy)
explosion(src.loc, 0, 1, 2, 3, 0)
//Large-sized
/obj/effect/meteor/big
name = "big meteor"
icon_state = "large"
hits = 6
heavy = 1
dropamt = 4
/obj/effect/meteor/big/meteor_effect()
..(heavy)
explosion(src.loc, 1, 2, 3, 4, 0)
//Flaming meteor
/obj/effect/meteor/flaming
name = "flaming meteor"
icon_state = "flaming"
hits = 5
heavy = 1
meteorsound = 'sound/effects/bamf.ogg'
meteordrop = list(/obj/item/weapon/ore/plasma)
/obj/effect/meteor/flaming/meteor_effect()
..(heavy)
explosion(src.loc, 1, 2, 3, 4, 0, 0, 5)
//Radiation meteor
/obj/effect/meteor/irradiated
name = "glowing meteor"
icon_state = "glowing"
heavy = 1
meteordrop = list(/obj/item/weapon/ore/uranium)
/obj/effect/meteor/irradiated/meteor_effect()
..(heavy)
explosion(src.loc, 0, 0, 4, 3, 0)
new /obj/effect/decal/cleanable/greenglow(get_turf(src))
radiation_pulse(get_turf(src), 2, 5, 50, 1)
//Meaty Ore
/obj/effect/meteor/meaty
name = "meaty ore"
icon_state = "meateor"
desc = "Just... don't think too hard about where this thing came from."
hits = 2
heavy = 1
meteorsound = 'sound/effects/blobattack.ogg'
meteordrop = list(/obj/item/weapon/reagent_containers/food/snacks/meat/slab/human, /obj/item/weapon/reagent_containers/food/snacks/meat/slab/human/mutant, /obj/item/organ/heart, /obj/item/organ/lungs, /obj/item/organ/tongue, /obj/item/organ/appendix/)
var/meteorgibs = /obj/effect/gibspawner/generic
/obj/effect/meteor/meaty/New()
for(var/obj/item/weapon/reagent_containers/food/snacks/meat/slab/human/mutant/M in meteordrop)
meteordrop -= M
meteordrop += pick(subtypesof(M))
for(var/obj/item/organ/tongue/T in meteordrop)
meteordrop -= T
meteordrop += pick(typesof(T))
..()
/obj/effect/meteor/meaty/make_debris()
..()
new meteorgibs(get_turf(src))
/obj/effect/meteor/meaty/ram_turf(turf/T)
if(!istype(T, /turf/open/space))
new /obj/effect/decal/cleanable/blood (T)
/obj/effect/meteor/meaty/Bump(atom/A)
A.ex_act(hitpwr)
get_hit()
//Meaty Ore Xeno edition
/obj/effect/meteor/meaty/xeno
color = "#5EFF00"
meteordrop = list(/obj/item/weapon/reagent_containers/food/snacks/meat/slab/xeno, /obj/item/organ/tongue/alien)
meteorgibs = /obj/effect/gibspawner/xeno
/obj/effect/meteor/meaty/xeno/New()
meteordrop += subtypesof(/obj/item/organ/alien)
..()
/obj/effect/meteor/meaty/xeno/ram_turf(turf/T)
if(!istype(T, /turf/open/space))
new /obj/effect/decal/cleanable/xenoblood (T)
//Station buster Tunguska
/obj/effect/meteor/tunguska
name = "tunguska meteor"
icon_state = "flaming"
desc = "Your life briefly passes before your eyes the moment you lay them on this monstruosity"
hits = 30
hitpwr = 1
heavy = 1
meteorsound = 'sound/effects/bamf.ogg'
meteordrop = list(/obj/item/weapon/ore/plasma)
/obj/effect/meteor/tunguska/meteor_effect()
..(heavy)
explosion(src.loc, 5, 10, 15, 20, 0)
/obj/effect/meteor/tunguska/Bump()
..()
if(prob(20))
explosion(src.loc,2,4,6,8)
//////////////////////////
//Spookoween meteors
/////////////////////////
/var/list/meteorsSPOOKY = list(/obj/effect/meteor/pumpkin)
/obj/effect/meteor/pumpkin
name = "PUMPKING"
desc = "THE PUMPKING'S COMING!"
icon = 'icons/obj/meteor_spooky.dmi'
icon_state = "pumpkin"
hits = 10
heavy = 1
dropamt = 1
meteordrop = list(/obj/item/clothing/head/hardhat/pumpkinhead, /obj/item/weapon/reagent_containers/food/snacks/grown/pumpkin)
/obj/effect/meteor/pumpkin/New()
..()
meteorsound = pick('sound/hallucinations/im_here1.ogg','sound/hallucinations/im_here2.ogg')
//////////////////////////
@@ -0,0 +1,464 @@
/datum/game_mode
var/abductor_teams = 0
var/list/datum/mind/abductors = list()
var/list/datum/mind/abductees = list()
/datum/game_mode/abduction
name = "abduction"
config_tag = "abduction"
antag_flag = ROLE_ABDUCTOR
recommended_enemies = 2
required_players = 15
var/max_teams = 4
abductor_teams = 1
var/list/datum/mind/scientists = list()
var/list/datum/mind/agents = list()
var/list/datum/objective/team_objectives = list()
var/list/team_names = list()
var/finished = 0
/datum/game_mode/abduction/announce()
world << "<B>The current game mode is - Abduction!</B>"
world << "There are alien <b>abductors</b> sent to [world.name] to perform nefarious experiments!"
world << "<b>Abductors</b> - kidnap the crew and replace their organs with experimental ones."
world << "<b>Crew</b> - don't get abducted and stop the abductors."
/datum/game_mode/abduction/pre_setup()
abductor_teams = max(1, min(max_teams,round(num_players()/config.abductor_scaling_coeff)))
var/possible_teams = max(1,round(antag_candidates.len / 2))
abductor_teams = min(abductor_teams,possible_teams)
abductors.len = 2*abductor_teams
scientists.len = abductor_teams
agents.len = abductor_teams
team_objectives.len = abductor_teams
team_names.len = abductor_teams
for(var/i=1,i<=abductor_teams,i++)
if(!make_abductor_team(i))
return 0
return 1
/datum/game_mode/abduction/proc/make_abductor_team(team_number,preset_agent=null,preset_scientist=null)
//Team Name
team_names[team_number] = "Mothership [pick(possible_changeling_IDs)]" //TODO Ensure unique and actual alieny names
//Team Objective
var/datum/objective/experiment/team_objective = new
team_objective.team = team_number
team_objectives[team_number] = team_objective
//Team Members
if(!preset_agent || !preset_scientist)
if(antag_candidates.len <=2)
return 0
var/datum/mind/scientist
var/datum/mind/agent
if(!preset_scientist)
scientist = pick(antag_candidates)
antag_candidates -= scientist
else
scientist = preset_scientist
if(!preset_agent)
agent = pick(antag_candidates)
antag_candidates -= agent
else
agent = preset_agent
scientist.assigned_role = "abductor scientist"
scientist.special_role = "abductor scientist"
log_game("[scientist.key] (ckey) has been selected as an abductor team [team_number] scientist.")
agent.assigned_role = "abductor agent"
agent.special_role = "abductor agent"
log_game("[agent.key] (ckey) has been selected as an abductor team [team_number] agent.")
abductors |= agent
abductors |= scientist
scientists[team_number] = scientist
agents[team_number] = agent
return 1
/datum/game_mode/abduction/post_setup()
//Spawn Team
var/list/obj/effect/landmark/abductor/agent_landmarks = new
var/list/obj/effect/landmark/abductor/scientist_landmarks = new
agent_landmarks.len = max_teams
scientist_landmarks.len = max_teams
for(var/obj/effect/landmark/abductor/A in landmarks_list)
if(istype(A,/obj/effect/landmark/abductor/agent))
agent_landmarks[text2num(A.team)] = A
else if(istype(A,/obj/effect/landmark/abductor/scientist))
scientist_landmarks[text2num(A.team)] = A
var/datum/mind/agent
var/obj/effect/landmark/L
var/datum/mind/scientist
var/team_name
var/mob/living/carbon/human/H
var/datum/species/abductor/S
for(var/team_number=1,team_number<=abductor_teams,team_number++)
team_name = team_names[team_number]
agent = agents[team_number]
H = agent.current
L = agent_landmarks[team_number]
H.loc = L.loc
H.set_species(/datum/species/abductor)
S = H.dna.species
S.agent = 1
S.team = team_number
H.real_name = team_name + " Agent"
equip_common(H,team_number)
equip_agent(H,team_number)
greet_agent(agent,team_number)
scientist = scientists[team_number]
H = scientist.current
L = scientist_landmarks[team_number]
H.loc = L.loc
H.set_species(/datum/species/abductor)
S = H.dna.species
S.scientist = 1
S.team = team_number
H.real_name = team_name + " Scientist"
equip_common(H,team_number)
equip_scientist(H,team_number)
greet_scientist(scientist,team_number)
return ..()
//Used for create antag buttons
/datum/game_mode/abduction/proc/post_setup_team(team_number)
var/list/obj/effect/landmark/abductor/agent_landmarks = new
var/list/obj/effect/landmark/abductor/scientist_landmarks = new
agent_landmarks.len = max_teams
scientist_landmarks.len = max_teams
for(var/obj/effect/landmark/abductor/A in landmarks_list)
if(istype(A,/obj/effect/landmark/abductor/agent))
agent_landmarks[text2num(A.team)] = A
else if(istype(A,/obj/effect/landmark/abductor/scientist))
scientist_landmarks[text2num(A.team)] = A
var/datum/mind/agent
var/obj/effect/landmark/L
var/datum/mind/scientist
var/team_name
var/mob/living/carbon/human/H
var/datum/species/abductor/S
team_name = team_names[team_number]
agent = agents[team_number]
H = agent.current
L = agent_landmarks[team_number]
H.loc = L.loc
H.set_species(/datum/species/abductor)
S = H.dna.species
S.agent = 1
S.team = team_number
H.real_name = team_name + " Agent"
equip_common(H,team_number)
equip_agent(H,team_number)
greet_agent(agent,team_number)
scientist = scientists[team_number]
H = scientist.current
L = scientist_landmarks[team_number]
H.loc = L.loc
H.set_species(/datum/species/abductor)
S = H.dna.species
S.scientist = 1
S.team = team_number
H.real_name = team_name + " Scientist"
equip_common(H,team_number)
equip_scientist(H,team_number)
greet_scientist(scientist,team_number)
/datum/game_mode/abduction/proc/greet_agent(datum/mind/abductor,team_number)
abductor.objectives += team_objectives[team_number]
var/team_name = team_names[team_number]
abductor.current << "<span class='notice'>You are an agent of [team_name]!</span>"
abductor.current << "<span class='notice'>With the help of your teammate, kidnap and experiment on station crew members!</span>"
abductor.current << "<span class='notice'>Use your stealth technology and equipment to incapacitate humans for your scientist to retrieve.</span>"
var/obj_count = 1
for(var/datum/objective/objective in abductor.objectives)
abductor.current << "<B>Objective #[obj_count]</B>: [objective.explanation_text]"
obj_count++
return
/datum/game_mode/abduction/proc/greet_scientist(datum/mind/abductor,team_number)
abductor.objectives += team_objectives[team_number]
var/team_name = team_names[team_number]
abductor.current << "<span class='notice'>You are a scientist of [team_name]!</span>"
abductor.current << "<span class='notice'>With the help of your teammate, kidnap and experiment on station crew members!</span>"
abductor.current << "<span class='notice'>Use your tool and ship consoles to support the agent and retrieve human specimens.</span>"
var/obj_count = 1
for(var/datum/objective/objective in abductor.objectives)
abductor.current << "<B>Objective #[obj_count]</B>: [objective.explanation_text]"
obj_count++
return
/datum/game_mode/abduction/proc/equip_common(mob/living/carbon/human/agent,team_number)
var/radio_freq = SYND_FREQ
var/obj/item/device/radio/R = new /obj/item/device/radio/headset/syndicate/alt(agent)
R.set_frequency(radio_freq)
agent.equip_to_slot_or_del(R, slot_ears)
agent.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(agent), slot_shoes)
agent.equip_to_slot_or_del(new /obj/item/clothing/under/color/grey(agent), slot_w_uniform) //they're greys gettit
agent.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack(agent), slot_back)
/datum/game_mode/abduction/proc/get_team_console(team)
var/obj/machinery/abductor/console/console
for(var/obj/machinery/abductor/console/c in machines)
if(c.team == team)
console = c
break
return console
/datum/game_mode/abduction/proc/equip_agent(mob/living/carbon/human/agent,team_number)
if(!team_number)
var/datum/species/abductor/S = agent.dna.species
team_number = S.team
var/obj/machinery/abductor/console/console = get_team_console(team_number)
var/obj/item/clothing/suit/armor/abductor/vest/V = new /obj/item/clothing/suit/armor/abductor/vest(agent)
if(console!=null)
console.vest = V
V.flags |= NODROP
agent.equip_to_slot_or_del(V, slot_wear_suit)
agent.equip_to_slot_or_del(new /obj/item/weapon/abductor_baton(agent), slot_belt)
agent.equip_to_slot_or_del(new /obj/item/weapon/gun/energy/alien(agent), slot_in_backpack)
agent.equip_to_slot_or_del(new /obj/item/device/abductor/silencer(agent), slot_in_backpack)
agent.equip_to_slot_or_del(new /obj/item/clothing/head/helmet/abductor(agent), slot_head)
/datum/game_mode/abduction/proc/equip_scientist(var/mob/living/carbon/human/scientist,var/team_number)
if(!team_number)
var/datum/species/abductor/S = scientist.dna.species
team_number = S.team
var/obj/machinery/abductor/console/console = get_team_console(team_number)
var/obj/item/device/abductor/gizmo/G = new /obj/item/device/abductor/gizmo(scientist)
if(console!=null)
console.gizmo = G
G.console = console
scientist.equip_to_slot_or_del(G, slot_in_backpack)
var/obj/item/weapon/implant/abductor/beamplant = new /obj/item/weapon/implant/abductor(scientist)
beamplant.implant(scientist)
/datum/game_mode/abduction/check_finished()
if(!finished)
for(var/team_number=1,team_number<=abductor_teams,team_number++)
var/obj/machinery/abductor/console/con = get_team_console(team_number)
var/datum/objective/objective = team_objectives[team_number]
if (con.experiment.points >= objective.target_amount)
SSshuttle.emergency.request(null, 0.5)
finished = 1
return ..()
return ..()
/datum/game_mode/abduction/declare_completion()
for(var/team_number=1,team_number<=abductor_teams,team_number++)
var/obj/machinery/abductor/console/console = get_team_console(team_number)
var/datum/objective/objective = team_objectives[team_number]
var/team_name = team_names[team_number]
if(console.experiment.points >= objective.target_amount)
world << "<span class='greenannounce'>[team_name] team fullfilled its mission!</span>"
else
world << "<span class='boldannounce'>[team_name] team failed its mission.</span>"
..()
return 1
/datum/game_mode/proc/auto_declare_completion_abduction()
var/text = ""
if(abductors.len)
text += "<br><span class='big'><b>The abductors were:</b></span>"
for(var/datum/mind/abductor_mind in abductors)
text += printplayer(abductor_mind)
text += printobjectives(abductor_mind)
text += "<br>"
if(abductees.len)
text += "<br><span class='big'><b>The abductees were:</b></span>"
for(var/datum/mind/abductee_mind in abductees)
text += printplayer(abductee_mind)
text += printobjectives(abductee_mind)
text += "<br>"
world << text
//Landmarks
// TODO: Split into seperate landmarks for prettier ships
/obj/effect/landmark/abductor
var/team = 1
/obj/effect/landmark/abductor/console/New()
var/obj/machinery/abductor/console/c = new /obj/machinery/abductor/console(src.loc)
c.team = team
spawn(5) // I'd do this properly when i got some time, temporary hack for mappers
c.Initialize()
qdel(src)
/obj/effect/landmark/abductor/agent
/obj/effect/landmark/abductor/scientist
// OBJECTIVES
/datum/objective/experiment
dangerrating = 10
target_amount = 6
var/team
/datum/objective/experiment/New()
explanation_text = "Experiment on [target_amount] humans."
/datum/objective/experiment/check_completion()
var/ab_team = team
if(owner)
if(!owner.current || !ishuman(owner.current))
return 0
var/mob/living/carbon/human/H = owner.current
if(H.dna.species.id != "abductor")
return 0
var/datum/species/abductor/S = H.dna.species
ab_team = S.team
for(var/obj/machinery/abductor/experiment/E in machines)
if(E.team == ab_team)
if(E.points >= target_amount)
return 1
else
return 0
return 0
/datum/game_mode/proc/update_abductor_icons_added(datum/mind/alien_mind)
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_ABDUCTOR]
hud.join_hud(alien_mind.current)
set_antag_hud(alien_mind.current, ((alien_mind in abductors) ? "abductor" : "abductee"))
/datum/game_mode/proc/update_abductor_icons_removed(datum/mind/alien_mind)
var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_ABDUCTOR]
hud.leave_hud(alien_mind.current)
set_antag_hud(alien_mind.current, null)
/datum/objective/abductee
dangerrating = 5
completed = 1
/datum/objective/abductee/steal
explanation_text = "Steal all"
/datum/objective/abductee/steal/New()
var/target = pick(list("pets","lights","monkeys","fruits","shoes","bars of soap"))
explanation_text+=" [target]."
/datum/objective/abductee/capture
explanation_text = "Capture"
/datum/objective/abductee/capture/New()
var/list/jobs = SSjob.occupations.Copy()
for(var/datum/job/J in jobs)
if(J.current_positions < 1)
jobs -= J
if(jobs.len > 0)
var/datum/job/target = pick(jobs)
explanation_text += " a [target.title]."
else
explanation_text += " someone."
/datum/objective/abductee/shuttle
explanation_text = "You must escape the station! Get the shuttle called!"
/datum/objective/abductee/noclone
explanation_text = "Don't allow anyone to be cloned."
/datum/objective/abductee/oxygen
explanation_text = "The oxygen is killing them all and they don't even know it. Make sure no oxygen is on the station."
/datum/objective/abductee/blazeit
explanation_text = "Your body must be improved. Ingest as many drugs as you can."
/datum/objective/abductee/yumyum
explanation_text = "You are hungry. Eat as much food as you can find."
/datum/objective/abductee/insane
explanation_text = "You see you see what they cannot you see the open door you seeE you SEeEe you SEe yOU seEee SHOW THEM ALL"
/datum/objective/abductee/cannotmove
explanation_text = "Convince the crew that you are a paraplegic."
/datum/objective/abductee/deadbodies
explanation_text = "Start a collection of corpses. Don't kill people to get these corpses."
/datum/objective/abductee/floors
explanation_text = "Replace all the floor tiles with carpeting, wooden boards, or grass."
/datum/objective/abductee/POWERUNLIMITED
explanation_text = "Flood the station's powernet with as much electricity as you can."
/datum/objective/abductee/pristine
explanation_text = "Ensure the station is in absolutely pristine condition."
/datum/objective/abductee/window
explanation_text = "Replace all normal windows with reinforced windows."
/datum/objective/abductee/nations
explanation_text = "Ensure your department prospers over all else."
/datum/objective/abductee/abductception
explanation_text = "You have been changed forever. Find the ones that did this to you and give them a taste of their own medicine."
/datum/objective/abductee/ghosts
explanation_text = "Conduct a seance with the spirits of the afterlife."
/datum/objective/abductee/summon
explanation_text = "Conduct a ritual to summon an elder god."
/datum/objective/abductee/machine
explanation_text = "You are secretly an android. Interface with as many machines as you can to boost your own power."
/datum/objective/abductee/prevent
explanation_text = "You have been enlightened. This knowledge must not escape. Ensure nobody else can become enlightened."
/datum/objective/abductee/calling
explanation_text = "Call forth a spirit from the other side."
/datum/objective/abductee/calling/New()
var/mob/dead/D = pick(dead_mob_list)
if(D)
explanation_text = "You know that [D] has perished. Call them from the spirit realm."
/datum/objective/abductee/social_experiment
explanation_text = "This is a secret social experiment conducted by Nanotrasen. Convince the crew that this is the truth."
/datum/objective/abductee/vr
explanation_text = "It's all an entirely virtual simulation within an underground vault. Convince the crew to escape the shackles of VR."
/datum/objective/abductee/pets
explanation_text = "Nanotrasen is abusing the animals! Save as many as you can!"
/datum/objective/abductee/defect
explanation_text = "Defect from your employer."
/datum/objective/abductee/promote
explanation_text = "Climb the corporate ladder all the way to the top!"
/datum/objective/abductee/science
explanation_text = "So much lies undiscovered. Look deeper into the machinations of the universe."
/datum/objective/abductee/build
explanation_text = "Expand the station."
/datum/objective/abductee/pragnant
explanation_text = "You are pregnant and soon due. Find a safe place to deliver your baby."
@@ -0,0 +1,678 @@
#define VEST_STEALTH 1
#define VEST_COMBAT 2
#define GIZMO_SCAN 1
#define GIZMO_MARK 2
//AGENT VEST
/obj/item/clothing/suit/armor/abductor/vest
name = "agent vest"
desc = "A vest outfitted with advanced stealth technology. It has two modes - combat and stealth."
icon = 'icons/obj/abductor.dmi'
icon_state = "vest_stealth"
item_state = "armor"
blood_overlay_type = "armor"
origin_tech = "magnets=7;biotech=4;powerstorage=4;abductor=4"
armor = list(melee = 15, bullet = 15, laser = 15, energy = 15, bomb = 15, bio = 15, rad = 15)
actions_types = list(/datum/action/item_action/hands_free/activate)
var/mode = VEST_STEALTH
var/stealth_active = 0
var/combat_cooldown = 10
var/datum/icon_snapshot/disguise
var/stealth_armor = list(melee = 15, bullet = 15, laser = 15, energy = 15, bomb = 15, bio = 15, rad = 15)
var/combat_armor = list(melee = 50, bullet = 50, laser = 50, energy = 50, bomb = 50, bio = 50, rad = 50)
/obj/item/clothing/suit/armor/abductor/vest/proc/flip_mode()
switch(mode)
if(VEST_STEALTH)
mode = VEST_COMBAT
DeactivateStealth()
armor = combat_armor
icon_state = "vest_combat"
if(VEST_COMBAT)// TO STEALTH
mode = VEST_STEALTH
armor = stealth_armor
icon_state = "vest_stealth"
if(istype(loc, /mob/living/carbon/human))
var/mob/living/carbon/human/H = loc
H.update_inv_wear_suit()
for(var/X in actions)
var/datum/action/A = X
A.UpdateButtonIcon()
/obj/item/clothing/suit/armor/abductor/vest/item_action_slot_check(slot, mob/user)
if(slot == slot_wear_suit) //we only give the mob the ability to activate the vest if he's actually wearing it.
return 1
/obj/item/clothing/suit/armor/abductor/vest/proc/SetDisguise(datum/icon_snapshot/entry)
disguise = entry
/obj/item/clothing/suit/armor/abductor/vest/proc/ActivateStealth()
if(disguise == null)
return
stealth_active = 1
if(istype(src.loc, /mob/living/carbon/human))
var/mob/living/carbon/human/M = src.loc
addtimer(GLOBAL_PROC, "anim", 0, FALSE, M.loc, M, 'icons/mob/mob.dmi',
null, "cloak", null, M.dir)
M.name_override = disguise.name
M.icon = disguise.icon
M.icon_state = disguise.icon_state
M.overlays = disguise.overlays
M.update_inv_r_hand()
M.update_inv_l_hand()
/obj/item/clothing/suit/armor/abductor/vest/proc/DeactivateStealth()
if(!stealth_active)
return
stealth_active = 0
if(istype(src.loc, /mob/living/carbon/human))
var/mob/living/carbon/human/M = src.loc
addtimer(GLOBAL_PROC, "anim", 0, FALSE, M.loc, M, 'icons/mob/mob.dmi',
null, "uncloak", null, M.dir)
M.name_override = null
M.cut_overlays()
M.regenerate_icons()
/obj/item/clothing/suit/armor/abductor/vest/hit_reaction()
DeactivateStealth()
return 0
/obj/item/clothing/suit/armor/abductor/vest/IsReflect()
DeactivateStealth()
return 0
/obj/item/clothing/suit/armor/abductor/vest/ui_action_click()
switch(mode)
if(VEST_COMBAT)
Adrenaline()
if(VEST_STEALTH)
if(stealth_active)
DeactivateStealth()
else
ActivateStealth()
/obj/item/clothing/suit/armor/abductor/vest/proc/Adrenaline()
if(istype(src.loc, /mob/living/carbon/human))
if(combat_cooldown != initial(combat_cooldown))
src.loc << "<span class='warning'>Combat injection is still recharging.</span>"
return
var/mob/living/carbon/human/M = src.loc
M.adjustStaminaLoss(-75)
M.SetParalysis(0)
M.SetStunned(0)
M.SetWeakened(0)
combat_cooldown = 0
START_PROCESSING(SSobj, src)
/obj/item/clothing/suit/armor/abductor/vest/process()
combat_cooldown++
if(combat_cooldown==initial(combat_cooldown))
STOP_PROCESSING(SSobj, src)
/obj/item/device/abductor/proc/AbductorCheck(user)
if(isabductor(user))
return TRUE
user << "<span class='warning'>You can't figure how this works!</span>"
return FALSE
/obj/item/device/abductor/proc/ScientistCheck(user)
var/mob/living/carbon/human/H = user
var/datum/species/abductor/S = H.dna.species
return S.scientist
/obj/item/device/abductor/gizmo
name = "science tool"
desc = "A dual-mode tool for retrieving specimens and scanning appearances. Scanning can be done through cameras."
icon = 'icons/obj/abductor.dmi'
icon_state = "gizmo_scan"
item_state = "silencer"
origin_tech = "engineering=7;magnets=4;bluespace=4;abductor=3"
var/mode = GIZMO_SCAN
var/mob/living/marked = null
var/obj/machinery/abductor/console/console
/obj/item/device/abductor/gizmo/attack_self(mob/user)
if(!AbductorCheck(user))
return
if(!ScientistCheck(user))
user << "<span class='warning'>You're not trained to use this!</span>"
return
if(mode == GIZMO_SCAN)
mode = GIZMO_MARK
icon_state = "gizmo_mark"
else
mode = GIZMO_SCAN
icon_state = "gizmo_scan"
user << "<span class='notice'>You switch the device to [mode==GIZMO_SCAN? "SCAN": "MARK"] MODE</span>"
/obj/item/device/abductor/gizmo/attack(mob/living/M, mob/user)
if(!AbductorCheck(user))
return
if(!ScientistCheck(user))
user << "<span class='notice'>You're not trained to use this</span>"
return
switch(mode)
if(GIZMO_SCAN)
scan(M, user)
if(GIZMO_MARK)
mark(M, user)
/obj/item/device/abductor/gizmo/afterattack(atom/target, mob/living/user, flag, params)
if(flag)
return
if(!AbductorCheck(user))
return
if(!ScientistCheck(user))
user << "<span class='notice'>You're not trained to use this</span>"
return
switch(mode)
if(GIZMO_SCAN)
scan(target, user)
if(GIZMO_MARK)
mark(target, user)
/obj/item/device/abductor/gizmo/proc/scan(atom/target, mob/living/user)
if(istype(target,/mob/living/carbon/human))
if(console!=null)
console.AddSnapshot(target)
user << "<span class='notice'>You scan [target] and add them to the database.</span>"
/obj/item/device/abductor/gizmo/proc/mark(atom/target, mob/living/user)
if(marked == target)
user << "<span class='warning'>This specimen is already marked!</span>"
return
if(istype(target,/mob/living/carbon/human))
if(isabductor(target))
marked = target
user << "<span class='notice'>You mark [target] for future retrieval.</span>"
else
prepare(target,user)
else
prepare(target,user)
/obj/item/device/abductor/gizmo/proc/prepare(atom/target, mob/living/user)
if(get_dist(target,user)>1)
user << "<span class='warning'>You need to be next to the specimen to prepare it for transport!</span>"
return
user << "<span class='notice'>You begin preparing [target] for transport...</span>"
if(do_after(user, 100, target = target))
marked = target
user << "<span class='notice'>You finish preparing [target] for transport.</span>"
/obj/item/device/abductor/silencer
name = "abductor silencer"
desc = "A compact device used to shut down communications equipment."
icon = 'icons/obj/abductor.dmi'
icon_state = "silencer"
item_state = "gizmo"
origin_tech = "materials=4;programming=7;abductor=3"
/obj/item/device/abductor/silencer/attack(mob/living/M, mob/user)
if(!AbductorCheck(user))
return
radio_off(M, user)
/obj/item/device/abductor/silencer/afterattack(atom/target, mob/living/user, flag, params)
if(flag)
return
if(!AbductorCheck(user))
return
radio_off(target, user)
/obj/item/device/abductor/silencer/proc/radio_off(atom/target, mob/living/user)
if( !(user in (viewers(7,target))) )
return
var/turf/targloc = get_turf(target)
var/mob/living/carbon/human/M
for(M in view(2,targloc))
if(M == user)
continue
user << "<span class='notice'>You silence [M]'s radio devices.</span>"
radio_off_mob(M)
/obj/item/device/abductor/silencer/proc/radio_off_mob(mob/living/carbon/human/M)
var/list/all_items = M.GetAllContents()
for(var/obj/I in all_items)
if(istype(I,/obj/item/device/radio/))
var/obj/item/device/radio/r = I
r.listening = 0
if(!istype(I,/obj/item/device/radio/headset))
r.broadcasting = 0 //goddamned headset hacks
/obj/item/weapon/implant/abductor
name = "recall implant"
desc = "Returns you to the mothership."
icon = 'icons/obj/abductor.dmi'
icon_state = "implant"
activated = 1
origin_tech = "materials=2;biotech=7;magnets=4;bluespace=4;abductor=5"
var/obj/machinery/abductor/pad/home
var/cooldown = 30
/obj/item/weapon/implant/abductor/activate()
if(cooldown == initial(cooldown))
home.Retrieve(imp_in,1)
cooldown = 0
START_PROCESSING(SSobj, src)
else
imp_in << "<span class='warning'>You must wait [30 - cooldown] seconds to use [src] again!</span>"
/obj/item/weapon/implant/abductor/process()
if(cooldown < initial(cooldown))
cooldown++
if(cooldown == initial(cooldown))
STOP_PROCESSING(SSobj, src)
/obj/item/weapon/implant/abductor/implant(var/mob/source, var/mob/user)
if(..())
var/obj/machinery/abductor/console/console
if(ishuman(source))
var/mob/living/carbon/human/H = source
if(H.dna.species.id == "abductor")
var/datum/species/abductor/S = H.dna.species
console = get_team_console(S.team)
home = console.pad
if(!home)
console = get_team_console(pick(1, 2, 3, 4))
home = console.pad
return 1
/obj/item/weapon/implant/abductor/proc/get_team_console(var/team)
var/obj/machinery/abductor/console/console
for(var/obj/machinery/abductor/console/c in machines)
if(c.team == team)
console = c
break
return console
/obj/item/device/firing_pin/abductor
name = "alien firing pin"
icon_state = "firing_pin_ayy"
desc = "This firing pin is slimy and warm; you can swear you feel it \
constantly trying to mentally probe you."
fail_message = "<span class='abductor'>\
Firing error, please contact Command.</span>"
/obj/item/device/firing_pin/abductor/pin_auth(mob/living/user)
. = isabductor(user)
/obj/item/weapon/gun/energy/alien
name = "alien pistol"
desc = "A complicated gun that fires bursts of high-intensity radiation."
ammo_type = list(/obj/item/ammo_casing/energy/declone)
pin = /obj/item/device/firing_pin/abductor
icon_state = "alienpistol"
item_state = "alienpistol"
origin_tech = "combat=4;magnets=7;powerstorage=3;abductor=3"
trigger_guard = TRIGGER_GUARD_ALLOW_ALL
/obj/item/weapon/paper/abductor
name = "Dissection Guide"
icon_state = "alienpaper_words"
info = {"<b>Dissection for Dummies</b><br>
<br>
1.Acquire fresh specimen.<br>
2.Put the specimen on operating table<br>
3.Apply surgical drapes preparing for dissection<br>
4.Apply scalpel to specimen torso<br>
5.Retract skin from specimen's torso<br>
6.Apply scalpel to specimen's torso<br>
7.Search through the specimen's torso with your hands to remove any organs<br>
8.Insert replacement gland (Retrieve one from gland storage)<br>
9.Consider dressing the specimen back to not disturb the habitat <br>
10.Put the specimen in the experiment machinery<br>
11.Choose one of the machine options and follow displayed instructions<br>
<br>
Congratulations! You are now trained for xenobiology research!"}
/obj/item/weapon/paper/abductor/update_icon()
return
/obj/item/weapon/paper/abductor/AltClick()
return
#define BATON_STUN 0
#define BATON_SLEEP 1
#define BATON_CUFF 2
#define BATON_PROBE 3
#define BATON_MODES 4
/obj/item/weapon/abductor_baton
name = "advanced baton"
desc = "A quad-mode baton used for incapacitation and restraining of specimens."
var/mode = BATON_STUN
icon = 'icons/obj/abductor.dmi'
icon_state = "wonderprodStun"
item_state = "wonderprod"
slot_flags = SLOT_BELT
origin_tech = "materials=4;combat=4;biotech=7;abductor=4"
force = 7
w_class = 3
actions_types = list(/datum/action/item_action/toggle_mode)
/obj/item/weapon/abductor_baton/proc/toggle(mob/living/user=usr)
mode = (mode+1)%BATON_MODES
var/txt
switch(mode)
if(BATON_STUN)
txt = "stunning"
if(BATON_SLEEP)
txt = "sleep inducement"
if(BATON_CUFF)
txt = "restraining"
if(BATON_PROBE)
txt = "probing"
usr << "<span class='notice'>You switch the baton to [txt] mode.</span>"
update_icon()
/obj/item/weapon/abductor_baton/update_icon()
switch(mode)
if(BATON_STUN)
icon_state = "wonderprodStun"
item_state = "wonderprodStun"
if(BATON_SLEEP)
icon_state = "wonderprodSleep"
item_state = "wonderprodSleep"
if(BATON_CUFF)
icon_state = "wonderprodCuff"
item_state = "wonderprodCuff"
if(BATON_PROBE)
icon_state = "wonderprodProbe"
item_state = "wonderprodProbe"
/obj/item/weapon/abductor_baton/attack(mob/target, mob/living/user)
if(!isabductor(user))
return
if(isrobot(target))
..()
return
if(!isliving(target))
return
var/mob/living/L = target
user.do_attack_animation(L)
if(ishuman(L))
var/mob/living/carbon/human/H = L
if(H.check_shields(0, "[user]'s [name]", src, MELEE_ATTACK))
playsound(L, 'sound/weapons/Genhit.ogg', 50, 1)
return 0
switch (mode)
if(BATON_STUN)
StunAttack(L,user)
if(BATON_SLEEP)
SleepAttack(L,user)
if(BATON_CUFF)
CuffAttack(L,user)
if(BATON_PROBE)
ProbeAttack(L,user)
/obj/item/weapon/abductor_baton/attack_self(mob/living/user)
toggle(user)
/obj/item/weapon/abductor_baton/proc/StunAttack(mob/living/L,mob/living/user)
user.lastattacked = L
L.lastattacker = user
L.Stun(7)
L.Weaken(7)
L.apply_effect(STUTTER, 7)
L.visible_message("<span class='danger'>[user] has stunned [L] with [src]!</span>", \
"<span class='userdanger'>[user] has stunned you with [src]!</span>")
playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
if(ishuman(L))
var/mob/living/carbon/human/H = L
H.forcesay(hit_appends)
add_logs(user, L, "stunned")
/obj/item/weapon/abductor_baton/proc/SleepAttack(mob/living/L,mob/living/user)
if(L.stunned || L.sleeping)
L.visible_message("<span class='danger'>[user] has induced sleep in [L] with [src]!</span>", \
"<span class='userdanger'>You suddenly feel very drowsy!</span>")
playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
L.Sleeping(60)
add_logs(user, L, "put to sleep")
else
L.drowsyness += 1
user << "<span class='warning'>Sleep inducement works fully only on stunned specimens! </span>"
L.visible_message("<span class='danger'>[user] tried to induce sleep in [L] with [src]!</span>", \
"<span class='userdanger'>You suddenly feel drowsy!</span>")
/obj/item/weapon/abductor_baton/proc/CuffAttack(mob/living/L,mob/living/user)
if(!iscarbon(L))
return
var/mob/living/carbon/C = L
if(!C.handcuffed)
if(C.get_num_arms() >= 2)
playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2)
C.visible_message("<span class='danger'>[user] begins restraining [C] with [src]!</span>", \
"<span class='userdanger'>[user] begins shaping an energy field around your hands!</span>")
if(do_mob(user, C, 30) && C.get_num_arms() >= 2)
if(!C.handcuffed)
C.handcuffed = new /obj/item/weapon/restraints/handcuffs/energy/used(C)
C.update_handcuffed()
user << "<span class='notice'>You handcuff [C].</span>"
add_logs(user, C, "handcuffed")
else
user << "<span class='warning'>You fail to handcuff [C].</span>"
else
user << "<span class='warning'>[C] doesn't have two hands...</span>"
/obj/item/weapon/abductor_baton/proc/ProbeAttack(mob/living/L,mob/living/user)
L.visible_message("<span class='danger'>[user] probes [L] with [src]!</span>", \
"<span class='userdanger'>[user] probes you!</span>")
var/species = "<span class='warning'>Unknown species</span>"
var/helptext = "<span class='warning'>Species unsuitable for experiments.</span>"
if(ishuman(L))
var/mob/living/carbon/human/H = L
species = "<span clas=='notice'>[H.dna.species.name]</span>"
if(L.mind && L.mind.changeling)
species = "<span class='warning'>Changeling lifeform</span>"
var/obj/item/organ/gland/temp = locate() in H.internal_organs
if(temp)
helptext = "<span class='warning'>Experimental gland detected!</span>"
else
helptext = "<span class='notice'>Subject suitable for experiments.</span>"
user << "<span class='notice'>Probing result:</span>[species]"
user << "[helptext]"
/obj/item/weapon/restraints/handcuffs/energy
name = "hard-light energy field"
desc = "A hard-light field restraining the hands."
icon_state = "cuff_white" // Needs sprite
breakouttime = 450
trashtype = /obj/item/weapon/restraints/handcuffs/energy/used
origin_tech = "materials=4;magnets=5;abductor=2"
/obj/item/weapon/restraints/handcuffs/energy/used
desc = "energy discharge"
flags = DROPDEL
/obj/item/weapon/restraints/handcuffs/energy/used/dropped(mob/user)
user.visible_message("<span class='danger'>[user]'s [src] break in a discharge of energy!</span>", \
"<span class='userdanger'>[user]'s [src] break in a discharge of energy!</span>")
var/datum/effect_system/spark_spread/S = new
S.set_up(4,0,user.loc)
S.start()
. = ..()
/obj/item/weapon/abductor_baton/examine(mob/user)
..()
switch(mode)
if(BATON_STUN)
user <<"<span class='warning'>The baton is in stun mode.</span>"
if(BATON_SLEEP)
user <<"<span class='warning'>The baton is in sleep inducement mode.</span>"
if(BATON_CUFF)
user <<"<span class='warning'>The baton is in restraining mode.</span>"
if(BATON_PROBE)
user << "<span class='warning'>The baton is in probing mode.</span>"
/obj/item/weapon/scalpel/alien
name = "alien scalpel"
desc = "It's a gleaming sharp knife made out of silvery-green metal."
icon = 'icons/obj/abductor.dmi'
origin_tech = "materials=2;biotech=2;abductor=2"
/obj/item/weapon/hemostat/alien
name = "alien hemostat"
desc = "You've never seen this before."
icon = 'icons/obj/abductor.dmi'
origin_tech = "materials=2;biotech=2;abductor=2"
/obj/item/weapon/retractor/alien
name = "alien retractor"
desc = "You're not sure if you want the veil pulled back."
icon = 'icons/obj/abductor.dmi'
origin_tech = "materials=2;biotech=2;abductor=2"
/obj/item/weapon/circular_saw/alien
name = "alien saw"
desc = "Do the aliens also lose this, and need to find an alien hatchet?"
icon = 'icons/obj/abductor.dmi'
origin_tech = "materials=2;biotech=2;abductor=2"
/obj/item/weapon/surgicaldrill/alien
name = "alien drill"
desc = "Maybe alien surgeons have finally found a use for the drill."
icon = 'icons/obj/abductor.dmi'
origin_tech = "materials=2;biotech=2;abductor=2"
/obj/item/weapon/cautery/alien
name = "alien cautery"
desc = "Why would bloodless aliens have a tool to stop bleeding? \
Unless..."
icon = 'icons/obj/abductor.dmi'
origin_tech = "materials=2;biotech=2;abductor=2"
/obj/item/clothing/head/helmet/abductor
name = "agent headgear"
desc = "Abduct with style - spiky style. Prevents digital tracking."
icon_state = "alienhelmet"
item_state = "alienhelmet"
blockTracking = 1
origin_tech = "materials=7;magnets=4;abductor=3"
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR
// Operating Table / Beds / Lockers
/obj/structure/table/optable/abductor
icon = 'icons/obj/abductor.dmi'
icon_state = "bed"
can_buckle = 1
buckle_lying = 1
flags = NODECONSTRUCT
/obj/structure/table/optable/abductor/table_destroy()
return //can't destroy the abductor's only optable.
/obj/structure/bed/abductor
name = "resting contraption"
desc = "This looks similar to contraptions from earth. Could aliens be stealing our technology?"
icon = 'icons/obj/abductor.dmi'
buildstacktype = /obj/item/stack/sheet/mineral/abductor
icon_state = "bed"
/obj/structure/table_frame/abductor
name = "alien table frame"
desc = "A sturdy table frame made from alien alloy."
icon_state = "alien_frame"
framestack = /obj/item/stack/sheet/mineral/abductor
framestackamount = 1
density = 1
/obj/structure/table_frame/abductor/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/weapon/wrench))
user << "<span class='notice'>You start disassembling [src]...</span>"
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
if(do_after(user, 30/I.toolspeed, target = src))
playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1)
for(var/i = 1, i <= framestackamount, i++)
new framestack(get_turf(src))
qdel(src)
return
if(istype(I, /obj/item/stack/sheet/mineral/abductor))
var/obj/item/stack/sheet/P = I
if(P.get_amount() < 1)
user << "<span class='warning'>You need one alien alloy sheet to do this!</span>"
return
user << "<span class='notice'>You start adding [P] to [src]...</span>"
if(do_after(user, 50, target = src))
P.use(1)
new /obj/structure/table/abductor(src.loc)
qdel(src)
return
/obj/structure/table/abductor
name = "alien table"
desc = "Advanced flat surface technology at work!"
icon = 'icons/obj/smooth_structures/alien_table.dmi'
icon_state = "alien_table"
buildstack = /obj/item/stack/sheet/mineral/abductor
framestack = /obj/item/stack/sheet/mineral/abductor
buildstackamount = 1
framestackamount = 1
canSmoothWith = null
frame = /obj/structure/table_frame/abductor
/obj/structure/closet/abductor
name = "alien locker"
desc = "Contains secrets of the universe."
icon_state = "abductor"
icon_door = "abductor"
can_weld_shut = FALSE
material_drop = /obj/item/stack/sheet/mineral/abductor
/obj/structure/door_assembly/door_assembly_abductor
name = "alien airlock assembly"
icon = 'icons/obj/doors/airlocks/abductor/abductor_airlock.dmi'
overlays_file = 'icons/obj/doors/airlocks/abductor/overlays.dmi'
typetext = "abductor"
icontext = "abductor"
airlock_type = /obj/machinery/door/airlock/abductor
anchored = 1
state = 1
/obj/structure/door_assembly/door_assembly_abductor/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/weapon/weldingtool) && !anchored )
var/obj/item/weapon/weldingtool/WT = W
if(WT.remove_fuel(0,user))
user.visible_message("<span class='warning'>[user] disassembles the airlock assembly.</span>", \
"You start to disassemble the airlock assembly...")
playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1)
if(do_after(user, 40/W.toolspeed, target = src))
if( !WT.isOn() )
return
user << "<span class='notice'>You disassemble the airlock assembly.</span>"
new /obj/item/stack/sheet/mineral/abductor(get_turf(src), 4)
qdel(src)
else
return
else if(istype(W, /obj/item/weapon/airlock_painter))
return // no repainting
else if(istype(W, /obj/item/stack/sheet))
return // no material modding
else
..()
@@ -0,0 +1,55 @@
/datum/surgery/organ_extraction
name = "experimental dissection"
steps = list(/datum/surgery_step/incise, /datum/surgery_step/clamp_bleeders, /datum/surgery_step/retract_skin,/datum/surgery_step/incise, /datum/surgery_step/extract_organ ,/datum/surgery_step/gland_insert)
possible_locs = list("chest")
ignore_clothes = 1
/datum/surgery/organ_extraction/can_start(mob/user, mob/living/carbon/target)
if(!ishuman(user))
return 0
var/mob/living/carbon/human/H = user
if(H.dna.species.id == "abductor")
return 1
if((locate(/obj/item/weapon/implant/abductor) in H))
return 1
return 0
/datum/surgery_step/extract_organ
name = "remove heart"
accept_hand = 1
time = 32
var/obj/item/organ/IC = null
var/list/organ_types = list(/obj/item/organ/heart)
/datum/surgery_step/extract_organ/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
for(var/obj/item/I in target.internal_organs)
if(I.type in organ_types)
IC = I
break
user.visible_message("[user] starts to remove [target]'s organs.", "<span class='notice'>You start to remove [target]'s organs...</span>")
/datum/surgery_step/extract_organ/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
if(IC)
user.visible_message("[user] pulls [IC] out of [target]'s [target_zone]!", "<span class='notice'>You pull [IC] out of [target]'s [target_zone].</span>")
user.put_in_hands(IC)
IC.Remove(target, special = 1)
return 1
else
user << "<span class='warning'>You don't find anything in [target]'s [target_zone]!</span>"
return 0
/datum/surgery_step/gland_insert
name = "insert gland"
implements = list(/obj/item/organ/gland = 100)
time = 32
/datum/surgery_step/gland_insert/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
user.visible_message("[user] starts to insert [tool] into [target].", "<span class ='notice'>You start to insert [tool] into [target]...</span>")
/datum/surgery_step/gland_insert/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
user.visible_message("[user] inserts [tool] into [target].", "<span class ='notice'>You insert [tool] into [target].</span>")
user.drop_item()
var/obj/item/organ/gland/gland = tool
gland.Insert(target, 2)
return 1
@@ -0,0 +1,261 @@
/obj/item/organ/gland
name = "fleshy mass"
desc = "A nausea-inducing hunk of twisting flesh and metal."
icon = 'icons/obj/abductor.dmi'
zone = "chest"
slot = "gland"
icon_state = "gland"
status = ORGAN_ROBOTIC
origin_tech = "materials=4;biotech=7;abductor=3"
var/cooldown_low = 300
var/cooldown_high = 300
var/next_activation = 0
var/uses // -1 For inifinite
var/human_only = 0
var/active = 0
/obj/item/organ/gland/proc/ownerCheck()
if(ishuman(owner))
return 1
if(!human_only && iscarbon(owner))
return 1
return 0
/obj/item/organ/gland/proc/Start()
active = 1
next_activation = world.time + rand(cooldown_low,cooldown_high)
/obj/item/organ/gland/Remove(var/mob/living/carbon/M, special = 0)
active = 0
if(initial(uses) == 1)
uses = initial(uses)
..()
/obj/item/organ/gland/Insert(var/mob/living/carbon/M, special = 0)
..()
if(special != 2 && uses) // Special 2 means abductor surgery
Start()
/obj/item/organ/gland/on_life()
if(!active)
return
if(!ownerCheck())
active = 0
return
if(next_activation <= world.time)
activate()
uses--
next_activation = world.time + rand(cooldown_low,cooldown_high)
if(!uses)
active = 0
/obj/item/organ/gland/proc/activate()
return
/obj/item/organ/gland/heals
cooldown_low = 200
cooldown_high = 400
uses = -1
icon_state = "health"
/obj/item/organ/gland/heals/activate()
owner << "<span class='notice'>You feel curiously revitalized.</span>"
owner.adjustBruteLoss(-20)
owner.adjustOxyLoss(-20)
owner.adjustFireLoss(-20)
/obj/item/organ/gland/slime
cooldown_low = 600
cooldown_high = 1200
uses = -1
icon_state = "slime"
/obj/item/organ/gland/slime/activate()
owner << "<span class='warning'>You feel nauseous!</span>"
owner.vomit(20)
var/mob/living/simple_animal/slime/Slime
Slime = new(get_turf(owner), "grey")
Slime.Friends = list(owner)
Slime.Leader = owner
/obj/item/organ/gland/mindshock
origin_tech = "materials=4;biotech=4;magnets=6;abductor=3"
cooldown_low = 300
cooldown_high = 300
uses = -1
icon_state = "mindshock"
/obj/item/organ/gland/mindshock/activate()
owner << "<span class='notice'>You get a headache.</span>"
var/turf/T = get_turf(owner)
for(var/mob/living/carbon/H in orange(4,T))
if(H == owner)
continue
H << "<span class='alien'>You hear a buzz in your head.</span>"
H.confused += 20
/obj/item/organ/gland/pop
cooldown_low = 900
cooldown_high = 1800
uses = 6
human_only = 1
icon_state = "species"
/obj/item/organ/gland/pop/activate()
owner << "<span class='notice'>You feel unlike yourself.</span>"
var/species = pick(list(/datum/species/lizard,/datum/species/jelly/slime,/datum/species/pod,/datum/species/fly))
owner.set_species(species)
/obj/item/organ/gland/ventcrawling
origin_tech = "materials=4;biotech=5;bluespace=4;abductor=3"
cooldown_low = 1800
cooldown_high = 2400
uses = 1
icon_state = "vent"
/obj/item/organ/gland/ventcrawling/activate()
owner << "<span class='notice'>You feel very stretchy.</span>"
owner.ventcrawler = 2
return
/obj/item/organ/gland/viral
cooldown_low = 1800
cooldown_high = 2400
uses = 1
icon_state = "viral"
/obj/item/organ/gland/viral/activate()
owner << "<span class='warning'>You feel sick.</span>"
var/virus_type = pick(/datum/disease/beesease, /datum/disease/brainrot, /datum/disease/magnitis)
var/datum/disease/D = new virus_type()
D.carrier = 1
owner.viruses += D
D.affected_mob = owner
D.holder = owner
owner.med_hud_set_status()
/obj/item/organ/gland/emp //TODO : Replace with something more interesting
origin_tech = "materials=4;biotech=4;magnets=6;abductor=3"
cooldown_low = 900
cooldown_high = 1600
uses = 10
icon_state = "emp"
/obj/item/organ/gland/emp/activate()
owner << "<span class='warning'>You feel a spike of pain in your head.</span>"
empulse(get_turf(owner), 2, 5, 1)
/obj/item/organ/gland/spiderman
cooldown_low = 450
cooldown_high = 900
uses = 10
icon_state = "spider"
/obj/item/organ/gland/spiderman/activate()
owner << "<span class='warning'>You feel something crawling in your skin.</span>"
owner.faction |= "spiders"
new /obj/effect/spider/spiderling(owner.loc)
/obj/item/organ/gland/egg
cooldown_low = 300
cooldown_high = 400
uses = -1
icon_state = "egg"
/obj/item/organ/gland/egg/activate()
owner << "<span class='boldannounce'>You lay an egg!</span>"
var/obj/item/weapon/reagent_containers/food/snacks/egg/egg = new(owner.loc)
egg.reagents.add_reagent("sacid",20)
egg.desc += " It smells bad."
/obj/item/organ/gland/bloody
cooldown_low = 200
cooldown_high = 400
uses = -1
/obj/item/organ/gland/bloody/activate()
owner.adjustBruteLoss(15)
owner.visible_message("<span class='danger'>[owner]'s skin erupts with blood!</span>",\
"<span class='userdanger'>Blood pours from your skin!</span>")
for(var/turf/T in oview(3,owner)) //Make this respect walls and such
owner.add_splatter_floor(T)
for(var/mob/living/carbon/human/H in oview(3,owner)) //Blood decals for simple animals would be neat. aka Carp with blood on it.
H.add_mob_blood(owner)
/obj/item/organ/gland/bodysnatch
cooldown_low = 600
cooldown_high = 600
human_only = 1
uses = 1
/obj/item/organ/gland/bodysnatch/activate()
owner << "<span class='warning'>You feel something moving around inside you...</span>"
//spawn cocoon with clone greytide snpc inside
if(ishuman(owner))
var/obj/effect/cocoon/abductor/C = new (get_turf(owner))
C.Copy(owner)
C.Start()
owner.gib()
return
/obj/effect/cocoon/abductor
name = "slimy cocoon"
desc = "Something is moving inside."
icon = 'icons/effects/effects.dmi'
icon_state = "cocoon_large3"
color = rgb(10,120,10)
density = 1
var/hatch_time = 0
/obj/effect/cocoon/abductor/proc/Copy(mob/living/carbon/human/H)
var/mob/living/carbon/human/interactive/greytide/clone = new(src)
clone.hardset_dna(H.dna.uni_identity,H.dna.struc_enzymes,H.real_name, H.dna.blood_type, H.dna.species.type, H.dna.features)
//There's no define for this / get all items ?
var/list/slots = list(slot_back,slot_w_uniform,slot_wear_suit,\
slot_wear_mask,slot_head,slot_shoes,slot_gloves,slot_ears,\
slot_glasses,slot_belt,slot_s_store,slot_l_store,slot_r_store,slot_wear_id)
for(var/slot in slots)
var/obj/item/I = H.get_item_by_slot(slot)
if(I)
clone.equip_to_slot_if_possible(I,slot)
/obj/effect/cocoon/abductor/proc/Start()
hatch_time = world.time + 600
START_PROCESSING(SSobj, src)
/obj/effect/cocoon/abductor/process()
if(world.time > hatch_time)
STOP_PROCESSING(SSobj, src)
for(var/mob/M in contents)
src.visible_message("<span class='warning'>[src] hatches!</span>")
M.loc = src.loc
qdel(src)
/obj/item/organ/gland/plasma
cooldown_low = 2400
cooldown_high = 3000
origin_tech = "materials=4;biotech=4;plasmatech=6;abductor=3"
uses = 1
/obj/item/organ/gland/plasma/activate()
owner << "<span class='warning'>You feel bloated.</span>"
sleep(150)
if(!owner) return
owner << "<span class='userdanger'>A massive stomachache overcomes you.</span>"
sleep(50)
if(!owner) return
owner.visible_message("<span class='danger'>[owner] explodes in a cloud of plasma!</span>")
var/turf/open/T = get_turf(owner)
if(istype(T))
T.atmos_spawn_air("plasma=300;TEMP=[T20C]")
owner.gib()
return

Some files were not shown because too many files have changed in this diff Show More